<commit_before>///
/// A simple clang plugin which will translate the given C file into Zomp
/// definitions
///

#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/AST.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;

namespace {

static std::string errorType(const char* msg)
{
    return std::string("error_t(\"") + msg + "\")";
}

static std::string zompTypeName(const Type* t)
{
    if( t == 0 )
    {
        return "type_was_null";
    }

    if( const BuiltinType* bt = dyn_cast<BuiltinType>(t) )
    {
        switch(bt->getKind())
        {
        case BuiltinType::Void: return "void";
        case BuiltinType::UShort: return "c_ushort";
        case BuiltinType::UInt: return "c_uint";
        case BuiltinType::ULong: return "c_ulong";
        case BuiltinType::ULongLong: return "c_ulong_long";
        case BuiltinType::UInt128: return "u128";
        case BuiltinType::Short: return "c_short";
        case BuiltinType::Int: return "c_int";
        case BuiltinType::Long: return "c_long";
        case BuiltinType::LongLong: return "c_long_long";
        case BuiltinType::Int128: return "s128";
        case BuiltinType::Float: return "float";
        case BuiltinType::Double: return "double";
        case BuiltinType::LongDouble: return "c_long_double";

        case BuiltinType::Char_U: return "c_implicit_uchar";
        case BuiltinType::Char_S: return "c_implicit_schar";
        case BuiltinType::UChar: return "c_uchar";
        case BuiltinType::SChar: return "c_schar";

        case BuiltinType::WChar_U:
        case BuiltinType::Char16:
        case BuiltinType::Char32:
        case BuiltinType::WChar_S:

        case BuiltinType::NullPtr:
        case BuiltinType::Dependent:
        case BuiltinType::Overload:

        case BuiltinType::ObjCId:
        case BuiltinType::ObjCClass:
        case BuiltinType::ObjCSel:

        default:
            return "UnsupportedBuiltinType";
        }
    }
    else if( const PointerType* pt = dyn_cast<PointerType>(t) )
    {
        QualType base_type = pt->getPointeeType();
        return zompTypeName(base_type.getTypePtrOrNull()) + "*";
    }
    else if( const ArrayType* at = dyn_cast<ArrayType>(t) )
    {
        assert( at );
        return errorType( "bindgen does not support array types, yet" );
    }
    else if( const FunctionType* ft = dyn_cast<FunctionType>(t) )
    {
        assert( ft );
        return errorType("bindgen does not support function types, yet" );
    }
    else if( const TypedefType* tt = dyn_cast<TypedefType>(t) )
    {
        return tt->getDecl()->getName();
        // return zompTypeName(
            // tt->getDecl()->getCanonicalDecl()->getUnderlyingType().getTypePtrOrNull() );
    }
    else if( const EnumType* et = dyn_cast<EnumType>(t) )
    {
        return et->getDecl()->getNameAsString();
    }
    else if( const RecordType* rt = dyn_cast<RecordType>(t) )
    {
        return rt->getDecl()->getNameAsString();
    }
    else
    {
        return errorType("type not understood by bindgen");
    }
}

static std::string zompTypeName( const QualType& qual_type )
{
    std::string base_name = zompTypeName( qual_type.getTypePtrOrNull() );
    std::string name = base_name;

    if( qual_type.isConstQualified() )
    {
        name = "/* const */" + name;
    }

    if( qual_type.isRestrictQualified() )
    {
        name = "/* restrict */" + name;
    }

    if( qual_type.isVolatileQualified() )
    {
        name = "/* volatile */" + name;
    }

    return name;
}

/** Visitor which will handle every top level declaration */
class GenBindingsConsumer : public ASTConsumer
{
    ASTContext* m_context;
    SourceManager* m_src_manager;
    FileID m_main_file_id;

public:
    GenBindingsConsumer() : m_context(0), m_src_manager(0)
    {
    }

    virtual void Initialize(ASTContext &context)
    {
        m_context = &context;
        m_src_manager = &context.getSourceManager();
        m_main_file_id = m_src_manager->getMainFileID();

        const char* main_file_name = m_src_manager->getFileEntryForID( m_main_file_id )->getName();
        llvm::outs() << "///\n";
        llvm::outs() << "/// Zomp bindings for " << main_file_name << "\n";
        llvm::outs() << "///\n";
        llvm::outs() << "\n";
    }

    virtual void HandleTopLevelDecl(DeclGroupRef DG)
    {
        for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; ++i)
        {
            const Decl *D = *i;

            const TranslationUnitDecl* tu = D->getTranslationUnitDecl();
            if( !tu ) {
                continue;
            }

            const SourceLocation& loc = D->getLocation();
            if( m_src_manager->getFileID(loc) != m_main_file_id)
            {
                continue;
            }

            handleAs<VarDecl>(D) ||
                handleAs<FunctionDecl>(D) ||
                handleAs<TypedefDecl>(D) ||
                handleAs<RecordDecl>(D) ||
                handleAs<EnumDecl>(D) ||
                handleUnknown(D);
        }
    }

private:

    enum HandlingResult { Handled, CouldNotHandle };

    template<typename T>
    bool handleAs(const Decl* D)
    {
        const T* typed_decl = dyn_cast<T>(D);
        if(typed_decl)
        {
            const bool result = handle(typed_decl) == Handled;
            // llvm::outs() << "\n";
            return result;
        }
        else
        {
            return false;
        }
    }

    HandlingResult handle(const FunctionDecl* func_decl)
    {
        bool ignore = func_decl->isCXXClassMember()
            || func_decl->isCXXInstanceMember()
            || func_decl->isVariadic()
            || func_decl->isMain();

        if(ignore) {
            return CouldNotHandle;
        }

        llvm::outs() << "nativeFn ";
        llvm::outs() << zompTypeName( func_decl->getResultType() ) << " ";
        llvm::outs() << func_decl->getNameAsString() << "(";
        bool first_param = true;
        for( FunctionDecl::param_const_iterator param_i = func_decl->param_begin(),
                 pend = func_decl->param_end( );
             param_i != pend;
             ++param_i, first_param = false )
        {
            ParmVarDecl* param = *param_i;

            if( !first_param ) {
                llvm::outs() << ", ";
            }

            QualType typesrc = param->getTypeSourceInfo()->getType();
            const Type* type = typesrc.getTypePtrOrNull();

            llvm::outs() << zompTypeName(type);

            if( param->getIdentifier() )
            {
                llvm::outs() << " "
                             << param->getNameAsString();
            }

            if ( param->hasDefaultArg() )
            {
                llvm::outs() << " /* = ... */";
            }
        }
        llvm::outs() << ")\n";

        return Handled;
    }

    HandlingResult handle(const VarDecl* var_decl)
    {
        llvm::outs() << "nativeVar "
            << zompTypeName( var_decl->getTypeSourceInfo()->getType() )
            << " "
            << var_decl->getNameAsString()
            << "\n";

        return Handled;
    }

    bool handle(const TypedefDecl* type_decl)
    {
        QualType typesrc = type_decl->getUnderlyingType();
        const Type* type = typesrc.getTypePtrOrNull();
        std::string zomp_name = zompTypeName(type);

        bool handled = false;

        if( type )
        {
            if( zomp_name.empty() )
            {
                if( const RecordType* record_type = dyn_cast<RecordType>(type) )
                {
                    handled = handle( record_type->getDecl(), type_decl->getName() );
                }
            }
            else
            {
                llvm::outs()
                    << "nativeTypedef "
                    << type_decl->getName()
                    << " " << zomp_name << "\n";
                handled = true;
            }
        }

        if( !handled )
        {
            llvm::outs()
                << "// ignoring typedef " << type_decl->getName() << "\n";
        }

        return Handled;
    }

    bool handle(const RecordDecl* record_decl, llvm::StringRef name = "" )
    {
        if( record_decl->isAnonymousStructOrUnion() ||
            name.empty() )
        {
            llvm::outs() << "// ignoring anonymous struct\n";
            return Handled;
        }

        if( record_decl->isDefinition() )
        {
            llvm::outs() << "nativeStruct " << name << ":\n";

            typedef RecordDecl::field_iterator FieldIter;
            for( FieldIter fi = record_decl->field_begin(), fi_end = record_decl->field_end();
                 fi != fi_end;
                 ++fi )
            {
                const FieldDecl& field = **fi;
                if( field.isBitField() )
                {
                    llvm::outs() << "  // ignored bitfield, not supported\n";
                }
                else
                {
                    llvm::outs()
                        << "  "
                        << zompTypeName( field.getType() )
                        << " "
                        << field.getName()
                        << "\n";
                }
            }

            llvm::outs() << "end\n";
        }
        else
        {
            llvm::outs() << "nativeStruct " << name << "\n";
        }

        return Handled;
    }

    bool handle(const EnumDecl* enum_decl)
    {
        if( enum_decl->getName().empty() )
        {
            llvm::outs() << "// ignoring name-less enum\n";
        }
        else if( enum_decl->isComplete() )
        {
            llvm::outs()
                << "nativeEnum "
                << enum_decl->getName() << " "
                << zompTypeName( enum_decl->getPromotionType() )
                << ":\n";

            typedef EnumDecl::enumerator_iterator EnumIter;
            for( EnumIter variant = enum_decl->enumerator_begin(), vend = enum_decl->enumerator_end();
                 variant != vend;
                 ++variant )
            {
                EnumConstantDecl* ecd = *variant;

                llvm::outs()
                    << "  "
                    << ecd->getName() << " "
                    << ecd->getInitVal()
                    << "\n";
            }

            llvm::outs() << "end\n";
        }
        else
        {
            llvm::outs()
                << "nativeEnum "
                << enum_decl->getName()
                << "\n";
        }

        return Handled;
    }

    bool handleUnknown(const Decl* D)
    {
        if(const NamedDecl* n = dyn_cast<NamedDecl>(D))
        {
            llvm::outs() << "// ignored " << n->getNameAsString() << "\n";
        }
        else
        {
            llvm::outs() << "// ignored nameless declaration\n";
        }

        return Handled;
    }
};

