<commit_before>/* This file is part of the KDE project
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2007 Mirko Stocker <me@misto.ch>
   Copyright (C) 2009 Dominik Haumann <dhaumann kde org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

//BEGIN Includes
#include "katefilebrowser.h"
#include "katefilebrowser.moc"

#include "katebookmarkhandler.h"

#include <ktexteditor/document.h>
#include <ktexteditor/view.h>

#include <KActionCollection>
#include <KActionMenu>
#include <KConfigGroup>
#include <KDebug>
#include <KDirOperator>
#include <KFilePlacesModel>
#include <KHistoryComboBox>
#include <KLocale>
#include <KToolBar>
#include <KUrlNavigator>
#include <QAbstractItemView>
#include <QDir>
#include <QLabel>
#include <QLineEdit>
#include <QToolButton>

#include <kdeversion.h>

//END Includes

KateFileBrowser::KateFileBrowser(Kate::MainWindow *mainWindow,
                                 QWidget * parent, const char * name)
  : KVBox (parent)
  , m_mainWindow(mainWindow)
{
  setObjectName(name);

  m_toolbar = new KToolBar(this);
  m_toolbar->setMovable(false);
  m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
  m_toolbar->setContextMenuPolicy(Qt::NoContextMenu);

  // includes some actions, but not hooked into the shortcut dialog atm
  m_actionCollection = new KActionCollection(this);
  m_actionCollection->addAssociatedWidget(this);

  KFilePlacesModel* model = new KFilePlacesModel(this);
  m_urlNavigator = new KUrlNavigator(model, KUrl(QDir::homePath()), this);
  connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)), SLOT(updateDirOperator(const KUrl&)));

  m_dirOperator = new KDirOperator(KUrl(), this);
  m_dirOperator->setView(KFile::/* Simple */Detail);
  m_dirOperator->view()->setSelectionMode(QAbstractItemView::ExtendedSelection);
  m_dirOperator->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));

#if KDE_IS_VERSION(4, 4, 60)
  // Mime filter for the KDirOperator
  QStringList filter;
  filter << "text/plain" << "text/html" << "inode/directory";
  m_dirOperator->setNewFileMenuSupportedMimeTypes(filter);
#endif

  setFocusProxy(m_dirOperator);
  connect(m_dirOperator, SIGNAL(viewChanged(QAbstractItemView *)),
          this, SLOT(selectorViewChanged(QAbstractItemView *)));
  connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)),
          m_dirOperator, SLOT(setFocus()));
  // now all actions exist in dir operator and we can use them in the toolbar
  setupActions();
  setupToolbar();

  KHBox* filterBox = new KHBox(this);

  QLabel* filterLabel = new QLabel(i18n("Filter:"), filterBox);
  m_filter = new KHistoryComboBox(true, filterBox);
  filterLabel->setBuddy(m_filter);
  m_filter->setMaxCount(10);
  m_filter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));

  connect(m_filter, SIGNAL(editTextChanged(const QString&)),
          SLOT(slotFilterChange(const QString&)));
  connect(m_filter, SIGNAL(returnPressed(const QString&)),
          m_filter, SLOT(addToHistory(const QString&)));
  connect(m_filter, SIGNAL(returnPressed(const QString&)),
          m_dirOperator, SLOT(setFocus()));

  connect(m_dirOperator, SIGNAL(urlEntered(const KUrl&)),
          this, SLOT(updateUrlNavigator(const KUrl&)));

  // Connect the bookmark handler
  connect(m_bookmarkHandler, SIGNAL(openUrl(const QString&)),
          this, SLOT(setDir(const QString&)));

  m_filter->setWhatsThis(i18n("Enter a name filter to limit which files are displayed."));

  connect(m_dirOperator, SIGNAL(fileSelected(const KFileItem&)), this, SLOT(fileSelected(const KFileItem&)));
  connect(m_mainWindow, SIGNAL(viewChanged()), this, SLOT(autoSyncFolder()));
}

KateFileBrowser::~KateFileBrowser()
{
}
//END Constroctor/Destrctor

//BEGIN Public Methods
void KateFileBrowser::setupToolbar()
{
  KConfigGroup config(KGlobal::config(), "filebrowser");
  QStringList actions = config.readEntry( "toolbar actions", QStringList() );
  if ( actions.isEmpty() ) // default toolbar
    actions << "back" << "forward" << "bookmarks" << "sync_dir" << "configure";

  // remove all actions from the toolbar (there should be none)
  m_toolbar->clear();

  // now add all actions to the toolbar
  foreach (QString it, actions)
  {
    QAction *ac = 0;
    if (it.isEmpty()) continue;
    if (it == "bookmarks" || it == "sync_dir" || it == "configure")
      ac = actionCollection()->action(it);
    else
      ac = m_dirOperator->actionCollection()->action(it);

    if (ac)
      m_toolbar->addAction(ac);
  }
}

void KateFileBrowser::readSessionConfig(KConfigBase *config, const QString & name)
{
  KConfigGroup cgDir(config, name + ":dir");
  m_dirOperator->readConfig(cgDir);
  m_dirOperator->setView(KFile::Default);

  KConfigGroup cg(config, name);
  m_urlNavigator->setUrl(cg.readPathEntry("location", QDir::homePath()));
  setDir(cg.readPathEntry("location", QDir::homePath()));
  m_autoSyncFolder->setChecked(cg.readEntry("auto sync folder", false));
  m_filter->setHistoryItems(cg.readEntry("filter history", QStringList()), true);
}

void KateFileBrowser::writeSessionConfig(KConfigBase *config, const QString & name)
{
  KConfigGroup cgDir(config, name + ":dir");
  m_dirOperator->writeConfig(cgDir);

  KConfigGroup cg = KConfigGroup(config, name);
  cg.writePathEntry("location", m_urlNavigator->url().url());
  cg.writeEntry("auto sync folder", m_autoSyncFolder->isChecked());
  cg.writeEntry("filter history", m_filter->historyItems());
}

//END Public Methods

//BEGIN Public Slots

void KateFileBrowser::slotFilterChange(const QString & nf)
{
  QString f = nf.trimmed();
  const bool empty = f.isEmpty() || f == "*";

  if (empty) {
    m_dirOperator->clearFilter();
  } else {
    m_dirOperator->setNameFilter(f);
  }

  m_dirOperator->updateDir();
}

bool kateFileSelectorIsReadable (const KUrl& url)
{
  if (!url.isLocalFile())
    return true; // what else can we say?

  QDir dir(url.toLocalFile());
  return dir.exists ();
}

void KateFileBrowser::setDir(KUrl u)
{
  KUrl newurl;

  if (!u.isValid())
    newurl.setPath(QDir::homePath());
  else
    newurl = u;

  QString pathstr = newurl.path(KUrl::AddTrailingSlash);
  newurl.setPath(pathstr);

  if (!kateFileSelectorIsReadable (newurl))
    newurl.cd(QString::fromLatin1(".."));

  if (!kateFileSelectorIsReadable (newurl))
    newurl.setPath(QDir::homePath());

  m_dirOperator->setUrl(newurl, true);
}

//END Public Slots

//BEGIN Private Slots

void KateFileBrowser::fileSelected(const KFileItem & /*file*/)
{
  openSelectedFiles();
}

void KateFileBrowser::openSelectedFiles()
{
  const KFileItemList list = m_dirOperator->selectedItems();

  foreach (const KFileItem& item, list)
  {
    m_mainWindow->openUrl(item.url());
  }

  m_dirOperator->view()->selectionModel()->clear();
}

void KateFileBrowser::updateDirOperator(const KUrl& u)
{
  m_dirOperator->setUrl(u, true);
}

void KateFileBrowser::updateUrlNavigator(const KUrl& u)
{
  m_urlNavigator->setUrl(u);
}

void KateFileBrowser::setActiveDocumentDir()
{
//   kDebug(13001)<<"KateFileBrowser::setActiveDocumentDir()";
  KUrl u = activeDocumentUrl();
//   kDebug(13001)<<"URL: "<<u.prettyUrl();
  if (!u.isEmpty())
    setDir(u.upUrl());
//   kDebug(13001)<<"... setActiveDocumentDir() DONE!";
}

void KateFileBrowser::autoSyncFolder()
{
  if (m_autoSyncFolder->isChecked()) {
    setActiveDocumentDir();
  }
}

void KateFileBrowser::selectorViewChanged(QAbstractItemView * newView)
{
  newView->setSelectionMode(QAbstractItemView::ExtendedSelection);
}

//END Private Slots

//BEGIN Protected

KUrl KateFileBrowser::activeDocumentUrl()
{
  KTextEditor::View *v = m_mainWindow->activeView();
  if (v)
    return v->document()->url();
  return KUrl();
}

void KateFileBrowser::setupActions()
{
  // bookmarks action!
  KActionMenu *acmBookmarks = new KActionMenu(KIcon("bookmarks"), i18n("Bookmarks"), this);
  acmBookmarks->setDelayed(false);
  m_bookmarkHandler = new KateBookmarkHandler(this, acmBookmarks->menu());
  acmBookmarks->setShortcutContext(Qt::WidgetWithChildrenShortcut);

  // action for synchronizing the dir operator with the current document path
  KAction* syncFolder = new KAction(this);
  syncFolder->setShortcutContext(Qt::WidgetWithChildrenShortcut);
  syncFolder->setText(i18n("Current Document Folder"));
  syncFolder->setIcon(KIcon("system-switch-user"));
  connect(syncFolder, SIGNAL(triggered()), this, SLOT(setActiveDocumentDir()));

  m_actionCollection->addAction("sync_dir", syncFolder);
  m_actionCollection->addAction("bookmarks", acmBookmarks);

  // section for settings menu
  KActionMenu *optionsMenu = new KActionMenu(KIcon("configure"), i18n("Options"), this);
  optionsMenu->setDelayed(false);
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("short view"));
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("detailed view"));
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("tree view"));
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("detailed tree view"));
  optionsMenu->addSeparator();
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("show hidden"));

  // action for synchronising the dir operator with the current document path
  m_autoSyncFolder = new KAction(this);
  m_autoSyncFolder->setCheckable(true);
  m_autoSyncFolder->setText(i18n("Automatically synchronize with current document"));
  m_autoSyncFolder->setIcon(KIcon("system-switch-user"));
  connect(m_autoSyncFolder, SIGNAL(triggered()), this, SLOT(autoSyncFolder()));
  optionsMenu->addAction(m_autoSyncFolder);

  m_actionCollection->addAction("configure", optionsMenu);
}
//END Protected

// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>dhaumann:     don't focus filesystembrowser on open-file if autosync enabled      BUG: 228319<commit_after>/* This file is part of the KDE project
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2007 Mirko Stocker <me@misto.ch>
   Copyright (C) 2009 Dominik Haumann <dhaumann kde org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

//BEGIN Includes
#include "katefilebrowser.h"
#include "katefilebrowser.moc"

#include "katebookmarkhandler.h"

#include <ktexteditor/document.h>
#include <ktexteditor/view.h>

#include <KActionCollection>
#include <KActionMenu>
#include <KConfigGroup>
#include <KDebug>
#include <KDirOperator>
#include <KFilePlacesModel>
#include <KHistoryComboBox>
#include <KLocale>
#include <KToolBar>
#include <KUrlNavigator>
#include <QAbstractItemView>
#include <QDir>
#include <QLabel>
#include <QLineEdit>
#include <QToolButton>

