<commit_before>/**************************************************************************
**
** This file is part of Doxygen plugin for Qt Creator
**
** Copyright (c) 2009 Kevin Tanguy (kofee@kofee.org).
**
** This plugin is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as
** published by the Free Software Foundation, either version 2.1
** of the License, or (at your option) any later version.
**
** This plugin 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 Lesser General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Doxygen Plugin. If not, see <http://www.gnu.org/licenses/>.
**/

#include "doxygen.h"

#include <QObject>
#include <plugins/cppeditor/cppeditorconstants.h>
#include <plugins/cpptools/cpptoolsconstants.h>
#include <plugins/cpptools/cppmodelmanagerinterface.h>
#include <plugins/texteditor/basetexteditor.h>
#include <plugins/coreplugin/icore.h>
#include <plugins/coreplugin/editormanager/ieditor.h>
#include <plugins/coreplugin/editormanager/editormanager.h>
#include <plugins/projectexplorer/project.h>
#include <plugins/projectexplorer/projectexplorer.h>
#include <plugins/projectexplorer/session.h>
#include <plugins/projectexplorer/projectexplorerconstants.h>

#include <libs/cplusplus/Overview.h>

#include <libs/extensionsystem/pluginmanager.h>
#include <shared/cplusplus/Scope.h>
#include <shared/cplusplus/Symbols.h>
#include <shared/cplusplus/Names.h>
#include <cplusplus/CppDocument.h>

#include <QString>
#include <QStringList>
#include <QFile>
#include <QFileInfo>
#include <QRegExp>

using namespace CPlusPlus;
using namespace ProjectExplorer;
using namespace DoxyPlugin;
using namespace DoxyPlugin::Internal;

Doxygen* Doxygen::m_instance = 0;

Doxygen::Doxygen()
{
}

Doxygen* Doxygen::instance()
{
    if (!m_instance)
        m_instance = new Doxygen();
    return m_instance;
}

// TODO, get rid of it.
QStringList scopesForSymbol(const Symbol* symbol)
{
    const Scope *scope = symbol->asScope();
    QStringList scopes;

    if(symbol->isFunction())
    {
        const Name *name = symbol->name();
        Overview overview;
        overview.setShowArgumentNames(false);
        overview.setShowReturnTypes(false);
        scopes.prepend(overview.prettyName(name));
        return scopes;
    }

    for (; scope; scope = scope->enclosingScope())
    {
        Symbol *owner = scope->memberAt(0);

        if (owner && owner->name() && ! scope->isEnum())
        {
            const Name *name = owner->name();
            Overview overview;
            overview.setShowArgumentNames(false);
            overview.setShowReturnTypes(false);
            scopes.prepend(overview.prettyName(name));
        }
    }
    return scopes;
}

Symbol* currentSymbol(Core::IEditor *editor)
{
    CppTools::CppModelManagerInterface *modelManager =
            ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    if (!modelManager)
        return 0;

    const Snapshot snapshot = modelManager->snapshot();
    Document::Ptr doc = snapshot.document(editor->file()->fileName());
    if (!doc)
        return 0;

    Symbol* last = doc->lastVisibleSymbolAt(editor->currentLine(), editor->currentColumn());
    return last;
}

// TODO, recode it entirely.
void Doxygen::createDocumentation(const DoxygenSettingsStruct &DoxySettings)
{
    const Core::EditorManager *editorManager = Core::EditorManager::instance();
    Core::IEditor *editor = editorManager->currentEditor();

    // before continuing, test if the editor is actually showing a file.
    if(!editor) return;

    // get the widget for later.
    TextEditor::BaseTextEditor *editorWidget = qobject_cast<TextEditor::BaseTextEditor*>(
            editorManager->currentEditor()->widget());

    // TODO, only do that if class and verbosePrinting
    // Catch hold of the plugin-manager
    ExtensionSystem::PluginManager* pm
            = ExtensionSystem::PluginManager::instance();
    // Look for the ProjectExplorerPlugin object
    ProjectExplorer::ProjectExplorerPlugin* projectExplorerPlugin
            = pm->getObject<ProjectExplorer::ProjectExplorerPlugin>();
    // Fetch a list of all open projects
    QList<ProjectExplorer::Project*> projects
            = projectExplorerPlugin->session()->projects();
    // Project root directory
    QString projectRoot;

    // Attempt to find our project
    Q_FOREACH(ProjectExplorer::Project* project, projects)
    {
        QStringList files = project->files(Project::ExcludeGeneratedFiles); // ProjectExplorer::Project::FilesMode::ExcludeGeneratedFiles
        // is it our project ?
        if(files.contains(editor->file()->fileName()))
        {
            // YES! get the .pro and remove the directory part from our filename
            // TODO, check if it is smart... (it's not really.)
            Q_FOREACH(QString f, files)
            {
                if(f.contains(QRegExp(".pro$")))
                {
                    projectRoot = f.section('/', 0, -2);
                    if(projectRoot.size()) projectRoot.append("/");
                    continue;
                }
            }
            if(projectRoot.size()) continue;
        }
    }

    // FIXME, this poutine isn't that digest.
    // "unroll" the process
    Symbol *lastSymbol = currentSymbol(editor);
    if(lastSymbol->line() != static_cast<unsigned>(editor->currentLine()))
        editorWidget->moveCursor(QTextCursor::StartOfLine);
    while(lastSymbol->line() != static_cast<unsigned>(editor->currentLine()) && lastSymbol)
    {
        editorWidget->moveCursor(QTextCursor::NextWord);
        lastSymbol = currentSymbol(editor);
    }
    if (!lastSymbol /*|| !lastSymbol->asScope()*/)
        return;

    QStringList scopes = scopesForSymbol(lastSymbol);
    Overview overview;
    overview.setShowArgumentNames(true);
    overview.setShowDefaultArguments(false);
    overview.setShowTemplateParameters(false);
    overview.setShowReturnTypes(true);
    overview.setShowFunctionSignatures(true);
    const Name *name = lastSymbol->name();
    scopes.append(overview.prettyName(name));

    QString docToWrite;
    // Do we print a short documentation block at end of line?
    bool printAtEnd = false;

    // Get current indentation as per bug #5
    QString indent;
    editorWidget->moveCursor(QTextCursor::StartOfLine);
    editorWidget->gotoLineEndWithSelection();
    QString currentText = editorWidget->textCursor().selectedText();
    QStringList textList = currentText.split(QRegExp("\\b"));
    indent = textList.at(0);

    // quickfix when calling the method on "};" (end class) or "}" (end namespace)
    if(indent.contains(QRegExp("^\\};?")))
        return;

    if(indent.endsWith('~'))
        indent.chop(1);

    if(lastSymbol->isClass())
    {
        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
        {
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
            docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        }
        if(DoxySettings.verbosePrinting)
        {
            QString fileName = editor->file()->fileName().remove(0, editor->file()->fileName().lastIndexOf("/") + 1);
            QString fileNameProj = editor->file()->fileName().remove(projectRoot);
            docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "class " + overview.prettyName(name) + " " + fileName + " \"" + fileNameProj + "\"\n";
        }
        docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
    }
    else if(lastSymbol->isTypedef())
    {
        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
        {
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
            docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        }
        if(DoxySettings.verbosePrinting)
            docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "typedef " + overview.prettyName(name);
        docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
    }
    else if(lastSymbol->isEnum())
    {
        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
        docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        if(DoxySettings.verbosePrinting)
            docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "enum " + overview.prettyName(name) + "\n";
        docToWrite += DoxySettings.DoxyComment.doxEnding;
    }
    // Here comes the bitch.
    else if(lastSymbol->isDeclaration() || lastSymbol->isFunction())
    {
        overview.setShowArgumentNames(true);
        overview.setShowReturnTypes(false);
        overview.setShowDefaultArguments(false);
        overview.setShowTemplateParameters(false);
        overview.setShowFunctionSignatures(true);

        QString arglist = overview.prettyType(lastSymbol->type(), name);

        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
        {
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
            docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        }

        // if variable, do it quickly...
        if(!arglist.contains('('))
        {
            if(DoxySettings.shortVarDoc)
            {
                printAtEnd = true;
                docToWrite = DoxySettings.DoxyComment.doxShortVarDoc + "TODO */";
            }
            else
            {
                if(DoxySettings.verbosePrinting)
                    docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "var " + overview.prettyName(name) + "\n" + indent + DoxySettings.DoxyComment.doxEnding;
                else
                    docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine + indent + DoxySettings.DoxyComment.doxEnding;
            }
        }
        else
        {
            // Never noticed it before, a useless comment block because of the Q_OBJECT macro
            // so let's just ignore that will we?
            if(overview.prettyName(name) == "qt_metacall")
                return;

            if(DoxySettings.verbosePrinting)
                docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "fn " + overview.prettyName(name) + "\n";

            // Check parameters
            // Do it the naive way first before finding better in the API
            arglist.remove(0, arglist.indexOf("(") + 1);
            arglist.remove(arglist.lastIndexOf(")"), arglist.size() - arglist.lastIndexOf(")"));
            int indexfrom, indexto;
            while( ((indexfrom = arglist.indexOf('<'))!= -1) && ((indexto = arglist.indexOf('>')) != -1) )
            {
                arglist.remove(indexfrom, indexto - indexfrom + 1);
            }
            QStringList args = arglist.trimmed().split(',', QString::SkipEmptyParts);

            Q_FOREACH(QString singleArg, args)
            {
                singleArg.remove(QRegExp("\\s*=.*")); // FIXME probably don't need the * after \\s but...
                singleArg.replace("*","");
                singleArg.replace("&","");
                docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "param " + singleArg.section(' ',  -1) + "\n";
            }

            // And now check the return type
            overview.setShowArgumentNames(false);
            overview.setShowDefaultArguments(false);
            overview.setShowTemplateParameters(false);
            overview.setShowReturnTypes(true);
            overview.setShowFunctionSignatures(false);

            arglist = overview.prettyType(lastSymbol->type(), name);

            // FIXME this check is just insane...
            if( arglist.contains(' ') && (((overview.prettyName(name) != scopes.front()) && (overview.prettyName(name).at(0) != '~')) || (lastSymbol->isFunction() && !overview.prettyName(name).contains("::~"))) )
            {
                QRegExp rx("void *");
                rx.setPatternSyntax(QRegExp::Wildcard);
                if(!rx.exactMatch(arglist))
                {
                    // dirty workarround
                    int last;
                    if(arglist.contains('>'))
                        last = arglist.lastIndexOf('>') + 1;
                    else
                        last = arglist.lastIndexOf(' ');

                    arglist.chop(arglist.size() - last);
                    docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "return " +  arglist + "\n";
                }

            }
            docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
        }
    }

    // Write the documentation in the editor
    if (editorWidget)
    {
        if(printAtEnd)
            editorWidget->moveCursor(QTextCursor::EndOfLine);
        else
            editorWidget->moveCursor(QTextCursor::StartOfBlock);
        editorWidget->insertPlainText(docToWrite);
    }

}