/** The plugin class. Will instantiate GenBindingsConsumer and run it */
class GenBindingsAction : public PluginASTAction
{
protected:
    ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef)
    {
        return new GenBindingsConsumer();
    }

    bool ParseArgs(
        const CompilerInstance &CI,
        const std::vector<std::string>& args )
    {
        for (unsigned i = 0, e = args.size(); i != e; ++i) {
            llvm::errs() << "PrintFunctionNames arg = " << args[i] << "\n";

            // Example error handling.
            if (args[i] == "-an-error") {
                Diagnostic &D = CI.getDiagnostics();
                unsigned DiagID = D.getCustomDiagID(
                    Diagnostic::Error, "invalid argument '" + args[i] + "'");
                D.Report(DiagID);
                return false;
            }
        }
        if (args.size() && args[0] == "help")
            PrintHelp(llvm::errs());

        return true;
    }

    void PrintHelp(llvm::raw_ostream& ros)
    {
        ros << "Translates declarations of a C file into Zomp declarations.\n"
            "This makes it easy to use C libaries from Zomp.";
    }
};

} // anonymous namespace

static FrontendPluginRegistry::Add<GenBindingsAction> X("gen-zomp-bindings", "Generate Zomp bindings");

<commit_msg>bindgen supports "typedef { ... } FooEnum;" constructs, fixed bug introduced by last commit<commit_after>///
/// A simple clang plugin which will translate the given C file into Zomp
/// definitions
///

#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/AST.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;

namespace {

static std::string errorType(const char* msg)
{
    return std::string("error_t(\"") + msg + "\")";
}

static std::string zompTypeName(const Type* t)
{
    if( t == 0 )
    {
        return "type_was_null";
    }

    if( const BuiltinType* bt = dyn_cast<BuiltinType>(t) )
    {
        switch(bt->getKind())
        {
        case BuiltinType::Void: return "void";
        case BuiltinType::UShort: return "c_ushort";
        case BuiltinType::UInt: return "c_uint";
        case BuiltinType::ULong: return "c_ulong";
        case BuiltinType::ULongLong: return "c_ulong_long";
        case BuiltinType::UInt128: return "u128";
        case BuiltinType::Short: return "c_short";
        case BuiltinType::Int: return "c_int";
        case BuiltinType::Long: return "c_long";
        case BuiltinType::LongLong: return "c_long_long";
        case BuiltinType::Int128: return "s128";
        case BuiltinType::Float: return "float";
        case BuiltinType::Double: return "double";
        case BuiltinType::LongDouble: return "c_long_double";

        case BuiltinType::Char_U: return "c_implicit_uchar";
        case BuiltinType::Char_S: return "c_implicit_schar";
        case BuiltinType::UChar: return "c_uchar";
        case BuiltinType::SChar: return "c_schar";

        case BuiltinType::WChar_U:
        case BuiltinType::Char16:
        case BuiltinType::Char32:
        case BuiltinType::WChar_S:

        case BuiltinType::NullPtr:
        case BuiltinType::Dependent:
        case BuiltinType::Overload:

        case BuiltinType::ObjCId:
        case BuiltinType::ObjCClass:
        case BuiltinType::ObjCSel:

        default:
            return "UnsupportedBuiltinType";
        }
    }
    else if( const PointerType* pt = dyn_cast<PointerType>(t) )
    {
        QualType base_type = pt->getPointeeType();
        return zompTypeName(base_type.getTypePtrOrNull()) + "*";
    }
    else if( const ArrayType* at = dyn_cast<ArrayType>(t) )
    {
        assert( at );
        return errorType( "bindgen does not support array types, yet" );
    }
    else if( const FunctionType* ft = dyn_cast<FunctionType>(t) )
    {
        assert( ft );
        return errorType("bindgen does not support function types, yet" );
    }
    else if( const TypedefType* tt = dyn_cast<TypedefType>(t) )
    {
        return tt->getDecl()->getName();
        // return zompTypeName(
            // tt->getDecl()->getCanonicalDecl()->getUnderlyingType().getTypePtrOrNull() );
    }
    else if( const EnumType* et = dyn_cast<EnumType>(t) )
    {
        return et->getDecl()->getNameAsString();
    }
    else if( const RecordType* rt = dyn_cast<RecordType>(t) )
    {
        return rt->getDecl()->getNameAsString();
    }
    else
    {
        return errorType("type not understood by bindgen");
    }
}

static std::string zompTypeName( const QualType& qual_type )
{
    std::string base_name = zompTypeName( qual_type.getTypePtrOrNull() );
    std::string name = base_name;

    if( qual_type.isConstQualified() )
    {
        name = "/* const */" + name;
    }

    if( qual_type.isRestrictQualified() )
    {
        name = "/* restrict */" + name;
    }

    if( qual_type.isVolatileQualified() )
    {
        name = "/* volatile */" + name;
    }

    return name;
}

/** Visitor which will handle every top level declaration */
class GenBindingsConsumer : public ASTConsumer
{
    ASTContext* m_context;
    SourceManager* m_src_manager;
    FileID m_main_file_id;

public:
    GenBindingsConsumer() : m_context(0), m_src_manager(0)
    {
    }

    virtual void Initialize(ASTContext &context)
    {
        m_context = &context;
        m_src_manager = &context.getSourceManager();
        m_main_file_id = m_src_manager->getMainFileID();

        const char* main_file_name = m_src_manager->getFileEntryForID( m_main_file_id )->getName();
        llvm::outs() << "///\n";
        llvm::outs() << "/// Zomp bindings for " << main_file_name << "\n";
        llvm::outs() << "///\n";
        llvm::outs() << "\n";
    }

    virtual void HandleTopLevelDecl(DeclGroupRef DG)
    {
        for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; ++i)
        {
            const Decl *D = *i;

            const TranslationUnitDecl* tu = D->getTranslationUnitDecl();
            if( !tu ) {
                continue;
            }

            const SourceLocation& loc = D->getLocation();
            if( m_src_manager->getFileID(loc) != m_main_file_id)
            {
                continue;
            }

            handleAs<VarDecl>(D) ||
                handleAs<FunctionDecl>(D) ||
                handleAs<TypedefDecl>(D) ||
                handleAs<RecordDecl>(D) ||
                handleAs<EnumDecl>(D) ||
                handleUnknown(D);
        }
    }

private:

    enum HandlingResult { Handled, CouldNotHandle };

    template<typename T>
    bool handleAs(const Decl* D)
    {
        const T* typed_decl = dyn_cast<T>(D);
        if(typed_decl)
        {
            const bool result = handle(typed_decl) == Handled;
            // llvm::outs() << "\n";
            return result;
        }
        else
        {
            return false;
        }
    }

    HandlingResult handle(const FunctionDecl* func_decl)
    {
        bool ignore = func_decl->isCXXClassMember()
            || func_decl->isCXXInstanceMember()
            || func_decl->isVariadic()
            || func_decl->isMain();

        if(ignore) {
            return CouldNotHandle;
        }

        llvm::outs() << "nativeFn ";
        llvm::outs() << zompTypeName( func_decl->getResultType() ) << " ";
        llvm::outs() << func_decl->getNameAsString() << "(";
        bool first_param = true;
        for( FunctionDecl::param_const_iterator param_i = func_decl->param_begin(),
                 pend = func_decl->param_end( );
             param_i != pend;
             ++param_i, first_param = false )
        {
            ParmVarDecl* param = *param_i;

            if( !first_param ) {
                llvm::outs() << ", ";
            }

            QualType typesrc = param->getTypeSourceInfo()->getType();
            const Type* type = typesrc.getTypePtrOrNull();

            llvm::outs() << zompTypeName(type);

            if( param->getIdentifier() )
            {
                llvm::outs() << " "
                             << param->getNameAsString();
            }

            if ( param->hasDefaultArg() )
            {
                llvm::outs() << " /* = ... */";
            }
        }
        llvm::outs() << ")\n";

        return Handled;
    }

    HandlingResult handle(const VarDecl* var_decl)
    {
        llvm::outs() << "nativeVar "
            << zompTypeName( var_decl->getTypeSourceInfo()->getType() )
            << " "
            << var_decl->getNameAsString()
            << "\n";

        return Handled;
    }

    bool handle(const TypedefDecl* type_decl)
    {
        QualType typesrc = type_decl->getUnderlyingType();
        const Type* type = typesrc.getTypePtrOrNull();
        std::string zomp_name = zompTypeName(type);

        bool handled = false;

        if( type )
        {
            if( zomp_name.empty() )
            {
                if( const RecordType* record_type = dyn_cast<RecordType>(type) )
                {
                    handled = handle( record_type->getDecl(), type_decl->getName() );
                }
                else if( const EnumType* enum_type = dyn_cast<EnumType>(type) )
                {
                    handled = handle( enum_type->getDecl(), type_decl->getName() );
                }
            }
            else
            {
                llvm::outs()
                    << "nativeTypedef "
                    << type_decl->getName()
                    << " " << zomp_name << "\n";
                handled = true;
            }
        }

        if( !handled )
        {
            llvm::outs()
                << "// ignoring typedef " << type_decl->getName() << "\n";
        }

        return Handled;
    }

    bool handle(const RecordDecl* record_decl, llvm::StringRef name = "" )
    {
        if( name == "" )
        {
            name = record_decl->getName();
        }

        if( record_decl->isAnonymousStructOrUnion() ||
            name.empty() )
        {
            llvm::outs() << "// ignoring anonymous struct\n";
            return Handled;
        }

        if( record_decl->isDefinition() )
        {
            llvm::outs() << "nativeStruct " << name << ":\n";

            typedef RecordDecl::field_iterator FieldIter;
            for( FieldIter fi = record_decl->field_begin(), fi_end = record_decl->field_end();
                 fi != fi_end;
                 ++fi )
            {
                const FieldDecl& field = **fi;
                if( field.isBitField() )
                {
                    llvm::outs() << "  // ignored bitfield, not supported\n";
                }
                else
                {
                    llvm::outs()
                        << "  "
                        << zompTypeName( field.getType() )
                        << " "
                        << field.getName()
                        << "\n";
                }
            }

            llvm::outs() << "end\n";
        }
        else
        {
            llvm::outs() << "nativeStruct " << name << "\n";
        }

        return Handled;
    }