#include <kdeversion.h>

//END Includes

KateFileBrowser::KateFileBrowser(Kate::MainWindow *mainWindow,
                                 QWidget * parent, const char * name)
  : KVBox (parent)
  , m_mainWindow(mainWindow)
{
  setObjectName(name);

  m_toolbar = new KToolBar(this);
  m_toolbar->setMovable(false);
  m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
  m_toolbar->setContextMenuPolicy(Qt::NoContextMenu);

  // includes some actions, but not hooked into the shortcut dialog atm
  m_actionCollection = new KActionCollection(this);
  m_actionCollection->addAssociatedWidget(this);

  KFilePlacesModel* model = new KFilePlacesModel(this);
  m_urlNavigator = new KUrlNavigator(model, KUrl(QDir::homePath()), this);
  connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)), SLOT(updateDirOperator(const KUrl&)));

  m_dirOperator = new KDirOperator(KUrl(), this);
  m_dirOperator->setView(KFile::/* Simple */Detail);
  m_dirOperator->view()->setSelectionMode(QAbstractItemView::ExtendedSelection);
  m_dirOperator->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));

#if KDE_IS_VERSION(4, 4, 60)
  // Mime filter for the KDirOperator
  QStringList filter;
  filter << "text/plain" << "text/html" << "inode/directory";
  m_dirOperator->setNewFileMenuSupportedMimeTypes(filter);
#endif

  setFocusProxy(m_dirOperator);
  connect(m_dirOperator, SIGNAL(viewChanged(QAbstractItemView *)),
          this, SLOT(selectorViewChanged(QAbstractItemView *)));
  connect(m_urlNavigator, SIGNAL(returnPressed()),
          m_dirOperator, SLOT(setFocus()));
  // now all actions exist in dir operator and we can use them in the toolbar
  setupActions();
  setupToolbar();

  KHBox* filterBox = new KHBox(this);

  QLabel* filterLabel = new QLabel(i18n("Filter:"), filterBox);
  m_filter = new KHistoryComboBox(true, filterBox);
  filterLabel->setBuddy(m_filter);
  m_filter->setMaxCount(10);
  m_filter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));

  connect(m_filter, SIGNAL(editTextChanged(const QString&)),
          SLOT(slotFilterChange(const QString&)));
  connect(m_filter, SIGNAL(returnPressed(const QString&)),
          m_filter, SLOT(addToHistory(const QString&)));
  connect(m_filter, SIGNAL(returnPressed(const QString&)),
          m_dirOperator, SLOT(setFocus()));

  connect(m_dirOperator, SIGNAL(urlEntered(const KUrl&)),
          this, SLOT(updateUrlNavigator(const KUrl&)));

  // Connect the bookmark handler
  connect(m_bookmarkHandler, SIGNAL(openUrl(const QString&)),
          this, SLOT(setDir(const QString&)));

  m_filter->setWhatsThis(i18n("Enter a name filter to limit which files are displayed."));

  connect(m_dirOperator, SIGNAL(fileSelected(const KFileItem&)), this, SLOT(fileSelected(const KFileItem&)));
  connect(m_mainWindow, SIGNAL(viewChanged()), this, SLOT(autoSyncFolder()));
}

KateFileBrowser::~KateFileBrowser()
{
}
//END Constroctor/Destrctor

//BEGIN Public Methods
void KateFileBrowser::setupToolbar()
{
  KConfigGroup config(KGlobal::config(), "filebrowser");
  QStringList actions = config.readEntry( "toolbar actions", QStringList() );
  if ( actions.isEmpty() ) // default toolbar
    actions << "back" << "forward" << "bookmarks" << "sync_dir" << "configure";

  // remove all actions from the toolbar (there should be none)
  m_toolbar->clear();

  // now add all actions to the toolbar
  foreach (QString it, actions)
  {
    QAction *ac = 0;
    if (it.isEmpty()) continue;
    if (it == "bookmarks" || it == "sync_dir" || it == "configure")
      ac = actionCollection()->action(it);
    else
      ac = m_dirOperator->actionCollection()->action(it);

    if (ac)
      m_toolbar->addAction(ac);
  }
}

void KateFileBrowser::readSessionConfig(KConfigBase *config, const QString & name)
{
  KConfigGroup cgDir(config, name + ":dir");
  m_dirOperator->readConfig(cgDir);
  m_dirOperator->setView(KFile::Default);

  KConfigGroup cg(config, name);
  m_urlNavigator->setUrl(cg.readPathEntry("location", QDir::homePath()));
  setDir(cg.readPathEntry("location", QDir::homePath()));
  m_autoSyncFolder->setChecked(cg.readEntry("auto sync folder", false));
  m_filter->setHistoryItems(cg.readEntry("filter history", QStringList()), true);
}

void KateFileBrowser::writeSessionConfig(KConfigBase *config, const QString & name)
{
  KConfigGroup cgDir(config, name + ":dir");
  m_dirOperator->writeConfig(cgDir);

  KConfigGroup cg = KConfigGroup(config, name);
  cg.writePathEntry("location", m_urlNavigator->url().url());
  cg.writeEntry("auto sync folder", m_autoSyncFolder->isChecked());
  cg.writeEntry("filter history", m_filter->historyItems());
}

//END Public Methods

//BEGIN Public Slots

void KateFileBrowser::slotFilterChange(const QString & nf)
{
  QString f = nf.trimmed();
  const bool empty = f.isEmpty() || f == "*";

  if (empty) {
    m_dirOperator->clearFilter();
  } else {
    m_dirOperator->setNameFilter(f);
  }

  m_dirOperator->updateDir();
}

bool kateFileSelectorIsReadable (const KUrl& url)
{
  if (!url.isLocalFile())
    return true; // what else can we say?

  QDir dir(url.toLocalFile());
  return dir.exists ();
}

void KateFileBrowser::setDir(KUrl u)
{
  KUrl newurl;

  if (!u.isValid())
    newurl.setPath(QDir::homePath());
  else
    newurl = u;

  QString pathstr = newurl.path(KUrl::AddTrailingSlash);
  newurl.setPath(pathstr);

  if (!kateFileSelectorIsReadable (newurl))
    newurl.cd(QString::fromLatin1(".."));

  if (!kateFileSelectorIsReadable (newurl))
    newurl.setPath(QDir::homePath());

  m_dirOperator->setUrl(newurl, true);
}

//END Public Slots

//BEGIN Private Slots

void KateFileBrowser::fileSelected(const KFileItem & /*file*/)
{
  openSelectedFiles();
}

void KateFileBrowser::openSelectedFiles()
{
  const KFileItemList list = m_dirOperator->selectedItems();

  foreach (const KFileItem& item, list)
  {
    m_mainWindow->openUrl(item.url());
  }

  m_dirOperator->view()->selectionModel()->clear();
}

void KateFileBrowser::updateDirOperator(const KUrl& u)
{
  m_dirOperator->setUrl(u, true);
}

void KateFileBrowser::updateUrlNavigator(const KUrl& u)
{
  m_urlNavigator->setUrl(u);
}

void KateFileBrowser::setActiveDocumentDir()
{
//   kDebug(13001)<<"KateFileBrowser::setActiveDocumentDir()";
  KUrl u = activeDocumentUrl();
//   kDebug(13001)<<"URL: "<<u.prettyUrl();
  if (!u.isEmpty())
    setDir(u.upUrl());
//   kDebug(13001)<<"... setActiveDocumentDir() DONE!";
}

void KateFileBrowser::autoSyncFolder()
{
  if (m_autoSyncFolder->isChecked()) {
    setActiveDocumentDir();
  }
}

void KateFileBrowser::selectorViewChanged(QAbstractItemView * newView)
{
  newView->setSelectionMode(QAbstractItemView::ExtendedSelection);
}

//END Private Slots

//BEGIN Protected

KUrl KateFileBrowser::activeDocumentUrl()
{
  KTextEditor::View *v = m_mainWindow->activeView();
  if (v)
    return v->document()->url();
  return KUrl();
}

void KateFileBrowser::setupActions()
{
  // bookmarks action!
  KActionMenu *acmBookmarks = new KActionMenu(KIcon("bookmarks"), i18n("Bookmarks"), this);
  acmBookmarks->setDelayed(false);
  m_bookmarkHandler = new KateBookmarkHandler(this, acmBookmarks->menu());
  acmBookmarks->setShortcutContext(Qt::WidgetWithChildrenShortcut);

  // action for synchronizing the dir operator with the current document path
  KAction* syncFolder = new KAction(this);
  syncFolder->setShortcutContext(Qt::WidgetWithChildrenShortcut);
  syncFolder->setText(i18n("Current Document Folder"));
  syncFolder->setIcon(KIcon("system-switch-user"));
  connect(syncFolder, SIGNAL(triggered()), this, SLOT(setActiveDocumentDir()));

  m_actionCollection->addAction("sync_dir", syncFolder);
  m_actionCollection->addAction("bookmarks", acmBookmarks);

  // section for settings menu
  KActionMenu *optionsMenu = new KActionMenu(KIcon("configure"), i18n("Options"), this);
  optionsMenu->setDelayed(false);
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("short view"));
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("detailed view"));
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("tree view"));
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("detailed tree view"));
  optionsMenu->addSeparator();
  optionsMenu->addAction(m_dirOperator->actionCollection()->action("show hidden"));

  // action for synchronising the dir operator with the current document path
  m_autoSyncFolder = new KAction(this);
  m_autoSyncFolder->setCheckable(true);
  m_autoSyncFolder->setText(i18n("Automatically synchronize with current document"));
  m_autoSyncFolder->setIcon(KIcon("system-switch-user"));
  connect(m_autoSyncFolder, SIGNAL(triggered()), this, SLOT(autoSyncFolder()));
  optionsMenu->addAction(m_autoSyncFolder);

  m_actionCollection->addAction("configure", optionsMenu);
}
//END Protected

// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>


<commit_before>/*
    open source routing machine
    Copyright (C) Dennis Luxen, 2010

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU AFFERO General Public License as published by
the Free Software Foundation; either version 3 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
or see http://www.gnu.org/licenses/agpl.txt.
 */

//g++ createHierarchy.cpp -fopenmp -Wno-deprecated -o createHierarchy -O3 -march=native -DNDEBUG

#define VERBOSE(x) x
#define VERBOSE2(x)

#ifdef NDEBUG
#undef VERBOSE
#undef VERBOSE2
#endif

#include <climits>
#include <fstream>
#include <istream>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>

#ifdef _OPENMP
#include <omp.h>
#endif

#include "typedefs.h"
#include "Contractor/GraphLoader.h"
#include "Contractor/BinaryHeap.h"
#include "Contractor/Contractor.h"
#include "Contractor/ContractionCleanup.h"
#include "Contractor/DynamicGraph.h"

#include "DataStructures/NNGrid.h"

using namespace std;

typedef ContractionCleanup::Edge::EdgeData EdgeData;
typedef DynamicGraph<EdgeData>::InputEdge GridEdge;
typedef NNGrid::NNGrid Grid;

vector<NodeInfo> * int2ExtNodeMap = new vector<NodeInfo>();