void Doxygen::addSymbol(const CPlusPlus::Symbol* symbol, QList<const Symbol*> &symmap)
{
    if(!symbol) return;

    if(symbol->isArgument()
        ||symbol->isFunction()
        || symbol->isDeclaration()
        || symbol->isEnum())
        {
        symmap.append(symbol);
        return;
    }
    else if(symbol->isForwardClassDeclaration()
        || symbol->isExtern()
        || symbol->isFriend()
        || symbol->isGenerated()
        || symbol->isUsingNamespaceDirective()
        || symbol->isUsingDeclaration()
        )
        {
        return;
    }
    symmap.append(symbol);

    const CPlusPlus::Scope* scopedSymbol = symbol->asScope();
    if(scopedSymbol)
    {
        int nbmembers = scopedSymbol->memberCount();
        for(int i=0; i <nbmembers; ++i)
        {
            addSymbol(scopedSymbol->memberAt(i), symmap);
        }
    }

}

void Doxygen::documentFile(const DoxygenSettingsStruct &DoxySettings)
{
    const Core::EditorManager *editorManager = Core::EditorManager::instance();
    Core::IEditor *editor = editorManager->currentEditor();

    // before continuing, test if the editor is actually showing a file.
    if(!editor)
        return;

    CppTools::CppModelManagerInterface *modelManager =
            ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    if(!modelManager)
        return;

    const Snapshot snapshot = modelManager->snapshot();
    Document::Ptr doc = snapshot.document(editor->file()->fileName());
    if (!doc)
        return;

    // TODO : check
    int globalSymbols = doc->globalSymbolCount();
    if(!globalSymbols)
        return;

    // check that as well...
    Scope* scope = doc->scopeAt(0,0);
    if(!scope)
        return;

    unsigned symbolcount = scope->memberCount();

    QList<const Symbol*> symmap;
    for(unsigned i=0; i < symbolcount; ++i)
        addSymbol(scope->memberAt(i), symmap);

    TextEditor::BaseTextEditor *editorWidget = qobject_cast<TextEditor::BaseTextEditor*>(
            editorManager->currentEditor()->widget());

    if (editorWidget)
    {
        QList<const Symbol*>::iterator it = symmap.end();
        for(; it != symmap.begin(); --it)
        {
            const Symbol* sym = *(it-1);
            editorWidget->gotoLine(sym->line());
            // should not be needed after the change in createDocumentation
            // will see that later.
            for(unsigned i=0; i<sym->column(); ++i)
                editorWidget->moveCursor(QTextCursor::Right);
            createDocumentation(DoxySettings);
        }
    }
}

<commit_msg>Various fixes: - enum doc indented correctly - infinite loop on some macros shouldn't happen again - don't document symbols on the same line like a psycho<commit_after>/**************************************************************************
**
** This file is part of Doxygen plugin for Qt Creator
**
** Copyright (c) 2009 Kevin Tanguy (kofee@kofee.org).
**
** This plugin is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as
** published by the Free Software Foundation, either version 2.1
** of the License, or (at your option) any later version.
**
** This plugin 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 Lesser General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Doxygen Plugin. If not, see <http://www.gnu.org/licenses/>.
**/

#include "doxygen.h"

#include <QObject>
#include <plugins/cppeditor/cppeditorconstants.h>
#include <plugins/cpptools/cpptoolsconstants.h>
#include <plugins/cpptools/cppmodelmanagerinterface.h>
#include <plugins/texteditor/basetexteditor.h>
#include <plugins/coreplugin/icore.h>
#include <plugins/coreplugin/editormanager/ieditor.h>
#include <plugins/coreplugin/editormanager/editormanager.h>
#include <plugins/projectexplorer/project.h>
#include <plugins/projectexplorer/projectexplorer.h>
#include <plugins/projectexplorer/session.h>
#include <plugins/projectexplorer/projectexplorerconstants.h>

#include <libs/cplusplus/Overview.h>

#include <libs/extensionsystem/pluginmanager.h>
#include <shared/cplusplus/Scope.h>
#include <shared/cplusplus/Symbols.h>
#include <shared/cplusplus/Names.h>
#include <cplusplus/CppDocument.h>

#include <QString>
#include <QStringList>
#include <QFile>
#include <QFileInfo>
#include <QRegExp>

using namespace CPlusPlus;
using namespace ProjectExplorer;
using namespace DoxyPlugin;
using namespace DoxyPlugin::Internal;

Doxygen* Doxygen::m_instance = 0;

Doxygen::Doxygen()
{
}

Doxygen* Doxygen::instance()
{
    if (!m_instance)
        m_instance = new Doxygen();
    return m_instance;
}

// TODO, get rid of it.
QStringList scopesForSymbol(const Symbol* symbol)
{
    const Scope *scope = symbol->asScope();
    QStringList scopes;

    if(symbol->isFunction())
    {
        const Name *name = symbol->name();
        Overview overview;
        overview.setShowArgumentNames(false);
        overview.setShowReturnTypes(false);
        scopes.prepend(overview.prettyName(name));
        return scopes;
    }

    for (; scope; scope = scope->enclosingScope())
    {
        Symbol *owner = scope->memberAt(0);

        if (owner && owner->name() && ! scope->isEnum())
        {
            const Name *name = owner->name();
            Overview overview;
            overview.setShowArgumentNames(false);
            overview.setShowReturnTypes(false);
            scopes.prepend(overview.prettyName(name));
        }
    }
    return scopes;
}

Symbol* currentSymbol(Core::IEditor *editor)
{
    CppTools::CppModelManagerInterface *modelManager =
            ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    if (!modelManager)
        return 0;

    const Snapshot snapshot = modelManager->snapshot();
    Document::Ptr doc = snapshot.document(editor->file()->fileName());
    if (!doc)
        return 0;

    Symbol* last = doc->lastVisibleSymbolAt(editor->currentLine(), editor->currentColumn());
    return last;
}

