From 2399813afa53dad73e69a4ba52c8e789e8250a24 Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Sat, 11 Mar 2023 14:44:59 -0600 Subject: [PATCH] Switch to string_view for some internal char handling. Add some debug validation in Jam interpreter. --- .vscode/launch.json | 2 +- .vscode/tasks.json | 25 - Jamroot.jam | 1 + src/engine/builtins.cpp | 4 +- src/engine/command.cpp | 2 +- src/engine/compile.cpp | 6 +- src/engine/config.h | 22 + src/engine/frames.cpp | 2 +- src/engine/frames.h | 23 +- src/engine/function.cpp | 156 +++++- src/engine/function.h | 61 +- src/engine/mod_jam_modules.cpp | 47 +- src/engine/mod_regex.cpp | 3 +- src/engine/mod_sequence.cpp | 2 +- src/engine/mod_sysinfo.cpp | 42 +- src/engine/mod_sysinfo.h | 7 + src/engine/regexp.cpp | 404 ++++---------- src/engine/regexp.h | 32 +- src/engine/rules.cpp | 4 +- src/engine/strview.h | 991 +++++++++++++++++++++++++++++++++ 20 files changed, 1414 insertions(+), 422 deletions(-) create mode 100644 src/engine/strview.h diff --git a/.vscode/launch.json b/.vscode/launch.json index 5b98e0231..f8b179cb6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -54,7 +54,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/.build/gcc-12/debug/cxxstd-11-iso/b2", - "args": ["-na"], + "args": ["--grep"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0b3f9bf43..e7c4fe0f7 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -98,31 +98,6 @@ } }, "group": "build" - }, - { - "label": "B2 Engine Infer", - "type": "shell", - "osx": { - "command": "CXX='infer run -- clang++' ./build.sh clang" - }, - "options": { - "cwd": "${workspaceFolder}/src/engine" - }, - "problemMatcher": { - "owner": "cpptools", - "fileLocation": [ - "relative", - "${workspaceFolder}/src/engine" - ], - "pattern": { - "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", - "file": 1, - "line": 2, - "column": 3, - "severity": 4, - "message": 5 - } - } } ] } \ No newline at end of file diff --git a/Jamroot.jam b/Jamroot.jam index e2a6d820c..48fc5332d 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -45,6 +45,7 @@ project b2 11 msvc:_CRT_SECURE_NO_WARNINGS=1 msvc:_CRT_NONSTDC_NO_DEPRECATE=1 + debug:B2_DEBUG=1 ; #| diff --git a/src/engine/builtins.cpp b/src/engine/builtins.cpp index ecae9aef0..56aa7ae78 100644 --- a/src/engine/builtins.cpp +++ b/src/engine/builtins.cpp @@ -980,7 +980,7 @@ LIST * builtin_subst( FRAME * frame, int flags ) if ( *in >= '0' && *in <= '9' ) { unsigned int const n = *in - '0'; - size_t const srclen = re_i[n].size; + size_t const srclen = re_i[n].size(); size_t const remaining = buf + BUFLEN - out; size_t const len = srclen < remaining ? srclen @@ -1030,7 +1030,7 @@ LIST * builtin_match( FRAME * frame, int flags ) { /* Find highest parameter */ int top = NSUBEXP-1; - while ( re_i[top].end() == nullptr ) top -= 1; + while ( re_i[top].empty() ) top -= 1; /* And add all parameters up to highest onto list. */ /* Must have parameters to have results! */ for ( int i = 1; i <= top ; ++i ) diff --git a/src/engine/command.cpp b/src/engine/command.cpp index bd308b842..0c33359a3 100644 --- a/src/engine/command.cpp +++ b/src/engine/command.cpp @@ -80,7 +80,7 @@ CMD * cmd_new( RULE * rule, LIST * targets, LIST * sources, LIST * shell ) string_new( cmd->buf ); frame_init( frame ); - frame->module = rule->module; + frame->module = b2::ensure_valid(rule->module); lol_init( frame->args ); lol_add( frame->args, list_copy( targets ) ); lol_add( frame->args, list_copy( sources ) ); diff --git a/src/engine/compile.cpp b/src/engine/compile.cpp index 1a033a564..44ed322b1 100644 --- a/src/engine/compile.cpp +++ b/src/engine/compile.cpp @@ -90,7 +90,7 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame ) if ( rule->procedure && rule->module != prev_module ) { /* Propagate current module to nested rule invocations. */ - frame->module = rule->module; + frame->module = b2::ensure_valid(rule->module); } /* Record current rule name in frame. */ @@ -180,7 +180,7 @@ LIST * call_rule( OBJECT * rulename, FRAME * caller_frame, LOL * args ) inner->prev_user = caller_frame->module->user_module ? caller_frame : caller_frame->prev_user; - inner->module = caller_frame->module; + inner->module = b2::ensure_valid(caller_frame->module); for ( int32_t a = 0; a < args->count; ++a) { @@ -259,7 +259,7 @@ LIST * call_member_rule( inner->prev = caller_frame; inner->prev_user = caller_frame->module->user_module ? caller_frame : caller_frame->prev_user; - inner->module = caller_frame->module; + inner->module = b2::ensure_valid(caller_frame->module); args.swap( inner->args[0] ); diff --git a/src/engine/config.h b/src/engine/config.h index ea34e05e8..b152bd96b 100644 --- a/src/engine/config.h +++ b/src/engine/config.h @@ -75,5 +75,27 @@ typedef long int32_t; #define B2_USE_STD_THREADS 1 #endif +#include + +namespace b2 { + +inline bool ensure(bool v) +{ +#if defined(B2_DEBUG) && B2_DEBUG + assert(v); +#endif + return v; +} + +template +T * ensure_valid(T * v) +{ +#if defined(B2_DEBUG) && B2_DEBUG + assert(v != nullptr); +#endif + return v; +} + +} // namespace b2 #endif diff --git a/src/engine/frames.cpp b/src/engine/frames.cpp index b64d54fa7..e59e31892 100644 --- a/src/engine/frames.cpp +++ b/src/engine/frames.cpp @@ -16,7 +16,7 @@ void frame_init( FRAME * frame ) frame->prev = 0; frame->prev_user = 0; lol_init( frame->args ); - frame->module = root_module(); + frame->module = b2::ensure_valid(root_module()); frame->rulename = "module scope"; frame->file = 0; frame->line = -1; diff --git a/src/engine/frames.h b/src/engine/frames.h index ea6e69f8d..8904eefc9 100644 --- a/src/engine/frames.h +++ b/src/engine/frames.h @@ -66,13 +66,13 @@ namespace b2 { namespace jam { struct module_scope { inline module_scope(FRAME * frame_, const char * module_name_) - : module_name(module_name_) - , module_frame(frame_) + : module_frame(frame_) { if (module_frame) { module_saved = module_frame->module; - module_frame->module = bindmodule(module_name); + module_frame->module + = b2::ensure_valid(bindmodule(value_ref(module_name_))); } } @@ -80,20 +80,31 @@ struct module_scope { if (module_frame && module_saved) { - module_frame->module = module_saved; + module_frame->module = b2::ensure_valid(module_saved); } } - inline const value_ref & name() const { return module_name; } + inline value_ref name() const { return module_frame->module->name; } inline FRAME * frame() const { return module_frame; } inline operator module_ptr() const { return module_frame->module; } private: - value_ref module_name; FRAME * module_frame = nullptr; module_t * module_saved = nullptr; }; +struct module_scope_in_function +{ + module_scope_in_function(FRAME * frame_, const char * module_name_); + ~module_scope_in_function(); + inline value_ref name() const { return module_frame->module->name; } + inline FRAME * frame() const { return module_frame; } + inline operator module_ptr() const { return module_frame->module; } + + private: + FRAME * module_frame = nullptr; +}; + }} // namespace b2::jam #endif diff --git a/src/engine/function.cpp b/src/engine/function.cpp index 50b2c02a9..c13819f47 100644 --- a/src/engine/function.cpp +++ b/src/engine/function.cpp @@ -24,6 +24,7 @@ #include "variable.h" #include "output.h" #include "startup.h" +#include "mod_sysinfo.h" #include #include @@ -267,9 +268,14 @@ struct _stack _stack() { + // The LIFO stack goes from start end (high/right) to (low/left). + // Byte size of the function stack. int32_t const size = 1 << 21; + // The beginning of the stack memory. start = BJAM_MALLOC( size ); + // The current past-end/tail of the stack memory. end = (char *)start + size; + // The next available stack location. data = end; } @@ -277,10 +283,12 @@ struct _stack { if ( cleanups_size > cleanups_t::size_type(0) ) { + // Call any left over cleanup functions to deallocate items that + // reference resources. while ( cleanups_size > 0 ) { cleanups_size -= 1; - cleanups[cleanups_size]( this ); + cleanups[cleanups_size].clean( this ); } } BJAM_FREE( start ); @@ -305,28 +313,39 @@ struct _stack // Move "v" to a new slot in ther stack. Returns a reference to the new item. template - remove_cref_t & push( T v ); + remove_cref_t & push( T v, FRAME * frame = nullptr ); // Copy "v" into "n" new items at the top of the stack. Returns a pointer - // to the first, i.e. top most, new item. + // to the first, i.e. top/front most, new item. template - remove_cref_t * push( T v, int32_t n ); + remove_cref_t * push( T v, int32_t n, FRAME * frame = nullptr ); - // Removes the top most "T" item from the stack and returns a copy of it. + // Removes the top/left most "T" item from the stack and returns a copy of + // it. template remove_cref_t pop() { using U = remove_cref_t; + // Copy the top/left item. U result = top(); + // Remove the obsolete top item. pop( 1 ); return result; } - // Removes the top "n" "T" items from the stack. + // Removes the top/left "n" "T" items from the stack. template void pop( int32_t n ) { + // Debugging validation. + using U = remove_cref_t; + for (auto c = 0; c < n; ++c) + cleanups[cleanups_size-c-1].check().~cleanup_t(); + // The removal of the n items happens by skipping the stack pointer + // past the n items. set_data( nth( n ) ); + // We also "skip" the cleanups. As the caller has dealt with that in + // some form. cleanups_size -= n; } @@ -336,7 +355,42 @@ struct _stack void * end = nullptr; void * data = nullptr; using cleanup_f = void(*)( _stack* ); - using cleanups_t = std::array; + struct cleanup_t + { + cleanup_f clean = nullptr; + #if defined(B2_DEBUG) && B2_DEBUG + const char * type_name = nullptr; + std::string native_stack; + std::string jam_stack; + #endif + inline cleanup_t() = default; + template + inline cleanup_t(cleanup_f f, FRAME * frame, T*_) : clean(f) + { + #if defined(B2_DEBUG) && B2_DEBUG + type_name = typeid(T).name(); + native_stack = b2::stacktrace::to_string(); + jam_stack = b2::jam::backtrace::to_string(frame); + #endif + } + inline ~cleanup_t() + { + #if defined(B2_DEBUG) && B2_DEBUG + clean = nullptr; + type_name = nullptr; + #endif + } + template + inline cleanup_t & check() + { + #if defined(B2_DEBUG) && B2_DEBUG + static const char * type_name_c = typeid(T).name(); + b2::ensure(type_name_c == this->type_name); + #endif + return *this; + } + }; + using cleanups_t = std::array; cleanups_t cleanups; cleanups_t::size_type cleanups_size = 0; @@ -359,6 +413,7 @@ struct _stack return data; } + // Get a pointer from the current top/left onward to the nth item. template remove_cref_t * nth( int32_t n ) { @@ -405,7 +460,7 @@ struct _stack } template - void cleanup_push(int32_t n, T*_ = nullptr); + void cleanup_push(int32_t n, FRAME * frame, T*_ = nullptr); }; template <> @@ -416,25 +471,27 @@ void _stack::cleanup_item(_stack * s, LIST**) } template -remove_cref_t & _stack::push( T v ) +remove_cref_t & _stack::push( T v, FRAME * frame ) { - return *push( v, 1 ); + return *push( v, 1, frame ); } template -remove_cref_t * _stack::push( T v, int32_t n ) +remove_cref_t * _stack::push( T v, int32_t n, FRAME * frame ) { using U = remove_cref_t; set_data( nth( -n ) ); std::uninitialized_fill_n( nth( 0 ), n, v ); - cleanup_push( n ); + cleanup_push( n, frame ); return nth( 0 ); } template -void _stack::cleanup_push( int32_t n, T*_ ) +void _stack::cleanup_push( int32_t n, FRAME * frame, T*_ ) { - std::uninitialized_fill_n( &cleanups[cleanups_size], n, (cleanup_f)&_stack::cleanup_item ); + using U = remove_cref_t; + cleanup_t c((cleanup_f)&_stack::cleanup_item, frame, (U*)(_)); + std::uninitialized_fill_n( &cleanups[cleanups_size], n, c ); cleanups_size += n; } @@ -958,7 +1015,7 @@ static int32_t expand_modifiers( STACK * s, int32_t n ) iter[ i ] = list_begin( args[ i ] ); } } - s->pop( n ); + s->pop( n ); } return total; } @@ -3416,12 +3473,12 @@ void argument_list_push( struct arg_list * formal, int32_t formal_count, { LIST * * const old = &frame->module->fixed_variables[ formal_arg->index ]; - s->push( *old ); + s->push( *old, frame ); *old = value.release(); } else s->push( var_swap( frame->module, formal_arg->arg_name, - value.release() ) ); + value.release() ), frame ); } if ( actual_iter != actual_end ) @@ -4562,7 +4619,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame ) PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_LOCAL); LIST * value = s->pop(); s->push( function_swap_variable( function, frame, code->arg, - value ) ); + value ), frame ); PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_LOCAL); break; } @@ -5149,10 +5206,10 @@ LIST * function_run( FUNCTION * function_, FRAME * frame ) LIST * const module_name = s->pop(); module_t * outer_module = frame->module; frame->module = !list_empty( module_name ) - ? bindmodule( list_front( module_name ) ) - : root_module(); + ? b2::ensure_valid(bindmodule( list_front( module_name ) )) + : b2::ensure_valid(root_module()); list_free( module_name ); - s->push( outer_module ); + s->push( b2::ensure_valid(outer_module) ); PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_MODULE); break; } @@ -5160,7 +5217,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame ) case INSTR_POP_MODULE: { PROFILE_ENTER_LOCAL(function_run_INSTR_POP_MODULE); - frame->module = s->pop(); + frame->module = b2::ensure_valid(s->pop()); PROFILE_EXIT_LOCAL(function_run_INSTR_POP_MODULE); break; } @@ -5173,10 +5230,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame ) OBJECT * class_module = make_class_module( name, bases ); module_t * outer_module = frame->module; - frame->module = bindmodule( class_module ); + // out_printf("function_run: OUTER-CLASS = %s, %s\n", + // outer_module->name ? outer_module->name->str() : "<>", + // class_module->str()); + frame->module = b2::ensure_valid(bindmodule( class_module )); object_free( class_module ); - s->push( outer_module ); + s->push( b2::ensure_valid(outer_module) ); PROFILE_EXIT_LOCAL(function_run_INSTR_CLASS); break; } @@ -5254,3 +5314,51 @@ void function_done( void ) { stack_global()->done(); } + +b2::jam::module_scope_in_function::module_scope_in_function( + FRAME * frame_, const char * module_name_) + : module_frame(frame_) +{ + auto stack = stack_global(); + stack->push(b2::ensure_valid(module_frame->module)); + module_frame->module = module_name_ == nullptr + ? b2::ensure_valid(root_module()) + : b2::ensure_valid(bindmodule(value_ref(module_name_))); +} + +b2::jam::module_scope_in_function::~module_scope_in_function() +{ + auto stack = stack_global(); + module_frame->module = b2::ensure_valid(stack->pop()); +} + +std::string b2::jam::backtrace::to_string(frame * f) +{ + std::string result; + for (auto i : backtrace::to_list(f)) + { + if (!result.empty()) result += "\n"; + result += i->str(); + } + return result; +} + +b2::list_ref b2::jam::backtrace::to_list(frame * f) +{ + b2::list_ref result; + for (; f != nullptr; f = f->prev) + { + std::string line; + if (f->module->name != nullptr) + { + line += f->module->name->str(); + line += "."; + } + line += f->rulename == nullptr ? "???" : f->rulename; + line += " at "; + line += f->file == nullptr ? "(builtin)" : f->file->str(); + line += ":" + std::to_string(f->line); + result.push_back(line); + } + return result; +} diff --git a/src/engine/function.h b/src/engine/function.h index 955b76fdc..879838c0e 100644 --- a/src/engine/function.h +++ b/src/engine/function.h @@ -1,45 +1,60 @@ /* - * Copyright 2022 René Ferdinand Rivera Morell + * Copyright 2022-2023 René Ferdinand Rivera Morell * Copyright 2011 Steven Watanabe * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) + * (See accompanying file LICENSE.txt or + * https://www.bfgroup.xyz/b2/LICENSE.txt) */ -#ifndef FUNCTION_SW20111123_H -#define FUNCTION_SW20111123_H +#ifndef B2_FUNCTION_H +#define B2_FUNCTION_H #include "config.h" -#include "object.h" #include "frames.h" -#include "lists.h" -#include "parse.h" #include "jam_strings.h" +#include "lists.h" +#include "object.h" +#include "parse.h" #include +#include typedef struct _function FUNCTION; -typedef std::function function_builtin_t; +typedef std::function function_builtin_t; -typedef FUNCTION* function_ptr; +typedef FUNCTION * function_ptr; -FUNCTION * function_compile( PARSE * parse ); -FUNCTION * function_builtin( function_builtin_t func, int32_t flags, const char * * args ); -void function_refer( FUNCTION * ); -void function_free( FUNCTION * ); -OBJECT * function_rulename( FUNCTION * ); -void function_set_rulename( FUNCTION *, OBJECT * ); -void function_location( FUNCTION *, OBJECT * *, int32_t * ); -LIST * function_run( FUNCTION * function, FRAME * frame ); +FUNCTION * function_compile(PARSE * parse); +FUNCTION * function_builtin( + function_builtin_t func, int32_t flags, const char ** args); +void function_refer(FUNCTION *); +void function_free(FUNCTION *); +OBJECT * function_rulename(FUNCTION *); +void function_set_rulename(FUNCTION *, OBJECT *); +void function_location(FUNCTION *, OBJECT **, int32_t *); +LIST * function_run(FUNCTION * function, FRAME * frame); -FUNCTION * function_compile_actions( const char * actions, OBJECT * file, int32_t line ); -void function_run_actions( FUNCTION * function, FRAME * frame, string * out ); +FUNCTION * function_compile_actions( + const char * actions, OBJECT * file, int32_t line); +void function_run_actions(FUNCTION * function, FRAME * frame, string * out); -FUNCTION * function_bind_variables( FUNCTION * f, module_t * module, int32_t * counter ); -FUNCTION * function_unbind_variables( FUNCTION * f ); +FUNCTION * function_bind_variables( + FUNCTION * f, module_t * module, int32_t * counter); +FUNCTION * function_unbind_variables(FUNCTION * f); -LIST * function_get_variables( FUNCTION * f ); +LIST * function_get_variables(FUNCTION * f); -void function_done( void ); +void function_done(void); + +namespace b2 { namespace jam { + +struct backtrace +{ + static std::string to_string(frame * f); + static list_ref to_list(frame * f); +}; + +}} // namespace b2::jam #endif diff --git a/src/engine/mod_jam_modules.cpp b/src/engine/mod_jam_modules.cpp index 070e91789..03c3daec4 100644 --- a/src/engine/mod_jam_modules.cpp +++ b/src/engine/mod_jam_modules.cpp @@ -18,6 +18,8 @@ Distributed under the Boost Software License, Version 1.0. #include "mod_path.h" +#include + namespace b2 { namespace jam { namespace modules { list_ref binding(std::string module_name) @@ -194,15 +196,18 @@ void load(value_ref module_name, // Set the binding rule. module_target_r.on_set("BINDRULE", "modules.record-binding"); // Do the load, in the module. - module_scope load_scope(context.frame, module_name->str()); - parse_include(module_target, load_scope.frame()); - // Allow the module to see its own names with full - // qualification. - list_ref to_import(module_rules(load_scope), true); - for (auto rule : to_import) { - import_rule(find_rule(rule, load_scope), load_scope, - module_name + "." + rule); + module_scope_in_function in_module( + context.frame, module_name->str()); + parse_include(module_target, in_module.frame()); + // Allow the module to see its own names with full + // qualification. + list_ref to_import(module_rules(in_module), true); + for (auto rule : to_import) + { + import_rule(find_rule(rule, in_module), in_module, + module_name + "." + rule); + } } } // Did the include succeed? @@ -251,6 +256,14 @@ void load(value_ref module_name, } } +namespace detail { +value_ref caller_module(FRAME * frame, int levels = 0) +{ + for (; levels >= 0; --levels) frame = frame->prev ? frame->prev : frame; + return frame->module == root_module() ? nullptr : frame->module->name; +} +} // namespace detail + void import(list_cref module_names, list_cref rules_opt, list_cref rename_opt, @@ -278,11 +291,7 @@ void import(list_cref module_names, + "When loading multiple modules, no specific rules or renaming is allowed."); } - FRAME * caller_frame - = context.frame->prev ? context.frame->prev : context.frame; - value_ref caller_module = caller_frame->module == root_module() - ? nullptr - : caller_frame->module->name; + value_ref caller_module = detail::caller_module(context.frame); // Import each specified module for (value_ref m : module_names) @@ -324,7 +333,19 @@ void import(list_cref module_names, } if (!(*loaded_v).contains(module_basename)) + { + auto grand_caller = detail::caller_module(context.frame, 1); + out_printf( + "b2::jam::modules::import: GRAND-CALLER-CURRENT-NEXT modules = %s, %s, %s, %s\n", + grand_caller.has_value() ? grand_caller->str() : "<>", + caller_module.has_value() ? caller_module->str() : "<>", + context.frame->module->name ? context.frame->module->name->str() + : "", + module_basename.c_str()); load(module_basename, value_ref(), list_cref(*search), context_ref); + // run_rule(context.frame, "modules.load", + // list_ref(module_basename)); + } else if (!(*tested_v).contains(m)) { // Native modules are pre-loaded but can be untested. In that case diff --git a/src/engine/mod_regex.cpp b/src/engine/mod_regex.cpp index f94f28ddc..d9bb94367 100644 --- a/src/engine/mod_regex.cpp +++ b/src/engine/mod_regex.cpp @@ -304,8 +304,7 @@ struct regex_grep_task { if (text_grep_result_expressions.get(i)) { - std::string m(grep_i[i].str, grep_i[i].size); - result->push_back(m); + result->push_back(grep_i[i]); } } } diff --git a/src/engine/mod_sequence.cpp b/src/engine/mod_sequence.cpp index eec1e68df..f7caa50a6 100644 --- a/src/engine/mod_sequence.cpp +++ b/src/engine/mod_sequence.cpp @@ -70,7 +70,7 @@ LIST * sequence_transform( FRAME * frame, int flags ) frame_init( inner ); inner->prev = frame; inner->prev_user = frame->prev_user; - inner->module = frame->prev->module; + inner->module = b2::ensure_valid(frame->prev->module); lol_add( inner->args, list_push_back( list_copy_range( function, args_begin, args_end ), object_copy( list_item( iter ) ) ) ); result = list_append( result, evaluate_rule( rule, function_name, inner ) ); diff --git a/src/engine/mod_sysinfo.cpp b/src/engine/mod_sysinfo.cpp index da8add8f2..3fef7c10d 100644 --- a/src/engine/mod_sysinfo.cpp +++ b/src/engine/mod_sysinfo.cpp @@ -11,23 +11,28 @@ Distributed under the Boost Software License, Version 1.0. #include #if defined(OS_MACOSX) -# include -# include +#include +#include #endif #if !defined(OS_NT) -# include +#include #else -# include +#include #endif #if defined(OS_LINUX) || defined(__GLIBC__) // Need to define this in case it's not as that's the only way to get the // sched_* APIs. -# ifndef _GNU_SOURCE -# define _GNU_SOURCE -# endif -# include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#endif + +#if defined(OS_LINUX) && defined(__GLIBC__) +// For backtrace_symbols() call. +#include #endif b2::system_info::system_info() {} @@ -164,3 +169,24 @@ unsigned int b2::system_info::cpu_thread_count() } return cpu_thread_count_; } + +std::string b2::stacktrace::to_string() +{ + std::string result; + +#if defined(OS_LINUX) && defined(__GLIBC__) + ; + void * frame_buffer[2048]; + auto frame_count = ::backtrace(frame_buffer, 2048); + if (auto frame_strings = ::backtrace_symbols(frame_buffer, frame_count)) + { + for (decltype(frame_count) i = 0; i < frame_count; ++i) + { + if (i > 0) result += "\n"; + result += frame_strings[i]; + } + } +#endif + + return result; +} \ No newline at end of file diff --git a/src/engine/mod_sysinfo.h b/src/engine/mod_sysinfo.h index af1748a0d..3db647602 100644 --- a/src/engine/mod_sysinfo.h +++ b/src/engine/mod_sysinfo.h @@ -12,6 +12,8 @@ Distributed under the Boost Software License, Version 1.0. #include "bind.h" #include "value.h" +#include + namespace b2 { /* @@ -44,6 +46,11 @@ class system_info : public object unsigned int cpu_thread_count_ = 0; }; +struct stacktrace +{ + static std::string to_string(); +}; + struct sysinfo_module : b2::bind::module_ { const char * module_name = "sysinfo"; diff --git a/src/engine/regexp.cpp b/src/engine/regexp.cpp index 9520f1af0..195d5e452 100644 --- a/src/engine/regexp.cpp +++ b/src/engine/regexp.cpp @@ -48,6 +48,7 @@ #include "regexp.h" #include "jam.h" #include "output.h" +#include "strview.h" #include #include @@ -73,7 +74,7 @@ void regerror(char const * s); * The "internal use only" fields in regexp.h are present to pass info from * compile to execute that permits the execute phase to run lots faster on * simple cases. They are: - : + * * regstart char that must begin a match; '\0' if none obvious. * reganch is the match anchored (at beginning-of-line only)? * regmust string (pointer into program) that match must include, or NULL. @@ -186,10 +187,6 @@ return (NULL); \ #define SPSTART 04 /* Starts with * or +. */ #define WORST 0 /* Worst case. */ -#ifndef STATIC -#define STATIC static -#endif - namespace { char regdummy = 0; /* @@ -215,6 +212,7 @@ inline C * regnext(C * p) // The compiled regex program to match with. struct regex_prog { + std::string regexpr; /* The not-compiled regex. */ char regstart = 0; /* Internal use only. */ char reganch = 0; /* Internal use only. */ const char * regmust = nullptr; /* Internal use only. */ @@ -281,8 +279,10 @@ struct compiler /* Allocate space. */ r = (regex_prog *)BJAM_MALLOC(sizeof(regex_prog) + regsize); - r->progsize = regsize; if (r == NULL) FAIL("out of space"); + b2::jam::ctor_ptr(r); + r->regexpr = exp; + r->progsize = regsize; /* Second pass: emit code. */ regparse = (char *)exp; @@ -814,114 +814,72 @@ struct executor /* * Global work variables for regexec(). */ - const char * reginput; /* String-input pointer. */ - const char * regbol; /* Beginning of input, for ^ check. */ - const char ** regstartp; /* Pointer to startp array. */ - const char ** regendp; /* Ditto for endp. */ - -#ifdef DEBUG - int32_t regnarrate = 0; - void regdump(); - STATIC char * regprop(); -#endif + string_view reg_in; /* String-input. */ + const char * reg_bol; /* Beginning of input, for ^ check. */ /* - regexec - match a regexp against a string */ - int32_t regexec( - const regex_prog * prog, regex_expr * expr, const char * string) + bool regexec( + const regex_prog & prog, regex_expr & expr, const string_view & string) { - char * s; - - /* Be paranoid... */ - if (prog == NULL) - { - regerror("NULL parameter"); - return (0); - } - if (string == NULL) - { - regerror("NULL string"); - return (0); - } - + string_view s; /* Check validity of program. */ - if (UCHARAT(prog->program) != MAGIC) + if (UCHARAT(prog.program) != MAGIC) { regerror("corrupted program"); - return (0); + return false; } /* If there is a "must appear" string, look for it. */ - if (prog->regmust != NULL) - { - s = (char *)string; - while ((s = strchr(s, prog->regmust[0])) != NULL) - { - if (!strncmp(s, prog->regmust, prog->regmlen)) - break; /* Found it. */ - ++s; - } - if (s == NULL) /* Not present. */ - return 0; - } + if (prog.regmust != NULL + && string.find(prog.regmust, 0, prog.regmlen) == string_view::npos) + return false; /* Not present. */ /* Mark beginning of line for ^ . */ - regbol = (char *)string; + reg_bol = string.begin(); /* Simplest case: anchored match need be tried only once. */ - if (prog->reganch) return regtry(prog, expr, string); + if (prog.reganch) return regtry(prog, expr, string); /* Messy cases: unanchored match. */ - s = (char *)string; - if (prog->regstart != '\0') /* We know what char it must start with. */ - while ((s = strchr(s, prog->regstart)) != NULL) + s = string; + if (prog.regstart != '\0') /* We know what char it must start with. */ + for (auto j = s.find(prog.regstart); j != string_view::npos; + j = s.find(prog.regstart)) { - if (regtry(prog, expr, s)) return (1); - s++; + s = s.substr(j); + if (regtry(prog, expr, s)) return true; + s = s.substr(1); } else /* We do not -- general case. */ do { - if (regtry(prog, expr, s)) return (1); + if (regtry(prog, expr, s)) return true; } - while (*s++ != '\0'); + while (!(s = s.substr(1)).empty()); /* Failure. */ - return 0; + return false; } /* * regtry() - try match at specific point. + * success == true */ - int32_t /* 0 failure, 1 success */ - regtry(const regex_prog * prog, regex_expr * expr, const char * string) + inline bool regtry( + const regex_prog & prog, regex_expr & expr, const string_view & string) { - int32_t i; - const char ** sp; - const char ** ep; - - reginput = string; - regstartp = expr->startp; - regendp = expr->endp; - - sp = expr->startp; - ep = expr->endp; - for (i = NSUBEXP; i > 0; --i) + reg_in = string; + expr = regex_expr {}; + if (regmatch(prog.program + 1, expr)) { - *sp++ = NULL; - *ep++ = NULL; + expr.sub[0] = string_view(string.begin(), reg_in.begin()); + return true; } - if (regmatch(prog->program + 1)) - { - expr->startp[0] = string; - expr->endp[0] = reginput; - return 1; - } - else - return 0; + return false; } /* @@ -934,47 +892,45 @@ struct executor * whether the rest of the match failed) by a loop instead of by recursion. */ - int32_t /* 0 failure, 1 success */ - regmatch(const char * prog) + bool regmatch(const char * prog, regex_expr & expr) { const char * scan; /* Current node. */ const char * next; /* Next node. */ scan = prog; -#ifdef DEBUG - if (scan != NULL && regnarrate) err_printf("%s(\n", regprop(scan)); -#endif while (scan != NULL) { -#ifdef DEBUG - if (regnarrate) err_printf("%s...\n", regprop(scan)); -#endif next = regnext(scan); switch (OP(scan)) { case BOL: - if (reginput != regbol) return (0); + if (reg_in.begin() != reg_bol) return false; break; case EOL: - if (*reginput != '\0') return (0); + if (!reg_in.empty()) return false; break; case WORDA: /* Must be looking at a letter, digit, or _ */ - if ((!isalnum(*reginput)) && *reginput != '_') return (0); + if (!reg_in.empty() && (!isalnum(reg_in[0])) + && reg_in[0] != '_') + return false; /* Prev must be BOL or nonword */ - if (reginput > regbol - && (isalnum(reginput[-1]) || reginput[-1] == '_')) - return (0); + if (reg_in.begin() > reg_bol + && (isalnum(reg_in.begin()[-1]) + || reg_in.begin()[-1] == '_')) + return false; break; case WORDZ: /* Must be looking at non letter, digit, or _ */ - if (isalnum(*reginput) || *reginput == '_') return (0); + if (reg_in.empty() || isalnum(reg_in[0]) + || reg_in[0] == '_') + return false; /* We don't care what the previous char was */ break; case ANY: - if (*reginput == '\0') return (0); - reginput++; + if (reg_in.empty()) return false; + reg_in = reg_in.substr(1); break; case EXACTLY: { @@ -983,24 +939,24 @@ struct executor opnd = OPERAND(scan); /* Inline the first character, for speed. */ - if (*opnd != *reginput) return (0); + if (reg_in.empty() || *opnd != reg_in[0]) return false; len = strlen(opnd); - if (len > 1 && strncmp(opnd, reginput, len) != 0) - return (0); - reginput += len; + if (len > 1 && reg_in.compare(0, len, opnd) != 0) + return false; + reg_in = reg_in.substr(len); } break; case ANYOF: - if (*reginput == '\0' - || strchr(OPERAND(scan), *reginput) == NULL) - return (0); - reginput++; + if (reg_in.empty() + || strchr(OPERAND(scan), reg_in[0]) == NULL) + return false; + reg_in = reg_in.substr(1); break; case ANYBUT: - if (*reginput == '\0' - || strchr(OPERAND(scan), *reginput) != NULL) - return (0); - reginput++; + if (reg_in.empty() + || strchr(OPERAND(scan), reg_in[0]) != NULL) + return false; + reg_in = reg_in.substr(1); break; case NOTHING: break; case BACK: break; @@ -1015,23 +971,23 @@ struct executor case OPEN + 9: { int32_t no; - const char * save; no = OP(scan) - OPEN; - save = reginput; + auto save = reg_in; - if (regmatch(next)) + if (regmatch(next, expr)) { /* * Don't set startp if some later * invocation of the same parentheses * already has. */ - if (regstartp[no] == NULL) regstartp[no] = save; - return (1); + if (expr.sub[no].begin() == nullptr) + expr.sub[no] = string_view(save.begin(), 0); + return true; } else - return (0); + return false; } break; case CLOSE + 1: @@ -1045,42 +1001,41 @@ struct executor case CLOSE + 9: { int32_t no; - const char * save; no = OP(scan) - CLOSE; - save = reginput; + auto save = reg_in; - if (regmatch(next)) + if (regmatch(next, expr)) { /* * Don't set endp if some later * invocation of the same parentheses * already has. */ - if (regendp[no] == NULL) regendp[no] = save; - return (1); + if (expr.sub[no].begin() && expr.sub[no].empty()) + expr.sub[no] = string_view( + expr.sub[no].begin(), save.begin()); + return true; } else - return (0); + return false; } break; case BRANCH: { - const char * save; - if (OP(next) != BRANCH) /* No choice. */ next = OPERAND(scan); /* Avoid recursion. */ else { do { - save = reginput; - if (regmatch(OPERAND(scan))) return (1); - reginput = save; + auto save = reg_in; + if (regmatch(OPERAND(scan), expr)) return true; + reg_in = save; scan = regnext(scan); } while (scan != NULL && OP(scan) == BRANCH); - return (0); + return false; /* NOTREACHED */ } } @@ -1090,7 +1045,6 @@ struct executor { char nextch; int32_t no; - const char * save; int32_t min; /* @@ -1100,26 +1054,26 @@ struct executor nextch = '\0'; if (OP(next) == EXACTLY) nextch = *OPERAND(next); min = (OP(scan) == STAR) ? 0 : 1; - save = reginput; + auto save = reg_in; no = regrepeat(OPERAND(scan)); while (no >= min) { /* If it could work, try it. */ - if (nextch == '\0' || *reginput == nextch) - if (regmatch(next)) return (1); + if (nextch == '\0' || reg_in[0] == nextch) + if (regmatch(next, expr)) return true; /* Couldn't or didn't -- back up. */ no--; - reginput = save + no; + reg_in = save.substr(no); } - return (0); + return false; } break; case END: - return (1); /* Success! */ + return true; /* Success! */ break; default: regerror("memory corruption"); - return (0); + return false; break; } @@ -1131,7 +1085,7 @@ struct executor * the terminating point. */ regerror("corrupted pointers"); - return (0); + return false; } /* @@ -1140,36 +1094,35 @@ struct executor int32_t regrepeat(const char * p) { int32_t count = 0; - const char * scan; const char * opnd; - scan = reginput; + auto scan = reg_in; opnd = OPERAND(p); switch (OP(p)) { case ANY: - count = int32_t(strlen(scan)); - scan += count; + count = int32_t(scan.length()); + scan = scan.substr(count); break; case EXACTLY: - while (*opnd == *scan) + while (*opnd == scan[0]) { count++; - scan++; + scan = scan.substr(1); } break; case ANYOF: - while (*scan != '\0' && strchr(opnd, *scan) != NULL) + while (!scan.empty() && strchr(opnd, scan[0]) != NULL) { count++; - scan++; + scan = scan.substr(1); } break; case ANYBUT: - while (*scan != '\0' && strchr(opnd, *scan) == NULL) + while (!scan.empty() && strchr(opnd, scan[0]) == NULL) { count++; - scan++; + scan = scan.substr(1); } break; default: /* Oh dear. Called inappropriately. */ @@ -1177,152 +1130,21 @@ struct executor count = 0; /* Best compromise. */ break; } - reginput = scan; + reg_in = scan; return (count); } }; // struct executor -int32_t regex_exec(const regex_prog & prog, - regex_expr & expr, - const char * start, - const char * end) +bool regex_exec( + const regex_prog & prog, regex_expr & expr, const string_view & text) { executor e; - return e.regexec(&prog, &expr, start); + auto result = e.regexec(prog, expr, text); + return result; } -#ifdef DEBUG - -STATIC char * regprop(); - -/* - - regdump - dump a regexp onto stdout in vaguely comprehensible form - */ -void regdump(regexp * r) -{ - char * s; - char op = EXACTLY; /* Arbitrary non-END op. */ - char * next; - - s = r->program + 1; - while (op != END) - { /* While that wasn't END last time... */ - op = OP(s); - out_printf("%2d%s", s - r->program, regprop(s)); /* Where, what. */ - next = regnext(s); - if (next == NULL) /* Next ptr. */ - out_printf("(0)"); - else - out_printf("(%d)", (s - r->program) + (next - s)); - s += 3; - if (op == ANYOF || op == ANYBUT || op == EXACTLY) - { - /* Literal string, where present. */ - while (*s != '\0') - { - out_putc(*s); - s++; - } - s++; - } - out_putc('\n'); - } - - /* Header fields of interest. */ - if (r->regstart != '\0') out_printf("start `%c' ", r->regstart); - if (r->reganch) out_printf("anchored "); - if (r->regmust != NULL) out_printf("must have \"%s\"", r->regmust); - out_printf("\n"); -} - -/* - - regprop - printable representation of opcode - */ -static char * regprop(char * op) -{ - char * p; - static char buf[50]; - - (void)strcpy(buf, ":"); - - switch (OP(op)) - { - case BOL: p = "BOL"; break; - case EOL: p = "EOL"; break; - case ANY: p = "ANY"; break; - case ANYOF: p = "ANYOF"; break; - case ANYBUT: p = "ANYBUT"; break; - case BRANCH: p = "BRANCH"; break; - case EXACTLY: p = "EXACTLY"; break; - case NOTHING: p = "NOTHING"; break; - case BACK: p = "BACK"; break; - case END: p = "END"; break; - case OPEN + 1: - case OPEN + 2: - case OPEN + 3: - case OPEN + 4: - case OPEN + 5: - case OPEN + 6: - case OPEN + 7: - case OPEN + 8: - case OPEN + 9: - sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN); - p = NULL; - break; - case CLOSE + 1: - case CLOSE + 2: - case CLOSE + 3: - case CLOSE + 4: - case CLOSE + 5: - case CLOSE + 6: - case CLOSE + 7: - case CLOSE + 8: - case CLOSE + 9: - sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE); - p = NULL; - break; - case STAR: p = "STAR"; break; - case PLUS: p = "PLUS"; break; - case WORDA: p = "WORDA"; break; - case WORDZ: p = "WORDZ"; break; - default: regerror("corrupted opcode"); break; - } - if (p != NULL) (void)strcat(buf, p); - return (buf); -} -#endif - -/* - * The following is provided for those people who do not have strcspn() in - * their C libraries. They should get off their butts and do something - * about it; at least one public-domain implementation of those (highly - * useful) string routines has been published on Usenet. - */ -#ifdef STRCSPN -/* - * strcspn - find length of initial segment of s1 consisting entirely - * of characters not from s2 - */ - -static int32_t strcspn(char * s1, char * s2) -{ - char * scan1; - char * scan2; - int32_t count; - - count = 0; - for (scan1 = s1; *scan1 != '\0'; scan1++) - { - for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ - if (*scan1 == *scan2++) return (count); - count++; - } - return (count); -} -#endif - void regerror(char const * s) { out_printf("re error %s\n", s); } regex_prog & program::compile(const char * pattern) @@ -1340,10 +1162,9 @@ program::program(const char * pattern) { reset(pattern); } void program::reset(const char * pattern) { compiled = &compile(pattern); } program::result_iterator::result_iterator( - const regex_prog & c, const char * b, const char * e) + const regex_prog & c, const string_view & s) : compiled(&c) - , match { b, 0 } - , rest { b, std::size_t(e - b) } + , rest(s) { advance(); } @@ -1351,20 +1172,11 @@ program::result_iterator::result_iterator( void program::result_iterator::advance() { // We start searching for a match at the end of the previous match. - if (regex_exec(*compiled, expressions, rest.begin(), rest.end()) == 0) - { - // No next match, reset to end/nothing. - match.str = nullptr; - match.size = 0; - } - else + if (regex_exec(*compiled, expressions, rest)) { // A match means the subexpressions are filled in and the first entry - // is the full match. - match.str = expressions.startp[0]; - match.size = expressions.endp[0] - expressions.startp[0]; - rest.str = expressions.endp[0]; - rest.size = rest.end() - rest.begin(); + // is the full match. Advance `rest` to follow the match. + rest = string_view(expressions.sub[0].end(), rest.end()); } } diff --git a/src/engine/regexp.h b/src/engine/regexp.h index be4bb9454..35353776b 100644 --- a/src/engine/regexp.h +++ b/src/engine/regexp.h @@ -10,6 +10,7 @@ #include "config.h" #include "mem.h" +#include "strview.h" #include "types.h" #include "value.h" @@ -24,8 +25,7 @@ namespace b2 { namespace regex { // And expressions [1,NSUBEXP] are the subexpressions matched. struct regex_expr { - char const * startp[NSUBEXP] = { nullptr }; - char const * endp[NSUBEXP] = { nullptr }; + string_view sub[NSUBEXP]; }; // The compiled regex program to match with. @@ -43,6 +43,7 @@ struct program void reset(const char * pattern); + result_iterator search(const string_view & str); result_iterator search(const char * str_begin, const char * str_end); result_iterator search(const char * str_begin); @@ -55,12 +56,12 @@ struct program struct program::result_iterator { using iterator_category = std::forward_iterator_tag; - using value_type = value::str_view; + using value_type = string_view; using difference_type = std::ptrdiff_t; using pointer = const value_type *; using reference = const value_type &; - result_iterator(const regex_prog & c, const char * b, const char * e); + result_iterator(const regex_prog & c, const string_view & s); result_iterator(const result_iterator & o) = default; result_iterator(result_iterator && o) = default; @@ -69,34 +70,37 @@ struct program::result_iterator advance(); return *this; } - inline reference operator*() const { return match; } - inline pointer operator->() const { return &match; } - explicit inline operator bool() const { return match.str != nullptr; } - inline value_type operator[](std::size_t i) const + inline reference operator*() const { return (*this)[0]; } + inline pointer operator->() const { return &(*this)[0]; } + explicit inline operator bool() const { return !(*this)[0].empty(); } + inline reference operator[](std::size_t i) const { - return i <= NSUBEXP ? value_type { expressions.startp[i], - std::size_t(expressions.endp[i] - expressions.startp[i]) } - : value_type { nullptr, 0 }; + static const value_type invalid { nullptr, 0 }; + return i <= NSUBEXP ? expressions.sub[i] : invalid; } private: const regex_prog * compiled = nullptr; regex_expr expressions; - value_type match; value_type rest; void advance(); }; +inline program::result_iterator program::search(const string_view & str) +{ + return result_iterator(*compiled, str); +} + inline program::result_iterator program::search( const char * str_begin, const char * str_end) { - return result_iterator(*compiled, str_begin, str_end); + return this->search(string_view(str_begin, str_end)); } inline program::result_iterator program::search(const char * str_begin) { - return this->search(str_begin, str_begin + std::strlen(str_begin)); + return this->search(string_view(str_begin, std::strlen(str_begin))); } }} // namespace b2::regex diff --git a/src/engine/rules.cpp b/src/engine/rules.cpp index f6ae47002..72c32dde5 100644 --- a/src/engine/rules.cpp +++ b/src/engine/rules.cpp @@ -109,7 +109,7 @@ static rule_ptr enter_rule( b2::value_ptr rulename, module_ptr target_module ) r->module = 0; r->actions = 0; r->exported = 0; - r->module = target_module; + r->module = b2::ensure_valid(target_module); } return r; } @@ -131,7 +131,7 @@ static rule_ptr define_rule( module_ptr src_module, b2::value_ptr rulename, set_rule_body( r, 0 ); set_rule_actions( r, 0 ); /* r will be executed in the source module. */ - r->module = src_module; + r->module = b2::ensure_valid(src_module); } return r; } diff --git a/src/engine/strview.h b/src/engine/strview.h new file mode 100644 index 000000000..b4d900312 --- /dev/null +++ b/src/engine/strview.h @@ -0,0 +1,991 @@ +#ifndef BOOST_CORE_STRING_VIEW_HPP_INCLUDED +#define BOOST_CORE_STRING_VIEW_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost::core::basic_string_view +// +// Copyright 2021 Peter Dimov +// Copyright 2023 René Ferdinand Rivera Morell +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + +namespace core +{ +namespace detail +{ + +template struct sv_to_uchar +{ + typedef Ch type; +}; + +template<> struct sv_to_uchar +{ + typedef unsigned char type; +}; + +#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +template constexpr std::size_t find_first_of( Ch const* p_, std::size_t n_, Ch const* s, std::size_t pos, std::size_t n ) noexcept +{ + typedef typename sv_to_uchar::type UCh; + + unsigned char table[ 256 ] = {}; + + bool use_table = true; + + for( std::size_t j = 0; j < n; ++j ) + { + UCh ch = s[ j ]; + + if( ch >= 0 && ch < 256 ) + { + table[ ch ] = 1; + } + else + { + use_table = false; + break; + } + } + + if( use_table ) + { + for( std::size_t i = pos; i < n_; ++i ) + { + UCh ch = p_[ i ]; + if( ch >= 0 && ch < 256 && table[ ch ] ) return i; + } + } + else if( n >= 16 ) + { + for( std::size_t i = pos; i < n_; ++i ) + { + Ch ch = p_[ i ]; + if( std::char_traits::find( s, n, ch ) != 0 ) return i; + } + } + else + { + for( std::size_t i = pos; i < n_; ++i ) + { + Ch ch = p_[ i ]; + + for( std::size_t j = 0; j < n; ++j ) + { + if( s[ j ] == ch ) return i; + } + } + } + + return static_cast( -1 ); +} + +template constexpr std::size_t find_last_of( Ch const* p_, Ch const* s, std::size_t pos, std::size_t n ) noexcept +{ + typedef typename sv_to_uchar::type UCh; + + unsigned char table[ 256 ] = {}; + + bool use_table = true; + + for( std::size_t j = 0; j < n; ++j ) + { + UCh ch = s[ j ]; + + if( ch >= 0 && ch < 256 ) + { + table[ ch ] = 1; + } + else + { + use_table = false; + break; + } + } + + std::size_t const npos = static_cast< std::size_t >( -1 ); + + std::size_t i = pos; + + if( use_table ) + { + do + { + UCh ch = p_[ i ]; + + if( ch >= 0 && ch < 256 && table[ ch ] ) return i; + + --i; + } + while( i != npos ); + } + else if( n >= 16 ) + { + do + { + Ch ch = p_[ i ]; + + if( std::char_traits::find( s, n, ch ) != 0 ) return i; + + --i; + } + while( i != npos ); + } + else + { + do + { + Ch ch = p_[ i ]; + + for( std::size_t j = 0; j < n; ++j ) + { + if( s[ j ] == ch ) return i; + } + + --i; + } + while( i != npos ); + } + + return npos; +} + +template constexpr std::size_t find_first_not_of( Ch const* p_, std::size_t n_, Ch const* s, std::size_t pos, std::size_t n ) noexcept +{ + typedef typename sv_to_uchar::type UCh; + + unsigned char table[ 256 ] = {}; + + bool use_table = true; + + for( std::size_t j = 0; j < n; ++j ) + { + UCh ch = s[ j ]; + + if( ch >= 0 && ch < 256 ) + { + table[ ch ] = 1; + } + else + { + use_table = false; + break; + } + } + + if( use_table ) + { + for( std::size_t i = pos; i < n_; ++i ) + { + UCh ch = p_[ i ]; + if( !( ch >= 0 && ch < 256 && table[ ch ] ) ) return i; + } + } + else if( n >= 16 ) + { + for( std::size_t i = pos; i < n_; ++i ) + { + Ch ch = p_[ i ]; + if( std::char_traits::find( s, n, ch ) == 0 ) return i; + } + } + else + { + for( std::size_t i = pos; i < n_; ++i ) + { + Ch ch = p_[ i ]; + + bool r = false; + + for( std::size_t j = 0; j < n; ++j ) + { + if( s[ j ] == ch ) + { + r = true; + break; + } + } + + if( !r ) return i; + } + } + + return static_cast( -1 ); +} + +template constexpr std::size_t find_last_not_of( Ch const* p_, Ch const* s, std::size_t pos, std::size_t n ) noexcept +{ + typedef typename sv_to_uchar::type UCh; + + unsigned char table[ 256 ] = {}; + + bool use_table = true; + + for( std::size_t j = 0; j < n; ++j ) + { + UCh ch = s[ j ]; + + if( ch >= 0 && ch < 256 ) + { + table[ ch ] = 1; + } + else + { + use_table = false; + break; + } + } + + std::size_t const npos = static_cast< std::size_t >( -1 ); + + std::size_t i = pos; + + if( use_table ) + { + do + { + UCh ch = p_[ i ]; + + if( !( ch >= 0 && ch < 256 && table[ ch ] ) ) return i; + + --i; + } + while( i != npos ); + } + else if( n >= 16 ) + { + do + { + Ch ch = p_[ i ]; + + if( std::char_traits::find( s, n, ch ) == 0 ) return i; + + --i; + } + while( i != npos ); + } + else + { + do + { + Ch ch = p_[ i ]; + + bool r = false; + + for( std::size_t j = 0; j < n; ++j ) + { + if( s[ j ] == ch ) + { + r = true; + break; + } + } + + if( !r ) return i; + + --i; + } + while( i != npos ); + } + + return npos; +} + +#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406 +# pragma GCC diagnostic pop +#endif + +} // namespace detail + +template class basic_string_view +{ +private: + + Ch const* p_; + mutable std::size_t n_; // mutable to allow mod within C++11 constexpr rules + +public: + + // types + + typedef std::char_traits traits_type; + typedef Ch value_type; + typedef Ch* pointer; + typedef Ch const* const_pointer; + typedef Ch& reference; + typedef Ch const& const_reference; + typedef Ch const* const_iterator; + typedef const_iterator iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef const_reverse_iterator reverse_iterator; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // npos + + static constexpr size_type npos = static_cast( -1 ); + +public: + + // construction and assignment + + constexpr basic_string_view() noexcept: p_(), n_() + { + } + + constexpr basic_string_view( Ch const* str ) noexcept: p_( str ), n_( traits_type::length( str ) ) + { + } + + constexpr basic_string_view( Ch const* str, size_type len ) noexcept: p_( str ), n_( len ) + { + } + + template constexpr basic_string_view( Ch const* first, End last, + typename std::enable_if::value, void >::type* = 0 ) noexcept: p_( first ), n_( last - first ) + { + } + + template basic_string_view( std::basic_string, A> const& str ) noexcept: p_( str.data() ), n_( str.size() ) + { + } + + basic_string_view( std::nullptr_t ) = delete; + + // constexpr basic_string_view& operator=( basic_string_view const& ) noexcept & = default; + + // conversions + + template operator std::basic_string, A>() const + { + return std::basic_string, A>( data(), size() ); + } + + // iterator support + + constexpr const_iterator begin() const noexcept + { + return p_; + } + + constexpr const_iterator end() const noexcept + { + return p_ + n_; + } + + constexpr const_iterator cbegin() const noexcept + { + return p_; + } + + constexpr const_iterator cend() const noexcept + { + return p_ + n_; + } + + constexpr const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator( end() ); + } + + constexpr const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator( begin() ); + } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator( end() ); + } + + constexpr const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator( begin() ); + } + + // capacity + + constexpr size_type size() const noexcept + { + return n_; + } + + constexpr size_type length() const noexcept + { + return n_; + } + + constexpr size_type max_size() const noexcept + { + return npos / sizeof( Ch ); + } + + constexpr bool empty() const noexcept + { + return n_ == 0; + } + + // element access + + constexpr const_reference operator[]( size_type pos ) const noexcept + { + assert( pos < size() ); + return p_[ pos ]; + } + + constexpr const_reference at( size_type pos ) const + { + if( pos >= size() ) + { + throw std::out_of_range( "basic_string_view::at" ); + } + + return p_[ pos ]; + } + + constexpr const_reference front() const noexcept + { + assert( !empty() ); + return p_[ 0 ]; + } + + constexpr const_reference back() const noexcept + { + assert( !empty() ); + return p_[ n_ - 1 ]; + } + + constexpr const_pointer data() const noexcept + { + return p_; + } + + // modifiers + + constexpr void remove_prefix( size_type n ) noexcept + { + assert( n <= size() ); + + p_ += n; + n_ -= n; + } + + constexpr void remove_suffix( size_type n ) noexcept + { + assert( n <= size() ); + + n_ -= n; + } + + constexpr void swap( basic_string_view& s ) noexcept + { + std::swap( p_, s.p_ ); + std::swap( n_, s.n_ ); + } + + // string operations + + constexpr size_type copy( Ch* s, size_type n, size_type pos = 0 ) const + { + if( pos > size() ) + { + throw std::out_of_range( "basic_string_view::copy" ); + } + + std::size_t rlen = (std::min)( n, size() - pos ); + + traits_type::copy( s, data() + pos, rlen ); + + return rlen; + } + + constexpr basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { + if( pos > size() ) + { + throw std::out_of_range( "basic_string_view::substr" ); + } + + std::size_t rlen = (std::min)( n, size() - pos ); + + return basic_string_view( data() + pos, rlen ); + } + + // compare + + constexpr int compare( basic_string_view str ) const noexcept + { + std::size_t rlen = (std::min)( size(), str.size() ); + + int cmp = traits_type::compare( data(), str.data(), rlen ); + + if( cmp != 0 ) return cmp; + + if( size() == str.size() ) return 0; + + return size() < str.size()? -1: +1; + } + + constexpr int compare( size_type pos1, size_type n1, basic_string_view str ) const + { + return substr( pos1, n1 ).compare( str ); + } + + constexpr int compare( size_type pos1, size_type n1, basic_string_view str, size_type pos2, size_type n2 ) const + { + return substr( pos1, n1 ).compare( str.substr( pos2, n2 ) ); + } + + constexpr int compare( Ch const* s ) const noexcept + { + return compare( basic_string_view( s ) ); + } + + constexpr int compare( size_type pos1, size_type n1, Ch const* s ) const + { + return substr( pos1, n1 ).compare( basic_string_view( s ) ); + } + + constexpr int compare( size_type pos1, size_type n1, Ch const* s, size_type n2 ) const + { + return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); + } + + // starts_with + + constexpr bool starts_with( basic_string_view x ) const noexcept + { + return size() >= x.size() && traits_type::compare( data(), x.data(), x.size() ) == 0; + } + + constexpr bool starts_with( Ch x ) const noexcept + { + return !empty() && front() == x; + } + + constexpr bool starts_with( Ch const* x ) const noexcept + { + return starts_with( basic_string_view( x ) ); + } + + // ends_with + + constexpr bool ends_with( basic_string_view x ) const noexcept + { + return size() >= x.size() && traits_type::compare( data() + size() - x.size(), x.data(), x.size() ) == 0; + } + + constexpr bool ends_with( Ch x ) const noexcept + { + return !empty() && back() == x; + } + + constexpr bool ends_with( Ch const* x ) const noexcept + { + return ends_with( basic_string_view( x ) ); + } + + // find + + constexpr size_type find( basic_string_view str, size_type pos = 0 ) const noexcept + { + return find( str.data(), pos, str.size() ); + } + + constexpr size_type find( Ch c, size_type pos = 0 ) const noexcept + { + if( pos >= size() ) return npos; + + Ch const* r = traits_type::find( data() + pos, size() - pos, c ); + + return r? r - data(): npos; + } + + constexpr size_type find( Ch const* s, size_type pos, size_type n ) const noexcept + { + if( n == 1 ) return find( s[0], pos ); + + if( pos + n > size() ) return npos; + if( n == 0 ) return pos; + + Ch const* p = data() + pos; + Ch const* last = data() + size() - n + 1; + + for( ;; ) + { + p = traits_type::find( p, last - p, s[0] ); + + if( p == 0 ) break; + + if( traits_type::compare( p + 1, s + 1, n - 1 ) == 0 ) return p - data(); + + ++p; + } + + return npos; + } + + constexpr size_type find( Ch const* s, size_type pos = 0 ) const noexcept + { + return find( s, pos, traits_type::length( s ) ); + } + + // rfind + + constexpr size_type rfind( basic_string_view str, size_type pos = npos ) const noexcept + { + return rfind( str.data(), pos, str.size() ); + } + + constexpr size_type rfind( Ch c, size_type pos = npos ) const noexcept + { + size_type n = size(); + + if( n == 0 ) + { + return npos; + } + + if( pos > n - 1 ) + { + pos = n - 1; + } + + do + { + if( p_[ pos ] == c ) return pos; + --pos; + } + while( pos != npos ); + + return npos; + } + + constexpr size_type rfind( Ch const* s, size_type pos, size_type n ) const noexcept + { + if( n > size() ) return npos; + + if( pos > size() - n ) + { + pos = size() - n; + } + + if( n == 0 ) return pos; + + for( ;; ) + { + size_type xpos = rfind( s[0], pos ); + + if( xpos == npos ) return npos; + + if( traits_type::compare( data() + xpos, s, n ) == 0 ) return xpos; + + if( xpos == 0 ) return npos; + + pos = xpos - 1; + } + } + + constexpr size_type rfind( Ch const* s, size_type pos = npos ) const noexcept + { + return rfind( s, pos, traits_type::length( s ) ); + } + + // find_first_of + + constexpr size_type find_first_of( basic_string_view str, size_type pos = 0 ) const noexcept + { + return find_first_of( str.data(), pos, str.size() ); + } + + constexpr size_type find_first_of( Ch c, size_type pos = 0 ) const noexcept + { + return find( c, pos ); + } + + constexpr size_type find_first_of( Ch const* s, size_type pos, size_type n ) const noexcept + { + if( n == 0 || pos >= size() ) return npos; + if( n == 1 ) return find( s[0], pos ); + + return detail::find_first_of( data(), size(), s, pos, n ); + } + + constexpr size_type find_first_of( Ch const* s, size_type pos = 0 ) const noexcept + { + return find_first_of( s, pos, traits_type::length( s ) ); + } + + // find_last_of + + constexpr size_type find_last_of( basic_string_view str, size_type pos = npos ) const noexcept + { + return find_last_of( str.data(), pos, str.size() ); + } + + constexpr size_type find_last_of( Ch c, size_type pos = npos ) const noexcept + { + return rfind( c, pos ); + } + + constexpr size_type find_last_of( Ch const* s, size_type pos, size_type n ) const noexcept + { + if( n == 1 ) + { + return rfind( s[0], pos ); + } + + size_type m = size(); + + if( m == 0 ) + { + return npos; + } + + if( pos > m - 1 ) + { + pos = m - 1; + } + + return detail::find_last_of( data(), s, pos, n ); + } + + constexpr size_type find_last_of( Ch const* s, size_type pos = npos ) const noexcept + { + return find_last_of( s, pos, traits_type::length( s ) ); + } + + // find_first_not_of + + constexpr size_type find_first_not_of( basic_string_view str, size_type pos = 0 ) const noexcept + { + return find_first_not_of( str.data(), pos, str.size() ); + } + + constexpr size_type find_first_not_of( Ch c, size_type pos = 0 ) const noexcept + { + for( std::size_t i = pos; i < n_; ++i ) + { + if( p_[ i ] != c ) return i; + } + + return npos; + } + + constexpr size_type find_first_not_of( Ch const* s, size_type pos, size_type n ) const noexcept + { + if( pos >= size() ) return npos; + if( n == 1 ) return find_first_not_of( s[0], pos ); + + return detail::find_first_not_of( data(), size(), s, pos, n ); + } + + constexpr size_type find_first_not_of( Ch const* s, size_type pos = 0 ) const noexcept + { + return find_first_not_of( s, pos, traits_type::length( s ) ); + } + + // find_last_not_of + + constexpr size_type find_last_not_of( basic_string_view str, size_type pos = npos ) const noexcept + { + return find_last_not_of( str.data(), pos, str.size() ); + } + + constexpr size_type find_last_not_of( Ch c, size_type pos = npos ) const noexcept + { + size_type m = size(); + + if( m == 0 ) + { + return npos; + } + + if( pos > m - 1 ) + { + pos = m - 1; + } + + do + { + if( p_[ pos ] != c ) return pos; + --pos; + } + while( pos != npos ); + + return npos; + } + + constexpr size_type find_last_not_of( Ch const* s, size_type pos, size_type n ) const noexcept + { + if( n == 1 ) + { + return find_last_not_of( s[0], pos ); + } + + size_type m = size(); + + if( m == 0 ) + { + return npos; + } + + if( pos > m - 1 ) + { + pos = m - 1; + } + + return detail::find_last_not_of( data(), s, pos, n ); + } + + constexpr size_type find_last_not_of( Ch const* s, size_type pos = npos ) const noexcept + { + return find_last_not_of( s, pos, traits_type::length( s ) ); + } + + // contains + + constexpr bool contains( basic_string_view sv ) const noexcept + { + return find( sv ) != npos; + } + + constexpr bool contains( Ch c ) const noexcept + { + Ch const* p = data(); + size_type n = size(); + + if( n >= 16 ) + { + return traits_type::find( p, n, c ) != 0; + } + else + { + for( size_type i = 0; i < n; ++i ) + { + if( p[ i ] == c ) return true; + } + + return false; + } + } + + constexpr bool contains( Ch const* s ) const noexcept + { + return find( s ) != npos; + } + + // relational operators + + constexpr friend bool operator==( basic_string_view sv1, basic_string_view sv2 ) noexcept + { + return sv1.size() == sv2.size() && traits_type::compare( sv1.data(), sv2.data(), sv1.size() ) == 0; + } + + constexpr friend bool operator!=( basic_string_view sv1, basic_string_view sv2 ) noexcept + { + return !( sv1 == sv2 ); + } + + constexpr friend bool operator<( basic_string_view sv1, basic_string_view sv2 ) noexcept + { + return sv1.compare( sv2 ) < 0; + } + + constexpr friend bool operator<=( basic_string_view sv1, basic_string_view sv2 ) noexcept + { + return sv1.compare( sv2 ) <= 0; + } + + constexpr friend bool operator>( basic_string_view sv1, basic_string_view sv2 ) noexcept + { + return sv1.compare( sv2 ) > 0; + } + + constexpr friend bool operator>=( basic_string_view sv1, basic_string_view sv2 ) noexcept + { + return sv1.compare( sv2 ) >= 0; + } +}; + +// stream inserter + +template std::basic_ostream& operator<<( std::basic_ostream& os, basic_string_view str ) +{ + Ch const* p = str.data(); + std::streamsize n = str.size(); + + std::streamsize m = os.width(); + + if( n >= m ) + { + os.write( p, n ); + } + else if( ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::left ) + { + os.write( p, n ); + + os.width( m - n ); + os << ""; + } + else + { + os.width( m - n ); + os << ""; + + os.write( p, n ); + } + + os.width( 0 ); + return os; +} + +#if defined(BOOST_NO_CXX17_INLINE_VARIABLES) +template constexpr_OR_CONST std::size_t basic_string_view::npos; +#endif + +// typedef names + +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; +typedef basic_string_view u16string_view; +typedef basic_string_view u32string_view; + +#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L +typedef basic_string_view u8string_view; +#endif + +} // namespace core +} // namespace boost + +namespace b2 { + +using string_view = boost::core::string_view; + +} + +#endif // #ifndef BOOST_CORE_STRING_VIEW_HPP_INCLUDED