int main (int argc, char *argv[])
{
    if(argc <= 1)
    {
        cerr << "usage: " << endl << argv[0] << " <osmr-data>" << endl;
        exit(-1);
    }
    cout << "preprocessing data from input file " << argv[1] << endl;

    ifstream in;
    in.open (argv[1]);
    if (!in.is_open()) {
        cerr << "Cannot open " << argv[1] << endl; exit(-1);
    }
    vector<ImportEdge> edgeList;
    const NodeID n = readOSMRGraphFromStream(in, edgeList, int2ExtNodeMap);
    in.close();

    char nodeOut[1024];
    char edgeOut[1024];
    char ramIndexOut[1024];
    char fileIndexOut[1024];
    strcpy(nodeOut, argv[1]);
    strcpy(edgeOut, argv[1]);
    strcpy(ramIndexOut, argv[1]);
    strcpy(fileIndexOut, argv[1]);

    strcat(nodeOut, ".nodes");
    strcat(edgeOut, ".hsgr");
    strcat(ramIndexOut, ".ramIndex");
    strcat(fileIndexOut, ".fileIndex");
    ofstream mapOutFile(nodeOut, ios::binary);

    Grid * g = new Grid();
    cout << "building grid ..." << flush;
    Percent p(edgeList.size());
    for(NodeID i = 0; i < edgeList.size(); i++)
    {
        p.printIncrement();
        int slat = int2ExtNodeMap->at(edgeList[i].source()).lat;
        int slon = int2ExtNodeMap->at(edgeList[i].source()).lon;
        int tlat = int2ExtNodeMap->at(edgeList[i].target()).lat;
        int tlon = int2ExtNodeMap->at(edgeList[i].target()).lon;
        g->AddEdge(
                _Edge(
                        edgeList[i].source(),
                        edgeList[i].target(),
                        0,
                        ((edgeList[i].isBackward() && edgeList[i].isForward()) ? 0 : 1),
                        edgeList[i].weight()
                ),

                _Coordinate(slat, slon),
                _Coordinate(tlat, tlon)
        );
    }
    g->ConstructGrid(ramIndexOut, fileIndexOut);
    delete g;

    //Serializing the node map.
    for(NodeID i = 0; i < int2ExtNodeMap->size(); i++)
    {
        mapOutFile.write((char *)&(int2ExtNodeMap->at(i)), sizeof(NodeInfo));
    }
    mapOutFile.close();
    int2ExtNodeMap->clear();

    Contractor* contractor = new Contractor( n, edgeList );

    contractor->Run();

    contractor->checkForAllOrigEdges(edgeList);

    std::vector< ContractionCleanup::Edge > contractedEdges;
    contractor->GetEdges( contractedEdges );

    ContractionCleanup * cleanup = new ContractionCleanup(n, contractedEdges);
    cleanup->Run();

    std::vector< GridEdge> cleanedEdgeList;
    cleanup->GetData(cleanedEdgeList);

    ofstream edgeOutFile(edgeOut, ios::binary);

    //Serializing the edge list.
    for(std::vector< GridEdge>::iterator it = cleanedEdgeList.begin(); it != cleanedEdgeList.end(); it++)
    {
        int distance= it->data.distance;
        assert(distance > 0);
        bool shortcut= it->data.shortcut;
        bool forward= it->data.forward;
        bool backward= it->data.backward;
        NodeID middle= it->data.middle;
        NodeID source = it->source;
        NodeID target = it->target;

        edgeOutFile.write((char *)&(distance), sizeof(int));
        edgeOutFile.write((char *)&(shortcut), sizeof(bool));
        edgeOutFile.write((char *)&(forward), sizeof(bool));
        edgeOutFile.write((char *)&(backward), sizeof(bool));
        edgeOutFile.write((char *)&(middle), sizeof(NodeID));

        edgeOutFile.write((char *)&(source), sizeof(NodeID));
        edgeOutFile.write((char *)&(target), sizeof(NodeID));
    }
    edgeOutFile.close();
    cleanedEdgeList.clear();

    delete cleanup;
    delete contractor;
    delete int2ExtNodeMap;
}
<commit_msg>Fix problem with long edges (ferry connections)<commit_after>/*
    open source routing machine
    Copyright (C) Dennis Luxen, 2010

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU AFFERO General Public License as published by
the Free Software Foundation; either version 3 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
or see http://www.gnu.org/licenses/agpl.txt.
 */

//g++ createHierarchy.cpp -fopenmp -Wno-deprecated -o createHierarchy -O3 -march=native -DNDEBUG

#define VERBOSE(x) x
#define VERBOSE2(x)

#ifdef NDEBUG
#undef VERBOSE
#undef VERBOSE2
#endif

#include <climits>
#include <fstream>
#include <istream>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>

#ifdef _OPENMP
#include <omp.h>
#endif

#include "typedefs.h"
#include "Contractor/GraphLoader.h"
#include "Contractor/BinaryHeap.h"
#include "Contractor/Contractor.h"
#include "Contractor/ContractionCleanup.h"
#include "Contractor/DynamicGraph.h"

#include "DataStructures/NNGrid.h"

using namespace std;

typedef ContractionCleanup::Edge::EdgeData EdgeData;
typedef DynamicGraph<EdgeData>::InputEdge GridEdge;
typedef NNGrid::NNGrid Grid;

vector<NodeInfo> * int2ExtNodeMap = new vector<NodeInfo>();

int main (int argc, char *argv[])
{
    if(argc <= 1)
    {
        cerr << "usage: " << endl << argv[0] << " <osmr-data>" << endl;
        exit(-1);
    }
    cout << "preprocessing data from input file " << argv[1] << endl;

    ifstream in;
    in.open (argv[1]);
    if (!in.is_open()) {
        cerr << "Cannot open " << argv[1] << endl; exit(-1);
    }
    vector<ImportEdge> edgeList;
    const NodeID n = readOSMRGraphFromStream(in, edgeList, int2ExtNodeMap);
    in.close();

    char nodeOut[1024];
    char edgeOut[1024];
    char ramIndexOut[1024];
    char fileIndexOut[1024];
    strcpy(nodeOut, argv[1]);
    strcpy(edgeOut, argv[1]);
    strcpy(ramIndexOut, argv[1]);
    strcpy(fileIndexOut, argv[1]);

    strcat(nodeOut, ".nodes");
    strcat(edgeOut, ".hsgr");
    strcat(ramIndexOut, ".ramIndex");
    strcat(fileIndexOut, ".fileIndex");
    ofstream mapOutFile(nodeOut, ios::binary);

    Grid * g = new Grid();
    cout << "building grid ..." << flush;
    Percent p(edgeList.size());
    for(NodeID i = 0; i < edgeList.size(); i++)
    {
        p.printIncrement();
        int slat = int2ExtNodeMap->at(edgeList[i].source()).lat;
        int slon = int2ExtNodeMap->at(edgeList[i].source()).lon;
        int tlat = int2ExtNodeMap->at(edgeList[i].target()).lat;
        int tlon = int2ExtNodeMap->at(edgeList[i].target()).lon;
        g->AddEdge(
                _Edge(
                        edgeList[i].source(),
                        edgeList[i].target(),
                        0,
                        ((edgeList[i].isBackward() && edgeList[i].isForward()) ? 0 : 1),
                        edgeList[i].weight()
                ),

                _Coordinate(slat, slon),
                _Coordinate(tlat, tlon)
        );
    }
    g->ConstructGrid(ramIndexOut, fileIndexOut);
    delete g;

    //Serializing the node map.
    for(NodeID i = 0; i < int2ExtNodeMap->size(); i++)
    {
        mapOutFile.write((char *)&(int2ExtNodeMap->at(i)), sizeof(NodeInfo));
    }
    mapOutFile.close();
    int2ExtNodeMap->clear();

    Contractor* contractor = new Contractor( n, edgeList );

    contractor->Run();

    cout << "checking data sanity ..." << flush;
    contractor->checkForAllOrigEdges(edgeList);
    cout << "ok" << endl;
    std::vector< ContractionCleanup::Edge > contractedEdges;
    contractor->GetEdges( contractedEdges );

    ContractionCleanup * cleanup = new ContractionCleanup(n, contractedEdges);
    cleanup->Run();

    std::vector< GridEdge> cleanedEdgeList;
    cleanup->GetData(cleanedEdgeList);

    ofstream edgeOutFile(edgeOut, ios::binary);

    //Serializing the edge list.
    for(std::vector< GridEdge>::iterator it = cleanedEdgeList.begin(); it != cleanedEdgeList.end(); it++)
    {
        int distance= it->data.distance;
        assert(distance > 0);
        bool shortcut= it->data.shortcut;
        bool forward= it->data.forward;
        bool backward= it->data.backward;
        NodeID middle= it->data.middle;
        NodeID source = it->source;
        NodeID target = it->target;

        edgeOutFile.write((char *)&(distance), sizeof(int));
        edgeOutFile.write((char *)&(shortcut), sizeof(bool));
        edgeOutFile.write((char *)&(forward), sizeof(bool));
        edgeOutFile.write((char *)&(backward), sizeof(bool));
        edgeOutFile.write((char *)&(middle), sizeof(NodeID));

        edgeOutFile.write((char *)&(source), sizeof(NodeID));
        edgeOutFile.write((char *)&(target), sizeof(NodeID));
    }
    edgeOutFile.close();
    cleanedEdgeList.clear();

    delete cleanup;
    delete contractor;
    delete int2ExtNodeMap;
}
<|endoftext|>


<commit_before>//===- llvm/CodeGen/GlobalISel/InstructionSelect.cpp - InstructionSelect ---==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements the InstructionSelect class.
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/Twine.h"
#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Target/TargetSubtargetInfo.h"

#define DEBUG_TYPE "instruction-select"

using namespace llvm;

char InstructionSelect::ID = 0;
INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE,
                      "Select target instructions out of generic instructions",
                      false, false)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
INITIALIZE_PASS_END(InstructionSelect, DEBUG_TYPE,
                    "Select target instructions out of generic instructions",
                    false, false)

InstructionSelect::InstructionSelect() : MachineFunctionPass(ID) {
  initializeInstructionSelectPass(*PassRegistry::getPassRegistry());
}

void InstructionSelect::getAnalysisUsage(AnalysisUsage &AU) const {
  AU.addRequired<TargetPassConfig>();
  MachineFunctionPass::getAnalysisUsage(AU);
}

static void reportSelectionError(const MachineInstr *MI, const Twine &Message) {
  const MachineFunction &MF = *MI->getParent()->getParent();
  std::string ErrStorage;
  raw_string_ostream Err(ErrStorage);
  Err << Message << ":\nIn function: " << MF.getName() << '\n';
  if (MI)
    Err << *MI << '\n';
  report_fatal_error(Err.str());
}

bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
  // If the ISel pipeline failed, do not bother running that pass.
  if (MF.getProperties().hasProperty(
          MachineFunctionProperties::Property::FailedISel))
    return false;

  DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n');

  const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
  const InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector();
  assert(ISel && "Cannot work without InstructionSelector");

  // FIXME: freezeReservedRegs is now done in IRTranslator, but there are many
  // other MF/MFI fields we need to initialize.

  const MachineRegisterInfo &MRI = MF.getRegInfo();