// TODO, recode it entirely.
void Doxygen::createDocumentation(const DoxygenSettingsStruct &DoxySettings)
{
    const Core::EditorManager *editorManager = Core::EditorManager::instance();
    Core::IEditor *editor = editorManager->currentEditor();

    // before continuing, test if the editor is actually showing a file.
    if(!editor) return;

    // get the widget for later.
    TextEditor::BaseTextEditor *editorWidget = qobject_cast<TextEditor::BaseTextEditor*>(
            editorManager->currentEditor()->widget());

    // TODO, only do that if class and verbosePrinting
    // Catch hold of the plugin-manager
    ExtensionSystem::PluginManager* pm
            = ExtensionSystem::PluginManager::instance();
    // Look for the ProjectExplorerPlugin object
    ProjectExplorer::ProjectExplorerPlugin* projectExplorerPlugin
            = pm->getObject<ProjectExplorer::ProjectExplorerPlugin>();
    // Fetch a list of all open projects
    QList<ProjectExplorer::Project*> projects
            = projectExplorerPlugin->session()->projects();
    // Project root directory
    QString projectRoot;

    // Attempt to find our project
    Q_FOREACH(ProjectExplorer::Project* project, projects)
    {
        QStringList files = project->files(Project::ExcludeGeneratedFiles); // ProjectExplorer::Project::FilesMode::ExcludeGeneratedFiles
        // is it our project ?
        if(files.contains(editor->file()->fileName()))
        {
            // YES! get the .pro and remove the directory part from our filename
            // TODO, check if it is smart... (it's not really.)
            Q_FOREACH(QString f, files)
            {
                if(f.contains(QRegExp(".pro$")))
                {
                    projectRoot = f.section('/', 0, -2);
                    if(projectRoot.size()) projectRoot.append("/");
                    continue;
                }
            }
            if(projectRoot.size()) continue;
        }
    }

    // FIXME, this poutine isn't that digest.
    // "unroll" the process
    Symbol *lastSymbol = currentSymbol(editor);
    if(lastSymbol->line() != static_cast<unsigned>(editor->currentLine()))
        editorWidget->moveCursor(QTextCursor::StartOfLine);
    while(lastSymbol->line() != static_cast<unsigned>(editor->currentLine()) && lastSymbol)
    {
        editorWidget->moveCursor(QTextCursor::NextWord);
        lastSymbol = currentSymbol(editor);
    }
    if (!lastSymbol /*|| !lastSymbol->asScope()*/)
        return;

    QStringList scopes = scopesForSymbol(lastSymbol);
    Overview overview;
    overview.setShowArgumentNames(true);
    overview.setShowDefaultArguments(false);
    overview.setShowTemplateParameters(false);
    overview.setShowReturnTypes(true);
    overview.setShowFunctionSignatures(true);
    const Name *name = lastSymbol->name();
    scopes.append(overview.prettyName(name));

    QString docToWrite;
    // Do we print a short documentation block at end of line?
    bool printAtEnd = false;

    // Get current indentation as per bug #5
    QString indent;
    editorWidget->moveCursor(QTextCursor::StartOfLine);
    editorWidget->gotoLineEndWithSelection();
    QString currentText = editorWidget->textCursor().selectedText();
    QStringList textList = currentText.split(QRegExp("\\b"));
    indent = textList.at(0);

    // quickfix when calling the method on "};" (end class) or "}" (end namespace)
    if(indent.contains(QRegExp("^\\};?")))
        return;

    if(indent.endsWith('~'))
        indent.chop(1);

    if(lastSymbol->isClass())
    {
        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
        {
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
            docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        }
        if(DoxySettings.verbosePrinting)
        {
            QString fileName = editor->file()->fileName().remove(0, editor->file()->fileName().lastIndexOf("/") + 1);
            QString fileNameProj = editor->file()->fileName().remove(projectRoot);
            docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "class " + overview.prettyName(name) + " " + fileName + " \"" + fileNameProj + "\"\n";
        }
        docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
    }
    else if(lastSymbol->isTypedef())
    {
        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
        {
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
            docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        }
        if(DoxySettings.verbosePrinting)
            docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "typedef " + overview.prettyName(name);
        docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
    }
    else if(lastSymbol->isEnum())
    {
        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
        docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        if(DoxySettings.verbosePrinting)
            docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "enum " + overview.prettyName(name) + "\n";
        docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
    }
    // Here comes the bitch.
    else if(lastSymbol->isDeclaration() || lastSymbol->isFunction())
    {
        overview.setShowArgumentNames(true);
        overview.setShowReturnTypes(false);
        overview.setShowDefaultArguments(false);
        overview.setShowTemplateParameters(false);
        overview.setShowFunctionSignatures(true);

        QString arglist = overview.prettyType(lastSymbol->type(), name);

        docToWrite += indent + DoxySettings.DoxyComment.doxBegin;
        if(DoxySettings.printBrief)
        {
            docToWrite += indent + DoxySettings.DoxyComment.doxBrief;
            docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine;
        }

        // if variable, do it quickly...
        if(!arglist.contains('('))
        {
            if(DoxySettings.shortVarDoc)
            {
                printAtEnd = true;
                docToWrite = DoxySettings.DoxyComment.doxShortVarDoc + "TODO */";
            }
            else
            {
                if(DoxySettings.verbosePrinting)
                    docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "var " + overview.prettyName(name) + "\n" + indent + DoxySettings.DoxyComment.doxEnding;
                else
                    docToWrite += indent + DoxySettings.DoxyComment.doxEmptyLine + indent + DoxySettings.DoxyComment.doxEnding;
            }
        }
        else
        {
            // Never noticed it before, a useless comment block because of the Q_OBJECT macro
            // so let's just ignore that will we?
            if(overview.prettyName(name) == "qt_metacall")
                return;

            if(DoxySettings.verbosePrinting)
                docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "fn " + overview.prettyName(name) + "\n";

            // Check parameters
            // Do it the naive way first before finding better in the API
            arglist.remove(0, arglist.indexOf("(") + 1);
            arglist.remove(arglist.lastIndexOf(")"), arglist.size() - arglist.lastIndexOf(")"));
            int indexfrom, indexto;
            while( ((indexfrom = arglist.indexOf('<'))!= -1) && ((indexto = arglist.indexOf('>')) != -1) )
            {
                arglist.remove(indexfrom, indexto - indexfrom + 1);
            }
            QStringList args = arglist.trimmed().split(',', QString::SkipEmptyParts);

            Q_FOREACH(QString singleArg, args)
            {
                singleArg.remove(QRegExp("\\s*=.*")); // FIXME probably don't need the * after \\s but...
                singleArg.replace("*","");
                singleArg.replace("&","");
                docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "param " + singleArg.section(' ',  -1) + "\n";
            }

            // And now check the return type
            overview.setShowArgumentNames(false);
            overview.setShowDefaultArguments(false);
            overview.setShowTemplateParameters(false);
            overview.setShowReturnTypes(true);
            overview.setShowFunctionSignatures(false);

            arglist = overview.prettyType(lastSymbol->type(), name);

            // FIXME this check is just insane...
            if( arglist.contains(' ') && (((overview.prettyName(name) != scopes.front()) && (overview.prettyName(name).at(0) != '~')) || (lastSymbol->isFunction() && !overview.prettyName(name).contains("::~"))) )
            {
                QRegExp rx("void *");
                rx.setPatternSyntax(QRegExp::Wildcard);
                if(!rx.exactMatch(arglist))
                {
                    // dirty workarround
                    int last;
                    if(arglist.contains('>'))
                        last = arglist.lastIndexOf('>') + 1;
                    else
                        last = arglist.lastIndexOf(' ');

                    arglist.chop(arglist.size() - last);
                    docToWrite += indent + DoxySettings.DoxyComment.doxNewLine + "return " +  arglist + "\n";
                }
            }
            docToWrite += indent + DoxySettings.DoxyComment.doxEnding;
        }
    }

    // Write the documentation in the editor
    if (editorWidget)
    {
        if(printAtEnd)
            editorWidget->moveCursor(QTextCursor::EndOfLine);
        else
            editorWidget->moveCursor(QTextCursor::StartOfBlock);
        editorWidget->insertPlainText(docToWrite);
    }
}

void Doxygen::addSymbol(const CPlusPlus::Symbol* symbol, QList<const Symbol*> &symmap)
{
    if(!symbol || symbol->isBaseClass() || symbol->isGenerated())
        return;
    if(symbol->isArgument()
        || symbol->isFunction()
        || symbol->isDeclaration()
        || symbol->isEnum())
    {
        symmap.append(symbol);
        return;
    }
    else if(symbol->isClass())
    {
        symmap.append(symbol);
    }

    const CPlusPlus::Scope* scopedSymbol = symbol->asScope();
    if(scopedSymbol)
    {
        int nbmembers = scopedSymbol->memberCount();
        for(int i=0; i<nbmembers; ++i)
        {
            addSymbol(scopedSymbol->memberAt(i), symmap);
        }
    }
}

void Doxygen::documentFile(const DoxygenSettingsStruct &DoxySettings)
{
    const Core::EditorManager *editorManager = Core::EditorManager::instance();
    Core::IEditor *editor = editorManager->currentEditor();

    // before continuing, test if the editor is actually showing a file.
    if(!editor)
        return;

    CppTools::CppModelManagerInterface *modelManager =
            ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    if(!modelManager)
        return;

    const Snapshot snapshot = modelManager->snapshot();
    Document::Ptr doc = snapshot.document(editor->file()->fileName());
    if(!doc)
        return;

    // TODO : check
    int globalSymbols = doc->globalSymbolCount();
    if(!globalSymbols)
        return;

    // check that as well...
    Scope* scope = doc->scopeAt(0,0);
    if(!scope)
        return;

    unsigned symbolcount = scope->memberCount();

    QList<const Symbol*> symmap;
    for(unsigned i=0; i < symbolcount; ++i)
        addSymbol(scope->memberAt(i), symmap);

    // sanity check, it's expensive and ugly but the result isn't pretty on some codes if not done.
    unsigned oldline=0;
    Q_FOREACH(const Symbol* sym, symmap)
    {
        if(sym->line() == oldline)
            symmap.removeOne(sym);
        oldline = sym->line();
    }

    TextEditor::BaseTextEditor *editorWidget = qobject_cast<TextEditor::BaseTextEditor*>(
            editorManager->currentEditor()->widget());

    if (editorWidget)
    {
        QList<const Symbol*>::iterator it = symmap.end();
        for(; it != symmap.begin(); --it)
        {
            const Symbol* sym = *(it-1);
            editorWidget->gotoLine(sym->line());
            createDocumentation(DoxySettings);
        }
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/http/url_security_manager.h"

#include "base/basictypes.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth_filter.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

struct TestData {
  const char* url;
  bool succeds_in_windows_default;
  bool succeeds_in_whitelist;
};

const char* kTestAuthWhitelist = "*example.com,*foobar.com,baz";

// Under Windows the following will be allowed by default:
//    localhost
//    host names without a period.
// In Posix systems (or on Windows if a whitelist is specified explicitly),
// everything depends on the whitelist.
const TestData kTestDataList[] = {
  { "http://localhost", true, false },
  { "http://bat", true, false },
  { "http://www.example.com", false, true },
  { "http://example.com", false, true },
  { "http://foobar.com", false, true },
  { "http://boo.foobar.com", false, true },
  { "http://baz", true, true },
  { "http://www.exampl.com", false, false },
  { "http://example.org", false, false },
  { "http://foobar.net", false, false },
  { "http://boo.fubar.com", false, false },
};

}  // namespace

// For Windows, This relies on the contents of the registry, so in theory it
// might fail.
TEST(URLSecurityManager, FLAKY_CreateNoWhitelist) {
  scoped_ptr<URLSecurityManager> url_security_manager(
      URLSecurityManager::Create(NULL));
  ASSERT_TRUE(url_security_manager.get());

  for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
    GURL gurl(kTestDataList[i].url);
    bool can_use_default =
        url_security_manager->CanUseDefaultCredentials(gurl);

#if defined(OS_WIN)
    EXPECT_EQ(kTestDataList[i].succeds_in_windows_default, can_use_default)
        << " Run: " << i << " URL: '" << gurl << "'";
#else
    // No whitelist means nothing can use the default.
    EXPECT_FALSE(can_use_default)
        << " Run: " << i << " URL: '" << gurl << "'";
#endif // OS_WIN
  }
}

TEST(URLSecurityManager, CreateWhitelist) {
  HttpAuthFilterWhitelist* auth_filter = new HttpAuthFilterWhitelist();
  ASSERT_TRUE(auth_filter);
  auth_filter->SetWhitelist(kTestAuthWhitelist);
  // The URL security manager takes ownership of |auth_filter|.
  scoped_ptr<URLSecurityManager> url_security_manager(
      URLSecurityManager::Create(auth_filter));
  ASSERT_TRUE(url_security_manager.get());

  for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
    GURL gurl(kTestDataList[i].url);
    bool can_use_default =
        url_security_manager->CanUseDefaultCredentials(gurl);

    EXPECT_EQ(kTestDataList[i].succeeds_in_whitelist, can_use_default)
        << " Run: " << i << " URL: '" << gurl << "'";
  }
}

}  // namespace net
<commit_msg>Cleaning up flakiness of a URLSecurityManager unit test.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/http/url_security_manager.h"

#include "base/basictypes.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth_filter.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

struct TestData {
  const char* url;
  bool succeds_in_windows_default;
  bool succeeds_in_whitelist;
};

const char* kTestAuthWhitelist = "*example.com,*foobar.com,baz";

// Under Windows the following will be allowed by default:
//    localhost
//    host names without a period.
// In Posix systems (or on Windows if a whitelist is specified explicitly),
// everything depends on the whitelist.
const TestData kTestDataList[] = {
  { "http://localhost", true, false },
  { "http://bat", true, false },
  { "http://www.example.com", false, true },
  { "http://example.com", false, true },
  { "http://foobar.com", false, true },
  { "http://boo.foobar.com", false, true },
  { "http://baz", true, true },
  { "http://www.exampl.com", false, false },
  { "http://example.org", false, false },
  { "http://foobar.net", false, false },
  { "http://boo.fubar.com", false, false },
};

}  // namespace