    bool handle(const EnumDecl* enum_decl, llvm::StringRef name = "")
    {
        if( name == "" )
        {
            name = enum_decl->getName();
        }

        if( name.empty() )
        {
            llvm::outs() << "// ignoring name-less enum\n";
        }
        else if( enum_decl->isComplete() )
        {
            llvm::outs()
                << "nativeEnum "
                << name << " "
                << zompTypeName( enum_decl->getPromotionType() )
                << ":\n";

            typedef EnumDecl::enumerator_iterator EnumIter;
            for( EnumIter variant = enum_decl->enumerator_begin(), vend = enum_decl->enumerator_end();
                 variant != vend;
                 ++variant )
            {
                EnumConstantDecl* ecd = *variant;

                llvm::outs()
                    << "  "
                    << ecd->getName() << " "
                    << ecd->getInitVal()
                    << "\n";
            }

            llvm::outs() << "end\n";
        }
        else
        {
            llvm::outs()
                << "nativeEnum "
                << name
                << "\n";
        }

        return Handled;
    }

    bool handleUnknown(const Decl* D)
    {
        if(const NamedDecl* n = dyn_cast<NamedDecl>(D))
        {
            llvm::outs() << "// ignored " << n->getNameAsString() << "\n";
        }
        else
        {
            llvm::outs() << "// ignored nameless declaration\n";
        }

        return Handled;
    }
};

/** The plugin class. Will instantiate GenBindingsConsumer and run it */
class GenBindingsAction : public PluginASTAction
{
protected:
    ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef)
    {
        return new GenBindingsConsumer();
    }

    bool ParseArgs(
        const CompilerInstance &CI,
        const std::vector<std::string>& args )
    {
        for (unsigned i = 0, e = args.size(); i != e; ++i) {
            llvm::errs() << "PrintFunctionNames arg = " << args[i] << "\n";

            // Example error handling.
            if (args[i] == "-an-error") {
                Diagnostic &D = CI.getDiagnostics();
                unsigned DiagID = D.getCustomDiagID(
                    Diagnostic::Error, "invalid argument '" + args[i] + "'");
                D.Report(DiagID);
                return false;
            }
        }
        if (args.size() && args[0] == "help")
            PrintHelp(llvm::errs());

        return true;
    }

    void PrintHelp(llvm::raw_ostream& ros)
    {
        ros << "Translates declarations of a C file into Zomp declarations.\n"
            "This makes it easy to use C libaries from Zomp.";
    }
};

} // anonymous namespace

static FrontendPluginRegistry::Add<GenBindingsAction> X("gen-zomp-bindings", "Generate Zomp bindings");

<|endoftext|>


<commit_before>#ifndef regex_impl_hh_INCLUDED
#define regex_impl_hh_INCLUDED

#include "exception.hh"
#include "flags.hh"
#include "ref_ptr.hh"
#include "unicode.hh"
#include "utf8.hh"
#include "utf8_iterator.hh"
#include "vector.hh"

#include <string.h>

namespace Kakoune
{

enum class MatchDirection
{
    Forward,
    Backward
};

struct CompiledRegex : RefCountable
{
    enum Op : char
    {
        Match,
        Literal,
        LiteralIgnoreCase,
        AnyChar,
        Matcher,
        Jump,
        Split_PrioritizeParent,
        Split_PrioritizeChild,
        Save,
        LineStart,
        LineEnd,
        WordBoundary,
        NotWordBoundary,
        SubjectBegin,
        SubjectEnd,
        LookAhead,
        NegativeLookAhead,
        LookBehind,
        NegativeLookBehind,
    };

    struct Instruction
    {
        Op op;
        mutable bool processed;
        mutable bool scheduled;
        uint32_t param;
    };
    static_assert(sizeof(Instruction) == 8, "");

    explicit operator bool() const { return not instructions.empty(); }

    Vector<Instruction> instructions;
    Vector<std::function<bool (Codepoint)>> matchers;
    Vector<Codepoint> lookarounds;
    MatchDirection direction;
    size_t save_count;

    struct StartChars { bool map[256]; };
    std::unique_ptr<StartChars> start_chars;
};

CompiledRegex compile_regex(StringView re, MatchDirection direction = MatchDirection::Forward);

enum class RegexExecFlags
{
    None              = 0,
    Search            = 1 << 0,
    NotBeginOfLine    = 1 << 1,
    NotEndOfLine      = 1 << 2,
    NotBeginOfWord    = 1 << 3,
    NotEndOfWord      = 1 << 4,
    NotBeginOfSubject = 1 << 5,
    NotInitialNull    = 1 << 6,
    AnyMatch          = 1 << 7,
    NoSaves           = 1 << 8,
    PrevAvailable     = 1 << 9,
};

constexpr bool with_bit_ops(Meta::Type<RegexExecFlags>) { return true; }

template<typename Iterator, MatchDirection direction>
struct ChooseUtf8It
{
    using Type = utf8::iterator<Iterator>;
};

template<typename Iterator>
struct ChooseUtf8It<Iterator, MatchDirection::Backward>
{
    using Type = std::reverse_iterator<utf8::iterator<Iterator>>;
};

template<typename Iterator, MatchDirection direction>
class ThreadedRegexVM
{
public:
    ThreadedRegexVM(const CompiledRegex& program)
      : m_program{program}
      {
          kak_assert(m_program);
          if (direction != program.direction)
              throw runtime_error{"Regex and VM direction mismatch"};
      }

    ThreadedRegexVM(const ThreadedRegexVM&) = delete;
    ThreadedRegexVM& operator=(const ThreadedRegexVM&) = delete;

    ~ThreadedRegexVM()
    {
        for (auto* saves : m_saves)
        {
            for (size_t i = m_program.save_count-1; i > 0; --i)
                saves->pos[i].~Iterator();
            saves->~Saves();
            ::operator delete(saves);
        }
    }

    bool exec(Iterator begin, Iterator end, RegexExecFlags flags)
    {
        const bool forward = direction == MatchDirection::Forward;
        const bool prev_avail = flags & RegexExecFlags::PrevAvailable;
        m_begin = Utf8It{utf8::iterator<Iterator>{forward ? begin : end,
                                                  prev_avail ? begin-1 : begin, end}};
        m_end = Utf8It{utf8::iterator<Iterator>{forward ? end : begin,
                                                prev_avail ? begin-1 : begin, end}};
        m_flags = flags;

        if (flags & RegexExecFlags::NotInitialNull and m_begin == m_end)
            return false;

        Vector<Thread> current_threads, next_threads;

        const bool no_saves = (m_flags & RegexExecFlags::NoSaves);
        Utf8It start{m_begin};

        const bool* start_chars = m_program.start_chars ? m_program.start_chars->map : nullptr;

        if (flags & RegexExecFlags::Search)
            to_next_start(start, m_end, start_chars);

        if (exec_from(start, no_saves ? nullptr : new_saves<false>(nullptr),
                      current_threads, next_threads))
            return true;

        if (not (flags & RegexExecFlags::Search))
            return false;

        do
        {
            to_next_start(++start, m_end, start_chars);
            if (exec_from(start, no_saves ? nullptr : new_saves<false>(nullptr),
                          current_threads, next_threads))
                return true;
        }
        while (start != m_end);

        return false;
    }

    ArrayView<const Iterator> captures() const
    {
        if (m_captures)
            return { m_captures->pos, m_program.save_count };
        return {};
    }

private:
    struct Saves
    {
        int refcount;
        Iterator pos[1];
    };

    template<bool copy>
    Saves* new_saves(Iterator* pos)
    {
        kak_assert(not copy or pos != nullptr);
        const auto count = m_program.save_count;
        if (not m_free_saves.empty())
        {
            Saves* res = m_free_saves.back();
            m_free_saves.pop_back();
            res->refcount = 1;
            if (copy)
                std::copy(pos, pos + count, res->pos);
            else
                std::fill(res->pos, res->pos + count, Iterator{});

            return res;
        }

        void* ptr = ::operator new (sizeof(Saves) + (count-1) * sizeof(Iterator));
        Saves* saves = new (ptr) Saves{1, copy ? pos[0] : Iterator{}};
        for (size_t i = 1; i < count; ++i)
            new (&saves->pos[i]) Iterator{copy ? pos[i] : Iterator{}};
        m_saves.push_back(saves);
        return saves;
    }

    void release_saves(Saves* saves)
    {
        if (saves and --saves->refcount == 0)
            m_free_saves.push_back(saves);
    };

    struct Thread
    {
        uint32_t inst;
        Saves* saves;
    };

    using Utf8It = typename ChooseUtf8It<Iterator, direction>::Type;

    enum class StepResult { Consumed, Matched, Failed };