#ifndef NDEBUG
  // Check that our input is fully legal: we require the function to have the
  // Legalized property, so it should be.
  // FIXME: This should be in the MachineVerifier, but it can't use the
  // LegalizerInfo as it's currently in the separate GlobalISel library.
  // The RegBankSelected property is already checked in the verifier. Note
  // that it has the same layering problem, but we only use inline methods so
  // end up not needing to link against the GlobalISel library.
  if (const LegalizerInfo *MLI = MF.getSubtarget().getLegalizerInfo())
    for (const MachineBasicBlock &MBB : MF)
      for (const MachineInstr &MI : MBB)
        if (isPreISelGenericOpcode(MI.getOpcode()) && !MLI->isLegal(MI, MRI))
          reportSelectionError(&MI, "Instruction is not legal");

#endif
  // FIXME: We could introduce new blocks and will need to fix the outer loop.
  // Until then, keep track of the number of blocks to assert that we don't.
  const size_t NumBlocks = MF.size();

  bool Failed = false;
  for (MachineBasicBlock *MBB : post_order(&MF)) {
    if (MBB->empty())
      continue;

    // Select instructions in reverse block order. We permit erasing so have
    // to resort to manually iterating and recognizing the begin (rend) case.
    bool ReachedBegin = false;
    for (auto MII = std::prev(MBB->end()), Begin = MBB->begin();
         !ReachedBegin;) {
#ifndef NDEBUG
      // Keep track of the insertion range for debug printing.
      const auto AfterIt = std::next(MII);
#endif
      // Select this instruction.
      MachineInstr &MI = *MII;

      // And have our iterator point to the next instruction, if there is one.
      if (MII == Begin)
        ReachedBegin = true;
      else
        --MII;

      DEBUG(dbgs() << "Selecting: \n  " << MI);

      if (!ISel->select(MI)) {
        if (TPC.isGlobalISelAbortEnabled())
          // FIXME: It would be nice to dump all inserted instructions.  It's
          // not obvious how, esp. considering select() can insert after MI.
          reportSelectionError(&MI, "Cannot select");
        Failed = true;
        break;
      }

      // Dump the range of instructions that MI expanded into.
      DEBUG({
        auto InsertedBegin = ReachedBegin ? MBB->begin() : std::next(MII);
        dbgs() << "Into:\n";
        for (auto &InsertedMI : make_range(InsertedBegin, AfterIt))
          dbgs() << "  " << InsertedMI;
        dbgs() << '\n';
      });
    }
  }

  // Now that selection is complete, there are no more generic vregs.  Verify
  // that the size of the now-constrained vreg is unchanged and that it has a
  // register class.
  for (auto &VRegToType : MRI.getVRegToType()) {
    unsigned VReg = VRegToType.first;
    auto *RC = MRI.getRegClassOrNull(VReg);
    auto *MI = MRI.def_instr_begin(VReg) == MRI.def_instr_end()
                   ? nullptr
                   : &*MRI.def_instr_begin(VReg);
    if (!RC) {
      if (TPC.isGlobalISelAbortEnabled())
        reportSelectionError(MI, "VReg as no regclass after selection");
      Failed = true;
      break;
    }

    if (VRegToType.second.isValid() &&
        VRegToType.second.getSizeInBits() > (RC->getSize() * 8)) {
      if (TPC.isGlobalISelAbortEnabled())
        reportSelectionError(
            MI, "VReg has explicit size different from class size");
      Failed = true;
      break;
    }
  }

  MRI.getVRegToType().clear();

  if (!TPC.isGlobalISelAbortEnabled() && (Failed || MF.size() != NumBlocks)) {
    MF.getProperties().set(MachineFunctionProperties::Property::FailedISel);
    return false;
  }
  assert(MF.size() == NumBlocks && "Inserting blocks is not supported yet");

  // FIXME: Should we accurately track changes?
  return true;
}
<commit_msg>GlobalISel: Pass the MachineFunction in to reportSelectionError directly<commit_after>//===- llvm/CodeGen/GlobalISel/InstructionSelect.cpp - InstructionSelect ---==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements the InstructionSelect class.
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/Twine.h"
#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Target/TargetSubtargetInfo.h"

#define DEBUG_TYPE "instruction-select"

using namespace llvm;

char InstructionSelect::ID = 0;
INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE,
                      "Select target instructions out of generic instructions",
                      false, false)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
INITIALIZE_PASS_END(InstructionSelect, DEBUG_TYPE,
                    "Select target instructions out of generic instructions",
                    false, false)

InstructionSelect::InstructionSelect() : MachineFunctionPass(ID) {
  initializeInstructionSelectPass(*PassRegistry::getPassRegistry());
}

void InstructionSelect::getAnalysisUsage(AnalysisUsage &AU) const {
  AU.addRequired<TargetPassConfig>();
  MachineFunctionPass::getAnalysisUsage(AU);
}

static void reportSelectionError(const MachineFunction &MF,
                                 const MachineInstr *MI, const Twine &Message) {
  std::string ErrStorage;
  raw_string_ostream Err(ErrStorage);
  Err << Message << ":\nIn function: " << MF.getName() << '\n';
  if (MI)
    Err << *MI << '\n';
  report_fatal_error(Err.str());
}

bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
  // If the ISel pipeline failed, do not bother running that pass.
  if (MF.getProperties().hasProperty(
          MachineFunctionProperties::Property::FailedISel))
    return false;

  DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n');

  const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
  const InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector();
  assert(ISel && "Cannot work without InstructionSelector");

  // FIXME: freezeReservedRegs is now done in IRTranslator, but there are many
  // other MF/MFI fields we need to initialize.

  const MachineRegisterInfo &MRI = MF.getRegInfo();

#ifndef NDEBUG
  // Check that our input is fully legal: we require the function to have the
  // Legalized property, so it should be.
  // FIXME: This should be in the MachineVerifier, but it can't use the
  // LegalizerInfo as it's currently in the separate GlobalISel library.
  // The RegBankSelected property is already checked in the verifier. Note
  // that it has the same layering problem, but we only use inline methods so
  // end up not needing to link against the GlobalISel library.
  if (const LegalizerInfo *MLI = MF.getSubtarget().getLegalizerInfo())
    for (const MachineBasicBlock &MBB : MF)
      for (const MachineInstr &MI : MBB)
        if (isPreISelGenericOpcode(MI.getOpcode()) && !MLI->isLegal(MI, MRI))
          reportSelectionError(MF, &MI, "Instruction is not legal");

#endif
  // FIXME: We could introduce new blocks and will need to fix the outer loop.
  // Until then, keep track of the number of blocks to assert that we don't.
  const size_t NumBlocks = MF.size();

  bool Failed = false;
  for (MachineBasicBlock *MBB : post_order(&MF)) {
    if (MBB->empty())
      continue;

    // Select instructions in reverse block order. We permit erasing so have
    // to resort to manually iterating and recognizing the begin (rend) case.
    bool ReachedBegin = false;
    for (auto MII = std::prev(MBB->end()), Begin = MBB->begin();
         !ReachedBegin;) {
#ifndef NDEBUG
      // Keep track of the insertion range for debug printing.
      const auto AfterIt = std::next(MII);
#endif
      // Select this instruction.
      MachineInstr &MI = *MII;

      // And have our iterator point to the next instruction, if there is one.
      if (MII == Begin)
        ReachedBegin = true;
      else
        --MII;

      DEBUG(dbgs() << "Selecting: \n  " << MI);

      if (!ISel->select(MI)) {
        if (TPC.isGlobalISelAbortEnabled())
          // FIXME: It would be nice to dump all inserted instructions.  It's
          // not obvious how, esp. considering select() can insert after MI.
          reportSelectionError(MF, &MI, "Cannot select");
        Failed = true;
        break;
      }

      // Dump the range of instructions that MI expanded into.
      DEBUG({
        auto InsertedBegin = ReachedBegin ? MBB->begin() : std::next(MII);
        dbgs() << "Into:\n";
        for (auto &InsertedMI : make_range(InsertedBegin, AfterIt))
          dbgs() << "  " << InsertedMI;
        dbgs() << '\n';
      });
    }
  }

  // Now that selection is complete, there are no more generic vregs.  Verify
  // that the size of the now-constrained vreg is unchanged and that it has a
  // register class.
  for (auto &VRegToType : MRI.getVRegToType()) {
    unsigned VReg = VRegToType.first;
    auto *RC = MRI.getRegClassOrNull(VReg);
    auto *MI = MRI.def_instr_begin(VReg) == MRI.def_instr_end()
                   ? nullptr
                   : &*MRI.def_instr_begin(VReg);
    if (!RC) {
      if (TPC.isGlobalISelAbortEnabled())
        reportSelectionError(MF, MI, "VReg as no regclass after selection");
      Failed = true;
      break;
    }

    if (VRegToType.second.isValid() &&
        VRegToType.second.getSizeInBits() > (RC->getSize() * 8)) {
      if (TPC.isGlobalISelAbortEnabled())
        reportSelectionError(
            MF, MI, "VReg has explicit size different from class size");
      Failed = true;
      break;
    }
  }

  MRI.getVRegToType().clear();

  if (!TPC.isGlobalISelAbortEnabled() && (Failed || MF.size() != NumBlocks)) {
    MF.getProperties().set(MachineFunctionProperties::Property::FailedISel);
    return false;
  }
  assert(MF.size() == NumBlocks && "Inserting blocks is not supported yet");

  // FIXME: Should we accurately track changes?
  return true;
}
<|endoftext|>


<commit_before>//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <vector>
#include "llvm/Bitcode/BitstreamWriter.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/DenseSet.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/Version.h"
#include "clang/Frontend/SerializedDiagnosticPrinter.h"

using namespace clang;

namespace {

/// \brief A utility class for entering and exiting bitstream blocks.
class BlockEnterExit {
  llvm::BitstreamWriter &Stream;
public:
  BlockEnterExit(llvm::BitstreamWriter &stream, unsigned blockID,
                 unsigned codelen = 3)
    : Stream(stream) {
      Stream.EnterSubblock(blockID, codelen);
  }
  ~BlockEnterExit() {
    Stream.ExitBlock();
  }
};

class AbbreviationMap {
  llvm::DenseMap<unsigned, unsigned> Abbrevs;
public:
  AbbreviationMap() {}

  void set(unsigned recordID, unsigned abbrevID) {
    assert(Abbrevs.find(recordID) == Abbrevs.end()
           && "Abbreviation already set.");
    Abbrevs[recordID] = abbrevID;
  }

  unsigned get(unsigned recordID) {
    assert(Abbrevs.find(recordID) != Abbrevs.end() &&
           "Abbreviation not set.");
    return Abbrevs[recordID];
  }
};

typedef llvm::SmallVector<uint64_t, 64> RecordData;
typedef llvm::SmallVectorImpl<uint64_t> RecordDataImpl;

class SDiagsWriter : public DiagnosticConsumer {
public:
  SDiagsWriter(DiagnosticsEngine &diags, llvm::raw_ostream *os)
    : Stream(Buffer), OS(os), Diags(diags)
  {
    EmitPreamble();
  };

  ~SDiagsWriter() {}

  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                        const Diagnostic &Info);

  void EndSourceFile();

  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
    // It makes no sense to clone this.
    return 0;
  }