TEST(URLSecurityManager, CreateWhitelist) {
  HttpAuthFilterWhitelist* auth_filter = new HttpAuthFilterWhitelist();
  ASSERT_TRUE(auth_filter);
  auth_filter->SetWhitelist(kTestAuthWhitelist);
  // The URL security manager takes ownership of |auth_filter|.
  scoped_ptr<URLSecurityManager> url_security_manager(
      URLSecurityManager::Create(auth_filter));
  ASSERT_TRUE(url_security_manager.get());

  for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
    GURL gurl(kTestDataList[i].url);
    bool can_use_default =
        url_security_manager->CanUseDefaultCredentials(gurl);

    EXPECT_EQ(kTestDataList[i].succeeds_in_whitelist, can_use_default)
        << " Run: " << i << " URL: '" << gurl << "'";
  }
}

}  // namespace net
<|endoftext|>


<commit_before>#include <beast/core/streambuf.hpp>
#include <beast/http/read.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/write.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <silicium/sink/file_sink.hpp>
#include <silicium/sink/throwing_sink.hpp>
#include <ventura/file_operations.hpp>
#include <ventura/read_file.hpp>
#include <html_generator/tools/all.hpp>

namespace
{

	boost::system::error_code
	generate_all_html(ventura::absolute_path snippets_source_code,
	                  ventura::absolute_path const &existing_output_root,
	                  boost::string_ref const file_name)
	{
		ventura::absolute_path const index_path =
		    existing_output_root /
		    ventura::relative_path(file_name.begin(), file_name.end());
		Si::error_or<Si::file_handle> const index = ventura::overwrite_file(
		    ventura::safe_c_str(to_os_string(index_path)));
		if (index.is_error())
		{
			std::cerr << "Could not overwrite file " << index_path << '\n';
			return index.error();
		}

		Si::file_sink index_sink(index.get().handle);
		using namespace Si::html;

		static const std::string site_title = "TyRoXx' blog";

		auto page_content = dynamic([
			&file_name,
			snippets_source_code = std::move(snippets_source_code)
		](code_sink & sink)
		                            {
			                            auto drafts =
#include "pages/how-to-choose-an-integer-type.hpp"
			                                ;
			                            drafts.generate(sink);
			                        });

		auto head_content = tags::head(
		    tag("meta", attribute("charset", "utf-8"), empty) +
		    tag("meta",
		        attribute("name", "viewport") +
		            attribute("content", "width=device-width, initial-scale=1"),
		        empty) +
		    tags::title(site_title) +
		    tag("link",
		        tags::href("stylesheets.css") + attribute("rel", "stylesheet"),
		        empty) +
		    tag("link", tags::href("stylesheets-dark.css") +
		                    attribute("rel", "stylesheet"),
		        empty) +
		    tag("script", attribute("src", "toggleTheme.js"), text(" ")));
		auto body_content = tags::body(
		    tags::h1(text(site_title)) +
		    tags::a(tags::href("#") + attribute("onclick", "toggleTheme()"),
		            text("Toggle theme")) +
		    std::move(page_content) +
#include "pages/footer.hpp"
		    +tag("script", text("setTheme();")));
		auto const document =
		    raw("<!DOCTYPE html>") +
		    tags::html(std::move(head_content) + std::move(body_content));
		auto erased_sink = Si::Sink<char, Si::success>::erase(
		    Si::make_throwing_sink(index_sink));
		try
		{
			document.generate(erased_sink);
			return {};
		}
		catch (boost::system::system_error const &ex)
		{
			// TODO: do this without an exception
			return ex.code();
		}
	}

	struct file_client
	{
		boost::asio::ip::tcp::socket socket;
		beast::streambuf receive_buffer;
		beast::http::response<beast::http::string_body> response;

		explicit file_client(boost::asio::ip::tcp::socket socket,
		                     beast::streambuf receive_buffer)
		    : socket(std::move(socket))
		    , receive_buffer(std::move(receive_buffer))
		{
		}
	};

	struct http_client
	{
		boost::asio::ip::tcp::socket socket;
		beast::streambuf receive_buffer;
		beast::http::request<beast::http::string_body> request;

		explicit http_client(boost::asio::ip::tcp::socket socket,
		                     beast::streambuf receive_buffer)
		    : socket(std::move(socket))
		    , receive_buffer(std::move(receive_buffer))
		{
		}
	};

	void begin_serve(std::shared_ptr<http_client> client,
	                 ventura::absolute_path const &document_root);

	void serve_prepared_response(
	    std::shared_ptr<file_client> client, bool const is_keep_alive,
	    ventura::absolute_path const &document_root,
	    boost::optional<boost::string_ref> const content_type)
	{
		client->response.version = 11;
		client->response.fields.insert(
		    "Content-Length",
		    boost::lexical_cast<std::string>(client->response.body.size()));
		if (content_type)
		{
			client->response.fields.insert("Content-Type", *content_type);
		}
		beast::http::async_write(
		    client->socket, client->response,
		    [client, is_keep_alive,
		     document_root](boost::system::error_code const ec)
		    {
			    Si::throw_if_error(ec);
			    if (is_keep_alive)
			    {
				    auto http_client_again = std::make_shared<http_client>(
				        std::move(client->socket),
				        std::move(client->receive_buffer));
				    begin_serve(http_client_again, document_root);
			    }
			    else
			    {
				    client->socket.shutdown(
				        boost::asio::ip::tcp::socket::shutdown_both);
			    }
			});
	}

	void serve_static_file(std::shared_ptr<file_client> client,
	                       bool const is_keep_alive,
	                       ventura::absolute_path const &document_root,
	                       ventura::absolute_path const &served_document)
	{
		Si::visit<void>(
		    ventura::read_file(
		        ventura::safe_c_str(ventura::to_os_string(served_document))),
		    [&](std::vector<char> content)
		    {
			    // TODO: avoid the copy of the content
			    client->response.body.assign(content.begin(), content.end());
			    client->response.reason = "OK";
			    client->response.status = 200;
			},
		    [&](boost::system::error_code const ec)
		    {
			    std::cerr << "Could not read file " << served_document << ": "
			              << ec << '\n';
			    client->response.reason = "Internal Server Error";
			    client->response.status = 500;
			    client->response.body = client->response.reason;
			},
		    [&](ventura::read_file_problem const problem)
		    {
			    std::cerr << "Could not read file " << served_document
			              << " due to problem " << static_cast<int>(problem)
			              << '\n';
			    client->response.reason = "Internal Server Error";
			    client->response.status = 500;
			    client->response.body = client->response.reason;
			});
		serve_prepared_response(client, is_keep_alive, document_root,
		                        boost::none);
	}

	void begin_serve(std::shared_ptr<http_client> client,
	                 ventura::absolute_path const &document_root)
	{
		beast::http::async_read(
		    client->socket, client->receive_buffer, client->request,
		    [client, document_root](boost::system::error_code const ec)
		    {
			    Si::throw_if_error(ec);
			    std::shared_ptr<file_client> const new_client =
			        std::make_shared<file_client>(
			            std::move(client->socket),
			            std::move(client->receive_buffer));
			    if (!client->request.url.empty() &&
			        (client->request.url.front() == '/'))
			    {
				    boost::filesystem::path requested_file(
				        client->request.url.begin() + 1,
				        client->request.url.end());
				    if (requested_file.empty())
				    {
					    requested_file = "index.html";
				    }
				    if (requested_file.is_relative())
				    {
					    serve_static_file(
					        new_client,
					        beast::http::is_keep_alive(client->request),
					        document_root,
					        document_root / ventura::relative_path(
					                            std::move(requested_file)));
					    return;
				    }
			    }
			    new_client->response.reason = "Bad Request";
			    new_client->response.status = 400;
			    new_client->response.body = new_client->response.reason;
			    serve_prepared_response(
			        new_client, beast::http::is_keep_alive(client->request),
			        document_root, boost::string_ref("text/html"));
			});
	}

	void begin_accept(boost::asio::ip::tcp::acceptor &acceptor,
	                  ventura::absolute_path const &document_root)
	{
		auto client = std::make_shared<http_client>(
		    boost::asio::ip::tcp::socket(acceptor.get_io_service()),
		    beast::streambuf());
		acceptor.async_accept(client->socket,
		                      [&acceptor, client, document_root](
		                          boost::system::error_code const ec)
		                      {
			                      Si::throw_if_error(ec);
			                      begin_accept(acceptor, document_root);
			                      begin_serve(client, document_root);
			                  });
	}
}