    // Steps a thread until it consumes the current character, matches or fail
    StepResult step(const Utf8It& pos, Thread& thread, Vector<Thread>& threads)
    {
        while (true)
        {
            auto& inst = m_program.instructions[thread.inst++];
            if (inst.processed)
                return StepResult::Failed;
            inst.processed = true;

            switch (inst.op)
            {
                case CompiledRegex::Literal:
                    if (pos != m_end and inst.param == *pos)
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::LiteralIgnoreCase:
                    if (pos != m_end and inst.param == to_lower(*pos))
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::AnyChar:
                    return StepResult::Consumed;
                case CompiledRegex::Jump:
                    thread.inst = inst.param;
                    break;
                case CompiledRegex::Split_PrioritizeParent:
                {
                    if (thread.saves)
                        ++thread.saves->refcount;
                    threads.push_back({inst.param, thread.saves});
                    break;
                }
                case CompiledRegex::Split_PrioritizeChild:
                {
                    if (thread.saves)
                        ++thread.saves->refcount;
                    threads.push_back({thread.inst, thread.saves});
                    thread.inst = inst.param;
                    break;
                }
                case CompiledRegex::Save:
                {
                    if (thread.saves == nullptr)
                        break;
                    if (thread.saves->refcount > 1)
                    {
                        --thread.saves->refcount;
                        thread.saves = new_saves<true>(thread.saves->pos);
                    }
                    thread.saves->pos[inst.param] = get_base(pos);
                    break;
                }
                case CompiledRegex::Matcher:
                    if (pos == m_end)
                        return StepResult::Failed;
                    return m_program.matchers[inst.param](*pos) ?
                        StepResult::Consumed : StepResult::Failed;
                case CompiledRegex::LineStart:
                    if (not is_line_start(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LineEnd:
                    if (not is_line_end(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::WordBoundary:
                    if (not is_word_boundary(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::NotWordBoundary:
                    if (is_word_boundary(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectBegin:
                    if (pos != m_begin or (m_flags & RegexExecFlags::NotBeginOfSubject))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectEnd:
                    if (pos != m_end)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LookAhead:
                case CompiledRegex::NegativeLookAhead:
                {
                    auto ref = m_program.lookarounds.begin() + inst.param;
                    for (auto it = pos; *ref != -1 and it != m_end; ++it, ++ref)
                        if (*it != *ref)
                            break;
                    if ((inst.op == CompiledRegex::LookAhead and *ref != -1) or
                        (inst.op == CompiledRegex::NegativeLookAhead and *ref == -1))
                        return StepResult::Failed;
                    break;
                }
                case CompiledRegex::LookBehind:
                case CompiledRegex::NegativeLookBehind:
                {
                    auto ref = m_program.lookarounds.begin() + inst.param;
                    for (auto it = pos; *ref != -1 and it > m_begin; --it, ++ref)
                        if (*(it-1) != *ref)
                            break;
                    if ((inst.op == CompiledRegex::LookBehind and *ref != -1) or
                        (inst.op == CompiledRegex::NegativeLookBehind and *ref == -1))
                        return StepResult::Failed;
                    break;
                }
                case CompiledRegex::Match:
                    return StepResult::Matched;
            }
        }
        return StepResult::Failed;
    }

    bool exec_from(Utf8It pos, Saves* initial_saves, Vector<Thread>& current_threads, Vector<Thread>& next_threads)
    {
        current_threads.push_back({0, initial_saves});
        next_threads.clear();

        bool found_match = false;
        while (true) // Iterate on all codepoints and once at the end
        {
            for (auto& inst : m_program.instructions)
            {
                inst.processed = false;
                inst.scheduled = false;
            }

            while (not current_threads.empty())
            {
                auto thread = current_threads.back();
                current_threads.pop_back();
                switch (step(pos, thread, current_threads))
                {
                case StepResult::Matched:
                    if ((pos != m_end and not (m_flags & RegexExecFlags::Search)) or
                        (m_flags & RegexExecFlags::NotInitialNull and pos == m_begin))
                    {
                        release_saves(thread.saves);
                        continue;
                    }

                    release_saves(m_captures);
                    m_captures = thread.saves;
                    if (pos == m_end or (m_flags & RegexExecFlags::AnyMatch))
                        return true;

                    found_match = true;
                    current_threads.clear(); // remove this and lower priority threads
                    break;
                case StepResult::Failed:
                    release_saves(thread.saves);
                    break;
                case StepResult::Consumed:
                    if (m_program.instructions[thread.inst].scheduled)
                    {
                        release_saves(thread.saves);
                        continue;
                    }
                    m_program.instructions[thread.inst].scheduled = true;
                    next_threads.push_back(thread);
                    break;
                }
            }
            if (pos == m_end or next_threads.empty())
                return found_match;

            std::swap(current_threads, next_threads);
            std::reverse(current_threads.begin(), current_threads.end());
            ++pos;
        }
    }

    void to_next_start(Utf8It& start, const Utf8It& end, const bool* start_chars)
    {
        if (not start_chars)
            return;

        while (start != end and *start >= 0 and *start < 256 and
               not start_chars[*start])
            ++start;
    }

    bool is_line_start(const Utf8It& pos) const
    {
        if (not (m_flags & RegexExecFlags::PrevAvailable) and pos == m_begin)
            return not (m_flags & RegexExecFlags::NotBeginOfLine);
        return *(pos-1) == '\n';
    }

    bool is_line_end(const Utf8It& pos) const
    {
        if (pos == m_end)
            return not (m_flags & RegexExecFlags::NotEndOfLine);
        return *pos == '\n';
    }

    bool is_word_boundary(const Utf8It& pos) const
    {
        if (not (m_flags & RegexExecFlags::PrevAvailable) and pos == m_begin)
            return not (m_flags & RegexExecFlags::NotBeginOfWord);
        if (pos == m_end)
            return not (m_flags & RegexExecFlags::NotEndOfWord);
        return is_word(*(pos-1)) != is_word(*pos);
    }

    static const Iterator& get_base(const utf8::iterator<Iterator>& it) { return it.base(); }
    static Iterator get_base(const std::reverse_iterator<utf8::iterator<Iterator>>& it) { return it.base().base(); }

    const CompiledRegex& m_program;

    Utf8It m_begin;
    Utf8It m_end;
    RegexExecFlags m_flags;

    Vector<Saves*> m_saves;
    Vector<Saves*> m_free_saves;

    Saves* m_captures = nullptr;
};

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_match(It begin, It end, const CompiledRegex& re, RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    return vm.exec(begin, end, (RegexExecFlags)(flags & ~(RegexExecFlags::Search)) |
                               RegexExecFlags::AnyMatch | RegexExecFlags::NoSaves);
}

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_match(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                 RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    if (vm.exec(begin, end,  flags & ~(RegexExecFlags::Search)))
    {
        std::copy(vm.captures().begin(), vm.captures().end(), std::back_inserter(captures));
        return true;
    }
    return false;
}

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_search(It begin, It end, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    return vm.exec(begin, end, flags | RegexExecFlags::Search | RegexExecFlags::AnyMatch | RegexExecFlags::NoSaves);
}

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_search(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    if (vm.exec(begin, end, flags | RegexExecFlags::Search))
    {
        std::copy(vm.captures().begin(), vm.captures().end(), std::back_inserter(captures));
        return true;
    }
    return false;
}

}

#endif // regex_impl_hh_INCLUDED
<commit_msg>Regex: add elided braces to fix compilation on older gcc<commit_after>#ifndef regex_impl_hh_INCLUDED
#define regex_impl_hh_INCLUDED

#include "exception.hh"
#include "flags.hh"
#include "ref_ptr.hh"
#include "unicode.hh"
#include "utf8.hh"
#include "utf8_iterator.hh"
#include "vector.hh"

#include <string.h>

namespace Kakoune
{

enum class MatchDirection
{
    Forward,
    Backward
};

struct CompiledRegex : RefCountable
{
    enum Op : char
    {
        Match,
        Literal,
        LiteralIgnoreCase,
        AnyChar,
        Matcher,
        Jump,
        Split_PrioritizeParent,
        Split_PrioritizeChild,
        Save,
        LineStart,
        LineEnd,
        WordBoundary,
        NotWordBoundary,
        SubjectBegin,
        SubjectEnd,
        LookAhead,
        NegativeLookAhead,
        LookBehind,
        NegativeLookBehind,
    };

    struct Instruction
    {
        Op op;
        mutable bool processed;
        mutable bool scheduled;
        uint32_t param;
    };
    static_assert(sizeof(Instruction) == 8, "");

    explicit operator bool() const { return not instructions.empty(); }

    Vector<Instruction> instructions;
    Vector<std::function<bool (Codepoint)>> matchers;
    Vector<Codepoint> lookarounds;
    MatchDirection direction;
    size_t save_count;

    struct StartChars { bool map[256]; };
    std::unique_ptr<StartChars> start_chars;
};

CompiledRegex compile_regex(StringView re, MatchDirection direction = MatchDirection::Forward);

enum class RegexExecFlags
{
    None              = 0,
    Search            = 1 << 0,
    NotBeginOfLine    = 1 << 1,
    NotEndOfLine      = 1 << 2,
    NotBeginOfWord    = 1 << 3,
    NotEndOfWord      = 1 << 4,
    NotBeginOfSubject = 1 << 5,
    NotInitialNull    = 1 << 6,
    AnyMatch          = 1 << 7,
    NoSaves           = 1 << 8,
    PrevAvailable     = 1 << 9,
};

constexpr bool with_bit_ops(Meta::Type<RegexExecFlags>) { return true; }

template<typename Iterator, MatchDirection direction>
struct ChooseUtf8It
{
    using Type = utf8::iterator<Iterator>;
};

template<typename Iterator>
struct ChooseUtf8It<Iterator, MatchDirection::Backward>
{
    using Type = std::reverse_iterator<utf8::iterator<Iterator>>;
};

template<typename Iterator, MatchDirection direction>
class ThreadedRegexVM
{
public:
    ThreadedRegexVM(const CompiledRegex& program)
      : m_program{program}
      {
          kak_assert(m_program);
          if (direction != program.direction)
              throw runtime_error{"Regex and VM direction mismatch"};
      }

    ThreadedRegexVM(const ThreadedRegexVM&) = delete;
    ThreadedRegexVM& operator=(const ThreadedRegexVM&) = delete;

    ~ThreadedRegexVM()
    {
        for (auto* saves : m_saves)
        {
            for (size_t i = m_program.save_count-1; i > 0; --i)
                saves->pos[i].~Iterator();
            saves->~Saves();
            ::operator delete(saves);
        }
    }

    bool exec(Iterator begin, Iterator end, RegexExecFlags flags)
    {
        const bool forward = direction == MatchDirection::Forward;
        const bool prev_avail = flags & RegexExecFlags::PrevAvailable;
        m_begin = Utf8It{utf8::iterator<Iterator>{forward ? begin : end,
                                                  prev_avail ? begin-1 : begin, end}};
        m_end = Utf8It{utf8::iterator<Iterator>{forward ? end : begin,
                                                prev_avail ? begin-1 : begin, end}};
        m_flags = flags;

        if (flags & RegexExecFlags::NotInitialNull and m_begin == m_end)
            return false;

        Vector<Thread> current_threads, next_threads;

        const bool no_saves = (m_flags & RegexExecFlags::NoSaves);
        Utf8It start{m_begin};

        const bool* start_chars = m_program.start_chars ? m_program.start_chars->map : nullptr;

        if (flags & RegexExecFlags::Search)
            to_next_start(start, m_end, start_chars);

        if (exec_from(start, no_saves ? nullptr : new_saves<false>(nullptr),
                      current_threads, next_threads))
            return true;

        if (not (flags & RegexExecFlags::Search))
            return false;

        do
        {
            to_next_start(++start, m_end, start_chars);
            if (exec_from(start, no_saves ? nullptr : new_saves<false>(nullptr),
                          current_threads, next_threads))
                return true;
        }
        while (start != m_end);

        return false;
    }

    ArrayView<const Iterator> captures() const
    {
        if (m_captures)
            return { m_captures->pos, m_program.save_count };
        return {};
    }

private:
    struct Saves
    {
        int refcount;
        Iterator pos[1];
    };

    template<bool copy>
    Saves* new_saves(Iterator* pos)
    {
        kak_assert(not copy or pos != nullptr);
        const auto count = m_program.save_count;
        if (not m_free_saves.empty())
        {
            Saves* res = m_free_saves.back();
            m_free_saves.pop_back();
            res->refcount = 1;
            if (copy)
                std::copy(pos, pos + count, res->pos);
            else
                std::fill(res->pos, res->pos + count, Iterator{});

            return res;
        }

        void* ptr = ::operator new (sizeof(Saves) + (count-1) * sizeof(Iterator));
        Saves* saves = new (ptr) Saves{1, {copy ? pos[0] : Iterator{}}};
        for (size_t i = 1; i < count; ++i)
            new (&saves->pos[i]) Iterator{copy ? pos[i] : Iterator{}};
        m_saves.push_back(saves);
        return saves;
    }

    void release_saves(Saves* saves)
    {
        if (saves and --saves->refcount == 0)
            m_free_saves.push_back(saves);
    };

    struct Thread
    {
        uint32_t inst;
        Saves* saves;
    };

    using Utf8It = typename ChooseUtf8It<Iterator, direction>::Type;

    enum class StepResult { Consumed, Matched, Failed };

    // Steps a thread until it consumes the current character, matches or fail
    StepResult step(const Utf8It& pos, Thread& thread, Vector<Thread>& threads)
    {
        while (true)
        {
            auto& inst = m_program.instructions[thread.inst++];
            if (inst.processed)
                return StepResult::Failed;
            inst.processed = true;

            switch (inst.op)
            {
                case CompiledRegex::Literal:
                    if (pos != m_end and inst.param == *pos)
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::LiteralIgnoreCase:
                    if (pos != m_end and inst.param == to_lower(*pos))
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::AnyChar:
                    return StepResult::Consumed;
                case CompiledRegex::Jump:
                    thread.inst = inst.param;
                    break;
                case CompiledRegex::Split_PrioritizeParent:
                {
                    if (thread.saves)
                        ++thread.saves->refcount;
                    threads.push_back({inst.param, thread.saves});
                    break;
                }
                case CompiledRegex::Split_PrioritizeChild:
                {
                    if (thread.saves)
                        ++thread.saves->refcount;
                    threads.push_back({thread.inst, thread.saves});
                    thread.inst = inst.param;
                    break;
                }
                case CompiledRegex::Save:
                {
                    if (thread.saves == nullptr)
                        break;
                    if (thread.saves->refcount > 1)
                    {
                        --thread.saves->refcount;
                        thread.saves = new_saves<true>(thread.saves->pos);
                    }
                    thread.saves->pos[inst.param] = get_base(pos);
                    break;
                }
                case CompiledRegex::Matcher:
                    if (pos == m_end)
                        return StepResult::Failed;
                    return m_program.matchers[inst.param](*pos) ?
                        StepResult::Consumed : StepResult::Failed;
                case CompiledRegex::LineStart:
                    if (not is_line_start(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LineEnd:
                    if (not is_line_end(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::WordBoundary:
                    if (not is_word_boundary(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::NotWordBoundary:
                    if (is_word_boundary(pos))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectBegin:
                    if (pos != m_begin or (m_flags & RegexExecFlags::NotBeginOfSubject))
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectEnd:
                    if (pos != m_end)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LookAhead:
                case CompiledRegex::NegativeLookAhead:
                {
                    auto ref = m_program.lookarounds.begin() + inst.param;
                    for (auto it = pos; *ref != -1 and it != m_end; ++it, ++ref)
                        if (*it != *ref)
                            break;
                    if ((inst.op == CompiledRegex::LookAhead and *ref != -1) or
                        (inst.op == CompiledRegex::NegativeLookAhead and *ref == -1))
                        return StepResult::Failed;
                    break;
                }
                case CompiledRegex::LookBehind:
                case CompiledRegex::NegativeLookBehind:
                {
                    auto ref = m_program.lookarounds.begin() + inst.param;
                    for (auto it = pos; *ref != -1 and it > m_begin; --it, ++ref)
                        if (*(it-1) != *ref)
                            break;
                    if ((inst.op == CompiledRegex::LookBehind and *ref != -1) or
                        (inst.op == CompiledRegex::NegativeLookBehind and *ref == -1))
                        return StepResult::Failed;
                    break;
                }
                case CompiledRegex::Match:
                    return StepResult::Matched;
            }
        }
        return StepResult::Failed;
    }

    bool exec_from(Utf8It pos, Saves* initial_saves, Vector<Thread>& current_threads, Vector<Thread>& next_threads)
    {
        current_threads.push_back({0, initial_saves});
        next_threads.clear();

        bool found_match = false;
        while (true) // Iterate on all codepoints and once at the end
        {
            for (auto& inst : m_program.instructions)
            {
                inst.processed = false;
                inst.scheduled = false;
            }

            while (not current_threads.empty())
            {
                auto thread = current_threads.back();
                current_threads.pop_back();
                switch (step(pos, thread, current_threads))
                {
                case StepResult::Matched:
                    if ((pos != m_end and not (m_flags & RegexExecFlags::Search)) or
                        (m_flags & RegexExecFlags::NotInitialNull and pos == m_begin))
                    {
                        release_saves(thread.saves);
                        continue;
                    }

                    release_saves(m_captures);
                    m_captures = thread.saves;
                    if (pos == m_end or (m_flags & RegexExecFlags::AnyMatch))
                        return true;

                    found_match = true;
                    current_threads.clear(); // remove this and lower priority threads
                    break;
                case StepResult::Failed:
                    release_saves(thread.saves);
                    break;
                case StepResult::Consumed:
                    if (m_program.instructions[thread.inst].scheduled)
                    {
                        release_saves(thread.saves);
                        continue;
                    }
                    m_program.instructions[thread.inst].scheduled = true;
                    next_threads.push_back(thread);
                    break;
                }
            }
            if (pos == m_end or next_threads.empty())
                return found_match;

            std::swap(current_threads, next_threads);
            std::reverse(current_threads.begin(), current_threads.end());
            ++pos;
        }
    }

    void to_next_start(Utf8It& start, const Utf8It& end, const bool* start_chars)
    {
        if (not start_chars)
            return;

        while (start != end and *start >= 0 and *start < 256 and
               not start_chars[*start])
            ++start;
    }

    bool is_line_start(const Utf8It& pos) const
    {
        if (not (m_flags & RegexExecFlags::PrevAvailable) and pos == m_begin)
            return not (m_flags & RegexExecFlags::NotBeginOfLine);
        return *(pos-1) == '\n';
    }

    bool is_line_end(const Utf8It& pos) const
    {
        if (pos == m_end)
            return not (m_flags & RegexExecFlags::NotEndOfLine);
        return *pos == '\n';
    }

    bool is_word_boundary(const Utf8It& pos) const
    {
        if (not (m_flags & RegexExecFlags::PrevAvailable) and pos == m_begin)
            return not (m_flags & RegexExecFlags::NotBeginOfWord);
        if (pos == m_end)
            return not (m_flags & RegexExecFlags::NotEndOfWord);
        return is_word(*(pos-1)) != is_word(*pos);
    }

    static const Iterator& get_base(const utf8::iterator<Iterator>& it) { return it.base(); }
    static Iterator get_base(const std::reverse_iterator<utf8::iterator<Iterator>>& it) { return it.base().base(); }

    const CompiledRegex& m_program;

    Utf8It m_begin;
    Utf8It m_end;
    RegexExecFlags m_flags;

    Vector<Saves*> m_saves;
    Vector<Saves*> m_free_saves;

    Saves* m_captures = nullptr;
};

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_match(It begin, It end, const CompiledRegex& re, RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    return vm.exec(begin, end, (RegexExecFlags)(flags & ~(RegexExecFlags::Search)) |
                               RegexExecFlags::AnyMatch | RegexExecFlags::NoSaves);
}

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_match(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                 RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    if (vm.exec(begin, end,  flags & ~(RegexExecFlags::Search)))
    {
        std::copy(vm.captures().begin(), vm.captures().end(), std::back_inserter(captures));
        return true;
    }
    return false;
}

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_search(It begin, It end, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    return vm.exec(begin, end, flags | RegexExecFlags::Search | RegexExecFlags::AnyMatch | RegexExecFlags::NoSaves);
}

template<typename It, MatchDirection direction = MatchDirection::Forward>
bool regex_search(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It, direction> vm{re};
    if (vm.exec(begin, end, flags | RegexExecFlags::Search))
    {
        std::copy(vm.captures().begin(), vm.captures().end(), std::back_inserter(captures));
        return true;
    }
    return false;
}

}

#endif // regex_impl_hh_INCLUDED
<|endoftext|>


<commit_before>// Copyright (c) 2009-2017 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.

// Maintainer: jglaser

#include "MolecularForceCompute.h"

#include "hoomd/CachedAllocator.h"
#include "hoomd/Autotuner.h"

#ifdef ENABLE_CUDA
#include "MolecularForceCompute.cuh"
#endif

#include <string.h>
#include <map>

namespace py = pybind11;

/*! \file MolecularForceCompute.cc
    \brief Contains code for the MolecularForceCompute class
*/

/*! \param sysdef SystemDefinition containing the ParticleData to compute forces on
*/
MolecularForceCompute::MolecularForceCompute(std::shared_ptr<SystemDefinition> sysdef)
    : ForceConstraint(sysdef), m_molecule_tag(m_exec_conf), m_n_molecules_global(0),
      m_molecule_list(m_exec_conf), m_molecule_length(m_exec_conf), m_molecule_order(m_exec_conf),
      m_dirty(true), m_molecule_idx(m_exec_conf)
    {
    // connect to the ParticleData to recieve notifications when particles change order in memory
    m_pdata->getParticleSortSignal().connect<MolecularForceCompute, &MolecularForceCompute::setDirty>(this);

    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        // initialize autotuner
        std::vector<unsigned int> valid_params;
        for (unsigned int block_size = 32; block_size <= 1024; block_size += 32)
            valid_params.push_back(block_size);

        m_tuner_fill.reset(new Autotuner(valid_params, 5, 100000, "fill_molecule_table", this->m_exec_conf));
        }
    #endif
    }

//! Destructor
MolecularForceCompute::~MolecularForceCompute()
    {
    m_pdata->getParticleSortSignal().disconnect<MolecularForceCompute, &MolecularForceCompute::setDirty>(this);
    }

#ifdef ENABLE_CUDA
void MolecularForceCompute::initMoleculesGPU()
    {
    if (m_prof) m_prof->push(m_exec_conf,"init molecules");

    unsigned int nptl_local = m_pdata->getN() + m_pdata->getNGhosts();

    unsigned int n_local_molecules = 0;

    // maximum molecule length
    unsigned int nmax = 0;

    // number of local particles that are part of molecules
    unsigned int n_local_ptls_in_molecules = 0;

    // resize to maximum possible number of local molecules
    m_molecule_length.resize(nptl_local);
    m_molecule_idx.resize(nptl_local);

    ScopedAllocation<unsigned int> d_idx_sorted_by_tag(m_exec_conf->getCachedAllocator(), nptl_local);
    ScopedAllocation<unsigned int> d_local_molecule_tags(m_exec_conf->getCachedAllocator(), nptl_local);

        {
        ArrayHandle<unsigned int> d_molecule_tag(m_molecule_tag, access_location::device, access_mode::read);
        ArrayHandle<unsigned int> d_tag(m_pdata->getTags(), access_location::device, access_mode::read);
        ArrayHandle<unsigned int> d_molecule_length(m_molecule_length, access_location::device, access_mode::overwrite);
        ArrayHandle<unsigned int> d_molecule_idx(m_molecule_idx, access_location::device, access_mode::overwrite);

        // temporary buffers
        ScopedAllocation<unsigned int> d_local_unique_molecule_tags(m_exec_conf->getCachedAllocator(), m_n_molecules_global);
        ScopedAllocation<unsigned int> d_sorted_by_tag(m_exec_conf->getCachedAllocator(), nptl_local);

        gpu_sort_by_molecule(nptl_local,
            d_tag.data,
            d_molecule_tag.data,
            d_local_molecule_tags.data,
            d_local_unique_molecule_tags.data,
            d_molecule_idx.data,
            d_sorted_by_tag.data,
            d_idx_sorted_by_tag.data,
            d_molecule_length.data,
            n_local_molecules,
            nmax,
            n_local_ptls_in_molecules,
            m_exec_conf->getCachedAllocator());

        if (m_exec_conf->isCUDAErrorCheckingEnabled())
            CHECK_CUDA_ERROR();
        }

    // set up indexer
    m_molecule_indexer = Index2D(n_local_molecules, nmax);

    m_exec_conf->msg->notice(7) << "MolecularForceCompute: " << n_local_molecules << " molecules, "
        << n_local_ptls_in_molecules << " particles in molceules " << std::endl;

    // resize molecule list
    m_molecule_list.resize(m_molecule_indexer.getNumElements());

    // resize molecule lookup to size of local particle data
    m_molecule_order.resize(m_pdata->getMaxN());

        {
        // write out molecule list and order
        ArrayHandle<unsigned int> d_molecule_list(m_molecule_list, access_location::device, access_mode::overwrite);
        ArrayHandle<unsigned int> d_molecule_order(m_molecule_order, access_location::device, access_mode::overwrite);
        ArrayHandle<unsigned int> d_molecule_idx(m_molecule_idx, access_location::device, access_mode::read);

        m_tuner_fill->begin();
        unsigned int block_size = m_tuner_fill->getParam();

        gpu_fill_molecule_table(nptl_local,
            n_local_ptls_in_molecules,
            m_molecule_indexer,
            d_molecule_idx.data,
            d_local_molecule_tags.data,
            d_idx_sorted_by_tag.data,
            d_molecule_list.data,
            d_molecule_order.data,
            block_size,
            m_exec_conf->getCachedAllocator());

        if (m_exec_conf->isCUDAErrorCheckingEnabled())
            CHECK_CUDA_ERROR();

        m_tuner_fill->end();
        }

    if (m_prof) m_prof->pop(m_exec_conf);
    }
#endif

void MolecularForceCompute::initMolecules()
    {
    // return early if no molecules are defined
    if (!m_n_molecules_global) return;

    m_exec_conf->msg->notice(7) << "MolecularForceCompute initializing molecule table" << std::endl;

    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        initMoleculesGPU();
        return;
        }
    #endif

    if (m_prof) m_prof->push("init molecules");

    // construct local molecule table
    unsigned int nptl_local = m_pdata->getN() + m_pdata->getNGhosts();

    ArrayHandle<unsigned int> h_molecule_tag(m_molecule_tag, access_location::host, access_mode::read);
    ArrayHandle<unsigned int> h_tag(m_pdata->getTags(), access_location::host, access_mode::read);

    std::set<unsigned int> local_molecule_tags;

    unsigned int n_local_molecules = 0;

    std::vector<unsigned int> local_molecule_idx(nptl_local, NO_MOLECULE);

    // resize molecule lookup to size of local particle data
    m_molecule_order.resize(m_pdata->getMaxN());

    // sort local molecules lexicographically by molecule and by ptl tag
    std::map<unsigned int, std::set<unsigned int> > local_molecules_sorted;

    for (unsigned int iptl = 0; iptl < nptl_local; ++iptl)
        {
        unsigned int tag = h_tag.data[iptl];
        assert(tag < m_molecule_tag.getNumElements());

        unsigned int mol_tag = h_molecule_tag.data[tag];
        if (mol_tag == NO_MOLECULE) continue;

        auto it = local_molecules_sorted.find(mol_tag);
        if (it == local_molecules_sorted.end())
            {
            auto res = local_molecules_sorted.insert(std::make_pair(mol_tag,std::set<unsigned int>()));
            assert(res.second);
            it = res.first;
            }

        it->second.insert(tag);
        }

    n_local_molecules = local_molecules_sorted.size();

    m_exec_conf->msg->notice(7) << "MolecularForceCompute: " << n_local_molecules << " molecules" << std::endl;

    m_molecule_length.resize(n_local_molecules);

    ArrayHandle<unsigned int> h_molecule_length(m_molecule_length, access_location::host, access_mode::overwrite);

    // reset lengths
    for (unsigned int imol = 0; imol < n_local_molecules; ++imol)
        {
        h_molecule_length.data[imol] = 0;
        }

    // count molecule lengths
    unsigned int i = 0;
    for (auto it = local_molecules_sorted.begin(); it != local_molecules_sorted.end(); ++it)
        {
        h_molecule_length.data[i++] = it->second.size();
        }

    // find maximum length
    unsigned nmax = 0;
    for (unsigned int imol = 0; imol < n_local_molecules; ++imol)
        {
        if (h_molecule_length.data[imol] > nmax)
            {
            nmax = h_molecule_length.data[imol];
            }
        }

    // set up indexer
    m_molecule_indexer = Index2D(n_local_molecules, nmax);

    // resize molecule list
    m_molecule_list.resize(m_molecule_indexer.getNumElements());

    // reset lengths again
    for (unsigned int imol = 0; imol < n_local_molecules; ++imol)
        {
        h_molecule_length.data[imol] = 0;
        }

    // reset molecule order
    ArrayHandle<unsigned int> h_molecule_order(m_molecule_order, access_location::host, access_mode::overwrite);
    memset(h_molecule_order.data, 0, sizeof(unsigned int)*(m_pdata->getN() + m_pdata->getNGhosts()));

    // resize reverse-lookup
    m_molecule_idx.resize(nptl_local);

    // fill molecule list
    ArrayHandle<unsigned int> h_molecule_list(m_molecule_list, access_location::host, access_mode::overwrite);
    ArrayHandle<unsigned int> h_molecule_idx(m_molecule_idx, access_location::host, access_mode::overwrite);
    ArrayHandle<unsigned int> h_rtag(m_pdata->getRTags(), access_location::host, access_mode::read);

    // reset reverse lookup
    memset(h_molecule_idx.data, 0, sizeof(unsigned int)*nptl_local);

    unsigned int i_mol = 0;
    for (auto it_mol = local_molecules_sorted.begin(); it_mol != local_molecules_sorted.end(); ++it_mol)
        {
        for (std::set<unsigned int>::iterator it_tag = it_mol->second.begin(); it_tag != it_mol->second.end(); ++it_tag)
            {
            unsigned int n = h_molecule_length.data[i_mol]++;
            unsigned int ptl_idx = h_rtag.data[*it_tag];
            assert(ptl_idx < m_pdata->getN() + m_pdata->getNGhosts());
            h_molecule_list.data[m_molecule_indexer(i_mol, n)] = ptl_idx;
            h_molecule_idx.data[ptl_idx] = i_mol;
            h_molecule_order.data[ptl_idx] = n;
            }
        i_mol ++;
        }

    if (m_prof) m_prof->pop(m_exec_conf);
    }

void export_MolecularForceCompute(py::module& m)
    {
    py::class_< MolecularForceCompute, std::shared_ptr<MolecularForceCompute> >(m, "MolecularForceCompute", py::base<ForceConstraint>())
    .def(py::init< std::shared_ptr<SystemDefinition> >())
    ;
    }
<commit_msg>try splitting molecule_idx across GPUs<commit_after>// Copyright (c) 2009-2017 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.

// Maintainer: jglaser

#include "MolecularForceCompute.h"

#include "hoomd/CachedAllocator.h"
#include "hoomd/Autotuner.h"

#ifdef ENABLE_CUDA
#include "MolecularForceCompute.cuh"
#endif

#include <string.h>
#include <map>

namespace py = pybind11;

/*! \file MolecularForceCompute.cc
    \brief Contains code for the MolecularForceCompute class
*/

/*! \param sysdef SystemDefinition containing the ParticleData to compute forces on
*/
MolecularForceCompute::MolecularForceCompute(std::shared_ptr<SystemDefinition> sysdef)
    : ForceConstraint(sysdef), m_molecule_tag(m_exec_conf), m_n_molecules_global(0), m_dirty(true),
      m_molecule_list(m_exec_conf), m_molecule_length(m_exec_conf), m_molecule_order(m_exec_conf),
      m_molecule_idx(m_exec_conf)
    {
    // connect to the ParticleData to recieve notifications when particles change order in memory
    m_pdata->getParticleSortSignal().connect<MolecularForceCompute, &MolecularForceCompute::setDirty>(this);

    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        // initialize autotuner
        std::vector<unsigned int> valid_params;
        for (unsigned int block_size = 32; block_size <= 1024; block_size += 32)
            valid_params.push_back(block_size);

        m_tuner_fill.reset(new Autotuner(valid_params, 5, 100000, "fill_molecule_table", this->m_exec_conf));
        }
    #endif
    }

//! Destructor
MolecularForceCompute::~MolecularForceCompute()
    {
    m_pdata->getParticleSortSignal().disconnect<MolecularForceCompute, &MolecularForceCompute::setDirty>(this);
    }

#ifdef ENABLE_CUDA
void MolecularForceCompute::initMoleculesGPU()
    {
    if (m_prof) m_prof->push(m_exec_conf,"init molecules");

    unsigned int nptl_local = m_pdata->getN() + m_pdata->getNGhosts();

    unsigned int n_local_molecules = 0;

    // maximum molecule length
    unsigned int nmax = 0;

    // number of local particles that are part of molecules
    unsigned int n_local_ptls_in_molecules = 0;

    // resize to maximum possible number of local molecules
    m_molecule_length.resize(nptl_local);
    m_molecule_idx.resize(nptl_local);

    ScopedAllocation<unsigned int> d_idx_sorted_by_tag(m_exec_conf->getCachedAllocator(), nptl_local);
    ScopedAllocation<unsigned int> d_local_molecule_tags(m_exec_conf->getCachedAllocator(), nptl_local);

        {
        ArrayHandle<unsigned int> d_molecule_tag(m_molecule_tag, access_location::device, access_mode::read);
        ArrayHandle<unsigned int> d_tag(m_pdata->getTags(), access_location::device, access_mode::read);
        ArrayHandle<unsigned int> d_molecule_length(m_molecule_length, access_location::device, access_mode::overwrite);
        ArrayHandle<unsigned int> d_molecule_idx(m_molecule_idx, access_location::device, access_mode::overwrite);

        // temporary buffers
        ScopedAllocation<unsigned int> d_local_unique_molecule_tags(m_exec_conf->getCachedAllocator(), m_n_molecules_global);
        ScopedAllocation<unsigned int> d_sorted_by_tag(m_exec_conf->getCachedAllocator(), nptl_local);

        gpu_sort_by_molecule(nptl_local,
            d_tag.data,
            d_molecule_tag.data,
            d_local_molecule_tags.data,
            d_local_unique_molecule_tags.data,
            d_molecule_idx.data,
            d_sorted_by_tag.data,
            d_idx_sorted_by_tag.data,
            d_molecule_length.data,
            n_local_molecules,
            nmax,
            n_local_ptls_in_molecules,
            m_exec_conf->getCachedAllocator());

        if (m_exec_conf->isCUDAErrorCheckingEnabled())
            CHECK_CUDA_ERROR();
        }

    // set up indexer
    m_molecule_indexer = Index2D(n_local_molecules, nmax);

    m_exec_conf->msg->notice(7) << "MolecularForceCompute: " << n_local_molecules << " molecules, "
        << n_local_ptls_in_molecules << " particles in molceules " << std::endl;

    // resize molecule list
    m_molecule_list.resize(m_molecule_indexer.getNumElements());

    // resize molecule lookup to size of local particle data
    m_molecule_order.resize(m_pdata->getMaxN());

        {
        // write out molecule list and order
        ArrayHandle<unsigned int> d_molecule_list(m_molecule_list, access_location::device, access_mode::overwrite);
        ArrayHandle<unsigned int> d_molecule_order(m_molecule_order, access_location::device, access_mode::overwrite);
        ArrayHandle<unsigned int> d_molecule_idx(m_molecule_idx, access_location::device, access_mode::read);

        m_tuner_fill->begin();
        unsigned int block_size = m_tuner_fill->getParam();

        gpu_fill_molecule_table(nptl_local,
            n_local_ptls_in_molecules,
            m_molecule_indexer,
            d_molecule_idx.data,
            d_local_molecule_tags.data,
            d_idx_sorted_by_tag.data,
            d_molecule_list.data,
            d_molecule_order.data,
            block_size,
            m_exec_conf->getCachedAllocator());

        if (m_exec_conf->isCUDAErrorCheckingEnabled())
            CHECK_CUDA_ERROR();

        m_tuner_fill->end();
        }

    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        auto gpu_map = m_exec_conf->getGPUIds();

        if (m_exec_conf->getNumActiveGPUs() > 1)
            {
            // unset previous hints
            for (unsigned int idev = 0; idev < m_exec_conf->getNumActiveGPUs(); ++idev)
                {
                cudaDeviceProp dev_prop = m_exec_conf->getDeviceProperties(idev);

                if (!dev_prop.concurrentManagedAccess)
                    continue;

                auto range = m_pdata->getGPUPartition().getRange(idev);
                unsigned int nelem =  range.second - range.first;

                // skip if no hint set
                if (!nelem)
                    continue;

                cudaMemAdvise(m_molecule_idx.get()+range.first, sizeof(unsigned int)*nelem, cudaMemAdviseSetPreferredLocation, gpu_map[idev]);
                cudaMemPrefetchAsync(m_molecule_idx.get()+range.first, sizeof(unsigned int)*nelem, gpu_map[idev]);
                }

            CHECK_CUDA_ERROR();
            }
        }
    #endif

    if (m_prof) m_prof->pop(m_exec_conf);
    }
#endif

void MolecularForceCompute::initMolecules()
    {
    // return early if no molecules are defined
    if (!m_n_molecules_global) return;

    m_exec_conf->msg->notice(7) << "MolecularForceCompute initializing molecule table" << std::endl;

    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        initMoleculesGPU();
        return;
        }
    #endif

    if (m_prof) m_prof->push("init molecules");

    // construct local molecule table
    unsigned int nptl_local = m_pdata->getN() + m_pdata->getNGhosts();

    ArrayHandle<unsigned int> h_molecule_tag(m_molecule_tag, access_location::host, access_mode::read);
    ArrayHandle<unsigned int> h_tag(m_pdata->getTags(), access_location::host, access_mode::read);

    std::set<unsigned int> local_molecule_tags;

    unsigned int n_local_molecules = 0;

    std::vector<unsigned int> local_molecule_idx(nptl_local, NO_MOLECULE);

    // resize molecule lookup to size of local particle data
    m_molecule_order.resize(m_pdata->getMaxN());

    // sort local molecules lexicographically by molecule and by ptl tag
    std::map<unsigned int, std::set<unsigned int> > local_molecules_sorted;

    for (unsigned int iptl = 0; iptl < nptl_local; ++iptl)
        {
        unsigned int tag = h_tag.data[iptl];
        assert(tag < m_molecule_tag.getNumElements());

        unsigned int mol_tag = h_molecule_tag.data[tag];
        if (mol_tag == NO_MOLECULE) continue;

        auto it = local_molecules_sorted.find(mol_tag);
        if (it == local_molecules_sorted.end())
            {
            auto res = local_molecules_sorted.insert(std::make_pair(mol_tag,std::set<unsigned int>()));
            assert(res.second);
            it = res.first;
            }

        it->second.insert(tag);
        }

    n_local_molecules = local_molecules_sorted.size();

    m_exec_conf->msg->notice(7) << "MolecularForceCompute: " << n_local_molecules << " molecules" << std::endl;

    m_molecule_length.resize(n_local_molecules);

    ArrayHandle<unsigned int> h_molecule_length(m_molecule_length, access_location::host, access_mode::overwrite);

    // reset lengths
    for (unsigned int imol = 0; imol < n_local_molecules; ++imol)
        {
        h_molecule_length.data[imol] = 0;
        }

    // count molecule lengths
    unsigned int i = 0;
    for (auto it = local_molecules_sorted.begin(); it != local_molecules_sorted.end(); ++it)
        {
        h_molecule_length.data[i++] = it->second.size();
        }

    // find maximum length
    unsigned nmax = 0;
    for (unsigned int imol = 0; imol < n_local_molecules; ++imol)
        {
        if (h_molecule_length.data[imol] > nmax)
            {
            nmax = h_molecule_length.data[imol];
            }
        }

    // set up indexer
    m_molecule_indexer = Index2D(n_local_molecules, nmax);

    // resize molecule list
    m_molecule_list.resize(m_molecule_indexer.getNumElements());

    // reset lengths again
    for (unsigned int imol = 0; imol < n_local_molecules; ++imol)
        {
        h_molecule_length.data[imol] = 0;
        }

    // reset molecule order
    ArrayHandle<unsigned int> h_molecule_order(m_molecule_order, access_location::host, access_mode::overwrite);
    memset(h_molecule_order.data, 0, sizeof(unsigned int)*(m_pdata->getN() + m_pdata->getNGhosts()));

    // resize reverse-lookup
    m_molecule_idx.resize(nptl_local);

    // fill molecule list
    ArrayHandle<unsigned int> h_molecule_list(m_molecule_list, access_location::host, access_mode::overwrite);
    ArrayHandle<unsigned int> h_molecule_idx(m_molecule_idx, access_location::host, access_mode::overwrite);
    ArrayHandle<unsigned int> h_rtag(m_pdata->getRTags(), access_location::host, access_mode::read);

    // reset reverse lookup
    memset(h_molecule_idx.data, 0, sizeof(unsigned int)*nptl_local);

    unsigned int i_mol = 0;
    for (auto it_mol = local_molecules_sorted.begin(); it_mol != local_molecules_sorted.end(); ++it_mol)
        {
        for (std::set<unsigned int>::iterator it_tag = it_mol->second.begin(); it_tag != it_mol->second.end(); ++it_tag)
            {
            unsigned int n = h_molecule_length.data[i_mol]++;
            unsigned int ptl_idx = h_rtag.data[*it_tag];
            assert(ptl_idx < m_pdata->getN() + m_pdata->getNGhosts());
            h_molecule_list.data[m_molecule_indexer(i_mol, n)] = ptl_idx;
            h_molecule_idx.data[ptl_idx] = i_mol;
            h_molecule_order.data[ptl_idx] = n;
            }
        i_mol ++;
        }

    if (m_prof) m_prof->pop(m_exec_conf);
    }

void export_MolecularForceCompute(py::module& m)
    {
    py::class_< MolecularForceCompute, std::shared_ptr<MolecularForceCompute> >(m, "MolecularForceCompute", py::base<ForceConstraint>())
    .def(py::init< std::shared_ptr<SystemDefinition> >())
    ;
    }
<|endoftext|>


<commit_before>#include "Register/Apply.hpp"
#include "Chip/MKL27Z4.hpp"

#include "Register/Seam.hpp"
#include <iostream>
#include <sstream>
#include <iomanip>

namespace Kvasir {
	namespace Register {
		template<typename T>
		struct ExecuteSeam<T, ::Kvasir::Tag::User> : RecordActions<T> {};
	}
}

int main() {
	using namespace Kvasir::Register;
	constexpr auto w = write(Kvasir::Adc0Cfg2::AdlstsValC::v00);
	using Arg = typename std::remove_cv<decltype(w)>::type;
	static_assert(Detail::ArgsToApplyArePlausible<Arg>::value, "one of the supplied arguments is not supported");
	using namespace Kvasir::MPL;
	//unsigned a[] = { Detail::argToInt(args)... };
	using IndexedActions = TransformT < brigand::list<Arg>, BuildIndicesT<1>, Kvasir::MPL::Template < Kvasir::Register::Detail::MakeIndexedAction >> ;
	using FlattenedActions = brigand::flatten<IndexedActions>;
	using Steps = SplitT<FlattenedActions, SequencePoint>;
	using Merged = Kvasir::Register::Detail::MergeActionStepsT<Steps>;
	using Actions = brigand::flatten<Merged>;
	int i = Steps{};
	//static_assert(Kvasir::Register::Detail::ArgsToApplyArePlausible<typename std::remove_cv<decltype(w)>::type>::value, "one of the supplied arguments is not supported");
	apply(w);
	if (Kvasir::Register::actions_.front().address_ == 0x4003b00c)
		return 0;
	return 1;
}
<commit_msg>added tests<commit_after>#include "Register/Apply.hpp"
#include "Chip/MKL27Z4.hpp"

#include "Register/Seam.hpp"
#include <iostream>
#include <sstream>
#include <iomanip>

namespace Kvasir {
	namespace Register {
		template<typename T>
		struct ExecuteSeam<T, ::Kvasir::Tag::User> : RecordActions<T> {};
	}
}

#define BRIGAND_NO_BOOST_SUPPORT 1
#include "brigand\brigand.hpp"
#include <utility>

using namespace brigand;

template<typename I, typename T>
struct indexed_type {};

template<typename T, typename U, typename V>
struct divide_impl;

template<template<typename...> class L, typename... T, typename... U, template<typename...> class L2, typename... V>
struct divide_impl<L<T...>, L<U...>, L2<V...>>
{
	template<typename... A, typename... B>
	static list<list<A...>, list<B...>> f(indexed_type<T, A>*..., indexed_type<U, B>*...);
	using type = decltype(f((V*)0 ...));
};

template<typename L, typename I>
using divide = typename divide_impl<
	//make_sequence<
	//	brigand::size_t<0>,
	//	I::value>,
	//make_sequence<
	//	brigand::size_t<I::value>,
	//	(size<L>::value - I::value)>,
	list<brigand::size_t<0>, brigand::size_t<2>>,
	list<brigand::size_t<2>, brigand::size_t<3>, brigand::size_t<4>>,
	transform<
	//make_sequence<
	//	brigand::size_t<0>,
	//	size<L>::value>,
	list<brigand::size_t<0>, brigand::size_t<2>, brigand::size_t<2>, brigand::size_t<3>, brigand::size_t<4>>,
	L,
	indexed_type<_1, _2 >> >::type;

template<typename T, typename...Ts> //find T in Ts... in constant time
struct find_impl {
	using is = make_sequence<brigand::size_t<0>, sizeof...(Ts)>;
	using l = list<Ts...>;
	using zipped = transform<is, l, indexed_type<_1, _2>>;
	template<typename C>
	struct convert_me;
	template<template<typename...> class C, typename... Cs>
	struct convert_me<C<Cs...>> : Cs...{};
	template<typename I>
	static I convert(indexed_type<I, T>*) {};
	using type = decltype(convert(&std::declval<convert_me<zipped>>()));
};

using l = list<char, int, bool, int, char>;

int main()
{
	using dl = divide<l, brigand::size_t<2>>;
	using namespace Kvasir::Register;
	constexpr auto w = write(Kvasir::Adc0Cfg2::AdlstsValC::v00);
	using Arg = typename std::remove_cv<decltype(w)>::type;
	static_assert(Detail::ArgsToApplyArePlausible<Arg>::value, "one of the supplied arguments is not supported");
	using namespace Kvasir::MPL;
	//unsigned a[] = { Detail::argToInt(args)... };
	//using IndexedActions = TransformT < brigand::list<Arg>, BuildIndicesT<1>, Kvasir::MPL::Template < Kvasir::Register::Detail::MakeIndexedAction >> ;
	//using FlattenedActions = brigand::flatten<IndexedActions>;
	//using Steps = SplitT<FlattenedActions, SequencePoint>;
	//using Merged = Kvasir::Register::Detail::MergeActionStepsT<Steps>;
	//using Actions = brigand::flatten<Merged>;
	//int i = Steps{};
	//static_assert(Kvasir::Register::Detail::ArgsToApplyArePlausible<typename std::remove_cv<decltype(w)>::type>::value, "one of the supplied arguments is not supported");
	//apply(w);
	if (Kvasir::Register::actions_.front().address_ == 0x4003b00c)
		return 0;
	return 1;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. 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.
 */

#include "replicator.h"

#ifdef XAPIAND_CLUSTERING

#include "servers/discovery.h"

XapiandReplicator::XapiandReplicator(const std::shared_ptr<XapiandManager>& manager_, ev::loop_ref* ev_loop_, unsigned int ev_flags_)
	: Worker(std::move(manager_), ev_loop_, ev_flags_) {

	L_OBJ(this, "CREATED XAPIAN REPLICATOR!");
}

XapiandReplicator::~XapiandReplicator()
{
	L_OBJ(this, "DESTROYING XAPIAN REPLICATOR!");

	destroyer();

	L_OBJ(this, "DESTROYED XAPIAN REPLICATOR!");
}

void
XapiandReplicator::destroy_impl()
{
	destroyer();
}

void
XapiandReplicator::destroyer()
{
	manager()->database_pool.updated_databases.finish();
}

void
XapiandReplicator::shutdown_impl(time_t asap, time_t now)
{
	L_OBJ(this , "SHUTDOWN XAPIAN REPLICATOR! (%d %d)", asap, now);

	Worker::shutdown_impl(asap, now);

	destroyer(); // Call implementation directly, as we don't use a loop
}

void
XapiandReplicator::run()
{
	// Function that retrieves a task from a queue, runs it and deletes it
	Endpoint endpoint;
	while (manager()->database_pool.updated_databases.pop(endpoint)) {
		L_DEBUG(this, "Replicator was informed database was updated: %s", endpoint.as_string().c_str());
		on_commit(endpoint);
	}

	// Call implementation directly, as we don't use a loop. Object gets
	// detached when run() ends:

	cleanup();
}

void
XapiandReplicator::on_commit(const Endpoint &endpoint)
{
	if (auto discovery = manager()->weak_discovery.lock()) {
		discovery->send_message(
        	Discovery::Message::DB_UPDATED,
			serialise_length(endpoint.mastery_level) +  // The mastery level of the database
			serialise_string(endpoint.path) +  // The path of the index
			local_node->serialise()   // The node where the index is at
		);
	}
}

#endif
<commit_msg>XapiandReplicator: Using destroy() and detach() in shutdown_impl()<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. 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.
 */

#include "replicator.h"

#ifdef XAPIAND_CLUSTERING

#include "servers/discovery.h"

XapiandReplicator::XapiandReplicator(const std::shared_ptr<XapiandManager>& manager_, ev::loop_ref* ev_loop_, unsigned int ev_flags_)
	: Worker(std::move(manager_), ev_loop_, ev_flags_) {

	L_OBJ(this, "CREATED XAPIAN REPLICATOR!");
}

XapiandReplicator::~XapiandReplicator()
{
	L_OBJ(this, "DESTROYING XAPIAN REPLICATOR!");

	destroyer();

	L_OBJ(this, "DESTROYED XAPIAN REPLICATOR!");
}

void
XapiandReplicator::destroy_impl()
{
	destroyer();
}

void
XapiandReplicator::destroyer()
{
	manager()->database_pool.updated_databases.finish();
}

void
XapiandReplicator::shutdown_impl(time_t asap, time_t now)
{
	L_OBJ(this , "SHUTDOWN XAPIAN REPLICATOR! (%d %d)", asap, now);

	Worker::shutdown_impl(asap, now);

	destroy();

	if (now) {
		detach();
	}
}

void
XapiandReplicator::run()
{
	// Function that retrieves a task from a queue, runs it and deletes it
	Endpoint endpoint;
	while (manager()->database_pool.updated_databases.pop(endpoint)) {
		L_DEBUG(this, "Replicator was informed database was updated: %s", endpoint.as_string().c_str());
		on_commit(endpoint);
	}

	cleanup();
}

void
XapiandReplicator::on_commit(const Endpoint &endpoint)
{
	if (auto discovery = manager()->weak_discovery.lock()) {
		discovery->send_message(
        	Discovery::Message::DB_UPDATED,
			serialise_length(endpoint.mastery_level) +  // The mastery level of the database
			serialise_string(endpoint.path) +  // The path of the index
			local_node->serialise()   // The node where the index is at
		);
	}
}

#endif
<|endoftext|>


<commit_before>#include "coincontroltreewidget.h"
#include "coincontroldialog.h"

CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) :
    QTreeWidget(parent)
{

}

void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox
    {
        event->ignore();
        int COLUMN_CHECKBOX = 0;
        this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked));
    }
    else if (event->key() == Qt::Key_Escape) // press esc -> close dialog
    {
        event->ignore();
        CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget();
        coinControlDialog->done(QDialog::Accepted);
    }
    else
    {
        this->QTreeWidget::keyPressEvent(event);
    }
}<commit_msg>CoinControl "space" bug<commit_after>#include "coincontroltreewidget.h"
#include "coincontroldialog.h"

CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) :
    QTreeWidget(parent)
{

}

void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox
    {
        event->ignore();
        int COLUMN_CHECKBOX = 0;
        if(this->currentItem())
            this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked));
    }
    else if (event->key() == Qt::Key_Escape) // press esc -> close dialog
    {
        event->ignore();
        CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget();
        coinControlDialog->done(QDialog::Accepted);
    }
    else
    {
        this->QTreeWidget::keyPressEvent(event);
    }
}<|endoftext|>