private:
  /// \brief Emit the preamble for the serialized diagnostics.
  void EmitPreamble();

  /// \brief Emit the BLOCKINFO block.
  void EmitBlockInfoBlock();

  /// \brief Emit the raw characters of the provided string.
  void EmitRawStringContents(StringRef str);

  /// \brief Emit the block containing categories and file names.
  void EmitCategoriesAndFileNames();

  /// \brief The version of the diagnostics file.
  enum { Version = 1 };

  /// \brief The byte buffer for the serialized content.
  std::vector<unsigned char> Buffer;

  /// \brief The BitStreamWriter for the serialized diagnostics.
  llvm::BitstreamWriter Stream;

  /// \brief The name of the diagnostics file.
  llvm::OwningPtr<llvm::raw_ostream> OS;

  /// \brief The DiagnosticsEngine tied to all diagnostic locations.
  DiagnosticsEngine &Diags;

  /// \brief The set of constructed record abbreviations.
  AbbreviationMap Abbrevs;

  /// \brief A utility buffer for constructing record content.
  RecordData Record;

  /// \brief A text buffer for rendering diagnostic text.
  llvm::SmallString<256> diagBuf;

  /// \brief The collection of diagnostic categories used.
  llvm::DenseSet<unsigned> Categories;

  /// \brief The collection of files used.
  llvm::DenseSet<FileID> Files;

  enum BlockIDs {
    /// \brief The DIAG block, which acts as a container around a diagnostic.
    BLOCK_DIAG = llvm::bitc::FIRST_APPLICATION_BLOCKID,
    /// \brief The STRINGS block, which contains strings
    /// from multiple diagnostics.
    BLOCK_STRINGS
  };

  enum RecordIDs {
    RECORD_DIAG = 1,
    RECORD_DIAG_FLAG,
    RECORD_CATEGORY,
    RECORD_FILENAME
  };

};
} // end anonymous namespace

namespace clang {
namespace serialized_diags {
DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
  return new SDiagsWriter(Diags, OS);
}
} // end namespace serialized_diags
} // end namespace clang

//===----------------------------------------------------------------------===//
// Serialization methods.
//===----------------------------------------------------------------------===//

/// \brief Emits a block ID in the BLOCKINFO block.
static void EmitBlockID(unsigned ID, const char *Name,
                        llvm::BitstreamWriter &Stream,
                        RecordDataImpl &Record) {
  Record.clear();
  Record.push_back(ID);
  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);

  // Emit the block name if present.
  if (Name == 0 || Name[0] == 0)
    return;

  Record.clear();

  while (*Name)
    Record.push_back(*Name++);

  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
}

/// \brief Emits a record ID in the BLOCKINFO block.
static void EmitRecordID(unsigned ID, const char *Name,
                         llvm::BitstreamWriter &Stream,
                         RecordDataImpl &Record){
  Record.clear();
  Record.push_back(ID);

  while (*Name)
    Record.push_back(*Name++);

  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
}

/// \brief Emits the preamble of the diagnostics file.
void SDiagsWriter::EmitPreamble() {
 // EmitRawStringContents("CLANG_DIAGS");
 // Stream.Emit(Version, 32);

  // Emit the file header.
  Stream.Emit((unsigned)'D', 8);
  Stream.Emit((unsigned)'I', 8);
  Stream.Emit((unsigned)'A', 8);
  Stream.Emit((unsigned)'G', 8);

  EmitBlockInfoBlock();
}

void SDiagsWriter::EmitBlockInfoBlock() {
  Stream.EnterBlockInfoBlock(3);

  // ==---------------------------------------------------------------------==//
  // The subsequent records and Abbrevs are for the "Diagnostic" block.
  // ==---------------------------------------------------------------------==//

  EmitBlockID(BLOCK_DIAG, "Diagnostic", Stream, Record);
  EmitRecordID(RECORD_DIAG, "Diagnostic Info", Stream, Record);
  EmitRecordID(RECORD_DIAG_FLAG, "Diagnostic Flag", Stream, Record);

  // Emit Abbrevs.
  using namespace llvm;

  // Emit abbreviation for RECORD_DIAG.
  BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16-3)); // Category.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
  Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));

  // Emit the abbreviation for RECORD_DIAG_FLAG.
  Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
  Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));

  // ==---------------------------------------------------------------------==//
  // The subsequent records and Abbrevs are for the "Strings" block.
  // ==---------------------------------------------------------------------==//

  EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
  EmitRecordID(RECORD_CATEGORY, "Category Name", Stream, Record);
  EmitRecordID(RECORD_FILENAME, "File Name", Stream, Record);

  Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.
  Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
                                                          Abbrev));

  Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
  Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
                                                          Abbrev));

  Stream.ExitBlock();
}

void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
  for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
    Stream.Emit(*I, 8);
}

void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                                    const Diagnostic &Info) {

  BlockEnterExit DiagBlock(Stream, BLOCK_DIAG);

  // Emit the RECORD_DIAG record.
  Record.clear();
  Record.push_back(RECORD_DIAG);
  Record.push_back(DiagLevel);
  unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
  Record.push_back(category);
  Categories.insert(category);
  diagBuf.clear();
  Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
  Record.push_back(diagBuf.str().size());
  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());

  // Emit the RECORD_DIAG_FLAG record.
  StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
  if (!FlagName.empty()) {
    Record.clear();
    Record.push_back(RECORD_DIAG_FLAG);
    Record.push_back(FlagName.size());
    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
                              Record, FlagName.str());
  }

  // FIXME: emit location
  // FIXME: emit ranges
  // FIXME: emit notes
  // FIXME: emit fixits
}

template <typename T>
static void populateAndSort(std::vector<T> &scribble,
                            llvm::DenseSet<T> &set) {
  scribble.clear();

  for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
       it != ei; ++it)
    scribble.push_back(*it);

  // Sort 'scribble' so we always have a deterministic ordering in the
  // serialized file.
  std::sort(scribble.begin(), scribble.end());
}

void SDiagsWriter::EmitCategoriesAndFileNames() {

  if (Categories.empty() && Files.empty())
    return;

  BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);

  // Emit the category names.
  {
    std::vector<unsigned> scribble;
    populateAndSort(scribble, Categories);
    for (std::vector<unsigned>::iterator it = scribble.begin(),
          ei = scribble.end(); it != ei ; ++it) {
      Record.clear();
      Record.push_back(RECORD_CATEGORY);
      StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it);
      Record.push_back(catName.size());
      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
    }
  }

  // Emit the file names.
  {
    std::vector<FileID> scribble;
    populateAndSort(scribble, Files);
    for (std::vector<FileID>::iterator it = scribble.begin(),
         ei = scribble.end(); it != ei; ++it) {
      SourceManager &SM = Diags.getSourceManager();
      const FileEntry *FE = SM.getFileEntryForID(*it);
      StringRef Name = FE->getName();

      Record.clear();
      Record.push_back(Name.size());
      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
    }
  }

}

void SDiagsWriter::EndSourceFile() {
  EmitCategoriesAndFileNames();

  // Write the generated bitstream to "Out".
  OS->write((char *)&Buffer.front(), Buffer.size());
  OS->flush();

  OS.reset(0);
}

<commit_msg>Also include file modification time and size in output of SerializedDiagnosticPrinter.<commit_after>//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <vector>
#include "llvm/Bitcode/BitstreamWriter.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/DenseSet.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/Version.h"
#include "clang/Frontend/SerializedDiagnosticPrinter.h"

using namespace clang;

namespace {

/// \brief A utility class for entering and exiting bitstream blocks.
class BlockEnterExit {
  llvm::BitstreamWriter &Stream;
public:
  BlockEnterExit(llvm::BitstreamWriter &stream, unsigned blockID,
                 unsigned codelen = 3)
    : Stream(stream) {
      Stream.EnterSubblock(blockID, codelen);
  }
  ~BlockEnterExit() {
    Stream.ExitBlock();
  }
};

class AbbreviationMap {
  llvm::DenseMap<unsigned, unsigned> Abbrevs;
public:
  AbbreviationMap() {}

  void set(unsigned recordID, unsigned abbrevID) {
    assert(Abbrevs.find(recordID) == Abbrevs.end()
           && "Abbreviation already set.");
    Abbrevs[recordID] = abbrevID;
  }

  unsigned get(unsigned recordID) {
    assert(Abbrevs.find(recordID) != Abbrevs.end() &&
           "Abbreviation not set.");
    return Abbrevs[recordID];
  }
};

typedef llvm::SmallVector<uint64_t, 64> RecordData;
typedef llvm::SmallVectorImpl<uint64_t> RecordDataImpl;

class SDiagsWriter : public DiagnosticConsumer {
public:
  SDiagsWriter(DiagnosticsEngine &diags, llvm::raw_ostream *os)
    : Stream(Buffer), OS(os), Diags(diags)
  {
    EmitPreamble();
  };

  ~SDiagsWriter() {}

  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                        const Diagnostic &Info);

  void EndSourceFile();

  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
    // It makes no sense to clone this.
    return 0;
  }

private:
  /// \brief Emit the preamble for the serialized diagnostics.
  void EmitPreamble();

  /// \brief Emit the BLOCKINFO block.
  void EmitBlockInfoBlock();

  /// \brief Emit the raw characters of the provided string.
  void EmitRawStringContents(StringRef str);

  /// \brief Emit the block containing categories and file names.
  void EmitCategoriesAndFileNames();

  /// \brief The version of the diagnostics file.
  enum { Version = 1 };

  /// \brief The byte buffer for the serialized content.
  std::vector<unsigned char> Buffer;

  /// \brief The BitStreamWriter for the serialized diagnostics.
  llvm::BitstreamWriter Stream;

  /// \brief The name of the diagnostics file.
  llvm::OwningPtr<llvm::raw_ostream> OS;

  /// \brief The DiagnosticsEngine tied to all diagnostic locations.
  DiagnosticsEngine &Diags;

  /// \brief The set of constructed record abbreviations.
  AbbreviationMap Abbrevs;

  /// \brief A utility buffer for constructing record content.
  RecordData Record;

  /// \brief A text buffer for rendering diagnostic text.
  llvm::SmallString<256> diagBuf;

  /// \brief The collection of diagnostic categories used.
  llvm::DenseSet<unsigned> Categories;

  /// \brief The collection of files used.
  llvm::DenseSet<FileID> Files;

  enum BlockIDs {
    /// \brief The DIAG block, which acts as a container around a diagnostic.
    BLOCK_DIAG = llvm::bitc::FIRST_APPLICATION_BLOCKID,
    /// \brief The STRINGS block, which contains strings
    /// from multiple diagnostics.
    BLOCK_STRINGS
  };

  enum RecordIDs {
    RECORD_DIAG = 1,
    RECORD_DIAG_FLAG,
    RECORD_CATEGORY,
    RECORD_FILENAME
  };

};
} // end anonymous namespace

namespace clang {
namespace serialized_diags {
DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
  return new SDiagsWriter(Diags, OS);
}
} // end namespace serialized_diags
} // end namespace clang

//===----------------------------------------------------------------------===//
// Serialization methods.
//===----------------------------------------------------------------------===//

/// \brief Emits a block ID in the BLOCKINFO block.
static void EmitBlockID(unsigned ID, const char *Name,
                        llvm::BitstreamWriter &Stream,
                        RecordDataImpl &Record) {
  Record.clear();
  Record.push_back(ID);
  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);

  // Emit the block name if present.
  if (Name == 0 || Name[0] == 0)
    return;

  Record.clear();

  while (*Name)
    Record.push_back(*Name++);

  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
}