int main(int argc, const char **argv)
{
	std::string output_option;
	boost::uint16_t web_server_port = 0;

	boost::program_options::options_description desc("Allowed options");
	desc.add_options()("help", "produce help message")(
	    "output", boost::program_options::value(&output_option),
	    "a directory to put the HTML files into")(
	    "serve", boost::program_options::value(&web_server_port),
	    "serve the output directory on this port");

	boost::program_options::positional_options_description positional;
	positional.add("output", 1);
	boost::program_options::variables_map vm;
	try
	{
		boost::program_options::store(
		    boost::program_options::command_line_parser(argc, argv)
		        .options(desc)
		        .positional(positional)
		        .run(),
		    vm);
	}
	catch (boost::program_options::error const &ex)
	{
		std::cerr << ex.what() << '\n' << desc << "\n";
		return 1;
	}

	boost::program_options::notify(vm);

	if (vm.count("help"))
	{
		std::cerr << desc << "\n";
		return 1;
	}

	if (output_option.empty())
	{
		std::cerr << "Please provide an absolute path to generate to.\n";
		std::cerr << desc << "\n";
		return 1;
	}

	// Setting directory
	Si::optional<ventura::absolute_path> const output_root =
	    ventura::absolute_path::create(output_option);
	if (!output_root)
	{
		std::cerr
		    << "The output directory must be given as an absolute path.\n";
		std::cerr << desc << "\n";
		return 1;
	}

	{
		boost::system::error_code const ec =
		    ventura::create_directories(*output_root, Si::return_);
		if (!!ec)
		{
			std::cerr << ec << '\n';
			return 1;
		}
	}

	ventura::absolute_path repo = *ventura::parent(
	    *ventura::parent(*ventura::absolute_path::create(__FILE__)));

	// Copying the assets
	ventura::copy(
	    repo / ventura::relative_path("html_generator/pages/stylesheet.css"),
	    *output_root / ventura::relative_path("stylesheets.css"), Si::return_);

	ventura::copy(repo / ventura::relative_path(
	                         "html_generator/pages/stylesheet-dark.css"),
	              *output_root / ventura::relative_path("stylesheets-dark.css"),
	              Si::return_);
	ventura::copy(
	    repo / ventura::relative_path("html_generator/pages/toggleTheme.js"),
	    *output_root / ventura::relative_path("toggleTheme.js"), Si::return_);

	// Generating the files
	static const boost::string_ref files_to_generate[] = {"index.html"};
	for (boost::string_ref const file : files_to_generate)
	{
		boost::system::error_code const ec = generate_all_html(
		    repo / ventura::relative_path("snippets"), *output_root, file);
		if (!!ec)
		{
			std::cerr << ec << '\n';
			return 1;
		}
	}

	if (!vm.count("serve"))
	{
		return 0;
	}

	// Starting the server
	try
	{
		using namespace boost::asio;

		io_service io;

			ip::tcp::acceptor acceptor_v4(
			    io, ip::tcp::endpoint(ip::tcp::v4(), web_server_port), true);
			acceptor_v4.listen();
			begin_accept(acceptor_v4, *output_root);

		while (!io.stopped())
		{
			try
			{
				io.run();
			}
			catch (boost::system::system_error const &ex)
			{
				std::cerr << "boost::system::system_error: " << ex.code()
				          << '\n';
				io.reset();
			}
			catch (std::exception const &ex)
			{
				std::cerr << "std::exception: " << ex.what() << '\n';
				io.reset();
			}
		}
	}
	catch (boost::system::system_error const &ex)
	{
		std::cerr << "boost::system::system_error: " << ex.code() << '\n';
		return 1;
	}
	catch (std::exception const &ex)
	{
		std::cerr << "std::exception: " << ex.what() << '\n';
		return 1;
	}
}
<commit_msg>clang-format<commit_after>#include <beast/core/streambuf.hpp>
#include <beast/http/read.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/write.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <silicium/sink/file_sink.hpp>
#include <silicium/sink/throwing_sink.hpp>
#include <ventura/file_operations.hpp>
#include <ventura/read_file.hpp>
#include <html_generator/tools/all.hpp>

namespace
{

	boost::system::error_code
	generate_all_html(ventura::absolute_path snippets_source_code,
	                  ventura::absolute_path const &existing_output_root,
	                  boost::string_ref const file_name)
	{
		ventura::absolute_path const index_path =
		    existing_output_root /
		    ventura::relative_path(file_name.begin(), file_name.end());
		Si::error_or<Si::file_handle> const index = ventura::overwrite_file(
		    ventura::safe_c_str(to_os_string(index_path)));
		if (index.is_error())
		{
			std::cerr << "Could not overwrite file " << index_path << '\n';
			return index.error();
		}

		Si::file_sink index_sink(index.get().handle);
		using namespace Si::html;

		static const std::string site_title = "TyRoXx' blog";

		auto page_content = dynamic([
			&file_name,
			snippets_source_code = std::move(snippets_source_code)
		](code_sink & sink)
		                            {
			                            auto drafts =
#include "pages/how-to-choose-an-integer-type.hpp"
			                                ;
			                            drafts.generate(sink);
			                        });

		auto head_content = tags::head(
		    tag("meta", attribute("charset", "utf-8"), empty) +
		    tag("meta",
		        attribute("name", "viewport") +
		            attribute("content", "width=device-width, initial-scale=1"),
		        empty) +
		    tags::title(site_title) +
		    tag("link",
		        tags::href("stylesheets.css") + attribute("rel", "stylesheet"),
		        empty) +
		    tag("link", tags::href("stylesheets-dark.css") +
		                    attribute("rel", "stylesheet"),
		        empty) +
		    tag("script", attribute("src", "toggleTheme.js"), text(" ")));
		auto body_content = tags::body(
		    tags::h1(text(site_title)) +
		    tags::a(tags::href("#") + attribute("onclick", "toggleTheme()"),
		            text("Toggle theme")) +
		    std::move(page_content) +
#include "pages/footer.hpp"
		    +tag("script", text("setTheme();")));
		auto const document =
		    raw("<!DOCTYPE html>") +
		    tags::html(std::move(head_content) + std::move(body_content));
		auto erased_sink = Si::Sink<char, Si::success>::erase(
		    Si::make_throwing_sink(index_sink));
		try
		{
			document.generate(erased_sink);
			return {};
		}
		catch (boost::system::system_error const &ex)
		{
			// TODO: do this without an exception
			return ex.code();
		}
	}

	struct file_client
	{
		boost::asio::ip::tcp::socket socket;
		beast::streambuf receive_buffer;
		beast::http::response<beast::http::string_body> response;

		explicit file_client(boost::asio::ip::tcp::socket socket,
		                     beast::streambuf receive_buffer)
		    : socket(std::move(socket))
		    , receive_buffer(std::move(receive_buffer))
		{
		}
	};

	struct http_client
	{
		boost::asio::ip::tcp::socket socket;
		beast::streambuf receive_buffer;
		beast::http::request<beast::http::string_body> request;

		explicit http_client(boost::asio::ip::tcp::socket socket,
		                     beast::streambuf receive_buffer)
		    : socket(std::move(socket))
		    , receive_buffer(std::move(receive_buffer))
		{
		}
	};

	void begin_serve(std::shared_ptr<http_client> client,
	                 ventura::absolute_path const &document_root);

	void serve_prepared_response(
	    std::shared_ptr<file_client> client, bool const is_keep_alive,
	    ventura::absolute_path const &document_root,
	    boost::optional<boost::string_ref> const content_type)
	{
		client->response.version = 11;
		client->response.fields.insert(
		    "Content-Length",
		    boost::lexical_cast<std::string>(client->response.body.size()));
		if (content_type)
		{
			client->response.fields.insert("Content-Type", *content_type);
		}
		beast::http::async_write(
		    client->socket, client->response,
		    [client, is_keep_alive,
		     document_root](boost::system::error_code const ec)
		    {
			    Si::throw_if_error(ec);
			    if (is_keep_alive)
			    {
				    auto http_client_again = std::make_shared<http_client>(
				        std::move(client->socket),
				        std::move(client->receive_buffer));
				    begin_serve(http_client_again, document_root);
			    }
			    else
			    {
				    client->socket.shutdown(
				        boost::asio::ip::tcp::socket::shutdown_both);
			    }
			});
	}

	void serve_static_file(std::shared_ptr<file_client> client,
	                       bool const is_keep_alive,
	                       ventura::absolute_path const &document_root,
	                       ventura::absolute_path const &served_document)
	{
		Si::visit<void>(
		    ventura::read_file(
		        ventura::safe_c_str(ventura::to_os_string(served_document))),
		    [&](std::vector<char> content)
		    {
			    // TODO: avoid the copy of the content
			    client->response.body.assign(content.begin(), content.end());
			    client->response.reason = "OK";
			    client->response.status = 200;
			},
		    [&](boost::system::error_code const ec)
		    {
			    std::cerr << "Could not read file " << served_document << ": "
			              << ec << '\n';
			    client->response.reason = "Internal Server Error";
			    client->response.status = 500;
			    client->response.body = client->response.reason;
			},
		    [&](ventura::read_file_problem const problem)
		    {
			    std::cerr << "Could not read file " << served_document
			              << " due to problem " << static_cast<int>(problem)
			              << '\n';
			    client->response.reason = "Internal Server Error";
			    client->response.status = 500;
			    client->response.body = client->response.reason;
			});
		serve_prepared_response(client, is_keep_alive, document_root,
		                        boost::none);
	}

	void begin_serve(std::shared_ptr<http_client> client,
	                 ventura::absolute_path const &document_root)
	{
		beast::http::async_read(
		    client->socket, client->receive_buffer, client->request,
		    [client, document_root](boost::system::error_code const ec)
		    {
			    Si::throw_if_error(ec);
			    std::shared_ptr<file_client> const new_client =
			        std::make_shared<file_client>(
			            std::move(client->socket),
			            std::move(client->receive_buffer));
			    if (!client->request.url.empty() &&
			        (client->request.url.front() == '/'))
			    {
				    boost::filesystem::path requested_file(
				        client->request.url.begin() + 1,
				        client->request.url.end());
				    if (requested_file.empty())
				    {
					    requested_file = "index.html";
				    }
				    if (requested_file.is_relative())
				    {
					    serve_static_file(
					        new_client,
					        beast::http::is_keep_alive(client->request),
					        document_root,
					        document_root / ventura::relative_path(
					                            std::move(requested_file)));
					    return;
				    }
			    }
			    new_client->response.reason = "Bad Request";
			    new_client->response.status = 400;
			    new_client->response.body = new_client->response.reason;
			    serve_prepared_response(
			        new_client, beast::http::is_keep_alive(client->request),
			        document_root, boost::string_ref("text/html"));
			});
	}

	void begin_accept(boost::asio::ip::tcp::acceptor &acceptor,
	                  ventura::absolute_path const &document_root)
	{
		auto client = std::make_shared<http_client>(
		    boost::asio::ip::tcp::socket(acceptor.get_io_service()),
		    beast::streambuf());
		acceptor.async_accept(client->socket,
		                      [&acceptor, client, document_root](
		                          boost::system::error_code const ec)
		                      {
			                      Si::throw_if_error(ec);
			                      begin_accept(acceptor, document_root);
			                      begin_serve(client, document_root);
			                  });
	}
}

