lnav/src/third-party/rapidyaml/ryml_all.hpp

30946 lines
1.0 MiB
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef _RYML_SINGLE_HEADER_AMALGAMATED_HPP_
//
// Rapid YAML - a library to parse and emit YAML, and do it fast.
//
// https://github.com/biojppm/rapidyaml
//
// DO NOT EDIT. This file is generated automatically.
// This is an amalgamated single-header version of the library.
//
// INSTRUCTIONS:
// - Include at will in any header of your project
// - In one (and only one) of your project source files,
// #define RYML_SINGLE_HDR_DEFINE_NOW and then include this header.
// This will enable the function and class definitions in
// the header file.
// - To compile into a shared library, just define the
// preprocessor symbol RYML_SHARED . This will take
// care of symbol export/import.
//
//********************************************************************************
//--------------------------------------------------------------------------------
// LICENSE.txt
// https://github.com/biojppm/rapidyaml/LICENSE.txt
//--------------------------------------------------------------------------------
//********************************************************************************
// Copyright (c) 2018, Joao Paulo Magalhaes <dev@jpmag.me>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// shared library: export when defining
#if defined(RYML_SHARED) && defined(RYML_SINGLE_HDR_DEFINE_NOW) && !defined(RYML_EXPORTS)
#define RYML_EXPORTS
#endif
// propagate defines to c4core
#if defined(RYML_SINGLE_HDR_DEFINE_NOW) && !defined(C4CORE_SINGLE_HDR_DEFINE_NOW)
#define C4CORE_SINGLE_HDR_DEFINE_NOW
#endif
#if defined(RYML_EXPORTS) && !defined(C4CORE_EXPORTS)
#define C4CORE_EXPORTS
#endif
#if defined(RYML_SHARED) && !defined(C4CORE_SHARED)
#define C4CORE_SHARED
#endif
// workaround for include removal while amalgamating
// resulting in <stdarg.h> missing in arm-none-eabi-g++
// https://github.com/biojppm/rapidyaml/issues/193
#include <stdarg.h>
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/c4core_all.hpp
// https://github.com/biojppm/rapidyaml/src/c4/c4core_all.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4CORE_SINGLE_HEADER_AMALGAMATED_HPP_
//
// c4core - C++ utilities
//
// https://github.com/biojppm/c4core
//
// DO NOT EDIT. This file is generated automatically.
// This is an amalgamated single-header version of the library.
//
// INSTRUCTIONS:
// - Include at will in any header of your project
// - In one (and only one) of your project source files,
// #define C4CORE_SINGLE_HDR_DEFINE_NOW and then include this header.
// This will enable the function and class definitions in
// the header file.
// - To compile into a shared library, just define the
// preprocessor symbol C4CORE_SHARED . This will take
// care of symbol export/import.
//
//********************************************************************************
//--------------------------------------------------------------------------------
// LICENSE.txt
// https://github.com/biojppm/c4core/LICENSE.txt
//--------------------------------------------------------------------------------
//********************************************************************************
// Copyright (c) 2018, Joao Paulo Magalhaes <dev@jpmag.me>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// shared library: export when defining
#if defined(C4CORE_SHARED) && defined(C4CORE_SINGLE_HDR_DEFINE_NOW) && !defined(C4CORE_EXPORTS)
#define C4CORE_EXPORTS
#endif
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/export.hpp
// https://github.com/biojppm/c4core/src/c4/export.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_EXPORT_HPP_
#define C4_EXPORT_HPP_
#ifdef _WIN32
#ifdef C4CORE_SHARED
#ifdef C4CORE_EXPORTS
#define C4CORE_EXPORT __declspec(dllexport)
#else
#define C4CORE_EXPORT __declspec(dllimport)
#endif
#else
#define C4CORE_EXPORT
#endif
#else
#define C4CORE_EXPORT
#endif
#endif /* C4CORE_EXPORT_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/export.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/preprocessor.hpp
// https://github.com/biojppm/c4core/src/c4/preprocessor.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_PREPROCESSOR_HPP_
#define _C4_PREPROCESSOR_HPP_
/** @file preprocessor.hpp Contains basic macros and preprocessor utilities.
* @ingroup basic_headers */
#ifdef __clang__
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
* clang requires switching off compiler warnings for pedantic mode.
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
#elif defined(__GNUC__)
/* GCC also issues a warning for zero-args calls to variadic macros.
* This warning is switched on with -pedantic and apparently there is no
* easy way to turn it off as with clang. But marking this as a system
* header works.
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
* @see http://stackoverflow.com/questions/35587137/ */
# pragma GCC system_header
#endif
#define C4_WIDEN(str) L"" str
#define C4_COUNTOF(arr) (sizeof(arr)/sizeof((arr)[0]))
#define C4_EXPAND(arg) arg
/** useful in some macro calls with template arguments */
#define C4_COMMA ,
/** useful in some macro calls with template arguments
* @see C4_COMMA */
#define C4_COMMA_X C4_COMMA
/** expand and quote */
#define C4_XQUOTE(arg) _C4_XQUOTE(arg)
#define _C4_XQUOTE(arg) C4_QUOTE(arg)
#define C4_QUOTE(arg) #arg
/** expand and concatenate */
#define C4_XCAT(arg1, arg2) _C4_XCAT(arg1, arg2)
#define _C4_XCAT(arg1, arg2) C4_CAT(arg1, arg2)
#define C4_CAT(arg1, arg2) arg1##arg2
#define C4_VERSION_CAT(major, minor, patch) ((major)*10000 + (minor)*100 + (patch))
/** A preprocessor foreach. Spectacular trick taken from:
* http://stackoverflow.com/a/1872506/5875572
* The first argument is for a macro receiving a single argument,
* which will be called with every subsequent argument. There is
* currently a limit of 32 arguments, and at least 1 must be provided.
*
Example:
@code{.cpp}
struct Example {
int a;
int b;
int c;
};
// define a one-arg macro to be called
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(Example, field)
#define PRN_STRUCT_OFFSETS_(structure, field) printf(C4_XQUOTE(structure) ":" C4_XQUOTE(field)" - offset=%zu\n", offsetof(structure, field));
// now call the macro for a, b and c
C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);
@endcode */
#define C4_FOR_EACH(what, ...) C4_FOR_EACH_SEP(what, ;, __VA_ARGS__)
/** same as C4_FOR_EACH(), but use a custom separator between statements.
* If a comma is needed as the separator, use the C4_COMMA macro.
* @see C4_FOR_EACH
* @see C4_COMMA
*/
#define C4_FOR_EACH_SEP(what, sep, ...) _C4_FOR_EACH_(_C4_FOR_EACH_NARG(__VA_ARGS__), what, sep, __VA_ARGS__)
/// @cond dev
#define _C4_FOR_EACH_01(what, sep, x) what(x) sep
#define _C4_FOR_EACH_02(what, sep, x, ...) what(x) sep _C4_FOR_EACH_01(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_03(what, sep, x, ...) what(x) sep _C4_FOR_EACH_02(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_04(what, sep, x, ...) what(x) sep _C4_FOR_EACH_03(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_05(what, sep, x, ...) what(x) sep _C4_FOR_EACH_04(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_06(what, sep, x, ...) what(x) sep _C4_FOR_EACH_05(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_07(what, sep, x, ...) what(x) sep _C4_FOR_EACH_06(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_08(what, sep, x, ...) what(x) sep _C4_FOR_EACH_07(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_09(what, sep, x, ...) what(x) sep _C4_FOR_EACH_08(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_10(what, sep, x, ...) what(x) sep _C4_FOR_EACH_09(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_11(what, sep, x, ...) what(x) sep _C4_FOR_EACH_10(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_12(what, sep, x, ...) what(x) sep _C4_FOR_EACH_11(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_13(what, sep, x, ...) what(x) sep _C4_FOR_EACH_12(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_14(what, sep, x, ...) what(x) sep _C4_FOR_EACH_13(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_15(what, sep, x, ...) what(x) sep _C4_FOR_EACH_14(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_16(what, sep, x, ...) what(x) sep _C4_FOR_EACH_15(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_17(what, sep, x, ...) what(x) sep _C4_FOR_EACH_16(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_18(what, sep, x, ...) what(x) sep _C4_FOR_EACH_17(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_19(what, sep, x, ...) what(x) sep _C4_FOR_EACH_18(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_20(what, sep, x, ...) what(x) sep _C4_FOR_EACH_19(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_21(what, sep, x, ...) what(x) sep _C4_FOR_EACH_20(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_22(what, sep, x, ...) what(x) sep _C4_FOR_EACH_21(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_23(what, sep, x, ...) what(x) sep _C4_FOR_EACH_22(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_24(what, sep, x, ...) what(x) sep _C4_FOR_EACH_23(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_25(what, sep, x, ...) what(x) sep _C4_FOR_EACH_24(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_26(what, sep, x, ...) what(x) sep _C4_FOR_EACH_25(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_27(what, sep, x, ...) what(x) sep _C4_FOR_EACH_26(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_28(what, sep, x, ...) what(x) sep _C4_FOR_EACH_27(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_29(what, sep, x, ...) what(x) sep _C4_FOR_EACH_28(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_30(what, sep, x, ...) what(x) sep _C4_FOR_EACH_29(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_31(what, sep, x, ...) what(x) sep _C4_FOR_EACH_30(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_32(what, sep, x, ...) what(x) sep _C4_FOR_EACH_31(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_NARG(...) _C4_FOR_EACH_NARG_(__VA_ARGS__, _C4_FOR_EACH_RSEQ_N())
#define _C4_FOR_EACH_NARG_(...) _C4_FOR_EACH_ARG_N(__VA_ARGS__)
#define _C4_FOR_EACH_ARG_N(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, N, ...) N
#define _C4_FOR_EACH_RSEQ_N() 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01
#define _C4_FOR_EACH_(N, what, sep, ...) C4_XCAT(_C4_FOR_EACH_, N)(what, sep, __VA_ARGS__)
/// @endcond
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#endif /* _C4_PREPROCESSOR_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/preprocessor.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/platform.hpp
// https://github.com/biojppm/c4core/src/c4/platform.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_PLATFORM_HPP_
#define _C4_PLATFORM_HPP_
/** @file platform.hpp Provides platform information macros
* @ingroup basic_headers */
// see also https://sourceforge.net/p/predef/wiki/OperatingSystems/
#if defined(_WIN64)
# define C4_WIN
# define C4_WIN64
#elif defined(_WIN32)
# define C4_WIN
# define C4_WIN32
#elif defined(__ANDROID__)
# define C4_ANDROID
#elif defined(__APPLE__)
# include "TargetConditionals.h"
# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
# define C4_IOS
# elif TARGET_OS_MAC || TARGET_OS_OSX
# define C4_MACOS
# else
# error "Unknown Apple platform"
# endif
#elif defined(__linux)
# define C4_UNIX
# define C4_LINUX
#elif defined(__unix)
# define C4_UNIX
#elif defined(__arm__) || defined(__aarch64__)
# define C4_ARM
#elif defined(SWIG)
# define C4_SWIG
#else
# error "unknown platform"
#endif
#if defined(__posix) || defined(__unix__) || defined(__linux)
# define C4_POSIX
#endif
#endif /* _C4_PLATFORM_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/platform.hpp)
#if 0
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/cpu.hpp
// https://github.com/biojppm/c4core/src/c4/cpu.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_CPU_HPP_
#define _C4_CPU_HPP_
/** @file cpu.hpp Provides processor information macros
* @ingroup basic_headers */
// see also https://sourceforge.net/p/predef/wiki/Architectures/
// see also https://sourceforge.net/p/predef/wiki/Endianness/
// see also https://github.com/googlesamples/android-ndk/blob/android-mk/hello-jni/jni/hello-jni.c
// see http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h
#ifdef __ORDER_LITTLE_ENDIAN__
#define _C4EL __ORDER_LITTLE_ENDIAN__
#else
#define _C4EL 1234
#endif
#ifdef __ORDER_BIG_ENDIAN__
#define _C4EB __ORDER_BIG_ENDIAN__
#else
#define _C4EB 4321
#endif
// mixed byte order (eg, PowerPC or ia64)
#define _C4EM 1111
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
#define C4_CPU_X86_64
#define C4_WORDSIZE 8
#define C4_BYTE_ORDER _C4EL
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
#define C4_CPU_X86
#define C4_WORDSIZE 4
#define C4_BYTE_ORDER _C4EL
#elif defined(__arm__) || defined(_M_ARM) \
|| defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64)
#if defined(__aarch64__) || defined(_M_ARM64)
#define C4_CPU_ARM64
#define C4_CPU_ARMV8
#define C4_WORDSIZE 8
#else
#define C4_CPU_ARM
#define C4_WORDSIZE 4
#if defined(__ARM_ARCH_8__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8)
#define C4_CPU_ARMV8
#elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \
|| defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \
|| defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \
|| (defined(_M_ARM) && _M_ARM >= 7)
#define C4_CPU_ARMV7
#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|| defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \
|| defined(__ARM_ARCH_6M__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6)
#define C4_CPU_ARMV6
#elif defined(__ARM_ARCH_5TEJ__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5)
#define C4_CPU_ARMV5
#elif defined(__ARM_ARCH_4T__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4)
#define C4_CPU_ARMV4
#else
#error "unknown CPU architecture: ARM"
#endif
#endif
#if defined(__ARMEL__) || defined(__LITTLE_ENDIAN__) || defined(__AARCH64EL__) \
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
#define C4_BYTE_ORDER _C4EL
#elif defined(__ARMEB__) || defined(__BIG_ENDIAN__) || defined(__AARCH64EB__) \
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#define C4_BYTE_ORDER _C4EB
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__)
#define C4_BYTE_ORDER _C4EM
#else
#error "unknown endianness"
#endif
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
#define C4_CPU_IA64
#define C4_WORDSIZE 8
#define C4_BYTE_ORDER _C4EM
// itanium is bi-endian - check byte order below
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
|| defined(_M_MPPC) || defined(_M_PPC)
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
#define C4_CPU_PPC64
#define C4_WORDSIZE 8
#else
#define C4_CPU_PPC
#define C4_WORDSIZE 4
#endif
#define C4_BYTE_ORDER _C4EM
// ppc is bi-endian - check byte order below
#elif defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH_)
# define C4_CPU_S390_X
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EB
#elif defined(__riscv)
#if __riscv_xlen == 64
#define C4_CPU_RISCV64
#define C4_WORDSIZE 8
#else
#define C4_CPU_RISCV32
#define C4_WORDSIZE 4
#endif
#define C4_BYTE_ORDER _C4EL
#elif defined(__EMSCRIPTEN__)
# define C4_BYTE_ORDER _C4EL
# define C4_WORDSIZE 4
#elif defined(SWIG)
#error "please define CPU architecture macros when compiling with swig"
#else
#error "unknown CPU architecture"
#endif
#define C4_LITTLE_ENDIAN (C4_BYTE_ORDER == _C4EL)
#define C4_BIG_ENDIAN (C4_BYTE_ORDER == _C4EB)
#define C4_MIXED_ENDIAN (C4_BYTE_ORDER == _C4EM)
#endif /* _C4_CPU_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/cpu.hpp)
#endif
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/compiler.hpp
// https://github.com/biojppm/c4core/src/c4/compiler.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_COMPILER_HPP_
#define _C4_COMPILER_HPP_
/** @file compiler.hpp Provides compiler information macros
* @ingroup basic_headers */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/platform.hpp
//#include "c4/platform.hpp"
#if !defined(C4_PLATFORM_HPP_) && !defined(_C4_PLATFORM_HPP_)
#error "amalgamate: file c4/platform.hpp must have been included at this point"
#endif /* C4_PLATFORM_HPP_ */
// Compilers:
// C4_MSVC
// Visual Studio 2022: MSVC++ 17, 1930
// Visual Studio 2019: MSVC++ 16, 1920
// Visual Studio 2017: MSVC++ 15
// Visual Studio 2015: MSVC++ 14
// Visual Studio 2013: MSVC++ 13
// Visual Studio 2013: MSVC++ 12
// Visual Studio 2012: MSVC++ 11
// Visual Studio 2010: MSVC++ 10
// Visual Studio 2008: MSVC++ 09
// Visual Studio 2005: MSVC++ 08
// C4_CLANG
// C4_GCC
// C4_ICC (intel compiler)
/** @see http://sourceforge.net/p/predef/wiki/Compilers/ for a list of compiler identifier macros */
/** @see https://msdn.microsoft.com/en-us/library/b0084kay.aspx for VS2013 predefined macros */
#if defined(_MSC_VER)// && (defined(C4_WIN) || defined(C4_XBOX) || defined(C4_UE4))
# define C4_MSVC
# define C4_MSVC_VERSION_2022 17
# define C4_MSVC_VERSION_2019 16
# define C4_MSVC_VERSION_2017 15
# define C4_MSVC_VERSION_2015 14
# define C4_MSVC_VERSION_2013 12
# define C4_MSVC_VERSION_2012 11
# if _MSC_VER >= 1930
# define C4_MSVC_VERSION C4_MSVC_VERSION_2022 // visual studio 2022
# define C4_MSVC_2022
# elif _MSC_VER >= 1920
# define C4_MSVC_VERSION C_4MSVC_VERSION_2019 // visual studio 2019
# define C4_MSVC_2019
# elif _MSC_VER >= 1910
# define C4_MSVC_VERSION C4_MSVC_VERSION_2017 // visual studio 2017
# define C4_MSVC_2017
# elif _MSC_VER == 1900
# define C4_MSVC_VERSION C4_MSVC_VERSION_2015 // visual studio 2015
# define C4_MSVC_2015
# elif _MSC_VER == 1800
# error "MSVC version not supported"
# define C4_MSVC_VERSION C4_MSVC_VERSION_2013 // visual studio 2013
# define C4_MSVC_2013
# elif _MSC_VER == 1700
# error "MSVC version not supported"
# define C4_MSVC_VERSION C4_MSVC_VERSION_2012 // visual studio 2012
# define C4_MSVC_2012
# elif _MSC_VER == 1600
# error "MSVC version not supported"
# define C4_MSVC_VERSION 10 // visual studio 2010
# define C4_MSVC_2010
# elif _MSC_VER == 1500
# error "MSVC version not supported"
# define C4_MSVC_VERSION 09 // visual studio 2008
# define C4_MSVC_2008
# elif _MSC_VER == 1400
# error "MSVC version not supported"
# define C4_MSVC_VERSION 08 // visual studio 2005
# define C4_MSVC_2005
# else
# error "MSVC version not supported"
# endif // _MSC_VER
#else
# define C4_MSVC_VERSION 0 // visual studio not present
# define C4_GCC_LIKE
# ifdef __INTEL_COMPILER // check ICC before checking GCC, as ICC defines __GNUC__ too
# define C4_ICC
# define C4_ICC_VERSION __INTEL_COMPILER
# elif defined(__APPLE_CC__)
# define C4_XCODE
# if defined(__clang__)
# define C4_CLANG
# ifndef __apple_build_version__
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
# else
# define C4_CLANG_VERSION __apple_build_version__
# endif
# else
# define C4_XCODE_VERSION __APPLE_CC__
# endif
# elif defined(__clang__)
# define C4_CLANG
# ifndef __apple_build_version__
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
# else
# define C4_CLANG_VERSION __apple_build_version__
# endif
# elif defined(__GNUC__)
# define C4_GCC
# if defined(__GNUC_PATCHLEVEL__)
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
# else
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, 0)
# endif
# if __GNUC__ < 5
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 8
// provided by cmake sub-project
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/gcc-4.8.hpp
//# include "c4/gcc-4.8.hpp"
#if !defined(C4_GCC-4_8_HPP_) && !defined(_C4_GCC-4_8_HPP_)
#error "amalgamate: file c4/gcc-4.8.hpp must have been included at this point"
#endif /* C4_GCC-4_8_HPP_ */
# else
// we do not support GCC < 4.8:
// * misses std::is_trivially_copyable
// * misses std::align
// * -Wshadow has false positives when a local function parameter has the same name as a method
# error "GCC < 4.8 is not supported"
# endif
# endif
# endif
#endif // defined(C4_WIN) && defined(_MSC_VER)
#endif /* _C4_COMPILER_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/compiler.hpp)
// these includes are needed to work around conditional
// includes in the gcc4.8 shim
#include <cstdint>
#include <type_traits>
#include <cstring>
//********************************************************************************
//--------------------------------------------------------------------------------
// cmake/compat/c4/gcc-4.8.hpp
// https://github.com/biojppm/c4core/cmake/compat/c4/gcc-4.8.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_COMPAT_GCC_4_8_HPP_
#define _C4_COMPAT_GCC_4_8_HPP_
#if __GNUC__ == 4 && __GNUC_MINOR__ >= 8
/* STL polyfills for old GNU compilers */
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
#if __cplusplus
//included above:
//#include <cstdint>
//included above:
//#include <type_traits>
namespace std {
template<typename _Tp>
struct is_trivially_copyable : public integral_constant<bool,
is_destructible<_Tp>::value && __has_trivial_destructor(_Tp) &&
(__has_trivial_constructor(_Tp) || __has_trivial_copy(_Tp) || __has_trivial_assign(_Tp))>
{ };
template<typename _Tp>
using is_trivially_copy_constructible = has_trivial_copy_constructor<_Tp>;
template<typename _Tp>
using is_trivially_default_constructible = has_trivial_default_constructor<_Tp>;
template<typename _Tp>
using is_trivially_copy_assignable = has_trivial_copy_assign<_Tp>;
/* not supported */
template<typename _Tp>
struct is_trivially_move_constructible : false_type
{ };
/* not supported */
template<typename _Tp>
struct is_trivially_move_assignable : false_type
{ };
inline void *align(size_t __align, size_t __size, void*& __ptr, size_t& __space) noexcept
{
if (__space < __size)
return nullptr;
const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
const auto __aligned = (__intptr - 1u + __align) & -__align;
const auto __diff = __aligned - __intptr;
if (__diff > (__space - __size))
return nullptr;
else
{
__space -= __diff;
return __ptr = reinterpret_cast<void*>(__aligned);
}
}
typedef long double max_align_t ;
}
#else // __cplusplus
//included above:
//#include <string.h>
// see https://sourceware.org/bugzilla/show_bug.cgi?id=25399 (ubuntu gcc-4.8)
#define memset(s, c, count) __builtin_memset(s, c, count)
#endif // __cplusplus
#endif // __GNUC__ == 4 && __GNUC_MINOR__ >= 8
#endif // _C4_COMPAT_GCC_4_8_HPP_
// (end https://github.com/biojppm/c4core/cmake/compat/c4/gcc-4.8.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/language.hpp
// https://github.com/biojppm/c4core/src/c4/language.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_LANGUAGE_HPP_
#define _C4_LANGUAGE_HPP_
/** @file language.hpp Provides language standard information macros and
* compiler agnostic utility macros: namespace facilities, function attributes,
* variable attributes, etc.
* @ingroup basic_headers */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/preprocessor.hpp
//#include "c4/preprocessor.hpp"
#if !defined(C4_PREPROCESSOR_HPP_) && !defined(_C4_PREPROCESSOR_HPP_)
#error "amalgamate: file c4/preprocessor.hpp must have been included at this point"
#endif /* C4_PREPROCESSOR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/compiler.hpp
//#include "c4/compiler.hpp"
#if !defined(C4_COMPILER_HPP_) && !defined(_C4_COMPILER_HPP_)
#error "amalgamate: file c4/compiler.hpp must have been included at this point"
#endif /* C4_COMPILER_HPP_ */
/* Detect C++ standard.
* @see http://stackoverflow.com/a/7132549/5875572 */
#ifndef C4_CPP
# ifdef _MSC_VER
# if _MSC_VER >= 1910 // >VS2015: VS2017, VS2019
# if (!defined(_MSVC_LANG))
# error _MSVC not defined
# endif
# if _MSVC_LANG >= 201705L
# define C4_CPP 20
# define C4_CPP20
# elif _MSVC_LANG == 201703L
# define C4_CPP 17
# define C4_CPP17
# elif _MSVC_LANG >= 201402L
# define C4_CPP 14
# define C4_CPP14
# elif _MSVC_LANG >= 201103L
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# else
# if _MSC_VER == 1900
# define C4_CPP 14 // VS2015 is c++14 https://devblogs.microsoft.com/cppblog/c111417-features-in-vs-2015-rtm/
# define C4_CPP14
# elif _MSC_VER == 1800 // VS2013
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# endif
# elif defined(__INTEL_COMPILER) // https://software.intel.com/en-us/node/524490
# ifdef __INTEL_CXX20_MODE__ // not sure about this
# define C4_CPP 20
# define C4_CPP20
# elif defined __INTEL_CXX17_MODE__ // not sure about this
# define C4_CPP 17
# define C4_CPP17
# elif defined __INTEL_CXX14_MODE__ // not sure about this
# define C4_CPP 14
# define C4_CPP14
# elif defined __INTEL_CXX11_MODE__
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# else
# ifndef __cplusplus
# error __cplusplus is not defined?
# endif
# if __cplusplus == 1
# error cannot handle __cplusplus==1
# elif __cplusplus >= 201709L
# define C4_CPP 20
# define C4_CPP20
# elif __cplusplus >= 201703L
# define C4_CPP 17
# define C4_CPP17
# elif __cplusplus >= 201402L
# define C4_CPP 14
# define C4_CPP14
# elif __cplusplus >= 201103L
# define C4_CPP 11
# define C4_CPP11
# elif __cplusplus >= 199711L
# error C++ lesser than C++11 not supported
# endif
# endif
#else
# ifdef C4_CPP == 20
# define C4_CPP20
# elif C4_CPP == 17
# define C4_CPP17
# elif C4_CPP == 14
# define C4_CPP14
# elif C4_CPP == 11
# define C4_CPP11
# elif C4_CPP == 98
# define C4_CPP98
# error C++ lesser than C++11 not supported
# else
# error C4_CPP must be one of 20, 17, 14, 11, 98
# endif
#endif
#ifdef C4_CPP20
# define C4_CPP17
# define C4_CPP14
# define C4_CPP11
#elif defined(C4_CPP17)
# define C4_CPP14
# define C4_CPP11
#elif defined(C4_CPP14)
# define C4_CPP11
#endif
/** lifted from this answer: http://stackoverflow.com/a/20170989/5875572 */
#ifndef _MSC_VER
# if __cplusplus < 201103
# define C4_CONSTEXPR11
# define C4_CONSTEXPR14
//# define C4_NOEXCEPT
# elif __cplusplus == 201103
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14
//# define C4_NOEXCEPT noexcept
# else
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14 constexpr
//# define C4_NOEXCEPT noexcept
# endif
#else // _MSC_VER
# if _MSC_VER < 1900
# define C4_CONSTEXPR11
# define C4_CONSTEXPR14
//# define C4_NOEXCEPT
# elif _MSC_VER < 2000
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14
//# define C4_NOEXCEPT noexcept
# else
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14 constexpr
//# define C4_NOEXCEPT noexcept
# endif
#endif // _MSC_VER
#if C4_CPP < 17
#define C4_IF_CONSTEXPR
#define C4_INLINE_CONSTEXPR constexpr
#else
#define C4_IF_CONSTEXPR constexpr
#define C4_INLINE_CONSTEXPR inline constexpr
#endif
//------------------------------------------------------------
#define _C4_BEGIN_NAMESPACE(ns) namespace ns {
#define _C4_END_NAMESPACE(ns) }
// MSVC cant handle the C4_FOR_EACH macro... need to fix this
//#define C4_BEGIN_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_BEGIN_NAMESPACE, , __VA_ARGS__)
//#define C4_END_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_END_NAMESPACE, , __VA_ARGS__)
#define C4_BEGIN_NAMESPACE(ns) namespace ns {
#define C4_END_NAMESPACE(ns) }
#define C4_BEGIN_HIDDEN_NAMESPACE namespace /*hidden*/ {
#define C4_END_HIDDEN_NAMESPACE } /* namespace hidden */
//------------------------------------------------------------
#ifndef C4_API
# if defined(_MSC_VER)
# if defined(C4_EXPORT)
# define C4_API __declspec(dllexport)
# elif defined(C4_IMPORT)
# define C4_API __declspec(dllimport)
# else
# define C4_API
# endif
# else
# define C4_API
# endif
#endif
#ifndef _MSC_VER ///< @todo assuming gcc-like compiler. check it is actually so.
/** for function attributes in GCC,
* @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes */
/** for __builtin functions in GCC,
* @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
# define C4_RESTRICT __restrict__
# define C4_RESTRICT_FN __attribute__((restrict))
# define C4_NO_INLINE __attribute__((noinline))
# define C4_ALWAYS_INLINE inline __attribute__((always_inline))
/** force inlining of every callee function */
# define C4_FLATTEN __atribute__((flatten))
/** mark a function as hot, ie as having a visible impact in CPU time
* thus making it more likely to inline, etc
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
# define C4_HOT __attribute__((hot))
/** mark a function as cold, ie as NOT having a visible impact in CPU time
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
# define C4_COLD __attribute__((cold))
# define C4_EXPECT(x, y) __builtin_expect(x, y) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
# define C4_LIKELY(x) __builtin_expect(x, 1)
# define C4_UNLIKELY(x) __builtin_expect(x, 0)
# define C4_UNREACHABLE() __builtin_unreachable()
# define C4_ATTR_FORMAT(...) //__attribute__((format (__VA_ARGS__))) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
# define C4_NORETURN __attribute__((noreturn))
#else
# define C4_RESTRICT __restrict
# define C4_RESTRICT_FN __declspec(restrict)
# define C4_NO_INLINE __declspec(noinline)
# define C4_ALWAYS_INLINE inline __forceinline
/** these are not available in VS AFAIK */
# define C4_FLATTEN
# define C4_HOT /** @todo */
# define C4_COLD /** @todo */
# define C4_EXPECT(x, y) x /** @todo */
# define C4_LIKELY(x) x /** @todo */
# define C4_UNLIKELY(x) x /** @todo */
# define C4_UNREACHABLE() /** @todo */
# define C4_ATTR_FORMAT(...) /** */
# define C4_NORETURN /** @todo */
#endif
#ifndef _MSC_VER
# define C4_FUNC __FUNCTION__
# define C4_PRETTY_FUNC __PRETTY_FUNCTION__
#else /// @todo assuming gcc-like compiler. check it is actually so.
# define C4_FUNC __FUNCTION__
# define C4_PRETTY_FUNC __FUNCSIG__
#endif
/** prevent compiler warnings about a specific var being unused */
#define C4_UNUSED(var) (void)var
#if C4_CPP >= 17
#define C4_STATIC_ASSERT(cond) static_assert(cond)
#else
#define C4_STATIC_ASSERT(cond) static_assert((cond), #cond)
#endif
#define C4_STATIC_ASSERT_MSG(cond, msg) static_assert((cond), #cond ": " msg)
/** @def C4_DONT_OPTIMIZE idea lifted from GoogleBenchmark.
* @see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark_api.h */
namespace c4 {
namespace detail {
#ifdef __GNUC__
# define C4_DONT_OPTIMIZE(var) c4::detail::dont_optimize(var)
template< class T >
C4_ALWAYS_INLINE void dont_optimize(T const& value) { asm volatile("" : : "g"(value) : "memory"); }
#else
# define C4_DONT_OPTIMIZE(var) c4::detail::use_char_pointer(reinterpret_cast< const char* >(&var))
void use_char_pointer(char const volatile*);
#endif
} // namespace detail
} // namespace c4
/** @def C4_KEEP_EMPTY_LOOP prevent an empty loop from being optimized out.
* @see http://stackoverflow.com/a/7084193/5875572 */
#ifndef _MSC_VER
# define C4_KEEP_EMPTY_LOOP { asm(""); }
#else
# define C4_KEEP_EMPTY_LOOP { char c; C4_DONT_OPTIMIZE(c); }
#endif
/** @def C4_VA_LIST_REUSE_MUST_COPY
* @todo <jpmag> I strongly suspect that this is actually only in UNIX platforms. revisit this. */
#ifdef __GNUC__
# define C4_VA_LIST_REUSE_MUST_COPY
#endif
#endif /* _C4_LANGUAGE_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/language.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/types.hpp
// https://github.com/biojppm/c4core/src/c4/types.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_TYPES_HPP_
#define _C4_TYPES_HPP_
//included above:
//#include <stdint.h>
#include <stddef.h>
//included above:
//#include <type_traits>
#if __cplusplus >= 201103L
#include <utility> // for integer_sequence and friends
#endif
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/preprocessor.hpp
//#include "c4/preprocessor.hpp"
#if !defined(C4_PREPROCESSOR_HPP_) && !defined(_C4_PREPROCESSOR_HPP_)
#error "amalgamate: file c4/preprocessor.hpp must have been included at this point"
#endif /* C4_PREPROCESSOR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/language.hpp
//#include "c4/language.hpp"
#if !defined(C4_LANGUAGE_HPP_) && !defined(_C4_LANGUAGE_HPP_)
#error "amalgamate: file c4/language.hpp must have been included at this point"
#endif /* C4_LANGUAGE_HPP_ */
/** @file types.hpp basic types, and utility macros and traits for types.
* @ingroup basic_headers */
/** @defgroup types Type utilities */
namespace c4 {
/** @defgroup intrinsic_types Intrinsic types
* @ingroup types
* @{ */
using cbyte = const char; /**< a constant byte */
using byte = char; /**< a mutable byte */
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using f32 = float;
using f64 = double;
using ssize_t = typename std::make_signed<size_t>::type;
/** @} */
//--------------------------------------------------
/** @defgroup utility_types Utility types
* @ingroup types
* @{ */
// some tag types
/** a tag type for initializing the containers with variadic arguments a la
* initializer_list, minus the initializer_list overload problems.
*/
struct aggregate_t {};
/** @see aggregate_t */
constexpr const aggregate_t aggregate{};
/** a tag type for specifying the initial capacity of allocatable contiguous storage */
struct with_capacity_t {};
/** @see with_capacity_t */
constexpr const with_capacity_t with_capacity{};
/** a tag type for disambiguating template parameter packs in variadic template overloads */
struct varargs_t {};
/** @see with_capacity_t */
constexpr const varargs_t varargs{};
//--------------------------------------------------
/** whether a value should be used in place of a const-reference in argument passing. */
template<class T>
struct cref_uses_val
{
enum { value = (
std::is_scalar<T>::value
||
(
#if C4_CPP >= 20
(std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value)
#else
std::is_pod<T>::value
#endif
&&
sizeof(T) <= sizeof(size_t))) };
};
/** utility macro to override the default behaviour for c4::fastcref<T>
@see fastcref */
#define C4_CREF_USES_VAL(T) \
template<> \
struct cref_uses_val<T> \
{ \
enum { value = true }; \
};
/** Whether to use pass-by-value or pass-by-const-reference in a function argument
* or return type. */
template<class T>
using fastcref = typename std::conditional<c4::cref_uses_val<T>::value, T, T const&>::type;
//--------------------------------------------------
/** Just what its name says. Useful sometimes as a default empty policy class. */
struct EmptyStruct
{
template<class... T> EmptyStruct(T && ...){}
};
/** Just what its name says. Useful sometimes as a default policy class to
* be inherited from. */
struct EmptyStructVirtual
{
virtual ~EmptyStructVirtual() = default;
template<class... T> EmptyStructVirtual(T && ...){}
};
/** */
template<class T>
struct inheritfrom : public T {};
//--------------------------------------------------
// Utilities to make a class obey size restrictions (eg, min size or size multiple of).
// DirectX usually makes this restriction with uniform buffers.
// This is also useful for padding to prevent false-sharing.
/** how many bytes must be added to size such that the result is at least minsize? */
C4_ALWAYS_INLINE constexpr size_t min_remainder(size_t size, size_t minsize) noexcept
{
return size < minsize ? minsize-size : 0;
}
/** how many bytes must be added to size such that the result is a multiple of multipleof? */
C4_ALWAYS_INLINE constexpr size_t mult_remainder(size_t size, size_t multipleof) noexcept
{
return (((size % multipleof) != 0) ? (multipleof-(size % multipleof)) : 0);
}
/* force the following class to be tightly packed. */
#pragma pack(push, 1)
/** pad a class with more bytes at the end.
* @see http://stackoverflow.com/questions/21092415/force-c-structure-to-pack-tightly */
template<class T, size_t BytesToPadAtEnd>
struct Padded : public T
{
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
char ___c4padspace___[BytesToPadAtEnd];
};
#pragma pack(pop)
/** When the padding argument is 0, we cannot declare the char[] array. */
template<class T>
struct Padded<T, 0> : public T
{
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
};
/** make T have a size which is at least Min bytes */
template<class T, size_t Min>
using MinSized = Padded<T, min_remainder(sizeof(T), Min)>;
/** make T have a size which is a multiple of Mult bytes */
template<class T, size_t Mult>
using MultSized = Padded<T, mult_remainder(sizeof(T), Mult)>;
/** make T have a size which is simultaneously:
* -bigger or equal than Min
* -a multiple of Mult */
template<class T, size_t Min, size_t Mult>
using MinMultSized = MultSized<MinSized<T, Min>, Mult>;
/** make T be suitable for use as a uniform buffer. (at least with DirectX). */
template<class T>
using UbufSized = MinMultSized<T, 64, 16>;
//-----------------------------------------------------------------------------
#define C4_NO_COPY_CTOR(ty) ty(ty const&) = delete
#define C4_NO_MOVE_CTOR(ty) ty(ty &&) = delete
#define C4_NO_COPY_ASSIGN(ty) ty& operator=(ty const&) = delete
#define C4_NO_MOVE_ASSIGN(ty) ty& operator=(ty &&) = delete
#define C4_DEFAULT_COPY_CTOR(ty) ty(ty const&) noexcept = default
#define C4_DEFAULT_MOVE_CTOR(ty) ty(ty &&) noexcept = default
#define C4_DEFAULT_COPY_ASSIGN(ty) ty& operator=(ty const&) noexcept = default
#define C4_DEFAULT_MOVE_ASSIGN(ty) ty& operator=(ty &&) noexcept = default
#define C4_NO_COPY_OR_MOVE_CTOR(ty) \
C4_NO_COPY_CTOR(ty); \
C4_NO_MOVE_CTOR(ty)
#define C4_NO_COPY_OR_MOVE_ASSIGN(ty) \
C4_NO_COPY_ASSIGN(ty); \
C4_NO_MOVE_ASSIGN(ty)
#define C4_NO_COPY_OR_MOVE(ty) \
C4_NO_COPY_OR_MOVE_CTOR(ty); \
C4_NO_COPY_OR_MOVE_ASSIGN(ty)
#define C4_DEFAULT_COPY_AND_MOVE_CTOR(ty) \
C4_DEFAULT_COPY_CTOR(ty); \
C4_DEFAULT_MOVE_CTOR(ty)
#define C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty) \
C4_DEFAULT_COPY_ASSIGN(ty); \
C4_DEFAULT_MOVE_ASSIGN(ty)
#define C4_DEFAULT_COPY_AND_MOVE(ty) \
C4_DEFAULT_COPY_AND_MOVE_CTOR(ty); \
C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty)
/** @see https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable */
#define C4_MUST_BE_TRIVIAL_COPY(ty) \
static_assert(std::is_trivially_copyable<ty>::value, #ty " must be trivially copyable")
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup traits_types Type traits utilities
* @ingroup types
* @{ */
// http://stackoverflow.com/questions/10821380/is-t-an-instance-of-a-template-in-c
template<template<typename...> class X, typename T> struct is_instance_of_tpl : std::false_type {};
template<template<typename...> class X, typename... Y> struct is_instance_of_tpl<X, X<Y...>> : std::true_type {};
//-----------------------------------------------------------------------------
/** SFINAE. use this macro to enable a template function overload
based on a compile-time condition.
@code
// define an overload for a non-pod type
template<class T, C4_REQUIRE_T(std::is_pod<T>::value)>
void foo() { std::cout << "pod type\n"; }
// define an overload for a non-pod type
template<class T, C4_REQUIRE_T(!std::is_pod<T>::value)>
void foo() { std::cout << "nonpod type\n"; }
struct non_pod
{
non_pod() : name("asdfkjhasdkjh") {}
const char *name;
};
int main()
{
foo<float>(); // prints "pod type"
foo<non_pod>(); // prints "nonpod type"
}
@endcode */
#define C4_REQUIRE_T(cond) typename std::enable_if<cond, bool>::type* = nullptr
/** enable_if for a return type
* @see C4_REQUIRE_T */
#define C4_REQUIRE_R(cond, type_) typename std::enable_if<cond, type_>::type
//-----------------------------------------------------------------------------
/** define a traits class reporting whether a type provides a member typedef */
#define C4_DEFINE_HAS_TYPEDEF(member_typedef) \
template<typename T> \
struct has_##stype \
{ \
private: \
\
typedef char yes; \
typedef struct { char array[2]; } no; \
\
template<typename C> \
static yes _test(typename C::member_typedef*); \
\
template<typename C> \
static no _test(...); \
\
public: \
\
enum { value = (sizeof(_test<T>(0)) == sizeof(yes)) }; \
\
}
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup type_declarations Type declaration utilities
* @ingroup types
* @{ */
#define _c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I) \
\
using size_type = I; \
using ssize_type = typename std::make_signed<I>::type; \
using difference_type = typename std::make_signed<I>::type; \
\
using value_type = T; \
using pointer = T*; \
using const_pointer = T const*; \
using reference = T&; \
using const_reference = T const&
#define _c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I) \
\
using size_type = I; \
using ssize_type = typename std::make_signed<I>::type; \
using difference_type = typename std::make_signed<I>::type; \
\
template<I n> using value_type = typename std::tuple_element< n, std::tuple<interior_types...>>::type; \
template<I n> using pointer = value_type<n>*; \
template<I n> using const_pointer = value_type<n> const*; \
template<I n> using reference = value_type<n>&; \
template<I n> using const_reference = value_type<n> const&
#define _c4_DEFINE_ARRAY_TYPES(T, I) \
\
_c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I); \
\
using iterator = T*; \
using const_iterator = T const*; \
using reverse_iterator = std::reverse_iterator<T*>; \
using const_reverse_iterator = std::reverse_iterator<T const*>
#define _c4_DEFINE_TUPLE_ARRAY_TYPES(interior_types, I) \
\
_c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I); \
\
template<I n> using iterator = value_type<n>*; \
template<I n> using const_iterator = value_type<n> const*; \
template<I n> using reverse_iterator = std::reverse_iterator< value_type<n>*>; \
template<I n> using const_reverse_iterator = std::reverse_iterator< value_type<n> const*>
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup compatility_utilities Backport implementation of some Modern C++ utilities
* @ingroup types
* @{ */
//-----------------------------------------------------------------------------
// index_sequence and friends are available only for C++14 and later.
// A C++11 implementation is provided here.
// This implementation was copied over from clang.
// see http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687
#if __cplusplus > 201103L
using std::integer_sequence;
using std::index_sequence;
using std::make_integer_sequence;
using std::make_index_sequence;
using std::index_sequence_for;
#else
/** C++11 implementation of integer sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class _Tp, _Tp... _Ip>
struct integer_sequence
{
static_assert(std::is_integral<_Tp>::value,
"std::integer_sequence can only be instantiated with an integral type" );
using value_type = _Tp;
static constexpr size_t size() noexcept { return sizeof...(_Ip); }
};
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<size_t... _Ip>
using index_sequence = integer_sequence<size_t, _Ip...>;
/** @cond DONT_DOCUMENT_THIS */
namespace __detail {
template<typename _Tp, size_t ..._Extra>
struct __repeat;
template<typename _Tp, _Tp ..._Np, size_t ..._Extra>
struct __repeat<integer_sequence<_Tp, _Np...>, _Extra...>
{
using type = integer_sequence<_Tp,
_Np...,
sizeof...(_Np) + _Np...,
2 * sizeof...(_Np) + _Np...,
3 * sizeof...(_Np) + _Np...,
4 * sizeof...(_Np) + _Np...,
5 * sizeof...(_Np) + _Np...,
6 * sizeof...(_Np) + _Np...,
7 * sizeof...(_Np) + _Np...,
_Extra...>;
};
template<size_t _Np> struct __parity;
template<size_t _Np> struct __make : __parity<_Np % 8>::template __pmake<_Np> {};
template<> struct __make<0> { using type = integer_sequence<size_t>; };
template<> struct __make<1> { using type = integer_sequence<size_t, 0>; };
template<> struct __make<2> { using type = integer_sequence<size_t, 0, 1>; };
template<> struct __make<3> { using type = integer_sequence<size_t, 0, 1, 2>; };
template<> struct __make<4> { using type = integer_sequence<size_t, 0, 1, 2, 3>; };
template<> struct __make<5> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4>; };
template<> struct __make<6> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5>; };
template<> struct __make<7> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5, 6>; };
template<> struct __parity<0> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type> {}; };
template<> struct __parity<1> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 1> {}; };
template<> struct __parity<2> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 2, _Np - 1> {}; };
template<> struct __parity<3> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<4> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<5> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<6> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<7> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 7, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<typename _Tp, typename _Up>
struct __convert
{
template<typename> struct __result;
template<_Tp ..._Np> struct __result<integer_sequence<_Tp, _Np...>>
{
using type = integer_sequence<_Up, _Np...>;
};
};
template<typename _Tp>
struct __convert<_Tp, _Tp>
{
template<typename _Up> struct __result
{
using type = _Up;
};
};
template<typename _Tp, _Tp _Np>
using __make_integer_sequence_unchecked = typename __detail::__convert<size_t, _Tp>::template __result<typename __detail::__make<_Np>::type>::type;
template<class _Tp, _Tp _Ep>
struct __make_integer_sequence
{
static_assert(std::is_integral<_Tp>::value,
"std::make_integer_sequence can only be instantiated with an integral type" );
static_assert(0 <= _Ep, "std::make_integer_sequence input shall not be negative");
typedef __make_integer_sequence_unchecked<_Tp, _Ep> type;
};
} // namespace __detail
/** @endcond */
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class _Tp, _Tp _Np>
using make_integer_sequence = typename __detail::__make_integer_sequence<_Tp, _Np>::type;
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<size_t _Np>
using make_index_sequence = make_integer_sequence<size_t, _Np>;
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class... _Tp>
using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
#endif
/** @} */
} // namespace c4
#endif /* _C4_TYPES_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/types.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/config.hpp
// https://github.com/biojppm/c4core/src/c4/config.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_CONFIG_HPP_
#define _C4_CONFIG_HPP_
/** @defgroup basic_headers Basic headers
* @brief Headers providing basic macros, platform+cpu+compiler information,
* C++ facilities and basic typedefs. */
/** @file config.hpp Contains configuration defines and includes the basic_headers.
* @ingroup basic_headers */
//#define C4_DEBUG
#define C4_ERROR_SHOWS_FILELINE
//#define C4_ERROR_SHOWS_FUNC
//#define C4_ERROR_THROWS_EXCEPTION
//#define C4_NO_ALLOC_DEFAULTS
//#define C4_REDEFINE_CPPNEW
#ifndef C4_SIZE_TYPE
# define C4_SIZE_TYPE size_t
#endif
#ifndef C4_STR_SIZE_TYPE
# define C4_STR_SIZE_TYPE C4_SIZE_TYPE
#endif
#ifndef C4_TIME_TYPE
# define C4_TIME_TYPE double
#endif
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/export.hpp
//#include "c4/export.hpp"
#if !defined(C4_EXPORT_HPP_) && !defined(_C4_EXPORT_HPP_)
#error "amalgamate: file c4/export.hpp must have been included at this point"
#endif /* C4_EXPORT_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/preprocessor.hpp
//#include "c4/preprocessor.hpp"
#if !defined(C4_PREPROCESSOR_HPP_) && !defined(_C4_PREPROCESSOR_HPP_)
#error "amalgamate: file c4/preprocessor.hpp must have been included at this point"
#endif /* C4_PREPROCESSOR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/platform.hpp
//#include "c4/platform.hpp"
#if !defined(C4_PLATFORM_HPP_) && !defined(_C4_PLATFORM_HPP_)
#error "amalgamate: file c4/platform.hpp must have been included at this point"
#endif /* C4_PLATFORM_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/cpu.hpp
//#include "c4/cpu.hpp"
//#if !defined(C4_CPU_HPP_) && !defined(_C4_CPU_HPP_)
//#error "amalgamate: file c4/cpu.hpp must have been included at this point"
//#endif /* C4_CPU_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/compiler.hpp
//#include "c4/compiler.hpp"
#if !defined(C4_COMPILER_HPP_) && !defined(_C4_COMPILER_HPP_)
#error "amalgamate: file c4/compiler.hpp must have been included at this point"
#endif /* C4_COMPILER_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/language.hpp
//#include "c4/language.hpp"
#if !defined(C4_LANGUAGE_HPP_) && !defined(_C4_LANGUAGE_HPP_)
#error "amalgamate: file c4/language.hpp must have been included at this point"
#endif /* C4_LANGUAGE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/types.hpp
//#include "c4/types.hpp"
#if !defined(C4_TYPES_HPP_) && !defined(_C4_TYPES_HPP_)
#error "amalgamate: file c4/types.hpp must have been included at this point"
#endif /* C4_TYPES_HPP_ */
#endif // _C4_CONFIG_HPP_
// (end https://github.com/biojppm/c4core/src/c4/config.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/ext/debugbreak/debugbreak.h
// https://github.com/biojppm/c4core/src/c4/ext/debugbreak/debugbreak.h
//--------------------------------------------------------------------------------
//********************************************************************************
/* Copyright (c) 2011-2021, Scott Tsai
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DEBUG_BREAK_H
#define DEBUG_BREAK_H
#ifdef _MSC_VER
#define debug_break __debugbreak
#else
#ifdef __cplusplus
extern "C" {
#endif
#define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1
#define DEBUG_BREAK_USE_BULTIN_TRAP 2
#define DEBUG_BREAK_USE_SIGTRAP 3
#if defined(__i386__) || defined(__x86_64__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__inline__ static void trap_instruction(void)
{
__asm__ volatile("int $0x03");
}
#elif defined(__thumb__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
/* FIXME: handle __THUMB_INTERWORK__ */
__attribute__((always_inline))
__inline__ static void trap_instruction(void)
{
/* See 'arm-linux-tdep.c' in GDB source.
* Both instruction sequences below work. */
#if 1
/* 'eabi_linux_thumb_le_breakpoint' */
__asm__ volatile(".inst 0xde01");
#else
/* 'eabi_linux_thumb2_le_breakpoint' */
__asm__ volatile(".inst.w 0xf7f0a000");
#endif
/* Known problem:
* After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
* 'step' would keep getting stuck on the same instruction.
*
* Workaround: use the new GDB commands 'debugbreak-step' and
* 'debugbreak-continue' that become available
* after you source the script from GDB:
*
* $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...>
*
* 'debugbreak-step' would jump over the breakpoint instruction with
* roughly equivalent of:
* (gdb) set $instruction_len = 2
* (gdb) tbreak *($pc + $instruction_len)
* (gdb) jump *($pc + $instruction_len)
*/
}
#elif defined(__arm__) && !defined(__thumb__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline))
__inline__ static void trap_instruction(void)
{
/* See 'arm-linux-tdep.c' in GDB source,
* 'eabi_linux_arm_le_breakpoint' */
__asm__ volatile(".inst 0xe7f001f0");
/* Known problem:
* Same problem and workaround as Thumb mode */
}
#elif defined(__aarch64__) && defined(__APPLE__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
#elif defined(__aarch64__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline))
__inline__ static void trap_instruction(void)
{
/* See 'aarch64-tdep.c' in GDB source,
* 'aarch64_default_breakpoint' */
__asm__ volatile(".inst 0xd4200000");
}
#elif defined(__powerpc__)
/* PPC 32 or 64-bit, big or little endian */
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline))
__inline__ static void trap_instruction(void)
{
/* See 'rs6000-tdep.c' in GDB source,
* 'rs6000_breakpoint' */
__asm__ volatile(".4byte 0x7d821008");
/* Known problem:
* After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
* 'step' stuck on the same instruction ("twge r2,r2").
*
* The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py
* or manually jump over the instruction. */
}
#elif defined(__riscv)
/* RISC-V 32 or 64-bit, whether the "C" extension
* for compressed, 16-bit instructions are supported or not */
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline))
__inline__ static void trap_instruction(void)
{
/* See 'riscv-tdep.c' in GDB source,
* 'riscv_sw_breakpoint_from_kind' */
__asm__ volatile(".4byte 0x00100073");
}
#else
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP
#endif
#ifndef DEBUG_BREAK_IMPL
#error "debugbreak.h is not supported on this target"
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline))
__inline__ static void debug_break(void)
{
trap_instruction();
}
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
__attribute__((always_inline))
__inline__ static void debug_break(void)
{
__builtin_debugtrap();
}
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP
__attribute__((always_inline))
__inline__ static void debug_break(void)
{
__builtin_trap();
}
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP
#include <signal.h>
__attribute__((always_inline))
__inline__ static void debug_break(void)
{
raise(SIGTRAP);
}
#else
#error "invalid DEBUG_BREAK_IMPL value"
#endif
#ifdef __cplusplus
}
#endif
#endif /* ifdef _MSC_VER */
#endif /* ifndef DEBUG_BREAK_H */
// (end https://github.com/biojppm/c4core/src/c4/ext/debugbreak/debugbreak.h)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/error.hpp
// https://github.com/biojppm/c4core/src/c4/error.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_ERROR_HPP_
#define _C4_ERROR_HPP_
/** @file error.hpp Facilities for error reporting and runtime assertions. */
/** @defgroup error_checking Error checking */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
#ifdef _DOXYGEN_
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
* will throw an exception
* @ingroup error_checking */
# define C4_EXCEPTIONS_ENABLED
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
* will throw an exception
* @see C4_EXCEPTIONS_ENABLED
* @ingroup error_checking */
# define C4_ERROR_THROWS_EXCEPTION
/** evaluates to noexcept when C4_ERROR might be called and
* exceptions are disabled. Otherwise, defaults to nothing.
* @ingroup error_checking */
# define C4_NOEXCEPT
#endif // _DOXYGEN_
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# define C4_NOEXCEPT
#else
# define C4_NOEXCEPT noexcept
#endif
namespace c4 {
namespace detail {
struct fail_type__ {};
} // detail
} // c4
#define C4_STATIC_ERROR(dummy_type, errmsg) \
static_assert(std::is_same<dummy_type, c4::detail::fail_type__>::value, errmsg)
//-----------------------------------------------------------------------------
#define C4_ASSERT_SAME_TYPE(ty1, ty2) \
C4_STATIC_ASSERT(std::is_same<ty1 C4_COMMA_X ty2>::value)
#define C4_ASSERT_DIFF_TYPE(ty1, ty2) \
C4_STATIC_ASSERT( ! std::is_same<ty1 C4_COMMA_X ty2>::value)
//-----------------------------------------------------------------------------
#ifdef _DOXYGEN_
/** utility macro that triggers a breakpoint when
* the debugger is attached and NDEBUG is not defined.
* @ingroup error_checking */
# define C4_DEBUG_BREAK()
#endif // _DOXYGEN_
#ifdef NDEBUG
# define C4_DEBUG_BREAK()
#else
# ifdef __clang__
# pragma clang diagnostic push
# if !defined(__APPLE_CC__)
# if __clang_major__ >= 10
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
# endif
# else
# if __clang_major__ >= 13
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
# endif
# endif
# elif defined(__GNUC__)
# endif
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/ext/debugbreak/debugbreak.h
//# include <c4/ext/debugbreak/debugbreak.h>
#if !defined(DEBUG_BREAK_H) && !defined(_DEBUG_BREAK_H)
#error "amalgamate: file c4/ext/debugbreak/debugbreak.h must have been included at this point"
#endif /* DEBUG_BREAK_H */
# define C4_DEBUG_BREAK() if(c4::is_debugger_attached()) { ::debug_break(); }
# ifdef __clang__
# pragma clang diagnostic pop
# elif defined(__GNUC__)
# endif
#endif
namespace c4 {
C4CORE_EXPORT bool is_debugger_attached();
} // namespace c4
//-----------------------------------------------------------------------------
#ifdef __clang__
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
* clang requires switching off compiler warnings for pedantic mode.
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
#elif defined(__GNUC__)
/* GCC also issues a warning for zero-args calls to variadic macros.
* This warning is switched on with -pedantic and apparently there is no
* easy way to turn it off as with clang. But marking this as a system
* header works.
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
* @see http://stackoverflow.com/questions/35587137/ */
# pragma GCC system_header
#endif
//-----------------------------------------------------------------------------
namespace c4 {
typedef enum : uint32_t {
/** when an error happens and the debugger is attached, call C4_DEBUG_BREAK().
* Without effect otherwise. */
ON_ERROR_DEBUGBREAK = 0x01 << 0,
/** when an error happens log a message. */
ON_ERROR_LOG = 0x01 << 1,
/** when an error happens invoke a callback if it was set with
* set_error_callback(). */
ON_ERROR_CALLBACK = 0x01 << 2,
/** when an error happens call std::terminate(). */
ON_ERROR_ABORT = 0x01 << 3,
/** when an error happens and exceptions are enabled throw an exception.
* Without effect otherwise. */
ON_ERROR_THROW = 0x01 << 4,
/** the default flags. */
ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT
} ErrorFlags_e;
using error_flags = uint32_t;
C4CORE_EXPORT void set_error_flags(error_flags f);
C4CORE_EXPORT error_flags get_error_flags();
using error_callback_type = void (*)(const char* msg, size_t msg_size);
C4CORE_EXPORT void set_error_callback(error_callback_type cb);
C4CORE_EXPORT error_callback_type get_error_callback();
//-----------------------------------------------------------------------------
/** RAII class controling the error settings inside a scope. */
struct ScopedErrorSettings
{
error_flags m_flags;
error_callback_type m_callback;
explicit ScopedErrorSettings(error_callback_type cb)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_callback(cb);
}
explicit ScopedErrorSettings(error_flags flags)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_flags(flags);
}
explicit ScopedErrorSettings(error_flags flags, error_callback_type cb)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_flags(flags);
set_error_callback(cb);
}
~ScopedErrorSettings()
{
set_error_flags(m_flags);
set_error_callback(m_callback);
}
};
//-----------------------------------------------------------------------------
/** source location */
struct srcloc;
C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...);
C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...);
# define C4_ERROR(msg, ...) \
do { \
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
{ \
C4_DEBUG_BREAK() \
} \
c4::handle_error(C4_SRCLOC(), msg, ## __VA_ARGS__); \
} while(0)
# define C4_WARNING(msg, ...) \
c4::handle_warning(C4_SRCLOC(), msg, ## __VA_ARGS__)
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
struct srcloc
{
const char *file = "";
const char *func = "";
int line = 0;
};
#define C4_SRCLOC() c4::srcloc{__FILE__, C4_PRETTY_FUNC, __LINE__}
#elif defined(C4_ERROR_SHOWS_FILELINE)
struct srcloc
{
const char *file;
int line;
};
#define C4_SRCLOC() c4::srcloc{__FILE__, __LINE__}
#elif ! defined(C4_ERROR_SHOWS_FUNC)
struct srcloc
{
};
#define C4_SRCLOC() c4::srcloc()
#else
# error not implemented
#endif
//-----------------------------------------------------------------------------
// assertions
// Doxygen needs this so that only one definition counts
#ifdef _DOXYGEN_
/** Explicitly enables assertions, independently of NDEBUG status.
* This is meant to allow enabling assertions even when NDEBUG is defined.
* Defaults to undefined.
* @ingroup error_checking */
# define C4_USE_ASSERT
/** assert that a condition is true; this is turned off when NDEBUG
* is defined and C4_USE_ASSERT is not true.
* @ingroup error_checking */
# define C4_ASSERT
/** same as C4_ASSERT(), additionally prints a printf-formatted message
* @ingroup error_checking */
# define C4_ASSERT_MSG
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults
* to noexcept
* @ingroup error_checking */
# define C4_NOEXCEPT_A
#endif // _DOXYGEN_
#ifndef C4_USE_ASSERT
# ifdef NDEBUG
# define C4_USE_ASSERT 0
# else
# define C4_USE_ASSERT 1
# endif
#endif
#if C4_USE_ASSERT
# define C4_ASSERT(cond) C4_CHECK(cond)
# define C4_ASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
# define C4_ASSERT_IF(predicate, cond) if(predicate) { C4_ASSERT(cond); }
# define C4_NOEXCEPT_A C4_NOEXCEPT
#else
# define C4_ASSERT(cond)
# define C4_ASSERT_MSG(cond, /*fmt, */...)
# define C4_ASSERT_IF(predicate, cond)
# define C4_NOEXCEPT_A noexcept
#endif
//-----------------------------------------------------------------------------
// extreme assertions
// Doxygen needs this so that only one definition counts
#ifdef _DOXYGEN_
/** Explicitly enables extreme assertions; this is meant to allow enabling
* assertions even when NDEBUG is defined. Defaults to undefined.
* @ingroup error_checking */
# define C4_USE_XASSERT
/** extreme assertion: can be switched off independently of
* the regular assertion; use for example for bounds checking in hot code.
* Turned on only when C4_USE_XASSERT is defined
* @ingroup error_checking */
# define C4_XASSERT
/** same as C4_XASSERT(), and additionally prints a printf-formatted message
* @ingroup error_checking */
# define C4_XASSERT_MSG
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults to noexcept
* @ingroup error_checking */
# define C4_NOEXCEPT_X
#endif // _DOXYGEN_
#ifndef C4_USE_XASSERT
# define C4_USE_XASSERT C4_USE_ASSERT
#endif
#if C4_USE_XASSERT
# define C4_XASSERT(cond) C4_CHECK(cond)
# define C4_XASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
# define C4_XASSERT_IF(predicate, cond) if(predicate) { C4_XASSERT(cond); }
# define C4_NOEXCEPT_X C4_NOEXCEPT
#else
# define C4_XASSERT(cond)
# define C4_XASSERT_MSG(cond, /*fmt, */...)
# define C4_XASSERT_IF(predicate, cond)
# define C4_NOEXCEPT_X noexcept
#endif
//-----------------------------------------------------------------------------
// checks: never switched-off
/** Check that a condition is true, or raise an error when not
* true. Unlike C4_ASSERT(), this check is not disabled in non-debug
* builds.
* @see C4_ASSERT
* @ingroup error_checking
*
* @todo add constexpr-compatible compile-time assert:
* https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
*/
#define C4_CHECK(cond) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
C4_ERROR("check failed: %s", #cond); \
} \
} while(0)
/** like C4_CHECK(), and additionally log a printf-style message.
* @see C4_CHECK
* @ingroup error_checking */
#define C4_CHECK_MSG(cond, fmt, ...) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
C4_ERROR("check failed: " #cond "\n" fmt, ## __VA_ARGS__); \
} \
} while(0)
//-----------------------------------------------------------------------------
// Common error conditions
#define C4_NOT_IMPLEMENTED() C4_ERROR("NOT IMPLEMENTED")
#define C4_NOT_IMPLEMENTED_MSG(/*msg, */...) C4_ERROR("NOT IMPLEMENTED: " ## __VA_ARGS__)
#define C4_NOT_IMPLEMENTED_IF(condition) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED"); } } while(0)
#define C4_NOT_IMPLEMENTED_IF_MSG(condition, /*msg, */...) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED: " ## __VA_ARGS__); } } while(0)
#define C4_NEVER_REACH() do { C4_ERROR("never reach this point"); C4_UNREACHABLE(); } while(0)
#define C4_NEVER_REACH_MSG(/*msg, */...) do { C4_ERROR("never reach this point: " ## __VA_ARGS__); C4_UNREACHABLE(); } while(0)
//-----------------------------------------------------------------------------
// helpers for warning suppression
// idea adapted from https://github.com/onqtam/doctest/
#ifdef C4_MSVC
#define C4_SUPPRESS_WARNING_MSVC_PUSH __pragma(warning(push))
#define C4_SUPPRESS_WARNING_MSVC(w) __pragma(warning(disable : w))
#define C4_SUPPRESS_WARNING_MSVC_POP __pragma(warning(pop))
#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_MSVC_PUSH \
C4_SUPPRESS_WARNING_MSVC(w)
#else // C4_MSVC
#define C4_SUPPRESS_WARNING_MSVC_PUSH
#define C4_SUPPRESS_WARNING_MSVC(w)
#define C4_SUPPRESS_WARNING_MSVC_POP
#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w)
#endif // C4_MSVC
#ifdef C4_CLANG
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
#define C4_SUPPRESS_WARNING_CLANG_PUSH _Pragma("clang diagnostic push")
#define C4_SUPPRESS_WARNING_CLANG(w) C4_PRAGMA_TO_STR(clang diagnostic ignored w)
#define C4_SUPPRESS_WARNING_CLANG_POP _Pragma("clang diagnostic pop")
#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_CLANG_PUSH \
C4_SUPPRESS_WARNING_CLANG(w)
#else // C4_CLANG
#define C4_SUPPRESS_WARNING_CLANG_PUSH
#define C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_CLANG_POP
#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w)
#endif // C4_CLANG
#ifdef C4_GCC
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
#define C4_SUPPRESS_WARNING_GCC_PUSH _Pragma("GCC diagnostic push")
#define C4_SUPPRESS_WARNING_GCC(w) C4_PRAGMA_TO_STR(GCC diagnostic ignored w)
#define C4_SUPPRESS_WARNING_GCC_POP _Pragma("GCC diagnostic pop")
#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_GCC_PUSH \
C4_SUPPRESS_WARNING_GCC(w)
#else // C4_GCC
#define C4_SUPPRESS_WARNING_GCC_PUSH
#define C4_SUPPRESS_WARNING_GCC(w)
#define C4_SUPPRESS_WARNING_GCC_POP
#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w)
#endif // C4_GCC
#define C4_SUPPRESS_WARNING_GCC_CLANG_PUSH \
C4_SUPPRESS_WARNING_GCC_PUSH \
C4_SUPPRESS_WARNING_CLANG_PUSH
#define C4_SUPPRESS_WARNING_GCC_CLANG(w) \
C4_SUPPRESS_WARNING_GCC(w) \
C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_POP \
C4_SUPPRESS_WARNING_GCC_POP \
C4_SUPPRESS_WARNING_CLANG_POP
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#endif /* _C4_ERROR_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/error.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/memory_util.hpp
// https://github.com/biojppm/c4core/src/c4/memory_util.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_MEMORY_UTIL_HPP_
#define _C4_MEMORY_UTIL_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
//included above:
//#include <string.h>
/** @file memory_util.hpp Some memory utilities. */
namespace c4 {
/** set the given memory to zero */
C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes)
{
memset(mem, 0, num_bytes);
}
/** set the given memory to zero */
template<class T>
C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms)
{
memset(mem, 0, sizeof(T) * num_elms);
}
/** set the given memory to zero */
template<class T>
C4_ALWAYS_INLINE void mem_zero(T* mem)
{
memset(mem, 0, sizeof(T));
}
bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb);
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T>
bool is_aligned(T *ptr, size_t alignment=alignof(T))
{
return (uintptr_t(ptr) & (alignment - 1)) == 0u;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// least significant bit
/** least significant bit; this function is constexpr-14 because of the local
* variable */
template<class I>
C4_CONSTEXPR14 I lsb(I v)
{
if(!v) return 0;
I b = 0;
while((v & I(1)) == I(0))
{
v >>= 1;
++b;
}
return b;
}
namespace detail {
template<class I, I val, I num_bits, bool finished>
struct _lsb11;
template<class I, I val, I num_bits>
struct _lsb11< I, val, num_bits, false>
{
enum : I { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num };
};
template<class I, I val, I num_bits>
struct _lsb11<I, val, num_bits, true>
{
enum : I { num = num_bits };
};
} // namespace detail
/** TMP version of lsb(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see lsb */
template<class I, I number>
struct lsb11
{
static_assert(number != 0, "lsb: number must be nonzero");
enum : I { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// most significant bit
/** most significant bit; this function is constexpr-14 because of the local
* variable
* @todo implement faster version
* @see https://stackoverflow.com/questions/2589096/find-most-significant-bit-left-most-that-is-set-in-a-bit-array
*/
template<class I>
C4_CONSTEXPR14 I msb(I v)
{
// TODO:
//
//int n;
//if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, n |= 32;
//if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, n |= 16;
//if(input_num & uint64_t( 0xff00)) input_num >>= 8, n |= 8;
//if(input_num & uint64_t( 0xf0)) input_num >>= 4, n |= 4;
//if(input_num & uint64_t( 0xc)) input_num >>= 2, n |= 2;
//if(input_num & uint64_t( 0x2)) input_num >>= 1, n |= 1;
if(!v) return static_cast<I>(-1);
I b = 0;
while(v != 0)
{
v >>= 1;
++b;
}
return b-1;
}
namespace detail {
template<class I, I val, I num_bits, bool finished>
struct _msb11;
template<class I, I val, I num_bits>
struct _msb11< I, val, num_bits, false>
{
enum : I { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num };
};
template<class I, I val, I num_bits>
struct _msb11<I, val, num_bits, true>
{
static_assert(val == 0, "bad implementation");
enum : I { num = num_bits-1 };
};
} // namespace detail
/** TMP version of msb(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see msb */
template<class I, I number>
struct msb11
{
enum : I { value = detail::_msb11<I, number, 0, (number==I(0))>::num };
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** return a mask with all bits set [first_bit,last_bit[; this function
* is constexpr-14 because of the local variables */
template<class I>
C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit)
{
I r = 0;
constexpr const I o = 1;
for(I i = first_bit; i < last_bit; ++i)
{
r |= (o << i);
}
return r;
}
namespace detail {
template<class I, I val, I first, I last, bool finished>
struct _ctgmsk11;
template<class I, I val, I first, I last>
struct _ctgmsk11< I, val, first, last, true>
{
enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value };
};
template<class I, I val, I first, I last>
struct _ctgmsk11< I, val, first, last, false>
{
enum : I { value = val };
};
} // namespace detail
/** TMP version of contiguous_mask(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see contiguous_mask */
template<class I, I first_bit, I last_bit>
struct contiguous_mask11
{
enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value };
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** use Empty Base Class Optimization to reduce the size of a pair of
* potentially empty types*/
namespace detail {
typedef enum {
tpc_same,
tpc_same_empty,
tpc_both_empty,
tpc_first_empty,
tpc_second_empty,
tpc_general
} TightPairCase_e;
template<class First, class Second>
constexpr TightPairCase_e tpc_which_case()
{
return std::is_same<First, Second>::value ?
std::is_empty<First>::value ?
tpc_same_empty
:
tpc_same
:
std::is_empty<First>::value && std::is_empty<Second>::value ?
tpc_both_empty
:
std::is_empty<First>::value ?
tpc_first_empty
:
std::is_empty<Second>::value ?
tpc_second_empty
:
tpc_general
;
}
template<class First, class Second, TightPairCase_e Case>
struct tight_pair
{
private:
First m_first;
Second m_second;
public:
using first_type = First;
using second_type = Second;
tight_pair() : m_first(), m_second() {}
tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_same_empty> : public First
{
static_assert(std::is_same<First, Second>::value, "bad implementation");
using first_type = First;
using second_type = Second;
tight_pair() : First() {}
tight_pair(First const& f, Second const& /*s*/) : First(f) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_both_empty> : public First, public Second
{
using first_type = First;
using second_type = Second;
tight_pair() : First(), Second() {}
tight_pair(First const& f, Second const& s) : First(f), Second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_same> : public First
{
Second m_second;
using first_type = First;
using second_type = Second;
tight_pair() : First() {}
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_first_empty> : public First
{
Second m_second;
using first_type = First;
using second_type = Second;
tight_pair() : First(), m_second() {}
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_second_empty> : public Second
{
First m_first;
using first_type = First;
using second_type = Second;
tight_pair() : Second(), m_first() {}
tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
};
} // namespace detail
template<class First, class Second>
using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>;
} // namespace c4
#endif /* _C4_MEMORY_UTIL_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/memory_util.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/memory_resource.hpp
// https://github.com/biojppm/c4core/src/c4/memory_resource.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_MEMORY_RESOURCE_HPP_
#define _C4_MEMORY_RESOURCE_HPP_
/** @file memory_resource.hpp Provides facilities to allocate typeless
* memory, via the memory resource model consecrated with C++17. */
/** @defgroup memory memory utilities */
/** @defgroup raw_memory_alloc Raw memory allocation
* @ingroup memory
*/
/** @defgroup memory_resources Memory resources
* @ingroup memory
*/
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
namespace c4 {
// need these forward decls here
struct MemoryResource;
struct MemoryResourceMalloc;
struct MemoryResourceStack;
MemoryResourceMalloc* get_memory_resource_malloc();
MemoryResourceStack* get_memory_resource_stack();
namespace detail { MemoryResource*& get_memory_resource(); }
// c-style allocation ---------------------------------------------------------
// this API provides aligned allocation functions.
// These functions forward the call to a user-modifiable function.
// aligned allocation.
/** Aligned allocation. Merely calls the current get_aalloc() function.
* @see get_aalloc()
* @ingroup raw_memory_alloc */
void* aalloc(size_t sz, size_t alignment);
/** Aligned free. Merely calls the current get_afree() function.
* @see get_afree()
* @ingroup raw_memory_alloc */
void afree(void* ptr);
/** Aligned reallocation. Merely calls the current get_arealloc() function.
* @see get_arealloc()
* @ingroup raw_memory_alloc */
void* arealloc(void* ptr, size_t oldsz, size_t newsz, size_t alignment);
// allocation setup facilities.
/** Function pointer type for aligned allocation
* @see set_aalloc()
* @ingroup raw_memory_alloc */
using aalloc_pfn = void* (*)(size_t size, size_t alignment);
/** Function pointer type for aligned deallocation
* @see set_afree()
* @ingroup raw_memory_alloc */
using afree_pfn = void (*)(void *ptr);
/** Function pointer type for aligned reallocation
* @see set_arealloc()
* @ingroup raw_memory_alloc */
using arealloc_pfn = void* (*)(void *ptr, size_t oldsz, size_t newsz, size_t alignment);
// allocation function pointer setters/getters
/** Set the global aligned allocation function.
* @see aalloc()
* @see get_aalloc()
* @ingroup raw_memory_alloc */
void set_aalloc(aalloc_pfn fn);
/** Set the global aligned deallocation function.
* @see afree()
* @see get_afree()
* @ingroup raw_memory_alloc */
void set_afree(afree_pfn fn);
/** Set the global aligned reallocation function.
* @see arealloc()
* @see get_arealloc()
* @ingroup raw_memory_alloc */
void set_arealloc(arealloc_pfn fn);
/** Get the global aligned reallocation function.
* @see arealloc()
* @ingroup raw_memory_alloc */
aalloc_pfn get_aalloc();
/** Get the global aligned deallocation function.
* @see afree()
* @ingroup raw_memory_alloc */
afree_pfn get_afree();
/** Get the global aligned reallocation function.
* @see arealloc()
* @ingroup raw_memory_alloc */
arealloc_pfn get_arealloc();
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// c++-style allocation -------------------------------------------------------
/** C++17-style memory_resource base class. See http://en.cppreference.com/w/cpp/experimental/memory_resource
* @ingroup memory_resources */
struct MemoryResource
{
const char *name = nullptr;
virtual ~MemoryResource() {}
void* allocate(size_t sz, size_t alignment=alignof(max_align_t), void *hint=nullptr)
{
void *mem = this->do_allocate(sz, alignment, hint);
C4_CHECK_MSG(mem != nullptr, "could not allocate %lu bytes", sz);
return mem;
}
void* reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment=alignof(max_align_t))
{
void *mem = this->do_reallocate(ptr, oldsz, newsz, alignment);
C4_CHECK_MSG(mem != nullptr, "could not reallocate from %lu to %lu bytes", oldsz, newsz);
return mem;
}
void deallocate(void* ptr, size_t sz, size_t alignment=alignof(max_align_t))
{
this->do_deallocate(ptr, sz, alignment);
}
protected:
virtual void* do_allocate(size_t sz, size_t alignment, void* hint) = 0;
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) = 0;
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) = 0;
};
/** get the current global memory resource. To avoid static initialization
* order problems, this is implemented using a function call to ensure
* that it is available when first used.
* @ingroup memory_resources */
C4_ALWAYS_INLINE MemoryResource* get_memory_resource()
{
return detail::get_memory_resource();
}
/** set the global memory resource
* @ingroup memory_resources */
C4_ALWAYS_INLINE void set_memory_resource(MemoryResource* mr)
{
C4_ASSERT(mr != nullptr);
detail::get_memory_resource() = mr;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A c4::aalloc-based memory resource. Thread-safe if the implementation
* called by c4::aalloc() is safe.
* @ingroup memory_resources */
struct MemoryResourceMalloc : public MemoryResource
{
MemoryResourceMalloc() { name = "malloc"; }
virtual ~MemoryResourceMalloc() override {}
protected:
virtual void* do_allocate(size_t sz, size_t alignment, void *hint) override
{
C4_UNUSED(hint);
return c4::aalloc(sz, alignment);
}
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override
{
C4_UNUSED(sz);
C4_UNUSED(alignment);
c4::afree(ptr);
}
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override
{
return c4::arealloc(ptr, oldsz, newsz, alignment);
}
};
/** returns a malloc-based memory resource
* @ingroup memory_resources */
C4_ALWAYS_INLINE MemoryResourceMalloc* get_memory_resource_malloc()
{
/** @todo use a nifty counter:
* https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter */
static MemoryResourceMalloc mr;
return &mr;
}
namespace detail {
C4_ALWAYS_INLINE MemoryResource* & get_memory_resource()
{
/** @todo use a nifty counter:
* https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter */
thread_local static MemoryResource* mr = get_memory_resource_malloc();
return mr;
}
} // namespace detail
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
/** Allows a memory resource to obtain its memory from another memory resource.
* @ingroup memory_resources */
struct DerivedMemoryResource : public MemoryResource
{
public:
DerivedMemoryResource(MemoryResource *mr_=nullptr) : m_local(mr_ ? mr_ : get_memory_resource()) {}
private:
MemoryResource *m_local;
protected:
virtual void* do_allocate(size_t sz, size_t alignment, void* hint) override
{
return m_local->allocate(sz, alignment, hint);
}
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override
{
return m_local->reallocate(ptr, oldsz, newsz, alignment);
}
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override
{
return m_local->deallocate(ptr, sz, alignment);
}
};
/** Provides common facilities for memory resource consisting of a single memory block
* @ingroup memory_resources */
struct _MemoryResourceSingleChunk : public DerivedMemoryResource
{
C4_NO_COPY_OR_MOVE(_MemoryResourceSingleChunk);
using impl_type = DerivedMemoryResource;
public:
_MemoryResourceSingleChunk(MemoryResource *impl=nullptr) : DerivedMemoryResource(impl) { name = "linear_malloc"; }
/** initialize with owned memory, allocated from the given (or the global) memory resource */
_MemoryResourceSingleChunk(size_t sz, MemoryResource *impl=nullptr) : _MemoryResourceSingleChunk(impl) { acquire(sz); }
/** initialize with borrowed memory */
_MemoryResourceSingleChunk(void *mem, size_t sz) : _MemoryResourceSingleChunk() { acquire(mem, sz); }
virtual ~_MemoryResourceSingleChunk() override { release(); }
public:
void const* mem() const { return m_mem; }
size_t capacity() const { return m_size; }
size_t size() const { return m_pos; }
size_t slack() const { C4_ASSERT(m_size >= m_pos); return m_size - m_pos; }
public:
char *m_mem{nullptr};
size_t m_size{0};
size_t m_pos{0};
bool m_owner;
public:
/** set the internal pointer to the beginning of the linear buffer */
void clear() { m_pos = 0; }
/** initialize with owned memory, allocated from the global memory resource */
void acquire(size_t sz);
/** initialize with borrowed memory */
void acquire(void *mem, size_t sz);
/** release the memory */
void release();
};
} // namespace detail
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** provides a linear memory resource. Allocates incrementally from a linear
* buffer, without ever deallocating. Deallocations are a no-op, and the
* memory is freed only when the resource is release()d. The memory used by
* this object can be either owned or borrowed. When borrowed, no calls to
* malloc/free take place.
*
* @ingroup memory_resources */
struct MemoryResourceLinear : public detail::_MemoryResourceSingleChunk
{
C4_NO_COPY_OR_MOVE(MemoryResourceLinear);
public:
using detail::_MemoryResourceSingleChunk::_MemoryResourceSingleChunk;
protected:
virtual void* do_allocate(size_t sz, size_t alignment, void *hint) override;
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override;
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** provides a stack-type malloc-based memory resource.
* @ingroup memory_resources */
struct MemoryResourceStack : public detail::_MemoryResourceSingleChunk
{
C4_NO_COPY_OR_MOVE(MemoryResourceStack);
public:
using detail::_MemoryResourceSingleChunk::_MemoryResourceSingleChunk;
protected:
virtual void* do_allocate(size_t sz, size_t alignment, void *hint) override;
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override;
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** provides a linear array-based memory resource.
* @see MemoryResourceLinear
* @ingroup memory_resources */
template<size_t N>
struct MemoryResourceLinearArr : public MemoryResourceLinear
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4324) // structure was padded due to alignment specifier
#endif
alignas(alignof(max_align_t)) char m_arr[N];
#ifdef _MSC_VER
#pragma warning(pop)
#endif
MemoryResourceLinearArr() : MemoryResourceLinear(m_arr, N) { name = "linear_arr"; }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
struct AllocationCounts
{
struct Item
{
ssize_t allocs;
ssize_t size;
void add(size_t sz)
{
++allocs;
size += static_cast<ssize_t>(sz);
}
void rem(size_t sz)
{
--allocs;
size -= static_cast<ssize_t>(sz);
}
Item max(Item const& that) const
{
Item r(*this);
r.allocs = r.allocs > that.allocs ? r.allocs : that.allocs;
r.size = r.size > that.size ? r.size : that.size;
return r;
}
};
Item curr = {0, 0};
Item total = {0, 0};
Item max = {0, 0};
void clear_counts()
{
curr = {0, 0};
total = {0, 0};
max = {0, 0};
}
void update(AllocationCounts const& that)
{
curr.allocs += that.curr.allocs;
curr.size += that.curr.size;
total.allocs += that.total.allocs;
total.size += that.total.size;
max.allocs += that.max.allocs;
max.size += that.max.size;
}
void add_counts(void* ptr, size_t sz)
{
if(ptr == nullptr) return;
curr.add(sz);
total.add(sz);
max = max.max(curr);
}
void rem_counts(void *ptr, size_t sz)
{
if(ptr == nullptr) return;
curr.rem(sz);
}
AllocationCounts operator- (AllocationCounts const& that) const
{
AllocationCounts r(*this);
r.curr.allocs -= that.curr.allocs;
r.curr.size -= that.curr.size;
r.total.allocs -= that.total.allocs;
r.total.size -= that.total.size;
r.max.allocs -= that.max.allocs;
r.max.size -= that.max.size;
return r;
}
AllocationCounts operator+ (AllocationCounts const& that) const
{
AllocationCounts r(*this);
r.curr.allocs += that.curr.allocs;
r.curr.size += that.curr.size;
r.total.allocs += that.total.allocs;
r.total.size += that.total.size;
r.max.allocs += that.max.allocs;
r.max.size += that.max.size;
return r;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a MemoryResource which latches onto another MemoryResource
* and counts allocations and sizes.
* @ingroup memory_resources */
class MemoryResourceCounts : public MemoryResource
{
public:
MemoryResourceCounts() : m_resource(get_memory_resource())
{
C4_ASSERT(m_resource != this);
name = "MemoryResourceCounts";
}
MemoryResourceCounts(MemoryResource *res) : m_resource(res)
{
C4_ASSERT(m_resource != this);
name = "MemoryResourceCounts";
}
MemoryResource *resource() { return m_resource; }
AllocationCounts const& counts() const { return m_counts; }
protected:
MemoryResource *m_resource;
AllocationCounts m_counts;
protected:
virtual void* do_allocate(size_t sz, size_t alignment, void * /*hint*/) override
{
void *ptr = m_resource->allocate(sz, alignment);
m_counts.add_counts(ptr, sz);
return ptr;
}
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override
{
m_counts.rem_counts(ptr, sz);
m_resource->deallocate(ptr, sz, alignment);
}
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override
{
m_counts.rem_counts(ptr, oldsz);
void* nptr = m_resource->reallocate(ptr, oldsz, newsz, alignment);
m_counts.add_counts(nptr, newsz);
return nptr;
}
};
//-----------------------------------------------------------------------------
/** RAII class which binds a memory resource with a scope duration.
* @ingroup memory_resources */
struct ScopedMemoryResource
{
MemoryResource *m_original;
ScopedMemoryResource(MemoryResource *r)
:
m_original(get_memory_resource())
{
set_memory_resource(r);
}
~ScopedMemoryResource()
{
set_memory_resource(m_original);
}
};
//-----------------------------------------------------------------------------
/** RAII class which counts allocations and frees inside a scope. Can
* optionally set also the memory resource to be used.
* @ingroup memory_resources */
struct ScopedMemoryResourceCounts
{
MemoryResourceCounts mr;
ScopedMemoryResourceCounts() : mr()
{
set_memory_resource(&mr);
}
ScopedMemoryResourceCounts(MemoryResource *m) : mr(m)
{
set_memory_resource(&mr);
}
~ScopedMemoryResourceCounts()
{
set_memory_resource(mr.resource());
}
};
} // namespace c4
#endif /* _C4_MEMORY_RESOURCE_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/memory_resource.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/ctor_dtor.hpp
// https://github.com/biojppm/c4core/src/c4/ctor_dtor.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_CTOR_DTOR_HPP_
#define _C4_CTOR_DTOR_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/preprocessor.hpp
//#include "c4/preprocessor.hpp"
#if !defined(C4_PREPROCESSOR_HPP_) && !defined(_C4_PREPROCESSOR_HPP_)
#error "amalgamate: file c4/preprocessor.hpp must have been included at this point"
#endif /* C4_PREPROCESSOR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/language.hpp
//#include "c4/language.hpp"
#if !defined(C4_LANGUAGE_HPP_) && !defined(_C4_LANGUAGE_HPP_)
#error "amalgamate: file c4/language.hpp must have been included at this point"
#endif /* C4_LANGUAGE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/memory_util.hpp
//#include "c4/memory_util.hpp"
#if !defined(C4_MEMORY_UTIL_HPP_) && !defined(_C4_MEMORY_UTIL_HPP_)
#error "amalgamate: file c4/memory_util.hpp must have been included at this point"
#endif /* C4_MEMORY_UTIL_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
//included above:
//#include <type_traits>
//included above:
//#include <utility> // std::forward
/** @file ctor_dtor.hpp object construction and destruction facilities.
* Some of these are not yet available in C++11. */
namespace c4 {
/** default-construct an object, trivial version */
template <class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_default_constructible<U>::value, void>::type
construct(U *ptr) noexcept
{
memset(ptr, 0, sizeof(U));
}
/** default-construct an object, non-trivial version */
template<class U> C4_ALWAYS_INLINE typename std ::enable_if< ! std::is_trivially_default_constructible<U>::value, void>::type
construct(U* ptr) noexcept
{
new ((void*)ptr) U();
}
/** default-construct n objects, trivial version */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_default_constructible<U>::value, void>::type
construct_n(U* ptr, I n) noexcept
{
memset(ptr, 0, n * sizeof(U));
}
/** default-construct n objects, non-trivial version */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_default_constructible<U>::value, void>::type
construct_n(U* ptr, I n) noexcept
{
for(I i = 0; i < n; ++i)
{
new ((void*)(ptr + i)) U();
}
}
#ifdef __clang__
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
template<class U, class ...Args>
inline void construct(U* ptr, Args&&... args)
{
new ((void*)ptr) U(std::forward<Args>(args)...);
}
template<class U, class I, class ...Args>
inline void construct_n(U* ptr, I n, Args&&... args)
{
for(I i = 0; i < n; ++i)
{
new ((void*)(ptr + i)) U(args...);
}
}
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
//-----------------------------------------------------------------------------
// copy-construct
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_constructible<U>::value, void>::type
copy_construct(U* dst, U const* src) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, sizeof(U));
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_constructible<U>::value, void>::type
copy_construct(U* dst, U const* src)
{
C4_ASSERT(dst != src);
new ((void*)dst) U(*src);
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_constructible<U>::value, void>::type
copy_construct_n(U* dst, U const* src, I n) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, n * sizeof(U));
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_constructible<U>::value, void>::type
copy_construct_n(U* dst, U const* src, I n)
{
C4_ASSERT(dst != src);
for(I i = 0; i < n; ++i)
{
new ((void*)(dst + i)) U(*(src + i));
}
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type
copy_construct(U* dst, U src) noexcept // pass by value for scalar types
{
*dst = src;
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type
copy_construct(U* dst, U const& src) // pass by reference for non-scalar types
{
C4_ASSERT(dst != &src);
new ((void*)dst) U(src);
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type
copy_construct_n(U* dst, U src, I n) noexcept // pass by value for scalar types
{
for(I i = 0; i < n; ++i)
{
dst[i] = src;
}
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type
copy_construct_n(U* dst, U const& src, I n) // pass by reference for non-scalar types
{
C4_ASSERT(dst != &src);
for(I i = 0; i < n; ++i)
{
new ((void*)(dst + i)) U(src);
}
}
template<class U, size_t N>
C4_ALWAYS_INLINE void copy_construct(U (&dst)[N], U const (&src)[N]) noexcept
{
copy_construct_n(dst, src, N);
}
//-----------------------------------------------------------------------------
// copy-assign
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_assignable<U>::value, void>::type
copy_assign(U* dst, U const* src) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, sizeof(U));
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_assignable<U>::value, void>::type
copy_assign(U* dst, U const* src) noexcept
{
C4_ASSERT(dst != src);
*dst = *src;
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_assignable<U>::value, void>::type
copy_assign_n(U* dst, U const* src, I n) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, n * sizeof(U));
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_assignable<U>::value, void>::type
copy_assign_n(U* dst, U const* src, I n) noexcept
{
C4_ASSERT(dst != src);
for(I i = 0; i < n; ++i)
{
dst[i] = src[i];
}
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type
copy_assign(U* dst, U src) noexcept // pass by value for scalar types
{
*dst = src;
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type
copy_assign(U* dst, U const& src) noexcept // pass by reference for non-scalar types
{
C4_ASSERT(dst != &src);
*dst = src;
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type
copy_assign_n(U* dst, U src, I n) noexcept // pass by value for scalar types
{
for(I i = 0; i < n; ++i)
{
dst[i] = src;
}
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type
copy_assign_n(U* dst, U const& src, I n) noexcept // pass by reference for non-scalar types
{
C4_ASSERT(dst != &src);
for(I i = 0; i < n; ++i)
{
dst[i] = src;
}
}
template<class U, size_t N>
C4_ALWAYS_INLINE void copy_assign(U (&dst)[N], U const (&src)[N]) noexcept
{
copy_assign_n(dst, src, N);
}
//-----------------------------------------------------------------------------
// move-construct
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type
move_construct(U* dst, U* src) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, sizeof(U));
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type
move_construct(U* dst, U* src) noexcept
{
C4_ASSERT(dst != src);
new ((void*)dst) U(std::move(*src));
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type
move_construct_n(U* dst, U* src, I n) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, n * sizeof(U));
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type
move_construct_n(U* dst, U* src, I n) noexcept
{
C4_ASSERT(dst != src);
for(I i = 0; i < n; ++i)
{
new ((void*)(dst + i)) U(std::move(src[i]));
}
}
//-----------------------------------------------------------------------------
// move-assign
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_assignable<U>::value, void>::type
move_assign(U* dst, U* src) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, sizeof(U));
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_assignable<U>::value, void>::type
move_assign(U* dst, U* src) noexcept
{
C4_ASSERT(dst != src);
*dst = std::move(*src);
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_assignable<U>::value, void>::type
move_assign_n(U* dst, U* src, I n) noexcept
{
C4_ASSERT(dst != src);
memcpy(dst, src, n * sizeof(U));
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_assignable<U>::value, void>::type
move_assign_n(U* dst, U* src, I n) noexcept
{
C4_ASSERT(dst != src);
for(I i = 0; i < n; ++i)
{
*(dst + i) = std::move(*(src + i));
}
}
//-----------------------------------------------------------------------------
// destroy
template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_destructible<U>::value, void>::type
destroy(U* ptr) noexcept
{
C4_UNUSED(ptr); // nothing to do
}
template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_destructible<U>::value, void>::type
destroy(U* ptr) noexcept
{
ptr->~U();
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_destructible<U>::value, void>::type
destroy_n(U* ptr, I n) noexcept
{
C4_UNUSED(ptr);
C4_UNUSED(n); // nothing to do
}
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_destructible<U>::value, void>::type
destroy_n(U* ptr, I n) noexcept
{
for(I i = 0; i <n; ++i)
{
ptr[i].~U();
}
}
//-----------------------------------------------------------------------------
/** makes room at the beginning of buf, which has a current size of n */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type
make_room(U *buf, I bufsz, I room) C4_NOEXCEPT_A
{
C4_ASSERT(bufsz >= 0 && room >= 0);
if(room >= bufsz)
{
memcpy (buf + room, buf, bufsz * sizeof(U));
}
else
{
memmove(buf + room, buf, bufsz * sizeof(U));
}
}
/** makes room at the beginning of buf, which has a current size of bufsz */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type
make_room(U *buf, I bufsz, I room) C4_NOEXCEPT_A
{
C4_ASSERT(bufsz >= 0 && room >= 0);
if(room >= bufsz)
{
for(I i = 0; i < bufsz; ++i)
{
new ((void*)(buf + (i + room))) U(std::move(buf[i]));
}
}
else
{
for(I i = 0; i < bufsz; ++i)
{
I w = bufsz-1 - i; // do a backwards loop
new ((void*)(buf + (w + room))) U(std::move(buf[w]));
}
}
}
/** make room to the right of pos */
template<class U, class I>
C4_ALWAYS_INLINE void make_room(U *buf, I bufsz, I currsz, I pos, I room)
{
C4_ASSERT(pos >= 0 && pos <= currsz);
C4_ASSERT(currsz <= bufsz);
C4_ASSERT(room + currsz <= bufsz);
C4_UNUSED(bufsz);
make_room(buf + pos, currsz - pos, room);
}
/** make room to the right of pos, copying to the beginning of a different buffer */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type
make_room(U *dst, U const* src, I srcsz, I room, I pos) C4_NOEXCEPT_A
{
C4_ASSERT(srcsz >= 0 && room >= 0 && pos >= 0);
C4_ASSERT(pos < srcsz || (pos == 0 && srcsz == 0));
memcpy(dst , src , pos * sizeof(U));
memcpy(dst + room + pos, src + pos, (srcsz - pos) * sizeof(U));
}
/** make room to the right of pos, copying to the beginning of a different buffer */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type
make_room(U *dst, U const* src, I srcsz, I room, I pos)
{
C4_ASSERT(srcsz >= 0 && room >= 0 && pos >= 0);
C4_ASSERT(pos < srcsz || (pos == 0 && srcsz == 0));
for(I i = 0; i < pos; ++i)
{
new ((void*)(dst + i)) U(std::move(src[i]));
}
src += pos;
dst += room + pos;
for(I i = 0, e = srcsz - pos; i < e; ++i)
{
new ((void*)(dst + i)) U(std::move(src[i]));
}
}
template<class U, class I>
C4_ALWAYS_INLINE void make_room
(
U * dst, I dstsz,
U const* src, I srcsz,
I room, I pos
)
{
C4_ASSERT(pos >= 0 && pos < srcsz || (srcsz == 0 && pos == 0));
C4_ASSERT(pos >= 0 && pos < dstsz || (dstsz == 0 && pos == 0));
C4_ASSERT(srcsz+room <= dstsz);
C4_UNUSED(dstsz);
make_room(dst, src, srcsz, room, pos);
}
//-----------------------------------------------------------------------------
/** destroy room at the beginning of buf, which has a current size of n */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value || (std::is_standard_layout<U>::value && std::is_trivial<U>::value), void>::type
destroy_room(U *buf, I n, I room) C4_NOEXCEPT_A
{
C4_ASSERT(n >= 0 && room >= 0);
C4_ASSERT(room <= n);
if(room < n)
{
memmove(buf, buf + room, (n - room) * sizeof(U));
}
else
{
// nothing to do - no need to destroy scalar types
}
}
/** destroy room at the beginning of buf, which has a current size of n */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! (std::is_scalar<U>::value || (std::is_standard_layout<U>::value && std::is_trivial<U>::value)), void>::type
destroy_room(U *buf, I n, I room)
{
C4_ASSERT(n >= 0 && room >= 0);
C4_ASSERT(room <= n);
if(room < n)
{
for(I i = 0, e = n - room; i < e; ++i)
{
buf[i] = std::move(buf[i + room]);
}
}
else
{
for(I i = 0; i < n; ++i)
{
buf[i].~U();
}
}
}
/** destroy room to the right of pos, copying to a different buffer */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type
destroy_room(U *dst, U const* src, I n, I room, I pos) C4_NOEXCEPT_A
{
C4_ASSERT(n >= 0 && room >= 0 && pos >= 0);
C4_ASSERT(pos <n);
C4_ASSERT(pos + room <= n);
memcpy(dst, src, pos * sizeof(U));
memcpy(dst + pos, src + room + pos, (n - pos - room) * sizeof(U));
}
/** destroy room to the right of pos, copying to a different buffer */
template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type
destroy_room(U *dst, U const* src, I n, I room, I pos)
{
C4_ASSERT(n >= 0 && room >= 0 && pos >= 0);
C4_ASSERT(pos < n);
C4_ASSERT(pos + room <= n);
for(I i = 0; i < pos; ++i)
{
new ((void*)(dst + i)) U(std::move(src[i]));
}
src += room + pos;
dst += pos;
for(I i = 0, e = n - pos - room; i < e; ++i)
{
new ((void*)(dst + i)) U(std::move(src[i]));
}
}
} // namespace c4
#undef _C4REQUIRE
#endif /* _C4_CTOR_DTOR_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/ctor_dtor.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/allocator.hpp
// https://github.com/biojppm/c4core/src/c4/allocator.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_ALLOCATOR_HPP_
#define _C4_ALLOCATOR_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/memory_resource.hpp
//#include "c4/memory_resource.hpp"
#if !defined(C4_MEMORY_RESOURCE_HPP_) && !defined(_C4_MEMORY_RESOURCE_HPP_)
#error "amalgamate: file c4/memory_resource.hpp must have been included at this point"
#endif /* C4_MEMORY_RESOURCE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/ctor_dtor.hpp
//#include "c4/ctor_dtor.hpp"
#if !defined(C4_CTOR_DTOR_HPP_) && !defined(_C4_CTOR_DTOR_HPP_)
#error "amalgamate: file c4/ctor_dtor.hpp must have been included at this point"
#endif /* C4_CTOR_DTOR_HPP_ */
#include <memory> // std::allocator_traits
//included above:
//#include <type_traits>
/** @file allocator.hpp Contains classes to make typeful allocations (note
* that memory resources are typeless) */
/** @defgroup mem_res_providers Memory resource providers
* @brief Policy classes which provide a memory resource for
* use in an allocator.
* @ingroup memory
*/
/** @defgroup allocators Allocators
* @brief Lightweight classes that act as handles to specific memory
* resources and provide typeful memory.
* @ingroup memory
*/
namespace c4 {
namespace detail {
template<class T> inline size_t size_for (size_t num_objs) noexcept { return num_objs * sizeof(T); }
template< > inline size_t size_for<void>(size_t num_objs) noexcept { return num_objs; }
} // namespace detail
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** provides a per-allocator memory resource
* @ingroup mem_res_providers */
class MemRes
{
public:
MemRes() : m_resource(get_memory_resource()) {}
MemRes(MemoryResource* r) noexcept : m_resource(r ? r : get_memory_resource()) {}
inline MemoryResource* resource() const { return m_resource; }
private:
MemoryResource* m_resource;
};
/** the allocators using this will default to the global memory resource
* @ingroup mem_res_providers */
class MemResGlobal
{
public:
MemResGlobal() {}
MemResGlobal(MemoryResource* r) noexcept { C4_UNUSED(r); C4_ASSERT(r == get_memory_resource()); }
inline MemoryResource* resource() const { return get_memory_resource(); }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
template<class MemRes>
struct _AllocatorUtil;
template<class T, class ...Args>
struct has_no_alloc
: public std::integral_constant<bool,
!(std::uses_allocator<T, MemoryResource*>::value)
&& std::is_constructible<T, Args...>::value> {};
// std::uses_allocator_v<U, MemoryResource> && std::is_constructible<U, std::allocator_arg_t, MemoryResource*, Args...>
// ie can construct(std::allocator_arg_t, MemoryResource*, Args...)
template<class T, class ...Args>
struct has_alloc_arg
: public std::integral_constant<bool,
std::uses_allocator<T, MemoryResource*>::value
&& std::is_constructible<T, std::allocator_arg_t, MemoryResource*, Args...>::value> {};
// std::uses_allocator<U> && std::is_constructible<U, Args..., MemoryResource*>
// ie, can construct(Args..., MemoryResource*)
template<class T, class ...Args>
struct has_alloc
: public std::integral_constant<bool,
std::uses_allocator<T, MemoryResource*>::value
&& std::is_constructible<T, Args..., MemoryResource*>::value> {};
} // namespace detail
template<class MemRes>
struct detail::_AllocatorUtil : public MemRes
{
using MemRes::MemRes;
/** for construct:
* @see http://en.cppreference.com/w/cpp/experimental/polymorphic_allocator/construct */
// 1. types with no allocators
template <class U, class... Args>
C4_ALWAYS_INLINE typename std::enable_if<detail::has_no_alloc<U, Args...>::value, void>::type
construct(U *ptr, Args &&...args)
{
c4::construct(ptr, std::forward<Args>(args)...);
}
template<class U, class I, class... Args>
C4_ALWAYS_INLINE typename std::enable_if<detail::has_no_alloc<U, Args...>::value, void>::type
construct_n(U* ptr, I n, Args&&... args)
{
c4::construct_n(ptr, n, std::forward<Args>(args)...);
}
// 2. types using allocators (ie, containers)
// 2.1. can construct(std::allocator_arg_t, MemoryResource*, Args...)
template<class U, class... Args>
C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc_arg<U, Args...>::value, void>::type
construct(U* ptr, Args&&... args)
{
c4::construct(ptr, std::allocator_arg, this->resource(), std::forward<Args>(args)...);
}
template<class U, class I, class... Args>
C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc_arg<U, Args...>::value, void>::type
construct_n(U* ptr, I n, Args&&... args)
{
c4::construct_n(ptr, n, std::allocator_arg, this->resource(), std::forward<Args>(args)...);
}
// 2.2. can construct(Args..., MemoryResource*)
template<class U, class... Args>
C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc<U, Args...>::value, void>::type
construct(U* ptr, Args&&... args)
{
c4::construct(ptr, std::forward<Args>(args)..., this->resource());
}
template<class U, class I, class... Args>
C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc<U, Args...>::value, void>::type
construct_n(U* ptr, I n, Args&&... args)
{
c4::construct_n(ptr, n, std::forward<Args>(args)..., this->resource());
}
template<class U>
static C4_ALWAYS_INLINE void destroy(U* ptr)
{
c4::destroy(ptr);
}
template<class U, class I>
static C4_ALWAYS_INLINE void destroy_n(U* ptr, I n)
{
c4::destroy_n(ptr, n);
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** An allocator is simply a proxy to a memory resource.
* @param T
* @param MemResProvider
* @ingroup allocators */
template<class T, class MemResProvider=MemResGlobal>
class Allocator : public detail::_AllocatorUtil<MemResProvider>
{
public:
using impl_type = detail::_AllocatorUtil<MemResProvider>;
using value_type = T;
using pointer = T*;
using const_pointer = T const*;
using reference = T&;
using const_reference = T const&;
using size_type = size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assigment = std::true_type;
public:
template<class U, class MRProv>
bool operator== (Allocator<U, MRProv> const& that) const
{
return this->resource() == that.resource();
}
template<class U, class MRProv>
bool operator!= (Allocator<U, MRProv> const& that) const
{
return this->resource() != that.resource();
}
public:
template<class U, class MRProv> friend class Allocator;
template<class U>
struct rebind
{
using other = Allocator<U, MemResProvider>;
};
template<class U>
typename rebind<U>::other rebound()
{
return typename rebind<U>::other(*this);
}
public:
using impl_type::impl_type;
Allocator() : impl_type() {} // VS demands this
template<class U> Allocator(Allocator<U, MemResProvider> const& that) : impl_type(that.resource()) {}
Allocator(Allocator const&) = default;
Allocator(Allocator &&) = default;
Allocator& operator= (Allocator const&) = default; // WTF? why? @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator
Allocator& operator= (Allocator &&) = default;
/** returns a default-constructed polymorphic allocator object
* @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator/select_on_container_copy_construction */
Allocator select_on_container_copy_construct() const { return Allocator(*this); }
T* allocate(size_t num_objs, size_t alignment=alignof(T))
{
C4_ASSERT(this->resource() != nullptr);
C4_ASSERT(alignment >= alignof(T));
void* vmem = this->resource()->allocate(detail::size_for<T>(num_objs), alignment);
T* mem = static_cast<T*>(vmem);
return mem;
}
void deallocate(T * ptr, size_t num_objs, size_t alignment=alignof(T))
{
C4_ASSERT(this->resource() != nullptr);
C4_ASSERT(alignment>= alignof(T));
this->resource()->deallocate(ptr, detail::size_for<T>(num_objs), alignment);
}
T* reallocate(T* ptr, size_t oldnum, size_t newnum, size_t alignment=alignof(T))
{
C4_ASSERT(this->resource() != nullptr);
C4_ASSERT(alignment >= alignof(T));
void* vmem = this->resource()->reallocate(ptr, detail::size_for<T>(oldnum), detail::size_for<T>(newnum), alignment);
T* mem = static_cast<T*>(vmem);
return mem;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @ingroup allocators */
template<class T, size_t N=16, size_t Alignment=alignof(T), class MemResProvider=MemResGlobal>
class SmallAllocator : public detail::_AllocatorUtil<MemResProvider>
{
static_assert(Alignment >= alignof(T), "invalid alignment");
using impl_type = detail::_AllocatorUtil<MemResProvider>;
alignas(Alignment) char m_arr[N * sizeof(T)];
size_t m_num{0};
public:
using value_type = T;
using pointer = T*;
using const_pointer = T const*;
using reference = T&;
using const_reference = T const&;
using size_type = size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assigment = std::true_type;
template<class U>
bool operator== (SmallAllocator<U,N,Alignment,MemResProvider> const&) const
{
return false;
}
template<class U>
bool operator!= (SmallAllocator<U,N,Alignment,MemResProvider> const&) const
{
return true;
}
public:
template<class U, size_t, size_t, class> friend class SmallAllocator;
template<class U>
struct rebind
{
using other = SmallAllocator<U, N, alignof(U), MemResProvider>;
};
template<class U>
typename rebind<U>::other rebound()
{
return typename rebind<U>::other(*this);
}
public:
using impl_type::impl_type;
SmallAllocator() : impl_type() {} // VS demands this
template<class U, size_t N2, size_t A2, class MP2>
SmallAllocator(SmallAllocator<U,N2,A2,MP2> const& that) : impl_type(that.resource())
{
C4_ASSERT(that.m_num == 0);
}
SmallAllocator(SmallAllocator const&) = default;
SmallAllocator(SmallAllocator &&) = default;
SmallAllocator& operator= (SmallAllocator const&) = default; // WTF? why? @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator
SmallAllocator& operator= (SmallAllocator &&) = default;
/** returns a default-constructed polymorphic allocator object
* @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator/select_on_container_copy_construction */
SmallAllocator select_on_container_copy_construct() const { return SmallAllocator(*this); }
T* allocate(size_t num_objs, size_t alignment=Alignment)
{
C4_ASSERT(this->resource() != nullptr);
C4_ASSERT(alignment >= alignof(T));
void *vmem;
if(m_num + num_objs <= N)
{
vmem = (m_arr + m_num * sizeof(T));
}
else
{
vmem = this->resource()->allocate(num_objs * sizeof(T), alignment);
}
m_num += num_objs;
T *mem = static_cast<T*>(vmem);
return mem;
}
void deallocate(T * ptr, size_t num_objs, size_t alignment=Alignment)
{
C4_ASSERT(m_num >= num_objs);
m_num -= num_objs;
if((char*)ptr >= m_arr && (char*)ptr < m_arr + (N * sizeof(T)))
{
return;
}
C4_ASSERT(this->resource() != nullptr);
C4_ASSERT(alignment >= alignof(T));
this->resource()->deallocate(ptr, num_objs * sizeof(T), alignment);
}
T* reallocate(T * ptr, size_t oldnum, size_t newnum, size_t alignment=Alignment)
{
C4_ASSERT(this->resource() != nullptr);
C4_ASSERT(alignment >= alignof(T));
if(oldnum <= N && newnum <= N)
{
return m_arr;
}
else if(oldnum <= N && newnum > N)
{
return allocate(newnum, alignment);
}
else if(oldnum > N && newnum <= N)
{
deallocate(ptr, oldnum, alignment);
return m_arr;
}
void* vmem = this->resource()->reallocate(ptr, oldnum * sizeof(T), newnum * sizeof(T), alignment);
T* mem = static_cast<T*>(vmem);
return mem;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** An allocator making use of the global memory resource.
* @ingroup allocators */
template<class T> using allocator = Allocator<T, MemResGlobal>;
/** An allocator with a per-instance memory resource
* @ingroup allocators */
template<class T> using allocator_mr = Allocator<T, MemRes>;
/** @ingroup allocators */
template<class T, size_t N=16, size_t Alignment=alignof(T)> using small_allocator = SmallAllocator<T, N, Alignment, MemResGlobal>;
/** @ingroup allocators */
template<class T, size_t N=16, size_t Alignment=alignof(T)> using small_allocator_mr = SmallAllocator<T, N, Alignment, MemRes>;
} // namespace c4
#endif /* _C4_ALLOCATOR_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/allocator.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/char_traits.hpp
// https://github.com/biojppm/c4core/src/c4/char_traits.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_CHAR_TRAITS_HPP_
#define _C4_CHAR_TRAITS_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
#include <string> // needed because of std::char_traits
#include <cctype>
#include <cwctype>
namespace c4 {
C4_ALWAYS_INLINE bool isspace(char c) { return std::isspace(c) != 0; }
C4_ALWAYS_INLINE bool isspace(wchar_t c) { return std::iswspace(static_cast<wint_t>(c)) != 0; }
//-----------------------------------------------------------------------------
template<typename C>
struct char_traits;
template<>
struct char_traits<char> : public std::char_traits<char>
{
constexpr static const char whitespace_chars[] = " \f\n\r\t\v";
constexpr static const size_t num_whitespace_chars = sizeof(whitespace_chars) - 1;
};
template<>
struct char_traits<wchar_t> : public std::char_traits<wchar_t>
{
constexpr static const wchar_t whitespace_chars[] = L" \f\n\r\t\v";
constexpr static const size_t num_whitespace_chars = sizeof(whitespace_chars) - 1;
};
//-----------------------------------------------------------------------------
namespace detail {
template<typename C>
struct needed_chars;
template<>
struct needed_chars<char>
{
template<class SizeType>
C4_ALWAYS_INLINE constexpr static SizeType for_bytes(SizeType num_bytes)
{
return num_bytes;
}
};
template<>
struct needed_chars<wchar_t>
{
template<class SizeType>
C4_ALWAYS_INLINE constexpr static SizeType for_bytes(SizeType num_bytes)
{
// wchar_t is not necessarily 2 bytes.
return (num_bytes / static_cast<SizeType>(sizeof(wchar_t))) + ((num_bytes & static_cast<SizeType>(SizeType(sizeof(wchar_t)) - SizeType(1))) != 0);
}
};
} // namespace detail
/** get the number of C characters needed to store a number of bytes */
template<typename C, typename SizeType>
C4_ALWAYS_INLINE constexpr SizeType num_needed_chars(SizeType num_bytes)
{
return detail::needed_chars<C>::for_bytes(num_bytes);
}
//-----------------------------------------------------------------------------
/** get the given text string as either char or wchar_t according to the given type */
#define C4_TXTTY(txt, type) \
/* is there a smarter way to do this? */\
c4::detail::literal_as<type>::get(txt, C4_WIDEN(txt))
namespace detail {
template<typename C>
struct literal_as;
template<>
struct literal_as<char>
{
C4_ALWAYS_INLINE static constexpr const char* get(const char* str, const wchar_t *)
{
return str;
}
};
template<>
struct literal_as<wchar_t>
{
C4_ALWAYS_INLINE static constexpr const wchar_t* get(const char*, const wchar_t *wstr)
{
return wstr;
}
};
} // namespace detail
} // namespace c4
#endif /* _C4_CHAR_TRAITS_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/char_traits.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/hash.hpp
// https://github.com/biojppm/c4core/src/c4/hash.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_HASH_HPP_
#define _C4_HASH_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
#include <climits>
/** @file hash.hpp */
/** @defgroup hash Hash utils
* @see http://aras-p.info/blog/2016/08/02/Hash-Functions-all-the-way-down/ */
namespace c4 {
namespace detail {
/** @internal
* @ingroup hash
* @see this was taken a great answer in stackoverflow:
* https://stackoverflow.com/a/34597785/5875572
* @see http://aras-p.info/blog/2016/08/02/Hash-Functions-all-the-way-down/ */
template<typename ResultT, ResultT OffsetBasis, ResultT Prime>
class basic_fnv1a final
{
static_assert(std::is_unsigned<ResultT>::value, "need unsigned integer");
public:
using result_type = ResultT;
private:
result_type state_ {};
public:
C4_CONSTEXPR14 basic_fnv1a() noexcept : state_ {OffsetBasis} {}
C4_CONSTEXPR14 void update(const void *const data, const size_t size) noexcept
{
auto cdata = static_cast<const unsigned char *>(data);
auto acc = this->state_;
for(size_t i = 0; i < size; ++i)
{
const auto next = size_t(cdata[i]);
acc = (acc ^ next) * Prime;
}
this->state_ = acc;
}
C4_CONSTEXPR14 result_type digest() const noexcept
{
return this->state_;
}
};
using fnv1a_32 = basic_fnv1a<uint32_t, UINT32_C( 2166136261), UINT32_C( 16777619)>;
using fnv1a_64 = basic_fnv1a<uint64_t, UINT64_C(14695981039346656037), UINT64_C(1099511628211)>;
template<size_t Bits> struct fnv1a;
template<> struct fnv1a<32> { using type = fnv1a_32; };
template<> struct fnv1a<64> { using type = fnv1a_64; };
} // namespace detail
/** @ingroup hash */
template<size_t Bits>
using fnv1a_t = typename detail::fnv1a<Bits>::type;
/** @ingroup hash */
C4_CONSTEXPR14 inline size_t hash_bytes(const void *const data, const size_t size) noexcept
{
fnv1a_t<CHAR_BIT * sizeof(size_t)> fn{};
fn.update(data, size);
return fn.digest();
}
/**
* @overload hash_bytes
* @ingroup hash */
template<size_t N>
C4_CONSTEXPR14 inline size_t hash_bytes(const char (&str)[N]) noexcept
{
fnv1a_t<CHAR_BIT * sizeof(size_t)> fn{};
fn.update(str, N);
return fn.digest();
}
} // namespace c4
#endif // _C4_HASH_HPP_
// (end https://github.com/biojppm/c4core/src/c4/hash.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/szconv.hpp
// https://github.com/biojppm/c4core/src/c4/szconv.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_SZCONV_HPP_
#define _C4_SZCONV_HPP_
/** @file szconv.hpp utilities to deal safely with narrowing conversions */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
#include <limits>
namespace c4 {
/** @todo this would be so much easier with calls to numeric_limits::max()... */
template<class SizeOut, class SizeIn>
struct is_narrower_size : std::conditional
<
(std::is_signed<SizeOut>::value == std::is_signed<SizeIn>::value)
?
(sizeof(SizeOut) < sizeof(SizeIn))
:
(
(sizeof(SizeOut) < sizeof(SizeIn))
||
(
(sizeof(SizeOut) == sizeof(SizeIn))
&&
(std::is_signed<SizeOut>::value && std::is_unsigned<SizeIn>::value)
)
),
std::true_type,
std::false_type
>::type
{
static_assert(std::is_integral<SizeIn >::value, "must be integral type");
static_assert(std::is_integral<SizeOut>::value, "must be integral type");
};
/** when SizeOut is wider than SizeIn, assignment can occur without reservations */
template<class SizeOut, class SizeIn>
C4_ALWAYS_INLINE
typename std::enable_if< ! is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
szconv(SizeIn sz) noexcept
{
return static_cast<SizeOut>(sz);
}
/** when SizeOut is narrower than SizeIn, narrowing will occur, so we check
* for overflow. Note that this check is done only if C4_XASSERT is enabled.
* @see C4_XASSERT */
template<class SizeOut, class SizeIn>
C4_ALWAYS_INLINE
typename std::enable_if<is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
szconv(SizeIn sz) C4_NOEXCEPT_X
{
C4_XASSERT(sz >= 0);
C4_XASSERT_MSG((SizeIn)sz <= (SizeIn)std::numeric_limits<SizeOut>::max(), "size conversion overflow: in=%zu", (size_t)sz);
SizeOut szo = static_cast<SizeOut>(sz);
return szo;
}
} // namespace c4
#endif /* _C4_SZCONV_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/szconv.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/blob.hpp
// https://github.com/biojppm/c4core/src/c4/blob.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_BLOB_HPP_
#define _C4_BLOB_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/types.hpp
//#include "c4/types.hpp"
#if !defined(C4_TYPES_HPP_) && !defined(_C4_TYPES_HPP_)
#error "amalgamate: file c4/types.hpp must have been included at this point"
#endif /* C4_TYPES_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
/** @file blob.hpp Mutable and immutable binary data blobs.
*/
namespace c4 {
template<class T>
struct blob_
{
T * buf;
size_t len;
C4_ALWAYS_INLINE blob_() noexcept : buf(), len() {}
C4_ALWAYS_INLINE blob_(blob_ const& that) noexcept = default;
C4_ALWAYS_INLINE blob_(blob_ && that) noexcept = default;
C4_ALWAYS_INLINE blob_& operator=(blob_ && that) noexcept = default;
C4_ALWAYS_INLINE blob_& operator=(blob_ const& that) noexcept = default;
// need to sfinae out copy constructors! (why? isn't the above sufficient?)
#define _C4_REQUIRE_NOT_SAME class=typename std::enable_if<( ! std::is_same<U, blob_>::value) && ( ! std::is_pointer<U>::value), T>::type
template<class U, _C4_REQUIRE_NOT_SAME> C4_ALWAYS_INLINE blob_(U &var) noexcept : buf(reinterpret_cast<T*>(&var)), len(sizeof(U)) {}
template<class U, _C4_REQUIRE_NOT_SAME> C4_ALWAYS_INLINE blob_& operator= (U &var) noexcept { buf = reinterpret_cast<T*>(&var); len = sizeof(U); return *this; }
#undef _C4_REQUIRE_NOT_SAME
template<class U, size_t N> C4_ALWAYS_INLINE blob_(U (&arr)[N]) noexcept : buf(reinterpret_cast<T*>(arr)), len(sizeof(U) * N) {}
template<class U, size_t N> C4_ALWAYS_INLINE blob_& operator= (U (&arr)[N]) noexcept { buf = reinterpret_cast<T*>(arr); len = sizeof(U) * N; return *this; }
template<class U>
C4_ALWAYS_INLINE blob_(U *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(sizeof(U) * n) { C4_ASSERT(is_aligned(ptr)); }
C4_ALWAYS_INLINE blob_(void *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {}
C4_ALWAYS_INLINE blob_(void const *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {}
};
/** an immutable binary blob */
using cblob = blob_<cbyte>;
/** a mutable binary blob */
using blob = blob_< byte>;
C4_MUST_BE_TRIVIAL_COPY(blob);
C4_MUST_BE_TRIVIAL_COPY(cblob);
} // namespace c4
#endif // _C4_BLOB_HPP_
// (end https://github.com/biojppm/c4core/src/c4/blob.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/substr_fwd.hpp
// https://github.com/biojppm/c4core/src/c4/substr_fwd.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_SUBSTR_FWD_HPP_
#define _C4_SUBSTR_FWD_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/export.hpp
//#include "c4/export.hpp"
#if !defined(C4_EXPORT_HPP_) && !defined(_C4_EXPORT_HPP_)
#error "amalgamate: file c4/export.hpp must have been included at this point"
#endif /* C4_EXPORT_HPP_ */
namespace c4 {
#ifndef DOXYGEN
template<class C> struct basic_substring;
using csubstr = C4CORE_EXPORT basic_substring<const char>;
using substr = C4CORE_EXPORT basic_substring<char>;
#endif // !DOXYGEN
} // namespace c4
#endif /* _C4_SUBSTR_FWD_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/substr_fwd.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/substr.hpp
// https://github.com/biojppm/c4core/src/c4/substr.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_SUBSTR_HPP_
#define _C4_SUBSTR_HPP_
/** @file substr.hpp read+write string views */
//included above:
//#include <string.h>
//included above:
//#include <ctype.h>
//included above:
//#include <type_traits>
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr_fwd.hpp
//#include "c4/substr_fwd.hpp"
#if !defined(C4_SUBSTR_FWD_HPP_) && !defined(_C4_SUBSTR_FWD_HPP_)
#error "amalgamate: file c4/substr_fwd.hpp must have been included at this point"
#endif /* C4_SUBSTR_FWD_HPP_ */
#ifdef __clang__
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtype-limits" // disable warnings on size_t>=0, used heavily in assertions below. These assertions are a preparation step for providing the index type as a template parameter.
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
namespace c4 {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
template<typename C>
static inline void _do_reverse(C *C4_RESTRICT first, C *C4_RESTRICT last)
{
while(last > first)
{
C tmp = *last;
*last-- = *first;
*first++ = tmp;
}
}
} // namespace detail
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// utility macros to deuglify SFINAE code; undefined after the class.
// https://stackoverflow.com/questions/43051882/how-to-disable-a-class-member-funrtion-for-certain-template-types
#define C4_REQUIRE_RW(ret_type) \
template <typename U=C> \
typename std::enable_if< ! std::is_const<U>::value, ret_type>::type
// non-const-to-const
#define C4_NC2C(ty) \
typename std::enable_if<std::is_const<C>::value && ( ! std::is_const<ty>::value), ty>::type
/** a non-owning string-view, consisting of a character pointer
* and a length.
*
* @note The pointer is explicitly restricted.
* @note Because of a C++ limitation, there cannot coexist overloads for
* constructing from a char[N] and a char*; the latter will always be chosen
* by the compiler. To construct an object of this type, call to_substr() or
* to_csubstr(). For a more detailed explanation on why the overloads cannot
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html
*
* @see to_substr()
* @see to_csubstr()
*/
template<class C>
struct C4CORE_EXPORT basic_substring
{
public:
/** a restricted pointer to the first character of the substring */
C * C4_RESTRICT str;
/** the length of the substring */
size_t len;
public:
/** @name Types */
/** @{ */
using CC = typename std::add_const<C>::type; //!< CC=const char
using NCC_ = typename std::remove_const<C>::type; //!< NCC_=non const char
using ro_substr = basic_substring<CC>;
using rw_substr = basic_substring<NCC_>;
using char_type = C;
using size_type = size_t;
using iterator = C*;
using const_iterator = CC*;
enum : size_t { npos = (size_t)-1, NONE = (size_t)-1 };
/// convert automatically to substring of const C
operator ro_substr () const { ro_substr s(str, len); return s; }
/** @} */
public:
/** @name Default construction and assignment */
/** @{ */
constexpr basic_substring() : str(nullptr), len(0) {}
constexpr basic_substring(basic_substring const&) = default;
constexpr basic_substring(basic_substring &&) = default;
constexpr basic_substring(std::nullptr_t) : str(nullptr), len(0) {}
basic_substring& operator= (basic_substring const&) = default;
basic_substring& operator= (basic_substring &&) = default;
basic_substring& operator= (std::nullptr_t) { str = nullptr; len = 0; return *this; }
/** @} */
public:
/** @name Construction and assignment from characters with the same type */
/** @{ */
//basic_substring(C *s_) : str(s_), len(s_ ? strlen(s_) : 0) {}
/** the overload for receiving a single C* pointer will always
* hide the array[N] overload. So it is disabled. If you want to
* construct a substr from a single pointer containing a C-style string,
* you can call c4::to_substr()/c4::to_csubstr().
* @see c4::to_substr()
* @see c4::to_csubstr() */
template<size_t N>
constexpr basic_substring(C (&s_)[N]) noexcept : str(s_), len(N-1) {}
basic_substring(C *s_, size_t len_) : str(s_), len(len_) { C4_ASSERT(str || !len_); }
basic_substring(C *beg_, C *end_) : str(beg_), len(static_cast<size_t>(end_ - beg_)) { C4_ASSERT(end_ >= beg_); }
//basic_substring& operator= (C *s_) { this->assign(s_); return *this; }
template<size_t N>
basic_substring& operator= (C (&s_)[N]) { this->assign<N>(s_); return *this; }
//void assign(C *s_) { str = (s_); len = (s_ ? strlen(s_) : 0); }
/** the overload for receiving a single C* pointer will always
* hide the array[N] overload. So it is disabled. If you want to
* construct a substr from a single pointer containing a C-style string,
* you can call c4::to_substr()/c4::to_csubstr().
* @see c4::to_substr()
* @see c4::to_csubstr() */
template<size_t N>
void assign(C (&s_)[N]) { str = (s_); len = (N-1); }
void assign(C *s_, size_t len_) { str = s_; len = len_; C4_ASSERT(str || !len_); }
void assign(C *beg_, C *end_) { C4_ASSERT(end_ >= beg_); str = (beg_); len = (end_ - beg_); }
void clear() { str = nullptr; len = 0; }
/** @} */
public:
/** @name Construction from non-const characters */
/** @{ */
// when the char type is const, allow construction and assignment from non-const chars
/** only available when the char type is const */
template<size_t N, class U=NCC_> explicit basic_substring(C4_NC2C(U) (&s_)[N]) { str = s_; len = N-1; }
/** only available when the char type is const */
template< class U=NCC_> basic_substring(C4_NC2C(U) *s_, size_t len_) { str = s_; len = len_; }
/** only available when the char type is const */
template< class U=NCC_> basic_substring(C4_NC2C(U) *beg_, C4_NC2C(U) *end_) { C4_ASSERT(end_ >= beg_); str = beg_; len = end_ - beg_; }
/** only available when the char type is const */
template<size_t N, class U=NCC_> void assign(C4_NC2C(U) (&s_)[N]) { str = s_; len = N-1; }
/** only available when the char type is const */
template< class U=NCC_> void assign(C4_NC2C(U) *s_, size_t len_) { str = s_; len = len_; }
/** only available when the char type is const */
template< class U=NCC_> void assign(C4_NC2C(U) *beg_, C4_NC2C(U) *end_) { C4_ASSERT(end_ >= beg_); str = beg_; len = end_ - beg_; }
/** only available when the char type is const */
template<size_t N, class U=NCC_>
basic_substring& operator=(C4_NC2C(U) (&s_)[N]) { str = s_; len = N-1; return *this; }
/** @} */
public:
/** @name Standard accessor methods */
/** @{ */
bool has_str() const { return ! empty() && str[0] != C(0); }
bool empty() const { return (len == 0 || str == nullptr); }
bool not_empty() const { return (len != 0 && str != nullptr); }
size_t size() const { return len; }
iterator begin() { return str; }
iterator end () { return str + len; }
const_iterator begin() const { return str; }
const_iterator end () const { return str + len; }
C * data() { return str; }
C const* data() const { return str; }
inline C & operator[] (size_t i) { C4_ASSERT(i >= 0 && i < len); return str[i]; }
inline C const& operator[] (size_t i) const { C4_ASSERT(i >= 0 && i < len); return str[i]; }
inline C & front() { C4_ASSERT(len > 0 && str != nullptr); return *str; }
inline C const& front() const { C4_ASSERT(len > 0 && str != nullptr); return *str; }
inline C & back() { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); }
inline C const& back() const { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); }
/** @} */
public:
/** @name Comparison methods */
/** @{ */
int compare(C const c) const
{
C4_XASSERT((str != nullptr) || len == 0);
if( ! len)
return -1;
if(*str == c)
return static_cast<int>(len - 1);
return *str - c;
}
int compare(const char *that, size_t sz) const
{
C4_XASSERT(that || sz == 0);
C4_XASSERT(str || len == 0);
if(C4_LIKELY(str && that))
{
int ret = strncmp(str, that, len < sz ? len : sz);
if(ret == 0 && len != sz)
ret = len < sz ? -1 : 1;
return ret;
}
if((!str && !that) || (len == sz))
{
C4_XASSERT(len == 0 && sz == 0);
return 0;
}
return len < sz ? -1 : 1;
}
C4_ALWAYS_INLINE int compare(ro_substr const that) const { return this->compare(that.str, that.len); }
C4_ALWAYS_INLINE bool operator== (std::nullptr_t) const { return str == nullptr || len == 0; }
C4_ALWAYS_INLINE bool operator!= (std::nullptr_t) const { return str != nullptr || len == 0; }
C4_ALWAYS_INLINE bool operator== (C const c) const { return this->compare(c) == 0; }
C4_ALWAYS_INLINE bool operator!= (C const c) const { return this->compare(c) != 0; }
C4_ALWAYS_INLINE bool operator< (C const c) const { return this->compare(c) < 0; }
C4_ALWAYS_INLINE bool operator> (C const c) const { return this->compare(c) > 0; }
C4_ALWAYS_INLINE bool operator<= (C const c) const { return this->compare(c) <= 0; }
C4_ALWAYS_INLINE bool operator>= (C const c) const { return this->compare(c) >= 0; }
template<class U> C4_ALWAYS_INLINE bool operator== (basic_substring<U> const that) const { return this->compare(that) == 0; }
template<class U> C4_ALWAYS_INLINE bool operator!= (basic_substring<U> const that) const { return this->compare(that) != 0; }
template<class U> C4_ALWAYS_INLINE bool operator< (basic_substring<U> const that) const { return this->compare(that) < 0; }
template<class U> C4_ALWAYS_INLINE bool operator> (basic_substring<U> const that) const { return this->compare(that) > 0; }
template<class U> C4_ALWAYS_INLINE bool operator<= (basic_substring<U> const that) const { return this->compare(that) <= 0; }
template<class U> C4_ALWAYS_INLINE bool operator>= (basic_substring<U> const that) const { return this->compare(that) >= 0; }
template<size_t N> C4_ALWAYS_INLINE bool operator== (const char (&that)[N]) const { return this->compare(that, N-1) == 0; }
template<size_t N> C4_ALWAYS_INLINE bool operator!= (const char (&that)[N]) const { return this->compare(that, N-1) != 0; }
template<size_t N> C4_ALWAYS_INLINE bool operator< (const char (&that)[N]) const { return this->compare(that, N-1) < 0; }
template<size_t N> C4_ALWAYS_INLINE bool operator> (const char (&that)[N]) const { return this->compare(that, N-1) > 0; }
template<size_t N> C4_ALWAYS_INLINE bool operator<= (const char (&that)[N]) const { return this->compare(that, N-1) <= 0; }
template<size_t N> C4_ALWAYS_INLINE bool operator>= (const char (&that)[N]) const { return this->compare(that, N-1) >= 0; }
/** @} */
public:
/** @name Sub-selection methods */
/** @{ */
/** true if *this is a substring of that (ie, from the same buffer) */
inline bool is_sub(ro_substr const that) const
{
return that.is_super(*this);
}
/** true if that is a substring of *this (ie, from the same buffer) */
inline bool is_super(ro_substr const that) const
{
if(C4_UNLIKELY(len == 0))
{
return that.len == 0 && that.str == str && str != nullptr;
}
return that.begin() >= begin() && that.end() <= end();
}
/** true if there is overlap of at least one element between that and *this */
inline bool overlaps(ro_substr const that) const
{
// thanks @timwynants
return (that.end() > begin() && that.begin() < end());
}
public:
/** return [first,len[ */
basic_substring sub(size_t first) const
{
C4_ASSERT(first >= 0 && first <= len);
return basic_substring(str + first, len - first);
}
/** return [first,first+num[. If num==npos, return [first,len[ */
basic_substring sub(size_t first, size_t num) const
{
C4_ASSERT(first >= 0 && first <= len);
C4_ASSERT((num >= 0 && num <= len) || (num == npos));
size_t rnum = num != npos ? num : len - first;
C4_ASSERT((first >= 0 && first + rnum <= len) || (num == 0));
return basic_substring(str + first, rnum);
}
/** return [first,last[. If last==npos, return [first,len[ */
basic_substring range(size_t first, size_t last=npos) const
{
C4_ASSERT(first >= 0 && first <= len);
last = last != npos ? last : len;
C4_ASSERT(first <= last);
C4_ASSERT(last >= 0 && last <= len);
return basic_substring(str + first, last - first);
}
/** return [0,num[*/
basic_substring first(size_t num) const
{
return sub(0, num);
}
/** return [len-num,len[*/
basic_substring last(size_t num) const
{
if(num == npos)
return *this;
return sub(len - num);
}
/** offset from the ends: return [left,len-right[ ; ie, trim a
number of characters from the left and right. This is
equivalent to python's negative list indices. */
basic_substring offs(size_t left, size_t right) const
{
C4_ASSERT(left >= 0 && left <= len);
C4_ASSERT(right >= 0 && right <= len);
C4_ASSERT(left <= len - right + 1);
return basic_substring(str + left, len - right - left);
}
/** return [0, pos+include_pos[ */
basic_substring left_of(size_t pos, bool include_pos=false) const
{
if(pos == npos)
return *this;
return first(pos + include_pos);
}
/** return [pos+!include_pos, len[ */
basic_substring right_of(size_t pos, bool include_pos=false) const
{
if(pos == npos)
return sub(len, 0);
return sub(pos + !include_pos);
}
public:
/** given @p subs a substring of the current string, get the
* portion of the current string to the left of it */
basic_substring left_of(ro_substr const subs) const
{
C4_ASSERT(is_super(subs) || subs.empty());
auto ssb = subs.begin();
auto b = begin();
auto e = end();
if(ssb >= b && ssb <= e)
return sub(0, static_cast<size_t>(ssb - b));
else
return sub(0, 0);
}
/** given @p subs a substring of the current string, get the
* portion of the current string to the right of it */
basic_substring right_of(ro_substr const subs) const
{
C4_ASSERT(is_super(subs) || subs.empty());
auto sse = subs.end();
auto b = begin();
auto e = end();
if(sse >= b && sse <= e)
return sub(static_cast<size_t>(sse - b), static_cast<size_t>(e - sse));
else
return sub(0, 0);
}
/** @} */
public:
/** @name Removing characters (trim()) / patterns (strip()) from the tips of the string */
/** @{ */
/** trim left */
basic_substring triml(const C c) const
{
if( ! empty())
{
size_t pos = first_not_of(c);
if(pos != npos)
return sub(pos);
}
return sub(0, 0);
}
/** trim left ANY of the characters.
* @see stripl() to remove a pattern from the left */
basic_substring triml(ro_substr chars) const
{
if( ! empty())
{
size_t pos = first_not_of(chars);
if(pos != npos)
return sub(pos);
}
return sub(0, 0);
}
/** trim the character c from the right */
basic_substring trimr(const C c) const
{
if( ! empty())
{
size_t pos = last_not_of(c, npos);
if(pos != npos)
return sub(0, pos+1);
}
return sub(0, 0);
}
/** trim right ANY of the characters
* @see stripr() to remove a pattern from the right */
basic_substring trimr(ro_substr chars) const
{
if( ! empty())
{
size_t pos = last_not_of(chars, npos);
if(pos != npos)
return sub(0, pos+1);
}
return sub(0, 0);
}
/** trim the character c left and right */
basic_substring trim(const C c) const
{
return triml(c).trimr(c);
}
/** trim left and right ANY of the characters
* @see strip() to remove a pattern from the left and right */
basic_substring trim(ro_substr const chars) const
{
return triml(chars).trimr(chars);
}
/** remove a pattern from the left
* @see triml() to remove characters*/
basic_substring stripl(ro_substr pattern) const
{
if( ! begins_with(pattern))
return *this;
return sub(pattern.len < len ? pattern.len : len);
}
/** remove a pattern from the right
* @see trimr() to remove characters*/
basic_substring stripr(ro_substr pattern) const
{
if( ! ends_with(pattern))
return *this;
return left_of(len - (pattern.len < len ? pattern.len : len));
}
/** @} */
public:
/** @name Lookup methods */
/** @{ */
inline size_t find(const C c, size_t start_pos=0) const
{
return first_of(c, start_pos);
}
inline size_t find(ro_substr pattern, size_t start_pos=0) const
{
C4_ASSERT(start_pos == npos || (start_pos >= 0 && start_pos <= len));
if(len < pattern.len) return npos;
for(size_t i = start_pos, e = len - pattern.len + 1; i < e; ++i)
{
bool gotit = true;
for(size_t j = 0; j < pattern.len; ++j)
{
C4_ASSERT(i + j < len);
if(str[i + j] != pattern.str[j])
{
gotit = false;
break;
}
}
if(gotit)
{
return i;
}
}
return npos;
}
public:
/** count the number of occurrences of c */
inline size_t count(const C c, size_t pos=0) const
{
C4_ASSERT(pos >= 0 && pos <= len);
size_t num = 0;
pos = find(c, pos);
while(pos != npos)
{
++num;
pos = find(c, pos + 1);
}
return num;
}
/** count the number of occurrences of s */
inline size_t count(ro_substr c, size_t pos=0) const
{
C4_ASSERT(pos >= 0 && pos <= len);
size_t num = 0;
pos = find(c, pos);
while(pos != npos)
{
++num;
pos = find(c, pos + c.len);
}
return num;
}
/** get the substr consisting of the first occurrence of @p c after @p pos, or an empty substr if none occurs */
inline basic_substring select(const C c, size_t pos=0) const
{
pos = find(c, pos);
return pos != npos ? sub(pos, 1) : basic_substring();
}
/** get the substr consisting of the first occurrence of @p pattern after @p pos, or an empty substr if none occurs */
inline basic_substring select(ro_substr pattern, size_t pos=0) const
{
pos = find(pattern, pos);
return pos != npos ? sub(pos, pattern.len) : basic_substring();
}
public:
struct first_of_any_result
{
size_t which;
size_t pos;
inline operator bool() const { return which != NONE && pos != npos; }
};
first_of_any_result first_of_any(ro_substr s0, ro_substr s1) const
{
ro_substr s[2] = {s0, s1};
return first_of_any_iter(&s[0], &s[0] + 2);
}
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2) const
{
ro_substr s[3] = {s0, s1, s2};
return first_of_any_iter(&s[0], &s[0] + 3);
}
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3) const
{
ro_substr s[4] = {s0, s1, s2, s3};
return first_of_any_iter(&s[0], &s[0] + 4);
}
first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3, ro_substr s4) const
{
ro_substr s[5] = {s0, s1, s2, s3, s4};
return first_of_any_iter(&s[0], &s[0] + 5);
}
template<class It>
first_of_any_result first_of_any_iter(It first_span, It last_span) const
{
for(size_t i = 0; i < len; ++i)
{
size_t curr = 0;
for(It it = first_span; it != last_span; ++curr, ++it)
{
auto const& chars = *it;
if((i + chars.len) > len) continue;
bool gotit = true;
for(size_t j = 0; j < chars.len; ++j)
{
C4_ASSERT(i + j < len);
if(str[i + j] != chars[j])
{
gotit = false;
break;
}
}
if(gotit)
{
return {curr, i};
}
}
}
return {NONE, npos};
}
public:
/** true if the first character of the string is @p c */
bool begins_with(const C c) const
{
return len > 0 ? str[0] == c : false;
}
/** true if the first @p num characters of the string are @p c */
bool begins_with(const C c, size_t num) const
{
if(len < num)
{
return false;
}
for(size_t i = 0; i < num; ++i)
{
if(str[i] != c)
{
return false;
}
}
return true;
}
/** true if the string begins with the given @p pattern */
bool begins_with(ro_substr pattern) const
{
if(len < pattern.len)
{
return false;
}
for(size_t i = 0; i < pattern.len; ++i)
{
if(str[i] != pattern[i])
{
return false;
}
}
return true;
}
/** true if the first character of the string is any of the given @p chars */
bool begins_with_any(ro_substr chars) const
{
if(len == 0)
{
return false;
}
for(size_t i = 0; i < chars.len; ++i)
{
if(str[0] == chars.str[i])
{
return true;
}
}
return false;
}
/** true if the last character of the string is @p c */
bool ends_with(const C c) const
{
return len > 0 ? str[len-1] == c : false;
}
/** true if the last @p num characters of the string are @p c */
bool ends_with(const C c, size_t num) const
{
if(len < num)
{
return false;
}
for(size_t i = len - num; i < len; ++i)
{
if(str[i] != c)
{
return false;
}
}
return true;
}
/** true if the string ends with the given @p pattern */
bool ends_with(ro_substr pattern) const
{
if(len < pattern.len)
{
return false;
}
for(size_t i = 0, s = len-pattern.len; i < pattern.len; ++i)
{
if(str[s+i] != pattern[i])
{
return false;
}
}
return true;
}
/** true if the last character of the string is any of the given @p chars */
bool ends_with_any(ro_substr chars) const
{
if(len == 0)
{
return false;
}
for(size_t i = 0; i < chars.len; ++i)
{
if(str[len - 1] == chars[i])
{
return true;
}
}
return false;
}
public:
/** @return the first position where c is found in the string, or npos if none is found */
size_t first_of(const C c, size_t start=0) const
{
C4_ASSERT(start == npos || (start >= 0 && start <= len));
for(size_t i = start; i < len; ++i)
{
if(str[i] == c)
return i;
}
return npos;
}
/** @return the last position where c is found in the string, or npos if none is found */
size_t last_of(const C c, size_t start=npos) const
{
C4_ASSERT(start == npos || (start >= 0 && start <= len));
if(start == npos)
start = len;
for(size_t i = start-1; i != size_t(-1); --i)
{
if(str[i] == c)
return i;
}
return npos;
}
/** @return the first position where ANY of the chars is found in the string, or npos if none is found */
size_t first_of(ro_substr chars, size_t start=0) const
{
C4_ASSERT(start == npos || (start >= 0 && start <= len));
for(size_t i = start; i < len; ++i)
{
for(size_t j = 0; j < chars.len; ++j)
{
if(str[i] == chars[j])
return i;
}
}
return npos;
}
/** @return the last position where ANY of the chars is found in the string, or npos if none is found */
size_t last_of(ro_substr chars, size_t start=npos) const
{
C4_ASSERT(start == npos || (start >= 0 && start <= len));
if(start == npos)
start = len;
for(size_t i = start-1; i != size_t(-1); --i)
{
for(size_t j = 0; j < chars.len; ++j)
{
if(str[i] == chars[j])
return i;
}
}
return npos;
}
public:
size_t first_not_of(const C c, size_t start=0) const
{
C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0));
for(size_t i = start; i < len; ++i)
{
if(str[i] != c)
return i;
}
return npos;
}
size_t last_not_of(const C c, size_t start=npos) const
{
C4_ASSERT(start == npos || (start >= 0 && start <= len));
if(start == npos)
start = len;
for(size_t i = start-1; i != size_t(-1); --i)
{
if(str[i] != c)
return i;
}
return npos;
}
size_t first_not_of(ro_substr chars, size_t start=0) const
{
C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0));
for(size_t i = start; i < len; ++i)
{
bool gotit = true;
for(size_t j = 0; j < chars.len; ++j)
{
if(str[i] == chars.str[j])
{
gotit = false;
break;
}
}
if(gotit)
{
return i;
}
}
return npos;
}
size_t last_not_of(ro_substr chars, size_t start=npos) const
{
C4_ASSERT(start == npos || (start >= 0 && start <= len));
if(start == npos)
start = len;
for(size_t i = start-1; i != size_t(-1); --i)
{
bool gotit = true;
for(size_t j = 0; j < chars.len; ++j)
{
if(str[i] == chars.str[j])
{
gotit = false;
break;
}
}
if(gotit)
{
return i;
}
}
return npos;
}
/** @} */
public:
/** @name Range lookup methods */
/** @{ */
/** get the range delimited by an open-close pair of characters.
* @note There must be no nested pairs.
* @note No checks for escapes are performed. */
basic_substring pair_range(CC open, CC close) const
{
size_t b = find(open);
if(b == npos)
return basic_substring();
size_t e = find(close, b+1);
if(e == npos)
return basic_substring();
basic_substring ret = range(b, e+1);
C4_ASSERT(ret.sub(1).find(open) == npos);
return ret;
}
/** get the range delimited by a single open-close character (eg, quotes).
* @note The open-close character can be escaped. */
basic_substring pair_range_esc(CC open_close, CC escape=CC('\\'))
{
size_t b = find(open_close);
if(b == npos) return basic_substring();
for(size_t i = b+1; i < len; ++i)
{
CC c = str[i];
if(c == open_close)
{
if(str[i-1] != escape)
{
return range(b, i+1);
}
}
}
return basic_substring();
}
/** get the range delimited by an open-close pair of characters,
* with possibly nested occurrences. No checks for escapes are
* performed. */
basic_substring pair_range_nested(CC open, CC close) const
{
size_t b = find(open);
if(b == npos) return basic_substring();
size_t e, curr = b+1, count = 0;
const char both[] = {open, close, '\0'};
while((e = first_of(both, curr)) != npos)
{
if(str[e] == open)
{
++count;
curr = e+1;
}
else if(str[e] == close)
{
if(count == 0) return range(b, e+1);
--count;
curr = e+1;
}
}
return basic_substring();
}
basic_substring unquoted() const
{
constexpr const C dq('"'), sq('\'');
if(len >= 2 && (str[len - 2] != C('\\')) &&
((begins_with(sq) && ends_with(sq))
||
(begins_with(dq) && ends_with(dq))))
{
return range(1, len -1);
}
return *this;
}
/** @} */
public:
/** @name Number-matching query methods */
/** @{ */
/** @return true if the substring contents are a floating-point or integer number.
* @note any leading or trailing whitespace will return false. */
bool is_number() const
{
if(empty() || (first_non_empty_span().empty()))
return false;
if(first_uint_span() == *this)
return true;
if(first_int_span() == *this)
return true;
if(first_real_span() == *this)
return true;
return false;
}
/** @return true if the substring contents are a real number.
* @note any leading or trailing whitespace will return false. */
bool is_real() const
{
if(empty() || (first_non_empty_span().empty()))
return false;
if(first_real_span() == *this)
return true;
return false;
}
/** @return true if the substring contents are an integer number.
* @note any leading or trailing whitespace will return false. */
bool is_integer() const
{
if(empty() || (first_non_empty_span().empty()))
return false;
if(first_uint_span() == *this)
return true;
if(first_int_span() == *this)
return true;
return false;
}
/** @return true if the substring contents are an unsigned integer number.
* @note any leading or trailing whitespace will return false. */
bool is_unsigned_integer() const
{
if(empty() || (first_non_empty_span().empty()))
return false;
if(first_uint_span() == *this)
return true;
return false;
}
/** get the first span consisting exclusively of non-empty characters */
basic_substring first_non_empty_span() const
{
constexpr const ro_substr empty_chars(" \n\r\t");
size_t pos = first_not_of(empty_chars);
if(pos == npos)
return first(0);
auto ret = sub(pos);
pos = ret.first_of(empty_chars);
return ret.first(pos);
}
/** get the first span which can be interpreted as an unsigned integer */
basic_substring first_uint_span() const
{
basic_substring ne = first_non_empty_span();
if(ne.empty())
return ne;
if(ne.str[0] == '-')
return first(0);
size_t skip_start = (ne.str[0] == '+') ? 1 : 0;
return ne._first_integral_span(skip_start);
}
/** get the first span which can be interpreted as a signed integer */
basic_substring first_int_span() const
{
basic_substring ne = first_non_empty_span();
if(ne.empty())
return ne;
size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-') ? 1 : 0;
return ne._first_integral_span(skip_start);
}
basic_substring _first_integral_span(size_t skip_start) const
{
C4_ASSERT(!empty());
if(skip_start == len) {
return first(0);
}
C4_ASSERT(skip_start < len);
if(first_of_any("0x", "0X")) // hexadecimal
{
skip_start += 2;
if(len == skip_start)
return first(0);
for(size_t i = skip_start; i < len; ++i)
{
if( ! _is_hex_char(str[i]))
return _is_delim_char(str[i]) ? first(i) : first(0);
}
}
else if(first_of_any("0o", "0O")) // octal
{
skip_start += 2;
if(len == skip_start)
return first(0);
for(size_t i = skip_start; i < len; ++i)
{
char c = str[i];
if(c < '0' || c > '7')
return _is_delim_char(str[i]) ? first(i) : first(0);
}
}
else if(first_of_any("0b", "0B")) // binary
{
skip_start += 2;
if(len == skip_start)
return first(0);
for(size_t i = skip_start; i < len; ++i)
{
char c = str[i];
if(c != '0' && c != '1')
return _is_delim_char(c) ? first(i) : first(0);
}
}
else // otherwise, decimal
{
if(len == skip_start)
return first(0);
for(size_t i = skip_start; i < len; ++i)
{
char c = str[i];
if(c < '0' || c > '9')
return _is_delim_char(c) ? first(i) : first(0);
}
}
return *this;
}
/** get the first span which can be interpreted as a real (floating-point) number */
basic_substring first_real_span() const
{
basic_substring ne = first_non_empty_span();
if(ne.empty())
return ne;
size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-') ? 1 : 0;
if(ne.first_of_any("0x", "0X")) // hexadecimal
{
skip_start += 2;
if(ne.len == skip_start)
return ne.first(0);
for(size_t i = skip_start; i < ne.len; ++i)
{
char c = ne.str[i];
if(( ! _is_hex_char(c)) && c != '.' && c != 'p' && c != 'P')
{
if(c == '-' || c == '+')
{
// we can also have a sign for the exponent
if(i > 1 && (ne[i-1] == 'p' || ne[i-1] == 'P'))
{
continue;
}
}
return _is_delim_char(c) ? ne.first(i) : ne.first(0);
}
}
}
else if(ne.first_of_any("0b", "0B")) // binary
{
skip_start += 2;
if(ne.len == skip_start)
return ne.first(0);
for(size_t i = skip_start; i < ne.len; ++i)
{
char c = ne.str[i];
if(c != '0' && c != '1' && c != '.')
{
return _is_delim_char(c) ? ne.first(i) : ne.first(0);
}
}
}
else if(ne.first_of_any("0o", "0O")) // octal
{
skip_start += 2;
if(ne.len == skip_start)
return ne.first(0);
for(size_t i = skip_start; i < ne.len; ++i)
{
char c = ne.str[i];
if((c < '0' || c > '7') && c != '.')
{
return _is_delim_char(c) ? ne.first(i) : ne.first(0);
}
}
}
else // assume decimal
{
if(ne.len == skip_start)
return ne.first(0);
for(size_t i = skip_start; i < ne.len; ++i)
{
char c = ne.str[i];
if((c < '0' || c > '9') && (c != '.' && c != 'e' && c != 'E'))
{
if(c == '-' || c == '+')
{
// we can also have a sign for the exponent
if(i > 1 && (ne[i-1] == 'e' || ne[i-1] == 'E'))
{
continue;
}
}
else if(i == skip_start)
{
if(c == 'i')
{
if(ne.len >= skip_start + 8 && ne.sub(skip_start, 8) == "infinity")
return _is_delim_char(ne.str[skip_start + 8]) ? ne.first(skip_start + 8) : ne.first(0);
else if(ne.len >= skip_start + 3 && ne.sub(skip_start, 3) == "inf")
return _is_delim_char(ne.str[skip_start + 3]) ? ne.first(skip_start + 3) : ne.first(0);
else
return ne.first(0);
}
else if(c == 'n')
{
if(ne.len >= skip_start + 3 && ne.sub(skip_start, 3) == "nan")
return _is_delim_char(ne.str[skip_start + 3]) ? ne.first(skip_start + 3) : ne.first(0);
else
return ne.first(0);
}
else
{
return ne.first(0);
}
}
else
{
return _is_delim_char(c) ? ne.first(i) : ne.first(0);
}
}
}
}
return ne;
}
/** true if the character is a delimiter character *at the end* */
static constexpr C4_ALWAYS_INLINE bool _is_delim_char(char c) noexcept
{
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\0'
|| c == ']' || c == ')' || c == '}'
|| c == ',' || c == ';';
}
/** true if the character is in [0-9a-fA-F] */
static constexpr C4_ALWAYS_INLINE bool _is_hex_char(char c) noexcept
{
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
/** true if the character is in [0-9a-fA-F] */
static constexpr C4_ALWAYS_INLINE bool _is_oct_char(char c) noexcept
{
return (c >= '0' && c <= '7');
}
/** @} */
public:
/** @name Splitting methods */
/** @{ */
/** returns true if the string has not been exhausted yet, meaning
* it's ok to call next_split() again. When no instance of sep
* exists in the string, returns the full string. When the input
* is an empty string, the output string is the empty string. */
bool next_split(C sep, size_t *C4_RESTRICT start_pos, basic_substring *C4_RESTRICT out) const
{
if(C4_LIKELY(*start_pos < len))
{
for(size_t i = *start_pos, e = len; i < e; i++)
{
if(str[i] == sep)
{
out->assign(str + *start_pos, i - *start_pos);
*start_pos = i+1;
return true;
}
}
out->assign(str + *start_pos, len - *start_pos);
*start_pos = len + 1;
return true;
}
else
{
bool valid = len > 0 && (*start_pos == len);
if(valid && !empty() && str[len-1] == sep)
{
out->assign(str + len, (size_t)0); // the cast is needed to prevent overload ambiguity
}
else
{
out->assign(str + len + 1, (size_t)0); // the cast is needed to prevent overload ambiguity
}
*start_pos = len + 1;
return valid;
}
}
private:
struct split_proxy_impl
{
struct split_iterator_impl
{
split_proxy_impl const* m_proxy;
basic_substring m_str;
size_t m_pos;
NCC_ m_sep;
split_iterator_impl(split_proxy_impl const* proxy, size_t pos, C sep)
: m_proxy(proxy), m_pos(pos), m_sep(sep)
{
_tick();
}
void _tick()
{
m_proxy->m_str.next_split(m_sep, &m_pos, &m_str);
}
split_iterator_impl& operator++ () { _tick(); return *this; }
split_iterator_impl operator++ (int) { split_iterator_impl it = *this; _tick(); return it; }
basic_substring& operator* () { return m_str; }
basic_substring* operator-> () { return &m_str; }
bool operator!= (split_iterator_impl const& that) const
{
return !(this->operator==(that));
}
bool operator== (split_iterator_impl const& that) const
{
C4_XASSERT((m_sep == that.m_sep) && "cannot compare split iterators with different separators");
if(m_str.size() != that.m_str.size())
return false;
if(m_str.data() != that.m_str.data())
return false;
return m_pos == that.m_pos;
}
};
basic_substring m_str;
size_t m_start_pos;
C m_sep;
split_proxy_impl(basic_substring str_, size_t start_pos, C sep)
: m_str(str_), m_start_pos(start_pos), m_sep(sep)
{
}
split_iterator_impl begin() const
{
auto it = split_iterator_impl(this, m_start_pos, m_sep);
return it;
}
split_iterator_impl end() const
{
size_t pos = m_str.size() + 1;
auto it = split_iterator_impl(this, pos, m_sep);
return it;
}
};
public:
using split_proxy = split_proxy_impl;
/** a view into the splits */
split_proxy split(C sep, size_t start_pos=0) const
{
C4_XASSERT((start_pos >= 0 && start_pos < len) || empty());
auto ss = sub(0, len);
auto it = split_proxy(ss, start_pos, sep);
return it;
}
public:
/** pop right: return the first split from the right. Use
* gpop_left() to get the reciprocal part.
*/
basic_substring pop_right(C sep=C('/'), bool skip_empty=false) const
{
if(C4_LIKELY(len > 1))
{
auto pos = last_of(sep);
if(pos != npos)
{
if(pos + 1 < len) // does not end with sep
{
return sub(pos + 1); // return from sep to end
}
else // the string ends with sep
{
if( ! skip_empty)
{
return sub(pos + 1, 0);
}
auto ppos = last_not_of(sep); // skip repeated seps
if(ppos == npos) // the string is all made of seps
{
return sub(0, 0);
}
// find the previous sep
auto pos0 = last_of(sep, ppos);
if(pos0 == npos) // only the last sep exists
{
return sub(0); // return the full string (because skip_empty is true)
}
++pos0;
return sub(pos0);
}
}
else // no sep was found, return the full string
{
return *this;
}
}
else if(len == 1)
{
if(begins_with(sep))
{
return sub(0, 0);
}
return *this;
}
else // an empty string
{
return basic_substring();
}
}
/** return the first split from the left. Use gpop_right() to get
* the reciprocal part. */
basic_substring pop_left(C sep = C('/'), bool skip_empty=false) const
{
if(C4_LIKELY(len > 1))
{
auto pos = first_of(sep);
if(pos != npos)
{
if(pos > 0) // does not start with sep
{
return sub(0, pos); // return everything up to it
}
else // the string starts with sep
{
if( ! skip_empty)
{
return sub(0, 0);
}
auto ppos = first_not_of(sep); // skip repeated seps
if(ppos == npos) // the string is all made of seps
{
return sub(0, 0);
}
// find the next sep
auto pos0 = first_of(sep, ppos);
if(pos0 == npos) // only the first sep exists
{
return sub(0); // return the full string (because skip_empty is true)
}
C4_XASSERT(pos0 > 0);
// return everything up to the second sep
return sub(0, pos0);
}
}
else // no sep was found, return the full string
{
return sub(0);
}
}
else if(len == 1)
{
if(begins_with(sep))
{
return sub(0, 0);
}
return sub(0);
}
else // an empty string
{
return basic_substring();
}
}
public:
/** greedy pop left. eg, csubstr("a/b/c").gpop_left('/')="c" */
basic_substring gpop_left(C sep = C('/'), bool skip_empty=false) const
{
auto ss = pop_right(sep, skip_empty);
ss = left_of(ss);
if(ss.find(sep) != npos)
{
if(ss.ends_with(sep))
{
if(skip_empty)
{
ss = ss.trimr(sep);
}
else
{
ss = ss.sub(0, ss.len-1); // safe to subtract because ends_with(sep) is true
}
}
}
return ss;
}
/** greedy pop right. eg, csubstr("a/b/c").gpop_right('/')="a" */
basic_substring gpop_right(C sep = C('/'), bool skip_empty=false) const
{
auto ss = pop_left(sep, skip_empty);
ss = right_of(ss);
if(ss.find(sep) != npos)
{
if(ss.begins_with(sep))
{
if(skip_empty)
{
ss = ss.triml(sep);
}
else
{
ss = ss.sub(1);
}
}
}
return ss;
}
/** @} */
public:
/** @name Path-like manipulation methods */
/** @{ */
basic_substring basename(C sep=C('/')) const
{
auto ss = pop_right(sep, /*skip_empty*/true);
ss = ss.trimr(sep);
return ss;
}
basic_substring dirname(C sep=C('/')) const
{
auto ss = basename(sep);
ss = ss.empty() ? *this : left_of(ss);
return ss;
}
C4_ALWAYS_INLINE basic_substring name_wo_extshort() const
{
return gpop_left('.');
}
C4_ALWAYS_INLINE basic_substring name_wo_extlong() const
{
return pop_left('.');
}
C4_ALWAYS_INLINE basic_substring extshort() const
{
return pop_right('.');
}
C4_ALWAYS_INLINE basic_substring extlong() const
{
return gpop_right('.');
}
/** @} */
public:
/** @name Content-modification methods (only for non-const C) */
/** @{ */
/** convert the string to upper-case
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) toupper()
{
for(size_t i = 0; i < len; ++i)
{
str[i] = static_cast<C>(::toupper(str[i]));
}
}
/** convert the string to lower-case
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) tolower()
{
for(size_t i = 0; i < len; ++i)
{
str[i] = static_cast<C>(::tolower(str[i]));
}
}
public:
/** fill the entire contents with the given @p val
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) fill(C val)
{
for(size_t i = 0; i < len; ++i)
{
str[i] = val;
}
}
public:
/** set the current substring to a copy of the given csubstr
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) copy_from(ro_substr that, size_t ifirst=0, size_t num=npos)
{
C4_ASSERT(ifirst >= 0 && ifirst <= len);
num = num != npos ? num : len - ifirst;
num = num < that.len ? num : that.len;
C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num);
}
public:
/** reverse in place
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) reverse()
{
if(len == 0) return;
detail::_do_reverse(str, str + len - 1);
}
/** revert a subpart in place
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) reverse_sub(size_t ifirst, size_t num)
{
C4_ASSERT(ifirst >= 0 && ifirst <= len);
C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
if(num == 0) return;
detail::_do_reverse(str + ifirst, str + ifirst + num - 1);
}
/** revert a range in place
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(void) reverse_range(size_t ifirst, size_t ilast)
{
C4_ASSERT(ifirst >= 0 && ifirst <= len);
C4_ASSERT(ilast >= 0 && ilast <= len);
if(ifirst == ilast) return;
detail::_do_reverse(str + ifirst, str + ilast - 1);
}
public:
/** erase part of the string. eg, with char s[] = "0123456789",
* substr(s).erase(3, 2) = "01256789", and s is now "01245678989"
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(basic_substring) erase(size_t pos, size_t num)
{
C4_ASSERT(pos >= 0 && pos+num <= len);
size_t num_to_move = len - pos - num;
memmove(str + pos, str + pos + num, sizeof(C) * num_to_move);
return basic_substring{str, len - num};
}
/** @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(basic_substring) erase_range(size_t first, size_t last)
{
C4_ASSERT(first <= last);
return erase(first, static_cast<size_t>(last-first));
}
/** erase a part of the string.
* @note @p sub must be a substring of this string
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(basic_substring) erase(ro_substr sub)
{
C4_ASSERT(is_super(sub));
C4_ASSERT(sub.str >= str);
return erase(static_cast<size_t>(sub.str - str), sub.len);
}
public:
/** replace every occurrence of character @p value with the character @p repl
* @return the number of characters that were replaced
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(size_t) replace(C value, C repl, size_t pos=0)
{
C4_ASSERT((pos >= 0 && pos <= len) || pos == npos);
size_t did_it = 0;
while((pos = find(value, pos)) != npos)
{
str[pos++] = repl;
++did_it;
}
return did_it;
}
/** replace every occurrence of each character in @p value with
* the character @p repl.
* @return the number of characters that were replaced
* @note this method requires that the string memory is writeable and is SFINAEd out for const C */
C4_REQUIRE_RW(size_t) replace(ro_substr chars, C repl, size_t pos=0)
{
C4_ASSERT((pos >= 0 && pos <= len) || pos == npos);
size_t did_it = 0;
while((pos = first_of(chars, pos)) != npos)
{
str[pos++] = repl;
++did_it;
}
return did_it;
}
/** replace @p pattern with @p repl, and write the result into
* @dst. pattern and repl don't need equal sizes.
*
* @return the required size for dst. No overflow occurs if
* dst.len is smaller than the required size; this can be used to
* determine the required size for an existing container. */
size_t replace_all(rw_substr dst, ro_substr pattern, ro_substr repl, size_t pos=0) const
{
C4_ASSERT( ! pattern.empty()); //!< @todo relax this precondition
C4_ASSERT( ! this ->overlaps(dst)); //!< @todo relax this precondition
C4_ASSERT( ! pattern.overlaps(dst));
C4_ASSERT( ! repl .overlaps(dst));
C4_ASSERT((pos >= 0 && pos <= len) || pos == npos);
C4_SUPPRESS_WARNING_GCC_PUSH
C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc11 has a false positive here
#if (!defined(__clang__)) && (defined(__GNUC__) && (__GNUC__ >= 7))
C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc11 has a false positive here
#endif
#define _c4append(first, last) \
{ \
C4_ASSERT((last) >= (first)); \
size_t num = static_cast<size_t>((last) - (first)); \
if(sz + num <= dst.len) \
{ \
memcpy(dst.str + sz, first, num * sizeof(C)); \
} \
sz += num; \
}
size_t sz = 0;
size_t b = pos;
_c4append(str, str + pos);
do {
size_t e = find(pattern, b);
if(e == npos)
{
_c4append(str + b, str + len);
break;
}
_c4append(str + b, str + e);
_c4append(repl.begin(), repl.end());
b = e + pattern.size();
} while(b < len && b != npos);
return sz;
#undef _c4append
C4_SUPPRESS_WARNING_GCC_POP
}
/** @} */
}; // template class basic_substring
#undef C4_REQUIRE_RW
#undef C4_REQUIRE_RO
#undef C4_NC2C
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** Because of a C++ limitation, substr cannot provide simultaneous
* overloads for constructing from a char[N] and a char*; the latter
* will always be chosen by the compiler. So this specialization is
* provided to simplify obtaining a substr from a char*. Being a
* function has the advantage of highlighting the strlen() cost.
*
* @see to_csubstr
* @see For a more detailed explanation on why the overloads cannot
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
inline substr to_substr(char *s)
{
return substr(s, s ? strlen(s) : 0);
}
/** Because of a C++ limitation, substr cannot provide simultaneous
* overloads for constructing from a char[N] and a char*; the latter
* will always be chosen by the compiler. So this specialization is
* provided to simplify obtaining a substr from a char*. Being a
* function has the advantage of highlighting the strlen() cost.
*
* @see to_substr
* @see For a more detailed explanation on why the overloads cannot
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
inline csubstr to_csubstr(char *s)
{
return csubstr(s, s ? strlen(s) : 0);
}
/** Because of a C++ limitation, substr cannot provide simultaneous
* overloads for constructing from a const char[N] and a const char*;
* the latter will always be chosen by the compiler. So this
* specialization is provided to simplify obtaining a substr from a
* char*. Being a function has the advantage of highlighting the
* strlen() cost.
*
* @overload to_csubstr
* @see to_substr
* @see For a more detailed explanation on why the overloads cannot
* coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
inline csubstr to_csubstr(const char *s)
{
return csubstr(s, s ? strlen(s) : 0);
}
/** neutral version for use in generic code */
inline csubstr to_csubstr(csubstr s)
{
return s;
}
/** neutral version for use in generic code */
inline csubstr to_csubstr(substr s)
{
return s;
}
/** neutral version for use in generic code */
inline substr to_substr(substr s)
{
return s;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<typename C, size_t N> inline bool operator== (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) == 0; }
template<typename C, size_t N> inline bool operator!= (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) != 0; }
template<typename C, size_t N> inline bool operator< (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) > 0; }
template<typename C, size_t N> inline bool operator> (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) < 0; }
template<typename C, size_t N> inline bool operator<= (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) >= 0; }
template<typename C, size_t N> inline bool operator>= (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) <= 0; }
template<typename C> inline bool operator== (C const c, basic_substring<C> const that) { return that.compare(c) == 0; }
template<typename C> inline bool operator!= (C const c, basic_substring<C> const that) { return that.compare(c) != 0; }
template<typename C> inline bool operator< (C const c, basic_substring<C> const that) { return that.compare(c) > 0; }
template<typename C> inline bool operator> (C const c, basic_substring<C> const that) { return that.compare(c) < 0; }
template<typename C> inline bool operator<= (C const c, basic_substring<C> const that) { return that.compare(c) >= 0; }
template<typename C> inline bool operator>= (C const c, basic_substring<C> const that) { return that.compare(c) <= 0; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @define C4_SUBSTR_NO_OSTREAM_LSHIFT doctest does not deal well with
* template operator<<
* @see https://github.com/onqtam/doctest/pull/431 */
#ifndef C4_SUBSTR_NO_OSTREAM_LSHIFT
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
/** output the string to a stream */
template<class OStream, class C>
inline OStream& operator<< (OStream& os, basic_substring<C> s)
{
os.write(s.str, s.len);
return os;
}
// this causes ambiguity
///** this is used by google test */
//template<class OStream, class C>
//inline void PrintTo(basic_substring<C> s, OStream* os)
//{
// os->write(s.str, s.len);
//}
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif // !C4_SUBSTR_NO_OSTREAM_LSHIFT
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* _C4_SUBSTR_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/substr.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/ext/fast_float.hpp
// https://github.com/biojppm/c4core/src/c4/ext/fast_float.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_EXT_FAST_FLOAT_HPP_
#define _C4_EXT_FAST_FLOAT_HPP_
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
#elif defined(__clang__) || defined(__APPLE_CC__) || defined(_LIBCPP_VERSION)
# pragma clang diagnostic push
# if (defined(__clang_major__) && _clang_major__ >= 9) || defined(__APPLE_CC__)
# pragma clang diagnostic ignored "-Wfortify-source"
# endif
# pragma clang diagnostic ignored "-Wshift-count-overflow"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
// fast_float by Daniel Lemire
// fast_float by João Paulo Magalhaes
//
// with contributions from Eugene Golushkov
// with contributions from Maksim Kita
// with contributions from Marcin Wojdyr
// with contributions from Neal Richardson
// with contributions from Tim Paine
// with contributions from Fabio Pellacini
//
// MIT License Notice
//
// MIT License
//
// Copyright (c) 2021 The fast_float authors
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without
// limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice
// shall be included in all copies or substantial portions
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#ifndef FASTFLOAT_FAST_FLOAT_H
#define FASTFLOAT_FAST_FLOAT_H
#include <system_error>
namespace fast_float {
enum chars_format {
scientific = 1<<0,
fixed = 1<<2,
hex = 1<<3,
general = fixed | scientific
};
struct from_chars_result {
const char *ptr;
std::errc ec;
};
struct parse_options {
constexpr explicit parse_options(chars_format fmt = chars_format::general,
char dot = '.')
: format(fmt), decimal_point(dot) {}
/** Which number formats are accepted */
chars_format format;
/** The character used as decimal point */
char decimal_point;
};
/**
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
* The resulting floating-point value is the closest floating-point values (using either float or double),
* using the "round to even" convention for values that would otherwise fall right in-between two values.
* That is, we provide exact parsing according to the IEEE standard.
*
* Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
*
* The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
*
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
* the type `fast_float::chars_format`. It is a bitset value: we check whether
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
* to determine whether we allowe the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/
template<typename T>
from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt = chars_format::general) noexcept;
/**
* Like from_chars, but accepts an `options` argument to govern number parsing.
*/
template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept;
}
#endif // FASTFLOAT_FAST_FLOAT_H
#ifndef FASTFLOAT_FLOAT_COMMON_H
#define FASTFLOAT_FLOAT_COMMON_H
#include <cfloat>
//included above:
//#include <cstdint>
#include <cassert>
//included above:
//#include <cstring>
//included above:
//#include <type_traits>
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|| defined(__MINGW64__) \
|| defined(__s390x__) \
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
|| defined(__EMSCRIPTEN__))
#define FASTFLOAT_64BIT
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|| defined(__arm__) || defined(_M_ARM) \
|| defined(__MINGW32__))
#define FASTFLOAT_32BIT
#else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
// We can never tell the register width, but the SIZE_MAX is a good approximation.
// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
#if SIZE_MAX == 0xffff
#error Unknown platform (16-bit, unsupported)
#elif SIZE_MAX == 0xffffffff
#define FASTFLOAT_32BIT
#elif SIZE_MAX == 0xffffffffffffffff
#define FASTFLOAT_64BIT
#else
#error Unknown platform (not 32-bit, not 64-bit?)
#endif
#endif
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__))
#include <intrin.h>
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define FASTFLOAT_VISUAL_STUDIO 1
#endif
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define FASTFLOAT_IS_BIG_ENDIAN 0
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <machine/endian.h>
#elif defined(sun) || defined(__sun)
#include <sys/byteorder.h>
#else
#include <endian.h>
#endif
#
#ifndef __BYTE_ORDER__
// safe choice
#define FASTFLOAT_IS_BIG_ENDIAN 0
#endif
#
#ifndef __ORDER_LITTLE_ENDIAN__
// safe choice
#define FASTFLOAT_IS_BIG_ENDIAN 0
#endif
#
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN 0
#else
#define FASTFLOAT_IS_BIG_ENDIAN 1
#endif
#endif
#ifdef FASTFLOAT_VISUAL_STUDIO
#define fastfloat_really_inline __forceinline
#else
#define fastfloat_really_inline inline __attribute__((always_inline))
#endif
#ifndef FASTFLOAT_ASSERT
#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); }
#endif
#ifndef FASTFLOAT_DEBUG_ASSERT
//included above:
//#include <cassert>
#define FASTFLOAT_DEBUG_ASSERT(x) assert(x)
#endif
// rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
namespace fast_float {
// Compares two ASCII strings in a case insensitive manner.
inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
size_t length) {
char running_diff{0};
for (size_t i = 0; i < length; i++) {
running_diff |= (input1[i] ^ input2[i]);
}
return (running_diff == 0) || (running_diff == 32);
}
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
// a pointer and a length to a contiguous block of memory
template <typename T>
struct span {
const T* ptr;
size_t length;
span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept {
return length;
}
const T& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index];
}
};
struct value128 {
uint64_t low;
uint64_t high;
value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
value128() : low(0), high(0) {}
};
/* result might be undefined when input_num is zero */
fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
assert(input_num > 0);
#ifdef FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64)
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero);
#else
int last_bit = 0;
if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32;
if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16;
if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8;
if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4;
if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2;
if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1;
return 63 - last_bit;
#endif
#else
return __builtin_clzll(input_num);
#endif
}
#ifdef FASTFLOAT_32BIT
// slow emulation routine for 32-bit
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
// slow emulation routine for 32-bit
#if !defined(__MINGW64__)
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif // !__MINGW64__
#endif // FASTFLOAT_32BIT
// compute 64-bit a*b
fastfloat_really_inline value128 full_multiplication(uint64_t a,
uint64_t b) {
value128 answer;
#ifdef _M_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emulate
answer.high = __umulh(a, b);
answer.low = a * b;
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
#elif defined(FASTFLOAT_64BIT)
__uint128_t r = ((__uint128_t)a) * b;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#else
#error Not implemented
#endif
return answer;
}
struct adjusted_mantissa {
uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default;
bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2;
}
bool operator!=(const adjusted_mantissa &o) const {
return mantissa != o.mantissa || power2 != o.power2;
}
};
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static int32_t invalid_am_bias = -0x8000;
constexpr static double powers_of_ten_double[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5,
1e6, 1e7, 1e8, 1e9, 1e10};
template <typename T> struct binary_format {
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
static inline constexpr int mantissa_explicit_bits();
static inline constexpr int minimum_exponent();
static inline constexpr int infinite_power();
static inline constexpr int sign_index();
static inline constexpr int min_exponent_fast_path();
static inline constexpr int max_exponent_fast_path();
static inline constexpr int max_exponent_round_to_even();
static inline constexpr int min_exponent_round_to_even();
static inline constexpr uint64_t max_mantissa_fast_path();
static inline constexpr int largest_power_of_ten();
static inline constexpr int smallest_power_of_ten();
static inline constexpr T exact_power_of_ten(int64_t power);
static inline constexpr size_t max_digits();
static inline constexpr equiv_uint exponent_mask();
static inline constexpr equiv_uint mantissa_mask();
static inline constexpr equiv_uint hidden_bit_mask();
};
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52;
}
template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
return 23;
}
template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
return 23;
}
template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
return 10;
}
template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
return -4;
}
template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
return -17;
}
template <> inline constexpr int binary_format<double>::minimum_exponent() {
return -1023;
}
template <> inline constexpr int binary_format<float>::minimum_exponent() {
return -127;
}
template <> inline constexpr int binary_format<double>::infinite_power() {
return 0x7FF;
}
template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF;
}
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -22;
#endif
}
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -10;
#endif
}
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22;
}
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
return 10;
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
return powers_of_ten_double[power];
}
template <>
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
return powers_of_ten_float[power];
}
template <>
inline constexpr int binary_format<double>::largest_power_of_ten() {
return 308;
}
template <>
inline constexpr int binary_format<float>::largest_power_of_ten() {
return 38;
}
template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() {
return -342;
}
template <>
inline constexpr int binary_format<float>::smallest_power_of_ten() {
return -65;
}
template <> inline constexpr size_t binary_format<double>::max_digits() {
return 769;
}
template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() {
return 0x7F800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() {
return 0x7FF0000000000000;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() {
return 0x007FFFFF;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() {
return 0x000FFFFFFFFFFFFF;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() {
return 0x00800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() {
return 0x0010000000000000;
}
template<typename T>
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
uint64_t word = am.mantissa;
word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
word = negative
? word | (uint64_t(1) << binary_format<T>::sign_index()) : word;
#if FASTFLOAT_IS_BIG_ENDIAN == 1
if (std::is_same<T, float>::value) {
::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian
} else {
::memcpy(&value, &word, sizeof(T));
}
#else
// For little-endian systems:
::memcpy(&value, &word, sizeof(T));
#endif
}
} // namespace fast_float
#endif
#ifndef FASTFLOAT_ASCII_NUMBER_H
#define FASTFLOAT_ASCII_NUMBER_H
//included above:
//#include <cctype>
//included above:
//#include <cstdint>
//included above:
//#include <cstring>
#include <iterator>
namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
| (val & 0x000000FF00000000) >> 8
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
}
fastfloat_really_inline uint64_t read_u64(const char *chars) {
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
return val;
}
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
::memcpy(chars, &val, sizeof(uint64_t));
}
// credit @aqrit
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
}
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
return parse_eight_digits_unrolled(read_u64(chars));
}
// credit @aqrit
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080));
}
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
return is_made_of_eight_digits_fast(read_u64(chars));
}
typedef span<const char> byte_span;
struct parsed_number_string {
int64_t exponent{0};
uint64_t mantissa{0};
const char *lastmatch{nullptr};
bool negative{false};
bool valid{false};
bool too_many_digits{false};
// contains the range of the significant digits
byte_span integer{}; // non-nullable
byte_span fraction{}; // nullable
};
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
fastfloat_really_inline
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
const chars_format fmt = options.format;
const char decimal_point = options.decimal_point;
parsed_number_string answer;
answer.valid = false;
answer.too_many_digits = false;
answer.negative = (*p == '-');
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
++p;
if (p == pend) {
return answer;
}
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
return answer;
}
}
const char *const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p - '0'); // might overflow, we will handle the overflow later
++p;
}
const char *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = byte_span(start_digits, size_t(digit_count));
int64_t exponent = 0;
if ((p != pend) && (*p == decimal_point)) {
++p;
const char* before = p;
// can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck.
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = before - p;
answer.fraction = byte_span(before, size_t(p - before));
digit_count -= exponent;
}
// we must have encountered at least one integer!
if (digit_count == 0) {
return answer;
}
int64_t exp_number = 0; // explicit exponential part
if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
const char * location_of_e = p;
++p;
bool neg_exp = false;
if ((p != pend) && ('-' == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
if ((p == pend) || !is_integer(*p)) {
if(!(fmt & chars_format::fixed)) {
// We are in error.
return answer;
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
if(neg_exp) { exp_number = - exp_number; }
exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
}
answer.lastmatch = p;
answer.valid = true;
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits.
if (digit_count > 19) { // this is uncommon
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
const char *start = start_digits;
while ((start != pend) && (*start == '0' || *start == decimal_point)) {
if(*start == '0') { digit_count --; }
start++;
}
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to check if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = answer.integer.ptr;
const char* int_end = p + answer.integer.len();
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - '0');
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component.
p = answer.fraction.ptr;
const char* frac_end = p + answer.fraction.len();
while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - '0');
++p;
}
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
}
}
answer.exponent = exponent;
answer.mantissa = i;
return answer;
}
} // namespace fast_float
#endif
#ifndef FASTFLOAT_FAST_TABLE_H
#define FASTFLOAT_FAST_TABLE_H
//included above:
//#include <cstdint>
namespace fast_float {
/**
* When mapping numbers from decimal to binary,
* we go from w * 10^q to m * 2^p but we have
* 10^q = 5^q * 2^q, so effectively
* we are trying to match
* w * 2^q * 5^q to m * 2^p. Thus the powers of two
* are not a concern since they can be represented
* exactly using the binary notation, only the powers of five
* affect the binary significand.
*/
/**
* The smallest non-zero float (binary64) is 2^1074.
* We take as input numbers of the form w x 10^q where w < 2^64.
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
* However, we have that
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^1074.
* Thus it is possible for a number of the form w * 10^-342 where
* w is a 64-bit value to be a non-zero floating-point number.
*********
* Any number of form w * 10^309 where w>= 1 is going to be
* infinite in binary64 so we never need to worry about powers
* of 5 greater than 308.
*/
template <class unused = void>
struct powers_template {
constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
static const uint64_t power_of_five_128[number_of_entries];
};
template <class unused>
const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
0xe95a99df8ace6f53,0xf4d82c2c107973dc,
0x91d8a02bb6c10594,0x79071b9b8a4be869,
0xb64ec836a47146f9,0x9748e2826cdee284,
0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
0xb208ef855c969f4f,0xbdbd2d335e51a935,
0xde8b2b66b3bc4723,0xad2c788035e61382,
0x8b16fb203055ac76,0x4c3bcb5021afcc31,
0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
0xd953e8624b85dd78,0xd71d6dad34a2af0d,
0x87d4713d6f33aa6b,0x8672648c40e5ad68,
0xa9c98d8ccb009506,0x680efdaf511f18c2,
0xd43bf0effdc0ba48,0x212bd1b2566def2,
0x84a57695fe98746d,0x14bb630f7604b57,
0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
0xcf42894a5dce35ea,0x52064cac828675b9,
0x818995ce7aa0e1b2,0x7343efebd1940993,
0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
0xca66fa129f9b60a6,0xd41a26e077774ef6,
0xfd00b897478238d0,0x8920b098955522b4,
0x9e20735e8cb16382,0x55b46e5f5d5535b0,
0xc5a890362fddbc62,0xeb2189f734aa831d,
0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
0x9a6bb0aa55653b2d,0x47b233c92125366e,
0xc1069cd4eabe89f8,0x999ec0bb696e840a,
0xf148440a256e2c76,0xc00670ea43ca250d,
0x96cd2a865764dbca,0x380406926a5e5728,
0xbc807527ed3e12bc,0xc605083704f5ecf2,
0xeba09271e88d976b,0xf7864a44c633682e,
0x93445b8731587ea3,0x7ab3ee6afbe0211d,
0xb8157268fdae9e4c,0x5960ea05bad82964,
0xe61acf033d1a45df,0x6fb92487298e33bd,
0x8fd0c16206306bab,0xa5d3b6d479f8e056,
0xb3c4f1ba87bc8696,0x8f48a4899877186c,
0xe0b62e2929aba83c,0x331acdabfe94de87,
0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
0x892731ac9faf056e,0xbe311c083a225cd2,
0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
0xd64d3d9db981787d,0x92cbbccdad5b108,
0x85f0468293f0eb4e,0x25bbf56008c58ea5,
0xa76c582338ed2621,0xaf2af2b80af6f24e,
0xd1476e2c07286faa,0x1af5af660db4aee1,
0x82cca4db847945ca,0x50d98d9fc890ed4d,
0xa37fce126597973c,0xe50ff107bab528a0,
0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
0x9faacf3df73609b1,0x77b191618c54e9ac,
0xc795830d75038c1d,0xd59df5b9ef6a2417,
0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
0x9becce62836ac577,0x4ee367f9430aec32,
0xc2e801fb244576d5,0x229c41f793cda73f,
0xf3a20279ed56d48a,0x6b43527578c1110f,
0x9845418c345644d6,0x830a13896b78aaa9,
0xbe5691ef416bd60c,0x23cc986bc656d553,
0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
0x91376c36d99995be,0x23100809b9c21fa1,
0xb58547448ffffb2d,0xabd40a0c2832a78a,
0xe2e69915b3fff9f9,0x16c90c8f323f516c,
0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
0xb1442798f49ffb4a,0x99cd11cfdf41779c,
0xdd95317f31c7fa1d,0x40405643d711d583,
0x8a7d3eef7f1cfc52,0x482835ea666b2572,
0xad1c8eab5ee43b66,0xda3243650005eecf,
0xd863b256369d4a40,0x90bed43e40076a82,
0x873e4f75e2224e68,0x5a7744a6e804a291,
0xa90de3535aaae202,0x711515d0a205cb36,
0xd3515c2831559a83,0xd5a5b44ca873e03,
0x8412d9991ed58091,0xe858790afe9486c2,
0xa5178fff668ae0b6,0x626e974dbe39a872,
0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
0xa139029f6a239f72,0x1c1fffc1ebc44e80,
0xc987434744ac874e,0xa327ffb266b56220,
0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
0xc4ce17b399107c22,0xcb550fb4384d21d3,
0xf6019da07f549b2b,0x7e2a53a146606a48,
0x99c102844f94e0fb,0x2eda7444cbfc426d,
0xc0314325637a1939,0xfa911155fefb5308,
0xf03d93eebc589f88,0x793555ab7eba27ca,
0x96267c7535b763b5,0x4bc1558b2f3458de,
0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
0xea9c227723ee8bcb,0x465e15a979c1cadc,
0x92a1958a7675175f,0xbfacd89ec191ec9,
0xb749faed14125d36,0xcef980ec671f667b,
0xe51c79a85916f484,0x82b7e12780e7401a,
0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
0xdfbdcece67006ac9,0x67a791e093e1d49a,
0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
0xaecc49914078536d,0x58fae9f773886e18,
0xda7f5bf590966848,0xaf39a475506a899e,
0x888f99797a5e012d,0x6d8406c952429603,
0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
0x855c3be0a17fcd26,0x5cf2eea09a55067f,
0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
0xd0601d8efc57b08b,0xf13b94daf124da26,
0x823c12795db6ce57,0x76c53d08d6b70858,
0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
0xfe5d54150b090b02,0xd3f93b35435d7c4c,
0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
0xc6b8e9b0709f109a,0x359ab6419ca1091b,
0xf867241c8cc6d4c0,0xc30163d203c94b62,
0x9b407691d7fc44f8,0x79e0de63425dcf1d,
0xc21094364dfb5636,0x985915fc12f542e4,
0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
0xbd8430bd08277231,0x50c6ff782a838353,
0xece53cec4a314ebd,0xa4f8bf5635246428,
0x940f4613ae5ed136,0x871b7795e136be99,
0xb913179899f68584,0x28e2557b59846e3f,
0xe757dd7ec07426e5,0x331aeada2fe589cf,
0x9096ea6f3848984f,0x3ff0d2c85def7621,
0xb4bca50b065abe63,0xfed077a756b53a9,
0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
0xb080392cc4349dec,0xbd8d794d96aacfb3,
0xdca04777f541c567,0xecf0d7a0fc5583a0,
0x89e42caaf9491b60,0xf41686c49db57244,
0xac5d37d5b79b6239,0x311c2875c522ced5,
0xd77485cb25823ac7,0x7d633293366b828b,
0x86a8d39ef77164bc,0xae5dff9c02033197,
0xa8530886b54dbdeb,0xd9f57f830283fdfc,
0xd267caa862a12d66,0xd072df63c324fd7b,
0x8380dea93da4bc60,0x4247cb9e59f71e6d,
0xa46116538d0deb78,0x52d9be85f074e608,
0xcd795be870516656,0x67902e276c921f8b,
0x806bd9714632dff6,0xba1cd8a3db53b6,
0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
0xfad2a4b13d1b5d6c,0x796b805720085f81,
0x9cc3a6eec6311a63,0xcbe3303674053bb0,
0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
0xf4f1b4d515acb93b,0xee92fb5515482d44,
0x991711052d8bf3c5,0x751bdd152d4d1c4a,
0xbf5cd54678eef0b6,0xd262d45a78a0635d,
0xef340a98172aace4,0x86fb897116c87c34,
0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
0xbae0a846d2195712,0x8974836059cca109,
0xe998d258869facd7,0x2bd1a438703fc94b,
0x91ff83775423cc06,0x7b6306a34627ddcf,
0xb67f6455292cbf08,0x1a3bc84c17b1d542,
0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
0x8e938662882af53e,0x547eb47b7282ee9c,
0xb23867fb2a35b28d,0xe99e619a4f23aa43,
0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
0xae0b158b4738705e,0x9624ab50b148d445,
0xd98ddaee19068c76,0x3badd624dd9b0957,
0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
0xd47487cc8470652b,0x7647c3200069671f,
0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
0xa5fb0a17c777cf09,0xf468107100525890,
0xcf79cc9db955c2cc,0x7182148d4066eeb4,
0x81ac1fe293d599bf,0xc6f14cd848405530,
0xa21727db38cb002f,0xb8ada00e5a506a7c,
0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
0xfd442e4688bd304a,0x908f4a166d1da663,
0x9e4a9cec15763e2e,0x9a598e4e043287fe,
0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
0xf7549530e188c128,0xd12bee59e68ef47c,
0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
0xc13a148e3032d6e7,0xe36a52363c1faf01,
0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
0xebdf661791d60f56,0x111b495b3464ad21,
0x936b9fcebb25c995,0xcab10dd900beec34,
0xb84687c269ef3bfb,0x3d5d514f40eea742,
0xe65829b3046b0afa,0xcb4a5a3112a5112,
0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
0xb3f4e093db73a093,0x59ed216765690f56,
0xe0f218b8d25088b8,0x306869c13ec3532c,
0x8c974f7383725573,0x1e414218c73a13fb,
0xafbd2350644eeacf,0xe5d1929ef90898fa,
0xdbac6c247d62a583,0xdf45f746b74abf39,
0x894bc396ce5da772,0x6b8bba8c328eb783,
0xab9eb47c81f5114f,0x66ea92f3f326564,
0xd686619ba27255a2,0xc80a537b0efefebd,
0x8613fd0145877585,0xbd06742ce95f5f36,
0xa798fc4196e952e7,0x2c48113823b73704,
0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
0x82ef85133de648c4,0x9a984d73dbe722fb,
0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
0xcc963fee10b7d1b3,0x318df905079926a8,
0xffbbcfe994e5c61f,0xfdf17746497f7052,
0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
0x9c1661a651213e2d,0x6bea10ca65c084e,
0xc31bfa0fe5698db8,0x486e494fcff30a62,
0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
0x986ddb5c6b3a76b7,0xf89629465a75e01c,
0xbe89523386091465,0xf6bbb397f1135823,
0xee2ba6c0678b597f,0x746aa07ded582e2c,
0x94db483840b717ef,0xa8c2a44eb4571cdc,
0xba121a4650e4ddeb,0x92f34d62616ce413,
0xe896a0d7e51e1566,0x77b020baf9c81d17,
0x915e2486ef32cd60,0xace1474dc1d122e,
0xb5b5ada8aaff80b8,0xd819992132456ba,
0xe3231912d5bf60e6,0x10e1fff697ed6c69,
0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
0xad4ab7112eb3929d,0x86c16c98d2c953c6,
0xd89d64d57a607744,0xe871c7bf077ba8b7,
0x87625f056c7c4a8b,0x11471cd764ad4972,
0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
0xd389b47879823479,0x4aff1d108d4ec2c3,
0x843610cb4bf160cb,0xcedf722a585139ba,
0xa54394fe1eedb8fe,0xc2974eb4ee658828,
0xce947a3da6a9273e,0x733d226229feea32,
0x811ccc668829b887,0x806357d5a3f525f,
0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
0xc9bcff6034c13052,0xfc89b393dd02f0b5,
0xfc2c3f3841f17c67,0xbbac2078d443ace2,
0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
0xc5029163f384a931,0xa9e795e65d4df11,
0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
0x99ea0196163fa42e,0x504bced1bf8e4e45,
0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
0xf07da27a82c37088,0x5d767327bb4e5a4c,
0x964e858c91ba2655,0x3a6a07f8d510f86f,
0xbbe226efb628afea,0x890489f70a55368b,
0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
0xb77ada0617e3bbcb,0x9ce6ebb40173744,
0xe55990879ddcaabd,0xcc420a6a101d0515,
0x8f57fa54c2a9eab6,0x9fa946824a12232d,
0xb32df8e9f3546564,0x47939822dc96abf9,
0xdff9772470297ebd,0x59787e2b93bc56f7,
0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
0xaefae51477a06b03,0xede622920b6b23f1,
0xdab99e59958885c4,0xe95fab368e45eced,
0x88b402f7fd75539b,0x11dbcb0218ebb414,
0xaae103b5fcd2a881,0xd652bdc29f26a119,
0xd59944a37c0752a2,0x4be76d3346f0495f,
0x857fcae62d8493a5,0x6f70a4400c562ddb,
0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
0xd097ad07a71f26b2,0x7e2000a41346a7a7,
0x825ecc24c873782f,0x8ed400668c0c28c8,
0xa2f67f2dfa90563b,0x728900802f0f32fa,
0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
0xfea126b7d78186bc,0xe2f610c84987bfa8,
0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
0xc6ede63fa05d3143,0x91503d1c79720dbb,
0xf8a95fcf88747d94,0x75a44c6397ce912a,
0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
0xc24452da229b021b,0xfbe85badce996168,
0xf2d56790ab41c2a2,0xfae27299423fb9c3,
0x97c560ba6b0919a5,0xdccd879fc967d41a,
0xbdb6b8e905cb600f,0x5400e987bbc1c920,
0xed246723473e3813,0x290123e9aab23b68,
0x9436c0760c86e30b,0xf9a0b6720aaf6521,
0xb94470938fa89bce,0xf808e40e8d5b3e69,
0xe7958cb87392c2c2,0xb60b1d1230b20e04,
0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
0xe2280b6c20dd5232,0x25c6da63c38de1b0,
0x8d590723948a535f,0x579c487e5a38ad0e,
0xb0af48ec79ace837,0x2d835a9df0c6d851,
0xdcdb1b2798182244,0xf8e431456cf88e65,
0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
0xac8b2d36eed2dac5,0xe272467e3d222f3f,
0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
0x86ccbb52ea94baea,0x98e947129fc2b4e9,
0xa87fea27a539e9a5,0x3f2398d747b36224,
0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
0x83a3eeeef9153e89,0x1953cf68300424ac,
0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
0xcdb02555653131b6,0x3792f412cb06794d,
0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
0xc8de047564d20a8b,0xf245825a5a445275,
0xfb158592be068d2e,0xeed6e2f0f0d56712,
0x9ced737bb6c4183d,0x55464dd69685606b,
0xc428d05aa4751e4c,0xaa97e14c3c26b886,
0xf53304714d9265df,0xd53dd99f4b3066a8,
0x993fe2c6d07b7fab,0xe546a8038efe4029,
0xbf8fdb78849a5f96,0xde98520472bdd033,
0xef73d256a5c0f77c,0x963e66858f6d4440,
0x95a8637627989aad,0xdde7001379a44aa8,
0xbb127c53b17ec159,0x5560c018580d5d52,
0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
0x9226712162ab070d,0xcab3961304ca70e8,
0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
0xb267ed1940f1c61c,0x55f038b237591ed3,
0xdf01e85f912e37a3,0x6b6c46dec52f6688,
0x8b61313bbabce2c6,0x2323ac4b3b3da015,
0xae397d8aa96c1b77,0xabec975e0a0d081a,
0xd9c7dced53c72255,0x96e7bd358c904a21,
0x881cea14545c7575,0x7e50d64177da2e54,
0xaa242499697392d2,0xdde50bd1d5d0b9e9,
0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
0x84ec3c97da624ab4,0xbd5af13bef0b113e,
0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
0xcfb11ead453994ba,0x67de18eda5814af2,
0x81ceb32c4b43fcf4,0x80eacf948770ced7,
0xa2425ff75e14fc31,0xa1258379a94d028d,
0xcad2f7f5359a3b3e,0x96ee45813a04330,
0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
0x9e74d1b791e07e48,0x775ea264cf55347e,
0xc612062576589dda,0x95364afe032a819e,
0xf79687aed3eec551,0x3a83ddbd83f52205,
0x9abe14cd44753b52,0xc4926a9672793543,
0xc16d9a0095928a27,0x75b7053c0f178294,
0xf1c90080baf72cb1,0x5324c68b12dd6339,
0x971da05074da7bee,0xd3f6fc16ebca5e04,
0xbce5086492111aea,0x88f4bb1ca6bcf585,
0xec1e4a7db69561a5,0x2b31e9e3d06c32e6,
0x9392ee8e921d5d07,0x3aff322e62439fd0,
0xb877aa3236a4b449,0x9befeb9fad487c3,
0xe69594bec44de15b,0x4c2ebe687989a9b4,
0x901d7cf73ab0acd9,0xf9d37014bf60a11,
0xb424dc35095cd80f,0x538484c19ef38c95,
0xe12e13424bb40e13,0x2865a5f206b06fba,
0x8cbccc096f5088cb,0xf93f87b7442e45d4,
0xafebff0bcb24aafe,0xf78f69a51539d749,
0xdbe6fecebdedd5be,0xb573440e5a884d1c,
0x89705f4136b4a597,0x31680a88f8953031,
0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
0xd6bf94d5e57a42bc,0x3d32907604691b4d,
0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
0xa7c5ac471b478423,0xfcf80dc33721d54,
0xd1b71758e219652b,0xd3c36113404ea4a9,
0x83126e978d4fdf3b,0x645a1cac083126ea,
0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
0xcccccccccccccccc,0xcccccccccccccccd,
0x8000000000000000,0x0,
0xa000000000000000,0x0,
0xc800000000000000,0x0,
0xfa00000000000000,0x0,
0x9c40000000000000,0x0,
0xc350000000000000,0x0,
0xf424000000000000,0x0,
0x9896800000000000,0x0,
0xbebc200000000000,0x0,
0xee6b280000000000,0x0,
0x9502f90000000000,0x0,
0xba43b74000000000,0x0,
0xe8d4a51000000000,0x0,
0x9184e72a00000000,0x0,
0xb5e620f480000000,0x0,
0xe35fa931a0000000,0x0,
0x8e1bc9bf04000000,0x0,
0xb1a2bc2ec5000000,0x0,
0xde0b6b3a76400000,0x0,
0x8ac7230489e80000,0x0,
0xad78ebc5ac620000,0x0,
0xd8d726b7177a8000,0x0,
0x878678326eac9000,0x0,
0xa968163f0a57b400,0x0,
0xd3c21bcecceda100,0x0,
0x84595161401484a0,0x0,
0xa56fa5b99019a5c8,0x0,
0xcecb8f27f4200f3a,0x0,
0x813f3978f8940984,0x4000000000000000,
0xa18f07d736b90be5,0x5000000000000000,
0xc9f2c9cd04674ede,0xa400000000000000,
0xfc6f7c4045812296,0x4d00000000000000,
0x9dc5ada82b70b59d,0xf020000000000000,
0xc5371912364ce305,0x6c28000000000000,
0xf684df56c3e01bc6,0xc732000000000000,
0x9a130b963a6c115c,0x3c7f400000000000,
0xc097ce7bc90715b3,0x4b9f100000000000,
0xf0bdc21abb48db20,0x1e86d40000000000,
0x96769950b50d88f4,0x1314448000000000,
0xbc143fa4e250eb31,0x17d955a000000000,
0xeb194f8e1ae525fd,0x5dcfab0800000000,
0x92efd1b8d0cf37be,0x5aa1cae500000000,
0xb7abc627050305ad,0xf14a3d9e40000000,
0xe596b7b0c643c719,0x6d9ccd05d0000000,
0x8f7e32ce7bea5c6f,0xe4820023a2000000,
0xb35dbf821ae4f38b,0xdda2802c8a800000,
0xe0352f62a19e306e,0xd50b2037ad200000,
0x8c213d9da502de45,0x4526f422cc340000,
0xaf298d050e4395d6,0x9670b12b7f410000,
0xdaf3f04651d47b4c,0x3c0cdd765f114000,
0x88d8762bf324cd0f,0xa5880a69fb6ac800,
0xab0e93b6efee0053,0x8eea0d047a457a00,
0xd5d238a4abe98068,0x72a4904598d6d880,
0x85a36366eb71f041,0x47a6da2b7f864750,
0xa70c3c40a64e6c51,0x999090b65f67d924,
0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
0x82818f1281ed449f,0xbff8f10e7a8921a4,
0xa321f2d7226895c7,0xaff72d52192b6a0d,
0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
0xfee50b7025c36a08,0x2f236d04753d5b4,
0x9f4f2726179a2245,0x1d762422c946590,
0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
0x9b934c3b330c8577,0x63cc55f49f88eb2f,
0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
0xf316271c7fc3908a,0x8bef464e3945ef7a,
0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
0xb975d6b6ee39e436,0xb3e2fd538e122b44,
0xe7d34c64a9c85d44,0x60dbbca87196b616,
0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
0xb51d13aea4a488dd,0x6babab6398bdbe41,
0xe264589a4dcdab14,0xc696963c7eed2dd1,
0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
0xb0de65388cc8ada8,0x3b25a55f43294bcb,
0xdd15fe86affad912,0x49ef0eb713f39ebe,
0x8a2dbf142dfcc7ab,0x6e3569326c784337,
0xacb92ed9397bf996,0x49c2c37f07965404,
0xd7e77a8f87daf7fb,0xdc33745ec97be906,
0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
0xd2d80db02aabd62b,0xf50a3fa490c30190,
0x83c7088e1aab65db,0x792667c6da79e0fa,
0xa4b8cab1a1563f52,0x577001b891185938,
0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
0x80b05e5ac60b6178,0x544f8158315b05b4,
0xa0dc75f1778e39d6,0x696361ae3db1c721,
0xc913936dd571c84c,0x3bc3a19cd1e38e9,
0xfb5878494ace3a5f,0x4ab48a04065c723,
0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
0xc45d1df942711d9a,0x3ba5d0bd324f8394,
0xf5746577930d6500,0xca8f44ec7ee36479,
0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
0x95d04aee3b80ece5,0xbba1f1d158724a12,
0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
0xea1575143cf97226,0xf52d09d71a3293bd,
0x924d692ca61be758,0x593c2626705f9c56,
0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
0xe498f455c38b997a,0xb6dfb9c0f956447,
0x8edf98b59a373fec,0x4724bd4189bd5eac,
0xb2977ee300c50fe7,0x58edec91ec2cb657,
0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
0x8b865b215899f46c,0xbd79e0d20082ee74,
0xae67f1e9aec07187,0xecd8590680a3aa11,
0xda01ee641a708de9,0xe80e6f4820cc9495,
0x884134fe908658b2,0x3109058d147fdcdd,
0xaa51823e34a7eede,0xbd4b46f0599fd415,
0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
0x850fadc09923329e,0x3e2cf6bc604ddb0,
0xa6539930bf6bff45,0x84db8346b786151c,
0xcfe87f7cef46ff16,0xe612641865679a63,
0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
0xa26da3999aef7749,0xe3be5e330f38f09d,
0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
0xc646d63501a1511d,0xb281e1fd541501b8,
0xf7d88bc24209a565,0x1f225a7ca91a4226,
0x9ae757596946075f,0x3375788de9b06958,
0xc1a12d2fc3978937,0x52d6b1641c83ae,
0xf209787bb47d6b84,0xc0678c5dbd23a49a,
0x9745eb4d50ce6332,0xf840b7ba963646e0,
0xbd176620a501fbff,0xb650e5a93bc3d898,
0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
0x93ba47c980e98cdf,0xc66f336c36b10137,
0xb8a8d9bbe123f017,0xb80b0047445d4184,
0xe6d3102ad96cec1d,0xa60dc059157491e5,
0x9043ea1ac7e41392,0x87c89837ad68db2f,
0xb454e4a179dd1877,0x29babe4598c311fb,
0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
0x8ce2529e2734bb1d,0x1899e4a65f58660c,
0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
0xdc21a1171d42645d,0x76707543f4fa1f73,
0x899504ae72497eba,0x6a06494a791c53a8,
0xabfa45da0edbde69,0x487db9d17636892,
0xd6f8d7509292d603,0x45a9d2845d3c42b6,
0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
0xa7f26836f282b732,0x8e6cac7768d7141e,
0xd1ef0244af2364ff,0x3207d795430cd926,
0x8335616aed761f1f,0x7f44e6bd49e807b8,
0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
0xcd036837130890a1,0x36dba887c37a8c0f,
0x802221226be55a64,0xc2494954da2c9789,
0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
0xc83553c5c8965d3d,0x6f92829494e5acc7,
0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
0x9c69a97284b578d7,0xff2a760414536efb,
0xc38413cf25e2d70d,0xfef5138519684aba,
0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
0x98bf2f79d5993802,0xef2f773ffbd97a61,
0xbeeefb584aff8603,0xaafb550ffacfd8fa,
0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
0x952ab45cfa97a0b2,0xdd945a747bf26183,
0xba756174393d88df,0x94f971119aeef9e4,
0xe912b9d1478ceb17,0x7a37cd5601aab85d,
0x91abb422ccb812ee,0xac62e055c10ab33a,
0xb616a12b7fe617aa,0x577b986b314d6009,
0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
0x8e41ade9fbebc27d,0x14588f13be847307,
0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
0x8aec23d680043bee,0x25de7bb9480d5854,
0xada72ccc20054ae9,0xaf561aa79a10ae6a,
0xd910f7ff28069da4,0x1b2ba1518094da04,
0x87aa9aff79042286,0x90fb44d2f05d0842,
0xa99541bf57452b28,0x353a1607ac744a53,
0xd3fa922f2d1675f2,0x42889b8997915ce8,
0x847c9b5d7c2e09b7,0x69956135febada11,
0xa59bc234db398c25,0x43fab9837e699095,
0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
0x8161afb94b44f57d,0x1d1be0eebac278f5,
0xa1ba1ba79e1632dc,0x6462d92a69731732,
0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
0xfcb2cb35e702af78,0x5cda735244c3d43e,
0x9defbf01b061adab,0x3a0888136afa64a7,
0xc56baec21c7a1916,0x88aaa1845b8fdd0,
0xf6c69a72a3989f5b,0x8aad549e57273d45,
0x9a3c2087a63f6399,0x36ac54e2f678864b,
0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
0x969eb7c47859e743,0x9f644ae5a4b1b325,
0xbc4665b596706114,0x873d5d9f0dde1fee,
0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
0x9316ff75dd87cbd8,0x9a7f12442d588f2,
0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
0x8fa475791a569d10,0xf96e017d694487bc,
0xb38d92d760ec4455,0x37c981dcc395a9ac,
0xe070f78d3927556a,0x85bbe253f47b1417,
0x8c469ab843b89562,0x93956d7478ccec8e,
0xaf58416654a6babb,0x387ac8d1970027b2,
0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
0x88fcf317f22241e2,0x441fece3bdf81f03,
0xab3c2fddeeaad25a,0xd527e81cad7626c3,
0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
0x85c7056562757456,0xf6872d5667844e49,
0xa738c6bebb12d16c,0xb428f8ac016561db,
0xd106f86e69d785c7,0xe13336d701beba52,
0x82a45b450226b39c,0xecc0024661173473,
0xa34d721642b06084,0x27f002d7f95d0190,
0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
0xff290242c83396ce,0x7e67047175a15271,
0x9f79a169bd203e41,0xf0062c6e984d386,
0xc75809c42c684dd1,0x52c07b78a3e60868,
0xf92e0c3537826145,0xa7709a56ccdf8a82,
0x9bbcc7a142b17ccb,0x88a66076400bb691,
0xc2abf989935ddbfe,0x6acff893d00ea435,
0xf356f7ebf83552fe,0x583f6b8c4124d43,
0x98165af37b2153de,0xc3727a337a8b704a,
0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
0xeda2ee1c7064130c,0x1162def06f79df73,
0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
0x910ab1d4db9914a0,0x1d9c9892400a22a2,
0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
0x8da471a9de737e24,0x5ceaecfed289e5d2,
0xb10d8e1456105dad,0x7425a83e872c5f47,
0xdd50f1996b947518,0xd12f124e28f77719,
0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
0xace73cbfdc0bfb7b,0x636cc64d1001550b,
0xd8210befd30efa5a,0x3c47f7e05401aa4e,
0x8714a775e3e95c78,0x65acfaec34810a71,
0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
0xd31045a8341ca07c,0x1ede48111209a050,
0x83ea2b892091e44d,0x934aed0aab460432,
0xa4e4b66b68b65d60,0xf81da84d5617853f,
0xce1de40642e3f4b9,0x36251260ab9d668e,
0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
0xa1075a24e4421730,0xb24cf65b8612f81f,
0xc94930ae1d529cfc,0xdee033f26797b627,
0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
0x9d412e0806e88aa5,0x8e1f289560ee864e,
0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
0xf5b5d7ec8acb58a2,0xae10af696774b1db,
0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
0xbff610b0cc6edd3f,0x17fd090a58d32af3,
0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
0xea53df5fd18d5513,0x84c86189216dc5ed,
0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
0xe4d5e82392a40515,0xfabaf3feaa5334a,
0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
0xb2c71d5bca9023f8,0x743e20e9ef511012,
0xdf78e4b2bd342cf6,0x914da9246b255416,
0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
0xae9672aba3d0c320,0xa184ac2473b529b1,
0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
0x8865899617fb1871,0x7e2fa67c7a658892,
0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
0xd51ea6fa85785631,0x552a74227f3ea565,
0x8533285c936b35de,0xd53a88958f87275f,
0xa67ff273b8460356,0x8a892abaf368f137,
0xd01fef10a657842c,0x2d2b7569b0432d85,
0x8213f56a67f6b29b,0x9c3b29620e29fc73,
0xa298f2c501f45f42,0x8349f3ba91b47b8f,
0xcb3f2f7642717713,0x241c70a936219a73,
0xfe0efb53d30dd4d7,0xed238cd383aa0110,
0x9ec95d1463e8a506,0xf4363804324a40aa,
0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
0xf81aa16fdc1b81da,0xdd94b7868e94050a,
0x9b10a4e5e9913128,0xca7cf2b4191c8326,
0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
0xf24a01a73cf2dccf,0xbc633b39673c8cec,
0x976e41088617ca01,0xd5be0503e085d813,
0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
0xec9c459d51852ba2,0xddf8e7d60ed1219e,
0x93e1ab8252f33b45,0xcabb90e5c942b503,
0xb8da1662e7b00a17,0x3d6a751f3b936243,
0xe7109bfba19c0c9d,0xcc512670a783ad4,
0x906a617d450187e2,0x27fb2b80668b24c5,
0xb484f9dc9641e9da,0xb1f9f660802dedf6,
0xe1a63853bbd26451,0x5e7873f8a0396973,
0x8d07e33455637eb2,0xdb0b487b6423e1e8,
0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
0xdc5c5301c56b75f7,0x7641a140cc7810fb,
0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
0xac2820d9623bf429,0x546345fa9fbdcd44,
0xd732290fbacaf133,0xa97c177947ad4095,
0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
0xa81f301449ee8c70,0x5c68f256bfff5a74,
0xd226fc195c6a2f8c,0x73832eec6fff3111,
0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
0xa0555e361951c366,0xd7e105bcc332621f,
0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
0xfa856334878fc150,0xb14f98f6f0feb951,
0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
0xc3b8358109e84f07,0xa862f80ec4700c8,
0xf4a642e14c6262c8,0xcd27bb612758c0fa,
0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
0xeeea5d5004981478,0x1858ccfce06cac74,
0x95527a5202df0ccb,0xf37801e0c43ebc8,
0xbaa718e68396cffd,0xd30560258f54e6ba,
0xe950df20247c83fd,0x47c6b82ef32a2069,
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
using powers = powers_template<>;
}
#endif
#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H
#define FASTFLOAT_DECIMAL_TO_BINARY_H
//included above:
//#include <cfloat>
#include <cinttypes>
#include <cmath>
//included above:
//#include <cstdint>
#include <cstdlib>
//included above:
//#include <cstring>
namespace fast_float {
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
// the result, with the "high" part corresponding to the most significant bits and the
// low part corresponding to the least significant bits.
//
template <int bit_precision>
fastfloat_really_inline
value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
// gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
constexpr uint64_t precision_mask = (bit_precision < 64) ?
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) {
firstproduct.high++;
}
}
return firstproduct;
}
namespace detail {
/**
* For q in (0,350), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* floor(p) + q
* where
* p = log(5**q)/log(2) = q * log(5)/log(2)
*
* For negative values of q in (-400,0), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* -ceil(p) + q
* where
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63;
}
} // namespace detail
// create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q.
template <typename binary>
fastfloat_really_inline
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer;
answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
return answer;
}
// w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary>
fastfloat_really_inline
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w);
w <<= lz;
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
return compute_error_scaled<binary>(q, product.high, lz);
}
// w * 10 ** q
// The returned value should be a valid ieee64 number that simply need to be packed.
// However, in some very rare cases, the computation will fail. In such cases, we
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
// in such cases.
template <typename binary>
fastfloat_really_inline
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
if (q > binary::largest_power_of_ten()) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
// At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w);
w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because
// 1. We need the implicit bit
// 2. We need an extra bit for rounding purposes
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further
// In some very rare cases, this could happen, in which case we might need a more accurate
// computation that what we can provide cheaply. This is very, very unlikely.
//
const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0,
// and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation.
if(!inside_safe_exponent) {
return compute_error_scaled<binary>(q, product.high, lz);
}
}
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
// value128 product = compute_product(q, w);
// but in practice, we can win big with the compute_product_approximation if its additional branch
// is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63);
answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal?
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
// next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
return answer;
}
// usually, we round *up*, but if we fall right in between and and we have an
// even basis, we need to round down
// We are only concerned with the cases where 5**q fits in single 64-bit word.
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
// To be in-between two floats we need that in doing
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
// ... we dropped out only zeroes. But if this happened, then we can go back!!!
if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
answer.power2++; // undo previous addition
}
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
if (answer.power2 >= binary::infinite_power()) { // infinity
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
}
return answer;
}
} // namespace fast_float
#endif
#ifndef FASTFLOAT_BIGINT_H
#define FASTFLOAT_BIGINT_H
#include <algorithm>
//included above:
//#include <cstdint>
//included above:
//#include <climits>
//included above:
//#include <cstring>
namespace fast_float {
// the limb width: we want efficient multiplication of double the bits in
// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
// extract the high and low parts efficiently. this is every 64-bit
// architecture except for sparc, which emulates 128-bit multiplication.
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
// doing `8 * sizeof(limb)`.
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB
typedef uint64_t limb;
constexpr size_t limb_bits = 64;
#else
#define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb;
constexpr size_t limb_bits = 32;
#endif
typedef span<limb> limb_span;
// number of bits in a bigint. this needs to be at least the number
// of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000;
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes.
template <uint16_t size>
struct stackvec {
limb data[size];
// we never need more than 150 limbs
uint16_t length{0};
stackvec() = default;
stackvec(const stackvec &) = delete;
stackvec &operator=(const stackvec &) = delete;
stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span.
stackvec(limb_span s) {
FASTFLOAT_ASSERT(try_extend(s));
}
limb& operator[](size_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
const limb& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
// index from the end of the container
const limb& rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1;
return data[rindex];
}
// set the length, without bounds checking.
void set_len(size_t len) noexcept {
length = uint16_t(len);
}
constexpr size_t len() const noexcept {
return length;
}
constexpr bool is_empty() const noexcept {
return length == 0;
}
constexpr size_t capacity() const noexcept {
return size;
}
// append item to vector, without bounds checking
void push_unchecked(limb value) noexcept {
data[length] = value;
length++;
}
// append item to vector, returning if item was added
bool try_push(limb value) noexcept {
if (len() < capacity()) {
push_unchecked(value);
return true;
} else {
return false;
}
}
// add items to the vector, from a span, without bounds checking
void extend_unchecked(limb_span s) noexcept {
limb* ptr = data + length;
::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len());
set_len(len() + s.len());
}
// try to add items to the vector, returning if items were added
bool try_extend(limb_span s) noexcept {
if (len() + s.len() <= capacity()) {
extend_unchecked(s);
return true;
} else {
return false;
}
}
// resize the vector, without bounds checking
// if the new size is longer than the vector, assign value to each
// appended item.
void resize_unchecked(size_t new_len, limb value) noexcept {
if (new_len > len()) {
size_t count = new_len - len();
limb* first = data + len();
limb* last = first + count;
::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
}
}
// try to resize the vector, returning if the vector was resized.
bool try_resize(size_t new_len, limb value) noexcept {
if (new_len > capacity()) {
return false;
} else {
resize_unchecked(new_len, value);
return true;
}
}
// check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index
// is relative to the most significant limbs.
bool nonzero(size_t index) const noexcept {
while (index < len()) {
if (rindex(index) != 0) {
return true;
}
index++;
}
return false;
}
// normalize the big integer, so most-significant zero limbs are removed.
void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) {
length--;
}
}
};
fastfloat_really_inline
uint64_t empty_hi64(bool& truncated) noexcept {
truncated = false;
return 0;
}
fastfloat_really_inline
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
truncated = false;
int shl = leading_zeroes(r0);
return r0 << shl;
}
fastfloat_really_inline
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
int shl = leading_zeroes(r0);
if (shl == 0) {
truncated = r1 != 0;
return r0;
} else {
int shr = 64 - shl;
truncated = (r1 << shl) != 0;
return (r0 << shl) | (r1 >> shr);
}
}
fastfloat_really_inline
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
return uint64_hi64(r0, truncated);
}
fastfloat_really_inline
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated);
}
fastfloat_really_inline
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
uint64_t x2 = r2;
return uint64_hi64(x0, (x1 << 32) | x2, truncated);
}
// add two small integers, checking for overflow.
// we want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still
// pretty fast.
fastfloat_really_inline
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
limb z;
// gcc and clang
#if defined(__has_builtin)
#if __has_builtin(__builtin_add_overflow)
overflow = __builtin_add_overflow(x, y, &z);
return z;
#endif
#endif
// generic, this still optimizes correctly on MSVC.
z = x + y;
overflow = z < x;
return z;
}
// multiply two small integers, getting both the high and low bits.
fastfloat_really_inline
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension.
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#else
// fallback, no native 128-bit integer multiplication with carry.
// on msvc, this optimizes identically, somehow.
value128 z = full_multiplication(x, y);
bool overflow;
z.low = scalar_add(z.low, carry, overflow);
z.high += uint64_t(overflow); // cannot overflow
carry = z.high;
return z.low;
#endif
#else
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#endif
}
// add scalar value to bigint starting from offset.
// used in grade school multiplication
template <uint16_t size>
inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
size_t index = start;
limb carry = y;
bool overflow;
while (carry != 0 && index < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow);
carry = limb(overflow);
index += 1;
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add scalar value to bigint.
template <uint16_t size>
fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept {
return small_add_from(vec, y, 0);
}
// multiply bigint by scalar value.
template <uint16_t size>
inline bool small_mul(stackvec<size>& vec, limb y) noexcept {
limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) {
vec[index] = scalar_mul(vec[index], y, carry);
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add bigint to bigint starting from index.
// used in grade school multiplication
template <uint16_t size>
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) {
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
}
bool carry = false;
for (size_t index = 0; index < y.len(); index++) {
limb xi = x[index + start];
limb yi = y[index];
bool c1 = false;
bool c2 = false;
xi = scalar_add(xi, yi, c1);
if (carry) {
xi = scalar_add(xi, 1, c2);
}
x[index + start] = xi;
carry = c1 | c2;
}
// handle overflow
if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
}
return true;
}
// add bigint to bigint.
template <uint16_t size>
fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
return large_add_from(x, y, 0);
}
// grade-school multiplication algorithm
template <uint16_t size>
bool long_mul(stackvec<size>& x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len());
if (y.len() != 0) {
limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) {
limb yi = y[index];
stackvec<size> zi;
if (yi != 0) {
// re-use the same buffer throughout
zi.set_len(0);
FASTFLOAT_TRY(zi.try_extend(zs));
FASTFLOAT_TRY(small_mul(zi, yi));
limb_span zis = limb_span(zi.data, zi.len());
FASTFLOAT_TRY(large_add_from(x, zis, index));
}
}
}
x.normalize();
return true;
}
// grade-school multiplication algorithm
template <uint16_t size>
bool large_mul(stackvec<size>& x, limb_span y) noexcept {
if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0]));
} else {
FASTFLOAT_TRY(long_mul(x, y));
}
return true;
}
// big integer type. implements a small subset of big integer
// arithmetic, using simple algorithms since asymptotically
// faster algorithms are slower for a small number of limbs.
// all operations assume the big-integer is normalized.
struct bigint {
// storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec;
bigint(): vec() {}
bigint(const bigint &) = delete;
bigint &operator=(const bigint &) = delete;
bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete;
bigint(uint64_t value): vec() {
#ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value);
#else
vec.push_unchecked(uint32_t(value));
vec.push_unchecked(uint32_t(value >> 32));
#endif
vec.normalize();
}
// get the high 64 bits from the vector, and if bits were truncated.
// this is to get the significant digits for the float.
uint64_t hi64(bool& truncated) const noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint64_hi64(vec.rindex(0), truncated);
} else {
uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
truncated |= vec.nonzero(2);
return result;
}
#else
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint32_hi64(vec.rindex(0), truncated);
} else if (vec.len() == 2) {
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
} else {
uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
truncated |= vec.nonzero(3);
return result;
}
#endif
}
// compare two big integers, returning the large value.
// assumes both are normalized. if the return value is
// negative, other is larger, if the return value is
// positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we
// must compare the limbs in ever order.
int compare(const bigint& other) const noexcept {
if (vec.len() > other.vec.len()) {
return 1;
} else if (vec.len() < other.vec.len()) {
return -1;
} else {
for (size_t index = vec.len(); index > 0; index--) {
limb xi = vec[index - 1];
limb yi = other.vec[index - 1];
if (xi > yi) {
return 1;
} else if (xi < yi) {
return -1;
}
}
return 0;
}
}
// shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits.
bool shl_bits(size_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to:
// b10100100 b01000010
// b10 b10010001 b00001000
FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n;
size_t shr = limb_bits - shl;
limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) {
limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr);
prev = xi;
}
limb carry = prev >> shr;
if (carry != 0) {
return vec.try_push(carry);
}
return true;
}
// move the limbs left by `n` limbs.
bool shl_limbs(size_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) {
return false;
} else if (!vec.is_empty()) {
// move limbs
limb* dst = vec.data + n;
const limb* src = vec.data;
::memmove(dst, src, sizeof(limb) * vec.len());
// fill in empty limbs
limb* first = vec.data;
limb* last = first + n;
::std::fill(first, last, 0);
vec.set_len(n + vec.len());
return true;
} else {
return true;
}
}
// move the limbs left by `n` bits.
bool shl(size_t n) noexcept {
size_t rem = n % limb_bits;
size_t div = n / limb_bits;
if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem));
}
if (div != 0) {
FASTFLOAT_TRY(shl_limbs(div));
}
return true;
}
// get the number of leading zeros in the bigint.
int ctlz() const noexcept {
if (vec.is_empty()) {
return 0;
} else {
#ifdef FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0));
#else
// no use defining a specialized leading_zeroes for a 32-bit type.
uint64_t r0 = vec.rindex(0);
return leading_zeroes(r0 << 32);
#endif
}
}
// get the number of bits in the bigint.
int bit_length() const noexcept {
int lz = ctlz();
return int(limb_bits * vec.len()) - lz;
}
bool mul(limb y) noexcept {
return small_mul(vec, y);
}
bool add(limb y) noexcept {
return small_add(vec, y);
}
// multiply as if by 2 raised to a power.
bool pow2(uint32_t exp) noexcept {
return shl(exp);
}
// multiply as if by 5 raised to a power.
bool pow5(uint32_t exp) noexcept {
// multiply by a power of 5
static constexpr uint32_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = {
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
};
#ifdef FASTFLOAT_64BIT_LIMB
constexpr static limb large_power_of_5[] = {
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
10482974169319127550UL, 198276706040285095UL};
#else
constexpr static limb large_power_of_5[] = {
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
#endif
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step;
}
#ifdef FASTFLOAT_64BIT_LIMB
uint32_t small_step = 27;
limb max_native = 7450580596923828125UL;
#else
uint32_t small_step = 13;
limb max_native = 1220703125U;
#endif
while (exp >= small_step) {
FASTFLOAT_TRY(small_mul(vec, max_native));
exp -= small_step;
}
if (exp != 0) {
FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp])));
}
return true;
}
// multiply as if by 10 raised to a power.
bool pow10(uint32_t exp) noexcept {
FASTFLOAT_TRY(pow5(exp));
return pow2(exp);
}
};
} // namespace fast_float
#endif
#ifndef FASTFLOAT_ASCII_NUMBER_H
#define FASTFLOAT_ASCII_NUMBER_H
//included above:
//#include <cctype>
//included above:
//#include <cstdint>
//included above:
//#include <cstring>
//included above:
//#include <iterator>
namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
| (val & 0x000000FF00000000) >> 8
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
}
fastfloat_really_inline uint64_t read_u64(const char *chars) {
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
return val;
}
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
::memcpy(chars, &val, sizeof(uint64_t));
}
// credit @aqrit
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
}
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
return parse_eight_digits_unrolled(read_u64(chars));
}
// credit @aqrit
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080));
}
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
return is_made_of_eight_digits_fast(read_u64(chars));
}
typedef span<const char> byte_span;
struct parsed_number_string {
int64_t exponent{0};
uint64_t mantissa{0};
const char *lastmatch{nullptr};
bool negative{false};
bool valid{false};
bool too_many_digits{false};
// contains the range of the significant digits
byte_span integer{}; // non-nullable
byte_span fraction{}; // nullable
};
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
fastfloat_really_inline
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
const chars_format fmt = options.format;
const char decimal_point = options.decimal_point;
parsed_number_string answer;
answer.valid = false;
answer.too_many_digits = false;
answer.negative = (*p == '-');
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
++p;
if (p == pend) {
return answer;
}
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
return answer;
}
}
const char *const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p - '0'); // might overflow, we will handle the overflow later
++p;
}
const char *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = byte_span(start_digits, size_t(digit_count));
int64_t exponent = 0;
if ((p != pend) && (*p == decimal_point)) {
++p;
const char* before = p;
// can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck.
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = before - p;
answer.fraction = byte_span(before, size_t(p - before));
digit_count -= exponent;
}
// we must have encountered at least one integer!
if (digit_count == 0) {
return answer;
}
int64_t exp_number = 0; // explicit exponential part
if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
const char * location_of_e = p;
++p;
bool neg_exp = false;
if ((p != pend) && ('-' == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
if ((p == pend) || !is_integer(*p)) {
if(!(fmt & chars_format::fixed)) {
// We are in error.
return answer;
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
if(neg_exp) { exp_number = - exp_number; }
exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
}
answer.lastmatch = p;
answer.valid = true;
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits.
if (digit_count > 19) { // this is uncommon
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
const char *start = start_digits;
while ((start != pend) && (*start == '0' || *start == decimal_point)) {
if(*start == '0') { digit_count --; }
start++;
}
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to check if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = answer.integer.ptr;
const char* int_end = p + answer.integer.len();
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - '0');
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component.
p = answer.fraction.ptr;
const char* frac_end = p + answer.fraction.len();
while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - '0');
++p;
}
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
}
}
answer.exponent = exponent;
answer.mantissa = i;
return answer;
}
} // namespace fast_float
#endif
#ifndef FASTFLOAT_DIGIT_COMPARISON_H
#define FASTFLOAT_DIGIT_COMPARISON_H
//included above:
//#include <algorithm>
//included above:
//#include <cstdint>
//included above:
//#include <cstring>
//included above:
//#include <iterator>
namespace fast_float {
// 1e0 to 1e19
constexpr static uint64_t powers_of_ten_uint64[] = {
1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
1000000000000000000UL, 10000000000000000000UL};
// calculate the exponent, in scientific notation, of the number.
// this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast.
fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept {
uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) {
mantissa /= 10000;
exponent += 4;
}
while (mantissa >= 100) {
mantissa /= 100;
exponent += 2;
}
while (mantissa >= 10) {
mantissa /= 10;
exponent += 1;
}
return exponent;
}
// this converts a native floating-point number to an extended-precision float.
template <typename T>
fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
equiv_uint bits;
::memcpy(&bits, &value, sizeof(T));
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
return am;
}
// get the extended precision value of the halfway point between b and b+u.
// we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u.
template <typename T>
fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept {
adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1;
am.mantissa += 1;
am.power2 -= 1;
return am;
}
// round an extended-precision float to the nearest machine float.
template <typename T, typename callback>
fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) {
// have a denormal float
int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
return;
}
// have a normal float, use the default shift.
cb(am, mantissa_shift);
// check for carry
if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++;
}
// check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0;
}
}
template <typename callback>
fastfloat_really_inline
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
uint64_t mask;
uint64_t halfway;
if (shift == 64) {
mask = UINT64_MAX;
} else {
mask = (uint64_t(1) << shift) - 1;
}
if (shift == 0) {
halfway = 0;
} else {
halfway = uint64_t(1) << (shift - 1);
}
uint64_t truncated_bits = am.mantissa & mask;
uint64_t is_above = truncated_bits > halfway;
uint64_t is_halfway = truncated_bits == halfway;
// shift digits into position
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
}
fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
}
fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept {
uint64_t val;
while (std::distance(first, last) >= 8) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != 0x3030303030303030) {
break;
}
first += 8;
}
while (first != last) {
if (*first != '0') {
break;
}
first++;
}
}
// determine if any non-zero digits were truncated.
// all characters must be valid digits.
fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (std::distance(first, last) >= 8) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != 0x3030303030303030) {
return true;
}
first += 8;
}
while (first != last) {
if (*first != '0') {
return true;
}
first++;
}
return false;
}
fastfloat_really_inline bool is_truncated(byte_span s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len());
}
fastfloat_really_inline
void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
counter += 8;
count += 8;
}
fastfloat_really_inline
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - '0');
p++;
counter++;
count++;
}
fastfloat_really_inline
void add_native(bigint& big, limb power, limb value) noexcept {
big.mul(power);
big.add(value);
}
fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept {
// need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1);
count++;
}
// parse the significant digits into a big integer
inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept {
// try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step.
size_t counter = 0;
digits = 0;
limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19;
#else
size_t step = 9;
#endif
// process all integer digits.
const char* p = num.integer.ptr;
const char* pend = p + num.integer.len();
skip_zeros(p, pend);
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (num.fraction.ptr != nullptr) {
truncated |= is_truncated(num.fraction);
}
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
// add our fraction digits, if they're available.
if (num.fraction.ptr != nullptr) {
p = num.fraction.ptr;
pend = p + num.fraction.len();
if (digits == 0) {
skip_zeros(p, pend);
}
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
}
if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
}
}
template <typename T>
inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
adjusted_mantissa answer;
bool truncated;
answer.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias;
round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
});
});
return answer;
}
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
// we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude.
template <typename T>
inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
bigint& real_digits = bigmant;
int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
T b;
to_float(false, am_b, b);
adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp);
if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
}
if (pow2_exp > 0) {
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
} else if (pow2_exp < 0) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
}
// compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(void)_; // not needed, since we've done our comparison
(void)__; // not needed, since we've done our comparison
if (ord > 0) {
return true;
} else if (ord < 0) {
return false;
} else {
return is_odd;
}
});
});
return answer;
}
// parse the significant digits as a big integer to unambiguously round the
// the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this
// algorithm is always correct, and uses one of two approaches. when
// the exponent is positive relative to the significant digits (such as
// 1234), we create a big-integer representation, get the high 64-bits,
// determine if any lower bits are truncated, and use that to direct
// rounding. in case of a negative exponent relative to the significant
// digits (such as 1.2345), we create a theoretical representation of
// `b` as a big-integer type, scaled to the same binary exponent as
// the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding.
template <typename T>
inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias
am.power2 -= invalid_am_bias;
int32_t sci_exp = scientific_exponent(num);
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits);
// can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits);
if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent);
} else {
return negative_digit_comp<T>(bigmant, am, exponent);
}
}
} // namespace fast_float
#endif
#ifndef FASTFLOAT_PARSE_NUMBER_H
#define FASTFLOAT_PARSE_NUMBER_H
//included above:
//#include <cmath>
//included above:
//#include <cstring>
//included above:
//#include <limits>
//included above:
//#include <system_error>
namespace fast_float {
namespace detail {
/**
* Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the
* strings a null-free and fixed.
**/
template <typename T>
from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
from_chars_result answer;
answer.ptr = first;
answer.ec = std::errc(); // be optimistic
bool minusSign = false;
if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true;
++first;
}
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, "nan", 3)) {
answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if(first != last && *first == '(') {
for(const char* ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == ')') {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break;
}
else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_'))
break; // forbidden char, not nan(n-char-seq-opt)
}
}
return answer;
}
if (fastfloat_strncasecmp(first, "inf", 3)) {
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) {
answer.ptr = first + 8;
} else {
answer.ptr = first + 3;
}
value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
return answer;
}
}
answer.ec = std::errc::invalid_argument;
return answer;
}
} // namespace detail
template<typename T>
from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options{fmt});
}
template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept {
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
from_chars_result answer;
if (first == last) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
parsed_number_string pns = parse_number_string(first, last, options);
if (!pns.valid) {
return detail::parse_infnan(first, last, value);
}
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// Next is Clinger's fast path.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) {
value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
if (pns.negative) { value = -value; }
return answer;
}
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits && am.power2 >= 0) {
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
}
}
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
// then we need to go the long way around again. This is very uncommon.
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
to_float(pns.negative, am, value);
return answer;
}
} // namespace fast_float
#endif
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__) || defined(__APPLE_CC__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif // _C4_EXT_FAST_FLOAT_HPP_
// (end https://github.com/biojppm/c4core/src/c4/ext/fast_float.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/std/vector_fwd.hpp
// https://github.com/biojppm/c4core/src/c4/std/vector_fwd.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_STD_VECTOR_FWD_HPP_
#define _C4_STD_VECTOR_FWD_HPP_
/** @file vector_fwd.hpp */
//included above:
//#include <cstddef>
// forward declarations for std::vector
#if defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_MSC_VER)
namespace std {
template<typename> class allocator;
template<typename T, typename Alloc> class vector;
} // namespace std
#elif defined(_LIBCPP_ABI_NAMESPACE)
namespace std {
inline namespace _LIBCPP_ABI_NAMESPACE {
template<typename> class allocator;
template<typename T, typename Alloc> class vector;
} // namespace _LIBCPP_ABI_NAMESPACE
} // namespace std
#else
#error "unknown standard library"
#endif
#ifndef C4CORE_SINGLE_HEADER
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr_fwd.hpp
//#include "c4/substr_fwd.hpp"
#if !defined(C4_SUBSTR_FWD_HPP_) && !defined(_C4_SUBSTR_FWD_HPP_)
#error "amalgamate: file c4/substr_fwd.hpp must have been included at this point"
#endif /* C4_SUBSTR_FWD_HPP_ */
#endif
namespace c4 {
template<class Alloc> c4::substr to_substr(std::vector<char, Alloc> &vec);
template<class Alloc> c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec);
template<class Alloc> bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s);
template<class Alloc> bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s);
} // namespace c4
#endif // _C4_STD_VECTOR_FWD_HPP_
// (end https://github.com/biojppm/c4core/src/c4/std/vector_fwd.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/std/string_fwd.hpp
// https://github.com/biojppm/c4core/src/c4/std/string_fwd.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_STD_STRING_FWD_HPP_
#define _C4_STD_STRING_FWD_HPP_
/** @file string_fwd.hpp */
#ifndef DOXYGEN
#ifndef C4CORE_SINGLE_HEADER
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr_fwd.hpp
//#include "c4/substr_fwd.hpp"
#if !defined(C4_SUBSTR_FWD_HPP_) && !defined(_C4_SUBSTR_FWD_HPP_)
#error "amalgamate: file c4/substr_fwd.hpp must have been included at this point"
#endif /* C4_SUBSTR_FWD_HPP_ */
#endif
//included above:
//#include <cstddef>
// forward declarations for std::string
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
#include <bits/stringfwd.h> // use the fwd header in glibcxx
#elif defined(_LIBCPP_VERSION) || defined(__APPLE_CC__)
#include <iosfwd> // use the fwd header in stdlibc++
#elif defined(_MSC_VER)
//! @todo is there a fwd header in msvc?
namespace std {
template<typename> struct char_traits;
template<typename> class allocator;
template<typename _CharT, typename _Traits, typename _Alloc> class basic_string;
using string = basic_string<char, char_traits<char>, allocator<char>>;
} /* namespace std */
#else
#error "unknown standard library"
#endif
namespace c4 {
c4::substr to_substr(std::string &s);
c4::csubstr to_csubstr(std::string const& s);
bool operator== (c4::csubstr ss, std::string const& s);
bool operator!= (c4::csubstr ss, std::string const& s);
bool operator>= (c4::csubstr ss, std::string const& s);
bool operator> (c4::csubstr ss, std::string const& s);
bool operator<= (c4::csubstr ss, std::string const& s);
bool operator< (c4::csubstr ss, std::string const& s);
bool operator== (std::string const& s, c4::csubstr ss);
bool operator!= (std::string const& s, c4::csubstr ss);
bool operator>= (std::string const& s, c4::csubstr ss);
bool operator> (std::string const& s, c4::csubstr ss);
bool operator<= (std::string const& s, c4::csubstr ss);
bool operator< (std::string const& s, c4::csubstr ss);
size_t to_chars(c4::substr buf, std::string const& s);
bool from_chars(c4::csubstr buf, std::string * s);
} // namespace c4
#endif // DOXYGEN
#endif // _C4_STD_STRING_FWD_HPP_
// (end https://github.com/biojppm/c4core/src/c4/std/string_fwd.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/std/std_fwd.hpp
// https://github.com/biojppm/c4core/src/c4/std/std_fwd.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_STD_STD_FWD_HPP_
#define _C4_STD_STD_FWD_HPP_
/** @file std_fwd.hpp includes all c4-std interop fwd files */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/std/vector_fwd.hpp
//#include "c4/std/vector_fwd.hpp"
#if !defined(C4_STD_VECTOR_FWD_HPP_) && !defined(_C4_STD_VECTOR_FWD_HPP_)
#error "amalgamate: file c4/std/vector_fwd.hpp must have been included at this point"
#endif /* C4_STD_VECTOR_FWD_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/std/string_fwd.hpp
//#include "c4/std/string_fwd.hpp"
#if !defined(C4_STD_STRING_FWD_HPP_) && !defined(_C4_STD_STRING_FWD_HPP_)
#error "amalgamate: file c4/std/string_fwd.hpp must have been included at this point"
#endif /* C4_STD_STRING_FWD_HPP_ */
//#include "c4/std/tuple_fwd.hpp"
#endif // _C4_STD_STD_FWD_HPP_
// (end https://github.com/biojppm/c4core/src/c4/std/std_fwd.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/charconv.hpp
// https://github.com/biojppm/c4core/src/c4/charconv.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_CHARCONV_HPP_
#define _C4_CHARCONV_HPP_
/** @file charconv.hpp Lightweight generic type-safe wrappers for
* converting individual values to/from strings.
*
* These are the main functions:
*
* @code{.cpp}
* // Convert the given value, writing into the string.
* // The resulting string will NOT be null-terminated.
* // Return the number of characters needed.
* // This function is safe to call when the string is too small -
* // no writes will occur beyond the string's last character.
* template<class T> size_t c4::to_chars(substr buf, T const& C4_RESTRICT val);
*
*
* // Convert the given value to a string using to_chars(), and
* // return the resulting string, up to and including the last
* // written character.
* template<class T> substr c4::to_chars_sub(substr buf, T const& C4_RESTRICT val);
*
*
* // Read a value from the string, which must be
* // trimmed to the value (ie, no leading/trailing whitespace).
* // return true if the conversion succeeded.
* template<class T> bool c4::from_chars(csubstr buf, T * C4_RESTRICT val);
*
*
* // Read the first valid sequence of characters from the string,
* // skipping leading whitespace, and convert it using from_chars().
* // Return the number of characters read for converting.
* template<class T> size_t c4::from_chars_first(csubstr buf, T * C4_RESTRICT val);
* @endcode
*/
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/language.hpp
//#include "c4/language.hpp"
#if !defined(C4_LANGUAGE_HPP_) && !defined(_C4_LANGUAGE_HPP_)
#error "amalgamate: file c4/language.hpp must have been included at this point"
#endif /* C4_LANGUAGE_HPP_ */
//included above:
//#include <inttypes.h>
//included above:
//#include <type_traits>
//included above:
//#include <climits>
//included above:
//#include <limits>
//included above:
//#include <utility>
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr.hpp
//#include "c4/substr.hpp"
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/std/std_fwd.hpp
//#include "c4/std/std_fwd.hpp"
#if !defined(C4_STD_STD_FWD_HPP_) && !defined(_C4_STD_STD_FWD_HPP_)
#error "amalgamate: file c4/std/std_fwd.hpp must have been included at this point"
#endif /* C4_STD_STD_FWD_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/memory_util.hpp
//#include "c4/memory_util.hpp"
#if !defined(C4_MEMORY_UTIL_HPP_) && !defined(_C4_MEMORY_UTIL_HPP_)
#error "amalgamate: file c4/memory_util.hpp must have been included at this point"
#endif /* C4_MEMORY_UTIL_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/szconv.hpp
//#include "c4/szconv.hpp"
#if !defined(C4_SZCONV_HPP_) && !defined(_C4_SZCONV_HPP_)
#error "amalgamate: file c4/szconv.hpp must have been included at this point"
#endif /* C4_SZCONV_HPP_ */
#ifndef C4CORE_NO_FAST_FLOAT
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wsign-conversion")
C4_SUPPRESS_WARNING_GCC("-Warray-bounds")
#if __GNUC__ >= 5
C4_SUPPRESS_WARNING_GCC("-Wshift-count-overflow")
#endif
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/ext/fast_float.hpp
//# include "c4/ext/fast_float.hpp"
#if !defined(C4_EXT_FAST_FLOAT_HPP_) && !defined(_C4_EXT_FAST_FLOAT_HPP_)
#error "amalgamate: file c4/ext/fast_float.hpp must have been included at this point"
#endif /* C4_EXT_FAST_FLOAT_HPP_ */
C4_SUPPRESS_WARNING_GCC_POP
# define C4CORE_HAVE_FAST_FLOAT 1
# define C4CORE_HAVE_STD_FROMCHARS 0
# if (C4_CPP >= 17)
# if defined(_MSC_VER)
# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
# include <charconv>
# define C4CORE_HAVE_STD_TOCHARS 1
# else
# define C4CORE_HAVE_STD_TOCHARS 0
# endif
# else // VS2017 and lower do not have these macros
# if __has_include(<charconv>) && __cpp_lib_to_chars
# define C4CORE_HAVE_STD_TOCHARS 1
//included above:
//# include <charconv>
# else
# define C4CORE_HAVE_STD_TOCHARS 0
# endif
# endif
# else
# define C4CORE_HAVE_STD_TOCHARS 0
# endif
#elif (C4_CPP >= 17)
# if defined(_MSC_VER)
# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
//included above:
//# include <charconv>
# define C4CORE_HAVE_STD_TOCHARS 1
# define C4CORE_HAVE_STD_FROMCHARS 1
# else
# define C4CORE_HAVE_STD_TOCHARS 0
# define C4CORE_HAVE_STD_FROMCHARS 0
# endif
# else // VS2017 and lower do not have these macros
# if __has_include(<charconv>) && __cpp_lib_to_chars
# define C4CORE_HAVE_STD_TOCHARS 1
# define C4CORE_HAVE_STD_FROMCHARS 1
//included above:
//# include <charconv>
# else
# define C4CORE_HAVE_STD_TOCHARS 0
# define C4CORE_HAVE_STD_FROMCHARS 0
# endif
# endif
#else
# define C4CORE_HAVE_STD_TOCHARS 0
# define C4CORE_HAVE_STD_FROMCHARS 0
#endif
#if !C4CORE_HAVE_STD_FROMCHARS && !defined(C4CORE_HAVE_FAST_FLOAT)
#include <cstdio>
#endif
#ifdef _MSC_VER
# pragma warning(push)
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
# pragma warning(disable: 4800) //'int': forcing value to bool 'true' or 'false' (performance warning)
# endif
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
namespace c4 {
typedef enum : uint8_t {
/** print the real number in floating point format (like %f) */
FTOA_FLOAT = 0,
/** print the real number in scientific format (like %e) */
FTOA_SCIENT = 1,
/** print the real number in flexible format (like %g) */
FTOA_FLEX = 2,
/** print the real number in hexadecimal format (like %a) */
FTOA_HEXA = 3,
_FTOA_COUNT
} RealFormat_e;
inline C4_CONSTEXPR14 char to_c_fmt(RealFormat_e f)
{
constexpr const char fmt[] = {
'f', // FTOA_FLOAT
'e', // FTOA_SCIENT
'g', // FTOA_FLEX
'a', // FTOA_HEXA
};
C4_STATIC_ASSERT(C4_COUNTOF(fmt) == _FTOA_COUNT);
#if C4_CPP > 14
C4_ASSERT(f < _FTOA_COUNT);
#endif
return fmt[f];
}
#if C4CORE_HAVE_STD_TOCHARS
inline C4_CONSTEXPR14 std::chars_format to_std_fmt(RealFormat_e f)
{
constexpr const std::chars_format fmt[] = {
std::chars_format::fixed, // FTOA_FLOAT
std::chars_format::scientific, // FTOA_SCIENT
std::chars_format::general, // FTOA_FLEX
std::chars_format::hex, // FTOA_HEXA
};
C4_STATIC_ASSERT(C4_COUNTOF(fmt) == _FTOA_COUNT);
#if C4_CPP >= 14
C4_ASSERT(f < _FTOA_COUNT);
#endif
return fmt[f];
}
#endif // C4CORE_HAVE_STD_TOCHARS
/** in some platforms, int,unsigned int
* are not any of int8_t...int64_t and
* long,unsigned long are not any of uint8_t...uint64_t */
template<class T>
struct is_fixed_length
{
enum : bool {
/** true if T is one of the fixed length signed types */
value_i = (std::is_integral<T>::value
&& (std::is_same<T, int8_t>::value
|| std::is_same<T, int16_t>::value
|| std::is_same<T, int32_t>::value
|| std::is_same<T, int64_t>::value)),
/** true if T is one of the fixed length unsigned types */
value_u = (std::is_integral<T>::value
&& (std::is_same<T, uint8_t>::value
|| std::is_same<T, uint16_t>::value
|| std::is_same<T, uint32_t>::value
|| std::is_same<T, uint64_t>::value)),
/** true if T is one of the fixed length signed or unsigned types */
value = value_i || value_u
};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifdef _MSC_VER
# pragma warning(push)
#elif defined(__clang__)
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
// Helper macros, undefined below
#define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } }
#define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } }
}
#include <iostream>
namespace c4 {
C4_INLINE_CONSTEXPR const char hexchars[] = "0123456789abcdef";
/** write an integer to a string in decimal format. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @return the number of characters required for the string,
* even if the string is not long enough for the result.
* No writes are done past the end of the string. */
template<class T>
size_t write_dec(substr buf, T v)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
C4_ASSERT(v >= 0);
size_t pos = 0;
do {
_c4append('0' + (v % T(10)));
v /= T(10);
} while(v);
buf.reverse_range(0, pos <= buf.len ? pos : buf.len);
return pos;
}
/** write an integer to a string in hexadecimal format. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @return the number of characters required for the string,
* even if the string is not long enough for the result.
* No writes are done past the end of the string. */
template<class T>
size_t write_hex(substr buf, T v)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
C4_ASSERT(v >= 0);
size_t pos = 0;
do {
_c4appendhex(v & T(15));
v >>= 4;
} while(v);
buf.reverse_range(0, pos <= buf.len ? pos : buf.len);
return pos;
}
/** write an integer to a string in octal format. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @note does not prefix with 0o
* @return the number of characters required for the string,
* even if the string is not long enough for the result.
* No writes are done past the end of the string. */
template<class T>
size_t write_oct(substr buf, T v)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
C4_ASSERT(v >= 0);
size_t pos = 0;
do {
_c4append('0' + (v & T(7)));
v >>= 3;
} while(v);
buf.reverse_range(0, pos <= buf.len ? pos : buf.len);
return pos;
}
/** write an integer to a string in binary format. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @note does not prefix with 0b
* @return the number of characters required for the string,
* even if the string is not long enough for the result.
* No writes are done past the end of the string. */
template<class T>
size_t write_bin(substr buf, T v)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
C4_ASSERT(v >= 0);
size_t pos = 0;
do {
_c4append('0' + (v & T(1)));
v >>= 1;
} while(v);
buf.reverse_range(0, pos <= buf.len ? pos : buf.len);
return pos;
}
namespace detail {
template<class U> using NumberWriter = size_t (*)(substr, U);
/** @todo pass the writer as a template parameter */
template<class T, NumberWriter<T> writer>
size_t write_num_digits(substr buf, T v, size_t num_digits)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
size_t ret = writer(buf, v);
if(ret >= num_digits)
return ret;
else if(ret >= buf.len || num_digits > buf.len)
return num_digits;
C4_ASSERT(num_digits >= ret);
size_t delta = static_cast<size_t>(num_digits - ret);
memmove(buf.str + delta, buf.str, ret);
memset(buf.str, '0', delta);
return num_digits;
}
} // namespace detail
/** same as c4::write_dec(), but pad with zeroes on the left
* such that the resulting string is @p num_digits wide.
* If the given number is wider than num_digits, then the number prevails. */
template<class T>
size_t write_dec(substr buf, T val, size_t num_digits)
{
return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
}
/** same as c4::write_hex(), but pad with zeroes on the left
* such that the resulting string is @p num_digits wide.
* If the given number is wider than num_digits, then the number prevails. */
template<class T>
size_t write_hex(substr buf, T val, size_t num_digits)
{
return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
}
/** same as c4::write_bin(), but pad with zeroes on the left
* such that the resulting string is @p num_digits wide.
* If the given number is wider than num_digits, then the number prevails. */
template<class T>
size_t write_bin(substr buf, T val, size_t num_digits)
{
return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
}
/** same as c4::write_oct(), but pad with zeroes on the left
* such that the resulting string is @p num_digits wide.
* If the given number is wider than num_digits, then the number prevails. */
template<class T>
size_t write_oct(substr buf, T val, size_t num_digits)
{
return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** read a decimal integer from a string. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @note The string must be trimmed. Whitespace is not accepted.
* @return true if the conversion was successful */
template<class I>
C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v)
{
C4_STATIC_ASSERT(std::is_integral<I>::value);
*v = 0;
for(char c : s)
{
if(C4_UNLIKELY(c < '0' || c > '9'))
return false;
*v = (*v) * I(10) + (I(c) - I('0'));
}
return true;
}
/** read an hexadecimal integer from a string. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @note does not accept leading 0x or 0X
* @note the string must be trimmed. Whitespace is not accepted.
* @return true if the conversion was successful */
template<class I>
C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v)
{
C4_STATIC_ASSERT(std::is_integral<I>::value);
*v = 0;
for(char c : s)
{
I cv;
if(c >= '0' && c <= '9')
cv = I(c) - I('0');
else if(c >= 'a' && c <= 'f')
cv = I(10) + (I(c) - I('a'));
else if(c >= 'A' && c <= 'F')
cv = I(10) + (I(c) - I('A'));
else
return false;
*v = (*v) * I(16) + cv;
}
return true;
}
/** read a binary integer from a string. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @note does not accept leading 0b or 0B
* @note the string must be trimmed. Whitespace is not accepted.
* @return true if the conversion was successful */
template<class I>
C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v)
{
C4_STATIC_ASSERT(std::is_integral<I>::value);
*v = 0;
for(char c : s)
{
*v <<= 1;
if(c == '1')
*v |= 1;
else if(c != '0')
return false;
}
return true;
}
/** read an octal integer from a string. This is the
* lowest level (and the fastest) function to do this task.
* @note does not accept negative numbers
* @note does not accept leading 0o or 0O
* @note the string must be trimmed. Whitespace is not accepted.
* @return true if the conversion was successful */
template<class I>
C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v)
{
C4_STATIC_ASSERT(std::is_integral<I>::value);
*v = 0;
for(char c : s)
{
if(C4_UNLIKELY(c < '0' || c > '7'))
return false;
*v = (*v) * I(8) + (I(c) - I('0'));
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
// do not use the type as the template argument because in some
// platforms long!=int32 and long!=int64. Just use the numbytes
// which is more generic and spares lengthy SFINAE code.
template<size_t numbytes> struct itoa_min;
template<> struct itoa_min<1>
{
static csubstr value_dec() { return csubstr("128"); }
static csubstr value_hex() { return csubstr("80"); }
static csubstr value_oct() { return csubstr("200"); }
static csubstr value_bin() { return csubstr("10000000"); }
};
template<> struct itoa_min<2>
{
static csubstr value_dec() { return csubstr("32768"); }
static csubstr value_hex() { return csubstr("8000"); }
static csubstr value_oct() { return csubstr("100000"); }
static csubstr value_bin() { return csubstr("1000000000000000"); }
};
template<> struct itoa_min<4>
{
static csubstr value_dec() { return csubstr("2147483648"); }
static csubstr value_hex() { return csubstr("80000000"); }
static csubstr value_oct() { return csubstr("20000000000"); }
static csubstr value_bin() { return csubstr("10000000000000000000000000000000"); }
};
template<> struct itoa_min<8>
{
static csubstr value_dec() { return csubstr("9223372036854775808"); }
static csubstr value_hex() { return csubstr("8000000000000000"); }
static csubstr value_oct() { return csubstr("1000000000000000000000"); }
static csubstr value_bin() { return csubstr("1000000000000000000000000000000000000000000000000000000000000000"); }
};
inline size_t _itoa2buf(substr buf, size_t pos, csubstr val)
{
if(C4_LIKELY(pos + val.len <= buf.len))
memcpy(buf.str + pos, val.str, val.len);
return pos + val.len;
}
inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val)
{
num_digits = num_digits > val.len ? num_digits - val.len : 0;
for(size_t i = 0; i < num_digits; ++i)
_c4append('0');
return _itoa2buf(buf, pos, val);
}
template<class T>
size_t _itoadec2buf(substr buf)
{
if(C4_LIKELY(buf.len > 0))
{
buf.str[0] = '-';
return detail::_itoa2buf(buf, 1, detail::itoa_min<sizeof(T)>::value_dec());
}
else
{
return detail::_itoa2buf({}, 1, detail::itoa_min<sizeof(T)>::value_dec());
}
C4_UNREACHABLE();
}
template<class I>
size_t _itoa2buf(substr buf, I radix)
{
size_t pos = 0;
_c4append('-');
switch(radix)
{
case I(10):
/*...........................*/ return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_dec());
case I(16):
_c4append('0'); _c4append('x'); return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_hex());
case I( 2):
_c4append('0'); _c4append('b'); return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_bin());
case I( 8):
_c4append('0'); _c4append('o'); return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_oct());
}
C4_ERROR("unknown radix");
return 0;
}
template<class I>
size_t _itoa2buf(substr buf, I radix, size_t num_digits)
{
size_t pos = 0;
_c4append('-');
switch(radix)
{
case I(10):
/*...........................*/ return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_dec());
case I(16):
_c4append('0'); _c4append('x'); return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_hex());
case I( 2):
_c4append('0'); _c4append('b'); return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_bin());
case I( 8):
_c4append('0'); _c4append('o'); return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_oct());
}
C4_ERROR("unknown radix");
return 0;
}
} // namespace detail
/** convert an integral signed decimal to a string.
* The resulting string is NOT zero-terminated.
* Writing stops at the buffer's end.
* @return the number of characters needed for the result, even if the buffer size is insufficient */
template<class T>
size_t itoa(substr buf, T v)
{
C4_STATIC_ASSERT(std::is_signed<T>::value);
if(v >= 0)
{
return write_dec(buf, v);
}
else
{
if(C4_LIKELY(v != std::numeric_limits<T>::min()))
{
if(C4_LIKELY(buf.len > 0))
{
buf.str[0] = '-';
return size_t(1) + write_dec(buf.sub(1), -v);
}
else
{
return size_t(1) + write_dec({}, -v);
}
C4_UNREACHABLE();
}
else
{
// when T is the min value (eg i8: -128), negating it
// will overflow. so we just use the explicit value
return detail::_itoadec2buf<T>(buf);
}
C4_UNREACHABLE();
}
C4_UNREACHABLE();
}
/** convert an integral signed integer to a string, using a specific
* radix. The radix must be 2, 8, 10 or 16.
*
* The resulting string is NOT zero-terminated.
* Writing stops at the buffer's end.
* @return the number of characters needed for the result, even if the buffer size is insufficient */
template<class T>
size_t itoa(substr buf, T v, T radix)
{
C4_STATIC_ASSERT(std::is_signed<T>::value);
C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
// when T is the min value (eg i8: -128), negating it
// will overflow
if(C4_LIKELY(v != std::numeric_limits<T>::min()))
{
size_t pos = 0;
if(v < 0)
{
v = -v;
_c4append('-');
}
switch(radix)
{
case 10:
/*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v);
case 16:
_c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v);
case 2:
_c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v);
case 8:
_c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v);
}
}
// when T is the min value (eg i8: -128), negating it
// will overflow
return detail::_itoa2buf<T>(buf, radix);
}
/** same as c4::itoa(), but pad with zeroes on the left such that the
* resulting string is @p num_digits wide. The @p radix must be 2,
* 8, 10 or 16. The resulting string is NOT zero-terminated. Writing
* stops at the buffer's end.
*
* @return the number of characters needed for the result, even if
* the buffer size is insufficient */
template<class T>
size_t itoa(substr buf, T v, T radix, size_t num_digits)
{
C4_STATIC_ASSERT(std::is_signed<T>::value);
C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
if(C4_LIKELY(v != std::numeric_limits<T>::min()))
{
size_t pos = 0;
if(v < 0)
{
v = -v;
_c4append('-');
}
switch(radix)
{
case 10:
/*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
case 16:
_c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
case 2:
_c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
case 8:
_c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
}
}
// when T is the min value (eg i8: -128), negating it
// will overflow
return detail::_itoa2buf<T>(buf, radix, num_digits);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** convert an integral unsigned decimal to a string.
* The resulting string is NOT zero-terminated.
* Writing stops at the buffer's end.
* @return the number of characters needed for the result, even if the buffer size is insufficient */
template<class T>
size_t utoa(substr buf, T v)
{
C4_STATIC_ASSERT(std::is_unsigned<T>::value);
return write_dec(buf, v);
}
/** convert an integral unsigned integer to a string, using a specific radix. The radix must be 2, 8, 10 or 16.
* The resulting string is NOT zero-terminated.
* Writing stops at the buffer's end.
* @return the number of characters needed for the result, even if the buffer size is insufficient */
template<class T>
size_t utoa(substr buf, T v, T radix)
{
C4_STATIC_ASSERT(std::is_unsigned<T>::value);
C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
size_t pos = 0;
switch(radix)
{
case 10:
/*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v);
case 16:
_c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v);
case 2:
_c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v);
case 8:
_c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v);
}
C4_UNREACHABLE();
return substr::npos;
}
/** same as c4::utoa(), but pad with zeroes on the left such that the
* resulting string is @p num_digits wide. The @p radix must be 2,
* 8, 10 or 16. The resulting string is NOT zero-terminated. Writing
* stops at the buffer's end.
*
* @return the number of characters needed for the result, even if
* the buffer size is insufficient */
template<class T>
size_t utoa(substr buf, T v, T radix, size_t num_digits)
{
C4_STATIC_ASSERT(std::is_unsigned<T>::value);
C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
size_t pos = 0;
switch(radix)
{
case 10:
/*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
case 16:
_c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
case 2:
_c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
case 8:
_c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits);
}
C4_UNREACHABLE();
return substr::npos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** Convert a trimmed string to a signed integral value. The string
* can be formatted as decimal, binary (prefix 0b or 0B), octal
* (prefix 0o or 0O) or hexadecimal (prefix 0x or 0X). Strings with
* leading zeroes are considered as decimal. Every character in the
* input string is read for the conversion; it must not contain any
* leading or trailing whitespace.
*
* @return true if the conversion was successful.
*
* @note overflow is not detected: the return status is true even if
* the conversion would return a value outside of the type's range, in
* which case the result will wrap around the type's range.
* This is similar to native behavior.
*
* @see atoi_first() if the string is not trimmed to the value to read. */
template<class T>
bool atoi(csubstr str, T * C4_RESTRICT v)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
C4_STATIC_ASSERT(std::is_signed<T>::value);
if(C4_UNLIKELY(str.len == 0))
return false;
T sign = 1;
size_t start = 0;
if(str.str[0] == '-')
{
if(C4_UNLIKELY(str.len == 1))
return false;
++start;
sign = -1;
}
if(str.str[start] != '0')
{
if(C4_UNLIKELY( ! read_dec(str.sub(start), v)))
return false;
}
else
{
if(str.len == start+1)
{
*v = 0; // because the first character is 0
return true;
}
else
{
char pfx = str.str[start+1];
if(pfx == 'x' || pfx == 'X') // hexadecimal
{
if(C4_UNLIKELY(str.len <= start + 2))
return false;
if(C4_UNLIKELY( ! read_hex(str.sub(start + 2), v)))
return false;
}
else if(pfx == 'b' || pfx == 'B') // binary
{
if(C4_UNLIKELY(str.len <= start + 2))
return false;
if(C4_UNLIKELY( ! read_bin(str.sub(start + 2), v)))
return false;
}
else if(pfx == 'o' || pfx == 'O') // octal
{
if(C4_UNLIKELY(str.len <= start + 2))
return false;
if(C4_UNLIKELY( ! read_oct(str.sub(start + 2), v)))
return false;
}
else
{
// we know the first character is 0
auto fno = str.first_not_of('0', start + 1);
if(fno == csubstr::npos)
{
*v = 0;
return true;
}
if(C4_UNLIKELY( ! read_dec(str.sub(fno), v)))
{
return false;
}
}
}
}
*v *= sign;
return true;
}
/** Select the next range of characters in the string that can be parsed
* as a signed integral value, and convert it using atoi(). Leading
* whitespace (space, newline, tabs) is skipped.
* @return the number of characters read for conversion, or csubstr::npos if the conversion failed
* @see atoi() if the string is already trimmed to the value to read.
* @see csubstr::first_int_span() */
template<class T>
inline size_t atoi_first(csubstr str, T * C4_RESTRICT v)
{
csubstr trimmed = str.first_int_span();
if(trimmed.len == 0)
return csubstr::npos;
if(atoi(trimmed, v))
return static_cast<size_t>(trimmed.end() - str.begin());
return csubstr::npos;
}
//-----------------------------------------------------------------------------
/** Convert a trimmed string to an unsigned integral value. The string can be
* formatted as decimal, binary (prefix 0b or 0B), octal (prefix 0o or 0O)
* or hexadecimal (prefix 0x or 0X). Every character in the input string is read
* for the conversion; it must not contain any leading or trailing whitespace.
*
* @return true if the conversion was successful.
*
* @note overflow is not detected: the return status is true even if
* the conversion would return a value outside of the type's range, in
* which case the result will wrap around the type's range.
*
* @note If the string has a minus character, the return status
* will be false.
*
* @see atou_first() if the string is not trimmed to the value to read. */
template<class T>
bool atou(csubstr str, T * C4_RESTRICT v)
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
if(C4_UNLIKELY(str.len == 0 || str.front() == '-'))
return false;
if(str.str[0] != '0')
{
if(C4_UNLIKELY( ! read_dec(str, v)))
return false;
}
else
{
if(str.len == 1)
{
*v = 0; // we know the first character is 0
return true;
}
else
{
char pfx = str.str[1];
if(pfx == 'x' || pfx == 'X') // hexadecimal
{
if(C4_UNLIKELY(str.len <= 2))
return false;
return read_hex(str.sub(2), v);
}
else if(pfx == 'b' || pfx == 'B') // binary
{
if(C4_UNLIKELY(str.len <= 2))
return false;
return read_bin(str.sub(2), v);
}
else if(pfx == 'o' || pfx == 'O') // octal
{
if(C4_UNLIKELY(str.len <= 2))
return false;
return read_oct(str.sub(2), v);
}
else
{
// we know the first character is 0
auto fno = str.first_not_of('0');
if(fno == csubstr::npos)
{
*v = 0;
return true;
}
return read_dec(str.sub(fno), v);
}
}
}
return true;
}
/** Select the next range of characters in the string that can be parsed
* as an unsigned integral value, and convert it using atou(). Leading
* whitespace (space, newline, tabs) is skipped.
* @return the number of characters read for conversion, or csubstr::npos if the conversion faileds
* @see atou() if the string is already trimmed to the value to read.
* @see csubstr::first_uint_span() */
template<class T>
inline size_t atou_first(csubstr str, T *v)
{
csubstr trimmed = str.first_uint_span();
if(trimmed.len == 0)
return csubstr::npos;
if(atou(trimmed, v))
return static_cast<size_t>(trimmed.end() - str.begin());
return csubstr::npos;
}
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
/** @see http://www.exploringbinary.com/ for many good examples on float-str conversion */
template<size_t N>
void get_real_format_str(char (& C4_RESTRICT fmt)[N], int precision, RealFormat_e formatting, const char* length_modifier="")
{
int iret;
if(precision == -1)
iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, to_c_fmt(formatting));
else if(precision == 0)
iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, to_c_fmt(formatting));
else
iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, to_c_fmt(formatting));
C4_ASSERT(iret >= 2 && size_t(iret) < sizeof(fmt));
C4_UNUSED(iret);
}
/** @todo we're depending on snprintf()/sscanf() for converting to/from
* floating point numbers. Apparently, this increases the binary size
* by a considerable amount. There are some lightweight printf
* implementations:
*
* @see http://www.sparetimelabs.com/tinyprintf/tinyprintf.php (BSD)
* @see https://github.com/weiss/c99-snprintf
* @see https://github.com/nothings/stb/blob/master/stb_sprintf.h
* @see http://www.exploringbinary.com/
* @see https://blog.benoitblanchon.fr/lightweight-float-to-string/
* @see http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
*/
template<class T>
size_t print_one(substr str, const char* full_fmt, T v)
{
#ifdef _MSC_VER
/** use _snprintf() to prevent early termination of the output
* for writing the null character at the last position
* @see https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx */
int iret = _snprintf(str.str, str.len, full_fmt, v);
if(iret < 0)
{
/* when buf.len is not enough, VS returns a negative value.
* so call it again with a negative value for getting an
* actual length of the string */
iret = snprintf(nullptr, 0, full_fmt, v);
C4_ASSERT(iret > 0);
}
size_t ret = (size_t) iret;
return ret;
#else
int iret = snprintf(str.str, str.len, full_fmt, v);
C4_ASSERT(iret >= 0);
size_t ret = (size_t) iret;
if(ret >= str.len)
++ret; /* snprintf() reserves the last character to write \0 */
return ret;
#endif
}
#if !C4CORE_HAVE_STD_FROMCHARS && !defined(C4CORE_HAVE_FAST_FLOAT)
/** scans a string using the given type format, while at the same time
* allowing non-null-terminated strings AND guaranteeing that the given
* string length is strictly respected, so that no buffer overflows
* might occur. */
template<typename T>
inline size_t scan_one(csubstr str, const char *type_fmt, T *v)
{
/* snscanf() is absolutely needed here as we must be sure that
* str.len is strictly respected, because substr is
* generally not null-terminated.
*
* Alas, there is no snscanf().
*
* So we fake it by using a dynamic format with an explicit
* field size set to the length of the given span.
* This trick is taken from:
* https://stackoverflow.com/a/18368910/5875572 */
/* this is the actual format we'll use for scanning */
char fmt[16];
/* write the length into it. Eg "%12f".
* Also, get the number of characters read from the string.
* So the final format ends up as "%12f%n"*/
int iret = std::snprintf(fmt, sizeof(fmt), "%%" "%zu" "%s" "%%n", str.len, type_fmt);
/* no nasty surprises, please! */
C4_ASSERT(iret >= 0 && size_t(iret) < C4_COUNTOF(fmt));
/* now we scan with confidence that the span length is respected */
int num_chars;
iret = std::sscanf(str.str, fmt, v, &num_chars);
/* scanf returns the number of successful conversions */
if(iret != 1) return csubstr::npos;
C4_ASSERT(num_chars >= 0);
return (size_t)(num_chars);
}
#endif
#if C4CORE_HAVE_STD_TOCHARS
template<class T>
size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX)
{
std::to_chars_result result;
size_t pos = 0;
if(formatting == FTOA_HEXA)
{
_c4append('0');
_c4append('x');
}
if(precision == -1)
result = std::to_chars(buf.str + pos, buf.str + buf.len, v, to_std_fmt(formatting));
else
result = std::to_chars(buf.str + pos, buf.str + buf.len, v, to_std_fmt(formatting), precision);
if(result.ec == std::errc())
{
// all good, no errors.
C4_ASSERT(result.ptr >= buf.str);
ptrdiff_t delta = result.ptr - buf.str;
return static_cast<size_t>(delta);
}
C4_ASSERT(result.ec == std::errc::value_too_large);
// This is unfortunate.
//
// When the result can't fit in the given buffer,
// std::to_chars() returns the end pointer it was originally
// given, which is useless because here we would like to know
// _exactly_ how many characters the buffer must have to fit
// the result.
//
// So we take the pessimistic view, and assume as many digits
// as could ever be required:
size_t ret = static_cast<size_t>(std::numeric_limits<T>::max_digits10);
return ret > buf.len ? ret : buf.len + 1;
}
#endif // C4CORE_HAVE_STD_TOCHARS
} // namespace detail
#undef _c4appendhex
#undef _c4append
/** Convert a single-precision real number to string.
* The string will in general be NOT null-terminated.
* For FTOA_FLEX, \p precision is the number of significand digits. Otherwise
* \p precision is the number of decimals. */
inline size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX)
{
#if C4CORE_HAVE_STD_TOCHARS
return detail::rtoa(str, v, precision, formatting);
#else
char fmt[16];
detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"");
return detail::print_one(str, fmt, v);
#endif
}
/** Convert a double-precision real number to string.
* The string will in general be NOT null-terminated.
* For FTOA_FLEX, \p precision is the number of significand digits. Otherwise
* \p precision is the number of decimals.
*
* @return the number of characters written.
*/
inline size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX)
{
#if C4CORE_HAVE_STD_TOCHARS
return detail::rtoa(str, v, precision, formatting);
#else
char fmt[16];
detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"l");
return detail::print_one(str, fmt, v);
#endif
}
/** Convert a string to a single precision real number.
* The input string must be trimmed to the value, ie
* no leading or trailing whitespace can be present.
* @return true iff the conversion succeeded
* @see atof_first() if the string is not trimmed
*/
inline bool atof(csubstr str, float * C4_RESTRICT v)
{
C4_ASSERT(str.triml(" \r\t\n").len == str.len);
#if C4CORE_HAVE_FAST_FLOAT
fast_float::from_chars_result result;
result = fast_float::from_chars(str.str, str.str + str.len, *v);
return result.ec == std::errc();
#elif C4CORE_HAVE_STD_FROMCHARS
std::from_chars_result result;
result = std::from_chars(str.str, str.str + str.len, *v);
return result.ec == std::errc();
#else
size_t ret = detail::scan_one(str, "f", v);
return ret != csubstr::npos;
#endif
}
/** Convert a string to a double precision real number.
* The input string must be trimmed to the value, ie
* no leading or trailing whitespace can be present.
* @return true iff the conversion succeeded
* @see atod_first() if the string is not trimmed
*/
inline bool atod(csubstr str, double * C4_RESTRICT v)
{
C4_ASSERT(str.triml(" \r\t\n").len == str.len);
#if C4CORE_HAVE_FAST_FLOAT
fast_float::from_chars_result result;
result = fast_float::from_chars(str.str, str.str + str.len, *v);
return result.ec == std::errc();
#elif C4CORE_HAVE_STD_FROMCHARS
std::from_chars_result result;
result = std::from_chars(str.str, str.str + str.len, *v);
return result.ec == std::errc();
#else
size_t ret = detail::scan_one(str, "lf", v);
return ret != csubstr::npos;
#endif
}
/** Convert a string to a single precision real number.
* Leading whitespace is skipped until valid characters are found.
* @return the number of characters read from the string, or npos if
* conversion was not successful or if the string was empty */
inline size_t atof_first(csubstr str, float * C4_RESTRICT v)
{
csubstr trimmed = str.first_real_span();
if(trimmed.len == 0)
return csubstr::npos;
if(atof(trimmed, v))
return static_cast<size_t>(trimmed.end() - str.begin());
return csubstr::npos;
}
/** Convert a string to a double precision real number.
* Leading whitespace is skipped until valid characters are found.
* @return the number of characters read from the string, or npos if
* conversion was not successful or if the string was empty */
inline size_t atod_first(csubstr str, double * C4_RESTRICT v)
{
csubstr trimmed = str.first_real_span();
if(trimmed.len == 0)
return csubstr::npos;
if(atod(trimmed, v))
return static_cast<size_t>(trimmed.end() - str.begin());
return csubstr::npos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// generic versions
C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) { return utoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) { return utoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) { return utoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) { return utoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) { return itoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) { return itoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) { return itoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) { return itoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, float v) { return ftoa(s, v); }
C4_ALWAYS_INLINE size_t xtoa(substr s, double v) { return dtoa(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) { return atou(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) { return atou(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) { return atou(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) { return atou(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) { return atoi(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) { return atoi(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) { return atoi(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) { return atoi(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) { return atof(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) { return atod(s, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) { return utoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) { return utoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) { return utoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) { return utoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) { return itoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) { return itoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) { return itoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) { return itoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) { return ftoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) { return dtoa(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) { return atou(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) { return atou(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) { return atou(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) { return atou(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) { return atoi(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) { return atoi(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) { return atoi(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) { return atoi(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) { return atof(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) { return atod(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) { return atou_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) { return atou_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) { return atou_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) { return atou_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) { return atoi_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) { return atoi_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) { return atoi_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) { return atoi_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) { return atof_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) { return atod_first(buf, v); }
//-----------------------------------------------------------------------------
// on some platforms, (unsigned) int and (unsigned) long
// are not any of the fixed length types above
#define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty>
#define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty>
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type xtoa(substr buf, T v) { return itoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type xtoa(substr buf, T v) { return utoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) { return atoi(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) { return atou(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type to_chars(substr buf, T v) { return itoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type to_chars(substr buf, T v) { return utoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) { return atoi(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) { return atou(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) { return atoi_first(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) { return atou_first(buf, v); }
#undef _C4_IF_NOT_FIXED_LENGTH_I
#undef _C4_IF_NOT_FIXED_LENGTH_U
//-----------------------------------------------------------------------------
// for pointers
template <class T> C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) { return itoa(s, (intptr_t)v, (intptr_t)16); }
template <class T> C4_ALWAYS_INLINE bool atox(csubstr s, T **v) { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
template <class T> C4_ALWAYS_INLINE size_t to_chars(substr s, T *v) { return itoa(s, (intptr_t)v, (intptr_t)16); }
template <class T> C4_ALWAYS_INLINE bool from_chars(csubstr buf, T **v) { intptr_t tmp; bool ret = from_chars(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
template <class T> C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** call to_chars() and return a substr consisting of the
* written portion of the input buffer. Ie, same as to_chars(),
* but return a substr instead of a size_t.
*
* @see to_chars() */
template<class T>
inline substr to_chars_sub(substr buf, T const& C4_RESTRICT v)
{
size_t sz = to_chars(buf, v);
return buf.left_of(sz <= buf.len ? sz : buf.len);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// bool implementation
inline size_t to_chars(substr buf, bool v)
{
int val = v;
return to_chars(buf, val);
}
inline bool from_chars(csubstr buf, bool * C4_RESTRICT v)
{
if(buf == '0')
{
*v = false; return true;
}
else if(buf == '1')
{
*v = true; return true;
}
else if(buf == "false")
{
*v = false; return true;
}
else if(buf == "true")
{
*v = true; return true;
}
else if(buf == "False")
{
*v = false; return true;
}
else if(buf == "True")
{
*v = true; return true;
}
else if(buf == "FALSE")
{
*v = false; return true;
}
else if(buf == "TRUE")
{
*v = true; return true;
}
// fallback to c-style int bools
int val = 0;
bool ret = from_chars(buf, &val);
if(C4_LIKELY(ret))
{
*v = (val != 0);
}
return ret;
}
inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v)
{
csubstr trimmed = buf.first_non_empty_span();
if(trimmed.len == 0 || !from_chars(buf, v))
return csubstr::npos;
return trimmed.len;
}
//-----------------------------------------------------------------------------
// single-char implementation
inline size_t to_chars(substr buf, char v)
{
if(buf.len > 0)
buf[0] = v;
return 1;
}
/** extract a single character from a substring
* @note to extract a string instead and not just a single character, use the csubstr overload */
inline bool from_chars(csubstr buf, char * C4_RESTRICT v)
{
if(buf.len != 1)
return false;
*v = buf[0];
return true;
}
inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v)
{
if(buf.len < 1)
return csubstr::npos;
*v = buf[0];
return 1;
}
//-----------------------------------------------------------------------------
// csubstr implementation
inline size_t to_chars(substr buf, csubstr v)
{
C4_ASSERT(!buf.overlaps(v));
size_t len = buf.len < v.len ? buf.len : v.len;
memcpy(buf.str, v.str, len);
return v.len;
}
inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v)
{
*v = buf;
return true;
}
inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v)
{
csubstr trimmed = buf.first_non_empty_span();
if(trimmed.len == 0)
return csubstr::npos;
*v = trimmed;
return static_cast<size_t>(trimmed.end() - buf.begin());
}
//-----------------------------------------------------------------------------
// substr
inline size_t to_chars(substr buf, substr v)
{
C4_ASSERT(!buf.overlaps(v));
size_t len = buf.len < v.len ? buf.len : v.len;
memcpy(buf.str, v.str, len);
return v.len;
}
inline bool from_chars(csubstr buf, substr * C4_RESTRICT v)
{
C4_ASSERT(!buf.overlaps(*v));
if(buf.len <= v->len)
{
memcpy(v->str, buf.str, buf.len);
v->len = buf.len;
return true;
}
memcpy(v->str, buf.str, v->len);
return false;
}
inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v)
{
csubstr trimmed = buf.first_non_empty_span();
C4_ASSERT(!trimmed.overlaps(*v));
if(C4_UNLIKELY(trimmed.len == 0))
return csubstr::npos;
size_t len = trimmed.len > v->len ? v->len : trimmed.len;
memcpy(v->str, trimmed.str, len);
if(C4_UNLIKELY(trimmed.len > v->len))
return csubstr::npos;
return static_cast<size_t>(trimmed.end() - buf.begin());
}
//-----------------------------------------------------------------------------
template<size_t N>
inline size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N])
{
csubstr sp(v);
return to_chars(buf, sp);
}
inline size_t to_chars(substr buf, const char * C4_RESTRICT v)
{
return to_chars(buf, to_csubstr(v));
}
} // namespace c4
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* _C4_CHARCONV_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/charconv.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/utf.hpp
// https://github.com/biojppm/c4core/src/c4/utf.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_UTF_HPP_
#define C4_UTF_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/language.hpp
//#include "c4/language.hpp"
#if !defined(C4_LANGUAGE_HPP_) && !defined(_C4_LANGUAGE_HPP_)
#error "amalgamate: file c4/language.hpp must have been included at this point"
#endif /* C4_LANGUAGE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr_fwd.hpp
//#include "c4/substr_fwd.hpp"
#if !defined(C4_SUBSTR_FWD_HPP_) && !defined(_C4_SUBSTR_FWD_HPP_)
#error "amalgamate: file c4/substr_fwd.hpp must have been included at this point"
#endif /* C4_SUBSTR_FWD_HPP_ */
//included above:
//#include <stddef.h>
//included above:
//#include <stdint.h>
namespace c4 {
substr decode_code_point(substr out, csubstr code_point);
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code);
} // namespace c4
#endif // C4_UTF_HPP_
// (end https://github.com/biojppm/c4core/src/c4/utf.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/format.hpp
// https://github.com/biojppm/c4core/src/c4/format.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_FORMAT_HPP_
#define _C4_FORMAT_HPP_
/** @file format.hpp provides type-safe facilities for formatting arguments
* to string buffers */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/charconv.hpp
//#include "c4/charconv.hpp"
#if !defined(C4_CHARCONV_HPP_) && !defined(_C4_CHARCONV_HPP_)
#error "amalgamate: file c4/charconv.hpp must have been included at this point"
#endif /* C4_CHARCONV_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/blob.hpp
//#include "c4/blob.hpp"
#if !defined(C4_BLOB_HPP_) && !defined(_C4_BLOB_HPP_)
#error "amalgamate: file c4/blob.hpp must have been included at this point"
#endif /* C4_BLOB_HPP_ */
#ifdef _MSC_VER
# pragma warning(push)
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning)
# endif
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
#elif defined(__clang__)
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
namespace c4 {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting truthy types as booleans
namespace fmt {
/** write a variable as an alphabetic boolean, ie as either true or false
* @param strict_read */
template<class T>
struct boolalpha_
{
boolalpha_(T val_, bool strict_read_=false) : val(val_ ? true : false), strict_read(strict_read_) {}
bool val;
bool strict_read;
};
template<class T>
boolalpha_<T> boolalpha(T const& val, bool strict_read=false)
{
return boolalpha_<T>(val, strict_read);
}
} // namespace fmt
/** write a variable as an alphabetic boolean, ie as either true or false */
template<class T>
inline size_t to_chars(substr buf, fmt::boolalpha_<T> fmt)
{
return to_chars(buf, fmt.val ? "true" : "false");
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting integral types
namespace fmt {
/** format an integral type with a custom radix */
template<typename T>
struct integral_
{
T val;
T radix;
C4_ALWAYS_INLINE integral_(T val_, T radix_) : val(val_), radix(radix_) {}
};
/** format an integral type with a custom radix, and pad with zeroes on the left */
template<typename T>
struct integral_padded_
{
T val;
T radix;
size_t num_digits;
C4_ALWAYS_INLINE integral_padded_(T val_, T radix_, size_t nd) : val(val_), radix(radix_), num_digits(nd) {}
};
/** format an integral type with a custom radix */
template<class T>
C4_ALWAYS_INLINE integral_<T> integral(T val, T radix=10)
{
return integral_<T>(val, radix);
}
/** format an integral type with a custom radix */
template<class T>
C4_ALWAYS_INLINE integral_<intptr_t> integral(T const* val, T radix=10)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(val), static_cast<intptr_t>(radix));
}
/** format an integral type with a custom radix */
template<class T>
C4_ALWAYS_INLINE integral_<intptr_t> integral(std::nullptr_t, T radix=10)
{
return integral_<intptr_t>(intptr_t(0), static_cast<intptr_t>(radix));
}
/** pad the argument with zeroes on the left, with decimal radix */
template<class T>
C4_ALWAYS_INLINE integral_padded_<T> zpad(T val, size_t num_digits)
{
return integral_padded_<T>(val, T(10), num_digits);
}
/** pad the argument with zeroes on the left */
template<class T>
C4_ALWAYS_INLINE integral_padded_<T> zpad(integral_<T> val, size_t num_digits)
{
return integral_padded_<T>(val.val, val.radix, num_digits);
}
/** pad the argument with zeroes on the left */
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(std::nullptr_t, size_t num_digits)
{
return integral_padded_<intptr_t>(0, 16, num_digits);
}
/** pad the argument with zeroes on the left */
template<class T>
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T const* val, size_t num_digits)
{
return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);
}
template<class T>
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T * val, size_t num_digits)
{
return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);
}
/** format the pointer as an hexadecimal value */
template<class T>
inline integral_<intptr_t> hex(T * v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));
}
/** format the pointer as an hexadecimal value */
template<class T>
inline integral_<intptr_t> hex(T const* v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));
}
/** format null as an hexadecimal value
* @overload hex */
inline integral_<intptr_t> hex(std::nullptr_t)
{
return integral_<intptr_t>(0, intptr_t(16));
}
/** format the integral_ argument as an hexadecimal value
* @overload hex */
template<class T>
inline integral_<T> hex(T v)
{
return integral_<T>(v, T(16));
}
/** format the pointer as an octal value */
template<class T>
inline integral_<intptr_t> oct(T const* v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));
}
/** format the pointer as an octal value */
template<class T>
inline integral_<intptr_t> oct(T * v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));
}
/** format null as an octal value */
inline integral_<intptr_t> oct(std::nullptr_t)
{
return integral_<intptr_t>(intptr_t(0), intptr_t(8));
}
/** format the integral_ argument as an octal value */
template<class T>
inline integral_<T> oct(T v)
{
return integral_<T>(v, T(8));
}
/** format the pointer as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
template<class T>
inline integral_<intptr_t> bin(T const* v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));
}
/** format the pointer as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
template<class T>
inline integral_<intptr_t> bin(T * v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));
}
/** format null as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
inline integral_<intptr_t> bin(std::nullptr_t)
{
return integral_<intptr_t>(intptr_t(0), intptr_t(2));
}
/** format the integral_ argument as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
template<class T>
inline integral_<T> bin(T v)
{
return integral_<T>(v, T(2));
}
} // namespace fmt
/** format an integral_ signed type */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_signed<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_<T> fmt)
{
return itoa(buf, fmt.val, fmt.radix);
}
/** format an integral_ signed type, pad with zeroes */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_signed<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_padded_<T> fmt)
{
return itoa(buf, fmt.val, fmt.radix, fmt.num_digits);
}
/** format an integral_ unsigned type */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_<T> fmt)
{
return utoa(buf, fmt.val, fmt.radix);
}
/** format an integral_ unsigned type, pad with zeroes */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_padded_<T> fmt)
{
return utoa(buf, fmt.val, fmt.radix, fmt.num_digits);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting real types
namespace fmt {
template<class T>
struct real_
{
T val;
int precision;
RealFormat_e fmt;
real_(T v, int prec=-1, RealFormat_e f=FTOA_FLOAT) : val(v), precision(prec), fmt(f) {}
};
template<class T>
real_<T> real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
{
return real_<T>(val, precision, fmt);
}
} // namespace fmt
inline size_t to_chars(substr buf, fmt::real_< float> fmt) { return ftoa(buf, fmt.val, fmt.precision, fmt.fmt); }
inline size_t to_chars(substr buf, fmt::real_<double> fmt) { return dtoa(buf, fmt.val, fmt.precision, fmt.fmt); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// writing raw binary data
namespace fmt {
/** @see blob_ */
template<class T>
struct raw_wrapper_ : public blob_<T>
{
size_t alignment;
C4_ALWAYS_INLINE raw_wrapper_(blob_<T> data, size_t alignment_) noexcept
:
blob_<T>(data),
alignment(alignment_)
{
C4_ASSERT_MSG(alignment > 0 && (alignment & (alignment - 1)) == 0, "alignment must be a power of two");
}
};
using const_raw_wrapper = raw_wrapper_<cbyte>;
using raw_wrapper = raw_wrapper_<byte>;
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
inline const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t))
{
return const_raw_wrapper(data, alignment);
}
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
inline const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t))
{
return const_raw_wrapper(data, alignment);
}
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
template<class T>
inline const_raw_wrapper craw(T const& C4_RESTRICT data, size_t alignment=alignof(T))
{
return const_raw_wrapper(cblob(data), alignment);
}
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
template<class T>
inline const_raw_wrapper raw(T const& C4_RESTRICT data, size_t alignment=alignof(T))
{
return const_raw_wrapper(cblob(data), alignment);
}
/** mark a variable to be read in raw binary format, using memcpy */
inline raw_wrapper raw(blob data, size_t alignment=alignof(max_align_t))
{
return raw_wrapper(data, alignment);
}
/** mark a variable to be read in raw binary format, using memcpy */
template<class T>
inline raw_wrapper raw(T & C4_RESTRICT data, size_t alignment=alignof(T))
{
return raw_wrapper(blob(data), alignment);
}
} // namespace fmt
/** write a variable in raw binary format, using memcpy */
C4CORE_EXPORT size_t to_chars(substr buf, fmt::const_raw_wrapper r);
/** read a variable in raw binary format, using memcpy */
C4CORE_EXPORT bool from_chars(csubstr buf, fmt::raw_wrapper *r);
/** read a variable in raw binary format, using memcpy */
inline bool from_chars(csubstr buf, fmt::raw_wrapper r)
{
return from_chars(buf, &r);
}
/** read a variable in raw binary format, using memcpy */
inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper *r)
{
return from_chars(buf, r);
}
/** read a variable in raw binary format, using memcpy */
inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper r)
{
return from_chars(buf, &r);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting aligned to left/right
namespace fmt {
template<class T>
struct left_
{
T val;
size_t width;
char pad;
left_(T v, size_t w, char p) : val(v), width(w), pad(p) {}
};
template<class T>
struct right_
{
T val;
size_t width;
char pad;
right_(T v, size_t w, char p) : val(v), width(w), pad(p) {}
};
/** mark an argument to be aligned left */
template<class T>
left_<T> left(T val, size_t width, char padchar=' ')
{
return left_<T>(val, width, padchar);
}
/** mark an argument to be aligned right */
template<class T>
right_<T> right(T val, size_t width, char padchar=' ')
{
return right_<T>(val, width, padchar);
}
} // namespace fmt
template<class T>
size_t to_chars(substr buf, fmt::left_<T> const& C4_RESTRICT align)
{
size_t ret = to_chars(buf, align.val);
if(ret >= buf.len || ret >= align.width)
return ret > align.width ? ret : align.width;
buf.first(align.width).sub(ret).fill(align.pad);
to_chars(buf, align.val);
return align.width;
}
template<class T>
size_t to_chars(substr buf, fmt::right_<T> const& C4_RESTRICT align)
{
size_t ret = to_chars(buf, align.val);
if(ret >= buf.len || ret >= align.width)
return ret > align.width ? ret : align.width;
size_t rem = static_cast<size_t>(align.width - ret);
buf.first(rem).fill(align.pad);
to_chars(buf.sub(rem), align.val);
return align.width;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t cat(substr /*buf*/)
{
return 0;
}
/// @endcond
/** serialize the arguments, concatenating them to the given fixed-size buffer.
* The buffer size is strictly respected: no writes will occur beyond its end.
* @return the number of characters needed to write all the arguments into the buffer.
* @see c4::catrs() if instead of a fixed-size buffer, a resizeable container is desired
* @see c4::uncat() for the inverse function
* @see c4::catsep() if a separator between each argument is to be used
* @see c4::format() if a format string is desired */
template<class Arg, class... Args>
size_t cat(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t num = to_chars(buf, a);
buf = buf.len >= num ? buf.sub(num) : substr{};
num += cat(buf, more...);
return num;
}
/** like c4::cat() but return a substr instead of a size */
template<class... Args>
substr cat_sub(substr buf, Args && ...args)
{
size_t sz = cat(buf, std::forward<Args>(args)...);
C4_CHECK(sz <= buf.len);
return {buf.str, sz <= buf.len ? sz : buf.len};
}
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t uncat(csubstr /*buf*/)
{
return 0;
}
/// @endcond
/** deserialize the arguments from the given buffer.
*
* @return the number of characters read from the buffer, or csubstr::npos
* if a conversion was not successful.
* @see c4::cat(). c4::uncat() is the inverse of c4::cat(). */
template<class Arg, class... Args>
size_t uncat(csubstr buf, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
size_t out = from_chars_first(buf, &a);
if(C4_UNLIKELY(out == csubstr::npos))
return csubstr::npos;
buf = buf.len >= out ? buf.sub(out) : substr{};
size_t num = uncat(buf, more...);
if(C4_UNLIKELY(num == csubstr::npos))
return csubstr::npos;
return out + num;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
template<class Sep>
inline size_t catsep_more(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)
{
return 0;
}
template<class Sep, class Arg, class... Args>
size_t catsep_more(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t ret = to_chars(buf, sep), num = ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = to_chars(buf, a);
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = catsep_more(buf, sep, more...);
num += ret;
return num;
}
template<class Sep>
inline size_t uncatsep_more(csubstr /*buf*/, Sep & /*sep*/)
{
return 0;
}
template<class Sep, class Arg, class... Args>
size_t uncatsep_more(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
size_t ret = from_chars_first(buf, &sep), num = ret;
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = from_chars_first(buf, &a);
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = uncatsep_more(buf, sep, more...);
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
num += ret;
return num;
}
} // namespace detail
/** serialize the arguments, concatenating them to the given fixed-size
* buffer, using a separator between each argument.
* The buffer size is strictly respected: no writes will occur beyond its end.
* @return the number of characters needed to write all the arguments into the buffer.
* @see c4::catseprs() if instead of a fixed-size buffer, a resizeable container is desired
* @see c4::uncatsep() for the inverse function (ie, reading instead of writing)
* @see c4::cat() if no separator is needed
* @see c4::format() if a format string is desired */
template<class Sep, class Arg, class... Args>
size_t catsep(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t num = to_chars(buf, a);
buf = buf.len >= num ? buf.sub(num) : substr{};
num += detail::catsep_more(buf, sep, more...);
return num;
}
/** like c4::catsep() but return a substr instead of a size
* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */
template<class... Args>
substr catsep_sub(substr buf, Args && ...args)
{
size_t sz = catsep(buf, std::forward<Args>(args)...);
C4_CHECK(sz <= buf.len);
return {buf.str, sz <= buf.len ? sz : buf.len};
}
/** deserialize the arguments from the given buffer, using a separator.
*
* @return the number of characters read from the buffer, or csubstr::npos
* if a conversion was not successful
* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */
template<class Sep, class Arg, class... Args>
size_t uncatsep(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
size_t ret = from_chars_first(buf, &a), num = ret;
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = detail::uncatsep_more(buf, sep, more...);
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
num += ret;
return num;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t format(substr buf, csubstr fmt)
{
return to_chars(buf, fmt);
}
/// @endcond
/** using a format string, serialize the arguments into the given
* fixed-size buffer.
* The buffer size is strictly respected: no writes will occur beyond its end.
* In the format string, each argument is marked with a compact
* curly-bracket pair: {}. Arguments beyond the last curly bracket pair
* are silently ignored. For example:
* @code{.cpp}
* c4::format(buf, "the {} drank {} {}", "partier", 5, "beers"); // the partier drank 5 beers
* c4::format(buf, "the {} drank {} {}", "programmer", 6, "coffees"); // the programmer drank 6 coffees
* @endcode
* @return the number of characters needed to write into the buffer.
* @see c4::formatrs() if instead of a fixed-size buffer, a resizeable container is desired
* @see c4::unformat() for the inverse function
* @see c4::cat() if no format or separator is needed
* @see c4::catsep() if no format is needed, but a separator must be used */
template<class Arg, class... Args>
size_t format(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
return to_chars(buf, fmt);
size_t num = to_chars(buf, fmt.sub(0, pos));
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = to_chars(buf, a);
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = format(buf, fmt.sub(pos + 2), more...);
out += num;
return out;
}
/** like c4::format() but return a substr instead of a size
* @see c4::format()
* @see c4::catsep(). uncatsep() is the inverse of catsep(). */
template<class... Args>
substr format_sub(substr buf, csubstr fmt, Args const& C4_RESTRICT ...args)
{
size_t sz = c4::format(buf, fmt, args...);
C4_CHECK(sz <= buf.len);
return {buf.str, sz <= buf.len ? sz : buf.len};
}
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t unformat(csubstr /*buf*/, csubstr fmt)
{
return fmt.len;
}
/// @endcond
/** using a format string, deserialize the arguments from the given
* buffer.
* @return the number of characters read from the buffer, or npos if a conversion failed.
* @see c4::format(). c4::unformat() is the inverse function to format(). */
template<class Arg, class... Args>
size_t unformat(csubstr buf, csubstr fmt, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
const size_t pos = fmt.find("{}");
if(C4_UNLIKELY(pos == csubstr::npos))
return unformat(buf, fmt);
size_t num = pos;
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = from_chars_first(buf, &a);
if(C4_UNLIKELY(num == csubstr::npos))
return csubstr::npos;
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = unformat(buf, fmt.sub(pos + 2), more...);
if(C4_UNLIKELY(num == csubstr::npos))
return csubstr::npos;
out += num;
return out;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a tag type for marking append to container
* @see c4::catrs() */
struct append_t {};
/** a tag variable
* @see c4::catrs() */
constexpr const append_t append = {};
//-----------------------------------------------------------------------------
/** like c4::cat(), but receives a container, and resizes it as needed to contain
* the result. The container is overwritten. To append to it, use the append
* overload.
* @see c4::cat() */
template<class CharOwningContainer, class... Args>
inline void catrs(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)
{
retry:
substr buf = to_substr(*cont);
size_t ret = cat(buf, args...);
cont->resize(ret);
if(ret > buf.len)
goto retry;
}
/** like c4::cat(), but creates and returns a new container sized as needed to contain
* the result.
* @see c4::cat() */
template<class CharOwningContainer, class... Args>
inline CharOwningContainer catrs(Args const& C4_RESTRICT ...args)
{
CharOwningContainer cont;
catrs(&cont, args...);
return cont;
}
/** like c4::cat(), but receives a container, and appends to it instead of
* overwriting it. The container is resized as needed to contain the result.
* @return the region newly appended to the original container
* @see c4::cat()
* @see c4::catrs() */
template<class CharOwningContainer, class... Args>
inline csubstr catrs(append_t, CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)
{
const size_t pos = cont->size();
retry:
substr buf = to_substr(*cont).sub(pos);
size_t ret = cat(buf, args...);
cont->resize(pos + ret);
if(ret > buf.len)
goto retry;
return to_csubstr(*cont).range(pos, cont->size());
}
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the recursion
template<class CharOwningContainer, class Sep, class... Args>
inline void catseprs(CharOwningContainer * C4_RESTRICT, Sep const& C4_RESTRICT)
{
return;
}
/// @end cond
/** like c4::catsep(), but receives a container, and resizes it as needed to contain the result.
* The container is overwritten. To append to the container use the append overload.
* @see c4::catsep() */
template<class CharOwningContainer, class Sep, class... Args>
inline void catseprs(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
{
retry:
substr buf = to_substr(*cont);
size_t ret = catsep(buf, sep, args...);
cont->resize(ret);
if(ret > buf.len)
goto retry;
}
/** like c4::catsep(), but create a new container with the result.
* @return the requested container */
template<class CharOwningContainer, class Sep, class... Args>
inline CharOwningContainer catseprs(Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
{
CharOwningContainer cont;
catseprs(&cont, sep, args...);
return cont;
}
/// @cond dev
// terminates the recursion
template<class CharOwningContainer, class Sep, class... Args>
inline csubstr catseprs(append_t, CharOwningContainer * C4_RESTRICT, Sep const& C4_RESTRICT)
{
csubstr s;
return s;
}
/// @endcond
/** like catsep(), but receives a container, and appends the arguments, resizing the
* container as needed to contain the result. The buffer is appended to.
* @return a csubstr of the appended part
* @ingroup formatting_functions */
template<class CharOwningContainer, class Sep, class... Args>
inline csubstr catseprs(append_t, CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
{
const size_t pos = cont->size();
retry:
substr buf = to_substr(*cont).sub(pos);
size_t ret = catsep(buf, sep, args...);
cont->resize(pos + ret);
if(ret > buf.len)
goto retry;
return to_csubstr(*cont).range(pos, cont->size());
}
//-----------------------------------------------------------------------------
/** like c4::format(), but receives a container, and resizes it as needed
* to contain the result. The container is overwritten. To append to
* the container use the append overload.
* @see c4::format() */
template<class CharOwningContainer, class... Args>
inline void formatrs(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
{
retry:
substr buf = to_substr(*cont);
size_t ret = format(buf, fmt, args...);
cont->resize(ret);
if(ret > buf.len)
goto retry;
}
/** like c4::format(), but create a new container with the result.
* @return the requested container */
template<class CharOwningContainer, class... Args>
inline CharOwningContainer formatrs(csubstr fmt, Args const& C4_RESTRICT ...args)
{
CharOwningContainer cont;
formatrs(&cont, fmt, args...);
return cont;
}
/** like format(), but receives a container, and appends the
* arguments, resizing the container as needed to contain the
* result. The buffer is appended to.
* @return the region newly appended to the original container
* @ingroup formatting_functions */
template<class CharOwningContainer, class... Args>
inline csubstr formatrs(append_t, CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
{
const size_t pos = cont->size();
retry:
substr buf = to_substr(*cont).sub(pos);
size_t ret = format(buf, fmt, args...);
cont->resize(pos + ret);
if(ret > buf.len)
goto retry;
return to_csubstr(*cont).range(pos, cont->size());
}
} // namespace c4
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* _C4_FORMAT_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/format.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/dump.hpp
// https://github.com/biojppm/c4core/src/c4/dump.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_DUMP_HPP_
#define C4_DUMP_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr.hpp
//#include <c4/substr.hpp>
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
namespace c4 {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** type of the function to dump characters */
using DumperPfn = void (*)(csubstr buf);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<DumperPfn dumpfn, class Arg>
inline size_t dump(substr buf, Arg const& a)
{
size_t sz = to_chars(buf, a); // need to serialize to the buffer
if(C4_LIKELY(sz <= buf.len))
dumpfn(buf.first(sz));
return sz;
}
template<class DumperFn, class Arg>
inline size_t dump(DumperFn &&dumpfn, substr buf, Arg const& a)
{
size_t sz = to_chars(buf, a); // need to serialize to the buffer
if(C4_LIKELY(sz <= buf.len))
dumpfn(buf.first(sz));
return sz;
}
template<DumperPfn dumpfn>
inline size_t dump(substr buf, csubstr a)
{
if(buf.len)
dumpfn(a); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<class DumperFn>
inline size_t dump(DumperFn &&dumpfn, substr buf, csubstr a)
{
if(buf.len)
dumpfn(a); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<DumperPfn dumpfn, size_t N>
inline size_t dump(substr buf, const char (&a)[N])
{
if(buf.len)
dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<class DumperFn, size_t N>
inline size_t dump(DumperFn &&dumpfn, substr buf, const char (&a)[N])
{
if(buf.len)
dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** */
struct DumpResults
{
enum : size_t { noarg = (size_t)-1 };
size_t bufsize = 0;
size_t lastok = noarg;
bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; }
bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; }
size_t argfail() const { return lastok + 1; }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
template<class DumperFn>
size_t cat_dump(DumperFn &&, substr)
{
return 0;
}
// terminates the variadic recursion
template<DumperPfn dumpfn>
size_t cat_dump(substr)
{
return 0;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Arg, class... Args>
size_t cat_dump(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t size_for_a = dump(dumpfn, buf, a);
if(C4_UNLIKELY(size_for_a > buf.len))
buf = buf.first(0); // ensure no more calls
size_t size_for_more = cat_dump(dumpfn, buf, more...);
return size_for_more > size_for_a ? size_for_more : size_for_a;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn,class Arg, class... Args>
size_t cat_dump(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t size_for_a = dump<dumpfn>(buf, a);
if(C4_LIKELY(size_for_a > buf.len))
buf = buf.first(0); // ensure no more calls
size_t size_for_more = cat_dump<dumpfn>(buf, more...);
return size_for_more > size_for_a ? size_for_more : size_for_a;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
// terminates the variadic recursion
template<DumperPfn dumpfn, class Arg>
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results.write_arg(currarg)))
{
size_t sz = dump<dumpfn>(buf, a); // yield to the specialized function
if(currarg == results.lastok + 1 && sz <= buf.len)
results.lastok = currarg;
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
}
return results;
}
// terminates the variadic recursion
template<class DumperFn, class Arg>
DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results.write_arg(currarg)))
{
size_t sz = dump(dumpfn, buf, a); // yield to the specialized function
if(currarg == results.lastok + 1 && sz <= buf.len)
results.lastok = currarg;
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
}
return results;
}
template<DumperPfn dumpfn, class Arg, class... Args>
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
results = detail::cat_dump_resume<dumpfn>(currarg, results, buf, a);
return detail::cat_dump_resume<dumpfn>(currarg + 1u, results, buf, more...);
}
template<class DumperFn, class Arg, class... Args>
DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
results = detail::cat_dump_resume(currarg, dumpfn, results, buf, a);
return detail::cat_dump_resume(currarg + 1u, dumpfn, results, buf, more...);
}
} // namespace detail
/// @endcond
template<DumperPfn dumpfn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
if(results.bufsize > buf.len)
return results;
return detail::cat_dump_resume<dumpfn>(0u, results, buf, a, more...);
}
template<class DumperFn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
if(results.bufsize > buf.len)
return results;
return detail::cat_dump_resume(0u, dumpfn, results, buf, a, more...);
}
template<DumperPfn dumpfn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
return detail::cat_dump_resume<dumpfn>(0u, DumpResults{}, buf, a, more...);
}
template<class DumperFn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
return detail::cat_dump_resume(0u, dumpfn, DumpResults{}, buf, a, more...);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminate the recursion
template<class DumperFn, class Sep>
size_t catsep_dump(DumperFn &&, substr, Sep const& C4_RESTRICT)
{
return 0;
}
// terminate the recursion
template<DumperPfn dumpfn, class Sep>
size_t catsep_dump(substr, Sep const& C4_RESTRICT)
{
return 0;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Sep, class Arg, class... Args>
size_t catsep_dump(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t sz = dump(dumpfn, buf, a);
if(C4_UNLIKELY(sz > buf.len))
buf = buf.first(0); // ensure no more calls
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
{
size_t szsep = dump(dumpfn, buf, sep);
if(C4_UNLIKELY(szsep > buf.len))
buf = buf.first(0); // ensure no more calls
sz = sz > szsep ? sz : szsep;
}
size_t size_for_more = catsep_dump(dumpfn, buf, sep, more...);
return size_for_more > sz ? size_for_more : sz;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
size_t catsep_dump(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t sz = dump<dumpfn>(buf, a);
if(C4_UNLIKELY(sz > buf.len))
buf = buf.first(0); // ensure no more calls
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
{
size_t szsep = dump<dumpfn>(buf, sep);
if(C4_UNLIKELY(szsep > buf.len))
buf = buf.first(0); // ensure no more calls
sz = sz > szsep ? sz : szsep;
}
size_t size_for_more = catsep_dump<dumpfn>(buf, sep, more...);
return size_for_more > sz ? size_for_more : sz;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
template<DumperPfn dumpfn, class Arg>
void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results->write_arg(currarg)))
{
size_t sz = dump<dumpfn>(*buf, a);
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
if(C4_LIKELY(sz <= buf->len))
results->lastok = currarg;
else
buf->len = 0;
}
}
template<class DumperFn, class Arg>
void catsep_dump_resume_(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results->write_arg(currarg)))
{
size_t sz = dump(dumpfn, *buf, a);
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
if(C4_LIKELY(sz <= buf->len))
results->lastok = currarg;
else
buf->len = 0;
}
}
template<DumperPfn dumpfn, class Sep, class Arg>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a)
{
detail::catsep_dump_resume_<dumpfn>(currarg, results, buf, a);
}
template<class DumperFn, class Sep, class Arg>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a)
{
detail::catsep_dump_resume_(currarg, dumpfn, results, buf, a);
}
template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume_<dumpfn>(currarg , results, buf, a);
detail::catsep_dump_resume_<dumpfn>(currarg + 1u, results, buf, sep);
detail::catsep_dump_resume <dumpfn>(currarg + 2u, results, buf, sep, more...);
}
template<class DumperFn, class Sep, class Arg, class... Args>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume_(currarg , dumpfn, results, buf, a);
detail::catsep_dump_resume_(currarg + 1u, dumpfn, results, buf, sep);
detail::catsep_dump_resume (currarg + 2u, dumpfn, results, buf, sep, more...);
}
} // namespace detail
/// @endcond
template<DumperPfn dumpfn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
return results;
}
template<class DumperFn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
return results;
}
template<DumperPfn dumpfn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
DumpResults results;
detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
return results;
}
template<class DumperFn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
DumpResults results;
detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
return results;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** take the function pointer as a function argument */
template<class DumperFn>
C4_ALWAYS_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0 && fmt.len))
dumpfn(fmt);
return 0u;
}
/** take the function pointer as a function argument */
template<DumperPfn dumpfn>
C4_ALWAYS_INLINE size_t format_dump(substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
/** take the function pointer as a function argument */
template<class DumperFn, class Arg, class... Args>
size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
if(C4_LIKELY(buf.len > 0 && pos > 0))
dumpfn(fmt.first(pos)); // we can dump without using buf
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
pos = dump(dumpfn, buf, a);
if(C4_UNLIKELY(pos > buf.len))
buf.len = 0; // ensure no more calls to dump
size_t size_for_more = format_dump(dumpfn, buf, fmt, more...);
return size_for_more > pos ? size_for_more : pos;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn, class Arg, class... Args>
size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
if(C4_LIKELY(buf.len > 0 && pos > 0))
dumpfn(fmt.first(pos)); // we can dump without using buf
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
pos = dump<dumpfn>(buf, a);
if(C4_UNLIKELY(pos > buf.len))
buf.len = 0; // ensure no more calls to dump
size_t size_for_more = format_dump<dumpfn>(buf, fmt, more...);
return size_for_more > pos ? size_for_more : pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
template<DumperPfn dumpfn>
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0))
{
dumpfn(fmt);
results.lastok = currarg;
}
return results;
}
template<class DumperFn>
DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0))
{
dumpfn(fmt);
results.lastok = currarg;
}
return results;
}
template<DumperPfn dumpfn, class Arg, class... Args>
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we need to process the format even if we're not
// going to print the first arguments because we're resuming
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(results.write_arg(currarg)))
{
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt);
}
return results;
}
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt.first(pos));
}
}
fmt = fmt.sub(pos + 2);
if(C4_LIKELY(results.write_arg(currarg + 1)))
{
pos = dump<dumpfn>(buf, a);
results.bufsize = pos > results.bufsize ? pos : results.bufsize;
if(C4_LIKELY(pos <= buf.len))
results.lastok = currarg + 1;
else
buf.len = 0;
}
return detail::format_dump_resume<dumpfn>(currarg + 2u, results, buf, fmt, more...);
}
/// @endcond
template<class DumperFn, class Arg, class... Args>
DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we need to process the format even if we're not
// going to print the first arguments because we're resuming
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(results.write_arg(currarg)))
{
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt);
}
return results;
}
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt.first(pos));
}
}
fmt = fmt.sub(pos + 2);
if(C4_LIKELY(results.write_arg(currarg + 1)))
{
pos = dump(dumpfn, buf, a);
results.bufsize = pos > results.bufsize ? pos : results.bufsize;
if(C4_LIKELY(pos <= buf.len))
results.lastok = currarg + 1;
else
buf.len = 0;
}
return detail::format_dump_resume(currarg + 2u, dumpfn, results, buf, fmt, more...);
}
} // namespace detail
template<DumperPfn dumpfn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume<dumpfn>(0u, results, buf, fmt, more...);
}
template<class DumperFn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume(0u, dumpfn, results, buf, fmt, more...);
}
template<DumperPfn dumpfn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume<dumpfn>(0u, DumpResults{}, buf, fmt, more...);
}
template<class DumperFn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume(0u, dumpfn, DumpResults{}, buf, fmt, more...);
}
} // namespace c4
#endif /* C4_DUMP_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/dump.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/enum.hpp
// https://github.com/biojppm/c4core/src/c4/enum.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_ENUM_HPP_
#define _C4_ENUM_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
//included above:
//#include <string.h>
/** @file enum.hpp utilities for enums: convert to/from string
*/
namespace c4 {
//! taken from http://stackoverflow.com/questions/15586163/c11-type-trait-to-differentiate-between-enum-class-and-regular-enum
template<typename Enum>
using is_scoped_enum = std::integral_constant<bool, std::is_enum<Enum>::value && !std::is_convertible<Enum, int>::value>;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
typedef enum {
EOFFS_NONE = 0, ///< no offset
EOFFS_CLS = 1, ///< get the enum offset for the class name. @see eoffs_cls()
EOFFS_PFX = 2, ///< get the enum offset for the enum prefix. @see eoffs_pfx()
_EOFFS_LAST ///< reserved
} EnumOffsetType;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A simple (proxy) container for the value-name pairs of an enum type.
* Uses linear search for finds; this could be improved for time-critical
* code. */
template<class Enum>
class EnumSymbols
{
public:
struct Sym
{
Enum value;
const char *name;
bool cmp(const char *s) const;
bool cmp(const char *s, size_t len) const;
const char *name_offs(EnumOffsetType t) const;
};
using const_iterator = Sym const*;
public:
template<size_t N>
EnumSymbols(Sym const (&p)[N]) : m_symbols(p), m_num(N) {}
size_t size() const { return m_num; }
bool empty() const { return m_num == 0; }
Sym const* get(Enum v) const { auto p = find(v); C4_CHECK_MSG(p != nullptr, "could not find symbol=%zd", (std::ptrdiff_t)v); return p; }
Sym const* get(const char *s) const { auto p = find(s); C4_CHECK_MSG(p != nullptr, "could not find symbol \"%s\"", s); return p; }
Sym const* get(const char *s, size_t len) const { auto p = find(s, len); C4_CHECK_MSG(p != nullptr, "could not find symbol \"%.*s\"", len, s); return p; }
Sym const* find(Enum v) const;
Sym const* find(const char *s) const;
Sym const* find(const char *s, size_t len) const;
Sym const& operator[] (size_t i) const { C4_CHECK(i < m_num); return m_symbols[i]; }
Sym const* begin() const { return m_symbols; }
Sym const* end () const { return m_symbols + m_num; }
private:
Sym const* m_symbols;
size_t const m_num;
};
//-----------------------------------------------------------------------------
/** return an EnumSymbols object for the enum type T
*
* @warning SPECIALIZE! This needs to be specialized for each enum
* type. Failure to provide a specialization will cause a linker
* error. */
template<class Enum>
EnumSymbols<Enum> const esyms();
/** return the offset for an enum symbol class. For example,
* eoffs_cls<MyEnumClass>() would be 13=strlen("MyEnumClass::").
*
* With this function you can announce that the full prefix (including
* an eventual enclosing class or C++11 enum class) is of a certain
* length.
*
* @warning Needs to be specialized for each enum class type that
* wants to use this. When no specialization is given, will return
* 0. */
template<class Enum>
size_t eoffs_cls()
{
return 0;
}
/** return the offset for an enum symbol prefix. This includes
* eoffs_cls(). With this function you can announce that the full
* prefix (including an eventual enclosing class or C++11 enum class
* plus the string prefix) is of a certain length.
*
* @warning Needs to be specialized for each enum class type that
* wants to use this. When no specialization is given, will return
* 0. */
template<class Enum>
size_t eoffs_pfx()
{
return 0;
}
template<class Enum>
size_t eoffs(EnumOffsetType which)
{
switch(which)
{
case EOFFS_NONE:
return 0;
case EOFFS_CLS:
return eoffs_cls<Enum>();
case EOFFS_PFX:
{
size_t pfx = eoffs_pfx<Enum>();
return pfx > 0 ? pfx : eoffs_cls<Enum>();
}
default:
C4_ERROR("unknown offset type %d", (int)which);
return 0;
}
}
//-----------------------------------------------------------------------------
/** get the enum value corresponding to a c-string */
#ifdef __clang__
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
template<class Enum>
Enum str2e(const char* str)
{
auto pairs = esyms<Enum>();
auto *p = pairs.get(str);
C4_CHECK_MSG(p != nullptr, "no valid enum pair name for '%s'", str);
return p->value;
}
/** get the c-string corresponding to an enum value */
template<class Enum>
const char* e2str(Enum e)
{
auto es = esyms<Enum>();
auto *p = es.get(e);
C4_CHECK_MSG(p != nullptr, "no valid enum pair name");
return p->name;
}
/** like e2str(), but add an offset. */
template<class Enum>
const char* e2stroffs(Enum e, EnumOffsetType ot=EOFFS_PFX)
{
const char *s = e2str<Enum>(e) + eoffs<Enum>(ot);
return s;
}
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
//-----------------------------------------------------------------------------
/** Find a symbol by value. Returns nullptr when none is found */
template<class Enum>
typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(Enum v) const
{
for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p)
if(p->value == v)
return p;
return nullptr;
}
/** Find a symbol by name. Returns nullptr when none is found */
template<class Enum>
typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(const char *s) const
{
for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p)
if(p->cmp(s))
return p;
return nullptr;
}
/** Find a symbol by name. Returns nullptr when none is found */
template<class Enum>
typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(const char *s, size_t len) const
{
for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p)
if(p->cmp(s, len))
return p;
return nullptr;
}
//-----------------------------------------------------------------------------
template<class Enum>
bool EnumSymbols<Enum>::Sym::cmp(const char *s) const
{
if(strcmp(name, s) == 0)
return true;
for(int i = 1; i < _EOFFS_LAST; ++i)
{
auto o = eoffs<Enum>((EnumOffsetType)i);
if(o > 0)
if(strcmp(name + o, s) == 0)
return true;
}
return false;
}
template<class Enum>
bool EnumSymbols<Enum>::Sym::cmp(const char *s, size_t len) const
{
if(strncmp(name, s, len) == 0)
return true;
size_t nlen = 0;
for(int i = 1; i <_EOFFS_LAST; ++i)
{
auto o = eoffs<Enum>((EnumOffsetType)i);
if(o > 0)
{
if(!nlen)
{
nlen = strlen(name);
}
C4_ASSERT(o < nlen);
size_t rem = nlen - o;
auto m = len > rem ? len : rem;
if(len >= m && strncmp(name + o, s, m) == 0)
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
template<class Enum>
const char* EnumSymbols<Enum>::Sym::name_offs(EnumOffsetType t) const
{
C4_ASSERT(eoffs<Enum>(t) < strlen(name));
return name + eoffs<Enum>(t);
}
} // namespace c4
#endif // _C4_ENUM_HPP_
// (end https://github.com/biojppm/c4core/src/c4/enum.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/bitmask.hpp
// https://github.com/biojppm/c4core/src/c4/bitmask.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_BITMASK_HPP_
#define _C4_BITMASK_HPP_
/** @file bitmask.hpp bitmask utilities */
//included above:
//#include <cstring>
//included above:
//#include <type_traits>
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/enum.hpp
//#include "c4/enum.hpp"
#if !defined(C4_ENUM_HPP_) && !defined(_C4_ENUM_HPP_)
#error "amalgamate: file c4/enum.hpp must have been included at this point"
#endif /* C4_ENUM_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/format.hpp
//#include "c4/format.hpp"
#if !defined(C4_FORMAT_HPP_) && !defined(_C4_FORMAT_HPP_)
#error "amalgamate: file c4/format.hpp must have been included at this point"
#endif /* C4_FORMAT_HPP_ */
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4996) // 'strncpy', fopen, etc: This function or variable may be unsafe
#elif defined(__clang__)
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 8
# pragma GCC diagnostic ignored "-Wstringop-truncation"
# pragma GCC diagnostic ignored "-Wstringop-overflow"
# endif
#endif
namespace c4 {
//-----------------------------------------------------------------------------
/** write a bitmask to a stream, formatted as a string */
template<class Enum, class Stream>
Stream& bm2stream(Stream &s, typename std::underlying_type<Enum>::type bits, EnumOffsetType offst=EOFFS_PFX)
{
using I = typename std::underlying_type<Enum>::type;
bool written = false;
auto const& pairs = esyms<Enum>();
// write non null value
if(bits)
{
// do reverse iteration to give preference to composite enum symbols,
// which are likely to appear at the end of the enum sequence
for(size_t i = pairs.size() - 1; i != size_t(-1); --i)
{
auto p = pairs[i];
I b(static_cast<I>(p.value));
if(b && (bits & b) == b)
{
if(written) s << '|'; // append bit-or character
written = true;
s << p.name_offs(offst); // append bit string
bits &= ~b;
}
}
return s;
}
else
{
// write a null value
for(size_t i = pairs.size() - 1; i != size_t(-1); --i)
{
auto p = pairs[i];
I b(static_cast<I>(p.value));
if(b == 0)
{
s << p.name_offs(offst);
written = true;
break;
}
}
}
if(!written)
{
s << '0';
}
return s;
}
template<class Enum, class Stream>
typename std::enable_if<is_scoped_enum<Enum>::value, Stream&>::type
bm2stream(Stream &s, Enum value, EnumOffsetType offst=EOFFS_PFX)
{
using I = typename std::underlying_type<Enum>::type;
return bm2stream<Enum>(s, static_cast<I>(value), offst);
}
//-----------------------------------------------------------------------------
// some utility macros, undefed below
/// @cond dev
/* Execute `code` if the `num` of characters is available in the str
* buffer. This macro simplifies the code for bm2str().
* @todo improve performance by writing from the end and moving only once. */
#define _c4prependchars(code, num) \
if(str && (pos + num <= sz)) \
{ \
/* move the current string to the right */ \
memmove(str + num, str, pos); \
/* now write in the beginning of the string */ \
code; \
} \
else if(str && sz) \
{ \
C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \
(int)pos, (int)num, (int)sz); \
} \
pos += num
/* Execute `code` if the `num` of characters is available in the str
* buffer. This macro simplifies the code for bm2str(). */
#define _c4appendchars(code, num) \
if(str && (pos + num <= sz)) \
{ \
code; \
} \
else if(str && sz) \
{ \
C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \
(int)pos, (int)num, (int)sz); \
} \
pos += num
/// @endcond
/** convert a bitmask to string.
* return the number of characters written. To find the needed size,
* call first with str=nullptr and sz=0 */
template<class Enum>
size_t bm2str
(
typename std::underlying_type<Enum>::type bits,
char *str=nullptr,
size_t sz=0,
EnumOffsetType offst=EOFFS_PFX
)
{
using I = typename std::underlying_type<Enum>::type;
C4_ASSERT((str == nullptr) == (sz == 0));
auto syms = esyms<Enum>();
size_t pos = 0;
typename EnumSymbols<Enum>::Sym const* C4_RESTRICT zero = nullptr;
// do reverse iteration to give preference to composite enum symbols,
// which are likely to appear later in the enum sequence
for(size_t i = syms.size()-1; i != size_t(-1); --i)
{
auto const &C4_RESTRICT p = syms[i]; // do not copy, we are assigning to `zero`
I b = static_cast<I>(p.value);
if(b == 0)
{
zero = &p; // save this symbol for later
}
else if((bits & b) == b)
{
bits &= ~b;
// append bit-or character
if(pos > 0)
{
_c4prependchars(*str = '|', 1);
}
// append bit string
const char *pname = p.name_offs(offst);
size_t len = strlen(pname);
_c4prependchars(strncpy(str, pname, len), len);
}
}
C4_CHECK_MSG(bits == 0, "could not find all bits");
if(pos == 0) // make sure at least something is written
{
if(zero) // if we have a zero symbol, use that
{
const char *pname = zero->name_offs(offst);
size_t len = strlen(pname);
_c4prependchars(strncpy(str, pname, len), len);
}
else // otherwise just write an integer zero
{
_c4prependchars(*str = '0', 1);
}
}
_c4appendchars(str[pos] = '\0', 1);
return pos;
}
// cleanup!
#undef _c4appendchars
#undef _c4prependchars
/** scoped enums do not convert automatically to their underlying type,
* so this SFINAE overload will accept scoped enum symbols and cast them
* to the underlying type */
template<class Enum>
typename std::enable_if<is_scoped_enum<Enum>::value, size_t>::type
bm2str
(
Enum bits,
char *str=nullptr,
size_t sz=0,
EnumOffsetType offst=EOFFS_PFX
)
{
using I = typename std::underlying_type<Enum>::type;
return bm2str<Enum>(static_cast<I>(bits), str, sz, offst);
}
//-----------------------------------------------------------------------------
namespace detail {
#ifdef __clang__
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
template<class Enum>
typename std::underlying_type<Enum>::type str2bm_read_one(const char *str, size_t sz, bool alnum)
{
using I = typename std::underlying_type<Enum>::type;
auto pairs = esyms<Enum>();
if(alnum)
{
auto *p = pairs.find(str, sz);
C4_CHECK_MSG(p != nullptr, "no valid enum pair name for '%.*s'", (int)sz, str);
return static_cast<I>(p->value);
}
I tmp;
size_t len = uncat(csubstr(str, sz), tmp);
C4_CHECK_MSG(len != csubstr::npos, "could not read string as an integral type: '%.*s'", (int)sz, str);
return tmp;
}
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
} // namespace detail
/** convert a string to a bitmask */
template<class Enum>
typename std::underlying_type<Enum>::type str2bm(const char *str, size_t sz)
{
using I = typename std::underlying_type<Enum>::type;
I val = 0;
bool started = false;
bool alnum = false, num = false;
const char *f = nullptr, *pc = str;
for( ; pc < str+sz; ++pc)
{
const char c = *pc;
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
{
C4_CHECK(( ! num) || ((pc - f) == 1 && (c == 'x' || c == 'X'))); // accept hexadecimal numbers
if( ! started)
{
f = pc;
alnum = started = true;
}
}
else if(c >= '0' && c <= '9')
{
C4_CHECK( ! alnum);
if(!started)
{
f = pc;
num = started = true;
}
}
else if(c == ':' || c == ' ')
{
// skip this char
}
else if(c == '|' || c == '\0')
{
C4_ASSERT(num != alnum);
C4_ASSERT(pc >= f);
val |= detail::str2bm_read_one<Enum>(f, static_cast<size_t>(pc-f), alnum);
started = num = alnum = false;
if(c == '\0')
{
return val;
}
}
else
{
C4_ERROR("bad character '%c' in bitmask string", c);
}
}
if(f)
{
C4_ASSERT(num != alnum);
C4_ASSERT(pc >= f);
val |= detail::str2bm_read_one<Enum>(f, static_cast<size_t>(pc-f), alnum);
}
return val;
}
/** convert a string to a bitmask */
template<class Enum>
typename std::underlying_type<Enum>::type str2bm(const char *str)
{
return str2bm<Enum>(str, strlen(str));
}
} // namespace c4
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__)
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif // _C4_BITMASK_HPP_
// (end https://github.com/biojppm/c4core/src/c4/bitmask.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/span.hpp
// https://github.com/biojppm/c4core/src/c4/span.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_SPAN_HPP_
#define _C4_SPAN_HPP_
/** @file span.hpp Provides span classes. */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/config.hpp
//#include "c4/config.hpp"
#if !defined(C4_CONFIG_HPP_) && !defined(_C4_CONFIG_HPP_)
#error "amalgamate: file c4/config.hpp must have been included at this point"
#endif /* C4_CONFIG_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/szconv.hpp
//#include "c4/szconv.hpp"
#if !defined(C4_SZCONV_HPP_) && !defined(_C4_SZCONV_HPP_)
#error "amalgamate: file c4/szconv.hpp must have been included at this point"
#endif /* C4_SZCONV_HPP_ */
//included above:
//#include <algorithm>
namespace c4 {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a crtp base for implementing span classes
*
* A span is a non-owning range of elements contiguously stored in memory.
* Unlike STL's array_view, the span allows write-access to its members.
*
* To obtain subspans from a span, the following const member functions
* are available:
* - subspan(first, num)
* - range(first, last)
* - first(num)
* - last(num)
*
* A span can also be resized via the following non-const member functions:
* - resize(sz)
* - ltrim(num)
* - rtrim(num)
*
* @see span
* @see cspan
* @see spanrs
* @see cspanrs
* @see spanrsl
* @see cspanrsl
*/
template<class T, class I, class SpanImpl>
class span_crtp
{
// some utility defines, undefined at the end of this class
#define _c4this ((SpanImpl *)this)
#define _c4cthis ((SpanImpl const*)this)
#define _c4ptr ((SpanImpl *)this)->m_ptr
#define _c4cptr ((SpanImpl const*)this)->m_ptr
#define _c4sz ((SpanImpl *)this)->m_size
#define _c4csz ((SpanImpl const*)this)->m_size
public:
_c4_DEFINE_ARRAY_TYPES(T, I);
public:
C4_ALWAYS_INLINE constexpr I value_size() const noexcept { return sizeof(T); }
C4_ALWAYS_INLINE constexpr I elm_size () const noexcept { return sizeof(T); }
C4_ALWAYS_INLINE constexpr I type_size () const noexcept { return sizeof(T); }
C4_ALWAYS_INLINE I byte_size () const noexcept { return _c4csz*sizeof(T); }
C4_ALWAYS_INLINE bool empty() const noexcept { return _c4csz == 0; }
C4_ALWAYS_INLINE I size() const noexcept { return _c4csz; }
//C4_ALWAYS_INLINE I capacity() const noexcept { return _c4sz; } // this must be defined by impl classes
C4_ALWAYS_INLINE void clear() noexcept { _c4sz = 0; }
C4_ALWAYS_INLINE T * data() noexcept { return _c4ptr; }
C4_ALWAYS_INLINE T const* data() const noexcept { return _c4cptr; }
C4_ALWAYS_INLINE iterator begin() noexcept { return _c4ptr; }
C4_ALWAYS_INLINE const_iterator begin() const noexcept { return _c4cptr; }
C4_ALWAYS_INLINE const_iterator cbegin() const noexcept { return _c4cptr; }
C4_ALWAYS_INLINE iterator end() noexcept { return _c4ptr + _c4sz; }
C4_ALWAYS_INLINE const_iterator end() const noexcept { return _c4cptr + _c4csz; }
C4_ALWAYS_INLINE const_iterator cend() const noexcept { return _c4cptr + _c4csz; }
C4_ALWAYS_INLINE reverse_iterator rbegin() noexcept { return reverse_iterator(_c4ptr + _c4sz); }
C4_ALWAYS_INLINE const_reverse_iterator rbegin() const noexcept { return reverse_iterator(_c4cptr + _c4sz); }
C4_ALWAYS_INLINE const_reverse_iterator crbegin() const noexcept { return reverse_iterator(_c4cptr + _c4sz); }
C4_ALWAYS_INLINE reverse_iterator rend() noexcept { return const_reverse_iterator(_c4ptr); }
C4_ALWAYS_INLINE const_reverse_iterator rend() const noexcept { return const_reverse_iterator(_c4cptr); }
C4_ALWAYS_INLINE const_reverse_iterator crend() const noexcept { return const_reverse_iterator(_c4cptr); }
C4_ALWAYS_INLINE T & front() C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4ptr [0]; }
C4_ALWAYS_INLINE T const& front() const C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4cptr[0]; }
C4_ALWAYS_INLINE T & back() C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4ptr [_c4sz - 1]; }
C4_ALWAYS_INLINE T const& back() const C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4cptr[_c4csz - 1]; }
C4_ALWAYS_INLINE T & operator[] (I i) C4_NOEXCEPT_X { C4_XASSERT(i >= 0 && i < _c4sz ); return _c4ptr [i]; }
C4_ALWAYS_INLINE T const& operator[] (I i) const C4_NOEXCEPT_X { C4_XASSERT(i >= 0 && i < _c4csz); return _c4cptr[i]; }
C4_ALWAYS_INLINE SpanImpl subspan(I first, I num) const C4_NOEXCEPT_X
{
C4_XASSERT((first >= 0 && first < _c4csz) || (first == _c4csz && num == 0));
C4_XASSERT((first + num >= 0) && (first + num <= _c4csz));
return _c4cthis->_select(_c4cptr + first, num);
}
C4_ALWAYS_INLINE SpanImpl subspan(I first) const C4_NOEXCEPT_X ///< goes up until the end of the span
{
C4_XASSERT(first >= 0 && first <= _c4csz);
return _c4cthis->_select(_c4cptr + first, _c4csz - first);
}
C4_ALWAYS_INLINE SpanImpl range(I first, I last) const C4_NOEXCEPT_X ///< last element is NOT included
{
C4_XASSERT(((first >= 0) && (first < _c4csz)) || (first == _c4csz && first == last));
C4_XASSERT((last >= 0) && (last <= _c4csz));
C4_XASSERT(last >= first);
return _c4cthis->_select(_c4cptr + first, last - first);
}
C4_ALWAYS_INLINE SpanImpl range(I first) const C4_NOEXCEPT_X ///< goes up until the end of the span
{
C4_XASSERT(((first >= 0) && (first <= _c4csz)));
return _c4cthis->_select(_c4cptr + first, _c4csz - first);
}
C4_ALWAYS_INLINE SpanImpl first(I num) const C4_NOEXCEPT_X ///< get the first num elements, starting at 0
{
C4_XASSERT((num >= 0) && (num <= _c4csz));
return _c4cthis->_select(_c4cptr, num);
}
C4_ALWAYS_INLINE SpanImpl last(I num) const C4_NOEXCEPT_X ///< get the last num elements, starting at size()-num
{
C4_XASSERT((num >= 0) && (num <= _c4csz));
return _c4cthis->_select(_c4cptr + _c4csz - num, num);
}
bool is_subspan(span_crtp const& ss) const noexcept
{
if(_c4cptr == nullptr) return false;
auto *b = begin(), *e = end();
auto *ssb = ss.begin(), *sse = ss.end();
if(ssb >= b && sse <= e)
{
return true;
}
else
{
return false;
}
}
/** COMPLement Left: return the complement to the left of the beginning of the given subspan.
* If ss does not begin inside this, returns an empty substring. */
SpanImpl compll(span_crtp const& ss) const C4_NOEXCEPT_X
{
auto ssb = ss.begin();
auto b = begin();
auto e = end();
if(ssb >= b && ssb <= e)
{
return subspan(0, static_cast<size_t>(ssb - b));
}
else
{
return subspan(0, 0);
}
}
/** COMPLement Right: return the complement to the right of the end of the given subspan.
* If ss does not end inside this, returns an empty substring. */
SpanImpl complr(span_crtp const& ss) const C4_NOEXCEPT_X
{
auto sse = ss.end();
auto b = begin();
auto e = end();
if(sse >= b && sse <= e)
{
return subspan(static_cast<size_t>(sse - b), static_cast<size_t>(e - sse));
}
else
{
return subspan(0, 0);
}
}
C4_ALWAYS_INLINE bool same_span(span_crtp const& that) const noexcept
{
return size() == that.size() && data() == that.data();
}
template<class I2, class Impl2>
C4_ALWAYS_INLINE bool same_span(span_crtp<T, I2, Impl2> const& that) const C4_NOEXCEPT_X
{
I tsz = szconv<I>(that.size()); // x-asserts that the size does not overflow
return size() == tsz && data() == that.data();
}
#undef _c4this
#undef _c4cthis
#undef _c4ptr
#undef _c4cptr
#undef _c4sz
#undef _c4csz
};
//-----------------------------------------------------------------------------
template<class T, class Il, class Ir, class _Impll, class _Implr>
inline constexpr bool operator==
(
span_crtp<T, Il, _Impll> const& l,
span_crtp<T, Ir, _Implr> const& r
)
{
#if C4_CPP >= 14
return std::equal(l.begin(), l.end(), r.begin(), r.end());
#else
return l.same_span(r) || std::equal(l.begin(), l.end(), r.begin());
#endif
}
template<class T, class Il, class Ir, class _Impll, class _Implr>
inline constexpr bool operator!=
(
span_crtp<T, Il, _Impll> const& l,
span_crtp<T, Ir, _Implr> const& r
)
{
return ! (l == r);
}
//-----------------------------------------------------------------------------
template<class T, class Il, class Ir, class _Impll, class _Implr>
inline constexpr bool operator<
(
span_crtp<T, Il, _Impll> const& l,
span_crtp<T, Ir, _Implr> const& r
)
{
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
}
template<class T, class Il, class Ir, class _Impll, class _Implr>
inline constexpr bool operator<=
(
span_crtp<T, Il, _Impll> const& l,
span_crtp<T, Ir, _Implr> const& r
)
{
return ! (l > r);
}
//-----------------------------------------------------------------------------
template<class T, class Il, class Ir, class _Impll, class _Implr>
inline constexpr bool operator>
(
span_crtp<T, Il, _Impll> const& l,
span_crtp<T, Ir, _Implr> const& r
)
{
return r < l;
}
//-----------------------------------------------------------------------------
template<class T, class Il, class Ir, class _Impll, class _Implr>
inline constexpr bool operator>=
(
span_crtp<T, Il, _Impll> const& l,
span_crtp<T, Ir, _Implr> const& r
)
{
return ! (l < r);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A non-owning span of elements contiguously stored in memory. */
template<class T, class I=C4_SIZE_TYPE>
class span : public span_crtp<T, I, span<T, I>>
{
friend class span_crtp<T, I, span<T, I>>;
T * C4_RESTRICT m_ptr;
I m_size;
C4_ALWAYS_INLINE span _select(T *p, I sz) const { return span(p, sz); }
public:
_c4_DEFINE_ARRAY_TYPES(T, I);
using NCT = typename std::remove_const<T>::type; //!< NCT=non const type
using CT = typename std::add_const<T>::type; //!< CT=const type
using const_type = span<CT, I>;
/// convert automatically to span of const T
operator span<CT, I> () const { span<CT, I> s(m_ptr, m_size); return s; }
public:
C4_ALWAYS_INLINE C4_CONSTEXPR14 span() noexcept : m_ptr{nullptr}, m_size{0} {}
span(span const&) = default;
span(span &&) = default;
span& operator= (span const&) = default;
span& operator= (span &&) = default;
public:
/** @name Construction and assignment from same type */
/** @{ */
template<size_t N> C4_ALWAYS_INLINE C4_CONSTEXPR14 span (T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N} {}
template<size_t N> C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 span(T *p, I sz) noexcept : m_ptr{p}, m_size{sz} {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 span (c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{&*il.begin()}, m_size{il.size()} {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = &*il.begin(); m_size = il.size(); }
/** @} */
public:
C4_ALWAYS_INLINE I capacity() const noexcept { return m_size; }
C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_size); m_size = sz; }
C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; }
C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; }
};
template<class T, class I=C4_SIZE_TYPE> using cspan = span<const T, I>;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A non-owning span resizeable up to a capacity. Subselection or resizing
* will keep the original provided it starts at begin(). If subselection or
* resizing change the pointer, then the original capacity information will
* be lost.
*
* Thus, resizing via resize() and ltrim() and subselecting via first()
* or any of subspan() or range() when starting from the beginning will keep
* the original capacity. OTOH, using last(), or any of subspan() or range()
* with an offset from the start will remove from capacity (shifting the
* pointer) by the corresponding offset. If this is undesired, then consider
* using spanrsl.
*
* @see spanrs for a span resizeable on the right
* @see spanrsl for a span resizeable on the right and left
*/
template<class T, class I=C4_SIZE_TYPE>
class spanrs : public span_crtp<T, I, spanrs<T, I>>
{
friend class span_crtp<T, I, spanrs<T, I>>;
T * C4_RESTRICT m_ptr;
I m_size;
I m_capacity;
C4_ALWAYS_INLINE spanrs _select(T *p, I sz) const noexcept
{
C4_ASSERT(p >= m_ptr);
size_t delta = static_cast<size_t>(p - m_ptr);
C4_ASSERT(m_capacity >= delta);
return spanrs(p, sz, static_cast<size_t>(m_capacity - delta));
}
public:
_c4_DEFINE_ARRAY_TYPES(T, I);
using NCT = typename std::remove_const<T>::type; //!< NCT=non const type
using CT = typename std::add_const<T>::type; //!< CT=const type
using const_type = spanrs<CT, I>;
/// convert automatically to span of T
C4_ALWAYS_INLINE operator span<T, I > () const noexcept { return span<T, I>(m_ptr, m_size); }
/// convert automatically to span of const T
//C4_ALWAYS_INLINE operator span<CT, I> () const noexcept { span<CT, I> s(m_ptr, m_size); return s; }
/// convert automatically to spanrs of const T
C4_ALWAYS_INLINE operator spanrs<CT, I> () const noexcept { spanrs<CT, I> s(m_ptr, m_size, m_capacity); return s; }
public:
C4_ALWAYS_INLINE spanrs() noexcept : m_ptr{nullptr}, m_size{0}, m_capacity{0} {}
spanrs(spanrs const&) = default;
spanrs(spanrs &&) = default;
spanrs& operator= (spanrs const&) = default;
spanrs& operator= (spanrs &&) = default;
public:
/** @name Construction and assignment from same type */
/** @{ */
C4_ALWAYS_INLINE spanrs(T *p, I sz) noexcept : m_ptr{p}, m_size{sz}, m_capacity{sz} {}
/** @warning will reset the capacity to sz */
C4_ALWAYS_INLINE void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; m_capacity = sz; }
C4_ALWAYS_INLINE spanrs(T *p, I sz, I cap) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap} {}
C4_ALWAYS_INLINE void assign(T *p, I sz, I cap) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; }
template<size_t N> C4_ALWAYS_INLINE spanrs(T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N}, m_capacity{N} {}
template<size_t N> C4_ALWAYS_INLINE void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; m_capacity = N; }
C4_ALWAYS_INLINE spanrs(c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{il.begin()}, m_size{il.size()}, m_capacity{il.size()} {}
C4_ALWAYS_INLINE void assign(c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = il.begin(); m_size = il.size(); m_capacity = il.size(); }
/** @} */
public:
C4_ALWAYS_INLINE I capacity() const noexcept { return m_capacity; }
C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_capacity); m_size = sz; }
C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; }
C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; m_capacity -= n; }
};
template<class T, class I=C4_SIZE_TYPE> using cspanrs = spanrs<const T, I>;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A non-owning span which always retains the capacity of the original
* range it was taken from (though it may loose its original size).
* The resizing methods resize(), ltrim(), rtrim() as well
* as the subselection methods subspan(), range(), first() and last() can be
* used at will without loosing the original capacity; the full capacity span
* can always be recovered by calling original().
*/
template<class T, class I=C4_SIZE_TYPE>
class spanrsl : public span_crtp<T, I, spanrsl<T, I>>
{
friend class span_crtp<T, I, spanrsl<T, I>>;
T *C4_RESTRICT m_ptr; ///< the current ptr. the original ptr is (m_ptr - m_offset).
I m_size; ///< the current size. the original size is unrecoverable.
I m_capacity; ///< the current capacity. the original capacity is (m_capacity + m_offset).
I m_offset; ///< the offset of the current m_ptr to the start of the original memory block.
C4_ALWAYS_INLINE spanrsl _select(T *p, I sz) const noexcept
{
C4_ASSERT(p >= m_ptr);
I delta = static_cast<I>(p - m_ptr);
C4_ASSERT(m_capacity >= delta);
return spanrsl(p, sz, static_cast<I>(m_capacity - delta), m_offset + delta);
}
public:
_c4_DEFINE_ARRAY_TYPES(T, I);
using NCT = typename std::remove_const<T>::type; //!< NCT=non const type
using CT = typename std::add_const<T>::type; //!< CT=const type
using const_type = spanrsl<CT, I>;
C4_ALWAYS_INLINE operator span<T, I> () const noexcept { return span<T, I>(m_ptr, m_size); }
C4_ALWAYS_INLINE operator spanrs<T, I> () const noexcept { return spanrs<T, I>(m_ptr, m_size, m_capacity); }
C4_ALWAYS_INLINE operator spanrsl<CT, I> () const noexcept { return spanrsl<CT, I>(m_ptr, m_size, m_capacity, m_offset); }
public:
C4_ALWAYS_INLINE spanrsl() noexcept : m_ptr{nullptr}, m_size{0}, m_capacity{0}, m_offset{0} {}
spanrsl(spanrsl const&) = default;
spanrsl(spanrsl &&) = default;
spanrsl& operator= (spanrsl const&) = default;
spanrsl& operator= (spanrsl &&) = default;
public:
C4_ALWAYS_INLINE spanrsl(T *p, I sz) noexcept : m_ptr{p}, m_size{sz}, m_capacity{sz}, m_offset{0} {}
C4_ALWAYS_INLINE void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; m_capacity = sz; m_offset = 0; }
C4_ALWAYS_INLINE spanrsl(T *p, I sz, I cap) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap}, m_offset{0} {}
C4_ALWAYS_INLINE void assign(T *p, I sz, I cap) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; m_offset = 0; }
C4_ALWAYS_INLINE spanrsl(T *p, I sz, I cap, I offs) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap}, m_offset{offs} {}
C4_ALWAYS_INLINE void assign(T *p, I sz, I cap, I offs) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; m_offset = offs; }
template<size_t N> C4_ALWAYS_INLINE spanrsl(T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N}, m_capacity{N}, m_offset{0} {}
template<size_t N> C4_ALWAYS_INLINE void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; m_capacity = N; m_offset = 0; }
C4_ALWAYS_INLINE spanrsl(c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{il.begin()}, m_size{il.size()}, m_capacity{il.size()}, m_offset{0} {}
C4_ALWAYS_INLINE void assign (c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = il.begin(); m_size = il.size(); m_capacity = il.size(); m_offset = 0; }
public:
C4_ALWAYS_INLINE I offset() const noexcept { return m_offset; }
C4_ALWAYS_INLINE I capacity() const noexcept { return m_capacity; }
C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_capacity); m_size = sz; }
C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; }
C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; m_offset += n; m_capacity -= n; }
/** recover the original span as an spanrsl */
C4_ALWAYS_INLINE spanrsl original() const
{
return spanrsl(m_ptr - m_offset, m_capacity + m_offset, m_capacity + m_offset, 0);
}
/** recover the original span as a different span type. Example: spanrs<...> orig = s.original<spanrs>(); */
template<template<class, class> class OtherSpanType>
C4_ALWAYS_INLINE OtherSpanType<T, I> original()
{
return OtherSpanType<T, I>(m_ptr - m_offset, m_capacity + m_offset);
}
};
template<class T, class I=C4_SIZE_TYPE> using cspanrsl = spanrsl<const T, I>;
} // namespace c4
#endif /* _C4_SPAN_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/span.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/type_name.hpp
// https://github.com/biojppm/c4core/src/c4/type_name.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_TYPENAME_HPP_
#define _C4_TYPENAME_HPP_
/** @file type_name.hpp compile-time type name */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/span.hpp
//#include "c4/span.hpp"
#if !defined(C4_SPAN_HPP_) && !defined(_C4_SPAN_HPP_)
#error "amalgamate: file c4/span.hpp must have been included at this point"
#endif /* C4_SPAN_HPP_ */
/// @cond dev
struct _c4t
{
const char *str;
size_t sz;
template<size_t N>
constexpr _c4t(const char (&s)[N]) : str(s), sz(N-1) {} // take off the \0
};
// this is a more abbreviated way of getting the type name
// (if we used span in the return type, the name would involve
// templates and would create longer type name strings,
// as well as larger differences between compilers)
template<class T>
C4_CONSTEXPR14 C4_ALWAYS_INLINE
_c4t _c4tn()
{
auto p = _c4t(C4_PRETTY_FUNC);
return p;
}
/// @endcond
namespace c4 {
/** compile-time type name
* @see http://stackoverflow.com/a/20170989/5875572 */
template<class T>
C4_CONSTEXPR14 cspan<char> type_name()
{
const _c4t p = _c4tn<T>();
#if (0) // _C4_THIS_IS_A_DEBUG_SCAFFOLD
for(size_t index = 0; index < p.sz; ++index)
{
printf(" %2c", p.str[index]);
}
printf("\n");
for(size_t index = 0; index < p.sz; ++index)
{
printf(" %2d", (int)index);
}
printf("\n");
#endif
#if defined(_MSC_VER)
# if defined(__clang__) // Visual Studio has the clang toolset
// example:
// ..........................xxx.
// _c4t __cdecl _c4tn() [T = int]
enum : size_t { tstart = 26, tend = 1};
# elif defined(C4_MSVC_2015) || defined(C4_MSVC_2017) || defined(C4_MSVC_2019) || defined(C4_MSVC_2022)
// Note: subtract 7 at the end because the function terminates with ">(void)" in VS2015+
cspan<char>::size_type tstart = 26, tend = 7;
const char *s = p.str + tstart; // look at the start
// we're not using strcmp() or memcmp() to spare the #include
// does it start with 'class '?
if(p.sz > 6 && s[0] == 'c' && s[1] == 'l' && s[2] == 'a' && s[3] == 's' && s[4] == 's' && s[5] == ' ')
{
tstart += 6;
}
// does it start with 'struct '?
else if(p.sz > 7 && s[0] == 's' && s[1] == 't' && s[2] == 'r' && s[3] == 'u' && s[4] == 'c' && s[5] == 't' && s[6] == ' ')
{
tstart += 7;
}
# else
C4_NOT_IMPLEMENTED();
# endif
#elif defined(__ICC)
// example:
// ........................xxx.
// "_c4t _c4tn() [with T = int]"
enum : size_t { tstart = 23, tend = 1};
#elif defined(__clang__)
// example:
// ...................xxx.
// "_c4t _c4tn() [T = int]"
enum : size_t { tstart = 18, tend = 1};
#elif defined(__GNUC__)
#if __GNUC__ >= 7 && C4_CPP >= 14
// example:
// ..................................xxx.
// "constexpr _c4t _c4tn() [with T = int]"
enum : size_t { tstart = 33, tend = 1 };
#else
// example:
// ........................xxx.
// "_c4t _c4tn() [with T = int]"
enum : size_t { tstart = 23, tend = 1 };
#endif
#else
C4_NOT_IMPLEMENTED();
#endif
cspan<char> o(p.str + tstart, p.sz - tstart - tend);
return o;
}
/** compile-time type name
* @overload */
template<class T>
C4_CONSTEXPR14 C4_ALWAYS_INLINE cspan<char> type_name(T const&)
{
return type_name<T>();
}
} // namespace c4
#endif //_C4_TYPENAME_HPP_
// (end https://github.com/biojppm/c4core/src/c4/type_name.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/base64.hpp
// https://github.com/biojppm/c4core/src/c4/base64.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_BASE64_HPP_
#define _C4_BASE64_HPP_
/** @file base64.hpp encoding/decoding for base64.
* @see https://en.wikipedia.org/wiki/Base64
* @see https://www.base64encode.org/
* */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/charconv.hpp
//#include "c4/charconv.hpp"
#if !defined(C4_CHARCONV_HPP_) && !defined(_C4_CHARCONV_HPP_)
#error "amalgamate: file c4/charconv.hpp must have been included at this point"
#endif /* C4_CHARCONV_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/blob.hpp
//#include "c4/blob.hpp"
#if !defined(C4_BLOB_HPP_) && !defined(_C4_BLOB_HPP_)
#error "amalgamate: file c4/blob.hpp must have been included at this point"
#endif /* C4_BLOB_HPP_ */
namespace c4 {
/** check that the given buffer is a valid base64 encoding
* @see https://en.wikipedia.org/wiki/Base64 */
bool base64_valid(csubstr encoded);
/** base64-encode binary data.
* @param encoded [out] output buffer for encoded data
* @param data [in] the input buffer with the binary data
* @return the number of bytes needed to return the output. No writes occur beyond the end of the output buffer.
* @see https://en.wikipedia.org/wiki/Base64 */
size_t base64_encode(substr encoded, cblob data);
/** decode the base64 encoding in the given buffer
* @param encoded [in] the encoded base64
* @param data [out] the output buffer
* @return the number of bytes needed to return the output.. No writes occur beyond the end of the output buffer.
* @see https://en.wikipedia.org/wiki/Base64 */
size_t base64_decode(csubstr encoded, blob data);
namespace fmt {
template<typename CharOrConstChar>
struct base64_wrapper_
{
blob_<CharOrConstChar> data;
base64_wrapper_() : data() {}
base64_wrapper_(blob_<CharOrConstChar> blob) : data(blob) {}
};
using const_base64_wrapper = base64_wrapper_<cbyte>;
using base64_wrapper = base64_wrapper_<byte>;
/** mark a variable to be written in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE const_base64_wrapper cbase64(Args const& C4_RESTRICT ...args)
{
return const_base64_wrapper(cblob(args...));
}
/** mark a csubstr to be written in base64 format */
C4_ALWAYS_INLINE const_base64_wrapper cbase64(csubstr s)
{
return const_base64_wrapper(cblob(s.str, s.len));
}
/** mark a variable to be written in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE const_base64_wrapper base64(Args const& C4_RESTRICT ...args)
{
return const_base64_wrapper(cblob(args...));
}
/** mark a csubstr to be written in base64 format */
C4_ALWAYS_INLINE const_base64_wrapper base64(csubstr s)
{
return const_base64_wrapper(cblob(s.str, s.len));
}
/** mark a variable to be read in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE base64_wrapper base64(Args &... args)
{
return base64_wrapper(blob(args...));
}
/** mark a variable to be read in base64 format */
C4_ALWAYS_INLINE base64_wrapper base64(substr s)
{
return base64_wrapper(blob(s.str, s.len));
}
} // namespace fmt
/** write a variable in base64 format */
inline size_t to_chars(substr buf, fmt::const_base64_wrapper b)
{
return base64_encode(buf, b.data);
}
/** read a variable in base64 format */
inline size_t from_chars(csubstr buf, fmt::base64_wrapper *b)
{
return base64_decode(buf, b->data);
}
} // namespace c4
#endif /* _C4_BASE64_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/base64.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/std/string.hpp
// https://github.com/biojppm/c4core/src/c4/std/string.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_STD_STRING_HPP_
#define _C4_STD_STRING_HPP_
/** @file string.hpp */
#ifndef C4CORE_SINGLE_HEADER
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr.hpp
//#include "c4/substr.hpp"
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
#endif
//included above:
//#include <string>
namespace c4 {
//-----------------------------------------------------------------------------
/** get a writeable view to an existing std::string */
inline c4::substr to_substr(std::string &s)
{
char* data = ! s.empty() ? &s[0] : nullptr;
return c4::substr(data, s.size());
}
/** get a readonly view to an existing std::string */
inline c4::csubstr to_csubstr(std::string const& s)
{
const char* data = ! s.empty() ? &s[0] : nullptr;
return c4::csubstr(data, s.size());
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) == 0; }
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) != 0; }
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) >= 0; }
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) > 0; }
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) <= 0; }
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) < 0; }
C4_ALWAYS_INLINE bool operator== (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) == 0; }
C4_ALWAYS_INLINE bool operator!= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) != 0; }
C4_ALWAYS_INLINE bool operator>= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) <= 0; }
C4_ALWAYS_INLINE bool operator> (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) < 0; }
C4_ALWAYS_INLINE bool operator<= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) >= 0; }
C4_ALWAYS_INLINE bool operator< (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) > 0; }
//-----------------------------------------------------------------------------
/** copy an std::string to a writeable string view */
inline size_t to_chars(c4::substr buf, std::string const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
memcpy(buf.str, s.data(), len);
return s.size(); // return the number of needed chars
}
/** copy a string view to an existing std::string */
inline bool from_chars(c4::csubstr buf, std::string * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
memcpy(&(*s)[0], buf.str, buf.len);
return true;
}
} // namespace c4
#endif // _C4_STD_STRING_HPP_
// (end https://github.com/biojppm/c4core/src/c4/std/string.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/std/vector.hpp
// https://github.com/biojppm/c4core/src/c4/std/vector.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_STD_VECTOR_HPP_
#define _C4_STD_VECTOR_HPP_
/** @file vector.hpp provides conversion and comparison facilities
* from/between std::vector<char> to c4::substr and c4::csubstr.
* @todo add to_span() and friends
*/
#ifndef C4CORE_SINGLE_HEADER
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/substr.hpp
//#include "c4/substr.hpp"
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
#endif
#include <vector>
namespace c4 {
//-----------------------------------------------------------------------------
/** get a substr (writeable string view) of an existing std::vector<char> */
template<class Alloc>
c4::substr to_substr(std::vector<char, Alloc> &vec)
{
char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
return c4::substr(data, vec.size());
}
/** get a csubstr (read-only string) view of an existing std::vector<char> */
template<class Alloc>
c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec)
{
const char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
return c4::csubstr(data, vec.size());
}
//-----------------------------------------------------------------------------
// comparisons between substrings and std::vector<char>
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss != to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss == to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss >= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss > to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss <= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss < to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss != to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss == to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss <= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss < to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss >= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss > to_csubstr(s); }
//-----------------------------------------------------------------------------
/** copy a std::vector<char> to a writeable string view */
template<class Alloc>
inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
memcpy(buf.str, s.data(), len);
return s.size(); // return the number of needed chars
}
/** copy a string view to an existing std::vector<char> */
template<class Alloc>
inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
memcpy(&(*s)[0], buf.str, buf.len);
return true;
}
} // namespace c4
#endif // _C4_STD_VECTOR_HPP_
// (end https://github.com/biojppm/c4core/src/c4/std/vector.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/std/tuple.hpp
// https://github.com/biojppm/c4core/src/c4/std/tuple.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_STD_TUPLE_HPP_
#define _C4_STD_TUPLE_HPP_
/** @file tuple.hpp */
#ifndef C4CORE_SINGLE_HEADER
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/format.hpp
//#include "c4/format.hpp"
#if !defined(C4_FORMAT_HPP_) && !defined(_C4_FORMAT_HPP_)
#error "amalgamate: file c4/format.hpp must have been included at this point"
#endif /* C4_FORMAT_HPP_ */
#endif
#include <tuple>
/** this is a work in progress */
#undef C4_TUPLE_TO_CHARS
namespace c4 {
#ifdef C4_TUPLE_TO_CHARS
namespace detail {
template< size_t Curr, class... Types >
struct tuple_helper
{
static size_t do_cat(substr buf, std::tuple< Types... > const& tp)
{
size_t num = to_chars(buf, std::get<Curr>(tp));
buf = buf.len >= num ? buf.sub(num) : substr{};
num += tuple_helper< Curr+1, Types... >::do_cat(buf, tp);
return num;
}
static size_t do_uncat(csubstr buf, std::tuple< Types... > & tp)
{
size_t num = from_str_trim(buf, &std::get<Curr>(tp));
if(num == csubstr::npos) return csubstr::npos;
buf = buf.len >= num ? buf.sub(num) : substr{};
num += tuple_helper< Curr+1, Types... >::do_uncat(buf, tp);
return num;
}
template< class Sep >
static size_t do_catsep_more(substr buf, Sep const& sep, std::tuple< Types... > const& tp)
{
size_t ret = to_chars(buf, sep), num = ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = to_chars(buf, std::get<Curr>(tp));
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = tuple_helper< Curr+1, Types... >::do_catsep_more(buf, sep, tp);
num += ret;
return num;
}
template< class Sep >
static size_t do_uncatsep_more(csubstr buf, Sep & sep, std::tuple< Types... > & tp)
{
size_t ret = from_str_trim(buf, &sep), num = ret;
if(ret == csubstr::npos) return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = from_str_trim(buf, &std::get<Curr>(tp));
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = tuple_helper< Curr+1, Types... >::do_uncatsep_more(buf, sep, tp);
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
return num;
}
static size_t do_format(substr buf, csubstr fmt, std::tuple< Types... > const& tp)
{
auto pos = fmt.find("{}");
if(pos != csubstr::npos)
{
size_t num = to_chars(buf, fmt.sub(0, pos));
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = to_chars(buf, std::get<Curr>(tp));
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = tuple_helper< Curr+1, Types... >::do_format(buf, fmt.sub(pos + 2), tp);
out += num;
return out;
}
else
{
return format(buf, fmt);
}
}
static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp)
{
auto pos = fmt.find("{}");
if(pos != csubstr::npos)
{
size_t num = pos;
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = from_str_trim(buf, &std::get<Curr>(tp));
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = tuple_helper< Curr+1, Types... >::do_unformat(buf, fmt.sub(pos + 2), tp);
out += num;
return out;
}
else
{
return tuple_helper< sizeof...(Types), Types... >::do_unformat(buf, fmt, tp);
}
}
};
/** @todo VS compilation fails for this class */
template< class... Types >
struct tuple_helper< sizeof...(Types), Types... >
{
static size_t do_cat(substr /*buf*/, std::tuple<Types...> const& /*tp*/) { return 0; }
static size_t do_uncat(csubstr /*buf*/, std::tuple<Types...> & /*tp*/) { return 0; }
template< class Sep > static size_t do_catsep_more(substr /*buf*/, Sep const& /*sep*/, std::tuple<Types...> const& /*tp*/) { return 0; }
template< class Sep > static size_t do_uncatsep_more(csubstr /*buf*/, Sep & /*sep*/, std::tuple<Types...> & /*tp*/) { return 0; }
static size_t do_format(substr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/)
{
return to_chars(buf, fmt);
}
static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/)
{
return 0;
}
};
} // namespace detail
template< class... Types >
inline size_t cat(substr buf, std::tuple< Types... > const& tp)
{
return detail::tuple_helper< 0, Types... >::do_cat(buf, tp);
}
template< class... Types >
inline size_t uncat(csubstr buf, std::tuple< Types... > & tp)
{
return detail::tuple_helper< 0, Types... >::do_uncat(buf, tp);
}
template< class Sep, class... Types >
inline size_t catsep(substr buf, Sep const& sep, std::tuple< Types... > const& tp)
{
size_t num = to_chars(buf, std::cref(std::get<0>(tp)));
buf = buf.len >= num ? buf.sub(num) : substr{};
num += detail::tuple_helper< 1, Types... >::do_catsep_more(buf, sep, tp);
return num;
}
template< class Sep, class... Types >
inline size_t uncatsep(csubstr buf, Sep & sep, std::tuple< Types... > & tp)
{
size_t ret = from_str_trim(buf, &std::get<0>(tp)), num = ret;
if(ret == csubstr::npos) return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = detail::tuple_helper< 1, Types... >::do_uncatsep_more(buf, sep, tp);
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
return num;
}
template< class... Types >
inline size_t format(substr buf, csubstr fmt, std::tuple< Types... > const& tp)
{
return detail::tuple_helper< 0, Types... >::do_format(buf, fmt, tp);
}
template< class... Types >
inline size_t unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp)
{
return detail::tuple_helper< 0, Types... >::do_unformat(buf, fmt, tp);
}
#endif // C4_TUPLE_TO_CHARS
} // namespace c4
#endif /* _C4_STD_TUPLE_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/std/tuple.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/ext/rng/rng.hpp
// https://github.com/biojppm/c4core/src/c4/ext/rng/rng.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
/* Copyright (c) 2018 Arvid Gerstmann.
*
* https://arvid.io/2018/07/02/better-cxx-prng/
*
* This code is licensed under MIT license. */
#ifndef AG_RANDOM_H
#define AG_RANDOM_H
//included above:
//#include <stdint.h>
#include <random>
namespace c4 {
namespace rng {
class splitmix
{
public:
using result_type = uint32_t;
static constexpr result_type (min)() { return 0; }
static constexpr result_type (max)() { return UINT32_MAX; }
friend bool operator==(splitmix const &, splitmix const &);
friend bool operator!=(splitmix const &, splitmix const &);
splitmix() : m_seed(1) {}
explicit splitmix(std::random_device &rd)
{
seed(rd);
}
void seed(std::random_device &rd)
{
m_seed = uint64_t(rd()) << 31 | uint64_t(rd());
}
result_type operator()()
{
uint64_t z = (m_seed += UINT64_C(0x9E3779B97F4A7C15));
z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB);
return result_type((z ^ (z >> 31)) >> 31);
}
void discard(unsigned long long n)
{
for (unsigned long long i = 0; i < n; ++i)
operator()();
}
private:
uint64_t m_seed;
};
inline bool operator==(splitmix const &lhs, splitmix const &rhs)
{
return lhs.m_seed == rhs.m_seed;
}
inline bool operator!=(splitmix const &lhs, splitmix const &rhs)
{
return lhs.m_seed != rhs.m_seed;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class xorshift
{
public:
using result_type = uint32_t;
static constexpr result_type (min)() { return 0; }
static constexpr result_type (max)() { return UINT32_MAX; }
friend bool operator==(xorshift const &, xorshift const &);
friend bool operator!=(xorshift const &, xorshift const &);
xorshift() : m_seed(0xc1f651c67c62c6e0ull) {}
explicit xorshift(std::random_device &rd)
{
seed(rd);
}
void seed(std::random_device &rd)
{
m_seed = uint64_t(rd()) << 31 | uint64_t(rd());
}
result_type operator()()
{
uint64_t result = m_seed * 0xd989bcacc137dcd5ull;
m_seed ^= m_seed >> 11;
m_seed ^= m_seed << 31;
m_seed ^= m_seed >> 18;
return uint32_t(result >> 32ull);
}
void discard(unsigned long long n)
{
for (unsigned long long i = 0; i < n; ++i)
operator()();
}
private:
uint64_t m_seed;
};
inline bool operator==(xorshift const &lhs, xorshift const &rhs)
{
return lhs.m_seed == rhs.m_seed;
}
inline bool operator!=(xorshift const &lhs, xorshift const &rhs)
{
return lhs.m_seed != rhs.m_seed;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class pcg
{
public:
using result_type = uint32_t;
static constexpr result_type (min)() { return 0; }
static constexpr result_type (max)() { return UINT32_MAX; }
friend bool operator==(pcg const &, pcg const &);
friend bool operator!=(pcg const &, pcg const &);
pcg()
: m_state(0x853c49e6748fea9bULL)
, m_inc(0xda3e39cb94b95bdbULL)
{}
explicit pcg(std::random_device &rd)
{
seed(rd);
}
void seed(std::random_device &rd)
{
uint64_t s0 = uint64_t(rd()) << 31 | uint64_t(rd());
uint64_t s1 = uint64_t(rd()) << 31 | uint64_t(rd());
m_state = 0;
m_inc = (s1 << 1) | 1;
(void)operator()();
m_state += s0;
(void)operator()();
}
result_type operator()()
{
uint64_t oldstate = m_state;
m_state = oldstate * 6364136223846793005ULL + m_inc;
uint32_t xorshifted = uint32_t(((oldstate >> 18u) ^ oldstate) >> 27u);
//int rot = oldstate >> 59u; // the original. error?
int64_t rot = (int64_t)oldstate >> 59u; // error?
return (xorshifted >> rot) | (xorshifted << ((uint64_t)(-rot) & 31));
}
void discard(unsigned long long n)
{
for (unsigned long long i = 0; i < n; ++i)
operator()();
}
private:
uint64_t m_state;
uint64_t m_inc;
};
inline bool operator==(pcg const &lhs, pcg const &rhs)
{
return lhs.m_state == rhs.m_state
&& lhs.m_inc == rhs.m_inc;
}
inline bool operator!=(pcg const &lhs, pcg const &rhs)
{
return lhs.m_state != rhs.m_state
|| lhs.m_inc != rhs.m_inc;
}
} // namespace rng
} // namespace c4
#endif /* AG_RANDOM_H */
// (end https://github.com/biojppm/c4core/src/c4/ext/rng/rng.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/ext/sg14/inplace_function.h
// https://github.com/biojppm/c4core/src/c4/ext/sg14/inplace_function.h
//--------------------------------------------------------------------------------
//********************************************************************************
/*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _C4_EXT_SG14_INPLACE_FUNCTION_H_
#define _C4_EXT_SG14_INPLACE_FUNCTION_H_
//included above:
//#include <type_traits>
//included above:
//#include <utility>
#include <functional>
namespace stdext {
namespace inplace_function_detail {
static constexpr size_t InplaceFunctionDefaultCapacity = 32;
#if defined(__GLIBCXX__) // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61458
template<size_t Cap>
union aligned_storage_helper {
struct double1 { double a; };
struct double4 { double a[4]; };
template<class T> using maybe = typename std::conditional<(Cap >= sizeof(T)), T, char>::type;
char real_data[Cap];
maybe<int> a;
maybe<long> b;
maybe<long long> c;
maybe<void*> d;
maybe<void(*)()> e;
maybe<double1> f;
maybe<double4> g;
maybe<long double> h;
};
template<size_t Cap, size_t Align = std::alignment_of<aligned_storage_helper<Cap>>::value>
struct aligned_storage {
using type = typename std::aligned_storage<Cap, Align>::type;
};
#else
using std::aligned_storage;
#endif
template<typename T> struct wrapper
{
using type = T;
};
template<typename R, typename... Args> struct vtable
{
using storage_ptr_t = void*;
using invoke_ptr_t = R(*)(storage_ptr_t, Args&&...);
using process_ptr_t = void(*)(storage_ptr_t, storage_ptr_t);
using destructor_ptr_t = void(*)(storage_ptr_t);
const invoke_ptr_t invoke_ptr;
const process_ptr_t copy_ptr;
const process_ptr_t move_ptr;
const destructor_ptr_t destructor_ptr;
explicit constexpr vtable() noexcept :
invoke_ptr{ [](storage_ptr_t, Args&&...) -> R
{ throw std::bad_function_call(); }
},
copy_ptr{ [](storage_ptr_t, storage_ptr_t) noexcept -> void {} },
move_ptr{ [](storage_ptr_t, storage_ptr_t) noexcept -> void {} },
destructor_ptr{ [](storage_ptr_t) noexcept -> void {} }
{}
template<typename C> explicit constexpr vtable(wrapper<C>) noexcept :
invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args)
noexcept(noexcept(std::declval<C>()(args...))) -> R
{ return (*static_cast<C*>(storage_ptr))(
std::forward<Args>(args)...
); }
},
copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr)
noexcept(std::is_nothrow_copy_constructible<C>::value) -> void
{ new (dst_ptr) C{ (*static_cast<C*>(src_ptr)) }; }
},
move_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr)
noexcept(std::is_nothrow_move_constructible<C>::value) -> void
{ new (dst_ptr) C{ std::move(*static_cast<C*>(src_ptr)) }; }
},
destructor_ptr{ [](storage_ptr_t storage_ptr)
noexcept -> void
{ static_cast<C*>(storage_ptr)->~C(); }
}
{}
vtable(const vtable&) = delete;
vtable(vtable&&) = delete;
vtable& operator= (const vtable&) = delete;
vtable& operator= (vtable&&) = delete;
~vtable() = default;
};
template<size_t DstCap, size_t DstAlign, size_t SrcCap, size_t SrcAlign>
struct is_valid_inplace_dst : std::true_type
{
static_assert(DstCap >= SrcCap,
"Can't squeeze larger inplace_function into a smaller one"
);
static_assert(DstAlign % SrcAlign == 0,
"Incompatible inplace_function alignments"
);
};
} // namespace inplace_function_detail
template<
typename Signature,
size_t Capacity = inplace_function_detail::InplaceFunctionDefaultCapacity,
size_t Alignment = std::alignment_of<typename inplace_function_detail::aligned_storage<Capacity>::type>::value
>
class inplace_function; // unspecified
template<
typename R,
typename... Args,
size_t Capacity,
size_t Alignment
>
class inplace_function<R(Args...), Capacity, Alignment>
{
static const constexpr inplace_function_detail::vtable<R, Args...> empty_vtable{};
public:
using capacity = std::integral_constant<size_t, Capacity>;
using alignment = std::integral_constant<size_t, Alignment>;
using storage_t = typename inplace_function_detail::aligned_storage<Capacity, Alignment>::type;
using vtable_t = inplace_function_detail::vtable<R, Args...>;
using vtable_ptr_t = const vtable_t*;
template <typename, size_t, size_t> friend class inplace_function;
inplace_function() noexcept :
vtable_ptr_{std::addressof(empty_vtable)}
{}
template<
typename T,
typename C = typename std::decay<T>::type,
typename = typename std::enable_if<
!(std::is_same<C, inplace_function>::value
|| std::is_convertible<C, inplace_function>::value)
>::type
>
inplace_function(T&& closure)
{
#if __cplusplus >= 201703L
static_assert(std::is_invocable_r<R, C, Args...>::value,
"inplace_function cannot be constructed from non-callable type"
);
#endif
static_assert(std::is_copy_constructible<C>::value,
"inplace_function cannot be constructed from non-copyable type"
);
static_assert(sizeof(C) <= Capacity,
"inplace_function cannot be constructed from object with this (large) size"
);
static_assert(Alignment % std::alignment_of<C>::value == 0,
"inplace_function cannot be constructed from object with this (large) alignment"
);
static const vtable_t vt{inplace_function_detail::wrapper<C>{}};
vtable_ptr_ = std::addressof(vt);
new (std::addressof(storage_)) C{std::forward<T>(closure)};
}
inplace_function(std::nullptr_t) noexcept :
vtable_ptr_{std::addressof(empty_vtable)}
{}
inplace_function(const inplace_function& other) :
vtable_ptr_{other.vtable_ptr_}
{
vtable_ptr_->copy_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
inplace_function(inplace_function&& other) :
vtable_ptr_{other.vtable_ptr_}
{
vtable_ptr_->move_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
inplace_function& operator= (std::nullptr_t) noexcept
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
vtable_ptr_ = std::addressof(empty_vtable);
return *this;
}
inplace_function& operator= (const inplace_function& other)
{
if(this != std::addressof(other))
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
vtable_ptr_ = other.vtable_ptr_;
vtable_ptr_->copy_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
return *this;
}
inplace_function& operator= (inplace_function&& other)
{
if(this != std::addressof(other))
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
vtable_ptr_ = other.vtable_ptr_;
vtable_ptr_->move_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
return *this;
}
~inplace_function()
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
}
R operator() (Args... args) const
{
return vtable_ptr_->invoke_ptr(
std::addressof(storage_),
std::forward<Args>(args)...
);
}
constexpr bool operator== (std::nullptr_t) const noexcept
{
return !operator bool();
}
constexpr bool operator!= (std::nullptr_t) const noexcept
{
return operator bool();
}
explicit constexpr operator bool() const noexcept
{
return vtable_ptr_ != std::addressof(empty_vtable);
}
template<size_t Cap, size_t Align>
operator inplace_function<R(Args...), Cap, Align>() const&
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Cap, Align, Capacity, Alignment
>::value, "conversion not allowed");
return {vtable_ptr_, vtable_ptr_->copy_ptr, std::addressof(storage_)};
}
template<size_t Cap, size_t Align>
operator inplace_function<R(Args...), Cap, Align>() &&
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Cap, Align, Capacity, Alignment
>::value, "conversion not allowed");
return {vtable_ptr_, vtable_ptr_->move_ptr, std::addressof(storage_)};
}
void swap(inplace_function& other)
{
if (this == std::addressof(other)) return;
storage_t tmp;
vtable_ptr_->move_ptr(
std::addressof(tmp),
std::addressof(storage_)
);
vtable_ptr_->destructor_ptr(std::addressof(storage_));
other.vtable_ptr_->move_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
other.vtable_ptr_->destructor_ptr(std::addressof(other.storage_));
vtable_ptr_->move_ptr(
std::addressof(other.storage_),
std::addressof(tmp)
);
vtable_ptr_->destructor_ptr(std::addressof(tmp));
std::swap(vtable_ptr_, other.vtable_ptr_);
}
private:
vtable_ptr_t vtable_ptr_;
mutable storage_t storage_;
inplace_function(
vtable_ptr_t vtable_ptr,
typename vtable_t::process_ptr_t process_ptr,
typename vtable_t::storage_ptr_t storage_ptr
) : vtable_ptr_{vtable_ptr}
{
process_ptr(std::addressof(storage_), storage_ptr);
}
};
} // namespace stdext
#endif /* _C4_EXT_SG14_INPLACE_FUNCTION_H_ */
// (end https://github.com/biojppm/c4core/src/c4/ext/sg14/inplace_function.h)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/language.cpp
// https://github.com/biojppm/c4core/src/c4/language.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/language.hpp
//#include "c4/language.hpp"
#if !defined(C4_LANGUAGE_HPP_) && !defined(_C4_LANGUAGE_HPP_)
#error "amalgamate: file c4/language.hpp must have been included at this point"
#endif /* C4_LANGUAGE_HPP_ */
namespace c4 {
namespace detail {
#ifndef __GNUC__
void use_char_pointer(char const volatile* v)
{
C4_UNUSED(v);
}
#else
void foo() {} // to avoid empty file warning from the linker
#endif
} // namespace detail
} // namespace c4
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/language.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/format.cpp
// https://github.com/biojppm/c4core/src/c4/format.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/format.hpp
//#include "c4/format.hpp"
#if !defined(C4_FORMAT_HPP_) && !defined(_C4_FORMAT_HPP_)
#error "amalgamate: file c4/format.hpp must have been included at this point"
#endif /* C4_FORMAT_HPP_ */
//included above:
//#include <memory> // for std::align
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
namespace c4 {
size_t to_chars(substr buf, fmt::const_raw_wrapper r)
{
void * vptr = buf.str;
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r.alignment, r.len, vptr, space);
if(ptr == nullptr)
{
// if it was not possible to align, return a conservative estimate
// of the required space
return r.alignment + r.len;
}
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
size_t sz = static_cast<size_t>(ptr - buf.str) + r.len;
if(sz <= buf.len)
{
memcpy(ptr, r.buf, r.len);
}
return sz;
}
bool from_chars(csubstr buf, fmt::raw_wrapper *r)
{
void * vptr = (void*)buf.str;
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r->alignment, r->len, vptr, space);
C4_CHECK(ptr != nullptr);
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
//size_t dim = (ptr - buf.str) + r->len;
memcpy(r->buf, ptr, r->len);
return true;
}
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/format.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/memory_util.cpp
// https://github.com/biojppm/c4core/src/c4/memory_util.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/memory_util.hpp
//#include "c4/memory_util.hpp"
#if !defined(C4_MEMORY_UTIL_HPP_) && !defined(_C4_MEMORY_UTIL_HPP_)
#error "amalgamate: file c4/memory_util.hpp must have been included at this point"
#endif /* C4_MEMORY_UTIL_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
namespace c4 {
/** returns true if the memory overlaps */
bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb)
{
if(a < b)
{
if(size_t(a) + sza > size_t(b))
return true;
}
else if(a > b)
{
if(size_t(b) + szb > size_t(a))
return true;
}
else if(a == b)
{
if(sza != 0 && szb != 0)
return true;
}
return false;
}
/** Fills 'dest' with the first 'pattern_size' bytes at 'pattern', 'num_times'. */
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times)
{
if(C4_UNLIKELY(num_times == 0))
return;
C4_ASSERT( ! mem_overlaps(dest, pattern, num_times*pattern_size, pattern_size));
char *begin = (char*)dest;
char *end = begin + num_times * pattern_size;
// copy the pattern once
::memcpy(begin, pattern, pattern_size);
// now copy from dest to itself, doubling up every time
size_t n = pattern_size;
while(begin + 2*n < end)
{
::memcpy(begin + n, begin, n);
n <<= 1; // double n
}
// copy the missing part
if(begin + n < end)
{
::memcpy(begin + n, begin, static_cast<size_t>(end - (begin + n)));
}
}
} // namespace c4
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/memory_util.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/char_traits.cpp
// https://github.com/biojppm/c4core/src/c4/char_traits.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/char_traits.hpp
//#include "c4/char_traits.hpp"
#if !defined(C4_CHAR_TRAITS_HPP_) && !defined(_C4_CHAR_TRAITS_HPP_)
#error "amalgamate: file c4/char_traits.hpp must have been included at this point"
#endif /* C4_CHAR_TRAITS_HPP_ */
namespace c4 {
constexpr const char char_traits< char >::whitespace_chars[];
constexpr const size_t char_traits< char >::num_whitespace_chars;
constexpr const wchar_t char_traits< wchar_t >::whitespace_chars[];
constexpr const size_t char_traits< wchar_t >::num_whitespace_chars;
} // namespace c4
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/char_traits.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/memory_resource.cpp
// https://github.com/biojppm/c4core/src/c4/memory_resource.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/memory_resource.hpp
//#include "c4/memory_resource.hpp"
#if !defined(C4_MEMORY_RESOURCE_HPP_) && !defined(_C4_MEMORY_RESOURCE_HPP_)
#error "amalgamate: file c4/memory_resource.hpp must have been included at this point"
#endif /* C4_MEMORY_RESOURCE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/memory_util.hpp
//#include "c4/memory_util.hpp"
#if !defined(C4_MEMORY_UTIL_HPP_) && !defined(_C4_MEMORY_UTIL_HPP_)
#error "amalgamate: file c4/memory_util.hpp must have been included at this point"
#endif /* C4_MEMORY_UTIL_HPP_ */
//included above:
//#include <stdlib.h>
//included above:
//#include <string.h>
#if defined(C4_POSIX) || defined(C4_IOS) || defined(C4_MACOS) || defined(C4_ARM)
# include <errno.h>
#endif
#if defined(C4_ARM)
# include <malloc.h>
#endif
//included above:
//#include <memory>
namespace c4 {
namespace detail {
#ifdef C4_NO_ALLOC_DEFAULTS
aalloc_pfn s_aalloc = nullptr;
free_pfn s_afree = nullptr;
arealloc_pfn s_arealloc = nullptr;
#else
void afree_impl(void *ptr)
{
#if defined(C4_WIN) || defined(C4_XBOX)
::_aligned_free(ptr);
#else
::free(ptr);
#endif
}
void* aalloc_impl(size_t size, size_t alignment)
{
void *mem;
#if defined(C4_WIN) || defined(C4_XBOX)
mem = ::_aligned_malloc(size, alignment);
C4_CHECK(mem != nullptr || size == 0);
#elif defined(C4_ARM)
// https://stackoverflow.com/questions/53614538/undefined-reference-to-posix-memalign-in-arm-gcc
// https://electronics.stackexchange.com/questions/467382/e2-studio-undefined-reference-to-posix-memalign/467753
mem = memalign(alignment, size);
C4_CHECK(mem != nullptr || size == 0);
#elif defined(C4_POSIX) || defined(C4_IOS) || defined(C4_MACOS)
// NOTE: alignment needs to be sized in multiples of sizeof(void*)
size_t amult = alignment;
if(C4_UNLIKELY(alignment < sizeof(void*)))
{
amult = sizeof(void*);
}
int ret = ::posix_memalign(&mem, amult, size);
if(C4_UNLIKELY(ret))
{
if(ret == EINVAL)
{
C4_ERROR("The alignment argument %zu was not a power of two, "
"or was not a multiple of sizeof(void*)", alignment);
}
else if(ret == ENOMEM)
{
C4_ERROR("There was insufficient memory to fulfill the "
"allocation request of %zu bytes (alignment=%lu)", size, size);
}
return nullptr;
}
#else
C4_NOT_IMPLEMENTED_MSG("need to implement an aligned allocation for this platform");
#endif
C4_ASSERT_MSG((uintptr_t(mem) & (alignment-1)) == 0, "address %p is not aligned to %zu boundary", mem, alignment);
return mem;
}
void* arealloc_impl(void* ptr, size_t oldsz, size_t newsz, size_t alignment)
{
/** @todo make this more efficient
* @see https://stackoverflow.com/questions/9078259/does-realloc-keep-the-memory-alignment-of-posix-memalign
* @see look for qReallocAligned() in http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qmalloc.cpp
*/
void *tmp = aalloc(newsz, alignment);
size_t min = newsz < oldsz ? newsz : oldsz;
if(mem_overlaps(ptr, tmp, oldsz, newsz))
{
::memmove(tmp, ptr, min);
}
else
{
::memcpy(tmp, ptr, min);
}
afree(ptr);
return tmp;
}
aalloc_pfn s_aalloc = aalloc_impl;
afree_pfn s_afree = afree_impl;
arealloc_pfn s_arealloc = arealloc_impl;
#endif // C4_NO_ALLOC_DEFAULTS
} // namespace detail
aalloc_pfn get_aalloc()
{
return detail::s_aalloc;
}
void set_aalloc(aalloc_pfn fn)
{
detail::s_aalloc = fn;
}
afree_pfn get_afree()
{
return detail::s_afree;
}
void set_afree(afree_pfn fn)
{
detail::s_afree = fn;
}
arealloc_pfn get_arealloc()
{
return detail::s_arealloc;
}
void set_arealloc(arealloc_pfn fn)
{
detail::s_arealloc = fn;
}
void* aalloc(size_t sz, size_t alignment)
{
C4_ASSERT_MSG(c4::get_aalloc() != nullptr, "did you forget to call set_aalloc()?");
auto fn = c4::get_aalloc();
void* ptr = fn(sz, alignment);
return ptr;
}
void afree(void* ptr)
{
C4_ASSERT_MSG(c4::get_afree() != nullptr, "did you forget to call set_afree()?");
auto fn = c4::get_afree();
fn(ptr);
}
void* arealloc(void *ptr, size_t oldsz, size_t newsz, size_t alignment)
{
C4_ASSERT_MSG(c4::get_arealloc() != nullptr, "did you forget to call set_arealloc()?");
auto fn = c4::get_arealloc();
void* nptr = fn(ptr, oldsz, newsz, alignment);
return nptr;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void detail::_MemoryResourceSingleChunk::release()
{
if(m_mem && m_owner)
{
impl_type::deallocate(m_mem, m_size);
}
m_mem = nullptr;
m_size = 0;
m_owner = false;
m_pos = 0;
}
void detail::_MemoryResourceSingleChunk::acquire(size_t sz)
{
clear();
m_owner = true;
m_mem = (char*) impl_type::allocate(sz, alignof(max_align_t));
m_size = sz;
m_pos = 0;
}
void detail::_MemoryResourceSingleChunk::acquire(void *mem, size_t sz)
{
clear();
m_owner = false;
m_mem = (char*) mem;
m_size = sz;
m_pos = 0;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void* MemoryResourceLinear::do_allocate(size_t sz, size_t alignment, void *hint)
{
C4_UNUSED(hint);
if(sz == 0) return nullptr;
// make sure there's enough room to allocate
if(m_pos + sz > m_size)
{
C4_ERROR("out of memory");
return nullptr;
}
void *mem = m_mem + m_pos;
size_t space = m_size - m_pos;
if(std::align(alignment, sz, mem, space))
{
C4_ASSERT(m_pos <= m_size);
C4_ASSERT(m_size - m_pos >= space);
m_pos += (m_size - m_pos) - space;
m_pos += sz;
C4_ASSERT(m_pos <= m_size);
}
else
{
C4_ERROR("could not align memory");
mem = nullptr;
}
return mem;
}
void MemoryResourceLinear::do_deallocate(void* ptr, size_t sz, size_t alignment)
{
C4_UNUSED(ptr);
C4_UNUSED(sz);
C4_UNUSED(alignment);
// nothing to do!!
}
void* MemoryResourceLinear::do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment)
{
if(newsz == oldsz) return ptr;
// is ptr the most recently allocated (MRA) block?
char *cptr = (char*)ptr;
bool same_pos = (m_mem + m_pos == cptr + oldsz);
// no need to get more memory when shrinking
if(newsz < oldsz)
{
// if this is the MRA, we can safely shrink the position
if(same_pos)
{
m_pos -= oldsz - newsz;
}
return ptr;
}
// we're growing the block, and it fits in size
else if(same_pos && cptr + newsz <= m_mem + m_size)
{
// if this is the MRA, we can safely shrink the position
m_pos += newsz - oldsz;
return ptr;
}
// we're growing the block or it doesn't fit -
// delegate any of these situations to do_deallocate()
return do_allocate(newsz, alignment, ptr);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @todo add a free list allocator. A good candidate because of its
* small size is TLSF.
*
* @see https://github.com/mattconte/tlsf
*
* Comparisons:
*
* @see https://www.researchgate.net/publication/262375150_A_Comparative_Study_on_Memory_Allocators_in_Multicore_and_Multithreaded_Applications_-_SBESC_2011_-_Presentation_Slides
* @see http://webkit.sed.hu/blog/20100324/war-allocators-tlsf-action
* @see https://github.com/emeryberger/Malloc-Implementations/tree/master/allocators
*
* */
} // namespace c4
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifdef C4_REDEFINE_CPPNEW
#include <new>
void* operator new(size_t size)
{
auto *mr = ::c4::get_memory_resource();
return mr->allocate(size);
}
void operator delete(void *p) noexcept
{
C4_NEVER_REACH();
}
void operator delete(void *p, size_t size)
{
auto *mr = ::c4::get_memory_resource();
mr->deallocate(p, size);
}
void* operator new[](size_t size)
{
return operator new(size);
}
void operator delete[](void *p) noexcept
{
operator delete(p);
}
void operator delete[](void *p, size_t size)
{
operator delete(p, size);
}
void* operator new(size_t size, std::nothrow_t)
{
return operator new(size);
}
void operator delete(void *p, std::nothrow_t)
{
operator delete(p);
}
void operator delete(void *p, size_t size, std::nothrow_t)
{
operator delete(p, size);
}
void* operator new[](size_t size, std::nothrow_t)
{
return operator new(size);
}
void operator delete[](void *p, std::nothrow_t)
{
operator delete(p);
}
void operator delete[](void *p, size_t, std::nothrow_t)
{
operator delete(p, size);
}
#endif // C4_REDEFINE_CPPNEW
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/memory_resource.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/utf.cpp
// https://github.com/biojppm/c4core/src/c4/utf.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/utf.hpp
//#include "c4/utf.hpp"
#if !defined(C4_UTF_HPP_) && !defined(_C4_UTF_HPP_)
#error "amalgamate: file c4/utf.hpp must have been included at this point"
#endif /* C4_UTF_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/charconv.hpp
//#include "c4/charconv.hpp"
#if !defined(C4_CHARCONV_HPP_) && !defined(_C4_CHARCONV_HPP_)
#error "amalgamate: file c4/charconv.hpp must have been included at this point"
#endif /* C4_CHARCONV_HPP_ */
namespace c4 {
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code)
{
C4_UNUSED(buflen);
C4_ASSERT(buflen >= 4);
if (code <= UINT32_C(0x7f))
{
buf[0] = (uint8_t)code;
return 1u;
}
else if(code <= UINT32_C(0x7ff))
{
buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6)); /* 110xxxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | (code & UINT32_C(0x3f))); /* 10xxxxxx */
return 2u;
}
else if(code <= UINT32_C(0xffff))
{
buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12))); /* 1110xxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 3u;
}
else if(code <= UINT32_C(0x10ffff))
{
buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18))); /* 11110xxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 4u;
}
return 0;
}
substr decode_code_point(substr out, csubstr code_point)
{
C4_ASSERT(out.len >= 4);
C4_ASSERT(!code_point.begins_with("U+"));
C4_ASSERT(!code_point.begins_with("\\x"));
C4_ASSERT(!code_point.begins_with("\\u"));
C4_ASSERT(!code_point.begins_with("\\U"));
C4_ASSERT(!code_point.begins_with('0'));
C4_ASSERT(code_point.len <= 8);
uint32_t code_point_val;
C4_CHECK(read_hex(code_point, &code_point_val));
size_t ret = decode_code_point((uint8_t*)out.str, out.len, code_point_val);
C4_ASSERT(ret <= 4);
return out.first(ret);
}
} // namespace c4
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/utf.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/base64.cpp
// https://github.com/biojppm/c4core/src/c4/base64.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/base64.hpp
//#include "c4/base64.hpp"
#if !defined(C4_BASE64_HPP_) && !defined(_C4_BASE64_HPP_)
#error "amalgamate: file c4/base64.hpp must have been included at this point"
#endif /* C4_BASE64_HPP_ */
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wchar-subscripts" // array subscript is of type 'char'
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wchar-subscripts"
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
namespace c4 {
namespace detail {
constexpr static const char base64_sextet_to_char_[64] = {
/* 0/ 65*/ 'A', /* 1/ 66*/ 'B', /* 2/ 67*/ 'C', /* 3/ 68*/ 'D',
/* 4/ 69*/ 'E', /* 5/ 70*/ 'F', /* 6/ 71*/ 'G', /* 7/ 72*/ 'H',
/* 8/ 73*/ 'I', /* 9/ 74*/ 'J', /*10/ 75*/ 'K', /*11/ 74*/ 'L',
/*12/ 77*/ 'M', /*13/ 78*/ 'N', /*14/ 79*/ 'O', /*15/ 78*/ 'P',
/*16/ 81*/ 'Q', /*17/ 82*/ 'R', /*18/ 83*/ 'S', /*19/ 82*/ 'T',
/*20/ 85*/ 'U', /*21/ 86*/ 'V', /*22/ 87*/ 'W', /*23/ 88*/ 'X',
/*24/ 89*/ 'Y', /*25/ 90*/ 'Z', /*26/ 97*/ 'a', /*27/ 98*/ 'b',
/*28/ 99*/ 'c', /*29/100*/ 'd', /*30/101*/ 'e', /*31/102*/ 'f',
/*32/103*/ 'g', /*33/104*/ 'h', /*34/105*/ 'i', /*35/106*/ 'j',
/*36/107*/ 'k', /*37/108*/ 'l', /*38/109*/ 'm', /*39/110*/ 'n',
/*40/111*/ 'o', /*41/112*/ 'p', /*42/113*/ 'q', /*43/114*/ 'r',
/*44/115*/ 's', /*45/116*/ 't', /*46/117*/ 'u', /*47/118*/ 'v',
/*48/119*/ 'w', /*49/120*/ 'x', /*50/121*/ 'y', /*51/122*/ 'z',
/*52/ 48*/ '0', /*53/ 49*/ '1', /*54/ 50*/ '2', /*55/ 51*/ '3',
/*56/ 52*/ '4', /*57/ 53*/ '5', /*58/ 54*/ '6', /*59/ 55*/ '7',
/*60/ 56*/ '8', /*61/ 57*/ '9', /*62/ 43*/ '+', /*63/ 47*/ '/',
};
// https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html
constexpr static const char base64_char_to_sextet_[128] = {
#define __ char(-1) // undefined below
/* 0 NUL*/ __, /* 1 SOH*/ __, /* 2 STX*/ __, /* 3 ETX*/ __,
/* 4 EOT*/ __, /* 5 ENQ*/ __, /* 6 ACK*/ __, /* 7 BEL*/ __,
/* 8 BS */ __, /* 9 TAB*/ __, /* 10 LF */ __, /* 11 VT */ __,
/* 12 FF */ __, /* 13 CR */ __, /* 14 SO */ __, /* 15 SI */ __,
/* 16 DLE*/ __, /* 17 DC1*/ __, /* 18 DC2*/ __, /* 19 DC3*/ __,
/* 20 DC4*/ __, /* 21 NAK*/ __, /* 22 SYN*/ __, /* 23 ETB*/ __,
/* 24 CAN*/ __, /* 25 EM */ __, /* 26 SUB*/ __, /* 27 ESC*/ __,
/* 28 FS */ __, /* 29 GS */ __, /* 30 RS */ __, /* 31 US */ __,
/* 32 SPC*/ __, /* 33 ! */ __, /* 34 " */ __, /* 35 # */ __,
/* 36 $ */ __, /* 37 % */ __, /* 38 & */ __, /* 39 ' */ __,
/* 40 ( */ __, /* 41 ) */ __, /* 42 * */ __, /* 43 + */ 62,
/* 44 , */ __, /* 45 - */ __, /* 46 . */ __, /* 47 / */ 63,
/* 48 0 */ 52, /* 49 1 */ 53, /* 50 2 */ 54, /* 51 3 */ 55,
/* 52 4 */ 56, /* 53 5 */ 57, /* 54 6 */ 58, /* 55 7 */ 59,
/* 56 8 */ 60, /* 57 9 */ 61, /* 58 : */ __, /* 59 ; */ __,
/* 60 < */ __, /* 61 = */ __, /* 62 > */ __, /* 63 ? */ __,
/* 64 @ */ __, /* 65 A */ 0, /* 66 B */ 1, /* 67 C */ 2,
/* 68 D */ 3, /* 69 E */ 4, /* 70 F */ 5, /* 71 G */ 6,
/* 72 H */ 7, /* 73 I */ 8, /* 74 J */ 9, /* 75 K */ 10,
/* 76 L */ 11, /* 77 M */ 12, /* 78 N */ 13, /* 79 O */ 14,
/* 80 P */ 15, /* 81 Q */ 16, /* 82 R */ 17, /* 83 S */ 18,
/* 84 T */ 19, /* 85 U */ 20, /* 86 V */ 21, /* 87 W */ 22,
/* 88 X */ 23, /* 89 Y */ 24, /* 90 Z */ 25, /* 91 [ */ __,
/* 92 \ */ __, /* 93 ] */ __, /* 94 ^ */ __, /* 95 _ */ __,
/* 96 ` */ __, /* 97 a */ 26, /* 98 b */ 27, /* 99 c */ 28,
/*100 d */ 29, /*101 e */ 30, /*102 f */ 31, /*103 g */ 32,
/*104 h */ 33, /*105 i */ 34, /*106 j */ 35, /*107 k */ 36,
/*108 l */ 37, /*109 m */ 38, /*110 n */ 39, /*111 o */ 40,
/*112 p */ 41, /*113 q */ 42, /*114 r */ 43, /*115 s */ 44,
/*116 t */ 45, /*117 u */ 46, /*118 v */ 47, /*119 w */ 48,
/*120 x */ 49, /*121 y */ 50, /*122 z */ 51, /*123 { */ __,
/*124 | */ __, /*125 } */ __, /*126 ~ */ __, /*127 DEL*/ __,
#undef __
};
#ifndef NDEBUG
void base64_test_tables()
{
for(size_t i = 0; i < C4_COUNTOF(detail::base64_sextet_to_char_); ++i)
{
char s2c = base64_sextet_to_char_[i];
char c2s = base64_char_to_sextet_[(int)s2c];
C4_CHECK((size_t)c2s == i);
}
for(size_t i = 0; i < C4_COUNTOF(detail::base64_char_to_sextet_); ++i)
{
char c2s = base64_char_to_sextet_[i];
if(c2s == char(-1))
continue;
char s2c = base64_sextet_to_char_[(int)c2s];
C4_CHECK((size_t)s2c == i);
}
}
#endif
} // namespace detail
bool base64_valid(csubstr encoded)
{
if(encoded.len % 4) return false;
for(const char c : encoded)
{
if(c < 0/* || c >= 128*/)
return false;
if(c == '=')
continue;
if(detail::base64_char_to_sextet_[c] == char(-1))
return false;
}
return true;
}
size_t base64_encode(substr buf, cblob data)
{
#define c4append_(c) { if(pos < buf.len) { buf.str[pos] = (c); } ++pos; }
#define c4append_idx_(char_idx) \
{\
C4_XASSERT((char_idx) < sizeof(detail::base64_sextet_to_char_));\
c4append_(detail::base64_sextet_to_char_[(char_idx)]);\
}
size_t rem, pos = 0;
constexpr const uint32_t sextet_mask = uint32_t(1 << 6) - 1;
const unsigned char *C4_RESTRICT d = (unsigned char *) data.buf; // cast to unsigned to avoid wrapping high-bits
for(rem = data.len; rem >= 3; rem -= 3, d += 3)
{
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8) | (uint32_t(d[2])));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_idx_((val >> 6) & sextet_mask);
c4append_idx_((val ) & sextet_mask);
}
C4_ASSERT(rem < 3);
if(rem == 2)
{
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_idx_((val >> 6) & sextet_mask);
c4append_('=');
}
else if(rem == 1)
{
const uint32_t val = ((uint32_t(d[0]) << 16));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_('=');
c4append_('=');
}
return pos;
#undef c4append_
#undef c4append_idx_
}
size_t base64_decode(csubstr encoded, blob data)
{
#define c4append_(c) { if(wpos < data.len) { data.buf[wpos] = static_cast<c4::byte>(c); } ++wpos; }
#define c4appendval_(c, shift)\
{\
C4_XASSERT(c >= 0);\
C4_XASSERT(size_t(c) < sizeof(detail::base64_char_to_sextet_));\
val |= static_cast<uint32_t>(detail::base64_char_to_sextet_[(c)]) << ((shift) * 6);\
}
C4_ASSERT(base64_valid(encoded));
C4_CHECK(encoded.len % 4 == 0);
size_t wpos = 0; // the write position
const char *C4_RESTRICT d = encoded.str;
constexpr const uint32_t full_byte = 0xff;
// process every quartet of input 6 bits --> triplet of output bytes
for(size_t rpos = 0; rpos < encoded.len; rpos += 4, d += 4)
{
if(d[2] == '=' || d[3] == '=') // skip the last quartet if it is padded
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
break;
}
uint32_t val = 0;
c4appendval_(d[3], 0);
c4appendval_(d[2], 1);
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
c4append_((val >> (1 * 8)) & full_byte);
c4append_((val ) & full_byte);
}
// deal with the last quartet when it is padded
if(d == encoded.str + encoded.len)
return wpos;
if(d[2] == '=') // 2 padding chars
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
C4_ASSERT(d[3] == '=');
uint32_t val = 0;
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
}
else if(d[3] == '=') // 1 padding char
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
uint32_t val = 0;
c4appendval_(d[2], 1);
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
c4append_((val >> (1 * 8)) & full_byte);
}
return wpos;
#undef c4append_
#undef c4appendval_
}
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/base64.cpp)
#define C4_WINDOWS_POP_HPP_
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/windows_push.hpp
// https://github.com/biojppm/c4core/src/c4/windows_push.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_WINDOWS_PUSH_HPP_
#define _C4_WINDOWS_PUSH_HPP_
/** @file windows_push.hpp sets up macros to include windows header files
* without pulling in all of <windows.h>
*
* @see #include windows_pop.hpp to undefine these macros
*
* @see https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/ */
#if defined(_WIN64) || defined(_WIN32)
#if defined(_M_AMD64)
# ifndef _AMD64_
# define _c4_AMD64_
# define _AMD64_
# endif
#elif defined(_M_IX86)
# ifndef _X86_
# define _c4_X86_
# define _X86_
# endif
#elif defined(_M_ARM64)
# ifndef _ARM64_
# define _c4_ARM64_
# define _ARM64_
# endif
#elif defined(_M_ARM)
# ifndef _ARM_
# define _c4_ARM_
# define _ARM_
# endif
#endif
#ifndef NOMINMAX
# define _c4_NOMINMAX
# define NOMINMAX
#endif
#ifndef NOGDI
# define _c4_NOGDI
# define NOGDI
#endif
#ifndef VC_EXTRALEAN
# define _c4_VC_EXTRALEAN
# define VC_EXTRALEAN
#endif
#ifndef WIN32_LEAN_AND_MEAN
# define _c4_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
/* If defined, the following flags inhibit definition
* of the indicated items.
*
* NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
* NOVIRTUALKEYCODES - VK_*
* NOWINMESSAGES - WM_*, EM_*, LB_*, CB_*
* NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
* NOSYSMETRICS - SM_*
* NOMENUS - MF_*
* NOICONS - IDI_*
* NOKEYSTATES - MK_*
* NOSYSCOMMANDS - SC_*
* NORASTEROPS - Binary and Tertiary raster ops
* NOSHOWWINDOW - SW_*
* OEMRESOURCE - OEM Resource values
* NOATOM - Atom Manager routines
* NOCLIPBOARD - Clipboard routines
* NOCOLOR - Screen colors
* NOCTLMGR - Control and Dialog routines
* NODRAWTEXT - DrawText() and DT_*
* NOGDI - All GDI defines and routines
* NOKERNEL - All KERNEL defines and routines
* NOUSER - All USER defines and routines
* NONLS - All NLS defines and routines
* NOMB - MB_* and MessageBox()
* NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines
* NOMETAFILE - typedef METAFILEPICT
* NOMINMAX - Macros min(a,b) and max(a,b)
* NOMSG - typedef MSG and associated routines
* NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
* NOSCROLL - SB_* and scrolling routines
* NOSERVICE - All Service Controller routines, SERVICE_ equates, etc.
* NOSOUND - Sound driver routines
* NOTEXTMETRIC - typedef TEXTMETRIC and associated routines
* NOWH - SetWindowsHook and WH_*
* NOWINOFFSETS - GWL_*, GCL_*, associated routines
* NOCOMM - COMM driver routines
* NOKANJI - Kanji support stuff.
* NOHELP - Help engine interface.
* NOPROFILER - Profiler interface.
* NODEFERWINDOWPOS - DeferWindowPos routines
* NOMCX - Modem Configuration Extensions
*/
#endif /* defined(_WIN64) || defined(_WIN32) */
#endif /* _C4_WINDOWS_PUSH_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/windows_push.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/windows.hpp
// https://github.com/biojppm/c4core/src/c4/windows.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_WINDOWS_HPP_
#define _C4_WINDOWS_HPP_
#if defined(_WIN64) || defined(_WIN32)
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/windows_push.hpp
//#include "c4/windows_push.hpp"
#if !defined(C4_WINDOWS_PUSH_HPP_) && !defined(_C4_WINDOWS_PUSH_HPP_)
#error "amalgamate: file c4/windows_push.hpp must have been included at this point"
#endif /* C4_WINDOWS_PUSH_HPP_ */
#include <windows.h>
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/windows_pop.hpp
//#include "c4/windows_pop.hpp"
#if !defined(C4_WINDOWS_POP_HPP_) && !defined(_C4_WINDOWS_POP_HPP_)
#error "amalgamate: file c4/windows_pop.hpp must have been included at this point"
#endif /* C4_WINDOWS_POP_HPP_ */
#endif
#endif /* _C4_WINDOWS_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/windows.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/windows_pop.hpp
// https://github.com/biojppm/c4core/src/c4/windows_pop.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_WINDOWS_POP_HPP_
#define _C4_WINDOWS_POP_HPP_
#if defined(_WIN64) || defined(_WIN32)
#ifdef _c4_AMD64_
# undef _c4_AMD64_
# undef _AMD64_
#endif
#ifdef _c4_X86_
# undef _c4_X86_
# undef _X86_
#endif
#ifdef _c4_ARM_
# undef _c4_ARM_
# undef _ARM_
#endif
#ifdef _c4_NOMINMAX
# undef _c4_NOMINMAX
# undef NOMINMAX
#endif
#ifdef NOGDI
# undef _c4_NOGDI
# undef NOGDI
#endif
#ifdef VC_EXTRALEAN
# undef _c4_VC_EXTRALEAN
# undef VC_EXTRALEAN
#endif
#ifdef WIN32_LEAN_AND_MEAN
# undef _c4_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif /* defined(_WIN64) || defined(_WIN32) */
#endif /* _C4_WINDOWS_POP_HPP_ */
// (end https://github.com/biojppm/c4core/src/c4/windows_pop.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/error.cpp
// https://github.com/biojppm/c4core/src/c4/error.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef C4CORE_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
//included above:
//#include <stdlib.h>
//included above:
//#include <stdio.h>
//included above:
//#include <stdarg.h>
#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGP(msg, ...) printf(msg)
#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
// amalgamate: removed include of
// https://github.com/biojppm/c4core/src/c4/windows.hpp
//# include "c4/windows.hpp"
#if !defined(C4_WINDOWS_HPP_) && !defined(_C4_WINDOWS_HPP_)
#error "amalgamate: file c4/windows.hpp must have been included at this point"
#endif /* C4_WINDOWS_HPP_ */
#elif defined(C4_PS4)
# include <libdbg.h>
#elif defined(C4_UNIX) || defined(C4_LINUX)
# include <sys/stat.h>
//included above:
//# include <cstring>
# include <fcntl.h>
#elif defined(C4_MACOS) || defined(C4_IOS)
//included above:
//# include <assert.h>
# include <stdbool.h>
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
// the amalgamation tool is dumb and was omitting this include under MACOS.
// So do it only once:
#if defined(C4_UNIX) || defined(C4_LINUX) || defined(C4_MACOS) || defined(C4_IOS)
# include <unistd.h>
#endif
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# include <exception>
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
//-----------------------------------------------------------------------------
namespace c4 {
static error_flags s_error_flags = ON_ERROR_DEFAULTS;
static error_callback_type s_error_callback = nullptr;
//-----------------------------------------------------------------------------
error_flags get_error_flags()
{
return s_error_flags;
}
void set_error_flags(error_flags flags)
{
s_error_flags = flags;
}
error_callback_type get_error_callback()
{
return s_error_callback;
}
/** Set the function which is called when an error occurs. */
void set_error_callback(error_callback_type cb)
{
s_error_callback = cb;
}
//-----------------------------------------------------------------------------
void handle_error(srcloc where, const char *fmt, ...)
{
char buf[1024];
size_t msglen = 0;
if(s_error_flags & (ON_ERROR_LOG|ON_ERROR_CALLBACK))
{
va_list args;
va_start(args, fmt);
int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // ss.vprintf(fmt, args);
va_end(args);
msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1;
}
if(s_error_flags & ON_ERROR_LOG)
{
C4_LOGF_ERR("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
C4_LOGF_ERR("%s:%d: ERROR here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("ERROR: %s\n", buf);
#endif
}
if(s_error_flags & ON_ERROR_CALLBACK)
{
if(s_error_callback)
{
s_error_callback(buf, msglen/*ss.c_strp(), ss.tellp()*/);
}
}
if(s_error_flags & ON_ERROR_ABORT)
{
abort();
}
if(s_error_flags & ON_ERROR_THROW)
{
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
throw Exception(buf);
#else
abort();
#endif
}
}
//-----------------------------------------------------------------------------
void handle_warning(srcloc where, const char *fmt, ...)
{
va_list args;
char buf[1024]; //sstream<c4::string> ss;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
C4_LOGF_WARN("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("WARNING: %s\n", buf/*ss.c_strp()*/);
#endif
//c4::log.flush();
}
//-----------------------------------------------------------------------------
bool is_debugger_attached()
{
#if defined(C4_UNIX) || defined(C4_LINUX)
static bool first_call = true;
static bool first_call_result = false;
if(first_call)
{
first_call = false;
//! @see http://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
//! (this answer: http://stackoverflow.com/a/24969863/3968589 )
char buf[1024] = "";
int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
{
return 0;
}
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
if (num_read > 0)
{
static const char TracerPid[] = "TracerPid:";
char *tracer_pid;
if(num_read < 1024)
{
buf[num_read] = 0;
}
tracer_pid = strstr(buf, TracerPid);
if (tracer_pid)
{
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1);
}
}
}
return first_call_result;
#elif defined(C4_PS4)
return (sceDbgIsDebuggerAttached() != 0);
#elif defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
return IsDebuggerPresent() != 0;
#elif defined(C4_MACOS) || defined(C4_IOS)
// https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
#else
return false;
#endif
} // is_debugger_attached()
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* C4CORE_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/c4core/src/c4/error.cpp)
#endif /* _C4CORE_SINGLE_HEADER_AMALGAMATED_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/c4core_all.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/export.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/export.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_YML_EXPORT_HPP_
#define C4_YML_EXPORT_HPP_
#ifdef _WIN32
#ifdef RYML_SHARED
#ifdef RYML_EXPORTS
#define RYML_EXPORT __declspec(dllexport)
#else
#define RYML_EXPORT __declspec(dllimport)
#endif
#else
#define RYML_EXPORT
#endif
#else
#define RYML_EXPORT
#endif
#endif /* C4_YML_EXPORT_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/export.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/common.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/common.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_COMMON_HPP_
#define _C4_YML_COMMON_HPP_
//included above:
//#include <cstddef>
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/substr.hpp
//#include <c4/substr.hpp>
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/export.hpp
//#include <c4/yml/export.hpp>
#if !defined(C4_YML_EXPORT_HPP_) && !defined(_C4_YML_EXPORT_HPP_)
#error "amalgamate: file c4/yml/export.hpp must have been included at this point"
#endif /* C4_YML_EXPORT_HPP_ */
#ifndef RYML_USE_ASSERT
# define RYML_USE_ASSERT C4_USE_ASSERT
#endif
#if RYML_USE_ASSERT
# define RYML_ASSERT(cond) RYML_CHECK(cond)
# define RYML_ASSERT_MSG(cond, msg) RYML_CHECK_MSG(cond, msg)
#else
# define RYML_ASSERT(cond)
# define RYML_ASSERT_MSG(cond, msg)
#endif
#define RYML_CHECK(cond) \
do { \
if(!(cond)) \
{ \
C4_DEBUG_BREAK(); \
c4::yml::error("check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
} \
} while(0)
#define RYML_CHECK_MSG(cond, msg) \
do \
{ \
if(!(cond)) \
{ \
C4_DEBUG_BREAK(); \
c4::yml::error(msg ": check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
} \
} while(0)
#if C4_CPP >= 14
# define RYML_DEPRECATED(msg) [[deprecated(msg)]]
#else
# if defined(_MSC_VER)
# define RYML_DEPRECATED(msg) __declspec(deprecated)
# else // defined(__GNUC__) || defined(__clang__)
# define RYML_DEPRECATED(msg) __attribute__((deprecated))
# endif
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace c4 {
namespace yml {
enum : size_t {
/** a null position */
npos = size_t(-1),
/** an index to none */
NONE = size_t(-1)
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//! holds a position into a source buffer
struct RYML_EXPORT LineCol
{
//! number of bytes from the beginning of the source buffer
size_t offset;
//! line
size_t line;
//! column
size_t col;
LineCol() : offset(), line(), col() {}
//! construct from line and column
LineCol(size_t l, size_t c) : offset(0), line(l), col(c) {}
//! construct from offset, line and column
LineCol(size_t o, size_t l, size_t c) : offset(o), line(l), col(c) {}
};
//! a source file position
struct RYML_EXPORT Location : public LineCol
{
csubstr name;
operator bool () const { return !name.empty() || line != 0 || offset != 0; }
Location() : LineCol(), name() {}
Location( size_t l, size_t c) : LineCol{ l, c}, name( ) {}
Location( csubstr n, size_t l, size_t c) : LineCol{ l, c}, name(n) {}
Location( csubstr n, size_t b, size_t l, size_t c) : LineCol{b, l, c}, name(n) {}
Location(const char *n, size_t l, size_t c) : LineCol{ l, c}, name(to_csubstr(n)) {}
Location(const char *n, size_t b, size_t l, size_t c) : LineCol{b, l, c}, name(to_csubstr(n)) {}
};
//-----------------------------------------------------------------------------
/** the type of the function used to report errors. This function must
* interrupt execution, either by raising an exception or calling
* std::abort(). */
using pfn_error = void (*)(const char* msg, size_t msg_len, Location location, void *user_data);
/** the type of the function used to allocate memory */
using pfn_allocate = void* (*)(size_t len, void* hint, void *user_data);
/** the type of the function used to free memory */
using pfn_free = void (*)(void* mem, size_t size, void *user_data);
/** trigger an error: call the current error callback. */
RYML_EXPORT void error(const char *msg, size_t msg_len, Location loc);
/** @overload error */
inline void error(const char *msg, size_t msg_len)
{
error(msg, msg_len, Location{});
}
/** @overload error */
template<size_t N>
inline void error(const char (&msg)[N], Location loc)
{
error(msg, N-1, loc);
}
/** @overload error */
template<size_t N>
inline void error(const char (&msg)[N])
{
error(msg, N-1, Location{});
}
//-----------------------------------------------------------------------------
/// a c-style callbacks class
struct RYML_EXPORT Callbacks
{
void * m_user_data;
pfn_allocate m_allocate;
pfn_free m_free;
pfn_error m_error;
Callbacks();
Callbacks(void *user_data, pfn_allocate alloc, pfn_free free, pfn_error error_);
bool operator!= (Callbacks const& that) const { return !operator==(that); }
bool operator== (Callbacks const& that) const
{
return (m_user_data == that.m_user_data &&
m_allocate == that.m_allocate &&
m_free == that.m_free &&
m_error == that.m_error);
}
};
/// get the global callbacks
RYML_EXPORT Callbacks const& get_callbacks();
/// set the global callbacks
RYML_EXPORT void set_callbacks(Callbacks const& c);
/// set the global callbacks to their defaults
RYML_EXPORT void reset_callbacks();
/// @cond dev
#define _RYML_CB_ERR(cb, msg_literal) \
do \
{ \
const char msg[] = msg_literal; \
C4_DEBUG_BREAK(); \
(cb).m_error(msg, sizeof(msg), c4::yml::Location(__FILE__, 0, __LINE__, 0), (cb).m_user_data); \
} while(0)
#define _RYML_CB_CHECK(cb, cond) \
do \
{ \
if(!(cond)) \
{ \
const char msg[] = "check failed: " #cond; \
C4_DEBUG_BREAK(); \
(cb).m_error(msg, sizeof(msg), c4::yml::Location(__FILE__, 0, __LINE__, 0), (cb).m_user_data); \
} \
} while(0)
#ifdef RYML_USE_ASSERT
#define _RYML_CB_ASSERT(cb, cond) _RYML_CB_CHECK((cb), (cond))
#else
#define _RYML_CB_ASSERT(cb, cond) do {} while(0)
#endif
#define _RYML_CB_ALLOC_HINT(cb, T, num, hint) (T*) (cb).m_allocate((num) * sizeof(T), (hint), (cb).m_user_data)
#define _RYML_CB_ALLOC(cb, T, num) _RYML_CB_ALLOC_HINT((cb), (T), (num), nullptr)
#define _RYML_CB_FREE(cb, buf, T, num) \
do { \
(cb).m_free((buf), (num) * sizeof(T), (cb).m_user_data); \
(buf) = nullptr; \
} while(0)
namespace detail {
template<int8_t signedval, uint8_t unsignedval>
struct _charconstant_t
: public std::conditional<std::is_signed<char>::value,
std::integral_constant<int8_t, signedval>,
std::integral_constant<uint8_t, unsignedval>>::type
{};
#define _RYML_CHCONST(signedval, unsignedval) ::c4::yml::detail::_charconstant_t<INT8_C(signedval), UINT8_C(unsignedval)>::value
} // namespace detail
namespace detail {
struct _SubstrWriter
{
substr buf;
size_t pos;
_SubstrWriter(substr buf_, size_t pos_=0) : buf(buf_), pos(pos_) {}
void append(csubstr s)
{
C4_ASSERT(!s.overlaps(buf));
if(pos + s.len <= buf.len)
memcpy(buf.str + pos, s.str, s.len);
pos += s.len;
}
void append(char c)
{
if(pos < buf.len)
buf.str[pos] = c;
++pos;
}
void append_n(char c, size_t numtimes)
{
if(pos + numtimes < buf.len)
memset(buf.str + pos, c, numtimes);
pos += numtimes;
}
size_t slack() const { return pos <= buf.len ? buf.len - pos : 0; }
size_t excess() const { return pos > buf.len ? pos - buf.len : 0; }
//! get the part written so far
csubstr curr() const { return pos <= buf.len ? buf.first(pos) : buf; }
//! get the part that is still free to write to (the remainder)
substr rem() { return pos < buf.len ? buf.sub(pos) : buf.last(0); }
size_t advance(size_t more) { pos += more; return pos; }
};
} // namespace detail
/// @endcond
} // namespace yml
} // namespace c4
#endif /* _C4_YML_COMMON_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/common.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/tree.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_TREE_HPP_
#define _C4_YML_TREE_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/types.hpp
//#include "c4/types.hpp"
#if !defined(C4_TYPES_HPP_) && !defined(_C4_TYPES_HPP_)
#error "amalgamate: file c4/types.hpp must have been included at this point"
#endif /* C4_TYPES_HPP_ */
#ifndef _C4_YML_COMMON_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/common.hpp
//#include "c4/yml/common.hpp"
#if !defined(C4_YML_COMMON_HPP_) && !defined(_C4_YML_COMMON_HPP_)
#error "amalgamate: file c4/yml/common.hpp must have been included at this point"
#endif /* C4_YML_COMMON_HPP_ */
#endif
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/charconv.hpp
//#include <c4/charconv.hpp>
#if !defined(C4_CHARCONV_HPP_) && !defined(_C4_CHARCONV_HPP_)
#error "amalgamate: file c4/charconv.hpp must have been included at this point"
#endif /* C4_CHARCONV_HPP_ */
//included above:
//#include <cmath>
//included above:
//#include <limits>
C4_SUPPRESS_WARNING_MSVC_PUSH
C4_SUPPRESS_WARNING_MSVC(4251) // needs to have dll-interface to be used by clients of struct
C4_SUPPRESS_WARNING_MSVC(4296) // expression is always 'boolean_value'
C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
namespace c4 {
namespace yml {
struct NodeScalar;
struct NodeInit;
struct NodeData;
class NodeRef;
class Tree;
/** encode a floating point value to a string. */
template<class T>
size_t to_chars_float(substr buf, T val)
{
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal");
static_assert(std::is_floating_point<T>::value, "must be floating point");
if(C4_UNLIKELY(std::isnan(val)))
return to_chars(buf, csubstr(".nan"));
else if(C4_UNLIKELY(val == std::numeric_limits<T>::infinity()))
return to_chars(buf, csubstr(".inf"));
else if(C4_UNLIKELY(val == -std::numeric_limits<T>::infinity()))
return to_chars(buf, csubstr("-.inf"));
return to_chars(buf, val);
C4_SUPPRESS_WARNING_GCC_CLANG_POP
}
/** decode a floating point from string. Accepts special values: .nan,
* .inf, -.inf */
template<class T>
bool from_chars_float(csubstr buf, T *C4_RESTRICT val)
{
static_assert(std::is_floating_point<T>::value, "must be floating point");
if(C4_LIKELY(from_chars(buf, val)))
{
return true;
}
else if(C4_UNLIKELY(buf == ".nan" || buf == ".NaN" || buf == ".NAN"))
{
*val = std::numeric_limits<T>::quiet_NaN();
return true;
}
else if(C4_UNLIKELY(buf == ".inf" || buf == ".Inf" || buf == ".INF"))
{
*val = std::numeric_limits<T>::infinity();
return true;
}
else if(C4_UNLIKELY(buf == "-.inf" || buf == "-.Inf" || buf == "-.INF"))
{
*val = -std::numeric_limits<T>::infinity();
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** the integral type necessary to cover all the bits marking node tags */
using tag_bits = uint16_t;
/** a bit mask for marking tags for types */
typedef enum : tag_bits {
// container types
TAG_NONE = 0,
TAG_MAP = 1, /**< !!map Unordered set of key: value pairs without duplicates. @see https://yaml.org/type/map.html */
TAG_OMAP = 2, /**< !!omap Ordered sequence of key: value pairs without duplicates. @see https://yaml.org/type/omap.html */
TAG_PAIRS = 3, /**< !!pairs Ordered sequence of key: value pairs allowing duplicates. @see https://yaml.org/type/pairs.html */
TAG_SET = 4, /**< !!set Unordered set of non-equal values. @see https://yaml.org/type/set.html */
TAG_SEQ = 5, /**< !!seq Sequence of arbitrary values. @see https://yaml.org/type/seq.html */
// scalar types
TAG_BINARY = 6, /**< !!binary A sequence of zero or more octets (8 bit values). @see https://yaml.org/type/binary.html */
TAG_BOOL = 7, /**< !!bool Mathematical Booleans. @see https://yaml.org/type/bool.html */
TAG_FLOAT = 8, /**< !!float Floating-point approximation to real numbers. https://yaml.org/type/float.html */
TAG_INT = 9, /**< !!float Mathematical integers. https://yaml.org/type/int.html */
TAG_MERGE = 10, /**< !!merge Specify one or more mapping to be merged with the current one. https://yaml.org/type/merge.html */
TAG_NULL = 11, /**< !!null Devoid of value. https://yaml.org/type/null.html */
TAG_STR = 12, /**< !!str A sequence of zero or more Unicode characters. https://yaml.org/type/str.html */
TAG_TIMESTAMP = 13, /**< !!timestamp A point in time https://yaml.org/type/timestamp.html */
TAG_VALUE = 14, /**< !!value Specify the default value of a mapping https://yaml.org/type/value.html */
TAG_YAML = 15, /**< !!yaml Specify the default value of a mapping https://yaml.org/type/yaml.html */
} YamlTag_e;
YamlTag_e to_tag(csubstr tag);
csubstr from_tag(YamlTag_e tag);
csubstr from_tag_long(YamlTag_e tag);
csubstr normalize_tag(csubstr tag);
csubstr normalize_tag_long(csubstr tag);
struct TagDirective
{
/** Eg `!e!` in `%TAG !e! tag:example.com,2000:app/` */
csubstr handle;
/** Eg `tag:example.com,2000:app/` in `%TAG !e! tag:example.com,2000:app/` */
csubstr prefix;
/** The next node to which this tag directive applies */
size_t next_node_id;
};
#ifndef RYML_MAX_TAG_DIRECTIVES
/** the maximum number of tag directives in a Tree */
#define RYML_MAX_TAG_DIRECTIVES 4
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** the integral type necessary to cover all the bits marking node types */
using type_bits = uint64_t;
/** a bit mask for marking node types */
typedef enum : type_bits {
// a convenience define, undefined below
#define c4bit(v) (type_bits(1) << v)
NOTYPE = 0, ///< no node type is set
VAL = c4bit(0), ///< a leaf node, has a (possibly empty) value
KEY = c4bit(1), ///< is member of a map, must have non-empty key
MAP = c4bit(2), ///< a map: a parent of keyvals
SEQ = c4bit(3), ///< a seq: a parent of vals
DOC = c4bit(4), ///< a document
STREAM = c4bit(5)|SEQ, ///< a stream: a seq of docs
KEYREF = c4bit(6), ///< a *reference: the key references an &anchor
VALREF = c4bit(7), ///< a *reference: the val references an &anchor
KEYANCH = c4bit(8), ///< the key has an &anchor
VALANCH = c4bit(9), ///< the val has an &anchor
KEYTAG = c4bit(10), ///< the key has an explicit tag/type
VALTAG = c4bit(11), ///< the val has an explicit tag/type
_TYMASK = c4bit(12)-1, // all the bits up to here
VALQUO = c4bit(12), ///< the val is quoted by '', "", > or |
KEYQUO = c4bit(13), ///< the key is quoted by '', "", > or |
KEYVAL = KEY|VAL,
KEYSEQ = KEY|SEQ,
KEYMAP = KEY|MAP,
DOCMAP = DOC|MAP,
DOCSEQ = DOC|SEQ,
DOCVAL = DOC|VAL,
// these flags are from a work in progress and should not be used yet
_WIP_STYLE_FLOW_SL = c4bit(14), ///< mark container with single-line flow format (seqs as '[val1,val2], maps as '{key: val, key2: val2}')
_WIP_STYLE_FLOW_ML = c4bit(15), ///< mark container with multi-line flow format (seqs as '[val1,\nval2], maps as '{key: val,\nkey2: val2}')
_WIP_STYLE_BLOCK = c4bit(16), ///< mark container with block format (seqs as '- val\n', maps as 'key: val')
_WIP_KEY_LITERAL = c4bit(17), ///< mark key scalar as multiline, block literal |
_WIP_VAL_LITERAL = c4bit(18), ///< mark val scalar as multiline, block literal |
_WIP_KEY_FOLDED = c4bit(19), ///< mark key scalar as multiline, block folded >
_WIP_VAL_FOLDED = c4bit(20), ///< mark val scalar as multiline, block folded >
_WIP_KEY_SQUO = c4bit(21), ///< mark key scalar as single quoted
_WIP_VAL_SQUO = c4bit(22), ///< mark val scalar as single quoted
_WIP_KEY_DQUO = c4bit(23), ///< mark key scalar as double quoted
_WIP_VAL_DQUO = c4bit(24), ///< mark val scalar as double quoted
_WIP_KEY_PLAIN = c4bit(25), ///< mark key scalar as plain scalar (unquoted, even when multiline)
_WIP_VAL_PLAIN = c4bit(26), ///< mark val scalar as plain scalar (unquoted, even when multiline)
_WIP_KEY_STYLE = _WIP_KEY_LITERAL|_WIP_KEY_FOLDED|_WIP_KEY_SQUO|_WIP_KEY_DQUO|_WIP_KEY_PLAIN,
_WIP_VAL_STYLE = _WIP_VAL_LITERAL|_WIP_VAL_FOLDED|_WIP_VAL_SQUO|_WIP_VAL_DQUO|_WIP_VAL_PLAIN,
_WIP_KEY_FT_NL = c4bit(27), ///< features: mark key scalar as having \n in its contents
_WIP_VAL_FT_NL = c4bit(28), ///< features: mark val scalar as having \n in its contents
_WIP_KEY_FT_SQ = c4bit(29), ///< features: mark key scalar as having single quotes in its contents
_WIP_VAL_FT_SQ = c4bit(30), ///< features: mark val scalar as having single quotes in its contents
_WIP_KEY_FT_DQ = c4bit(31), ///< features: mark key scalar as having double quotes in its contents
_WIP_VAL_FT_DQ = c4bit(32), ///< features: mark val scalar as having double quotes in its contents
#undef c4bit
} NodeType_e;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** wraps a NodeType_e element with some syntactic sugar and predicates */
struct NodeType
{
public:
NodeType_e type;
public:
C4_ALWAYS_INLINE operator NodeType_e & C4_RESTRICT () { return type; }
C4_ALWAYS_INLINE operator NodeType_e const& C4_RESTRICT () const { return type; }
C4_ALWAYS_INLINE NodeType() : type(NOTYPE) {}
C4_ALWAYS_INLINE NodeType(NodeType_e t) : type(t) {}
C4_ALWAYS_INLINE NodeType(type_bits t) : type((NodeType_e)t) {}
C4_ALWAYS_INLINE const char *type_str() const { return type_str(type); }
static const char* type_str(NodeType_e t);
C4_ALWAYS_INLINE void set(NodeType_e t) { type = t; }
C4_ALWAYS_INLINE void set(type_bits t) { type = (NodeType_e)t; }
C4_ALWAYS_INLINE void add(NodeType_e t) { type = (NodeType_e)(type|t); }
C4_ALWAYS_INLINE void add(type_bits t) { type = (NodeType_e)(type|t); }
C4_ALWAYS_INLINE void rem(NodeType_e t) { type = (NodeType_e)(type & ~t); }
C4_ALWAYS_INLINE void rem(type_bits t) { type = (NodeType_e)(type & ~t); }
C4_ALWAYS_INLINE void clear() { type = NOTYPE; }
public:
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnull-dereference"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
C4_ALWAYS_INLINE bool is_stream() const { return ((type & STREAM) == STREAM) != 0; }
C4_ALWAYS_INLINE bool is_doc() const { return (type & DOC) != 0; }
C4_ALWAYS_INLINE bool is_container() const { return (type & (MAP|SEQ|STREAM)) != 0; }
C4_ALWAYS_INLINE bool is_map() const { return (type & MAP) != 0; }
C4_ALWAYS_INLINE bool is_seq() const { return (type & SEQ) != 0; }
C4_ALWAYS_INLINE bool has_val() const { return (type & VAL) != 0; }
C4_ALWAYS_INLINE bool has_key() const { return (type & KEY) != 0; }
C4_ALWAYS_INLINE bool is_val() const { return (type & (KEYVAL)) == VAL; }
C4_ALWAYS_INLINE bool is_keyval() const { return (type & KEYVAL) == KEYVAL; }
C4_ALWAYS_INLINE bool has_key_tag() const { return (type & (KEY|KEYTAG)) == (KEY|KEYTAG); }
C4_ALWAYS_INLINE bool has_val_tag() const { return ((type & (VALTAG)) && (type & (VAL|MAP|SEQ))); }
C4_ALWAYS_INLINE bool has_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); }
C4_ALWAYS_INLINE bool is_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); }
C4_ALWAYS_INLINE bool has_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; }
C4_ALWAYS_INLINE bool is_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; }
C4_ALWAYS_INLINE bool has_anchor() const { return (type & (KEYANCH|VALANCH)) != 0; }
C4_ALWAYS_INLINE bool is_anchor() const { return (type & (KEYANCH|VALANCH)) != 0; }
C4_ALWAYS_INLINE bool is_key_ref() const { return (type & KEYREF) != 0; }
C4_ALWAYS_INLINE bool is_val_ref() const { return (type & VALREF) != 0; }
C4_ALWAYS_INLINE bool is_ref() const { return (type & (KEYREF|VALREF)) != 0; }
C4_ALWAYS_INLINE bool is_anchor_or_ref() const { return (type & (KEYANCH|VALANCH|KEYREF|VALREF)) != 0; }
C4_ALWAYS_INLINE bool is_key_quoted() const { return (type & (KEY|KEYQUO)) == (KEY|KEYQUO); }
C4_ALWAYS_INLINE bool is_val_quoted() const { return (type & (VAL|VALQUO)) == (VAL|VALQUO); }
C4_ALWAYS_INLINE bool is_quoted() const { return (type & (KEY|KEYQUO)) == (KEY|KEYQUO) || (type & (VAL|VALQUO)) == (VAL|VALQUO); }
// these predicates are a work in progress and subject to change. Don't use yet.
C4_ALWAYS_INLINE bool default_block() const { return (type & (_WIP_STYLE_BLOCK|_WIP_STYLE_FLOW_ML|_WIP_STYLE_FLOW_SL)) == 0; }
C4_ALWAYS_INLINE bool marked_block() const { return (type & (_WIP_STYLE_BLOCK)) != 0; }
C4_ALWAYS_INLINE bool marked_flow_sl() const { return (type & (_WIP_STYLE_FLOW_SL)) != 0; }
C4_ALWAYS_INLINE bool marked_flow_ml() const { return (type & (_WIP_STYLE_FLOW_ML)) != 0; }
C4_ALWAYS_INLINE bool marked_flow() const { return (type & (_WIP_STYLE_FLOW_ML|_WIP_STYLE_FLOW_SL)) != 0; }
C4_ALWAYS_INLINE bool key_marked_literal() const { return (type & (_WIP_KEY_LITERAL)) != 0; }
C4_ALWAYS_INLINE bool val_marked_literal() const { return (type & (_WIP_VAL_LITERAL)) != 0; }
C4_ALWAYS_INLINE bool key_marked_folded() const { return (type & (_WIP_KEY_FOLDED)) != 0; }
C4_ALWAYS_INLINE bool val_marked_folded() const { return (type & (_WIP_VAL_FOLDED)) != 0; }
C4_ALWAYS_INLINE bool key_marked_squo() const { return (type & (_WIP_KEY_SQUO)) != 0; }
C4_ALWAYS_INLINE bool val_marked_squo() const { return (type & (_WIP_VAL_SQUO)) != 0; }
C4_ALWAYS_INLINE bool key_marked_dquo() const { return (type & (_WIP_KEY_DQUO)) != 0; }
C4_ALWAYS_INLINE bool val_marked_dquo() const { return (type & (_WIP_VAL_DQUO)) != 0; }
C4_ALWAYS_INLINE bool key_marked_plain() const { return (type & (_WIP_KEY_PLAIN)) != 0; }
C4_ALWAYS_INLINE bool val_marked_plain() const { return (type & (_WIP_VAL_PLAIN)) != 0; }
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a node scalar is a csubstr, which may be tagged and anchored. */
struct NodeScalar
{
csubstr tag;
csubstr scalar;
csubstr anchor;
public:
/// initialize as an empty scalar
inline NodeScalar() noexcept : tag(), scalar(), anchor() {}
/// initialize as an untagged scalar
template<size_t N>
inline NodeScalar(const char (&s)[N]) noexcept : tag(), scalar(s), anchor() {}
inline NodeScalar(csubstr s ) noexcept : tag(), scalar(s), anchor() {}
/// initialize as a tagged scalar
template<size_t N, size_t M>
inline NodeScalar(const char (&t)[N], const char (&s)[N]) noexcept : tag(t), scalar(s), anchor() {}
inline NodeScalar(csubstr t , csubstr s ) noexcept : tag(t), scalar(s), anchor() {}
public:
~NodeScalar() noexcept = default;
NodeScalar(NodeScalar &&) noexcept = default;
NodeScalar(NodeScalar const&) noexcept = default;
NodeScalar& operator= (NodeScalar &&) noexcept = default;
NodeScalar& operator= (NodeScalar const&) noexcept = default;
public:
bool empty() const noexcept { return tag.empty() && scalar.empty() && anchor.empty(); }
void clear() noexcept { tag.clear(); scalar.clear(); anchor.clear(); }
void set_ref_maybe_replacing_scalar(csubstr ref, bool has_scalar) noexcept
{
csubstr trimmed = ref.begins_with('*') ? ref.sub(1) : ref;
anchor = trimmed;
if((!has_scalar) || !scalar.ends_with(trimmed))
scalar = ref;
}
};
C4_MUST_BE_TRIVIAL_COPY(NodeScalar);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** convenience class to initialize nodes */
struct NodeInit
{
NodeType type;
NodeScalar key;
NodeScalar val;
public:
/// initialize as an empty node
NodeInit() : type(NOTYPE), key(), val() {}
/// initialize as a typed node
NodeInit(NodeType_e t) : type(t), key(), val() {}
/// initialize as a sequence member
NodeInit(NodeScalar const& v) : type(VAL), key(), val(v) { _add_flags(); }
/// initialize as a mapping member
NodeInit( NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); }
/// initialize as a mapping member with explicit type
NodeInit(NodeType_e t, NodeScalar const& k, NodeScalar const& v) : type(t ), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); }
/// initialize as a mapping member with explicit type (eg SEQ or MAP)
NodeInit(NodeType_e t, NodeScalar const& k ) : type(t ), key(k.tag, k.scalar), val( ) { _add_flags(KEY); }
public:
void clear()
{
type.clear();
key.clear();
val.clear();
}
void _add_flags(type_bits more_flags=0)
{
type = (type|more_flags);
if( ! key.tag.empty())
type = (type|KEYTAG);
if( ! val.tag.empty())
type = (type|VALTAG);
if( ! key.anchor.empty())
type = (type|KEYANCH);
if( ! val.anchor.empty())
type = (type|VALANCH);
}
bool _check() const
{
// key cannot be empty
RYML_ASSERT(key.scalar.empty() == ((type & KEY) == 0));
// key tag cannot be empty
RYML_ASSERT(key.tag.empty() == ((type & KEYTAG) == 0));
// val may be empty even though VAL is set. But when VAL is not set, val must be empty
RYML_ASSERT(((type & VAL) != 0) || val.scalar.empty());
// val tag cannot be empty
RYML_ASSERT(val.tag.empty() == ((type & VALTAG) == 0));
return true;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** contains the data for each YAML node. */
struct NodeData
{
NodeType m_type;
NodeScalar m_key;
NodeScalar m_val;
size_t m_parent;
size_t m_first_child;
size_t m_last_child;
size_t m_next_sibling;
size_t m_prev_sibling;
};
C4_MUST_BE_TRIVIAL_COPY(NodeData);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class RYML_EXPORT Tree
{
public:
/** @name construction and assignment */
/** @{ */
Tree() : Tree(get_callbacks()) {}
Tree(Callbacks const& cb);
Tree(size_t node_capacity, size_t arena_capacity=0) : Tree(node_capacity, arena_capacity, get_callbacks()) {}
Tree(size_t node_capacity, size_t arena_capacity, Callbacks const& cb);
~Tree();
Tree(Tree const& that) noexcept;
Tree(Tree && that) noexcept;
Tree& operator= (Tree const& that) noexcept;
Tree& operator= (Tree && that) noexcept;
/** @} */
public:
/** @name memory and sizing */
/** @{ */
void reserve(size_t node_capacity);
/** clear the tree and zero every node
* @note does NOT clear the arena
* @see clear_arena() */
void clear();
inline void clear_arena() { m_arena_pos = 0; }
inline bool empty() const { return m_size == 0; }
inline size_t size () const { return m_size; }
inline size_t capacity() const { return m_cap; }
inline size_t slack() const { RYML_ASSERT(m_cap >= m_size); return m_cap - m_size; }
inline size_t arena_size() const { return m_arena_pos; }
inline size_t arena_capacity() const { return m_arena.len; }
inline size_t arena_slack() const { RYML_ASSERT(m_arena.len >= m_arena_pos); return m_arena.len - m_arena_pos; }
Callbacks const& callbacks() const { return m_callbacks; }
void callbacks(Callbacks const& cb) { m_callbacks = cb; }
/** @} */
public:
/** @name node getters */
/** @{ */
//! get the index of a node belonging to this tree.
//! @p n can be nullptr, in which case a
size_t id(NodeData const* n) const
{
if( ! n)
{
return NONE;
}
RYML_ASSERT(n >= m_buf && n < m_buf + m_cap);
return static_cast<size_t>(n - m_buf);
}
//! get a pointer to a node's NodeData.
//! i can be NONE, in which case a nullptr is returned
inline NodeData *get(size_t i)
{
if(i == NONE)
return nullptr;
RYML_ASSERT(i >= 0 && i < m_cap);
return m_buf + i;
}
//! get a pointer to a node's NodeData.
//! i can be NONE, in which case a nullptr is returned.
inline NodeData const *get(size_t i) const
{
if(i == NONE)
return nullptr;
RYML_ASSERT(i >= 0 && i < m_cap);
return m_buf + i;
}
//! An if-less form of get() that demands a valid node index.
//! This function is implementation only; use at your own risk.
inline NodeData * _p(size_t i) { RYML_ASSERT(i != NONE && i >= 0 && i < m_cap); return m_buf + i; }
//! An if-less form of get() that demands a valid node index.
//! This function is implementation only; use at your own risk.
inline NodeData const * _p(size_t i) const { RYML_ASSERT(i != NONE && i >= 0 && i < m_cap); return m_buf + i; }
//! Get the id of the root node
size_t root_id() { if(m_cap == 0) { reserve(16); } RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; }
//! Get the id of the root node
size_t root_id() const { RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; }
//! Get a NodeRef of a node by id
NodeRef ref(size_t id);
//! Get a NodeRef of a node by id
NodeRef const ref(size_t id) const;
//! Get the root as a NodeRef
NodeRef rootref();
//! Get the root as a NodeRef
NodeRef const rootref() const;
//! find a root child by name, return it as a NodeRef
//! @note requires the root to be a map.
NodeRef operator[] (csubstr key);
//! find a root child by name, return it as a NodeRef
//! @note requires the root to be a map.
NodeRef const operator[] (csubstr key) const;
//! find a root child by index: return the root node's @p i-th child as a NodeRef
//! @note @i is NOT the node id, but the child's position
NodeRef operator[] (size_t i);
//! find a root child by index: return the root node's @p i-th child as a NodeRef
//! @note @i is NOT the node id, but the child's position
NodeRef const operator[] (size_t i) const;
//! get the i-th document of the stream
//! @note @i is NOT the node id, but the doc position within the stream
NodeRef docref(size_t i);
//! get the i-th document of the stream
//! @note @i is NOT the node id, but the doc position within the stream
NodeRef const docref(size_t i) const;
/** @} */
public:
/** @name node property getters */
/** @{ */
NodeType type(size_t node) const { return _p(node)->m_type; }
const char* type_str(size_t node) const { return NodeType::type_str(_p(node)->m_type); }
csubstr const& key (size_t node) const { RYML_ASSERT(has_key(node)); return _p(node)->m_key.scalar; }
csubstr const& key_tag (size_t node) const { RYML_ASSERT(has_key_tag(node)); return _p(node)->m_key.tag; }
csubstr const& key_ref (size_t node) const { RYML_ASSERT(is_key_ref(node) && ! has_key_anchor(node)); return _p(node)->m_key.anchor; }
csubstr const& key_anchor(size_t node) const { RYML_ASSERT( ! is_key_ref(node) && has_key_anchor(node)); return _p(node)->m_key.anchor; }
NodeScalar const& keysc (size_t node) const { RYML_ASSERT(has_key(node)); return _p(node)->m_key; }
csubstr const& val (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val.scalar; }
csubstr const& val_tag (size_t node) const { RYML_ASSERT(has_val_tag(node)); return _p(node)->m_val.tag; }
csubstr const& val_ref (size_t node) const { RYML_ASSERT(is_val_ref(node) && ! has_val_anchor(node)); return _p(node)->m_val.anchor; }
csubstr const& val_anchor(size_t node) const { RYML_ASSERT( ! is_val_ref(node) && has_val_anchor(node)); return _p(node)->m_val.anchor; }
NodeScalar const& valsc (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val; }
bool key_is_null(size_t node) const { RYML_ASSERT(has_key(node)); if(is_key_quoted(node)) return false; csubstr s = _p(node)->m_key.scalar; return s == nullptr || s == "~" || s == "null" || s == "Null" || s == "NULL"; }
bool val_is_null(size_t node) const { RYML_ASSERT(has_val(node)); if(is_val_quoted(node)) return false; csubstr s = _p(node)->m_val.scalar; return s == nullptr || s == "~" || s == "null" || s == "Null" || s == "NULL"; }
/** @} */
public:
/** @name node type predicates */
/** @{ */
C4_ALWAYS_INLINE bool is_stream(size_t node) const { return _p(node)->m_type.is_stream(); }
C4_ALWAYS_INLINE bool is_doc(size_t node) const { return _p(node)->m_type.is_doc(); }
C4_ALWAYS_INLINE bool is_container(size_t node) const { return _p(node)->m_type.is_container(); }
C4_ALWAYS_INLINE bool is_map(size_t node) const { return _p(node)->m_type.is_map(); }
C4_ALWAYS_INLINE bool is_seq(size_t node) const { return _p(node)->m_type.is_seq(); }
C4_ALWAYS_INLINE bool has_key(size_t node) const { return _p(node)->m_type.has_key(); }
C4_ALWAYS_INLINE bool has_val(size_t node) const { return _p(node)->m_type.has_val(); }
C4_ALWAYS_INLINE bool is_val(size_t node) const { return _p(node)->m_type.is_val(); }
C4_ALWAYS_INLINE bool is_keyval(size_t node) const { return _p(node)->m_type.is_keyval(); }
C4_ALWAYS_INLINE bool has_key_tag(size_t node) const { return _p(node)->m_type.has_key_tag(); }
C4_ALWAYS_INLINE bool has_val_tag(size_t node) const { return _p(node)->m_type.has_val_tag(); }
C4_ALWAYS_INLINE bool has_key_anchor(size_t node) const { return _p(node)->m_type.has_key_anchor(); }
C4_ALWAYS_INLINE bool is_key_anchor(size_t node) const { return _p(node)->m_type.is_key_anchor(); }
C4_ALWAYS_INLINE bool has_val_anchor(size_t node) const { return _p(node)->m_type.has_val_anchor(); }
C4_ALWAYS_INLINE bool is_val_anchor(size_t node) const { return _p(node)->m_type.is_val_anchor(); }
C4_ALWAYS_INLINE bool has_anchor(size_t node) const { return _p(node)->m_type.has_anchor(); }
C4_ALWAYS_INLINE bool is_anchor(size_t node) const { return _p(node)->m_type.is_anchor(); }
C4_ALWAYS_INLINE bool is_key_ref(size_t node) const { return _p(node)->m_type.is_key_ref(); }
C4_ALWAYS_INLINE bool is_val_ref(size_t node) const { return _p(node)->m_type.is_val_ref(); }
C4_ALWAYS_INLINE bool is_ref(size_t node) const { return _p(node)->m_type.is_ref(); }
C4_ALWAYS_INLINE bool is_anchor_or_ref(size_t node) const { return _p(node)->m_type.is_anchor_or_ref(); }
C4_ALWAYS_INLINE bool is_key_quoted(size_t node) const { return _p(node)->m_type.is_key_quoted(); }
C4_ALWAYS_INLINE bool is_val_quoted(size_t node) const { return _p(node)->m_type.is_val_quoted(); }
C4_ALWAYS_INLINE bool is_quoted(size_t node) const { return _p(node)->m_type.is_quoted(); }
C4_ALWAYS_INLINE bool parent_is_seq(size_t node) const { RYML_ASSERT(has_parent(node)); return is_seq(_p(node)->m_parent); }
C4_ALWAYS_INLINE bool parent_is_map(size_t node) const { RYML_ASSERT(has_parent(node)); return is_map(_p(node)->m_parent); }
/** true when key and val are empty, and has no children */
bool empty(size_t node) const { return ! has_children(node) && _p(node)->m_key.empty() && (( ! (_p(node)->m_type & VAL)) || _p(node)->m_val.empty()); }
/** true when the node has an anchor named a */
bool has_anchor(size_t node, csubstr a) const { return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; }
/** @} */
public:
/** @name hierarchy predicates */
/** @{ */
bool is_root(size_t node) const { RYML_ASSERT(_p(node)->m_parent != NONE || node == 0); return _p(node)->m_parent == NONE; }
bool has_parent(size_t node) const { return _p(node)->m_parent != NONE; }
bool has_child(size_t node, csubstr key) const { return find_child(node, key) != npos; }
bool has_child(size_t node, size_t ch) const { return child_pos(node, ch) != npos; }
bool has_children(size_t node) const { return _p(node)->m_first_child != NONE; }
bool has_sibling(size_t node, size_t sib) const { return is_root(node) ? sib==node : child_pos(_p(node)->m_parent, sib) != npos; }
bool has_sibling(size_t node, csubstr key) const { return find_sibling(node, key) != npos; }
/** counts with *this */
bool has_siblings(size_t /*node*/) const { return true; }
/** does not count with *this */
bool has_other_siblings(size_t node) const { return is_root(node) ? false : (_p(_p(node)->m_parent)->m_first_child != _p(_p(node)->m_parent)->m_last_child); }
/** @} */
public:
/** @name hierarchy getters */
/** @{ */
size_t parent(size_t node) const { return _p(node)->m_parent; }
size_t prev_sibling(size_t node) const { return _p(node)->m_prev_sibling; }
size_t next_sibling(size_t node) const { return _p(node)->m_next_sibling; }
/** O(#num_children) */
size_t num_children(size_t node) const;
size_t child_pos(size_t node, size_t ch) const;
size_t first_child(size_t node) const { return _p(node)->m_first_child; }
size_t last_child(size_t node) const { return _p(node)->m_last_child; }
size_t child(size_t node, size_t pos) const;
size_t find_child(size_t node, csubstr const& key) const;
/** O(#num_siblings) */
/** counts with this */
size_t num_siblings(size_t node) const { return is_root(node) ? 1 : num_children(_p(node)->m_parent); }
/** does not count with this */
size_t num_other_siblings(size_t node) const { size_t ns = num_siblings(node); RYML_ASSERT(ns > 0); return ns-1; }
size_t sibling_pos(size_t node, size_t sib) const { RYML_ASSERT( ! is_root(node) || node == root_id()); return child_pos(_p(node)->m_parent, sib); }
size_t first_sibling(size_t node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_first_child; }
size_t last_sibling(size_t node) const { return is_root(node) ? node : _p(_p(node)->m_parent)->m_last_child; }
size_t sibling(size_t node, size_t pos) const { return child(_p(node)->m_parent, pos); }
size_t find_sibling(size_t node, csubstr const& key) const { return find_child(_p(node)->m_parent, key); }
size_t doc(size_t i) const { size_t rid = root_id(); RYML_ASSERT(is_stream(rid)); return child(rid, i); } //!< gets the @p i document node index. requires that the root node is a stream.
/** @} */
public:
/** @name node modifiers */
/** @{ */
void to_keyval(size_t node, csubstr key, csubstr val, type_bits more_flags=0);
void to_map(size_t node, csubstr key, type_bits more_flags=0);
void to_seq(size_t node, csubstr key, type_bits more_flags=0);
void to_val(size_t node, csubstr val, type_bits more_flags=0);
void to_map(size_t node, type_bits more_flags=0);
void to_seq(size_t node, type_bits more_flags=0);
void to_doc(size_t node, type_bits more_flags=0);
void to_stream(size_t node, type_bits more_flags=0);
void set_key(size_t node, csubstr key) { RYML_ASSERT(has_key(node)); _p(node)->m_key.scalar = key; }
void set_val(size_t node, csubstr val) { RYML_ASSERT(has_val(node)); _p(node)->m_val.scalar = val; }
void set_key_tag(size_t node, csubstr tag) { RYML_ASSERT(has_key(node)); _p(node)->m_key.tag = tag; _add_flags(node, KEYTAG); }
void set_val_tag(size_t node, csubstr tag) { RYML_ASSERT(has_val(node) || is_container(node)); _p(node)->m_val.tag = tag; _add_flags(node, VALTAG); }
void set_key_anchor(size_t node, csubstr anchor) { RYML_ASSERT( ! is_key_ref(node)); _p(node)->m_key.anchor = anchor.triml('&'); _add_flags(node, KEYANCH); }
void set_val_anchor(size_t node, csubstr anchor) { RYML_ASSERT( ! is_val_ref(node)); _p(node)->m_val.anchor = anchor.triml('&'); _add_flags(node, VALANCH); }
void set_key_ref (size_t node, csubstr ref ) { RYML_ASSERT( ! has_key_anchor(node)); NodeData* C4_RESTRICT n = _p(node); n->m_key.set_ref_maybe_replacing_scalar(ref, n->m_type.has_key()); _add_flags(node, KEY|KEYREF); }
void set_val_ref (size_t node, csubstr ref ) { RYML_ASSERT( ! has_val_anchor(node)); NodeData* C4_RESTRICT n = _p(node); n->m_val.set_ref_maybe_replacing_scalar(ref, n->m_type.has_val()); _add_flags(node, VAL|VALREF); }
void rem_key_anchor(size_t node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYANCH); }
void rem_val_anchor(size_t node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALANCH); }
void rem_key_ref (size_t node) { _p(node)->m_key.anchor.clear(); _rem_flags(node, KEYREF); }
void rem_val_ref (size_t node) { _p(node)->m_val.anchor.clear(); _rem_flags(node, VALREF); }
void rem_anchor_ref(size_t node) { _p(node)->m_key.anchor.clear(); _p(node)->m_val.anchor.clear(); _rem_flags(node, KEYANCH|VALANCH|KEYREF|VALREF); }
/** @} */
public:
/** @name tree modifiers */
/** @{ */
/** reorder the tree in memory so that all the nodes are stored
* in a linear sequence when visited in depth-first order.
* This will invalidate existing ids, since the node id is its
* position in the node array. */
void reorder();
/** Resolve references (aliases <- anchors) in the tree.
*
* Dereferencing is opt-in; after parsing, Tree::resolve()
* has to be called explicitly for obtaining resolved references in the
* tree. This method will resolve all references and substitute the
* anchored values in place of the reference.
*
* This method first does a full traversal of the tree to gather all
* anchors and references in a separate collection, then it goes through
* that collection to locate the names, which it does by obeying the YAML
* standard diktat that "an alias node refers to the most recent node in
* the serialization having the specified anchor"
*
* So, depending on the number of anchor/alias nodes, this is a
* potentially expensive operation, with a best-case linear complexity
* (from the initial traversal). This potential cost is the reason for
* requiring an explicit call.
*/
void resolve();
/** @} */
public:
/** @name tag directives */
/** @{ */
void resolve_tags();
size_t num_tag_directives() const;
size_t add_tag_directive(TagDirective const& td);
void clear_tag_directives();
size_t resolve_tag(substr output, csubstr tag, size_t node_id) const;
csubstr resolve_tag_sub(substr output, csubstr tag, size_t node_id) const
{
size_t needed = resolve_tag(output, tag, node_id);
return needed <= output.len ? output.first(needed) : output;
}
using tag_directive_const_iterator = TagDirective const*;
tag_directive_const_iterator begin_tag_directives() const { return m_tag_directives; }
tag_directive_const_iterator end_tag_directives() const { return m_tag_directives + num_tag_directives(); }
struct TagDirectiveProxy
{
tag_directive_const_iterator b, e;
tag_directive_const_iterator begin() const { return b; }
tag_directive_const_iterator end() const { return e; }
};
TagDirectiveProxy tag_directives() const { return TagDirectiveProxy{begin_tag_directives(), end_tag_directives()}; }
/** @} */
public:
/** @name modifying hierarchy */
/** @{ */
/** create and insert a new child of "parent". insert after the (to-be)
* sibling "after", which must be a child of "parent". To insert as the
* first child, set after to NONE */
inline size_t insert_child(size_t parent, size_t after)
{
RYML_ASSERT(parent != NONE);
RYML_ASSERT(is_container(parent) || is_root(parent));
RYML_ASSERT(after == NONE || has_child(parent, after));
size_t child = _claim();
_set_hierarchy(child, parent, after);
return child;
}
inline size_t prepend_child(size_t parent) { return insert_child(parent, NONE); }
inline size_t append_child(size_t parent) { return insert_child(parent, last_child(parent)); }
public:
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnull-dereference"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
//! create and insert a new sibling of n. insert after "after"
inline size_t insert_sibling(size_t node, size_t after)
{
RYML_ASSERT(node != NONE);
RYML_ASSERT( ! is_root(node));
RYML_ASSERT(parent(node) != NONE);
RYML_ASSERT(after == NONE || (has_sibling(node, after) && has_sibling(after, node)));
RYML_ASSERT(get(node) != nullptr);
return insert_child(get(node)->m_parent, after);
}
inline size_t prepend_sibling(size_t node) { return insert_sibling(node, NONE); }
inline size_t append_sibling(size_t node) { return insert_sibling(node, last_sibling(node)); }
public:
/** remove an entire branch at once: ie remove the children and the node itself */
inline void remove(size_t node)
{
remove_children(node);
_release(node);
}
/** remove all the node's children, but keep the node itself */
void remove_children(size_t node);
/** change the @p type of the node to one of MAP, SEQ or VAL. @p
* type must have one and only one of MAP,SEQ,VAL; @p type may
* possibly have KEY, but if it does, then the @p node must also
* have KEY. Changing to the same type is a no-op. Otherwise,
* changing to a different type will initialize the node with an
* empty value of the desired type: changing to VAL will
* initialize with a null scalar (~), changing to MAP will
* initialize with an empty map ({}), and changing to SEQ will
* initialize with an empty seq ([]). */
bool change_type(size_t node, NodeType type);
bool change_type(size_t node, type_bits type)
{
return change_type(node, (NodeType)type);
}
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
public:
/** change the node's position in the parent */
void move(size_t node, size_t after);
/** change the node's parent and position */
void move(size_t node, size_t new_parent, size_t after);
/** change the node's parent and position to a different tree
* @return the index of the new node in the destination tree */
size_t move(Tree * src, size_t node, size_t new_parent, size_t after);
/** ensure the first node is a stream. Eg, change this tree
*
* DOCMAP
* MAP
* KEYVAL
* KEYVAL
* SEQ
* VAL
*
* to
*
* STREAM
* DOCMAP
* MAP
* KEYVAL
* KEYVAL
* SEQ
* VAL
*
* If the root is already a stream, this is a no-op.
*/
void set_root_as_stream();
public:
/** recursively duplicate a node from this tree into a new parent,
* placing it after one of its children
* @return the index of the copy */
size_t duplicate(size_t node, size_t new_parent, size_t after);
/** recursively duplicate a node from a different tree into a new parent,
* placing it after one of its children
* @return the index of the copy */
size_t duplicate(Tree const* src, size_t node, size_t new_parent, size_t after);
/** recursively duplicate the node's children (but not the node)
* @return the index of the last duplicated child */
size_t duplicate_children(size_t node, size_t parent, size_t after);
/** recursively duplicate the node's children (but not the node), where
* the node is from a different tree
* @return the index of the last duplicated child */
size_t duplicate_children(Tree const* src, size_t node, size_t parent, size_t after);
void duplicate_contents(size_t node, size_t where);
void duplicate_contents(Tree const* src, size_t node, size_t where);
/** duplicate the node's children (but not the node) in a new parent, but
* omit repetitions where a duplicated node has the same key (in maps) or
* value (in seqs). If one of the duplicated children has the same key
* (in maps) or value (in seqs) as one of the parent's children, the one
* that is placed closest to the end will prevail. */
size_t duplicate_children_no_rep(size_t node, size_t parent, size_t after);
size_t duplicate_children_no_rep(Tree const* src, size_t node, size_t parent, size_t after);
public:
void merge_with(Tree const* src, size_t src_node=NONE, size_t dst_root=NONE);
/** @} */
public:
/** @name internal string arena */
/** @{ */
/** get the current size of the tree's internal arena */
size_t arena_pos() const { return m_arena_pos; }
/** get the current arena */
substr arena() const { return m_arena.first(m_arena_pos); }
/** return true if the given substring is part of the tree's string arena */
bool in_arena(csubstr s) const
{
return m_arena.is_super(s);
}
/** serialize the given non-floating-point variable to the tree's arena, growing it as
* needed to accomodate the serialization.
* @note Growing the arena may cause relocation of the entire
* existing arena, and thus change the contents of individual nodes.
* @see alloc_arena() */
template<class T>
typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type
to_arena(T const& C4_RESTRICT a)
{
substr rem(m_arena.sub(m_arena_pos));
size_t num = to_chars(rem, a);
if(num > rem.len)
{
rem = _grow_arena(num);
num = to_chars(rem, a);
RYML_ASSERT(num <= rem.len);
}
rem = _request_span(num);
return rem;
}
/** serialize the given floating-point variable to the tree's arena, growing it as
* needed to accomodate the serialization.
* @note Growing the arena may cause relocation of the entire
* existing arena, and thus change the contents of individual nodes.
* @see alloc_arena() */
template<class T>
typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type
to_arena(T const& C4_RESTRICT a)
{
substr rem(m_arena.sub(m_arena_pos));
size_t num = to_chars_float(rem, a);
if(num > rem.len)
{
rem = _grow_arena(num);
num = to_chars_float(rem, a);
RYML_ASSERT(num <= rem.len);
}
rem = _request_span(num);
return rem;
}
/** copy the given substr to the tree's arena, growing it by the required size
* @note Growing the arena may cause relocation of the entire
* existing arena, and thus change the contents of individual nodes.
* @see alloc_arena() */
substr copy_to_arena(csubstr s)
{
substr cp = alloc_arena(s.len);
RYML_ASSERT(cp.len == s.len);
RYML_ASSERT(!s.overlaps(cp));
#if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
C4_SUPPRESS_WARNING_GCC_PUSH
C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow=") // no need for terminating \0
C4_SUPPRESS_WARNING_GCC( "-Wrestrict") // there's an assert to ensure no violation of restrict behavior
#endif
memcpy(cp.str, s.str, s.len);
#if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10)
C4_SUPPRESS_WARNING_GCC_POP
#endif
return cp;
}
/** grow the tree's string arena by the given size and return a substr
* of the added portion
* @note Growing the arena may cause relocation of the entire
* existing arena, and thus change the contents of individual nodes. */
substr alloc_arena(size_t sz)
{
if(sz > arena_slack())
_grow_arena(sz - arena_slack());
substr s = _request_span(sz);
return s;
}
/** ensure the tree's internal string arena is at least the given capacity
* @note Growing the arena may cause relocation of the entire
* existing arena, and thus change the contents of individual nodes. */
void reserve_arena(size_t arena_cap)
{
if(arena_cap > m_arena.len)
{
substr buf;
buf.str = (char*) m_callbacks.m_allocate(arena_cap, m_arena.str, m_callbacks.m_user_data);
buf.len = arena_cap;
if(m_arena.str)
{
RYML_ASSERT(m_arena.len >= 0);
_relocate(buf); // does a memcpy and changes nodes using the arena
m_callbacks.m_free(m_arena.str, m_arena.len, m_callbacks.m_user_data);
}
m_arena = buf;
}
}
/** @} */
private:
substr _grow_arena(size_t more)
{
size_t cap = m_arena_pos + more;
cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap;
cap = cap < 64 ? 64 : cap;
reserve_arena(cap);
return m_arena.sub(m_arena_pos);
}
substr _request_span(size_t sz)
{
substr s;
s = m_arena.sub(m_arena_pos, sz);
m_arena_pos += sz;
return s;
}
substr _relocated(csubstr s, substr next_arena) const
{
RYML_ASSERT(m_arena.is_super(s));
RYML_ASSERT(m_arena.sub(0, m_arena_pos).is_super(s));
auto pos = (s.str - m_arena.str);
substr r(next_arena.str + pos, s.len);
RYML_ASSERT(r.str - next_arena.str == pos);
RYML_ASSERT(next_arena.sub(0, m_arena_pos).is_super(r));
return r;
}
public:
/** @name lookup */
/** @{ */
struct lookup_result
{
size_t target;
size_t closest;
size_t path_pos;
csubstr path;
inline operator bool() const { return target != NONE; }
lookup_result() : target(NONE), closest(NONE), path_pos(0), path() {}
lookup_result(csubstr path_, size_t start) : target(NONE), closest(start), path_pos(0), path(path_) {}
/** get the part ot the input path that was resolved */
csubstr resolved() const;
/** get the part ot the input path that was unresolved */
csubstr unresolved() const;
};
/** for example foo.bar[0].baz */
lookup_result lookup_path(csubstr path, size_t start=NONE) const;
/** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
* the tree so that the corresponding lookup_path() would return the
* default value.
* @see lookup_path() */
size_t lookup_path_or_modify(csubstr default_value, csubstr path, size_t start=NONE);
/** defaulted lookup: lookup @p path; if the lookup fails, recursively modify
* the tree so that the corresponding lookup_path() would return the
* branch @p src_node (from the tree @p src).
* @see lookup_path() */
size_t lookup_path_or_modify(Tree const *src, size_t src_node, csubstr path, size_t start=NONE);
/** @} */
private:
struct _lookup_path_token
{
csubstr value;
NodeType type;
_lookup_path_token() : value(), type() {}
_lookup_path_token(csubstr v, NodeType t) : value(v), type(t) {}
inline operator bool() const { return type != NOTYPE; }
bool is_index() const { return value.begins_with('[') && value.ends_with(']'); }
};
size_t _lookup_path_or_create(csubstr path, size_t start);
void _lookup_path (lookup_result *r) const;
void _lookup_path_modify(lookup_result *r);
size_t _next_node (lookup_result *r, _lookup_path_token *parent) const;
size_t _next_node_modify(lookup_result *r, _lookup_path_token *parent);
void _advance(lookup_result *r, size_t more) const;
_lookup_path_token _next_token(lookup_result *r, _lookup_path_token const& parent) const;
private:
void _clear();
void _free();
void _copy(Tree const& that);
void _move(Tree & that);
void _relocate(substr next_arena);
public:
#if ! RYML_USE_ASSERT
C4_ALWAYS_INLINE void _check_next_flags(size_t, type_bits) {}
#else
void _check_next_flags(size_t node, type_bits f)
{
auto n = _p(node);
type_bits o = n->m_type; // old
C4_UNUSED(o);
if(f & MAP)
{
RYML_ASSERT_MSG((f & SEQ) == 0, "cannot mark simultaneously as map and seq");
RYML_ASSERT_MSG((f & VAL) == 0, "cannot mark simultaneously as map and val");
RYML_ASSERT_MSG((o & SEQ) == 0, "cannot turn a seq into a map; clear first");
RYML_ASSERT_MSG((o & VAL) == 0, "cannot turn a val into a map; clear first");
}
else if(f & SEQ)
{
RYML_ASSERT_MSG((f & MAP) == 0, "cannot mark simultaneously as seq and map");
RYML_ASSERT_MSG((f & VAL) == 0, "cannot mark simultaneously as seq and val");
RYML_ASSERT_MSG((o & MAP) == 0, "cannot turn a map into a seq; clear first");
RYML_ASSERT_MSG((o & VAL) == 0, "cannot turn a val into a seq; clear first");
}
if(f & KEY)
{
RYML_ASSERT(!is_root(node));
auto pid = parent(node); C4_UNUSED(pid);
RYML_ASSERT(is_map(pid));
}
if((f & VAL) && !is_root(node))
{
auto pid = parent(node); C4_UNUSED(pid);
RYML_ASSERT(is_map(pid) || is_seq(pid));
}
}
#endif
inline void _set_flags(size_t node, NodeType_e f) { _check_next_flags(node, f); _p(node)->m_type = f; }
inline void _set_flags(size_t node, type_bits f) { _check_next_flags(node, f); _p(node)->m_type = f; }
inline void _add_flags(size_t node, NodeType_e f) { NodeData *d = _p(node); type_bits fb = f | d->m_type; _check_next_flags(node, fb); d->m_type = (NodeType_e) fb; }
inline void _add_flags(size_t node, type_bits f) { NodeData *d = _p(node); f |= d->m_type; _check_next_flags(node, f); d->m_type = f; }
inline void _rem_flags(size_t node, NodeType_e f) { NodeData *d = _p(node); type_bits fb = d->m_type & ~f; _check_next_flags(node, fb); d->m_type = (NodeType_e) fb; }
inline void _rem_flags(size_t node, type_bits f) { NodeData *d = _p(node); f = d->m_type & ~f; _check_next_flags(node, f); d->m_type = f; }
void _set_key(size_t node, csubstr key, type_bits more_flags=0)
{
_p(node)->m_key.scalar = key;
_add_flags(node, KEY|more_flags);
}
void _set_key(size_t node, NodeScalar const& key, type_bits more_flags=0)
{
_p(node)->m_key = key;
_add_flags(node, KEY|more_flags);
}
void _set_val(size_t node, csubstr val, type_bits more_flags=0)
{
RYML_ASSERT(num_children(node) == 0);
RYML_ASSERT(!is_seq(node) && !is_map(node));
_p(node)->m_val.scalar = val;
_add_flags(node, VAL|more_flags);
}
void _set_val(size_t node, NodeScalar const& val, type_bits more_flags=0)
{
RYML_ASSERT(num_children(node) == 0);
RYML_ASSERT( ! is_container(node));
_p(node)->m_val = val;
_add_flags(node, VAL|more_flags);
}
void _set(size_t node, NodeInit const& i)
{
RYML_ASSERT(i._check());
NodeData *n = _p(node);
RYML_ASSERT(n->m_key.scalar.empty() || i.key.scalar.empty() || i.key.scalar == n->m_key.scalar);
_add_flags(node, i.type);
if(n->m_key.scalar.empty())
{
if( ! i.key.scalar.empty())
{
_set_key(node, i.key.scalar);
}
}
n->m_key.tag = i.key.tag;
n->m_val = i.val;
}
void _set_parent_as_container_if_needed(size_t in)
{
NodeData const* n = _p(in);
size_t ip = parent(in);
if(ip != NONE)
{
if( ! (is_seq(ip) || is_map(ip)))
{
if((in == first_child(ip)) && (in == last_child(ip)))
{
if( ! n->m_key.empty() || has_key(in))
{
_add_flags(ip, MAP);
}
else
{
_add_flags(ip, SEQ);
}
}
}
}
}
void _seq2map(size_t node)
{
RYML_ASSERT(is_seq(node));
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
{
NodeData *C4_RESTRICT ch = _p(i);
if(ch->m_type.is_keyval())
continue;
ch->m_type.add(KEY);
ch->m_key = ch->m_val;
}
auto *C4_RESTRICT n = _p(node);
n->m_type.rem(SEQ);
n->m_type.add(MAP);
}
size_t _do_reorder(size_t *node, size_t count);
void _swap(size_t n_, size_t m_);
void _swap_props(size_t n_, size_t m_);
void _swap_hierarchy(size_t n_, size_t m_);
void _copy_hierarchy(size_t dst_, size_t src_);
void _copy_props(size_t dst_, size_t src_)
{
auto & C4_RESTRICT dst = *_p(dst_);
auto const& C4_RESTRICT src = *_p(src_);
dst.m_type = src.m_type;
dst.m_key = src.m_key;
dst.m_val = src.m_val;
}
void _copy_props_wo_key(size_t dst_, size_t src_)
{
auto & C4_RESTRICT dst = *_p(dst_);
auto const& C4_RESTRICT src = *_p(src_);
dst.m_type = src.m_type;
dst.m_val = src.m_val;
}
void _copy_props(size_t dst_, Tree const* that_tree, size_t src_)
{
auto & C4_RESTRICT dst = *_p(dst_);
auto const& C4_RESTRICT src = *that_tree->_p(src_);
dst.m_type = src.m_type;
dst.m_key = src.m_key;
dst.m_val = src.m_val;
}
void _copy_props_wo_key(size_t dst_, Tree const* that_tree, size_t src_)
{
auto & C4_RESTRICT dst = *_p(dst_);
auto const& C4_RESTRICT src = *that_tree->_p(src_);
dst.m_type = src.m_type;
dst.m_val = src.m_val;
}
inline void _clear_type(size_t node)
{
_p(node)->m_type = NOTYPE;
}
inline void _clear(size_t node)
{
auto *C4_RESTRICT n = _p(node);
n->m_type = NOTYPE;
n->m_key.clear();
n->m_val.clear();
n->m_parent = NONE;
n->m_first_child = NONE;
n->m_last_child = NONE;
}
inline void _clear_key(size_t node)
{
_p(node)->m_key.clear();
_rem_flags(node, KEY);
}
inline void _clear_val(size_t node)
{
_p(node)->m_key.clear();
_rem_flags(node, VAL);
}
private:
void _clear_range(size_t first, size_t num);
size_t _claim();
void _claim_root();
void _release(size_t node);
void _free_list_add(size_t node);
void _free_list_rem(size_t node);
void _set_hierarchy(size_t node, size_t parent, size_t after_sibling);
void _rem_hierarchy(size_t node);
public:
// members are exposed, but you should NOT access them directly
NodeData * m_buf;
size_t m_cap;
size_t m_size;
size_t m_free_head;
size_t m_free_tail;
substr m_arena;
size_t m_arena_pos;
Callbacks m_callbacks;
TagDirective m_tag_directives[RYML_MAX_TAG_DIRECTIVES];
};
} // namespace yml
} // namespace c4
C4_SUPPRESS_WARNING_MSVC_POP
C4_SUPPRESS_WARNING_GCC_CLANG_POP
#endif /* _C4_YML_TREE_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/node.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_NODE_HPP_
#define _C4_YML_NODE_HPP_
/** @file node.hpp
* @see NodeRef */
//included above:
//#include <cstddef>
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//#include "c4/yml/tree.hpp"
#if !defined(C4_YML_TREE_HPP_) && !defined(_C4_YML_TREE_HPP_)
#error "amalgamate: file c4/yml/tree.hpp must have been included at this point"
#endif /* C4_YML_TREE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/base64.hpp
//#include "c4/base64.hpp"
#if !defined(C4_BASE64_HPP_) && !defined(_C4_BASE64_HPP_)
#error "amalgamate: file c4/base64.hpp must have been included at this point"
#endif /* C4_BASE64_HPP_ */
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
# pragma warning(disable: 4296/*expression is always 'boolean_value'*/)
#endif
namespace c4 {
namespace yml {
template<class K> struct Key { K & k; };
template<> struct Key<fmt::const_base64_wrapper> { fmt::const_base64_wrapper wrapper; };
template<> struct Key<fmt::base64_wrapper> { fmt::base64_wrapper wrapper; };
template<class K> C4_ALWAYS_INLINE Key<K> key(K & k) { return Key<K>{k}; }
C4_ALWAYS_INLINE Key<fmt::const_base64_wrapper> key(fmt::const_base64_wrapper w) { return {w}; }
C4_ALWAYS_INLINE Key<fmt::base64_wrapper> key(fmt::base64_wrapper w) { return {w}; }
template<class T> void write(NodeRef *n, T const& v);
template<class T>
typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type
read(NodeRef const& n, T *v);
template<class T>
typename std::enable_if< std::is_floating_point<T>::value, bool>::type
read(NodeRef const& n, T *v);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a reference to a node in an existing yaml tree, offering a more
* convenient API than the index-based API used in the tree. */
class RYML_EXPORT NodeRef
{
private:
// require valid: a helper macro, undefined at the end
#define _C4RV() RYML_ASSERT(valid() && !is_seed())
Tree *C4_RESTRICT m_tree;
size_t m_id;
/** This member is used to enable lazy operator[] writing. When a child
* with a key or index is not found, m_id is set to the id of the parent
* and the asked-for key or index are stored in this member until a write
* does happen. Then it is given as key or index for creating the child.
* When a key is used, the csubstr stores it (so the csubstr's string is
* non-null and the csubstr's size is different from NONE). When an index is
* used instead, the csubstr's string is set to null, and only the csubstr's
* size is set to a value different from NONE. Otherwise, when operator[]
* does find the child then this member is empty: the string is null and
* the size is NONE. */
csubstr m_seed;
public:
/** @name node construction */
/** @{ */
NodeRef() : m_tree(nullptr), m_id(NONE), m_seed() { _clear_seed(); }
NodeRef(Tree &t) : m_tree(&t), m_id(t .root_id()), m_seed() { _clear_seed(); }
NodeRef(Tree *t) : m_tree(t ), m_id(t->root_id()), m_seed() { _clear_seed(); }
NodeRef(Tree *t, size_t id) : m_tree(t), m_id(id), m_seed() { _clear_seed(); }
NodeRef(Tree *t, size_t id, size_t seed_pos) : m_tree(t), m_id(id), m_seed() { m_seed.str = nullptr; m_seed.len = seed_pos; }
NodeRef(Tree *t, size_t id, csubstr seed_key) : m_tree(t), m_id(id), m_seed(seed_key) {}
NodeRef(std::nullptr_t) : m_tree(nullptr), m_id(NONE), m_seed() {}
NodeRef(NodeRef const&) = default;
NodeRef(NodeRef &&) = default;
NodeRef& operator= (NodeRef const&) = default;
NodeRef& operator= (NodeRef &&) = default;
/** @} */
public:
inline Tree * tree() { return m_tree; }
inline Tree const* tree() const { return m_tree; }
inline size_t id() const { return m_id; }
inline NodeData * get() { return m_tree->get(m_id); }
inline NodeData const* get() const { return m_tree->get(m_id); }
inline bool operator== (NodeRef const& that) const { _C4RV(); RYML_ASSERT(that.valid() && !that.is_seed()); RYML_ASSERT(that.m_tree == m_tree); return m_id == that.m_id; }
inline bool operator!= (NodeRef const& that) const { return ! this->operator==(that); }
inline bool operator== (std::nullptr_t) const { return m_tree == nullptr || m_id == NONE || is_seed(); }
inline bool operator!= (std::nullptr_t) const { return ! this->operator== (nullptr); }
inline bool operator== (csubstr val) const { _C4RV(); RYML_ASSERT(has_val()); return m_tree->val(m_id) == val; }
inline bool operator!= (csubstr val) const { _C4RV(); RYML_ASSERT(has_val()); return m_tree->val(m_id) != val; }
//inline operator bool () const { return m_tree == nullptr || m_id == NONE || is_seed(); }
public:
inline bool valid() const { return m_tree != nullptr && m_id != NONE; }
inline bool is_seed() const { return m_seed.str != nullptr || m_seed.len != NONE; }
inline void _clear_seed() { /*do this manually or an assert is triggered*/ m_seed.str = nullptr; m_seed.len = NONE; }
public:
/** @name node property getters */
/** @{ */
inline NodeType type() const { _C4RV(); return m_tree->type(m_id); }
inline const char* type_str() const { _C4RV(); RYML_ASSERT(valid() && ! is_seed()); return m_tree->type_str(m_id); }
inline csubstr key() const { _C4RV(); return m_tree->key(m_id); }
inline csubstr key_tag() const { _C4RV(); return m_tree->key_tag(m_id); }
inline csubstr key_ref() const { _C4RV(); return m_tree->key_ref(m_id); }
inline csubstr key_anchor() const { _C4RV(); return m_tree->key_anchor(m_id); }
inline NodeScalar keysc() const { _C4RV(); return m_tree->keysc(m_id); }
inline csubstr val() const { _C4RV(); return m_tree->val(m_id); }
inline csubstr val_tag() const { _C4RV(); return m_tree->val_tag(m_id); }
inline csubstr val_ref() const { _C4RV(); return m_tree->val_ref(m_id); }
inline csubstr val_anchor() const { _C4RV(); return m_tree->val_anchor(m_id); }
inline NodeScalar valsc() const { _C4RV(); return m_tree->valsc(m_id); }
inline bool key_is_null() const { _C4RV(); return m_tree->key_is_null(m_id); }
inline bool val_is_null() const { _C4RV(); return m_tree->val_is_null(m_id); }
/** decode the base64-encoded key deserialize and assign the
* decoded blob to the given buffer/
* @return the size of base64-decoded blob */
size_t deserialize_key(fmt::base64_wrapper v) const;
/** decode the base64-encoded key deserialize and assign the
* decoded blob to the given buffer/
* @return the size of base64-decoded blob */
size_t deserialize_val(fmt::base64_wrapper v) const;
/** @} */
public:
/** @name node property predicates */
/** @{ */
C4_ALWAYS_INLINE bool is_stream() const { _C4RV(); return m_tree->is_stream(m_id); }
C4_ALWAYS_INLINE bool is_doc() const { _C4RV(); return m_tree->is_doc(m_id); }
C4_ALWAYS_INLINE bool is_container() const { _C4RV(); return m_tree->is_container(m_id); }
C4_ALWAYS_INLINE bool is_map() const { _C4RV(); return m_tree->is_map(m_id); }
C4_ALWAYS_INLINE bool is_seq() const { _C4RV(); return m_tree->is_seq(m_id); }
C4_ALWAYS_INLINE bool has_val() const { _C4RV(); return m_tree->has_val(m_id); }
C4_ALWAYS_INLINE bool has_key() const { _C4RV(); return m_tree->has_key(m_id); }
C4_ALWAYS_INLINE bool is_val() const { _C4RV(); return m_tree->is_val(m_id); }
C4_ALWAYS_INLINE bool is_keyval() const { _C4RV(); return m_tree->is_keyval(m_id); }
C4_ALWAYS_INLINE bool has_key_tag() const { _C4RV(); return m_tree->has_key_tag(m_id); }
C4_ALWAYS_INLINE bool has_val_tag() const { _C4RV(); return m_tree->has_val_tag(m_id); }
C4_ALWAYS_INLINE bool has_key_anchor() const { _C4RV(); return m_tree->has_key_anchor(m_id); }
C4_ALWAYS_INLINE bool is_key_anchor() const { _C4RV(); return m_tree->is_key_anchor(m_id); }
C4_ALWAYS_INLINE bool has_val_anchor() const { _C4RV(); return m_tree->has_val_anchor(m_id); }
C4_ALWAYS_INLINE bool is_val_anchor() const { _C4RV(); return m_tree->is_val_anchor(m_id); }
C4_ALWAYS_INLINE bool has_anchor() const { _C4RV(); return m_tree->has_anchor(m_id); }
C4_ALWAYS_INLINE bool is_anchor() const { _C4RV(); return m_tree->is_anchor(m_id); }
C4_ALWAYS_INLINE bool is_key_ref() const { _C4RV(); return m_tree->is_key_ref(m_id); }
C4_ALWAYS_INLINE bool is_val_ref() const { _C4RV(); return m_tree->is_val_ref(m_id); }
C4_ALWAYS_INLINE bool is_ref() const { _C4RV(); return m_tree->is_ref(m_id); }
C4_ALWAYS_INLINE bool is_anchor_or_ref() const { _C4RV(); return m_tree->is_anchor_or_ref(m_id); }
C4_ALWAYS_INLINE bool is_key_quoted() const { _C4RV(); return m_tree->is_key_quoted(m_id); }
C4_ALWAYS_INLINE bool is_val_quoted() const { _C4RV(); return m_tree->is_val_quoted(m_id); }
C4_ALWAYS_INLINE bool is_quoted() const { _C4RV(); return m_tree->is_quoted(m_id); }
C4_ALWAYS_INLINE bool parent_is_seq() const { _C4RV(); return m_tree->parent_is_seq(m_id); }
C4_ALWAYS_INLINE bool parent_is_map() const { _C4RV(); return m_tree->parent_is_map(m_id); }
/** true when name and value are empty, and has no children */
C4_ALWAYS_INLINE bool empty() const { _C4RV(); return m_tree->empty(m_id); }
/** @} */
public:
/** @name hierarchy predicates */
/** @{ */
inline bool is_root() const { _C4RV(); return m_tree->is_root(m_id); }
inline bool has_parent() const { _C4RV(); return m_tree->has_parent(m_id); }
inline bool has_child(NodeRef const& ch) const { _C4RV(); return m_tree->has_child(m_id, ch.m_id); }
inline bool has_child(csubstr name) const { _C4RV(); return m_tree->has_child(m_id, name); }
inline bool has_children() const { _C4RV(); return m_tree->has_children(m_id); }
inline bool has_sibling(NodeRef const& n) const { _C4RV(); return m_tree->has_sibling(m_id, n.m_id); }
inline bool has_sibling(csubstr name) const { _C4RV(); return m_tree->has_sibling(m_id, name); }
/** counts with this */
inline bool has_siblings() const { _C4RV(); return m_tree->has_siblings(m_id); }
/** does not count with this */
inline bool has_other_siblings() const { _C4RV(); return m_tree->has_other_siblings(m_id); }
/** @} */
public:
/** @name hierarchy getters */
/** @{ */
NodeRef parent() { _C4RV(); return {m_tree, m_tree->parent(m_id)}; }
NodeRef const parent() const { _C4RV(); return {m_tree, m_tree->parent(m_id)}; }
NodeRef prev_sibling() { _C4RV(); return {m_tree, m_tree->prev_sibling(m_id)}; }
NodeRef const prev_sibling() const { _C4RV(); return {m_tree, m_tree->prev_sibling(m_id)}; }
NodeRef next_sibling() { _C4RV(); return {m_tree, m_tree->next_sibling(m_id)}; }
NodeRef const next_sibling() const { _C4RV(); return {m_tree, m_tree->next_sibling(m_id)}; }
/** O(#num_children) */
size_t num_children() const { _C4RV(); return m_tree->num_children(m_id); }
size_t child_pos(NodeRef const& n) const { _C4RV(); return m_tree->child_pos(m_id, n.m_id); }
NodeRef first_child() { _C4RV(); return {m_tree, m_tree->first_child(m_id)}; }
NodeRef const first_child() const { _C4RV(); return {m_tree, m_tree->first_child(m_id)}; }
NodeRef last_child () { _C4RV(); return {m_tree, m_tree->last_child (m_id)}; }
NodeRef const last_child () const { _C4RV(); return {m_tree, m_tree->last_child (m_id)}; }
NodeRef child(size_t pos) { _C4RV(); return {m_tree, m_tree->child(m_id, pos)}; }
NodeRef const child(size_t pos) const { _C4RV(); return {m_tree, m_tree->child(m_id, pos)}; }
NodeRef find_child(csubstr name) { _C4RV(); return {m_tree, m_tree->find_child(m_id, name)}; }
NodeRef const find_child(csubstr name) const { _C4RV(); return {m_tree, m_tree->find_child(m_id, name)}; }
/** O(#num_siblings) */
size_t num_siblings() const { _C4RV(); return m_tree->num_siblings(m_id); }
size_t num_other_siblings() const { _C4RV(); return m_tree->num_other_siblings(m_id); }
size_t sibling_pos(NodeRef const& n) const { _C4RV(); return m_tree->child_pos(m_tree->parent(m_id), n.m_id); }
NodeRef first_sibling() { _C4RV(); return {m_tree, m_tree->first_sibling(m_id)}; }
NodeRef const first_sibling() const { _C4RV(); return {m_tree, m_tree->first_sibling(m_id)}; }
NodeRef last_sibling () { _C4RV(); return {m_tree, m_tree->last_sibling(m_id)}; }
NodeRef const last_sibling () const { _C4RV(); return {m_tree, m_tree->last_sibling(m_id)}; }
NodeRef sibling(size_t pos) { _C4RV(); return {m_tree, m_tree->sibling(m_id, pos)}; }
NodeRef const sibling(size_t pos) const { _C4RV(); return {m_tree, m_tree->sibling(m_id, pos)}; }
NodeRef find_sibling(csubstr name) { _C4RV(); return {m_tree, m_tree->find_sibling(m_id, name)}; }
NodeRef const find_sibling(csubstr name) const { _C4RV(); return {m_tree, m_tree->find_sibling(m_id, name)}; }
NodeRef doc(size_t num) { _C4RV(); return {m_tree, m_tree->doc(num)}; }
NodeRef const doc(size_t num) const { _C4RV(); return {m_tree, m_tree->doc(num)}; }
/** @} */
public:
/** @name node modifiers */
/** @{ */
void change_type(NodeType t) { _C4RV(); m_tree->change_type(m_id, t); }
void set_type(NodeType t) { _C4RV(); m_tree->_set_flags(m_id, t); }
void set_key(csubstr key) { _C4RV(); m_tree->_set_key(m_id, key); }
void set_val(csubstr val) { _C4RV(); m_tree->_set_val(m_id, val); }
void set_key_tag(csubstr key_tag) { _C4RV(); m_tree->set_key_tag(m_id, key_tag); }
void set_val_tag(csubstr val_tag) { _C4RV(); m_tree->set_val_tag(m_id, val_tag); }
void set_key_anchor(csubstr key_anchor) { _C4RV(); m_tree->set_key_anchor(m_id, key_anchor); }
void set_val_anchor(csubstr val_anchor) { _C4RV(); m_tree->set_val_anchor(m_id, val_anchor); }
void set_key_ref(csubstr key_ref) { _C4RV(); m_tree->set_key_ref(m_id, key_ref); }
void set_val_ref(csubstr val_ref) { _C4RV(); m_tree->set_val_ref(m_id, val_ref); }
template<class T>
size_t set_key_serialized(T const& C4_RESTRICT k)
{
_C4RV();
csubstr s = m_tree->to_arena(k);
m_tree->_set_key(m_id, s);
return s.len;
}
template<class T>
size_t set_val_serialized(T const& C4_RESTRICT v)
{
_C4RV();
csubstr s = m_tree->to_arena(v);
m_tree->_set_val(m_id, s);
return s.len;
}
/** encode a blob as base64, then assign the result to the node's key
* @return the size of base64-encoded blob */
size_t set_key_serialized(fmt::const_base64_wrapper w);
/** encode a blob as base64, then assign the result to the node's val
* @return the size of base64-encoded blob */
size_t set_val_serialized(fmt::const_base64_wrapper w);
public:
inline void clear()
{
if(is_seed())
return;
m_tree->remove_children(m_id);
m_tree->_clear(m_id);
}
inline void clear_key()
{
if(is_seed())
return;
m_tree->_clear_key(m_id);
}
inline void clear_val()
{
if(is_seed())
return;
m_tree->_clear_val(m_id);
}
inline void clear_children()
{
if(is_seed())
return;
m_tree->remove_children(m_id);
}
/** @} */
public:
/** hierarchy getters */
/** @{ */
/** O(num_children) */
NodeRef operator[] (csubstr k)
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
size_t ch = m_tree->find_child(m_id, k);
NodeRef r = ch != NONE ? NodeRef(m_tree, ch) : NodeRef(m_tree, m_id, k);
return r;
}
/** O(num_children) */
NodeRef const operator[] (csubstr k) const
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
size_t ch = m_tree->find_child(m_id, k);
RYML_ASSERT(ch != NONE);
NodeRef const r(m_tree, ch);
return r;
}
/** O(num_children) */
NodeRef operator[] (size_t pos)
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
size_t ch = m_tree->child(m_id, pos);
NodeRef r = ch != NONE ? NodeRef(m_tree, ch) : NodeRef(m_tree, m_id, pos);
return r;
}
/** O(num_children) */
NodeRef const operator[] (size_t pos) const
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
size_t ch = m_tree->child(m_id, pos);
RYML_ASSERT(ch != NONE);
NodeRef const r(m_tree, ch);
return r;
}
/** @} */
public:
/** node modification */
/** @{ */
void create() { _apply_seed(); }
inline void operator= (NodeType_e t)
{
_apply_seed();
m_tree->_add_flags(m_id, t);
}
inline void operator|= (NodeType_e t)
{
_apply_seed();
m_tree->_add_flags(m_id, t);
}
inline void operator= (NodeInit const& v)
{
_apply_seed();
_apply(v);
}
inline void operator= (NodeScalar const& v)
{
_apply_seed();
_apply(v);
}
inline void operator= (csubstr v)
{
_apply_seed();
_apply(v);
}
template<size_t N>
inline void operator= (const char (&v)[N])
{
_apply_seed();
csubstr sv;
sv.assign<N>(v);
_apply(sv);
}
/** @} */
public:
/** serialize a variable to the arena */
template<class T>
inline csubstr to_arena(T const& C4_RESTRICT s) const
{
_C4RV();
return m_tree->to_arena(s);
}
/** serialize a variable, then assign the result to the node's val */
inline NodeRef& operator<< (csubstr s)
{
// this overload is needed to prevent ambiguity (there's also
// operator<< for writing a substr to a stream)
_apply_seed();
write(this, s);
RYML_ASSERT(val() == s);
return *this;
}
template<class T>
inline NodeRef& operator<< (T const& C4_RESTRICT v)
{
_apply_seed();
write(this, v);
return *this;
}
template<class T>
inline NodeRef const& operator>> (T &v) const
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
RYML_ASSERT(get() != nullptr);
if( ! read(*this, &v))
{
c4::yml::error("could not deserialize value");
}
return *this;
}
public:
/** serialize a variable, then assign the result to the node's key */
template<class T>
inline NodeRef& operator<< (Key<const T> const& C4_RESTRICT v)
{
_apply_seed();
set_key_serialized(v.k);
return *this;
}
/** serialize a variable, then assign the result to the node's key */
template<class T>
inline NodeRef& operator<< (Key<T> const& C4_RESTRICT v)
{
_apply_seed();
set_key_serialized(v.k);
return *this;
}
/** deserialize the node's key to the given variable */
template<class T>
inline NodeRef const& operator>> (Key<T> v) const
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
RYML_ASSERT(get() != nullptr);
from_chars(key(), &v.k);
return *this;
}
public:
NodeRef& operator<< (Key<fmt::const_base64_wrapper> w)
{
set_key_serialized(w.wrapper);
return *this;
}
NodeRef& operator<< (fmt::const_base64_wrapper w)
{
set_val_serialized(w);
return *this;
}
NodeRef const& operator>> (Key<fmt::base64_wrapper> w) const
{
deserialize_key(w.wrapper);
return *this;
}
NodeRef const& operator>> (fmt::base64_wrapper w) const
{
deserialize_val(w);
return *this;
}
public:
template<class T>
void get_if(csubstr name, T *var) const
{
auto ch = find_child(name);
if(ch.valid())
{
ch >> *var;
}
}
template<class T>
void get_if(csubstr name, T *var, T fallback) const
{
auto ch = find_child(name);
if(ch.valid())
{
ch >> *var;
}
else
{
*var = fallback;
}
}
private:
void _apply_seed()
{
if(m_seed.str) // we have a seed key: use it to create the new child
{
//RYML_ASSERT(i.key.scalar.empty() || m_key == i.key.scalar || m_key.empty());
m_id = m_tree->append_child(m_id);
m_tree->_set_key(m_id, m_seed);
m_seed.str = nullptr;
m_seed.len = NONE;
}
else if(m_seed.len != NONE) // we have a seed index: create a child at that position
{
RYML_ASSERT(m_tree->num_children(m_id) == m_seed.len);
m_id = m_tree->append_child(m_id);
m_seed.str = nullptr;
m_seed.len = NONE;
}
else
{
RYML_ASSERT(valid());
}
}
inline void _apply(csubstr v)
{
m_tree->_set_val(m_id, v);
}
inline void _apply(NodeScalar const& v)
{
m_tree->_set_val(m_id, v);
}
inline void _apply(NodeInit const& i)
{
m_tree->_set(m_id, i);
}
public:
inline NodeRef insert_child(NodeRef after)
{
_C4RV();
RYML_ASSERT(after.m_tree == m_tree);
NodeRef r(m_tree, m_tree->insert_child(m_id, after.m_id));
return r;
}
inline NodeRef insert_child(NodeInit const& i, NodeRef after)
{
_C4RV();
RYML_ASSERT(after.m_tree == m_tree);
NodeRef r(m_tree, m_tree->insert_child(m_id, after.m_id));
r._apply(i);
return r;
}
inline NodeRef prepend_child()
{
_C4RV();
NodeRef r(m_tree, m_tree->insert_child(m_id, NONE));
return r;
}
inline NodeRef prepend_child(NodeInit const& i)
{
_C4RV();
NodeRef r(m_tree, m_tree->insert_child(m_id, NONE));
r._apply(i);
return r;
}
inline NodeRef append_child()
{
_C4RV();
NodeRef r(m_tree, m_tree->append_child(m_id));
return r;
}
inline NodeRef append_child(NodeInit const& i)
{
_C4RV();
NodeRef r(m_tree, m_tree->append_child(m_id));
r._apply(i);
return r;
}
public:
inline NodeRef insert_sibling(NodeRef const after)
{
_C4RV();
RYML_ASSERT(after.m_tree == m_tree);
NodeRef r(m_tree, m_tree->insert_sibling(m_id, after.m_id));
return r;
}
inline NodeRef insert_sibling(NodeInit const& i, NodeRef const after)
{
_C4RV();
RYML_ASSERT(after.m_tree == m_tree);
NodeRef r(m_tree, m_tree->insert_sibling(m_id, after.m_id));
r._apply(i);
return r;
}
inline NodeRef prepend_sibling()
{
_C4RV();
NodeRef r(m_tree, m_tree->prepend_sibling(m_id));
return r;
}
inline NodeRef prepend_sibling(NodeInit const& i)
{
_C4RV();
NodeRef r(m_tree, m_tree->prepend_sibling(m_id));
r._apply(i);
return r;
}
inline NodeRef append_sibling()
{
_C4RV();
NodeRef r(m_tree, m_tree->append_sibling(m_id));
return r;
}
inline NodeRef append_sibling(NodeInit const& i)
{
_C4RV();
NodeRef r(m_tree, m_tree->append_sibling(m_id));
r._apply(i);
return r;
}
public:
inline void remove_child(NodeRef & child)
{
_C4RV();
RYML_ASSERT(has_child(child));
RYML_ASSERT(child.parent().id() == id());
m_tree->remove(child.id());
child.clear();
}
//! remove the nth child of this node
inline void remove_child(size_t pos)
{
_C4RV();
RYML_ASSERT(pos >= 0 && pos < num_children());
size_t child = m_tree->child(m_id, pos);
RYML_ASSERT(child != NONE);
m_tree->remove(child);
}
//! remove a child by name
inline void remove_child(csubstr key)
{
_C4RV();
size_t child = m_tree->find_child(m_id, key);
RYML_ASSERT(child != NONE);
m_tree->remove(child);
}
public:
/** change the node's position within its parent */
inline void move(NodeRef const after)
{
_C4RV();
m_tree->move(m_id, after.m_id);
}
/** move the node to a different parent, which may belong to a different
* tree. When this is the case, then this node's tree pointer is reset to
* the tree of the parent node. */
inline void move(NodeRef const parent, NodeRef const after)
{
_C4RV();
RYML_ASSERT(parent.m_tree == after.m_tree);
if(parent.m_tree == m_tree)
{
m_tree->move(m_id, parent.m_id, after.m_id);
}
else
{
parent.m_tree->move(m_tree, m_id, parent.m_id, after.m_id);
m_tree = parent.m_tree;
}
}
inline NodeRef duplicate(NodeRef const parent, NodeRef const after) const
{
_C4RV();
RYML_ASSERT(parent.m_tree == after.m_tree);
if(parent.m_tree == m_tree)
{
size_t dup = m_tree->duplicate(m_id, parent.m_id, after.m_id);
NodeRef r(m_tree, dup);
return r;
}
else
{
size_t dup = parent.m_tree->duplicate(m_tree, m_id, parent.m_id, after.m_id);
NodeRef r(parent.m_tree, dup);
return r;
}
}
inline void duplicate_children(NodeRef const parent, NodeRef const after) const
{
_C4RV();
RYML_ASSERT(parent.m_tree == after.m_tree);
if(parent.m_tree == m_tree)
{
m_tree->duplicate_children(m_id, parent.m_id, after.m_id);
}
else
{
parent.m_tree->duplicate_children(m_tree, m_id, parent.m_id, after.m_id);
}
}
private:
template<class Nd>
struct child_iterator
{
Tree * m_tree;
size_t m_child_id;
using value_type = NodeRef;
child_iterator(Tree * t, size_t id) : m_tree(t), m_child_id(id) {}
child_iterator& operator++ () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->next_sibling(m_child_id); return *this; }
child_iterator& operator-- () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->prev_sibling(m_child_id); return *this; }
Nd operator* () const { return Nd(m_tree, m_child_id); }
Nd operator-> () const { return Nd(m_tree, m_child_id); }
bool operator!= (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id != that.m_child_id; }
bool operator== (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id == that.m_child_id; }
};
public:
using iterator = child_iterator< NodeRef>;
using const_iterator = child_iterator<const NodeRef>;
inline iterator begin() { return iterator(m_tree, m_tree->first_child(m_id)); }
inline iterator end () { return iterator(m_tree, NONE); }
inline const_iterator begin() const { return const_iterator(m_tree, m_tree->first_child(m_id)); }
inline const_iterator end () const { return const_iterator(m_tree, NONE); }
private:
template<class Nd>
struct children_view_
{
using n_iterator = child_iterator<Nd>;
n_iterator b, e;
inline children_view_(n_iterator const& b_, n_iterator const& e_) : b(b_), e(e_) {}
inline n_iterator begin() const { return b; }
inline n_iterator end () const { return e; }
};
public:
using children_view = children_view_< NodeRef>;
using const_children_view = children_view_<const NodeRef>;
children_view children() { return children_view(begin(), end()); }
const_children_view children() const { return const_children_view(begin(), end()); }
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnull-dereference"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
children_view siblings() { if(is_root()) { return children_view(end(), end()); } else { size_t p = get()->m_parent; return children_view(iterator(m_tree, m_tree->get(p)->m_first_child), iterator(m_tree, NONE)); } }
const_children_view siblings() const { if(is_root()) { return const_children_view(end(), end()); } else { size_t p = get()->m_parent; return const_children_view(const_iterator(m_tree, m_tree->get(p)->m_first_child), const_iterator(m_tree, NONE)); } }
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
public:
/** visit every child node calling fn(node) */
template<class Visitor> bool visit(Visitor fn, size_t indentation_level=0, bool skip_root=true);
/** visit every child node calling fn(node) */
template<class Visitor> bool visit(Visitor fn, size_t indentation_level=0, bool skip_root=true) const;
/** visit every child node calling fn(node, level) */
template<class Visitor> bool visit_stacked(Visitor fn, size_t indentation_level=0, bool skip_root=true);
/** visit every child node calling fn(node, level) */
template<class Visitor> bool visit_stacked(Visitor fn, size_t indentation_level=0, bool skip_root=true) const;
#undef _C4RV
};
//-----------------------------------------------------------------------------
template<class T>
inline void write(NodeRef *n, T const& v)
{
n->set_val_serialized(v);
}
template<class T>
typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type
inline read(NodeRef const& n, T *v)
{
return from_chars(n.val(), v);
}
template<class T>
typename std::enable_if< std::is_floating_point<T>::value, bool>::type
inline read(NodeRef const& n, T *v)
{
return from_chars_float(n.val(), v);
}
//-----------------------------------------------------------------------------
template<class Visitor>
bool NodeRef::visit(Visitor fn, size_t indentation_level, bool skip_root)
{
return const_cast<NodeRef const*>(this)->visit(fn, indentation_level, skip_root);
}
template<class Visitor>
bool NodeRef::visit(Visitor fn, size_t indentation_level, bool skip_root) const
{
size_t increment = 0;
if( ! (is_root() && skip_root))
{
if(fn(this, indentation_level))
{
return true;
}
++increment;
}
if(has_children())
{
for(auto ch : children())
{
if(ch.visit(fn, indentation_level + increment)) // no need to forward skip_root as it won't be root
{
return true;
}
}
}
return false;
}
template<class Visitor>
bool NodeRef::visit_stacked(Visitor fn, size_t indentation_level, bool skip_root)
{
return const_cast< NodeRef const* >(this)->visit_stacked(fn, indentation_level, skip_root);
}
template<class Visitor>
bool NodeRef::visit_stacked(Visitor fn, size_t indentation_level, bool skip_root) const
{
size_t increment = 0;
if( ! (is_root() && skip_root))
{
if(fn(this, indentation_level))
{
return true;
}
++increment;
}
if(has_children())
{
fn.push(this, indentation_level);
for(auto ch : children())
{
if(ch.visit(fn, indentation_level + increment)) // no need to forward skip_root as it won't be root
{
fn.pop(this, indentation_level);
return true;
}
}
fn.pop(this, indentation_level);
}
return false;
}
} // namespace yml
} // namespace c4
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
#endif /* _C4_YML_NODE_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/writer.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/writer.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_WRITER_HPP_
#define _C4_YML_WRITER_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "./common.hpp"
#endif
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/substr.hpp
//#include <c4/substr.hpp>
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
//included above:
//#include <stdio.h> // fwrite(), fputc()
//included above:
//#include <string.h> // memcpy()
namespace c4 {
namespace yml {
/** Repeat-Character: a character to be written a number of times. */
struct RepC
{
char c;
size_t num_times;
};
inline RepC indent_to(size_t num_levels)
{
return {' ', size_t(2) * num_levels};
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A writer that outputs to a file. Defaults to stdout. */
struct WriterFile
{
FILE * m_file;
size_t m_pos;
WriterFile(FILE *f = nullptr) : m_file(f ? f : stdout), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
{
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
fwrite(a, sizeof(char), N - 1, m_file);
m_pos += N - 1;
}
inline void _do_write(csubstr sp)
{
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if(sp.empty()) return;
fwrite(sp.str, sizeof(csubstr::char_type), sp.len, m_file);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
inline void _do_write(const char c)
{
fputc(c, m_file);
++m_pos;
}
inline void _do_write(RepC const rc)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
fputc(rc.c, m_file);
}
m_pos += rc.num_times;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A writer that outputs to an STL-like ostream. */
template<class OStream>
struct WriterOStream
{
OStream& m_stream;
size_t m_pos;
WriterOStream(OStream &s) : m_stream(s), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
{
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
m_stream.write(a, N - 1);
m_pos += N - 1;
}
inline void _do_write(csubstr sp)
{
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if(sp.empty()) return;
m_stream.write(sp.str, sp.len);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
inline void _do_write(const char c)
{
m_stream.put(c);
++m_pos;
}
inline void _do_write(RepC const rc)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_stream.put(rc.c);
}
m_pos += rc.num_times;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a writer to a substr */
struct WriterBuf
{
substr m_buf;
size_t m_pos;
WriterBuf(substr sp) : m_buf(sp), m_pos(0) {}
inline substr _get(bool error_on_excess)
{
if(m_pos <= m_buf.len)
{
return m_buf.first(m_pos);
}
if(error_on_excess)
{
c4::yml::error("not enough space in the given buffer");
}
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
RYML_ASSERT( ! m_buf.overlaps(a));
if(m_pos + N-1 <= m_buf.len)
{
memcpy(&(m_buf[m_pos]), a, N-1);
}
m_pos += N-1;
}
inline void _do_write(csubstr sp)
{
if(sp.empty()) return;
RYML_ASSERT( ! sp.overlaps(m_buf));
if(m_pos + sp.len <= m_buf.len)
{
memcpy(&(m_buf[m_pos]), sp.str, sp.len);
}
m_pos += sp.len;
}
inline void _do_write(const char c)
{
if(m_pos + 1 <= m_buf.len)
{
m_buf[m_pos] = c;
}
++m_pos;
}
inline void _do_write(RepC const rc)
{
if(m_pos + rc.num_times <= m_buf.len)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_buf[m_pos + i] = rc.c;
}
}
m_pos += rc.num_times;
}
};
} // namespace yml
} // namespace c4
#endif /* _C4_YML_WRITER_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/writer.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/detail/parser_dbg.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/parser_dbg.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_DETAIL_PARSER_DBG_HPP_
#define _C4_YML_DETAIL_PARSER_DBG_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "../common.hpp"
#endif
//included above:
//#include <cstdio>
//-----------------------------------------------------------------------------
// some debugging scaffolds
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4068/*unknown pragma*/)
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
//#pragma GCC diagnostic ignored "-Wpragma-system-header-outside-header"
#pragma GCC system_header
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Werror"
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
// some debugging scaffolds
#ifdef RYML_DBG
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/dump.hpp
//#include <c4/dump.hpp>
#if !defined(C4_DUMP_HPP_) && !defined(_C4_DUMP_HPP_)
#error "amalgamate: file c4/dump.hpp must have been included at this point"
#endif /* C4_DUMP_HPP_ */
namespace c4 {
inline void _dbg_dumper(csubstr s) { fwrite(s.str, 1, s.len, stdout); };
template<class ...Args>
void _dbg_printf(c4::csubstr fmt, Args&& ...args)
{
static char writebuf[256];
auto results = c4::format_dump_resume<&_dbg_dumper>(writebuf, fmt, std::forward<Args>(args)...);
// resume writing if the results failed to fit the buffer
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf))) // bufsize will be that of the largest element serialized. Eg int(1), will require 1 byte.
{
results = format_dump_resume<&_dbg_dumper>(results, writebuf, fmt, std::forward<Args>(args)...);
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf)))
{
results = format_dump_resume<&_dbg_dumper>(results, writebuf, fmt, std::forward<Args>(args)...);
}
}
}
} // namespace c4
# define _c4dbgt(fmt, ...) this->_dbg ("{}:{}: " fmt , __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgpf(fmt, ...) _dbg_printf("{}:{}: " fmt "\n", __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgp(msg) _dbg_printf("{}:{}: " msg "\n", __FILE__, __LINE__ )
# define _c4dbgq(msg) _dbg_printf(msg "\n")
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR:\n" "{}:{}: " fmt, __FILE__, __LINE__, ## __VA_ARGS__); } while(0)
#else
# define _c4dbgt(fmt, ...)
# define _c4dbgpf(fmt, ...)
# define _c4dbgp(msg)
# define _c4dbgq(msg)
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR: " fmt, ## __VA_ARGS__); } while(0)
#endif
#define _c4prsp(sp) sp
#define _c4presc(s) __c4presc(s.str, s.len)
inline c4::csubstr _c4prc(const char &C4_RESTRICT c)
{
switch(c)
{
case '\n': return c4::csubstr("\\n");
case '\t': return c4::csubstr("\\t");
case '\0': return c4::csubstr("\\0");
case '\r': return c4::csubstr("\\r");
case '\f': return c4::csubstr("\\f");
case '\b': return c4::csubstr("\\b");
case '\v': return c4::csubstr("\\v");
case '\a': return c4::csubstr("\\a");
default: return c4::csubstr(&c, 1);
}
}
inline void __c4presc(const char *s, size_t len)
{
size_t prev = 0;
for(size_t i = 0; i < len; ++i)
{
switch(s[i])
{
case '\n' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('n'); putchar('\n'); prev = i+1; break;
case '\t' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('t'); prev = i+1; break;
case '\0' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('0'); prev = i+1; break;
case '\r' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('r'); prev = i+1; break;
case '\f' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('f'); prev = i+1; break;
case '\b' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('b'); prev = i+1; break;
case '\v' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('v'); prev = i+1; break;
case '\a' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('a'); prev = i+1; break;
case '\x1b': fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('e'); prev = i+1; break;
case -0x3e/*0xc2u*/:
if(i+1 < len)
{
if(s[i+1] == -0x60/*0xa0u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('_'); prev = i+2; ++i;
}
else if(s[i+1] == -0x7b/*0x85u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('N'); prev = i+2; ++i;
}
break;
}
case -0x1e/*0xe2u*/:
if(i+2 < len && s[i+1] == -0x80/*0x80u*/)
{
if(s[i+2] == -0x58/*0xa8u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('L'); prev = i+3; i += 2;
}
else if(s[i+2] == -0x57/*0xa9u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('P'); prev = i+3; i += 2;
}
break;
}
}
}
fwrite(s + prev, 1, len - prev, stdout);
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_DETAIL_PARSER_DBG_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/detail/parser_dbg.hpp)
#define C4_YML_EMIT_DEF_HPP_
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/emit.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/emit.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_EMIT_HPP_
#define _C4_YML_EMIT_HPP_
#ifndef _C4_YML_WRITER_HPP_
#include "./writer.hpp"
#endif
#ifndef _C4_YML_TREE_HPP_
#include "./tree.hpp"
#endif
#ifndef _C4_YML_NODE_HPP_
#include "./node.hpp"
#endif
namespace c4 {
namespace yml {
template<class Writer> class Emitter;
template<class OStream>
using EmitterOStream = Emitter<WriterOStream<OStream>>;
using EmitterFile = Emitter<WriterFile>;
using EmitterBuf = Emitter<WriterBuf>;
typedef enum {
EMIT_YAML = 0,
EMIT_JSON = 1
} EmitType_e;
/** mark a tree or node to be emitted as json */
struct as_json
{
Tree const* tree;
size_t node;
as_json(Tree const& t) : tree(&t), node(t.empty() ? NONE : t.root_id()) {}
as_json(Tree const& t, size_t id) : tree(&t), node(id) {}
as_json(NodeRef const& n) : tree(n.tree()), node(n.id()) {}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class Writer>
class Emitter : public Writer
{
public:
using Writer::Writer;
/** emit!
*
* When writing to a buffer, returns a substr of the emitted YAML.
* If the given buffer has insufficient space, the returned span will
* be null and its size will be the needed space. No writes are done
* after the end of the buffer.
*
* When writing to a file, the returned substr will be null, but its
* length will be set to the number of bytes written. */
substr emit(EmitType_e type, Tree const& t, size_t id, bool error_on_excess);
/** emit starting at the root node */
substr emit(EmitType_e type, Tree const& t, bool error_on_excess=true);
/** emit the given node */
substr emit(EmitType_e type, NodeRef const& n, bool error_on_excess=true);
private:
Tree const* C4_RESTRICT m_tree;
void _emit_yaml(size_t id);
void _do_visit_flow_sl(size_t id, size_t ilevel=0);
void _do_visit_flow_ml(size_t id, size_t ilevel=0, size_t do_indent=1);
void _do_visit_block(size_t id, size_t ilevel=0, size_t do_indent=1);
void _do_visit_block_container(size_t id, size_t next_level, size_t do_indent);
void _do_visit_json(size_t id);
private:
void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t level);
void _write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags);
void _write_doc(size_t id);
void _write_scalar(csubstr s, bool was_quoted);
void _write_scalar_json(csubstr s, bool as_key, bool was_quoted);
void _write_scalar_literal(csubstr s, size_t level, bool as_key, bool explicit_indentation=false);
void _write_scalar_folded(csubstr s, size_t level, bool as_key);
void _write_scalar_squo(csubstr s, size_t level);
void _write_scalar_dquo(csubstr s, size_t level);
void _write_scalar_plain(csubstr s, size_t level);
void _write_tag(csubstr tag)
{
if(!tag.begins_with('!'))
this->Writer::_do_write('!');
this->Writer::_do_write(tag);
}
enum : type_bits {
_keysc = (KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | ~(VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE),
_valsc = ~(KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | (VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE),
_keysc_json = (KEY) | ~(VAL),
_valsc_json = ~(KEY) | (VAL),
};
C4_ALWAYS_INLINE void _writek(size_t id, size_t level) { _write(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~_valsc, level); }
C4_ALWAYS_INLINE void _writev(size_t id, size_t level) { _write(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~_keysc, level); }
C4_ALWAYS_INLINE void _writek_json(size_t id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); }
C4_ALWAYS_INLINE void _writev_json(size_t id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit(Tree const& t, size_t id, FILE *f)
{
EmitterFile em(f);
return em.emit(EMIT_YAML, t, id, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_json(Tree const& t, size_t id, FILE *f)
{
EmitterFile em(f);
return em.emit(EMIT_JSON, t, id, /*error_on_excess*/true).len;
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit(EMIT_YAML, t, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_json(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit(EMIT_JSON, t, /*error_on_excess*/true).len;
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit(NodeRef const& r, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit(EMIT_YAML, r, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_json(NodeRef const& r, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit(EMIT_JSON, r, /*error_on_excess*/true).len;
}
//-----------------------------------------------------------------------------
/** emit YAML to an STL-like ostream */
template<class OStream>
inline OStream& operator<< (OStream& s, Tree const& t)
{
EmitterOStream<OStream> em(s);
em.emit(EMIT_YAML, t);
return s;
}
/** emit YAML to an STL-like ostream
* @overload */
template<class OStream>
inline OStream& operator<< (OStream& s, NodeRef const& n)
{
EmitterOStream<OStream> em(s);
em.emit(EMIT_YAML, n);
return s;
}
/** emit json to an STL-like stream */
template<class OStream>
inline OStream& operator<< (OStream& s, as_json const& j)
{
EmitterOStream<OStream> em(s);
em.emit(EMIT_JSON, *j.tree, j.node, true);
return s;
}
//-----------------------------------------------------------------------------
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit(EMIT_YAML, t, id, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_json(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit(EMIT_JSON, t, id, error_on_excess);
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit(EMIT_YAML, t, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit(EMIT_JSON, t, error_on_excess);
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
inline substr emit(NodeRef const& r, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit(EMIT_YAML, r, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
inline substr emit_json(NodeRef const& r, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit(EMIT_JSON, r, error_on_excess);
}
//-----------------------------------------------------------------------------
/** emit+resize: emit YAML to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont)
{
substr buf = to_substr(*cont);
substr ret = emit(t, id, buf, /*error_on_excess*/false);
if(ret.str == nullptr && ret.len > 0)
{
cont->resize(ret.len);
buf = to_substr(*cont);
ret = emit(t, id, buf, /*error_on_excess*/true);
}
return ret;
}
/** emit+resize: emit JSON to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont)
{
substr buf = to_substr(*cont);
substr ret = emit_json(t, id, buf, /*error_on_excess*/false);
if(ret.str == nullptr && ret.len > 0)
{
cont->resize(ret.len);
buf = to_substr(*cont);
ret = emit_json(t, id, buf, /*error_on_excess*/true);
}
return ret;
}
/** emit+resize: emit YAML to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs(t, id, &c);
return c;
}
/** emit+resize: emit JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs_json(t, id, &c);
return c;
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs(Tree const& t, CharOwningContainer * cont)
{
if(t.empty())
return {};
return emitrs(t, t.root_id(), cont);
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, CharOwningContainer * cont)
{
if(t.empty())
return {};
return emitrs_json(t, t.root_id(), cont);
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs(Tree const& t)
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs(t, t.root_id(), &c);
return c;
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t)
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_json(t, t.root_id(), &c);
return c;
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs(NodeRef const& n, CharOwningContainer * cont)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
return emitrs(*n.tree(), n.id(), cont);
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(NodeRef const& n, CharOwningContainer * cont)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
return emitrs_json(*n.tree(), n.id(), cont);
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs(NodeRef const& n)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
CharOwningContainer c;
emitrs(*n.tree(), n.id(), &c);
return c;
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(NodeRef const& n)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
CharOwningContainer c;
emitrs_json(*n.tree(), n.id(), &c);
return c;
}
} // namespace yml
} // namespace c4
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/emit.def.hpp
//#include "c4/yml/emit.def.hpp"
#if !defined(C4_YML_EMIT_DEF_HPP_) && !defined(_C4_YML_EMIT_DEF_HPP_)
#error "amalgamate: file c4/yml/emit.def.hpp must have been included at this point"
#endif /* C4_YML_EMIT_DEF_HPP_ */
#endif /* _C4_YML_EMIT_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/emit.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/emit.def.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/emit.def.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_EMIT_DEF_HPP_
#define _C4_YML_EMIT_DEF_HPP_
#ifndef _C4_YML_EMIT_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/emit.hpp
//#include "c4/yml/emit.hpp"
#if !defined(C4_YML_EMIT_HPP_) && !defined(_C4_YML_EMIT_HPP_)
#error "amalgamate: file c4/yml/emit.hpp must have been included at this point"
#endif /* C4_YML_EMIT_HPP_ */
#endif
namespace c4 {
namespace yml {
template<class Writer>
substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)
{
if(t.empty())
{
_RYML_CB_ASSERT(t.callbacks(), id == NONE);
return {};
}
_RYML_CB_CHECK(t.callbacks(), id < t.size());
m_tree = &t;
if(type == EMIT_YAML)
_emit_yaml(id);
else if(type == EMIT_JSON)
_do_visit_json(id);
else
_RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
return this->Writer::_get(error_on_excess);
}
template<class Writer>
substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, bool error_on_excess)
{
if(t.empty())
return {};
return emit(type, t, t.root_id(), error_on_excess);
}
template<class Writer>
substr Emitter<Writer>::emit(EmitType_e type, NodeRef const& n, bool error_on_excess)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
return emit(type, *n.tree(), n.id(), error_on_excess);
}
//-----------------------------------------------------------------------------
template<class Writer>
void Emitter<Writer>::_emit_yaml(size_t id)
{
// save branches in the visitor by doing the initial stream/doc
// logic here, sparing the need to check stream/val/keyval inside
// the visitor functions
auto dispatch = [this](size_t node){
NodeType ty = m_tree->type(node);
if(ty.marked_flow_sl())
_do_visit_flow_sl(node, 0);
else if(ty.marked_flow_ml())
_do_visit_flow_ml(node, 0);
else
{
_do_visit_block(node, 0);
}
};
if(!m_tree->is_root(id))
{
if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())
{
size_t ilevel = 0;
if(m_tree->has_key(id))
{
this->Writer::_do_write(m_tree->key(id));
this->Writer::_do_write(":\n");
++ilevel;
}
_do_visit_block_container(id, ilevel, ilevel);
return;
}
}
auto *btd = m_tree->tag_directives().b;
auto *etd = m_tree->tag_directives().e;
auto write_tag_directives = [&btd, etd, this](size_t next_node){
auto end = btd;
while(end < etd)
{
if(end->next_node_id > next_node)
break;
++end;
}
for( ; btd != end; ++btd)
{
if(next_node != m_tree->first_child(m_tree->parent(next_node)))
this->Writer::_do_write("...\n");
this->Writer::_do_write("%TAG ");
this->Writer::_do_write(btd->handle);
this->Writer::_do_write(' ');
this->Writer::_do_write(btd->prefix);
this->Writer::_do_write('\n');
}
};
if(m_tree->is_stream(id))
{
if(m_tree->first_child(id) != NONE)
write_tag_directives(m_tree->first_child(id));
for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
{
dispatch(child);
if(m_tree->next_sibling(child) != NONE)
write_tag_directives(m_tree->next_sibling(child));
}
}
else if(m_tree->is_container(id))
{
dispatch(id);
}
else if(m_tree->is_doc(id))
{
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
_write_doc(id);
}
else if(m_tree->is_keyval(id))
{
_writek(id, 0);
this->Writer::_do_write(": ");
_writev(id, 0);
if(!m_tree->type(id).marked_flow())
this->Writer::_do_write('\n');
}
else if(m_tree->is_val(id))
{
//this->Writer::_do_write("- ");
_writev(id, 0);
if(!m_tree->type(id).marked_flow())
this->Writer::_do_write('\n');
}
else if(m_tree->type(id) == NOTYPE)
{
;
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "unknown type");
}
}
template<class Writer>
void Emitter<Writer>::_write_doc(size_t id)
{
RYML_ASSERT(m_tree->is_doc(id));
if(!m_tree->is_root(id))
{
RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));
this->Writer::_do_write("---");
}
if(!m_tree->has_val(id)) // this is more frequent
{
if(m_tree->has_val_tag(id))
{
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(id));
}
if(m_tree->has_val_anchor(id))
{
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(id));
}
}
else // docval
{
RYML_ASSERT(m_tree->has_val(id));
RYML_ASSERT(!m_tree->has_key(id));
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
_writev(id, 0);
}
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)
{
RYML_ASSERT(!m_tree->is_stream(node));
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
if(m_tree->is_doc(node))
{
_write_doc(node);
if(!m_tree->has_children(node))
return;
}
else if(m_tree->is_container(node))
{
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
bool spc = false; // write a space
if(m_tree->has_key(node))
{
_writek(node, ilevel);
this->Writer::_do_write(':');
spc = true;
}
if(m_tree->has_val_tag(node))
{
if(spc)
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(node));
spc = true;
}
if(m_tree->has_val_anchor(node))
{
if(spc)
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(node));
spc = true;
}
if(spc)
this->Writer::_do_write(' ');
if(m_tree->is_map(node))
{
this->Writer::_do_write('{');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
this->Writer::_do_write('[');
}
} // container
for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
{
if(count++)
this->Writer::_do_write(',');
if(m_tree->is_keyval(child))
{
_writek(child, ilevel);
this->Writer::_do_write(": ");
_writev(child, ilevel);
}
else if(m_tree->is_val(child))
{
_writev(child, ilevel);
}
else
{
// with single-line flow, we can never go back to block
_do_visit_flow_sl(child, ilevel + 1);
}
}
if(m_tree->is_map(node))
{
this->Writer::_do_write('}');
}
else if(m_tree->is_seq(node))
{
this->Writer::_do_write(']');
}
}
template<class Writer>
void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)
{
C4_UNUSED(id);
C4_UNUSED(ilevel);
C4_UNUSED(do_indent);
RYML_CHECK(false/*not implemented*/);
}
template<class Writer>
void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)
{
RepC ind = indent_to(do_indent * next_level);
if(m_tree->is_seq(node))
{
for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
{
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
if(m_tree->is_val(child))
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_writev(child, next_level);
this->Writer::_do_write('\n');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
NodeType ty = m_tree->type(child);
if(ty.marked_flow_sl())
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_do_visit_flow_sl(child, 0u);
this->Writer::_do_write('\n');
}
else if(ty.marked_flow_ml())
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_do_visit_flow_ml(child, next_level, do_indent);
this->Writer::_do_write('\n');
}
else
{
_do_visit_block(child, next_level, do_indent);
}
}
do_indent = true;
ind = indent_to(do_indent * next_level);
}
}
else // map
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
if(m_tree->is_keyval(ich))
{
this->Writer::_do_write(ind);
_writek(ich, next_level);
this->Writer::_do_write(": ");
_writev(ich, next_level);
this->Writer::_do_write('\n');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
NodeType ty = m_tree->type(ich);
if(ty.marked_flow_sl())
{
this->Writer::_do_write(ind);
_do_visit_flow_sl(ich, 0u);
this->Writer::_do_write('\n');
}
else if(ty.marked_flow_ml())
{
this->Writer::_do_write(ind);
_do_visit_flow_ml(ich, 0u);
this->Writer::_do_write('\n');
}
else
{
_do_visit_block(ich, next_level, do_indent);
}
}
do_indent = true;
ind = indent_to(do_indent * next_level);
}
}
}
template<class Writer>
void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)
{
RYML_ASSERT(!m_tree->is_stream(node));
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
RepC ind = indent_to(do_indent * ilevel);
if(m_tree->is_doc(node))
{
_write_doc(node);
if(!m_tree->has_children(node))
return;
}
else if(m_tree->is_container(node))
{
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
bool spc = false; // write a space
bool nl = false; // write a newline
if(m_tree->has_key(node))
{
this->Writer::_do_write(ind);
_writek(node, ilevel);
this->Writer::_do_write(':');
spc = true;
}
else if(!m_tree->is_root(node))
{
this->Writer::_do_write(ind);
this->Writer::_do_write('-');
spc = true;
}
if(m_tree->has_val_tag(node))
{
if(spc)
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(node));
spc = true;
nl = true;
}
if(m_tree->has_val_anchor(node))
{
if(spc)
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(node));
spc = true;
nl = true;
}
if(m_tree->has_children(node))
{
if(m_tree->has_key(node))
nl = true;
else
if(!m_tree->is_root(node) && !nl)
spc = true;
}
else
{
if(m_tree->is_seq(node))
this->Writer::_do_write(" []\n");
else if(m_tree->is_map(node))
this->Writer::_do_write(" {}\n");
return;
}
if(spc && !nl)
this->Writer::_do_write(' ');
do_indent = 0;
if(nl)
{
this->Writer::_do_write('\n');
do_indent = 1;
}
} // container
size_t next_level = ilevel + 1;
if(m_tree->is_root(node) || m_tree->is_doc(node))
next_level = ilevel; // do not indent at top level
_do_visit_block_container(node, next_level, do_indent);
}
template<class Writer>
void Emitter<Writer>::_do_visit_json(size_t id)
{
_RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
if(m_tree->is_keyval(id))
{
_writek_json(id);
this->Writer::_do_write(": ");
_writev_json(id);
}
else if(m_tree->is_val(id))
{
_writev_json(id);
}
else if(m_tree->is_container(id))
{
if(m_tree->has_key(id))
{
_writek_json(id);
this->Writer::_do_write(": ");
}
if(m_tree->is_seq(id))
this->Writer::_do_write('[');
else if(m_tree->is_map(id))
this->Writer::_do_write('{');
} // container
for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
{
if(ich != m_tree->first_child(id))
this->Writer::_do_write(',');
_do_visit_json(ich);
}
if(m_tree->is_seq(id))
this->Writer::_do_write(']');
else if(m_tree->is_map(id))
this->Writer::_do_write('}');
}
template<class Writer>
void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)
{
if( ! sc.tag.empty())
{
_write_tag(sc.tag);
this->Writer::_do_write(' ');
}
if(flags.has_anchor())
{
RYML_ASSERT(flags.is_ref() != flags.has_anchor());
RYML_ASSERT( ! sc.anchor.empty());
this->Writer::_do_write('&');
this->Writer::_do_write(sc.anchor);
this->Writer::_do_write(' ');
}
else if(flags.is_ref())
{
if(sc.anchor != "<<")
this->Writer::_do_write('*');
this->Writer::_do_write(sc.anchor);
return;
}
// ensure the style flags only have one of KEY or VAL
_RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));
auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);
if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key());
}
else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))
{
_write_scalar_folded(sc.scalar, ilevel, flags.has_key());
}
else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))
{
_write_scalar_squo(sc.scalar, ilevel);
}
else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))
{
_write_scalar_dquo(sc.scalar, ilevel);
}
else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))
{
_write_scalar_plain(sc.scalar, ilevel);
}
else if(!style_marks)
{
size_t first_non_nl = sc.scalar.first_not_of('\n');
bool all_newlines = first_non_nl == npos;
bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");
bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));
if(do_literal)
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
}
else
{
for(size_t i = 0; i < sc.scalar.len; ++i)
{
if(sc.scalar.str[i] == '\n')
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
goto wrote_special;
}
// todo: check for escaped characters requiring double quotes
}
_write_scalar(sc.scalar, flags.is_quoted());
wrote_special:
;
}
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "not implemented");
}
}
template<class Writer>
void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
{
if(C4_UNLIKELY( ! sc.tag.empty()))
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
if(C4_UNLIKELY(flags.has_anchor()))
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
_write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());
}
#define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }
template<class Writer>
void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)
{
if(explicit_key)
this->Writer::_do_write("? ");
csubstr trimmed = s.trimr("\n\r");
size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');
//
if(!explicit_indentation)
this->Writer::_do_write('|');
else
this->Writer::_do_write("|2");
//
if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)
this->Writer::_do_write("+\n");
else if(numnewlines_at_end == 1)
this->Writer::_do_write('\n');
else
this->Writer::_do_write("-\n");
//
if(trimmed.len)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < trimmed.len; ++i)
{
if(trimmed[i] != '\n')
continue;
// write everything up to this point
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
_rymlindent_nextline()
this->Writer::_do_write(since_pos);
pos = i+1; // already written
}
if(pos < trimmed.len)
{
_rymlindent_nextline()
this->Writer::_do_write(trimmed.sub(pos));
}
if(numnewlines_at_end)
{
this->Writer::_do_write('\n');
--numnewlines_at_end;
}
}
for(size_t i = 0; i < numnewlines_at_end; ++i)
{
_rymlindent_nextline()
if(i+1 < numnewlines_at_end || explicit_key)
this->Writer::_do_write('\n');
}
if(explicit_key && !numnewlines_at_end)
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)
{
if(explicit_key)
{
this->Writer::_do_write("? ");
}
RYML_ASSERT(s.find("\r") == csubstr::npos);
csubstr trimmed = s.trimr('\n');
size_t numnewlines_at_end = s.len - trimmed.len;
if(numnewlines_at_end == 0)
{
this->Writer::_do_write(">-\n");
}
else if(numnewlines_at_end == 1)
{
this->Writer::_do_write(">\n");
}
else if(numnewlines_at_end > 1)
{
this->Writer::_do_write(">+\n");
}
if(trimmed.len)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < trimmed.len; ++i)
{
if(trimmed[i] != '\n')
continue;
// write everything up to this point
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
pos = i+1; // because of the newline
_rymlindent_nextline()
this->Writer::_do_write(since_pos);
this->Writer::_do_write('\n'); // write the newline twice
}
if(pos < trimmed.len)
{
_rymlindent_nextline()
this->Writer::_do_write(trimmed.sub(pos));
}
if(numnewlines_at_end)
{
this->Writer::_do_write('\n');
--numnewlines_at_end;
}
}
for(size_t i = 0; i < numnewlines_at_end; ++i)
{
_rymlindent_nextline()
if(i+1 < numnewlines_at_end || explicit_key)
this->Writer::_do_write('\n');
}
if(explicit_key && !numnewlines_at_end)
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
this->Writer::_do_write('\'');
for(size_t i = 0; i < s.len; ++i)
{
if(s[i] == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this char
this->Writer::_do_write('\n'); // write the character again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
}
else if(s[i] == '\'')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this char
this->Writer::_do_write('\''); // write the character again
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
this->Writer::_do_write(s.sub(pos));
this->Writer::_do_write('\'');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
this->Writer::_do_write('"');
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s.str[i];
if(curr == '"' || curr == '\\')
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write('\\'); // write the escape
this->Writer::_do_write(curr); // write the char
pos = i+1;
}
else if(s[i] == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this newline
this->Writer::_do_write('\n'); // write the newline again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
if(i+1 < s.len) // escape leading whitespace after the newline
{
const char next = s.str[i+1];
if(next == ' ' || next == '\t')
this->Writer::_do_write('\\');
}
}
else if(curr == ' ' || curr == '\t')
{
// escape trailing whitespace before a newline
size_t next = s.first_not_of(" \t\r", i);
if(next != npos && s[next] == '\n')
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write('\\'); // escape the whitespace
pos = i;
}
}
}
// write missing characters at the end of the string
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
this->Writer::_do_write('"');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s.str[i];
if(curr == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this newline
this->Writer::_do_write('\n'); // write the newline again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
}
#undef _rymlindent_nextline
template<class Writer>
void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)
{
// this block of code needed to be moved to before the needs_quotes
// assignment to work around a g++ optimizer bug where (s.str != nullptr)
// was evaluated as true even if s.str was actually a nullptr (!!!)
if(s.len == size_t(0))
{
if(was_quoted)
this->Writer::_do_write("''");
return;
}
const bool needs_quotes = (
was_quoted
||
(
( ! s.is_number())
&&
(
// has leading whitespace
s.begins_with_any(" \n\t\r")
||
// looks like reference or anchor or would be treated as a directive
s.begins_with_any("*&%")
||
s.begins_with("<<")
||
// has trailing whitespace
s.ends_with_any(" \n\t\r")
||
// has special chars
(s.first_of("#:-?,\n{}[]'\"") != npos)
)
)
);
if( ! needs_quotes)
{
this->Writer::_do_write(s);
}
else
{
const bool has_dquotes = s.first_of( '"') != npos;
const bool has_squotes = s.first_of('\'') != npos;
if(!has_squotes && has_dquotes)
{
this->Writer::_do_write('\'');
this->Writer::_do_write(s);
this->Writer::_do_write('\'');
}
else if(has_squotes && !has_dquotes)
{
RYML_ASSERT(s.count('\n') == 0);
this->Writer::_do_write('"');
this->Writer::_do_write(s);
this->Writer::_do_write('"');
}
else
{
_write_scalar_squo(s, /*FIXME FIXME FIXME*/0);
}
}
}
template<class Writer>
void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool was_quoted)
{
if(was_quoted)
{
this->Writer::_do_write('"');
this->Writer::_do_write(s);
this->Writer::_do_write('"');
}
// json only allows strings as keys
else if(!as_key && (s.is_number() || s == "true" || s == "null" || s == "false"))
{
this->Writer::_do_write(s);
}
else
{
size_t pos = 0;
this->Writer::_do_write('"');
for(size_t i = 0; i < s.len; ++i)
{
switch (s[i])
{
case '"':
case '\n': {
if(i > 0)
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub);
}
pos = i + 1;
switch (s[i]) {
case '"':
this->Writer::_do_write("\\\"");
break;
case '\n':
this->Writer::_do_write("\\n");
break;
}
break;
}
}
}
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
this->Writer::_do_write('"');
}
}
} // namespace yml
} // namespace c4
#endif /* _C4_YML_EMIT_DEF_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/emit.def.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/detail/stack.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/stack.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_DETAIL_STACK_HPP_
#define _C4_YML_DETAIL_STACK_HPP_
#ifndef _C4_YML_COMMON_HPP_
//included above:
//#include "../common.hpp"
#endif
#ifdef RYML_DBG
//included above:
//# include <type_traits>
#endif
//included above:
//#include <string.h>
namespace c4 {
namespace yml {
namespace detail {
/** A lightweight contiguous stack with SSO. This avoids a dependency on std. */
template<class T, size_t N=16>
class stack
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible");
enum : size_t { sso_size = N };
public:
T m_buf[N];
T * m_stack;
size_t m_size;
size_t m_capacity;
Callbacks m_callbacks;
public:
constexpr static bool is_contiguous() { return true; }
stack(Callbacks const& cb)
: m_buf()
, m_stack(m_buf)
, m_size(0)
, m_capacity(N)
, m_callbacks(cb) {}
stack() : stack(get_callbacks()) {}
~stack()
{
_free();
}
stack(stack const& that) noexcept : stack(that.m_callbacks)
{
resize(that.m_size);
_cp(&that);
}
stack(stack &&that) noexcept : stack(that.m_callbacks)
{
_mv(&that);
}
stack& operator= (stack const& that) noexcept
{
_cb(that.m_callbacks);
resize(that.m_size);
_cp(&that);
return *this;
}
stack& operator= (stack &&that) noexcept
{
_cb(that.m_callbacks);
_mv(&that);
return *this;
}
public:
size_t size() const { return m_size; }
size_t empty() const { return m_size == 0; }
size_t capacity() const { return m_capacity; }
void clear()
{
m_size = 0;
}
void resize(size_t sz)
{
reserve(sz);
m_size = sz;
}
void reserve(size_t sz);
void push(T const& C4_RESTRICT n)
{
RYML_ASSERT((const char*)&n + sizeof(T) < (const char*)m_stack || &n > m_stack + m_capacity);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = n;
++m_size;
}
void push_top()
{
RYML_ASSERT(m_size > 0);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = m_stack[m_size - 1];
++m_size;
}
T const& C4_RESTRICT pop()
{
RYML_ASSERT(m_size > 0);
--m_size;
return m_stack[m_size];
}
C4_ALWAYS_INLINE T const& C4_RESTRICT top() const { RYML_ASSERT(m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top() { RYML_ASSERT(m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom() const { RYML_ASSERT(m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom() { RYML_ASSERT(m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT top(size_t i) const { RYML_ASSERT(i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top(size_t i) { RYML_ASSERT(i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom(size_t i) const { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom(size_t i) { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT operator[](size_t i) const { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT operator[](size_t i) { RYML_ASSERT(i < m_size); return m_stack[i]; }
public:
using iterator = T *;
using const_iterator = T const *;
iterator begin() { return m_stack; }
iterator end () { return m_stack + m_size; }
const_iterator begin() const { return (const_iterator)m_stack; }
const_iterator end () const { return (const_iterator)m_stack + m_size; }
public:
void _free();
void _cp(stack const* C4_RESTRICT that);
void _mv(stack * that);
void _cb(Callbacks const& cb);
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::reserve(size_t sz)
{
if(sz <= m_size)
return;
if(sz <= N)
{
m_stack = m_buf;
m_capacity = N;
return;
}
T *buf = (T*) m_callbacks.m_allocate(sz * sizeof(T), m_stack, m_callbacks.m_user_data);
memcpy(buf, m_stack, m_size * sizeof(T));
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
}
m_stack = buf;
m_capacity = sz;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_free()
{
RYML_ASSERT(m_stack != nullptr); // this structure cannot be memset() to zero
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
m_stack = m_buf;
m_size = N;
m_capacity = N;
}
else
{
RYML_ASSERT(m_capacity == N);
}
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_cp(stack const* C4_RESTRICT that)
{
if(that->m_stack != that->m_buf)
{
RYML_ASSERT(that->m_capacity > N);
RYML_ASSERT(that->m_size <= that->m_capacity);
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
}
memcpy(m_stack, that->m_stack, that->m_size * sizeof(T));
m_size = that->m_size;
m_capacity = that->m_size < N ? N : that->m_size;
m_callbacks = that->m_callbacks;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_mv(stack * that)
{
if(that->m_stack != that->m_buf)
{
RYML_ASSERT(that->m_capacity > N);
RYML_ASSERT(that->m_size <= that->m_capacity);
m_stack = that->m_stack;
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
memcpy(m_buf, that->m_buf, that->m_size * sizeof(T));
m_stack = m_buf;
}
m_size = that->m_size;
m_capacity = that->m_capacity;
m_callbacks = that->m_callbacks;
// make sure no deallocation happens on destruction
RYML_ASSERT(that->m_stack != m_buf);
that->m_stack = that->m_buf;
that->m_capacity = N;
that->m_size = 0;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_cb(Callbacks const& cb)
{
if(cb != m_callbacks)
{
_free();
m_callbacks = cb;
}
}
} // namespace detail
} // namespace yml
} // namespace c4
#endif /* _C4_YML_DETAIL_STACK_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/detail/stack.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/parse.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/parse.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_PARSE_HPP_
#define _C4_YML_PARSE_HPP_
#ifndef _C4_YML_TREE_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//#include "c4/yml/tree.hpp"
#if !defined(C4_YML_TREE_HPP_) && !defined(_C4_YML_TREE_HPP_)
#error "amalgamate: file c4/yml/tree.hpp must have been included at this point"
#endif /* C4_YML_TREE_HPP_ */
#endif
#ifndef _C4_YML_NODE_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
#endif
#ifndef _C4_YML_DETAIL_STACK_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/stack.hpp
//#include "c4/yml/detail/stack.hpp"
#if !defined(C4_YML_DETAIL_STACK_HPP_) && !defined(_C4_YML_DETAIL_STACK_HPP_)
#error "amalgamate: file c4/yml/detail/stack.hpp must have been included at this point"
#endif /* C4_YML_DETAIL_STACK_HPP_ */
#endif
//included above:
//#include <stdarg.h>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
#endif
namespace c4 {
namespace yml {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class RYML_EXPORT Parser
{
public:
/** @name construction and assignment */
/** @{ */
Parser() : Parser(get_callbacks()) {}
Parser(Callbacks const& cb);
~Parser();
Parser(Parser &&);
Parser(Parser const&);
Parser& operator=(Parser &&);
Parser& operator=(Parser const&);
/** @} */
public:
/** @name modifiers */
/** @{ */
/** Reserve a certain capacity for the parsing stack.
* This should be larger than the expected depth of the parsed
* YAML tree.
*
* The parsing stack is the only (potential) heap memory used by
* the parser.
*
* If the requested capacity is below the default
* stack size of 16, the memory is used directly in the parser
* object; otherwise it will be allocated from the heap.
*
* @note this reserves memory only for the parser itself; all the
* allocations for the parsed tree will go through the tree's
* allocator.
*
* @note the tree and the arena can (and should) also be reserved. */
void reserve_stack(size_t capacity)
{
m_stack.reserve(capacity);
}
/** Reserve a certain capacity for the array used to track node
* locations in the source buffer. */
void reserve_locations(size_t num_source_lines)
{
_resize_locations(num_source_lines);
}
/** Reserve a certain capacity for the character arena used to
* filter scalars. */
void reserve_filter_arena(size_t num_characters)
{
_resize_filter_arena(num_characters);
}
/** @} */
public:
/** @name getters and modifiers */
/** @{ */
/** Get the current callbacks in the parser. */
Callbacks callbacks() const { return m_stack.m_callbacks; }
/** Get the name of the latest file parsed by this object. */
csubstr filename() const { return m_file; }
/** Get the latest YAML buffer parsed by this object. */
csubstr source() const { return m_buf; }
size_t stack_capacity() const { return m_stack.capacity(); }
size_t locations_capacity() const { return m_newline_offsets_capacity; }
size_t filter_arena_capacity() const { return m_filter_arena.len; }
/** @} */
public:
/** @name parse_in_place */
/** @{ */
/** Create a new tree and parse into its root.
* The tree is created with the callbacks currently in the parser. */
Tree parse_in_place(csubstr filename, substr src)
{
Tree t(callbacks());
t.reserve(_estimate_capacity(src));
this->parse_in_place(filename, src, &t, t.root_id());
return t;
}
/** Parse into an existing tree, starting at its root node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, Tree *t)
{
this->parse_in_place(filename, src, t, t->root_id());
}
/** Parse into an existing node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, Tree *t, size_t node_id);
// ^^^^^^^^^^^^^ this is the workhorse overload; everything else is syntactic candy
/** Parse into an existing node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, NodeRef node)
{
this->parse_in_place(filename, src, node.tree(), node.id());
}
RYML_DEPRECATED("use parse_in_place() instead") Tree parse(csubstr filename, substr src) { return parse_in_place(filename, src); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, Tree *t) { parse_in_place(filename, src, t); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, Tree *t, size_t node_id) { parse_in_place(filename, src, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, NodeRef node) { parse_in_place(filename, src, node); }
/** @} */
public:
/** @name parse_in_arena: copy the YAML source buffer to the
* tree's arena, then parse the copy in situ
*
* @note overloads receiving a substr YAML buffer are intentionally
* left undefined, such that calling parse_in_arena() with a substr
* will cause a linker error. This is to prevent an accidental
* copy of the source buffer to the tree's arena, because substr
* is implicitly convertible to csubstr. If you really intend to parse
* a mutable buffer in the tree's arena, convert it first to immutable
* by assigning the substr to a csubstr prior to calling parse_in_arena().
* This is not needed for parse_in_place() because csubstr is not
* implicitly convertible to substr. */
/** @{ */
// READ THE NOTE ABOVE!
#define RYML_DONT_PARSE_SUBSTR_IN_ARENA "Do not pass a (mutable) substr to parse_in_arena(); if you have a substr, it should be parsed in place. Consider using parse_in_place() instead, or convert the buffer to csubstr prior to calling. This function is deliberately left undefined and will cause a compiler error."
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr csrc);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, NodeRef node);
/** Create a new tree and parse into its root.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
Tree parse_in_arena(csubstr filename, csubstr csrc)
{
Tree t(callbacks());
substr src = t.copy_to_arena(csrc);
t.reserve(_estimate_capacity(csrc));
this->parse_in_place(filename, src, &t, t.root_id());
return t;
}
/** Parse into an existing tree, starting at its root node.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, Tree *t)
{
substr src = t->copy_to_arena(csrc);
this->parse_in_place(filename, src, t, t->root_id());
}
/** Parse into a specific node in an existing tree.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, Tree *t, size_t node_id)
{
substr src = t->copy_to_arena(csrc);
this->parse_in_place(filename, src, t, node_id);
}
/** Parse into a specific node in an existing tree.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, NodeRef node)
{
substr src = node.tree()->copy_to_arena(csrc);
this->parse_in_place(filename, src, node.tree(), node.id());
}
RYML_DEPRECATED("use parse_in_arena() instead") Tree parse(csubstr filename, csubstr csrc) { return parse_in_arena(filename, csrc); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, Tree *t) { parse_in_arena(filename, csrc, t); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, Tree *t, size_t node_id) { parse_in_arena(filename, csrc, t, node_id); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, NodeRef node) { parse_in_arena(filename, csrc, node); }
/** @} */
public:
/** @name locations */
/** @{ */
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(Tree const& tree, size_t node_id) const;
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(NodeRef node) const;
/** Get the string starting at a particular location, to the end
* of the parsed source buffer. */
csubstr location_contents(Location const& loc) const;
/** Given a pointer to a buffer position, get the location. @p val
* must be pointing to somewhere in the source buffer that was
* last parsed by this object. */
Location val_location(const char *val) const;
/** @} */
private:
typedef enum {
BLOCK_LITERAL, //!< keep newlines (|)
BLOCK_FOLD //!< replace newline with single space (>)
} BlockStyle_e;
typedef enum {
CHOMP_CLIP, //!< single newline at end (default)
CHOMP_STRIP, //!< no newline at end (-)
CHOMP_KEEP //!< all newlines from end (+)
} BlockChomp_e;
private:
using flag_t = int;
static size_t _estimate_capacity(csubstr src) { size_t c = _count_nlines(src); c = c >= 16 ? c : 16; return c; }
void _reset();
bool _finished_file() const;
bool _finished_line() const;
csubstr _peek_next_line(size_t pos=npos) const;
bool _advance_to_peeked();
void _scan_line();
csubstr _slurp_doc_scalar();
/**
* @param [out] quoted
* Will only be written to if this method returns true.
* Will be set to true if the scanned scalar was quoted, by '', "", > or |.
*/
bool _scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
csubstr _scan_comment();
csubstr _scan_squot_scalar();
csubstr _scan_dquot_scalar();
csubstr _scan_block();
substr _scan_plain_scalar_blck(csubstr currscalar, csubstr peeked_line, size_t indentation);
substr _scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line);
substr _scan_complex_key(csubstr currscalar, csubstr peeked_line);
csubstr _scan_to_next_nonempty_line(size_t indentation);
csubstr _extend_scanned_scalar(csubstr currscalar);
csubstr _filter_squot_scalar(const substr s);
csubstr _filter_dquot_scalar(substr s);
csubstr _filter_plain_scalar(substr s, size_t indentation);
csubstr _filter_block_scalar(substr s, BlockStyle_e style, BlockChomp_e chomp, size_t indentation);
template<bool backslash_is_escape, bool keep_trailing_whitespace>
bool _filter_nl(substr scalar, size_t *C4_RESTRICT pos, size_t *C4_RESTRICT filter_arena_pos, size_t indentation);
template<bool keep_trailing_whitespace>
void _filter_ws(substr scalar, size_t *C4_RESTRICT pos, size_t *C4_RESTRICT filter_arena_pos);
bool _apply_chomp(substr buf, size_t *C4_RESTRICT pos, BlockChomp_e chomp);
void _handle_finished_file();
void _handle_line();
bool _handle_indentation();
bool _handle_unk();
bool _handle_map_flow();
bool _handle_map_blck();
bool _handle_seq_flow();
bool _handle_seq_blck();
bool _handle_top();
bool _handle_types();
bool _handle_key_anchors_and_refs();
bool _handle_val_anchors_and_refs();
void _move_val_tag_to_key_tag();
void _move_key_tag_to_val_tag();
void _move_key_tag2_to_key_tag();
void _move_val_anchor_to_key_anchor();
void _move_key_anchor_to_val_anchor();
void _push_level(bool explicit_flow_chars = false);
void _pop_level();
void _start_unk(bool as_child=true);
void _start_map(bool as_child=true);
void _start_map_unk(bool as_child);
void _stop_map();
void _start_seq(bool as_child=true);
void _stop_seq();
void _start_seqimap();
void _stop_seqimap();
void _start_doc(bool as_child=true);
void _stop_doc();
void _start_new_doc(csubstr rem);
void _end_stream();
NodeData* _append_val(csubstr val, flag_t quoted=false);
NodeData* _append_key_val(csubstr val, flag_t val_quoted=false);
bool _rval_dash_start_or_continue_seq();
void _store_scalar(csubstr s, flag_t is_quoted);
csubstr _consume_scalar();
void _move_scalar_from_top();
inline NodeData* _append_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_val({str, size_t(0)}); }
inline NodeData* _append_key_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_key_val({str, size_t(0)}); }
inline void _store_scalar_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); _store_scalar({str, size_t(0)}, false); }
void _set_indentation(size_t behind);
void _save_indentation(size_t behind=0);
bool _maybe_set_indentation_from_anchor_or_tag();
void _write_key_anchor(size_t node_id);
void _write_val_anchor(size_t node_id);
void _handle_directive(csubstr directive);
void _skipchars(char c);
template<size_t N>
void _skipchars(const char (&chars)[N]);
private:
static size_t _count_nlines(csubstr src);
private:
typedef enum : flag_t {
RTOP = 0x01 << 0, ///< reading at top level
RUNK = 0x01 << 1, ///< reading an unknown: must determine whether scalar, map or seq
RMAP = 0x01 << 2, ///< reading a map
RSEQ = 0x01 << 3, ///< reading a seq
FLOW = 0x01 << 4, ///< reading is inside explicit flow chars: [] or {}
QMRK = 0x01 << 5, ///< reading an explicit key (`? key`)
RKEY = 0x01 << 6, ///< reading a scalar as key
RVAL = 0x01 << 7, ///< reading a scalar as val
RNXT = 0x01 << 8, ///< read next val or keyval
SSCL = 0x01 << 9, ///< there's a stored scalar
QSCL = 0x01 << 10, ///< stored scalar was quoted
RSET = 0x01 << 11, ///< the (implicit) map being read is a !!set. @see https://yaml.org/type/set.html
NDOC = 0x01 << 12, ///< no document mode. a document has ended and another has not started yet.
//! reading an implicit map nested in an explicit seq.
//! eg, {key: [key2: value2, key3: value3]}
//! is parsed as {key: [{key2: value2}, {key3: value3}]}
RSEQIMAP = 0x01 << 13,
} State_e;
struct LineContents
{
csubstr full; ///< the full line, including newlines on the right
csubstr stripped; ///< the stripped line, excluding newlines on the right
csubstr rem; ///< the stripped line remainder; initially starts at the first non-space character
size_t indentation; ///< the number of spaces on the beginning of the line
LineContents() : full(), stripped(), rem(), indentation() {}
void reset_with_next_line(csubstr buf, size_t pos);
void reset(csubstr full_, csubstr stripped_)
{
full = full_;
stripped = stripped_;
rem = stripped_;
// find the first column where the character is not a space
indentation = full.first_not_of(' ');
}
size_t current_col() const
{
return current_col(rem);
}
size_t current_col(csubstr s) const
{
RYML_ASSERT(s.str >= full.str);
RYML_ASSERT(full.is_super(s));
size_t col = static_cast<size_t>(s.str - full.str);
return col;
}
};
struct State
{
flag_t flags;
size_t level;
size_t node_id; // don't hold a pointer to the node as it will be relocated during tree resizes
csubstr scalar;
size_t scalar_col; // the column where the scalar (or its quotes) begin
Location pos;
LineContents line_contents;
size_t indref;
State() : flags(), level(), node_id(), scalar(), scalar_col(), pos(), line_contents(), indref() {}
void reset(const char *file, size_t node_id_)
{
flags = RUNK|RTOP;
level = 0;
pos.name = to_csubstr(file);
pos.offset = 0;
pos.line = 1;
pos.col = 1;
node_id = node_id_;
scalar_col = 0;
scalar.clear();
indref = 0;
}
};
void _line_progressed(size_t ahead);
void _line_ended();
void _line_ended_undo();
void _prepare_pop()
{
RYML_ASSERT(m_stack.size() > 1);
State const& curr = m_stack.top();
State & next = m_stack.top(1);
next.pos = curr.pos;
next.line_contents = curr.line_contents;
next.scalar = curr.scalar;
}
inline bool _at_line_begin() const
{
return m_state->line_contents.rem.begin() == m_state->line_contents.full.begin();
}
inline bool _at_line_end() const
{
csubstr r = m_state->line_contents.rem;
return r.empty() || r.begins_with(' ', r.len);
}
inline bool _token_is_from_this_line(csubstr token) const
{
return token.is_sub(m_state->line_contents.full);
}
inline NodeData * node(State const* s) const { return m_tree->get(s->node_id); }
inline NodeData * node(State const& s) const { return m_tree->get(s .node_id); }
inline NodeData * node(size_t node_id) const { return m_tree->get( node_id); }
inline bool has_all(flag_t f) const { return (m_state->flags & f) == f; }
inline bool has_any(flag_t f) const { return (m_state->flags & f) != 0; }
inline bool has_none(flag_t f) const { return (m_state->flags & f) == 0; }
static inline bool has_all(flag_t f, State const* s) { return (s->flags & f) == f; }
static inline bool has_any(flag_t f, State const* s) { return (s->flags & f) != 0; }
static inline bool has_none(flag_t f, State const* s) { return (s->flags & f) == 0; }
inline void set_flags(flag_t f) { set_flags(f, m_state); }
inline void add_flags(flag_t on) { add_flags(on, m_state); }
inline void addrem_flags(flag_t on, flag_t off) { addrem_flags(on, off, m_state); }
inline void rem_flags(flag_t off) { rem_flags(off, m_state); }
void set_flags(flag_t f, State * s);
void add_flags(flag_t on, State * s);
void addrem_flags(flag_t on, flag_t off, State * s);
void rem_flags(flag_t off, State * s);
void _resize_filter_arena(size_t num_characters);
void _grow_filter_arena(size_t num_characters);
substr _finish_filter_arena(substr dst, size_t pos);
void _prepare_locations() const; // only changes mutable members
void _resize_locations(size_t sz) const; // only changes mutable members
void _mark_locations_dirty();
bool _locations_dirty() const;
private:
void _free();
void _clr();
void _cp(Parser const* that);
void _mv(Parser *that);
#ifdef RYML_DBG
template<class ...Args> void _dbg(csubstr fmt, Args const& C4_RESTRICT ...args) const;
#endif
template<class ...Args> void _err(csubstr fmt, Args const& C4_RESTRICT ...args) const;
template<class DumpFn> void _fmt_msg(DumpFn &&dumpfn) const;
static csubstr _prfl(substr buf, flag_t v);
private:
csubstr m_file;
substr m_buf;
size_t m_root_id;
Tree * m_tree;
detail::stack<State> m_stack;
State * m_state;
size_t m_key_tag_indentation;
size_t m_key_tag2_indentation;
csubstr m_key_tag;
csubstr m_key_tag2;
size_t m_val_tag_indentation;
csubstr m_val_tag;
bool m_key_anchor_was_before;
size_t m_key_anchor_indentation;
csubstr m_key_anchor;
size_t m_val_anchor_indentation;
csubstr m_val_anchor;
substr m_filter_arena;
mutable size_t *m_newline_offsets;
mutable size_t m_newline_offsets_size;
mutable size_t m_newline_offsets_capacity;
mutable csubstr m_newline_offsets_buf;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @name parse_in_place
*
* @desc parse a mutable YAML source buffer.
*
* @note These freestanding functions use a temporary parser object,
* and are convenience functions to easily parse YAML without the need
* to instantiate a separate parser. Note that some properties
* (notably node locations in the original source code) are only
* available through the parser object after it has parsed the
* code. If you need access to any of these properties, use
* Parser::parse_in_place() */
/** @{ */
inline Tree parse_in_place( substr yaml ) { Parser np; return np.parse_in_place({} , yaml); } //!< parse in-situ a modifiable YAML source buffer.
inline Tree parse_in_place(csubstr filename, substr yaml ) { Parser np; return np.parse_in_place(filename, yaml); } //!< parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, Tree *t ) { Parser np; np.parse_in_place({} , yaml, t); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, Tree *t ) { Parser np; np.parse_in_place(filename, yaml, t); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place({} , yaml, t, node_id); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, NodeRef node ) { Parser np; np.parse_in_place({} , yaml, node); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, NodeRef node ) { Parser np; np.parse_in_place(filename, yaml, node); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_place() instead") inline Tree parse( substr yaml ) { Parser np; return np.parse_in_place({} , yaml); }
RYML_DEPRECATED("use parse_in_place() instead") inline Tree parse(csubstr filename, substr yaml ) { Parser np; return np.parse_in_place(filename, yaml); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, Tree *t ) { Parser np; np.parse_in_place({} , yaml, t); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, Tree *t ) { Parser np; np.parse_in_place(filename, yaml, t); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place({} , yaml, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place(filename, yaml, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, NodeRef node ) { Parser np; np.parse_in_place({} , yaml, node); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, NodeRef node ) { Parser np; np.parse_in_place(filename, yaml, node); }
/** @} */
//-----------------------------------------------------------------------------
/** @name parse_in_arena
* @desc parse a read-only YAML source buffer, copying it first to the tree's arena.
*
* @note These freestanding functions use a temporary parser object,
* and are convenience functions to easily parse YAML without the need
* to instantiate a separate parser. Note that some properties
* (notably node locations in the original source code) are only
* available through the parser object after it has parsed the
* code. If you need access to any of these properties, use
* Parser::parse_in_arena().
*
* @note overloads receiving a substr YAML buffer are intentionally
* left undefined, such that calling parse_in_arena() with a substr
* will cause a linker error. This is to prevent an accidental
* copy of the source buffer to the tree's arena, because substr
* is implicitly convertible to csubstr. If you really intend to parse
* a mutable buffer in the tree's arena, convert it first to immutable
* by assigning the substr to a csubstr prior to calling parse_in_arena().
* This is not needed for parse_in_place() because csubstr is not
* implicitly convertible to substr. */
/** @{ */
/* READ THE NOTE ABOVE! */
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena( substr yaml );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr yaml );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, Tree *t );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, Tree *t );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, NodeRef node );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, NodeRef node );
inline Tree parse_in_arena( csubstr yaml ) { Parser np; return np.parse_in_arena({} , yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline Tree parse_in_arena(csubstr filename, csubstr yaml ) { Parser np; return np.parse_in_arena(filename, yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena({} , yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena(filename, yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena({} , yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena({} , yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena(filename, yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline Tree parse( csubstr yaml ) { Parser np; return np.parse_in_arena({} , yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline Tree parse(csubstr filename, csubstr yaml ) { Parser np; return np.parse_in_arena(filename, yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena({} , yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena(filename, yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena({} , yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena({} , yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena(filename, yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
/** @} */
} // namespace yml
} // namespace c4
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_PARSE_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/parse.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/std/map.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/map.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_STD_MAP_HPP_
#define _C4_YML_STD_MAP_HPP_
/** @file map.hpp write/read std::map to/from a YAML tree. */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
#include <map>
namespace c4 {
namespace yml {
// std::map requires child nodes in the data
// tree hierarchy (a MAP node in ryml parlance).
// So it should be serialized via write()/read().
template<class K, class V, class Less, class Alloc>
void write(c4::yml::NodeRef *n, std::map<K, V, Less, Alloc> const& m)
{
*n |= c4::yml::MAP;
for(auto const& p : m)
{
auto ch = n->append_child();
ch << c4::yml::key(p.first);
ch << p.second;
}
}
template<class K, class V, class Less, class Alloc>
bool read(c4::yml::NodeRef const& n, std::map<K, V, Less, Alloc> * m)
{
K k{};
V v;
for(auto const ch : n)
{
ch >> c4::yml::key(k);
ch >> v;
m->emplace(std::make_pair(std::move(k), std::move(v)));
}
return true;
}
} // namespace yml
} // namespace c4
#endif // _C4_YML_STD_MAP_HPP_
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/std/map.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/std/string.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/string.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_YML_STD_STRING_HPP_
#define C4_YML_STD_STRING_HPP_
/** @file string.hpp substring conversions for/from std::string */
// everything we need is implemented here:
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/std/string.hpp
//#include <c4/std/string.hpp>
#if !defined(C4_STD_STRING_HPP_) && !defined(_C4_STD_STRING_HPP_)
#error "amalgamate: file c4/std/string.hpp must have been included at this point"
#endif /* C4_STD_STRING_HPP_ */
#endif // C4_YML_STD_STRING_HPP_
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/std/string.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/std/vector.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/vector.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_STD_VECTOR_HPP_
#define _C4_YML_STD_VECTOR_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/std/vector.hpp
//#include <c4/std/vector.hpp>
#if !defined(C4_STD_VECTOR_HPP_) && !defined(_C4_STD_VECTOR_HPP_)
#error "amalgamate: file c4/std/vector.hpp must have been included at this point"
#endif /* C4_STD_VECTOR_HPP_ */
//included above:
//#include <vector>
namespace c4 {
namespace yml {
// vector is a sequence-like type, and it requires child nodes
// in the data tree hierarchy (a SEQ node in ryml parlance).
// So it should be serialized via write()/read().
template<class V, class Alloc>
void write(c4::yml::NodeRef *n, std::vector<V, Alloc> const& vec)
{
*n |= c4::yml::SEQ;
for(auto const& v : vec)
{
n->append_child() << v;
}
}
template<class V, class Alloc>
bool read(c4::yml::NodeRef const& n, std::vector<V, Alloc> *vec)
{
vec->resize(n.num_children());
size_t pos = 0;
for(auto const ch : n)
{
ch >> (*vec)[pos++];
}
return true;
}
} // namespace yml
} // namespace c4
#endif // _C4_YML_STD_VECTOR_HPP_
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/std/vector.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/std/std.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/std.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_STD_STD_HPP_
#define _C4_YML_STD_STD_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/string.hpp
//#include "c4/yml/std/string.hpp"
#if !defined(C4_YML_STD_STRING_HPP_) && !defined(_C4_YML_STD_STRING_HPP_)
#error "amalgamate: file c4/yml/std/string.hpp must have been included at this point"
#endif /* C4_YML_STD_STRING_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/vector.hpp
//#include "c4/yml/std/vector.hpp"
#if !defined(C4_YML_STD_VECTOR_HPP_) && !defined(_C4_YML_STD_VECTOR_HPP_)
#error "amalgamate: file c4/yml/std/vector.hpp must have been included at this point"
#endif /* C4_YML_STD_VECTOR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/std/map.hpp
//#include "c4/yml/std/map.hpp"
#if !defined(C4_YML_STD_MAP_HPP_) && !defined(_C4_YML_STD_MAP_HPP_)
#error "amalgamate: file c4/yml/std/map.hpp must have been included at this point"
#endif /* C4_YML_STD_MAP_HPP_ */
#endif // _C4_YML_STD_STD_HPP_
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/std/std.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/common.cpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/common.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef RYML_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/common.hpp
//#include "c4/yml/common.hpp"
#if !defined(C4_YML_COMMON_HPP_) && !defined(_C4_YML_COMMON_HPP_)
#error "amalgamate: file c4/yml/common.hpp must have been included at this point"
#endif /* C4_YML_COMMON_HPP_ */
#ifndef RYML_NO_DEFAULT_CALLBACKS
//included above:
//# include <stdlib.h>
//included above:
//# include <stdio.h>
#endif // RYML_NO_DEFAULT_CALLBACKS
namespace c4 {
namespace yml {
namespace {
thread_local Callbacks s_default_callbacks;
} // anon namespace
#ifndef RYML_NO_DEFAULT_CALLBACKS
void report_error_impl(const char* msg, size_t length, Location loc, FILE *f)
{
if(!f)
f = stderr;
if(loc)
{
if(!loc.name.empty())
{
fwrite(loc.name.str, 1, loc.name.len, f);
fputc(':', f);
}
fprintf(f, "%zu:", loc.line);
if(loc.col)
fprintf(f, "%zu:", loc.col);
if(loc.offset)
fprintf(f, " (%zuB):", loc.offset);
}
fprintf(f, "%.*s\n", (int)length, msg);
fflush(f);
}
void error_impl(const char* msg, size_t length, Location loc, void * /*user_data*/)
{
report_error_impl(msg, length, loc, nullptr);
::abort();
}
void* allocate_impl(size_t length, void * /*hint*/, void * /*user_data*/)
{
void *mem = ::malloc(length);
if(mem == nullptr)
{
const char msg[] = "could not allocate memory";
error_impl(msg, sizeof(msg)-1, {}, nullptr);
}
return mem;
}
void free_impl(void *mem, size_t /*length*/, void * /*user_data*/)
{
::free(mem);
}
#endif // RYML_NO_DEFAULT_CALLBACKS
Callbacks::Callbacks()
:
m_user_data(nullptr),
#ifndef RYML_NO_DEFAULT_CALLBACKS
m_allocate(allocate_impl),
m_free(free_impl),
m_error(error_impl)
#else
m_allocate(nullptr),
m_free(nullptr),
m_error(nullptr)
#endif
{
}
Callbacks::Callbacks(void *user_data, pfn_allocate alloc_, pfn_free free_, pfn_error error_)
:
m_user_data(user_data),
#ifndef RYML_NO_DEFAULT_CALLBACKS
m_allocate(alloc_ ? alloc_ : allocate_impl),
m_free(free_ ? free_ : free_impl),
m_error(error_ ? error_ : error_impl)
#else
m_allocate(alloc_),
m_free(free_),
m_error(error_)
#endif
{
C4_CHECK(m_allocate);
C4_CHECK(m_free);
C4_CHECK(m_error);
}
void set_callbacks(Callbacks const& c)
{
s_default_callbacks = c;
}
Callbacks const& get_callbacks()
{
return s_default_callbacks;
}
void reset_callbacks()
{
set_callbacks(Callbacks());
}
void error(const char *msg, size_t msg_len, Location loc)
{
s_default_callbacks.m_error(msg, msg_len, loc, s_default_callbacks.m_user_data);
}
} // namespace yml
} // namespace c4
#endif /* RYML_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/common.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/tree.cpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef RYML_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//#include "c4/yml/tree.hpp"
#if !defined(C4_YML_TREE_HPP_) && !defined(_C4_YML_TREE_HPP_)
#error "amalgamate: file c4/yml/tree.hpp must have been included at this point"
#endif /* C4_YML_TREE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/parser_dbg.hpp
//#include "c4/yml/detail/parser_dbg.hpp"
#if !defined(C4_YML_DETAIL_PARSER_DBG_HPP_) && !defined(_C4_YML_DETAIL_PARSER_DBG_HPP_)
#error "amalgamate: file c4/yml/detail/parser_dbg.hpp must have been included at this point"
#endif /* C4_YML_DETAIL_PARSER_DBG_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/stack.hpp
//#include "c4/yml/detail/stack.hpp"
#if !defined(C4_YML_DETAIL_STACK_HPP_) && !defined(_C4_YML_DETAIL_STACK_HPP_)
#error "amalgamate: file c4/yml/detail/stack.hpp must have been included at this point"
#endif /* C4_YML_DETAIL_STACK_HPP_ */
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wtype-limits")
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4296/*expression is always 'boolean_value'*/)
namespace c4 {
namespace yml {
csubstr normalize_tag(csubstr tag)
{
YamlTag_e t = to_tag(tag);
if(t != TAG_NONE)
return from_tag(t);
if(tag.begins_with("!<"))
tag = tag.sub(1);
if(tag.begins_with("<!"))
return tag;
return tag;
}
csubstr normalize_tag_long(csubstr tag)
{
YamlTag_e t = to_tag(tag);
if(t != TAG_NONE)
return from_tag_long(t);
if(tag.begins_with("!<"))
tag = tag.sub(1);
if(tag.begins_with("<!"))
return tag;
return tag;
}
YamlTag_e to_tag(csubstr tag)
{
if(tag.begins_with("!<"))
tag = tag.sub(1);
if(tag.begins_with("!!"))
tag = tag.sub(2);
else if(tag.begins_with('!'))
return TAG_NONE;
else if(tag.begins_with("tag:yaml.org,2002:"))
{
RYML_ASSERT(csubstr("tag:yaml.org,2002:").len == 18);
tag = tag.sub(18);
}
else if(tag.begins_with("<tag:yaml.org,2002:"))
{
RYML_ASSERT(csubstr("<tag:yaml.org,2002:").len == 19);
tag = tag.sub(19);
if(!tag.len)
return TAG_NONE;
tag = tag.offs(0, 1);
}
if(tag == "map")
return TAG_MAP;
else if(tag == "omap")
return TAG_OMAP;
else if(tag == "pairs")
return TAG_PAIRS;
else if(tag == "set")
return TAG_SET;
else if(tag == "seq")
return TAG_SEQ;
else if(tag == "binary")
return TAG_BINARY;
else if(tag == "bool")
return TAG_BOOL;
else if(tag == "float")
return TAG_FLOAT;
else if(tag == "int")
return TAG_INT;
else if(tag == "merge")
return TAG_MERGE;
else if(tag == "null")
return TAG_NULL;
else if(tag == "str")
return TAG_STR;
else if(tag == "timestamp")
return TAG_TIMESTAMP;
else if(tag == "value")
return TAG_VALUE;
return TAG_NONE;
}
csubstr from_tag_long(YamlTag_e tag)
{
switch(tag)
{
case TAG_MAP:
return {"<tag:yaml.org,2002:map>"};
case TAG_OMAP:
return {"<tag:yaml.org,2002:omap>"};
case TAG_PAIRS:
return {"<tag:yaml.org,2002:pairs>"};
case TAG_SET:
return {"<tag:yaml.org,2002:set>"};
case TAG_SEQ:
return {"<tag:yaml.org,2002:seq>"};
case TAG_BINARY:
return {"<tag:yaml.org,2002:binary>"};
case TAG_BOOL:
return {"<tag:yaml.org,2002:bool>"};
case TAG_FLOAT:
return {"<tag:yaml.org,2002:float>"};
case TAG_INT:
return {"<tag:yaml.org,2002:int>"};
case TAG_MERGE:
return {"<tag:yaml.org,2002:merge>"};
case TAG_NULL:
return {"<tag:yaml.org,2002:null>"};
case TAG_STR:
return {"<tag:yaml.org,2002:str>"};
case TAG_TIMESTAMP:
return {"<tag:yaml.org,2002:timestamp>"};
case TAG_VALUE:
return {"<tag:yaml.org,2002:value>"};
case TAG_YAML:
return {"<tag:yaml.org,2002:yaml>"};
case TAG_NONE:
return {""};
}
return {""};
}
csubstr from_tag(YamlTag_e tag)
{
switch(tag)
{
case TAG_MAP:
return {"!!map"};
case TAG_OMAP:
return {"!!omap"};
case TAG_PAIRS:
return {"!!pairs"};
case TAG_SET:
return {"!!set"};
case TAG_SEQ:
return {"!!seq"};
case TAG_BINARY:
return {"!!binary"};
case TAG_BOOL:
return {"!!bool"};
case TAG_FLOAT:
return {"!!float"};
case TAG_INT:
return {"!!int"};
case TAG_MERGE:
return {"!!merge"};
case TAG_NULL:
return {"!!null"};
case TAG_STR:
return {"!!str"};
case TAG_TIMESTAMP:
return {"!!timestamp"};
case TAG_VALUE:
return {"!!value"};
case TAG_YAML:
return {"!!yaml"};
case TAG_NONE:
return {""};
}
return {""};
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char* NodeType::type_str(NodeType_e ty)
{
switch(ty & _TYMASK)
{
case KEYVAL:
return "KEYVAL";
case KEY:
return "KEY";
case VAL:
return "VAL";
case MAP:
return "MAP";
case SEQ:
return "SEQ";
case KEYMAP:
return "KEYMAP";
case KEYSEQ:
return "KEYSEQ";
case DOCSEQ:
return "DOCSEQ";
case DOCMAP:
return "DOCMAP";
case DOCVAL:
return "DOCVAL";
case DOC:
return "DOC";
case STREAM:
return "STREAM";
case NOTYPE:
return "NOTYPE";
default:
if((ty & KEYVAL) == KEYVAL)
return "KEYVAL***";
if((ty & KEYMAP) == KEYMAP)
return "KEYMAP***";
if((ty & KEYSEQ) == KEYSEQ)
return "KEYSEQ***";
if((ty & DOCSEQ) == DOCSEQ)
return "DOCSEQ***";
if((ty & DOCMAP) == DOCMAP)
return "DOCMAP***";
if((ty & DOCVAL) == DOCVAL)
return "DOCVAL***";
if(ty & KEY)
return "KEY***";
if(ty & VAL)
return "VAL***";
if(ty & MAP)
return "MAP***";
if(ty & SEQ)
return "SEQ***";
if(ty & DOC)
return "DOC***";
return "(unk)";
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
NodeRef Tree::rootref()
{
return NodeRef(this, root_id());
}
NodeRef const Tree::rootref() const
{
return NodeRef(const_cast<Tree*>(this), root_id());
}
NodeRef Tree::ref(size_t id)
{
_RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size);
return NodeRef(this, id);
}
NodeRef const Tree::ref(size_t id) const
{
_RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size);
return NodeRef(const_cast<Tree*>(this), id);
}
NodeRef Tree::operator[] (csubstr key)
{
return rootref()[key];
}
NodeRef const Tree::operator[] (csubstr key) const
{
return rootref()[key];
}
NodeRef Tree::operator[] (size_t i)
{
return rootref()[i];
}
NodeRef const Tree::operator[] (size_t i) const
{
return rootref()[i];
}
NodeRef Tree::docref(size_t i)
{
return ref(doc(i));
}
NodeRef const Tree::docref(size_t i) const
{
return ref(doc(i));
}
//-----------------------------------------------------------------------------
Tree::Tree(Callbacks const& cb)
: m_buf(nullptr)
, m_cap(0)
, m_size(0)
, m_free_head(NONE)
, m_free_tail(NONE)
, m_arena()
, m_arena_pos(0)
, m_callbacks(cb)
{
}
Tree::Tree(size_t node_capacity, size_t arena_capacity, Callbacks const& cb)
: Tree(cb)
{
reserve(node_capacity);
reserve_arena(arena_capacity);
}
Tree::~Tree()
{
_free();
}
Tree::Tree(Tree const& that) noexcept : Tree(that.m_callbacks)
{
_copy(that);
}
Tree& Tree::operator= (Tree const& that) noexcept
{
_free();
m_callbacks = that.m_callbacks;
_copy(that);
return *this;
}
Tree::Tree(Tree && that) noexcept : Tree(that.m_callbacks)
{
_move(that);
}
Tree& Tree::operator= (Tree && that) noexcept
{
_free();
m_callbacks = that.m_callbacks;
_move(that);
return *this;
}
void Tree::_free()
{
if(m_buf)
{
_RYML_CB_ASSERT(m_callbacks, m_cap > 0);
_RYML_CB_FREE(m_callbacks, m_buf, NodeData, m_cap);
}
if(m_arena.str)
{
_RYML_CB_ASSERT(m_callbacks, m_arena.len > 0);
_RYML_CB_FREE(m_callbacks, m_arena.str, char, m_arena.len);
}
_clear();
}
C4_SUPPRESS_WARNING_GCC_PUSH
#if defined(__GNUC__) && __GNUC__>= 8
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wclass-memaccess") // error: void* memset(void*, int, size_t) clearing an object of type class c4::yml::Tree with no trivial copy-assignment; use assignment or value-initialization instead
#endif
void Tree::_clear()
{
m_buf = nullptr;
m_cap = 0;
m_size = 0;
m_free_head = 0;
m_free_tail = 0;
m_arena = {};
m_arena_pos = 0;
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
m_tag_directives[i] = {};
}
void Tree::_copy(Tree const& that)
{
_RYML_CB_ASSERT(m_callbacks, m_buf == nullptr);
_RYML_CB_ASSERT(m_callbacks, m_arena.str == nullptr);
_RYML_CB_ASSERT(m_callbacks, m_arena.len == 0);
m_buf = _RYML_CB_ALLOC_HINT(m_callbacks, NodeData, that.m_cap, that.m_buf);
memcpy(m_buf, that.m_buf, that.m_cap * sizeof(NodeData));
m_cap = that.m_cap;
m_size = that.m_size;
m_free_head = that.m_free_head;
m_free_tail = that.m_free_tail;
m_arena_pos = that.m_arena_pos;
m_arena = that.m_arena;
if(that.m_arena.str)
{
_RYML_CB_ASSERT(m_callbacks, that.m_arena.len > 0);
substr arena;
arena.str = _RYML_CB_ALLOC_HINT(m_callbacks, char, that.m_arena.len, that.m_arena.str);
arena.len = that.m_arena.len;
_relocate(arena); // does a memcpy of the arena and updates nodes using the old arena
m_arena = arena;
}
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
m_tag_directives[i] = that.m_tag_directives[i];
}
void Tree::_move(Tree & that)
{
_RYML_CB_ASSERT(m_callbacks, m_buf == nullptr);
_RYML_CB_ASSERT(m_callbacks, m_arena.str == nullptr);
_RYML_CB_ASSERT(m_callbacks, m_arena.len == 0);
m_buf = that.m_buf;
m_cap = that.m_cap;
m_size = that.m_size;
m_free_head = that.m_free_head;
m_free_tail = that.m_free_tail;
m_arena = that.m_arena;
m_arena_pos = that.m_arena_pos;
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
m_tag_directives[i] = that.m_tag_directives[i];
that._clear();
}
void Tree::_relocate(substr next_arena)
{
_RYML_CB_ASSERT(m_callbacks, next_arena.not_empty());
_RYML_CB_ASSERT(m_callbacks, next_arena.len >= m_arena.len);
memcpy(next_arena.str, m_arena.str, m_arena_pos);
for(NodeData *C4_RESTRICT n = m_buf, *e = m_buf + m_cap; n != e; ++n)
{
if(in_arena(n->m_key.scalar))
n->m_key.scalar = _relocated(n->m_key.scalar, next_arena);
if(in_arena(n->m_key.tag))
n->m_key.tag = _relocated(n->m_key.tag, next_arena);
if(in_arena(n->m_key.anchor))
n->m_key.anchor = _relocated(n->m_key.anchor, next_arena);
if(in_arena(n->m_val.scalar))
n->m_val.scalar = _relocated(n->m_val.scalar, next_arena);
if(in_arena(n->m_val.tag))
n->m_val.tag = _relocated(n->m_val.tag, next_arena);
if(in_arena(n->m_val.anchor))
n->m_val.anchor = _relocated(n->m_val.anchor, next_arena);
}
for(TagDirective &C4_RESTRICT td : m_tag_directives)
{
if(in_arena(td.prefix))
td.prefix = _relocated(td.prefix, next_arena);
if(in_arena(td.handle))
td.handle = _relocated(td.handle, next_arena);
}
}
//-----------------------------------------------------------------------------
void Tree::reserve(size_t cap)
{
if(cap > m_cap)
{
NodeData *buf = _RYML_CB_ALLOC_HINT(m_callbacks, NodeData, cap, m_buf);
if(m_buf)
{
memcpy(buf, m_buf, m_cap * sizeof(NodeData));
_RYML_CB_FREE(m_callbacks, m_buf, NodeData, m_cap);
}
size_t first = m_cap, del = cap - m_cap;
m_cap = cap;
m_buf = buf;
_clear_range(first, del);
if(m_free_head != NONE)
{
_RYML_CB_ASSERT(m_callbacks, m_buf != nullptr);
_RYML_CB_ASSERT(m_callbacks, m_free_tail != NONE);
m_buf[m_free_tail].m_next_sibling = first;
m_buf[first].m_prev_sibling = m_free_tail;
m_free_tail = cap-1;
}
else
{
_RYML_CB_ASSERT(m_callbacks, m_free_tail == NONE);
m_free_head = first;
m_free_tail = cap-1;
}
_RYML_CB_ASSERT(m_callbacks, m_free_head == NONE || (m_free_head >= 0 && m_free_head < cap));
_RYML_CB_ASSERT(m_callbacks, m_free_tail == NONE || (m_free_tail >= 0 && m_free_tail < cap));
if( ! m_size)
_claim_root();
}
}
//-----------------------------------------------------------------------------
void Tree::clear()
{
_clear_range(0, m_cap);
m_size = 0;
if(m_buf)
{
_RYML_CB_ASSERT(m_callbacks, m_cap >= 0);
m_free_head = 0;
m_free_tail = m_cap-1;
_claim_root();
}
else
{
m_free_head = NONE;
m_free_tail = NONE;
}
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
m_tag_directives[i] = {};
}
void Tree::_claim_root()
{
size_t r = _claim();
_RYML_CB_ASSERT(m_callbacks, r == 0);
_set_hierarchy(r, NONE, NONE);
}
//-----------------------------------------------------------------------------
void Tree::_clear_range(size_t first, size_t num)
{
if(num == 0)
return; // prevent overflow when subtracting
_RYML_CB_ASSERT(m_callbacks, first >= 0 && first + num <= m_cap);
memset(m_buf + first, 0, num * sizeof(NodeData)); // TODO we should not need this
for(size_t i = first, e = first + num; i < e; ++i)
{
_clear(i);
NodeData *n = m_buf + i;
n->m_prev_sibling = i - 1;
n->m_next_sibling = i + 1;
}
m_buf[first + num - 1].m_next_sibling = NONE;
}
C4_SUPPRESS_WARNING_GCC_POP
//-----------------------------------------------------------------------------
void Tree::_release(size_t i)
{
_RYML_CB_ASSERT(m_callbacks, i >= 0 && i < m_cap);
_rem_hierarchy(i);
_free_list_add(i);
_clear(i);
--m_size;
}
//-----------------------------------------------------------------------------
// add to the front of the free list
void Tree::_free_list_add(size_t i)
{
_RYML_CB_ASSERT(m_callbacks, i >= 0 && i < m_cap);
NodeData &C4_RESTRICT w = m_buf[i];
w.m_parent = NONE;
w.m_next_sibling = m_free_head;
w.m_prev_sibling = NONE;
if(m_free_head != NONE)
m_buf[m_free_head].m_prev_sibling = i;
m_free_head = i;
if(m_free_tail == NONE)
m_free_tail = m_free_head;
}
void Tree::_free_list_rem(size_t i)
{
if(m_free_head == i)
m_free_head = _p(i)->m_next_sibling;
_rem_hierarchy(i);
}
//-----------------------------------------------------------------------------
size_t Tree::_claim()
{
if(m_free_head == NONE || m_buf == nullptr)
{
size_t sz = 2 * m_cap;
sz = sz ? sz : 16;
reserve(sz);
_RYML_CB_ASSERT(m_callbacks, m_free_head != NONE);
}
_RYML_CB_ASSERT(m_callbacks, m_size < m_cap);
_RYML_CB_ASSERT(m_callbacks, m_free_head >= 0 && m_free_head < m_cap);
size_t ichild = m_free_head;
NodeData *child = m_buf + ichild;
++m_size;
m_free_head = child->m_next_sibling;
if(m_free_head == NONE)
{
m_free_tail = NONE;
_RYML_CB_ASSERT(m_callbacks, m_size == m_cap);
}
_clear(ichild);
return ichild;
}
//-----------------------------------------------------------------------------
C4_SUPPRESS_WARNING_GCC_PUSH
C4_SUPPRESS_WARNING_CLANG_PUSH
C4_SUPPRESS_WARNING_CLANG("-Wnull-dereference")
#if defined(__GNUC__) && (__GNUC__ >= 6)
C4_SUPPRESS_WARNING_GCC("-Wnull-dereference")
#endif
void Tree::_set_hierarchy(size_t ichild, size_t iparent, size_t iprev_sibling)
{
_RYML_CB_ASSERT(m_callbacks, iparent == NONE || (iparent >= 0 && iparent < m_cap));
_RYML_CB_ASSERT(m_callbacks, iprev_sibling == NONE || (iprev_sibling >= 0 && iprev_sibling < m_cap));
NodeData *C4_RESTRICT child = get(ichild);
child->m_parent = iparent;
child->m_prev_sibling = NONE;
child->m_next_sibling = NONE;
if(iparent == NONE)
{
_RYML_CB_ASSERT(m_callbacks, ichild == 0);
_RYML_CB_ASSERT(m_callbacks, iprev_sibling == NONE);
}
if(iparent == NONE)
return;
size_t inext_sibling = iprev_sibling != NONE ? next_sibling(iprev_sibling) : first_child(iparent);
NodeData *C4_RESTRICT parent = get(iparent);
NodeData *C4_RESTRICT psib = get(iprev_sibling);
NodeData *C4_RESTRICT nsib = get(inext_sibling);
if(psib)
{
_RYML_CB_ASSERT(m_callbacks, next_sibling(iprev_sibling) == id(nsib));
child->m_prev_sibling = id(psib);
psib->m_next_sibling = id(child);
_RYML_CB_ASSERT(m_callbacks, psib->m_prev_sibling != psib->m_next_sibling || psib->m_prev_sibling == NONE);
}
if(nsib)
{
_RYML_CB_ASSERT(m_callbacks, prev_sibling(inext_sibling) == id(psib));
child->m_next_sibling = id(nsib);
nsib->m_prev_sibling = id(child);
_RYML_CB_ASSERT(m_callbacks, nsib->m_prev_sibling != nsib->m_next_sibling || nsib->m_prev_sibling == NONE);
}
if(parent->m_first_child == NONE)
{
_RYML_CB_ASSERT(m_callbacks, parent->m_last_child == NONE);
parent->m_first_child = id(child);
parent->m_last_child = id(child);
}
else
{
if(child->m_next_sibling == parent->m_first_child)
parent->m_first_child = id(child);
if(child->m_prev_sibling == parent->m_last_child)
parent->m_last_child = id(child);
}
}
C4_SUPPRESS_WARNING_GCC_POP
C4_SUPPRESS_WARNING_CLANG_POP
//-----------------------------------------------------------------------------
void Tree::_rem_hierarchy(size_t i)
{
_RYML_CB_ASSERT(m_callbacks, i >= 0 && i < m_cap);
NodeData &C4_RESTRICT w = m_buf[i];
// remove from the parent
if(w.m_parent != NONE)
{
NodeData &C4_RESTRICT p = m_buf[w.m_parent];
if(p.m_first_child == i)
{
p.m_first_child = w.m_next_sibling;
}
if(p.m_last_child == i)
{
p.m_last_child = w.m_prev_sibling;
}
}
// remove from the used list
if(w.m_prev_sibling != NONE)
{
NodeData *C4_RESTRICT prev = get(w.m_prev_sibling);
prev->m_next_sibling = w.m_next_sibling;
}
if(w.m_next_sibling != NONE)
{
NodeData *C4_RESTRICT next = get(w.m_next_sibling);
next->m_prev_sibling = w.m_prev_sibling;
}
}
//-----------------------------------------------------------------------------
void Tree::reorder()
{
size_t r = root_id();
_do_reorder(&r, 0);
}
//-----------------------------------------------------------------------------
size_t Tree::_do_reorder(size_t *node, size_t count)
{
// swap this node if it's not in place
if(*node != count)
{
_swap(*node, count);
*node = count;
}
++count; // bump the count from this node
// now descend in the hierarchy
for(size_t i = first_child(*node); i != NONE; i = next_sibling(i))
{
// this child may have been relocated to a different index,
// so get an updated version
count = _do_reorder(&i, count);
}
return count;
}
//-----------------------------------------------------------------------------
void Tree::_swap(size_t n_, size_t m_)
{
_RYML_CB_ASSERT(m_callbacks, (parent(n_) != NONE) || type(n_) == NOTYPE);
_RYML_CB_ASSERT(m_callbacks, (parent(m_) != NONE) || type(m_) == NOTYPE);
NodeType tn = type(n_);
NodeType tm = type(m_);
if(tn != NOTYPE && tm != NOTYPE)
{
_swap_props(n_, m_);
_swap_hierarchy(n_, m_);
}
else if(tn == NOTYPE && tm != NOTYPE)
{
_copy_props(n_, m_);
_free_list_rem(n_);
_copy_hierarchy(n_, m_);
_clear(m_);
_free_list_add(m_);
}
else if(tn != NOTYPE && tm == NOTYPE)
{
_copy_props(m_, n_);
_free_list_rem(m_);
_copy_hierarchy(m_, n_);
_clear(n_);
_free_list_add(n_);
}
else
{
C4_NEVER_REACH();
}
}
//-----------------------------------------------------------------------------
void Tree::_swap_hierarchy(size_t ia, size_t ib)
{
if(ia == ib) return;
for(size_t i = first_child(ia); i != NONE; i = next_sibling(i))
{
if(i == ib || i == ia)
continue;
_p(i)->m_parent = ib;
}
for(size_t i = first_child(ib); i != NONE; i = next_sibling(i))
{
if(i == ib || i == ia)
continue;
_p(i)->m_parent = ia;
}
auto & C4_RESTRICT a = *_p(ia);
auto & C4_RESTRICT b = *_p(ib);
auto & C4_RESTRICT pa = *_p(a.m_parent);
auto & C4_RESTRICT pb = *_p(b.m_parent);
if(&pa == &pb)
{
if((pa.m_first_child == ib && pa.m_last_child == ia)
||
(pa.m_first_child == ia && pa.m_last_child == ib))
{
std::swap(pa.m_first_child, pa.m_last_child);
}
else
{
bool changed = false;
if(pa.m_first_child == ia)
{
pa.m_first_child = ib;
changed = true;
}
if(pa.m_last_child == ia)
{
pa.m_last_child = ib;
changed = true;
}
if(pb.m_first_child == ib && !changed)
{
pb.m_first_child = ia;
}
if(pb.m_last_child == ib && !changed)
{
pb.m_last_child = ia;
}
}
}
else
{
if(pa.m_first_child == ia)
pa.m_first_child = ib;
if(pa.m_last_child == ia)
pa.m_last_child = ib;
if(pb.m_first_child == ib)
pb.m_first_child = ia;
if(pb.m_last_child == ib)
pb.m_last_child = ia;
}
std::swap(a.m_first_child , b.m_first_child);
std::swap(a.m_last_child , b.m_last_child);
if(a.m_prev_sibling != ib && b.m_prev_sibling != ia &&
a.m_next_sibling != ib && b.m_next_sibling != ia)
{
if(a.m_prev_sibling != NONE && a.m_prev_sibling != ib)
_p(a.m_prev_sibling)->m_next_sibling = ib;
if(a.m_next_sibling != NONE && a.m_next_sibling != ib)
_p(a.m_next_sibling)->m_prev_sibling = ib;
if(b.m_prev_sibling != NONE && b.m_prev_sibling != ia)
_p(b.m_prev_sibling)->m_next_sibling = ia;
if(b.m_next_sibling != NONE && b.m_next_sibling != ia)
_p(b.m_next_sibling)->m_prev_sibling = ia;
std::swap(a.m_prev_sibling, b.m_prev_sibling);
std::swap(a.m_next_sibling, b.m_next_sibling);
}
else
{
if(a.m_next_sibling == ib) // n will go after m
{
_RYML_CB_ASSERT(m_callbacks, b.m_prev_sibling == ia);
if(a.m_prev_sibling != NONE)
{
_RYML_CB_ASSERT(m_callbacks, a.m_prev_sibling != ib);
_p(a.m_prev_sibling)->m_next_sibling = ib;
}
if(b.m_next_sibling != NONE)
{
_RYML_CB_ASSERT(m_callbacks, b.m_next_sibling != ia);
_p(b.m_next_sibling)->m_prev_sibling = ia;
}
size_t ns = b.m_next_sibling;
b.m_prev_sibling = a.m_prev_sibling;
b.m_next_sibling = ia;
a.m_prev_sibling = ib;
a.m_next_sibling = ns;
}
else if(a.m_prev_sibling == ib) // m will go after n
{
_RYML_CB_ASSERT(m_callbacks, b.m_next_sibling == ia);
if(b.m_prev_sibling != NONE)
{
_RYML_CB_ASSERT(m_callbacks, b.m_prev_sibling != ia);
_p(b.m_prev_sibling)->m_next_sibling = ia;
}
if(a.m_next_sibling != NONE)
{
_RYML_CB_ASSERT(m_callbacks, a.m_next_sibling != ib);
_p(a.m_next_sibling)->m_prev_sibling = ib;
}
size_t ns = b.m_prev_sibling;
a.m_prev_sibling = b.m_prev_sibling;
a.m_next_sibling = ib;
b.m_prev_sibling = ia;
b.m_next_sibling = ns;
}
else
{
C4_NEVER_REACH();
}
}
_RYML_CB_ASSERT(m_callbacks, a.m_next_sibling != ia);
_RYML_CB_ASSERT(m_callbacks, a.m_prev_sibling != ia);
_RYML_CB_ASSERT(m_callbacks, b.m_next_sibling != ib);
_RYML_CB_ASSERT(m_callbacks, b.m_prev_sibling != ib);
if(a.m_parent != ib && b.m_parent != ia)
{
std::swap(a.m_parent, b.m_parent);
}
else
{
if(a.m_parent == ib && b.m_parent != ia)
{
a.m_parent = b.m_parent;
b.m_parent = ia;
}
else if(a.m_parent != ib && b.m_parent == ia)
{
b.m_parent = a.m_parent;
a.m_parent = ib;
}
else
{
C4_NEVER_REACH();
}
}
}
//-----------------------------------------------------------------------------
void Tree::_copy_hierarchy(size_t dst_, size_t src_)
{
auto const& C4_RESTRICT src = *_p(src_);
auto & C4_RESTRICT dst = *_p(dst_);
auto & C4_RESTRICT prt = *_p(src.m_parent);
for(size_t i = src.m_first_child; i != NONE; i = next_sibling(i))
{
_p(i)->m_parent = dst_;
}
if(src.m_prev_sibling != NONE)
{
_p(src.m_prev_sibling)->m_next_sibling = dst_;
}
if(src.m_next_sibling != NONE)
{
_p(src.m_next_sibling)->m_prev_sibling = dst_;
}
if(prt.m_first_child == src_)
{
prt.m_first_child = dst_;
}
if(prt.m_last_child == src_)
{
prt.m_last_child = dst_;
}
dst.m_parent = src.m_parent;
dst.m_first_child = src.m_first_child;
dst.m_last_child = src.m_last_child;
dst.m_prev_sibling = src.m_prev_sibling;
dst.m_next_sibling = src.m_next_sibling;
}
//-----------------------------------------------------------------------------
void Tree::_swap_props(size_t n_, size_t m_)
{
NodeData &C4_RESTRICT n = *_p(n_);
NodeData &C4_RESTRICT m = *_p(m_);
std::swap(n.m_type, m.m_type);
std::swap(n.m_key, m.m_key);
std::swap(n.m_val, m.m_val);
}
//-----------------------------------------------------------------------------
void Tree::move(size_t node, size_t after)
{
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, ! is_root(node));
_RYML_CB_ASSERT(m_callbacks, has_sibling(node, after) && has_sibling(after, node));
_rem_hierarchy(node);
_set_hierarchy(node, parent(node), after);
}
//-----------------------------------------------------------------------------
void Tree::move(size_t node, size_t new_parent, size_t after)
{
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, new_parent != NONE);
_RYML_CB_ASSERT(m_callbacks, ! is_root(node));
_rem_hierarchy(node);
_set_hierarchy(node, new_parent, after);
}
size_t Tree::move(Tree *src, size_t node, size_t new_parent, size_t after)
{
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, new_parent != NONE);
size_t dup = duplicate(src, node, new_parent, after);
src->remove(node);
return dup;
}
void Tree::set_root_as_stream()
{
size_t root = root_id();
if(is_stream(root))
return;
// don't use _add_flags() because it's checked and will fail
if(!has_children(root))
{
if(is_val(root))
{
_p(root)->m_type.add(SEQ);
size_t next_doc = append_child(root);
_copy_props_wo_key(next_doc, root);
_p(next_doc)->m_type.add(DOC);
_p(next_doc)->m_type.rem(SEQ);
}
_p(root)->m_type = STREAM;
return;
}
_RYML_CB_ASSERT(m_callbacks, !has_key(root));
size_t next_doc = append_child(root);
_copy_props_wo_key(next_doc, root);
_add_flags(next_doc, DOC);
for(size_t prev = NONE, ch = first_child(root), next = next_sibling(ch); ch != NONE; )
{
if(ch == next_doc)
break;
move(ch, next_doc, prev);
prev = ch;
ch = next;
next = next_sibling(next);
}
_p(root)->m_type = STREAM;
}
//-----------------------------------------------------------------------------
void Tree::remove_children(size_t node)
{
_RYML_CB_ASSERT(m_callbacks, get(node) != nullptr);
size_t ich = get(node)->m_first_child;
while(ich != NONE)
{
remove_children(ich);
_RYML_CB_ASSERT(m_callbacks, get(ich) != nullptr);
size_t next = get(ich)->m_next_sibling;
_release(ich);
if(ich == get(node)->m_last_child)
break;
ich = next;
}
}
bool Tree::change_type(size_t node, NodeType type)
{
_RYML_CB_ASSERT(m_callbacks, type.is_val() || type.is_map() || type.is_seq());
_RYML_CB_ASSERT(m_callbacks, type.is_val() + type.is_map() + type.is_seq() == 1);
_RYML_CB_ASSERT(m_callbacks, type.has_key() == has_key(node) || (has_key(node) && !type.has_key()));
NodeData *d = _p(node);
if(type.is_map() && is_map(node))
return false;
else if(type.is_seq() && is_seq(node))
return false;
else if(type.is_val() && is_val(node))
return false;
d->m_type = (d->m_type & (~(MAP|SEQ|VAL))) | type;
remove_children(node);
return true;
}
//-----------------------------------------------------------------------------
size_t Tree::duplicate(size_t node, size_t parent, size_t after)
{
return duplicate(this, node, parent, after);
}
size_t Tree::duplicate(Tree const* src, size_t node, size_t parent, size_t after)
{
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, parent != NONE);
_RYML_CB_ASSERT(m_callbacks, ! src->is_root(node));
size_t copy = _claim();
_copy_props(copy, src, node);
_set_hierarchy(copy, parent, after);
duplicate_children(src, node, copy, NONE);
return copy;
}
//-----------------------------------------------------------------------------
size_t Tree::duplicate_children(size_t node, size_t parent, size_t after)
{
return duplicate_children(this, node, parent, after);
}
size_t Tree::duplicate_children(Tree const* src, size_t node, size_t parent, size_t after)
{
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, parent != NONE);
_RYML_CB_ASSERT(m_callbacks, after == NONE || has_child(parent, after));
size_t prev = after;
for(size_t i = src->first_child(node); i != NONE; i = src->next_sibling(i))
{
prev = duplicate(src, i, parent, prev);
}
return prev;
}
//-----------------------------------------------------------------------------
void Tree::duplicate_contents(size_t node, size_t where)
{
duplicate_contents(this, node, where);
}
void Tree::duplicate_contents(Tree const *src, size_t node, size_t where)
{
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, where != NONE);
_copy_props_wo_key(where, src, node);
duplicate_children(src, node, where, last_child(where));
}
//-----------------------------------------------------------------------------
size_t Tree::duplicate_children_no_rep(size_t node, size_t parent, size_t after)
{
return duplicate_children_no_rep(this, node, parent, after);
}
size_t Tree::duplicate_children_no_rep(Tree const *src, size_t node, size_t parent, size_t after)
{
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, parent != NONE);
_RYML_CB_ASSERT(m_callbacks, after == NONE || has_child(parent, after));
// don't loop using pointers as there may be a relocation
// find the position where "after" is
size_t after_pos = NONE;
if(after != NONE)
{
for(size_t i = first_child(parent), icount = 0; i != NONE; ++icount, i = next_sibling(i))
{
if(i == after)
{
after_pos = icount;
break;
}
}
_RYML_CB_ASSERT(m_callbacks, after_pos != NONE);
}
// for each child to be duplicated...
size_t prev = after;
for(size_t i = src->first_child(node), icount = 0; i != NONE; ++icount, i = src->next_sibling(i))
{
if(is_seq(parent))
{
prev = duplicate(i, parent, prev);
}
else
{
_RYML_CB_ASSERT(m_callbacks, is_map(parent));
// does the parent already have a node with key equal to that of the current duplicate?
size_t rep = NONE, rep_pos = NONE;
for(size_t j = first_child(parent), jcount = 0; j != NONE; ++jcount, j = next_sibling(j))
{
if(key(j) == key(i))
{
rep = j;
rep_pos = jcount;
break;
}
}
if(rep == NONE) // there is no repetition; just duplicate
{
prev = duplicate(src, i, parent, prev);
}
else // yes, there is a repetition
{
if(after_pos != NONE && rep_pos < after_pos)
{
// rep is located before the node which will be inserted,
// and will be overridden by the duplicate. So replace it.
remove(rep);
prev = duplicate(src, i, parent, prev);
}
else if(after_pos == NONE || rep_pos >= after_pos)
{
// rep is located after the node which will be inserted
// and overrides it. So move the rep into this node's place.
if(rep != prev)
{
move(rep, prev);
prev = rep;
}
}
} // there's a repetition
}
}
return prev;
}
//-----------------------------------------------------------------------------
void Tree::merge_with(Tree const *src, size_t src_node, size_t dst_node)
{
_RYML_CB_ASSERT(m_callbacks, src != nullptr);
if(src_node == NONE)
src_node = src->root_id();
if(dst_node == NONE)
dst_node = root_id();
_RYML_CB_ASSERT(m_callbacks, src->has_val(src_node) || src->is_seq(src_node) || src->is_map(src_node));
if(src->has_val(src_node))
{
if( ! has_val(dst_node))
{
if(has_children(dst_node))
remove_children(dst_node);
}
if(src->is_keyval(src_node))
_copy_props(dst_node, src, src_node);
else if(src->is_val(src_node))
_copy_props_wo_key(dst_node, src, src_node);
else
C4_NEVER_REACH();
}
else if(src->is_seq(src_node))
{
if( ! is_seq(dst_node))
{
if(has_children(dst_node))
remove_children(dst_node);
_clear_type(dst_node);
if(src->has_key(src_node))
to_seq(dst_node, src->key(src_node));
else
to_seq(dst_node);
}
for(size_t sch = src->first_child(src_node); sch != NONE; sch = src->next_sibling(sch))
{
size_t dch = append_child(dst_node);
_copy_props_wo_key(dch, src, sch);
merge_with(src, sch, dch);
}
}
else if(src->is_map(src_node))
{
if( ! is_map(dst_node))
{
if(has_children(dst_node))
remove_children(dst_node);
_clear_type(dst_node);
if(src->has_key(src_node))
to_map(dst_node, src->key(src_node));
else
to_map(dst_node);
}
for(size_t sch = src->first_child(src_node); sch != NONE; sch = src->next_sibling(sch))
{
size_t dch = find_child(dst_node, src->key(sch));
if(dch == NONE)
{
dch = append_child(dst_node);
_copy_props(dch, src, sch);
}
merge_with(src, sch, dch);
}
}
else
{
C4_NEVER_REACH();
}
}
//-----------------------------------------------------------------------------
namespace detail {
/** @todo make this part of the public API, refactoring as appropriate
* to be able to use the same resolver to handle multiple trees (one
* at a time) */
struct ReferenceResolver
{
struct refdata
{
NodeType type;
size_t node;
size_t prev_anchor;
size_t target;
size_t parent_ref;
size_t parent_ref_sibling;
};
Tree *t;
/** from the specs: "an alias node refers to the most recent
* node in the serialization having the specified anchor". So
* we need to start looking upward from ref nodes.
*
* @see http://yaml.org/spec/1.2/spec.html#id2765878 */
stack<refdata> refs;
ReferenceResolver(Tree *t_) : t(t_), refs(t_->callbacks())
{
resolve();
}
void store_anchors_and_refs()
{
// minimize (re-)allocations by counting first
size_t num_anchors_and_refs = count_anchors_and_refs(t->root_id());
if(!num_anchors_and_refs)
return;
refs.reserve(num_anchors_and_refs);
// now descend through the hierarchy
_store_anchors_and_refs(t->root_id());
// finally connect the reference list
size_t prev_anchor = npos;
size_t count = 0;
for(auto &rd : refs)
{
rd.prev_anchor = prev_anchor;
if(rd.type.is_anchor())
prev_anchor = count;
++count;
}
}
size_t count_anchors_and_refs(size_t n)
{
size_t c = 0;
c += t->has_key_anchor(n);
c += t->has_val_anchor(n);
c += t->is_key_ref(n);
c += t->is_val_ref(n);
for(size_t ch = t->first_child(n); ch != NONE; ch = t->next_sibling(ch))
c += count_anchors_and_refs(ch);
return c;
}
void _store_anchors_and_refs(size_t n)
{
if(t->is_key_ref(n) || t->is_val_ref(n) || (t->has_key(n) && t->key(n) == "<<"))
{
if(t->is_seq(n))
{
// for merging multiple inheritance targets
// <<: [ *CENTER, *BIG ]
for(size_t ich = t->first_child(n); ich != NONE; ich = t->next_sibling(ich))
{
RYML_ASSERT(t->num_children(ich) == 0);
refs.push({VALREF, ich, npos, npos, n, t->next_sibling(n)});
}
return;
}
if(t->is_key_ref(n) && t->key(n) != "<<") // insert key refs BEFORE inserting val refs
{
RYML_CHECK((!t->has_key(n)) || t->key(n).ends_with(t->key_ref(n)));
refs.push({KEYREF, n, npos, npos, NONE, NONE});
}
if(t->is_val_ref(n))
{
RYML_CHECK((!t->has_val(n)) || t->val(n).ends_with(t->val_ref(n)));
refs.push({VALREF, n, npos, npos, NONE, NONE});
}
}
if(t->has_key_anchor(n))
{
RYML_CHECK(t->has_key(n));
refs.push({KEYANCH, n, npos, npos, NONE, NONE});
}
if(t->has_val_anchor(n))
{
RYML_CHECK(t->has_val(n) || t->is_container(n));
refs.push({VALANCH, n, npos, npos, NONE, NONE});
}
for(size_t ch = t->first_child(n); ch != NONE; ch = t->next_sibling(ch))
{
_store_anchors_and_refs(ch);
}
}
size_t lookup_(refdata *C4_RESTRICT ra)
{
RYML_ASSERT(ra->type.is_key_ref() || ra->type.is_val_ref());
RYML_ASSERT(ra->type.is_key_ref() != ra->type.is_val_ref());
csubstr refname;
if(ra->type.is_val_ref())
{
refname = t->val_ref(ra->node);
}
else
{
RYML_ASSERT(ra->type.is_key_ref());
refname = t->key_ref(ra->node);
}
while(ra->prev_anchor != npos)
{
ra = &refs[ra->prev_anchor];
if(t->has_anchor(ra->node, refname))
return ra->node;
}
#ifndef RYML_ERRMSG_SIZE
#define RYML_ERRMSG_SIZE 1024
#endif
char errmsg[RYML_ERRMSG_SIZE];
snprintf(errmsg, RYML_ERRMSG_SIZE, "anchor does not exist: '%.*s'",
static_cast<int>(refname.size()), refname.data());
c4::yml::error(errmsg);
return NONE;
}
void resolve()
{
store_anchors_and_refs();
if(refs.empty())
return;
/* from the specs: "an alias node refers to the most recent
* node in the serialization having the specified anchor". So
* we need to start looking upward from ref nodes.
*
* @see http://yaml.org/spec/1.2/spec.html#id2765878 */
for(size_t i = 0, e = refs.size(); i < e; ++i)
{
auto &C4_RESTRICT rd = refs.top(i);
if( ! rd.type.is_ref())
continue;
rd.target = lookup_(&rd);
}
}
}; // ReferenceResolver
} // namespace detail
void Tree::resolve()
{
if(m_size == 0)
return;
detail::ReferenceResolver rr(this);
// insert the resolved references
size_t prev_parent_ref = NONE;
size_t prev_parent_ref_after = NONE;
for(auto const& C4_RESTRICT rd : rr.refs)
{
if( ! rd.type.is_ref())
continue;
if(rd.parent_ref != NONE)
{
_RYML_CB_ASSERT(m_callbacks, is_seq(rd.parent_ref));
size_t after, p = parent(rd.parent_ref);
if(prev_parent_ref != rd.parent_ref)
{
after = rd.parent_ref;//prev_sibling(rd.parent_ref_sibling);
prev_parent_ref_after = after;
}
else
{
after = prev_parent_ref_after;
}
prev_parent_ref = rd.parent_ref;
prev_parent_ref_after = duplicate_children_no_rep(rd.target, p, after);
remove(rd.node);
}
else
{
if(has_key(rd.node) && is_key_ref(rd.node) && key(rd.node) == "<<")
{
_RYML_CB_ASSERT(m_callbacks, is_keyval(rd.node));
size_t p = parent(rd.node);
size_t after = prev_sibling(rd.node);
duplicate_children_no_rep(rd.target, p, after);
remove(rd.node);
}
else if(rd.type.is_key_ref())
{
_RYML_CB_ASSERT(m_callbacks, is_key_ref(rd.node));
_RYML_CB_ASSERT(m_callbacks, has_key_anchor(rd.target) || has_val_anchor(rd.target));
if(has_val_anchor(rd.target) && val_anchor(rd.target) == key_ref(rd.node))
{
_RYML_CB_CHECK(m_callbacks, !is_container(rd.target));
_RYML_CB_CHECK(m_callbacks, has_val(rd.target));
_p(rd.node)->m_key.scalar = val(rd.target);
_add_flags(rd.node, KEY);
}
else
{
_RYML_CB_CHECK(m_callbacks, key_anchor(rd.target) == key_ref(rd.node));
_p(rd.node)->m_key.scalar = key(rd.target);
_add_flags(rd.node, VAL);
}
}
else
{
_RYML_CB_ASSERT(m_callbacks, rd.type.is_val_ref());
if(has_key_anchor(rd.target) && key_anchor(rd.target) == val_ref(rd.node))
{
_RYML_CB_CHECK(m_callbacks, !is_container(rd.target));
_RYML_CB_CHECK(m_callbacks, has_val(rd.target));
_p(rd.node)->m_val.scalar = key(rd.target);
_add_flags(rd.node, VAL);
}
else
{
duplicate_contents(rd.target, rd.node);
}
}
}
}
// clear anchors and refs
for(auto const& C4_RESTRICT ar : rr.refs)
{
rem_anchor_ref(ar.node);
if(ar.parent_ref != NONE)
if(type(ar.parent_ref) != NOTYPE)
remove(ar.parent_ref);
}
}
//-----------------------------------------------------------------------------
size_t Tree::num_children(size_t node) const
{
size_t count = 0;
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
{
++count;
}
return count;
}
size_t Tree::child(size_t node, size_t pos) const
{
_RYML_CB_ASSERT(m_callbacks, node != NONE);
size_t count = 0;
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
{
if(count++ == pos)
return i;
}
return NONE;
}
size_t Tree::child_pos(size_t node, size_t ch) const
{
size_t count = 0;
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
{
if(i == ch)
return count;
++count;
}
return npos;
}
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wnull-dereference"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# if __GNUC__ >= 6
# pragma GCC diagnostic ignored "-Wnull-dereference"
# endif
#endif
size_t Tree::find_child(size_t node, csubstr const& name) const
{
_RYML_CB_ASSERT(m_callbacks, node != NONE);
_RYML_CB_ASSERT(m_callbacks, is_map(node));
if(get(node)->m_first_child == NONE)
{
_RYML_CB_ASSERT(m_callbacks, _p(node)->m_last_child == NONE);
return NONE;
}
else
{
_RYML_CB_ASSERT(m_callbacks, _p(node)->m_last_child != NONE);
}
for(size_t i = first_child(node); i != NONE; i = next_sibling(i))
{
if(_p(i)->m_key.scalar == name)
{
return i;
}
}
return NONE;
}
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
//-----------------------------------------------------------------------------
void Tree::to_val(size_t node, csubstr val, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || ! parent_is_map(node));
_set_flags(node, VAL|more_flags);
_p(node)->m_key.clear();
_p(node)->m_val = val;
}
void Tree::to_keyval(size_t node, csubstr key, csubstr val, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_map(node));
_set_flags(node, KEYVAL|more_flags);
_p(node)->m_key = key;
_p(node)->m_val = val;
}
void Tree::to_map(size_t node, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || ! parent_is_map(node)); // parent must not have children with keys
_set_flags(node, MAP|more_flags);
_p(node)->m_key.clear();
_p(node)->m_val.clear();
}
void Tree::to_map(size_t node, csubstr key, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_map(node));
_set_flags(node, KEY|MAP|more_flags);
_p(node)->m_key = key;
_p(node)->m_val.clear();
}
void Tree::to_seq(size_t node, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_seq(node));
_set_flags(node, SEQ|more_flags);
_p(node)->m_key.clear();
_p(node)->m_val.clear();
}
void Tree::to_seq(size_t node, csubstr key, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_RYML_CB_ASSERT(m_callbacks, parent(node) == NONE || parent_is_map(node));
_set_flags(node, KEY|SEQ|more_flags);
_p(node)->m_key = key;
_p(node)->m_val.clear();
}
void Tree::to_doc(size_t node, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_set_flags(node, DOC|more_flags);
_p(node)->m_key.clear();
_p(node)->m_val.clear();
}
void Tree::to_stream(size_t node, type_bits more_flags)
{
_RYML_CB_ASSERT(m_callbacks, ! has_children(node));
_set_flags(node, STREAM|more_flags);
_p(node)->m_key.clear();
_p(node)->m_val.clear();
}
//-----------------------------------------------------------------------------
size_t Tree::num_tag_directives() const
{
// this assumes we have a very small number of tag directives
for(size_t i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
if(m_tag_directives[i].handle.empty())
return i;
return RYML_MAX_TAG_DIRECTIVES;
}
void Tree::clear_tag_directives()
{
for(TagDirective &td : m_tag_directives)
td = {};
}
size_t Tree::add_tag_directive(TagDirective const& td)
{
_RYML_CB_CHECK(m_callbacks, !td.handle.empty());
_RYML_CB_CHECK(m_callbacks, !td.prefix.empty());
_RYML_CB_ASSERT(m_callbacks, td.handle.begins_with('!'));
_RYML_CB_ASSERT(m_callbacks, td.handle.ends_with('!'));
// https://yaml.org/spec/1.2.2/#rule-ns-word-char
_RYML_CB_ASSERT(m_callbacks, td.handle == '!' || td.handle == "!!" || td.handle.trim('!').first_not_of("01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-") == npos);
size_t pos = num_tag_directives();
_RYML_CB_CHECK(m_callbacks, pos < RYML_MAX_TAG_DIRECTIVES);
m_tag_directives[pos] = td;
return pos;
}
size_t Tree::resolve_tag(substr output, csubstr tag, size_t node_id) const
{
// lookup from the end. We want to find the first directive that
// matches the tag and has a target node id leq than the given
// node_id.
for(size_t i = RYML_MAX_TAG_DIRECTIVES-1; i != (size_t)-1; --i)
{
auto const& td = m_tag_directives[i];
if(td.handle.empty())
continue;
if(tag.begins_with(td.handle) && td.next_node_id <= node_id)
{
_RYML_CB_ASSERT(m_callbacks, tag.len >= td.handle.len);
csubstr rest = tag.sub(td.handle.len);
size_t len = 1u + td.prefix.len + rest.len + 1u;
size_t numpc = rest.count('%');
if(numpc == 0)
{
if(len <= output.len)
{
output.str[0] = '<';
memcpy(1u + output.str, td.prefix.str, td.prefix.len);
memcpy(1u + output.str + td.prefix.len, rest.str, rest.len);
output.str[1u + td.prefix.len + rest.len] = '>';
}
}
else
{
// need to decode URI % sequences
size_t pos = rest.find('%');
_RYML_CB_ASSERT(m_callbacks, pos != npos);
do {
size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
if(next == npos)
next = rest.len;
_RYML_CB_CHECK(m_callbacks, pos+1 < next);
_RYML_CB_CHECK(m_callbacks, pos+1 + 2 <= next);
size_t delta = next - (pos+1);
len -= delta;
pos = rest.find('%', pos+1);
} while(pos != npos);
if(len <= output.len)
{
size_t prev = 0, wpos = 0;
auto appendstr = [&](csubstr s) { memcpy(output.str + wpos, s.str, s.len); wpos += s.len; };
auto appendchar = [&](char c) { output.str[wpos++] = c; };
appendchar('<');
appendstr(td.prefix);
pos = rest.find('%');
_RYML_CB_ASSERT(m_callbacks, pos != npos);
do {
size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
if(next == npos)
next = rest.len;
_RYML_CB_CHECK(m_callbacks, pos+1 < next);
_RYML_CB_CHECK(m_callbacks, pos+1 + 2 <= next);
uint8_t val;
if(C4_UNLIKELY(!read_hex(rest.range(pos+1, next), &val) || val > 127))
_RYML_CB_ERR(m_callbacks, "invalid URI character");
appendstr(rest.range(prev, pos));
appendchar((char)val);
prev = next;
pos = rest.find('%', pos+1);
} while(pos != npos);
_RYML_CB_ASSERT(m_callbacks, pos == npos);
_RYML_CB_ASSERT(m_callbacks, prev > 0);
_RYML_CB_ASSERT(m_callbacks, rest.len >= prev);
appendstr(rest.sub(prev));
appendchar('>');
_RYML_CB_ASSERT(m_callbacks, wpos == len);
}
}
return len;
}
}
return 0; // return 0 to signal that the tag is local and cannot be resolved
}
namespace {
csubstr _transform_tag(Tree *t, csubstr tag, size_t node)
{
size_t required_size = t->resolve_tag(substr{}, tag, node);
if(!required_size)
return tag;
const char *prev_arena = t->arena().str;
substr buf = t->alloc_arena(required_size);
_RYML_CB_ASSERT(t->m_callbacks, t->arena().str == prev_arena);
size_t actual_size = t->resolve_tag(buf, tag, node);
_RYML_CB_ASSERT(t->m_callbacks, actual_size <= required_size);
return buf.first(actual_size);
}
void _resolve_tags(Tree *t, size_t node)
{
for(size_t child = t->first_child(node); child != NONE; child = t->next_sibling(child))
{
if(t->has_key(child) && t->has_key_tag(child))
t->set_key_tag(child, _transform_tag(t, t->key_tag(child), child));
if(t->has_val(child) && t->has_val_tag(child))
t->set_val_tag(child, _transform_tag(t, t->val_tag(child), child));
_resolve_tags(t, child);
}
}
size_t _count_resolved_tags_size(Tree const* t, size_t node)
{
size_t sz = 0;
for(size_t child = t->first_child(node); child != NONE; child = t->next_sibling(child))
{
if(t->has_key(child) && t->has_key_tag(child))
sz += t->resolve_tag(substr{}, t->key_tag(child), child);
if(t->has_val(child) && t->has_val_tag(child))
sz += t->resolve_tag(substr{}, t->val_tag(child), child);
sz += _count_resolved_tags_size(t, child);
}
return sz;
}
} // namespace
void Tree::resolve_tags()
{
if(empty())
return;
if(num_tag_directives() == 0)
return;
size_t needed_size = _count_resolved_tags_size(this, root_id());
if(needed_size)
reserve_arena(arena_pos() + needed_size);
_resolve_tags(this, root_id());
}
//-----------------------------------------------------------------------------
csubstr Tree::lookup_result::resolved() const
{
csubstr p = path.first(path_pos);
if(p.ends_with('.'))
p = p.first(p.len-1);
return p;
}
csubstr Tree::lookup_result::unresolved() const
{
return path.sub(path_pos);
}
void Tree::_advance(lookup_result *r, size_t more) const
{
r->path_pos += more;
if(r->path.sub(r->path_pos).begins_with('.'))
++r->path_pos;
}
Tree::lookup_result Tree::lookup_path(csubstr path, size_t start) const
{
if(start == NONE)
start = root_id();
lookup_result r(path, start);
if(path.empty())
return r;
_lookup_path(&r);
if(r.target == NONE && r.closest == start)
r.closest = NONE;
return r;
}
size_t Tree::lookup_path_or_modify(csubstr default_value, csubstr path, size_t start)
{
size_t target = _lookup_path_or_create(path, start);
if(parent_is_map(target))
to_keyval(target, key(target), default_value);
else
to_val(target, default_value);
return target;
}
size_t Tree::lookup_path_or_modify(Tree const *src, size_t src_node, csubstr path, size_t start)
{
size_t target = _lookup_path_or_create(path, start);
merge_with(src, src_node, target);
return target;
}
size_t Tree::_lookup_path_or_create(csubstr path, size_t start)
{
if(start == NONE)
start = root_id();
lookup_result r(path, start);
_lookup_path(&r);
if(r.target != NONE)
{
C4_ASSERT(r.unresolved().empty());
return r.target;
}
_lookup_path_modify(&r);
return r.target;
}
void Tree::_lookup_path(lookup_result *r) const
{
C4_ASSERT( ! r->unresolved().empty());
_lookup_path_token parent{"", type(r->closest)};
size_t node;
do
{
node = _next_node(r, &parent);
if(node != NONE)
r->closest = node;
if(r->unresolved().empty())
{
r->target = node;
return;
}
} while(node != NONE);
}
void Tree::_lookup_path_modify(lookup_result *r)
{
C4_ASSERT( ! r->unresolved().empty());
_lookup_path_token parent{"", type(r->closest)};
size_t node;
do
{
node = _next_node_modify(r, &parent);
if(node != NONE)
r->closest = node;
if(r->unresolved().empty())
{
r->target = node;
return;
}
} while(node != NONE);
}
size_t Tree::_next_node(lookup_result * r, _lookup_path_token *parent) const
{
_lookup_path_token token = _next_token(r, *parent);
if( ! token)
return NONE;
size_t node = NONE;
csubstr prev = token.value;
if(token.type == MAP || token.type == SEQ)
{
_RYML_CB_ASSERT(m_callbacks, !token.value.begins_with('['));
//_RYML_CB_ASSERT(m_callbacks, is_container(r->closest) || r->closest == NONE);
_RYML_CB_ASSERT(m_callbacks, is_map(r->closest));
node = find_child(r->closest, token.value);
}
else if(token.type == KEYVAL)
{
_RYML_CB_ASSERT(m_callbacks, r->unresolved().empty());
if(is_map(r->closest))
node = find_child(r->closest, token.value);
}
else if(token.type == KEY)
{
_RYML_CB_ASSERT(m_callbacks, token.value.begins_with('[') && token.value.ends_with(']'));
token.value = token.value.offs(1, 1).trim(' ');
size_t idx = 0;
_RYML_CB_CHECK(m_callbacks, from_chars(token.value, &idx));
node = child(r->closest, idx);
}
else
{
C4_NEVER_REACH();
}
if(node != NONE)
{
*parent = token;
}
else
{
csubstr p = r->path.sub(r->path_pos > 0 ? r->path_pos - 1 : r->path_pos);
r->path_pos -= prev.len;
if(p.begins_with('.'))
r->path_pos -= 1u;
}
return node;
}
size_t Tree::_next_node_modify(lookup_result * r, _lookup_path_token *parent)
{
_lookup_path_token token = _next_token(r, *parent);
if( ! token)
return NONE;
size_t node = NONE;
if(token.type == MAP || token.type == SEQ)
{
_RYML_CB_ASSERT(m_callbacks, !token.value.begins_with('['));
//_RYML_CB_ASSERT(m_callbacks, is_container(r->closest) || r->closest == NONE);
if( ! is_container(r->closest))
{
if(has_key(r->closest))
to_map(r->closest, key(r->closest));
else
to_map(r->closest);
}
else
{
if(is_map(r->closest))
node = find_child(r->closest, token.value);
else
{
size_t pos = NONE;
_RYML_CB_CHECK(m_callbacks, c4::atox(token.value, &pos));
_RYML_CB_ASSERT(m_callbacks, pos != NONE);
node = child(r->closest, pos);
}
}
if(node == NONE)
{
_RYML_CB_ASSERT(m_callbacks, is_map(r->closest));
node = append_child(r->closest);
NodeData *n = _p(node);
n->m_key.scalar = token.value;
n->m_type.add(KEY);
}
}
else if(token.type == KEYVAL)
{
_RYML_CB_ASSERT(m_callbacks, r->unresolved().empty());
if(is_map(r->closest))
{
node = find_child(r->closest, token.value);
if(node == NONE)
node = append_child(r->closest);
}
else
{
_RYML_CB_ASSERT(m_callbacks, !is_seq(r->closest));
_add_flags(r->closest, MAP);
node = append_child(r->closest);
}
NodeData *n = _p(node);
n->m_key.scalar = token.value;
n->m_val.scalar = "";
n->m_type.add(KEYVAL);
}
else if(token.type == KEY)
{
_RYML_CB_ASSERT(m_callbacks, token.value.begins_with('[') && token.value.ends_with(']'));
token.value = token.value.offs(1, 1).trim(' ');
size_t idx;
if( ! from_chars(token.value, &idx))
return NONE;
if( ! is_container(r->closest))
{
if(has_key(r->closest))
{
csubstr k = key(r->closest);
_clear_type(r->closest);
to_seq(r->closest, k);
}
else
{
_clear_type(r->closest);
to_seq(r->closest);
}
}
_RYML_CB_ASSERT(m_callbacks, is_container(r->closest));
node = child(r->closest, idx);
if(node == NONE)
{
_RYML_CB_ASSERT(m_callbacks, num_children(r->closest) <= idx);
for(size_t i = num_children(r->closest); i <= idx; ++i)
{
node = append_child(r->closest);
if(i < idx)
{
if(is_map(r->closest))
to_keyval(node, /*"~"*/{}, /*"~"*/{});
else if(is_seq(r->closest))
to_val(node, /*"~"*/{});
}
}
}
}
else
{
C4_NEVER_REACH();
}
_RYML_CB_ASSERT(m_callbacks, node != NONE);
*parent = token;
return node;
}
/** types of tokens:
* - seeing "map." ---> "map"/MAP
* - finishing "scalar" ---> "scalar"/KEYVAL
* - seeing "seq[n]" ---> "seq"/SEQ (--> "[n]"/KEY)
* - seeing "[n]" ---> "[n]"/KEY
*/
Tree::_lookup_path_token Tree::_next_token(lookup_result *r, _lookup_path_token const& parent) const
{
csubstr unres = r->unresolved();
if(unres.empty())
return {};
// is it an indexation like [0], [1], etc?
if(unres.begins_with('['))
{
size_t pos = unres.find(']');
if(pos == csubstr::npos)
return {};
csubstr idx = unres.first(pos + 1);
_advance(r, pos + 1);
return {idx, KEY};
}
// no. so it must be a name
size_t pos = unres.first_of(".[");
if(pos == csubstr::npos)
{
_advance(r, unres.len);
NodeType t;
if(( ! parent) || parent.type.is_seq())
return {unres, VAL};
return {unres, KEYVAL};
}
// it's either a map or a seq
_RYML_CB_ASSERT(m_callbacks, unres[pos] == '.' || unres[pos] == '[');
if(unres[pos] == '.')
{
_RYML_CB_ASSERT(m_callbacks, pos != 0);
_advance(r, pos + 1);
return {unres.first(pos), MAP};
}
_RYML_CB_ASSERT(m_callbacks, unres[pos] == '[');
_advance(r, pos);
return {unres.first(pos), SEQ};
}
} // namespace ryml
} // namespace c4
C4_SUPPRESS_WARNING_GCC_POP
C4_SUPPRESS_WARNING_MSVC_POP
#endif /* RYML_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/tree.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/parse.cpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/parse.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef RYML_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/parse.hpp
//#include "c4/yml/parse.hpp"
#if !defined(C4_YML_PARSE_HPP_) && !defined(_C4_YML_PARSE_HPP_)
#error "amalgamate: file c4/yml/parse.hpp must have been included at this point"
#endif /* C4_YML_PARSE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/error.hpp
//#include "c4/error.hpp"
#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_)
#error "amalgamate: file c4/error.hpp must have been included at this point"
#endif /* C4_ERROR_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/utf.hpp
//#include "c4/utf.hpp"
#if !defined(C4_UTF_HPP_) && !defined(_C4_UTF_HPP_)
#error "amalgamate: file c4/utf.hpp must have been included at this point"
#endif /* C4_UTF_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/dump.hpp
//#include <c4/dump.hpp>
#if !defined(C4_DUMP_HPP_) && !defined(_C4_DUMP_HPP_)
#error "amalgamate: file c4/dump.hpp must have been included at this point"
#endif /* C4_DUMP_HPP_ */
//included above:
//#include <ctype.h>
//included above:
//#include <stdarg.h>
//included above:
//#include <stdio.h>
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/parser_dbg.hpp
//#include "c4/yml/detail/parser_dbg.hpp"
#if !defined(C4_YML_DETAIL_PARSER_DBG_HPP_) && !defined(_C4_YML_DETAIL_PARSER_DBG_HPP_)
#error "amalgamate: file c4/yml/detail/parser_dbg.hpp must have been included at this point"
#endif /* C4_YML_DETAIL_PARSER_DBG_HPP_ */
#ifdef RYML_DBG
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/print.hpp
//#include "c4/yml/detail/print.hpp"
#if !defined(C4_YML_DETAIL_PRINT_HPP_) && !defined(_C4_YML_DETAIL_PRINT_HPP_)
#error "amalgamate: file c4/yml/detail/print.hpp must have been included at this point"
#endif /* C4_YML_DETAIL_PRINT_HPP_ */
#endif
#ifndef RYML_ERRMSG_SIZE
#define RYML_ERRMSG_SIZE 1024
#endif
//#define RYML_WITH_TAB_TOKENS
#ifdef RYML_WITH_TAB_TOKENS
#define _RYML_WITH_TAB_TOKENS(...) __VA_ARGS__
#define _RYML_WITH_OR_WITHOUT_TAB_TOKENS(with, without) with
#else
#define _RYML_WITH_TAB_TOKENS(...)
#define _RYML_WITH_OR_WITHOUT_TAB_TOKENS(with, without) without
#endif
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4296/*expression is always 'boolean_value'*/)
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtype-limits" // to remove a warning on an assertion that a size_t >= 0. Later on, this size_t will turn into a template argument, and then it can become < 0.
# pragma clang diagnostic ignored "-Wformat-nonliteral"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtype-limits" // to remove a warning on an assertion that a size_t >= 0. Later on, this size_t will turn into a template argument, and then it can become < 0.
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# if __GNUC__ >= 7
# pragma GCC diagnostic ignored "-Wduplicated-branches"
# endif
#endif
namespace c4 {
namespace yml {
namespace {
template<class DumpFn, class ...Args>
void _parse_dump(DumpFn dumpfn, c4::csubstr fmt, Args&& ...args)
{
char writebuf[256];
auto results = c4::format_dump_resume(dumpfn, writebuf, fmt, std::forward<Args>(args)...);
// resume writing if the results failed to fit the buffer
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf))) // bufsize will be that of the largest element serialized. Eg int(1), will require 1 byte.
{
results = format_dump_resume(dumpfn, results, writebuf, fmt, std::forward<Args>(args)...);
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf)))
{
results = format_dump_resume(dumpfn, results, writebuf, fmt, std::forward<Args>(args)...);
}
}
}
bool _is_scalar_next__runk(csubstr s)
{
return !(s.begins_with(": ") || s.begins_with_any("#,:{}[]%&") || s.begins_with("? ") || s == "-" || s.begins_with("- "));
}
bool _is_scalar_next__rseq_rval(csubstr s)
{
return !(s.begins_with_any("[{!&") || s.begins_with("? ") || s.begins_with("- ") || s == "-");
}
bool _is_scalar_next__rmap(csubstr s)
{
return !(s.begins_with(": ") || s.begins_with_any("#,!&") || s.begins_with("? ") _RYML_WITH_TAB_TOKENS(|| s.begins_with(":\t")));
}
bool _is_scalar_next__rmap_val(csubstr s)
{
return !(s.begins_with("- ") || s.begins_with_any("{[") || s == "-");
}
bool _is_doc_sep(csubstr s)
{
constexpr const csubstr dashes = "---";
constexpr const csubstr ellipsis = "...";
constexpr const csubstr whitesp = " \t";
if(s.begins_with(dashes))
return s == dashes || s.sub(3).begins_with_any(whitesp);
else if(s.begins_with(ellipsis))
return s == ellipsis || s.sub(3).begins_with_any(whitesp);
return false;
}
/** @p i is set to the first non whitespace character after the line
* @return the number of empty lines after the initial position */
size_t count_following_newlines(csubstr r, size_t *C4_RESTRICT i, size_t indentation)
{
RYML_ASSERT(r[*i] == '\n');
size_t numnl_following = 0;
++(*i);
for( ; *i < r.len; ++(*i))
{
if(r.str[*i] == '\n')
{
++numnl_following;
if(indentation) // skip the indentation after the newline
{
size_t stop = *i + indentation;
for( ; *i < r.len; ++(*i))
{
if(r.str[*i] != ' ' && r.str[*i] != '\r')
break;
RYML_ASSERT(*i < stop);
}
C4_UNUSED(stop);
}
}
else if(r.str[*i] == ' ' || r.str[*i] == '\t' || r.str[*i] == '\r') // skip leading whitespace
;
else
break;
}
return numnl_following;
}
} // anon namespace
//-----------------------------------------------------------------------------
Parser::~Parser()
{
_free();
_clr();
}
Parser::Parser(Callbacks const& cb)
: m_file()
, m_buf()
, m_root_id(NONE)
, m_tree()
, m_stack(cb)
, m_state()
, m_key_tag_indentation(0)
, m_key_tag2_indentation(0)
, m_key_tag()
, m_key_tag2()
, m_val_tag_indentation(0)
, m_val_tag()
, m_key_anchor_was_before(false)
, m_key_anchor_indentation(0)
, m_key_anchor()
, m_val_anchor_indentation(0)
, m_val_anchor()
, m_filter_arena()
, m_newline_offsets()
, m_newline_offsets_size(0)
, m_newline_offsets_capacity(0)
, m_newline_offsets_buf()
{
m_stack.push(State{});
m_state = &m_stack.top();
}
Parser::Parser(Parser &&that)
: m_file(that.m_file)
, m_buf(that.m_buf)
, m_root_id(that.m_root_id)
, m_tree(that.m_tree)
, m_stack(std::move(that.m_stack))
, m_state(&m_stack.top())
, m_key_tag_indentation(that.m_key_tag_indentation)
, m_key_tag2_indentation(that.m_key_tag2_indentation)
, m_key_tag(that.m_key_tag)
, m_key_tag2(that.m_key_tag2)
, m_val_tag_indentation(that.m_val_tag_indentation)
, m_val_tag(that.m_val_tag)
, m_key_anchor_was_before(that.m_key_anchor_was_before)
, m_key_anchor_indentation(that.m_key_anchor_indentation)
, m_key_anchor(that.m_key_anchor)
, m_val_anchor_indentation(that.m_val_anchor_indentation)
, m_val_anchor(that.m_val_anchor)
, m_filter_arena(that.m_filter_arena)
, m_newline_offsets(that.m_newline_offsets)
, m_newline_offsets_size(that.m_newline_offsets_size)
, m_newline_offsets_capacity(that.m_newline_offsets_capacity)
, m_newline_offsets_buf(that.m_newline_offsets_buf)
{
that._clr();
}
Parser::Parser(Parser const& that)
: m_file(that.m_file)
, m_buf(that.m_buf)
, m_root_id(that.m_root_id)
, m_tree(that.m_tree)
, m_stack(that.m_stack)
, m_state(&m_stack.top())
, m_key_tag_indentation(that.m_key_tag_indentation)
, m_key_tag2_indentation(that.m_key_tag2_indentation)
, m_key_tag(that.m_key_tag)
, m_key_tag2(that.m_key_tag2)
, m_val_tag_indentation(that.m_val_tag_indentation)
, m_val_tag(that.m_val_tag)
, m_key_anchor_was_before(that.m_key_anchor_was_before)
, m_key_anchor_indentation(that.m_key_anchor_indentation)
, m_key_anchor(that.m_key_anchor)
, m_val_anchor_indentation(that.m_val_anchor_indentation)
, m_val_anchor(that.m_val_anchor)
, m_filter_arena()
, m_newline_offsets()
, m_newline_offsets_size()
, m_newline_offsets_capacity()
, m_newline_offsets_buf()
{
if(that.m_newline_offsets_capacity)
{
_resize_locations(that.m_newline_offsets_capacity);
_RYML_CB_CHECK(m_stack.m_callbacks, m_newline_offsets_capacity == that.m_newline_offsets_capacity);
memcpy(m_newline_offsets, that.m_newline_offsets, that.m_newline_offsets_size * sizeof(size_t));
m_newline_offsets_size = that.m_newline_offsets_size;
}
if(that.m_filter_arena.len)
{
_resize_filter_arena(that.m_filter_arena.len);
}
}
Parser& Parser::operator=(Parser &&that)
{
_free();
m_file = (that.m_file);
m_buf = (that.m_buf);
m_root_id = (that.m_root_id);
m_tree = (that.m_tree);
m_stack = std::move(that.m_stack);
m_state = (&m_stack.top());
m_key_tag_indentation = (that.m_key_tag_indentation);
m_key_tag2_indentation = (that.m_key_tag2_indentation);
m_key_tag = (that.m_key_tag);
m_key_tag2 = (that.m_key_tag2);
m_val_tag_indentation = (that.m_val_tag_indentation);
m_val_tag = (that.m_val_tag);
m_key_anchor_was_before = (that.m_key_anchor_was_before);
m_key_anchor_indentation = (that.m_key_anchor_indentation);
m_key_anchor = (that.m_key_anchor);
m_val_anchor_indentation = (that.m_val_anchor_indentation);
m_val_anchor = (that.m_val_anchor);
m_filter_arena = that.m_filter_arena;
m_newline_offsets = (that.m_newline_offsets);
m_newline_offsets_size = (that.m_newline_offsets_size);
m_newline_offsets_capacity = (that.m_newline_offsets_capacity);
m_newline_offsets_buf = (that.m_newline_offsets_buf);
that._clr();
return *this;
}
Parser& Parser::operator=(Parser const& that)
{
_free();
m_file = (that.m_file);
m_buf = (that.m_buf);
m_root_id = (that.m_root_id);
m_tree = (that.m_tree);
m_stack = that.m_stack;
m_state = &m_stack.top();
m_key_tag_indentation = (that.m_key_tag_indentation);
m_key_tag2_indentation = (that.m_key_tag2_indentation);
m_key_tag = (that.m_key_tag);
m_key_tag2 = (that.m_key_tag2);
m_val_tag_indentation = (that.m_val_tag_indentation);
m_val_tag = (that.m_val_tag);
m_key_anchor_was_before = (that.m_key_anchor_was_before);
m_key_anchor_indentation = (that.m_key_anchor_indentation);
m_key_anchor = (that.m_key_anchor);
m_val_anchor_indentation = (that.m_val_anchor_indentation);
m_val_anchor = (that.m_val_anchor);
if(that.m_filter_arena.len > 0)
_resize_filter_arena(that.m_filter_arena.len);
if(that.m_newline_offsets_capacity > m_newline_offsets_capacity)
_resize_locations(that.m_newline_offsets_capacity);
_RYML_CB_CHECK(m_stack.m_callbacks, m_newline_offsets_capacity >= that.m_newline_offsets_capacity);
_RYML_CB_CHECK(m_stack.m_callbacks, m_newline_offsets_capacity >= that.m_newline_offsets_size);
memcpy(m_newline_offsets, that.m_newline_offsets, that.m_newline_offsets_size * sizeof(size_t));
m_newline_offsets_size = that.m_newline_offsets_size;
m_newline_offsets_buf = that.m_newline_offsets_buf;
return *this;
}
void Parser::_clr()
{
m_file = {};
m_buf = {};
m_root_id = {};
m_tree = {};
m_stack.clear();
m_state = {};
m_key_tag_indentation = {};
m_key_tag2_indentation = {};
m_key_tag = {};
m_key_tag2 = {};
m_val_tag_indentation = {};
m_val_tag = {};
m_key_anchor_was_before = {};
m_key_anchor_indentation = {};
m_key_anchor = {};
m_val_anchor_indentation = {};
m_val_anchor = {};
m_filter_arena = {};
m_newline_offsets = {};
m_newline_offsets_size = {};
m_newline_offsets_capacity = {};
m_newline_offsets_buf = {};
}
void Parser::_free()
{
if(m_newline_offsets)
{
_RYML_CB_FREE(m_stack.m_callbacks, m_newline_offsets, size_t, m_newline_offsets_capacity);
m_newline_offsets = nullptr;
m_newline_offsets_size = 0u;
m_newline_offsets_capacity = 0u;
m_newline_offsets_buf = 0u;
}
if(m_filter_arena.len)
{
_RYML_CB_FREE(m_stack.m_callbacks, m_filter_arena.str, char, m_filter_arena.len);
m_filter_arena = {};
}
m_stack._free();
}
//-----------------------------------------------------------------------------
void Parser::_reset()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_stack.size() == 1);
m_stack.clear();
m_stack.push({});
m_state = &m_stack.top();
m_state->reset(m_file.str, m_root_id);
m_key_tag_indentation = 0;
m_key_tag2_indentation = 0;
m_key_tag.clear();
m_key_tag2.clear();
m_val_tag_indentation = 0;
m_val_tag.clear();
m_key_anchor_was_before = false;
m_key_anchor_indentation = 0;
m_key_anchor.clear();
m_val_anchor_indentation = 0;
m_val_anchor.clear();
_mark_locations_dirty();
}
//-----------------------------------------------------------------------------
template<class DumpFn>
void Parser::_fmt_msg(DumpFn &&dumpfn) const
{
auto const& lc = m_state->line_contents;
csubstr contents = lc.stripped;
if(contents.len)
{
// print the yaml src line
size_t offs = 3u + to_chars(substr{}, m_state->pos.line) + to_chars(substr{}, m_state->pos.col);
if(m_file.len)
{
_parse_dump(dumpfn, "{}:", m_file);
offs += m_file.len + 1;
}
_parse_dump(dumpfn, "{}:{}: ", m_state->pos.line, m_state->pos.col);
csubstr maybe_full_content = (contents.len < 80u ? contents : contents.first(80u));
csubstr maybe_ellipsis = (contents.len < 80u ? csubstr{} : csubstr("..."));
_parse_dump(dumpfn, "{}{} (size={})\n", maybe_full_content, maybe_ellipsis, contents.len);
// highlight the remaining portion of the previous line
size_t firstcol = (size_t)(lc.rem.begin() - lc.full.begin());
size_t lastcol = firstcol + lc.rem.len;
for(size_t i = 0; i < offs + firstcol; ++i)
dumpfn(" ");
dumpfn("^");
for(size_t i = 1, e = (lc.rem.len < 80u ? lc.rem.len : 80u); i < e; ++i)
dumpfn("~");
_parse_dump(dumpfn, "{} (cols {}-{})\n", maybe_ellipsis, firstcol+1, lastcol+1);
}
else
{
dumpfn("\n");
}
#ifdef RYML_DBG
// next line: print the state flags
{
char flagbuf_[64];
_parse_dump(dumpfn, "top state: {}\n", _prfl(flagbuf_, m_state->flags));
}
#endif
}
//-----------------------------------------------------------------------------
template<class ...Args>
void Parser::_err(csubstr fmt, Args const& C4_RESTRICT ...args) const
{
char errmsg[RYML_ERRMSG_SIZE];
detail::_SubstrWriter writer(errmsg);
auto dumpfn = [&writer](csubstr s){ writer.append(s); };
_parse_dump(dumpfn, fmt, args...);
writer.append('\n');
_fmt_msg(dumpfn);
size_t len = writer.pos < RYML_ERRMSG_SIZE ? writer.pos : RYML_ERRMSG_SIZE;
m_tree->m_callbacks.m_error(errmsg, len, m_state->pos, m_tree->m_callbacks.m_user_data);
}
//-----------------------------------------------------------------------------
#ifdef RYML_DBG
template<class ...Args>
void Parser::_dbg(csubstr fmt, Args const& C4_RESTRICT ...args) const
{
auto dumpfn = [](csubstr s){ fwrite(s.str, 1, s.len, stdout); };
_parse_dump(dumpfn, fmt, args...);
dumpfn("\n");
_fmt_msg(dumpfn);
}
#endif
//-----------------------------------------------------------------------------
bool Parser::_finished_file() const
{
bool ret = m_state->pos.offset >= m_buf.len;
if(ret)
{
_c4dbgp("finished file!!!");
}
return ret;
}
//-----------------------------------------------------------------------------
bool Parser::_finished_line() const
{
return m_state->line_contents.rem.empty();
}
//-----------------------------------------------------------------------------
void Parser::parse_in_place(csubstr file, substr buf, Tree *t, size_t node_id)
{
m_file = file;
m_buf = buf;
m_root_id = node_id;
m_tree = t;
_reset();
while( ! _finished_file())
{
_scan_line();
while( ! _finished_line())
_handle_line();
if(_finished_file())
break; // it may have finished because of multiline blocks
_line_ended();
}
_handle_finished_file();
}
//-----------------------------------------------------------------------------
void Parser::_handle_finished_file()
{
_end_stream();
}
//-----------------------------------------------------------------------------
void Parser::_handle_line()
{
_c4dbgq("\n-----------");
_c4dbgt("handling line={}, offset={}B", m_state->pos.line, m_state->pos.offset);
_RYML_CB_ASSERT(m_stack.m_callbacks, ! m_state->line_contents.rem.empty());
if(has_any(RSEQ))
{
if(has_any(FLOW))
{
if(_handle_seq_flow())
return;
}
else
{
if(_handle_seq_blck())
return;
}
}
else if(has_any(RMAP))
{
if(has_any(FLOW))
{
if(_handle_map_flow())
return;
}
else
{
if(_handle_map_blck())
return;
}
}
else if(has_any(RUNK))
{
if(_handle_unk())
return;
}
if(_handle_top())
return;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_unk()
{
_c4dbgp("handle_unk");
csubstr rem = m_state->line_contents.rem;
const bool start_as_child = (node(m_state) == nullptr);
if(C4_UNLIKELY(has_any(NDOC)))
{
if(rem == "---" || rem.begins_with("--- "))
{
_start_new_doc(rem);
return true;
}
auto trimmed = rem.triml(' ');
if(trimmed == "---" || trimmed.begins_with("--- "))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, rem.len >= trimmed.len);
_line_progressed(rem.len - trimmed.len);
_start_new_doc(trimmed);
_save_indentation();
return true;
}
else if(trimmed.begins_with("..."))
{
_end_stream();
}
else if(trimmed.first_of("#%") == csubstr::npos) // neither a doc nor a tag
{
_c4dbgpf("starting implicit doc to accomodate unexpected tokens: '{}'", rem);
size_t indref = m_state->indref;
_push_level();
_start_doc();
_set_indentation(indref);
}
_RYML_CB_ASSERT(m_stack.m_callbacks, !trimmed.empty());
}
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT|RSEQ|RMAP));
if(m_state->indref > 0)
{
csubstr ws = rem.left_of(rem.first_not_of(' '));
if(m_state->indref <= ws.len)
{
_c4dbgpf("skipping base indentation of {}", m_state->indref);
_line_progressed(m_state->indref);
rem = rem.sub(m_state->indref);
}
}
if(rem.begins_with("- ") _RYML_WITH_TAB_TOKENS( || rem.begins_with("-\t")))
{
_c4dbgpf("it's a seq (as_child={})", start_as_child);
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level();
_start_seq(start_as_child);
_save_indentation();
_line_progressed(2);
return true;
}
else if(rem == '-')
{
_c4dbgpf("it's a seq (as_child={})", start_as_child);
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level();
_start_seq(start_as_child);
_save_indentation();
_line_progressed(1);
return true;
}
else if(rem.begins_with('['))
{
_c4dbgpf("it's a seq, flow (as_child={})", start_as_child);
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level(/*explicit flow*/true);
_start_seq(start_as_child);
add_flags(FLOW);
_line_progressed(1);
return true;
}
else if(rem.begins_with('{'))
{
_c4dbgpf("it's a map, flow (as_child={})", start_as_child);
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level(/*explicit flow*/true);
_start_map(start_as_child);
addrem_flags(FLOW|RKEY, RVAL);
_line_progressed(1);
return true;
}
else if(rem.begins_with("? "))
{
_c4dbgpf("it's a map (as_child={}) + this key is complex", start_as_child);
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level();
_start_map(start_as_child);
addrem_flags(RKEY|QMRK, RVAL);
_save_indentation();
_line_progressed(2);
return true;
}
else if(rem.begins_with(": ") && !has_all(SSCL))
{
_c4dbgp("it's a map with an empty key");
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level();
_start_map(start_as_child);
_store_scalar_null(rem.str);
addrem_flags(RVAL, RKEY);
_save_indentation();
_line_progressed(2);
return true;
}
else if(rem == ':' && !has_all(SSCL))
{
_c4dbgp("it's a map with an empty key");
_move_key_anchor_to_val_anchor();
_move_key_tag_to_val_tag();
_push_level();
_start_map(start_as_child);
_store_scalar_null(rem.str);
addrem_flags(RVAL, RKEY);
_save_indentation();
_line_progressed(1);
return true;
}
else if(_handle_types())
{
return true;
}
else if(!rem.begins_with('*') && _handle_key_anchors_and_refs())
{
return true;
}
else if(has_all(SSCL))
{
_c4dbgpf("there's a stored scalar: '{}'", m_state->scalar);
csubstr saved_scalar;
bool is_quoted;
if(_scan_scalar(&saved_scalar, &is_quoted))
{
rem = m_state->line_contents.rem;
_c4dbgpf("... and there's also a scalar next! '{}'", saved_scalar);
if(rem.begins_with_any(" \t"))
{
size_t n = rem.first_not_of(" \t");
_c4dbgpf("skipping {} spaces/tabs", n);
rem = rem.sub(n);
_line_progressed(n);
}
}
_c4dbgpf("rem='{}'", rem);
if(rem.begins_with(", "))
{
_c4dbgpf("got a ',' -- it's a seq (as_child={})", start_as_child);
_start_seq(start_as_child);
add_flags(FLOW);
_append_val(_consume_scalar());
_line_progressed(2);
}
else if(rem.begins_with(','))
{
_c4dbgpf("got a ',' -- it's a seq (as_child={})", start_as_child);
_start_seq(start_as_child);
add_flags(FLOW);
_append_val(_consume_scalar());
_line_progressed(1);
}
else if(rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t")))
{
_c4dbgpf("got a ': ' -- it's a map (as_child={})", start_as_child);
_start_map_unk(start_as_child); // wait for the val scalar to append the key-val pair
_line_progressed(2);
}
else if(rem == ":" || rem.begins_with(":\"") || rem.begins_with(":'"))
{
if(rem == ":") { _c4dbgpf("got a ':' -- it's a map (as_child={})", start_as_child); }
else { _c4dbgpf("got a '{}' -- it's a map (as_child={})", rem.first(2), start_as_child); }
_start_map_unk(start_as_child); // wait for the val scalar to append the key-val pair
_line_progressed(1); // advance only 1
}
else if(rem.begins_with('}'))
{
if(!has_all(RMAP|FLOW))
{
_c4err("invalid token: not reading a map");
}
if(!has_all(SSCL))
{
_c4err("no scalar stored");
}
_append_key_val(saved_scalar);
_stop_map();
_line_progressed(1);
}
else if(rem.begins_with("..."))
{
_c4dbgp("got stream end '...'");
_end_stream();
_line_progressed(3);
}
else if(rem.begins_with('#'))
{
_c4dbgpf("it's a comment: '{}'", rem);
_scan_comment();
return true;
}
else if(_handle_key_anchors_and_refs())
{
return true;
}
else if(rem.begins_with(" ") || rem.begins_with("\t"))
{
size_t n = rem.first_not_of(" \t");
if(n == npos)
n = rem.len;
_c4dbgpf("has {} spaces/tabs, skip...", n);
_line_progressed(n);
return true;
}
else if(rem.empty())
{
// nothing to do
}
else if(rem == "---" || rem.begins_with("--- "))
{
_c4dbgp("caught ---: starting doc");
_start_new_doc(rem);
return true;
}
else if(rem.begins_with('%'))
{
_c4dbgp("caught a directive: ignoring...");
_line_progressed(rem.len);
return true;
}
else
{
_c4err("parse error");
}
if( ! saved_scalar.empty())
{
_store_scalar(saved_scalar, is_quoted);
}
return true;
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, ! has_any(SSCL));
csubstr scalar;
size_t indentation = m_state->line_contents.indentation; // save
bool is_quoted;
if(_scan_scalar(&scalar, &is_quoted))
{
_c4dbgpf("got a {} scalar", is_quoted ? "quoted" : "");
rem = m_state->line_contents.rem;
{
size_t first = rem.first_not_of(" \t");
if(first && first != npos)
{
_c4dbgpf("skip {} whitespace characters", first);
_line_progressed(first);
rem = rem.sub(first);
}
}
_store_scalar(scalar, is_quoted);
if(rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t")))
{
_c4dbgpf("got a ': ' next -- it's a map (as_child={})", start_as_child);
_push_level();
_start_map(start_as_child); // wait for the val scalar to append the key-val pair
_set_indentation(indentation);
_line_progressed(2); // call this AFTER saving the indentation
}
else if(rem == ":")
{
_c4dbgpf("got a ':' next -- it's a map (as_child={})", start_as_child);
_push_level();
_start_map(start_as_child); // wait for the val scalar to append the key-val pair
_set_indentation(indentation);
_line_progressed(1); // call this AFTER saving the indentation
}
else
{
// we still don't know whether it's a seq or a map
// so just store the scalar
}
return true;
}
else if(rem.begins_with_any(" \t"))
{
csubstr ws = rem.left_of(rem.first_not_of(" \t"));
rem = rem.right_of(ws);
if(has_all(RTOP) && rem.begins_with("---"))
{
_c4dbgp("there's a doc starting, and it's indented");
_set_indentation(ws.len);
}
_c4dbgpf("skipping {} spaces/tabs", ws.len);
_line_progressed(ws.len);
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE void Parser::_skipchars(char c)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->line_contents.rem.begins_with(c));
size_t pos = m_state->line_contents.rem.first_not_of(c);
if(pos == npos)
pos = m_state->line_contents.rem.len; // maybe the line is just whitespace
_c4dbgpf("skip {} '{}'", pos, c);
_line_progressed(pos);
}
template<size_t N>
C4_ALWAYS_INLINE void Parser::_skipchars(const char (&chars)[N])
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->line_contents.rem.begins_with_any(chars));
size_t pos = m_state->line_contents.rem.first_not_of(chars);
if(pos == npos)
pos = m_state->line_contents.rem.len; // maybe the line is just whitespace
_c4dbgpf("skip {} characters", pos);
_line_progressed(pos);
}
//-----------------------------------------------------------------------------
bool Parser::_handle_seq_flow()
{
_c4dbgpf("handle_seq_flow: node_id={} level={}", m_state->node_id, m_state->level);
csubstr rem = m_state->line_contents.rem;
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RSEQ|FLOW));
if(rem.begins_with(' '))
{
// with explicit flow, indentation does not matter
_c4dbgp("starts with spaces");
_skipchars(' ');
return true;
}
_RYML_WITH_TAB_TOKENS(else if(rem.begins_with('\t'))
{
_c4dbgp("starts with tabs");
_skipchars('\t');
return true;
})
else if(rem.begins_with('#'))
{
_c4dbgp("it's a comment");
rem = _scan_comment(); // also progresses the line
return true;
}
else if(rem.begins_with(']'))
{
_c4dbgp("end the sequence");
_pop_level();
_line_progressed(1);
if(has_all(RSEQIMAP))
{
_stop_seqimap();
_pop_level();
}
return true;
}
if(has_any(RVAL))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT));
bool is_quoted;
if(_scan_scalar(&rem, &is_quoted))
{
_c4dbgp("it's a scalar");
addrem_flags(RNXT, RVAL);
_append_val(rem, is_quoted);
return true;
}
else if(rem.begins_with('['))
{
_c4dbgp("val is a child seq");
addrem_flags(RNXT, RVAL); // before _push_level!
_push_level(/*explicit flow*/true);
_start_seq();
add_flags(FLOW);
_line_progressed(1);
return true;
}
else if(rem.begins_with('{'))
{
_c4dbgp("val is a child map");
addrem_flags(RNXT, RVAL); // before _push_level!
_push_level(/*explicit flow*/true);
_start_map();
addrem_flags(FLOW|RKEY, RVAL);
_line_progressed(1);
return true;
}
else if(rem == ':')
{
_c4dbgpf("found ':' -- there's an implicit map in the seq node[{}]", m_state->node_id);
_start_seqimap();
_line_progressed(1);
return true;
}
else if(rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t")))
{
_c4dbgpf("found ': ' -- there's an implicit map in the seq node[{}]", m_state->node_id);
_start_seqimap();
_line_progressed(2);
return true;
}
else if(rem.begins_with("? "))
{
_c4dbgpf("found '? ' -- there's an implicit map in the seq node[{}]", m_state->node_id);
_start_seqimap();
_line_progressed(2);
_RYML_CB_ASSERT(m_stack.m_callbacks, has_any(SSCL) && m_state->scalar == "");
addrem_flags(QMRK|RKEY, RVAL|SSCL);
return true;
}
else if(_handle_types())
{
return true;
}
else if(_handle_val_anchors_and_refs())
{
return true;
}
else if(rem.begins_with(", "))
{
_c4dbgp("found ',' -- the value was null");
_append_val_null(rem.str - 1);
_line_progressed(2);
return true;
}
else if(rem.begins_with(','))
{
_c4dbgp("found ',' -- the value was null");
_append_val_null(rem.str - 1);
_line_progressed(1);
return true;
}
else if(rem.begins_with('\t'))
{
_skipchars('\t');
return true;
}
else
{
_c4err("parse error");
}
}
else if(has_any(RNXT))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL));
if(rem.begins_with(", "))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(FLOW));
_c4dbgp("seq: expect next val");
addrem_flags(RVAL, RNXT);
_line_progressed(2);
return true;
}
else if(rem.begins_with(','))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(FLOW));
_c4dbgp("seq: expect next val");
addrem_flags(RVAL, RNXT);
_line_progressed(1);
return true;
}
else if(rem == ':')
{
_c4dbgpf("found ':' -- there's an implicit map in the seq node[{}]", m_state->node_id);
_start_seqimap();
_line_progressed(1);
return true;
}
else if(rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t")))
{
_c4dbgpf("found ': ' -- there's an implicit map in the seq node[{}]", m_state->node_id);
_start_seqimap();
_line_progressed(2);
return true;
}
else
{
_c4err("was expecting a comma");
}
}
else
{
_c4err("internal error");
}
return true;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_seq_blck()
{
_c4dbgpf("handle_seq_impl: node_id={} level={}", m_state->node_id, m_state->level);
csubstr rem = m_state->line_contents.rem;
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RSEQ));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(FLOW));
if(rem.begins_with('#'))
{
_c4dbgp("it's a comment");
rem = _scan_comment();
return true;
}
if(has_any(RNXT))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL));
if(_handle_indentation())
return true;
if(rem.begins_with("- ") _RYML_WITH_TAB_TOKENS( || rem.begins_with("-\t")))
{
_c4dbgp("expect another val");
addrem_flags(RVAL, RNXT);
_line_progressed(2);
return true;
}
else if(rem == '-')
{
_c4dbgp("expect another val");
addrem_flags(RVAL, RNXT);
_line_progressed(1);
return true;
}
else if(rem.begins_with_any(" \t"))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, ! _at_line_begin());
_skipchars(" \t");
return true;
}
else if(rem.begins_with("..."))
{
_c4dbgp("got stream end '...'");
_end_stream();
_line_progressed(3);
return true;
}
else if(rem.begins_with("---"))
{
_c4dbgp("got document start '---'");
_start_new_doc(rem);
return true;
}
else
{
_c4err("parse error");
}
}
else if(has_any(RVAL))
{
// there can be empty values
if(_handle_indentation())
return true;
csubstr s;
bool is_quoted;
if(_scan_scalar(&s, &is_quoted)) // this also progresses the line
{
_c4dbgpf("it's a{} scalar", is_quoted ? " quoted" : "");
rem = m_state->line_contents.rem;
if(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(rem.begins_with_any(" \t"), rem.begins_with(' ')))
{
_c4dbgp("skipping whitespace...");
size_t skip = rem.first_not_of(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' '));
if(skip == csubstr::npos)
skip = rem.len; // maybe the line is just whitespace
_line_progressed(skip);
rem = rem.sub(skip);
}
_c4dbgpf("rem=[{}]~~~{}~~~", rem.len, rem);
if(!rem.begins_with('#') && (rem.ends_with(':') || rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t"))))
{
_c4dbgp("actually, the scalar is the first key of a map, and it opens a new scope");
if(m_key_anchor.empty())
_move_val_anchor_to_key_anchor();
if(m_key_tag.empty())
_move_val_tag_to_key_tag();
addrem_flags(RNXT, RVAL); // before _push_level! This prepares the current level for popping by setting it to RNXT
_push_level();
_start_map();
_store_scalar(s, is_quoted);
if( ! _maybe_set_indentation_from_anchor_or_tag())
{
_c4dbgpf("set indentation from scalar: {}", m_state->scalar_col);
_set_indentation(m_state->scalar_col); // this is the column where the scalar starts
}
_move_key_tag2_to_key_tag();
addrem_flags(RVAL, RKEY);
_line_progressed(1);
}
else
{
_c4dbgp("appending val to current seq");
_append_val(s, is_quoted);
addrem_flags(RNXT, RVAL);
}
return true;
}
else if(rem.begins_with("- ") _RYML_WITH_TAB_TOKENS( || rem.begins_with("-\t")))
{
if(_rval_dash_start_or_continue_seq())
_line_progressed(2);
return true;
}
else if(rem == '-')
{
if(_rval_dash_start_or_continue_seq())
_line_progressed(1);
return true;
}
else if(rem.begins_with('['))
{
_c4dbgp("val is a child seq, flow");
addrem_flags(RNXT, RVAL); // before _push_level!
_push_level(/*explicit flow*/true);
_start_seq();
add_flags(FLOW);
_line_progressed(1);
return true;
}
else if(rem.begins_with('{'))
{
_c4dbgp("val is a child map, flow");
addrem_flags(RNXT, RVAL); // before _push_level!
_push_level(/*explicit flow*/true);
_start_map();
addrem_flags(FLOW|RKEY, RVAL);
_line_progressed(1);
return true;
}
else if(rem.begins_with("? "))
{
_c4dbgp("val is a child map + this key is complex");
addrem_flags(RNXT, RVAL); // before _push_level!
_push_level();
_start_map();
addrem_flags(QMRK|RKEY, RVAL);
_save_indentation();
_line_progressed(2);
return true;
}
else if(rem.begins_with(' '))
{
csubstr spc = rem.left_of(rem.first_not_of(' '));
if(_at_line_begin())
{
_c4dbgpf("skipping value indentation: {} spaces", spc.len);
_line_progressed(spc.len);
return true;
}
else
{
_c4dbgpf("skipping {} spaces", spc.len);
_line_progressed(spc.len);
return true;
}
}
else if(_handle_types())
{
return true;
}
else if(_handle_val_anchors_and_refs())
{
return true;
}
/* pathological case:
* - &key : val
* - &key :
* - : val
*/
else if((!has_all(SSCL)) &&
(rem.begins_with(": ") || rem.left_of(rem.find("#")).trimr("\t") == ":"))
{
if(!m_val_anchor.empty() || !m_val_tag.empty())
{
_c4dbgp("val is a child map + this key is empty, with anchors or tags");
addrem_flags(RNXT, RVAL); // before _push_level!
_move_val_tag_to_key_tag();
_move_val_anchor_to_key_anchor();
_push_level();
_start_map();
_store_scalar_null(rem.str);
addrem_flags(RVAL, RKEY);
RYML_CHECK(_maybe_set_indentation_from_anchor_or_tag()); // one of them must exist
_line_progressed(rem.begins_with(": ") ? 2u : 1u);
return true;
}
else
{
_c4dbgp("val is a child map + this key is empty, no anchors or tags");
addrem_flags(RNXT, RVAL); // before _push_level!
size_t ind = m_state->indref;
_push_level();
_start_map();
_store_scalar_null(rem.str);
addrem_flags(RVAL, RKEY);
_c4dbgpf("set indentation from map anchor: {}", ind + 2);
_set_indentation(ind + 2); // this is the column where the map starts
_line_progressed(rem.begins_with(": ") ? 2u : 1u);
return true;
}
}
else
{
_c4err("parse error");
}
}
return false;
}
//-----------------------------------------------------------------------------
bool Parser::_rval_dash_start_or_continue_seq()
{
size_t ind = m_state->line_contents.current_col();
_RYML_CB_ASSERT(m_stack.m_callbacks, ind >= m_state->indref);
size_t delta_ind = ind - m_state->indref;
if( ! delta_ind)
{
_c4dbgp("prev val was empty");
addrem_flags(RNXT, RVAL);
_append_val_null(&m_state->line_contents.full[ind]);
return false;
}
_c4dbgp("val is a nested seq, indented");
addrem_flags(RNXT, RVAL); // before _push_level!
_push_level();
_start_seq();
_save_indentation();
return true;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_map_flow()
{
// explicit flow, ie, inside {}, separated by commas
_c4dbgpf("handle_map_flow: node_id={} level={}", m_state->node_id, m_state->level);
csubstr rem = m_state->line_contents.rem;
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RMAP|FLOW));
if(rem.begins_with(' '))
{
// with explicit flow, indentation does not matter
_c4dbgp("starts with spaces");
_skipchars(' ');
return true;
}
_RYML_WITH_TAB_TOKENS(else if(rem.begins_with('\t'))
{
// with explicit flow, indentation does not matter
_c4dbgp("starts with tabs");
_skipchars('\t');
return true;
})
else if(rem.begins_with('#'))
{
_c4dbgp("it's a comment");
rem = _scan_comment(); // also progresses the line
return true;
}
else if(rem.begins_with('}'))
{
_c4dbgp("end the map");
if(has_all(SSCL))
{
_c4dbgp("the last val was null");
_append_key_val_null(rem.str - 1);
rem_flags(RVAL);
}
_pop_level();
_line_progressed(1);
if(has_all(RSEQIMAP))
{
_c4dbgp("stopping implicitly nested 1x map");
_stop_seqimap();
_pop_level();
}
return true;
}
if(has_any(RNXT))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RSEQIMAP));
if(rem.begins_with(", "))
{
_c4dbgp("seq: expect next keyval");
addrem_flags(RKEY, RNXT);
_line_progressed(2);
return true;
}
else if(rem.begins_with(','))
{
_c4dbgp("seq: expect next keyval");
addrem_flags(RKEY, RNXT);
_line_progressed(1);
return true;
}
else
{
_c4err("parse error");
}
}
else if(has_any(RKEY))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL));
bool is_quoted;
if(has_none(SSCL) && _scan_scalar(&rem, &is_quoted))
{
_c4dbgp("it's a scalar");
_store_scalar(rem, is_quoted);
rem = m_state->line_contents.rem;
csubstr trimmed = rem.triml(" \t");
if(trimmed.len && (trimmed.begins_with(": ") || trimmed.begins_with_any(":,}") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t"))))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, trimmed.str >= rem.str);
size_t num = static_cast<size_t>(trimmed.str - rem.str);
_c4dbgpf("trimming {} whitespace after the scalar: '{}' --> '{}'", num, rem, rem.sub(num));
rem = rem.sub(num);
_line_progressed(num);
}
}
if(rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t")))
{
_c4dbgp("wait for val");
addrem_flags(RVAL, RKEY|QMRK);
_line_progressed(2);
if(!has_all(SSCL))
{
_c4dbgp("no key was found, defaulting to empty key ''");
_store_scalar_null(rem.str);
}
return true;
}
else if(rem == ':')
{
_c4dbgp("wait for val");
addrem_flags(RVAL, RKEY|QMRK);
_line_progressed(1);
if(!has_all(SSCL))
{
_c4dbgp("no key was found, defaulting to empty key ''");
_store_scalar_null(rem.str);
}
return true;
}
else if(rem.begins_with('?'))
{
_c4dbgp("complex key");
add_flags(QMRK);
_line_progressed(1);
return true;
}
else if(rem.begins_with(','))
{
_c4dbgp("prev scalar was a key with null value");
_append_key_val_null(rem.str - 1);
_line_progressed(1);
return true;
}
else if(rem.begins_with('}'))
{
_c4dbgp("map terminates after a key...");
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(SSCL));
_c4dbgp("the last val was null");
_append_key_val_null(rem.str - 1);
rem_flags(RVAL);
if(has_all(RSEQIMAP))
{
_c4dbgp("stopping implicitly nested 1x map");
_stop_seqimap();
_pop_level();
}
_pop_level();
_line_progressed(1);
return true;
}
else if(_handle_types())
{
return true;
}
else if(_handle_key_anchors_and_refs())
{
return true;
}
else if(rem == "")
{
return true;
}
else
{
size_t pos = rem.first_not_of(" \t");
if(pos == csubstr::npos)
pos = 0;
rem = rem.sub(pos);
if(rem.begins_with(':'))
{
_c4dbgp("wait for val");
addrem_flags(RVAL, RKEY|QMRK);
_line_progressed(pos + 1);
if(!has_all(SSCL))
{
_c4dbgp("no key was found, defaulting to empty key ''");
_store_scalar_null(rem.str);
}
return true;
}
else if(rem.begins_with('#'))
{
_c4dbgp("it's a comment");
_line_progressed(pos);
rem = _scan_comment(); // also progresses the line
return true;
}
else
{
_c4err("parse error");
}
}
}
else if(has_any(RVAL))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(SSCL));
bool is_quoted;
if(_scan_scalar(&rem, &is_quoted))
{
_c4dbgp("it's a scalar");
addrem_flags(RNXT, RVAL|RKEY);
_append_key_val(rem, is_quoted);
if(has_all(RSEQIMAP))
{
_c4dbgp("stopping implicitly nested 1x map");
_stop_seqimap();
_pop_level();
}
return true;
}
else if(rem.begins_with('['))
{
_c4dbgp("val is a child seq");
addrem_flags(RNXT, RVAL|RKEY); // before _push_level!
_push_level(/*explicit flow*/true);
_move_scalar_from_top();
_start_seq();
add_flags(FLOW);
_line_progressed(1);
return true;
}
else if(rem.begins_with('{'))
{
_c4dbgp("val is a child map");
addrem_flags(RNXT, RVAL|RKEY); // before _push_level!
_push_level(/*explicit flow*/true);
_move_scalar_from_top();
_start_map();
addrem_flags(FLOW|RKEY, RNXT|RVAL);
_line_progressed(1);
return true;
}
else if(_handle_types())
{
return true;
}
else if(_handle_val_anchors_and_refs())
{
return true;
}
else if(rem.begins_with(','))
{
_c4dbgp("appending empty val");
_append_key_val_null(rem.str - 1);
addrem_flags(RKEY, RVAL);
_line_progressed(1);
if(has_any(RSEQIMAP))
{
_c4dbgp("stopping implicitly nested 1x map");
_stop_seqimap();
_pop_level();
}
return true;
}
else if(has_any(RSEQIMAP) && rem.begins_with(']'))
{
_c4dbgp("stopping implicitly nested 1x map");
if(has_any(SSCL))
{
_append_key_val_null(rem.str - 1);
}
_stop_seqimap();
_pop_level();
return true;
}
else
{
_c4err("parse error");
}
}
else
{
_c4err("internal error");
}
return false;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_map_blck()
{
_c4dbgpf("handle_map_impl: node_id={} level={}", m_state->node_id, m_state->level);
csubstr rem = m_state->line_contents.rem;
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RMAP));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(FLOW));
if(rem.begins_with('#'))
{
_c4dbgp("it's a comment");
rem = _scan_comment();
return true;
}
if(has_any(RNXT))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL));
// actually, we don't need RNXT in indent-based maps.
addrem_flags(RKEY, RNXT);
}
if(_handle_indentation())
return true;
if(has_any(RKEY))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL));
_c4dbgp("read scalar?");
bool is_quoted;
if(_scan_scalar(&rem, &is_quoted)) // this also progresses the line
{
_c4dbgpf("it's a{} scalar", is_quoted ? " quoted" : "");
if(has_all(QMRK|SSCL))
{
_c4dbgpf("current key is QMRK; SSCL is set. so take store scalar='{}' as key and add an empty val", m_state->scalar);
_append_key_val_null(rem.str - 1);
}
_store_scalar(rem, is_quoted);
if(has_all(QMRK|RSET))
{
_c4dbgp("it's a complex key, so use null value '~'");
_append_key_val_null(rem.str);
}
rem = m_state->line_contents.rem;
if(rem.begins_with(':'))
{
_c4dbgp("wait for val");
addrem_flags(RVAL, RKEY|QMRK);
_line_progressed(1);
rem = m_state->line_contents.rem;
if(rem.begins_with_any(" \t"))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, ! _at_line_begin());
rem = rem.left_of(rem.first_not_of(" \t"));
_c4dbgpf("skip {} spaces/tabs", rem.len);
_line_progressed(rem.len);
}
}
return true;
}
else if(rem.begins_with_any(" \t"))
{
size_t pos = rem.first_not_of(" \t");
if(pos == npos)
pos = rem.len;
_c4dbgpf("skip {} spaces/tabs", pos);
_line_progressed(pos);
return true;
}
else if(rem == '?' || rem.begins_with("? "))
{
_c4dbgp("it's a complex key");
_line_progressed(rem.begins_with("? ") ? 2u : 1u);
if(has_any(SSCL))
_append_key_val_null(rem.str - 1);
add_flags(QMRK);
return true;
}
else if(has_all(QMRK) && rem.begins_with(':'))
{
_c4dbgp("complex key finished");
if(!has_any(SSCL))
_store_scalar_null(rem.str);
addrem_flags(RVAL, RKEY|QMRK);
_line_progressed(1);
rem = m_state->line_contents.rem;
if(rem.begins_with(' '))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, ! _at_line_begin());
_skipchars(' ');
}
return true;
}
else if(rem == ':' || rem.begins_with(": ") _RYML_WITH_TAB_TOKENS( || rem.begins_with(":\t")))
{
_c4dbgp("key finished");
if(!has_all(SSCL))
{
_c4dbgp("key was empty...");
_store_scalar_null(rem.str);
rem_flags(QMRK);
}
addrem_flags(RVAL, RKEY);
_line_progressed(rem == ':' ? 1 : 2);
return true;
}
else if(rem.begins_with("..."))
{
_c4dbgp("end current document");
_end_stream();
_line_progressed(3);
return true;
}
else if(rem.begins_with("---"))
{
_c4dbgp("start new document '---'");
_start_new_doc(rem);
return true;
}
else if(_handle_types())
{
return true;
}
else if(_handle_key_anchors_and_refs())
{
return true;
}
else
{
_c4err("parse error");
}
}
else if(has_any(RVAL))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY));
csubstr s;
bool is_quoted;
if(_scan_scalar(&s, &is_quoted)) // this also progresses the line
{
_c4dbgpf("it's a{} scalar", is_quoted ? " quoted" : "");
rem = m_state->line_contents.rem;
if(rem.begins_with(": "))
{
_c4dbgp("actually, the scalar is the first key of a map");
addrem_flags(RKEY, RVAL); // before _push_level! This prepares the current level for popping by setting it to RNXT
_push_level();
_move_scalar_from_top();
_move_val_anchor_to_key_anchor();
_start_map();
_save_indentation(m_state->scalar_col);
addrem_flags(RVAL, RKEY);
_line_progressed(2);
}
else if(rem.begins_with(':'))
{
_c4dbgp("actually, the scalar is the first key of a map, and it opens a new scope");
addrem_flags(RKEY, RVAL); // before _push_level! This prepares the current level for popping by setting it to RNXT
_push_level();
_move_scalar_from_top();
_move_val_anchor_to_key_anchor();
_start_map();
_save_indentation(/*behind*/s.len);
addrem_flags(RVAL, RKEY);
_line_progressed(1);
}
else
{
_c4dbgp("appending keyval to current map");
_append_key_val(s, is_quoted);
addrem_flags(RKEY, RVAL);
}
return true;
}
else if(rem.begins_with("- ") _RYML_WITH_TAB_TOKENS( || rem.begins_with("-\t")))
{
_c4dbgp("val is a nested seq, indented");
addrem_flags(RKEY, RVAL); // before _push_level!
_push_level();
_move_scalar_from_top();
_start_seq();
_save_indentation();
_line_progressed(2);
return true;
}
else if(rem == '-')
{
_c4dbgp("maybe a seq. start unknown, indented");
_start_unk();
_save_indentation();
_line_progressed(1);
return true;
}
else if(rem.begins_with('['))
{
_c4dbgp("val is a child seq, flow");
addrem_flags(RKEY, RVAL); // before _push_level!
_push_level(/*explicit flow*/true);
_move_scalar_from_top();
_start_seq();
add_flags(FLOW);
_line_progressed(1);
return true;
}
else if(rem.begins_with('{'))
{
_c4dbgp("val is a child map, flow");
addrem_flags(RKEY, RVAL); // before _push_level!
_push_level(/*explicit flow*/true);
_move_scalar_from_top();
_start_map();
addrem_flags(FLOW|RKEY, RVAL);
_line_progressed(1);
return true;
}
else if(rem.begins_with(' '))
{
csubstr spc = rem.left_of(rem.first_not_of(' '));
if(_at_line_begin())
{
_c4dbgpf("skipping value indentation: {} spaces", spc.len);
_line_progressed(spc.len);
return true;
}
else
{
_c4dbgpf("skipping {} spaces", spc.len);
_line_progressed(spc.len);
return true;
}
}
else if(_handle_types())
{
return true;
}
else if(_handle_val_anchors_and_refs())
{
return true;
}
else if(rem.begins_with("--- ") || rem == "---" || rem.begins_with("---\t"))
{
_start_new_doc(rem);
return true;
}
else
{
_c4err("parse error");
}
}
else
{
_c4err("internal error");
}
return false;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_top()
{
_c4dbgp("handle_top");
csubstr rem = m_state->line_contents.rem;
if(rem.begins_with('#'))
{
_c4dbgp("a comment line");
_scan_comment();
return true;
}
csubstr trimmed = rem.triml(' ');
if(trimmed.begins_with('%'))
{
_handle_directive(trimmed);
_line_progressed(rem.len);
return true;
}
else if(trimmed.begins_with("--- ") || trimmed == "---" || trimmed.begins_with("---\t"))
{
_start_new_doc(rem);
if(trimmed.len < rem.len)
{
_line_progressed(rem.len - trimmed.len);
_save_indentation();
}
return true;
}
else if(trimmed.begins_with("..."))
{
_c4dbgp("end current document");
_end_stream();
if(trimmed.len < rem.len)
{
_line_progressed(rem.len - trimmed.len);
}
_line_progressed(3);
return true;
}
else
{
_c4err("parse error");
}
return false;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_key_anchors_and_refs()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, !has_any(RVAL));
const csubstr rem = m_state->line_contents.rem;
if(rem.begins_with('&'))
{
_c4dbgp("found a key anchor!!!");
if(has_all(QMRK|SSCL))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RKEY));
_c4dbgp("there is a stored key, so this anchor is for the next element");
_append_key_val_null(rem.str - 1);
rem_flags(QMRK);
return true;
}
csubstr anchor = rem.left_of(rem.first_of(' '));
_line_progressed(anchor.len);
anchor = anchor.sub(1); // skip the first character
_move_key_anchor_to_val_anchor();
_c4dbgpf("key anchor value: '{}'", anchor);
m_key_anchor = anchor;
m_key_anchor_indentation = m_state->line_contents.current_col(rem);
return true;
}
else if(C4_UNLIKELY(rem.begins_with('*')))
{
_c4err("not implemented - this should have been catched elsewhere");
C4_NEVER_REACH();
return false;
}
return false;
}
bool Parser::_handle_val_anchors_and_refs()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, !has_any(RKEY));
const csubstr rem = m_state->line_contents.rem;
if(rem.begins_with('&'))
{
csubstr anchor = rem.left_of(rem.first_of(' '));
_line_progressed(anchor.len);
anchor = anchor.sub(1); // skip the first character
_c4dbgpf("val: found an anchor: '{}', indentation={}!!!", anchor, m_state->line_contents.current_col(rem));
if(m_val_anchor.empty())
{
_c4dbgpf("save val anchor: '{}'", anchor);
m_val_anchor = anchor;
m_val_anchor_indentation = m_state->line_contents.current_col(rem);
}
else
{
_c4dbgpf("there is a pending val anchor '{}'", m_val_anchor);
if(m_tree->is_seq(m_state->node_id))
{
if(m_tree->has_children(m_state->node_id))
{
_c4dbgpf("current node={} is a seq, has {} children", m_state->node_id, m_tree->num_children(m_state->node_id));
_c4dbgpf("... so take the new one as a key anchor '{}'", anchor);
m_key_anchor = anchor;
m_key_anchor_indentation = m_state->line_contents.current_col(rem);
}
else
{
_c4dbgpf("current node={} is a seq, has no children", m_state->node_id);
if(m_tree->has_val_anchor(m_state->node_id))
{
_c4dbgpf("... node={} already has val anchor: '{}'", m_state->node_id, m_tree->val_anchor(m_state->node_id));
_c4dbgpf("... so take the new one as a key anchor '{}'", anchor);
m_key_anchor = anchor;
m_key_anchor_indentation = m_state->line_contents.current_col(rem);
}
else
{
_c4dbgpf("... so set pending val anchor: '{}' on current node {}", m_val_anchor, m_state->node_id);
m_tree->set_val_anchor(m_state->node_id, m_val_anchor);
m_val_anchor = anchor;
m_val_anchor_indentation = m_state->line_contents.current_col(rem);
}
}
}
}
return true;
}
else if(C4_UNLIKELY(rem.begins_with('*')))
{
_c4err("not implemented - this should have been catched elsewhere");
C4_NEVER_REACH();
return false;
}
return false;
}
void Parser::_move_key_anchor_to_val_anchor()
{
if(m_key_anchor.empty())
return;
_c4dbgpf("move current key anchor to val slot: key='{}' -> val='{}'", m_key_anchor, m_val_anchor);
if(!m_val_anchor.empty())
_c4err("triple-pending anchor");
m_val_anchor = m_key_anchor;
m_val_anchor_indentation = m_key_anchor_indentation;
m_key_anchor = {};
m_key_anchor_indentation = {};
}
void Parser::_move_val_anchor_to_key_anchor()
{
if(m_val_anchor.empty())
return;
if(!_token_is_from_this_line(m_val_anchor))
return;
_c4dbgpf("move current val anchor to key slot: key='{}' <- val='{}'", m_key_anchor, m_val_anchor);
if(!m_key_anchor.empty())
_c4err("triple-pending anchor");
m_key_anchor = m_val_anchor;
m_key_anchor_indentation = m_val_anchor_indentation;
m_val_anchor = {};
m_val_anchor_indentation = {};
}
void Parser::_move_key_tag_to_val_tag()
{
if(m_key_tag.empty())
return;
_c4dbgpf("move key tag to val tag: key='{}' -> val='{}'", m_key_tag, m_val_tag);
m_val_tag = m_key_tag;
m_val_tag_indentation = m_key_tag_indentation;
m_key_tag.clear();
m_key_tag_indentation = 0;
}
void Parser::_move_val_tag_to_key_tag()
{
if(m_val_tag.empty())
return;
if(!_token_is_from_this_line(m_val_tag))
return;
_c4dbgpf("move val tag to key tag: key='{}' <- val='{}'", m_key_tag, m_val_tag);
m_key_tag = m_val_tag;
m_key_tag_indentation = m_val_tag_indentation;
m_val_tag.clear();
m_val_tag_indentation = 0;
}
void Parser::_move_key_tag2_to_key_tag()
{
if(m_key_tag2.empty())
return;
_c4dbgpf("move key tag2 to key tag: key='{}' <- key2='{}'", m_key_tag, m_key_tag2);
m_key_tag = m_key_tag2;
m_key_tag_indentation = m_key_tag2_indentation;
m_key_tag2.clear();
m_key_tag2_indentation = 0;
}
//-----------------------------------------------------------------------------
bool Parser::_handle_types()
{
csubstr rem = m_state->line_contents.rem.triml(' ');
csubstr t;
if(rem.begins_with("!!"))
{
_c4dbgp("begins with '!!'");
t = rem.left_of(rem.first_of(" ,"));
_RYML_CB_ASSERT(m_stack.m_callbacks, t.len >= 2);
//t = t.sub(2);
if(t == "!!set")
add_flags(RSET);
}
else if(rem.begins_with("!<"))
{
_c4dbgp("begins with '!<'");
t = rem.left_of(rem.first_of('>'), true);
_RYML_CB_ASSERT(m_stack.m_callbacks, t.len >= 2);
//t = t.sub(2, t.len-1);
}
else if(rem.begins_with("!h!"))
{
_c4dbgp("begins with '!h!'");
t = rem.left_of(rem.first_of(' '));
_RYML_CB_ASSERT(m_stack.m_callbacks, t.len >= 3);
//t = t.sub(3);
}
else if(rem.begins_with('!'))
{
_c4dbgp("begins with '!'");
t = rem.left_of(rem.first_of(' '));
_RYML_CB_ASSERT(m_stack.m_callbacks, t.len >= 1);
//t = t.sub(1);
}
if(t.empty())
return false;
if(has_all(QMRK|SSCL))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RKEY));
_c4dbgp("there is a stored key, so this tag is for the next element");
_append_key_val_null(rem.str - 1);
rem_flags(QMRK);
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
const char *tag_beginning = rem.str;
#endif
size_t tag_indentation = m_state->line_contents.current_col(t);
_c4dbgpf("there was a tag: '{}', indentation={}", t, tag_indentation);
_RYML_CB_ASSERT(m_stack.m_callbacks, t.end() > m_state->line_contents.rem.begin());
_line_progressed(static_cast<size_t>(t.end() - m_state->line_contents.rem.begin()));
{
size_t pos = m_state->line_contents.rem.first_not_of(" \t");
if(pos != csubstr::npos)
_line_progressed(pos);
}
if(has_all(RMAP|RKEY))
{
_c4dbgpf("saving map key tag '{}'", t);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_key_tag.empty());
m_key_tag = t;
m_key_tag_indentation = tag_indentation;
}
else if(has_all(RMAP|RVAL))
{
/* foo: !!str
* !!str : bar */
rem = m_state->line_contents.rem;
rem = rem.left_of(rem.find("#"));
rem = rem.trimr(" \t");
_c4dbgpf("rem='{}'", rem);
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
if(rem == ':' || rem.begins_with(": "))
{
_c4dbgp("the last val was null, and this is a tag from a null key");
_append_key_val_null(tag_beginning - 1);
_store_scalar_null(rem.str - 1);
// do not change the flag to key, it is ~
_RYML_CB_ASSERT(m_stack.m_callbacks, rem.begin() > m_state->line_contents.rem.begin());
size_t token_len = rem == ':' ? 1 : 2;
_line_progressed(static_cast<size_t>(token_len + rem.begin() - m_state->line_contents.rem.begin()));
}
#endif
_c4dbgpf("saving map val tag '{}'", t);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_val_tag.empty());
m_val_tag = t;
m_val_tag_indentation = tag_indentation;
}
else if(has_all(RSEQ|RVAL) || has_all(RTOP|RUNK|NDOC))
{
if(m_val_tag.empty())
{
_c4dbgpf("saving seq/doc val tag '{}'", t);
m_val_tag = t;
m_val_tag_indentation = tag_indentation;
}
else
{
_c4dbgpf("saving seq/doc key tag '{}'", t);
m_key_tag = t;
m_key_tag_indentation = tag_indentation;
}
}
else if(has_all(RTOP|RUNK) || has_any(RUNK))
{
rem = m_state->line_contents.rem;
rem = rem.left_of(rem.find("#"));
rem = rem.trimr(" \t");
if(rem.empty())
{
_c4dbgpf("saving val tag '{}'", t);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_val_tag.empty());
m_val_tag = t;
m_val_tag_indentation = tag_indentation;
}
else
{
_c4dbgpf("saving key tag '{}'", t);
if(m_key_tag.empty())
{
m_key_tag = t;
m_key_tag_indentation = tag_indentation;
}
else
{
/* handle this case:
* !!str foo: !!map
* !!int 1: !!float 20.0
* !!int 3: !!float 40.0
*
* (m_key_tag would be !!str and m_key_tag2 would be !!int)
*/
m_key_tag2 = t;
m_key_tag2_indentation = tag_indentation;
}
}
}
else
{
_c4err("internal error");
}
if(m_val_tag.not_empty())
{
YamlTag_e tag = to_tag(t);
if(tag == TAG_STR)
{
_c4dbgpf("tag '{}' is a str-type tag", t);
if(has_all(RTOP|RUNK|NDOC))
{
_c4dbgpf("docval. slurping the string. pos={}", m_state->pos.offset);
csubstr scalar = _slurp_doc_scalar();
_c4dbgpf("docval. after slurp: {}, at node {}: '{}'", m_state->pos.offset, m_state->node_id, scalar);
m_tree->to_val(m_state->node_id, scalar, DOC);
_c4dbgpf("docval. val tag {} -> {}", m_val_tag, normalize_tag(m_val_tag));
m_tree->set_val_tag(m_state->node_id, normalize_tag(m_val_tag));
m_val_tag.clear();
if(!m_val_anchor.empty())
{
_c4dbgpf("setting val anchor[{}]='{}'", m_state->node_id, m_val_anchor);
m_tree->set_val_anchor(m_state->node_id, m_val_anchor);
m_val_anchor.clear();
}
_end_stream();
}
}
}
return true;
}
//-----------------------------------------------------------------------------
csubstr Parser::_slurp_doc_scalar()
{
csubstr s = m_state->line_contents.rem;
size_t pos = m_state->pos.offset;
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->line_contents.full.find("---") != csubstr::npos);
_c4dbgpf("slurp 0 '{}'. REM='{}'", s, m_buf.sub(m_state->pos.offset));
if(s.len == 0)
{
_line_ended();
_scan_line();
s = m_state->line_contents.rem;
pos = m_state->pos.offset;
}
size_t skipws = s.first_not_of(" \t");
_c4dbgpf("slurp 1 '{}'. REM='{}'", s, m_buf.sub(m_state->pos.offset));
if(skipws != npos)
{
_line_progressed(skipws);
s = m_state->line_contents.rem;
pos = m_state->pos.offset;
_c4dbgpf("slurp 2 '{}'. REM='{}'", s, m_buf.sub(m_state->pos.offset));
}
_RYML_CB_ASSERT(m_stack.m_callbacks, m_val_anchor.empty());
_handle_val_anchors_and_refs();
if(!m_val_anchor.empty())
{
s = m_state->line_contents.rem;
skipws = s.first_not_of(" \t");
if(skipws != npos)
{
_line_progressed(skipws);
}
s = m_state->line_contents.rem;
pos = m_state->pos.offset;
_c4dbgpf("slurp 3 '{}'. REM='{}'", s, m_buf.sub(m_state->pos.offset));
}
if(s.begins_with('\''))
{
m_state->scalar_col = m_state->line_contents.current_col(s);
return _scan_squot_scalar();
}
else if(s.begins_with('"'))
{
m_state->scalar_col = m_state->line_contents.current_col(s);
return _scan_dquot_scalar();
}
else if(s.begins_with('|') || s.begins_with('>'))
{
return _scan_block();
}
_c4dbgpf("slurp 4 '{}'. REM='{}'", s, m_buf.sub(m_state->pos.offset));
m_state->scalar_col = m_state->line_contents.current_col(s);
_RYML_CB_ASSERT(m_stack.m_callbacks, s.end() >= m_buf.begin() + pos);
_line_progressed(static_cast<size_t>(s.end() - (m_buf.begin() + pos)));
_c4dbgpf("slurp 5 '{}'. REM='{}'", s, m_buf.sub(m_state->pos.offset));
if(_at_line_end())
{
_c4dbgpf("at line end. curr='{}'", s);
s = _extend_scanned_scalar(s);
}
_c4dbgpf("scalar was '{}'", s);
return s;
}
//-----------------------------------------------------------------------------
bool Parser::_scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted)
{
csubstr s = m_state->line_contents.rem;
if(s.len == 0)
return false;
s = s.trim(" \t");
if(s.len == 0)
return false;
if(s.begins_with('\''))
{
_c4dbgp("got a ': scanning single-quoted scalar");
m_state->scalar_col = m_state->line_contents.current_col(s);
*scalar = _scan_squot_scalar();
*quoted = true;
return true;
}
else if(s.begins_with('"'))
{
_c4dbgp("got a \": scanning double-quoted scalar");
m_state->scalar_col = m_state->line_contents.current_col(s);
*scalar = _scan_dquot_scalar();
*quoted = true;
return true;
}
else if(s.begins_with('|') || s.begins_with('>'))
{
*scalar = _scan_block();
*quoted = false;
return true;
}
else if(has_any(RTOP) && _is_doc_sep(s))
{
return false;
}
else if(has_any(RSEQ))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, ! has_all(RKEY));
if(has_all(RVAL))
{
_c4dbgp("RSEQ|RVAL");
if( ! _is_scalar_next__rseq_rval(s))
return false;
_RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t"))
return false;
)
if(s.ends_with(':'))
{
--s.len;
}
else
{
auto first = s.first_of_any(": " _RYML_WITH_TAB_TOKENS( , ":\t"), " #");
if(first)
s.len = first.pos;
}
if(has_all(FLOW))
{
_c4dbgp("RSEQ|RVAL|EXPL");
s = s.left_of(s.first_of(",]"));
}
s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' '));
}
else
{
_c4err("internal error");
}
}
else if(has_any(RMAP))
{
if( ! _is_scalar_next__rmap(s))
return false;
size_t colon_space = s.find(": ");
if(colon_space == npos)
{
_RYML_WITH_OR_WITHOUT_TAB_TOKENS(
// with tab tokens
colon_space = s.find(":\t");
if(colon_space == npos)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, s.len > 0);
colon_space = s.find(':');
if(colon_space != s.len-1)
colon_space = npos;
}
,
// without tab tokens
colon_space = s.find(':');
_RYML_CB_ASSERT(m_stack.m_callbacks, s.len > 0);
if(colon_space != s.len-1)
colon_space = npos;
)
}
if(has_all(RKEY))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, !s.begins_with(' '));
if(has_any(QMRK))
{
_c4dbgp("RMAP|RKEY|CPLX");
_RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RMAP));
if(s.begins_with("? ") || s == '?')
return false;
s = s.left_of(colon_space);
s = s.left_of(s.first_of("#"));
if(has_any(FLOW))
s = s.left_of(s.first_of(':'));
s = s.trimr(" \t");
if(s.begins_with("---"))
return false;
else if(s.begins_with("..."))
return false;
}
else
{
_c4dbgp("RMAP|RKEY");
_RYML_CB_CHECK(m_stack.m_callbacks, !s.begins_with('{'));
if(s.begins_with("? ") || s == '?')
return false;
s = s.left_of(colon_space);
s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' '));
if(has_any(FLOW))
{
_c4dbgpf("RMAP|RKEY|EXPL: '{}'", s);
s = s.left_of(s.first_of(",}"));
if(s.ends_with(':'))
s = s.offs(0, 1);
}
else if(s.begins_with("---"))
{
return false;
}
else if(s.begins_with("..."))
{
return false;
}
}
}
else if(has_all(RVAL))
{
_c4dbgp("RMAP|RVAL");
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(QMRK));
if( ! _is_scalar_next__rmap_val(s))
return false;
_RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t"))
return false;
)
s = s.left_of(s.find(" #")); // is there a comment?
s = s.left_of(s.find("\t#")); // is there a comment?
if(has_any(FLOW))
{
_c4dbgp("RMAP|RVAL|EXPL");
if(has_none(RSEQIMAP))
s = s.left_of(s.first_of(",}"));
else
s = s.left_of(s.first_of(",]"));
}
s = s.trim(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' '));
if(s.begins_with("---"))
return false;
else if(s.begins_with("..."))
return false;
}
else
{
_c4err("parse error");
}
}
else if(has_all(RUNK))
{
_c4dbgpf("RUNK '[{}]~~~{}~~~", s.len, s);
if( ! _is_scalar_next__runk(s))
{
_c4dbgp("RUNK: no scalar next");
return false;
}
s = s.left_of(s.find(" #"));
size_t pos = s.find(": ");
if(pos != npos)
s = s.left_of(pos);
else if(s.ends_with(':'))
s = s.left_of(s.len-1);
_RYML_WITH_TAB_TOKENS(
else if((pos = s.find(":\t")) != npos) // TABS
s = s.left_of(pos);
)
else
s = s.left_of(s.first_of(','));
s = s.trim(" \t");
_c4dbgpf("RUNK: scalar='{}'", s);
}
else
{
_c4err("not implemented");
}
if(s.empty())
return false;
m_state->scalar_col = m_state->line_contents.current_col(s);
_RYML_CB_ASSERT(m_stack.m_callbacks, s.str >= m_state->line_contents.rem.str);
_line_progressed(static_cast<size_t>(s.str - m_state->line_contents.rem.str) + s.len);
if(_at_line_end() && s != '~')
{
_c4dbgpf("at line end. curr='{}'", s);
s = _extend_scanned_scalar(s);
}
_c4dbgpf("scalar was '{}'", s);
*scalar = s;
*quoted = false;
return true;
}
//-----------------------------------------------------------------------------
csubstr Parser::_extend_scanned_scalar(csubstr s)
{
if(has_all(RMAP|RKEY|QMRK))
{
size_t scalar_indentation = has_any(FLOW) ? 0 : m_state->scalar_col;
_c4dbgpf("extend_scalar: explicit key! indref={} scalar_indentation={} scalar_col={}", m_state->indref, scalar_indentation, m_state->scalar_col);
csubstr n = _scan_to_next_nonempty_line(scalar_indentation);
if(!n.empty())
{
substr full = _scan_complex_key(s, n).trimr(" \t\r\n");
if(full != s)
s = _filter_plain_scalar(full, scalar_indentation);
}
}
// deal with plain (unquoted) scalars that continue to the next line
else if(!s.begins_with_any("*")) // cannot be a plain scalar if it starts with * (that's an anchor reference)
{
_c4dbgpf("extend_scalar: line ended, scalar='{}'", s);
if(has_none(FLOW))
{
size_t scalar_indentation = m_state->indref + 1;
if(has_all(RUNK) && scalar_indentation == 1)
scalar_indentation = 0;
csubstr n = _scan_to_next_nonempty_line(scalar_indentation);
if(!n.empty())
{
_c4dbgpf("rscalar[IMPL]: state_indref={} state_indentation={} scalar_indentation={}", m_state->indref, m_state->line_contents.indentation, scalar_indentation);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->line_contents.full.is_super(n));
substr full = _scan_plain_scalar_blck(s, n, scalar_indentation);
if(full.len >= s.len)
s = _filter_plain_scalar(full, scalar_indentation);
}
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(FLOW));
csubstr n = _scan_to_next_nonempty_line(/*indentation*/0);
if(!n.empty())
{
_c4dbgp("rscalar[FLOW]");
substr full = _scan_plain_scalar_flow(s, n);
s = _filter_plain_scalar(full, /*indentation*/0);
}
}
}
return s;
}
//-----------------------------------------------------------------------------
substr Parser::_scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line)
{
static constexpr const csubstr chars = "[]{}?#,";
size_t pos = peeked_line.first_of(chars);
bool first = true;
while(pos != 0)
{
if(has_all(RMAP|RKEY) || has_any(RUNK))
{
csubstr tpkl = peeked_line.triml(' ').trimr("\r\n");
if(tpkl.begins_with(": ") || tpkl == ':')
{
_c4dbgpf("rscalar[EXPL]: map value starts on the peeked line: '{}'", peeked_line);
peeked_line = peeked_line.first(0);
break;
}
else
{
auto colon_pos = peeked_line.first_of_any(": ", ":");
if(colon_pos && colon_pos.pos < pos)
{
peeked_line = peeked_line.first(colon_pos.pos);
_c4dbgpf("rscalar[EXPL]: found colon at {}. peeked='{}'", colon_pos.pos, peeked_line);
_RYML_CB_ASSERT(m_stack.m_callbacks, peeked_line.end() >= m_state->line_contents.rem.begin());
_line_progressed(static_cast<size_t>(peeked_line.end() - m_state->line_contents.rem.begin()));
break;
}
}
}
if(pos != npos)
{
_c4dbgpf("rscalar[EXPL]: found special character '{}' at {}, stopping: '{}'", peeked_line[pos], pos, peeked_line.left_of(pos).trimr("\r\n"));
peeked_line = peeked_line.left_of(pos);
_RYML_CB_ASSERT(m_stack.m_callbacks, peeked_line.end() >= m_state->line_contents.rem.begin());
_line_progressed(static_cast<size_t>(peeked_line.end() - m_state->line_contents.rem.begin()));
break;
}
_c4dbgpf("rscalar[EXPL]: append another line, full: '{}'", peeked_line.trimr("\r\n"));
if(!first)
{
RYML_CHECK(_advance_to_peeked());
}
peeked_line = _scan_to_next_nonempty_line(/*indentation*/0);
if(peeked_line.empty())
{
_c4err("expected token or continuation");
}
pos = peeked_line.first_of(chars);
first = false;
}
substr full(m_buf.str + (currscalar.str - m_buf.str), m_buf.begin() + m_state->pos.offset);
full = full.trimr("\n\r ");
return full;
}
//-----------------------------------------------------------------------------
substr Parser::_scan_plain_scalar_blck(csubstr currscalar, csubstr peeked_line, size_t indentation)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(currscalar));
// NOTE. there's a problem with _scan_to_next_nonempty_line(), as it counts newlines twice
// size_t offs = m_state->pos.offset; // so we workaround by directly counting from the end of the given scalar
_RYML_CB_ASSERT(m_stack.m_callbacks, currscalar.end() >= m_buf.begin());
size_t offs = static_cast<size_t>(currscalar.end() - m_buf.begin());
_RYML_CB_ASSERT(m_stack.m_callbacks, peeked_line.begins_with(' ', indentation));
while(true)
{
_c4dbgpf("rscalar[IMPL]: continuing... ref_indentation={}", indentation);
if(peeked_line.begins_with("...") || peeked_line.begins_with("---"))
{
_c4dbgpf("rscalar[IMPL]: document termination next -- bail now '{}'", peeked_line.trimr("\r\n"));
break;
}
else if(( ! peeked_line.begins_with(' ', indentation))) // is the line deindented?
{
if(!peeked_line.trim(" \r\n\t").empty()) // is the line not blank?
{
_c4dbgpf("rscalar[IMPL]: deindented line, not blank -- bail now '{}'", peeked_line.trimr("\r\n"));
break;
}
_c4dbgpf("rscalar[IMPL]: line is blank and has less indentation: ref={} line={}: '{}'", indentation, peeked_line.first_not_of(' ') == csubstr::npos ? 0 : peeked_line.first_not_of(' '), peeked_line.trimr("\r\n"));
_c4dbgpf("rscalar[IMPL]: ... searching for a line starting at indentation {}", indentation);
csubstr next_peeked = _scan_to_next_nonempty_line(indentation);
if(next_peeked.empty())
{
_c4dbgp("rscalar[IMPL]: ... finished.");
break;
}
_c4dbgp("rscalar[IMPL]: ... continuing.");
peeked_line = next_peeked;
}
_c4dbgpf("rscalar[IMPL]: line contents: '{}'", peeked_line.right_of(indentation, true).trimr("\r\n"));
size_t token_pos;
if(peeked_line.find(": ") != npos)
{
_line_progressed(peeked_line.find(": "));
_c4err("': ' is not a valid token in plain flow (unquoted) scalars");
}
else if(peeked_line.ends_with(':'))
{
_line_progressed(peeked_line.find(':'));
_c4err("lines cannot end with ':' in plain flow (unquoted) scalars");
}
else if((token_pos = peeked_line.find(" #")) != npos)
{
_line_progressed(token_pos);
break;
//_c4err("' #' is not a valid token in plain flow (unquoted) scalars");
}
_c4dbgpf("rscalar[IMPL]: append another line: (len={})'{}'", peeked_line.len, peeked_line.trimr("\r\n"));
if(!_advance_to_peeked())
{
_c4dbgp("rscalar[IMPL]: file finishes after the scalar");
break;
}
peeked_line = m_state->line_contents.rem;
}
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.offset >= offs);
substr full(m_buf.str + (currscalar.str - m_buf.str),
currscalar.len + (m_state->pos.offset - offs));
full = full.trimr("\r\n ");
return full;
}
substr Parser::_scan_complex_key(csubstr currscalar, csubstr peeked_line)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(currscalar));
// NOTE. there's a problem with _scan_to_next_nonempty_line(), as it counts newlines twice
// size_t offs = m_state->pos.offset; // so we workaround by directly counting from the end of the given scalar
_RYML_CB_ASSERT(m_stack.m_callbacks, currscalar.end() >= m_buf.begin());
size_t offs = static_cast<size_t>(currscalar.end() - m_buf.begin());
while(true)
{
_c4dbgp("rcplxkey: continuing...");
if(peeked_line.begins_with("...") || peeked_line.begins_with("---"))
{
_c4dbgpf("rcplxkey: document termination next -- bail now '{}'", peeked_line.trimr("\r\n"));
break;
}
else
{
size_t pos = peeked_line.first_of("?:[]{}");
if(pos == csubstr::npos)
{
pos = peeked_line.find("- ");
}
if(pos != csubstr::npos)
{
_c4dbgpf("rcplxkey: found special characters at pos={}: '{}'", pos, peeked_line.trimr("\r\n"));
_line_progressed(pos);
break;
}
}
_c4dbgpf("rcplxkey: no special chars found '{}'", peeked_line.trimr("\r\n"));
csubstr next_peeked = _scan_to_next_nonempty_line(0);
if(next_peeked.empty())
{
_c4dbgp("rcplxkey: empty ... finished.");
break;
}
_c4dbgp("rcplxkey: ... continuing.");
peeked_line = next_peeked;
_c4dbgpf("rcplxkey: line contents: '{}'", peeked_line.trimr("\r\n"));
size_t colpos;
if((colpos = peeked_line.find(": ")) != npos)
{
_c4dbgp("rcplxkey: found ': ', stopping.");
_line_progressed(colpos);
break;
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
else if((colpos = peeked_line.ends_with(':')))
{
_c4dbgp("rcplxkey: ends with ':', stopping.");
_line_progressed(colpos);
break;
}
#endif
_c4dbgpf("rcplxkey: append another line: (len={})'{}'", peeked_line.len, peeked_line.trimr("\r\n"));
if(!_advance_to_peeked())
{
_c4dbgp("rcplxkey: file finishes after the scalar");
break;
}
peeked_line = m_state->line_contents.rem;
}
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.offset >= offs);
substr full(m_buf.str + (currscalar.str - m_buf.str),
currscalar.len + (m_state->pos.offset - offs));
return full;
}
//! scans to the next non-blank line starting with the given indentation
csubstr Parser::_scan_to_next_nonempty_line(size_t indentation)
{
csubstr next_peeked;
while(true)
{
_c4dbgpf("rscalar: ... curr offset: {} indentation={}", m_state->pos.offset, indentation);
next_peeked = _peek_next_line(m_state->pos.offset);
csubstr next_peeked_triml = next_peeked.triml(' ');
_c4dbgpf("rscalar: ... next peeked line='{}'", next_peeked.trimr("\r\n"));
if(next_peeked_triml.begins_with('#'))
{
_c4dbgp("rscalar: ... first non-space character is #");
return {};
}
else if(next_peeked.begins_with(' ', indentation))
{
_c4dbgpf("rscalar: ... begins at same indentation {}, assuming continuation", indentation);
_advance_to_peeked();
return next_peeked;
}
else // check for de-indentation
{
csubstr trimmed = next_peeked_triml.trimr("\t\r\n");
_c4dbgpf("rscalar: ... deindented! trimmed='{}'", trimmed);
if(!trimmed.empty())
{
_c4dbgp("rscalar: ... and not empty. bailing out.");
return {};
}
}
if(!_advance_to_peeked())
{
_c4dbgp("rscalar: file finished");
return {};
}
}
return {};
}
// returns false when the file finished
bool Parser::_advance_to_peeked()
{
_line_progressed(m_state->line_contents.rem.len);
_line_ended(); // advances to the peeked-at line, consuming all remaining (probably newline) characters on the current line
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->line_contents.rem.first_of("\r\n") == csubstr::npos);
_c4dbgpf("advance to peeked: scan more... pos={} len={}", m_state->pos.offset, m_buf.len);
_scan_line(); // puts the peeked-at line in the buffer
if(_finished_file())
{
_c4dbgp("rscalar: finished file!");
return false;
}
return true;
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE size_t _extend_from_combined_newline(char nl, char following)
{
return (nl == '\n' && following == '\r') || (nl == '\r' && following == '\n');
}
//! look for the next newline chars, and jump to the right of those
csubstr from_next_line(csubstr rem)
{
size_t nlpos = rem.first_of("\r\n");
if(nlpos == csubstr::npos)
return {};
const char nl = rem[nlpos];
rem = rem.right_of(nlpos);
if(rem.empty())
return {};
if(_extend_from_combined_newline(nl, rem.front()))
rem = rem.sub(1);
return rem;
}
csubstr Parser::_peek_next_line(size_t pos) const
{
csubstr rem{}; // declare here because of the goto
size_t nlpos{}; // declare here because of the goto
pos = pos == npos ? m_state->pos.offset : pos;
if(pos >= m_buf.len)
goto next_is_empty;
// look for the next newline chars, and jump to the right of those
rem = from_next_line(m_buf.sub(pos));
if(rem.empty())
goto next_is_empty;
// now get everything up to and including the following newline chars
nlpos = rem.first_of("\r\n");
if((nlpos != csubstr::npos) && (nlpos + 1 < rem.len))
nlpos += _extend_from_combined_newline(rem[nlpos], rem[nlpos+1]);
rem = rem.left_of(nlpos, /*include_pos*/true);
_c4dbgpf("peek next line @ {}: (len={})'{}'", pos, rem.len, rem.trimr("\r\n"));
return rem;
next_is_empty:
_c4dbgpf("peek next line @ {}: (len=0)''", pos);
return {};
}
//-----------------------------------------------------------------------------
void Parser::LineContents::reset_with_next_line(csubstr buf, size_t offset)
{
RYML_ASSERT(offset <= buf.len);
char const* C4_RESTRICT b = &buf[offset];
char const* C4_RESTRICT e = b;
// get the current line stripped of newline chars
while(e < buf.end() && (*e != '\n' && *e != '\r'))
++e;
RYML_ASSERT(e >= b);
const csubstr stripped_ = buf.sub(offset, static_cast<size_t>(e - b));
// advance pos to include the first line ending
if(e != buf.end() && *e == '\r')
++e;
if(e != buf.end() && *e == '\n')
++e;
RYML_ASSERT(e >= b);
const csubstr full_ = buf.sub(offset, static_cast<size_t>(e - b));
reset(full_, stripped_);
}
void Parser::_scan_line()
{
if(m_state->pos.offset >= m_buf.len)
{
m_state->line_contents.reset(m_buf.last(0), m_buf.last(0));
return;
}
m_state->line_contents.reset_with_next_line(m_buf, m_state->pos.offset);
}
//-----------------------------------------------------------------------------
void Parser::_line_progressed(size_t ahead)
{
_c4dbgpf("line[{}] ({} cols) progressed by {}: col {}-->{} offset {}-->{}", m_state->pos.line, m_state->line_contents.full.len, ahead, m_state->pos.col, m_state->pos.col+ahead, m_state->pos.offset, m_state->pos.offset+ahead);
m_state->pos.offset += ahead;
m_state->pos.col += ahead;
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.col <= m_state->line_contents.stripped.len+1);
m_state->line_contents.rem = m_state->line_contents.rem.sub(ahead);
}
void Parser::_line_ended()
{
_c4dbgpf("line[{}] ({} cols) ended! offset {}-->{}", m_state->pos.line, m_state->line_contents.full.len, m_state->pos.offset, m_state->pos.offset+m_state->line_contents.full.len - m_state->line_contents.stripped.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.col == m_state->line_contents.stripped.len+1);
m_state->pos.offset += m_state->line_contents.full.len - m_state->line_contents.stripped.len;
++m_state->pos.line;
m_state->pos.col = 1;
}
void Parser::_line_ended_undo()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.col == 1u);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.line > 0u);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.offset >= m_state->line_contents.full.len - m_state->line_contents.stripped.len);
_c4dbgpf("line[{}] undo ended! line {}-->{}, offset {}-->{}", m_state->pos.line, m_state->pos.line, m_state->pos.line - 1, m_state->pos.offset, m_state->pos.offset - (m_state->line_contents.full.len - m_state->line_contents.stripped.len));
m_state->pos.offset -= m_state->line_contents.full.len - m_state->line_contents.stripped.len;
--m_state->pos.line;
m_state->pos.col = m_state->line_contents.stripped.len + 1u;
}
//-----------------------------------------------------------------------------
void Parser::_set_indentation(size_t indentation)
{
m_state->indref = indentation;
_c4dbgpf("state[{}]: saving indentation: {}", m_state-m_stack.begin(), m_state->indref);
}
void Parser::_save_indentation(size_t behind)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->line_contents.rem.begin() >= m_state->line_contents.full.begin());
m_state->indref = static_cast<size_t>(m_state->line_contents.rem.begin() - m_state->line_contents.full.begin());
_RYML_CB_ASSERT(m_stack.m_callbacks, behind <= m_state->indref);
m_state->indref -= behind;
_c4dbgpf("state[{}]: saving indentation: {}", m_state-m_stack.begin(), m_state->indref);
}
bool Parser::_maybe_set_indentation_from_anchor_or_tag()
{
if(m_key_anchor.not_empty())
{
_c4dbgpf("set indentation from key anchor: {}", m_key_anchor_indentation);
_set_indentation(m_key_anchor_indentation); // this is the column where the anchor starts
return true;
}
else if(m_key_tag.not_empty())
{
_c4dbgpf("set indentation from key tag: {}", m_key_tag_indentation);
_set_indentation(m_key_tag_indentation); // this is the column where the tag starts
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void Parser::_write_key_anchor(size_t node_id)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->has_key(node_id));
if( ! m_key_anchor.empty())
{
_c4dbgpf("node={}: set key anchor to '{}'", node_id, m_key_anchor);
m_tree->set_key_anchor(node_id, m_key_anchor);
m_key_anchor.clear();
m_key_anchor_was_before = false;
m_key_anchor_indentation = 0;
}
else if( ! m_tree->is_key_quoted(node_id))
{
csubstr r = m_tree->key(node_id);
if(r.begins_with('*'))
{
_c4dbgpf("node={}: set key reference: '{}'", node_id, r);
m_tree->set_key_ref(node_id, r.sub(1));
}
else if(r == "<<")
{
m_tree->set_key_ref(node_id, r);
_c4dbgpf("node={}: it's an inheriting reference", node_id);
if(m_tree->is_seq(node_id))
{
_c4dbgpf("node={}: inheriting from seq of {}", node_id, m_tree->num_children(node_id));
for(size_t i = m_tree->first_child(node_id); i != NONE; i = m_tree->next_sibling(i))
{
if( ! (m_tree->val(i).begins_with('*')))
_c4err("malformed reference: '{}'", m_tree->val(i));
}
}
else if( ! m_tree->val(node_id).begins_with('*'))
{
_c4err("malformed reference: '{}'", m_tree->val(node_id));
}
//m_tree->set_key_ref(node_id, r);
}
}
}
//-----------------------------------------------------------------------------
void Parser::_write_val_anchor(size_t node_id)
{
if( ! m_val_anchor.empty())
{
_c4dbgpf("node={}: set val anchor to '{}'", node_id, m_val_anchor);
m_tree->set_val_anchor(node_id, m_val_anchor);
m_val_anchor.clear();
}
csubstr r = m_tree->has_val(node_id) ? m_tree->val(node_id) : "";
if(!m_tree->is_val_quoted(node_id) && r.begins_with('*'))
{
_c4dbgpf("node={}: set val reference: '{}'", node_id, r);
RYML_CHECK(!m_tree->has_val_anchor(node_id));
m_tree->set_val_ref(node_id, r.sub(1));
}
}
//-----------------------------------------------------------------------------
void Parser::_push_level(bool explicit_flow_chars)
{
_c4dbgpf("pushing level! currnode={} currlevel={} stacksize={} stackcap={}", m_state->node_id, m_state->level, m_stack.size(), m_stack.capacity());
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state == &m_stack.top());
if(node(m_state) == nullptr)
{
_c4dbgp("pushing level! actually no, current node is null");
//_RYML_CB_ASSERT(m_stack.m_callbacks, ! explicit_flow_chars);
return;
}
flag_t st = RUNK;
if(explicit_flow_chars || has_all(FLOW))
{
st |= FLOW;
}
m_stack.push_top();
m_state = &m_stack.top();
set_flags(st);
m_state->node_id = (size_t)NONE;
m_state->indref = (size_t)NONE;
++m_state->level;
_c4dbgpf("pushing level: now, currlevel={}", m_state->level);
}
void Parser::_pop_level()
{
_c4dbgpf("popping level! currnode={} currlevel={}", m_state->node_id, m_state->level);
if(has_any(RMAP) || m_tree->is_map(m_state->node_id))
{
_stop_map();
}
if(has_any(RSEQ) || m_tree->is_seq(m_state->node_id))
{
_stop_seq();
}
if(m_tree->is_doc(m_state->node_id))
{
_stop_doc();
}
_RYML_CB_ASSERT(m_stack.m_callbacks, m_stack.size() > 1);
_prepare_pop();
m_stack.pop();
m_state = &m_stack.top();
/*if(has_any(RMAP))
{
_toggle_key_val();
}*/
if(m_state->line_contents.indentation == 0)
{
//_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RTOP));
add_flags(RTOP);
}
_c4dbgpf("popping level: now, currnode={} currlevel={}", m_state->node_id, m_state->level);
}
//-----------------------------------------------------------------------------
void Parser::_start_unk(bool /*as_child*/)
{
_c4dbgp("start_unk");
_push_level();
_move_scalar_from_top();
}
//-----------------------------------------------------------------------------
void Parser::_start_doc(bool as_child)
{
_c4dbgpf("start_doc (as child={})", as_child);
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_stack.bottom()) == node(m_root_id));
size_t parent_id = m_stack.size() < 2 ? m_root_id : m_stack.top(1).node_id;
_RYML_CB_ASSERT(m_stack.m_callbacks, parent_id != NONE);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_root(parent_id));
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_state) == nullptr || node(m_state) == node(m_root_id));
if(as_child)
{
_c4dbgpf("start_doc: parent={}", parent_id);
if( ! m_tree->is_stream(parent_id))
{
_c4dbgp("start_doc: rearranging with root as STREAM");
m_tree->set_root_as_stream();
}
m_state->node_id = m_tree->append_child(parent_id);
m_tree->to_doc(m_state->node_id);
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_seq(parent_id) || m_tree->empty(parent_id));
m_state->node_id = parent_id;
if( ! m_tree->is_doc(parent_id))
{
m_tree->to_doc(parent_id, DOC);
}
}
#endif
_c4dbgpf("start_doc: id={}", m_state->node_id);
add_flags(RUNK|RTOP|NDOC);
_handle_types();
rem_flags(NDOC);
}
void Parser::_stop_doc()
{
size_t doc_node = m_state->node_id;
_c4dbgpf("stop_doc[{}]", doc_node);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_doc(doc_node));
if(!m_tree->is_seq(doc_node) && !m_tree->is_map(doc_node) && !m_tree->is_val(doc_node))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(SSCL));
_c4dbgpf("stop_doc[{}]: there was nothing; adding null val", doc_node);
m_tree->to_val(doc_node, {}, DOC);
}
}
void Parser::_end_stream()
{
_c4dbgpf("end_stream, level={} node_id={}", m_state->level, m_state->node_id);
_RYML_CB_ASSERT(m_stack.m_callbacks, ! m_stack.empty());
NodeData *added = nullptr;
if(has_any(SSCL))
{
if(m_tree->is_seq(m_state->node_id))
{
_c4dbgp("append val...");
added = _append_val(_consume_scalar());
}
else if(m_tree->is_map(m_state->node_id))
{
_c4dbgp("append null key val...");
added = _append_key_val_null(m_state->line_contents.rem.str);
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
if(has_any(RSEQIMAP))
{
_stop_seqimap();
_pop_level();
}
#endif
}
else if(m_tree->is_doc(m_state->node_id) || m_tree->type(m_state->node_id) == NOTYPE)
{
NodeType_e quoted = has_any(QSCL) ? VALQUO : NOTYPE; // do this before consuming the scalar
csubstr scalar = _consume_scalar();
_c4dbgpf("node[{}]: to docval '{}'{}", m_state->node_id, scalar, quoted == VALQUO ? ", quoted" : "");
m_tree->to_val(m_state->node_id, scalar, DOC|quoted);
added = m_tree->get(m_state->node_id);
}
else
{
_c4err("internal error");
}
}
else if(has_all(RSEQ|RVAL) && has_none(FLOW))
{
_c4dbgp("add last...");
added = _append_val_null(m_state->line_contents.rem.str);
}
else if(!m_val_tag.empty() && (m_tree->is_doc(m_state->node_id) || m_tree->type(m_state->node_id) == NOTYPE))
{
csubstr scalar = m_state->line_contents.rem.first(0);
_c4dbgpf("node[{}]: add null scalar as docval", m_state->node_id);
m_tree->to_val(m_state->node_id, scalar, DOC);
added = m_tree->get(m_state->node_id);
}
if(added)
{
size_t added_id = m_tree->id(added);
if(m_tree->is_seq(m_state->node_id) || m_tree->is_doc(m_state->node_id))
{
if(!m_key_anchor.empty())
{
_c4dbgpf("node[{}]: move key to val anchor: '{}'", added_id, m_key_anchor);
m_val_anchor = m_key_anchor;
m_key_anchor = {};
}
if(!m_key_tag.empty())
{
_c4dbgpf("node[{}]: move key to val tag: '{}'", added_id, m_key_tag);
m_val_tag = m_key_tag;
m_key_tag = {};
}
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
if(!m_key_anchor.empty())
{
_c4dbgpf("node[{}]: set key anchor='{}'", added_id, m_key_anchor);
m_tree->set_key_anchor(added_id, m_key_anchor);
m_key_anchor = {};
}
#endif
if(!m_val_anchor.empty())
{
_c4dbgpf("node[{}]: set val anchor='{}'", added_id, m_val_anchor);
m_tree->set_val_anchor(added_id, m_val_anchor);
m_val_anchor = {};
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
if(!m_key_tag.empty())
{
_c4dbgpf("node[{}]: set key tag='{}' -> '{}'", added_id, m_key_tag, normalize_tag(m_key_tag));
m_tree->set_key_tag(added_id, normalize_tag(m_key_tag));
m_key_tag = {};
}
#endif
if(!m_val_tag.empty())
{
_c4dbgpf("node[{}]: set val tag='{}' -> '{}'", added_id, m_val_tag, normalize_tag(m_val_tag));
m_tree->set_val_tag(added_id, normalize_tag(m_val_tag));
m_val_tag = {};
}
}
while(m_stack.size() > 1)
{
_c4dbgpf("popping level: {} (stack sz={})", m_state->level, m_stack.size());
_RYML_CB_ASSERT(m_stack.m_callbacks, ! has_any(SSCL, &m_stack.top()));
if(has_all(RSEQ|FLOW))
_err("closing ] not found");
_pop_level();
}
add_flags(NDOC);
}
void Parser::_start_new_doc(csubstr rem)
{
_c4dbgp("_start_new_doc");
_RYML_CB_ASSERT(m_stack.m_callbacks, rem.begins_with("---"));
C4_UNUSED(rem);
_end_stream();
size_t indref = m_state->indref;
_c4dbgpf("start a document, indentation={}", indref);
_line_progressed(3);
_push_level();
_start_doc();
_set_indentation(indref);
}
//-----------------------------------------------------------------------------
void Parser::_start_map(bool as_child)
{
_c4dbgpf("start_map (as child={})", as_child);
addrem_flags(RMAP|RVAL, RKEY|RUNK);
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_stack.bottom()) == node(m_root_id));
size_t parent_id = m_stack.size() < 2 ? m_root_id : m_stack.top(1).node_id;
_RYML_CB_ASSERT(m_stack.m_callbacks, parent_id != NONE);
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_state) == nullptr || node(m_state) == node(m_root_id));
if(as_child)
{
m_state->node_id = m_tree->append_child(parent_id);
if(has_all(SSCL))
{
type_bits key_quoted = NOTYPE;
if(m_state->flags & QSCL) // before consuming the scalar
key_quoted |= KEYQUO;
csubstr key = _consume_scalar();
m_tree->to_map(m_state->node_id, key, key_quoted);
_c4dbgpf("start_map: id={} key='{}'", m_state->node_id, m_tree->key(m_state->node_id));
_write_key_anchor(m_state->node_id);
if( ! m_key_tag.empty())
{
_c4dbgpf("node[{}]: set key tag='{}' -> '{}'", m_state->node_id, m_key_tag, normalize_tag(m_key_tag));
m_tree->set_key_tag(m_state->node_id, normalize_tag(m_key_tag));
m_key_tag.clear();
}
}
else
{
m_tree->to_map(m_state->node_id);
_c4dbgpf("start_map: id={}", m_state->node_id);
}
m_tree->_p(m_state->node_id)->m_val.scalar.str = m_state->line_contents.rem.str;
_write_val_anchor(m_state->node_id);
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, parent_id != NONE);
m_state->node_id = parent_id;
_c4dbgpf("start_map: id={}", m_state->node_id);
type_bits as_doc = 0;
if(m_tree->is_doc(m_state->node_id))
as_doc |= DOC;
if(!m_tree->is_map(parent_id))
{
RYML_CHECK(!m_tree->has_children(parent_id));
m_tree->to_map(parent_id, as_doc);
}
else
{
m_tree->_add_flags(parent_id, as_doc);
}
_move_scalar_from_top();
if(m_key_anchor.not_empty())
m_key_anchor_was_before = true;
_write_val_anchor(parent_id);
if(m_stack.size() >= 2)
{
State const& parent_state = m_stack.top(1);
if(parent_state.flags & RSET)
add_flags(RSET);
}
m_tree->_p(parent_id)->m_val.scalar.str = m_state->line_contents.rem.str;
}
if( ! m_val_tag.empty())
{
_c4dbgpf("node[{}]: set val tag='{}' -> '{}'", m_state->node_id, m_val_tag, normalize_tag(m_val_tag));
m_tree->set_val_tag(m_state->node_id, normalize_tag(m_val_tag));
m_val_tag.clear();
}
}
void Parser::_start_map_unk(bool as_child)
{
if(!m_key_anchor_was_before)
{
_c4dbgpf("stash key anchor before starting map... '{}'", m_key_anchor);
csubstr ka = m_key_anchor;
m_key_anchor = {};
_start_map(as_child);
m_key_anchor = ka;
}
else
{
_start_map(as_child);
m_key_anchor_was_before = false;
}
if(m_key_tag2.not_empty())
{
m_key_tag = m_key_tag2;
m_key_tag_indentation = m_key_tag2_indentation;
m_key_tag2.clear();
m_key_tag2_indentation = 0;
}
}
void Parser::_stop_map()
{
_c4dbgpf("stop_map[{}]", m_state->node_id);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_map(m_state->node_id));
if(has_all(QMRK|RKEY) && !has_all(SSCL))
{
_c4dbgpf("stop_map[{}]: RKEY", m_state->node_id);
_store_scalar_null(m_state->line_contents.rem.str);
_append_key_val_null(m_state->line_contents.rem.str);
}
}
//-----------------------------------------------------------------------------
void Parser::_start_seq(bool as_child)
{
_c4dbgpf("start_seq (as child={})", as_child);
if(has_all(RTOP|RUNK))
{
_c4dbgpf("start_seq: moving key tag to val tag: '{}'", m_key_tag);
m_val_tag = m_key_tag;
m_key_tag.clear();
}
addrem_flags(RSEQ|RVAL, RUNK);
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_stack.bottom()) == node(m_root_id));
size_t parent_id = m_stack.size() < 2 ? m_root_id : m_stack.top(1).node_id;
_RYML_CB_ASSERT(m_stack.m_callbacks, parent_id != NONE);
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_state) == nullptr || node(m_state) == node(m_root_id));
if(as_child)
{
m_state->node_id = m_tree->append_child(parent_id);
if(has_all(SSCL))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_map(parent_id));
type_bits key_quoted = 0;
if(m_state->flags & QSCL) // before consuming the scalar
key_quoted |= KEYQUO;
csubstr key = _consume_scalar();
m_tree->to_seq(m_state->node_id, key, key_quoted);
_c4dbgpf("start_seq: id={} name='{}'", m_state->node_id, m_tree->key(m_state->node_id));
_write_key_anchor(m_state->node_id);
if( ! m_key_tag.empty())
{
_c4dbgpf("start_seq[{}]: set key tag='{}' -> '{}'", m_state->node_id, m_key_tag, normalize_tag(m_key_tag));
m_tree->set_key_tag(m_state->node_id, normalize_tag(m_key_tag));
m_key_tag.clear();
}
}
else
{
type_bits as_doc = 0;
_RYML_CB_ASSERT(m_stack.m_callbacks, !m_tree->is_doc(m_state->node_id));
m_tree->to_seq(m_state->node_id, as_doc);
_c4dbgpf("start_seq: id={}{}", m_state->node_id, as_doc ? " as doc" : "");
}
_write_val_anchor(m_state->node_id);
m_tree->_p(m_state->node_id)->m_val.scalar.str = m_state->line_contents.rem.str;
}
else
{
m_state->node_id = parent_id;
type_bits as_doc = 0;
if(m_tree->is_doc(m_state->node_id))
as_doc |= DOC;
if(!m_tree->is_seq(parent_id))
{
RYML_CHECK(!m_tree->has_children(parent_id));
m_tree->to_seq(parent_id, as_doc);
}
else
{
m_tree->_add_flags(parent_id, as_doc);
}
_move_scalar_from_top();
_c4dbgpf("start_seq: id={}{}", m_state->node_id, as_doc ? " as_doc" : "");
_write_val_anchor(parent_id);
m_tree->_p(parent_id)->m_val.scalar.str = m_state->line_contents.rem.str;
}
if( ! m_val_tag.empty())
{
_c4dbgpf("start_seq[{}]: set val tag='{}' -> '{}'", m_state->node_id, m_val_tag, normalize_tag(m_val_tag));
m_tree->set_val_tag(m_state->node_id, normalize_tag(m_val_tag));
m_val_tag.clear();
}
}
void Parser::_stop_seq()
{
_c4dbgp("stop_seq");
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_seq(m_state->node_id));
}
//-----------------------------------------------------------------------------
void Parser::_start_seqimap()
{
_c4dbgpf("start_seqimap at node={}. has_children={}", m_state->node_id, m_tree->has_children(m_state->node_id));
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RSEQ|FLOW));
// create a map, and turn the last scalar of this sequence
// into the key of the map's first child. This scalar was
// understood to be a value in the sequence, but it is
// actually a key of a map, implicitly opened here.
// Eg [val, key: val]
//
// Yep, YAML is crazy.
if(m_tree->has_children(m_state->node_id) && m_tree->has_val(m_tree->last_child(m_state->node_id)))
{
size_t prev = m_tree->last_child(m_state->node_id);
NodeType ty = m_tree->_p(prev)->m_type; // don't use type() because it masks out the quotes
NodeScalar tmp = m_tree->valsc(prev);
_c4dbgpf("has children and last child={} has val. saving the scalars, val='{}' quoted={}", prev, tmp.scalar, ty.is_val_quoted());
m_tree->remove(prev);
_push_level();
_start_map();
_store_scalar(tmp.scalar, ty.is_val_quoted());
m_key_anchor = tmp.anchor;
m_key_tag = tmp.tag;
}
else
{
_c4dbgpf("node {} has no children yet, using empty key", m_state->node_id);
_push_level();
_start_map();
_store_scalar_null(m_state->line_contents.rem.str);
}
add_flags(RSEQIMAP|FLOW);
}
void Parser::_stop_seqimap()
{
_c4dbgp("stop_seqimap");
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RSEQIMAP));
}
//-----------------------------------------------------------------------------
NodeData* Parser::_append_val(csubstr val, flag_t quoted)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, ! has_all(SSCL));
_RYML_CB_ASSERT(m_stack.m_callbacks, node(m_state) != nullptr);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_seq(m_state->node_id));
type_bits additional_flags = quoted ? VALQUO : NOTYPE;
_c4dbgpf("append val: '{}' to parent id={} (level={}){}", val, m_state->node_id, m_state->level, quoted ? " VALQUO!" : "");
size_t nid = m_tree->append_child(m_state->node_id);
m_tree->to_val(nid, val, additional_flags);
_c4dbgpf("append val: id={} val='{}'", nid, m_tree->get(nid)->m_val.scalar);
if( ! m_val_tag.empty())
{
_c4dbgpf("append val[{}]: set val tag='{}' -> '{}'", nid, m_val_tag, normalize_tag(m_val_tag));
m_tree->set_val_tag(nid, normalize_tag(m_val_tag));
m_val_tag.clear();
}
_write_val_anchor(nid);
return m_tree->get(nid);
}
NodeData* Parser::_append_key_val(csubstr val, flag_t val_quoted)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_map(m_state->node_id));
type_bits additional_flags = 0;
if(m_state->flags & QSCL)
additional_flags |= KEYQUO;
if(val_quoted)
additional_flags |= VALQUO;
csubstr key = _consume_scalar();
_c4dbgpf("append keyval: '{}' '{}' to parent id={} (level={}){}{}", key, val, m_state->node_id, m_state->level, (additional_flags & KEYQUO) ? " KEYQUO!" : "", (additional_flags & VALQUO) ? " VALQUO!" : "");
size_t nid = m_tree->append_child(m_state->node_id);
m_tree->to_keyval(nid, key, val, additional_flags);
_c4dbgpf("append keyval: id={} key='{}' val='{}'", nid, m_tree->key(nid), m_tree->val(nid));
if( ! m_key_tag.empty())
{
_c4dbgpf("append keyval[{}]: set key tag='{}' -> '{}'", nid, m_key_tag, normalize_tag(m_key_tag));
m_tree->set_key_tag(nid, normalize_tag(m_key_tag));
m_key_tag.clear();
}
if( ! m_val_tag.empty())
{
_c4dbgpf("append keyval[{}]: set val tag='{}' -> '{}'", nid, m_val_tag, normalize_tag(m_val_tag));
m_tree->set_val_tag(nid, normalize_tag(m_val_tag));
m_val_tag.clear();
}
_write_key_anchor(nid);
_write_val_anchor(nid);
rem_flags(QMRK);
return m_tree->get(nid);
}
//-----------------------------------------------------------------------------
void Parser::_store_scalar(csubstr s, flag_t is_quoted)
{
_c4dbgpf("state[{}]: storing scalar '{}' (flag: {}) (old scalar='{}')",
m_state-m_stack.begin(), s, m_state->flags & SSCL, m_state->scalar);
RYML_CHECK(has_none(SSCL));
add_flags(SSCL | (is_quoted * QSCL));
m_state->scalar = s;
}
csubstr Parser::_consume_scalar()
{
_c4dbgpf("state[{}]: consuming scalar '{}' (flag: {}))", m_state-m_stack.begin(), m_state->scalar, m_state->flags & SSCL);
RYML_CHECK(m_state->flags & SSCL);
csubstr s = m_state->scalar;
rem_flags(SSCL | QSCL);
m_state->scalar.clear();
return s;
}
void Parser::_move_scalar_from_top()
{
if(m_stack.size() < 2) return;
State &prev = m_stack.top(1);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state == &m_stack.top());
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state != &prev);
if(prev.flags & SSCL)
{
_c4dbgpf("moving scalar '{}' from state[{}] to state[{}] (overwriting '{}')", prev.scalar, &prev-m_stack.begin(), m_state-m_stack.begin(), m_state->scalar);
add_flags(prev.flags & (SSCL | QSCL));
m_state->scalar = prev.scalar;
rem_flags(SSCL | QSCL, &prev);
prev.scalar.clear();
}
}
//-----------------------------------------------------------------------------
/** @todo this function is a monster and needs love. */
bool Parser::_handle_indentation()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(FLOW));
if( ! _at_line_begin())
return false;
size_t ind = m_state->line_contents.indentation;
csubstr rem = m_state->line_contents.rem;
/** @todo instead of trimming, we should use the indentation index from above */
csubstr remt = rem.triml(' ');
if(remt.empty() || remt.begins_with('#')) // this is a blank or comment line
{
_line_progressed(rem.size());
return true;
}
_c4dbgpf("indentation? ind={} indref={}", ind, m_state->indref);
if(ind == m_state->indref)
{
if(has_all(SSCL|RVAL) && ! rem.sub(ind).begins_with('-'))
{
if(has_all(RMAP))
{
_append_key_val_null(rem.str + ind - 1);
addrem_flags(RKEY, RVAL);
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
else if(has_all(RSEQ))
{
_append_val(_consume_scalar());
addrem_flags(RNXT, RVAL);
}
else
{
_c4err("internal error");
}
#endif
}
else if(has_all(RSEQ|RNXT) && ! rem.sub(ind).begins_with('-'))
{
if(m_stack.size() > 2) // do not pop to root level
{
_c4dbgp("end the indentless seq");
_pop_level();
return true;
}
}
else
{
_c4dbgpf("same indentation ({}) -- nothing to see here", ind);
}
_line_progressed(ind);
return ind > 0;
}
else if(ind < m_state->indref)
{
_c4dbgpf("smaller indentation ({} < {})!!!", ind, m_state->indref);
if(has_all(RVAL))
{
_c4dbgp("there was an empty val -- appending");
if(has_all(RMAP))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_all(SSCL));
_append_key_val_null(rem.sub(ind).str - 1);
}
else if(has_all(RSEQ))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, has_none(SSCL));
_append_val_null(rem.sub(ind).str - 1);
}
}
// search the stack frame to jump to based on its indentation
State const* popto = nullptr;
_RYML_CB_ASSERT(m_stack.m_callbacks, m_stack.is_contiguous()); // this search relies on the stack being contiguous
for(State const* s = m_state-1; s >= m_stack.begin(); --s)
{
_c4dbgpf("searching for state with indentation {}. curr={} (level={},node={})", ind, s->indref, s->level, s->node_id);
if(s->indref == ind)
{
_c4dbgpf("gotit!!! level={} node={}", s->level, s->node_id);
popto = s;
// while it may be tempting to think we're done at this
// point, we must still determine whether we're jumping to a
// parent with the same indentation. Consider this case with
// an indentless sequence:
//
// product:
// - sku: BL394D
// quantity: 4
// description: Basketball
// price: 450.00
// - sku: BL4438H
// quantity: 1
// description: Super Hoop
// price: 2392.00 # jumping one level here would be wrong.
// tax: 1234.5 # we must jump two levels
if(popto > m_stack.begin())
{
auto parent = popto - 1;
if(parent->indref == popto->indref)
{
_c4dbgpf("the parent (level={},node={}) has the same indentation ({}). is this in an indentless sequence?", parent->level, parent->node_id, popto->indref);
_c4dbgpf("isseq(popto)={} ismap(parent)={}", m_tree->is_seq(popto->node_id), m_tree->is_map(parent->node_id));
if(m_tree->is_seq(popto->node_id) && m_tree->is_map(parent->node_id))
{
if( ! remt.begins_with('-'))
{
_c4dbgp("this is an indentless sequence");
popto = parent;
}
else
{
_c4dbgp("not an indentless sequence");
}
}
}
}
break;
}
}
if(!popto || popto >= m_state || popto->level >= m_state->level)
{
_c4err("parse error: incorrect indentation?");
}
_c4dbgpf("popping {} levels: from level {} to level {}", m_state->level-popto->level, m_state->level, popto->level);
while(m_state != popto)
{
_c4dbgpf("popping level {} (indentation={})", m_state->level, m_state->indref);
_pop_level();
}
_RYML_CB_ASSERT(m_stack.m_callbacks, ind == m_state->indref);
_line_progressed(ind);
return true;
}
else
{
_c4dbgpf("larger indentation ({} > {})!!!", ind, m_state->indref);
_RYML_CB_ASSERT(m_stack.m_callbacks, ind > m_state->indref);
if(has_all(RMAP|RVAL))
{
if(_is_scalar_next__rmap_val(remt) && remt.first_of(":?") == npos)
{
_c4dbgpf("actually it seems a value: '{}'", remt);
}
else
{
addrem_flags(RKEY, RVAL);
_start_unk();
//_move_scalar_from_top();
_line_progressed(ind);
_save_indentation();
return true;
}
}
else if(has_all(RSEQ|RVAL))
{
// nothing to do here
}
else
{
_c4err("parse error - indentation should not increase at this point");
}
}
return false;
}
//-----------------------------------------------------------------------------
csubstr Parser::_scan_comment()
{
csubstr s = m_state->line_contents.rem;
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begins_with('#'));
_line_progressed(s.len);
// skip the # character
s = s.sub(1);
// skip leading whitespace
s = s.right_of(s.first_not_of(' '), /*include_pos*/true);
_c4dbgpf("comment was '{}'", s);
return s;
}
//-----------------------------------------------------------------------------
csubstr Parser::_scan_squot_scalar()
{
// quoted scalars can spread over multiple lines!
// nice explanation here: http://yaml-multiline.info/
// a span to the end of the file
size_t b = m_state->pos.offset;
substr s = m_buf.sub(b);
if(s.begins_with(' '))
{
s = s.triml(' ');
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.sub(b).is_super(s));
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begin() >= m_buf.sub(b).begin());
_line_progressed((size_t)(s.begin() - m_buf.sub(b).begin()));
}
b = m_state->pos.offset; // take this into account
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begins_with('\''));
// skip the opening quote
_line_progressed(1);
s = s.sub(1);
bool needs_filter = false;
size_t numlines = 1; // we already have one line
size_t pos = npos; // find the pos of the matching quote
while( ! _finished_file())
{
const csubstr line = m_state->line_contents.rem;
bool line_is_blank = true;
_c4dbgpf("scanning single quoted scalar @ line[{}]: ~~~{}~~~", m_state->pos.line, line);
for(size_t i = 0; i < line.len; ++i)
{
const char curr = line.str[i];
if(curr == '\'') // single quotes are escaped with two single quotes
{
const char next = i+1 < line.len ? line.str[i+1] : '~';
if(next != '\'') // so just look for the first quote
{ // without another after it
pos = i;
break;
}
else
{
needs_filter = true; // needs filter to remove escaped quotes
++i; // skip the escaped quote
}
}
else if(curr != ' ')
{
line_is_blank = false;
}
}
// leading whitespace also needs filtering
needs_filter = needs_filter
|| numlines > 1
|| line_is_blank
|| (_at_line_begin() && line.begins_with(' '))
|| (m_state->line_contents.full.last_of('\r') != csubstr::npos);
if(pos == npos)
{
_line_progressed(line.len);
++numlines;
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, pos >= 0 && pos < m_buf.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf[m_state->pos.offset + pos] == '\'');
_line_progressed(pos + 1); // progress beyond the quote
pos = m_state->pos.offset - b - 1; // but we stop before it
break;
}
_line_ended();
_scan_line();
}
if(pos == npos)
{
_c4err("reached end of file while looking for closing quote");
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, pos > 0);
_RYML_CB_ASSERT(m_stack.m_callbacks, s.end() >= m_buf.begin() && s.end() <= m_buf.end());
_RYML_CB_ASSERT(m_stack.m_callbacks, s.end() == m_buf.end() || *s.end() == '\'');
s = s.sub(0, pos-1);
}
if(needs_filter)
{
csubstr ret = _filter_squot_scalar(s);
_RYML_CB_ASSERT(m_stack.m_callbacks, ret.len <= s.len || s.empty() || s.trim(' ').empty());
_c4dbgpf("final scalar: \"{}\"", ret);
return ret;
}
_c4dbgpf("final scalar: \"{}\"", s);
return s;
}
//-----------------------------------------------------------------------------
csubstr Parser::_scan_dquot_scalar()
{
// quoted scalars can spread over multiple lines!
// nice explanation here: http://yaml-multiline.info/
// a span to the end of the file
size_t b = m_state->pos.offset;
substr s = m_buf.sub(b);
if(s.begins_with(' '))
{
s = s.triml(' ');
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.sub(b).is_super(s));
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begin() >= m_buf.sub(b).begin());
_line_progressed((size_t)(s.begin() - m_buf.sub(b).begin()));
}
b = m_state->pos.offset; // take this into account
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begins_with('"'));
// skip the opening quote
_line_progressed(1);
s = s.sub(1);
bool needs_filter = false;
size_t numlines = 1; // we already have one line
size_t pos = npos; // find the pos of the matching quote
while( ! _finished_file())
{
const csubstr line = m_state->line_contents.rem;
bool line_is_blank = true;
_c4dbgpf("scanning double quoted scalar @ line[{}]: line='{}'", m_state->pos.line, line);
for(size_t i = 0; i < line.len; ++i)
{
const char curr = line.str[i];
if(curr != ' ')
line_is_blank = false;
// every \ is an escape
if(curr == '\\')
{
const char next = i+1 < line.len ? line.str[i+1] : '~';
needs_filter = true;
if(next == '"' || next == '\\')
++i;
}
else if(curr == '"')
{
pos = i;
break;
}
}
// leading whitespace also needs filtering
needs_filter = needs_filter
|| numlines > 1
|| line_is_blank
|| (_at_line_begin() && line.begins_with(' '))
|| (m_state->line_contents.full.last_of('\r') != csubstr::npos);
if(pos == npos)
{
_line_progressed(line.len);
++numlines;
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, pos >= 0 && pos < m_buf.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf[m_state->pos.offset + pos] == '"');
_line_progressed(pos + 1); // progress beyond the quote
pos = m_state->pos.offset - b - 1; // but we stop before it
break;
}
_line_ended();
_scan_line();
}
if(pos == npos)
{
_c4err("reached end of file looking for closing quote");
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, pos > 0);
_RYML_CB_ASSERT(m_stack.m_callbacks, s.end() == m_buf.end() || *s.end() == '"');
_RYML_CB_ASSERT(m_stack.m_callbacks, s.end() >= m_buf.begin() && s.end() <= m_buf.end());
s = s.sub(0, pos-1);
}
if(needs_filter)
{
csubstr ret = _filter_dquot_scalar(s);
_c4dbgpf("final scalar: [{}]\"{}\"", ret.len, ret);
_RYML_CB_ASSERT(m_stack.m_callbacks, ret.len <= s.len || s.empty() || s.trim(' ').empty());
return ret;
}
_c4dbgpf("final scalar: \"{}\"", s);
return s;
}
//-----------------------------------------------------------------------------
csubstr Parser::_scan_block()
{
// nice explanation here: http://yaml-multiline.info/
csubstr s = m_state->line_contents.rem;
csubstr trimmed = s.triml(' ');
if(trimmed.str > s.str)
{
_c4dbgp("skipping whitespace");
_RYML_CB_ASSERT(m_stack.m_callbacks, trimmed.str >= s.str);
_line_progressed(static_cast<size_t>(trimmed.str - s.str));
s = trimmed;
}
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begins_with('|') || s.begins_with('>'));
_c4dbgpf("scanning block: specs=\"{}\"", s);
// parse the spec
BlockStyle_e newline = s.begins_with('>') ? BLOCK_FOLD : BLOCK_LITERAL;
BlockChomp_e chomp = CHOMP_CLIP; // default to clip unless + or - are used
size_t indentation = npos; // have to find out if no spec is given
csubstr digits;
if(s.len > 1)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, s.begins_with_any("|>"));
csubstr t = s.sub(1);
_c4dbgpf("scanning block: spec is multichar: '{}'", t);
_RYML_CB_ASSERT(m_stack.m_callbacks, t.len >= 1);
size_t pos = t.first_of("-+");
_c4dbgpf("scanning block: spec chomp char at {}", pos);
if(pos != npos)
{
if(t[pos] == '-')
chomp = CHOMP_STRIP;
else if(t[pos] == '+')
chomp = CHOMP_KEEP;
if(pos == 0)
t = t.sub(1);
else
t = t.first(pos);
}
// from here to the end, only digits are considered
digits = t.left_of(t.first_not_of("0123456789"));
if( ! digits.empty())
{
if( ! c4::atou(digits, &indentation))
_c4err("parse error: could not read decimal");
_c4dbgpf("scanning block: indentation specified: {}. add {} from curr state -> {}", indentation, m_state->indref, indentation+m_state->indref);
indentation += m_state->indref;
}
}
// finish the current line
_line_progressed(s.len);
_line_ended();
_scan_line();
_c4dbgpf("scanning block: style={} chomp={} indentation={}", newline==BLOCK_FOLD ? "fold" : "literal",
chomp==CHOMP_CLIP ? "clip" : (chomp==CHOMP_STRIP ? "strip" : "keep"), indentation);
// start with a zero-length block, already pointing at the right place
substr raw_block(m_buf.data() + m_state->pos.offset, size_t(0));// m_state->line_contents.full.sub(0, 0);
_RYML_CB_ASSERT(m_stack.m_callbacks, raw_block.begin() == m_state->line_contents.full.begin());
// read every full line into a raw block,
// from which newlines are to be stripped as needed.
//
// If no explicit indentation was given, pick it from the first
// non-empty line. See
// https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator
size_t num_lines = 0, first = m_state->pos.line, provisional_indentation = npos;
LineContents lc;
while(( ! _finished_file()))
{
// peek next line, but do not advance immediately
lc.reset_with_next_line(m_buf, m_state->pos.offset);
_c4dbgpf("scanning block: peeking at '{}'", lc.stripped);
// evaluate termination conditions
if(indentation != npos)
{
// stop when the line is deindented and not empty
if(lc.indentation < indentation && ( ! lc.rem.trim(" \t\r\n").empty()))
{
_c4dbgpf("scanning block: indentation decreased ref={} thisline={}", indentation, lc.indentation);
break;
}
else if(indentation == 0)
{
if((lc.rem == "..." || lc.rem.begins_with("... "))
||
(lc.rem == "---" || lc.rem.begins_with("--- ")))
{
_c4dbgp("scanning block: stop. indentation=0 and stream ended");
break;
}
}
}
else
{
_c4dbgpf("scanning block: indentation ref not set. firstnonws={}", lc.stripped.first_not_of(' '));
if(lc.stripped.first_not_of(' ') != npos) // non-empty line
{
_c4dbgpf("scanning block: line not empty. indref={} indprov={} indentation={}", m_state->indref, provisional_indentation, lc.indentation);
if(provisional_indentation == npos)
{
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
if(lc.indentation < m_state->indref)
{
_c4dbgpf("scanning block: block terminated indentation={} < indref={}", lc.indentation, m_state->indref);
break;
}
else
#endif
if(lc.indentation == m_state->indref)
{
if(has_any(RSEQ|RMAP))
{
_c4dbgpf("scanning block: block terminated. reading container and indentation={}==indref={}", lc.indentation, m_state->indref);
break;
}
}
_c4dbgpf("scanning block: set indentation ref from this line: ref={}", lc.indentation);
indentation = lc.indentation;
}
else
{
if(lc.indentation >= provisional_indentation)
{
_c4dbgpf("scanning block: set indentation ref from provisional indentation: provisional_ref={}, thisline={}", provisional_indentation, lc.indentation);
//indentation = provisional_indentation ? provisional_indentation : lc.indentation;
indentation = lc.indentation;
}
else
{
break;
//_c4err("parse error: first non-empty block line should have at least the original indentation");
}
}
}
else // empty line
{
_c4dbgpf("scanning block: line empty or {} spaces. line_indentation={} prov_indentation={}", lc.stripped.len, lc.indentation, provisional_indentation);
if(provisional_indentation != npos)
{
if(lc.stripped.len >= provisional_indentation)
{
_c4dbgpf("scanning block: increase provisional_ref {} -> {}", provisional_indentation, lc.stripped.len);
provisional_indentation = lc.stripped.len;
}
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
else if(lc.indentation >= provisional_indentation && lc.indentation != npos)
{
_c4dbgpf("scanning block: increase provisional_ref {} -> {}", provisional_indentation, lc.indentation);
provisional_indentation = lc.indentation;
}
#endif
}
else
{
provisional_indentation = lc.indentation ? lc.indentation : has_any(RSEQ|RVAL);
_c4dbgpf("scanning block: initialize provisional_ref={}", provisional_indentation);
if(provisional_indentation == npos)
{
provisional_indentation = lc.stripped.len ? lc.stripped.len : has_any(RSEQ|RVAL);
_c4dbgpf("scanning block: initialize provisional_ref={}", provisional_indentation);
}
}
}
}
// advance now that we know the folded scalar continues
m_state->line_contents = lc;
_c4dbgpf("scanning block: append '{}'", m_state->line_contents.rem);
raw_block.len += m_state->line_contents.full.len;
_line_progressed(m_state->line_contents.rem.len);
_line_ended();
++num_lines;
}
_RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.line == (first + num_lines));
C4_UNUSED(num_lines);
C4_UNUSED(first);
if(indentation == npos)
{
_c4dbgpf("scanning block: set indentation from provisional: {}", provisional_indentation);
indentation = provisional_indentation;
}
if(num_lines)
_line_ended_undo();
_c4dbgpf("scanning block: raw=~~~{}~~~", raw_block);
// ok! now we strip the newlines and spaces according to the specs
s = _filter_block_scalar(raw_block, newline, chomp, indentation);
_c4dbgpf("scanning block: final=~~~{}~~~", s);
return s;
}
//-----------------------------------------------------------------------------
template<bool backslash_is_escape, bool keep_trailing_whitespace>
bool Parser::_filter_nl(substr r, size_t *C4_RESTRICT i, size_t *C4_RESTRICT pos, size_t indentation)
{
// a debugging scaffold:
#if 0
#define _c4dbgfnl(fmt, ...) _c4dbgpf("filter_nl[{}]: " fmt, *i, __VA_ARGS__)
#else
#define _c4dbgfnl(...)
#endif
const char curr = r[*i];
bool replaced = false;
_RYML_CB_ASSERT(m_stack.m_callbacks, indentation != npos);
_RYML_CB_ASSERT(m_stack.m_callbacks, curr == '\n');
_c4dbgfnl("found newline. sofar=[{}]~~~{}~~~", *pos, m_filter_arena.first(*pos));
size_t ii = *i;
size_t numnl_following = count_following_newlines(r, &ii, indentation);
if(numnl_following)
{
_c4dbgfnl("{} consecutive (empty) lines {} in the middle. totalws={}", 1+numnl_following, ii < r.len ? "in the middle" : "at the end", ii - *i);
for(size_t j = 0; j < numnl_following; ++j)
m_filter_arena.str[(*pos)++] = '\n';
}
else
{
if(r.first_not_of(" \t", *i+1) != npos)
{
m_filter_arena.str[(*pos)++] = ' ';
_c4dbgfnl("single newline. convert to space. ii={}/{}. sofar=[{}]~~~{}~~~", ii, r.len, *pos, m_filter_arena.first(*pos));
replaced = true;
}
else
{
if C4_IF_CONSTEXPR (keep_trailing_whitespace)
{
m_filter_arena.str[(*pos)++] = ' ';
_c4dbgfnl("single newline. convert to space. ii={}/{}. sofar=[{}]~~~{}~~~", ii, r.len, *pos, m_filter_arena.first(*pos));
replaced = true;
}
else
{
_c4dbgfnl("last newline, everything else is whitespace. ii={}/{}", ii, r.len);
*i = r.len;
}
}
if C4_IF_CONSTEXPR (backslash_is_escape)
{
if(ii < r.len && r.str[ii] == '\\')
{
const char next = ii+1 < r.len ? r.str[ii+1] : '\0';
if(next == ' ' || next == '\t')
{
_c4dbgfnl("extend skip to backslash{}", "");
++ii;
}
}
}
}
*i = ii - 1; // correct for the loop increment
#undef _c4dbgfnl
return replaced;
}
//-----------------------------------------------------------------------------
template<bool keep_trailing_whitespace>
void Parser::_filter_ws(substr r, size_t *C4_RESTRICT i, size_t *C4_RESTRICT pos)
{
// a debugging scaffold:
#if 0
#define _c4dbgfws(fmt, ...) _c4dbgpf("filt_nl[{}]: " fmt, *i, __VA_ARGS__)
#else
#define _c4dbgfws(...)
#endif
const char curr = r[*i];
_c4dbgfws("found whitespace '{}'", _c4prc(curr));
_RYML_CB_ASSERT(m_stack.m_callbacks, curr == ' ' || curr == '\t');
size_t first = *i > 0 ? r.first_not_of(" \t", *i) : r.first_not_of(' ', *i);
if(first != npos)
{
if(r[first] == '\n' || r[first] == '\r') // skip trailing whitespace
{
_c4dbgfws("whitespace is trailing on line. firstnonws='{}'@{}", _c4prc(r[first]), first);
*i = first - 1; // correct for the loop increment
}
else // a legit whitespace
{
m_filter_arena.str[(*pos)++] = curr;
_c4dbgfws("legit whitespace. sofar=[{}]~~~{}~~~", *pos, m_filter_arena.first(*pos));
}
}
else
{
_c4dbgfws("... everything else is trailing whitespace{}", "");
if C4_IF_CONSTEXPR (keep_trailing_whitespace)
for(size_t j = *i; j < r.len; ++j)
m_filter_arena.str[(*pos)++] = r[j];
*i = r.len;
}
#undef _c4dbgfws
}
//-----------------------------------------------------------------------------
csubstr Parser::_filter_plain_scalar(substr s, size_t indentation)
{
// a debugging scaffold:
#if 0
#define _c4dbgfps(...) _c4dbgpf("filt_plain_scalar" __VA_ARGS__)
#else
#define _c4dbgfps(...)
#endif
_c4dbgfps("before=~~~{}~~~", s);
substr r = s.triml(" \t");
_grow_filter_arena(r.len);
size_t pos = 0; // the filtered size
bool filtered_chars = false;
for(size_t i = 0; i < r.len; ++i)
{
const char curr = r.str[i];
_c4dbgfps("[{}]: '{}'", i, _c4prc(curr));
if(curr == ' ' || curr == '\t')
{
_filter_ws</*keep_trailing_ws*/false>(r, &i, &pos);
}
else if(curr == '\n')
{
filtered_chars = _filter_nl</*backslash_is_escape*/false, /*keep_trailing_ws*/false>(r, &i, &pos, indentation);
}
else if(curr == '\r') // skip \r --- https://stackoverflow.com/questions/1885900
{
;
}
else
{
m_filter_arena.str[pos++] = r[i];
}
}
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= m_filter_arena.len);
if(pos < r.len || filtered_chars)
{
r = _finish_filter_arena(r, pos);
}
_RYML_CB_ASSERT(m_stack.m_callbacks, s.len >= r.len);
_c4dbgfps("#filteredchars={} after=~~~{}~~~", s.len - r.len, r);
#undef _c4dbgfps
return r;
}
//-----------------------------------------------------------------------------
csubstr Parser::_filter_squot_scalar(substr s)
{
// a debugging scaffold:
#if 0
#define _c4dbgfsq(...) _c4dbgpf("filt_squo_scalar")
#else
#define _c4dbgfsq(...)
#endif
// from the YAML spec for double-quoted scalars:
// https://yaml.org/spec/1.2-old/spec.html#style/flow/single-quoted
_c4dbgfsq(": before=~~~{}~~~", s);
_grow_filter_arena(s.len);
substr r = s;
size_t pos = 0; // the filtered size
bool filtered_chars = false;
for(size_t i = 0; i < r.len; ++i)
{
const char curr = r[i];
_c4dbgfsq("[{}]: '{}'", i, _c4prc(curr));
if(curr == ' ' || curr == '\t')
{
_filter_ws</*keep_trailing_ws*/true>(r, &i, &pos);
}
else if(curr == '\n')
{
filtered_chars = _filter_nl</*backslash_is_escape*/false, /*keep_trailing_ws*/true>(r, &i, &pos, /*indentation*/0);
}
else if(curr == '\r') // skip \r --- https://stackoverflow.com/questions/1885900
{
;
}
else if(curr == '\'')
{
char next = i+1 < r.len ? r[i+1] : '\0';
if(next == '\'')
{
_c4dbgfsq("[{}]: two consecutive quotes", i);
filtered_chars = true;
m_filter_arena.str[pos++] = '\'';
++i;
}
}
else
{
m_filter_arena.str[pos++] = curr;
}
}
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= m_filter_arena.len);
if(pos < r.len || filtered_chars)
{
r = _finish_filter_arena(r, pos);
}
_RYML_CB_ASSERT(m_stack.m_callbacks, s.len >= r.len);
_c4dbgpf(": #filteredchars={} after=~~~{}~~~", s.len - r.len, r);
#undef _c4dbgfsq
return r;
}
//-----------------------------------------------------------------------------
csubstr Parser::_filter_dquot_scalar(substr s)
{
// a debugging scaffold:
#if 0
#define _c4dbgfdq(...) _c4dbgpf("filt_dquo_scalar" __VA_ARGS__)
#else
#define _c4dbgfdq(...)
#endif
_c4dbgfdq(": before=~~~{}~~~", s);
// from the YAML spec for double-quoted scalars:
// https://yaml.org/spec/1.2-old/spec.html#style/flow/double-quoted
//
// All leading and trailing white space characters are excluded
// from the content. Each continuation line must therefore contain
// at least one non-space character. Empty lines, if any, are
// consumed as part of the line folding.
_grow_filter_arena(s.len + 2u * s.count('\\'));
substr r = s;
size_t pos = 0; // the filtered size
bool filtered_chars = false;
for(size_t i = 0; i < r.len; ++i)
{
const char curr = r[i];
_c4dbgfdq("[{}]: '{}'", i, _c4prc(curr));
if(curr == ' ' || curr == '\t')
{
_filter_ws</*keep_trailing_ws*/true>(r, &i, &pos);
}
else if(curr == '\n')
{
filtered_chars = _filter_nl</*backslash_is_escape*/true, /*keep_trailing_ws*/true>(r, &i, &pos, /*indentation*/0);
}
else if(curr == '\r') // skip \r --- https://stackoverflow.com/questions/1885900
{
;
}
else if(curr == '\\')
{
char next = i+1 < r.len ? r[i+1] : '\0';
_c4dbgfdq("[{}]: backslash, next='{}'", i, _c4prc(next));
filtered_chars = true;
if(next == '\r')
{
if(i+2 < r.len && r[i+2] == '\n')
{
++i; // newline escaped with \ -- skip both (add only one as i is loop-incremented)
next = '\n';
_c4dbgfdq("[{}]: was \\r\\n, now next='\\n'", i);
}
}
// remember the loop will also increment i
if(next == '\n')
{
size_t ii = i + 2;
for( ; ii < r.len; ++ii)
{
if(r.str[ii] == ' ' || r.str[ii] == '\t') // skip leading whitespace
;
else
break;
}
i += ii - i - 1;
}
else if(next == '"' || next == '/' || next == ' ' || next == '\t') // escapes for json compatibility
{
m_filter_arena.str[pos++] = next;
++i;
}
else if(next == '\r')
{
//++i;
}
else if(next == 'n')
{
m_filter_arena.str[pos++] = '\n';
++i;
}
else if(next == 'r')
{
m_filter_arena.str[pos++] = '\r';
++i; // skip
}
else if(next == 't')
{
m_filter_arena.str[pos++] = '\t';
++i;
}
else if(next == '\\')
{
m_filter_arena.str[pos++] = '\\';
++i;
}
else if(next == 'x') // UTF8
{
if(i + 1u + 2u >= r.len)
_c4err("\\x requires 2 hex digits");
uint8_t byteval = {};
if(!read_hex(r.sub(i + 2u, 2u), &byteval))
_c4err("failed to read \\x codepoint");
m_filter_arena.str[pos++] = *(char*)&byteval;
i += 1u + 2u;
}
else if(next == 'u') // UTF16
{
if(i + 1u + 4u >= r.len)
_c4err("\\u requires 4 hex digits");
char readbuf[8];
csubstr codepoint = r.sub(i + 2u, 4u);
uint32_t codepoint_val = {};
if(!read_hex(codepoint, &codepoint_val))
_c4err("failed to parse \\u codepoint");
size_t numbytes = decode_code_point((uint8_t*)readbuf, sizeof(readbuf), codepoint_val);
C4_ASSERT(numbytes <= 4);
memcpy(m_filter_arena.str + pos, readbuf, numbytes);
pos += numbytes;
i += 1u + 4u;
}
else if(next == 'U') // UTF32
{
if(i + 1u + 8u >= r.len)
_c4err("\\U requires 8 hex digits");
char readbuf[8];
csubstr codepoint = r.sub(i + 2u, 8u);
uint32_t codepoint_val = {};
if(!read_hex(codepoint, &codepoint_val))
_c4err("failed to parse \\U codepoint");
size_t numbytes = decode_code_point((uint8_t*)readbuf, sizeof(readbuf), codepoint_val);
C4_ASSERT(numbytes <= 4);
memcpy(m_filter_arena.str + pos, readbuf, numbytes);
pos += numbytes;
i += 1u + 8u;
}
// https://yaml.org/spec/1.2.2/#rule-c-ns-esc-char
else if(next == '0')
{
m_filter_arena.str[pos++] = '\0';
++i;
}
else if(next == 'b') // backspace
{
m_filter_arena.str[pos++] = '\b';
++i;
}
else if(next == 'f') // form feed
{
m_filter_arena.str[pos++] = '\f';
++i;
}
else if(next == 'a') // bell character
{
m_filter_arena.str[pos++] = '\a';
++i;
}
else if(next == 'v') // vertical tab
{
m_filter_arena.str[pos++] = '\v';
++i;
}
else if(next == 'e') // escape character
{
m_filter_arena.str[pos++] = '\x1b';
++i;
}
else if(next == '_') // unicode non breaking space \u00a0
{
// https://www.compart.com/en/unicode/U+00a0
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x3e, 0xc2);
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x60, 0xa0);
++i;
}
else if(next == 'N') // unicode next line \u0085
{
// https://www.compart.com/en/unicode/U+0085
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x3e, 0xc2);
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x7b, 0x85);
++i;
}
else if(next == 'L') // unicode line separator \u2028
{
// https://www.utf8-chartable.de/unicode-utf8-table.pl?start=8192&number=1024&names=-&utf8=0x&unicodeinhtml=hex
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x1e, 0xe2);
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x80, 0x80);
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x58, 0xa8);
++i;
}
else if(next == 'P') // unicode paragraph separator \u2029
{
// https://www.utf8-chartable.de/unicode-utf8-table.pl?start=8192&number=1024&names=-&utf8=0x&unicodeinhtml=hex
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x1e, 0xe2);
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x80, 0x80);
m_filter_arena.str[pos++] = _RYML_CHCONST(-0x57, 0xa9);
++i;
}
_c4dbgfdq("[{}]: backslash...sofar=[{}]~~~{}~~~", i, pos, m_filter_arena.first(pos));
}
else
{
m_filter_arena.str[pos++] = curr;
}
}
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= m_filter_arena.len);
if(pos < r.len || filtered_chars)
{
r = _finish_filter_arena(r, pos);
}
_RYML_CB_ASSERT(m_stack.m_callbacks, s.len >= r.len);
_c4dbgpf(": #filteredchars={} after=~~~{}~~~", s.len - r.len, r);
#undef _c4dbgfdq
return r;
}
//-----------------------------------------------------------------------------
bool Parser::_apply_chomp(substr buf, size_t *C4_RESTRICT pos, BlockChomp_e chomp)
{
substr trimmed = buf.first(*pos).trimr('\n');
bool added_newline = false;
switch(chomp)
{
case CHOMP_KEEP:
if(trimmed.len == *pos)
{
_c4dbgpf("chomp=KEEP: add missing newline @{}", *pos);
//m_filter_arena.str[(*pos)++] = '\n';
added_newline = true;
}
break;
case CHOMP_CLIP:
if(trimmed.len == *pos)
{
_c4dbgpf("chomp=CLIP: add missing newline @{}", *pos);
m_filter_arena.str[(*pos)++] = '\n';
added_newline = true;
}
else
{
_c4dbgpf("chomp=CLIP: include single trailing newline @{}", trimmed.len+1);
*pos = trimmed.len + 1;
}
break;
case CHOMP_STRIP:
_c4dbgpf("chomp=STRIP: strip {}-{}-{} newlines", *pos, trimmed.len, *pos-trimmed.len);
*pos = trimmed.len;
break;
default:
_c4err("unknown chomp style");
}
return added_newline;
}
//-----------------------------------------------------------------------------
csubstr Parser::_filter_block_scalar(substr s, BlockStyle_e style, BlockChomp_e chomp, size_t indentation)
{
// a debugging scaffold:
#if 0
#define _c4dbgfbl(fmt, ...) _c4dbgpf("filt_block" fmt, __VA_ARGS__)
#else
#define _c4dbgfbl(...)
#endif
_c4dbgfbl(": indentation={} before=[{}]~~~{}~~~", indentation, s.len, s);
if(chomp != CHOMP_KEEP && s.trim(" \n\r\t").len == 0u)
{
_c4dbgp("filt_block: empty scalar");
return s.first(0);
}
substr r = s;
switch(style)
{
case BLOCK_LITERAL:
{
_c4dbgp("filt_block: style=literal");
// trim leading whitespace up to indentation
{
size_t numws = r.first_not_of(' ');
if(numws != npos)
{
if(numws > indentation)
r = r.sub(indentation);
else
r = r.sub(numws);
_c4dbgfbl(": after triml=[{}]~~~{}~~~", r.len, r);
}
else
{
if(chomp != CHOMP_KEEP || r.len == 0)
{
_c4dbgfbl(": all spaces {}, return empty", r.len);
return r.first(0);
}
else
{
r[0] = '\n';
return r.first(1);
}
}
}
_grow_filter_arena(s.len + 2u); // use s.len! because we may need to add a newline at the end, so the leading indentation will allow space for that newline
size_t pos = 0; // the filtered size
for(size_t i = 0; i < r.len; ++i)
{
const char curr = r.str[i];
_c4dbgfbl("[{}]='{}' pos={}", i, _c4prc(curr), pos);
if(curr == '\r')
continue;
m_filter_arena.str[pos++] = curr;
if(curr == '\n')
{
_c4dbgfbl("[{}]: found newline", i);
// skip indentation on the next line
csubstr rem = r.sub(i+1);
size_t first = rem.first_not_of(' ');
if(first != npos)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, first < rem.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, i+1+first < r.len);
_c4dbgfbl("[{}]: {} spaces follow before next nonws character @ [{}]='{}'", i, first, i+1+first, rem.str[first]);
if(first < indentation)
{
_c4dbgfbl("[{}]: skip {}<{} spaces from indentation", i, first, indentation);
i += first;
}
else
{
_c4dbgfbl("[{}]: skip {} spaces from indentation", i, indentation);
i += indentation;
}
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, i+1 <= r.len);
first = rem.len;
_c4dbgfbl("[{}]: {} spaces to the end", i, first);
if(first)
{
if(first < indentation)
{
_c4dbgfbl("[{}]: skip everything", i);
--pos;
break;
}
else
{
_c4dbgfbl("[{}]: skip {} spaces from indentation", i, indentation);
i += indentation;
}
}
else if(i+1 == r.len)
{
if(chomp == CHOMP_STRIP)
--pos;
break;
}
}
}
}
_RYML_CB_ASSERT(m_stack.m_callbacks, s.len >= pos);
_c4dbgfbl(": #filteredchars={} after=~~~{}~~~", s.len - r.len, r);
bool changed = _apply_chomp(m_filter_arena, &pos, chomp);
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= m_filter_arena.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= s.len);
if(pos < r.len || changed)
{
r = _finish_filter_arena(s, pos); // write into s
}
break;
}
case BLOCK_FOLD:
{
_c4dbgp("filt_block: style=fold");
_grow_filter_arena(r.len + 2);
size_t pos = 0; // the filtered size
bool filtered_chars = false;
bool started = false;
bool is_indented = false;
size_t i = r.first_not_of(' ');
_c4dbgfbl(": first non space at {}", i);
if(i > indentation)
{
is_indented = true;
i = indentation;
}
_c4dbgfbl(": start folding at {}, is_indented={}", i, (int)is_indented);
auto on_change_indentation = [&](size_t numnl_following, size_t last_newl, size_t first_non_whitespace){
_c4dbgfbl("[{}]: add 1+{} newlines", i, numnl_following);
for(size_t j = 0; j < 1 + numnl_following; ++j)
m_filter_arena.str[pos++] = '\n';
for(i = last_newl + 1 + indentation; i < first_non_whitespace; ++i)
{
if(r.str[i] == '\r')
continue;
_c4dbgfbl("[{}]: add '{}'", i, _c4prc(r.str[i]));
m_filter_arena.str[pos++] = r.str[i];
}
--i;
};
for( ; i < r.len; ++i)
{
const char curr = r.str[i];
_c4dbgfbl("[{}]='{}'", i, _c4prc(curr));
if(curr == '\n')
{
filtered_chars = true;
// skip indentation on the next line, and advance over the next non-indented blank lines as well
size_t first_non_whitespace;
size_t numnl_following = (size_t)-1;
while(r[i] == '\n')
{
++numnl_following;
csubstr rem = r.sub(i+1);
size_t first = rem.first_not_of(' ');
_c4dbgfbl("[{}]: found newline. first={} rem.len={}", i, first, rem.len);
if(first != npos)
{
first_non_whitespace = first + i+1;
while(first_non_whitespace < r.len && r[first_non_whitespace] == '\r')
++first_non_whitespace;
_RYML_CB_ASSERT(m_stack.m_callbacks, first < rem.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, i+1+first < r.len);
_c4dbgfbl("[{}]: {} spaces follow before next nonws character @ [{}]='{}'", i, first, i+1+first, _c4prc(rem.str[first]));
if(first < indentation)
{
_c4dbgfbl("[{}]: skip {}<{} spaces from indentation", i, first, indentation);
i += first;
}
else
{
_c4dbgfbl("[{}]: skip {} spaces from indentation", i, indentation);
i += indentation;
if(first > indentation)
{
_c4dbgfbl("[{}]: {} further indented than {}, stop newlining", i, first, indentation);
goto finished_counting_newlines;
}
}
// prepare the next while loop iteration
// by setting i at the next newline after
// an empty line
if(r[first_non_whitespace] == '\n')
i = first_non_whitespace;
else
goto finished_counting_newlines;
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, i+1 <= r.len);
first = rem.len;
first_non_whitespace = first + i+1;
if(first)
{
_c4dbgfbl("[{}]: {} spaces to the end", i, first);
if(first < indentation)
{
_c4dbgfbl("[{}]: skip everything", i);
i += first;
}
else
{
_c4dbgfbl("[{}]: skip {} spaces from indentation", i, indentation);
i += indentation;
if(first > indentation)
{
_c4dbgfbl("[{}]: {} spaces missing. not done yet", i, indentation - first);
goto finished_counting_newlines;
}
}
}
else // if(i+1 == r.len)
{
_c4dbgfbl("[{}]: it's the final newline", i);
_RYML_CB_ASSERT(m_stack.m_callbacks, i+1 == r.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, rem.len == 0);
}
goto end_of_scalar;
}
}
end_of_scalar:
// Write all the trailing newlines. Since we're
// at the end no folding is needed, so write every
// newline (add 1).
_c4dbgfbl("[{}]: add {} trailing newlines", i, 1+numnl_following);
for(size_t j = 0; j < 1 + numnl_following; ++j)
m_filter_arena.str[pos++] = '\n';
break;
finished_counting_newlines:
_c4dbgfbl("[{}]: #newlines={} firstnonws={}", i, numnl_following, first_non_whitespace);
while(first_non_whitespace < r.len && r[first_non_whitespace] == '\t')
++first_non_whitespace;
_c4dbgfbl("[{}]: #newlines={} firstnonws={}", i, numnl_following, first_non_whitespace);
_RYML_CB_ASSERT(m_stack.m_callbacks, first_non_whitespace <= r.len);
size_t last_newl = r.last_of('\n', first_non_whitespace);
size_t this_indentation = first_non_whitespace - last_newl - 1;
_c4dbgfbl("[{}]: #newlines={} firstnonws={} lastnewl={} this_indentation={} vs indentation={}", i, numnl_following, first_non_whitespace, last_newl, this_indentation, indentation);
_RYML_CB_ASSERT(m_stack.m_callbacks, first_non_whitespace >= last_newl + 1);
_RYML_CB_ASSERT(m_stack.m_callbacks, this_indentation >= indentation);
if(!started)
{
_c4dbgfbl("[{}]: #newlines={}. write all leading newlines", i, numnl_following);
for(size_t j = 0; j < 1 + numnl_following; ++j)
m_filter_arena.str[pos++] = '\n';
if(this_indentation > indentation)
{
is_indented = true;
_c4dbgfbl("[{}]: advance ->{}", i, last_newl + indentation);
i = last_newl + indentation;
}
else
{
i = first_non_whitespace - 1;
_c4dbgfbl("[{}]: advance ->{}", i, first_non_whitespace);
}
}
else if(this_indentation == indentation)
{
_c4dbgfbl("[{}]: same indentation", i);
if(!is_indented)
{
if(numnl_following == 0)
{
_c4dbgfbl("[{}]: fold!", i);
m_filter_arena.str[pos++] = ' ';
}
else
{
_c4dbgfbl("[{}]: add {} newlines", i, 1 + numnl_following);
for(size_t j = 0; j < numnl_following; ++j)
m_filter_arena.str[pos++] = '\n';
}
i = first_non_whitespace - 1;
_c4dbgfbl("[{}]: advance {}->{}", i, i, first_non_whitespace);
}
else
{
_c4dbgfbl("[{}]: back to ref indentation", i);
is_indented = false;
on_change_indentation(numnl_following, last_newl, first_non_whitespace);
_c4dbgfbl("[{}]: advance {}->{}", i, i, first_non_whitespace);
}
}
else
{
_c4dbgfbl("[{}]: increased indentation.", i);
is_indented = true;
_RYML_CB_ASSERT(m_stack.m_callbacks, this_indentation > indentation);
on_change_indentation(numnl_following, last_newl, first_non_whitespace);
_c4dbgfbl("[{}]: advance {}->{}", i, i, first_non_whitespace);
}
}
else if(curr != '\r')
{
if(curr != '\t')
started = true;
m_filter_arena.str[pos++] = curr;
}
}
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= m_filter_arena.len);
_c4dbgfbl(": #filteredchars={} after=[{}]~~~{}~~~", (int)s.len - (int)pos, pos, m_filter_arena.first(pos));
bool changed = _apply_chomp(m_filter_arena, &pos, chomp);
if(pos < r.len || filtered_chars || changed)
{
r = _finish_filter_arena(s, pos); // write into s
}
}
break;
default:
_c4err("unknown block style");
}
_c4dbgfbl(": final=[{}]~~~{}~~~", r.len, r);
#undef _c4dbgfbl
return r;
}
//-----------------------------------------------------------------------------
size_t Parser::_count_nlines(csubstr src)
{
return 1 + src.count('\n');
}
//-----------------------------------------------------------------------------
void Parser::_handle_directive(csubstr directive_)
{
csubstr directive = directive_;
if(directive.begins_with("%TAG"))
{
TagDirective td;
_c4dbgpf("%TAG directive: {}", directive_);
directive = directive.sub(4);
if(!directive.begins_with(' '))
_c4err("malformed tag directive: {}", directive_);
directive = directive.triml(' ');
size_t pos = directive.find(' ');
if(pos == npos)
_c4err("malformed tag directive: {}", directive_);
td.handle = directive.first(pos);
directive = directive.sub(td.handle.len).triml(' ');
pos = directive.find(' ');
if(pos != npos)
directive = directive.first(pos);
td.prefix = directive;
td.next_node_id = m_tree->size();
if(m_tree->size() > 0)
{
size_t prev = m_tree->size() - 1;
if(m_tree->is_root(prev) && m_tree->type(prev) != NOTYPE && !m_tree->is_stream(prev))
++td.next_node_id;
}
_c4dbgpf("%TAG: handle={} prefix={} next_node={}", td.handle, td.prefix, td.next_node_id);
m_tree->add_tag_directive(td);
}
else if(directive.begins_with("%YAML"))
{
_c4dbgpf("%YAML directive! ignoring...: {}", directive);
}
}
//-----------------------------------------------------------------------------
void Parser::set_flags(flag_t f, State * s)
{
#ifdef RYML_DBG
char buf1_[64], buf2_[64];
csubstr buf1 = _prfl(buf1_, f);
csubstr buf2 = _prfl(buf2_, s->flags);
_c4dbgpf("state[{}]: setting flags to {}: before={}", s-m_stack.begin(), buf1, buf2);
#endif
s->flags = f;
}
void Parser::add_flags(flag_t on, State * s)
{
#ifdef RYML_DBG
char buf1_[64], buf2_[64], buf3_[64];
csubstr buf1 = _prfl(buf1_, on);
csubstr buf2 = _prfl(buf2_, s->flags);
csubstr buf3 = _prfl(buf3_, s->flags|on);
_c4dbgpf("state[{}]: adding flags {}: before={} after={}", s-m_stack.begin(), buf1, buf2, buf3);
#endif
s->flags |= on;
}
void Parser::addrem_flags(flag_t on, flag_t off, State * s)
{
#ifdef RYML_DBG
char buf1_[64], buf2_[64], buf3_[64], buf4_[64];
csubstr buf1 = _prfl(buf1_, on);
csubstr buf2 = _prfl(buf2_, off);
csubstr buf3 = _prfl(buf3_, s->flags);
csubstr buf4 = _prfl(buf4_, ((s->flags|on)&(~off)));
_c4dbgpf("state[{}]: adding flags {} / removing flags {}: before={} after={}", s-m_stack.begin(), buf1, buf2, buf3, buf4);
#endif
s->flags |= on;
s->flags &= ~off;
}
void Parser::rem_flags(flag_t off, State * s)
{
#ifdef RYML_DBG
char buf1_[64], buf2_[64], buf3_[64];
csubstr buf1 = _prfl(buf1_, off);
csubstr buf2 = _prfl(buf2_, s->flags);
csubstr buf3 = _prfl(buf3_, s->flags&(~off));
_c4dbgpf("state[{}]: removing flags {}: before={} after={}", s-m_stack.begin(), buf1, buf2, buf3);
#endif
s->flags &= ~off;
}
//-----------------------------------------------------------------------------
csubstr Parser::_prfl(substr buf, flag_t flags)
{
size_t pos = 0;
bool gotone = false;
#define _prflag(fl) \
if((flags & fl) == (fl)) \
{ \
if(gotone) \
{ \
if(pos + 1 < buf.len) \
buf[pos] = '|'; \
++pos; \
} \
csubstr fltxt = #fl; \
if(pos + fltxt.len <= buf.len) \
memcpy(buf.str + pos, fltxt.str, fltxt.len); \
pos += fltxt.len; \
gotone = true; \
}
_prflag(RTOP);
_prflag(RUNK);
_prflag(RMAP);
_prflag(RSEQ);
_prflag(FLOW);
_prflag(QMRK);
_prflag(RKEY);
_prflag(RVAL);
_prflag(RNXT);
_prflag(SSCL);
_prflag(QSCL);
_prflag(RSET);
_prflag(NDOC);
_prflag(RSEQIMAP);
#undef _prflag
RYML_ASSERT(pos <= buf.len);
return buf.first(pos);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Parser::_grow_filter_arena(size_t num_characters_needed)
{
_c4dbgpf("grow: arena={} numchars={}", m_filter_arena.len, num_characters_needed);
if(num_characters_needed <= m_filter_arena.len)
return;
size_t sz = m_filter_arena.len << 1;
_c4dbgpf("grow: sz={}", sz);
sz = num_characters_needed > sz ? num_characters_needed : sz;
_c4dbgpf("grow: sz={}", sz);
sz = sz < 128u ? 128u : sz;
_c4dbgpf("grow: sz={}", sz);
_RYML_CB_ASSERT(m_stack.m_callbacks, sz >= num_characters_needed);
_resize_filter_arena(sz);
}
void Parser::_resize_filter_arena(size_t num_characters)
{
if(num_characters > m_filter_arena.len)
{
_c4dbgpf("resize: sz={}", num_characters);
char *prev = m_filter_arena.str;
if(m_filter_arena.str)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_filter_arena.len > 0);
_RYML_CB_FREE(m_stack.m_callbacks, m_filter_arena.str, char, m_filter_arena.len);
}
m_filter_arena.str = _RYML_CB_ALLOC_HINT(m_stack.m_callbacks, char, num_characters, prev);
m_filter_arena.len = num_characters;
}
}
substr Parser::_finish_filter_arena(substr dst, size_t pos)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= m_filter_arena.len);
_RYML_CB_ASSERT(m_stack.m_callbacks, pos <= dst.len);
memcpy(dst.str, m_filter_arena.str, pos);
return dst.first(pos);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
csubstr Parser::location_contents(Location const& loc) const
{
_RYML_CB_ASSERT(m_stack.m_callbacks, loc.offset < m_buf.len);
return m_buf.sub(loc.offset);
}
Location Parser::location(NodeRef node) const
{
_RYML_CB_ASSERT(m_stack.m_callbacks, node.valid());
return location(*node.tree(), node.id());
}
Location Parser::location(Tree const& tree, size_t node) const
{
_RYML_CB_CHECK(m_stack.m_callbacks, m_buf.str == m_newline_offsets_buf.str);
_RYML_CB_CHECK(m_stack.m_callbacks, m_buf.len == m_newline_offsets_buf.len);
if(tree.has_key(node))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, tree.key(node).is_sub(m_buf));
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(tree.key(node)));
return val_location(tree.key(node).str);
}
else if(tree.has_val(node))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, tree.val(node).is_sub(m_buf));
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(tree.val(node)));
return val_location(tree.val(node).str);
}
else if(tree.is_container(node))
{
_RYML_CB_ASSERT(m_stack.m_callbacks, !tree.has_key(node));
if(!tree.is_stream(node))
{
const char *node_start = tree._p(node)->m_val.scalar.str; // this was stored in the container
if(tree.has_children(node))
{
size_t child = tree.first_child(node);
if(tree.has_key(child))
{
// when a map starts, the container was set after the key
csubstr k = tree.key(child);
if(node_start > k.str)
node_start = k.str;
}
}
return val_location(node_start);
}
else // it's a stream
{
return val_location(m_buf.str); // just return the front of the buffer
}
}
_RYML_CB_ASSERT(m_stack.m_callbacks, tree.type(node) == NOTYPE);
return val_location(m_buf.str);
}
Location Parser::val_location(const char *val) const
{
if(_locations_dirty())
_prepare_locations();
csubstr src = m_buf;
_RYML_CB_CHECK(m_stack.m_callbacks, src.str == m_newline_offsets_buf.str);
_RYML_CB_CHECK(m_stack.m_callbacks, src.len == m_newline_offsets_buf.len);
_RYML_CB_CHECK(m_stack.m_callbacks, val >= src.begin() && val <= src.end());
_RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets != nullptr);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets_size > 0);
using linetype = size_t const* C4_RESTRICT;
linetype line = nullptr;
size_t offset = (size_t)(val - src.begin());
if(m_newline_offsets_size < 30)
{
// do a linear search if the size is small.
for(linetype curr = m_newline_offsets; curr < m_newline_offsets + m_newline_offsets_size; ++curr)
{
if(*curr > offset)
{
line = curr;
break;
}
}
}
else
{
// Do a bisection search if the size is not small.
//
// We could use std::lower_bound but this is simple enough and
// spares the include of <algorithm>.
size_t count = m_newline_offsets_size;
size_t step;
linetype it;
line = m_newline_offsets;
while(count)
{
step = count >> 1;
it = line + step;
if(*it < offset)
{
line = ++it;
count -= step + 1;
}
else
{
count = step;
}
}
}
if(line)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, *line > offset);
}
else
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.empty());
_RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets_size == 1);
line = m_newline_offsets;
}
_RYML_CB_ASSERT(m_stack.m_callbacks, line >= m_newline_offsets && line < m_newline_offsets + m_newline_offsets_size);;
Location loc = {};
loc.name = m_file;
loc.offset = offset;
loc.line = (size_t)(line - m_newline_offsets);
if(line > m_newline_offsets)
loc.col = (offset - *(line-1) - 1u);
else
loc.col = offset;
return loc;
}
void Parser::_prepare_locations() const
{
_RYML_CB_ASSERT(m_stack.m_callbacks, !m_file.empty());
size_t numnewlines = 1u + m_buf.count('\n');
_resize_locations(numnewlines);
m_newline_offsets_size = 0;
for(size_t i = 0; i < m_buf.len; i++)
if(m_buf[i] == '\n')
m_newline_offsets[m_newline_offsets_size++] = i;
m_newline_offsets[m_newline_offsets_size++] = m_buf.len;
_RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets_size == numnewlines);
}
void Parser::_resize_locations(size_t numnewlines) const
{
if(numnewlines > m_newline_offsets_capacity)
{
if(m_newline_offsets)
_RYML_CB_FREE(m_stack.m_callbacks, m_newline_offsets, size_t, m_newline_offsets_capacity);
m_newline_offsets = _RYML_CB_ALLOC_HINT(m_stack.m_callbacks, size_t, numnewlines, m_newline_offsets);
m_newline_offsets_capacity = numnewlines;
}
}
void Parser::_mark_locations_dirty()
{
m_newline_offsets_size = 0u;
m_newline_offsets_buf = m_buf;
}
bool Parser::_locations_dirty() const
{
return !m_newline_offsets_size;
}
} // namespace yml
} // namespace c4
#if defined(_MSC_VER)
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* RYML_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/parse.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/node.cpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef RYML_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
namespace c4 {
namespace yml {
size_t NodeRef::set_key_serialized(c4::fmt::const_base64_wrapper w)
{
_apply_seed();
csubstr encoded = this->to_arena(w);
this->set_key(encoded);
return encoded.len;
}
size_t NodeRef::set_val_serialized(c4::fmt::const_base64_wrapper w)
{
_apply_seed();
csubstr encoded = this->to_arena(w);
this->set_val(encoded);
return encoded.len;
}
size_t NodeRef::deserialize_key(c4::fmt::base64_wrapper w) const
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
RYML_ASSERT(get() != nullptr);
return from_chars(key(), &w);
}
size_t NodeRef::deserialize_val(c4::fmt::base64_wrapper w) const
{
RYML_ASSERT( ! is_seed());
RYML_ASSERT(valid());
RYML_ASSERT(get() != nullptr);
return from_chars(val(), &w);
}
} // namespace yml
} // namespace c4
#endif /* RYML_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/node.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/preprocess.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/preprocess.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_PREPROCESS_HPP_
#define _C4_YML_PREPROCESS_HPP_
/** @file preprocess.hpp Functions for preprocessing YAML prior to parsing. */
/** @defgroup Preprocessors Preprocessor functions
*
* These are the existing preprocessors:
*
* @code{.cpp}
* size_t preprocess_json(csubstr json, substr buf)
* size_t preprocess_rxmap(csubstr json, substr buf)
* @endcode
*/
#ifndef _C4_YML_COMMON_HPP_
//included above:
//#include "./common.hpp"
#endif
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/substr.hpp
//#include <c4/substr.hpp>
#if !defined(C4_SUBSTR_HPP_) && !defined(_C4_SUBSTR_HPP_)
#error "amalgamate: file c4/substr.hpp must have been included at this point"
#endif /* C4_SUBSTR_HPP_ */
namespace c4 {
namespace yml {
namespace detail {
using Preprocessor = size_t(csubstr, substr);
template<Preprocessor PP, class CharContainer>
substr preprocess_into_container(csubstr input, CharContainer *out)
{
// try to write once. the preprocessor will stop writing at the end of
// the container, but will process all the input to determine the
// required container size.
size_t sz = PP(input, to_substr(*out));
// if the container size is not enough, resize, and run again in the
// resized container
if(sz > out->size())
{
out->resize(sz);
sz = PP(input, to_substr(*out));
}
return to_substr(*out).first(sz);
}
} // namespace detail
//-----------------------------------------------------------------------------
/** @name preprocess_rxmap
* Convert flow-type relaxed maps (with implicit bools) into strict YAML
* flow map.
*
* @code{.yaml}
* {a, b, c, d: [e, f], g: {a, b}}
* # is converted into this:
* {a: 1, b: 1, c: 1, d: [e, f], g: {a, b}}
* @endcode
* @note this is NOT recursive - conversion happens only in the top-level map
* @param rxmap A relaxed map
* @param buf output buffer
* @param out output container
*/
//@{
/** Write into a given output buffer. This function is safe to call with
* empty or small buffers; it won't write beyond the end of the buffer.
*
* @return the number of characters required for output
*/
RYML_EXPORT size_t preprocess_rxmap(csubstr rxmap, substr buf);
/** Write into an existing container. It is resized to contained the output.
* @return a substr of the container
* @overload preprocess_rxmap */
template<class CharContainer>
substr preprocess_rxmap(csubstr rxmap, CharContainer *out)
{
return detail::preprocess_into_container<preprocess_rxmap>(rxmap, out);
}
/** Create a container with the result.
* @overload preprocess_rxmap */
template<class CharContainer>
CharContainer preprocess_rxmap(csubstr rxmap)
{
CharContainer out;
preprocess_rxmap(rxmap, &out);
return out;
}
//@}
} // namespace yml
} // namespace c4
#endif /* _C4_YML_PREPROCESS_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/preprocess.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/preprocess.cpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/preprocess.cpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifdef RYML_SINGLE_HDR_DEFINE_NOW
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/preprocess.hpp
//#include "c4/yml/preprocess.hpp"
#if !defined(C4_YML_PREPROCESS_HPP_) && !defined(_C4_YML_PREPROCESS_HPP_)
#error "amalgamate: file c4/yml/preprocess.hpp must have been included at this point"
#endif /* C4_YML_PREPROCESS_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/parser_dbg.hpp
//#include "c4/yml/detail/parser_dbg.hpp"
#if !defined(C4_YML_DETAIL_PARSER_DBG_HPP_) && !defined(_C4_YML_DETAIL_PARSER_DBG_HPP_)
#error "amalgamate: file c4/yml/detail/parser_dbg.hpp must have been included at this point"
#endif /* C4_YML_DETAIL_PARSER_DBG_HPP_ */
/** @file preprocess.hpp Functions for preprocessing YAML prior to parsing. */
namespace c4 {
namespace yml {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace {
C4_ALWAYS_INLINE bool _is_idchar(char c)
{
return (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| (c == '_' || c == '-' || c == '~' || c == '$');
}
typedef enum { kReadPending = 0, kKeyPending = 1, kValPending = 2 } _ppstate;
C4_ALWAYS_INLINE _ppstate _next(_ppstate s)
{
int n = (int)s + 1;
return (_ppstate)(n <= (int)kValPending ? n : 0);
}
} // empty namespace
//-----------------------------------------------------------------------------
size_t preprocess_rxmap(csubstr s, substr buf)
{
detail::_SubstrWriter writer(buf);
_ppstate state = kReadPending;
size_t last = 0;
if(s.begins_with('{'))
{
RYML_CHECK(s.ends_with('}'));
s = s.offs(1, 1);
}
writer.append('{');
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s[i];
const char next = i+1 < s.len ? s[i+1] : '\0';
if(curr == '\'' || curr == '"')
{
csubstr ss = s.sub(i).pair_range_esc(curr, '\\');
i += static_cast<size_t>(ss.end() - (s.str + i));
state = _next(state);
}
else if(state == kReadPending && _is_idchar(curr))
{
state = _next(state);
}
switch(state)
{
case kKeyPending:
{
if(curr == ':' && next == ' ')
{
state = _next(state);
}
else if(curr == ',' && next == ' ')
{
writer.append(s.range(last, i));
writer.append(": 1, ");
last = i + 2;
}
break;
}
case kValPending:
{
if(curr == '[' || curr == '{' || curr == '(')
{
csubstr ss = s.sub(i).pair_range_nested(curr, '\\');
i += static_cast<size_t>(ss.end() - (s.str + i));
state = _next(state);
}
else if(curr == ',' && next == ' ')
{
state = _next(state);
}
break;
}
default:
// nothing to do
break;
}
}
writer.append(s.sub(last));
if(state == kKeyPending)
writer.append(": 1");
writer.append('}');
return writer.pos;
}
} // namespace yml
} // namespace c4
#endif /* RYML_SINGLE_HDR_DEFINE_NOW */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/preprocess.cpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/detail/checks.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/checks.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_YML_DETAIL_CHECKS_HPP_
#define C4_YML_DETAIL_CHECKS_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//#include "c4/yml/tree.hpp"
#if !defined(C4_YML_TREE_HPP_) && !defined(_C4_YML_TREE_HPP_)
#error "amalgamate: file c4/yml/tree.hpp must have been included at this point"
#endif /* C4_YML_TREE_HPP_ */
#ifdef __clang__
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtype-limits" // error: comparison of unsigned expression >= 0 is always true
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4296/*expression is always 'boolean_value'*/)
#endif
namespace c4 {
namespace yml {
void check_invariants(Tree const& t, size_t node=NONE);
void check_free_list(Tree const& t);
void check_arena(Tree const& t);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_invariants(Tree const& t, size_t node)
{
if(node == NONE)
{
if(t.size() == 0) return;
node = t.root_id();
}
auto const& n = *t._p(node);
#ifdef RYML_DBG
if(n.m_first_child != NONE || n.m_last_child != NONE)
{
printf("check(%zu): fc=%zu lc=%zu\n", node, n.m_first_child, n.m_last_child);
}
else
{
printf("check(%zu)\n", node);
}
#endif
C4_CHECK(n.m_parent != node);
if(n.m_parent == NONE)
{
C4_CHECK(t.is_root(node));
}
else //if(n.m_parent != NONE)
{
C4_CHECK(t.has_child(n.m_parent, node));
auto const& p = *t._p(n.m_parent);
if(n.m_prev_sibling == NONE)
{
C4_CHECK(p.m_first_child == node);
C4_CHECK(t.first_sibling(node) == node);
}
else
{
C4_CHECK(p.m_first_child != node);
C4_CHECK(t.first_sibling(node) != node);
}
if(n.m_next_sibling == NONE)
{
C4_CHECK(p.m_last_child == node);
C4_CHECK(t.last_sibling(node) == node);
}
else
{
C4_CHECK(p.m_last_child != node);
C4_CHECK(t.last_sibling(node) != node);
}
}
C4_CHECK(n.m_first_child != node);
C4_CHECK(n.m_last_child != node);
if(n.m_first_child != NONE || n.m_last_child != NONE)
{
C4_CHECK(n.m_first_child != NONE);
C4_CHECK(n.m_last_child != NONE);
}
C4_CHECK(n.m_prev_sibling != node);
C4_CHECK(n.m_next_sibling != node);
if(n.m_prev_sibling != NONE)
{
C4_CHECK(t._p(n.m_prev_sibling)->m_next_sibling == node);
C4_CHECK(t._p(n.m_prev_sibling)->m_prev_sibling != node);
}
if(n.m_next_sibling != NONE)
{
C4_CHECK(t._p(n.m_next_sibling)->m_prev_sibling == node);
C4_CHECK(t._p(n.m_next_sibling)->m_next_sibling != node);
}
size_t count = 0;
for(size_t i = n.m_first_child; i != NONE; i = t.next_sibling(i))
{
#ifdef RYML_DBG
printf("check(%zu): descend to child[%zu]=%zu\n", node, count, i);
#endif
auto const& ch = *t._p(i);
C4_CHECK(ch.m_parent == node);
C4_CHECK(ch.m_next_sibling != i);
++count;
}
C4_CHECK(count == t.num_children(node));
if(n.m_prev_sibling == NONE && n.m_next_sibling == NONE)
{
if(n.m_parent != NONE)
{
C4_CHECK(t.num_children(n.m_parent) == 1);
C4_CHECK(t.num_siblings(node) == 1);
}
}
if(node == t.root_id())
{
C4_CHECK(t.size() == t.m_size);
C4_CHECK(t.capacity() == t.m_cap);
C4_CHECK(t.m_cap == t.m_size + t.slack());
check_free_list(t);
check_arena(t);
}
for(size_t i = t.first_child(node); i != NONE; i = t.next_sibling(i))
{
check_invariants(t, i);
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_free_list(Tree const& t)
{
if(t.m_free_head == NONE)
{
C4_CHECK(t.m_free_tail == t.m_free_head);
return;
}
C4_CHECK(t.m_free_head >= 0 && t.m_free_head < t.m_cap);
C4_CHECK(t.m_free_tail >= 0 && t.m_free_tail < t.m_cap);
auto const& head = *t._p(t.m_free_head);
//auto const& tail = *t._p(t.m_free_tail);
//C4_CHECK(head.m_prev_sibling == NONE);
//C4_CHECK(tail.m_next_sibling == NONE);
size_t count = 0;
for(size_t i = t.m_free_head, prev = NONE; i != NONE; i = t._p(i)->m_next_sibling)
{
auto const& elm = *t._p(i);
if(&elm != &head)
{
C4_CHECK(elm.m_prev_sibling == prev);
}
prev = i;
++count;
}
C4_CHECK(count == t.slack());
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_arena(Tree const& t)
{
C4_CHECK(t.m_arena.len == 0 || (t.m_arena_pos >= 0 && t.m_arena_pos <= t.m_arena.len));
C4_CHECK(t.arena_size() == t.m_arena_pos);
C4_CHECK(t.arena_slack() + t.m_arena_pos == t.m_arena.len);
}
} /* namespace yml */
} /* namespace c4 */
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* C4_YML_DETAIL_CHECKS_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/detail/checks.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/detail/print.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/detail/print.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef C4_YML_DETAIL_PRINT_HPP_
#define C4_YML_DETAIL_PRINT_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//#include "c4/yml/tree.hpp"
#if !defined(C4_YML_TREE_HPP_) && !defined(_C4_YML_TREE_HPP_)
#error "amalgamate: file c4/yml/tree.hpp must have been included at this point"
#endif /* C4_YML_TREE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
namespace c4 {
namespace yml {
inline size_t print_node(Tree const& p, size_t node, int level, size_t count, bool print_children)
{
printf("[%zd]%*s[%zd] %p", count, (2*level), "", node, (void*)p.get(node));
if(p.is_root(node))
{
printf(" [ROOT]");
}
printf(" %s:", p.type_str(node));
if(p.has_key(node))
{
if(p.has_key_anchor(node))
{
csubstr ka = p.key_anchor(node);
printf(" &%.*s", (int)ka.len, ka.str);
}
if(p.has_key_tag(node))
{
csubstr kt = p.key_tag(node);
csubstr k = p.key(node);
printf(" %.*s '%.*s'", (int)kt.len, kt.str, (int)k.len, k.str);
}
else
{
csubstr k = p.key(node);
printf(" '%.*s'", (int)k.len, k.str);
}
}
else
{
RYML_ASSERT( ! p.has_key_tag(node));
}
if(p.has_val(node))
{
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
csubstr v = p.val(node);
printf(" %.*s '%.*s'", (int)vt.len, vt.str, (int)v.len, v.str);
}
else
{
csubstr v = p.val(node);
printf(" '%.*s'", (int)v.len, v.str);
}
}
else
{
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
printf(" %.*s", (int)vt.len, vt.str);
}
}
if(p.has_val_anchor(node))
{
auto &a = p.val_anchor(node);
printf(" valanchor='&%.*s'", (int)a.len, a.str);
}
printf(" (%zd sibs)", p.num_siblings(node));
++count;
if(p.is_container(node))
{
printf(" %zd children:\n", p.num_children(node));
if(print_children)
{
for(size_t i = p.first_child(node); i != NONE; i = p.next_sibling(i))
{
count = print_node(p, i, level+1, count, print_children);
}
}
}
else
{
printf("\n");
}
return count;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void print_node(NodeRef const& p, int level=0)
{
print_node(*p.tree(), p.id(), level, 0, true);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline size_t print_tree(Tree const& p, size_t node=NONE)
{
printf("--------------------------------------\n");
size_t ret = 0;
if(!p.empty())
{
if(node == NONE)
node = p.root_id();
ret = print_node(p, node, 0, 0, true);
}
printf("#nodes=%zd vs #printed=%zd\n", p.size(), ret);
printf("--------------------------------------\n");
return ret;
}
} /* namespace yml */
} /* namespace c4 */
#endif /* C4_YML_DETAIL_PRINT_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/detail/print.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/c4/yml/yml.hpp
// https://github.com/biojppm/rapidyaml/src/c4/yml/yml.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _C4_YML_YML_HPP_
#define _C4_YML_YML_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/tree.hpp
//#include "c4/yml/tree.hpp"
#if !defined(C4_YML_TREE_HPP_) && !defined(_C4_YML_TREE_HPP_)
#error "amalgamate: file c4/yml/tree.hpp must have been included at this point"
#endif /* C4_YML_TREE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/node.hpp
//#include "c4/yml/node.hpp"
#if !defined(C4_YML_NODE_HPP_) && !defined(_C4_YML_NODE_HPP_)
#error "amalgamate: file c4/yml/node.hpp must have been included at this point"
#endif /* C4_YML_NODE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/emit.hpp
//#include "c4/yml/emit.hpp"
#if !defined(C4_YML_EMIT_HPP_) && !defined(_C4_YML_EMIT_HPP_)
#error "amalgamate: file c4/yml/emit.hpp must have been included at this point"
#endif /* C4_YML_EMIT_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/parse.hpp
//#include "c4/yml/parse.hpp"
#if !defined(C4_YML_PARSE_HPP_) && !defined(_C4_YML_PARSE_HPP_)
#error "amalgamate: file c4/yml/parse.hpp must have been included at this point"
#endif /* C4_YML_PARSE_HPP_ */
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/preprocess.hpp
//#include "c4/yml/preprocess.hpp"
#if !defined(C4_YML_PREPROCESS_HPP_) && !defined(_C4_YML_PREPROCESS_HPP_)
#error "amalgamate: file c4/yml/preprocess.hpp must have been included at this point"
#endif /* C4_YML_PREPROCESS_HPP_ */
#endif // _C4_YML_YML_HPP_
// (end https://github.com/biojppm/rapidyaml/src/c4/yml/yml.hpp)
//********************************************************************************
//--------------------------------------------------------------------------------
// src/ryml.hpp
// https://github.com/biojppm/rapidyaml/src/ryml.hpp
//--------------------------------------------------------------------------------
//********************************************************************************
#ifndef _RYML_HPP_
#define _RYML_HPP_
// amalgamate: removed include of
// https://github.com/biojppm/rapidyaml/src/c4/yml/yml.hpp
//#include "c4/yml/yml.hpp"
#if !defined(C4_YML_YML_HPP_) && !defined(_C4_YML_YML_HPP_)
#error "amalgamate: file c4/yml/yml.hpp must have been included at this point"
#endif /* C4_YML_YML_HPP_ */
namespace ryml {
using namespace c4::yml;
using namespace c4;
}
#endif /* _RYML_HPP_ */
// (end https://github.com/biojppm/rapidyaml/src/ryml.hpp)
#endif /* _RYML_SINGLE_HEADER_AMALGAMATED_HPP_ */