/// \brief Emits a record ID in the BLOCKINFO block.
static void EmitRecordID(unsigned ID, const char *Name,
                         llvm::BitstreamWriter &Stream,
                         RecordDataImpl &Record){
  Record.clear();
  Record.push_back(ID);

  while (*Name)
    Record.push_back(*Name++);

  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
}

/// \brief Emits the preamble of the diagnostics file.
void SDiagsWriter::EmitPreamble() {
 // EmitRawStringContents("CLANG_DIAGS");
 // Stream.Emit(Version, 32);

  // Emit the file header.
  Stream.Emit((unsigned)'D', 8);
  Stream.Emit((unsigned)'I', 8);
  Stream.Emit((unsigned)'A', 8);
  Stream.Emit((unsigned)'G', 8);

  EmitBlockInfoBlock();
}

void SDiagsWriter::EmitBlockInfoBlock() {
  Stream.EnterBlockInfoBlock(3);

  // ==---------------------------------------------------------------------==//
  // The subsequent records and Abbrevs are for the "Diagnostic" block.
  // ==---------------------------------------------------------------------==//

  EmitBlockID(BLOCK_DIAG, "Diagnostic", Stream, Record);
  EmitRecordID(RECORD_DIAG, "Diagnostic Info", Stream, Record);
  EmitRecordID(RECORD_DIAG_FLAG, "Diagnostic Flag", Stream, Record);

  // Emit Abbrevs.
  using namespace llvm;

  // Emit abbreviation for RECORD_DIAG.
  BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16-3)); // Category.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
  Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));

  // Emit the abbreviation for RECORD_DIAG_FLAG.
  Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
  Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));

  // ==---------------------------------------------------------------------==//
  // The subsequent records and Abbrevs are for the "Strings" block.
  // ==---------------------------------------------------------------------==//

  EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
  EmitRecordID(RECORD_CATEGORY, "Category Name", Stream, Record);
  EmitRecordID(RECORD_FILENAME, "File Name", Stream, Record);

  Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.
  Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
                                                          Abbrev));

  Abbrev = new BitCodeAbbrev();
  Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
  Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
                                                          Abbrev));

  Stream.ExitBlock();
}

void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
  for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
    Stream.Emit(*I, 8);
}

void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                                    const Diagnostic &Info) {

  BlockEnterExit DiagBlock(Stream, BLOCK_DIAG);

  // Emit the RECORD_DIAG record.
  Record.clear();
  Record.push_back(RECORD_DIAG);
  Record.push_back(DiagLevel);
  unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
  Record.push_back(category);
  Categories.insert(category);
  diagBuf.clear();
  Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
  Record.push_back(diagBuf.str().size());
  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());

  // Emit the RECORD_DIAG_FLAG record.
  StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
  if (!FlagName.empty()) {
    Record.clear();
    Record.push_back(RECORD_DIAG_FLAG);
    Record.push_back(FlagName.size());
    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
                              Record, FlagName.str());
  }

  // FIXME: emit location
  // FIXME: emit ranges
  // FIXME: emit notes
  // FIXME: emit fixits
}

template <typename T>
static void populateAndSort(std::vector<T> &scribble,
                            llvm::DenseSet<T> &set) {
  scribble.clear();

  for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
       it != ei; ++it)
    scribble.push_back(*it);

  // Sort 'scribble' so we always have a deterministic ordering in the
  // serialized file.
  std::sort(scribble.begin(), scribble.end());
}

void SDiagsWriter::EmitCategoriesAndFileNames() {

  if (Categories.empty() && Files.empty())
    return;

  BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);

  // Emit the category names.
  {
    std::vector<unsigned> scribble;
    populateAndSort(scribble, Categories);
    for (std::vector<unsigned>::iterator it = scribble.begin(),
          ei = scribble.end(); it != ei ; ++it) {
      Record.clear();
      Record.push_back(RECORD_CATEGORY);
      StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it);
      Record.push_back(catName.size());
      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
    }
  }

  // Emit the file names.
  {
    std::vector<FileID> scribble;
    populateAndSort(scribble, Files);
    for (std::vector<FileID>::iterator it = scribble.begin(),
         ei = scribble.end(); it != ei; ++it) {
      SourceManager &SM = Diags.getSourceManager();
      const FileEntry *FE = SM.getFileEntryForID(*it);
      StringRef Name = FE->getName();

      Record.clear();
      Record.push_back(FE->getSize());
      Record.push_back(FE->getModificationTime());
      Record.push_back(Name.size());
      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
    }
  }

}

void SDiagsWriter::EndSourceFile() {
  EmitCategoriesAndFileNames();

  // Write the generated bitstream to "Out".
  OS->write((char *)&Buffer.front(), Buffer.size());
  OS->flush();

  OS.reset(0);
}

<|endoftext|>


<commit_before>
#include "cmdline/cmdline.h"
#include "mhap/overlap.h"
#include "mhap/parser.h"
#include "ra/include/ra/ra.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>

// trimming params
int READ_LEN_THRESHOLD = 100000;

// BFS params in bubble popping
size_t MAX_NODES = 750;
int MAX_DISTANCE = MAX_NODES * 10000;
double MAX_DIFFERENCE = 0.25;

// contig extraction params
size_t MAX_BRANCHES = 12;
size_t MAX_START_NODES = 24;

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::fstream;
using std::max;
using std::string;
using std::vector;

// map reads so we can access reads with mapped[read_id]
void map_reads(vector<Read*>* mapped, vector<Read*>& reads) {

  int max_id = -1;
  for (auto r: reads) {
    max_id = max(max_id, r->getId());
  }

  mapped->resize(max_id + 1, nullptr);
  for (auto r: reads) {
    (*mapped)[r->getId()] = r;
  }
}

int main(int argc, char **argv) {

  cmdline::parser args;

  // input params
  args.add<string>("reads", 'r', "reads file", true);
  args.add<string>("reads_format", 's', "reads format; supported: fasta, fastq, afg", false, "fasta");
  args.add<int>("reads_id_offset", 'a', "reads id offset (first read id)", false, 0);
  args.add<string>("overlaps", 'x', "overlaps file", true);
  args.add<string>("overlaps_format", 'f', "overlaps file format; supported: afg, mhap", false, "afg");

  args.add<bool>("verbose", 'v', "verbose output", false);

  // bubble popping params
  args.add<int>("bp_max_nodes", 'm', "max nodes in bubble branch", false, 750);
  args.add<double>("bp_max_diff", 'n', "max difference between bubble branches", false, 0.25);

  args.parse_check(argc, argv);

  const int thread_num = std::max(std::thread::hardware_concurrency(), 1U);
  const string reads_filename = args.get<string>("reads");
  const string reads_format = args.get<string>("reads_format");
  const string overlaps_filename = args.get<string>("overlaps");
  const string overlaps_format = args.get<string>("overlaps_format");
  const bool verbose_output = args.get<bool>("verbose");
  const int reads_id_offset = args.get<int>("reads_id_offset");

  MAX_NODES = args.get<int>("bp_max_nodes");
  MAX_DISTANCE = MAX_NODES * 10000;
  MAX_DIFFERENCE = args.get<double>("bp_max_diff");

  vector<Overlap*> overlaps, filtered;
  vector<Read*> reads;
  vector<Read*> reads_mapped;

  if (reads_format == "fasta") {
    readFastaReads(reads, reads_filename.c_str());
  } else if (reads_format == "fastq") {
    readFastqReads(reads, reads_filename.c_str());
  } else if (reads_format == "afg") {
    readAfgReads(reads, reads_filename.c_str());
  } else {
    assert(false);
  }

  // map reads so we have reads_mapped[read_id] -> read
  map_reads(&reads_mapped, reads);

  std::cerr << "Read " << reads.size() << " reads" << std::endl;

  if (overlaps_format == "afg") {
    readAfgOverlaps(overlaps, overlaps_filename.c_str());
  } else if (overlaps_format == "mhap") {
    fstream overlaps_file(overlaps_filename);
    MHAP::read_overlaps(overlaps_file, &overlaps);
    overlaps_file.close();
  } else {
    assert(false);
  }

  // fix overlap read ids
  for (auto o: overlaps) {
    o->setA(o->getA() - reads_id_offset);
    o->setB(o->getB() - reads_id_offset);
  }

  for (auto o: overlaps) {
    const auto a = o->getA();
    const auto b = o->getB();
    if (reads_mapped[a] == nullptr) {
      cerr << "Read " << a << " not found" << endl;
      exit(1);
    }
    if (reads_mapped[b] == nullptr) {
      cerr << "Read " << b << " not found" << endl;
      exit(1);
    }

    o->setReadA(reads_mapped[a]);
    o->setReadB(reads_mapped[b]);
  }

  cerr << overlaps.size() << " overlaps read" << endl;

  vector<Overlap*> nocontainments;
  filterContainedOverlaps(nocontainments, overlaps, reads_mapped, true);

  {
    int filtered =  overlaps.size() - nocontainments.size();
    cerr << "Removed " << filtered << " overlaps as contained "
      << "(" << (1.*filtered)/overlaps.size() << ")" << endl;
  }

  if (verbose_output) {
    writeAfgOverlaps(nocontainments, "nocont.afg");
  }

  vector<Overlap*> notransitives;
  filterTransitiveOverlaps(notransitives, nocontainments, thread_num, true);

  {
    int filtered = nocontainments.size() - notransitives.size();
    cerr << "Removed " << filtered << " overlaps as transitive "
      << "(" << (1.*filtered)/nocontainments.size() << ")" << endl;
  }

  if (verbose_output) {
    writeAfgOverlaps(notransitives, "nocont.notran.afg");
  }

  createReverseComplements(reads, thread_num);

  StringGraph* graph = new StringGraph(reads, notransitives);
  graph->simplify();

  std::vector<StringGraphComponent*> components;
  graph->extractComponents(components);

  std::vector<Contig*> contigs;

  for (const auto& component : components) {

    Contig* contig = component->createContig();

    if (contig != nullptr) {
      contigs.emplace_back(contig);
    }
  }

  std::cerr << "number of contigs " << contigs.size() << std::endl;

  writeAfgContigs(contigs, "contigs.afg");

  for (auto r: reads)      delete r;
  for (auto o: overlaps)   delete o;
  for (auto c: components) delete c;
  for (auto c: contigs)    delete c;

  delete graph;

  return 0;
}
<commit_msg>set MAX_BRANCHES to inf by default<commit_after>
#include "cmdline/cmdline.h"
#include "mhap/overlap.h"
#include "mhap/parser.h"
#include "ra/include/ra/ra.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>

// trimming params
int READ_LEN_THRESHOLD = 100000;

// BFS params in bubble popping
size_t MAX_NODES = 750;
int MAX_DISTANCE = MAX_NODES * 10000;
double MAX_DIFFERENCE = 0.25;

// contig extraction params
size_t MAX_BRANCHES = 10000;
size_t MAX_START_NODES = 24;

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::fstream;
using std::max;
using std::string;
using std::vector;