int main(int argc, const char **argv)
{
	std::string output_option;
	boost::uint16_t web_server_port = 0;

	boost::program_options::options_description desc("Allowed options");
	desc.add_options()("help", "produce help message")(
	    "output", boost::program_options::value(&output_option),
	    "a directory to put the HTML files into")(
	    "serve", boost::program_options::value(&web_server_port),
	    "serve the output directory on this port");

	boost::program_options::positional_options_description positional;
	positional.add("output", 1);
	boost::program_options::variables_map vm;
	try
	{
		boost::program_options::store(
		    boost::program_options::command_line_parser(argc, argv)
		        .options(desc)
		        .positional(positional)
		        .run(),
		    vm);
	}
	catch (boost::program_options::error const &ex)
	{
		std::cerr << ex.what() << '\n' << desc << "\n";
		return 1;
	}

	boost::program_options::notify(vm);

	if (vm.count("help"))
	{
		std::cerr << desc << "\n";
		return 1;
	}

	if (output_option.empty())
	{
		std::cerr << "Please provide an absolute path to generate to.\n";
		std::cerr << desc << "\n";
		return 1;
	}

	// Setting directory
	Si::optional<ventura::absolute_path> const output_root =
	    ventura::absolute_path::create(output_option);
	if (!output_root)
	{
		std::cerr
		    << "The output directory must be given as an absolute path.\n";
		std::cerr << desc << "\n";
		return 1;
	}

	{
		boost::system::error_code const ec =
		    ventura::create_directories(*output_root, Si::return_);
		if (!!ec)
		{
			std::cerr << ec << '\n';
			return 1;
		}
	}

	ventura::absolute_path repo = *ventura::parent(
	    *ventura::parent(*ventura::absolute_path::create(__FILE__)));

	// Copying the assets
	ventura::copy(
	    repo / ventura::relative_path("html_generator/pages/stylesheet.css"),
	    *output_root / ventura::relative_path("stylesheets.css"), Si::return_);

	ventura::copy(repo / ventura::relative_path(
	                         "html_generator/pages/stylesheet-dark.css"),
	              *output_root / ventura::relative_path("stylesheets-dark.css"),
	              Si::return_);
	ventura::copy(
	    repo / ventura::relative_path("html_generator/pages/toggleTheme.js"),
	    *output_root / ventura::relative_path("toggleTheme.js"), Si::return_);

	// Generating the files
	static const boost::string_ref files_to_generate[] = {"index.html"};
	for (boost::string_ref const file : files_to_generate)
	{
		boost::system::error_code const ec = generate_all_html(
		    repo / ventura::relative_path("snippets"), *output_root, file);
		if (!!ec)
		{
			std::cerr << ec << '\n';
			return 1;
		}
	}

	if (!vm.count("serve"))
	{
		return 0;
	}

	// Starting the server
	try
	{
		using namespace boost::asio;

		io_service io;

		ip::tcp::acceptor acceptor_v4(
		    io, ip::tcp::endpoint(ip::tcp::v4(), web_server_port), true);
		acceptor_v4.listen();
		begin_accept(acceptor_v4, *output_root);

		while (!io.stopped())
		{
			try
			{
				io.run();
			}
			catch (boost::system::system_error const &ex)
			{
				std::cerr << "boost::system::system_error: " << ex.code()
				          << '\n';
				io.reset();
			}
			catch (std::exception const &ex)
			{
				std::cerr << "std::exception: " << ex.what() << '\n';
				io.reset();
			}
		}
	}
	catch (boost::system::system_error const &ex)
	{
		std::cerr << "boost::system::system_error: " << ex.code() << '\n';
		return 1;
	}
	catch (std::exception const &ex)
	{
		std::cerr << "std::exception: " << ex.what() << '\n';
		return 1;
	}
}
<|endoftext|>


<commit_before>/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 *  IN THE SOFTWARE.
 */

#ifndef CPPCODEC_DETAIL_STREAM_CODEC
#define CPPCODEC_DETAIL_STREAM_CODEC

#include <stdlib.h> // for abort()
#include <stdint.h>

#include "../parse_error.hpp"

namespace cppcodec {
namespace detail {

template <typename Codec, typename CodecVariant>
class stream_codec
{
public:
    template <typename Result, typename ResultState> static void encode(
            Result& encoded_result, ResultState&, const unsigned char* binary, size_t binary_size);

    template <typename Result, typename ResultState> static void decode(
            Result& binary_result, ResultState&, const char* encoded, size_t encoded_size);

