2
0
mirror of https://github.com/boostorg/build.git synced 2026-01-19 04:02:14 +00:00

Switch to string_view for some internal char handling.

Add some debug validation in Jam interpreter.
This commit is contained in:
Rene Rivera
2023-03-11 14:44:59 -06:00
parent 326a75bb97
commit 2399813afa
20 changed files with 1414 additions and 422 deletions

2
.vscode/launch.json vendored
View File

@@ -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": [],

25
.vscode/tasks.json vendored
View File

@@ -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
}
}
}
]
}

View File

@@ -45,6 +45,7 @@ project b2
<cxxstd>11
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_NONSTDC_NO_DEPRECATE=1
<variant>debug:<define>B2_DEBUG=1
;
#|

View File

@@ -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 )

View File

@@ -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 ) );

View File

@@ -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] );

View File

@@ -75,5 +75,27 @@ typedef long int32_t;
#define B2_USE_STD_THREADS 1
#endif
#include <cassert>
namespace b2 {
inline bool ensure(bool v)
{
#if defined(B2_DEBUG) && B2_DEBUG
assert(v);
#endif
return v;
}
template <typename T>
T * ensure_valid(T * v)
{
#if defined(B2_DEBUG) && B2_DEBUG
assert(v != nullptr);
#endif
return v;
}
} // namespace b2
#endif

View File

@@ -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;

View File

@@ -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

View File

@@ -24,6 +24,7 @@
#include "variable.h"
#include "output.h"
#include "startup.h"
#include "mod_sysinfo.h"
#include <assert.h>
#include <errno.h>
@@ -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 <class T>
remove_cref_t<T> & push( T v );
remove_cref_t<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 <class T>
remove_cref_t<T> * push( T v, int32_t n );
remove_cref_t<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 <class T>
remove_cref_t<T> pop()
{
using U = remove_cref_t<T>;
// Copy the top/left item.
U result = top<U>();
// Remove the obsolete top item.
pop<T>( 1 );
return result;
}
// Removes the top "n" "T" items from the stack.
// Removes the top/left "n" "T" items from the stack.
template <class T>
void pop( int32_t n )
{
// Debugging validation.
using U = remove_cref_t<T>;
for (auto c = 0; c < n; ++c)
cleanups[cleanups_size-c-1].check<U>().~cleanup_t();
// The removal of the n items happens by skipping the stack pointer
// past the n items.
set_data( nth<T>( 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<cleanup_f, (1<<21)/sizeof(void*)>;
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 <class T>
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 <class T>
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<cleanup_t, (1 << 21)/sizeof(void*)>;
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 <typename T>
remove_cref_t<T> * nth( int32_t n )
{
@@ -405,7 +460,7 @@ struct _stack
}
template <typename T>
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<LIST*>(_stack * s, LIST**)
}
template <class T>
remove_cref_t<T> & _stack::push( T v )
remove_cref_t<T> & _stack::push( T v, FRAME * frame )
{
return *push<T>( v, 1 );
return *push<T>( v, 1, frame );
}
template <class T>
remove_cref_t<T> * _stack::push( T v, int32_t n )
remove_cref_t<T> * _stack::push( T v, int32_t n, FRAME * frame )
{
using U = remove_cref_t<T>;
set_data( nth<T>( -n ) );
std::uninitialized_fill_n( nth<U>( 0 ), n, v );
cleanup_push<U>( n );
cleanup_push<U>( n, frame );
return nth<U>( 0 );
}
template <typename T>
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<T> );
using U = remove_cref_t<T>;
cleanup_t c((cleanup_f)&_stack::cleanup_item<T>, 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<LIST*>( n );
s->pop<LISTITER>( 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<LIST *>();
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<LIST *>();
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<module_t *>();
frame->module = b2::ensure_valid(s->pop<module_t *>());
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<module_t *>());
}
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;
}

View File

@@ -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 <functional>
#include <string>
typedef struct _function FUNCTION;
typedef std::function<LIST* (FRAME *, int32_t flags)> function_builtin_t;
typedef std::function<LIST *(FRAME *, int32_t flags)> 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

View File

@@ -18,6 +18,8 @@ Distributed under the Boost Software License, Version 1.0.
#include "mod_path.h"
#include <cassert>
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()
: "<root>",
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

View File

@@ -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]);
}
}
}

View File

@@ -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 ) );

View File

@@ -11,23 +11,28 @@ Distributed under the Boost Software License, Version 1.0.
#include <thread>
#if defined(OS_MACOSX)
# include <sys/sysctl.h>
# include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#endif
#if !defined(OS_NT)
# include <unistd.h>
#include <unistd.h>
#else
# include <windows.h>
#include <windows.h>
#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 <sched.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sched.h>
#endif
#if defined(OS_LINUX) && defined(__GLIBC__)
// For backtrace_symbols() call.
#include <execinfo.h>
#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;
}

View File

@@ -12,6 +12,8 @@ Distributed under the Boost Software License, Version 1.0.
#include "bind.h"
#include "value.h"
#include <string>
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_<sysinfo_module>
{
const char * module_name = "sysinfo";

View File

@@ -48,6 +48,7 @@
#include "regexp.h"
#include "jam.h"
#include "output.h"
#include "strview.h"
#include <ctype.h>
#include <stdio.h>
@@ -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<regex_prog>(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());
}
}

View File

@@ -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

View File

@@ -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;
}

991
src/engine/strview.h Normal file
View File

@@ -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<Ch>
//
// 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 <string>
#include <algorithm>
#include <utility>
#include <stdexcept>
#include <cstddef>
#include <cstring>
#include <climits>
#include <iosfwd>
#include <ios>
#include <type_traits>
#include <cassert>
namespace boost
{
namespace core
{
namespace detail
{
template<class Ch> struct sv_to_uchar
{
typedef Ch type;
};
template<> struct sv_to_uchar<char>
{
typedef unsigned char type;
};
#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
template<class Ch> 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<Ch>::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<Ch>::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<std::size_t>( -1 );
}
template<class Ch> 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<Ch>::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<Ch>::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<class Ch> 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<Ch>::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<Ch>::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<std::size_t>( -1 );
}
template<class Ch> 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<Ch>::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<Ch>::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 Ch> 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<Ch> 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_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<size_type>( -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<class End> constexpr basic_string_view( Ch const* first, End last,
typename std::enable_if<std::is_same<End, Ch const*>::value, void >::type* = 0 ) noexcept: p_( first ), n_( last - first )
{
}
template<class A> basic_string_view( std::basic_string<Ch, std::char_traits<Ch>, 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<class A> operator std::basic_string<Ch, std::char_traits<Ch>, A>() const
{
return std::basic_string<Ch, std::char_traits<Ch>, 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<class Ch> std::basic_ostream<Ch>& operator<<( std::basic_ostream<Ch>& os, basic_string_view<Ch> 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<class Ch> constexpr_OR_CONST std::size_t basic_string_view<Ch>::npos;
#endif
// typedef names
typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
typedef basic_string_view<char16_t> u16string_view;
typedef basic_string_view<char32_t> u32string_view;
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
typedef basic_string_view<char8_t> u8string_view;
#endif
} // namespace core
} // namespace boost
namespace b2 {
using string_view = boost::core::string_view;
}
#endif // #ifndef BOOST_CORE_STRING_VIEW_HPP_INCLUDED