// map reads so we can access reads with mapped[read_id]
void map_reads(vector<Read*>* mapped, vector<Read*>& reads) {

  int max_id = -1;
  for (auto r: reads) {
    max_id = max(max_id, r->getId());
  }

  mapped->resize(max_id + 1, nullptr);
  for (auto r: reads) {
    (*mapped)[r->getId()] = r;
  }
}

int main(int argc, char **argv) {

  cmdline::parser args;

  // input params
  args.add<string>("reads", 'r', "reads file", true);
  args.add<string>("reads_format", 's', "reads format; supported: fasta, fastq, afg", false, "fasta");
  args.add<int>("reads_id_offset", 'a', "reads id offset (first read id)", false, 0);
  args.add<string>("overlaps", 'x', "overlaps file", true);
  args.add<string>("overlaps_format", 'f', "overlaps file format; supported: afg, mhap", false, "afg");

  args.add<bool>("verbose", 'v', "verbose output", false);

  // bubble popping params
  args.add<int>("bp_max_nodes", 'm', "max nodes in bubble branch", false, 750);
  args.add<double>("bp_max_diff", 'n', "max difference between bubble branches", false, 0.25);

  args.parse_check(argc, argv);

  const int thread_num = std::max(std::thread::hardware_concurrency(), 1U);
  const string reads_filename = args.get<string>("reads");
  const string reads_format = args.get<string>("reads_format");
  const string overlaps_filename = args.get<string>("overlaps");
  const string overlaps_format = args.get<string>("overlaps_format");
  const bool verbose_output = args.get<bool>("verbose");
  const int reads_id_offset = args.get<int>("reads_id_offset");

  MAX_NODES = args.get<int>("bp_max_nodes");
  MAX_DISTANCE = MAX_NODES * 10000;
  MAX_DIFFERENCE = args.get<double>("bp_max_diff");

  vector<Overlap*> overlaps, filtered;
  vector<Read*> reads;
  vector<Read*> reads_mapped;

  if (reads_format == "fasta") {
    readFastaReads(reads, reads_filename.c_str());
  } else if (reads_format == "fastq") {
    readFastqReads(reads, reads_filename.c_str());
  } else if (reads_format == "afg") {
    readAfgReads(reads, reads_filename.c_str());
  } else {
    assert(false);
  }

  // map reads so we have reads_mapped[read_id] -> read
  map_reads(&reads_mapped, reads);

  std::cerr << "Read " << reads.size() << " reads" << std::endl;

  if (overlaps_format == "afg") {
    readAfgOverlaps(overlaps, overlaps_filename.c_str());
  } else if (overlaps_format == "mhap") {
    fstream overlaps_file(overlaps_filename);
    MHAP::read_overlaps(overlaps_file, &overlaps);
    overlaps_file.close();
  } else {
    assert(false);
  }

  // fix overlap read ids
  for (auto o: overlaps) {
    o->setA(o->getA() - reads_id_offset);
    o->setB(o->getB() - reads_id_offset);
  }

  for (auto o: overlaps) {
    const auto a = o->getA();
    const auto b = o->getB();
    if (reads_mapped[a] == nullptr) {
      cerr << "Read " << a << " not found" << endl;
      exit(1);
    }
    if (reads_mapped[b] == nullptr) {
      cerr << "Read " << b << " not found" << endl;
      exit(1);
    }

    o->setReadA(reads_mapped[a]);
    o->setReadB(reads_mapped[b]);
  }

  cerr << overlaps.size() << " overlaps read" << endl;

  vector<Overlap*> nocontainments;
  filterContainedOverlaps(nocontainments, overlaps, reads_mapped, true);

  {
    int filtered =  overlaps.size() - nocontainments.size();
    cerr << "Removed " << filtered << " overlaps as contained "
      << "(" << (1.*filtered)/overlaps.size() << ")" << endl;
  }

  if (verbose_output) {
    writeAfgOverlaps(nocontainments, "nocont.afg");
  }

  vector<Overlap*> notransitives;
  filterTransitiveOverlaps(notransitives, nocontainments, thread_num, true);

  {
    int filtered = nocontainments.size() - notransitives.size();
    cerr << "Removed " << filtered << " overlaps as transitive "
      << "(" << (1.*filtered)/nocontainments.size() << ")" << endl;
  }

  if (verbose_output) {
    writeAfgOverlaps(notransitives, "nocont.notran.afg");
  }

  createReverseComplements(reads, thread_num);

  StringGraph* graph = new StringGraph(reads, notransitives);
  graph->simplify();

  std::vector<StringGraphComponent*> components;
  graph->extractComponents(components);

  std::vector<Contig*> contigs;

  for (const auto& component : components) {

    Contig* contig = component->createContig();

    if (contig != nullptr) {
      contigs.emplace_back(contig);
    }
  }

  std::cerr << "number of contigs " << contigs.size() << std::endl;

  writeAfgContigs(contigs, "contigs.afg");

  for (auto r: reads)      delete r;
  for (auto o: overlaps)   delete o;
  for (auto c: components) delete c;
  for (auto c: contigs)    delete c;

  delete graph;

  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "lhttp_stock.hxx"
#include "lhttp_address.hxx"
#include "stock/Stock.hxx"
#include "stock/MapStock.hxx"
#include "stock/MultiStock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "pool/tpool.hxx"
#include "lease.hxx"
#include "child_stock.hxx"
#include "spawn/JailParams.hxx"
#include "spawn/Prepared.hxx"
#include "event/NewSocketEvent.hxx"
#include "event/TimerEvent.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/Logger.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"

#include <assert.h>
#include <unistd.h>
#include <string.h>

class LhttpStock final : StockClass, ChildStockClass {
    ChildStock child_stock;
    MultiStock mchild_stock;
    StockMap hstock;

public:
    LhttpStock(unsigned limit, unsigned max_idle,
               EventLoop &event_loop, SpawnService &spawn_service,
               SocketDescriptor log_socket) noexcept;

    void FadeAll() noexcept {
        hstock.FadeAll();
        child_stock.GetStockMap().FadeAll();
        mchild_stock.FadeAll();
    }

    void FadeTag(const char *tag) noexcept;

    StockMap &GetConnectionStock() noexcept {
        return hstock;
    }

private:
    /* virtual methods from class StockClass */
    void Create(CreateStockItem c, void *info, struct pool &caller_pool,
                CancellablePointer &cancel_ptr) override;

    /* virtual methods from class ChildStockClass */
    int GetChildSocketType(void *info) const noexcept override;
    const char *GetChildTag(void *info) const noexcept override;
    void PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                      PreparedChildProcess &p) override;
};

class LhttpConnection final : LoggerDomainFactory, StockItem {
    LazyDomainLogger logger;

    StockItem *child = nullptr;

    struct lease_ref lease_ref;

    UniqueSocketDescriptor fd;
    NewSocketEvent event;
    TimerEvent idle_timeout_event;

public:
    explicit LhttpConnection(CreateStockItem c) noexcept
        :StockItem(c),
         logger(*this),
         event(c.stock.GetEventLoop(), BIND_THIS_METHOD(EventCallback)),
         idle_timeout_event(c.stock.GetEventLoop(),
                            BIND_THIS_METHOD(OnIdleTimeout)) {}

    ~LhttpConnection() noexcept override;

    void Connect(MultiStock &child_stock, struct pool &caller_pool,
                 const char *key, void *info,
                 unsigned concurrency);

    SocketDescriptor GetSocket() const noexcept {
        assert(fd.IsDefined());
        return fd;
    }

    gcc_pure
    const char *GetTag() const noexcept {
        assert(child != nullptr);

        return child_stock_item_get_tag(*child);
    }

    void SetSite(const char *site) noexcept {
        child_stock_item_set_site(*child, site);
    }

    void SetUri(const char *uri) noexcept {
        child_stock_item_set_uri(*child, uri);
    }

private:
    void EventCallback(unsigned events) noexcept;
    void OnIdleTimeout() noexcept;

    /* virtual methods from LoggerDomainFactory */
    std::string MakeLoggerDomain() const noexcept override {
        return GetStockName();
    }

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override {
        event.Cancel();
        idle_timeout_event.Cancel();
        return true;
    }

    bool Release() noexcept override {
        event.ScheduleRead();
        idle_timeout_event.Add(EventDuration<300>::value);
        return true;
    }
};

void
LhttpConnection::Connect(MultiStock &child_stock, struct pool &caller_pool,
                         const char *key, void *info,
                         unsigned concurrency)
{
    try {
        child = child_stock.GetNow(caller_pool,
                                   key, info, concurrency,
                                   lease_ref);
    } catch (...) {
        delete this;
        std::throw_with_nested(FormatRuntimeError("Failed to launch LHTTP server '%s'",
                                                  key));
    }

    try {
        fd = child_stock_item_connect(*child);
    } catch (...) {
        delete this;
        std::throw_with_nested(FormatRuntimeError("Failed to connect to LHTTP server '%s'",
                                                  key));
    }

    event.Open(fd);
    InvokeCreateSuccess();
}

static const char *
lhttp_stock_key(struct pool *pool, const LhttpAddress *address) noexcept
{
    return address->GetServerId(pool);
}

/*
 * libevent callback
 *
 */

inline void
LhttpConnection::EventCallback(unsigned) noexcept
{
    char buffer;
    ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
    if (nbytes < 0)
        logger(2, "error on idle LHTTP connection: ", strerror(errno));
    else if (nbytes > 0)
        logger(2, "unexpected data from idle LHTTP connection");

    InvokeIdleDisconnect();
}

inline void
LhttpConnection::OnIdleTimeout() noexcept
{
    InvokeIdleDisconnect();
}

/*
 * child_stock class
 *
 */

int
LhttpStock::GetChildSocketType(void *info) const noexcept
{
    const auto &address = *(const LhttpAddress *)info;

    int type = SOCK_STREAM;
    if (!address.blocking)
        type |= SOCK_NONBLOCK;

    return type;
}

const char *
LhttpStock::GetChildTag(void *info) const noexcept
{
    const auto &address = *(const LhttpAddress *)info;

    return address.options.tag;
}

void
LhttpStock::PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                         PreparedChildProcess &p)
{
    const auto &address = *(const LhttpAddress *)info;

    p.SetStdin(std::move(fd));
    address.CopyTo(p);
}

/*
 * stock class
 *
 */

void
LhttpStock::Create(CreateStockItem c, void *info,
                   struct pool &caller_pool,
                   gcc_unused CancellablePointer &cancel_ptr)
{
    const auto *address = (const LhttpAddress *)info;

    assert(address != nullptr);
    assert(address->path != nullptr);

    auto *connection = new LhttpConnection(c);

    connection->Connect(mchild_stock, caller_pool,
                        c.GetStockName(), info, address->concurrency);
}

LhttpConnection::~LhttpConnection() noexcept
{
    if (fd.IsDefined()) {
        event.Cancel();
        fd.Close();
    }

    if (child != nullptr)
        lease_ref.Release(true);
}

/*
 * interface
 *
 */

inline
LhttpStock::LhttpStock(unsigned limit, unsigned max_idle,
                       EventLoop &event_loop, SpawnService &spawn_service,
                       SocketDescriptor log_socket) noexcept
    :child_stock(event_loop, spawn_service,
                 *this,
                 log_socket,
                 limit, max_idle),
     mchild_stock(child_stock.GetStockMap()),
     hstock(event_loop, *this, limit, max_idle) {}