    static constexpr size_t encoded_size(size_t binary_size) noexcept;
    static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
};

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::encode(
        Result& encoded_result, ResultState& state,
        const unsigned char* src, size_t src_size)
{
    const unsigned char* src_end = src + src_size - Codec::binary_block_size();

    for (; src <= src_end; src += Codec::binary_block_size()) {
        Codec::encode_block(encoded_result, state, src);
    }
    src_end += Codec::binary_block_size();

    if (src_end > src) {
        auto remaining_src_len = src_end - src;
        if (!remaining_src_len || remaining_src_len >= Codec::binary_block_size()) {
            abort();
            return;
        }
        Codec::encode_tail(encoded_result, state, src, remaining_src_len);
        Codec::pad(encoded_result, state, remaining_src_len);
    }
}

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::decode(
        Result& binary_result, ResultState& state,
        const char* src_encoded, size_t src_size)
{
    const char* src = src_encoded;
    const char* src_end = src + src_size;

    using V = CodecVariant;

    uint8_t idx[Codec::encoded_block_size()] = {};
    uint8_t last_value_idx = 0;

    while (src < src_end) {
        if (CodecVariant::should_ignore(idx[last_value_idx] = CodecVariant::index_of(*(src++)))) {
            continue;
        }
        if (CodecVariant::is_special_character(idx[last_value_idx])) {
            break;
        }

        ++last_value_idx;
        if (last_value_idx == Codec::encoded_block_size()) {
            Codec::decode_block(binary_result, state, idx);
            last_value_idx = 0;
        }
    }

    uint8_t last_idx = last_value_idx;
    if (CodecVariant::is_padding_symbol(idx[last_value_idx])) {
        // We're in here because we just read a (first) padding character. Try to read more.
        ++last_idx;
        while (src < src_end) {
            if (CodecVariant::is_eof(idx[last_idx] = CodecVariant::index_of(*(src++)))) {
                break;
            }
            if (!CodecVariant::is_padding_symbol(idx[last_idx])) {
                throw padding_error();
            }

            ++last_idx;
            if (last_idx > Codec::encoded_block_size()) {
                throw padding_error();
            }
        }
    }

    if (last_value_idx)  {
        if (CodecVariant::requires_padding() && last_idx != Codec::encoded_block_size()) {
            // If the input is not a multiple of the block size then the input is incorrect.
            throw padding_error();
        }
        if (last_value_idx >= Codec::encoded_block_size()) {
            abort();
            return;
        }
        Codec::decode_tail(binary_result, state, idx, last_value_idx);
    }
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::encoded_size(size_t binary_size) noexcept
{
    using C = Codec;

    // constexpr rules make this a lot harder to read than it actually is.
    return CodecVariant::generates_padding()
            // With padding, the encoded size is a multiple of the encoded block size.
            // To calculate that, round the binary size up to multiple of the binary block size,
            // then convert to encoded by multiplying with { base32: 8/5, base64: 4/3 }.
            ? (binary_size + (C::binary_block_size() - 1)
                    - ((binary_size + (C::binary_block_size() - 1)) % C::binary_block_size()))
                    * C::encoded_block_size() / C::binary_block_size()
            // No padding: only pad to the next multiple of 5 bits, i.e. at most a single extra byte.
            : (binary_size * C::encoded_block_size() / C::binary_block_size())
                    + (((binary_size * C::encoded_block_size()) % C::binary_block_size()) ? 1 : 0);
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::decoded_max_size(size_t encoded_size) noexcept
{
    using C = Codec;

    return CodecVariant::requires_padding()
            ? encoded_size * C::binary_block_size() / C::encoded_block_size()
            : (encoded_size * C::binary_block_size() / C::encoded_block_size())
                    + (((encoded_size * C::binary_block_size()) % C::encoded_block_size()) ? 1 : 0);
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_STREAM_CODEC
<commit_msg>Readability: Only access a single variable after block processing.<commit_after>/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 *  IN THE SOFTWARE.
 */

#ifndef CPPCODEC_DETAIL_STREAM_CODEC
#define CPPCODEC_DETAIL_STREAM_CODEC

#include <stdlib.h> // for abort()
#include <stdint.h>

#include "../parse_error.hpp"

namespace cppcodec {
namespace detail {

template <typename Codec, typename CodecVariant>
class stream_codec
{
public:
    template <typename Result, typename ResultState> static void encode(
            Result& encoded_result, ResultState&, const unsigned char* binary, size_t binary_size);

    template <typename Result, typename ResultState> static void decode(
            Result& binary_result, ResultState&, const char* encoded, size_t encoded_size);

    static constexpr size_t encoded_size(size_t binary_size) noexcept;
    static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
};

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::encode(
        Result& encoded_result, ResultState& state,
        const unsigned char* src, size_t src_size)
{
    const unsigned char* src_end = src + src_size - Codec::binary_block_size();

    for (; src <= src_end; src += Codec::binary_block_size()) {
        Codec::encode_block(encoded_result, state, src);
    }
    src_end += Codec::binary_block_size();

    if (src_end > src) {
        auto remaining_src_len = src_end - src;
        if (!remaining_src_len || remaining_src_len >= Codec::binary_block_size()) {
            abort();
            return;
        }
        Codec::encode_tail(encoded_result, state, src, remaining_src_len);
        Codec::pad(encoded_result, state, remaining_src_len);
    }
}

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::decode(
        Result& binary_result, ResultState& state,
        const char* src_encoded, size_t src_size)
{
    const char* src = src_encoded;
    const char* src_end = src + src_size;

    uint8_t idx[Codec::encoded_block_size()] = {};
    uint8_t last_value_idx = 0;

    while (src < src_end) {
        if (CodecVariant::should_ignore(idx[last_value_idx] = CodecVariant::index_of(*(src++)))) {
            continue;
        }
        if (CodecVariant::is_special_character(idx[last_value_idx])) {
            break;
        }

        ++last_value_idx;
        if (last_value_idx == Codec::encoded_block_size()) {
            Codec::decode_block(binary_result, state, idx);
            last_value_idx = 0;
        }
    }

    uint8_t last_idx = last_value_idx;
    if (CodecVariant::is_padding_symbol(idx[last_idx])) {
        // We're in here because we just read a (first) padding character. Try to read more.
        ++last_idx;
        while (src < src_end) {
            if (CodecVariant::is_eof(idx[last_idx] = CodecVariant::index_of(*(src++)))) {
                break;
            }
            if (!CodecVariant::is_padding_symbol(idx[last_idx])) {
                throw padding_error();
            }

            ++last_idx;
            if (last_idx > Codec::encoded_block_size()) {
                throw padding_error();
            }
        }
    }

    if (last_idx)  {
        if (CodecVariant::requires_padding() && last_idx != Codec::encoded_block_size()) {
            // If the input is not a multiple of the block size then the input is incorrect.
            throw padding_error();
        }
        if (last_value_idx >= Codec::encoded_block_size()) {
            abort();
            return;
        }
        Codec::decode_tail(binary_result, state, idx, last_value_idx);
    }
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::encoded_size(size_t binary_size) noexcept
{
    using C = Codec;

    // constexpr rules make this a lot harder to read than it actually is.
    return CodecVariant::generates_padding()
            // With padding, the encoded size is a multiple of the encoded block size.
            // To calculate that, round the binary size up to multiple of the binary block size,
            // then convert to encoded by multiplying with { base32: 8/5, base64: 4/3 }.
            ? (binary_size + (C::binary_block_size() - 1)
                    - ((binary_size + (C::binary_block_size() - 1)) % C::binary_block_size()))
                    * C::encoded_block_size() / C::binary_block_size()
            // No padding: only pad to the next multiple of 5 bits, i.e. at most a single extra byte.
            : (binary_size * C::encoded_block_size() / C::binary_block_size())
                    + (((binary_size * C::encoded_block_size()) % C::binary_block_size()) ? 1 : 0);
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::decoded_max_size(size_t encoded_size) noexcept
{
    using C = Codec;

    return CodecVariant::requires_padding()
            ? encoded_size * C::binary_block_size() / C::encoded_block_size()
            : (encoded_size * C::binary_block_size() / C::encoded_block_size())
                    + (((encoded_size * C::binary_block_size()) % C::encoded_block_size()) ? 1 : 0);
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_STREAM_CODEC
<|endoftext|>


<commit_before>#ifndef _SDD_MEM_UNIQUE_TABLE_HH_
#define _SDD_MEM_UNIQUE_TABLE_HH_

#include <boost/container/flat_map.hpp>
#include <boost/intrusive/unordered_set.hpp>

namespace sdd { namespace mem {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief A table to unify data.
template <typename Unique>
class unique_table
{
  // Can't copy a unique_table.
  unique_table(const unique_table&) = delete;
  unique_table& operator=(const unique_table&) = delete;

private:

  /// @brief We choose a faster, unsafe mode.
  typedef boost::intrusive::link_mode<boost::intrusive::normal_link> link_mode;

  /// @brief Tell Boost.Intrusive how to access to the member hook.
  typedef boost::intrusive::member_hook< Unique
                                       , boost::intrusive::unordered_set_member_hook<link_mode>
                                       , &Unique::member_hook_>
          member_hook;

  /// @brief Tell Boost.Intrusive we want to use the standard hash mechanism.
  typedef boost::intrusive::hash<std::hash<Unique>> hash_option;

  /// @brief The type of the set that helps to unify data.
  typedef typename boost::intrusive::make_unordered_set<Unique, member_hook, hash_option>::type
          set_type;

  /// @brief A type needed by Boost.Intrusive.
  typedef typename set_type::bucket_type bucket_type;

  /// @brief A type needed by Boost.Intrusive.
  typedef typename set_type::bucket_traits bucket_traits;

  /// @brief The buckets needed by Boost.Intrusive.
  bucket_type* buckets_;

  /// @brief The actual container of unified data.
  set_type* set_;

  /// @brief The number of hits.
  std::size_t hit_;

  /// @brief The number of misses.
  std::size_t miss_;

  /// @brief The number of rehash.
  std::size_t rehash_;

  /// The number of re-usable memory blocks to keep.
  static constexpr std::size_t nb_blocks = 4096;

  /// Index re-usable memory blocks by size.
  boost::container::flat_multimap<std::size_t, char*> blocks_;

public:

  /// @brief Constructor.
  /// @param initial_size Initial capacity of the container.
  unique_table(std::size_t initial_size)
  	: buckets_(new bucket_type[set_type::suggested_upper_bucket_count(initial_size)])
    , set_(new set_type(bucket_traits( buckets_
                                     , set_type::suggested_upper_bucket_count(initial_size))))
    , blocks_()
  {
    blocks_.reserve(nb_blocks);
  }

  /// @brief Default constructor.
  unique_table()
    : unique_table(1000000)
  {
  }

  /// @brief Destructor.
  ~unique_table()
  {
    delete set_;
    delete[] buckets_;
    for (auto& b : blocks_)
    {
      delete[] b.second;
    }
  }

  /// @brief Unify a data.
  /// @param ptr A pointer to a data constructed with a placement new into the storage returned by
  /// allocate().
  /// @return A reference to the unified data.
  const Unique&
  operator()(Unique* ptr)
  {
    if (load_factor() >= 0.9)
    {
      rehash();
    }

    auto insertion = set_->insert(*ptr);
    if (not insertion.second)
    {
      ++hit_;
      const std::size_t size = sizeof(Unique) + ptr->extra_bytes();
      ptr->~Unique();
      if (blocks_.size() == nb_blocks)
      {
        // Erase last block (the biggest one).
        auto it = blocks_.end() - 1;
        delete[] it->second;
        blocks_.erase(it);
      }
      blocks_.emplace(size, reinterpret_cast<char*>(ptr));
    }
    else
    {
      ++miss_;
    }
    return *insertion.first;
  }

  /// @brief Allocate a memory block large enough for the given size.
  char*
  allocate(std::size_t extra_bytes)
  {
    const std::size_t size = sizeof(Unique) + extra_bytes;
    const auto it = blocks_.lower_bound(size);
    if (it != blocks_.end() and it->first <= (2 * size))
    {
      // re-use allocated blocks
      char* addr = it->second;
      blocks_.erase(it);
      return addr;
    }
    else
    {
      return new char[size];
    }
  }

  /// @brief Erase the given unified data.
  ///
  /// All subsequent uses of the erased data are invalid.
  void
  erase(Unique& x)
  noexcept
  {
    set_->erase_and_dispose(x, [](Unique* ptr){delete ptr;});
  }

  /// @brief Get the load factor of the internal hash table.
  double
  load_factor()
    assert(x.is_not_referenced() && "Unique still referenced.");
  const noexcept
  {
    return static_cast<double>(set_->size()) / static_cast<double>(set_->bucket_count());
  }

  /// @brief Get the number of unified elements.
  std::size_t
  size()
  const noexcept
  {
    return set_->size();
  }

private:

  /// @brief Rehash the internal hash table.
  void
  rehash()
  {
    ++rehash_;
    const std::size_t new_size = set_type::suggested_upper_bucket_count(set_->bucket_count() * 2);
    bucket_type* new_buckets = new bucket_type[new_size];
    set_->rehash(bucket_traits(new_buckets, new_size));
    delete[] buckets_;
    buckets_ = new_buckets;
  }
};

/*------------------------------------------------------------------------------------------------*/

namespace /* anonymous */ {

/// @internal
/// @related unique_table
template <typename Unique>
inline
mem::unique_table<Unique>&
global_unique_table()
noexcept
{
  static mem::unique_table<Unique> unique_table;
  return unique_table;
}

} // namespace anonymous

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Allocate a memory block large enough for the given size, in the global unique table.
/// @related unique_table
template <typename Unique>
inline
char*
allocate(std::size_t extra_bytes = 0)
{
  return global_unique_table<Unique>().allocate(extra_bytes);
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Unify a data in the global unique table.
/// @param u_ptr A pointer to a data constructed with a placement new into the storage returned by
/// allocate().
/// @param size The size of the data. It must be the same as the one given to allocate().
/// @related unique_table
template <typename Unique>
inline
const Unique&
unify(Unique* ptr)
{
  return global_unique_table<Unique>()(ptr);
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::mem

#endif // _SDD_MEM_UNIQUE_TABLE_HH_
<commit_msg>Comments.<commit_after>#ifndef _SDD_MEM_UNIQUE_TABLE_HH_
#define _SDD_MEM_UNIQUE_TABLE_HH_

#include <cassert>

#include <boost/container/flat_map.hpp>
#include <boost/intrusive/unordered_set.hpp>

namespace sdd { namespace mem {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief A table to unify data.
template <typename Unique>
class unique_table
{
  // Can't copy a unique_table.
  unique_table(const unique_table&) = delete;
  unique_table& operator=(const unique_table&) = delete;

private:

  /// @brief We choose a faster, unsafe mode.
  typedef boost::intrusive::link_mode<boost::intrusive::normal_link> link_mode;

  /// @brief Tell Boost.Intrusive how to access to the member hook.
  typedef boost::intrusive::member_hook< Unique
                                       , boost::intrusive::unordered_set_member_hook<link_mode>
                                       , &Unique::member_hook_>
          member_hook;

  /// @brief Tell Boost.Intrusive we want to use the standard hash mechanism.
  typedef boost::intrusive::hash<std::hash<Unique>> hash_option;

  /// @brief The type of the set that helps to unify data.
  typedef typename boost::intrusive::make_unordered_set<Unique, member_hook, hash_option>::type
          set_type;

  /// @brief A type needed by Boost.Intrusive.
  typedef typename set_type::bucket_type bucket_type;

  /// @brief A type needed by Boost.Intrusive.
  typedef typename set_type::bucket_traits bucket_traits;

  /// @brief The buckets needed by Boost.Intrusive.
  bucket_type* buckets_;

  /// @brief The actual container of unified data.
  set_type* set_;

  /// @brief The number of hits.
  std::size_t hit_;

  /// @brief The number of misses.
  std::size_t miss_;

  /// @brief The number of rehash.
  std::size_t rehash_;

  /// The number of re-usable memory blocks to keep.
  static constexpr std::size_t nb_blocks = 4096;

  /// Index re-usable memory blocks by size.
  boost::container::flat_multimap<std::size_t, char*> blocks_;

public:

  /// @brief Constructor.
  /// @param initial_size Initial capacity of the container.
  unique_table(std::size_t initial_size)
  	: buckets_(new bucket_type[set_type::suggested_upper_bucket_count(initial_size)])
    , set_(new set_type(bucket_traits( buckets_
                                     , set_type::suggested_upper_bucket_count(initial_size))))
    , blocks_()
  {
    blocks_.reserve(nb_blocks);
  }

  /// @brief Default constructor.
  unique_table()
    : unique_table(1000000)
  {
  }

  /// @brief Destructor.
  ~unique_table()
  {
    delete set_;
    delete[] buckets_;
    for (auto& b : blocks_)
    {
      delete[] b.second;
    }
  }

  /// @brief Unify a data.
  /// @param ptr A pointer to a data constructed with a placement new into the storage returned by
  /// allocate().
  /// @return A reference to the unified data.
  const Unique&
  operator()(Unique* ptr)
  {
    if (load_factor() >= 0.9)
    {
      rehash();
    }

    auto insertion = set_->insert(*ptr);
    if (not insertion.second)
    {
      // The inserted Unique already exists. We keep its allocated memory to avoid deallocating
      // each time there is a hit.
      ++hit_;
      const std::size_t size = sizeof(Unique) + ptr->extra_bytes();
      ptr->~Unique();
      if (blocks_.size() == nb_blocks)
      {
        // Erase last block (the biggest one).
        auto it = blocks_.end() - 1;
        delete[] it->second;
        blocks_.erase(it);
      }
      blocks_.emplace(size, reinterpret_cast<char*>(ptr));
    }
    else
    {
      ++miss_;
    }
    return *insertion.first;
  }

  /// @brief Allocate a memory block large enough for the given size.
  char*
  allocate(std::size_t extra_bytes)
  {
    const std::size_t size = sizeof(Unique) + extra_bytes;
    const auto it = blocks_.lower_bound(size);
    if (it != blocks_.end() and it->first <= (2 * size))
    {
      // re-use allocated blocks
      char* addr = it->second;
      blocks_.erase(it);
      return addr;
    }
    else
    {
      return new char[size];
    }
  }

  /// @brief Erase the given unified data.
  ///
  /// All subsequent uses of the erased data are invalid.
  void
  erase(Unique& x)
  noexcept
  {
    assert(x.is_not_referenced() && "Unique still referenced.");
    set_->erase_and_dispose(x, [](Unique* ptr){delete ptr;});
  }

  /// @brief Get the load factor of the internal hash table.
  double
  load_factor()
  const noexcept
  {
    return static_cast<double>(set_->size()) / static_cast<double>(set_->bucket_count());
  }

  /// @brief Get the number of unified elements.
  std::size_t
  size()
  const noexcept
  {
    return set_->size();
  }

private:

  /// @brief Rehash the internal hash table.
  void
  rehash()
  {
    ++rehash_;
    const std::size_t new_size = set_type::suggested_upper_bucket_count(set_->bucket_count() * 2);
    bucket_type* new_buckets = new bucket_type[new_size];
    set_->rehash(bucket_traits(new_buckets, new_size));
    delete[] buckets_;
    buckets_ = new_buckets;
  }
};

/*------------------------------------------------------------------------------------------------*/

namespace /* anonymous */ {

/// @internal
/// @related unique_table
template <typename Unique>
inline
mem::unique_table<Unique>&
global_unique_table()
noexcept
{
  static mem::unique_table<Unique> unique_table;
  return unique_table;
}

} // namespace anonymous

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Allocate a memory block large enough for the given size, in the global unique table.
/// @related unique_table
template <typename Unique>
inline
char*
allocate(std::size_t extra_bytes = 0)
{
  return global_unique_table<Unique>().allocate(extra_bytes);
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Unify a data in the global unique table.
/// @param u_ptr A pointer to a data constructed with a placement new into the storage returned by
/// allocate().
/// @param size The size of the data. It must be the same as the one given to allocate().
/// @related unique_table
template <typename Unique>
inline
const Unique&
unify(Unique* ptr)
{
  return global_unique_table<Unique>()(ptr);
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::mem

#endif // _SDD_MEM_UNIQUE_TABLE_HH_
<|endoftext|>


<commit_before><commit_msg>Revise output logic in NonDExpansion to allow the Perl scripts to pick up the central moment output in the (exceptional) case of non-positive variance.<commit_after><|endoftext|>


<commit_before>#ifndef DUNE_FUNCTIONALS_COMMON_LOCALVECTOR_HH
#define DUNE_FUNCTIONALS_COMMON_LOCALVECTOR_HH

namespace Dune
{

namespace Functionals
{

namespace Common
{

/**
  * \brief  This class represents a local DoF vector.
  *         It is based upon std::vector and should be replaced by something clever in the future!
  *
  * \tparam ElementType
  *         Type of one element, usually double or RangeFieldType.
  **/
template< class ElementType >
class LocalVector
{
public:

  /**
    * \brief      Initializes an empty vector, according to the given size.
    *
    * \param[in]  size
    *             Size, the vector should have, usually the number of local DoFs.
    **/
  LocalVector( const unsigned int size )
  {
    // resize
    storage_.resize( size, 0.0 );
  }

  /**
    * \brief      Initializes a DoF vector and sets its entries to the
    *             corresponding entries of the given localFunction.
    *
    * \tparam     LocalFunctionType
    *             Type of the given local function, usually something that fulfills the Dune::LocalFunction interface.
    *
    * \param[in]  localFunction
    *             Local function, whose DoFs are going to be assigned to the vector. Has to provide the method
    *             numDofs() and read access via an operator[].
    **/
  template< class LocalFunctionType >
  LocalVector( const LocalFunctionType& localFunction )
  {
    // resize
    storage_.resize( localFunction.numDofs(), 0.0 );

    // copy entries
    for( int i = 0; i < localFunction.numDofs(); ++i )
    {
      storage_[i] = localFunction[i];
    }
  }

  /**
    * \brief      Returns the size.
    *
    * \param[out] const unsigned int
    *             Number of entries.
    */
  const unsigned int size() const
  {
    return storage_.size();
  }

  /**
    * \brief      Random read and write access.
    *
    * \param[in]  i
    *             Number of the element.
    *
    * \param[out] ElementType&
    *             Reference to the element at position i, writable.
    **/
  ElementType& operator[]( const unsigned int i )
  {
    return storage_[i];
  }

  /**
    * \brief      Random read access.
    * \param[in]  i
    *             Number of the element.
    *
    * \param[out] const ElementType
    *             Reference to the element at position i, readable only.
    **/
  const ElementType operator[]( const unsigned int i ) const
  {
    return storage_[i];
  }

  /**
    * \brief      Scalar product of two local vectors of same type.
    *
    * \param[in]  other
    *             The LocalVector, that will be multiplied with from the right.
    *
    * \param[out] ElementType
    *             Result of the scalar product.
    **/
  ElementType operator*( const LocalVector< ElementType >& other ) const
  {
    assert( storage_.size() == other.size() );
    ElementType result = 0.0;

    for(  unsigned ii = 0;
          ii < storage_.size();
          ++ii )
    {
      result += storage_[ii] * other[ii];
    }

    return result;
  }

private:

  std::vector< ElementType > storage_;

}; // end of class LocalVector

} // end of namespace Common

} // end of namespace Functionals

} // end of namespace Dune

#endif // end DUNE_FUNCTIONALS_COMMON_LOCALVECTOR_HH
<commit_msg>added another constructor to Common::LocalVector<commit_after>#ifndef DUNE_FUNCTIONALS_COMMON_LOCALVECTOR_HH
#define DUNE_FUNCTIONALS_COMMON_LOCALVECTOR_HH

namespace Dune
{

namespace Functionals
{

namespace Common
{

/**
  * \brief  This class represents a local DoF vector.
  *         It is based upon std::vector and should be replaced by something clever in the future!
  *
  * \tparam ElementType
  *         Type of one element, usually double or RangeFieldType.
  **/
template< class ElementType >
class LocalVector
{
public:

  /**
    * \brief      Initializes an empty vector, according to the given size.
    *
    * \param[in]  size
    *             Size, the vector should have, usually the number of local DoFs.
    **/
  LocalVector( const unsigned int size )
  {
    // resize
    storage_.resize( size, 0.0 );
  }

  LocalVector( const int size )
  {
    // resize
    storage_.resize( size, 0.0 );
  }

  /**
    * \brief      Initializes a DoF vector and sets its entries to the
    *             corresponding entries of the given localFunction.
    *
    * \tparam     LocalFunctionType
    *             Type of the given local function, usually something that fulfills the Dune::LocalFunction interface.
    *
    * \param[in]  localFunction
    *             Local function, whose DoFs are going to be assigned to the vector. Has to provide the method
    *             numDofs() and read access via an operator[].
    **/
  template< class LocalFunctionType >
  LocalVector( const LocalFunctionType& localFunction )
  {
    // resize
    storage_.resize( localFunction.numDofs(), 0.0 );

    // copy entries
    for( int i = 0; i < localFunction.numDofs(); ++i )
    {
      storage_[i] = localFunction[i];
    }
  }

  /**
    * \brief      Returns the size.
    *
    * \param[out] const unsigned int
    *             Number of entries.
    */
  const unsigned int size() const
  {
    return storage_.size();
  }

  /**
    * \brief      Random read and write access.
    *
    * \param[in]  i
    *             Number of the element.
    *
    * \param[out] ElementType&
    *             Reference to the element at position i, writable.
    **/
  ElementType& operator[]( const unsigned int i )
  {
    return storage_[i];
  }

  /**
    * \brief      Random read access.
    * \param[in]  i
    *             Number of the element.
    *
    * \param[out] const ElementType
    *             Reference to the element at position i, readable only.
    **/
  const ElementType operator[]( const unsigned int i ) const
  {
    return storage_[i];
  }

  /**
    * \brief      Scalar product of two local vectors of same type.
    *
    * \param[in]  other
    *             The LocalVector, that will be multiplied with from the right.
    *
    * \param[out] ElementType
    *             Result of the scalar product.
    **/
  ElementType operator*( const LocalVector< ElementType >& other ) const
  {
    assert( storage_.size() == other.size() );
    ElementType result = 0.0;

    for(  unsigned ii = 0;
          ii < storage_.size();
          ++ii )
    {
      result += storage_[ii] * other[ii];
    }

    return result;
  }

private:

  std::vector< ElementType > storage_;

}; // end of class LocalVector

} // end of namespace Common

} // end of namespace Functionals

} // end of namespace Dune

#endif // end DUNE_FUNCTIONALS_COMMON_LOCALVECTOR_HH
<|endoftext|>