void
LhttpStock::FadeTag(const char *tag) noexcept
{
    assert(tag != nullptr);

    hstock.FadeIf([tag](const StockItem &item){
            const auto &connection = (const LhttpConnection &)item;
            const char *tag2 = connection.GetTag();
            return tag2 != nullptr && strcmp(tag, tag2) == 0;
        });

    mchild_stock.FadeIf([tag](const StockItem &item){
            const char *tag2 = child_stock_item_get_tag(item);
            return tag2 != nullptr && strcmp(tag, tag2) == 0;
        });

    child_stock.FadeTag(tag);
}

LhttpStock *
lhttp_stock_new(unsigned limit, unsigned max_idle,
                EventLoop &event_loop, SpawnService &spawn_service,
                SocketDescriptor log_socket) noexcept
{
    return new LhttpStock(limit, max_idle, event_loop, spawn_service,
                          log_socket);
}

void
lhttp_stock_free(LhttpStock *ls) noexcept
{
    delete ls;
}

void
lhttp_stock_fade_all(LhttpStock &ls) noexcept
{
    ls.FadeAll();
}

void
lhttp_stock_fade_tag(LhttpStock &ls, const char *tag) noexcept
{
    ls.FadeTag(tag);
}

StockItem *
lhttp_stock_get(LhttpStock *lhttp_stock,
                const LhttpAddress *address)
{
    const auto *const jail = address->options.jail;
    if (jail != nullptr)
        jail->Check();

    union {
        const LhttpAddress *in;
        void *out;
    } deconst = { .in = address };

    const AutoRewindPool auto_rewind(*tpool);

    return lhttp_stock->GetConnectionStock().GetNow(*tpool,
                                                    lhttp_stock_key(tpool, address),
                                                    deconst.out);
}

SocketDescriptor
lhttp_stock_item_get_socket(const StockItem &item) noexcept
{
    const auto *connection = (const LhttpConnection *)&item;

    return connection->GetSocket();
}

FdType
lhttp_stock_item_get_type(gcc_unused const StockItem &item) noexcept
{
    return FdType::FD_SOCKET;
}

void
lhttp_stock_item_set_site(StockItem &item, const char *site) noexcept
{
    auto &connection = (LhttpConnection &)item;
    connection.SetSite(site);
}

void
lhttp_stock_item_set_uri(StockItem &item, const char *uri) noexcept
{
    auto &connection = (LhttpConnection &)item;
    connection.SetUri(uri);
}
<commit_msg>lhttp_stock: add missing include<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "lhttp_stock.hxx"
#include "lhttp_address.hxx"
#include "stock/Stock.hxx"
#include "stock/MapStock.hxx"
#include "stock/MultiStock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "pool/tpool.hxx"
#include "lease.hxx"
#include "child_stock.hxx"
#include "spawn/JailParams.hxx"
#include "spawn/Prepared.hxx"
#include "event/NewSocketEvent.hxx"
#include "event/TimerEvent.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/Logger.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"

#include <assert.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>

class LhttpStock final : StockClass, ChildStockClass {
    ChildStock child_stock;
    MultiStock mchild_stock;
    StockMap hstock;

public:
    LhttpStock(unsigned limit, unsigned max_idle,
               EventLoop &event_loop, SpawnService &spawn_service,
               SocketDescriptor log_socket) noexcept;

    void FadeAll() noexcept {
        hstock.FadeAll();
        child_stock.GetStockMap().FadeAll();
        mchild_stock.FadeAll();
    }

    void FadeTag(const char *tag) noexcept;

    StockMap &GetConnectionStock() noexcept {
        return hstock;
    }

private:
    /* virtual methods from class StockClass */
    void Create(CreateStockItem c, void *info, struct pool &caller_pool,
                CancellablePointer &cancel_ptr) override;

    /* virtual methods from class ChildStockClass */
    int GetChildSocketType(void *info) const noexcept override;
    const char *GetChildTag(void *info) const noexcept override;
    void PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                      PreparedChildProcess &p) override;
};

class LhttpConnection final : LoggerDomainFactory, StockItem {
    LazyDomainLogger logger;

    StockItem *child = nullptr;

    struct lease_ref lease_ref;

    UniqueSocketDescriptor fd;
    NewSocketEvent event;
    TimerEvent idle_timeout_event;

public:
    explicit LhttpConnection(CreateStockItem c) noexcept
        :StockItem(c),
         logger(*this),
         event(c.stock.GetEventLoop(), BIND_THIS_METHOD(EventCallback)),
         idle_timeout_event(c.stock.GetEventLoop(),
                            BIND_THIS_METHOD(OnIdleTimeout)) {}

    ~LhttpConnection() noexcept override;

    void Connect(MultiStock &child_stock, struct pool &caller_pool,
                 const char *key, void *info,
                 unsigned concurrency);

    SocketDescriptor GetSocket() const noexcept {
        assert(fd.IsDefined());
        return fd;
    }

    gcc_pure
    const char *GetTag() const noexcept {
        assert(child != nullptr);

        return child_stock_item_get_tag(*child);
    }

    void SetSite(const char *site) noexcept {
        child_stock_item_set_site(*child, site);
    }

    void SetUri(const char *uri) noexcept {
        child_stock_item_set_uri(*child, uri);
    }

private:
    void EventCallback(unsigned events) noexcept;
    void OnIdleTimeout() noexcept;

    /* virtual methods from LoggerDomainFactory */
    std::string MakeLoggerDomain() const noexcept override {
        return GetStockName();
    }

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override {
        event.Cancel();
        idle_timeout_event.Cancel();
        return true;
    }

    bool Release() noexcept override {
        event.ScheduleRead();
        idle_timeout_event.Add(EventDuration<300>::value);
        return true;
    }
};

void
LhttpConnection::Connect(MultiStock &child_stock, struct pool &caller_pool,
                         const char *key, void *info,
                         unsigned concurrency)
{
    try {
        child = child_stock.GetNow(caller_pool,
                                   key, info, concurrency,
                                   lease_ref);
    } catch (...) {
        delete this;
        std::throw_with_nested(FormatRuntimeError("Failed to launch LHTTP server '%s'",
                                                  key));
    }

    try {
        fd = child_stock_item_connect(*child);
    } catch (...) {
        delete this;
        std::throw_with_nested(FormatRuntimeError("Failed to connect to LHTTP server '%s'",
                                                  key));
    }

    event.Open(fd);
    InvokeCreateSuccess();
}

static const char *
lhttp_stock_key(struct pool *pool, const LhttpAddress *address) noexcept
{
    return address->GetServerId(pool);
}

/*
 * libevent callback
 *
 */

inline void
LhttpConnection::EventCallback(unsigned) noexcept
{
    char buffer;
    ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
    if (nbytes < 0)
        logger(2, "error on idle LHTTP connection: ", strerror(errno));
    else if (nbytes > 0)
        logger(2, "unexpected data from idle LHTTP connection");

    InvokeIdleDisconnect();
}

inline void
LhttpConnection::OnIdleTimeout() noexcept
{
    InvokeIdleDisconnect();
}

/*
 * child_stock class
 *
 */

int
LhttpStock::GetChildSocketType(void *info) const noexcept
{
    const auto &address = *(const LhttpAddress *)info;

    int type = SOCK_STREAM;
    if (!address.blocking)
        type |= SOCK_NONBLOCK;

    return type;
}

const char *
LhttpStock::GetChildTag(void *info) const noexcept
{
    const auto &address = *(const LhttpAddress *)info;

    return address.options.tag;
}

void
LhttpStock::PrepareChild(void *info, UniqueSocketDescriptor &&fd,
                         PreparedChildProcess &p)
{
    const auto &address = *(const LhttpAddress *)info;

    p.SetStdin(std::move(fd));
    address.CopyTo(p);
}

/*
 * stock class
 *
 */

void
LhttpStock::Create(CreateStockItem c, void *info,
                   struct pool &caller_pool,
                   gcc_unused CancellablePointer &cancel_ptr)
{
    const auto *address = (const LhttpAddress *)info;

    assert(address != nullptr);
    assert(address->path != nullptr);

    auto *connection = new LhttpConnection(c);

    connection->Connect(mchild_stock, caller_pool,
                        c.GetStockName(), info, address->concurrency);
}

LhttpConnection::~LhttpConnection() noexcept
{
    if (fd.IsDefined()) {
        event.Cancel();
        fd.Close();
    }

    if (child != nullptr)
        lease_ref.Release(true);
}

/*
 * interface
 *
 */

inline
LhttpStock::LhttpStock(unsigned limit, unsigned max_idle,
                       EventLoop &event_loop, SpawnService &spawn_service,
                       SocketDescriptor log_socket) noexcept
    :child_stock(event_loop, spawn_service,
                 *this,
                 log_socket,
                 limit, max_idle),
     mchild_stock(child_stock.GetStockMap()),
     hstock(event_loop, *this, limit, max_idle) {}

void
LhttpStock::FadeTag(const char *tag) noexcept
{
    assert(tag != nullptr);

    hstock.FadeIf([tag](const StockItem &item){
            const auto &connection = (const LhttpConnection &)item;
            const char *tag2 = connection.GetTag();
            return tag2 != nullptr && strcmp(tag, tag2) == 0;
        });

    mchild_stock.FadeIf([tag](const StockItem &item){
            const char *tag2 = child_stock_item_get_tag(item);
            return tag2 != nullptr && strcmp(tag, tag2) == 0;
        });

    child_stock.FadeTag(tag);
}

LhttpStock *
lhttp_stock_new(unsigned limit, unsigned max_idle,
                EventLoop &event_loop, SpawnService &spawn_service,
                SocketDescriptor log_socket) noexcept
{
    return new LhttpStock(limit, max_idle, event_loop, spawn_service,
                          log_socket);
}

void
lhttp_stock_free(LhttpStock *ls) noexcept
{
    delete ls;
}

void
lhttp_stock_fade_all(LhttpStock &ls) noexcept
{
    ls.FadeAll();
}

void
lhttp_stock_fade_tag(LhttpStock &ls, const char *tag) noexcept
{
    ls.FadeTag(tag);
}

StockItem *
lhttp_stock_get(LhttpStock *lhttp_stock,
                const LhttpAddress *address)
{
    const auto *const jail = address->options.jail;
    if (jail != nullptr)
        jail->Check();

    union {
        const LhttpAddress *in;
        void *out;
    } deconst = { .in = address };

    const AutoRewindPool auto_rewind(*tpool);

    return lhttp_stock->GetConnectionStock().GetNow(*tpool,
                                                    lhttp_stock_key(tpool, address),
                                                    deconst.out);
}

SocketDescriptor
lhttp_stock_item_get_socket(const StockItem &item) noexcept
{
    const auto *connection = (const LhttpConnection *)&item;

    return connection->GetSocket();
}

FdType
lhttp_stock_item_get_type(gcc_unused const StockItem &item) noexcept
{
    return FdType::FD_SOCKET;
}

void
lhttp_stock_item_set_site(StockItem &item, const char *site) noexcept
{
    auto &connection = (LhttpConnection &)item;
    connection.SetSite(site);
}

void
lhttp_stock_item_set_uri(StockItem &item, const char *uri) noexcept
{
    auto &connection = (LhttpConnection &)item;
    connection.SetUri(uri);
}
<|endoftext|>