diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6082673 --- /dev/null +++ b/.clang-format @@ -0,0 +1,78 @@ +# Generated from CLion C/C++ Code Style settings +BinPackParameters: false +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignArrayOfStructures: Left +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + +AlignOperands: DontAlign +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakTemplateDeclarations: Yes +# 函数和返回类型分两行,方便阅读 +AlwaysBreakAfterReturnType: TopLevelDefinitions +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true +BreakInheritanceList: BeforeColon +ColumnLimit: 120 +CompactNamespaces: false +ContinuationIndentWidth: 4 +EmptyLineBeforeAccessModifier: LogicalBlock +SeparateDefinitionBlocks: Always +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PointerAlignment: Right +ReflowComments: false +SortIncludes: CaseSensitive +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never +PenaltyIndentedWhitespace: 1 diff --git a/.gitea/workflows/android.yml b/.gitea/workflows/android.yml index 4ba1529..b6c4e6e 100644 --- a/.gitea/workflows/android.yml +++ b/.gitea/workflows/android.yml @@ -36,6 +36,10 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + - name: install-tools + run: | + apt-get update -y + apt-get install -y cmake make - name: armeabi-v7a run: | diff --git a/.gitea/workflows/linux-aarch64-gcc.yml b/.gitea/workflows/linux-aarch64-gcc.yml index 7102486..415d7fe 100644 --- a/.gitea/workflows/linux-aarch64-gcc.yml +++ b/.gitea/workflows/linux-aarch64-gcc.yml @@ -41,7 +41,7 @@ jobs: - name: install-tools run: | sudo apt-get update -y - sudo apt-get install -y g++-aarch64-linux-gnu qemu-user-binfmt + sudo apt-get install -y cmake make g++-aarch64-linux-gnu qemu-user-binfmt - name: configure run: | mkdir build && cd build diff --git a/.gitea/workflows/linux-arm-gcc.yml b/.gitea/workflows/linux-arm-gcc.yml index 0e747bb..5f10a39 100644 --- a/.gitea/workflows/linux-arm-gcc.yml +++ b/.gitea/workflows/linux-arm-gcc.yml @@ -36,10 +36,10 @@ jobs: build_type: ["Debug", "Release"] steps: - uses: actions/checkout@v4 - # - name: install-tools - # run: | - # sudo apt-get update -y - # sudo apt-get install -y g++-arm-linux-gnueabi qemu-user-binfmt + - name: install-tools + run: | + sudo apt-get update -y + sudo apt-get install -y cmake g++-arm-linux-gnueabi qemu-user-binfmt - name: configure run: | mkdir build && cd build @@ -51,8 +51,8 @@ jobs: - name: test run: | cd build - #sudo ln -sf /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib/ld-linux.so.3 - #export LD_LIBRARY_PATH=/usr/arm-linux-gnueabi/lib + sudo ln -sf /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib/ld-linux.so.3 + export LD_LIBRARY_PATH=/usr/arm-linux-gnueabi/lib ctest --output-on-failure -j $(nproc) linux-gcc-armhf: diff --git a/.gitea/workflows/linux-mips-gcc.yml b/.gitea/workflows/linux-mips-gcc.yml index c59edef..c281183 100644 --- a/.gitea/workflows/linux-mips-gcc.yml +++ b/.gitea/workflows/linux-mips-gcc.yml @@ -38,7 +38,7 @@ jobs: - name: install-tools run: | sudo apt-get update -y - sudo apt-get install -y g++-mipsel-linux-gnu qemu-user-binfmt + sudo apt-get install -y cmake make g++-mipsel-linux-gnu qemu-user-binfmt - name: configure run: | mkdir build && cd build diff --git a/.gitea/workflows/linux-mips64-gcc.yml b/.gitea/workflows/linux-mips64-gcc.yml index a199aa9..4531e39 100644 --- a/.gitea/workflows/linux-mips64-gcc.yml +++ b/.gitea/workflows/linux-mips64-gcc.yml @@ -39,7 +39,7 @@ jobs: - name: install-tools run: | sudo apt-get update -y - sudo apt-get install -y g++-mips64el-linux-gnuabi64 qemu-user-binfmt + sudo apt-get install -y cmake make g++-mips64el-linux-gnuabi64 qemu-user-binfmt - name: configure run: | mkdir build && cd build diff --git a/.gitea/workflows/linux-riscv64-gcc.yml b/.gitea/workflows/linux-riscv64-gcc.yml index 9e8be76..b7fa618 100644 --- a/.gitea/workflows/linux-riscv64-gcc.yml +++ b/.gitea/workflows/linux-riscv64-gcc.yml @@ -40,7 +40,7 @@ jobs: - name: install-tools run: | sudo apt-get update -y - sudo apt-get install -y g++-riscv64-linux-gnu qemu-user-binfmt + sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-binfmt - name: configure run: | mkdir build && cd build diff --git a/.gitea/workflows/linux-x64-clang.yml b/.gitea/workflows/linux-x64-clang.yml index 0f282d0..639234c 100644 --- a/.gitea/workflows/linux-x64-clang.yml +++ b/.gitea/workflows/linux-x64-clang.yml @@ -32,7 +32,7 @@ jobs: - name: install-tools run: | sudo apt-get update -y - sudo apt-get install -y cmake make + sudo apt-get install -y cmake make clang-tools - name: configure env: CC: clang diff --git a/.gitea/workflows/linux-x86-gcc.yml b/.gitea/workflows/linux-x86-gcc.yml index 76d1b77..19710af 100644 --- a/.gitea/workflows/linux-x86-gcc.yml +++ b/.gitea/workflows/linux-x86-gcc.yml @@ -38,7 +38,7 @@ jobs: - name: install-tools run: | sudo apt-get update -y - sudo apt-get install -y gcc-multilib g++-multilib + sudo apt-get install -y cmake make gcc-multilib g++-multilib - name: configure run: | mkdir build && cd build diff --git a/.woodpecker/linux-aarch64-gcc.yml b/.woodpecker/linux-aarch64-gcc.yml deleted file mode 100644 index 02f22df..0000000 --- a/.woodpecker/linux-aarch64-gcc.yml +++ /dev/null @@ -1,37 +0,0 @@ -when: - - event: - - push - - pull_request - - path: - include: - - ".woodpecker/linux-aarch64-gcc.yml" - - "cmake/**" - - "third_party/**" - - "tile/**" - - "CMakeLists.txt" - -matrix: - BUILD_TYPE: - - Debug - - Release - -steps: - - name: linux-aarch64-gcc-build - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - mkdir build - - cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=./toolchains/aarch64-linux-gnu.toolchain.cmake - - cmake --build build -j $(nproc) - - - name: linux-aarch64-gcc-test - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - cd build - - ctest --output-on-failure -j $(nproc) - - # - name: linux-aarch64-gcc-benchmark - # image: art.uocat.com/docker/tqcq/cross:v1.0.1 - # commands: - # - ./build/bin/tile_bm_all - - diff --git a/.woodpecker/linux-arm-gcc.yml.stop b/.woodpecker/linux-arm-gcc.yml.stop deleted file mode 100644 index 9a167bb..0000000 --- a/.woodpecker/linux-arm-gcc.yml.stop +++ /dev/null @@ -1,30 +0,0 @@ -when: - - event: [] - #- push - #- pull_request - -matrix: - BUILD_TYPE: - - Debug - - Release - -steps: - - name: linux-arm-gcc-build - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - mkdir build - - cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=./toolchains/arm-linux-gnueabihf.toolchain.cmake - - cmake --build build -j $(nproc) - - - name: linux-arm-gcc-test - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - cd build - - ctest --output-on-failure -j $(nproc) - - # - name: linux-arm-gcc-benchmark - # image: art.uocat.com/docker/tqcq/cross:v1.0.1 - # commands: - # - ./build/bin/tile_bm_all - - diff --git a/.woodpecker/linux-x64-gcc.yml b/.woodpecker/linux-x64-gcc.yml deleted file mode 100644 index 5b03d91..0000000 --- a/.woodpecker/linux-x64-gcc.yml +++ /dev/null @@ -1,37 +0,0 @@ -when: - - event: - - push - - pull_request - - path: - include: - - ".woodpecker/linux-x64-gcc.yml" - - "cmake/**" - - "third_party/**" - - "tile/**" - - "CMakeLists.txt" - -matrix: - BUILD_TYPE: - - Debug - - Release - -steps: - - name: linux-x64-gcc-build - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - mkdir build - - cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON - - cmake --build build -j $(nproc) - - - name: linux-x64-gcc-test - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - cd build - - ctest --output-on-failure -j $(nproc) - - - name: linux-x64-gcc-benchmark - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - ./build/bin/tile_bm_all - - diff --git a/.woodpecker/linux-x86-gcc.yml.stop b/.woodpecker/linux-x86-gcc.yml.stop deleted file mode 100644 index 80fbaf1..0000000 --- a/.woodpecker/linux-x86-gcc.yml.stop +++ /dev/null @@ -1,36 +0,0 @@ -when: - - event: - - push - - pull_request - - path: - include: - - ".woodpecker/linux-x86-gcc.yml" - - "cmake/**" - - "third_party/**" - - "tile/**" - - "CMakeLists.txt" -matrix: - BUILD_TYPE: - - Debug - - Release - -steps: - - name: linux-x86-gcc-build - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - mkdir build - - cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=./toolchains/host.gcc-m32.toolchain.cmake - - cmake --build build -j $(nproc) - - - name: linux-x86-gcc-test - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - cd build - - ctest --output-on-failure -j $(nproc) - - - name: linux-x86-gcc-benchmark - image: art.uocat.com/docker/tqcq/cross:v1.0.1 - commands: - - ./build/bin/tile_bm_all - - diff --git a/CMakeLists.txt b/CMakeLists.txt index ea5994c..e2d0272 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,11 +19,14 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# add static libgcc and libstdc++ for static linking +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc -static-libstdc++") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") + option(TILE_BUILD_TESTS "Build tests" OFF) option(TILE_BUILD_BENCHMARKS "Build tests" OFF) option(TILE_WITH_OPENSSL "Build with openssl" OFF) option(TILE_BUILD_SHARED "Build shared library" ON) -option(TILE_BUILD_STATIC "Build static library" ON) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(TILE_BUILD_TESTS ON) @@ -34,34 +37,34 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz") - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS - # "${CMAKE_CXX_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} - # -fsanitize=address ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address - # ") - - set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load") - # set(NO_WHOLE_ARCHIVE_PREFIX "") -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz") - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS - # "${CMAKE_CXX_FLAGS} -static") - - set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,") - # set(NO_WHOLE_ARCHIVE_PREFIX "") -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") - # set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") - - set(STATIC_BINARY_FLAGS "-static-libgcc -static-libstdc++") - set(WHOLE_ARCHIVE_PREFIX "-Wl,--whole-archive") - set(WHOLE_ARCHIVE_SUFFIX "-Wl,--no-whole-archive") -endif() +# if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") +# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz") +# # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") +# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS +# # "${CMAKE_CXX_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} +# # -fsanitize=address ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address +# # ") +# +# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load") +# # set(NO_WHOLE_ARCHIVE_PREFIX "") +# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") +# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz") +# # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") +# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS +# # "${CMAKE_CXX_FLAGS} -static") +# +# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,") +# # set(NO_WHOLE_ARCHIVE_PREFIX "") +# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") +# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") +# # set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") +# +# set(STATIC_BINARY_FLAGS "-static-libgcc -static-libstdc++") +# set(WHOLE_ARCHIVE_PREFIX "-Wl,--whole-archive") +# set(WHOLE_ARCHIVE_SUFFIX "-Wl,--no-whole-archive") +# endif() # extern int getifaddrs(struct ifaddrs **ifap); extern void freeifaddrs(struct # ifaddrs *ifa); @@ -71,11 +74,11 @@ include(cmake/BuildInfo.cmake) check_symbol_exists("getifaddrs" "ifaddrs.h" TILE_HAVE_GETIFADDRS) check_symbol_exists("freeifaddrs" "ifaddrs.h" TILE_HAVE_FREEIFADDRS) -find_package(Git REQUIRED) - -get_git_commit_hash(GIT_COMMIT_HASH) -get_git_commit_date(GIT_COMMIT_DATE) -get_git_commit_subject(GIT_COMMIT_SUBJECT) +# find_package(Git REQUIRED) +# +# get_git_commit_hash(GIT_COMMIT_HASH) +# get_git_commit_date(GIT_COMMIT_DATE) +# get_git_commit_subject(GIT_COMMIT_SUBJECT) set(THIRD_PARTY_INCLUDE_DIRS # "third_party/json" "third_party/inja" @@ -211,16 +214,22 @@ set(TILE_SRCS "tile/rpc/protocol/http/buffer_io.cc" "tile/rpc/protocol/message.cc" # "tile/rpc/server.cc" - "tile/base/config/config.cc" - "tile/base/config/ini_file_config.cc" - "tile/base/config/layered_config.cc" + "tile/base/config/configuration.cc" + "tile/base/config/ini_file_configuration.cc" + "tile/base/config/json_configuration.cc" + "tile/base/config/layered_configuration.cc" + "tile/base/config/toml_configuration.cc" ) if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS)) list(APPEND TILE_SRCS "tile/base/net/detail/android/ifaddrs.c") endif() -add_library(tile SHARED ${TILE_SRCS}) +if (TILE_BUILD_SHARED) + add_library(tile SHARED ${TILE_SRCS}) +else() + add_library(tile STATIC ${TILE_SRCS}) +endif() set_target_properties(tile PROPERTIES VERSION ${PROJECT_VERSION} target_precompile_headers(tile PUBLIC inja/inja.h) target_precompile_headers(tile PUBLIC inja/string_view.h) @@ -238,6 +247,7 @@ target_include_directories( "${CMAKE_CURRENT_SOURCE_DIR}" ${THIRD_PARTY_INCLUDE_DIRS} RPIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/header_only/" "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include") target_link_libraries( diff --git a/third_party/README.md b/third_party/README.md new file mode 100644 index 0000000..6b79838 --- /dev/null +++ b/third_party/README.md @@ -0,0 +1,3 @@ +# Library + +- [toml11](https://github.com/ToruNiina/toml11/archive/refs/tags/v4.2.0.tar.gz) diff --git a/third_party/curl/CMakeLists.txt b/third_party/curl/CMakeLists.txt index f4a42d1..8e7297e 100644 --- a/third_party/curl/CMakeLists.txt +++ b/third_party/curl/CMakeLists.txt @@ -107,7 +107,7 @@ if(WIN32) endif() endif() endif() -option(CURL_LTO "Turn on compiler Link Time Optimizations" ON) +option(CURL_LTO "Turn on compiler Link Time Optimizations" OFF) cmake_dependent_option(ENABLE_THREADED_RESOLVER "Set to ON to enable threaded DNS lookup" ON "NOT ENABLE_ARES" diff --git a/third_party/header_only/toml.hpp b/third_party/header_only/toml.hpp new file mode 100644 index 0000000..afdaf5b --- /dev/null +++ b/third_party/header_only/toml.hpp @@ -0,0 +1,17241 @@ +#ifndef TOML11_VERSION_HPP +#define TOML11_VERSION_HPP + +#define TOML11_VERSION_MAJOR 4 +#define TOML11_VERSION_MINOR 2 +#define TOML11_VERSION_PATCH 0 + +#ifndef __cplusplus +# error "__cplusplus is not defined" +#endif + +// Since MSVC does not define `__cplusplus` correctly unless you pass +// `/Zc:__cplusplus` when compiling, the workaround macros are added. +// +// The value of `__cplusplus` macro is defined in the C++ standard spec, but +// MSVC ignores the value, maybe because of backward compatibility. Instead, +// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in +// the C++ standard. So we check if _MSVC_LANG is defined before using `__cplusplus`. +// +// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 +// + +#if defined(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER +# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG +#else +# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L +# error "toml11 requires C++11 or later." +#endif + +#if ! defined(__has_include) +# define __has_include(x) 0 +#endif + +#if ! defined(__has_cpp_attribute) +# define __has_cpp_attribute(x) 0 +#endif + +#if ! defined(__has_builtin) +# define __has_builtin(x) 0 +#endif + +// hard to remember + +#ifndef TOML11_CXX14_VALUE +#define TOML11_CXX14_VALUE 201402L +#endif//TOML11_CXX14_VALUE + +#ifndef TOML11_CXX17_VALUE +#define TOML11_CXX17_VALUE 201703L +#endif//TOML11_CXX17_VALUE + +#ifndef TOML11_CXX20_VALUE +#define TOML11_CXX20_VALUE 202002L +#endif//TOML11_CXX20_VALUE + +#if defined(__cpp_char8_t) +# if __cpp_char8_t >= 201811L +# define TOML11_HAS_CHAR8_T 1 +# endif +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_STRING_VIEW 1 +# endif +#endif + +#ifndef TOML11_DISABLE_STD_FILESYSTEM +# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_FILESYSTEM 1 +# endif +# endif +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_OPTIONAL 1 +# endif +#endif + +#if defined(TOML11_COMPILE_SOURCES) +# define TOML11_INLINE +#else +# define TOML11_INLINE inline +#endif + +namespace toml +{ + +inline const char* license_notice() noexcept +{ + return R"(The MIT License (MIT) + +Copyright (c) 2017-now Toru Niina + +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.)"; +} + +} // toml +#endif // TOML11_VERSION_HPP +#ifndef TOML11_FORMAT_HPP +#define TOML11_FORMAT_HPP + +#ifndef TOML11_FORMAT_FWD_HPP +#define TOML11_FORMAT_FWD_HPP + +#include +#include +#include + +#include +#include + +namespace toml +{ + +// toml types with serialization info + +enum class indent_char : std::uint8_t +{ + space, // use space + tab, // use tab + none // no indent +}; + +std::ostream& operator<<(std::ostream& os, const indent_char& c); +std::string to_string(const indent_char c); + +// ---------------------------------------------------------------------------- +// boolean + +struct boolean_format_info +{ + // nothing, for now +}; + +inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept +{ + return true; +} +inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept +{ + return false; +} + +// ---------------------------------------------------------------------------- +// integer + +enum class integer_format : std::uint8_t +{ + dec = 0, + bin = 1, + oct = 2, + hex = 3, +}; + +std::ostream& operator<<(std::ostream& os, const integer_format f); +std::string to_string(const integer_format); + +struct integer_format_info +{ + integer_format fmt = integer_format::dec; + bool uppercase = true; // hex with uppercase + std::size_t width = 0; // minimal width (may exceed) + std::size_t spacer = 0; // position of `_` (if 0, no spacer) + std::string suffix = ""; // _suffix (library extension) +}; + +bool operator==(const integer_format_info&, const integer_format_info&) noexcept; +bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// floating + +enum class floating_format : std::uint8_t +{ + defaultfloat = 0, + fixed = 1, // does not include exponential part + scientific = 2, // always include exponential part + hex = 3 // hexfloat extension +}; + +std::ostream& operator<<(std::ostream& os, const floating_format f); +std::string to_string(const floating_format); + +struct floating_format_info +{ + floating_format fmt = floating_format::defaultfloat; + std::size_t prec = 0; // precision (if 0, use the default) + std::string suffix = ""; // 1.0e+2_suffix (library extension) +}; + +bool operator==(const floating_format_info&, const floating_format_info&) noexcept; +bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// string + +enum class string_format : std::uint8_t +{ + basic = 0, + literal = 1, + multiline_basic = 2, + multiline_literal = 3 +}; + +std::ostream& operator<<(std::ostream& os, const string_format f); +std::string to_string(const string_format); + +struct string_format_info +{ + string_format fmt = string_format::basic; + bool start_with_newline = false; +}; + +bool operator==(const string_format_info&, const string_format_info&) noexcept; +bool operator!=(const string_format_info&, const string_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// datetime + +enum class datetime_delimiter_kind : std::uint8_t +{ + upper_T = 0, + lower_t = 1, + space = 2, +}; +std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); +std::string to_string(const datetime_delimiter_kind); + +struct offset_datetime_format_info +{ + datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; +bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; + +struct local_datetime_format_info +{ + datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; +bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; + +struct local_date_format_info +{ + // nothing, for now +}; + +bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; +bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; + +struct local_time_format_info +{ + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; +bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// array + +enum class array_format : std::uint8_t +{ + default_format = 0, + oneline = 1, + multiline = 2, + array_of_tables = 3 // [[format.in.this.way]] +}; + +std::ostream& operator<<(std::ostream& os, const array_format f); +std::string to_string(const array_format); + +struct array_format_info +{ + array_format fmt = array_format::default_format; + indent_char indent_type = indent_char::space; + std::int32_t body_indent = 4; // indent in case of multiline + std::int32_t closing_indent = 0; // indent of `]` +}; + +bool operator==(const array_format_info&, const array_format_info&) noexcept; +bool operator!=(const array_format_info&, const array_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// table + +enum class table_format : std::uint8_t +{ + multiline = 0, // [foo] \n bar = "baz" + oneline = 1, // foo = {bar = "baz"} + dotted = 2, // foo.bar = "baz" + multiline_oneline = 3, // foo = { \n bar = "baz" \n } + implicit = 4 // [x] defined by [x.y.z]. skip in serializer. +}; + +std::ostream& operator<<(std::ostream& os, const table_format f); +std::string to_string(const table_format); + +struct table_format_info +{ + table_format fmt = table_format::multiline; + indent_char indent_type = indent_char::space; + std::int32_t body_indent = 0; // indent of values + std::int32_t name_indent = 0; // indent of [table] + std::int32_t closing_indent = 0; // in case of {inline-table} +}; + +bool operator==(const table_format_info&, const table_format_info&) noexcept; +bool operator!=(const table_format_info&, const table_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// wrapper + +namespace detail +{ +template +struct value_with_format +{ + using value_type = T; + using format_type = F; + + value_with_format() = default; + ~value_with_format() = default; + value_with_format(const value_with_format&) = default; + value_with_format(value_with_format&&) = default; + value_with_format& operator=(const value_with_format&) = default; + value_with_format& operator=(value_with_format&&) = default; + + value_with_format(value_type v, format_type f) + : value{std::move(v)}, format{std::move(f)} + {} + + template + value_with_format(value_with_format other) + : value{std::move(other.value)}, format{std::move(other.format)} + {} + + value_type value; + format_type format; +}; +} // detail + +} // namespace toml +#endif // TOML11_FORMAT_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_FORMAT_IMPL_HPP +#define TOML11_FORMAT_IMPL_HPP + + +#include +#include + +namespace toml +{ + +// toml types with serialization info + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c) +{ + switch(c) + { + case indent_char::space: {os << "space" ; break;} + case indent_char::tab: {os << "tab" ; break;} + case indent_char::none: {os << "none" ; break;} + default: + { + os << "unknown indent char: " << static_cast(c); + } + } + return os; +} + +TOML11_INLINE std::string to_string(const indent_char c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +// ---------------------------------------------------------------------------- +// boolean + +// ---------------------------------------------------------------------------- +// integer + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f) +{ + switch(f) + { + case integer_format::dec: {os << "dec"; break;} + case integer_format::bin: {os << "bin"; break;} + case integer_format::oct: {os << "oct"; break;} + case integer_format::hex: {os << "hex"; break;} + default: + { + os << "unknown integer_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const integer_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + + +TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.uppercase == rhs.uppercase && + lhs.width == rhs.width && + lhs.spacer == rhs.spacer && + lhs.suffix == rhs.suffix ; +} +TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// floating + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f) +{ + switch(f) + { + case floating_format::defaultfloat: {os << "defaultfloat"; break;} + case floating_format::fixed : {os << "fixed" ; break;} + case floating_format::scientific : {os << "scientific" ; break;} + case floating_format::hex : {os << "hex" ; break;} + default: + { + os << "unknown floating_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const floating_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.prec == rhs.prec && + lhs.suffix == rhs.suffix ; +} +TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// string + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f) +{ + switch(f) + { + case string_format::basic : {os << "basic" ; break;} + case string_format::literal : {os << "literal" ; break;} + case string_format::multiline_basic : {os << "multiline_basic" ; break;} + case string_format::multiline_literal: {os << "multiline_literal"; break;} + default: + { + os << "unknown string_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const string_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.start_with_newline == rhs.start_with_newline ; +} +TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} +// ---------------------------------------------------------------------------- +// datetime + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d) +{ + switch(d) + { + case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; } + case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; } + case datetime_delimiter_kind::space: { os << "space, "; break; } + default: + { + os << "unknown datetime delimiter: " << static_cast(d); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const datetime_delimiter_kind c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept +{ + return lhs.delimiter == rhs.delimiter && + lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept +{ + return lhs.delimiter == rhs.delimiter && + lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept +{ + return true; +} +TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept +{ + return lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// array + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f) +{ + switch(f) + { + case array_format::default_format : {os << "default_format" ; break;} + case array_format::oneline : {os << "oneline" ; break;} + case array_format::multiline : {os << "multiline" ; break;} + case array_format::array_of_tables: {os << "array_of_tables"; break;} + default: + { + os << "unknown array_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const array_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.indent_type == rhs.indent_type && + lhs.body_indent == rhs.body_indent && + lhs.closing_indent == rhs.closing_indent ; +} +TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// table + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f) +{ + switch(f) + { + case table_format::multiline : {os << "multiline" ; break;} + case table_format::oneline : {os << "oneline" ; break;} + case table_format::dotted : {os << "dotted" ; break;} + case table_format::multiline_oneline: {os << "multiline_oneline"; break;} + case table_format::implicit : {os << "implicit" ; break;} + default: + { + os << "unknown table_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const table_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.indent_type == rhs.indent_type && + lhs.body_indent == rhs.body_indent && + lhs.name_indent == rhs.name_indent && + lhs.closing_indent == rhs.closing_indent ; +} +TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +} // namespace toml +#endif // TOML11_FORMAT_IMPL_HPP +#endif + +#endif// TOML11_FORMAT_HPP +#ifndef TOML11_DATETIME_HPP +#define TOML11_DATETIME_HPP + +#ifndef TOML11_DATETIME_FWD_HPP +#define TOML11_DATETIME_FWD_HPP + +#include +#include +#include + +#include +#include +#include + +namespace toml +{ + +enum class month_t : std::uint8_t +{ + Jan = 0, + Feb = 1, + Mar = 2, + Apr = 3, + May = 4, + Jun = 5, + Jul = 6, + Aug = 7, + Sep = 8, + Oct = 9, + Nov = 10, + Dec = 11 +}; + +// ---------------------------------------------------------------------------- + +struct local_date +{ + std::int16_t year{0}; // A.D. (like, 2018) + std::uint8_t month{0}; // [0, 11] + std::uint8_t day{0}; // [1, 31] + + local_date(int y, month_t m, int d) + : year {static_cast(y)}, + month{static_cast(m)}, + day {static_cast(d)} + {} + + explicit local_date(const std::tm& t) + : year {static_cast(t.tm_year + 1900)}, + month{static_cast(t.tm_mon)}, + day {static_cast(t.tm_mday)} + {} + + explicit local_date(const std::chrono::system_clock::time_point& tp); + explicit local_date(const std::time_t t); + + operator std::chrono::system_clock::time_point() const; + operator std::time_t() const; + + local_date() = default; + ~local_date() = default; + local_date(local_date const&) = default; + local_date(local_date&&) = default; + local_date& operator=(local_date const&) = default; + local_date& operator=(local_date&&) = default; +}; +bool operator==(const local_date& lhs, const local_date& rhs); +bool operator!=(const local_date& lhs, const local_date& rhs); +bool operator< (const local_date& lhs, const local_date& rhs); +bool operator<=(const local_date& lhs, const local_date& rhs); +bool operator> (const local_date& lhs, const local_date& rhs); +bool operator>=(const local_date& lhs, const local_date& rhs); + +std::ostream& operator<<(std::ostream& os, const local_date& date); +std::string to_string(const local_date& date); + +// ----------------------------------------------------------------------------- + +struct local_time +{ + std::uint8_t hour{0}; // [0, 23] + std::uint8_t minute{0}; // [0, 59] + std::uint8_t second{0}; // [0, 60] + std::uint16_t millisecond{0}; // [0, 999] + std::uint16_t microsecond{0}; // [0, 999] + std::uint16_t nanosecond{0}; // [0, 999] + + local_time(int h, int m, int s, + int ms = 0, int us = 0, int ns = 0) + : hour {static_cast(h)}, + minute{static_cast(m)}, + second{static_cast(s)}, + millisecond{static_cast(ms)}, + microsecond{static_cast(us)}, + nanosecond {static_cast(ns)} + {} + + explicit local_time(const std::tm& t) + : hour {static_cast(t.tm_hour)}, + minute{static_cast(t.tm_min )}, + second{static_cast(t.tm_sec )}, + millisecond{0}, microsecond{0}, nanosecond{0} + {} + + template + explicit local_time(const std::chrono::duration& t) + { + const auto h = std::chrono::duration_cast(t); + this->hour = static_cast(h.count()); + const auto t2 = t - h; + const auto m = std::chrono::duration_cast(t2); + this->minute = static_cast(m.count()); + const auto t3 = t2 - m; + const auto s = std::chrono::duration_cast(t3); + this->second = static_cast(s.count()); + const auto t4 = t3 - s; + const auto ms = std::chrono::duration_cast(t4); + this->millisecond = static_cast(ms.count()); + const auto t5 = t4 - ms; + const auto us = std::chrono::duration_cast(t5); + this->microsecond = static_cast(us.count()); + const auto t6 = t5 - us; + const auto ns = std::chrono::duration_cast(t6); + this->nanosecond = static_cast(ns.count()); + } + + operator std::chrono::nanoseconds() const; + + local_time() = default; + ~local_time() = default; + local_time(local_time const&) = default; + local_time(local_time&&) = default; + local_time& operator=(local_time const&) = default; + local_time& operator=(local_time&&) = default; +}; + +bool operator==(const local_time& lhs, const local_time& rhs); +bool operator!=(const local_time& lhs, const local_time& rhs); +bool operator< (const local_time& lhs, const local_time& rhs); +bool operator<=(const local_time& lhs, const local_time& rhs); +bool operator> (const local_time& lhs, const local_time& rhs); +bool operator>=(const local_time& lhs, const local_time& rhs); + +std::ostream& operator<<(std::ostream& os, const local_time& time); +std::string to_string(const local_time& time); + +// ---------------------------------------------------------------------------- + +struct time_offset +{ + std::int8_t hour{0}; // [-12, 12] + std::int8_t minute{0}; // [-59, 59] + + time_offset(int h, int m) + : hour {static_cast(h)}, + minute{static_cast(m)} + {} + + operator std::chrono::minutes() const; + + time_offset() = default; + ~time_offset() = default; + time_offset(time_offset const&) = default; + time_offset(time_offset&&) = default; + time_offset& operator=(time_offset const&) = default; + time_offset& operator=(time_offset&&) = default; +}; + +bool operator==(const time_offset& lhs, const time_offset& rhs); +bool operator!=(const time_offset& lhs, const time_offset& rhs); +bool operator< (const time_offset& lhs, const time_offset& rhs); +bool operator<=(const time_offset& lhs, const time_offset& rhs); +bool operator> (const time_offset& lhs, const time_offset& rhs); +bool operator>=(const time_offset& lhs, const time_offset& rhs); + +std::ostream& operator<<(std::ostream& os, const time_offset& offset); + +std::string to_string(const time_offset& offset); + +// ----------------------------------------------------------------------------- + +struct local_datetime +{ + local_date date{}; + local_time time{}; + + local_datetime(local_date d, local_time t): date{d}, time{t} {} + + explicit local_datetime(const std::tm& t): date{t}, time{t}{} + + explicit local_datetime(const std::chrono::system_clock::time_point& tp); + explicit local_datetime(const std::time_t t); + + operator std::chrono::system_clock::time_point() const; + operator std::time_t() const; + + local_datetime() = default; + ~local_datetime() = default; + local_datetime(local_datetime const&) = default; + local_datetime(local_datetime&&) = default; + local_datetime& operator=(local_datetime const&) = default; + local_datetime& operator=(local_datetime&&) = default; +}; + +bool operator==(const local_datetime& lhs, const local_datetime& rhs); +bool operator!=(const local_datetime& lhs, const local_datetime& rhs); +bool operator< (const local_datetime& lhs, const local_datetime& rhs); +bool operator<=(const local_datetime& lhs, const local_datetime& rhs); +bool operator> (const local_datetime& lhs, const local_datetime& rhs); +bool operator>=(const local_datetime& lhs, const local_datetime& rhs); + +std::ostream& operator<<(std::ostream& os, const local_datetime& dt); + +std::string to_string(const local_datetime& dt); + +// ----------------------------------------------------------------------------- + +struct offset_datetime +{ + local_date date{}; + local_time time{}; + time_offset offset{}; + + offset_datetime(local_date d, local_time t, time_offset o) + : date{d}, time{t}, offset{o} + {} + offset_datetime(const local_datetime& dt, time_offset o) + : date{dt.date}, time{dt.time}, offset{o} + {} + // use the current local timezone offset + explicit offset_datetime(const local_datetime& ld); + explicit offset_datetime(const std::chrono::system_clock::time_point& tp); + explicit offset_datetime(const std::time_t& t); + explicit offset_datetime(const std::tm& t); + + operator std::chrono::system_clock::time_point() const; + + operator std::time_t() const; + + offset_datetime() = default; + ~offset_datetime() = default; + offset_datetime(offset_datetime const&) = default; + offset_datetime(offset_datetime&&) = default; + offset_datetime& operator=(offset_datetime const&) = default; + offset_datetime& operator=(offset_datetime&&) = default; + + private: + + static time_offset get_local_offset(const std::time_t* tp); +}; + +bool operator==(const offset_datetime& lhs, const offset_datetime& rhs); +bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs); +bool operator< (const offset_datetime& lhs, const offset_datetime& rhs); +bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs); +bool operator> (const offset_datetime& lhs, const offset_datetime& rhs); +bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs); + +std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); + +std::string to_string(const offset_datetime& dt); + +}//toml +#endif // TOML11_DATETIME_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_DATETIME_IMPL_HPP +#define TOML11_DATETIME_IMPL_HPP + + +#include +#include +#include +#include +#include + +#include +#include + +namespace toml +{ + +// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is +// provided in the absolutely same purpose, but C++11 is actually not compatible +// with C11. We need to dispatch the function depending on the OS. +namespace detail +{ +// TODO: find more sophisticated way to handle this +#if defined(_MSC_VER) +TOML11_INLINE std::tm localtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::localtime_s(&dst, src); + if (result) { throw std::runtime_error("localtime_s failed."); } + return dst; +} +TOML11_INLINE std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_s(&dst, src); + if (result) { throw std::runtime_error("gmtime_s failed."); } + return dst; +} +#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) +TOML11_INLINE std::tm localtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::localtime_r(src, &dst); + if (!result) { throw std::runtime_error("localtime_r failed."); } + return dst; +} +TOML11_INLINE std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_r(src, &dst); + if (!result) { throw std::runtime_error("gmtime_r failed."); } + return dst; +} +#else // fallback. not threadsafe +TOML11_INLINE std::tm localtime_s(const std::time_t* src) +{ + const auto result = std::localtime(src); + if (!result) { throw std::runtime_error("localtime failed."); } + return *result; +} +TOML11_INLINE std::tm gmtime_s(const std::time_t* src) +{ + const auto result = std::gmtime(src); + if (!result) { throw std::runtime_error("gmtime failed."); } + return *result; +} +#endif +} // detail + +// ---------------------------------------------------------------------------- + +TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point& tp) +{ + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto time = detail::localtime_s(&t); + *this = local_date(time); +} + +TOML11_INLINE local_date::local_date(const std::time_t t) + : local_date{std::chrono::system_clock::from_time_t(t)} +{} + +TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const +{ + // std::mktime returns date as local time zone. no conversion needed + std::tm t; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = static_cast(this->day); + t.tm_mon = static_cast(this->month); + t.tm_year = static_cast(this->year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + return std::chrono::system_clock::from_time_t(std::mktime(&t)); +} + +TOML11_INLINE local_date::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE bool operator==(const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) == + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) < + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date) +{ + os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; + return os; +} + +TOML11_INLINE std::string to_string(const local_date& date) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << date; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + +TOML11_INLINE local_time::operator std::chrono::nanoseconds() const +{ + return std::chrono::nanoseconds (this->nanosecond) + + std::chrono::microseconds(this->microsecond) + + std::chrono::milliseconds(this->millisecond) + + std::chrono::seconds(this->second) + + std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); +} + +TOML11_INLINE bool operator==(const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time) +{ + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second); + if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) + { + os << '.'; + os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); + if(time.microsecond != 0 || time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); + } + } + } + return os; +} + +TOML11_INLINE std::string to_string(const local_time& time) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << time; + return oss.str(); +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE time_offset::operator std::chrono::minutes() const +{ + return std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); +} + +TOML11_INLINE bool operator==(const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) == + std::make_tuple(rhs.hour, rhs.minute); +} +TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) < + std::make_tuple(rhs.hour, rhs.minute); +} +TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset) +{ + if(offset.hour == 0 && offset.minute == 0) + { + os << 'Z'; + return os; + } + int minute = static_cast(offset.hour) * 60 + offset.minute; + if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} + os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; + os << std::setfill('0') << std::setw(2) << minute % 60; + return os; +} + +TOML11_INLINE std::string to_string(const time_offset& offset) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << offset; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + +TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp) +{ + const auto t = std::chrono::system_clock::to_time_t(tp); + std::tm ltime = detail::localtime_s(&t); + + this->date = local_date(ltime); + this->time = local_time(ltime); + + // std::tm lacks subsecond information, so diff between tp and tm + // can be used to get millisecond & microsecond information. + const auto t_diff = tp - + std::chrono::system_clock::from_time_t(std::mktime(<ime)); + this->time.millisecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.microsecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.nanosecond = static_cast( + std::chrono::duration_cast(t_diff).count()); +} + +TOML11_INLINE local_datetime::local_datetime(const std::time_t t) + : local_datetime{std::chrono::system_clock::from_time_t(t)} +{} + +TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const +{ + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + + // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator + // of local_date and local_time independently, the conversion fails if + // it is the day when DST begins or ends. Since local_date considers the + // time is 00:00 A.M. and local_time does not consider DST because it + // does not have any date information. We need to consider both date and + // time information at the same time to convert it correctly. + + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + + // std::mktime returns date as local time zone. no conversion needed + auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); + dt += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + return dt; +} + +TOML11_INLINE local_datetime::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE bool operator==(const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) == + std::make_tuple(rhs.date, rhs.time); +} +TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) < + std::make_tuple(rhs.date, rhs.time); +} +TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt) +{ + os << dt.date << 'T' << dt.time; + return os; +} + +TOML11_INLINE std::string to_string(const local_datetime& dt) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << dt; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + + +TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) + : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)} + // use the current local timezone offset +{} +TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp) + : offset{0, 0} // use gmtime +{ + const auto timet = std::chrono::system_clock::to_time_t(tp); + const auto tm = detail::gmtime_s(&timet); + this->date = local_date(tm); + this->time = local_time(tm); +} +TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t) + : offset{0, 0} // use gmtime +{ + const auto tm = detail::gmtime_s(&t); + this->date = local_date(tm); + this->time = local_time(tm); +} +TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) + : offset{0, 0} // assume gmtime +{ + this->date = local_date(t); + this->time = local_time(t); +} + +TOML11_INLINE offset_datetime::operator std::chrono::system_clock::time_point() const +{ + // get date-time + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + + // first, convert it to local date-time information in the same way as + // local_datetime does. later we will use time_t to adjust time offset. + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + const std::time_t tp_loc = std::mktime(std::addressof(t)); + + auto tp = std::chrono::system_clock::from_time_t(tp_loc); + tp += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + + // Since mktime uses local time zone, it should be corrected. + // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if + // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need + // to add `+09:00` to `03:00:00Z`. + // Here, it uses the time_t converted from date-time info to handle + // daylight saving time. + const auto ofs = get_local_offset(std::addressof(tp_loc)); + tp += std::chrono::hours (ofs.hour); + tp += std::chrono::minutes(ofs.minute); + + // We got `12:00:00Z` by correcting local timezone applied by mktime. + // Then we will apply the offset. Let's say `12:00:00-08:00` is given. + // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. + // So we need to subtract the offset. + tp -= std::chrono::minutes(this->offset); + return tp; +} + +TOML11_INLINE offset_datetime::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE time_offset offset_datetime::get_local_offset(const std::time_t* tp) +{ + // get local timezone with the same date-time information as mktime + const auto t = detail::localtime_s(tp); + + std::array buf; + const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 + if(result != 5) + { + throw std::runtime_error("toml::offset_datetime: cannot obtain " + "timezone information of current env"); + } + const int ofs = std::atoi(buf.data()); + const int ofs_h = ofs / 100; + const int ofs_m = ofs - (ofs_h * 100); + return time_offset(ofs_h, ofs_m); +} + +TOML11_INLINE bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) == + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) < + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) +{ + os << dt.date << 'T' << dt.time << dt.offset; + return os; +} + +TOML11_INLINE std::string to_string(const offset_datetime& dt) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << dt; + return oss.str(); +} + +}//toml +#endif // TOML11_DATETIME_IMPL_HPP +#endif + +#endif // TOML11_DATETIME_HPP +#ifndef TOML11_COMPAT_HPP +#define TOML11_COMPAT_HPP + + +#include +#include +#include +#include +#include + +#include + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if __has_include() +# include +# endif +#endif + +#include + +// ---------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if __has_cpp_attribute(deprecated) +# define TOML11_HAS_ATTR_DEPRECATED 1 +# endif +#endif + +#if defined(TOML11_HAS_ATTR_DEPRECATED) +# define TOML11_DEPRECATED(msg) [[deprecated(msg)]] +#elif defined(__GNUC__) +# define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg))) +#elif defined(_MSC_VER) +# define TOML11_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +# define TOML11_DEPRECATED(msg) +#endif + +// ---------------------------------------------------------------------------- + +#if defined(__cpp_if_constexpr) +# if __cpp_if_constexpr >= 201606L +# define TOML11_HAS_CONSTEXPR_IF 1 +# endif +#endif + +#if defined(TOML11_HAS_CONSTEXPR_IF) +# define TOML11_CONSTEXPR_IF if constexpr +#else +# define TOML11_CONSTEXPR_IF if +#endif + +// ---------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_make_unique) +# if __cpp_lib_make_unique >= 201304L +# define TOML11_HAS_STD_MAKE_UNIQUE 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ + +#if defined(TOML11_HAS_STD_MAKE_UNIQUE) + +using std::make_unique; + +#else + +template +std::unique_ptr make_unique(Ts&& ... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +#endif // TOML11_HAS_STD_MAKE_UNIQUE + +} // cxx +} // toml + +// --------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_make_reverse_iterator) +# if __cpp_lib_make_reverse_iterator >= 201402L +# define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +# if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR) + +using std::make_reverse_iterator; + +#else + +template +std::reverse_iterator make_reverse_iterator(Iterator iter) +{ + return std::reverse_iterator(iter); +} + +#endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR + +} // cxx +} // toml + +// --------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if defined(__cpp_lib_clamp) +# if __cpp_lib_clamp >= 201603L +# define TOML11_HAS_STD_CLAMP 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_CLAMP) + +using std::clamp; + +#else + +template +T clamp(const T& x, const T& low, const T& high) noexcept +{ + assert(low <= high); + return (std::min)((std::max)(x, low), high); +} + +#endif // TOML11_HAS_STD_CLAMP + +} // cxx +} // toml + +// --------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if defined(__cpp_lib_bit_cast) +# if __cpp_lib_bit_cast >= 201806L +# define TOML11_HAS_STD_BIT_CAST 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_BIT_CAST) + +using std::bit_cast; + +#else + +template +U bit_cast(const T& x) noexcept +{ + static_assert(sizeof(T) == sizeof(U), ""); + static_assert(std::is_default_constructible::value, ""); + + U z; + std::memcpy(reinterpret_cast(std::addressof(z)), + reinterpret_cast(std::addressof(x)), + sizeof(T)); + + return z; +} + +#endif // TOML11_HAS_STD_BIT_CAST + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++20 remove_cvref_t + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if defined(__cpp_lib_remove_cvref) +# if __cpp_lib_remove_cvref >= 201711L +# define TOML11_HAS_STD_REMOVE_CVREF 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_REMOVE_CVREF) + +using std::remove_cvref; +using std::remove_cvref_t; + +#else + +template +struct remove_cvref +{ + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template +using remove_cvref_t = typename remove_cvref::type; + +#endif // TOML11_HAS_STD_REMOVE_CVREF + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++17 and/or/not + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if defined(__cpp_lib_logical_traits) +# if __cpp_lib_logical_traits >= 201510L +# define TOML11_HAS_STD_CONJUNCTION 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_CONJUNCTION) + +using std::conjunction; +using std::disjunction; +using std::negation; + +#else + +template struct conjunction : std::true_type{}; +template struct conjunction : T{}; +template +struct conjunction : + std::conditional(T::value), conjunction, T>::type +{}; + +template struct disjunction : std::false_type{}; +template struct disjunction : T {}; +template +struct disjunction : + std::conditional(T::value), T, disjunction>::type +{}; + +template +struct negation : std::integral_constant(T::value)>{}; + +#endif // TOML11_HAS_STD_CONJUNCTION + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++14 index_sequence + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_integer_sequence) +# if __cpp_lib_integer_sequence >= 201304L +# define TOML11_HAS_STD_INTEGER_SEQUENCE 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_INTEGER_SEQUENCE) + +using std::index_sequence; +using std::make_index_sequence; + +#else + +template struct index_sequence{}; + +template +struct double_index_sequence; + +template +struct double_index_sequence> +{ + using type = index_sequence; +}; +template +struct double_index_sequence> +{ + using type = index_sequence; +}; + +template +struct index_sequence_maker +{ + using type = typename double_index_sequence< + N % 2 == 1, N/2, typename index_sequence_maker::type + >::type; +}; +template<> +struct index_sequence_maker<0> +{ + using type = index_sequence<>; +}; + +template +using make_index_sequence = typename index_sequence_maker::type; + +#endif // TOML11_HAS_STD_INTEGER_SEQUENCE + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++14 enable_if_t + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_transformation_trait_aliases) +# if __cpp_lib_transformation_trait_aliases >= 201304L +# define TOML11_HAS_STD_ENABLE_IF_T 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_ENABLE_IF_T) + +using std::enable_if_t; + +#else + +template +using enable_if_t = typename std::enable_if::type; + +#endif // TOML11_HAS_STD_ENABLE_IF_T + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// return_type_of_t + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if defined(__cpp_lib_is_invocable) +# if __cpp_lib_is_invocable >= 201703 +# define TOML11_HAS_STD_INVOKE_RESULT 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_INVOKE_RESULT) + +template +using return_type_of_t = std::invoke_result_t; + +#else + +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif // TOML11_HAS_STD_INVOKE_RESULT + +} // cxx +} // toml + +// ---------------------------------------------------------------------------- +// (subset of) source_location + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L +# if __has_include() +# define TOML11_HAS_STD_SOURCE_LOCATION +# endif // has_include +#endif // c++20 + +#if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) +# if defined(__GNUC__) && ! defined(__clang__) +# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if __has_include() +# define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION +# endif +# endif +# endif // GNU g++ +#endif // not TOML11_HAS_STD_SOURCE_LOCATION + +#if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) +# if defined(__GNUC__) && ! defined(__clang__) +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) +# define TOML11_HAS_BUILTIN_FILE_LINE 1 +# define TOML11_BUILTIN_LINE_TYPE int +# endif +# elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE +# if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE) +# define TOML11_HAS_BUILTIN_FILE_LINE 1 +# define TOML11_BUILTIN_LINE_TYPE unsigned int +# endif +# elif defined(_MSVC_LANG) && defined(_MSC_VER) +# if _MSC_VER > 1926 +# define TOML11_HAS_BUILTIN_FILE_LINE 1 +# define TOML11_BUILTIN_LINE_TYPE int +# endif +# endif +#endif + +#if defined(TOML11_HAS_STD_SOURCE_LOCATION) +#include +namespace toml +{ +namespace cxx +{ +using source_location = std::source_location; + +inline std::string to_string(const source_location& loc) +{ + return std::string(" at line ") + std::to_string(loc.line()) + + std::string(" in file ") + std::string(loc.file_name()); +} +} // cxx +} // toml +#elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) +#include +namespace toml +{ +namespace cxx +{ +using source_location = std::experimental::source_location; + +inline std::string to_string(const source_location& loc) +{ + return std::string(" at line ") + std::to_string(loc.line()) + + std::string(" in file ") + std::string(loc.file_name()); +} +} // cxx +} // toml +#elif defined(TOML11_HAS_BUILTIN_FILE_LINE) +namespace toml +{ +namespace cxx +{ +struct source_location +{ + using line_type = TOML11_BUILTIN_LINE_TYPE; + static source_location current(const line_type line = __builtin_LINE(), + const char* file = __builtin_FILE()) + { + return source_location(line, file); + } + + source_location(const line_type line, const char* file) + : line_(line), file_name_(file) + {} + + line_type line() const noexcept {return line_;} + const char* file_name() const noexcept {return file_name_;} + + private: + + line_type line_; + const char* file_name_; +}; + +inline std::string to_string(const source_location& loc) +{ + return std::string(" at line ") + std::to_string(loc.line()) + + std::string(" in file ") + std::string(loc.file_name()); +} +} // cxx +} // toml +#else // no builtin +namespace toml +{ +namespace cxx +{ +struct source_location +{ + static source_location current() { return source_location{}; } +}; + +inline std::string to_string(const source_location&) +{ + return std::string(""); +} +} // cxx +} // toml +#endif // TOML11_HAS_STD_SOURCE_LOCATION + +// ---------------------------------------------------------------------------- +// (subset of) optional + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# include +# endif // has_include(optional) +#endif // C++17 + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if defined(__cpp_lib_optional) +# if __cpp_lib_optional >= 201606L +# define TOML11_HAS_STD_OPTIONAL 1 +# endif +# endif +#endif + +#if defined(TOML11_HAS_STD_OPTIONAL) + +namespace toml +{ +namespace cxx +{ +using std::optional; + +inline std::nullopt_t make_nullopt() {return std::nullopt;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const std::nullopt_t&) +{ + os << "nullopt"; + return os; +} + +} // cxx +} // toml + +#else // TOML11_HAS_STD_OPTIONAL + +namespace toml +{ +namespace cxx +{ + +struct nullopt_t{}; +inline nullopt_t make_nullopt() {return nullopt_t{};} + +inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;} +inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;} +inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;} +inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;} +inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;} +inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const nullopt_t&) +{ + os << "nullopt"; + return os; +} + +template +class optional +{ + public: + + using value_type = T; + + public: + + optional() noexcept : has_value_(false), null_('\0') {} + optional(nullopt_t) noexcept : has_value_(false), null_('\0') {} + + optional(const T& x): has_value_(true), value_(x) {} + optional(T&& x): has_value_(true), value_(std::move(x)) {} + + template::value, std::nullptr_t> = nullptr> + explicit optional(U&& x): has_value_(true), value_(std::forward(x)) {} + + optional(const optional& rhs): has_value_(rhs.has_value_) + { + if(rhs.has_value_) + { + this->assigner(rhs.value_); + } + } + optional(optional&& rhs): has_value_(rhs.has_value_) + { + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + } + + optional& operator=(const optional& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(rhs.value_); + } + return *this; + } + optional& operator=(optional&& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + return *this; + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + explicit optional(const optional& rhs): has_value_(rhs.has_value_), null_('\0') + { + if(rhs.has_value_) + { + this->assigner(rhs.value_); + } + } + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + explicit optional(optional&& rhs): has_value_(rhs.has_value_), null_('\0') + { + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + optional& operator=(const optional& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(rhs.value_); + } + return *this; + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + optional& operator=(optional&& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + return *this; + } + ~optional() noexcept + { + this->cleanup(); + } + + explicit operator bool() const noexcept + { + return has_value_; + } + + bool has_value() const noexcept {return has_value_;} + + value_type const& value(source_location loc = source_location::current()) const + { + if( ! this->has_value_) + { + throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); + } + return this->value_; + } + value_type& value(source_location loc = source_location::current()) + { + if( ! this->has_value_) + { + throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); + } + return this->value_; + } + + value_type const& value_or(const value_type& opt) const + { + if(this->has_value_) {return this->value_;} else {return opt;} + } + value_type& value_or(value_type& opt) + { + if(this->has_value_) {return this->value_;} else {return opt;} + } + + private: + + void cleanup() noexcept + { + if(this->has_value_) + { + value_.~T(); + } + } + + template + void assigner(U&& x) + { + const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward(x)); + assert(tmp == std::addressof(this->value_)); + (void)tmp; + } + + private: + + bool has_value_; + union + { + char null_; + T value_; + }; +}; +} // cxx +} // toml +#endif // TOML11_HAS_STD_OPTIONAL + +#endif // TOML11_COMPAT_HPP +#ifndef TOML11_VALUE_T_HPP +#define TOML11_VALUE_T_HPP + +#ifndef TOML11_VALUE_T_FWD_HPP +#define TOML11_VALUE_T_FWD_HPP + + +#include +#include +#include + +#include + +namespace toml +{ + +// forward decl +template +class basic_value; + +// ---------------------------------------------------------------------------- +// enum representing toml types + +enum class value_t : std::uint8_t +{ + empty = 0, + boolean = 1, + integer = 2, + floating = 3, + string = 4, + offset_datetime = 5, + local_datetime = 6, + local_date = 7, + local_time = 8, + array = 9, + table = 10 +}; + +std::ostream& operator<<(std::ostream& os, value_t t); +std::string to_string(value_t t); + + +// ---------------------------------------------------------------------------- +// meta functions for internal use + +namespace detail +{ + +template +using value_t_constant = std::integral_constant; + +template +struct type_to_enum : value_t_constant {}; + +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; + +template +struct enum_to_type { using type = void; }; + +template struct enum_to_type { using type = typename V::boolean_type ; }; +template struct enum_to_type { using type = typename V::integer_type ; }; +template struct enum_to_type { using type = typename V::floating_type ; }; +template struct enum_to_type { using type = typename V::string_type ; }; +template struct enum_to_type { using type = typename V::offset_datetime_type; }; +template struct enum_to_type { using type = typename V::local_datetime_type ; }; +template struct enum_to_type { using type = typename V::local_date_type ; }; +template struct enum_to_type { using type = typename V::local_time_type ; }; +template struct enum_to_type { using type = typename V::array_type ; }; +template struct enum_to_type { using type = typename V::table_type ; }; + +template +using enum_to_type_t = typename enum_to_type::type; + +template +struct enum_to_fmt_type { using type = void; }; + +template<> struct enum_to_fmt_type { using type = boolean_format_info ; }; +template<> struct enum_to_fmt_type { using type = integer_format_info ; }; +template<> struct enum_to_fmt_type { using type = floating_format_info ; }; +template<> struct enum_to_fmt_type { using type = string_format_info ; }; +template<> struct enum_to_fmt_type { using type = offset_datetime_format_info; }; +template<> struct enum_to_fmt_type { using type = local_datetime_format_info ; }; +template<> struct enum_to_fmt_type { using type = local_date_format_info ; }; +template<> struct enum_to_fmt_type { using type = local_time_format_info ; }; +template<> struct enum_to_fmt_type { using type = array_format_info ; }; +template<> struct enum_to_fmt_type { using type = table_format_info ; }; + +template +using enum_to_fmt_type_t = typename enum_to_fmt_type::type; + +template +struct is_exact_toml_type0 : cxx::disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same + >{}; +template struct is_exact_toml_type: is_exact_toml_type0, V> {}; +template struct is_not_toml_type : cxx::negation> {}; + +} // namespace detail +} // namespace toml +#endif // TOML11_VALUE_T_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_VALUE_T_IMPL_HPP +#define TOML11_VALUE_T_IMPL_HPP + + +#include +#include +#include + +namespace toml +{ + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, value_t t) +{ + switch(t) + { + case value_t::boolean : os << "boolean"; return os; + case value_t::integer : os << "integer"; return os; + case value_t::floating : os << "floating"; return os; + case value_t::string : os << "string"; return os; + case value_t::offset_datetime : os << "offset_datetime"; return os; + case value_t::local_datetime : os << "local_datetime"; return os; + case value_t::local_date : os << "local_date"; return os; + case value_t::local_time : os << "local_time"; return os; + case value_t::array : os << "array"; return os; + case value_t::table : os << "table"; return os; + case value_t::empty : os << "empty"; return os; + default : os << "unknown"; return os; + } +} + +TOML11_INLINE std::string to_string(value_t t) +{ + std::ostringstream oss; + oss << t; + return oss.str(); +} + +} // namespace toml +#endif // TOML11_VALUE_T_IMPL_HPP +#endif + +#endif // TOML11_VALUE_T_HPP +#ifndef TOML11_STORAGE_HPP +#define TOML11_STORAGE_HPP + + +namespace toml +{ +namespace detail +{ + +// It owns a pointer to T. It does deep-copy when copied. +// This struct is introduced to implement a recursive type. +// +// `toml::value` contains `std::vector` to represent a toml array. +// But, in the definition of `toml::value`, `toml::value` is still incomplete. +// `std::vector` of an incomplete type is not allowed in C++11 (it is allowed +// after C++17). To avoid this, we need to use a pointer to `toml::value`, like +// `std::vector>`. Although `std::unique_ptr` is +// noncopyable, we want to make `toml::value` copyable. `storage` is introduced +// to resolve those problems. +template +struct storage +{ + using value_type = T; + + explicit storage(value_type v): ptr_(cxx::make_unique(std::move(v))) {} + ~storage() = default; + + storage(const storage& rhs): ptr_(cxx::make_unique(*rhs.ptr_)) {} + storage& operator=(const storage& rhs) + { + this->ptr_ = cxx::make_unique(*rhs.ptr_); + return *this; + } + + storage(storage&&) = default; + storage& operator=(storage&&) = default; + + bool is_ok() const noexcept {return static_cast(ptr_);} + + value_type& get() const noexcept {return *ptr_;} + + private: + std::unique_ptr ptr_; +}; + +} // detail +} // toml +#endif // TOML11_STORAGE_HPP +#ifndef TOML11_COMMENTS_HPP +#define TOML11_COMMENTS_HPP + +#ifndef TOML11_COMMENTS_FWD_HPP +#define TOML11_COMMENTS_FWD_HPP + +// to use __has_builtin + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This file provides mainly two classes, `preserve_comments` and `discard_comments`. +// Those two are a container that have the same interface as `std::vector` +// but bahaves in the opposite way. `preserve_comments` is just the same as +// `std::vector` and each `std::string` corresponds to a comment line. +// Conversely, `discard_comments` discards all the strings and ignores everything +// assigned in it. `discard_comments` is always empty and you will encounter an +// error whenever you access to the element. +namespace toml +{ +class discard_comments; // forward decl + +class preserve_comments +{ + public: + // `container_type` is not provided in discard_comments. + // do not use this inner-type in a generic code. + using container_type = std::vector; + + using size_type = container_type::size_type; + using difference_type = container_type::difference_type; + using value_type = container_type::value_type; + using reference = container_type::reference; + using const_reference = container_type::const_reference; + using pointer = container_type::pointer; + using const_pointer = container_type::const_pointer; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using reverse_iterator = container_type::reverse_iterator; + using const_reverse_iterator = container_type::const_reverse_iterator; + + public: + + preserve_comments() = default; + ~preserve_comments() = default; + preserve_comments(preserve_comments const&) = default; + preserve_comments(preserve_comments &&) = default; + preserve_comments& operator=(preserve_comments const&) = default; + preserve_comments& operator=(preserve_comments &&) = default; + + explicit preserve_comments(const std::vector& c): comments(c){} + explicit preserve_comments(std::vector&& c) + : comments(std::move(c)) + {} + preserve_comments& operator=(const std::vector& c) + { + comments = c; + return *this; + } + preserve_comments& operator=(std::vector&& c) + { + comments = std::move(c); + return *this; + } + + explicit preserve_comments(const discard_comments&) {} + + explicit preserve_comments(size_type n): comments(n) {} + preserve_comments(size_type n, const std::string& x): comments(n, x) {} + preserve_comments(std::initializer_list x): comments(x) {} + template + preserve_comments(InputIterator first, InputIterator last) + : comments(first, last) + {} + + template + void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} + void assign(std::initializer_list ini) {comments.assign(ini);} + void assign(size_type n, const std::string& val) {comments.assign(n, val);} + + // Related to the issue #97. + // + // `std::vector::insert` and `std::vector::erase` in the STL implementation + // included in GCC 4.8.5 takes `std::vector::iterator` instead of + // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5. +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) +# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 +# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION +# endif +#endif + +#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION + iterator insert(iterator p, const std::string& x) + { + return comments.insert(p, x); + } + iterator insert(iterator p, std::string&& x) + { + return comments.insert(p, std::move(x)); + } + void insert(iterator p, size_type n, const std::string& x) + { + return comments.insert(p, n, x); + } + template + void insert(iterator p, InputIterator first, InputIterator last) + { + return comments.insert(p, first, last); + } + void insert(iterator p, std::initializer_list ini) + { + return comments.insert(p, ini); + } + + template + iterator emplace(iterator p, Ts&& ... args) + { + return comments.emplace(p, std::forward(args)...); + } + + iterator erase(iterator pos) {return comments.erase(pos);} + iterator erase(iterator first, iterator last) + { + return comments.erase(first, last); + } +#else + iterator insert(const_iterator p, const std::string& x) + { + return comments.insert(p, x); + } + iterator insert(const_iterator p, std::string&& x) + { + return comments.insert(p, std::move(x)); + } + iterator insert(const_iterator p, size_type n, const std::string& x) + { + return comments.insert(p, n, x); + } + template + iterator insert(const_iterator p, InputIterator first, InputIterator last) + { + return comments.insert(p, first, last); + } + iterator insert(const_iterator p, std::initializer_list ini) + { + return comments.insert(p, ini); + } + + template + iterator emplace(const_iterator p, Ts&& ... args) + { + return comments.emplace(p, std::forward(args)...); + } + + iterator erase(const_iterator pos) {return comments.erase(pos);} + iterator erase(const_iterator first, const_iterator last) + { + return comments.erase(first, last); + } +#endif + + void swap(preserve_comments& other) {comments.swap(other.comments);} + + void push_back(const std::string& v) {comments.push_back(v);} + void push_back(std::string&& v) {comments.push_back(std::move(v));} + void pop_back() {comments.pop_back();} + + template + void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} + + void clear() {comments.clear();} + + size_type size() const noexcept {return comments.size();} + size_type max_size() const noexcept {return comments.max_size();} + size_type capacity() const noexcept {return comments.capacity();} + bool empty() const noexcept {return comments.empty();} + + void reserve(size_type n) {comments.reserve(n);} + void resize(size_type n) {comments.resize(n);} + void resize(size_type n, const std::string& c) {comments.resize(n, c);} + void shrink_to_fit() {comments.shrink_to_fit();} + + reference operator[](const size_type n) noexcept {return comments[n];} + const_reference operator[](const size_type n) const noexcept {return comments[n];} + reference at(const size_type n) {return comments.at(n);} + const_reference at(const size_type n) const {return comments.at(n);} + reference front() noexcept {return comments.front();} + const_reference front() const noexcept {return comments.front();} + reference back() noexcept {return comments.back();} + const_reference back() const noexcept {return comments.back();} + + pointer data() noexcept {return comments.data();} + const_pointer data() const noexcept {return comments.data();} + + iterator begin() noexcept {return comments.begin();} + iterator end() noexcept {return comments.end();} + const_iterator begin() const noexcept {return comments.begin();} + const_iterator end() const noexcept {return comments.end();} + const_iterator cbegin() const noexcept {return comments.cbegin();} + const_iterator cend() const noexcept {return comments.cend();} + + reverse_iterator rbegin() noexcept {return comments.rbegin();} + reverse_iterator rend() noexcept {return comments.rend();} + const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} + const_reverse_iterator rend() const noexcept {return comments.rend();} + const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} + const_reverse_iterator crend() const noexcept {return comments.crend();} + + friend bool operator==(const preserve_comments&, const preserve_comments&); + friend bool operator!=(const preserve_comments&, const preserve_comments&); + friend bool operator< (const preserve_comments&, const preserve_comments&); + friend bool operator<=(const preserve_comments&, const preserve_comments&); + friend bool operator> (const preserve_comments&, const preserve_comments&); + friend bool operator>=(const preserve_comments&, const preserve_comments&); + + friend void swap(preserve_comments&, std::vector&); + friend void swap(std::vector&, preserve_comments&); + + private: + + container_type comments; +}; + +bool operator==(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator< (const preserve_comments& lhs, const preserve_comments& rhs); +bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator> (const preserve_comments& lhs, const preserve_comments& rhs); +bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs); + +void swap(preserve_comments& lhs, preserve_comments& rhs); +void swap(preserve_comments& lhs, std::vector& rhs); +void swap(std::vector& lhs, preserve_comments& rhs); + +std::ostream& operator<<(std::ostream& os, const preserve_comments& com); + +namespace detail +{ + +// To provide the same interface with `preserve_comments`, `discard_comments` +// should have an iterator. But it does not contain anything, so we need to +// add an iterator that points nothing. +// +// It always points null, so DO NOT unwrap this iterator. It always crashes +// your program. +template +struct empty_iterator +{ + using value_type = T; + using reference_type = typename std::conditional::type; + using pointer_type = typename std::conditional::type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + empty_iterator() = default; + ~empty_iterator() = default; + empty_iterator(empty_iterator const&) = default; + empty_iterator(empty_iterator &&) = default; + empty_iterator& operator=(empty_iterator const&) = default; + empty_iterator& operator=(empty_iterator &&) = default; + + // DO NOT call these operators. + reference_type operator*() const noexcept {std::terminate();} + pointer_type operator->() const noexcept {return nullptr;} + reference_type operator[](difference_type) const noexcept {return this->operator*();} + + // These operators do nothing. + empty_iterator& operator++() noexcept {return *this;} + empty_iterator operator++(int) noexcept {return *this;} + empty_iterator& operator--() noexcept {return *this;} + empty_iterator operator--(int) noexcept {return *this;} + + empty_iterator& operator+=(difference_type) noexcept {return *this;} + empty_iterator& operator-=(difference_type) noexcept {return *this;} + + empty_iterator operator+(difference_type) const noexcept {return *this;} + empty_iterator operator-(difference_type) const noexcept {return *this;} +}; + +template +bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} +template +bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} +template +bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} + +template +typename empty_iterator::difference_type +operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} + +template +empty_iterator +operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} +template +empty_iterator +operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} + +} // detail + +// The default comment type. It discards all the comments. It requires only one +// byte to contain, so the memory footprint is smaller than preserve_comments. +// +// It just ignores `push_back`, `insert`, `erase`, and any other modifications. +// IT always returns size() == 0, the iterator taken by `begin()` is always the +// same as that of `end()`, and accessing through `operator[]` or iterators +// always causes a segmentation fault. DO NOT access to the element of this. +// +// Why this is chose as the default type is because the last version (2.x.y) +// does not contain any comments in a value. To minimize the impact on the +// efficiency, this is chosen as a default. +// +// To reduce the memory footprint, later we can try empty base optimization (EBO). +class discard_comments +{ + public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using reference = std::string&; + using const_reference = std::string const&; + using pointer = std::string*; + using const_pointer = std::string const*; + using iterator = detail::empty_iterator; + using const_iterator = detail::empty_iterator; + using reverse_iterator = detail::empty_iterator; + using const_reverse_iterator = detail::empty_iterator; + + public: + discard_comments() = default; + ~discard_comments() = default; + discard_comments(discard_comments const&) = default; + discard_comments(discard_comments &&) = default; + discard_comments& operator=(discard_comments const&) = default; + discard_comments& operator=(discard_comments &&) = default; + + explicit discard_comments(const std::vector&) noexcept {} + explicit discard_comments(std::vector&&) noexcept {} + discard_comments& operator=(const std::vector&) noexcept {return *this;} + discard_comments& operator=(std::vector&&) noexcept {return *this;} + + explicit discard_comments(const preserve_comments&) noexcept {} + + explicit discard_comments(size_type) noexcept {} + discard_comments(size_type, const std::string&) noexcept {} + discard_comments(std::initializer_list) noexcept {} + template + discard_comments(InputIterator, InputIterator) noexcept {} + + template + void assign(InputIterator, InputIterator) noexcept {} + void assign(std::initializer_list) noexcept {} + void assign(size_type, const std::string&) noexcept {} + + iterator insert(const_iterator, const std::string&) {return iterator{};} + iterator insert(const_iterator, std::string&&) {return iterator{};} + iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} + template + iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} + iterator insert(const_iterator, std::initializer_list) {return iterator{};} + + template + iterator emplace(const_iterator, Ts&& ...) {return iterator{};} + iterator erase(const_iterator) {return iterator{};} + iterator erase(const_iterator, const_iterator) {return iterator{};} + + void swap(discard_comments&) {return;} + + void push_back(const std::string&) {return;} + void push_back(std::string&& ) {return;} + void pop_back() {return;} + + template + void emplace_back(Ts&& ...) {return;} + + void clear() {return;} + + size_type size() const noexcept {return 0;} + size_type max_size() const noexcept {return 0;} + size_type capacity() const noexcept {return 0;} + bool empty() const noexcept {return true;} + + void reserve(size_type) {return;} + void resize(size_type) {return;} + void resize(size_type, const std::string&) {return;} + void shrink_to_fit() {return;} + + // DO NOT access to the element of this container. This container is always + // empty, so accessing through operator[], front/back, data causes address + // error. + + reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} + const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} + reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} + const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} + reference front() noexcept {never_call("toml::discard_comment::front");} + const_reference front() const noexcept {never_call("toml::discard_comment::front");} + reference back() noexcept {never_call("toml::discard_comment::back");} + const_reference back() const noexcept {never_call("toml::discard_comment::back");} + + pointer data() noexcept {return nullptr;} + const_pointer data() const noexcept {return nullptr;} + + iterator begin() noexcept {return iterator{};} + iterator end() noexcept {return iterator{};} + const_iterator begin() const noexcept {return const_iterator{};} + const_iterator end() const noexcept {return const_iterator{};} + const_iterator cbegin() const noexcept {return const_iterator{};} + const_iterator cend() const noexcept {return const_iterator{};} + + reverse_iterator rbegin() noexcept {return iterator{};} + reverse_iterator rend() noexcept {return iterator{};} + const_reverse_iterator rbegin() const noexcept {return const_iterator{};} + const_reverse_iterator rend() const noexcept {return const_iterator{};} + const_reverse_iterator crbegin() const noexcept {return const_iterator{};} + const_reverse_iterator crend() const noexcept {return const_iterator{};} + + private: + + [[noreturn]] static void never_call(const char *const this_function) + { +#if __has_builtin(__builtin_unreachable) + __builtin_unreachable(); +#endif + throw std::logic_error{this_function}; + } +}; + +inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} +inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} +inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} + +inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} + +inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;} + +} // toml11 +#endif // TOML11_COMMENTS_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_COMMENTS_IMPL_HPP +#define TOML11_COMMENTS_IMPL_HPP + + +namespace toml +{ + +TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} +TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} +TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} +TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} +TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} +TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} + +TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs); + return; +} +TOML11_INLINE void swap(preserve_comments& lhs, std::vector& rhs) +{ + lhs.comments.swap(rhs); + return; +} +TOML11_INLINE void swap(std::vector& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs.comments); + return; +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com) +{ + for(const auto& c : com) + { + if(c.front() != '#') + { + os << '#'; + } + os << c << '\n'; + } + return os; +} + +} // toml11 +#endif // TOML11_COMMENTS_IMPL_HPP +#endif + +#endif // TOML11_COMMENTS_HPP +#ifndef TOML11_COLOR_HPP +#define TOML11_COLOR_HPP + +#ifndef TOML11_COLOR_FWD_HPP +#define TOML11_COLOR_FWD_HPP + +#include + +#ifdef TOML11_COLORIZE_ERROR_MESSAGE +#define TOML11_ERROR_MESSAGE_COLORIZED true +#else +#define TOML11_ERROR_MESSAGE_COLORIZED false +#endif + +#ifdef TOML11_USE_THREAD_LOCAL_COLORIZATION +#define TOML11_THREAD_LOCAL_COLORIZATION thread_local +#else +#define TOML11_THREAD_LOCAL_COLORIZATION +#endif + +namespace toml +{ +namespace color +{ +// put ANSI escape sequence to ostream +inline namespace ansi +{ +namespace detail +{ + +// Control color mode globally +class color_mode +{ + public: + + void enable() noexcept + { + should_color_ = true; + } + void disable() noexcept + { + should_color_ = false; + } + bool should_color() const noexcept + { + return should_color_; + } + + private: + + bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED; +}; + +inline color_mode& color_status() noexcept +{ + static TOML11_THREAD_LOCAL_COLORIZATION color_mode status; + return status; +} + +} // detail + +std::ostream& reset (std::ostream& os); +std::ostream& bold (std::ostream& os); +std::ostream& grey (std::ostream& os); +std::ostream& gray (std::ostream& os); +std::ostream& red (std::ostream& os); +std::ostream& green (std::ostream& os); +std::ostream& yellow (std::ostream& os); +std::ostream& blue (std::ostream& os); +std::ostream& magenta(std::ostream& os); +std::ostream& cyan (std::ostream& os); +std::ostream& white (std::ostream& os); + +} // ansi + +inline void enable() +{ + return detail::color_status().enable(); +} +inline void disable() +{ + return detail::color_status().disable(); +} +inline bool should_color() +{ + return detail::color_status().should_color(); +} + +} // color +} // toml +#endif // TOML11_COLOR_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_COLOR_IMPL_HPP +#define TOML11_COLOR_IMPL_HPP + + +#include + +namespace toml +{ +namespace color +{ +// put ANSI escape sequence to ostream +inline namespace ansi +{ + +TOML11_INLINE std::ostream& reset(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[00m";} + return os; +} +TOML11_INLINE std::ostream& bold(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[01m";} + return os; +} +TOML11_INLINE std::ostream& grey(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[30m";} + return os; +} +TOML11_INLINE std::ostream& gray(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[30m";} + return os; +} +TOML11_INLINE std::ostream& red(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[31m";} + return os; +} +TOML11_INLINE std::ostream& green(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[32m";} + return os; +} +TOML11_INLINE std::ostream& yellow(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[33m";} + return os; +} +TOML11_INLINE std::ostream& blue(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[34m";} + return os; +} +TOML11_INLINE std::ostream& magenta(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[35m";} + return os; +} +TOML11_INLINE std::ostream& cyan (std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[36m";} + return os; +} +TOML11_INLINE std::ostream& white (std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[37m";} + return os; +} + +} // ansi +} // color +} // toml +#endif // TOML11_COLOR_IMPL_HPP +#endif + +#endif // TOML11_COLOR_HPP +#ifndef TOML11_SPEC_HPP +#define TOML11_SPEC_HPP + +#include +#include + +#include + +namespace toml +{ + +struct semantic_version +{ + constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept + : major{mjr}, minor{mnr}, patch{p} + {} + + std::uint32_t major; + std::uint32_t minor; + std::uint32_t patch; +}; + +constexpr inline semantic_version +make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept +{ + return semantic_version(mjr, mnr, p); +} + +constexpr inline bool +operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return lhs.major == rhs.major && + lhs.minor == rhs.minor && + lhs.patch == rhs.patch; +} +constexpr inline bool +operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs == rhs); +} +constexpr inline bool +operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return lhs.major < rhs.major || + (lhs.major == rhs.major && lhs.minor < rhs.minor) || + (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch); +} +constexpr inline bool +operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return rhs < lhs; +} +constexpr inline bool +operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs > rhs); +} +constexpr inline bool +operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs < rhs); +} + +inline std::ostream& operator<<(std::ostream& os, const semantic_version& v) +{ + os << v.major << '.' << v.minor << '.' << v.patch; + return os; +} + +inline std::string to_string(const semantic_version& v) +{ + std::ostringstream oss; + oss << v; + return oss.str(); +} + +struct spec +{ + constexpr static spec default_version() noexcept + { + return spec::v(1, 0, 0); + } + + constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept + { + return spec(make_semver(mjr, mnr, p)); + } + + constexpr explicit spec(const semantic_version& semver) noexcept + : version{semver}, + v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver}, + v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver}, + v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver}, + v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver}, + ext_hex_float {false}, + ext_num_suffix{false}, + ext_null_value{false} + {} + + semantic_version version; // toml version + + // diff from v1.0.0 -> v1.1.0 + bool v1_1_0_allow_control_characters_in_comments; + bool v1_1_0_allow_newlines_in_inline_tables; + bool v1_1_0_allow_trailing_comma_in_inline_tables; + bool v1_1_0_allow_non_english_in_bare_keys; + bool v1_1_0_add_escape_sequence_e; + bool v1_1_0_add_escape_sequence_x; + bool v1_1_0_make_seconds_optional; + + // library extensions + bool ext_hex_float; // allow hex float (in C++ style) + bool ext_num_suffix; // allow number suffix (in C++ style) + bool ext_null_value; // allow `null` as a value +}; + +} // namespace toml +#endif // TOML11_SPEC_HPP +#ifndef TOML11_ORDERED_MAP_HPP +#define TOML11_ORDERED_MAP_HPP + +#include +#include +#include +#include + +namespace toml +{ + +namespace detail +{ +template +struct ordered_map_ebo_container +{ + Cmp cmp_; // empty base optimization for empty Cmp type +}; +} // detail + +template, + typename Allocator = std::allocator>> +class ordered_map : detail::ordered_map_ebo_container +{ + public: + using key_type = Key; + using mapped_type = Val; + using value_type = std::pair; + + using key_compare = Cmp; + using allocator_type = Allocator; + + using container_type = std::vector; + using reference = typename container_type::reference; + using pointer = typename container_type::pointer; + using const_reference = typename container_type::const_reference; + using const_pointer = typename container_type::const_pointer; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using size_type = typename container_type::size_type; + using difference_type = typename container_type::difference_type; + + private: + + using ebo_base = detail::ordered_map_ebo_container; + + public: + + ordered_map() = default; + ~ordered_map() = default; + ordered_map(const ordered_map&) = default; + ordered_map(ordered_map&&) = default; + ordered_map& operator=(const ordered_map&) = default; + ordered_map& operator=(ordered_map&&) = default; + + ordered_map(const ordered_map& other, const Allocator& alloc) + : container_(other.container_, alloc) + {} + ordered_map(ordered_map&& other, const Allocator& alloc) + : container_(std::move(other.container_), alloc) + {} + + explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(alloc) + {} + explicit ordered_map(const Allocator& alloc) + : container_(alloc) + {} + + template + ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(first, last, alloc) + {} + template + ordered_map(InputIterator first, InputIterator last, const Allocator& alloc) + : container_(first, last, alloc) + {} + + ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(std::move(v), alloc) + {} + ordered_map(std::initializer_list v, const Allocator& alloc) + : container_(std::move(v), alloc) + {} + ordered_map& operator=(std::initializer_list v) + { + this->container_ = std::move(v); + return *this; + } + + iterator begin() noexcept {return container_.begin();} + iterator end() noexcept {return container_.end();} + const_iterator begin() const noexcept {return container_.begin();} + const_iterator end() const noexcept {return container_.end();} + const_iterator cbegin() const noexcept {return container_.cbegin();} + const_iterator cend() const noexcept {return container_.cend();} + + bool empty() const noexcept {return container_.empty();} + std::size_t size() const noexcept {return container_.size();} + std::size_t max_size() const noexcept {return container_.max_size();} + + void clear() {container_.clear();} + + void push_back(const value_type& v) + { + if(this->contains(v.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(v); + } + void push_back(value_type&& v) + { + if(this->contains(v.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(std::move(v)); + } + void emplace_back(key_type k, mapped_type v) + { + if(this->contains(k)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.emplace_back(std::move(k), std::move(v)); + } + void pop_back() {container_.pop_back();} + + void insert(value_type kv) + { + if(this->contains(kv.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(std::move(kv)); + } + void emplace(key_type k, mapped_type v) + { + if(this->contains(k)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.emplace_back(std::move(k), std::move(v)); + } + + std::size_t count(const key_type& key) const + { + if(this->find(key) != this->end()) + { + return 1; + } + else + { + return 0; + } + } + bool contains(const key_type& key) const + { + return this->find(key) != this->end(); + } + iterator find(const key_type& key) noexcept + { + return std::find_if(this->begin(), this->end(), + [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); + } + const_iterator find(const key_type& key) const noexcept + { + return std::find_if(this->begin(), this->end(), + [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); + } + + mapped_type& at(const key_type& k) + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + mapped_type const& at(const key_type& k) const + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + + mapped_type& operator[](const key_type& k) + { + const auto iter = this->find(k); + if(iter == this->end()) + { + this->container_.emplace_back(k, mapped_type{}); + return this->container_.back().second; + } + return iter->second; + } + + mapped_type const& operator[](const key_type& k) const + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + + key_compare key_comp() const {return this->cmp_;} + + void swap(ordered_map& other) + { + container_.swap(other.container_); + } + + private: + + container_type container_; +}; + +template +bool operator==(const ordered_map& lhs, const ordered_map& rhs) +{ + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} +template +bool operator!=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs == rhs); +} +template +bool operator<(const ordered_map& lhs, const ordered_map& rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} +template +bool operator>(const ordered_map& lhs, const ordered_map& rhs) +{ + return rhs < lhs; +} +template +bool operator<=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs > rhs); +} +template +bool operator>=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs < rhs); +} + +template +void swap(ordered_map& lhs, ordered_map& rhs) +{ + lhs.swap(rhs); + return; +} + + +} // toml +#endif // TOML11_ORDERED_MAP_HPP +#ifndef TOML11_INTO_HPP +#define TOML11_INTO_HPP + +namespace toml +{ + +template +struct into; +// { +// static toml::value into_toml(const T& user_defined_type) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_INTO_HPP +#ifndef TOML11_FROM_HPP +#define TOML11_FROM_HPP + +namespace toml +{ + +template +struct from; +// { +// static T from_toml(const toml::value& v) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_FROM_HPP +#ifndef TOML11_TRAITS_HPP +#define TOML11_TRAITS_HPP + + +#include +#include +#include +#include +#include +#include +#include + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif + +namespace toml +{ +template +class basic_value; + +namespace detail +{ +// --------------------------------------------------------------------------- +// check whether type T is a kind of container/map class + +struct has_iterator_impl +{ + template static std::true_type check(typename T::iterator*); + template static std::false_type check(...); +}; +struct has_value_type_impl +{ + template static std::true_type check(typename T::value_type*); + template static std::false_type check(...); +}; +struct has_key_type_impl +{ + template static std::true_type check(typename T::key_type*); + template static std::false_type check(...); +}; +struct has_mapped_type_impl +{ + template static std::true_type check(typename T::mapped_type*); + template static std::false_type check(...); +}; +struct has_reserve_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().reserve(std::declval()))*); +}; +struct has_push_back_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().push_back(std::declval()))*); +}; +struct is_comparable_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval() < std::declval())*); +}; + +struct has_from_toml_method_impl +{ + template + static std::true_type check( + decltype(std::declval().from_toml(std::declval<::toml::basic_value>()))*); + + template + static std::false_type check(...); +}; +struct has_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().into_toml())*); + template + static std::false_type check(...); +}; + +struct has_template_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().template into_toml())*); + template + static std::false_type check(...); +}; + +struct has_specialized_from_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; +struct has_specialized_into_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::into*); +}; + + +/// Intel C++ compiler can not use decltype in parent class declaration, here +/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 +#ifdef __INTEL_COMPILER +#define decltype(...) std::enable_if::type +#endif + +template +struct has_iterator: decltype(has_iterator_impl::check(nullptr)){}; +template +struct has_value_type: decltype(has_value_type_impl::check(nullptr)){}; +template +struct has_key_type: decltype(has_key_type_impl::check(nullptr)){}; +template +struct has_mapped_type: decltype(has_mapped_type_impl::check(nullptr)){}; +template +struct has_reserve_method: decltype(has_reserve_method_impl::check(nullptr)){}; +template +struct has_push_back_method: decltype(has_push_back_method_impl::check(nullptr)){}; +template +struct is_comparable: decltype(is_comparable_impl::check(nullptr)){}; + +template +struct has_from_toml_method: decltype(has_from_toml_method_impl::check(nullptr)){}; + +template +struct has_into_toml_method: decltype(has_into_toml_method_impl::check(nullptr)){}; + +template +struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check(nullptr)){}; + +template +struct has_specialized_from: decltype(has_specialized_from_impl::check(nullptr)){}; +template +struct has_specialized_into: decltype(has_specialized_into_impl::check(nullptr)){}; + +#ifdef __INTEL_COMPILER +#undef decltype +#endif + +// --------------------------------------------------------------------------- +// type checkers + +template struct is_std_pair_impl : std::false_type{}; +template +struct is_std_pair_impl> : std::true_type{}; +template +using is_std_pair = is_std_pair_impl>; + +template struct is_std_tuple_impl : std::false_type{}; +template +struct is_std_tuple_impl> : std::true_type{}; +template +using is_std_tuple = is_std_tuple_impl>; + +template struct is_std_array_impl : std::false_type{}; +template +struct is_std_array_impl> : std::true_type{}; +template +using is_std_array = is_std_array_impl>; + +template struct is_std_forward_list_impl : std::false_type{}; +template +struct is_std_forward_list_impl> : std::true_type{}; +template +using is_std_forward_list = is_std_forward_list_impl>; + +template struct is_std_basic_string_impl : std::false_type{}; +template +struct is_std_basic_string_impl> : std::true_type{}; +template +using is_std_basic_string = is_std_basic_string_impl>; + +template struct is_1byte_std_basic_string_impl : std::false_type{}; +template +struct is_1byte_std_basic_string_impl> + : std::integral_constant {}; +template +using is_1byte_std_basic_string = is_std_basic_string_impl>; + +#if defined(TOML11_HAS_STRING_VIEW) +template struct is_std_basic_string_view_impl : std::false_type{}; +template +struct is_std_basic_string_view_impl> : std::true_type{}; +template +using is_std_basic_string_view = is_std_basic_string_view_impl>; + +template +struct is_string_view_of : std::false_type {}; +template +struct is_string_view_of, std::basic_string> : std::true_type {}; +#endif + +template struct is_chrono_duration_impl: std::false_type{}; +template +struct is_chrono_duration_impl>: std::true_type{}; +template +using is_chrono_duration = is_chrono_duration_impl>; + +template +struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions + has_iterator, // has T::iterator + has_value_type, // has T::value_type + has_key_type, // has T::key_type + has_mapped_type // has T::mapped_type + >{}; +template +using is_map = is_map_impl>; + +template +struct is_container_impl : cxx::conjunction< + cxx::negation>, // not a map + cxx::negation>, // not a std::string +#ifdef TOML11_HAS_STRING_VIEW + cxx::negation>, // not a std::string_view +#endif + has_iterator, // has T::iterator + has_value_type // has T::value_type + >{}; +template +using is_container = is_container_impl>; + +template +struct is_basic_value_impl: std::false_type{}; +template +struct is_basic_value_impl<::toml::basic_value>: std::true_type{}; +template +using is_basic_value = is_basic_value_impl>; + +}// detail +}//toml +#endif // TOML11_TRAITS_HPP +#ifndef TOML11_EXCEPTION_HPP +#define TOML11_EXCEPTION_HPP + +#include + +namespace toml +{ + +struct exception : public std::exception +{ + public: + virtual ~exception() noexcept override = default; + virtual const char* what() const noexcept override {return "";} +}; + +} // toml +#endif // TOMl11_EXCEPTION_HPP +#ifndef TOML11_RESULT_HPP +#define TOML11_RESULT_HPP + + +#include +#include +#include +#include + +#include + +namespace toml +{ + +struct bad_result_access final : public ::toml::exception +{ + public: + explicit bad_result_access(std::string what_arg) + : what_(std::move(what_arg)) + {} + ~bad_result_access() noexcept override = default; + const char* what() const noexcept override {return what_.c_str();} + + private: + std::string what_; +}; + +// ----------------------------------------------------------------------------- + +template +struct success +{ + static_assert( ! std::is_same::value, ""); + + using value_type = T; + + explicit success(value_type v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template, T>::value, + std::nullptr_t> = nullptr> + explicit success(U&& v): value(std::forward(v)) {} + + template + explicit success(success v): value(std::move(v.value)) {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; + + value_type& get() noexcept {return value;} + value_type const& get() const noexcept {return value;} + + private: + + value_type value; +}; + +template +struct success> +{ + static_assert( ! std::is_same::value, ""); + + using value_type = T; + + explicit success(std::reference_wrapper v) noexcept + : value(std::move(v)) + {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; + + value_type& get() noexcept {return value.get();} + value_type const& get() const noexcept {return value.get();} + + private: + + std::reference_wrapper value; +}; + +template +success::type> ok(T&& v) +{ + return success::type>(std::forward(v)); +} +template +success ok(const char (&literal)[N]) +{ + return success(std::string(literal)); +} + +// ----------------------------------------------------------------------------- + +template +struct failure +{ + using value_type = T; + + explicit failure(value_type v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template, T>::value, + std::nullptr_t> = nullptr> + explicit failure(U&& v): value(std::forward(v)) {} + + template + explicit failure(failure v): value(std::move(v.value)) {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; + + value_type& get() noexcept {return value;} + value_type const& get() const noexcept {return value;} + + private: + + value_type value; +}; + +template +struct failure> +{ + using value_type = T; + + explicit failure(std::reference_wrapper v) noexcept + : value(std::move(v)) + {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; + + value_type& get() noexcept {return value.get();} + value_type const& get() const noexcept {return value.get();} + + private: + + std::reference_wrapper value; +}; + +template +failure::type> err(T&& v) +{ + return failure::type>(std::forward(v)); +} + +template +failure err(const char (&literal)[N]) +{ + return failure(std::string(literal)); +} + +/* ============================================================================ + * _ _ + * _ _ ___ ____ _| | |_ + * | '_/ -_|_-< || | | _| + * |_| \___/__/\_,_|_|\__| + */ + +template +struct result +{ + using success_type = success; + using failure_type = failure; + using value_type = typename success_type::value_type; + using error_type = typename failure_type::value_type; + + result(success_type s): is_ok_(true), succ_(std::move(s)) {} + result(failure_type f): is_ok_(false), fail_(std::move(f)) {} + + template, value_type>>, + std::is_convertible, value_type> + >::value, std::nullptr_t> = nullptr> + result(success s): is_ok_(true), succ_(std::move(s.value)) {} + + template, error_type>>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result(failure f): is_ok_(false), fail_(std::move(f.value)) {} + + result& operator=(success_type s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + return *this; + } + result& operator=(failure_type f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + return *this; + } + + template + result& operator=(success s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + return *this; + } + template + result& operator=(failure f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + return *this; + } + + ~result() noexcept {this->cleanup();} + + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + } + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + } + + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + template, value_type>>, + cxx::negation, error_type>>, + std::is_convertible, value_type>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result(result other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + } + + template, value_type>>, + cxx::negation, error_type>>, + std::is_convertible, value_type>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result& operator=(result other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + bool is_ok() const noexcept {return is_ok_;} + bool is_err() const noexcept {return !is_ok_;} + + explicit operator bool() const noexcept {return is_ok_;} + + value_type& unwrap(cxx::source_location loc = cxx::source_location::current()) + { + if(this->is_err()) + { + throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); + } + return this->succ_.get(); + } + value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const + { + if(this->is_err()) + { + throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); + } + return this->succ_.get(); + } + + value_type& unwrap_or(value_type& opt) noexcept + { + if(this->is_err()) {return opt;} + return this->succ_.get(); + } + value_type const& unwrap_or(value_type const& opt) const noexcept + { + if(this->is_err()) {return opt;} + return this->succ_.get(); + } + + error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) + { + if(this->is_ok()) + { + throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); + } + return this->fail_.get(); + } + error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const + { + if(this->is_ok()) + { + throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); + } + return this->fail_.get(); + } + + value_type& as_ok() noexcept + { + assert(this->is_ok()); + return this->succ_.get(); + } + value_type const& as_ok() const noexcept + { + assert(this->is_ok()); + return this->succ_.get(); + } + + error_type& as_err() noexcept + { + assert(this->is_err()); + return this->fail_.get(); + } + error_type const& as_err() const noexcept + { + assert(this->is_err()); + return this->fail_.get(); + } + + private: + + void cleanup() noexcept + { +#if defined(__GNUC__) && ! defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wduplicated-branches" +#endif + + if(this->is_ok_) {this->succ_.~success_type();} + else {this->fail_.~failure_type();} + +#if defined(__GNUC__) && ! defined(__clang__) +#pragma GCC diagnostic pop +#endif + return; + } + + private: + + bool is_ok_; + union + { + success_type succ_; + failure_type fail_; + }; +}; + +// ---------------------------------------------------------------------------- + +namespace detail +{ +struct none_t {}; +inline bool operator==(const none_t&, const none_t&) noexcept {return true;} +inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} +inline bool operator< (const none_t&, const none_t&) noexcept {return false;} +inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} +inline bool operator> (const none_t&, const none_t&) noexcept {return false;} +inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} +inline std::ostream& operator<<(std::ostream& os, const none_t&) +{ + os << "none"; + return os; +} +} // detail + +inline success ok() noexcept +{ + return success(detail::none_t{}); +} +inline failure err() noexcept +{ + return failure(detail::none_t{}); +} + +} // toml +#endif // TOML11_RESULT_HPP +#ifndef TOML11_UTILITY_HPP +#define TOML11_UTILITY_HPP + + +#include +#include + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// to output character in an error message. +inline std::string show_char(const int c) +{ + using char_type = unsigned char; + if(std::isgraph(c)) + { + return std::string(1, static_cast(c)); + } + else + { + std::array buf; + buf.fill('\0'); + const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF); + assert(r == static_cast(buf.size()) - 1); + (void) r; // Unused variable warning + auto in_hex = std::string(buf.data()); + switch(c) + { + case char_type('\0'): {in_hex += "(NUL)"; break;} + case char_type(' ') : {in_hex += "(SPACE)"; break;} + case char_type('\n'): {in_hex += "(LINE FEED)"; break;} + case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;} + case char_type('\t'): {in_hex += "(TAB)"; break;} + case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;} + case char_type('\f'): {in_hex += "(FORM FEED)"; break;} + case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;} + default: break; + } + return in_hex; + } +} + +// --------------------------------------------------------------------------- + +template +void try_reserve_impl(Container& container, std::size_t N, std::true_type) +{ + container.reserve(N); + return; +} +template +void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept +{ + return; +} + +template +void try_reserve(Container& container, std::size_t N) +{ + try_reserve_impl(container, N, has_reserve_method{}); + return; +} + +// --------------------------------------------------------------------------- + +template +result from_string(const std::string& str) +{ + T v; + std::istringstream iss(str); + iss >> v; + if(iss.fail()) + { + return err(); + } + return ok(v); +} + +// --------------------------------------------------------------------------- + +// helper function to avoid std::string(0, 'c') or std::string(iter, iter) +template +std::string make_string(Iterator first, Iterator last) +{ + if(first == last) {return "";} + return std::string(first, last); +} +inline std::string make_string(std::size_t len, char c) +{ + if(len == 0) {return "";} + return std::string(len, c); +} + +// --------------------------------------------------------------------------- + +template +struct string_conv_impl +{ + static_assert(sizeof(Char) == sizeof(char), ""); + static_assert(sizeof(Char2) == sizeof(char), ""); + + static std::basic_string invoke(std::basic_string s) + { + std::basic_string retval; + std::transform(s.begin(), s.end(), std::back_inserter(retval), + [](const Char2 c) {return static_cast(c);}); + return retval; + } + template + static std::basic_string invoke(const Char2 (&s)[N]) + { + std::basic_string retval; + // "string literal" has null-char at the end. to skip it, we use prev. + std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval), + [](const Char2 c) {return static_cast(c);}); + return retval; + } +}; + +template +struct string_conv_impl +{ + static_assert(sizeof(Char) == sizeof(char), ""); + + static std::basic_string invoke(std::basic_string s) + { + return s; + } + template + static std::basic_string invoke(const Char (&s)[N]) + { + return std::basic_string(s); + } +}; + +template +cxx::enable_if_t::value, S> +string_conv(std::basic_string s) +{ + using C = typename S::value_type; + using T = typename S::traits_type; + using A = typename S::allocator_type; + return string_conv_impl::invoke(std::move(s)); +} +template +cxx::enable_if_t::value, S> +string_conv(const char (&s)[N]) +{ + using C = typename S::value_type; + using T = typename S::traits_type; + using A = typename S::allocator_type; + using C2 = char; + using T2 = std::char_traits; + using A2 = std::allocator; + + return string_conv_impl::template invoke(s); +} + +} // namespace detail +} // namespace toml +#endif // TOML11_UTILITY_HPP +#ifndef TOML11_LOCATION_HPP +#define TOML11_LOCATION_HPP + +#ifndef TOML11_LOCATION_FWD_HPP +#define TOML11_LOCATION_FWD_HPP + + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +class region; // fwd decl + +// +// To represent where we are reading in the parse functions. +// Since it "points" somewhere in the input stream, the length is always 1. +// +class location +{ + public: + + using char_type = unsigned char; // must be unsigned + using container_type = std::vector; + using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning + using source_ptr = std::shared_ptr; + + public: + + location(source_ptr src, std::string src_name) + : source_(std::move(src)), source_name_(std::move(src_name)), + location_(0), line_number_(1) + {} + + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + void advance(std::size_t n = 1) noexcept; + void retrace(std::size_t n = 1) noexcept; + + bool is_ok() const noexcept { return static_cast(this->source_); } + + bool eof() const noexcept; + char_type current() const; + + char_type peek(); + + std::size_t get_location() const noexcept + { + return this->location_; + } + void set_location(const std::size_t loc) noexcept; + + std::size_t line_number() const noexcept + { + return this->line_number_; + } + std::string get_line() const; + std::size_t column_number() const noexcept; + + source_ptr const& source() const noexcept {return this->source_;} + std::string const& source_name() const noexcept {return this->source_name_;} + + private: + + void advance_line_number(const std::size_t n); + void retrace_line_number(const std::size_t n); + + private: + + friend region; + + private: + + source_ptr source_; + std::string source_name_; + std::size_t location_; // std::vector<>::difference_type is signed + std::size_t line_number_; +}; + +bool operator==(const location& lhs, const location& rhs) noexcept; +bool operator!=(const location& lhs, const location& rhs); + +location prev(const location& loc); +location next(const location& loc); +location make_temporary_location(const std::string& str) noexcept; + +template +result +find_if(const location& first, const location& last, const F& func) noexcept +{ + if(first.source() != last.source()) { return err(); } + if(first.get_location() >= last.get_location()) { return err(); } + + auto loc = first; + while(loc.get_location() != last.get_location()) + { + if(func(loc.current())) + { + return ok(loc); + } + loc.advance(); + } + return err(); +} + +template +result +rfind_if(location first, const location& last, const F& func) +{ + if(first.source() != last.source()) { return err(); } + if(first.get_location() >= last.get_location()) { return err(); } + + auto loc = last; + while(loc.get_location() != first.get_location()) + { + if(func(loc.current())) + { + return ok(loc); + } + loc.retrace(); + } + if(func(first.current())) + { + return ok(first); + } + return err(); +} + +result find(const location& first, const location& last, + const location::char_type val); +result rfind(const location& first, const location& last, + const location::char_type val); + +std::size_t count(const location& first, const location& last, + const location::char_type& c); + +} // detail +} // toml +#endif // TOML11_LOCATION_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_LOCATION_IMPL_HPP +#define TOML11_LOCATION_IMPL_HPP + + +namespace toml +{ +namespace detail +{ + +TOML11_INLINE void location::advance(std::size_t n) noexcept +{ + assert(this->is_ok()); + if(this->location_ + n < this->source_->size()) + { + this->advance_line_number(n); + this->location_ += n; + } + else + { + this->advance_line_number(this->source_->size() - this->location_); + this->location_ = this->source_->size(); + } +} +TOML11_INLINE void location::retrace(std::size_t n) noexcept +{ + assert(this->is_ok()); + if(this->location_ < n) + { + this->location_ = 0; + this->line_number_ = 1; + } + else + { + this->retrace_line_number(n); + this->location_ -= n; + } +} + +TOML11_INLINE bool location::eof() const noexcept +{ + assert(this->is_ok()); + return this->location_ >= this->source_->size(); +} +TOML11_INLINE location::char_type location::current() const +{ + assert(this->is_ok()); + if(this->eof()) {return '\0';} + + assert(this->location_ < this->source_->size()); + return this->source_->at(this->location_); +} + +TOML11_INLINE location::char_type location::peek() +{ + assert(this->is_ok()); + if(this->location_ >= this->source_->size()) + { + return '\0'; + } + else + { + return this->source_->at(this->location_ + 1); + } +} + +TOML11_INLINE void location::set_location(const std::size_t loc) noexcept +{ + if(this->location_ == loc) + { + return ; + } + + if(loc == 0) + { + this->line_number_ = 1; + } + else if(this->location_ < loc) + { + const auto d = loc - this->location_; + this->advance_line_number(d); + } + else + { + const auto d = this->location_ - loc; + this->retrace_line_number(d); + } + this->location_ = loc; +} + +TOML11_INLINE std::string location::get_line() const +{ + assert(this->is_ok()); + const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); + const auto riter = cxx::make_reverse_iterator(iter); + + const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); + const auto next = std::find(iter, this->source_->cend(), char_type('\n')); + + return make_string(std::next(prev.base()), next); +} +TOML11_INLINE std::size_t location::column_number() const noexcept +{ + assert(this->is_ok()); + const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); + const auto riter = cxx::make_reverse_iterator(iter); + const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); + + assert(prev.base() <= iter); + return static_cast(std::distance(prev.base(), iter) + 1); // 1-origin +} + + +TOML11_INLINE void location::advance_line_number(const std::size_t n) +{ + assert(this->is_ok()); + assert(this->location_ + n <= this->source_->size()); + + const auto iter = this->source_->cbegin(); + this->line_number_ += static_cast(std::count( + std::next(iter, static_cast(this->location_)), + std::next(iter, static_cast(this->location_ + n)), + char_type('\n'))); + + return; +} +TOML11_INLINE void location::retrace_line_number(const std::size_t n) +{ + assert(this->is_ok()); + assert(n <= this->location_); // loc - n >= 0 + + const auto iter = this->source_->cbegin(); + const auto dline_num = static_cast(std::count( + std::next(iter, static_cast(this->location_ - n)), + std::next(iter, static_cast(this->location_)), + char_type('\n'))); + + if(this->line_number_ <= dline_num) + { + this->line_number_ = 1; + } + else + { + this->line_number_ -= dline_num; + } + return; +} + +TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept +{ + if( ! lhs.is_ok() || ! rhs.is_ok()) + { + return (!lhs.is_ok()) && (!rhs.is_ok()); + } + return lhs.source() == rhs.source() && + lhs.source_name() == rhs.source_name() && + lhs.get_location() == rhs.get_location(); +} +TOML11_INLINE bool operator!=(const location& lhs, const location& rhs) +{ + return !(lhs == rhs); +} + +TOML11_INLINE location prev(const location& loc) +{ + location p(loc); + p.retrace(1); + return p; +} +TOML11_INLINE location next(const location& loc) +{ + location p(loc); + p.advance(1); + return p; +} + +TOML11_INLINE location make_temporary_location(const std::string& str) noexcept +{ + location::container_type cont(str.size()); + std::transform(str.begin(), str.end(), cont.begin(), + [](const std::string::value_type& c) { + return cxx::bit_cast(c); + }); + return location(std::make_shared( + std::move(cont)), "internal temporary"); +} + +TOML11_INLINE result +find(const location& first, const location& last, const location::char_type val) +{ + return find_if(first, last, [val](const location::char_type c) { + return c == val; + }); +} +TOML11_INLINE result +rfind(const location& first, const location& last, const location::char_type val) +{ + return rfind_if(first, last, [val](const location::char_type c) { + return c == val; + }); +} + +TOML11_INLINE std::size_t +count(const location& first, const location& last, const location::char_type& c) +{ + if(first.source() != last.source()) { return 0; } + if(first.get_location() >= last.get_location()) { return 0; } + + auto loc = first; + std::size_t num = 0; + while(loc.get_location() != last.get_location()) + { + if(loc.current() == c) + { + num += 1; + } + loc.advance(); + } + return num; +} + +} // detail +} // toml +#endif // TOML11_LOCATION_HPP +#endif + +#endif // TOML11_LOCATION_HPP +#ifndef TOML11_REGION_HPP +#define TOML11_REGION_HPP + +#ifndef TOML11_REGION_FWD_HPP +#define TOML11_REGION_FWD_HPP + + +#include +#include + +#include + +namespace toml +{ +namespace detail +{ + +// +// To represent where is a toml::value defined, or where does an error occur. +// Stored in toml::value. source_location will be constructed based on this. +// +class region +{ + public: + + using char_type = location::char_type; + using container_type = location::container_type; + using difference_type = location::difference_type; + using source_ptr = location::source_ptr; + + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + public: + + // a value that is constructed manually does not have input stream info + region() + : source_(nullptr), source_name_(""), length_(0), + first_(0), first_line_(0), first_column_(0), last_(0), last_line_(0), + last_column_(0) + {} + + // a value defined in [first, last). + // Those source must be the same. Instread, `region` does not make sense. + region(const location& first, const location& last); + + // shorthand of [loc, loc+1) + explicit region(const location& loc); + + ~region() = default; + region(const region&) = default; + region(region&&) = default; + region& operator=(const region&) = default; + region& operator=(region&&) = default; + + bool is_ok() const noexcept { return static_cast(this->source_); } + + operator bool() const noexcept { return this->is_ok(); } + + std::size_t length() const noexcept {return this->length_;} + + std::size_t first_line_number() const noexcept + { + return this->first_line_; + } + std::size_t first_column_number() const noexcept + { + return this->first_column_; + } + std::size_t last_line_number() const noexcept + { + return this->last_line_; + } + std::size_t last_column_number() const noexcept + { + return this->last_column_; + } + + char_type at(std::size_t i) const; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + std::string as_string() const; + std::vector as_lines() const; + + source_ptr const& source() const noexcept {return this->source_;} + std::string const& source_name() const noexcept {return this->source_name_;} + + private: + + source_ptr source_; + std::string source_name_; + std::size_t length_; + std::size_t first_; + std::size_t first_line_; + std::size_t first_column_; + std::size_t last_; + std::size_t last_line_; + std::size_t last_column_; +}; + +} // namespace detail +} // namespace toml +#endif // TOML11_REGION_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_REGION_IMPL_HPP +#define TOML11_REGION_IMPL_HPP + + +#include +#include +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// a value defined in [first, last). +// Those source must be the same. Instread, `region` does not make sense. +TOML11_INLINE region::region(const location& first, const location& last) + : source_(first.source()), source_name_(first.source_name()), + length_(last.get_location() - first.get_location()), + first_(first.get_location()), + first_line_(first.line_number()), + first_column_(first.column_number()), + last_(last.get_location()), + last_line_(last.line_number()), + last_column_(last.column_number()) +{ + assert(first.source() == last.source()); + assert(first.source_name() == last.source_name()); +} + + // shorthand of [loc, loc+1) +TOML11_INLINE region::region(const location& loc) + : source_(loc.source()), source_name_(loc.source_name()), length_(0), + first_line_(0), first_column_(0), last_line_(0), last_column_(0) +{ + // if the file ends with LF, the resulting region points no char. + if(loc.eof()) + { + if(loc.get_location() == 0) + { + this->length_ = 0; + this->first_ = 0; + this->first_line_ = 0; + this->first_column_ = 0; + this->last_ = 0; + this->last_line_ = 0; + this->last_column_ = 0; + } + else + { + const auto first = prev(loc); + this->first_ = first.get_location(); + this->first_line_ = first.line_number(); + this->first_column_ = first.column_number(); + this->last_ = loc.get_location(); + this->last_line_ = loc.line_number(); + this->last_column_ = loc.column_number(); + this->length_ = 1; + } + } + else + { + this->first_ = loc.get_location(); + this->first_line_ = loc.line_number(); + this->first_column_ = loc.column_number(); + this->last_ = loc.get_location() + 1; + this->last_line_ = loc.line_number(); + this->last_column_ = loc.column_number() + 1; + this->length_ = 1; + } +} + +TOML11_INLINE region::char_type region::at(std::size_t i) const +{ + if(this->last_ <= this->first_ + i) + { + throw std::out_of_range("range::at: index " + std::to_string(i) + + " exceeds length " + std::to_string(this->length_)); + } + const auto iter = std::next(this->source_->cbegin(), + static_cast(this->first_ + i)); + return *iter; +} + +TOML11_INLINE region::const_iterator region::begin() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->first_)); +} +TOML11_INLINE region::const_iterator region::end() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->last_)); +} +TOML11_INLINE region::const_iterator region::cbegin() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->first_)); +} +TOML11_INLINE region::const_iterator region::cend() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->last_)); +} + +TOML11_INLINE std::string region::as_string() const +{ + if(this->is_ok()) + { + const auto begin = std::next(this->source_->cbegin(), static_cast(this->first_)); + const auto end = std::next(this->source_->cbegin(), static_cast(this->last_ )); + return ::toml::detail::make_string(begin, end); + } + else + { + return std::string(""); + } +} + +TOML11_INLINE std::vector region::as_lines() const +{ + assert(this->is_ok()); + if(this->length_ == 0) + { + return std::vector{""}; + } + + // Consider the following toml file + // ``` + // array = [ + // ] # comment + // ``` + // and the region represnets + // ``` + // [ + // ] + // ``` + // but we want to show the following. + // ``` + // array = [ + // ] # comment + // ``` + // So we need to find LFs before `begin` and after `end`. + // + // But, if region ends with LF, it should not include the next line. + // ``` + // a = 42 + // ^^^- with the last LF + // ``` + // So we start from `end-1` when looking for LF. + + const auto begin_idx = static_cast(this->first_); + const auto end_idx = static_cast(this->last_) - 1; + + // length_ != 0, so begin < end. then begin <= end-1 + assert(begin_idx <= end_idx); + + const auto begin = std::next(this->source_->cbegin(), begin_idx); + const auto end = std::next(this->source_->cbegin(), end_idx); + + const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base(); + const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); + + const auto reg_lines = make_string(line_begin, line_end); + + if(reg_lines == "") // the region is an empty line that only contains LF + { + return std::vector{""}; + } + + std::istringstream iss(reg_lines); + + std::vector lines; + std::string line; + while(std::getline(iss, line)) + { + lines.push_back(line); + } + return lines; +} + +} // namespace detail +} // namespace toml +#endif // TOML11_REGION_IMPL_HPP +#endif + +#endif // TOML11_REGION_HPP +#ifndef TOML11_SOURCE_LOCATION_HPP +#define TOML11_SOURCE_LOCATION_HPP + +#ifndef TOML11_SOURCE_LOCATION_FWD_HPP +#define TOML11_SOURCE_LOCATION_FWD_HPP + + +#include +#include +#include + +namespace toml +{ + +// A struct to contain location in a toml file. +struct source_location +{ + public: + + explicit source_location(const detail::region& r); + ~source_location() = default; + source_location(source_location const&) = default; + source_location(source_location &&) = default; + source_location& operator=(source_location const&) = default; + source_location& operator=(source_location &&) = default; + + bool is_ok() const noexcept {return this->is_ok_;} + std::size_t length() const noexcept {return this->length_;} + + std::size_t first_line_number() const noexcept {return this->first_line_;} + std::size_t first_column_number() const noexcept {return this->first_column_;} + std::size_t last_line_number() const noexcept {return this->last_line_;} + std::size_t last_column_number() const noexcept {return this->last_column_;} + + std::string const& file_name() const noexcept {return this->file_name_;} + + std::size_t num_lines() const noexcept {return this->line_str_.size();} + + std::string const& first_line() const; + std::string const& last_line() const; + + std::vector const& lines() const noexcept {return line_str_;} + + private: + + bool is_ok_; + std::size_t first_line_; + std::size_t first_column_; + std::size_t last_line_; + std::size_t last_column_; + std::size_t length_; + std::string file_name_; + std::vector line_str_; +}; + +namespace detail +{ + +std::size_t integer_width_base10(std::size_t i) noexcept; + +inline std::size_t line_width() noexcept {return 0;} + +template +std::size_t line_width(const source_location& loc, const std::string& /*msg*/, + const Ts& ... tail) noexcept +{ + return (std::max)( + integer_width_base10(loc.last_line_number()), line_width(tail...)); +} + +std::ostringstream& +format_filename(std::ostringstream& oss, const source_location& loc); + +std::ostringstream& +format_empty_line(std::ostringstream& oss, const std::size_t lnw); + +std::ostringstream& format_line(std::ostringstream& oss, + const std::size_t lnw, const std::size_t linenum, const std::string& line); + +std::ostringstream& format_underline(std::ostringstream& oss, + const std::size_t lnw, const std::size_t col, const std::size_t len, + const std::string& msg); + +std::string format_location_impl(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg); + +inline std::string format_location_rec(const std::size_t, const std::string&) +{ + return ""; +} + +template +std::string format_location_rec(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg, + const Ts& ... tail) +{ + return format_location_impl(lnw, prev_fname, loc, msg) + + format_location_rec(lnw, loc.file_name(), tail...); +} + +} // namespace detail + +// format a location info without title +template +std::string format_location( + const source_location& loc, const std::string& msg, const Ts& ... tail) +{ + const auto lnw = detail::line_width(loc, msg, tail...); + + const std::string f(""); // at the 1st iteration, no prev_filename is given + return detail::format_location_rec(lnw, f, loc, msg, tail...); +} + +} // toml +#endif // TOML11_SOURCE_LOCATION_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_SOURCE_LOCATION_IMPL_HPP +#define TOML11_SOURCE_LOCATION_IMPL_HPP + + + +#include +#include +#include +#include + +#include + +namespace toml +{ + +TOML11_INLINE source_location::source_location(const detail::region& r) + : is_ok_(false), + first_line_(1), + first_column_(1), + last_line_(1), + last_column_(1), + length_(0), + file_name_("unknown file") +{ + if(r.is_ok()) + { + this->is_ok_ = true; + this->file_name_ = r.source_name(); + this->first_line_ = r.first_line_number(); + this->first_column_ = r.first_column_number(); + this->last_line_ = r.last_line_number(); + this->last_column_ = r.last_column_number(); + this->length_ = r.length(); + this->line_str_ = r.as_lines(); + } +} + +TOML11_INLINE std::string const& source_location::first_line() const +{ + if(this->line_str_.size() == 0) + { + throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); + } + return this->line_str_.front(); +} +TOML11_INLINE std::string const& source_location::last_line() const +{ + if(this->line_str_.size() == 0) + { + throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); + } + return this->line_str_.back(); +} + +namespace detail +{ + +TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept +{ + std::size_t width = 0; + while(i != 0) + { + i /= 10; + width += 1; + } + return width; +} + +TOML11_INLINE std::ostringstream& +format_filename(std::ostringstream& oss, const source_location& loc) +{ + // --> example.toml + oss << color::bold << color::blue << " --> " << color::reset + << color::bold << loc.file_name() << '\n' << color::reset; + return oss; +} + +TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss, + const std::size_t lnw) +{ + // | + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + return oss; +} + +TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss, + const std::size_t lnw, const std::size_t linenum, const std::string& line) +{ + // 10 | key = "value" + oss << ' ' << color::bold << color::blue + << std::setw(static_cast(lnw)) + << std::right << linenum << " | " << color::reset; + for(const char c : line) + { + if(std::isgraph(c) || c == ' ') + { + oss << c; + } + else + { + oss << show_char(c); + } + } + oss << '\n'; + return oss; +} +TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss, + const std::size_t lnw, const std::size_t col, const std::size_t len, + const std::string& msg) +{ + // | ^^^^^^^-- this part + oss << make_string(lnw + 1, ' ') + << color::bold << color::blue << " | " << color::reset; + + oss << make_string(col-1 /*1-origin*/, ' ') + << color::bold << color::red + << make_string(len, '^') << "-- " + << color::reset << msg << '\n'; + + return oss; +} + +TOML11_INLINE std::string format_location_impl(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg) +{ + std::ostringstream oss; + + if(loc.file_name() != prev_fname) + { + format_filename(oss, loc); + if( ! loc.lines().empty()) + { + format_empty_line(oss, lnw); + } + } + + if(loc.lines().size() == 1) + { + // when column points LF, it exceeds the size of the first line. + std::size_t underline_limit = 1; + if(loc.first_line().size() < loc.first_column_number()) + { + underline_limit = 1; + } + else + { + underline_limit = loc.first_line().size() - loc.first_column_number() + 1; + } + const auto underline_len = (std::min)(underline_limit, loc.length()); + + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), underline_len, msg); + } + else if(loc.lines().size() == 2) + { + const auto first_underline_len = + loc.first_line().size() - loc.first_column_number() + 1; + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), + first_underline_len, ""); + + format_line(oss, lnw, loc.last_line_number(), loc.last_line()); + format_underline(oss, lnw, 1, loc.last_column_number(), msg); + } + else if(loc.lines().size() > 2) + { + const auto first_underline_len = + loc.first_line().size() - loc.first_column_number() + 1; + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), + first_underline_len, "and"); + + if(loc.lines().size() == 3) + { + format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1)); + format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and"); + } + else + { + format_line(oss, lnw, loc.first_line_number()+1, " ..."); + format_empty_line(oss, lnw); + } + format_line(oss, lnw, loc.last_line_number(), loc.last_line()); + format_underline(oss, lnw, 1, loc.last_column_number(), msg); + } + // if loc is empty, do nothing. + return oss.str(); +} + +} // namespace detail +} // toml +#endif // TOML11_SOURCE_LOCATION_IMPL_HPP +#endif + +#endif // TOML11_SOURCE_LOCATION_HPP +#ifndef TOML11_ERROR_INFO_HPP +#define TOML11_ERROR_INFO_HPP + +#ifndef TOML11_ERROR_INFO_FWD_HPP +#define TOML11_ERROR_INFO_FWD_HPP + + +namespace toml +{ + +// error info returned from parser. +struct error_info +{ + error_info(std::string t, source_location l, std::string m, std::string s = "") + : title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))}, + suffix_(std::move(s)) + {} + + error_info(std::string t, std::vector> l, + std::string s = "") + : title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s)) + {} + + std::string const& title() const noexcept {return title_;} + std::string & title() noexcept {return title_;} + + std::vector> const& + locations() const noexcept {return locations_;} + + void add_locations(source_location loc, std::string msg) noexcept + { + locations_.emplace_back(std::move(loc), std::move(msg)); + } + + std::string const& suffix() const noexcept {return suffix_;} + std::string & suffix() noexcept {return suffix_;} + + private: + + std::string title_; + std::vector> locations_; + std::string suffix_; // hint or something like that +}; + +// forward decl +template +class basic_value; + +namespace detail +{ +inline error_info make_error_info_rec(error_info e) +{ + return e; +} +inline error_info make_error_info_rec(error_info e, std::string s) +{ + e.suffix() = s; + return e; +} + +template +error_info make_error_info_rec(error_info e, + const basic_value& v, std::string msg, Ts&& ... tail); + +template +error_info make_error_info_rec(error_info e, + source_location loc, std::string msg, Ts&& ... tail) +{ + e.add_locations(std::move(loc), std::move(msg)); + return make_error_info_rec(std::move(e), std::forward(tail)...); +} + +} // detail + +template +error_info make_error_info( + std::string title, source_location loc, std::string msg, Ts&& ... tail) +{ + error_info ei(std::move(title), std::move(loc), std::move(msg)); + return detail::make_error_info_rec(ei, std::forward(tail) ... ); +} + +std::string format_error(const std::string& errkind, const error_info& err); +std::string format_error(const error_info& err); + +// for custom error message +template +std::string format_error(std::string title, + source_location loc, std::string msg, Ts&& ... tail) +{ + return format_error("", make_error_info(std::move(title), + std::move(loc), std::move(msg), std::forward(tail)...)); +} + +std::ostream& operator<<(std::ostream& os, const error_info& e); + +} // toml +#endif // TOML11_ERROR_INFO_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_ERROR_INFO_IMPL_HPP +#define TOML11_ERROR_INFO_IMPL_HPP + + +#include + +namespace toml +{ + +TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err) +{ + std::string errmsg; + if( ! errkind.empty()) + { + errmsg = errkind; + errmsg += ' '; + } + errmsg += err.title(); + errmsg += '\n'; + + const auto lnw = [&err]() { + std::size_t width = 0; + for(const auto& l : err.locations()) + { + width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width); + } + return width; + }(); + + bool first = true; + std::string prev_fname; + for(const auto& lm : err.locations()) + { + if( ! first) + { + std::ostringstream oss; + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |" << color::reset + << color::bold << " ...\n" << color::reset; + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + errmsg += oss.str(); + } + + const auto& l = lm.first; + const auto& m = lm.second; + + errmsg += detail::format_location_impl(lnw, prev_fname, l, m); + + prev_fname = l.file_name(); + first = false; + } + + errmsg += err.suffix(); + + return errmsg; +} + +TOML11_INLINE std::string format_error(const error_info& err) +{ + std::ostringstream oss; + oss << color::red << color::bold << "[error]" << color::reset; + return format_error(oss.str(), err); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e) +{ + os << format_error(e); + return os; +} + +} // toml +#endif // TOML11_ERROR_INFO_IMPL_HPP +#endif + +#endif // TOML11_ERROR_INFO_HPP +#ifndef TOML11_VALUE_HPP +#define TOML11_VALUE_HPP + + +#ifdef TOML11_HAS_STRING_VIEW +#include +#endif + +#include + +namespace toml +{ +template +class basic_value; + +struct type_error final : public ::toml::exception +{ + public: + type_error(std::string what_arg, source_location loc) + : what_(std::move(what_arg)), loc_(std::move(loc)) + {} + ~type_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + source_location const& location() const noexcept {return loc_;} + + private: + std::string what_; + source_location loc_; +}; + +// only for internal use +namespace detail +{ +template +error_info make_type_error(const basic_value&, const std::string&, const value_t); + +template +error_info make_not_found_error(const basic_value&, const std::string&, const typename basic_value::key_type&); + +template +void change_region_of_value(basic_value&, const basic_value&); + +template +struct getter; +} // detail + +template +class basic_value +{ + public: + + using config_type = TypeConfig; + using key_type = typename config_type::string_type; + using value_type = basic_value; + using boolean_type = typename config_type::boolean_type; + using integer_type = typename config_type::integer_type; + using floating_type = typename config_type::floating_type; + using string_type = typename config_type::string_type; + using local_time_type = ::toml::local_time; + using local_date_type = ::toml::local_date; + using local_datetime_type = ::toml::local_datetime; + using offset_datetime_type = ::toml::offset_datetime; + using array_type = typename config_type::template array_type; + using table_type = typename config_type::template table_type; + using comment_type = typename config_type::comment_type; + using char_type = typename string_type::value_type; + + private: + + using region_type = detail::region; + + public: + + basic_value() noexcept + : type_(value_t::empty), empty_('\0'), region_{}, comments_{} + {} + ~basic_value() noexcept {this->cleanup();} + + // copy/move constructor/assigner ===================================== {{{ + + basic_value(const basic_value& v) + : type_(v.type_), region_(v.region_), comments_(v.comments_) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default : assigner(empty_ , '\0' ); break; + } + } + basic_value(basic_value&& v) + : type_(v.type()), region_(std::move(v.region_)), + comments_(std::move(v.comments_)) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + } + + basic_value& operator=(const basic_value& v) + { + if(this == std::addressof(v)) {return *this;} + + this->cleanup(); + this->type_ = v.type_; + this->region_ = v.region_; + this->comments_ = v.comments_; + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default : assigner(empty_ , '\0' ); break; + } + return *this; + } + basic_value& operator=(basic_value&& v) + { + if(this == std::addressof(v)) {return *this;} + + this->cleanup(); + this->type_ = v.type_; + this->region_ = std::move(v.region_); + this->comments_ = std::move(v.comments_); + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + return *this; + } + // }}} + + // constructor to overwrite commnets ================================== {{{ + + basic_value(basic_value v, std::vector com) + : type_(v.type()), region_(std::move(v.region_)), + comments_(std::move(com)) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + } + // }}} + + // conversion between different basic_values ========================== {{{ + + template + basic_value(basic_value other) + : type_(other.type_), + region_(std::move(other.region_)), + comments_(std::move(other.comments_)) + { + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + } + + template + basic_value(basic_value other, std::vector com) + : type_(other.type_), + region_(std::move(other.region_)), + comments_(std::move(com)) + { + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + } + template + basic_value& operator=(basic_value other) + { + this->cleanup(); + this->region_ = other.region_; + this->comments_ = comment_type(other.comments_); + this->type_ = other.type_; + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + return *this; + } + // }}} + + // constructor (boolean) ============================================== {{{ + + basic_value(boolean_type x) + : basic_value(x, boolean_format_info{}, std::vector{}, region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(boolean_type x, std::vector com) + : basic_value(x, boolean_format_info{}, std::move(com), region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::boolean), boolean_(boolean_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(boolean_type x) + { + boolean_format_info fmt; + if(this->is_boolean()) + { + fmt = this->as_boolean_fmt(); + } + this->cleanup(); + this->type_ = value_t::boolean; + this->region_ = region_type{}; + assigner(this->boolean_, boolean_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (integer) ============================================== {{{ + + basic_value(integer_type x) + : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(integer_type x, std::vector com) + : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt, std::vector com, region_type reg) + : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(integer_type x) + { + integer_format_info fmt; + if(this->is_integer()) + { + fmt = this->as_integer_fmt(); + } + this->cleanup(); + this->type_ = value_t::integer; + this->region_ = region_type{}; + assigner(this->integer_, integer_storage(std::move(x), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_integer_like_t = cxx::enable_if_t, boolean_type>>, + cxx::negation, integer_type>>, + std::is_integral> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt, std::vector com, region_type reg) + : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + integer_format_info fmt; + if(this->is_integer()) + { + fmt = this->as_integer_fmt(); + } + this->cleanup(); + this->type_ = value_t::integer; + this->region_ = region_type{}; + assigner(this->integer_, integer_storage(x, std::move(fmt))); + return *this; + } + + // }}} + + // constructor (floating) ============================================= {{{ + + basic_value(floating_type x) + : basic_value(std::move(x), floating_format_info{}, std::vector{}, region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(floating_type x, std::vector com) + : basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt, std::vector com, region_type reg) + : type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(floating_type x) + { + floating_format_info fmt; + if(this->is_floating()) + { + fmt = this->as_floating_fmt(); + } + this->cleanup(); + this->type_ = value_t::floating; + this->region_ = region_type{}; + assigner(this->floating_, floating_storage(std::move(x), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_floating_like_t = cxx::enable_if_t, floating_type>>, + std::is_floating_point> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(x, floating_format_info{}, std::vector{}, region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(x, floating_format_info{}, std::move(com), region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt, std::vector com, region_type reg) + : type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + + template = nullptr> + basic_value& operator=(T x) + { + floating_format_info fmt; + if(this->is_floating()) + { + fmt = this->as_floating_fmt(); + } + this->cleanup(); + this->type_ = value_t::floating; + this->region_ = region_type{}; + assigner(this->floating_, floating_storage(x, std::move(fmt))); + return *this; + } + + // }}} + + // constructor (string) =============================================== {{{ + + basic_value(string_type x) + : basic_value(std::move(x), string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(string_type x, string_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(string_type x, std::vector com) + : basic_value(std::move(x), string_format_info{}, std::move(com), region_type{}) + {} + basic_value(string_type x, string_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(string_type x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(string_type x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(x, std::move(fmt))); + return *this; + } + + // "string literal" + + basic_value(const typename string_type::value_type* x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(const typename string_type::value_type* x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(const typename string_type::value_type* x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(string_type(x), std::move(fmt))); + return *this; + } + +#if defined(TOML11_HAS_STRING_VIEW) + using string_view_type = std::basic_string_view< + typename string_type::value_type, typename string_type::traits_type>; + + basic_value(string_view_type x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(string_view_type x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(string_view_type x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(string_type(x), std::move(fmt))); + return *this; + } + +#endif // TOML11_HAS_STRING_VIEW + + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), + string_(string_storage(detail::string_conv(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(detail::string_conv(x), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (local_date) =========================================== {{{ + + basic_value(local_date_type x) + : basic_value(x, local_date_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_date_type x, std::vector com) + : basic_value(x, local_date_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_date), local_date_(local_date_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_date_type x) + { + local_date_format_info fmt; + if(this->is_local_date()) + { + fmt = this->as_local_date_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_date; + this->region_ = region_type{}; + assigner(this->local_date_, local_date_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (local_time) =========================================== {{{ + + basic_value(local_time_type x) + : basic_value(x, local_time_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_time_type x, std::vector com) + : basic_value(x, local_time_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_time), local_time_(local_time_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_time_type x) + { + local_time_format_info fmt; + if(this->is_local_time()) + { + fmt = this->as_local_time_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_time; + this->region_ = region_type{}; + assigner(this->local_time_, local_time_storage(x, fmt)); + return *this; + } + + template + basic_value(const std::chrono::duration& x) + : basic_value(local_time_type(x), local_time_format_info{}, std::vector{}, region_type{}) + {} + template + basic_value(const std::chrono::duration& x, local_time_format_info fmt) + : basic_value(local_time_type(x), std::move(fmt), std::vector{}, region_type{}) + {} + template + basic_value(const std::chrono::duration& x, std::vector com) + : basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{}) + {} + template + basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) + : basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{}) + {} + template + basic_value(const std::chrono::duration& x, + local_time_format_info fmt, + std::vector com, region_type reg) + : basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg)) + {} + template + basic_value& operator=(const std::chrono::duration& x) + { + local_time_format_info fmt; + if(this->is_local_time()) + { + fmt = this->as_local_time_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_time; + this->region_ = region_type{}; + assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (local_datetime) =========================================== {{{ + + basic_value(local_datetime_type x) + : basic_value(x, local_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_datetime_type x, std::vector com) + : basic_value(x, local_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_datetime_type x) + { + local_datetime_format_info fmt; + if(this->is_local_datetime()) + { + fmt = this->as_local_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_datetime; + this->region_ = region_type{}; + assigner(this->local_datetime_, local_datetime_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (offset_datetime) =========================================== {{{ + + basic_value(offset_datetime_type x) + : basic_value(x, offset_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(offset_datetime_type x, std::vector com) + : basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(offset_datetime_type x) + { + offset_datetime_format_info fmt; + if(this->is_offset_datetime()) + { + fmt = this->as_offset_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_ = region_type{}; + assigner(this->offset_datetime_, offset_datetime_storage(x, fmt)); + return *this; + } + + // system_clock::time_point + + basic_value(std::chrono::system_clock::time_point x) + : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt) + : basic_value(offset_datetime_type(x), fmt, std::vector{}, region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, std::vector com) + : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com) + : basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, + std::vector com, region_type reg) + : basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg)) + {} + basic_value& operator=(std::chrono::system_clock::time_point x) + { + offset_datetime_format_info fmt; + if(this->is_offset_datetime()) + { + fmt = this->as_offset_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_ = region_type{}; + assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt)); + return *this; + } + + // }}} + + // constructor (array) ================================================ {{{ + + basic_value(array_type x) + : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) + {} + basic_value(array_type x, array_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(array_type x, std::vector com) + : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) + {} + basic_value(array_type x, array_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + basic_value(array_type x, array_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::array), array_(array_storage( + detail::storage(std::move(x)), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(array_type x) + { + array_format_info fmt; + if(this->is_array()) + { + fmt = this->as_array_fmt(); + } + this->cleanup(); + this->type_ = value_t::array; + this->region_ = region_type{}; + assigner(this->array_, array_storage( + detail::storage(std::move(x)), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_array_like_t = cxx::enable_if_t, + cxx::negation>, + cxx::negation>, +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, +#endif + cxx::negation>, + cxx::negation> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::array), array_(array_storage( + detail::storage(array_type( + std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())) + ), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + array_format_info fmt; + if(this->is_array()) + { + fmt = this->as_array_fmt(); + } + this->cleanup(); + this->type_ = value_t::array; + this->region_ = region_type{}; + + array_type a(std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())); + assigner(this->array_, array_storage( + detail::storage(std::move(a)), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (table) ================================================ {{{ + + basic_value(table_type x) + : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) + {} + basic_value(table_type x, table_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(table_type x, std::vector com) + : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) + {} + basic_value(table_type x, table_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + basic_value(table_type x, table_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::table), table_(table_storage( + detail::storage(std::move(x)), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(table_type x) + { + table_format_info fmt; + if(this->is_table()) + { + fmt = this->as_table_fmt(); + } + this->cleanup(); + this->type_ = value_t::table; + this->region_ = region_type{}; + assigner(this->table_, table_storage( + detail::storage(std::move(x)), std::move(fmt))); + return *this; + } + + // table-like + + private: + + template + using enable_if_table_like_t = cxx::enable_if_t>, + detail::is_map, + cxx::negation>, + cxx::negation> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::table), table_(table_storage( + detail::storage(table_type( + std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end()) + )), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + table_format_info fmt; + if(this->is_table()) + { + fmt = this->as_table_fmt(); + } + this->cleanup(); + this->type_ = value_t::table; + this->region_ = region_type{}; + + table_type t(std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())); + assigner(this->table_, table_storage( + detail::storage(std::move(t)), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (user_defined) ========================================= {{{ + + template::value, std::nullptr_t> = nullptr> + basic_value(const T& ud) + : basic_value( + into>::template into_toml(ud)) + {} + template::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value( + into>::template into_toml(ud), + std::move(com)) + {} + template::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = into>::template into_toml(ud); + return *this; + } + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud): basic_value(ud.into_toml()) {} + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.into_toml(), std::move(com)) + {} + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.into_toml(); + return *this; + } + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud): basic_value(ud.template into_toml()) {} + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.template into_toml(), std::move(com)) + {} + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.template into_toml(); + return *this; + } + // }}} + + // empty value with region info ======================================= {{{ + + // mainly for `null` extension + basic_value(detail::none_t, region_type reg) noexcept + : type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{} + {} + + // }}} + + // type checking ====================================================== {{{ + + template, value_type>::value, + std::nullptr_t> = nullptr> + bool is() const noexcept + { + return detail::type_to_enum::value == this->type_; + } + bool is(value_t t) const noexcept {return t == this->type_;} + + bool is_empty() const noexcept {return this->is(value_t::empty );} + bool is_boolean() const noexcept {return this->is(value_t::boolean );} + bool is_integer() const noexcept {return this->is(value_t::integer );} + bool is_floating() const noexcept {return this->is(value_t::floating );} + bool is_string() const noexcept {return this->is(value_t::string );} + bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} + bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} + bool is_local_date() const noexcept {return this->is(value_t::local_date );} + bool is_local_time() const noexcept {return this->is(value_t::local_time );} + bool is_array() const noexcept {return this->is(value_t::array );} + bool is_table() const noexcept {return this->is(value_t::table );} + + bool is_array_of_tables() const noexcept + { + if( ! this->is_array()) {return false;} + const auto& a = this->as_array(std::nothrow); // already checked. + + // when you define [[array.of.tables]], at least one empty table will be + // assigned. In case of array of inline tables, `array_of_tables = []`, + // there is no reason to consider this as an array of *tables*. + // So empty array is not an array-of-tables. + if(a.empty()) {return false;} + + // since toml v1.0.0 allows array of heterogeneous types, we need to + // check all the elements. if any of the elements is not a table, it + // is a heterogeneous array and cannot be expressed by `[[aot]]` form. + for(const auto& e : a) + { + if( ! e.is_table()) {return false;} + } + return true; + } + + value_t type() const noexcept {return type_;} + + // }}} + + // as_xxx (noexcept) version ========================================== {{{ + + template + detail::enum_to_type_t> const& + as(const std::nothrow_t&) const noexcept + { + return detail::getter::get_nothrow(*this); + } + template + detail::enum_to_type_t>& + as(const std::nothrow_t&) noexcept + { + return detail::getter::get_nothrow(*this); + } + + boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {return this->boolean_.value;} + integer_type const& as_integer (const std::nothrow_t&) const noexcept {return this->integer_.value;} + floating_type const& as_floating (const std::nothrow_t&) const noexcept {return this->floating_.value;} + string_type const& as_string (const std::nothrow_t&) const noexcept {return this->string_.value;} + offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {return this->offset_datetime_.value;} + local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {return this->local_datetime_.value;} + local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {return this->local_date_.value;} + local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {return this->local_time_.value;} + array_type const& as_array (const std::nothrow_t&) const noexcept {return this->array_.value.get();} + table_type const& as_table (const std::nothrow_t&) const noexcept {return this->table_.value.get();} + + boolean_type & as_boolean (const std::nothrow_t&) noexcept {return this->boolean_.value;} + integer_type & as_integer (const std::nothrow_t&) noexcept {return this->integer_.value;} + floating_type & as_floating (const std::nothrow_t&) noexcept {return this->floating_.value;} + string_type & as_string (const std::nothrow_t&) noexcept {return this->string_.value;} + offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {return this->offset_datetime_.value;} + local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {return this->local_datetime_.value;} + local_date_type & as_local_date (const std::nothrow_t&) noexcept {return this->local_date_.value;} + local_time_type & as_local_time (const std::nothrow_t&) noexcept {return this->local_time_.value;} + array_type & as_array (const std::nothrow_t&) noexcept {return this->array_.value.get();} + table_type & as_table (const std::nothrow_t&) noexcept {return this->table_.value.get();} + + // }}} + + // as_xxx (throw) ===================================================== {{{ + + template + detail::enum_to_type_t> const& as() const + { + return detail::getter::get(*this); + } + template + detail::enum_to_type_t>& as() + { + return detail::getter::get(*this); + } + + boolean_type const& as_boolean() const + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); + } + return this->boolean_.value; + } + integer_type const& as_integer() const + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer()", value_t::integer); + } + return this->integer_.value; + } + floating_type const& as_floating() const + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating()", value_t::floating); + } + return this->floating_.value; + } + string_type const& as_string() const + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string()", value_t::string); + } + return this->string_.value; + } + offset_datetime_type const& as_offset_datetime() const + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); + } + return this->offset_datetime_.value; + } + local_datetime_type const& as_local_datetime() const + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); + } + return this->local_datetime_.value; + } + local_date_type const& as_local_date() const + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); + } + return this->local_date_.value; + } + local_time_type const& as_local_time() const + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); + } + return this->local_time_.value; + } + array_type const& as_array() const + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array()", value_t::array); + } + return this->array_.value.get(); + } + table_type const& as_table() const + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table()", value_t::table); + } + return this->table_.value.get(); + } + + // ------------------------------------------------------------------------ + // nonconst reference + + boolean_type& as_boolean() + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); + } + return this->boolean_.value; + } + integer_type& as_integer() + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer()", value_t::integer); + } + return this->integer_.value; + } + floating_type& as_floating() + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating()", value_t::floating); + } + return this->floating_.value; + } + string_type& as_string() + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string()", value_t::string); + } + return this->string_.value; + } + offset_datetime_type& as_offset_datetime() + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); + } + return this->offset_datetime_.value; + } + local_datetime_type& as_local_datetime() + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); + } + return this->local_datetime_.value; + } + local_date_type& as_local_date() + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); + } + return this->local_date_.value; + } + local_time_type& as_local_time() + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); + } + return this->local_time_.value; + } + array_type& as_array() + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array()", value_t::array); + } + return this->array_.value.get(); + } + table_type& as_table() + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table()", value_t::table); + } + return this->table_.value.get(); + } + + // }}} + + // format accessors (noexcept) ======================================== {{{ + + template + detail::enum_to_fmt_type_t const& + as_fmt(const std::nothrow_t&) const noexcept + { + return detail::getter::get_fmt_nothrow(*this); + } + template + detail::enum_to_fmt_type_t& + as_fmt(const std::nothrow_t&) noexcept + { + return detail::getter::get_fmt_nothrow(*this); + } + + boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;} + integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;} + floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;} + string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;} + offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;} + local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;} + local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;} + local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;} + array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;} + table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;} + + boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;} + integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;} + floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;} + string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;} + offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;} + local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;} + local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;} + local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;} + array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;} + table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;} + + // }}} + + // format accessors (throw) =========================================== {{{ + + template + detail::enum_to_fmt_type_t const& as_fmt() const + { + return detail::getter::get_fmt(*this); + } + template + detail::enum_to_fmt_type_t& as_fmt() + { + return detail::getter::get_fmt(*this); + } + + boolean_format_info const& as_boolean_fmt() const + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); + } + return this->boolean_.format; + } + integer_format_info const& as_integer_fmt() const + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); + } + return this->integer_.format; + } + floating_format_info const& as_floating_fmt() const + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); + } + return this->floating_.format; + } + string_format_info const& as_string_fmt() const + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); + } + return this->string_.format; + } + offset_datetime_format_info const& as_offset_datetime_fmt() const + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); + } + return this->offset_datetime_.format; + } + local_datetime_format_info const& as_local_datetime_fmt() const + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); + } + return this->local_datetime_.format; + } + local_date_format_info const& as_local_date_fmt() const + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); + } + return this->local_date_.format; + } + local_time_format_info const& as_local_time_fmt() const + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); + } + return this->local_time_.format; + } + array_format_info const& as_array_fmt() const + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); + } + return this->array_.format; + } + table_format_info const& as_table_fmt() const + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); + } + return this->table_.format; + } + + // ------------------------------------------------------------------------ + // nonconst reference + + boolean_format_info& as_boolean_fmt() + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); + } + return this->boolean_.format; + } + integer_format_info& as_integer_fmt() + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); + } + return this->integer_.format; + } + floating_format_info& as_floating_fmt() + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); + } + return this->floating_.format; + } + string_format_info& as_string_fmt() + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); + } + return this->string_.format; + } + offset_datetime_format_info& as_offset_datetime_fmt() + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); + } + return this->offset_datetime_.format; + } + local_datetime_format_info& as_local_datetime_fmt() + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); + } + return this->local_datetime_.format; + } + local_date_format_info& as_local_date_fmt() + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); + } + return this->local_date_.format; + } + local_time_format_info& as_local_time_fmt() + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); + } + return this->local_time_.format; + } + array_format_info& as_array_fmt() + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); + } + return this->array_.format; + } + table_format_info& as_table_fmt() + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); + } + return this->table_.format; + } + // }}} + + // table accessors ==================================================== {{{ + + value_type& at(const key_type& k) + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::at(key_type)", value_t::table); + } + auto& table = this->as_table(std::nothrow); + const auto found = table.find(k); + if(found == table.end()) + { + this->throw_key_not_found_error("toml::value::at", k); + } + assert(found->first == k); + return found->second; + } + value_type const& at(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::at(key_type)", value_t::table); + } + const auto& table = this->as_table(std::nothrow); + const auto found = table.find(k); + if(found == table.end()) + { + this->throw_key_not_found_error("toml::value::at", k); + } + assert(found->first == k); + return found->second; + } + value_type& operator[](const key_type& k) + { + if(this->is_empty()) + { + (*this) = table_type{}; + } + else if( ! this->is_table()) // initialized, but not a table + { + this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table); + } + return (this->as_table(std::nothrow))[k]; + } + std::size_t count(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::count(key_type)", value_t::table); + } + return this->as_table(std::nothrow).count(k); + } + bool contains(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::contains(key_type)", value_t::table); + } + const auto& table = this->as_table(std::nothrow); + return table.find(k) != table.end(); + } + // }}} + + // array accessors ==================================================== {{{ + + value_type& at(const std::size_t idx) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::at(idx)", value_t::array); + } + auto& ar = this->as_array(std::nothrow); + + if(ar.size() <= idx) + { + std::ostringstream oss; + oss << "actual length (" << ar.size() + << ") is shorter than the specified index (" << idx << ")."; + throw std::out_of_range(format_error( + "toml::value::at(idx): no element corresponding to the index", + this->location(), oss.str() + )); + } + return ar.at(idx); + } + value_type const& at(const std::size_t idx) const + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::at(idx)", value_t::array); + } + const auto& ar = this->as_array(std::nothrow); + + if(ar.size() <= idx) + { + std::ostringstream oss; + oss << "actual length (" << ar.size() + << ") is shorter than the specified index (" << idx << ")."; + + throw std::out_of_range(format_error( + "toml::value::at(idx): no element corresponding to the index", + this->location(), oss.str() + )); + } + return ar.at(idx); + } + + value_type& operator[](const std::size_t idx) noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + value_type const& operator[](const std::size_t idx) const noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + + void push_back(const value_type& x) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); + } + this->as_array(std::nothrow).push_back(x); + return; + } + void push_back(value_type&& x) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); + } + this->as_array(std::nothrow).push_back(std::move(x)); + return; + } + + template + value_type& emplace_back(Ts&& ... args) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array); + } + auto& ar = this->as_array(std::nothrow); + ar.emplace_back(std::forward(args) ...); + return ar.back(); + } + + std::size_t size() const + { + switch(this->type_) + { + case value_t::array: + { + return this->as_array(std::nothrow).size(); + } + case value_t::table: + { + return this->as_table(std::nothrow).size(); + } + case value_t::string: + { + return this->as_string(std::nothrow).size(); + } + default: + { + throw type_error(format_error( + "toml::value::size(): bad_cast to container types", + this->location(), + "the actual type is " + to_string(this->type_) + ), this->location()); + } + } + } + + // }}} + + source_location location() const + { + return source_location(this->region_); + } + + comment_type const& comments() const noexcept {return this->comments_;} + comment_type& comments() noexcept {return this->comments_;} + + private: + + // private helper functions =========================================== {{{ + + void cleanup() noexcept + { + switch(this->type_) + { + case value_t::boolean : { boolean_ .~boolean_storage (); break; } + case value_t::integer : { integer_ .~integer_storage (); break; } + case value_t::floating : { floating_ .~floating_storage (); break; } + case value_t::string : { string_ .~string_storage (); break; } + case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; } + case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; } + case value_t::local_date : { local_date_ .~local_date_storage (); break; } + case value_t::local_time : { local_time_ .~local_time_storage (); break; } + case value_t::array : { array_ .~array_storage (); break; } + case value_t::table : { table_ .~table_storage (); break; } + default : { break; } + } + this->type_ = value_t::empty; + return; + } + + template + static void assigner(T& dst, U&& v) + { + const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); + assert(tmp == std::addressof(dst)); + (void)tmp; + } + + [[noreturn]] + void throw_bad_cast(const std::string& funcname, const value_t ty) const + { + throw type_error(format_error(detail::make_type_error(*this, funcname, ty)), + this->location()); + } + + [[noreturn]] + void throw_key_not_found_error(const std::string& funcname, const key_type& key) const + { + throw std::out_of_range(format_error( + detail::make_not_found_error(*this, funcname, key))); + } + + template + friend void detail::change_region_of_value(basic_value&, const basic_value&); + + template + friend class basic_value; + + // }}} + + private: + + using boolean_storage = detail::value_with_format; + using integer_storage = detail::value_with_format; + using floating_storage = detail::value_with_format; + using string_storage = detail::value_with_format; + using offset_datetime_storage = detail::value_with_format; + using local_datetime_storage = detail::value_with_format; + using local_date_storage = detail::value_with_format; + using local_time_storage = detail::value_with_format; + using array_storage = detail::value_with_format, array_format_info >; + using table_storage = detail::value_with_format, table_format_info >; + + private: + + value_t type_; + union + { + char empty_; // the smallest type + boolean_storage boolean_; + integer_storage integer_; + floating_storage floating_; + string_storage string_; + offset_datetime_storage offset_datetime_; + local_datetime_storage local_datetime_; + local_date_storage local_date_; + local_time_storage local_time_; + array_storage array_; + table_storage table_; + }; + region_type region_; + comment_type comments_; +}; + +template +bool operator==(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) {return false;} + if(lhs.comments() != rhs.comments()) {return false;} + + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() == rhs.as_boolean(); + } + case value_t::integer : + { + return lhs.as_integer() == rhs.as_integer(); + } + case value_t::floating : + { + return lhs.as_floating() == rhs.as_floating(); + } + case value_t::string : + { + return lhs.as_string() == rhs.as_string(); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() == rhs.as_offset_datetime(); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() == rhs.as_local_datetime(); + } + case value_t::local_date: + { + return lhs.as_local_date() == rhs.as_local_date(); + } + case value_t::local_time: + { + return lhs.as_local_time() == rhs.as_local_time(); + } + case value_t::array : + { + return lhs.as_array() == rhs.as_array(); + } + case value_t::table : + { + return lhs.as_table() == rhs.as_table(); + } + case value_t::empty : {return true; } + default: {return false;} + } +} + +template +bool operator!=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs == rhs); +} + +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator<(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) + { + return (lhs.type() < rhs.type()); + } + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() < rhs.as_boolean() || + (lhs.as_boolean() == rhs.as_boolean() && + lhs.comments() < rhs.comments()); + } + case value_t::integer : + { + return lhs.as_integer() < rhs.as_integer() || + (lhs.as_integer() == rhs.as_integer() && + lhs.comments() < rhs.comments()); + } + case value_t::floating : + { + return lhs.as_floating() < rhs.as_floating() || + (lhs.as_floating() == rhs.as_floating() && + lhs.comments() < rhs.comments()); + } + case value_t::string : + { + return lhs.as_string() < rhs.as_string() || + (lhs.as_string() == rhs.as_string() && + lhs.comments() < rhs.comments()); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() < rhs.as_offset_datetime() || + (lhs.as_offset_datetime() == rhs.as_offset_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() < rhs.as_local_datetime() || + (lhs.as_local_datetime() == rhs.as_local_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_date: + { + return lhs.as_local_date() < rhs.as_local_date() || + (lhs.as_local_date() == rhs.as_local_date() && + lhs.comments() < rhs.comments()); + } + case value_t::local_time: + { + return lhs.as_local_time() < rhs.as_local_time() || + (lhs.as_local_time() == rhs.as_local_time() && + lhs.comments() < rhs.comments()); + } + case value_t::array : + { + return lhs.as_array() < rhs.as_array() || + (lhs.as_array() == rhs.as_array() && + lhs.comments() < rhs.comments()); + } + case value_t::table : + { + return lhs.as_table() < rhs.as_table() || + (lhs.as_table() == rhs.as_table() && + lhs.comments() < rhs.comments()); + } + case value_t::empty : + { + return lhs.comments() < rhs.comments(); + } + default: + { + return lhs.comments() < rhs.comments(); + } + } +} + +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator<=(const basic_value& lhs, const basic_value& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator>(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs <= rhs); +} +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator>=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs < rhs); +} + +// error_info helper +namespace detail +{ +template +error_info make_error_info_rec(error_info e, + const basic_value& v, std::string msg, Ts&& ... tail) +{ + return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward(tail)...); +} +} // detail + +template +error_info make_error_info( + std::string title, const basic_value& v, std::string msg, Ts&& ... tail) +{ + return make_error_info(std::move(title), + v.location(), std::move(msg), std::forward(tail)...); +} +template +std::string format_error(std::string title, + const basic_value& v, std::string msg, Ts&& ... tail) +{ + return format_error(std::move(title), + v.location(), std::move(msg), std::forward(tail)...); +} + +namespace detail +{ + +template +error_info make_type_error(const basic_value& v, const std::string& fname, const value_t ty) +{ + return make_error_info(fname + ": bad_cast to " + to_string(ty), + v.location(), "the actual type is " + to_string(v.type())); +} +template +error_info make_not_found_error(const basic_value& v, const std::string& fname, const typename basic_value::key_type& key) +{ + const auto loc = v.location(); + const std::string title = fname + ": key \"" + string_conv(key) + "\" not found"; + + std::vector> locs; + if( ! loc.is_ok()) + { + return error_info(title, locs); + } + + if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1) + { + // The top-level table has its region at the 0th character of the file. + // That means that, in the case when a key is not found in the top-level + // table, the error message points to the first character. If the file has + // the first table at the first line, the error message would be like this. + // ```console + // [error] key "a" not found + // --> example.toml + // | + // 1 | [table] + // | ^------ in this table + // ``` + // It actually points to the top-level table at the first character, not + // `[table]`. But it is too confusing. To avoid the confusion, the error + // message should explicitly say "key not found in the top-level table". + locs.emplace_back(v.location(), "at the top-level table"); + } + else + { + locs.emplace_back(v.location(), "in this table"); + } + return error_info(title, locs); +} + +#define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ + template \ + struct getter \ + { \ + using value_type = basic_value; \ + using result_type = enum_to_type_t; \ + using format_type = enum_to_fmt_type_t; \ + \ + static result_type& get(value_type& v) \ + { \ + return v.as_ ## ty(); \ + } \ + static result_type const& get(const value_type& v) \ + { \ + return v.as_ ## ty(); \ + } \ + \ + static result_type& get_nothrow(value_type& v) noexcept \ + { \ + return v.as_ ## ty(std::nothrow); \ + } \ + static result_type const& get_nothrow(const value_type& v) noexcept \ + { \ + return v.as_ ## ty(std::nothrow); \ + } \ + \ + static format_type& get_fmt(value_type& v) \ + { \ + return v.as_ ## ty ## _fmt(); \ + } \ + static format_type const& get_fmt(const value_type& v) \ + { \ + return v.as_ ## ty ## _fmt(); \ + } \ + \ + static format_type& get_fmt_nothrow(value_type& v) noexcept \ + { \ + return v.as_ ## ty ## _fmt(std::nothrow); \ + } \ + static format_type const& get_fmt_nothrow(const value_type& v) noexcept \ + { \ + return v.as_ ## ty ## _fmt(std::nothrow); \ + } \ + }; + +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table ) + +#undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER + +template +void change_region_of_value(basic_value& dst, const basic_value& src) +{ + dst.region_ = std::move(src.region_); + return; +} + +} // namespace detail +} // namespace toml +#endif // TOML11_VALUE_HPP +#ifndef TOML11_VISIT_HPP +#define TOML11_VISIT_HPP + + +namespace toml +{ + +template +cxx::return_type_of_t::boolean_type&> +visit(Visitor&& visitor, const basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +template +cxx::return_type_of_t::boolean_type&> +visit(Visitor&& visitor, basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +template +cxx::return_type_of_t::boolean_type&&> +visit(Visitor&& visitor, basic_value&& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} + case value_t::integer : {return visitor(std::move(v.as_integer ()));} + case value_t::floating : {return visitor(std::move(v.as_floating ()));} + case value_t::string : {return visitor(std::move(v.as_string ()));} + case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} + case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} + case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} + case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} + case value_t::array : {return visitor(std::move(v.as_array ()));} + case value_t::table : {return visitor(std::move(v.as_table ()));} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +} // toml +#endif // TOML11_VISIT_HPP +#ifndef TOML11_TYPES_HPP +#define TOML11_TYPES_HPP + + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace toml +{ + +// forward decl +template +class basic_value; + +// when you use a special integer type as toml::value::integer_type, parse must +// be able to read it. So, type_config has static member functions that read the +// integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<< +// is enough. To make config easy, we provide the default read functions. +// +// Before this functions is called, syntax is checked and prefix(`0x` etc) and +// spacer(`_`) are removed. + +template +result +read_dec_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_dec_integer: " + "too large integer: current max digits = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_hex_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> std::hex >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_hex_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_oct_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> std::oct >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_oct_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_bin_int(const std::string& str, const source_location src) +{ + constexpr auto is_bounded = std::numeric_limits::is_bounded; + constexpr auto max_digits = std::numeric_limits::digits; + const auto max_value = (std::numeric_limits::max)(); + + T val{0}; + T base{1}; + for(auto i = str.rbegin(); i != str.rend(); ++i) + { + const auto c = *i; + if(c == '1') + { + val += base; + // prevent `base` from overflow + if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) + { + base = 0; + } + else + { + base *= 2; + } + } + else + { + assert(c == '0'); + + if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) + { + base = 0; + } + else + { + base *= 2; + } + } + } + if(base == 0) + { + return err(make_error_info("toml::parse_bin_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_int(const std::string& str, const source_location src, const std::uint8_t base) +{ + assert(base == 10 || base == 16 || base == 8 || base == 2); + switch(base) + { + case 2: { return read_bin_int(str, src); } + case 8: { return read_oct_int(str, src); } + case 16: { return read_hex_int(str, src); } + default: + { + assert(base == 10); + return read_dec_int(str, src); + } + } +} + +inline result +read_hex_float(const std::string& str, const source_location src, float val) +{ +#if defined(_MSC_VER) && ! defined(__clang__) + const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val)); +#else + const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val)); +#endif + if(res != 1) + { + return err(make_error_info("toml::parse_floating: " + "failed to read hexadecimal floating point value ", + std::move(src), "here")); + } + return ok(val); +} +inline result +read_hex_float(const std::string& str, const source_location src, double val) +{ +#if defined(_MSC_VER) && ! defined(__clang__) + const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val)); +#else + const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val)); +#endif + if(res != 1) + { + return err(make_error_info("toml::parse_floating: " + "failed to read hexadecimal floating point value ", + std::move(src), "here")); + } + return ok(val); +} +template +cxx::enable_if_t, double>>, + cxx::negation, float>> + >::value, result> +read_hex_float(const std::string&, const source_location src, T) +{ + return err(make_error_info("toml::parse_floating: failed to read " + "floating point value because of unknown type in type_config", + std::move(src), "here")); +} + +template +result +read_dec_float(const std::string& str, const source_location src) +{ + T val; + std::istringstream iss(str); + iss >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_floating: " + "failed to read floating point value from stream", + std::move(src), "here")); + } + return ok(val); +} + +template +result +read_float(const std::string& str, const source_location src, const bool is_hex) +{ + if(is_hex) + { + return read_hex_float(str, src, T{}); + } + else + { + return read_dec_float(str, src); + } +} + +struct type_config +{ + using comment_type = preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = std::unordered_map; + + static result + parse_int(const std::string& str, const source_location src, const std::uint8_t base) + { + return read_int(str, src, base); + } + static result + parse_float(const std::string& str, const source_location src, const bool is_hex) + { + return read_float(str, src, is_hex); + } +}; + +using value = basic_value; +using table = typename value::table_type; +using array = typename value::array_type; + +struct ordered_type_config +{ + using comment_type = preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = ordered_map; + + static result + parse_int(const std::string& str, const source_location src, const std::uint8_t base) + { + return read_int(str, src, base); + } + static result + parse_float(const std::string& str, const source_location src, const bool is_hex) + { + return read_float(str, src, is_hex); + } +}; + +using ordered_value = basic_value; +using ordered_table = typename ordered_value::table_type; +using ordered_array = typename ordered_value::array_type; + +// ---------------------------------------------------------------------------- +// meta functions for internal use + +namespace detail +{ + +// ---------------------------------------------------------------------------- +// check if type T has all the needed member types + +struct has_comment_type_impl +{ + template static std::true_type check(typename T::comment_type*); + template static std::false_type check(...); +}; +template +using has_comment_type = decltype(has_comment_type_impl::check(nullptr)); + +struct has_integer_type_impl +{ + template static std::true_type check(typename T::integer_type*); + template static std::false_type check(...); +}; +template +using has_integer_type = decltype(has_integer_type_impl::check(nullptr)); + +struct has_floating_type_impl +{ + template static std::true_type check(typename T::floating_type*); + template static std::false_type check(...); +}; +template +using has_floating_type = decltype(has_floating_type_impl::check(nullptr)); + +struct has_string_type_impl +{ + template static std::true_type check(typename T::string_type*); + template static std::false_type check(...); +}; +template +using has_string_type = decltype(has_string_type_impl::check(nullptr)); + +struct has_array_type_impl +{ + template static std::true_type check(typename T::template array_type*); + template static std::false_type check(...); +}; +template +using has_array_type = decltype(has_array_type_impl::check(nullptr)); + +struct has_table_type_impl +{ + template static std::true_type check(typename T::template table_type*); + template static std::false_type check(...); +}; +template +using has_table_type = decltype(has_table_type_impl::check(nullptr)); + +struct has_parse_int_impl +{ + template static std::true_type check(decltype(std::declval().parse_int( + std::declval(), + std::declval(), + std::declval() + ))*); + template static std::false_type check(...); +}; +template +using has_parse_int = decltype(has_parse_int_impl::check(nullptr)); + +struct has_parse_float_impl +{ + template static std::true_type check(decltype(std::declval().parse_float( + std::declval(), + std::declval(), + std::declval() + ))*); + template static std::false_type check(...); +}; +template +using has_parse_float = decltype(has_parse_float_impl::check(nullptr)); + +template +using is_type_config = cxx::conjunction< + has_comment_type, + has_integer_type, + has_floating_type, + has_string_type, + has_array_type, + has_table_type, + has_parse_int, + has_parse_float + >; + +} // namespace detail +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +extern template class basic_value; +extern template class basic_value; +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_TYPES_HPP +#ifndef TOML11_GET_HPP +#define TOML11_GET_HPP + +#include + + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif // string_view + +namespace toml +{ + +// ============================================================================ +// T is toml::value; identity transformation. + +template +cxx::enable_if_t>::value, T>& +get(basic_value& v) +{ + return v; +} + +template +cxx::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + return v; +} + +template +cxx::enable_if_t>::value, T> +get(basic_value&& v) +{ + return basic_value(std::move(v)); +} + +// ============================================================================ +// exact toml::* type + +template +cxx::enable_if_t>::value, T> & +get(basic_value& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(v); +} + +template +cxx::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(v); +} + +template +cxx::enable_if_t>::value, T> +get(basic_value&& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(std::move(v)); +} + +// ============================================================================ +// T is toml::basic_value + +template +cxx::enable_if_t, + cxx::negation>> + >::value, T> +get(basic_value v) +{ + return T(std::move(v)); +} + +// ============================================================================ +// integer convertible from toml::value::integer_type + +template +cxx::enable_if_t, + cxx::negation>, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_integer()); +} + +// ============================================================================ +// floating point convertible from toml::value::floating_type + +template +cxx::enable_if_t, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_floating()); +} + +// ============================================================================ +// std::string with different char/trait/allocator + +template +cxx::enable_if_t>, + detail::is_1byte_std_basic_string + >::value, T> +get(const basic_value& v) +{ + return detail::string_conv>(v.as_string()); +} + +// ============================================================================ +// std::string_view + +#if defined(TOML11_HAS_STRING_VIEW) + +template +cxx::enable_if_t::string_type>::value, T> +get(const basic_value& v) +{ + return T(v.as_string()); +} + +#endif // string_view + +// ============================================================================ +// std::chrono::duration from toml::local_time + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + return std::chrono::duration_cast( + std::chrono::nanoseconds(v.as_local_time())); +} + +// ============================================================================ +// std::chrono::system_clock::time_point from toml::datetime variants + +template +cxx::enable_if_t< + std::is_same::value, T> +get(const basic_value& v) +{ + switch(v.type()) + { + case value_t::local_date: + { + return std::chrono::system_clock::time_point(v.as_local_date()); + } + case value_t::local_datetime: + { + return std::chrono::system_clock::time_point(v.as_local_datetime()); + } + case value_t::offset_datetime: + { + return std::chrono::system_clock::time_point(v.as_offset_datetime()); + } + default: + { + const auto loc = v.location(); + throw type_error(format_error("toml::get: " + "bad_cast to std::chrono::system_clock::time_point", loc, + "the actual type is " + to_string(v.type())), loc); + } + } +} + +// ============================================================================ +// forward declaration to use this recursively. ignore this and go ahead. + +// array-like (w/ push_back) +template +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // but not std::basic_string +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, // but not std::basic_string_view +#endif + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value&); + +// std::array +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::forward_list +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::pair +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::tuple +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::map (key is convertible from toml::value::key_type) +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + std::is_convertible::key_type, + typename T::key_type>, // keys are convertible + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v); + +// std::map (key is not convertible from toml::value::key_type, but +// is a std::basic_string) +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation::key_type, + typename T::key_type>>, // keys are NOT convertible + detail::is_1byte_std_basic_string, // is std::basic_string + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v); + +// toml::from::from_toml(v) +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// has T.from_toml(v) but no from +template +cxx::enable_if_t, // has T.from_toml() + cxx::negation>, // no toml::from + std::is_default_constructible // T{} works + >::value, T> +get(const basic_value&); + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template +cxx::enable_if_t&>, // has T(const basic_value&) + cxx::negation>, // but not basic_value itself + cxx::negation>, // no .from_toml() + cxx::negation> // no toml::from + >::value, T> +get(const basic_value&); + +// ============================================================================ +// array-like types; most likely STL container, like std::vector, etc. + +template +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // but not std::basic_string +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, // but not std::basic_string_view +#endif + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& a = v.as_array(); + + T container; + detail::try_reserve(container, a.size()); // if T has .reserve(), call it + + for(const auto& elem : a) + { + container.push_back(get(elem)); + } + return container; +} + +// ============================================================================ +// std::array + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& a = v.as_array(); + + T container; + if(a.size() != container.size()) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting to an array: " + " array size is " + std::to_string(container.size()) + + " but there are " + std::to_string(a.size()) + " elements in toml array.", + loc, "here")); + } + for(std::size_t i=0; i(a.at(i)); + } + return container; +} + +// ============================================================================ +// std::forward_list + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + + T container; + for(const auto& elem : v.as_array()) + { + container.push_front(get(elem)); + } + container.reverse(); + return container; +} + +// ============================================================================ +// std::pair + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using first_type = typename T::first_type; + using second_type = typename T::second_type; + + const auto& ar = v.as_array(); + if(ar.size() != 2) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting std::pair: " + " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", + loc, "here")); + } + return std::make_pair(::toml::get(ar.at(0)), + ::toml::get(ar.at(1))); +} + +// ============================================================================ +// std::tuple. + +namespace detail +{ +template +T get_tuple_impl(const Array& a, cxx::index_sequence) +{ + return std::make_tuple( + ::toml::get::type>(a.at(I))...); +} +} // detail + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + const auto& ar = v.as_array(); + if(ar.size() != std::tuple_size::value) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting std::tuple: " + " there are " + std::to_string(ar.size()) + " > " + + std::to_string(std::tuple_size::value) + " elements in toml array.", + loc, "here")); + } + return detail::get_tuple_impl(ar, + cxx::make_index_sequence::value>{}); +} + +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +// key is convertible from toml::value::key_type +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + std::is_convertible::key_type, + typename T::key_type>, // keys are convertible + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert( + std::is_convertible::key_type, key_type>::value, + "toml::get only supports map type of which key_type is " + "convertible from toml::basic_value::key_type."); + + T m; + for(const auto& kv : v.as_table()) + { + m.emplace(key_type(kv.first), get(kv.second)); + } + return m; +} + +// key is NOT convertible from toml::value::key_type but std::basic_string +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation::key_type, + typename T::key_type>>, // keys are NOT convertible + detail::is_1byte_std_basic_string, // is std::basic_string + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + + T m; + for(const auto& kv : v.as_table()) + { + m.emplace(detail::string_conv(kv.first), get(kv.second)); + } + return m; +} + +// ============================================================================ +// user-defined, but convertible types. + +// toml::from +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + return ::toml::from::from_toml(v); +} + +// has T.from_toml(v) but no from +template +cxx::enable_if_t, // has T.from_toml() + cxx::negation>, // no toml::from + std::is_default_constructible // T{} works + >::value, T> +get(const basic_value& v) +{ + T ud; + ud.from_toml(v); + return ud; +} + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template +cxx::enable_if_t&>, // has T(const basic_value&) + cxx::negation>, // but not basic_value itself + cxx::negation>, // no .from_toml() + cxx::negation> // no toml::from + >::value, T> +get(const basic_value& v) +{ + return T(v); +} + +// ============================================================================ +// get_or(value, fallback) + +template +cxx::enable_if_t::value, basic_value> const& +get_or(const basic_value& v, const basic_value&) +{ + return v; +} + +template +cxx::enable_if_t::value, basic_value>& +get_or(basic_value& v, basic_value&) +{ + return v; +} + +template +cxx::enable_if_t::value, basic_value> +get_or(basic_value&& v, basic_value&&) +{ + return v; +} + +// ---------------------------------------------------------------------------- +// specialization for the exact toml types (return type becomes lvalue ref) + +template +cxx::enable_if_t< + detail::is_exact_toml_type>::value, T> const& +get_or(const basic_value& v, const T& opt) noexcept +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t>, + detail::is_exact_toml_type> + >::value, T>& +get_or(basic_value& v, T& opt) noexcept +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t, + basic_value>::value, cxx::remove_cvref_t> +get_or(basic_value&& v, T&& opt) noexcept +{ + try + { + return get>(std::move(v)); + } + catch(...) + { + return cxx::remove_cvref_t(std::forward(opt)); + } +} + +// ---------------------------------------------------------------------------- +// specialization for string literal + +// template +// typename basic_value::string_type +// get_or(const basic_value& v, +// const typename basic_value::string_type::value_type (&opt)[N]) +// { +// try +// { +// return v.as_string(); +// } +// catch(...) +// { +// return typename basic_value::string_type(opt); +// } +// } +// +// The above only matches to the literal, like `get_or(v, "foo");` but not +// ```cpp +// const auto opt = "foo"; +// const auto str = get_or(v, opt); +// ``` +// . And the latter causes an error. +// To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to +// a character here. + +template +typename basic_value::string_type +get_or(const basic_value& v, + const typename basic_value::string_type::value_type* opt) +{ + try + { + return v.as_string(); + } + catch(...) + { + return typename basic_value::string_type(opt); + } +} + +// ---------------------------------------------------------------------------- +// others (require type conversion and return type cannot be lvalue reference) + +template +cxx::enable_if_t>, + cxx::negation>>, + cxx::negation, typename basic_value::string_type::value_type const*>> + >::value, cxx::remove_cvref_t> +get_or(const basic_value& v, T&& opt) +{ + try + { + return get>(v); + } + catch(...) + { + return cxx::remove_cvref_t(std::forward(opt)); + } +} + +} // toml +#endif // TOML11_GET_HPP +#ifndef TOML11_FIND_HPP +#define TOML11_FIND_HPP + +#include + + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif + +namespace toml +{ + +// ---------------------------------------------------------------------------- +// find(value, key); + +template +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(v.at(ky)); +} + +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(v.at(ky)); +} + +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(std::move(v.at(ky))); +} + +// ---------------------------------------------------------------------------- +// find(value, idx) + +template +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const std::size_t idx) +{ + return ::toml::get(v.at(idx)); +} +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const std::size_t idx) +{ + return ::toml::get(v.at(idx)); +} +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const std::size_t idx) +{ + return ::toml::get(std::move(v.at(idx))); +} + +// ---------------------------------------------------------------------------- +// find(value, key/idx), w/o conversion + +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const typename basic_value::key_type& ky) +{ + return v.at(ky); +} +template +cxx::enable_if_t::value, basic_value> const& +find(basic_value const& v, const typename basic_value::key_type& ky) +{ + return v.at(ky); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const typename basic_value::key_type& ky) +{ + return basic_value(std::move(v.at(ky))); +} + +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const std::size_t idx) +{ + return v.at(idx); +} +template +cxx::enable_if_t::value, basic_value> const& +find(basic_value const& v, const std::size_t idx) +{ + return v.at(idx); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const std::size_t idx) +{ + return basic_value(std::move(v.at(idx))); +} + +// -------------------------------------------------------------------------- +// toml::find(toml::value, toml::key, Ts&& ... keys) + +namespace detail +{ + +// It suppresses warnings by -Wsign-conversion when we pass integer literal +// to toml::find. integer literal `0` is deduced as an int, and will be +// converted to std::size_t. This causes sign-conversion. + +template +std::size_t key_cast(const std::size_t& v) noexcept +{ + return v; +} +template +cxx::enable_if_t>::value, std::size_t> +key_cast(const T& v) noexcept +{ + return static_cast(v); +} + +// for string-like (string, string literal, string_view) + +template +typename basic_value::key_type const& +key_cast(const typename basic_value::key_type& v) noexcept +{ + return v; +} +template +typename basic_value::key_type +key_cast(const typename basic_value::key_type::value_type* v) +{ + return typename basic_value::key_type(v); +} +#if defined(TOML11_HAS_STRING_VIEW) +template +typename basic_value::key_type +key_cast(const std::string_view v) +{ + return typename basic_value::key_type(v); +} +#endif // string_view + +} // detail + +// ---------------------------------------------------------------------------- +// find(v, keys...) + +template +cxx::enable_if_t::value, basic_value> const& +find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); +} + +// ---------------------------------------------------------------------------- +// find(v, keys...) + +template +decltype(::toml::get(std::declval&>())) +find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); +} + +// =========================================================================== +// find_or(value, key, fallback) + +// --------------------------------------------------------------------------- +// find_or(v, key, other_v) + +template +cxx::enable_if_t::value, basic_value>& +find_or(basic_value& v, const K& k, basic_value& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t::value, basic_value> const& +find_or(const basic_value& v, const K& k, const basic_value& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t::value, basic_value> +find_or(basic_value&& v, const K& k, basic_value&& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} + +// --------------------------------------------------------------------------- +// toml types (return type can be a reference) + +template +cxx::enable_if_t>::value, + cxx::remove_cvref_t const&> +find_or(const basic_value& v, const K& k, const T& opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return opt; + } +} + +template +cxx::enable_if_t>, + detail::is_exact_toml_type> + >::value, cxx::remove_cvref_t&> +find_or(basic_value& v, const K& k, T& opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return opt; + } +} + +template +cxx::enable_if_t>::value, + cxx::remove_cvref_t> +find_or(basic_value&& v, const K& k, T opt) +{ + try + { + return ::toml::get(std::move(v.at(detail::key_cast(k)))); + } + catch(...) + { + return T(std::move(opt)); + } +} + +// --------------------------------------------------------------------------- +// string literal (deduced as std::string) + +// XXX to avoid confusion when T is explicitly specified in find_or(), +// we restrict the string type as std::string. +template +cxx::enable_if_t::value, std::string> +find_or(const basic_value& v, const K& k, const char* opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return std::string(opt); + } +} + +// --------------------------------------------------------------------------- +// other types (requires type conversion and return type cannot be a reference) + +template +cxx::enable_if_t>>, + detail::is_not_toml_type, basic_value>, + cxx::negation, + const typename basic_value::string_type::value_type*>> + >::value, cxx::remove_cvref_t> +find_or(const basic_value& v, const K& ky, T opt) +{ + try + { + return ::toml::get>(v.at(detail::key_cast(ky))); + } + catch(...) + { + return cxx::remove_cvref_t(std::move(opt)); + } +} + +// ---------------------------------------------------------------------------- +// recursive + +namespace detail +{ + +template +auto last_one(Ts&&... args) + -> decltype(std::get(std::forward_as_tuple(std::forward(args)...))) +{ + return std::get(std::forward_as_tuple(std::forward(args)...)); +} + +} // detail + +template +auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept + -> cxx::enable_if_t< + detail::is_basic_value>::value, + decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) + > +{ + try + { + return find_or(v.at(k1), k2, std::forward(k3), std::forward(keys)...); + } + catch(...) + { + return detail::last_one(k3, keys...); + } +} + +template +T find_or(const basic_value& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept +{ + try + { + return find_or(v.at(k1), k2, k3, keys...); + } + catch(...) + { + return static_cast(detail::last_one(k3, keys...)); + } +} + +} // toml +#endif // TOML11_FIND_HPP +#ifndef TOML11_CONVERSION_HPP +#define TOML11_CONVERSION_HPP + + +#if defined(TOML11_HAS_OPTIONAL) + +#include + +namespace toml +{ +namespace detail +{ + +template +inline constexpr bool is_optional_v = false; + +template +inline constexpr bool is_optional_v> = true; + +template +void find_member_variable_from_value(T& obj, const basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(v.contains(var_name)) + { + obj = toml::find(v, var_name); + } + else + { + obj = std::nullopt; + } + } + else + { + obj = toml::find(v, var_name); + } +} + +template +void assign_member_variable_to_value(const T& obj, basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(obj.has_value()) + { + v[var_name] = obj.value(); + } + } + else + { + v[var_name] = obj; + } +} + +} // detail +} // toml + +#else + +namespace toml +{ +namespace detail +{ + +template +void find_member_variable_from_value(T& obj, const basic_value& v, const char* var_name) +{ + obj = toml::find(v, var_name); +} + +template +void assign_member_variable_to_value(const T& obj, basic_value& v, const char* var_name) +{ + v[var_name] = obj; +} + +} // detail +} // toml + +#endif // optional + +// use it in the following way. +// ```cpp +// namespace foo +// { +// struct Foo +// { +// std::string s; +// double d; +// int i; +// }; +// } // foo +// +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) +// ``` +// +// And then you can use `toml::get(v)` and `toml::find(file, "foo");` +// + +#define TOML11_STRINGIZE_AUX(x) #x +#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) + +#define TOML11_CONCATENATE_AUX(x, y) x##y +#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) + +// ============================================================================ +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE + +#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +// ---------------------------------------------------------------------------- +// TOML11_ARGS_SIZE + +#define TOML11_INDEX_RSEQ() \ + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#define TOML11_ARGS_SIZE_IMPL(\ + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ + ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ + ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ + ARG31, ARG32, N, ...) N +#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) +#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) + +// ---------------------------------------------------------------------------- +// TOML11_FOR_EACH_VA_ARGS + +#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) +#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) + +#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ + TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) + + +#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ + toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); + +#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ + toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); + +#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ + namespace toml { \ + template<> \ + struct from \ + { \ + template \ + static NAME from_toml(const basic_value& v) \ + { \ + NAME obj; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ + return obj; \ + } \ + }; \ + template<> \ + struct into \ + { \ + template \ + static basic_value into_toml(const NAME& obj) \ + { \ + ::toml::basic_value v = typename ::toml::basic_value::table_type{}; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ + return v; \ + } \ + }; \ + } /* toml */ + +#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +#endif // TOML11_CONVERSION_HPP +#ifndef TOML11_CONTEXT_HPP +#define TOML11_CONTEXT_HPP + + +#include + +namespace toml +{ +namespace detail +{ + +template +class context +{ + public: + + explicit context(const spec& toml_spec) + : toml_spec_(toml_spec), errors_{} + {} + + bool has_error() const noexcept {return !errors_.empty();} + + std::vector const& errors() const noexcept {return errors_;} + + semantic_version& toml_version() noexcept {return toml_spec_.version;} + semantic_version const& toml_version() const noexcept {return toml_spec_.version;} + + spec& toml_spec() noexcept {return toml_spec_;} + spec const& toml_spec() const noexcept {return toml_spec_;} + + void report_error(error_info err) + { + this->errors_.push_back(std::move(err)); + } + + error_info pop_last_error() + { + assert( ! errors_.empty()); + auto e = std::move(errors_.back()); + errors_.pop_back(); + return e; + } + + private: + + spec toml_spec_; + std::vector errors_; +}; + +} // detail +} // toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; +namespace detail +{ +extern template class context<::toml::type_config>; +extern template class context<::toml::ordered_type_config>; +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_CONTEXT_HPP +#ifndef TOML11_SCANNER_HPP +#define TOML11_SCANNER_HPP + +#ifndef TOML11_SCANNER_FWD_HPP +#define TOML11_SCANNER_FWD_HPP + + +#include +#include +#include +#include + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +class scanner_base +{ + public: + virtual ~scanner_base() = default; + virtual region scan(location& loc) const = 0; + virtual scanner_base* clone() const = 0; + + // returns expected character or set of characters or literal. + // to show the error location, it changes loc (in `sequence`, especially). + virtual std::string expected_chars(location& loc) const = 0; + virtual std::string name() const = 0; +}; + +// make `scanner*` copyable +struct scanner_storage +{ + template>::value, + std::nullptr_t> = nullptr> + explicit scanner_storage(Scanner&& s) + : scanner_(cxx::make_unique>(std::forward(s))) + {} + ~scanner_storage() = default; + + scanner_storage(const scanner_storage& other); + scanner_storage& operator=(const scanner_storage& other); + scanner_storage(scanner_storage&&) = default; + scanner_storage& operator=(scanner_storage&&) = default; + + bool is_ok() const noexcept {return static_cast(scanner_);} + + region scan(location& loc) const; + + std::string expected_chars(location& loc) const; + + scanner_base& get() const noexcept; + + std::string name() const; + + private: + + std::unique_ptr scanner_; +}; + +// ---------------------------------------------------------------------------- + +class character final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character(const char_type c) noexcept + : value_(c) + {} + ~character() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + char_type value_; +}; + +// ---------------------------------------------------------------------------- + +class character_either final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character_either(std::initializer_list cs) noexcept + : chars_(std::move(cs)) + { + assert(! this->chars_.empty()); + } + + template + explicit character_either(const char (&cs)[N]) noexcept + : chars_(N-1, '\0') + { + static_assert(N >= 1, ""); + for(std::size_t i=0; i+1 chars_; +}; + +// ---------------------------------------------------------------------------- + +class character_in_range final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character_in_range(const char_type from, const char_type to) noexcept + : from_(from), to_(to) + {} + ~character_in_range() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + char_type from_; + char_type to_; +}; + +// ---------------------------------------------------------------------------- + +class literal final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + template + explicit literal(const char (&cs)[N]) noexcept + : value_(cs), size_(N-1) // remove null character at the end + {} + ~literal() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + const char* value_; + std::size_t size_; +}; + +// ---------------------------------------------------------------------------- + +class sequence final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit sequence(Ts&& ... args) + { + push_back_all(std::forward(args)...); + } + sequence(const sequence&) = default; + sequence(sequence&&) = default; + sequence& operator=(const sequence&) = default; + sequence& operator=(sequence&&) = default; + ~sequence() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + template + void push_back(Scanner&& other_scanner) + { + this->others_.emplace_back(std::forward(other_scanner)); + } + + std::string name() const override; + + private: + + void push_back_all() + { + return; + } + template + void push_back_all(T&& head, Ts&& ... args) + { + others_.emplace_back(std::forward(head)); + push_back_all(std::forward(args)...); + return; + } + + private: + std::vector others_; +}; + +// ---------------------------------------------------------------------------- + +class either final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit either(Ts&& ... args) + { + push_back_all(std::forward(args)...); + } + either(const either&) = default; + either(either&&) = default; + either& operator=(const either&) = default; + either& operator=(either&&) = default; + ~either() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + template + void push_back(Scanner&& other_scanner) + { + this->others_.emplace_back(std::forward(other_scanner)); + } + + std::string name() const override; + + private: + + void push_back_all() + { + return; + } + template + void push_back_all(T&& head, Ts&& ... args) + { + others_.emplace_back(std::forward(head)); + push_back_all(std::forward(args)...); + return; + } + + private: + std::vector others_; +}; + +// ---------------------------------------------------------------------------- + +class repeat_exact final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + repeat_exact(const std::size_t length, Scanner&& other) + : length_(length), other_(std::forward(other)) + {} + repeat_exact(const repeat_exact&) = default; + repeat_exact(repeat_exact&&) = default; + repeat_exact& operator=(const repeat_exact&) = default; + repeat_exact& operator=(repeat_exact&&) = default; + ~repeat_exact() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + std::size_t length_; + scanner_storage other_; +}; + +// ---------------------------------------------------------------------------- + +class repeat_at_least final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + repeat_at_least(const std::size_t length, Scanner&& s) + : length_(length), other_(std::forward(s)) + {} + repeat_at_least(const repeat_at_least&) = default; + repeat_at_least(repeat_at_least&&) = default; + repeat_at_least& operator=(const repeat_at_least&) = default; + repeat_at_least& operator=(repeat_at_least&&) = default; + ~repeat_at_least() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + std::size_t length_; + scanner_storage other_; +}; + +// ---------------------------------------------------------------------------- + +class maybe final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit maybe(Scanner&& s) + : other_(std::forward(s)) + {} + maybe(const maybe&) = default; + maybe(maybe&&) = default; + maybe& operator=(const maybe&) = default; + maybe& operator=(maybe&&) = default; + ~maybe() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + scanner_storage other_; +}; + +} // detail +} // toml +#endif // TOML11_SCANNER_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_SCANNER_IMPL_HPP +#define TOML11_SCANNER_IMPL_HPP + + +namespace toml +{ +namespace detail +{ + +TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other) + : scanner_(nullptr) +{ + if(other.is_ok()) + { + scanner_.reset(other.get().clone()); + } +} +TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other) +{ + if(this == std::addressof(other)) {return *this;} + if(other.is_ok()) + { + scanner_.reset(other.get().clone()); + } + return *this; +} + +TOML11_INLINE region scanner_storage::scan(location& loc) const +{ + assert(this->is_ok()); + return this->scanner_->scan(loc); +} + +TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const +{ + assert(this->is_ok()); + return this->scanner_->expected_chars(loc); +} + +TOML11_INLINE scanner_base& scanner_storage::get() const noexcept +{ + assert(this->is_ok()); + return *scanner_; +} + +TOML11_INLINE std::string scanner_storage::name() const +{ + assert(this->is_ok()); + return this->scanner_->name(); +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE region character::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + if(loc.current() == this->value_) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + return region{}; +} + +TOML11_INLINE std::string character::expected_chars(location&) const +{ + return show_char(value_); +} + +TOML11_INLINE scanner_base* character::clone() const +{ + return new character(*this); +} + +TOML11_INLINE std::string character::name() const +{ + return "character{" + show_char(value_) + "}"; +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE region character_either::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + for(const auto c : this->chars_) + { + if(loc.current() == c) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + } + return region{}; +} + +TOML11_INLINE std::string character_either::expected_chars(location&) const +{ + assert( ! chars_.empty()); + + std::string expected; + if(chars_.size() == 1) + { + expected += show_char(chars_.at(0)); + } + else if(chars_.size() == 2) + { + expected += show_char(chars_.at(0)) + " or " + show_char(chars_.at(1)); + } + else + { + for(std::size_t i=0; ichars_) + { + n += show_char(c); + n += ", "; + } + if( ! this->chars_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// character_in_range + +TOML11_INLINE region character_in_range::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + const auto curr = loc.current(); + if(this->from_ <= curr && curr <= this->to_) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + return region{}; +} + +TOML11_INLINE std::string character_in_range::expected_chars(location&) const +{ + std::string expected("from `"); + expected += show_char(from_); + expected += "` to `"; + expected += show_char(to_); + expected += "`"; + return expected; +} + +TOML11_INLINE scanner_base* character_in_range::clone() const +{ + return new character_in_range(*this); +} + +TOML11_INLINE std::string character_in_range::name() const +{ + return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}"; +} + +// ---------------------------------------------------------------------------- +// literal + +TOML11_INLINE region literal::scan(location& loc) const +{ + const auto first = loc; + for(std::size_t i=0; iothers_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// either + +TOML11_INLINE region either::scan(location& loc) const +{ + for(const auto& other : others_) + { + const auto reg = other.scan(loc); + if(reg.is_ok()) + { + return reg; + } + } + return region{}; +} + +TOML11_INLINE std::string either::expected_chars(location& loc) const +{ + assert( ! others_.empty()); + + std::string expected = others_.at(0).expected_chars(loc); + if(others_.size() == 2) + { + expected += " or "; + expected += others_.at(1).expected_chars(loc); + } + else + { + for(std::size_t i=1; iothers_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// repeat_exact + +TOML11_INLINE region repeat_exact::scan(location& loc) const +{ + const auto first = loc; + for(std::size_t i=0; i(b1); + } + else if((b1 >> 5) == 6) // 0b110 == 6 + { + const auto b2 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 5) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t codep = (c1 << 6) + c2; + + if(codep < 0x80) + { + return 0xFFFFFFFF; + } + return codep; + } + else if((b1 >> 4) == 14) // 0b1110 == 14 + { + const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b3 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 4) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t c3 = b3 & ((1 << 6) - 1); + + const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3; + if(codep < 0x800) + { + return 0xFFFFFFFF; + } + return codep; + } + else if((b1 >> 3) == 30) // 0b11110 == 30 + { + const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b4 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 3) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t c3 = b3 & ((1 << 6) - 1); + const std::uint32_t c4 = b4 & ((1 << 6) - 1); + const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; + + if(codep < 0x10000) + { + return 0xFFFFFFFF; + } + return codep; + } + else // not a Unicode codepoint in UTF-8 + { + return 0xFFFFFFFF; + } +} + +TOML11_INLINE region non_ascii_key_char::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + const auto first = loc; + + const auto cp = read_utf8(loc); + + if(cp == 0xFFFFFFFF) + { + return region{}; + } + + // ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _ + // / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions + // / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block + // / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon + // / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ + // / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics + // / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces + // / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode) + // / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF) + + if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) || + (0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) || + (0x37F <= cp && cp <= 0x1FFF) || + (0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) || + (0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) || + (0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) || + (0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) || + (0x10000 <= cp && cp <= 0xEFFFF) ) + { + return region(first, loc); + } + loc = first; + return region{}; +} + +TOML11_INLINE repeat_at_least unquoted_key(const spec& s) +{ + auto keychar = either( + alpha(s), digit(s), character{0x2D}, character{0x5F} + ); + + if(s.v1_1_0_allow_non_english_in_bare_keys) + { + keychar.push_back(non_ascii_key_char(s)); + } + + return repeat_at_least(1, std::move(keychar)); +} + +TOML11_INLINE either quoted_key(const spec& s) +{ + return either(basic_string(s), literal_string(s)); +} + +TOML11_INLINE either simple_key(const spec& s) +{ + return either(unquoted_key(s), quoted_key(s)); +} + +TOML11_INLINE sequence dot_sep(const spec& s) +{ + return sequence(ws(s), character('.'), ws(s)); +} + +TOML11_INLINE sequence dotted_key(const spec& s) +{ + return sequence( + simple_key(s), + repeat_at_least(1, sequence(dot_sep(s), simple_key(s))) + ); +} + +TOML11_INLINE key::key(const spec& s) noexcept + : scanner_(dotted_key(s), simple_key(s)) +{} + +TOML11_INLINE sequence keyval_sep(const spec& s) +{ + return sequence(ws(s), character('='), ws(s)); +} + +// =========================================================================== +// Table key + +TOML11_INLINE sequence std_table(const spec& s) +{ + return sequence(character('['), ws(s), key(s), ws(s), character(']')); +} + +TOML11_INLINE sequence array_table(const spec& s) +{ + return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]")); +} + +// =========================================================================== +// extension: null + +TOML11_INLINE literal null_value(const spec&) +{ + return literal("null"); +} + +} // namespace syntax +} // namespace detail +} // namespace toml +#endif // TOML11_SYNTAX_IMPL_HPP +#endif + +#endif// TOML11_SYNTAX_HPP +#ifndef TOML11_SKIP_HPP +#define TOML11_SKIP_HPP + + +#include + +namespace toml +{ +namespace detail +{ + +template +bool skip_whitespace(location& loc, const context& ctx) +{ + return syntax::ws(ctx.toml_spec()).scan(loc).is_ok(); +} + +template +bool skip_empty_lines(location& loc, const context& ctx) +{ + return repeat_at_least(1, sequence( + syntax::ws(ctx.toml_spec()), + syntax::newline(ctx.toml_spec()) + )).scan(loc).is_ok(); +} + +// For error recovery. +// +// In case if a comment line contains an invalid character, we need to skip it +// to advance parsing. +template +void skip_comment_block(location& loc, const context& ctx) +{ + while( ! loc.eof()) + { + skip_whitespace(loc, ctx); + if(loc.current() == '#') + { + while( ! loc.eof()) + { + // both CRLF and LF ends with LF. + if(loc.current() == '\n') + { + loc.advance(); + break; + } + } + } + else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) + { + ; // an empty line. skip this also + } + else + { + // the next token is neither a comment nor empty line. + return ; + } + } + return ; +} + +template +void skip_empty_or_comment_lines(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + repeat_at_least(0, sequence( + syntax::ws(spec), + maybe(syntax::comment(spec)), + syntax::newline(spec)) + ).scan(loc); + return ; +} + +// For error recovery. +// +// Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`. +// To find delimiter, we need to skip delimiters in a string. +// Since we are skipping invalid value while error recovery, we don't need +// to check the syntax. Here we just skip string-like region until closing quote +// is found. +template +void skip_string_like(location& loc, const context&) +{ + // if """ is found, skip until the closing """ is found. + if(literal("\"\"\"").scan(loc).is_ok()) + { + while( ! loc.eof()) + { + if(literal("\"\"\"").scan(loc).is_ok()) + { + return; + } + loc.advance(); + } + } + else if(literal("'''").scan(loc).is_ok()) + { + while( ! loc.eof()) + { + if(literal("'''").scan(loc).is_ok()) + { + return; + } + loc.advance(); + } + } + // if " is found, skip until the closing " or newline is found. + else if(loc.current() == '"') + { + while( ! loc.eof()) + { + loc.advance(); + if(loc.current() == '"' || loc.current() == '\n') + { + loc.advance(); + return; + } + } + } + else if(loc.current() == '\'') + { + while( ! loc.eof()) + { + loc.advance(); + if(loc.current() == '\'' || loc.current() == '\n') + { + loc.advance(); + return ; + } + } + } + return; +} + +template +void skip_value(location& loc, const context& ctx); +template +void skip_array_like(location& loc, const context& ctx); +template +void skip_inline_table_like(location& loc, const context& ctx); +template +void skip_key_value_pair(location& loc, const context& ctx); + +template +result +guess_value_type(const location& loc, const context& ctx); + +template +void skip_array_like(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + assert(loc.current() == '['); + loc.advance(); + + while( ! loc.eof()) + { + if(loc.current() == '\"' || loc.current() == '\'') + { + skip_string_like(loc, ctx); + } + else if(loc.current() == '#') + { + skip_comment_block(loc, ctx); + } + else if(loc.current() == '{') + { + skip_inline_table_like(loc, ctx); + } + else if(loc.current() == '[') + { + const auto checkpoint = loc; + if(syntax::std_table(spec).scan(loc).is_ok() || + syntax::array_table(spec).scan(loc).is_ok()) + { + loc = checkpoint; + break; + } + // if it is not a table-definition, then it is an array. + skip_array_like(loc, ctx); + } + else if(loc.current() == '=') + { + // key-value pair cannot be inside the array. + // guessing the error is "missing closing bracket `]`". + // find the previous key just before `=`. + while(loc.get_location() != 0) + { + loc.retrace(); + if(loc.current() == '\n') + { + loc.advance(); + break; + } + } + break; + } + else if(loc.current() == ']') + { + break; // found closing bracket + } + else + { + loc.advance(); + } + } + return ; +} + +template +void skip_inline_table_like(location& loc, const context& ctx) +{ + assert(loc.current() == '{'); + loc.advance(); + + const auto& spec = ctx.toml_spec(); + + while( ! loc.eof()) + { + if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables) + { + break; // missing closing `}`. + } + else if(loc.current() == '\"' || loc.current() == '\'') + { + skip_string_like(loc, ctx); + } + else if(loc.current() == '#') + { + skip_comment_block(loc, ctx); + if( ! spec.v1_1_0_allow_newlines_in_inline_tables) + { + // comment must end with newline. + break; // missing closing `}`. + } + } + else if(loc.current() == '[') + { + const auto checkpoint = loc; + if(syntax::std_table(spec).scan(loc).is_ok() || + syntax::array_table(spec).scan(loc).is_ok()) + { + loc = checkpoint; + break; // missing closing `}`. + } + // if it is not a table-definition, then it is an array. + skip_array_like(loc, ctx); + } + else if(loc.current() == '{') + { + skip_inline_table_like(loc, ctx); + } + else if(loc.current() == '}') + { + // closing brace found. guessing the error is inside the table. + break; + } + else + { + // skip otherwise. + loc.advance(); + } + } + return ; +} + +template +void skip_value(location& loc, const context& ctx) +{ + value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty); + if(ty == value_t::string) + { + skip_string_like(loc, ctx); + } + else if(ty == value_t::array) + { + skip_array_like(loc, ctx); + } + else if(ty == value_t::table) + { + // In case of multiline tables, it may skip key-value pair but not the + // whole table. + skip_inline_table_like(loc, ctx); + } + else // others are an "in-line" values. skip until the next line + { + while( ! loc.eof()) + { + if(loc.current() == '\n') + { + break; + } + else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}') + { + break; + } + loc.advance(); + } + } + return; +} + +template +void skip_key_value_pair(location& loc, const context& ctx) +{ + while( ! loc.eof()) + { + if(loc.current() == '=') + { + skip_whitespace(loc, ctx); + skip_value(loc, ctx); + return; + } + else if(loc.current() == '\n') + { + // newline is found before finding `=`. assuming "missing `=`". + return; + } + loc.advance(); + } + return ; +} + +template +void skip_until_next_table(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + while( ! loc.eof()) + { + if(loc.current() == '\n') + { + loc.advance(); + const auto line_begin = loc; + + skip_whitespace(loc, ctx); + if(syntax::std_table(spec).scan(loc).is_ok()) + { + loc = line_begin; + return ; + } + if(syntax::array_table(spec).scan(loc).is_ok()) + { + loc = line_begin; + return ; + } + } + loc.advance(); + } +} + +} // namespace detail +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +namespace detail +{ +extern template bool skip_whitespace (location& loc, const context&); +extern template bool skip_empty_lines (location& loc, const context&); +extern template void skip_comment_block (location& loc, const context&); +extern template void skip_empty_or_comment_lines(location& loc, const context&); +extern template void skip_string_like (location& loc, const context&); +extern template void skip_array_like (location& loc, const context&); +extern template void skip_inline_table_like (location& loc, const context&); +extern template void skip_value (location& loc, const context&); +extern template void skip_key_value_pair (location& loc, const context&); +extern template void skip_until_next_table (location& loc, const context&); + +extern template bool skip_whitespace (location& loc, const context&); +extern template bool skip_empty_lines (location& loc, const context&); +extern template void skip_comment_block (location& loc, const context&); +extern template void skip_empty_or_comment_lines(location& loc, const context&); +extern template void skip_string_like (location& loc, const context&); +extern template void skip_array_like (location& loc, const context&); +extern template void skip_inline_table_like (location& loc, const context&); +extern template void skip_value (location& loc, const context&); +extern template void skip_key_value_pair (location& loc, const context&); +extern template void skip_until_next_table (location& loc, const context&); + +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_SKIP_HPP +#ifndef TOML11_PARSER_HPP +#define TOML11_PARSER_HPP + + +#include +#include + +#include +#include + +#if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM +#include +#endif + +namespace toml +{ + +struct syntax_error final : public ::toml::exception +{ + public: + syntax_error(std::string what_arg, std::vector err) + : what_(std::move(what_arg)), err_(std::move(err)) + {} + ~syntax_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + std::vector const& errors() const noexcept + { + return err_; + } + + private: + std::string what_; + std::vector err_; +}; + +struct file_io_error final : public ::toml::exception +{ + public: + + file_io_error(const std::string& msg, const std::string& fname) + : errno_(cxx::make_nullopt()), + what_(msg + " \"" + fname + "\"") + {} + file_io_error(int errnum, const std::string& msg, const std::string& fname) + : errno_(errnum), + what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum)) + {} + ~file_io_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + bool has_errno() const noexcept {return errno_.has_value();} + int get_errno() const noexcept {return errno_.value_or(0);} + + private: + + cxx::optional errno_; + std::string what_; +}; + +namespace detail +{ + +/* ============================================================================ + * __ ___ _ __ _ __ ___ _ _ + * / _/ _ \ ' \| ' \/ _ \ ' \ + * \__\___/_|_|_|_|_|_\___/_||_| + */ + +template +error_info make_syntax_error(std::string title, + const S& scanner, location loc, std::string suffix = "") +{ + auto msg = std::string("expected ") + scanner.expected_chars(loc); + auto src = source_location(region(loc)); + return make_error_info( + std::move(title), std::move(src), std::move(msg), std::move(suffix)); +} + + +/* ============================================================================ + * _ + * __ ___ _ __ _ __ ___ _ _| |_ + * / _/ _ \ ' \| ' \/ -_) ' \ _| + * \__\___/_|_|_|_|_|_\___|_||_\__| + */ + +template +result, error_info> +parse_comment_line(location& loc, context& ctx) +{ + const auto& spec = ctx.toml_spec(); + const auto first = loc; + + skip_whitespace(loc, ctx); + + const auto com_reg = syntax::comment(spec).scan(loc); + if(com_reg.is_ok()) + { + // once comment started, newline must follow (or reach EOF). + if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok()) + { + while( ! loc.eof()) // skip until newline to continue parsing + { + loc.advance(); + if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; } + } + return err(make_error_info("toml::parse_comment_line: " + "newline (LF / CRLF) or EOF is expected", + source_location(region(loc)), "but got this", + "Hint: most of the control characters are not allowed in comments")); + } + return ok(cxx::optional(com_reg.as_string())); + } + else + { + loc = first; // rollback whitespace to parse indent + return ok(cxx::optional(cxx::make_nullopt())); + } +} + +/* ============================================================================ + * ___ _ + * | _ ) ___ ___| |___ __ _ _ _ + * | _ \/ _ \/ _ \ / -_) _` | ' \ + * |___/\___/\___/_\___\__,_|_||_| + */ + +template +result, error_info> +parse_boolean(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::boolean(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_boolean: " + "invalid boolean: boolean must be `true` or `false`, in lowercase. " + "string must be surrounded by `\"`", syntax::boolean(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + const auto val = [&str]() { + if(str == "true") + { + return true; + } + else + { + assert(str == "false"); + return false; + } + }(); + + // ---------------------------------------------------------------------- + // no format info for boolean + boolean_format_info fmt; + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ + * |_ _|_ _| |_ ___ __ _ ___ _ _ + * | || ' \ _/ -_) _` / -_) '_| + * |___|_||_\__\___\__, \___|_| + * |___/ + */ + +template +result, error_info> +parse_bin_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::bin_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_bin_integer: " + "invalid integer: bin_int must be like: 0b0101, 0b1111_0000", + syntax::bin_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::bin; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0b` and zeros and underscores at the MSB + str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1')); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0b0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + const auto val = TC::parse_int(str, source_location(region(loc)), 2); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +// ---------------------------------------------------------------------------- + +template +result, error_info> +parse_oct_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::oct_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_oct_integer: " + "invalid integer: oct_int must be like: 0o775, 0o04_44", + syntax::oct_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::oct; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0o` and zeros and underscores at the MSB + str.erase(str.begin(), std::find_if( + std::next(str.begin(), 2), str.end(), [](const char c) { + return c != '0' && c != '_'; + })); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0o0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + const auto val = TC::parse_int(str, source_location(region(loc)), 8); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +template +result, error_info> +parse_hex_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::hex_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_hex_integer: " + "invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef", + syntax::hex_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::hex; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0x` and zeros and underscores at the MSB + str.erase(str.begin(), std::find_if( + std::next(str.begin(), 2), str.end(), [](const char c) { + return c != '0' && c != '_'; + })); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0x0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + // prefix zero and _ is removed. check if it uses upper/lower case. + // if both upper and lower case letters are found, set upper=true. + const auto lower_not_found = std::find_if(str.begin(), str.end(), + [](const char c) { return std::islower(static_cast(c)) != 0; }) == str.end(); + const auto upper_found = std::find_if(str.begin(), str.end(), + [](const char c) { return std::isupper(static_cast(c)) != 0; }) != str.end(); + fmt.uppercase = lower_not_found || upper_found; + + const auto val = TC::parse_int(str, source_location(region(loc)), 16); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +template +result, error_info> +parse_dec_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::dec_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_dec_integer: " + "invalid integer: dec_int must be like: 42, 123_456_789", + syntax::dec_int(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::dec; + fmt.width = str.size() - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + auto src = source_location(region(loc)); + const auto val = TC::parse_int(str, src, 10); + if(val.is_err()) + { + loc = first; + return err(val.as_err()); + } + + // ---------------------------------------------------------------------- + // parse suffix (extension) + + if(spec.ext_num_suffix && loc.current() == '_') + { + const auto sfx_reg = syntax::num_suffix(spec).scan(loc); + if( ! sfx_reg.is_ok()) + { + loc = first; + return err(make_error_info("toml::parse_dec_integer: " + "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", + source_location(region(loc)), "here")); + } + auto sfx = sfx_reg.as_string(); + assert( ! sfx.empty() && sfx.front() == '_'); + sfx.erase(sfx.begin()); // remove the first `_` + + fmt.suffix = sfx; + } + + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_integer(location& loc, const context& ctx) +{ + const auto first = loc; + + if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-')) + { + // skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid). + // without this, +0xDEAD_BEEF will be parsed as a decimal int and + // unexpected "xDEAD_BEEF" will appear after integer "+0". + loc.advance(); + } + + if( ! loc.eof() && loc.current() == '0') + { + loc.advance(); + if(loc.eof()) + { + // `[+-]?0`. parse as an decimal integer. + loc = first; + return parse_dec_integer(loc, ctx); + } + + const auto prefix = loc.current(); + auto prefix_src = source_location(region(loc)); + + loc = first; + + if(prefix == 'b') {return parse_bin_integer(loc, ctx);} + if(prefix == 'o') {return parse_oct_integer(loc, ctx);} + if(prefix == 'x') {return parse_hex_integer(loc, ctx);} + + if(std::isdigit(prefix)) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_integer: " + "leading zero in an decimal integer is not allowed", + std::move(src), "leading zero")); + } + } + + loc = first; + return parse_dec_integer(loc, ctx); +} + +/* ============================================================================ + * ___ _ _ _ + * | __| |___ __ _| |_(_)_ _ __ _ + * | _|| / _ \/ _` | _| | ' \/ _` | + * |_| |_\___/\__,_|\__|_|_||_\__, | + * |___/ + */ + +template +result, error_info> +parse_floating(location& loc, const context& ctx) +{ + using floating_type = typename basic_value::floating_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + bool is_hex = false; + std::string str; + region reg; + if(spec.ext_hex_float && sequence(character('0'), character('x')).scan(loc).is_ok()) + { + loc = first; + is_hex = true; + + reg = syntax::hex_floating(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_floating: " + "invalid hex floating: float must be like: 0xABCp-3f", + syntax::floating(spec), loc)); + } + str = reg.as_string(); + } + else + { + reg = syntax::floating(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_floating: " + "invalid floating: float must be like: -3.14159_26535, 6.022e+23, " + "inf, or nan (lowercase).", syntax::floating(spec), loc)); + } + str = reg.as_string(); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + floating_format_info fmt; + + if(is_hex) + { + fmt.fmt = floating_format::hex; + } + else + { + // since we already checked that the string conforms the TOML standard. + if(std::find(str.begin(), str.end(), 'e') != str.end() || + std::find(str.begin(), str.end(), 'E') != str.end()) + { + fmt.fmt = floating_format::scientific; // use exponent part + } + else + { + fmt.fmt = floating_format::fixed; // do not use exponent part + } + } + + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + floating_type val{0}; + + if(str == "inf" || str == "+inf") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) + { + val = std::numeric_limits::infinity(); + } + else + { + return err(make_error_info("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: inf is not supported")); + } + } + else if(str == "-inf") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) + { + val = -std::numeric_limits::infinity(); + } + else + { + return err(make_error_info("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: inf is not supported")); + } + } + else if(str == "nan" || str == "+nan") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) + { + val = std::numeric_limits::quiet_NaN(); + } + else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) + { + val = std::numeric_limits::signaling_NaN(); + } + else + { + return err(make_error_info("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: NaN is not supported")); + } + } + else if(str == "-nan") + { + using std::copysign; + TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) + { + val = copysign(std::numeric_limits::quiet_NaN(), floating_type(-1)); + } + else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) + { + val = copysign(std::numeric_limits::signaling_NaN(), floating_type(-1)); + } + else + { + return err(make_error_info("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: NaN is not supported")); + } + } + else + { + // set precision + const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-'); + const auto decpoint = std::find(str.begin(), str.end(), '.'); + const auto exponent = std::find_if(str.begin(), str.end(), + [](const char c) { return c == 'e' || c == 'E'; }); + if(decpoint != str.end() && exponent != str.end()) + { + assert(decpoint < exponent); + } + + if(fmt.fmt == floating_format::scientific) + { + // total width + fmt.prec = static_cast(std::distance(str.begin(), exponent)); + if(has_sign) + { + fmt.prec -= 1; + } + if(decpoint != str.end()) + { + fmt.prec -= 1; + } + } + else if(fmt.fmt == floating_format::hex) + { + fmt.prec = std::numeric_limits::max_digits10; + } + else + { + // width after decimal point + fmt.prec = static_cast(std::distance(std::next(decpoint), exponent)); + } + + auto src = source_location(region(loc)); + const auto res = TC::parse_float(str, src, is_hex); + if(res.is_ok()) + { + val = res.as_ok(); + } + else + { + return err(res.as_err()); + } + } + + // ---------------------------------------------------------------------- + // parse suffix (extension) + + if(spec.ext_num_suffix && loc.current() == '_') + { + const auto sfx_reg = syntax::num_suffix(spec).scan(loc); + if( ! sfx_reg.is_ok()) + { + auto src = source_location(region(loc)); + loc = first; + return err(make_error_info("toml::parse_floating: " + "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", + std::move(src), "here")); + } + auto sfx = sfx_reg.as_string(); + assert( ! sfx.empty() && sfx.front() == '_'); + sfx.erase(sfx.begin()); // remove the first `_` + + fmt.suffix = sfx; + } + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ _ _ + * | \ __ _| |_ ___| |_(_)_ __ ___ + * | |) / _` | _/ -_) _| | ' \/ -_) + * |___/\__,_|\__\___|\__|_|_|_|_\___| + */ + +// all the offset_datetime, local_datetime, local_date parses date part. +template +result, error_info> +parse_local_date_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + local_date_format_info fmt; + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::local_date(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_local_date: " + "invalid date: date must be like: 1234-05-06, yyyy-mm-dd.", + syntax::local_date(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + + // 0123456789 + // yyyy-mm-dd + const auto year_r = from_string(str.substr(0, 4)); + const auto month_r = from_string(str.substr(5, 2)); + const auto day_r = from_string(str.substr(8, 2)); + + if(year_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read year `" + str.substr(0, 4) + "`", + std::move(src), "here")); + } + if(month_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read month `" + str.substr(5, 2) + "`", + std::move(src), "here")); + } + if(day_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read day `" + str.substr(8, 2) + "`", + std::move(src), "here")); + } + + const auto year = year_r.unwrap(); + const auto month = month_r.unwrap(); + const auto day = day_r.unwrap(); + + { + // We briefly check whether the input date is valid or not. + // Actually, because of the historical reasons, there are several + // edge cases, such as 1582/10/5-1582/10/14 (only in several countries). + // But here, we do not care about it. + // It makes the code complicated and there is only low probability + // that such a specific date is needed in practice. If someone need to + // validate date accurately, that means that the one need a specialized + // library for their purpose in another layer. + + const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + const auto max_day = [month, is_leap]() { + if(month == 2) + { + return is_leap ? 29 : 28; + } + if(month == 4 || month == 6 || month == 9 || month == 11) + { + return 30; + } + return 31; + }(); + + if((month < 1 || 12 < month) || (day < 1 || max_day < day)) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: invalid date.", + std::move(src), "month must be 01-12, day must be any of " + "01-28,29,30,31 depending on the month/year.")); + } + } + + return ok(std::make_tuple( + local_date(year, static_cast(month - 1), day), + std::move(fmt), std::move(reg) + )); +} + +template +result, error_info> +parse_local_date(location& loc, const context& ctx) +{ + auto val_fmt_reg = parse_local_date_only(loc, ctx); + if(val_fmt_reg.is_err()) + { + return err(val_fmt_reg.unwrap_err()); + } + + auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); + auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); + auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +// all the offset_datetime, local_datetime, local_time parses date part. +template +result, error_info> +parse_local_time_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + local_time_format_info fmt; + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::local_time(spec).scan(loc); + if( ! reg.is_ok()) + { + if(spec.v1_1_0_make_seconds_optional) + { + return err(make_syntax_error("toml::parse_local_time: " + "invalid time: time must be HH:MM(:SS.sss) (seconds are optional)", + syntax::local_time(spec), loc)); + } + else + { + return err(make_syntax_error("toml::parse_local_time: " + "invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)", + syntax::local_time(spec), loc)); + } + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + + // at least we have HH:MM. + // 01234 + // HH:MM + const auto hour_r = from_string(str.substr(0, 2)); + const auto minute_r = from_string(str.substr(3, 2)); + + if(hour_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read hour `" + str.substr(0, 2) + "`", + std::move(src), "here")); + } + if(minute_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read minute `" + str.substr(3, 2) + "`", + std::move(src), "here")); + } + + const auto hour = hour_r.unwrap(); + const auto minute = minute_r.unwrap(); + + if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute)) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: invalid time.", + std::move(src), "hour must be 00-23, minute must be 00-59.")); + } + + // ----------------------------------------------------------------------- + // we have hour and minute. + // Since toml v1.1.0, second and subsecond part becomes optional. + // Check the version and return if second does not exist. + + if(str.size() == 5 && spec.v1_1_0_make_seconds_optional) + { + fmt.has_seconds = false; + fmt.subsecond_precision = 0; + return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg))); + } + assert(str.at(5) == ':'); + + // we have at least `:SS` part. `.subseconds` are optional. + + // 0 1 + // 012345678901234 + // HH:MM:SS.subsec + const auto sec_r = from_string(str.substr(6, 2)); + if(sec_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read second `" + str.substr(6, 2) + "`", + std::move(src), "here")); + } + const auto sec = sec_r.unwrap(); + + if(sec < 0 || 60 < sec) // :60 is allowed + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: invalid time.", + std::move(src), "second must be 00-60.")); + } + + if(str.size() == 8) + { + fmt.has_seconds = true; + fmt.subsecond_precision = 0; + return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg))); + } + + assert(str.at(8) == '.'); + + auto secfrac = str.substr(9, str.size() - 9); + + fmt.has_seconds = true; + fmt.subsecond_precision = secfrac.size(); + + while(secfrac.size() < 9) + { + secfrac += '0'; + } + assert(9 <= secfrac.size()); + const auto ms_r = from_string(secfrac.substr(0, 3)); + const auto us_r = from_string(secfrac.substr(3, 3)); + const auto ns_r = from_string(secfrac.substr(6, 3)); + + if(ms_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read milliseconds `" + secfrac.substr(0, 3) + "`", + std::move(src), "here")); + } + if(us_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read microseconds`" + str.substr(3, 3) + "`", + std::move(src), "here")); + } + if(ns_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read nanoseconds`" + str.substr(6, 3) + "`", + std::move(src), "here")); + } + const auto ms = ms_r.unwrap(); + const auto us = us_r.unwrap(); + const auto ns = ns_r.unwrap(); + + return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg))); +} + +template +result, error_info> +parse_local_time(location& loc, const context& ctx) +{ + const auto first = loc; + + auto val_fmt_reg = parse_local_time_only(loc, ctx); + if(val_fmt_reg.is_err()) + { + return err(val_fmt_reg.unwrap_err()); + } + + auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); + auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); + auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_local_datetime(location& loc, const context& ctx) +{ + using char_type = location::char_type; + + const auto first = loc; + + local_datetime_format_info fmt; + + // ---------------------------------------------------------------------- + + auto date_fmt_reg = parse_local_date_only(loc, ctx); + if(date_fmt_reg.is_err()) + { + return err(date_fmt_reg.unwrap_err()); + } + + if(loc.current() == char_type('T')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::upper_T; + } + else if(loc.current() == char_type('t')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::lower_t; + } + else if(loc.current() == char_type(' ')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::space; + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_local_datetime: " + "expect date-time delimiter `T`, `t` or ` `(space).", + std::move(src), "here")); + } + + auto time_fmt_reg = parse_local_time_only(loc, ctx); + if(time_fmt_reg.is_err()) + { + return err(time_fmt_reg.unwrap_err()); + } + + fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; + fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; + + // ---------------------------------------------------------------------- + + region reg(first, loc); + local_datetime val(std::get<0>(date_fmt_reg.unwrap()), + std::get<0>(time_fmt_reg.unwrap())); + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_offset_datetime(location& loc, const context& ctx) +{ + using char_type = location::char_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + offset_datetime_format_info fmt; + + // ---------------------------------------------------------------------- + // date part + + auto date_fmt_reg = parse_local_date_only(loc, ctx); + if(date_fmt_reg.is_err()) + { + return err(date_fmt_reg.unwrap_err()); + } + + // ---------------------------------------------------------------------- + // delimiter + + if(loc.current() == char_type('T')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::upper_T; + } + else if(loc.current() == char_type('t')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::lower_t; + } + else if(loc.current() == char_type(' ')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::space; + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "expect date-time delimiter `T` or ` `(space).", std::move(src), "here" + )); + } + + // ---------------------------------------------------------------------- + // time part + + auto time_fmt_reg = parse_local_time_only(loc, ctx); + if(time_fmt_reg.is_err()) + { + return err(time_fmt_reg.unwrap_err()); + } + + fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; + fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; + + // ---------------------------------------------------------------------- + // offset part + + const auto ofs_reg = syntax::time_offset(spec).scan(loc); + if( ! ofs_reg.is_ok()) + { + return err(make_syntax_error("toml::parse_offset_datetime: " + "invalid offset: offset must be like: Z, +01:00, or -10:00.", + syntax::time_offset(spec), loc)); + } + + const auto ofs_str = ofs_reg.as_string(); + + time_offset offset(0, 0); + + assert(ofs_str.size() != 0); + + if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-')) + { + const auto hour_r = from_string(ofs_str.substr(1, 2)); + const auto minute_r = from_string(ofs_str.substr(4, 2)); + if(hour_r.is_err()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "Failed to read offset hour part", std::move(src), "here")); + } + if(minute_r.is_err()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "Failed to read offset minute part", std::move(src), "here")); + } + const auto hour = hour_r.unwrap(); + const auto minute = minute_r.unwrap(); + + if(ofs_str.at(0) == '+') + { + offset = time_offset(hour, minute); + } + else + { + offset = time_offset(-hour, -minute); + } + } + else + { + assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z')); + } + + if (offset.hour < -24 || 24 < offset.hour || + offset.minute < -60 || 60 < offset.minute) + { + return err(make_error_info("toml::parse_offset_datetime: " + "too large offset: |hour| <= 24, |minute| <= 60", + source_location(region(first, loc)), "here")); + } + + + // ---------------------------------------------------------------------- + + region reg(first, loc); + offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()), + std::get<0>(time_fmt_reg.unwrap())), + offset); + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ _ + * / __| |_ _ _(_)_ _ __ _ + * \__ \ _| '_| | ' \/ _` | + * |___/\__|_| |_|_||_\__, | + * |___/ + */ + +template +result::string_type, error_info> +parse_utf8_codepoint(const region& reg) +{ + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + + // assert(reg.as_lines().size() == 1); // XXX heavy check + + const auto str = reg.as_string(); + assert( ! str.empty()); + assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x'); + + std::uint_least32_t codepoint; + std::istringstream iss(str.substr(1)); + iss >> std::hex >> codepoint; + + const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type { + const auto uc = static_cast(i & 0xFF); + return cxx::bit_cast(uc); + }; + + string_type character; + if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. + { + character += static_cast(codepoint); + } + else if(codepoint < 0x800) //U+0080 ... U+07FF + { + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + character += to_char(0xC0|(codepoint >> 6 )); + character += to_char(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x10000) // U+0800...U+FFFF + { + if(0xD800 <= codepoint && codepoint <= 0xDFFF) + { + auto src = source_location(reg); + return err(make_error_info("toml::parse_utf8_codepoint: " + "[0xD800, 0xDFFF] is not a valid UTF-8", + std::move(src), "here")); + } + assert(codepoint < 0xD800 || 0xDFFF < codepoint); + // 1110yyyy 10yxxxxx 10xxxxxx + character += to_char(0xE0| (codepoint >> 12)); + character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); + character += to_char(0x80|((codepoint ) & 0x3F)); + } + else if(codepoint < 0x110000) // U+010000 ... U+10FFFF + { + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + character += to_char(0xF0| (codepoint >> 18)); + character += to_char(0x80|((codepoint >> 12) & 0x3F)); + character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); + character += to_char(0x80|((codepoint ) & 0x3F)); + } + else // out of UTF-8 region + { + auto src = source_location(reg); + return err(make_error_info("toml::parse_utf8_codepoint: " + "input codepoint is too large.", + std::move(src), "must be in range [0x00, 0x10FFFF]")); + } + return ok(character); +} + +template +result::string_type, error_info> +parse_escape_sequence(location& loc, const context& ctx) +{ + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + + const auto& spec = ctx.toml_spec(); + + assert( ! loc.eof()); + assert(loc.current() == '\\'); + loc.advance(); // consume the first backslash + + string_type retval; + + if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); } + else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); } + else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); } + else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); } + else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); } + else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); } + else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); } + else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e') + { + retval += char_type('\x1b'); + loc.advance(); + } + else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x') + { + auto scanner = sequence(character('x'), repeat_exact(2, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\xhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else if(loc.current() == 'u') + { + auto scanner = sequence(character('u'), repeat_exact(4, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\uhhhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else if(loc.current() == 'U') + { + auto scanner = sequence(character('U'), repeat_exact(8, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\Uhhhhhhhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else + { + auto src = source_location(region(loc)); + std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t"; + if(spec.v1_1_0_add_escape_sequence_e) + { + escape_seqs += ", \\e"; + } + if(spec.v1_1_0_add_escape_sequence_x) + { + escape_seqs += ", \\xhh"; + } + escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh"; + + return err(make_error_info("toml::parse_escape_sequence: " + "unknown escape sequence.", std::move(src), escape_seqs)); + } + return ok(retval); +} + +template +result, error_info> +parse_ml_basic_string(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + string_format_info fmt; + fmt.fmt = string_format::multiline_basic; + + auto reg = syntax::ml_basic_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_ml_basic_string: " + "invalid string format", + syntax::ml_basic_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + // we already checked that it starts with """ and ends with """. + assert(str.substr(0, 3) == "\"\"\""); + str.erase(0, 3); + + assert(str.size() >= 3); + assert(str.substr(str.size()-3, 3) == "\"\"\""); + str.erase(str.size()-3, 3); + + // the first newline just after """ is trimmed + if(str.size() >= 1 && str.at(0) == '\n') + { + str.erase(0, 1); + fmt.start_with_newline = true; + } + else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') + { + str.erase(0, 2); + fmt.start_with_newline = true; + } + + using string_type = typename basic_value::string_type; + string_type val; + { + auto iter = str.cbegin(); + while(iter != str.cend()) + { + if(*iter == '\\') // remove whitespaces around escaped-newline + { + // we assume that the string is not too long to copy + auto loc2 = make_temporary_location(make_string(iter, str.cend())); + if(syntax::escaped_newline(spec).scan(loc2).is_ok()) + { + std::advance(iter, loc2.get_location()); // skip escaped newline and indent + // now iter points non-WS char + assert(iter == str.end() || (*iter != ' ' && *iter != '\t')); + } + else // normal escape seq. + { + auto esc = parse_escape_sequence(loc2, ctx); + + // syntax does not check its value. the unicode codepoint may be + // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] + if(esc.is_err()) + { + return err(esc.unwrap_err()); + } + + val += esc.unwrap(); + std::advance(iter, loc2.get_location()); + } + } + else // we already checked the syntax. we don't need to check it again. + { + val += static_cast(*iter); + ++iter; + } + } + } + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result::string_type, region>, error_info> +parse_basic_string_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::basic_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_basic_string: " + "invalid string format", + syntax::basic_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.back() == '\"'); + str.pop_back(); + assert(str.at(0) == '\"'); + str.erase(0, 1); + + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + string_type val; + + { + auto iter = str.begin(); + while(iter != str.end()) + { + if(*iter == '\\') + { + auto loc2 = make_temporary_location(make_string(iter, str.end())); + + auto esc = parse_escape_sequence(loc2, ctx); + + // syntax does not check its value. the unicode codepoint may be + // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] + if(esc.is_err()) + { + return err(esc.unwrap_err()); + } + + val += esc.unwrap(); + std::advance(iter, loc2.get_location()); + } + else + { + val += char_type(*iter); // we already checked the syntax. + ++iter; + } + } + } + return ok(std::make_pair(val, reg)); +} + +template +result, error_info> +parse_basic_string(location& loc, const context& ctx) +{ + const auto first = loc; + + string_format_info fmt; + fmt.fmt = string_format::basic; + + auto val_res = parse_basic_string_only(loc, ctx); + if(val_res.is_err()) + { + return err(std::move(val_res.unwrap_err())); + } + auto val = std::move(val_res.unwrap().first ); + auto reg = std::move(val_res.unwrap().second); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_ml_literal_string(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + string_format_info fmt; + fmt.fmt = string_format::multiline_literal; + + auto reg = syntax::ml_literal_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_ml_literal_string: " + "invalid string format", + syntax::ml_literal_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.substr(0, 3) == "'''"); + assert(str.substr(str.size()-3, 3) == "'''"); + str.erase(0, 3); + str.erase(str.size()-3, 3); + + // the first newline just after """ is trimmed + if(str.size() >= 1 && str.at(0) == '\n') + { + str.erase(0, 1); + fmt.start_with_newline = true; + } + else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') + { + str.erase(0, 2); + fmt.start_with_newline = true; + } + + using string_type = typename basic_value::string_type; + string_type val(str.begin(), str.end()); + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result::string_type, region>, error_info> +parse_literal_string_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::literal_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_literal_string: " + "invalid string format", + syntax::literal_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.back() == '\''); + str.pop_back(); + assert(str.at(0) == '\''); + str.erase(0, 1); + + using string_type = typename basic_value::string_type; + string_type val(str.begin(), str.end()); + + return ok(std::make_pair(std::move(val), std::move(reg))); +} + +template +result, error_info> +parse_literal_string(location& loc, const context& ctx) +{ + const auto first = loc; + + string_format_info fmt; + fmt.fmt = string_format::literal; + + auto val_res = parse_literal_string_only(loc, ctx); + if(val_res.is_err()) + { + return err(std::move(val_res.unwrap_err())); + } + auto val = std::move(val_res.unwrap().first ); + auto reg = std::move(val_res.unwrap().second); + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result, error_info> +parse_string(location& loc, const context& ctx) +{ + const auto first = loc; + + if( ! loc.eof() && loc.current() == '"') + { + if(literal("\"\"\"").scan(loc).is_ok()) + { + loc = first; + return parse_ml_basic_string(loc, ctx); + } + else + { + loc = first; + return parse_basic_string(loc, ctx); + } + } + else if( ! loc.eof() && loc.current() == '\'') + { + if(literal("'''").scan(loc).is_ok()) + { + loc = first; + return parse_ml_literal_string(loc, ctx); + } + else + { + loc = first; + return parse_literal_string(loc, ctx); + } + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_string: " + "not a string", std::move(src), "here")); + } +} + +template +result, error_info> +parse_null(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + if( ! spec.ext_null_value) + { + return err(make_error_info("toml::parse_null: " + "invalid spec: spec.ext_null_value must be true.", + source_location(region(loc)), "here")); + } + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::null_value(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_null: " + "invalid null: null must be lowercase. ", + syntax::null_value(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + // ---------------------------------------------------------------------- + // no format info for boolean + + return ok(basic_value(detail::none_t{}, std::move(reg))); +} + +/* ============================================================================ + * _ __ + * | |/ /___ _ _ + * | ' +result::key_type, error_info> +parse_simple_key(location& loc, const context& ctx) +{ + using key_type = typename basic_value::key_type; + const auto& spec = ctx.toml_spec(); + + if(loc.current() == '\"') + { + auto str_res = parse_basic_string_only(loc, ctx); + if(str_res.is_ok()) + { + return ok(std::move(str_res.unwrap().first)); + } + else + { + return err(std::move(str_res.unwrap_err())); + } + } + else if(loc.current() == '\'') + { + auto str_res = parse_literal_string_only(loc, ctx); + if(str_res.is_ok()) + { + return ok(std::move(str_res.unwrap().first)); + } + else + { + return err(std::move(str_res.unwrap_err())); + } + } + + // bare key. + + if(const auto bare = syntax::unquoted_key(spec).scan(loc)) + { + return ok(string_conv(bare.as_string())); + } + else + { + std::string postfix; + if(spec.v1_1_0_allow_non_english_in_bare_keys) + { + postfix = "Hint: Not all Unicode characters are allowed as bare key.\n"; + } + else + { + postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n"; + } + return err(make_syntax_error("toml::parse_simple_key: " + "invalid key: key must be \"quoted\", 'quoted-literal', or bare key.", + syntax::unquoted_key(spec), loc, postfix)); + } +} + +// dotted key become vector of keys +template +result::key_type>, region>, error_info> +parse_key(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + using key_type = typename basic_value::key_type; + std::vector keys; + while( ! loc.eof()) + { + auto key = parse_simple_key(loc, ctx); + if( ! key.is_ok()) + { + return err(key.unwrap_err()); + } + keys.push_back(std::move(key.unwrap())); + + auto reg = syntax::dot_sep(spec).scan(loc); + if( ! reg.is_ok()) + { + break; + } + } + if(keys.empty()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_key: expected a new key, " + "but got nothing", std::move(src), "reached EOF")); + } + + return ok(std::make_pair(std::move(keys), region(first, loc))); +} + +// ============================================================================ + +// forward-decl to implement parse_array and parse_table +template +result, error_info> +parse_value(location&, context& ctx); + +template +result::key_type>, region>, + basic_value + >, error_info> +parse_key_value_pair(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto key_res = parse_key(loc, ctx); + if(key_res.is_err()) + { + loc = first; + return err(key_res.unwrap_err()); + } + + if( ! syntax::keyval_sep(spec).scan(loc).is_ok()) + { + auto e = make_syntax_error("toml::parse_key_value_pair: " + "invalid key value separator `=`", syntax::keyval_sep(spec), loc); + loc = first; + return err(std::move(e)); + } + + auto v_res = parse_value(loc, ctx); + if(v_res.is_err()) + { + // loc = first; + return err(v_res.unwrap_err()); + } + return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap()))); +} + +/* ============================================================================ + * __ _ _ _ _ _ __ _ _ _ + * / _` | '_| '_/ _` | || | + * \__,_|_| |_| \__,_|\_, | + * |__/ + */ + +// array(and multiline inline table with `{` and `}`) has the following format. +// `[` +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` +// ... +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)? +// (ws|newline|comment-line)? `]` +// it skips (ws|newline|comment-line) and returns the token. +template +struct multiline_spacer +{ + using comment_type = typename TC::comment_type; + bool newline_found; + indent_char indent_type; + std::int32_t indent; + comment_type comments; +}; +template +std::ostream& operator<<(std::ostream& os, const multiline_spacer& sp) +{ + os << "{newline=" << sp.newline_found << ", "; + os << "indent_type=" << sp.indent_type << ", "; + os << "indent=" << sp.indent << ", "; + os << "comments=" << sp.comments.size() << "}"; + return os; +} + +template +cxx::optional> +skip_multiline_spacer(location& loc, context& ctx, const bool newline_found = false) +{ + const auto& spec = ctx.toml_spec(); + + multiline_spacer spacer; + spacer.newline_found = newline_found; + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer.comments.clear(); + + bool spacer_found = false; + while( ! loc.eof()) + { + if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc)) + { + spacer.newline_found = true; + auto comment = comm.as_string(); + if( ! comment.empty() && comment.back() == '\n') + { + comment.pop_back(); + if (!comment.empty() && comment.back() == '\r') + { + comment.pop_back(); + } + } + + spacer.comments.push_back(std::move(comment)); + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer_found = true; + } + else if(auto nl = syntax::newline(spec).scan(loc)) + { + spacer.newline_found = true; + spacer.comments.clear(); + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer_found = true; + } + else if(auto sp = repeat_at_least(1, character(cxx::bit_cast(' '))).scan(loc)) + { + spacer.indent_type = indent_char::space; + spacer.indent = static_cast(sp.length()); + spacer_found = true; + } + else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast('\t'))).scan(loc)) + { + spacer.indent_type = indent_char::tab; + spacer.indent = static_cast(tabs.length()); + spacer_found = true; + } + else + { + break; // done + } + } + if( ! spacer_found) + { + return cxx::make_nullopt(); + } + return spacer; +} + +// not an [[array.of.tables]]. It parses ["this", "type"] +template +result, error_info> +parse_array(location& loc, context& ctx) +{ + const auto num_errors = ctx.errors().size(); + + const auto first = loc; + + if(loc.eof() || loc.current() != '[') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_array: " + "The next token is not an array", std::move(src), "here")); + } + loc.advance(); + + typename basic_value::array_type val; + + array_format_info fmt; + fmt.fmt = array_format::oneline; + fmt.indent_type = indent_char::none; + + auto spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = array_format::multiline; + } + + bool comma_found = true; + while( ! loc.eof()) + { + if(loc.current() == location::char_type(']')) + { + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.closing_indent = spacer.value().indent; + } + break; + } + + if( ! comma_found) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_array: " + "expected value-separator `,` or closing `]`", + std::move(src), "here")); + } + + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + + if(auto elem_res = parse_value(loc, ctx)) + { + auto elem = std::move(elem_res.unwrap()); + + if(spacer.has_value()) // copy previous comments to value + { + elem.comments() = std::move(spacer.value().comments); + } + + // parse spaces between a value and a comma + // array = [ + // 42 , # the answer + // ^^^^ + // 3.14 # pi + // , 2.71 ^^^^ + // ^^ + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value()) + { + for(std::size_t i=0; i( + std::move(val), std::move(fmt), {}, region(first, loc) + )); +} + +/* ============================================================================ + * _ _ _ _ _ _ + * (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___ + * | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_) + * |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___| + */ + +// ---------------------------------------------------------------------------- +// insert_value is the most complicated part of the toml spec. +// +// To parse a toml file correctly, we sometimes need to check an exising value +// is appendable or not. +// +// For example while parsing an inline array of tables, +// +// ```toml +// aot = [ +// {a = "foo"}, +// {a = "bar", b = "baz"}, +// ] +// ``` +// +// this `aot` is appendable until parser reaches to `]`. After that, it becomes +// non-appendable. +// +// On the other hand, a normal array of tables, such as +// +// ```toml +// [[aot]] +// a = "foo" +// +// [[aot]] +// a = "bar" +// b = "baz" +// ``` +// This `[[aot]]` is appendable until the parser reaches to the EOF. +// +// +// It becomes a bit more difficult in case of dotted keys. +// In TOML, it is allowed to append a key-value pair to a table that is +// *implicitly* defined by a subtable definitino. +// +// ```toml +// [x.y.z] +// w = 123 +// +// [x] +// a = "foo" # OK. x is defined implicitly by `[x.y.z]`. +// ``` +// +// But if the table is defined by a dotted keys, it is not appendable. +// +// ```toml +// [x] +// y.z.w = 123 +// +// [x.y] +// # ERROR. x.y is already defined by a dotted table in the previous table. +// ``` +// +// Also, reopening a table using dotted keys is invalid. +// +// ```toml +// [x.y.z] +// w = 123 +// +// [x] +// y.z.v = 42 # ERROR. [x.y.z] is already defined. +// ``` +// +// +// ```toml +// [a] +// b.c = "foo" +// b.d = "bar" +// ``` +// +// +// ```toml +// a.b = "foo" +// [a] +// c = "bar" # ERROR +// ``` +// +// In summary, +// - a table must be defined only once. +// - assignment to an exising table is possible only when: +// - defining a subtable [x.y] to an existing table [x]. +// - defining supertable [x] explicitly after [x.y]. +// - adding dotted keys in the same table. + +enum class inserting_value_kind : std::uint8_t +{ + std_table, // insert [standard.table] + array_table, // insert [[array.of.tables]] + dotted_keys // insert a.b.c = "this" +}; + +template +result*, error_info> +insert_value(const inserting_value_kind kind, + typename basic_value::table_type* current_table_ptr, + const std::vector::key_type>& keys, region key_reg, + basic_value val) +{ + using value_type = basic_value; + using array_type = typename basic_value::array_type; + using table_type = typename basic_value::table_type; + + auto key_loc = source_location(key_reg); + + assert( ! keys.empty()); + + // dotted key can insert to dotted key tables defined at the same level. + // dotted key can NOT reopen a table even if it is implcitly-defined one. + // + // [x.y.z] # define x and x.y implicitly. + // a = 42 + // + // [x] # reopening implcitly defined table + // r.s.t = 3.14 # VALID r and r.s are new tables. + // r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid. + // + // y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key. + // y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key. + + // a table cannot reopen dotted-key tables. + // + // [t1] + // t2.t3.v = 0 + // [t1.t2] # INVALID t1.t2 is defined as a dotted-key table. + + for(std::size_t i=0; i{}, key_reg)); + + assert(current_table.at(key).is_table()); + current_table_ptr = std::addressof(current_table.at(key).as_table()); + } + else if (found->second.is_table()) + { + const auto fmt = found->second.as_table_fmt().fmt; + if(fmt == table_format::oneline || fmt == table_format::multiline_oneline) + { + // foo = {bar = "baz"} or foo = { \n bar = "baz" \n } + return err(make_error_info("toml::insert_value: " + "failed to insert a value: inline table is immutable", + key_loc, "inserting this", + found->second.location(), "to this table")); + } + // dotted key cannot reopen a table. + if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted) + { + return err(make_error_info("toml::insert_value: " + "reopening a table using dotted keys", + key_loc, "dotted key cannot reopen a table", + found->second.location(), "this table is already closed")); + } + assert(found->second.is_table()); + current_table_ptr = std::addressof(found->second.as_table()); + } + else if(found->second.is_array_of_tables()) + { + // aot = [{this = "type", of = "aot"}] # cannot be reopened + if(found->second.as_array_fmt().fmt != array_format::array_of_tables) + { + return err(make_error_info("toml::insert_value:" + "inline array of tables are immutable", + key_loc, "inserting this", + found->second.location(), "inline array of tables")); + } + // appending to [[aot]] + + if(kind == inserting_value_kind::dotted_keys) + { + // [[array.of.tables]] + // [array.of] # reopening supertable is okay + // tables.x = "foo" # appending `x` to the first table + return err(make_error_info("toml::insert_value:" + "dotted key cannot reopen an array-of-tables", + key_loc, "inserting this", + found->second.location(), "to this array-of-tables.")); + } + + // insert_value_by_dotkeys::std_table + // [[array.of.tables]] + // [array.of.tables.subtable] # appending to the last aot + // + // insert_value_by_dotkeys::array_table + // [[array.of.tables]] + // [[array.of.tables.subtable]] # appending to the last aot + auto& current_array_table = found->second.as_array().back(); + + assert(current_array_table.is_table()); + current_table_ptr = std::addressof(current_array_table.as_table()); + } + else + { + return err(make_error_info("toml::insert_value: " + "failed to insert a value, value already exists", + key_loc, "while inserting this", + found->second.location(), "non-table value already exists")); + } + } + else // this is the last key. insert a new value. + { + switch(kind) + { + case inserting_value_kind::dotted_keys: + { + if(current_table.find(key) != current_table.end()) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a value, value already exists", + key_loc, "inserting this", + current_table.at(key).location(), "but value already exists")); + } + current_table.emplace(key, std::move(val)); + return ok(std::addressof(current_table.at(key))); + } + case inserting_value_kind::std_table: + { + // defining a new table or reopening supertable + auto found = current_table.find(key); + if(found == current_table.end()) // define a new aot + { + current_table.emplace(key, std::move(val)); + return ok(std::addressof(current_table.at(key))); + } + else // the table is already defined, reopen it + { + // assigning a [std.table]. it must be an implicit table. + auto& target = found->second; + if( ! target.is_table() || // could be an array-of-tables + target.as_table_fmt().fmt != table_format::implicit) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a table, table already defined", + key_loc, "inserting this", + target.location(), "this table is explicitly defined")); + } + + // merge table + for(const auto& kv : val.as_table()) + { + if(target.contains(kv.first)) + { + // [x.y.z] + // w = "foo" + // [x] + // y = "bar" + return err(make_error_info("toml::insert_value: " + "failed to insert a table, table keys conflict to each other", + key_loc, "inserting this table", + kv.second.location(), "having this value", + target.at(kv.first).location(), "already defined here")); + } + else + { + target[kv.first] = kv.second; + } + } + // change implicit -> explicit + target.as_table_fmt().fmt = table_format::multiline; + // change definition region + change_region_of_value(target, val); + + return ok(std::addressof(current_table.at(key))); + } + } + case inserting_value_kind::array_table: + { + auto found = current_table.find(key); + if(found == current_table.end()) // define a new aot + { + array_format_info fmt; + fmt.fmt = array_format::array_of_tables; + fmt.indent_type = indent_char::none; + + current_table.emplace(key, value_type( + array_type{ std::move(val) }, std::move(fmt), + std::vector{}, std::move(key_reg) + )); + + assert( ! current_table.at(key).as_array().empty()); + return ok(std::addressof(current_table.at(key).as_array().back())); + } + else // the array is already defined, append to it + { + if( ! found->second.is_array_of_tables()) + { + return err(make_error_info("toml::insert_value: " + "failed to insert an array of tables, value already exists", + key_loc, "while inserting this", + found->second.location(), "non-table value already exists")); + } + if(found->second.as_array_fmt().fmt != array_format::array_of_tables) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a table, inline array of tables is immutable", + key_loc, "while inserting this", + found->second.location(), "this is inline array-of-tables")); + } + found->second.as_array().push_back(std::move(val)); + assert( ! current_table.at(key).as_array().empty()); + return ok(std::addressof(current_table.at(key).as_array().back())); + } + } + default: {assert(false);} + } + } + } + return err(make_error_info("toml::insert_key: no keys found", + std::move(key_loc), "here")); +} + +// ---------------------------------------------------------------------------- + +template +result, error_info> +parse_inline_table(location& loc, context& ctx) +{ + using table_type = typename basic_value::table_type; + + const auto num_errors = ctx.errors().size(); + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + if(loc.eof() || loc.current() != '{') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "The next token is not an inline table", std::move(src), "here")); + } + loc.advance(); + + table_type table; + table_format_info fmt; + fmt.fmt = table_format::oneline; + fmt.indent_type = indent_char::none; + + cxx::optional> spacer(cxx::make_nullopt()); + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + } + } + else + { + skip_whitespace(loc, ctx); + } + + bool still_empty = true; + bool comma_found = false; + while( ! loc.eof()) + { + // closing! + if(loc.current() == '}') + { + if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: trailing " + "comma is not allowed in TOML-v1.0.0)", std::move(src), "here")); + } + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.closing_indent = spacer.value().indent; + } + } + break; + } + + // if we already found a value and didn't found `,` nor `}`, error. + if( ! comma_found && ! still_empty) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "expected value-separator `,` or closing `}`", + std::move(src), "here")); + } + + // parse indent. + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + + still_empty = false; // parsing a value... + if(auto kv_res = parse_key_value_pair(loc, ctx)) + { + auto keys = std::move(kv_res.unwrap().first.first); + auto key_reg = std::move(kv_res.unwrap().first.second); + auto val = std::move(kv_res.unwrap().second); + + auto ins_res = insert_value(inserting_value_kind::dotted_keys, + std::addressof(table), keys, std::move(key_reg), std::move(val)); + if(ins_res.is_err()) + { + ctx.report_error(std::move(ins_res.unwrap_err())); + // we need to skip until the next value (or end of the table) + // because we don't have valid kv pair. + while( ! loc.eof()) + { + const auto c = loc.current(); + if(c == ',' || c == '\n' || c == '}') + { + comma_found = (c == ','); + break; + } + loc.advance(); + } + continue; + } + + // if comment line follows immediately(without newline) after `,`, then + // the comment is for the elem. we need to check if comment follows `,`. + // + // (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)? + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + if(spacer.has_value()) // copy previous comments to value + { + for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); + } + } + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value()) + { + for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); + } + if(spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + if(spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + } + } + } + else + { + skip_whitespace(loc, ctx); + } + + comma_found = character(',').scan(loc).is_ok(); + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + auto com_res = parse_comment_line(loc, ctx); + if(com_res.is_err()) + { + ctx.report_error(com_res.unwrap_err()); + } + const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); + if(comment_found) + { + fmt.fmt = table_format::multiline_oneline; + ins_res.unwrap()->comments().push_back(com_res.unwrap().value()); + } + if(comma_found) + { + spacer = skip_multiline_spacer(loc, ctx, comment_found); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + } + } + } + else + { + skip_whitespace(loc, ctx); + } + } + else + { + ctx.report_error(std::move(kv_res.unwrap_err())); + while( ! loc.eof()) + { + if(loc.current() == '}') + { + break; + } + if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n') + { + break; + } + loc.advance(); + } + break; + } + } + + if(loc.current() != '}') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "missing closing bracket `}`", + std::move(src), "expected `}`, reached line end")); + } + else + { + loc.advance(); // skip } + } + + // any error reported from this function + if(num_errors < ctx.errors().size()) + { + assert(ctx.has_error()); // already reported + return err(ctx.pop_last_error()); + } + + basic_value retval( + std::move(table), std::move(fmt), {}, region(first, loc)); + + return ok(std::move(retval)); +} + +/* ============================================================================ + * _ + * __ ____ _| |_ _ ___ + * \ V / _` | | || / -_) + * \_/\__,_|_|\_,_\___| + */ + +template +result +guess_number_type(const location& first, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + location loc = first; + + if(syntax::offset_datetime(spec).scan(loc).is_ok()) + { + return ok(value_t::offset_datetime); + } + loc = first; + + if(syntax::local_datetime(spec).scan(loc).is_ok()) + { + const auto curr = loc.current(); + // if offset_datetime contains bad offset, it syntax::offset_datetime + // fails to scan it. + if(curr == '+' || curr == '-') + { + return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z", + syntax::time_offset(spec), loc, std::string( + "Hint: valid : +09:00, -05:30\n" + "Hint: invalid: +9:00, -5:30\n"))); + } + return ok(value_t::local_datetime); + } + loc = first; + + if(syntax::local_date(spec).scan(loc).is_ok()) + { + // bad time may appear after this. + + if( ! loc.eof()) + { + const auto c = loc.current(); + if(c == 'T' || c == 't') + { + loc.advance(); + + return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", + syntax::local_time(spec), loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + if(c == ' ') + { + // A space is allowed as a delimiter between local time. + // But there is a case where bad time follows a space. + // - invalid: 2019-06-16 7:00:00 + // - valid : 2019-06-16 07:00:00 + loc.advance(); + if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9')) + { + return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", + syntax::local_time(spec), loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + } + if('0' <= c && c <= '9') + { + return err(make_syntax_error("bad datetime: missing T or space", + character_either{'T', 't', ' '}, loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + } + return ok(value_t::local_date); + } + loc = first; + + if(syntax::local_time(spec).scan(loc).is_ok()) + { + return ok(value_t::local_time); + } + loc = first; + + if(syntax::floating(spec).scan(loc).is_ok()) + { + if( ! loc.eof() && loc.current() == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::floating); + } + auto src = source_location(region(loc)); + return err(make_error_info( + "bad float: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); + } + return ok(value_t::floating); + } + loc = first; + + if(spec.ext_hex_float) + { + if(syntax::hex_floating(spec).scan(loc).is_ok()) + { + if( ! loc.eof() && loc.current() == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::floating); + } + auto src = source_location(region(loc)); + return err(make_error_info( + "bad float: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); + } + return ok(value_t::floating); + } + loc = first; + } + + if(auto int_reg = syntax::integer(spec).scan(loc)) + { + if( ! loc.eof()) + { + const auto c = loc.current(); + if(c == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::integer); + } + + if(int_reg.length() <= 2 && (int_reg.as_string() == "0" || + int_reg.as_string() == "-0" || int_reg.as_string() == "+0")) + { + auto src = source_location(region(loc)); + return err(make_error_info( + "bad integer: leading zero is not allowed in decimal int", + std::move(src), "leading zero", + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n")); + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info( + "bad integer: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n")); + } + } + if('0' <= c && c <= '9') + { + if(loc.current() == '0') + { + loc.retrace(); + return err(make_error_info( + "bad integer: leading zero", + source_location(region(loc)), "leading zero is not allowed", + std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + else // invalid digits, especially in oct/bin ints. + { + return err(make_error_info( + "bad integer: invalid digit after an integer", + source_location(region(loc)), "this digit is not allowed", + std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + } + if(c == ':' || c == '-') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad datetime: invalid format", + std::move(src), "here", + std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n" + "Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30") + )); + } + if(c == '.' || c == 'e' || c == 'E') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad float: invalid format", + std::move(src), "here", std::string( + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"))); + } + } + return ok(value_t::integer); + } + if( ! loc.eof() && loc.current() == '.') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad float: integer part is required before decimal point", + std::move(src), "missing integer part", std::string( + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n") + )); + } + if( ! loc.eof() && loc.current() == '_') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad number: `_` must be surrounded by digits", + std::move(src), "digits required before `_`", std::string( + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + + auto src = source_location(region(loc)); + return err(make_error_info("bad format: unknown value appeared", + std::move(src), "here")); +} + +template +result +guess_value_type(const location& loc, const context& ctx) +{ + const auto& sp = ctx.toml_spec(); + location inner(loc); + + switch(loc.current()) + { + case '"' : {return ok(value_t::string); } + case '\'': {return ok(value_t::string); } + case '[' : {return ok(value_t::array); } + case '{' : {return ok(value_t::table); } + case 't' : + { + return ok(value_t::boolean); + } + case 'f' : + { + return ok(value_t::boolean); + } + case 'T' : // invalid boolean. + { + return err(make_syntax_error("toml::parse_value: " + "`true` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::boolean(sp), inner)); + } + case 'F' : + { + return err(make_syntax_error("toml::parse_value: " + "`false` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::boolean(sp), inner)); + } + case 'i' : // inf or string without quotes(syntax error). + { + if(literal("inf").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`inf` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + case 'I' : // Inf or string without quotes(syntax error). + { + return err(make_syntax_error("toml::parse_value: " + "`inf` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + case 'n' : // nan or null-extension + { + if(sp.ext_null_value) + { + if(literal("nan").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else if(literal("null").scan(inner).is_ok()) + { + return ok(value_t::empty); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "Both `nan` and `null` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + else // must be nan. + { + if(literal("nan").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`nan` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + } + case 'N' : // nan or null-extension + { + if(sp.ext_null_value) + { + return err(make_syntax_error("toml::parse_value: " + "Both `nan` and `null` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`nan` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + default : + { + return guess_number_type(loc, ctx); + } + } +} + +template +result, error_info> +parse_value(location& loc, context& ctx) +{ + const auto ty_res = guess_value_type(loc, ctx); + if(ty_res.is_err()) + { + return err(ty_res.unwrap_err()); + } + + switch(ty_res.unwrap()) + { + case value_t::empty: + { + if(ctx.toml_spec().ext_null_value) + { + return parse_null(loc, ctx); + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_value: unknown value appeared", + std::move(src), "here")); + } + } + case value_t::boolean : {return parse_boolean (loc, ctx);} + case value_t::integer : {return parse_integer (loc, ctx);} + case value_t::floating : {return parse_floating (loc, ctx);} + case value_t::string : {return parse_string (loc, ctx);} + case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);} + case value_t::local_datetime : {return parse_local_datetime (loc, ctx);} + case value_t::local_date : {return parse_local_date (loc, ctx);} + case value_t::local_time : {return parse_local_time (loc, ctx);} + case value_t::array : {return parse_array (loc, ctx);} + case value_t::table : {return parse_inline_table (loc, ctx);} + default: + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_value: unknown value appeared", + std::move(src), "here")); + } + } +} + +/* ============================================================================ + * _____ _ _ + * |_ _|_ _| |__| |___ + * | |/ _` | '_ \ / -_) + * |_|\__,_|_.__/_\___| + */ + +template +result::key_type>, region>, error_info> +parse_table_key(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::std_table(spec).scan(loc); + if(!reg.is_ok()) + { + return err(make_syntax_error("toml::parse_table_key: invalid table key", + syntax::std_table(spec), loc)); + } + + loc = first; + loc.advance(); // skip [ + skip_whitespace(loc, ctx); + + auto keys_res = parse_key(loc, ctx); + if(keys_res.is_err()) + { + return err(std::move(keys_res.unwrap_err())); + } + + skip_whitespace(loc, ctx); + loc.advance(); // ] + + return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); +} + +template +result::key_type>, region>, error_info> +parse_array_table_key(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::array_table(spec).scan(loc); + if(!reg.is_ok()) + { + return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key", + syntax::array_table(spec), loc)); + } + + loc = first; + loc.advance(); // [ + loc.advance(); // [ + skip_whitespace(loc, ctx); + + auto keys_res = parse_key(loc, ctx); + if(keys_res.is_err()) + { + return err(std::move(keys_res.unwrap_err())); + } + + skip_whitespace(loc, ctx); + loc.advance(); // ] + loc.advance(); // ] + + return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); +} + +// called after reading [table.keys] and comments around it. +// Since table may already contain a subtable ([x.y.z] can be defined before [x]), +// the table that is being parsed is passed as an argument. +template +result +parse_table(location& loc, context& ctx, basic_value& table) +{ + assert(table.is_table()); + + const auto num_errors = ctx.errors().size(); + const auto& spec = ctx.toml_spec(); + + // clear indent info + table.as_table_fmt().indent_type = indent_char::none; + + bool newline_found = true; + while( ! loc.eof()) + { + const auto start = loc; + + auto sp = skip_multiline_spacer(loc, ctx, newline_found); + + // if reached to EOF, the table ends here. return. + if(loc.eof()) + { + break; + } + // if next table is comming, return. + if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok()) + { + loc = start; + break; + } + // otherwise, it should be a key-value pair. + newline_found = newline_found || (sp.has_value() && sp.value().newline_found); + if( ! newline_found) + { + return err(make_error_info("toml::parse_table: " + "newline (LF / CRLF) or EOF is expected", + source_location(region(loc)), "here")); + } + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + table.as_table_fmt().indent_type = sp.value().indent_type; + table.as_table_fmt().body_indent = sp.value().indent; + } + + newline_found = false; // reset + if(auto kv_res = parse_key_value_pair(loc, ctx)) + { + auto keys = std::move(kv_res.unwrap().first.first); + auto key_reg = std::move(kv_res.unwrap().first.second); + auto val = std::move(kv_res.unwrap().second); + + if(sp.has_value()) + { + for(const auto& com : sp.value().comments) + { + val.comments().push_back(com); + } + } + + if(auto com_res = parse_comment_line(loc, ctx)) + { + if(auto com_opt = com_res.unwrap()) + { + val.comments().push_back(com_opt.value()); + newline_found = true; // comment includes newline at the end + } + } + else + { + ctx.report_error(std::move(com_res.unwrap_err())); + } + + auto ins_res = insert_value(inserting_value_kind::dotted_keys, + std::addressof(table.as_table()), + keys, std::move(key_reg), std::move(val)); + if(ins_res.is_err()) + { + ctx.report_error(std::move(ins_res.unwrap_err())); + } + } + else + { + ctx.report_error(std::move(kv_res.unwrap_err())); + skip_key_value_pair(loc, ctx); + } + } + + if(num_errors < ctx.errors().size()) + { + assert(ctx.has_error()); // already reported + return err(ctx.pop_last_error()); + } + return ok(); +} + +template +result, std::vector> +parse_file(location& loc, context& ctx) +{ + using value_type = basic_value; + using table_type = typename value_type::table_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + if(loc.eof()) + { + return ok(value_type(table_type(), table_format_info{}, {}, region(loc))); + } + + value_type root(table_type(), table_format_info{}, {}, region(loc)); + root.as_table_fmt().fmt = table_format::multiline; + root.as_table_fmt().indent_type = indent_char::none; + + // parse top comment. + // + // ```toml + // # this is a comment for the top-level table. + // + // key = "the first value" + // ``` + // + // ```toml + // # this is a comment for "the first value". + // key = "the first value" + // ``` + while( ! loc.eof()) + { + if(auto com_res = parse_comment_line(loc, ctx)) + { + if(auto com_opt = com_res.unwrap()) + { + root.comments().push_back(std::move(com_opt.value())); + } + else // no comment found. + { + // if it is not an empty line, clear the root comment. + if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok()) + { + loc = first; + root.comments().clear(); + } + break; + } + } + else + { + ctx.report_error(std::move(com_res.unwrap_err())); + skip_comment_block(loc, ctx); + } + } + + // parse root table + { + const auto res = parse_table(loc, ctx, root); + if(res.is_err()) + { + ctx.report_error(std::move(res.unwrap_err())); + skip_until_next_table(loc, ctx); + } + } + + // parse tables + + while( ! loc.eof()) + { + auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true); + + if(auto key_res = parse_array_table_key(loc, ctx)) + { + auto key = std::move(std::get<0>(key_res.unwrap())); + auto reg = std::move(std::get<1>(key_res.unwrap())); + + std::vector com; + if(sp.has_value()) + { + for(std::size_t i=0; i(table_type()); + auto res = parse_table(loc, ctx, tmp); + if(res.is_err()) + { + ctx.report_error(res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + continue; + } + + auto tab_ptr = inserted.unwrap(); + assert(tab_ptr); + + const auto tab_res = parse_table(loc, ctx, *tab_ptr); + if(tab_res.is_err()) + { + ctx.report_error(tab_res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + + // parse_table first clears `indent_type`. + // to keep header indent info, we must store it later. + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; + tab_ptr->as_table_fmt().name_indent = sp.value().indent; + } + continue; + } + if(auto key_res = parse_table_key(loc, ctx)) + { + auto key = std::move(std::get<0>(key_res.unwrap())); + auto reg = std::move(std::get<1>(key_res.unwrap())); + + std::vector com; + if(sp.has_value()) + { + for(std::size_t i=0; i(table_type()); + auto res = parse_table(loc, ctx, tmp); + if(res.is_err()) + { + ctx.report_error(res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + continue; + } + + auto tab_ptr = inserted.unwrap(); + assert(tab_ptr); + + const auto tab_res = parse_table(loc, ctx, *tab_ptr); + if(tab_res.is_err()) + { + ctx.report_error(tab_res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; + tab_ptr->as_table_fmt().name_indent = sp.value().indent; + } + continue; + } + + // does not match array_table nor std_table. report an error. + const auto keytop = loc; + const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok(); + loc = keytop; + + if(maybe_array_of_tables) + { + ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key", + syntax::array_table(spec), loc)); + } + else + { + ctx.report_error(make_syntax_error("toml::parse_file: invalid table key", + syntax::std_table(spec), loc)); + } + skip_until_next_table(loc, ctx); + } + + if( ! ctx.errors().empty()) + { + return err(std::move(ctx.errors())); + } + return ok(std::move(root)); +} + +template +result, std::vector> +parse_impl(std::vector cs, std::string fname, const spec& s) +{ + using value_type = basic_value; + using table_type = typename value_type::table_type; + + // an empty file is a valid toml file. + if(cs.empty()) + { + auto src = std::make_shared>(std::move(cs)); + location loc(std::move(src), std::move(fname)); + return ok(value_type(table_type(), table_format_info{}, std::vector{}, region(loc))); + } + + // to simplify parser, add newline at the end if there is no LF. + // But, if it has raw CR, the file is invalid (in TOML, CR is not a valid + // newline char). if it ends with CR, do not add LF and report it. + if(cs.back() != '\n' && cs.back() != '\r') + { + cs.push_back('\n'); + } + + auto src = std::make_shared>(std::move(cs)); + + location loc(std::move(src), std::move(fname)); + + // skip BOM if found + if(loc.source()->size() >= 3) + { + auto first = loc.get_location(); + + const auto c0 = loc.current(); loc.advance(); + const auto c1 = loc.current(); loc.advance(); + const auto c2 = loc.current(); loc.advance(); + + const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF); + if( ! bom_found) + { + loc.set_location(first); + } + } + + context ctx(s); + + return parse_file(loc, ctx); +} + +} // detail + +// ----------------------------------------------------------------------------- +// parse(byte array) + +template +result, std::vector> +try_parse(std::vector content, std::string filename, + spec s = spec::default_version()) +{ + return detail::parse_impl(std::move(content), std::move(filename), std::move(s)); +} +template +basic_value +parse(std::vector content, std::string filename, + spec s = spec::default_version()) +{ + auto res = try_parse(std::move(content), std::move(filename), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ----------------------------------------------------------------------------- +// parse(istream) + +template +result, std::vector> +try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) +{ + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize), '\0'); + is.read(reinterpret_cast(letters.data()), static_cast(fsize)); + + return detail::parse_impl(std::move(letters), std::move(fname), std::move(s)); +} + +template +basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) +{ + auto res = try_parse(is, std::move(fname), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ----------------------------------------------------------------------------- +// parse(filename) + +template +result, std::vector> +try_parse(std::string fname, spec s = spec::default_version()) +{ + std::ifstream ifs(fname, std::ios_base::binary); + if(!ifs.good()) + { + std::vector e; + e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {})); + return err(std::move(e)); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return try_parse(ifs, std::move(fname), std::move(s)); +} + +template +basic_value parse(std::string fname, spec s = spec::default_version()) +{ + std::ifstream ifs(fname, std::ios_base::binary); + if(!ifs.good()) + { + throw file_io_error("toml::parse: error opening file", fname); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return parse(ifs, std::move(fname), std::move(s)); +} + +template +result, std::vector> +try_parse(const char (&fname)[N], spec s = spec::default_version()) +{ + return try_parse(std::string(fname), std::move(s)); +} + +template +basic_value parse(const char (&fname)[N], spec s = spec::default_version()) +{ + return parse(std::string(fname), std::move(s)); +} + +// ---------------------------------------------------------------------------- +// parse_str + +template +result, std::vector> +try_parse_str(std::string content, spec s = spec::default_version(), + cxx::source_location loc = cxx::source_location::current()) +{ + std::istringstream iss(std::move(content)); + std::string name("internal string" + cxx::to_string(loc)); + return try_parse(iss, std::move(name), std::move(s)); +} + +template +basic_value parse_str(std::string content, spec s = spec::default_version(), + cxx::source_location loc = cxx::source_location::current()) +{ + auto res = try_parse_str(std::move(content), std::move(s), std::move(loc)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ---------------------------------------------------------------------------- +// filesystem + +#if defined(TOML11_HAS_FILESYSTEM) + +template +cxx::enable_if_t::value, + result, std::vector>> +try_parse(const FSPATH& fpath, spec s = spec::default_version()) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + std::vector e; + e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {})); + return err(std::move(e)); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return try_parse(ifs, fpath.string(), std::move(s)); +} + +template +cxx::enable_if_t::value, + basic_value> +parse(const FSPATH& fpath, spec s = spec::default_version()) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + throw file_io_error("toml::parse: error opening file", fpath.string()); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return parse(ifs, fpath.string(), std::move(s)); +} +#endif + +// ----------------------------------------------------------------------------- +// FILE* + +template +result, std::vector> +try_parse(FILE* fp, std::string filename, spec s = spec::default_version()) +{ + const long beg = std::ftell(fp); + if (beg == -1L) + { + return err(std::vector{error_info( + std::string("Failed to access: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const int res_seekend = std::fseek(fp, 0, SEEK_END); + if (res_seekend != 0) + { + return err(std::vector{error_info( + std::string("Failed to seek: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const long end = std::ftell(fp); + if (end == -1L) + { + return err(std::vector{error_info( + std::string("Failed to access: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); + if (res_seekbeg != 0) + { + return err(std::vector{error_info( + std::string("Failed to seek: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); + if(actual != static_cast(fsize)) + { + return err(std::vector{error_info( + std::string("File size changed: \"") + filename + + std::string("\" make sure that FILE* is in binary mode " + "to avoid LF <-> CRLF conversion"), {} + )}); + } + + return detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); +} + +template +basic_value +parse(FILE* fp, std::string filename, spec s = spec::default_version()) +{ + const long beg = std::ftell(fp); + if (beg == -1L) + { + throw file_io_error(errno, "Failed to access", filename); + } + + const int res_seekend = std::fseek(fp, 0, SEEK_END); + if (res_seekend != 0) + { + throw file_io_error(errno, "Failed to seek", filename); + } + + const long end = std::ftell(fp); + if (end == -1L) + { + throw file_io_error(errno, "Failed to access", filename); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); + if (res_seekbeg != 0) + { + throw file_io_error(errno, "Failed to seek", filename); + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); + if(actual != static_cast(fsize)) + { + throw file_io_error(errno, "File size changed; make sure that " + "FILE* is in binary mode to avoid LF <-> CRLF conversion", filename); + } + + auto res = detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +extern template result, std::vector> try_parse(std::vector, std::string, spec); +extern template result, std::vector> try_parse(std::istream&, std::string, spec); +extern template result, std::vector> try_parse(std::string, spec); +extern template result, std::vector> try_parse(FILE*, std::string, spec); +extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); + +extern template basic_value parse(std::vector, std::string, spec); +extern template basic_value parse(std::istream&, std::string, spec); +extern template basic_value parse(std::string, spec); +extern template basic_value parse(FILE*, std::string, spec); +extern template basic_value parse_str(std::string, spec, cxx::source_location); + +extern template result, std::vector> try_parse(std::vector, std::string, spec); +extern template result, std::vector> try_parse(std::istream&, std::string, spec); +extern template result, std::vector> try_parse(std::string, spec); +extern template result, std::vector> try_parse(FILE*, std::string, spec); +extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); + +extern template basic_value parse(std::vector, std::string, spec); +extern template basic_value parse(std::istream&, std::string, spec); +extern template basic_value parse(std::string, spec); +extern template basic_value parse(FILE*, std::string, spec); +extern template basic_value parse_str(std::string, spec, cxx::source_location); + +#if defined(TOML11_HAS_FILESYSTEM) +extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); +#endif // filesystem + +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_PARSER_HPP +#ifndef TOML11_LITERAL_HPP +#define TOML11_LITERAL_HPP + +#ifndef TOML11_LITERAL_FWD_HPP +#define TOML11_LITERAL_FWD_HPP + + +namespace toml +{ + +namespace detail +{ +// implementation +::toml::value literal_internal_impl(location loc); +} // detail + +inline namespace literals +{ +inline namespace toml_literals +{ + +::toml::value operator"" _toml(const char* str, std::size_t len); + +#if defined(TOML11_HAS_CHAR8_T) +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +::toml::value operator"" _toml(const char8_t* str, std::size_t len); +#endif + +} // toml_literals +} // literals +} // toml +#endif // TOML11_LITERAL_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_LITERAL_IMPL_HPP +#define TOML11_LITERAL_IMPL_HPP + + +namespace toml +{ + +namespace detail +{ +// implementation +TOML11_INLINE ::toml::value literal_internal_impl(location loc) +{ + const auto s = ::toml::spec::default_version(); + context ctx(s); + + const auto front = loc; + + // ------------------------------------------------------------------------ + // check if it is a raw value. + + // skip empty lines and comment lines + auto sp = skip_multiline_spacer(loc, ctx); + if(loc.eof()) + { + ::toml::value val; + if(sp.has_value()) + { + for(std::size_t i=0; i(str), + reinterpret_cast(str + len), + c.begin()); + if( ! c.empty() && c.back()) + { + c.push_back('\n'); // to make it easy to parse comment, we add newline + } + + return literal_internal_impl(::toml::detail::location( + std::make_shared(std::move(c)), + "TOML literal encoded in a C++ code")); +} + +#if defined(__cpp_char8_t) +# if __cpp_char8_t >= 201811L +# define TOML11_HAS_CHAR8_T 1 +# endif +#endif + +#if defined(TOML11_HAS_CHAR8_T) +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +TOML11_INLINE ::toml::value +operator"" _toml(const char8_t* str, std::size_t len) +{ + if(len == 0) + { + return ::toml::value{}; + } + + ::toml::detail::location::container_type c(len); + std::copy(reinterpret_cast(str), + reinterpret_cast(str + len), + c.begin()); + if( ! c.empty() && c.back()) + { + c.push_back('\n'); // to make it easy to parse comment, we add newline + } + + return literal_internal_impl(::toml::detail::location( + std::make_shared(std::move(c)), + "TOML literal encoded in a C++ code")); +} +#endif + +} // toml_literals +} // literals +} // toml +#endif // TOML11_LITERAL_IMPL_HPP +#endif + +#endif // TOML11_LITERAL_HPP +#ifndef TOML11_SERIALIZER_HPP +#define TOML11_SERIALIZER_HPP + + +#include +#include +#include + +#include +#include + +namespace toml +{ + +struct serialization_error final : public ::toml::exception +{ + public: + explicit serialization_error(std::string what_arg, source_location loc) + : what_(std::move(what_arg)), loc_(std::move(loc)) + {} + ~serialization_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + source_location const& location() const noexcept {return loc_;} + + private: + std::string what_; + source_location loc_; +}; + +namespace detail +{ +template +class serializer +{ + public: + + using value_type = basic_value; + + using key_type = typename value_type::key_type ; + using comment_type = typename value_type::comment_type ; + using boolean_type = typename value_type::boolean_type ; + using integer_type = typename value_type::integer_type ; + using floating_type = typename value_type::floating_type ; + using string_type = typename value_type::string_type ; + using local_time_type = typename value_type::local_time_type ; + using local_date_type = typename value_type::local_date_type ; + using local_datetime_type = typename value_type::local_datetime_type ; + using offset_datetime_type = typename value_type::offset_datetime_type; + using array_type = typename value_type::array_type ; + using table_type = typename value_type::table_type ; + + using char_type = typename string_type::value_type; + + public: + + explicit serializer(const spec& sp) + : spec_(sp), force_inline_(false), current_indent_(0) + {} + + string_type operator()(const std::vector& ks, const value_type& v) + { + for(const auto& k : ks) + { + this->keys_.push_back(k); + } + return (*this)(v); + } + + string_type operator()(const key_type& k, const value_type& v) + { + this->keys_.push_back(k); + return (*this)(v); + } + + string_type operator()(const value_type& v) + { + switch(v.type()) + { + case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());} + case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());} + case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());} + case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());} + case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());} + case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());} + case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());} + case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());} + case value_t::array : + { + return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location()); + } + case value_t::table : + { + string_type retval; + if(this->keys_.empty()) // it might be the root table. emit comments here. + { + retval += format_comments(v.comments(), v.as_table_fmt().indent_type); + } + if( ! retval.empty()) // we have comment. + { + retval += char_type('\n'); + } + + retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location()); + return retval; + } + case value_t::empty: + { + if(this->spec_.ext_null_value) + { + return string_conv("null"); + } + break; + } + default: + { + break; + } + } + throw serialization_error(format_error( + "[error] toml::serializer: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); + } + + private: + + string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{ + { + if(b) + { + return string_conv("true"); + } + else + { + return string_conv("false"); + } + } // }}} + + string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{ + { + std::ostringstream oss; + this->set_locale(oss); + + const auto insert_spacer = [&fmt](std::string s) -> std::string { + if(fmt.spacer == 0) {return s;} + + std::string sign; + if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-')) + { + sign += s.at(0); + s.erase(s.begin()); + } + + std::string spaced; + std::size_t counter = 0; + for(auto iter = s.rbegin(); iter != s.rend(); ++iter) + { + if(counter != 0 && counter % fmt.spacer == 0) + { + spaced += '_'; + } + spaced += *iter; + counter += 1; + } + if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();} + + s.clear(); + std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s)); + return sign + s; + }; + + std::string retval; + if(fmt.fmt == integer_format::dec) + { + oss << std::setw(static_cast(fmt.width)) << std::dec << i; + retval = insert_spacer(oss.str()); + + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + retval += '_'; + retval += fmt.suffix; + } + } + else + { + if(i < 0) + { + throw serialization_error(format_error("binary, octal, hexadecimal " + "integer does not allow negative value", loc, "here"), loc); + } + switch(fmt.fmt) + { + case integer_format::hex: + { + oss << std::noshowbase + << std::setw(static_cast(fmt.width)) + << std::setfill('0') + << std::hex; + if(fmt.uppercase) + { + oss << std::uppercase; + } + else + { + oss << std::nouppercase; + } + oss << i; + retval = std::string("0x") + insert_spacer(oss.str()); + break; + } + case integer_format::oct: + { + oss << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::oct << i; + retval = std::string("0o") + insert_spacer(oss.str()); + break; + } + case integer_format::bin: + { + integer_type x{i}; + std::string tmp; + std::size_t bits(0); + while(x != 0) + { + if(fmt.spacer != 0) + { + if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} + } + if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; } + x >>= 1; + bits += 1; + } + for(; bits < fmt.width; ++bits) + { + if(fmt.spacer != 0) + { + if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} + } + tmp += '0'; + } + for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter) + { + oss << *iter; + } + retval = std::string("0b") + oss.str(); + break; + } + default: + { + throw serialization_error(format_error( + "none of dec, hex, oct, bin: " + to_string(fmt.fmt), + loc, "here"), loc); + } + } + } + return string_conv(retval); + } // }}} + + string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{ + { + using std::isnan; + using std::isinf; + using std::signbit; + + std::ostringstream oss; + this->set_locale(oss); + + if(isnan(f)) + { + if(signbit(f)) + { + oss << '-'; + } + oss << "nan"; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_'; + oss << fmt.suffix; + } + return string_conv(oss.str()); + } + + if(isinf(f)) + { + if(signbit(f)) + { + oss << '-'; + } + oss << "inf"; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_'; + oss << fmt.suffix; + } + return string_conv(oss.str()); + } + + switch(fmt.fmt) + { + case floating_format::defaultfloat: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << f; + // since defaultfloat may omit point, we need to add it + std::string s = oss.str(); + if (s.find('.') == std::string::npos && + s.find('e') == std::string::npos && + s.find('E') == std::string::npos ) + { + s += ".0"; + } + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + s += '_'; + s += fmt.suffix; + } + return string_conv(s); + } + case floating_format::fixed: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << std::fixed << f; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + case floating_format::scientific: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << std::scientific << f; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + case floating_format::hex: + { + if(this->spec_.ext_hex_float) + { + oss << std::hexfloat << f; + // suffix is only for decimal numbers. + return string_conv(oss.str()); + } + else // no hex allowed. output with max precision. + { + oss << std::setprecision(std::numeric_limits::max_digits10) + << std::scientific << f; + // suffix is only for decimal numbers. + return string_conv(oss.str()); + } + } + default: + { + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + } + } // }}} + + string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{ + { + string_type retval; + switch(fmt.fmt) + { + case string_format::basic: + { + retval += char_type('"'); + retval += this->escape_basic_string(s); + retval += char_type('"'); + return retval; + } + case string_format::literal: + { + if(std::find(s.begin(), s.end(), char_type('\n')) != s.end()) + { + throw serialization_error(format_error("toml::serializer: " + "(non-multiline) literal string cannot have a newline", + loc, "here"), loc); + } + retval += char_type('\''); + retval += s; + retval += char_type('\''); + return retval; + } + case string_format::multiline_basic: + { + retval += string_conv("\"\"\""); + if(fmt.start_with_newline) + { + retval += char_type('\n'); + } + + retval += this->escape_ml_basic_string(s); + + retval += string_conv("\"\"\""); + return retval; + } + case string_format::multiline_literal: + { + retval += string_conv("'''"); + if(fmt.start_with_newline) + { + retval += char_type('\n'); + } + retval += s; + retval += string_conv("'''"); + return retval; + } + default: + { + throw serialization_error(format_error( + "[error] toml::serializer::operator()(string): " + "invalid string_format value", loc, "here"), loc); + } + } + } // }}} + + string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{ + { + std::ostringstream oss; + oss << d; + return string_conv(oss.str()); + } // }}} + + string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{ + { + return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision); + } // }}} + + string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{ + { + std::ostringstream oss; + oss << dt.date; + switch(fmt.delimiter) + { + case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } + case datetime_delimiter_kind::lower_t: { oss << 't'; break; } + case datetime_delimiter_kind::space: { oss << ' '; break; } + default: { oss << 'T'; break; } + } + return string_conv(oss.str()) + + this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision); + } // }}} + + string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{ + { + std::ostringstream oss; + oss << odt.date; + switch(fmt.delimiter) + { + case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } + case datetime_delimiter_kind::lower_t: { oss << 't'; break; } + case datetime_delimiter_kind::space: { oss << ' '; break; } + default: { oss << 'T'; break; } + } + oss << string_conv(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision)); + oss << odt.offset; + return string_conv(oss.str()); + } // }}} + + string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ + { + array_format f = fmt.fmt; + if(fmt.fmt == array_format::default_format) + { + // [[in.this.form]], you cannot add a comment to the array itself + // (but you can add a comment to each table). + // To keep comments, we need to avoid multiline array-of-tables + // if array itself has a comment. + if( ! this->keys_.empty() && + ! a.empty() && + com.empty() && + std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();})) + { + f = array_format::array_of_tables; + } + else + { + f = array_format::oneline; + + // check if it becomes long + std::size_t approx_len = 0; + for(const auto& e : a) + { + // have a comment. cannot be inlined + if( ! e.comments().empty()) + { + f = array_format::multiline; + break; + } + // possibly long types ... + if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime()) + { + f = array_format::multiline; + break; + } + else if(e.is_boolean()) + { + approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size(); + } + else if(e.is_integer()) + { + approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size(); + } + else if(e.is_floating()) + { + approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size(); + } + else if(e.is_string()) + { + if(e.as_string_fmt().fmt == string_format::multiline_basic || + e.as_string_fmt().fmt == string_format::multiline_literal) + { + f = array_format::multiline; + break; + } + approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size(); + } + else if(e.is_local_date()) + { + approx_len += 10; // 1234-56-78 + } + else if(e.is_local_time()) + { + approx_len += 15; // 12:34:56.789012 + } + + if(approx_len > 60) // key, ` = `, `[...]` < 80 + { + f = array_format::multiline; + break; + } + approx_len += 2; // `, ` + } + } + } + if(this->force_inline_ && f == array_format::array_of_tables) + { + f = array_format::multiline; + } + if(a.empty() && f == array_format::array_of_tables) + { + f = array_format::oneline; + } + + // -------------------------------------------------------------------- + + if(f == array_format::array_of_tables) + { + if(this->keys_.empty()) + { + throw serialization_error("array of table must have its key. " + "use format(key, v)", loc); + } + string_type retval; + for(const auto& e : a) + { + assert(e.is_table()); + + this->current_indent_ += e.as_table_fmt().name_indent; + retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type); + retval += this->format_indent(e.as_table_fmt().indent_type); + this->current_indent_ -= e.as_table_fmt().name_indent; + + retval += string_conv("[["); + retval += this->format_keys(this->keys_).value(); + retval += string_conv("]]\n"); + + retval += this->format_ml_table(e.as_table(), e.as_table_fmt()); + } + return retval; + } + else if(f == array_format::oneline) + { + // ignore comments. we cannot emit comments + string_type retval; + retval += char_type('['); + for(const auto& e : a) + { + this->force_inline_ = true; + retval += (*this)(e); + retval += string_conv(", "); + } + if( ! a.empty()) + { + retval.pop_back(); // ` ` + retval.pop_back(); // `,` + } + retval += char_type(']'); + this->force_inline_ = false; + return retval; + } + else + { + assert(f == array_format::multiline); + + string_type retval; + retval += string_conv("[\n"); + + for(const auto& e : a) + { + this->current_indent_ += fmt.body_indent; + retval += this->format_comments(e.comments(), fmt.indent_type); + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.body_indent; + + this->force_inline_ = true; + retval += (*this)(e); + retval += string_conv(",\n"); + } + this->force_inline_ = false; + + this->current_indent_ += fmt.closing_indent; + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.closing_indent; + + retval += char_type(']'); + return retval; + } + } // }}} + + string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ + { + if(this->force_inline_) + { + if(fmt.fmt == table_format::multiline_oneline) + { + return this->format_ml_inline_table(t, fmt); + } + else + { + return this->format_inline_table(t, fmt); + } + } + else + { + if(fmt.fmt == table_format::multiline) + { + string_type retval; + // comment is emitted inside format_ml_table + if(auto k = this->format_keys(this->keys_)) + { + this->current_indent_ += fmt.name_indent; + retval += this->format_comments(com, fmt.indent_type); + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.name_indent; + retval += char_type('['); + retval += k.value(); + retval += string_conv("]\n"); + } + // otherwise, its the root. + + retval += this->format_ml_table(t, fmt); + return retval; + } + else if(fmt.fmt == table_format::oneline) + { + return this->format_inline_table(t, fmt); + } + else if(fmt.fmt == table_format::multiline_oneline) + { + return this->format_ml_inline_table(t, fmt); + } + else if(fmt.fmt == table_format::dotted) + { + std::vector keys; + if(this->keys_.empty()) + { + throw serialization_error(format_error("toml::serializer: " + "dotted table must have its key. use format(key, v)", + loc, "here"), loc); + } + keys.push_back(this->keys_.back()); + + const auto retval = this->format_dotted_table(t, fmt, loc, keys); + keys.pop_back(); + return retval; + } + else + { + assert(fmt.fmt == table_format::implicit); + + string_type retval; + for(const auto& kv : t) + { + const auto& k = kv.first; + const auto& v = kv.second; + + if( ! v.is_table() && ! v.is_array_of_tables()) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-table value.", + v.location(), "here"), v.location()); + } + if(v.is_table()) + { + if(v.as_table_fmt().fmt != table_format::multiline && + v.as_table_fmt().fmt != table_format::implicit) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-multiline table", + v.location(), "here"), v.location()); + } + } + else + { + assert(v.is_array()); + for(const auto& e : v.as_array()) + { + if(e.as_table_fmt().fmt != table_format::multiline && + v.as_table_fmt().fmt != table_format::implicit) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-multiline table", + e.location(), "here"), e.location()); + } + } + } + + keys_.push_back(k); + retval += (*this)(v); + keys_.pop_back(); + } + return retval; + } + } + } // }}} + + private: + + string_type escape_basic_string(const string_type& s) const // {{{ + { + string_type retval; + for(const char_type c : s) + { + switch(c) + { + case char_type('\\'): {retval += string_conv("\\\\"); break;} + case char_type('\"'): {retval += string_conv("\\\""); break;} + case char_type('\b'): {retval += string_conv("\\b" ); break;} + case char_type('\t'): {retval += string_conv("\\t" ); break;} + case char_type('\f'): {retval += string_conv("\\f" ); break;} + case char_type('\n'): {retval += string_conv("\\n" ); break;} + case char_type('\r'): {retval += string_conv("\\r" ); break;} + default : + { + if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) + { + retval += string_conv("\\e"); + } + else if((char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + retval += string_conv("\\x"); + } + else + { + retval += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + retval += static_cast('0' + c1); + if(c2 < 10) + { + retval += static_cast('0' + c2); + } + else // 10 <= c2 + { + retval += static_cast('A' + (c2 - 10)); + } + } + else + { + retval += c; + } + } + } + } + return retval; + } // }}} + + string_type escape_ml_basic_string(const string_type& s) // {{{ + { + string_type retval; + for(const char_type c : s) + { + switch(c) + { + case char_type('\\'): {retval += string_conv("\\\\"); break;} + case char_type('\b'): {retval += string_conv("\\b" ); break;} + case char_type('\t'): {retval += string_conv("\\t" ); break;} + case char_type('\f'): {retval += string_conv("\\f" ); break;} + case char_type('\n'): {retval += string_conv("\n" ); break;} + case char_type('\r'): {retval += string_conv("\\r" ); break;} + default : + { + if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) + { + retval += string_conv("\\e"); + } + else if((char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + retval += string_conv("\\x"); + } + else + { + retval += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + retval += static_cast('0' + c1); + if(c2 < 10) + { + retval += static_cast('0' + c2); + } + else // 10 <= c2 + { + retval += static_cast('A' + (c2 - 10)); + } + } + else + { + retval += c; + } + } + } + } + // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. + // 3 consecutive `"`s are considered as a closing delimiter. + // We need to check if there are 3 or more consecutive `"`s and insert + // backslash to break them down into several short `"`s like the `str6` + // in the following example. + // ```toml + // str4 = """Here are two quotation marks: "". Simple enough.""" + // # str5 = """Here are three quotation marks: """.""" # INVALID + // str5 = """Here are three quotation marks: ""\".""" + // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" + // ``` + auto found_3_quotes = retval.find(string_conv("\"\"\"")); + while(found_3_quotes != string_type::npos) + { + retval.replace(found_3_quotes, 3, string_conv("\"\"\\\"")); + found_3_quotes = retval.find(string_conv("\"\"\"")); + } + return retval; + } // }}} + + string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{ + { + std::ostringstream oss; + oss << std::setfill('0') << std::setw(2) << static_cast(t.hour); + oss << ':'; + oss << std::setfill('0') << std::setw(2) << static_cast(t.minute); + if(has_seconds) + { + oss << ':'; + oss << std::setfill('0') << std::setw(2) << static_cast(t.second); + if(subsec_prec != 0) + { + std::ostringstream subsec; + subsec << std::setfill('0') << std::setw(3) << static_cast(t.millisecond); + subsec << std::setfill('0') << std::setw(3) << static_cast(t.microsecond); + subsec << std::setfill('0') << std::setw(3) << static_cast(t.nanosecond); + std::string subsec_str = subsec.str(); + oss << '.' << subsec_str.substr(0, subsec_prec); + } + } + return string_conv(oss.str()); + } // }}} + + string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{ + { + const auto format_later = [](const value_type& v) -> bool { + + const bool is_ml_table = v.is_table() && + v.as_table_fmt().fmt != table_format::oneline && + v.as_table_fmt().fmt != table_format::multiline_oneline && + v.as_table_fmt().fmt != table_format::dotted ; + + const bool is_ml_array_table = v.is_array_of_tables() && + v.as_array_fmt().fmt != array_format::oneline && + v.as_array_fmt().fmt != array_format::multiline; + + return is_ml_table || is_ml_array_table; + }; + + string_type retval; + this->current_indent_ += fmt.body_indent; + for(const auto& kv : t) + { + const auto& key = kv.first; + const auto& val = kv.second; + if(format_later(val)) + { + continue; + } + this->keys_.push_back(key); + + retval += format_comments(val.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted) + { + retval += (*this)(val); + } + else + { + retval += format_key(key); + retval += string_conv(" = "); + retval += (*this)(val); + retval += char_type('\n'); + } + this->keys_.pop_back(); + } + this->current_indent_ -= fmt.body_indent; + + if( ! retval.empty()) + { + retval += char_type('\n'); // for readability, add empty line between tables + } + for(const auto& kv : t) + { + if( ! format_later(kv.second)) + { + continue; + } + // must be a [multiline.table] or [[multiline.array.of.tables]]. + // comments will be generated inside it. + this->keys_.push_back(kv.first); + retval += (*this)(kv.second); + this->keys_.pop_back(); + } + return retval; + } // }}} + + string_type format_inline_table(const table_type& t, const table_format_info&) // {{{ + { + // comments are ignored because we cannot write without newline + string_type retval; + retval += char_type('{'); + for(const auto& kv : t) + { + this->force_inline_ = true; + retval += this->format_key(kv.first); + retval += string_conv(" = "); + retval += (*this)(kv.second); + retval += string_conv(", "); + } + if( ! t.empty()) + { + retval.pop_back(); // ' ' + retval.pop_back(); // ',' + } + retval += char_type('}'); + this->force_inline_ = false; + return retval; + } // }}} + + string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{ + { + string_type retval; + retval += string_conv("{\n"); + this->current_indent_ += fmt.body_indent; + for(const auto& kv : t) + { + this->force_inline_ = true; + retval += format_comments(kv.second.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + retval += kv.first; + retval += string_conv(" = "); + + this->force_inline_ = true; + retval += (*this)(kv.second); + + retval += string_conv(",\n"); + } + if( ! t.empty()) + { + retval.pop_back(); // '\n' + retval.pop_back(); // ',' + } + this->current_indent_ -= fmt.body_indent; + this->force_inline_ = false; + + this->current_indent_ += fmt.closing_indent; + retval += format_indent(fmt.indent_type); + this->current_indent_ -= fmt.closing_indent; + + retval += char_type('}'); + return retval; + } // }}} + + string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{ + const source_location&, std::vector& keys) + { + // lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }` + // and `a` and `b` are `dotted`. + // + // - in case if `c` is `oneline`: + // ```toml + // a.b.c = {d = "foo", e = "bar"} + // ``` + // + // - in case if and `c` is `dotted`: + // ```toml + // a.b.c.d = "foo" + // a.b.c.e = "bar" + // ``` + + string_type retval; + + for(const auto& kv : t) + { + const auto& key = kv.first; + const auto& val = kv.second; + + keys.push_back(key); + + // format recursive dotted table? + if (val.is_table() && + val.as_table_fmt().fmt != table_format::oneline && + val.as_table_fmt().fmt != table_format::multiline_oneline) + { + retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys); + } + else // non-table or inline tables. format normally + { + retval += format_comments(val.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + retval += format_keys(keys).value(); + retval += string_conv(" = "); + this->force_inline_ = true; // sub-table must be inlined + retval += (*this)(val); + retval += char_type('\n'); + this->force_inline_ = false; + } + keys.pop_back(); + } + return retval; + } // }}} + + string_type format_key(const key_type& key) // {{{ + { + if(key.empty()) + { + return string_conv("\"\""); + } + + // check the key can be a bare (unquoted) key + auto loc = detail::make_temporary_location(string_conv(key)); + auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc); + if(reg.is_ok() && loc.eof()) + { + return key; + } + + //if it includes special characters, then format it in a "quoted" key. + string_type formatted = string_conv("\""); + for(const char_type c : key) + { + switch(c) + { + case char_type('\\'): {formatted += string_conv("\\\\"); break;} + case char_type('\"'): {formatted += string_conv("\\\""); break;} + case char_type('\b'): {formatted += string_conv("\\b" ); break;} + case char_type('\t'): {formatted += string_conv("\\t" ); break;} + case char_type('\f'): {formatted += string_conv("\\f" ); break;} + case char_type('\n'): {formatted += string_conv("\\n" ); break;} + case char_type('\r'): {formatted += string_conv("\\r" ); break;} + default : + { + // ASCII ctrl char + if( (char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + formatted += string_conv("\\x"); + } + else + { + formatted += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + formatted += static_cast('0' + c1); + if(c2 < 10) + { + formatted += static_cast('0' + c2); + } + else // 10 <= c2 + { + formatted += static_cast('A' + (c2 - 10)); + } + } + else + { + formatted += c; + } + break; + } + } + } + formatted += string_conv("\""); + return formatted; + } // }}} + cxx::optional format_keys(const std::vector& keys) // {{{ + { + if(keys.empty()) + { + return cxx::make_nullopt(); + } + + string_type formatted; + for(const auto& ky : keys) + { + formatted += format_key(ky); + formatted += char_type('.'); + } + formatted.pop_back(); // remove the last dot '.' + return formatted; + } // }}} + + string_type format_comments(const discard_comments&, const indent_char) const // {{{ + { + return string_conv(""); + } // }}} + string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{ + { + string_type retval; + for(const auto& c : comments) + { + if(c.empty()) {continue;} + retval += format_indent(indent_type); + if(c.front() != '#') {retval += char_type('#');} + retval += string_conv(c); + if(c.back() != '\n') {retval += char_type('\n');} + } + return retval; + } // }}} + + string_type format_indent(const indent_char indent_type) const // {{{ + { + const auto indent = static_cast((std::max)(0, this->current_indent_)); + if(indent_type == indent_char::space) + { + return string_conv(make_string(indent, ' ')); + } + else if(indent_type == indent_char::tab) + { + return string_conv(make_string(indent, '\t')); + } + else + { + return string_type{}; + } + } // }}} + + std::locale set_locale(std::ostream& os) const + { + return os.imbue(std::locale::classic()); + } + + private: + + spec spec_; + bool force_inline_; // table inside an array without fmt specification + std::int32_t current_indent_; + std::vector keys_; +}; +} // detail + +template +typename basic_value::string_type +format(const basic_value& v, const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(v); +} +template +typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, + const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(k, v); +} +template +typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, + const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(ks, v); +} + +template +std::ostream& operator<<(std::ostream& os, const basic_value& v) +{ + os << format(v); + return os; +} + +} // toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +extern template typename basic_value::string_type +format(const basic_value&, const spec); + +extern template typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, const spec); + +extern template typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, const spec s); + +extern template typename basic_value::string_type +format(const basic_value&, const spec); + +extern template typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, const spec); + +extern template typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, const spec s); + +namespace detail +{ +extern template class serializer<::toml::type_config>; +extern template class serializer<::toml::ordered_type_config>; +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + + +#endif // TOML11_SERIALIZER_HPP +#ifndef TOML11_TOML_HPP +#define TOML11_TOML_HPP + +// The MIT License (MIT) +// +// Copyright (c) 2017-now Toru Niina +// +// 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. + +// IWYU pragma: begin_exports +// IWYU pragma: end_exports + +#endif// TOML11_TOML_HPP diff --git a/tile/base/align.h b/tile/base/align.h index 05d498c..7adbbd8 100644 --- a/tile/base/align.h +++ b/tile/base/align.h @@ -15,19 +15,19 @@ constexpr std::size_t max_align_v = alignof(max_align_t); // @sa: https://github.com/facebook/folly/blob/master/folly/lang/Align.h // // Update at 20201124: Well, AMD's Zen 3 does the same. -constexpr std::size_t hardware_destructive_interference_size = 128; +constexpr std::size_t hardware_destructive_interference_size = 128; constexpr std::size_t hardware_constructive_interference_size = 64; #elif defined(__powerpc64__) // These values are read from // `/sys/devices/system/cpu/cpu0/cache/index*/coherency_line_size` -constexpr std::size_t hardware_destructive_interference_size = 128; +constexpr std::size_t hardware_destructive_interference_size = 128; constexpr std::size_t hardware_constructive_interference_size = 128; #elif defined(__ARM_ARCH_5T__) -constexpr std::size_t hardware_destructive_interference_size = 32; +constexpr std::size_t hardware_destructive_interference_size = 32; constexpr std::size_t hardware_constructive_interference_size = 32; #elif defined(__arm__) @@ -36,7 +36,7 @@ constexpr std::size_t hardware_constructive_interference_size = 32; // are even implementations with cache line sizes configurable at boot // time. -constexpr std::size_t hardware_destructive_interference_size = 64; +constexpr std::size_t hardware_destructive_interference_size = 64; constexpr std::size_t hardware_constructive_interference_size = 64; #elif defined(__aarch64__) @@ -47,13 +47,13 @@ constexpr std::size_t hardware_constructive_interference_size = 64; // Let's ignore those CPUs for now. // // @sa: https://www.mono-project.com/news/2016/09/12/arm64-icache/ -constexpr std::size_t hardware_destructive_interference_size = 64; +constexpr std::size_t hardware_destructive_interference_size = 64; constexpr std::size_t hardware_constructive_interference_size = 64; #else constexpr std::size_t hardware_constructive_interference_size = max_align_v; -constexpr std::size_t hardware_destructive_interference_size = max_align_v; +constexpr std::size_t hardware_destructive_interference_size = max_align_v; // #error Unsupported architecture. // #pragma message("Unsupported architecture, use max_align_v") @@ -62,6 +62,6 @@ static_assert(hardware_constructive_interference_size >= max_align_v, "hardware constructive interference size is too small"); static_assert(hardware_destructive_interference_size >= max_align_v, "hardware destructive interference size is too small"); -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_ALIGN_H +#endif// _TILE_BASE_ALIGN_H diff --git a/tile/base/buffer.cc b/tile/base/buffer.cc index 3eb688d..a7fbd5e 100644 --- a/tile/base/buffer.cc +++ b/tile/base/buffer.cc @@ -9,205 +9,214 @@ namespace tile { namespace { -template class OwningBufferBlock : public PolymorphicBufferBlock { +template +class OwningBufferBlock : public PolymorphicBufferBlock { public: - explicit OwningBufferBlock(T storage) : storage_(std::move(storage)) {} - const char *data() const noexcept override { - return reinterpret_cast(storage_.data()); - } + explicit OwningBufferBlock(T storage) : storage_(std::move(storage)) {} - std::size_t size() const noexcept override { return storage_.size(); } + const char *data() const noexcept override { return reinterpret_cast(storage_.data()); } + + std::size_t size() const noexcept override { return storage_.size(); } private: - T storage_; + T storage_; }; -} // namespace +}// namespace namespace detail { -void FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer, - std::size_t size) { - TILE_CHECK_GT(nb.ByteSize(), size, "No enough data"); - std::size_t copied = 0; - for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) { - auto len = std::min(size - copied, iter->size()); - memcpy(reinterpret_cast(buffer) + copied, iter->data(), len); - copied += len; - } +void +FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t size) +{ + TILE_CHECK_GT(nb.ByteSize(), size, "No enough data"); + std::size_t copied = 0; + for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) { + auto len = std::min(size - copied, iter->size()); + memcpy(reinterpret_cast(buffer) + copied, iter->data(), len); + copied += len; + } } -} // namespace detail +}// namespace detail -NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other) - : byte_size_(other.byte_size_) { - for (auto &&e : other.buffers_) { +NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other) : byte_size_(other.byte_size_) +{ + for (auto &&e : other.buffers_) { - auto b = object_pool::Get().Leak(); - *b = e; - buffers_.push_back(std::move(b)); - } + auto b = object_pool::Get().Leak(); + *b = e; + buffers_.push_back(std::move(b)); + } } NoncontiguousBuffer & -NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) { - if (TILE_UNLIKELY(&other == this)) { +NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) +{ + if (TILE_UNLIKELY(&other == this)) { return *this; } + + Clear(); + byte_size_ = other.byte_size_; + for (auto &&e : other.buffers_) { + + auto b = object_pool::Get().Leak(); + *b = e; + buffers_.push_back(std::move(b)); + } return *this; - } - - Clear(); - byte_size_ = other.byte_size_; - for (auto &&e : other.buffers_) { - - auto b = object_pool::Get().Leak(); - *b = e; - buffers_.push_back(std::move(b)); - } - return *this; } -void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept { - TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size"); - byte_size_ -= bytes; +void +NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept +{ + TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size"); + byte_size_ -= bytes; - while (bytes) { - auto &&first = buffers_.front(); - auto min_size = std::min(bytes, first.size()); - if (min_size == first.size()) { - object_pool::Put(buffers_.pop_front()); - } else { - TILE_DCHECK_LT(min_size, first.size()); - first.Skip(min_size); + while (bytes) { + auto &&first = buffers_.front(); + auto min_size = std::min(bytes, first.size()); + if (min_size == first.size()) { + object_pool::Put(buffers_.pop_front()); + } else { + TILE_DCHECK_LT(min_size, first.size()); + first.Skip(min_size); + } + bytes -= min_size; } - bytes -= min_size; - } } -void NoncontiguousBuffer::ClearSlow() noexcept { - byte_size_ = 0; - while (!buffers_.empty()) { - object_pool::Put(buffers_.pop_front()); - } +void +NoncontiguousBuffer::ClearSlow() noexcept +{ + byte_size_ = 0; + while (!buffers_.empty()) { object_pool::Put(buffers_.pop_front()); } } -void NoncontiguousBufferBuilder::InitializeNextBlock() { - if (current_) { - TILE_CHECK(SizeAvailable(), - "You should flush current block by `FlushCurrentBlock()`"); - return; - } - - current_ = MakeNativeBufferBlock(); - used_ = 0; -} - -void NoncontiguousBufferBuilder::FlushCurrentBlock() { - if (!used_) { - // current block is empty - return; - } - - nb_.Append(PolymorphicBuffer(std::move(current_), 0, used_)); - used_ = 0; - current_ = nullptr; -} - -void NoncontiguousBufferBuilder::AppendSlow(const void *ptr, - std::size_t length) { - while (length) { - auto copying = std::min(length, SizeAvailable()); - memcpy(data(), ptr, copying); - MarkWritten(copying); - ptr = static_cast(ptr) + copying; - length -= copying; - } -} - -void NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer) { - for (auto &&e : buffer) { - Append(e.data(), e.size()); - } -} - -NoncontiguousBuffer CreateBufferSlow(Slice s) { - NoncontiguousBufferBuilder nbb; - nbb.Append(s); - return nbb.DestructiveGet(); -} -NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size) { - NoncontiguousBufferBuilder nbb; - nbb.Append(ptr, size); - return nbb.DestructiveGet(); -} - -std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes) { - max_bytes = std::min(max_bytes, nb.ByteSize()); - std::string rc; - std::size_t left = max_bytes; - rc.reserve(max_bytes); - for (auto iter = nb.begin(); iter != nb.end() && left; ++iter) { - auto len = std::min(left, iter->size()); - rc.append(iter->data(), len); - left -= len; - } - return rc; -} - -std::string FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim, - std::size_t max_bytes) { - if (nb.Empty()) { - return {}; - } - - { - Slice current(nb.FirstContiguous().data(), nb.FirstContiguous().size()); - auto pos = current.find(delim); - if (pos != Slice::npos) { - auto expected_bytes = std::min(pos + delim.size(), max_bytes); - return std::string(nb.FirstContiguous().data(), - nb.FirstContiguous().data() + expected_bytes); +void +NoncontiguousBufferBuilder::InitializeNextBlock() +{ + if (current_) { + TILE_CHECK(SizeAvailable(), "You should flush current block by `FlushCurrentBlock()`"); + return; } - } - std::string rc; - for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes; - ++iter) { - auto old_len = rc.size(); - rc.append(iter->data(), iter->size()); - // find delim + current_ = MakeNativeBufferBlock(); + used_ = 0; +} + +void +NoncontiguousBufferBuilder::FlushCurrentBlock() +{ + if (!used_) { + // current block is empty + return; + } + + nb_.Append(PolymorphicBuffer(std::move(current_), 0, used_)); + used_ = 0; + current_ = nullptr; +} + +void +NoncontiguousBufferBuilder::AppendSlow(const void *ptr, std::size_t length) +{ + while (length) { + auto copying = std::min(length, SizeAvailable()); + memcpy(data(), ptr, copying); + MarkWritten(copying); + ptr = static_cast(ptr) + copying; + length -= copying; + } +} + +void +NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer) +{ + for (auto &&e : buffer) { Append(e.data(), e.size()); } +} + +NoncontiguousBuffer +CreateBufferSlow(Slice s) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(s); + return nbb.DestructiveGet(); +} + +NoncontiguousBuffer +CreateBufferSlow(const void *ptr, std::size_t size) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(ptr, size); + return nbb.DestructiveGet(); +} + +std::string +FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes) +{ + max_bytes = std::min(max_bytes, nb.ByteSize()); + std::string rc; + std::size_t left = max_bytes; + rc.reserve(max_bytes); + for (auto iter = nb.begin(); iter != nb.end() && left; ++iter) { + auto len = std::min(left, iter->size()); + rc.append(iter->data(), len); + left -= len; + } + return rc; +} + +std::string +FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim, std::size_t max_bytes) +{ + if (nb.Empty()) { return {}; } + { - // Avoiding the use of find_last_of - auto pos = rc.find(delim, old_len - std::min(old_len, delim.size())); - if (pos != Slice::npos) { - rc.erase(rc.begin() + pos + delim.size(), rc.end()); - break; - } + Slice current(nb.FirstContiguous().data(), nb.FirstContiguous().size()); + auto pos = current.find(delim); + if (pos != Slice::npos) { + auto expected_bytes = std::min(pos + delim.size(), max_bytes); + return std::string(nb.FirstContiguous().data(), nb.FirstContiguous().data() + expected_bytes); + } } - } - if (rc.size() > max_bytes) { - rc.erase(rc.begin() + max_bytes, rc.end()); - } - return rc; + + std::string rc; + for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes; ++iter) { + auto old_len = rc.size(); + rc.append(iter->data(), iter->size()); + // find delim + { + // Avoiding the use of find_last_of + auto pos = rc.find(delim, old_len - std::min(old_len, delim.size())); + if (pos != Slice::npos) { + rc.erase(rc.begin() + pos + delim.size(), rc.end()); + break; + } + } + } + if (rc.size() > max_bytes) { rc.erase(rc.begin() + max_bytes, rc.end()); } + return rc; } -PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size) { - return MakeReferencingBuffer(ptr, size, [] {}); +PolymorphicBuffer +MakeReferencingBuffer(const void *ptr, std::size_t size) +{ + return MakeReferencingBuffer(ptr, size, [] {}); } -PolymorphicBuffer MakeForeignBuffer(std::string buffer) { - auto size = buffer.size(); - return PolymorphicBuffer( - MakeRefCounted>(std::move(buffer)), 0, - size); +PolymorphicBuffer +MakeForeignBuffer(std::string buffer) +{ + auto size = buffer.size(); + return PolymorphicBuffer(MakeRefCounted>(std::move(buffer)), 0, size); } -template -PolymorphicBuffer MakeForeignBuffer(std::vector buffer) { - auto size = buffer.size() * sizeof(T); - return PolymorphicBuffer( - MakeRefCounted>>(std::move(buffer)), 0, - size); +template +PolymorphicBuffer +MakeForeignBuffer(std::vector buffer) +{ + auto size = buffer.size() * sizeof(T); + return PolymorphicBuffer(MakeRefCounted>>(std::move(buffer)), 0, size); } -#define TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(type) \ - template PolymorphicBuffer MakeForeignBuffer(std::vector buffer) +#define TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(type) template PolymorphicBuffer MakeForeignBuffer(std::vector buffer) TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(char); TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(signed char); @@ -224,51 +233,50 @@ TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(float); TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(double); TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(long double); -bool StartsWith(NoncontiguousBuffer buffer, Slice prefix) { - if (buffer.ByteSize() < prefix.size()) { - return false; - } +bool +StartsWith(NoncontiguousBuffer buffer, Slice prefix) +{ + if (buffer.ByteSize() < prefix.size()) { return false; } - while (!buffer.Empty() && prefix.empty()) { - auto first = buffer.FirstContiguous(); - auto min_len = std::min(first.size(), prefix.size()); - if (memcmp(first.data(), prefix.data(), min_len) != 0) { - return false; + while (!buffer.Empty() && prefix.empty()) { + auto first = buffer.FirstContiguous(); + auto min_len = std::min(first.size(), prefix.size()); + if (memcmp(first.data(), prefix.data(), min_len) != 0) { return false; } + + buffer.Skip(min_len); + prefix.RemovePrefix(min_len); } - - buffer.Skip(min_len); - prefix.RemovePrefix(min_len); - } - return prefix.empty(); + return prefix.empty(); } -bool EndsWith(NoncontiguousBuffer buffer, Slice suffix) { - TILE_NOT_IMPLEMENTED(""); - return false; -} - -bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) { - if (buffer.ByteSize() < prefix.size()) { +bool +EndsWith(NoncontiguousBuffer buffer, Slice suffix) +{ + TILE_NOT_IMPLEMENTED(""); return false; - } +} - while (!buffer.Empty() && prefix.empty()) { - auto first = buffer.FirstContiguous(); - auto min_len = std::min(first.size(), prefix.size()); +bool +StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) +{ + if (buffer.ByteSize() < prefix.size()) { return false; } - if (!EqualsIgnoreCase(first.substr(0, min_len), - prefix.substr(0, min_len))) { - return false; + while (!buffer.Empty() && prefix.empty()) { + auto first = buffer.FirstContiguous(); + auto min_len = std::min(first.size(), prefix.size()); + + if (!EqualsIgnoreCase(first.substr(0, min_len), prefix.substr(0, min_len))) { return false; } + + buffer.Skip(min_len); + prefix.RemovePrefix(min_len); } - - buffer.Skip(min_len); - prefix.RemovePrefix(min_len); - } - return prefix.empty(); + return prefix.empty(); } -bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix) { - TILE_NOT_IMPLEMENTED(""); +bool +EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix) +{ + TILE_NOT_IMPLEMENTED(""); } -} // namespace tile +}// namespace tile diff --git a/tile/base/buffer.h b/tile/base/buffer.h index c6a3022..3bf39d3 100644 --- a/tile/base/buffer.h +++ b/tile/base/buffer.h @@ -19,355 +19,376 @@ namespace tile { namespace detail { -template -constexpr auto data(const T &c) - -> enable_if_t::value, - const char *> { - return c.data(); +template +constexpr auto +data(const T &c) -> enable_if_t::value, const char *> +{ + return c.data(); } -template -constexpr auto data(const char (&c)[N]) -> decltype(c) { - return c; +template +constexpr auto +data(const char (&c)[N]) -> decltype(c) +{ + return c; } -template constexpr std::size_t size(const T &c) { - return c.size(); +template +constexpr std::size_t +size(const T &c) +{ + return c.size(); } -template struct size_impl { - static constexpr std::size_t size(const char (&c)[N]) { - return N - (c[N - 1] == '\0'); - } -}; -template <> struct size_impl<0> { - static constexpr std::size_t size(const char (&c)[0]) { return 0; } +template +struct size_impl { + static constexpr std::size_t size(const char (&c)[N]) { return N - (c[N - 1] == '\0'); } }; -template constexpr std::size_t size(const char (&c)[N]) { - return size_impl::size(c); +template<> +struct size_impl<0> { + static constexpr std::size_t size(const char (&c)[0]) { return 0; } +}; + +template +constexpr std::size_t +size(const char (&c)[N]) +{ + return size_impl::size(c); } -template constexpr std::size_t total_size(const A1 &a1) { - return size(a1); +template +constexpr std::size_t +total_size(const A1 &a1) +{ + return size(a1); } -template -constexpr std::size_t total_size(const A1 &a1, const Args &...args) { - return size(a1) + total_size(args...); +template +constexpr std::size_t +total_size(const A1 &a1, const Args &...args) +{ + return size(a1) + total_size(args...); } -} // namespace detail +}// namespace detail class NoncontiguousBuffer { - // - using LinkedBuffers = - internal::SinglyLinkedList; + // + using LinkedBuffers = internal::SinglyLinkedList; public: - using iterator = LinkedBuffers::iterator; - using const_iterator = LinkedBuffers::const_iterator; - using buffer_t = PolymorphicBuffer; + using iterator = LinkedBuffers::iterator; + using const_iterator = LinkedBuffers::const_iterator; + using buffer_t = PolymorphicBuffer; - constexpr NoncontiguousBuffer() = default; + constexpr NoncontiguousBuffer() = default; - // Copyable & moveable - NoncontiguousBuffer(const NoncontiguousBuffer &other); - NoncontiguousBuffer &operator=(const NoncontiguousBuffer &other); - NoncontiguousBuffer(NoncontiguousBuffer &&other) noexcept - : byte_size_(internal::Exchange(other.byte_size_, 0)), - buffers_(std::move(other.buffers_)) {} - NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept { - if (TILE_UNLIKELY(&other == this)) { - return *this; + // Copyable & moveable + NoncontiguousBuffer(const NoncontiguousBuffer &other); + NoncontiguousBuffer &operator=(const NoncontiguousBuffer &other); + + NoncontiguousBuffer(NoncontiguousBuffer &&other) noexcept + : byte_size_(internal::Exchange(other.byte_size_, 0)), + buffers_(std::move(other.buffers_)) + {} + + NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept + { + if (TILE_UNLIKELY(&other == this)) { return *this; } + + Clear(); + std::swap(byte_size_, other.byte_size_); + buffers_ = std::move(other.buffers_); + return *this; } - Clear(); - std::swap(byte_size_, other.byte_size_); - buffers_ = std::move(other.buffers_); - return *this; - } + ~NoncontiguousBuffer() { Clear(); } - ~NoncontiguousBuffer() { Clear(); } - - Slice FirstContiguous() const noexcept { - TILE_DCHECK(!Empty()); - auto &&first = buffers_.front(); - return Slice(first.data(), first.size()); - } - - void Skip(size_t bytes) noexcept { - TILE_DCHECK_LE(bytes, ByteSize()); - if (TILE_UNLIKELY(bytes == 0)) { - } else if (bytes < buffers_.front().size()) { - buffers_.front().Skip(bytes); - byte_size_ -= bytes; - } else { - SkipSlow(bytes); - } - } - - NoncontiguousBuffer Cut(std::size_t bytes) { - TILE_DCHECK_LE(bytes, ByteSize()); - TILE_DCHECK_GE(bytes, 0); - - NoncontiguousBuffer rc; - auto left = bytes; - while (left && left >= buffers_.front().size()) { - left -= buffers_.front().size(); - rc.buffers_.push_back(buffers_.pop_front()); + Slice FirstContiguous() const noexcept + { + TILE_DCHECK(!Empty()); + auto &&first = buffers_.front(); + return Slice(first.data(), first.size()); } - if (TILE_LIKELY(left > 0)) { - auto ncb = object_pool::Get().Leak(); - *ncb = buffers_.front(); - ncb->set_size(left); - rc.buffers_.push_back(ncb); - buffers_.front().Skip(left); - } - rc.byte_size_ = bytes; - byte_size_ -= bytes; - return rc; - } - - void Append(PolymorphicBuffer buffer) { - if (TILE_UNLIKELY(buffer.size() == 0)) { - return; + void Skip(size_t bytes) noexcept + { + TILE_DCHECK_LE(bytes, ByteSize()); + if (TILE_UNLIKELY(bytes == 0)) { + } else if (bytes < buffers_.front().size()) { + buffers_.front().Skip(bytes); + byte_size_ -= bytes; + } else { + SkipSlow(bytes); + } } - auto block = object_pool::Get(); - *block = std::move(buffer); - byte_size_ += block->size(); - buffers_.push_back(block.Leak()); - } + NoncontiguousBuffer Cut(std::size_t bytes) + { + TILE_DCHECK_LE(bytes, ByteSize()); + TILE_DCHECK_GE(bytes, 0); - void Append(NoncontiguousBuffer buffer) { - byte_size_ += internal::Exchange(buffer.byte_size_, 0); - buffers_.splice(std::move(buffer.buffers_)); - } + NoncontiguousBuffer rc; + auto left = bytes; + while (left && left >= buffers_.front().size()) { + left -= buffers_.front().size(); + rc.buffers_.push_back(buffers_.pop_front()); + } - std::size_t ByteSize() const noexcept { return byte_size_; } - - bool Empty() const noexcept { - TILE_CHECK_EQ(buffers_.empty(), !byte_size_); - return !byte_size_; - } - - void Clear() noexcept { - if (!Empty()) { - ClearSlow(); + if (TILE_LIKELY(left > 0)) { + auto ncb = object_pool::Get().Leak(); + *ncb = buffers_.front(); + ncb->set_size(left); + rc.buffers_.push_back(ncb); + buffers_.front().Skip(left); + } + rc.byte_size_ = bytes; + byte_size_ -= bytes; + return rc; } - } - iterator begin() noexcept { return buffers_.begin(); } - iterator end() noexcept { return buffers_.end(); } - const_iterator begin() const noexcept { return buffers_.begin(); } - const_iterator end() const noexcept { return buffers_.end(); } + void Append(PolymorphicBuffer buffer) + { + if (TILE_UNLIKELY(buffer.size() == 0)) { return; } + + auto block = object_pool::Get(); + *block = std::move(buffer); + byte_size_ += block->size(); + buffers_.push_back(block.Leak()); + } + + void Append(NoncontiguousBuffer buffer) + { + byte_size_ += internal::Exchange(buffer.byte_size_, 0); + buffers_.splice(std::move(buffer.buffers_)); + } + + std::size_t ByteSize() const noexcept { return byte_size_; } + + bool Empty() const noexcept + { + TILE_CHECK_EQ(buffers_.empty(), !byte_size_); + return !byte_size_; + } + + void Clear() noexcept + { + if (!Empty()) { ClearSlow(); } + } + + iterator begin() noexcept { return buffers_.begin(); } + + iterator end() noexcept { return buffers_.end(); } + + const_iterator begin() const noexcept { return buffers_.begin(); } + + const_iterator end() const noexcept { return buffers_.end(); } private: - void SkipSlow(size_t bytes) noexcept; - void ClearSlow() noexcept; + void SkipSlow(size_t bytes) noexcept; + void ClearSlow() noexcept; private: - std::size_t byte_size_{}; - LinkedBuffers buffers_; + std::size_t byte_size_{}; + LinkedBuffers buffers_; }; class NoncontiguousBufferBuilder { - static constexpr auto kAppendViaCopyThreshold = 128; + static constexpr auto kAppendViaCopyThreshold = 128; public: - NoncontiguousBufferBuilder() { InitializeNextBlock(); } + NoncontiguousBufferBuilder() { InitializeNextBlock(); } - // current write ptr - // It's size is available at `SizeAvailable()` - char *data() const noexcept { return current_->mutable_data() + used_; } + // current write ptr + // It's size is available at `SizeAvailable()` + char *data() const noexcept { return current_->mutable_data() + used_; } - std::size_t SizeAvailable() const noexcept { - return current_->size() - used_; - } + std::size_t SizeAvailable() const noexcept { return current_->size() - used_; } - void MarkWritten(std::size_t bytes) { - TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer."); - used_ += bytes; - // Is fulled ? - if (TILE_UNLIKELY(!SizeAvailable())) { - FlushCurrentBlock(); - InitializeNextBlock(); - } - } - - // Allocate new block - // return write ptr - char *Reserve(std::size_t bytes) { - static const auto kMaxBytes = 1024; - TILE_CHECK_LE(bytes, kMaxBytes, - "At most [{}] bytes may be reserved in single call.", - kMaxBytes); - if (SizeAvailable() < bytes) { - FlushCurrentBlock(); - InitializeNextBlock(); + void MarkWritten(std::size_t bytes) + { + TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer."); + used_ += bytes; + // Is fulled ? + if (TILE_UNLIKELY(!SizeAvailable())) { + FlushCurrentBlock(); + InitializeNextBlock(); + } } - auto ptr = data(); - MarkWritten(bytes); - return ptr; - } + // Allocate new block + // return write ptr + char *Reserve(std::size_t bytes) + { + static const auto kMaxBytes = 1024; + TILE_CHECK_LE(bytes, kMaxBytes, "At most [{}] bytes may be reserved in single call.", kMaxBytes); + if (SizeAvailable() < bytes) { + FlushCurrentBlock(); + InitializeNextBlock(); + } - // Total number of bytes written - std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; } - - NoncontiguousBuffer DestructiveGet() noexcept { - FlushCurrentBlock(); - return std::move(nb_); - } - - void Append(const void *ptr, std::size_t length) { - auto current = data(); - used_ += length; - if (TILE_LIKELY(used_ < current_->size())) { - memcpy(static_cast(current), ptr, length); - return; - } - used_ -= length; - AppendSlow(ptr, length); - } - - void Append(Slice s) { return Append(s.data(), s.size()); } - - void Append(const std::string &s) { Append(s.data(), s.size()); } - - void Append(PolymorphicBuffer buffer) { - if (buffer.size() < kAppendViaCopyThreshold && - SizeAvailable() >= buffer.size()) { - Append(buffer.data(), buffer.size()); - return; + auto ptr = data(); + MarkWritten(bytes); + return ptr; } - if (used_) { - FlushCurrentBlock(); - InitializeNextBlock(); + // Total number of bytes written + std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; } + + NoncontiguousBuffer DestructiveGet() noexcept + { + FlushCurrentBlock(); + return std::move(nb_); } - nb_.Append(std::move(buffer)); - } - - void Append(NoncontiguousBuffer buffer) { - if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) { - AppendCopy(buffer); - return; + void Append(const void *ptr, std::size_t length) + { + auto current = data(); + used_ += length; + if (TILE_LIKELY(used_ < current_->size())) { + memcpy(static_cast(current), ptr, length); + return; + } + used_ -= length; + AppendSlow(ptr, length); } - if (used_) { - FlushCurrentBlock(); - InitializeNextBlock(); + void Append(Slice s) { return Append(s.data(), s.size()); } + + void Append(const std::string &s) { Append(s.data(), s.size()); } + + void Append(PolymorphicBuffer buffer) + { + if (buffer.size() < kAppendViaCopyThreshold && SizeAvailable() >= buffer.size()) { + Append(buffer.data(), buffer.size()); + return; + } + + if (used_) { + FlushCurrentBlock(); + InitializeNextBlock(); + } + + nb_.Append(std::move(buffer)); } - nb_.Append(std::move(buffer)); - } - void Append(char c) { Append(static_cast(c)); } + void Append(NoncontiguousBuffer buffer) + { + if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) { + AppendCopy(buffer); + return; + } - void Append(unsigned char c) { - TILE_DCHECK(SizeAvailable()); - *reinterpret_cast(data()) = c; - MarkWritten(1); - } - - template < - typename... Ts, - typename = - internal::void_t()))...>, - typename = - internal::void_t()))...>> - void Append(const Ts &...buffers) { - auto current = data(); - auto total = detail::total_size(buffers...); - used_ += total; - if (TILE_LIKELY(used_ < current_->size())) { - UncheckedAppend(current, buffers...); - return; + if (used_) { + FlushCurrentBlock(); + InitializeNextBlock(); + } + nb_.Append(std::move(buffer)); } - used_ -= total; - int dummy[] = {(Append(buffers), 0)...}; - } - template - auto Append(T) -> enable_if_t::value> { - static_assert(sizeof(T) == 0, "Please use Append(char) instead."); - } + void Append(char c) { Append(static_cast(c)); } + + void Append(unsigned char c) + { + TILE_DCHECK(SizeAvailable()); + *reinterpret_cast(data()) = c; + MarkWritten(1); + } + + template()))...>, + typename = internal::void_t()))...>> + void Append(const Ts &...buffers) + { + auto current = data(); + auto total = detail::total_size(buffers...); + used_ += total; + if (TILE_LIKELY(used_ < current_->size())) { + UncheckedAppend(current, buffers...); + return; + } + used_ -= total; + int dummy[] = {(Append(buffers), 0)...}; + } + + template + auto Append(T) -> enable_if_t::value> + { + static_assert(sizeof(T) == 0, "Please use Append(char) instead."); + } private: - // Allocate a new buffer - void InitializeNextBlock(); + // Allocate a new buffer + void InitializeNextBlock(); - void FlushCurrentBlock(); + void FlushCurrentBlock(); - void AppendSlow(const void *ptr, std::size_t length); - void AppendCopy(const NoncontiguousBuffer &buffer); + void AppendSlow(const void *ptr, std::size_t length); + void AppendCopy(const NoncontiguousBuffer &buffer); - template - inline void UncheckedAppend(char *ptr, const T &slice) { - memcpy(ptr, detail::data(slice), detail::size(slice)); - } + template + inline void UncheckedAppend(char *ptr, const T &slice) + { + memcpy(ptr, detail::data(slice), detail::size(slice)); + } - template - inline void UncheckedAppend(char *ptr, const T &slice, const Ts &...slices) { - memcpy(ptr, detail::data(slice), detail::size(slice)); - UncheckedAppend(ptr + detail::size(slice), slices...); - } + template + inline void UncheckedAppend(char *ptr, const T &slice, const Ts &...slices) + { + memcpy(ptr, detail::data(slice), detail::size(slice)); + UncheckedAppend(ptr + detail::size(slice), slices...); + } private: - NoncontiguousBuffer nb_; - std::size_t used_; - RefPtr current_; + NoncontiguousBuffer nb_; + std::size_t used_; + RefPtr current_; }; namespace detail { -void FlattenToSlowSlow(const NoncontiguousBuffer &ncb, void *buffer, - std::size_t size); +void FlattenToSlowSlow(const NoncontiguousBuffer &ncb, void *buffer, std::size_t size); } NoncontiguousBuffer CreateBufferSlow(Slice s); NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size); -std::string -FlattenSlow(const NoncontiguousBuffer &nb, - std::size_t max_bytes = std::numeric_limits::max()); -std::string FlattenSlowUntil( - const NoncontiguousBuffer &nb, Slice delim, - std::size_t max_bytes = std::numeric_limits::max()); -inline void FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer, - std::size_t max_bytes) { - if (TILE_LIKELY(max_bytes <= nb.FirstContiguous().size())) { - std::memcpy(buffer, nb.FirstContiguous().data(), max_bytes); - } - return detail::FlattenToSlowSlow(nb, buffer, max_bytes); +std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes = std::numeric_limits::max()); +std::string FlattenSlowUntil(const NoncontiguousBuffer &nb, + Slice delim, + std::size_t max_bytes = std::numeric_limits::max()); + +inline void +FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t max_bytes) +{ + if (TILE_LIKELY(max_bytes <= nb.FirstContiguous().size())) { + std::memcpy(buffer, nb.FirstContiguous().data(), max_bytes); + } + return detail::FlattenToSlowSlow(nb, buffer, max_bytes); } PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size); -template -PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size, - F &&completion_cb) { - using BufferBlock = - ReferencingBufferBlock::type>; - return PolymorphicBuffer( - MakeRefCounted( - ptr, size, - std::forward::type>(completion_cb)), - 0, size); +template +PolymorphicBuffer +MakeReferencingBuffer(const void *ptr, std::size_t size, F &&completion_cb) +{ + using BufferBlock = ReferencingBufferBlock::type>; + return PolymorphicBuffer( + MakeRefCounted(ptr, size, std::forward::type>(completion_cb)), 0, + size); } PolymorphicBuffer MakeForeignBuffer(std::string buffer); // `T` in (`std::byte`, integral types, floating types) -template +template PolymorphicBuffer MakeForeignBuffer(std::vector buffer); bool StartsWith(NoncontiguousBuffer buffer, Slice prefix); bool EndsWith(NoncontiguousBuffer buffer, Slice suffix); bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix); bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix); -} // namespace tile +}// namespace tile -#endif // TILE_BASE_BUFFER_H +#endif// TILE_BASE_BUFFER_H diff --git a/tile/base/buffer/builtin_buffer_block.cc b/tile/base/buffer/builtin_buffer_block.cc index 8b1054a..29c76d1 100644 --- a/tile/base/buffer/builtin_buffer_block.cc +++ b/tile/base/buffer/builtin_buffer_block.cc @@ -19,104 +19,108 @@ namespace tile { namespace { -template -struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock - : NativeBufferBlock { +template +struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock : NativeBufferBlock { public: - static RefPtr New() { - // WARNING: don't use adopt_ptr - return RefPtr>( - adopt_ptr, object_pool::Get>().Leak()); - } + static RefPtr New() + { + // WARNING: don't use adopt_ptr + return RefPtr>( + adopt_ptr, object_pool::Get>().Leak()); + } - // HACK: add ref ensure `RefCount`. - static void Delete(FixedNativeBufferBlock *ptr) { - ptr->Save(); - object_pool::Put>(ptr); - } + // HACK: add ref ensure `RefCount`. + static void Delete(FixedNativeBufferBlock *ptr) + { + ptr->Save(); + object_pool::Put>(ptr); + } - char *mutable_data() noexcept override { return buffer.data(); } - const char *data() const noexcept override { return buffer.data(); } - std::size_t size() const noexcept override { return buffer.size(); } - void Destroy() noexcept override { Delete(this); } + char *mutable_data() noexcept override { return buffer.data(); } - static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock); - std::array buffer; + const char *data() const noexcept override { return buffer.data(); } + + std::size_t size() const noexcept override { return buffer.size(); } + + void Destroy() noexcept override { Delete(this); } + + static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock); + std::array buffer; }; -template -RefPtr MakeNativeBufferBlockOfBytes() { - return FixedNativeBufferBlock::New(); +template +RefPtr +MakeNativeBufferBlockOfBytes() +{ + return FixedNativeBufferBlock::New(); } -RefPtr (*make_native_buffer_block)() = - MakeNativeBufferBlockOfBytes; +RefPtr (*make_native_buffer_block)() = MakeNativeBufferBlockOfBytes; -void InitializeMakeNativeBufferBlock() { - static const std::unordered_map (*)()> - kMakers = {{"4K", MakeNativeBufferBlockOfBytes}, - {"64K", MakeNativeBufferBlockOfBytes}, - {"1M", MakeNativeBufferBlockOfBytes} - - }; - auto iter = kMakers.find(FLAGS_tile_buffer_block_size); - if (iter != kMakers.end()) { - make_native_buffer_block = iter->second; - } else { - TILE_UNEXPECTED( - "Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block " - "is supported.", - FLAGS_tile_buffer_block_size); - } +void +InitializeMakeNativeBufferBlock() +{ + static const std::unordered_map (*)()> kMakers = { + {"4K", MakeNativeBufferBlockOfBytes }, + {"64K", MakeNativeBufferBlockOfBytes}, + {"1M", MakeNativeBufferBlockOfBytes } + }; + auto iter = kMakers.find(FLAGS_tile_buffer_block_size); + if (iter != kMakers.end()) { + make_native_buffer_block = iter->second; + } else { + TILE_UNEXPECTED("Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block " + "is supported.", + FLAGS_tile_buffer_block_size); + } } TILE_ON_INIT(0, InitializeMakeNativeBufferBlock); -} // namespace +}// namespace -RefPtr MakeNativeBufferBlock() { - return make_native_buffer_block(); +RefPtr +MakeNativeBufferBlock() +{ + return make_native_buffer_block(); } -template <> struct PoolTraits> { - static constexpr auto kType = PoolType::MemoryNodeShared; - static constexpr auto kLowWaterMark = 16384; // 64M per node. - static constexpr auto kHighWaterMark = - std::numeric_limits::max(); - static constexpr auto kMaxIdle = std::chrono::seconds(10); - static constexpr auto kMinimumThreadCacheSize = 4096; // 16M per thread. - static constexpr auto kTransferBatchSize = 1024; // Extra 4M. +template<> +struct PoolTraits> { + static constexpr auto kType = PoolType::MemoryNodeShared; + static constexpr auto kLowWaterMark = 16384;// 64M per node. + static constexpr auto kHighWaterMark = std::numeric_limits::max(); + static constexpr auto kMaxIdle = std::chrono::seconds(10); + static constexpr auto kMinimumThreadCacheSize = 4096;// 16M per thread. + static constexpr auto kTransferBatchSize = 1024;// Extra 4M. }; -template <> struct PoolTraits> { - static constexpr auto kType = PoolType::MemoryNodeShared; - static constexpr auto kLowWaterMark = 1024; // 64M per node. - static constexpr auto kHighWaterMark = - std::numeric_limits::max(); - static constexpr auto kMaxIdle = std::chrono::seconds(10); - static constexpr auto kMinimumThreadCacheSize = 256; // 16M per thread. - static constexpr auto kTransferBatchSize = 64; // Extra 4M. +template<> +struct PoolTraits> { + static constexpr auto kType = PoolType::MemoryNodeShared; + static constexpr auto kLowWaterMark = 1024;// 64M per node. + static constexpr auto kHighWaterMark = std::numeric_limits::max(); + static constexpr auto kMaxIdle = std::chrono::seconds(10); + static constexpr auto kMinimumThreadCacheSize = 256;// 16M per thread. + static constexpr auto kTransferBatchSize = 64; // Extra 4M. }; -template <> struct PoolTraits> { - static constexpr auto kType = PoolType::MemoryNodeShared; - static constexpr auto kLowWaterMark = 128; // 128M per node. - static constexpr auto kHighWaterMark = - std::numeric_limits::max(); - static constexpr auto kMaxIdle = std::chrono::seconds(10); - static constexpr auto kMinimumThreadCacheSize = 64; // 64M per thread. - static constexpr auto kTransferBatchSize = 16; // Extra 16M. +template<> +struct PoolTraits> { + static constexpr auto kType = PoolType::MemoryNodeShared; + static constexpr auto kLowWaterMark = 128;// 128M per node. + static constexpr auto kHighWaterMark = std::numeric_limits::max(); + static constexpr auto kMaxIdle = std::chrono::seconds(10); + static constexpr auto kMinimumThreadCacheSize = 64;// 64M per thread. + static constexpr auto kTransferBatchSize = 16;// Extra 16M. }; constexpr PoolType PoolTraits>::kType; constexpr PoolType PoolTraits>::kType; constexpr PoolType PoolTraits>::kType; -constexpr std::chrono::seconds - PoolTraits>::kMaxIdle; -constexpr std::chrono::seconds - PoolTraits>::kMaxIdle; -constexpr std::chrono::seconds - PoolTraits>::kMaxIdle; +constexpr std::chrono::seconds PoolTraits>::kMaxIdle; +constexpr std::chrono::seconds PoolTraits>::kMaxIdle; +constexpr std::chrono::seconds PoolTraits>::kMaxIdle; -} // namespace tile +}// namespace tile diff --git a/tile/base/buffer/builtin_buffer_block.h b/tile/base/buffer/builtin_buffer_block.h index ef4480b..484b3ea 100644 --- a/tile/base/buffer/builtin_buffer_block.h +++ b/tile/base/buffer/builtin_buffer_block.h @@ -8,34 +8,32 @@ #include "tile/base/ref_ptr.h" namespace tile { -class alignas(hardware_destructive_interference_size) NativeBufferBlock - : public PolymorphicBufferBlock { +class alignas(hardware_destructive_interference_size) NativeBufferBlock : public PolymorphicBufferBlock { public: - virtual char *mutable_data() noexcept = 0; + virtual char *mutable_data() noexcept = 0; }; RefPtr MakeNativeBufferBlock(); -template -class ReferencingBufferBlock : public PolymorphicBufferBlock, - private F /* Empty Base Optimize */ { +template +class ReferencingBufferBlock : public PolymorphicBufferBlock, private F /* Empty Base Optimize */ { public: - explicit ReferencingBufferBlock(const void *ptr, std::size_t size, - F &&completion_cb) - : F(std::move(completion_cb)), ptr_(ptr), size_(size) {} + explicit ReferencingBufferBlock(const void *ptr, std::size_t size, F &&completion_cb) + : F(std::move(completion_cb)), + ptr_(ptr), + size_(size) + {} - ~ReferencingBufferBlock() override { (*this)(); } + ~ReferencingBufferBlock() override { (*this)(); } - const char *data() const noexcept override { - return reinterpret_cast(ptr_); - } + const char *data() const noexcept override { return reinterpret_cast(ptr_); } - std::size_t size() const noexcept override { return size_; } + std::size_t size() const noexcept override { return size_; } private: - const void *ptr_; - std::size_t size_; -}; // namespace tile -} // namespace tile + const void *ptr_; + std::size_t size_; +};// namespace tile +}// namespace tile -#endif // TILE_BASE_BUFFER_BUILTIN_BUFFER_BLOCK_H +#endif// TILE_BASE_BUFFER_BUILTIN_BUFFER_BLOCK_H diff --git a/tile/base/buffer/circular_buffer.h b/tile/base/buffer/circular_buffer.h index a35746d..9a55aad 100644 --- a/tile/base/buffer/circular_buffer.h +++ b/tile/base/buffer/circular_buffer.h @@ -9,59 +9,63 @@ #include namespace tile { -template class CircularBuffer { - class UninitializedObject; +template +class CircularBuffer { + class UninitializedObject; public: - explicit CircularBuffer(std::size_t capacity); + explicit CircularBuffer(std::size_t capacity); - template bool Emplace(Ts &&...args) { - auto head = head_.load(std::memory_order_relaxed); - auto next = NormalizeIndex(head + 1); + template + bool Emplace(Ts &&...args) + { + auto head = head_.load(std::memory_order_relaxed); + auto next = NormalizeIndex(head + 1); - if (next == tail_.load(std::memory_order_acquire)) { - return false; + if (next == tail_.load(std::memory_order_acquire)) { return false; } + + objects_[head].Initialize(std::forward(args)...); + head_.store(next, std::memory_order_release); + return true; } - objects_[head].Initialize(std::forward(args)...); - head_.store(next, std::memory_order_release); - return true; - } + void Pop(std::vector *objects) + { + auto upto = head_.load(std::memory_order_acquire); + auto current = tail_.load(std::memory_order_relaxed); + while (current != upto) { + objects->push_back(std::move(*objects_[current].Get())); + objects_[current].Destroy(); + current = NormalizeIndex(current + 1); + } - void Pop(std::vector *objects) { - auto upto = head_.load(std::memory_order_acquire); - auto current = tail_.load(std::memory_order_relaxed); - while (current != upto) { - objects->push_back(std::move(*objects_[current].Get())); - objects_[current].Destroy(); - current = NormalizeIndex(current + 1); + tail_.store(current, std::memory_order_release); } - tail_.store(current, std::memory_order_release); - } - private: - class UninitializedObject { - public: - T *Get() noexcept { return reinterpret_cast(&storage_); } - template void Initialize(Ts &&...args) { - new (Get()) T(std::forward(args)...); - } + class UninitializedObject { + public: + T *Get() noexcept { return reinterpret_cast(&storage_); } - void Destroy() { Get()->~T(); } + template + void Initialize(Ts &&...args) + { + new (Get()) T(std::forward(args)...); + } - private: - std::aligned_storage storage_; - }; - std::size_t NormalizeIndex(std::size_t x) { - return (x < capacity_) ? x : x - capacity_; - } + void Destroy() { Get()->~T(); } + + private: + std::aligned_storage storage_; + }; + + std::size_t NormalizeIndex(std::size_t x) { return (x < capacity_) ? x : x - capacity_; } private: - std::size_t capacity_; - std::unique_ptr objects_; - std::atomic head_{}, tail_{}; + std::size_t capacity_; + std::unique_ptr objects_; + std::atomic head_{}, tail_{}; }; -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_BUFFER_CIRCULAR_BUFFER_H +#endif// _TILE_BASE_BUFFER_CIRCULAR_BUFFER_H diff --git a/tile/base/buffer/compression_output_stream.cc b/tile/base/buffer/compression_output_stream.cc index 3bafc42..798aeed 100644 --- a/tile/base/buffer/compression_output_stream.cc +++ b/tile/base/buffer/compression_output_stream.cc @@ -2,42 +2,39 @@ namespace tile { -NoncontiguousBufferCompressionOutputStream:: - NoncontiguousBufferCompressionOutputStream( - NoncontiguousBufferBuilder *builder) - : builder_(builder) {} +NoncontiguousBufferCompressionOutputStream::NoncontiguousBufferCompressionOutputStream( + NoncontiguousBufferBuilder *builder) + : builder_(builder) +{} -NoncontiguousBufferCompressionOutputStream:: - ~NoncontiguousBufferCompressionOutputStream() { - Flush(); +NoncontiguousBufferCompressionOutputStream::~NoncontiguousBufferCompressionOutputStream() { Flush(); } + +void +NoncontiguousBufferCompressionOutputStream::Flush() +{ + if (using_bytes_ > 0) { + builder_->MarkWritten(using_bytes_); + using_bytes_ = 0; + } } -void NoncontiguousBufferCompressionOutputStream::Flush() { - if (using_bytes_ > 0) { - builder_->MarkWritten(using_bytes_); - using_bytes_ = 0; - } +bool +NoncontiguousBufferCompressionOutputStream::Next(void **data, std::size_t *size) noexcept +{ + if (!builder_) { return false; } + + if (using_bytes_) { builder_->MarkWritten(using_bytes_); } + + *data = builder_->data(); + *size = builder_->SizeAvailable(); + using_bytes_ = *size; + TILE_CHECK(*size > 0); + return true; } -bool NoncontiguousBufferCompressionOutputStream::Next( - void **data, std::size_t *size) noexcept { - if (!builder_) { - return false; - } - - if (using_bytes_) { - builder_->MarkWritten(using_bytes_); - } - - *data = builder_->data(); - *size = builder_->SizeAvailable(); - using_bytes_ = *size; - TILE_CHECK(*size > 0); - return true; +void +NoncontiguousBufferCompressionOutputStream::BackUp(std::size_t count) noexcept +{ + using_bytes_ -= count; } - -void NoncontiguousBufferCompressionOutputStream::BackUp( - std::size_t count) noexcept { - using_bytes_ -= count; -} -} // namespace tile +}// namespace tile diff --git a/tile/base/buffer/compression_output_stream.h b/tile/base/buffer/compression_output_stream.h index 7a8b090..244f130 100644 --- a/tile/base/buffer/compression_output_stream.h +++ b/tile/base/buffer/compression_output_stream.h @@ -6,22 +6,20 @@ #include "tile/base/compression/compression.h" namespace tile { -class NoncontiguousBufferCompressionOutputStream - : public CompressionOutputStream { +class NoncontiguousBufferCompressionOutputStream : public CompressionOutputStream { public: - explicit NoncontiguousBufferCompressionOutputStream( - NoncontiguousBufferBuilder *builder); - ~NoncontiguousBufferCompressionOutputStream() override; + explicit NoncontiguousBufferCompressionOutputStream(NoncontiguousBufferBuilder *builder); + ~NoncontiguousBufferCompressionOutputStream() override; - void Flush(); + void Flush(); - bool Next(void **data, std::size_t *size) noexcept override; - void BackUp(std::size_t count) noexcept override; + bool Next(void **data, std::size_t *size) noexcept override; + void BackUp(std::size_t count) noexcept override; private: - std::size_t using_bytes_{}; - NoncontiguousBufferBuilder *builder_; + std::size_t using_bytes_{}; + NoncontiguousBufferBuilder *builder_; }; -} // namespace tile +}// namespace tile -#endif // TILE_BASE_BUFFER_COMPRESSION_OUTPUT_STREAM_H +#endif// TILE_BASE_BUFFER_COMPRESSION_OUTPUT_STREAM_H diff --git a/tile/base/buffer/polymorphic_buffer.cc b/tile/base/buffer/polymorphic_buffer.cc index 0476644..663f629 100644 --- a/tile/base/buffer/polymorphic_buffer.cc +++ b/tile/base/buffer/polymorphic_buffer.cc @@ -8,14 +8,18 @@ constexpr std::chrono::seconds PoolTraits::kMaxIdle; constexpr std::size_t PoolTraits::kMinimumThreadCacheSize; constexpr std::size_t PoolTraits::kTransferBatchSize; -void PoolTraits::OnPut(PolymorphicBuffer *bb) { - bb->Clear(); +void +PoolTraits::OnPut(PolymorphicBuffer *bb) +{ + bb->Clear(); } namespace detail { -void PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p) { - TILE_DCHECK_EQ(p->UnsafeRefCount(), 0); - p->Destroy(); +void +PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p) +{ + TILE_DCHECK_EQ(p->UnsafeRefCount(), 0); + p->Destroy(); } -} // namespace detail -} // namespace tile +}// namespace detail +}// namespace tile diff --git a/tile/base/buffer/polymorphic_buffer.h b/tile/base/buffer/polymorphic_buffer.h index ec8547e..18b3840 100644 --- a/tile/base/buffer/polymorphic_buffer.h +++ b/tile/base/buffer/polymorphic_buffer.h @@ -19,102 +19,111 @@ namespace detail { struct PolymorphicBufferBlockDeleter; } -class PolymorphicBufferBlock - : public RefCounted { +class PolymorphicBufferBlock : public RefCounted { public: - virtual ~PolymorphicBufferBlock() = default; - virtual const char *data() const noexcept = 0; - virtual std::size_t size() const noexcept = 0; - virtual void Destroy() noexcept { delete this; } + virtual ~PolymorphicBufferBlock() = default; + virtual const char *data() const noexcept = 0; + virtual std::size_t size() const noexcept = 0; + + virtual void Destroy() noexcept { delete this; } }; -static_assert(!std::is_same, - PolymorphicBufferBlock>::value, - ""); -static_assert(detail::is_default_ref_traits_safe::value, - ""); +static_assert(!std::is_same, PolymorphicBufferBlock>::value, ""); +static_assert(detail::is_default_ref_traits_safe::value, ""); + class PolymorphicBuffer { public: - PolymorphicBuffer() = default; - PolymorphicBuffer(const PolymorphicBuffer &) = default; - PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default; + PolymorphicBuffer() = default; + PolymorphicBuffer(const PolymorphicBuffer &) = default; + PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default; - PolymorphicBuffer(PolymorphicBuffer &&other) noexcept - : ptr_(other.ptr_), size_(other.size_), ref_(std::move(other.ref_)) { - other.Clear(); - } - PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept { - if (this != &other) { - ptr_ = other.ptr_; - size_ = other.size_; - ref_ = other.ref_; - other.Clear(); + PolymorphicBuffer(PolymorphicBuffer &&other) noexcept + : ptr_(other.ptr_), + size_(other.size_), + ref_(std::move(other.ref_)) + { + other.Clear(); } - return *this; - } - PolymorphicBuffer(RefPtr data, std::size_t start, - std::size_t size) - : ptr_(data->data() + start), size_(size), ref_(std::move(data)) {} + PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept + { + if (this != &other) { + ptr_ = other.ptr_; + size_ = other.size_; + ref_ = other.ref_; + other.Clear(); + } + return *this; + } - const char *data() const noexcept { return ptr_; } - std::size_t size() const noexcept { return size_; } + PolymorphicBuffer(RefPtr data, std::size_t start, std::size_t size) + : ptr_(data->data() + start), + size_(size), + ref_(std::move(data)) + {} - void Skip(std::size_t bytes) { - TILE_CHECK_LT(bytes, size_); - size_ -= bytes; - ptr_ += bytes; - } + const char *data() const noexcept { return ptr_; } - void set_size(std::size_t size) { - TILE_DCHECK_LE(size, size_); - size_ = size; - } + std::size_t size() const noexcept { return size_; } - void Reset(RefPtr data, std::size_t start, - std::size_t size) { - TILE_DCHECK_LE(start, size); - TILE_DCHECK_LE(size, data->size()); + void Skip(std::size_t bytes) + { + TILE_CHECK_LT(bytes, size_); + size_ -= bytes; + ptr_ += bytes; + } - ref_ = std::move(data); - ptr_ = data->data() + start; - size_ = size; - } + void set_size(std::size_t size) + { + TILE_DCHECK_LE(size, size_); + size_ = size; + } - void Clear() { - ptr_ = nullptr; - size_ = 0; - ref_ = nullptr; - } + void Reset(RefPtr data, std::size_t start, std::size_t size) + { + TILE_DCHECK_LE(start, size); + TILE_DCHECK_LE(size, data->size()); + + ref_ = std::move(data); + ptr_ = data->data() + start; + size_ = size; + } + + void Clear() + { + ptr_ = nullptr; + size_ = 0; + ref_ = nullptr; + } private: - friend class NoncontiguousBuffer; + friend class NoncontiguousBuffer; - internal::SinglyLinkedListEntry chain; - const char *ptr_{}; - std::size_t size_{}; - RefPtr ref_; + internal::SinglyLinkedListEntry chain; + const char *ptr_{}; + std::size_t size_{}; + RefPtr ref_; }; + namespace detail { struct PolymorphicBufferBlockDeleter { - void operator()(PolymorphicBufferBlock *p); + void operator()(PolymorphicBufferBlock *p); }; -} // namespace detail +}// namespace detail -} // namespace tile +}// namespace tile namespace tile { -template <> struct PoolTraits { - static constexpr auto kType = PoolType::MemoryNodeShared; - static constexpr std::size_t kLowWaterMark = 32768; - static constexpr std::size_t kHighWaterMark = - std::numeric_limits::max(); - static constexpr std::chrono::seconds kMaxIdle = std::chrono::seconds(10); - static constexpr std::size_t kMinimumThreadCacheSize = 8192; - static constexpr std::size_t kTransferBatchSize = 1024; +template<> +struct PoolTraits { + static constexpr auto kType = PoolType::MemoryNodeShared; + static constexpr std::size_t kLowWaterMark = 32768; + static constexpr std::size_t kHighWaterMark = std::numeric_limits::max(); + static constexpr std::chrono::seconds kMaxIdle = std::chrono::seconds(10); + static constexpr std::size_t kMinimumThreadCacheSize = 8192; + static constexpr std::size_t kTransferBatchSize = 1024; - static void OnPut(PolymorphicBuffer *bb); + static void OnPut(PolymorphicBuffer *bb); }; -} // namespace tile -#endif // TILE_BASE_BUFFER_POLYMORPHIC_BUFFER_H +}// namespace tile +#endif// TILE_BASE_BUFFER_POLYMORPHIC_BUFFER_H diff --git a/tile/base/buffer_test.cc b/tile/base/buffer_test.cc index c972a9c..ddc1a0e 100644 --- a/tile/base/buffer_test.cc +++ b/tile/base/buffer_test.cc @@ -14,280 +14,308 @@ namespace tile { namespace { -PolymorphicBuffer MakeNativeBuffer(Slice s) { - auto buffer = MakeNativeBufferBlock(); - memcpy(buffer->mutable_data(), s.data(), s.size()); - return PolymorphicBuffer(buffer, 0, s.size()); +PolymorphicBuffer +MakeNativeBuffer(Slice s) +{ + auto buffer = MakeNativeBufferBlock(); + memcpy(buffer->mutable_data(), s.data(), s.size()); + return PolymorphicBuffer(buffer, 0, s.size()); } -} // namespace +}// namespace -TEST(CreateBufferSlow, All) { - static const auto kData = Slice("sadfas234sadf-/+8sdaf sd f~!#"); - auto nb = CreateBufferSlow(kData); - ASSERT_EQ(kData, nb.FirstContiguous().data()); - ASSERT_EQ(kData, FlattenSlow(nb)); +TEST(CreateBufferSlow, All) +{ + static const auto kData = Slice("sadfas234sadf-/+8sdaf sd f~!#"); + auto nb = CreateBufferSlow(kData); + ASSERT_EQ(kData, nb.FirstContiguous().data()); + ASSERT_EQ(kData, FlattenSlow(nb)); } -TEST(NoncontiguousBuffer, Cut) { - NoncontiguousBuffer nb; - nb.Append(CreateBufferSlow("asdf")); - auto r = nb.Cut(3); - ASSERT_EQ(1, nb.ByteSize()); - ASSERT_EQ("f", FlattenSlow(nb)); - ASSERT_EQ(3, r.ByteSize()); - ASSERT_EQ("asd", FlattenSlow(r)); +TEST(NoncontiguousBuffer, Cut) +{ + NoncontiguousBuffer nb; + nb.Append(CreateBufferSlow("asdf")); + auto r = nb.Cut(3); + ASSERT_EQ(1, nb.ByteSize()); + ASSERT_EQ("f", FlattenSlow(nb)); + ASSERT_EQ(3, r.ByteSize()); + ASSERT_EQ("asd", FlattenSlow(r)); } -TEST(NoncontiguousBuffer, Cut1) { - NoncontiguousBuffer nb; - nb.Append(CreateBufferSlow("asdf")); - auto r = nb.Cut(4); - ASSERT_TRUE(nb.Empty()); - ASSERT_EQ(4, r.ByteSize()); +TEST(NoncontiguousBuffer, Cut1) +{ + NoncontiguousBuffer nb; + nb.Append(CreateBufferSlow("asdf")); + auto r = nb.Cut(4); + ASSERT_TRUE(nb.Empty()); + ASSERT_EQ(4, r.ByteSize()); } -TEST(NoncontiguousBuffer, Cut2) { - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer("asdf")); - nb.Append(MakeNativeBuffer("asdf")); - auto r = nb.Cut(4); - ASSERT_EQ(4, nb.ByteSize()); - ASSERT_EQ(4, r.ByteSize()); +TEST(NoncontiguousBuffer, Cut2) +{ + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer("asdf")); + nb.Append(MakeNativeBuffer("asdf")); + auto r = nb.Cut(4); + ASSERT_EQ(4, nb.ByteSize()); + ASSERT_EQ(4, r.ByteSize()); } -TEST(NoncontiguousBuffer, Cut3) { - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer("asdf")); - nb.Append(MakeNativeBuffer("asdf")); - auto r = nb.Cut(8); - ASSERT_TRUE(nb.Empty()); - ASSERT_EQ(8, r.ByteSize()); +TEST(NoncontiguousBuffer, Cut3) +{ + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer("asdf")); + nb.Append(MakeNativeBuffer("asdf")); + auto r = nb.Cut(8); + ASSERT_TRUE(nb.Empty()); + ASSERT_EQ(8, r.ByteSize()); } -TEST(NoncontiguousBuffer, Cut4) { - auto nb = CreateBufferSlow("asdfasf2345sfsdfdf"); - auto nb2 = nb; - ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2)); - NoncontiguousBuffer splited; - splited.Append(nb.Cut(1)); - splited.Append(nb.Cut(2)); - splited.Append(nb.Cut(3)); - splited.Append(nb.Cut(4)); - splited.Append(std::move(nb)); - ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited)); +TEST(NoncontiguousBuffer, Cut4) +{ + auto nb = CreateBufferSlow("asdfasf2345sfsdfdf"); + auto nb2 = nb; + ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2)); + NoncontiguousBuffer splited; + splited.Append(nb.Cut(1)); + splited.Append(nb.Cut(2)); + splited.Append(nb.Cut(3)); + splited.Append(nb.Cut(4)); + splited.Append(std::move(nb)); + ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited)); } -TEST(NoncontiguousBuffer, Skip) { - NoncontiguousBuffer splited; - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Append(CreateBufferSlow("asdf")); - splited.Skip(32); - ASSERT_EQ(0, splited.ByteSize()); +TEST(NoncontiguousBuffer, Skip) +{ + NoncontiguousBuffer splited; + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Append(CreateBufferSlow("asdf")); + splited.Skip(32); + ASSERT_EQ(0, splited.ByteSize()); } -TEST(NoncontiguousBuffer, Skip2) { - NoncontiguousBuffer buffer; - EXPECT_TRUE(buffer.Empty()); - buffer.Skip(0); // Don't crash. - EXPECT_TRUE(buffer.Empty()); +TEST(NoncontiguousBuffer, Skip2) +{ + NoncontiguousBuffer buffer; + EXPECT_TRUE(buffer.Empty()); + buffer.Skip(0);// Don't crash. + EXPECT_TRUE(buffer.Empty()); } -TEST(NoncontiguousBuffer, FlattenSlow) { - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer("asd4234")); - nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); - ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10)); +TEST(NoncontiguousBuffer, FlattenSlow) +{ + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer("asd4234")); + nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); + ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10)); } -TEST(NoncontiguousBuffer, FlattenToSlow) { - struct C { - std::uint64_t ll; - int i; - bool f; - }; - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78\x9a\xbc\xde\xf0", 8))); - nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78", 4))); - nb.Append(MakeNativeBuffer(Slice("\x1", 1))); - nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3))); // Padding - C c; - FlattenToSlow(nb, &c, sizeof(C)); - ASSERT_EQ(0xf0debc9a78563412, c.ll); - ASSERT_EQ(0x78563412, c.i); - ASSERT_EQ(true, c.f); +TEST(NoncontiguousBuffer, FlattenToSlow) +{ + struct C { + std::uint64_t ll; + int i; + bool f; + }; + + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78\x9a\xbc\xde\xf0", 8))); + nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78", 4))); + nb.Append(MakeNativeBuffer(Slice("\x1", 1))); + nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3)));// Padding + C c; + FlattenToSlow(nb, &c, sizeof(C)); + ASSERT_EQ(0xf0debc9a78563412, c.ll); + ASSERT_EQ(0x78563412, c.i); + ASSERT_EQ(true, c.f); } -TEST(NoncontiguousBuffer, FlattenSlowUntil) { - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer("asd4234")); - nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); - ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX")); - ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4")); - ASSERT_EQ("asd4234aXsdfsadfasdf2342", FlattenSlowUntil(nb, "2342")); - ASSERT_EQ("asd42", FlattenSlowUntil(nb, "z", 5)); - ASSERT_EQ("asd42", FlattenSlowUntil(nb, "3", 5)); - ASSERT_EQ("asd42", FlattenSlowUntil(nb, "2", 5)); - ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5)); +TEST(NoncontiguousBuffer, FlattenSlowUntil) +{ + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer("asd4234")); + nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); + ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX")); + ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4")); + ASSERT_EQ("asd4234aXsdfsadfasdf2342", FlattenSlowUntil(nb, "2342")); + ASSERT_EQ("asd42", FlattenSlowUntil(nb, "z", 5)); + ASSERT_EQ("asd42", FlattenSlowUntil(nb, "3", 5)); + ASSERT_EQ("asd42", FlattenSlowUntil(nb, "2", 5)); + ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5)); } -TEST(NoncontiguousBuffer, FlattenSlowUntil2) { - auto nb = CreateBufferSlow( - "HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: " - "0\r\nRpc-Error-Reason: The operation completed " - "successfully.\r\nContent-Type: " - "application/x-protobuf\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 " - "OK\r\nRpc-Seq"); - ASSERT_EQ("HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: " - "0\r\nRpc-Error-Reason: The operation completed " - "successfully.\r\nContent-Type: " - "application/x-protobuf\r\nContent-Length: 0\r\n\r\n", - FlattenSlowUntil(nb, "\r\n\r\n")); +TEST(NoncontiguousBuffer, FlattenSlowUntil2) +{ + auto nb = CreateBufferSlow( + "HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: " + "0\r\nRpc-Error-Reason: The operation completed " + "successfully.\r\nContent-Type: " + "application/x-protobuf\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 " + "OK\r\nRpc-Seq"); + ASSERT_EQ("HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: " + "0\r\nRpc-Error-Reason: The operation completed " + "successfully.\r\nContent-Type: " + "application/x-protobuf\r\nContent-Length: 0\r\n\r\n", + FlattenSlowUntil(nb, "\r\n\r\n")); } -TEST(NoncontiguousBuffer, FlattenSlowUntil3) { - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer("asd4234")); - nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); - ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX")); - ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4")); - ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX")); +TEST(NoncontiguousBuffer, FlattenSlowUntil3) +{ + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer("asd4234")); + nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); + ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX")); + ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4")); + ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX")); } -TEST(NoncontiguousBuffer, FlattenSlowUntil4) { - NoncontiguousBuffer nb; - nb.Append(MakeNativeBuffer("AB")); - nb.Append(MakeNativeBuffer("CDEFGGGGHHHH")); - ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG")); +TEST(NoncontiguousBuffer, FlattenSlowUntil4) +{ + NoncontiguousBuffer nb; + nb.Append(MakeNativeBuffer("AB")); + nb.Append(MakeNativeBuffer("CDEFGGGGHHHH")); + ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG")); } -TEST(NoncontiguousBufferBuilder, Append) { - NoncontiguousBufferBuilder nbb; - nbb.Append(MakeForeignBuffer("")); - nbb.Append(MakeForeignBuffer("small")); - nbb.Append(MakeForeignBuffer(std::string(8192, 'a'))); - nbb.Append(CreateBufferSlow("")); - nbb.Append(CreateBufferSlow("small")); - nbb.Append(CreateBufferSlow(std::string(8192, 'a'))); - auto nb = nbb.DestructiveGet(); - EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'), - FlattenSlow(nb)); +TEST(NoncontiguousBufferBuilder, Append) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(MakeForeignBuffer("")); + nbb.Append(MakeForeignBuffer("small")); + nbb.Append(MakeForeignBuffer(std::string(8192, 'a'))); + nbb.Append(CreateBufferSlow("")); + nbb.Append(CreateBufferSlow("small")); + nbb.Append(CreateBufferSlow(std::string(8192, 'a'))); + auto nb = nbb.DestructiveGet(); + EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'), FlattenSlow(nb)); } -TEST(NoncontiguousBufferBuilder, Reserve) { - auto temp_block = MakeNativeBufferBlock(); - auto max_bytes = temp_block->size(); - NoncontiguousBufferBuilder nbb; - auto ptr = nbb.data(); - auto ptr2 = nbb.Reserve(10); - ASSERT_EQ(ptr, ptr2); - ASSERT_EQ(ptr + 10, nbb.data()); - nbb.Append(std::string(max_bytes - 10 - 1, 'a')); - ptr = nbb.data(); - ptr2 = nbb.Reserve(1); // Last byte in the block. - ASSERT_EQ(ptr, ptr2); - ASSERT_EQ(max_bytes, nbb.SizeAvailable()); +TEST(NoncontiguousBufferBuilder, Reserve) +{ + auto temp_block = MakeNativeBufferBlock(); + auto max_bytes = temp_block->size(); + NoncontiguousBufferBuilder nbb; + auto ptr = nbb.data(); + auto ptr2 = nbb.Reserve(10); + ASSERT_EQ(ptr, ptr2); + ASSERT_EQ(ptr + 10, nbb.data()); + nbb.Append(std::string(max_bytes - 10 - 1, 'a')); + ptr = nbb.data(); + ptr2 = nbb.Reserve(1);// Last byte in the block. + ASSERT_EQ(ptr, ptr2); + ASSERT_EQ(max_bytes, nbb.SizeAvailable()); - nbb.Append(std::string(max_bytes - 1, 'a')); - ptr = nbb.data(); - ptr2 = nbb.Reserve(2); - ASSERT_NE(ptr, ptr2); - ASSERT_EQ(ptr2 + 2, nbb.data()); + nbb.Append(std::string(max_bytes - 1, 'a')); + ptr = nbb.data(); + ptr2 = nbb.Reserve(2); + ASSERT_NE(ptr, ptr2); + ASSERT_EQ(ptr2 + 2, nbb.data()); } -TEST(NoncontiguousBufferBuilder, DestructiveGet1) { - NoncontiguousBufferBuilder nbb; - nbb.Append("asdf1234", 6); - nbb.Append("1122", 4); - ASSERT_EQ("asdf12" - "1122", - FlattenSlow(nbb.DestructiveGet())); +TEST(NoncontiguousBufferBuilder, DestructiveGet1) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append("asdf1234", 6); + nbb.Append("1122", 4); + ASSERT_EQ("asdf12" + "1122", + FlattenSlow(nbb.DestructiveGet())); } -TEST(NoncontiguousBufferBuilder, DestructiveGet2) { - NoncontiguousBufferBuilder nbb; - nbb.Append("aabbccd"); - ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet())); +TEST(NoncontiguousBufferBuilder, DestructiveGet2) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append("aabbccd"); + ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet())); } -TEST(NoncontiguousBufferBuilder, DestructiveGet3) { - NoncontiguousBufferBuilder nbb; - nbb.Append(std::string(1000000, 'A')); - ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet())); +TEST(NoncontiguousBufferBuilder, DestructiveGet3) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(std::string(1000000, 'A')); + ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet())); } -TEST(NoncontiguousBufferBuilder, DestructiveGet4) { - NoncontiguousBufferBuilder nbb; - nbb.Append('c'); - ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet())); +TEST(NoncontiguousBufferBuilder, DestructiveGet4) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append('c'); + ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet())); } -TEST(NoncontiguousBufferBuilder, DestructiveGet5) { - NoncontiguousBufferBuilder nbb; - nbb.Append(CreateBufferSlow("c")); - ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet())); +TEST(NoncontiguousBufferBuilder, DestructiveGet5) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(CreateBufferSlow("c")); + ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet())); } -TEST(NoncontiguousBufferBuilder, DestructiveGet6) { - NoncontiguousBufferBuilder nbb; - nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"), - Slice("6")); - nbb.Append("1122", 4); - ASSERT_EQ("11234561122", FlattenSlow(nbb.DestructiveGet())); +TEST(NoncontiguousBufferBuilder, DestructiveGet6) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"), Slice("6")); + nbb.Append("1122", 4); + ASSERT_EQ("11234561122", FlattenSlow(nbb.DestructiveGet())); } -TEST(MakeReferencingBuffer, Simple) { - NoncontiguousBufferBuilder nbb; - nbb.Append(MakeReferencingBuffer("abcdefg", 7)); - EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); +TEST(MakeReferencingBuffer, Simple) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(MakeReferencingBuffer("abcdefg", 7)); + EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); } -TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) { - int x = 0; +TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) +{ + int x = 0; - NoncontiguousBufferBuilder nbb; - nbb.Append("aaa", 3); - // Small buffers are copied by `Append` and freed immediately. - nbb.Append(MakeReferencingBuffer("abcdefg", 7, [&] { ++x; })); - // Therefore the callback should have fired on return of `Append`. - EXPECT_EQ(1, x); - auto buffer = nbb.DestructiveGet(); - EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer)); -} - -TEST(MakeReferencingBuffer, WithCallback) { - static const std::string kBuffer(12345, 'a'); - int x = 0; - - { NoncontiguousBufferBuilder nbb; nbb.Append("aaa", 3); - nbb.Append(MakeReferencingBuffer(kBuffer.data(), 1024, [&] { ++x; })); - EXPECT_EQ(0, x); + // Small buffers are copied by `Append` and freed immediately. + nbb.Append(MakeReferencingBuffer("abcdefg", 7, [&] { ++x; })); + // Therefore the callback should have fired on return of `Append`. + EXPECT_EQ(1, x); auto buffer = nbb.DestructiveGet(); - EXPECT_EQ(0, x); - EXPECT_EQ("aaa" + kBuffer.substr(0, 1024), FlattenSlow(buffer)); - } - EXPECT_EQ(1, x); + EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer)); } -TEST(MakeForeignBuffer, String) { - NoncontiguousBufferBuilder nbb; - nbb.Append(MakeForeignBuffer(std::string("abcdefg"))); - EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); +TEST(MakeReferencingBuffer, WithCallback) +{ + static const std::string kBuffer(12345, 'a'); + int x = 0; + + { + NoncontiguousBufferBuilder nbb; + nbb.Append("aaa", 3); + nbb.Append(MakeReferencingBuffer(kBuffer.data(), 1024, [&] { ++x; })); + EXPECT_EQ(0, x); + auto buffer = nbb.DestructiveGet(); + EXPECT_EQ(0, x); + EXPECT_EQ("aaa" + kBuffer.substr(0, 1024), FlattenSlow(buffer)); + } + EXPECT_EQ(1, x); } -TEST(MakeForeignBuffer, VectorOfChar) { - std::vector data{'a', 'b', 'c', 'd', 'e', 'f', 'g'}; - NoncontiguousBufferBuilder nbb; - nbb.Append(MakeForeignBuffer(std::move(data))); - EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); +TEST(MakeForeignBuffer, String) +{ + NoncontiguousBufferBuilder nbb; + nbb.Append(MakeForeignBuffer(std::string("abcdefg"))); + EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); +} + +TEST(MakeForeignBuffer, VectorOfChar) +{ + std::vector data{'a', 'b', 'c', 'd', 'e', 'f', 'g'}; + NoncontiguousBufferBuilder nbb; + nbb.Append(MakeForeignBuffer(std::move(data))); + EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); } // TEST(MakeForeignBuffer, VectorOfBytes) { @@ -299,13 +327,14 @@ TEST(MakeForeignBuffer, VectorOfChar) { // EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); // } -TEST(MakeForeignBuffer, VectorOfUInt8) { - std::vector data; - data.resize(7); - memcpy(data.data(), "abcdefg", 7); - NoncontiguousBufferBuilder nbb; - nbb.Append(MakeForeignBuffer(std::move(data))); - EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); +TEST(MakeForeignBuffer, VectorOfUInt8) +{ + std::vector data; + data.resize(7); + memcpy(data.data(), "abcdefg", 7); + NoncontiguousBufferBuilder nbb; + nbb.Append(MakeForeignBuffer(std::move(data))); + EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); } -} // namespace tile +}// namespace tile diff --git a/tile/base/byte_order.h b/tile/base/byte_order.h index 7ac6fae..2fa16f1 100644 --- a/tile/base/byte_order.h +++ b/tile/base/byte_order.h @@ -41,86 +41,116 @@ namespace tile { -inline uint16_t ByteSwap16(uint16_t val) { return __builtin_bswap16(val); } -inline uint32_t ByteSwap32(uint32_t val) { return __builtin_bswap32(val); } -inline uint64_t ByteSwap64(uint64_t val) { return __builtin_bswap64(val); } +inline uint16_t +ByteSwap16(uint16_t val) +{ + return __builtin_bswap16(val); +} -inline bool IsHostLittleEndian() { +inline uint32_t +ByteSwap32(uint32_t val) +{ + return __builtin_bswap32(val); +} + +inline uint64_t +ByteSwap64(uint64_t val) +{ + return __builtin_bswap64(val); +} + +inline bool +IsHostLittleEndian() +{ #if TILE_LITTLE_ENDIAN == TILE_BYTE_ORDER - return true; + return true; #else - return false; + return false; #endif } -inline bool IsHostBigEndian() { +inline bool +IsHostBigEndian() +{ #if TILE_BIG_ENDIAN == TILE_BYTE_ORDER - return true; + return true; #else - return false; + return false; #endif } -inline uint16_t HostToNetwork16(uint16_t val_in_host) { +inline uint16_t +HostToNetwork16(uint16_t val_in_host) +{ #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - return val_in_host; + return val_in_host; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - return ByteSwap16(val_in_host); + return ByteSwap16(val_in_host); #else #error "Unsupported byte order" #endif } -inline uint32_t HostToNetwork32(uint32_t val_in_host) { +inline uint32_t +HostToNetwork32(uint32_t val_in_host) +{ #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - return val_in_host; + return val_in_host; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - return ByteSwap32(val_in_host); + return ByteSwap32(val_in_host); #else #error "Unsupported byte order" #endif } -inline uint64_t HostToNetwork64(uint64_t val_in_host) { +inline uint64_t +HostToNetwork64(uint64_t val_in_host) +{ #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - return val_in_host; + return val_in_host; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - return ByteSwap64(val_in_host); + return ByteSwap64(val_in_host); #else #error "Unsupported byte order" #endif } -inline uint16_t NetworkToHost16(uint16_t val_in_network) { +inline uint16_t +NetworkToHost16(uint16_t val_in_network) +{ #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - return val_in_network; + return val_in_network; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - return ByteSwap16(val_in_network); + return ByteSwap16(val_in_network); #else #error "Unsupported byte order" #endif } -inline uint32_t NetworkToHost32(uint32_t val_in_network) { +inline uint32_t +NetworkToHost32(uint32_t val_in_network) +{ #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - return val_in_network; + return val_in_network; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - return ByteSwap32(val_in_network); + return ByteSwap32(val_in_network); #else #error "Unsupported byte order" #endif } -inline uint64_t NetworkToHost64(uint64_t val_in_network) { +inline uint64_t +NetworkToHost64(uint64_t val_in_network) +{ #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - return val_in_network; + return val_in_network; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - return ByteSwap64(val_in_network); + return ByteSwap64(val_in_network); #else #error "Unsupported byte order" #endif } -} // namespace tile +}// namespace tile -#endif // TILE_BASE_BYTE_ORDER_H +#endif// TILE_BASE_BYTE_ORDER_H diff --git a/tile/base/byte_order_test.cc b/tile/base/byte_order_test.cc index 8bb17bd..65f285b 100644 --- a/tile/base/byte_order_test.cc +++ b/tile/base/byte_order_test.cc @@ -8,109 +8,123 @@ namespace tile { static union EndianHelper { - uint16_t v16; - uint32_t v32; - uint32_t v64; - uint8_t bytes[8]; -} endian_helper = {.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}; + uint16_t v16; + uint32_t v32; + uint32_t v64; + uint8_t bytes[8]; +} endian_helper = { + .bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07} +}; -TEST(ByteOrder, ByteSwap16) { - const uint16_t val = 0x1234; - const uint16_t expected = 0x3412; - ASSERT_EQ(expected, ByteSwap16(val)); -} -TEST(ByteOrder, ByteSwap32) { - const uint32_t val = 0x12345678; - const uint32_t expected = 0x78563412; - ASSERT_EQ(expected, ByteSwap32(val)); -} -TEST(ByteOrder, ByteSwap64) { - const uint64_t val = 0x1234567890abcdef; - const uint64_t expected = 0xefcdab9078563412; - ASSERT_EQ(expected, ByteSwap64(val)); +TEST(ByteOrder, ByteSwap16) +{ + const uint16_t val = 0x1234; + const uint16_t expected = 0x3412; + ASSERT_EQ(expected, ByteSwap16(val)); } -TEST(ByteOrder, IsHostByteOrder) { - ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian()); +TEST(ByteOrder, ByteSwap32) +{ + const uint32_t val = 0x12345678; + const uint32_t expected = 0x78563412; + ASSERT_EQ(expected, ByteSwap32(val)); +} + +TEST(ByteOrder, ByteSwap64) +{ + const uint64_t val = 0x1234567890abcdef; + const uint64_t expected = 0xefcdab9078563412; + ASSERT_EQ(expected, ByteSwap64(val)); +} + +TEST(ByteOrder, IsHostByteOrder) +{ + ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian()); #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - ASSERT_TRUE(IsHostBigEndian()); - ASSERT_FALSE(IsHostLittleEndian()); + ASSERT_TRUE(IsHostBigEndian()); + ASSERT_FALSE(IsHostLittleEndian()); #else - ASSERT_TRUE(IsHostLittleEndian()); - ASSERT_FALSE(IsHostBigEndian()); + ASSERT_TRUE(IsHostLittleEndian()); + ASSERT_FALSE(IsHostBigEndian()); #endif } -TEST(ByteOrder, HostToNetwork16) { - const uint16_t val = 0x1234; +TEST(ByteOrder, HostToNetwork16) +{ + const uint16_t val = 0x1234; #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - const uint16_t expected = val; + const uint16_t expected = val; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - const uint16_t expected = 0x3412; + const uint16_t expected = 0x3412; #else #error "Unsupported byte order" #endif - ASSERT_EQ(expected, HostToNetwork16(val)); + ASSERT_EQ(expected, HostToNetwork16(val)); } -TEST(ByteOrder, HostToNetwork32) { - const uint32_t val = 0x12345678; +TEST(ByteOrder, HostToNetwork32) +{ + const uint32_t val = 0x12345678; #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - const uint32_t expected = val; + const uint32_t expected = val; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - const uint32_t expected = 0x78563412; + const uint32_t expected = 0x78563412; #else #error "Unsupported byte order" #endif - ASSERT_EQ(expected, HostToNetwork32(val)); + ASSERT_EQ(expected, HostToNetwork32(val)); } -TEST(ByteOrder, HostToNetwork64) { - const uint64_t val = 0x1234567890abcdef; +TEST(ByteOrder, HostToNetwork64) +{ + const uint64_t val = 0x1234567890abcdef; #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - const uint64_t expected = val; + const uint64_t expected = val; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - const uint64_t expected = 0xefcdab9078563412; + const uint64_t expected = 0xefcdab9078563412; #else #error "Unsupported byte order" #endif - ASSERT_EQ(expected, HostToNetwork64(val)); + ASSERT_EQ(expected, HostToNetwork64(val)); } -TEST(ByteOrder, NetworkToHost16) { - const uint16_t val = 0x1234; +TEST(ByteOrder, NetworkToHost16) +{ + const uint16_t val = 0x1234; #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - const uint16_t expected = val; + const uint16_t expected = val; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - const uint16_t expected = 0x3412; + const uint16_t expected = 0x3412; #else #error "Unsupported byte order" #endif - ASSERT_EQ(expected, NetworkToHost16(val)); + ASSERT_EQ(expected, NetworkToHost16(val)); } -TEST(ByteOrder, NetworkToHost32) { - const uint32_t val = 0x12345678; +TEST(ByteOrder, NetworkToHost32) +{ + const uint32_t val = 0x12345678; #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - const uint32_t expected = val; + const uint32_t expected = val; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - const uint32_t expected = 0x78563412; + const uint32_t expected = 0x78563412; #else #error "Unsupported byte order" #endif - ASSERT_EQ(expected, NetworkToHost32(val)); + ASSERT_EQ(expected, NetworkToHost32(val)); } -TEST(ByteOrder, NetworkToHost64) { - const uint64_t val = 0x1234567890abcdef; +TEST(ByteOrder, NetworkToHost64) +{ + const uint64_t val = 0x1234567890abcdef; #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN - const uint64_t expected = val; + const uint64_t expected = val; #elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN - const uint64_t expected = 0xefcdab9078563412; + const uint64_t expected = 0xefcdab9078563412; #else #error "Unsupported byte order" #endif - ASSERT_EQ(expected, NetworkToHost64(val)); + ASSERT_EQ(expected, NetworkToHost64(val)); } -} // namespace tile +}// namespace tile diff --git a/tile/base/casting.h b/tile/base/casting.h index a5ee6e1..7b1dadc 100644 --- a/tile/base/casting.h +++ b/tile/base/casting.h @@ -12,178 +12,200 @@ namespace tile { namespace detail { // Has classof -template struct HasClassofImpl { - template < - typename TT, typename TBase, - typename = enable_if_t())), bool>::value>> - static std::true_type test(int); - template static std::false_type test(...); +template +struct HasClassofImpl { + template())), bool>::value>> + static std::true_type test(int); + template + static std::false_type test(...); - static constexpr bool value = decltype(test(0))::value; + static constexpr bool value = decltype(test(0))::value; }; -} // namespace detail +}// namespace detail -template struct CastingTraits { - template - static auto RuntimeTypeCheck(const Base &val) - -> enable_if_t::value, bool> { - return T::classof(val); - } +template +struct CastingTraits { + template + static auto RuntimeTypeCheck(const Base &val) -> enable_if_t::value, bool> + { + return T::classof(val); + } - template - static auto RuntimeTypeCheck(const Base &val) - -> enable_if_t::value, bool> { - return dynamic_cast(&val) != nullptr; - } + template + static auto RuntimeTypeCheck(const Base &val) -> enable_if_t::value, bool> + { + return dynamic_cast(&val) != nullptr; + } }; class Castable { - class Dummy {}; + class Dummy {}; public: - friend void SetRuntimeType(Castable *ptr, int type, Dummy = {}) { - ptr->type_ = type; - } + friend void SetRuntimeType(Castable *ptr, int type, Dummy = {}) { ptr->type_ = type; } - friend int GetRuntimeType(const Castable &val, Dummy = {}) { - return val.type_; - } + friend int GetRuntimeType(const Castable &val, Dummy = {}) { return val.type_; } protected: - template friend struct CastingTraits; - ~Castable() = default; + template + friend struct CastingTraits; + ~Castable() = default; - template void SetRuntimeTypeTo() { - SetRuntimeType(this, GetRuntimeTypeId()); - } + template + void SetRuntimeTypeTo() + { + SetRuntimeType(this, GetRuntimeTypeId()); + } - template int GetRuntimeType() const { - return GetRuntimeTypeId(); - } + template + int GetRuntimeType() const + { + return GetRuntimeTypeId(); + } - template static int GetRuntimeTypeId() { - static const int value = internal::IndexAlloc::For()->Next(); - return value; - } + template + static int GetRuntimeTypeId() + { + static const int value = internal::IndexAlloc::For()->Next(); + return value; + } private: - std::int_fast32_t type_ = GetRuntimeTypeId(); + std::int_fast32_t type_ = GetRuntimeTypeId(); }; // ExactMatchCastable class ExactMatchCastable : public Castable { protected: - ~ExactMatchCastable() = default; + ~ExactMatchCastable() = default; }; -template -struct CastingTraits< - T, enable_if_t::value>> { - static bool RuntimeTypeCheck(const ExactMatchCastable &object) { - return GetRuntimeType(object) == Castable::GetRuntimeTypeId(); - } +template +struct CastingTraits::value>> { + static bool RuntimeTypeCheck(const ExactMatchCastable &object) + { + return GetRuntimeType(object) == Castable::GetRuntimeTypeId(); + } }; namespace casting { namespace detail { -template ::value && - !std::is_same::value>> -inline bool RuntimeTypeCheck(const Base &val) { - return CastingTraits::RuntimeTypeCheck(val); +template::value && !std::is_same::value>> +inline bool +RuntimeTypeCheck(const Base &val) +{ + return CastingTraits::RuntimeTypeCheck(val); } -template inline bool RuntimeTypeCheck(const T &val) { - return true; +template +inline bool +RuntimeTypeCheck(const T &val) +{ + return true; } -template -using casted_type_t = - internal::conditional_t::value, const T *, T *>; +template +using casted_type_t = internal::conditional_t::value, const T *, T *>; -template -auto ReallyCastable(Base *ptr) - -> enable_if_t::value, bool> { - return dynamic_cast(ptr) != nullptr; -} -template -auto ReallyCastable(Base *ptr) - -> enable_if_t::value, bool> { - return typeid(T) == typeid(*ptr); +template +auto +ReallyCastable(Base *ptr) -> enable_if_t::value, bool> +{ + return dynamic_cast(ptr) != nullptr; } -template void InvalidCast(Base *ptr) { - if (ReallyCastable(ptr)) { - TILE_LOG_FATAL( - "Casting to type [{}] failed. However, the C++ runtime reports that " - "you're indeed casting to the (right) runtime type of the object. This " - "can happen when either: 1) you haven't initialize object's runtime " - "type correctly (e.g. via `SetRuntimeTypeTo` if you're inheriting from " - "`ExactMatchCast`), or 2) the implementation of your `classof` is " - "incorrect.", - GetTypeName()); - } else { - TILE_LOG_FATAL( - "Invalid cast: Runtime type [{}] expected, got [{}]. If you believe " - "this is an error, check if your `classof` is implemented correctly", - GetTypeName(), GetTypeName(*ptr)); - } - TILE_UNREACHABLE(""); +template +auto +ReallyCastable(Base *ptr) -> enable_if_t::value, bool> +{ + return typeid(T) == typeid(*ptr); } -} // namespace detail -} // namespace casting - -template ::value>> -inline bool isa(const Base &val) { - return casting::detail::RuntimeTypeCheck(val); +template +void +InvalidCast(Base *ptr) +{ + if (ReallyCastable(ptr)) { + TILE_LOG_FATAL("Casting to type [{}] failed. However, the C++ runtime reports that " + "you're indeed casting to the (right) runtime type of the object. This " + "can happen when either: 1) you haven't initialize object's runtime " + "type correctly (e.g. via `SetRuntimeTypeTo` if you're inheriting from " + "`ExactMatchCast`), or 2) the implementation of your `classof` is " + "incorrect.", + GetTypeName()); + } else { + TILE_LOG_FATAL("Invalid cast: Runtime type [{}] expected, got [{}]. If you believe " + "this is an error, check if your `classof` is implemented correctly", + GetTypeName(), GetTypeName(*ptr)); + } + TILE_UNREACHABLE(""); } -template inline bool isa(const Base *val) { - return isa(*val); +}// namespace detail +}// namespace casting + +template::value>> +inline bool +isa(const Base &val) +{ + return casting::detail::RuntimeTypeCheck(val); } -template > -R dyn_cast(Base *ptr) { - return isa(ptr) ? static_cast(ptr) : nullptr; +template +inline bool +isa(const Base *val) +{ + return isa(*val); } -template ::value>> +template> +R +dyn_cast(Base *ptr) +{ + return isa(ptr) ? static_cast(ptr) : nullptr; +} + +template::value>> inline auto -dyn_cast(Base &val) -> decltype(dyn_cast(std::declval())) { - return dyn_cast(&val); +dyn_cast(Base &val) -> decltype(dyn_cast(std::declval())) +{ + return dyn_cast(&val); } -template -inline auto dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast(ptr)) { - return ptr ? dyn_cast(ptr) : nullptr; +template +inline auto +dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast(ptr)) +{ + return ptr ? dyn_cast(ptr) : nullptr; } -template > -inline R cast(Base *ptr) { - if (TILE_LIKELY(isa(ptr))) { - return static_cast(ptr); - } - casting::detail::InvalidCast(ptr); - return nullptr; +template> +inline R +cast(Base *ptr) +{ + if (TILE_LIKELY(isa(ptr))) { return static_cast(ptr); } + casting::detail::InvalidCast(ptr); + return nullptr; } -template ::value>> -inline auto cast(Base &val) -> decltype(cast(std::declval())) { - return cast(&val); +template::value>> +inline auto +cast(Base &val) -> decltype(cast(std::declval())) +{ + return cast(&val); } -template -inline auto cast_or_null(Base *ptr) -> decltype(cast(ptr)) { - return ptr ? cast(ptr) : nullptr; +template +inline auto +cast_or_null(Base *ptr) -> decltype(cast(ptr)) +{ + return ptr ? cast(ptr) : nullptr; } -} // namespace tile +}// namespace tile -#endif // TILE_BASE_CASTING_H +#endif// TILE_BASE_CASTING_H diff --git a/tile/base/casting_benchmark.cc b/tile/base/casting_benchmark.cc index 9b08b07..830e7f3 100644 --- a/tile/base/casting_benchmark.cc +++ b/tile/base/casting_benchmark.cc @@ -7,64 +7,63 @@ namespace tile { struct Base { - virtual ~Base() = default; - enum { kA, kB, kC [[maybe_unused]] } type; + virtual ~Base() = default; + + enum { kA, kB, kC [[maybe_unused]] } type; }; struct A : Base { - A() { type = kA; } + A() { type = kA; } - static bool classof(const Base &val) { - return val.type == kA || val.type == kB; - } + static bool classof(const Base &val) { return val.type == kA || val.type == kB; } }; struct B : A { - B() { type = kB; } + B() { type = kB; } - static bool classof(const Base &val) { return val.type == kB; } + static bool classof(const Base &val) { return val.type == kB; } }; -auto pb = make_unique(); +auto pb = make_unique(); Base *ptr = pb.get(); volatile A *converted_ptr; struct C1 : ExactMatchCastable {}; struct C2 : C1 { - C2() { SetRuntimeType(this, GetRuntimeTypeId()); } + C2() { SetRuntimeType(this, GetRuntimeTypeId()); } }; struct C3 : C1 { - C3() { SetRuntimeType(this, GetRuntimeTypeId()); } + C3() { SetRuntimeType(this, GetRuntimeTypeId()); } }; auto pc2 = make_unique(); -C1 *pc1 = pc2.get(); +C1 *pc1 = pc2.get(); volatile C3 *pc3; -void Benchmark_BuiltinDynamicCast(benchmark::State &state) { - while (state.KeepRunning()) { - converted_ptr = dynamic_cast(ptr); - } +void +Benchmark_BuiltinDynamicCast(benchmark::State &state) +{ + while (state.KeepRunning()) { converted_ptr = dynamic_cast(ptr); } } BENCHMARK(Benchmark_BuiltinDynamicCast); -void Benchmark_DynCast(benchmark::State &state) { - while (state.KeepRunning()) { - converted_ptr = dyn_cast(ptr); - } +void +Benchmark_DynCast(benchmark::State &state) +{ + while (state.KeepRunning()) { converted_ptr = dyn_cast(ptr); } } BENCHMARK(Benchmark_DynCast); -void Benchmark_ExactMatchCastableDynCast(benchmark::State &state) { - while (state.KeepRunning()) { - pc3 = dyn_cast(pc1); - } +void +Benchmark_ExactMatchCastableDynCast(benchmark::State &state) +{ + while (state.KeepRunning()) { pc3 = dyn_cast(pc1); } } BENCHMARK(Benchmark_ExactMatchCastableDynCast); -} // namespace tile +}// namespace tile diff --git a/tile/base/casting_test.cc b/tile/base/casting_test.cc index 1d9985e..9e2ce47 100644 --- a/tile/base/casting_test.cc +++ b/tile/base/casting_test.cc @@ -9,95 +9,99 @@ namespace tile { struct Base { - enum { kA, kB, kC } type; + enum { kA, kB, kC } type; }; struct A : Base { - A() { type = kA; } + A() { type = kA; } - static bool classof(const Base &val) { - return val.type == kA || val.type == kB; - } + static bool classof(const Base &val) { return val.type == kA || val.type == kB; } }; struct B : A { - B() { type = kB; } + B() { type = kB; } - static bool classof(const Base &val) { return val.type == kB; } + static bool classof(const Base &val) { return val.type == kB; } }; struct C : Base { - C() { type = kC; } + C() { type = kC; } - static bool classof(const Base &val) { return val.type == kC; } + static bool classof(const Base &val) { return val.type == kC; } }; struct C1 : ExactMatchCastable {}; struct C2 : C1 { - C2() { SetRuntimeType(this, GetRuntimeTypeId()); } + C2() { SetRuntimeType(this, GetRuntimeTypeId()); } }; struct C3 : C1 { - C3() { SetRuntimeType(this, GetRuntimeTypeId()); } + C3() { SetRuntimeType(this, GetRuntimeTypeId()); } }; -TEST(CastingDeathTest, InvalidCast) { - auto pa = make_unique(); - ASSERT_DEATH(cast(pa.get()), "Invalid cast"); +TEST(CastingDeathTest, InvalidCast) +{ + auto pa = make_unique(); + ASSERT_DEATH(cast(pa.get()), "Invalid cast"); } -TEST(Casting, Nullptr) { - B *pb = nullptr; - ASSERT_EQ(nullptr, dyn_cast_or_null(pb)); - ASSERT_EQ(nullptr, cast_or_null(pb)); +TEST(Casting, Nullptr) +{ + B *pb = nullptr; + ASSERT_EQ(nullptr, dyn_cast_or_null(pb)); + ASSERT_EQ(nullptr, cast_or_null(pb)); } -TEST(Casting, DownCastFailure) { - auto pa = make_unique(); - ASSERT_EQ(nullptr, dyn_cast(pa.get())); +TEST(Casting, DownCastFailure) +{ + auto pa = make_unique(); + ASSERT_EQ(nullptr, dyn_cast(pa.get())); } -TEST(Casting, Cast) { - auto pb = make_unique(); - Base *ptr = pb.get(); +TEST(Casting, Cast) +{ + auto pb = make_unique(); + Base *ptr = pb.get(); - ASSERT_NE(nullptr, dyn_cast(ptr)); - ASSERT_NE(nullptr, dyn_cast(ptr)); - ASSERT_NE(nullptr, dyn_cast(ptr)); + ASSERT_NE(nullptr, dyn_cast(ptr)); + ASSERT_NE(nullptr, dyn_cast(ptr)); + ASSERT_NE(nullptr, dyn_cast(ptr)); - ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. - ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. - ASSERT_NE(nullptr, cast(*ptr)); // Casting pointer. - ASSERT_NE(nullptr, cast(*ptr)); // Casting pointer. + ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. + ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. + ASSERT_NE(nullptr, cast(*ptr));// Casting pointer. + ASSERT_NE(nullptr, cast(*ptr));// Casting pointer. - ASSERT_NE(nullptr, dyn_cast(pb.get())); // Cast to self. - ASSERT_EQ(nullptr, dyn_cast(ptr)); // Cast failure. + ASSERT_NE(nullptr, dyn_cast(pb.get()));// Cast to self. + ASSERT_EQ(nullptr, dyn_cast(ptr)); // Cast failure. } -TEST(Casting, CastWithConst) { - auto pb = make_unique(); - const Base *ptr = pb.get(); +TEST(Casting, CastWithConst) +{ + auto pb = make_unique(); + const Base *ptr = pb.get(); - ASSERT_NE(nullptr, dyn_cast(ptr)); - ASSERT_NE(nullptr, dyn_cast(ptr)); - ASSERT_NE(nullptr, dyn_cast(ptr)); + ASSERT_NE(nullptr, dyn_cast(ptr)); + ASSERT_NE(nullptr, dyn_cast(ptr)); + ASSERT_NE(nullptr, dyn_cast(ptr)); - ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. - ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. - ASSERT_NE(nullptr, cast(*ptr)); // Casting pointer. - ASSERT_NE(nullptr, cast(*ptr)); // Casting pointer. + ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. + ASSERT_NE(nullptr, cast(ptr)); // Casting pointer. + ASSERT_NE(nullptr, cast(*ptr));// Casting pointer. + ASSERT_NE(nullptr, cast(*ptr));// Casting pointer. - ASSERT_NE(nullptr, dyn_cast(pb.get())); // Cast to self. - ASSERT_EQ(nullptr, dyn_cast(ptr)); // Cast failure. + ASSERT_NE(nullptr, dyn_cast(pb.get()));// Cast to self. + ASSERT_EQ(nullptr, dyn_cast(ptr)); // Cast failure. } -TEST(Casting, ExactMatchCastable) { - auto pc2 = make_unique(); - C1 *p = pc2.get(); - ASSERT_NE(nullptr, dyn_cast(p)); // Success. - ASSERT_NE(nullptr, dyn_cast(p)); // Success. - ASSERT_EQ(nullptr, dyn_cast(p)); // Failure. +TEST(Casting, ExactMatchCastable) +{ + auto pc2 = make_unique(); + C1 *p = pc2.get(); + ASSERT_NE(nullptr, dyn_cast(p));// Success. + ASSERT_NE(nullptr, dyn_cast(p));// Success. + ASSERT_EQ(nullptr, dyn_cast(p));// Failure. } -} // namespace tile +}// namespace tile diff --git a/tile/base/chrono.cc b/tile/base/chrono.cc index 1cba8f4..7e2c878 100644 --- a/tile/base/chrono.cc +++ b/tile/base/chrono.cc @@ -15,132 +15,150 @@ namespace tile { namespace { -template struct Epoch {}; -template <> struct Epoch { - static constexpr auto value = - std::chrono::duration_cast( - std::chrono::seconds(0)); +template +struct Epoch {}; + +template<> +struct Epoch { + static constexpr auto value = std::chrono::duration_cast(std::chrono::seconds(0)); }; -template <> struct Epoch { - static constexpr auto value = - std::chrono::duration_cast( - std::chrono::seconds(1716543273)); +template<> +struct Epoch { + static constexpr auto value = + std::chrono::duration_cast(std::chrono::seconds(1716543273)); }; -template -inline typename Clock::time_point ReadClock(clockid_t type) { - timespec ts; - clock_gettime(type, &ts); - const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec; - auto duration = std::chrono::duration_cast( - std::chrono::nanoseconds(ns)); - return typename Clock::time_point(duration); +template +inline typename Clock::time_point +ReadClock(clockid_t type) +{ + timespec ts; + clock_gettime(type, &ts); + const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec; + auto duration = std::chrono::duration_cast(std::chrono::nanoseconds(ns)); + return typename Clock::time_point(duration); } -void Sleep(long ns) { - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = (ns + 999) / 1000; +void +Sleep(long ns) +{ + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = (ns + 999) / 1000; - select(0, NULL, NULL, NULL, &timeout); + select(0, NULL, NULL, NULL, &timeout); } -} // namespace +}// namespace + namespace detail { namespace chrono { -struct alignas(hardware_destructive_interference_size) - AsynchronouslyUpdatedTimestamps { - std::atomic steady_clock_time_since_epoch; - std::atomic system_clock_time_since_epoch; +struct alignas(hardware_destructive_interference_size) AsynchronouslyUpdatedTimestamps { + std::atomic steady_clock_time_since_epoch; + std::atomic system_clock_time_since_epoch; }; + static AsynchronouslyUpdatedTimestamps async_updated_timestamps; -void UpdateCoarseTimestamps() { - async_updated_timestamps.steady_clock_time_since_epoch.store( - ReadSteadyClock().time_since_epoch(), - // std::chrono::steady_clock::now().time_since_epoch(), - std::memory_order_relaxed); - async_updated_timestamps.system_clock_time_since_epoch.store( - ReadSystemClock().time_since_epoch(), - // std::chrono::system_clock::now().time_since_epoch(), - std::memory_order_relaxed); +void +UpdateCoarseTimestamps() +{ + async_updated_timestamps.steady_clock_time_since_epoch.store( + ReadSteadyClock().time_since_epoch(), + // std::chrono::steady_clock::now().time_since_epoch(), + std::memory_order_relaxed); + async_updated_timestamps.system_clock_time_since_epoch.store( + ReadSystemClock().time_since_epoch(), + // std::chrono::system_clock::now().time_since_epoch(), + std::memory_order_relaxed); } + constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy; -CoarseClockInitializer *CoarseClockInitializer::Instance() { - static NeverDestroyedSingleton cci; - return cci.Get(); + +CoarseClockInitializer * +CoarseClockInitializer::Instance() +{ + static NeverDestroyedSingleton cci; + return cci.Get(); } -void CoarseClockInitializer::Start() { - if (!internal::TestAndSet(running_, false, true)) { - TILE_LOG_WARNING("CoarseClockInitializer is already running"); - return; - } - - UpdateCoarseTimestamps(); - - worker_ = make_unique([this] { - const auto accuracy_as_ns = - std::chrono::duration_cast(kAccuracy).count(); - TILE_CHECK_GE(accuracy_as_ns, 2000, - "accuracy is too small, MUST GE 2000ns, current is {}", - kAccuracy); - - while (running_.load(std::memory_order_relaxed)) { - // std::this_thread::sleep_for(std::chrono::nanoseconds(500)); - UpdateCoarseTimestamps(); - // Sleep(accuracy_as_ns / 2); - std::this_thread::sleep_for(kAccuracy / 2); +void +CoarseClockInitializer::Start() +{ + if (!internal::TestAndSet(running_, false, true)) { + TILE_LOG_WARNING("CoarseClockInitializer is already running"); + return; } - }); + + UpdateCoarseTimestamps(); + + worker_ = make_unique([this] { + const auto accuracy_as_ns = std::chrono::duration_cast(kAccuracy).count(); + TILE_CHECK_GE(accuracy_as_ns, 2000, "accuracy is too small, MUST GE 2000ns, current is {}", kAccuracy); + + while (running_.load(std::memory_order_relaxed)) { + // std::this_thread::sleep_for(std::chrono::nanoseconds(500)); + UpdateCoarseTimestamps(); + // Sleep(accuracy_as_ns / 2); + std::this_thread::sleep_for(kAccuracy / 2); + } + }); } -void CoarseClockInitializer::Stop() { - running_.store(false, std::memory_order_relaxed); +void +CoarseClockInitializer::Stop() +{ + running_.store(false, std::memory_order_relaxed); } -void CoarseClockInitializer::Join() { - if (worker_) { - worker_->join(); - worker_.reset(); - } +void +CoarseClockInitializer::Join() +{ + if (worker_) { + worker_->join(); + worker_.reset(); + } } CoarseClockInitializer::CoarseClockInitializer() {} CoarseClockInitializer::~CoarseClockInitializer() {} -} // namespace chrono -} // namespace detail +}// namespace chrono +}// namespace detail -std::chrono::steady_clock::time_point ReadSteadyClock() { - // return ReadClock(CLOCK_MONOTONIC); - return std::chrono::steady_clock::now(); -} -std::chrono::system_clock::time_point ReadSystemClock() { - // return ReadClock(CLOCK_REALTIME); - return std::chrono::system_clock::now(); +std::chrono::steady_clock::time_point +ReadSteadyClock() +{ + // return ReadClock(CLOCK_MONOTONIC); + return std::chrono::steady_clock::now(); } -std::chrono::steady_clock::time_point ReadCoarseSteadyClock() { - auto coarse_duration = - detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch - .load(std::memory_order_relaxed); - auto target_duration = - std::chrono::duration_cast( - coarse_duration); - return std::chrono::steady_clock::time_point(target_duration); +std::chrono::system_clock::time_point +ReadSystemClock() +{ + // return ReadClock(CLOCK_REALTIME); + return std::chrono::system_clock::now(); } -std::chrono::system_clock::time_point ReadCoarseSystemClock() { - auto coarse_duration = - detail::chrono::async_updated_timestamps.system_clock_time_since_epoch - .load(std::memory_order_relaxed); - auto target_duration = - std::chrono::duration_cast( - coarse_duration); - - return std::chrono::system_clock::time_point(target_duration); +std::chrono::steady_clock::time_point +ReadCoarseSteadyClock() +{ + auto coarse_duration = + detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch.load(std::memory_order_relaxed); + auto target_duration = std::chrono::duration_cast(coarse_duration); + return std::chrono::steady_clock::time_point(target_duration); } -} // namespace tile + +std::chrono::system_clock::time_point +ReadCoarseSystemClock() +{ + auto coarse_duration = + detail::chrono::async_updated_timestamps.system_clock_time_since_epoch.load(std::memory_order_relaxed); + + auto target_duration = std::chrono::duration_cast(coarse_duration); + + return std::chrono::system_clock::time_point(target_duration); +} +}// namespace tile diff --git a/tile/base/chrono.h b/tile/base/chrono.h index 7107782..415ff10 100644 --- a/tile/base/chrono.h +++ b/tile/base/chrono.h @@ -17,26 +17,26 @@ namespace chrono { class CoarseClockInitializer { public: - static constexpr auto kAccuracy = std::chrono::microseconds(500); - static CoarseClockInitializer *Instance(); + static constexpr auto kAccuracy = std::chrono::microseconds(500); + static CoarseClockInitializer *Instance(); - // for `tile::Start()` - void Start(); - void Stop(); - void Join(); + // for `tile::Start()` + void Start(); + void Stop(); + void Join(); private: - ~CoarseClockInitializer(); - CoarseClockInitializer(); - friend NeverDestroyedSingleton; + ~CoarseClockInitializer(); + CoarseClockInitializer(); + friend NeverDestroyedSingleton; private: - std::unique_ptr worker_; - std::atomic running_{false}; + std::unique_ptr worker_; + std::atomic running_{false}; }; -} // namespace chrono -} // namespace detail +}// namespace chrono +}// namespace detail std::chrono::steady_clock::time_point ReadSteadyClock(); std::chrono::system_clock::time_point ReadSystemClock(); @@ -45,10 +45,13 @@ std::chrono::system_clock::time_point ReadSystemClock(); std::chrono::steady_clock::time_point ReadCoarseSteadyClock(); // accuracy: 10us std::chrono::system_clock::time_point ReadCoarseSystemClock(); -inline std::int64_t ReadUnixTimestamp() { - return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1); + +inline std::int64_t +ReadUnixTimestamp() +{ + return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1); } -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_CHRONO_H +#endif// _TILE_BASE_CHRONO_H diff --git a/tile/base/chrono_benchmark.cc b/tile/base/chrono_benchmark.cc index 7e04423..9b0819e 100644 --- a/tile/base/chrono_benchmark.cc +++ b/tile/base/chrono_benchmark.cc @@ -8,63 +8,67 @@ namespace tile { -void Benchmark_GetTimeOfDay(benchmark::State &state) { - while (state.KeepRunning()) { - struct timeval tv; - gettimeofday(&tv, nullptr); - } +void +Benchmark_GetTimeOfDay(benchmark::State &state) +{ + while (state.KeepRunning()) { + struct timeval tv; + gettimeofday(&tv, nullptr); + } } BENCHMARK(Benchmark_GetTimeOfDay); -void Benchmark_StdSteadyClock(benchmark::State &state) { - while (state.KeepRunning()) { - (void)std::chrono::steady_clock::now(); - } +void +Benchmark_StdSteadyClock(benchmark::State &state) +{ + while (state.KeepRunning()) { (void) std::chrono::steady_clock::now(); } } BENCHMARK(Benchmark_StdSteadyClock); -void Benchmark_StdSystemClock(benchmark::State &state) { - while (state.KeepRunning()) { - (void)std::chrono::system_clock::now(); - } +void +Benchmark_StdSystemClock(benchmark::State &state) +{ + while (state.KeepRunning()) { (void) std::chrono::system_clock::now(); } } BENCHMARK(Benchmark_StdSystemClock); -void Benchmark_ReadSteadyClock(benchmark::State &state) { - while (state.KeepRunning()) { - ReadSteadyClock(); - } +void +Benchmark_ReadSteadyClock(benchmark::State &state) +{ + while (state.KeepRunning()) { ReadSteadyClock(); } } BENCHMARK(Benchmark_ReadSteadyClock); -void Benchmark_ReadSystemClock(benchmark::State &state) { - while (state.KeepRunning()) { - ReadSystemClock(); - } +void +Benchmark_ReadSystemClock(benchmark::State &state) +{ + while (state.KeepRunning()) { ReadSystemClock(); } } BENCHMARK(Benchmark_ReadSystemClock); -void Benchmark_ReadCoarseSteadyClock(benchmark::State &state) { - while (state.KeepRunning()) { - // ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch` - // ... <+30>: mov (%rax),%rax - ReadCoarseSteadyClock(); - } +void +Benchmark_ReadCoarseSteadyClock(benchmark::State &state) +{ + while (state.KeepRunning()) { + // ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch` + // ... <+30>: mov (%rax),%rax + ReadCoarseSteadyClock(); + } } BENCHMARK(Benchmark_ReadCoarseSteadyClock); -void Benchmark_ReadCoarseSystemClock(benchmark::State &state) { - while (state.KeepRunning()) { - ReadCoarseSystemClock(); - } +void +Benchmark_ReadCoarseSystemClock(benchmark::State &state) +{ + while (state.KeepRunning()) { ReadCoarseSystemClock(); } } BENCHMARK(Benchmark_ReadCoarseSystemClock); -} // namespace tile +}// namespace tile diff --git a/tile/base/chrono_test.cc b/tile/base/chrono_test.cc index 524fbce..6bee577 100644 --- a/tile/base/chrono_test.cc +++ b/tile/base/chrono_test.cc @@ -7,42 +7,38 @@ namespace tile { static constexpr auto one_ms = std::chrono::milliseconds(1); static constexpr auto kTestN = 1000; -long AvageTime(std::function f, std::size_t n = kTestN) { - long double total = 0; - for (std::size_t i = 0; i != n; ++i) { - total += 1.0f / n * f(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - return static_cast(total); +long +AvageTime(std::function f, std::size_t n = kTestN) +{ + long double total = 0; + for (std::size_t i = 0; i != n; ++i) { + total += 1.0f / n * f(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return static_cast(total); } -TEST(SystemClock, Compare) { - auto diff = AvageTime([] { - return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms; - }); - ASSERT_NEAR(diff, 0, 5); +TEST(SystemClock, Compare) +{ + auto diff = AvageTime([] { return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms; }); + ASSERT_NEAR(diff, 0, 5); } -TEST(SteadyClock, Compare) { - auto diff = AvageTime([] { - return (ReadSteadyClock() - std::chrono::steady_clock::now()) / one_ms; - }); - ASSERT_NEAR(diff, 0, 5); +TEST(SteadyClock, Compare) +{ + auto diff = AvageTime([] { return (ReadSteadyClock() - std::chrono::steady_clock::now()) / one_ms; }); + ASSERT_NEAR(diff, 0, 5); } -TEST(CoarseSystemClock, Compare) { - auto diff = AvageTime([] { - return (ReadCoarseSystemClock() - std::chrono::system_clock::now()) / - one_ms; - }); - ASSERT_NEAR(diff, 0, kTestN); +TEST(CoarseSystemClock, Compare) +{ + auto diff = AvageTime([] { return (ReadCoarseSystemClock() - std::chrono::system_clock::now()) / one_ms; }); + ASSERT_NEAR(diff, 0, kTestN); } -TEST(CoarseSteadyClock, Compare) { - auto diff = AvageTime([] { - return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) / - one_ms; - }); - ASSERT_NEAR(diff, 0, kTestN); +TEST(CoarseSteadyClock, Compare) +{ + auto diff = AvageTime([] { return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) / one_ms; }); + ASSERT_NEAR(diff, 0, kTestN); } -} // namespace tile +}// namespace tile diff --git a/tile/base/compression.cc b/tile/base/compression.cc index da0b1f2..54977ef 100644 --- a/tile/base/compression.cc +++ b/tile/base/compression.cc @@ -4,89 +4,96 @@ namespace tile { -std::unique_ptr MakeDecompressor(Slice name) { - return decompressor_registry.TryNew(name); +std::unique_ptr +MakeDecompressor(Slice name) +{ + return decompressor_registry.TryNew(name); } -std::unique_ptr MakeCompressor(Slice name) { - return compressor_registry.TryNew(name); +std::unique_ptr +MakeCompressor(Slice name) +{ + return compressor_registry.TryNew(name); } -std::optional Compress(Compressor *compressor, - const NoncontiguousBuffer &body) { - NoncontiguousBufferBuilder builder; - if (!Compress(compressor, body, &builder)) { - return std::nullopt; - } - return builder.DestructiveGet(); -} -std::optional Compress(Compressor *compressor, - Slice body) { - NoncontiguousBufferBuilder builder; - if (!Compress(compressor, body, &builder)) { - return std::nullopt; - } - return builder.DestructiveGet(); +std::optional +Compress(Compressor *compressor, const NoncontiguousBuffer &body) +{ + NoncontiguousBufferBuilder builder; + if (!Compress(compressor, body, &builder)) { return std::nullopt; } + return builder.DestructiveGet(); } -bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb, - NoncontiguousBufferBuilder *builder) { - if (!compressor) { - TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr"); - return false; - } - - NoncontiguousBufferCompressionOutputStream out(builder); - return compressor->Compress(nb, &out); +std::optional +Compress(Compressor *compressor, Slice body) +{ + NoncontiguousBufferBuilder builder; + if (!Compress(compressor, body, &builder)) { return std::nullopt; } + return builder.DestructiveGet(); } -bool Compress(Compressor *compressor, Slice body, - NoncontiguousBufferBuilder *builder) { - if (!compressor) { - TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr"); - return false; - } +bool +Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder) +{ + if (!compressor) { + TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr"); + return false; + } - NoncontiguousBufferCompressionOutputStream out(builder); - return compressor->Compress(body.data(), body.size(), &out); + NoncontiguousBufferCompressionOutputStream out(builder); + return compressor->Compress(nb, &out); } -std::optional Decompress(Decompressor *decompressor, - const NoncontiguousBuffer &body) { - NoncontiguousBufferBuilder builder; - if (!Decompress(decompressor, body, &builder)) { - return std::nullopt; - } - return builder.DestructiveGet(); -} -std::optional Decompress(Decompressor *decompressor, - Slice body) { - NoncontiguousBufferBuilder builder; - if (!Decompress(decompressor, body, &builder)) { - return std::nullopt; - } - return builder.DestructiveGet(); +bool +Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder) +{ + if (!compressor) { + TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr"); + return false; + } + + NoncontiguousBufferCompressionOutputStream out(builder); + return compressor->Compress(body.data(), body.size(), &out); } -bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, - NoncontiguousBufferBuilder *builder) { - if (!decompressor) { - TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr"); - return false; - } - - NoncontiguousBufferCompressionOutputStream out(builder); - return decompressor->Decompress(nb, &out); -} -bool Decompress(Decompressor *decompressor, Slice body, - NoncontiguousBufferBuilder *builder) { - if (!decompressor) { - TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr"); - return false; - } - - NoncontiguousBufferCompressionOutputStream out(builder); - return decompressor->Decompress(body.data(), body.size(), &out); +std::optional +Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body) +{ + NoncontiguousBufferBuilder builder; + if (!Decompress(decompressor, body, &builder)) { return std::nullopt; } + return builder.DestructiveGet(); } -} // namespace tile +std::optional +Decompress(Decompressor *decompressor, Slice body) +{ + NoncontiguousBufferBuilder builder; + if (!Decompress(decompressor, body, &builder)) { return std::nullopt; } + return builder.DestructiveGet(); +} + +bool +Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder) +{ + if (!decompressor) { + TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr"); + return false; + } + + NoncontiguousBufferCompressionOutputStream out(builder); + return decompressor->Decompress(nb, &out); +} + +bool +Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder) +{ + if (!decompressor) { + TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr"); + return false; + } + + NoncontiguousBufferCompressionOutputStream out(builder); + return decompressor->Decompress(body.data(), body.size(), &out); +} + +}// namespace tile diff --git a/tile/base/compression.h b/tile/base/compression.h index e325715..cbc1c99 100644 --- a/tile/base/compression.h +++ b/tile/base/compression.h @@ -11,26 +11,19 @@ namespace tile { std::unique_ptr MakeDecompressor(Slice name); std::unique_ptr MakeCompressor(Slice name); -std::optional Compress(Compressor *compressor, - const NoncontiguousBuffer &nb); +std::optional Compress(Compressor *compressor, const NoncontiguousBuffer &nb); std::optional Compress(Compressor *compressor, Slice body); -bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb, - NoncontiguousBufferBuilder *builder); +bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder); -bool Compress(Compressor *compressor, Slice body, - NoncontiguousBufferBuilder *builder); +bool Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder); -std::optional Decompress(Decompressor *decompressor, - const NoncontiguousBuffer &body); -std::optional Decompress(Decompressor *decompressor, - Slice body); +std::optional Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body); +std::optional Decompress(Decompressor *decompressor, Slice body); -bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, - NoncontiguousBufferBuilder *builder); -bool Decompress(Decompressor *decompressor, Slice body, - NoncontiguousBufferBuilder *builder); +bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder); +bool Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder); -} // namespace tile +}// namespace tile -#endif // TILE_BASE_COMPRESSION_H +#endif// TILE_BASE_COMPRESSION_H diff --git a/tile/base/compression/compression.cc b/tile/base/compression/compression.cc index 35d6702..072bc7f 100644 --- a/tile/base/compression/compression.cc +++ b/tile/base/compression/compression.cc @@ -4,25 +4,33 @@ namespace tile { TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(compressor_registry, Compressor); TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor); -TestCompressionOutputStream::TestCompressionOutputStream(std::string *s, - std::size_t every_size) - : buffer_(s), every_size_(every_size) {} +TestCompressionOutputStream::TestCompressionOutputStream(std::string *s, std::size_t every_size) + : buffer_(s), + every_size_(every_size) +{} -bool TestCompressionOutputStream::Next(void **data, - std::size_t *size) noexcept { - if (buffer_->size() < using_bytes_ + every_size_) { - buffer_->resize(using_bytes_ + every_size_); - } +bool +TestCompressionOutputStream::Next(void **data, std::size_t *size) noexcept +{ + if (buffer_->size() < using_bytes_ + every_size_) { buffer_->resize(using_bytes_ + every_size_); } - *data = internal::RemoveConstPtr(buffer_->data()) + using_bytes_; - *size = every_size_; + *data = internal::RemoveConstPtr(buffer_->data()) + using_bytes_; + *size = every_size_; - using_bytes_ += every_size_; - return true; + using_bytes_ += every_size_; + return true; } -void TestCompressionOutputStream::BackUp(std::size_t count) noexcept { - using_bytes_ -= count; -} -void TestCompressionOutputStream::Flush() { buffer_->resize(using_bytes_); } -} // namespace tile +void +TestCompressionOutputStream::BackUp(std::size_t count) noexcept +{ + using_bytes_ -= count; +} + +void +TestCompressionOutputStream::Flush() +{ + buffer_->resize(using_bytes_); +} + +}// namespace tile diff --git a/tile/base/compression/compression.h b/tile/base/compression/compression.h index c33d5e2..1d4546d 100644 --- a/tile/base/compression/compression.h +++ b/tile/base/compression/compression.h @@ -9,57 +9,50 @@ namespace tile { class CompressionOutputStream { public: - virtual ~CompressionOutputStream() = default; - // Get a buffer to write to. The buffer may be backed by multiple - virtual bool Next(void **data, std::size_t *size) noexcept = 0; - virtual void BackUp(std::size_t count) noexcept = 0; + virtual ~CompressionOutputStream() = default; + // Get a buffer to write to. The buffer may be backed by multiple + virtual bool Next(void **data, std::size_t *size) noexcept = 0; + virtual void BackUp(std::size_t count) noexcept = 0; }; class Compressor { public: - virtual ~Compressor() = default; - virtual bool Compress(const void *src, std::size_t size, - CompressionOutputStream *out) = 0; - virtual bool Compress(const NoncontiguousBuffer &src, - CompressionOutputStream *out) = 0; + virtual ~Compressor() = default; + virtual bool Compress(const void *src, std::size_t size, CompressionOutputStream *out) = 0; + virtual bool Compress(const NoncontiguousBuffer &src, CompressionOutputStream *out) = 0; }; class Decompressor { public: - ~Decompressor() = default; - virtual bool Decompress(const void *src, std::size_t size, - CompressionOutputStream *out) = 0; - virtual bool Decompress(const NoncontiguousBuffer &src, - CompressionOutputStream *out) = 0; + ~Decompressor() = default; + virtual bool Decompress(const void *src, std::size_t size, CompressionOutputStream *out) = 0; + virtual bool Decompress(const NoncontiguousBuffer &src, CompressionOutputStream *out) = 0; }; TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(compressor_registry, Compressor); TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor); -} // namespace tile +}// namespace tile -#define TILE_COMPRESSION_REGISTER_COMPRESSOR(Name, Implementation) \ - TILE_REGISTER_CLASS_DEPENDENCY(::tile::compressor_registry, Name, \ - Implementation) -#define TILE_COMPRESSION_REGISTER_DECOMPRESSOR(Name, Implementation) \ - TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, \ - Implementation) +#define TILE_COMPRESSION_REGISTER_COMPRESSOR(Name, Implementation) \ + TILE_REGISTER_CLASS_DEPENDENCY(::tile::compressor_registry, Name, Implementation) +#define TILE_COMPRESSION_REGISTER_DECOMPRESSOR(Name, Implementation) \ + TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, Implementation) namespace tile { class TestCompressionOutputStream : public CompressionOutputStream { public: - explicit TestCompressionOutputStream(std::string *s, - std::size_t every_size = 2); + explicit TestCompressionOutputStream(std::string *s, std::size_t every_size = 2); - bool Next(void **data, std::size_t *size) noexcept override; - void BackUp(std::size_t count) noexcept override; - void Flush(); + bool Next(void **data, std::size_t *size) noexcept override; + void BackUp(std::size_t count) noexcept override; + void Flush(); private: - std::size_t using_bytes_{}; - std::size_t every_size_; - std::string *buffer_; + std::size_t using_bytes_{}; + std::size_t every_size_; + std::string *buffer_; }; -} // namespace tile +}// namespace tile -#endif // TILE_BASE_COMPRESSION_COMPRESSION_H +#endif// TILE_BASE_COMPRESSION_COMPRESSION_H diff --git a/tile/base/compression/gzip.cc b/tile/base/compression/gzip.cc index 3041d88..3d4331f 100644 --- a/tile/base/compression/gzip.cc +++ b/tile/base/compression/gzip.cc @@ -11,24 +11,28 @@ TILE_COMPRESSION_REGISTER_DECOMPRESSOR("gzip", GzipDecompressor); namespace detail { struct ZStream { - z_stream stream; + z_stream stream; }; -} // namespace detail +}// namespace detail + namespace { -inline double EstimateCompressionRate(const z_stream *stream, - double default_value) { - if (stream->total_in > 0) { - double rate = 1.0f * stream->total_out / stream->total_in; - constexpr double kMinRate = 1.1; - return rate * kMinRate; - } +inline double +EstimateCompressionRate(const z_stream *stream, double default_value) +{ + if (stream->total_in > 0) { + double rate = 1.0f * stream->total_out / stream->total_in; + constexpr double kMinRate = 1.1; + return rate * kMinRate; + } - return default_value; + return default_value; } -inline uint32_t RestrictAvailSize(size_t size) { - return static_cast(std::min(size, static_cast(UINT32_MAX))); +inline uint32_t +RestrictAvailSize(size_t size) +{ + return static_cast(std::min(size, static_cast(UINT32_MAX))); } // See document of inflateInit2 in zlib.h @@ -37,207 +41,223 @@ constexpr int ZLIB_INIT_FLAG_GZIP = 16; // he size to increase every time Z_BUF_ERROR returns. constexpr int kOutBufferIncreaseSize = 32; -bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream, - const void *input_buffer, std::size_t input_size, bool is_deflate, - bool finish) { - TILE_CHECK(stream && out_stream); +bool +DoAppend(z_stream *stream, + CompressionOutputStream *out_stream, + const void *input_buffer, + std::size_t input_size, + bool is_deflate, + bool finish) +{ + TILE_CHECK(stream && out_stream); - if (!finish && (input_buffer == nullptr || input_size == 0)) { - // no input data - return true; - } + if (!finish && (input_buffer == nullptr || input_size == 0)) { + // no input data + return true; + } - stream->next_in = reinterpret_cast(const_cast(input_buffer)); - int code = Z_OK; - // the number of `buffer too small error` - int need_more_space_cnt = 0; - std::string tmp_buffer; - size_t left_size = input_size; - while (left_size > 0 || need_more_space_cnt > 0 || finish) { - stream->avail_in = RestrictAvailSize(left_size); - const auto current_avail_in = stream->avail_in; - std::size_t out_size; - if (!need_more_space_cnt) { - void *out_data; - if (!out_stream->Next(&out_data, &out_size)) { - // out buffer is full - return false; - } + stream->next_in = reinterpret_cast(const_cast(input_buffer)); + int code = Z_OK; + // the number of `buffer too small error` + int need_more_space_cnt = 0; + std::string tmp_buffer; + size_t left_size = input_size; + while (left_size > 0 || need_more_space_cnt > 0 || finish) { + stream->avail_in = RestrictAvailSize(left_size); + const auto current_avail_in = stream->avail_in; + std::size_t out_size; + if (!need_more_space_cnt) { + void *out_data; + if (!out_stream->Next(&out_data, &out_size)) { + // out buffer is full + return false; + } - stream->next_out = reinterpret_cast(out_data); + stream->next_out = reinterpret_cast(out_data); + } else { + double rate = EstimateCompressionRate(stream, 0.5); + tmp_buffer.resize(left_size * rate + need_more_space_cnt * kOutBufferIncreaseSize); + stream->next_out = reinterpret_cast(internal::RemoveConstPtr(tmp_buffer.data())); + out_size = tmp_buffer.size(); + } + + stream->avail_out = RestrictAvailSize(out_size); + const std::size_t current_avail_out = stream->avail_out; + + TILE_CHECK_GT(stream->avail_out, 0, "Avail_out should never be zero before the call."); + int flush_option = finish ? Z_FINISH : Z_NO_FLUSH; + if (is_deflate) { + code = deflate(stream, flush_option); + } else { + code = inflate(stream, flush_option); + } + + // https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-inflate-1.html + // No progress is possible; either avail_in or avail_out was zero. + if (code == Z_BUF_ERROR) { + if (need_more_space_cnt == 0) { out_stream->BackUp(out_size); } + + if (stream->avail_in == 0) { + // if not finish, we need more input data + // otherwise, fail to compress/decompress + return !finish; + } + + ++need_more_space_cnt; + continue; + } + + if (code < 0) { + TILE_LOG_ERROR_EVERY_SECOND("gzip compress error code [{}]", code); + break; + } + + // consume input data (current_avail_in - stream->avail_in) + // produce output data (stream->avail_out) + left_size -= current_avail_in - stream->avail_in; + if (need_more_space_cnt == 0) { + out_stream->BackUp(stream->avail_out); + } else { + if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream( + out_stream, tmp_buffer.data(), current_avail_out - stream->avail_out))) { + return false; + } + need_more_space_cnt = 0; + } + + if (code == Z_STREAM_END) { return true; } + } + + return code == Z_OK; +} +}// namespace + +bool +GzipCompressor::Compress(const void *src, std::size_t size, CompressionOutputStream *out) +{ + bool ok = Init(out); + ok &= Append(src, size); + ok &= Flush(); + out_ = nullptr; + return ok; +} + +bool +GzipCompressor::Compress(const NoncontiguousBuffer &bytes, CompressionOutputStream *out) +{ + bool ok = Init(out); + std::size_t left = bytes.ByteSize(); + for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) { + auto len = std::min(left, iter->size()); + ok &= Append(iter->data(), len); + left -= len; + } + ok &= Flush(); + out_ = nullptr; + return ok; +} + +bool +GzipCompressor::Append(const void *buffer, std::size_t size) +{ + return DoAppend(&stream_->stream, out_, buffer, size, true, false); +} + +bool +GzipCompressor::Flush() +{ + TILE_CHECK(out_); + if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) { return false; } + return Release(); +} + +bool +GzipCompressor::Init(CompressionOutputStream *out) +{ + TILE_CHECK(!out_); + stream_ = make_unique(); + int code = deflateInit2( + &stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY); + if (code != Z_OK) { + TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code); + stream_ = nullptr; } else { - double rate = EstimateCompressionRate(stream, 0.5); - tmp_buffer.resize(left_size * rate + - need_more_space_cnt * kOutBufferIncreaseSize); - stream->next_out = reinterpret_cast( - internal::RemoveConstPtr(tmp_buffer.data())); - out_size = tmp_buffer.size(); + out_ = out; } + return code == Z_OK; +} - stream->avail_out = RestrictAvailSize(out_size); - const std::size_t current_avail_out = stream->avail_out; +bool +GzipCompressor::Release() +{ + TILE_CHECK(stream_); + int code = deflateEnd(&stream_->stream); + if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); } + return code == Z_OK; +} - TILE_CHECK_GT(stream->avail_out, 0, - "Avail_out should never be zero before the call."); - int flush_option = finish ? Z_FINISH : Z_NO_FLUSH; - if (is_deflate) { - code = deflate(stream, flush_option); +bool +GzipDecompressor::Decompress(const void *src, std::size_t size, CompressionOutputStream *out) +{ + bool ok = Init(out); + ok &= Append(src, size); + ok &= Flush(); + out_ = nullptr; + return ok; +} + +bool +GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed, CompressionOutputStream *out) +{ + bool ok = Init(out); + std::size_t left = compressed.ByteSize(); + for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) { + auto len = std::min(left, iter->size()); + ok &= Append(iter->data(), len); + left -= len; + } + ok &= Flush(); + out_ = nullptr; + return ok; +} + +bool +GzipDecompressor::Append(const void *buffer, std::size_t size) +{ + return DoAppend(&stream_->stream, out_, buffer, size, false, false); +} + +bool +GzipDecompressor::Flush() +{ + TILE_CHECK(out_); + if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) { return false; } + return Release(); +} + +bool +GzipDecompressor::Init(CompressionOutputStream *out) +{ + TILE_CHECK(!out_); + stream_ = make_unique(); + int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP); + if (code != Z_OK) { + TILE_LOG_ERROR_EVERY_SECOND("inflateInit2 error code [{}]", code); + stream_ = nullptr; } else { - code = inflate(stream, flush_option); + out_ = out; } - - // https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-inflate-1.html - // No progress is possible; either avail_in or avail_out was zero. - if (code == Z_BUF_ERROR) { - if (need_more_space_cnt == 0) { - out_stream->BackUp(out_size); - } - - if (stream->avail_in == 0) { - // if not finish, we need more input data - // otherwise, fail to compress/decompress - return !finish; - } - - ++need_more_space_cnt; - continue; - } - - if (code < 0) { - TILE_LOG_ERROR_EVERY_SECOND("gzip compress error code [{}]", code); - break; - } - - // consume input data (current_avail_in - stream->avail_in) - // produce output data (stream->avail_out) - left_size -= current_avail_in - stream->avail_in; - if (need_more_space_cnt == 0) { - out_stream->BackUp(stream->avail_out); - } else { - if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream( - out_stream, tmp_buffer.data(), - current_avail_out - stream->avail_out))) { - return false; - } - need_more_space_cnt = 0; - } - - if (code == Z_STREAM_END) { - return true; - } - } - - return code == Z_OK; -} -} // namespace - -bool GzipCompressor::Compress(const void *src, std::size_t size, - CompressionOutputStream *out) { - bool ok = Init(out); - ok &= Append(src, size); - ok &= Flush(); - out_ = nullptr; - return ok; -} -bool GzipCompressor::Compress(const NoncontiguousBuffer &bytes, - CompressionOutputStream *out) { - bool ok = Init(out); - std::size_t left = bytes.ByteSize(); - for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) { - auto len = std::min(left, iter->size()); - ok &= Append(iter->data(), len); - left -= len; - } - ok &= Flush(); - out_ = nullptr; - return ok; -} -bool GzipCompressor::Append(const void *buffer, std::size_t size) { - return DoAppend(&stream_->stream, out_, buffer, size, true, false); -} -bool GzipCompressor::Flush() { - TILE_CHECK(out_); - if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) { - return false; - } - return Release(); + return code == Z_OK; } -bool GzipCompressor::Init(CompressionOutputStream *out) { - TILE_CHECK(!out_); - stream_ = make_unique(); - int code = - deflateInit2(&stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY); - if (code != Z_OK) { - TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code); - stream_ = nullptr; - } else { - out_ = out; - } - return code == Z_OK; -} -bool GzipCompressor::Release() { - TILE_CHECK(stream_); - int code = deflateEnd(&stream_->stream); - if (code != Z_OK) { - TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); - } - return code == Z_OK; +bool +GzipDecompressor::Release() +{ + TILE_CHECK(stream_); + int code = inflateEnd(&stream_->stream); + if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); } + return code == Z_OK; } -bool GzipDecompressor::Decompress(const void *src, std::size_t size, - CompressionOutputStream *out) { - bool ok = Init(out); - ok &= Append(src, size); - ok &= Flush(); - out_ = nullptr; - return ok; -} -bool GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed, - CompressionOutputStream *out) { - bool ok = Init(out); - std::size_t left = compressed.ByteSize(); - for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) { - auto len = std::min(left, iter->size()); - ok &= Append(iter->data(), len); - left -= len; - } - ok &= Flush(); - out_ = nullptr; - return ok; -} - -bool GzipDecompressor::Append(const void *buffer, std::size_t size) { - return DoAppend(&stream_->stream, out_, buffer, size, false, false); -} -bool GzipDecompressor::Flush() { - TILE_CHECK(out_); - if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) { - return false; - } - return Release(); -} -bool GzipDecompressor::Init(CompressionOutputStream *out) { - TILE_CHECK(!out_); - stream_ = make_unique(); - int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP); - if (code != Z_OK) { - TILE_LOG_ERROR_EVERY_SECOND("inflateInit2 error code [{}]", code); - stream_ = nullptr; - } else { - out_ = out; - } - return code == Z_OK; -} -bool GzipDecompressor::Release() { - TILE_CHECK(stream_); - int code = inflateEnd(&stream_->stream); - if (code != Z_OK) { - TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); - } - return code == Z_OK; -} - -} // namespace compression -} // namespace tile +}// namespace compression +}// namespace tile diff --git a/tile/base/compression/gzip.h b/tile/base/compression/gzip.h index 6340752..2d7d18a 100644 --- a/tile/base/compression/gzip.h +++ b/tile/base/compression/gzip.h @@ -5,48 +5,43 @@ #include - #include "tile/base/compression/compression.h" namespace tile { namespace compression { namespace detail { struct ZStream; -} // namespace detail +}// namespace detail class GzipCompressor : public Compressor { public: - bool Compress(const void *src, std::size_t size, - CompressionOutputStream *out) override; - bool Compress(const NoncontiguousBuffer &bytes, - CompressionOutputStream *out) override; + bool Compress(const void *src, std::size_t size, CompressionOutputStream *out) override; + bool Compress(const NoncontiguousBuffer &bytes, CompressionOutputStream *out) override; private: - bool Append(const void *buffer, std::size_t size); - bool Flush(); - bool Init(CompressionOutputStream *out); - bool Release(); - std::unique_ptr stream_; - CompressionOutputStream *out_ = nullptr; + bool Append(const void *buffer, std::size_t size); + bool Flush(); + bool Init(CompressionOutputStream *out); + bool Release(); + std::unique_ptr stream_; + CompressionOutputStream *out_ = nullptr; }; class GzipDecompressor : public Decompressor { public: - bool Decompress(const void *src, std::size_t size, - CompressionOutputStream *out) override; - bool Decompress(const NoncontiguousBuffer &compressed, - CompressionOutputStream *out) override; + bool Decompress(const void *src, std::size_t size, CompressionOutputStream *out) override; + bool Decompress(const NoncontiguousBuffer &compressed, CompressionOutputStream *out) override; private: - bool Append(const void *buffer, std::size_t size); - bool Flush(); - bool Init(CompressionOutputStream *out); - bool Release(); - std::unique_ptr stream_; - CompressionOutputStream *out_ = nullptr; + bool Append(const void *buffer, std::size_t size); + bool Flush(); + bool Init(CompressionOutputStream *out); + bool Release(); + std::unique_ptr stream_; + CompressionOutputStream *out_ = nullptr; }; -} // namespace compression -} // namespace tile +}// namespace compression +}// namespace tile -#endif // TILE_BASE_COMPRESSION_GZIP_H +#endif// TILE_BASE_COMPRESSION_GZIP_H diff --git a/tile/base/compression/util.cc b/tile/base/compression/util.cc index ae021b0..3d2c0ec 100644 --- a/tile/base/compression/util.cc +++ b/tile/base/compression/util.cc @@ -1,37 +1,33 @@ #include "util.h" + namespace tile { namespace compression { -bool CopyDataToCompressionOutputStream(CompressionOutputStream *out, - const void *data, std::size_t size) { +bool +CopyDataToCompressionOutputStream(CompressionOutputStream *out, const void *data, std::size_t size) +{ - if (size == 0) { - return true; - } + if (size == 0) { return true; } - std::size_t current_pos = 0; - std::size_t left_to_copy = size; + std::size_t current_pos = 0; + std::size_t left_to_copy = size; - while (true) { - void *next_data; - std::size_t next_size; - if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) { - return false; + while (true) { + void *next_data; + std::size_t next_size; + if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) { return false; } + + if (left_to_copy <= next_size) { + memcpy(next_data, reinterpret_cast(data) + current_pos, left_to_copy); + out->BackUp(next_size - left_to_copy); + return true; + } else { + memcpy(next_data, reinterpret_cast(data) + current_pos, next_size); + current_pos += next_size; + left_to_copy -= next_size; + } } - - if (left_to_copy <= next_size) { - memcpy(next_data, reinterpret_cast(data) + current_pos, - left_to_copy); - out->BackUp(next_size - left_to_copy); - return true; - } else { - memcpy(next_data, reinterpret_cast(data) + current_pos, - next_size); - current_pos += next_size; - left_to_copy -= next_size; - } - } - TILE_UNREACHABLE(""); - return false; + TILE_UNREACHABLE(""); + return false; } -} // namespace compression -} // namespace tile +}// namespace compression +}// namespace tile diff --git a/tile/base/compression/util.h b/tile/base/compression/util.h index ea14542..21aaad0 100644 --- a/tile/base/compression/util.h +++ b/tile/base/compression/util.h @@ -7,9 +7,8 @@ namespace tile { namespace compression { -bool CopyDataToCompressionOutputStream(CompressionOutputStream *out, - const void *data, std::size_t size); +bool CopyDataToCompressionOutputStream(CompressionOutputStream *out, const void *data, std::size_t size); } -} // namespace tile +}// namespace tile -#endif // TILE_BASE_COMPRESSION_UTIL_H +#endif// TILE_BASE_COMPRESSION_UTIL_H diff --git a/tile/base/compression/util_test.cc b/tile/base/compression/util_test.cc index 232bea7..ac75e09 100644 --- a/tile/base/compression/util_test.cc +++ b/tile/base/compression/util_test.cc @@ -4,19 +4,20 @@ namespace tile { namespace compression { -TEST(CopyDataToCompressionOutputStream, All) { - std::string s; - std::string a = "123456789+"; +TEST(CopyDataToCompressionOutputStream, All) +{ + std::string s; + std::string a = "123456789+"; - TestCompressionOutputStream out1(&s, 2); - CopyDataToCompressionOutputStream(&out1, a.data(), 1); - CopyDataToCompressionOutputStream(&out1, a.data() + 1, 2); - CopyDataToCompressionOutputStream(&out1, a.data() + 3, 3); - CopyDataToCompressionOutputStream(&out1, a.data() + 6, 4); - out1.Flush(); + TestCompressionOutputStream out1(&s, 2); + CopyDataToCompressionOutputStream(&out1, a.data(), 1); + CopyDataToCompressionOutputStream(&out1, a.data() + 1, 2); + CopyDataToCompressionOutputStream(&out1, a.data() + 3, 3); + CopyDataToCompressionOutputStream(&out1, a.data() + 6, 4); + out1.Flush(); - ASSERT_EQ(s, a); + ASSERT_EQ(s, a); } -} // namespace compression +}// namespace compression -} // namespace tile +}// namespace tile diff --git a/tile/base/compression_test.cc b/tile/base/compression_test.cc index 084140b..77d814b 100644 --- a/tile/base/compression_test.cc +++ b/tile/base/compression_test.cc @@ -11,62 +11,67 @@ static const char *algos[] = { "gzip", }; } -TEST(MakeCompressor, All) { - auto &&c = MakeCompressor("gzip"); - EXPECT_TRUE(c); - c = MakeCompressor("??"); - EXPECT_FALSE(c); + +TEST(MakeCompressor, All) +{ + auto &&c = MakeCompressor("gzip"); + EXPECT_TRUE(c); + c = MakeCompressor("??"); + EXPECT_FALSE(c); } -TEST(MakeDecompressor, All) { - auto &&c = MakeDecompressor("gzip"); - EXPECT_TRUE(c); - c = MakeDecompressor("??"); - EXPECT_FALSE(c); +TEST(MakeDecompressor, All) +{ + auto &&c = MakeDecompressor("gzip"); + EXPECT_TRUE(c); + c = MakeDecompressor("??"); + EXPECT_FALSE(c); } -TEST(CompressString, All) { - std::string original(1000, 'A'); - auto c = Compress(MakeCompressor("gzip").get(), original); - EXPECT_TRUE(c); - auto d = Decompress(MakeDecompressor("gzip").get(), *c); - EXPECT_TRUE(d); - EXPECT_EQ(FlattenSlow(*d), original); +TEST(CompressString, All) +{ + std::string original(1000, 'A'); + auto c = Compress(MakeCompressor("gzip").get(), original); + EXPECT_TRUE(c); + auto d = Decompress(MakeDecompressor("gzip").get(), *c); + EXPECT_TRUE(d); + EXPECT_EQ(FlattenSlow(*d), original); } -TEST(CompressNoncontiguousBuffer, All) { - NoncontiguousBufferBuilder nbb; - std::string original(1000, 'A'); - nbb.Append(original.data(), original.size()); - auto &&nb = nbb.DestructiveGet(); - auto c = Compress(MakeCompressor("gzip").get(), nb); - EXPECT_TRUE(c); - auto d = Decompress(MakeDecompressor("gzip").get(), *c); - EXPECT_TRUE(d); - EXPECT_EQ(FlattenSlow(*d), original); +TEST(CompressNoncontiguousBuffer, All) +{ + NoncontiguousBufferBuilder nbb; + std::string original(1000, 'A'); + nbb.Append(original.data(), original.size()); + auto &&nb = nbb.DestructiveGet(); + auto c = Compress(MakeCompressor("gzip").get(), nb); + EXPECT_TRUE(c); + auto d = Decompress(MakeDecompressor("gzip").get(), *c); + EXPECT_TRUE(d); + EXPECT_EQ(FlattenSlow(*d), original); } -TEST(Decompressor, Empty) { - for (auto &&algo : algos) { - auto res = Decompress(MakeDecompressor(algo).get(), ""); +TEST(Decompressor, Empty) +{ + for (auto &&algo : algos) { + auto res = Decompress(MakeDecompressor(algo).get(), ""); - // Failure in decompressing empty buffer is not an error. So long as - // decompressing a buffer produced by `Compress(..., "")` works correctly, - // we're fine. - if (res) { - // However, if the decompression does succeed, the resulting buffer should - // be empty. - EXPECT_TRUE(res->Empty()); + // Failure in decompressing empty buffer is not an error. So long as + // decompressing a buffer produced by `Compress(..., "")` works correctly, + // we're fine. + if (res) { + // However, if the decompression does succeed, the resulting buffer should + // be empty. + EXPECT_TRUE(res->Empty()); + } } - } } -TEST(Decompressor, Invalid) { - for (auto &&algo : algos) { - auto res = - Decompress(MakeDecompressor(algo).get(), - "this buffer is likely an invalid compressed buffer."); - EXPECT_FALSE(res); - } +TEST(Decompressor, Invalid) +{ + for (auto &&algo : algos) { + auto res = Decompress(MakeDecompressor(algo).get(), "this buffer is likely an invalid compressed buffer."); + EXPECT_FALSE(res); + } } -} // namespace tile +}// namespace tile diff --git a/tile/base/config/config.cc b/tile/base/config/config.cc deleted file mode 100644 index e56cf21..0000000 --- a/tile/base/config/config.cc +++ /dev/null @@ -1,113 +0,0 @@ -#include "tile/base/config/config.h" -#include "tile/base/thread/unique_lock.h" - -namespace tile { -namespace util { -bool Config::Has(const Slice &key) const { - UniqueLock lock(mutex_); - std::string value; - return GetRaw(key, &value); -} - -void Config::Remove(const Slice &key) {} - -void Config::EnableEvents(bool enable) { events_enabled_ = enable; } -bool Config::EventsEnabled() const { return events_enabled_; } - -Config::Keys Config::keys(const Slice &root) const { - UniqueLock lock(mutex_); - return Enumerate(root); -} - -#define TILE_DEFINE_CONFIG_GETTER(type, name) \ - std::optional Config::Get##name(const Slice &key) const { \ - std::string value; \ - if (GetRaw(key, &value)) { \ - auto opt = TryParseTraits::TryParse(value); \ - if (opt.has_value()) { \ - return *opt; \ - } else { \ - return std::nullopt; \ - } \ - } else { \ - return std::nullopt; \ - } \ - } \ - type Config::Get##name(const Slice &key, type default_value) const { \ - auto opt = Get##name(key); \ - if (opt.has_value()) { \ - return *opt; \ - } else { \ - return default_value; \ - } \ - } - -#define TILE_DEFINE_CONFIG_SETTER(type, name) \ - void Config::Set##name(const Slice &key, type value) { \ - SetRawWithEvent(key, Format("{}", value)); \ - } - -std::optional Config::GetString(const Slice &key) const { - std::string value; - UniqueLock lock(mutex_); - if (GetRaw(key, &value)) { - return value; - } else { - return std::nullopt; - } -} - -std::string Config::GetString(const Slice &key, - std::string default_value) const { - auto opt = GetString(key); - return opt.has_value() ? *opt : default_value; -} - -void Config::SetString(const Slice &key, const std::string &value) { - SetRawWithEvent(key, value); -} -void Config::SetBool(const Slice &key, bool value) { - SetRawWithEvent(key, value ? "true" : "false"); -} - -TILE_DEFINE_CONFIG_GETTER(int, Int) -TILE_DEFINE_CONFIG_GETTER(unsigned int, UInt) -TILE_DEFINE_CONFIG_GETTER(int16_t, Int16) -TILE_DEFINE_CONFIG_GETTER(int32_t, Int32) -TILE_DEFINE_CONFIG_GETTER(int64_t, Int64) -TILE_DEFINE_CONFIG_GETTER(uint16_t, UInt16) -TILE_DEFINE_CONFIG_GETTER(uint32_t, UInt32) -TILE_DEFINE_CONFIG_GETTER(uint64_t, UInt64) -TILE_DEFINE_CONFIG_GETTER(double, Double) -TILE_DEFINE_CONFIG_GETTER(bool, Bool) - -TILE_DEFINE_CONFIG_SETTER(int, Int) -TILE_DEFINE_CONFIG_SETTER(unsigned int, UInt) -TILE_DEFINE_CONFIG_SETTER(int16_t, Int16) -TILE_DEFINE_CONFIG_SETTER(int32_t, Int32) -TILE_DEFINE_CONFIG_SETTER(int64_t, Int64) -TILE_DEFINE_CONFIG_SETTER(uint16_t, UInt16) -TILE_DEFINE_CONFIG_SETTER(uint32_t, UInt32) -TILE_DEFINE_CONFIG_SETTER(uint64_t, UInt64) -TILE_DEFINE_CONFIG_SETTER(double, Double) - -void Config::SetRawWithEvent(const Slice &key, const std::string &value) { - if (events_enabled_) { - OnChanging(key, value); - } - - { - UniqueLock lock(mutex_); - SetRaw(key, value); - } - - if (events_enabled_) { - OnChanged(key, value); - } -} - -Config::~Config() {} - -} // namespace util - -} // namespace tile diff --git a/tile/base/config/config.h b/tile/base/config/config.h deleted file mode 100644 index 32e3204..0000000 --- a/tile/base/config/config.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef TILE_BASE_CONFIG_CONFIG_H -#define TILE_BASE_CONFIG_CONFIG_H - -#pragma once -#include "tile/base/optional.h" -#include "tile/base/ref_ptr.h" -#include "tile/base/slice.h" -#include "tile/base/string.h" -#include "tile/base/thread/mutex.h" -#include "tile/sigslot/sigslot.h" -#include - -namespace tile { -namespace util { -class Config : public RefCounted { -public: - using Keys = std::vector; - using Ptr = RefPtr; - - // events - // Key, Value - sigslot::signal2 OnChanging; - // Key, Value - sigslot::signal2 OnChanged; - // Key - sigslot::signal1 OnRemoving; - // Key - sigslot::signal1 OnRemoved; - - bool Has(const Slice &key) const; - void Remove(const Slice &key); - void EnableEvents(bool enable = true); - bool EventsEnabled() const; - - Keys keys(const Slice &root = "") const; - -#define TILE_DECLARE_CONFIG_GETTER(type, name) \ - std::optional Get##name(const Slice &key) const; \ - type Get##name(const Slice &key, type default_value) const; - -#define TILE_DECLARE_CONFIG_SETTER(type, name) \ - virtual void Set##name(const Slice &key, type value); - - std::string GetRawString(const Slice &key, const Slice &default_value) const; - // std::stirng GetString() - // int GetInt() - // unsigned int GetUInt() - // int16_t GetInt16() - // int32_t GetInt32() - // int64_t GetInt64() - // uint16_t GetUInt16() - // uint32_t GetUInt32() - // uint64_t GetUInt64() - // double GetDouble() - // bool GetBool() - - // getters - TILE_DECLARE_CONFIG_GETTER(std::string, String) - TILE_DECLARE_CONFIG_GETTER(int, Int) - TILE_DECLARE_CONFIG_GETTER(unsigned int, UInt) - TILE_DECLARE_CONFIG_GETTER(int16_t, Int16) - TILE_DECLARE_CONFIG_GETTER(int32_t, Int32) - TILE_DECLARE_CONFIG_GETTER(int64_t, Int64) - TILE_DECLARE_CONFIG_GETTER(uint16_t, UInt16) - TILE_DECLARE_CONFIG_GETTER(uint32_t, UInt32) - TILE_DECLARE_CONFIG_GETTER(uint64_t, UInt64) - TILE_DECLARE_CONFIG_GETTER(double, Double) - TILE_DECLARE_CONFIG_GETTER(bool, Bool) - -protected: - // setters - // SetString(const Slice &key, const std::string &value) - // SetInt(const Slice &key, int value) - // SetUInt(const Slice &key, unsigned int value) - // SetInt16(const Slice &key, int16_t value) - // SetInt32(const Slice &key, int32_t value) - // SetInt64(const Slice &key, int64_t value) - // SetUInt16(const Slice &key, uint16_t value) - // SetUInt32(const Slice &key, uint32_t value) - // SetUInt64(const Slice &key, uint64_t value) - // SetDouble(const Slice &key, double value) - // SetBool(const Slice &key, bool value) - TILE_DECLARE_CONFIG_SETTER(const std::string &, String) - TILE_DECLARE_CONFIG_SETTER(int, Int) - TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt) - TILE_DECLARE_CONFIG_SETTER(int16_t, Int16) - TILE_DECLARE_CONFIG_SETTER(int32_t, Int32) - TILE_DECLARE_CONFIG_SETTER(int64_t, Int64) - TILE_DECLARE_CONFIG_SETTER(uint16_t, UInt16) - TILE_DECLARE_CONFIG_SETTER(uint32_t, UInt32) - TILE_DECLARE_CONFIG_SETTER(uint64_t, UInt64) - TILE_DECLARE_CONFIG_SETTER(double, Double) - TILE_DECLARE_CONFIG_SETTER(bool, Bool) - -#undef TILE_DECLARE_CONFIG_GETTER -#undef TILE_DECLARE_CONFIG_SETTER - -protected: - class ScopedLock { - public: - explicit ScopedLock(const Config &config) : config_(config) { - config_.mutex_.Lock(); - } - ~ScopedLock() { config_.mutex_.Unlock(); } - - private: - const Config &config_; - }; - - virtual bool GetRaw(const Slice &key, std::string *value) const = 0; - virtual bool SetRaw(const Slice &key, const Slice &value) = 0; - virtual void RemoveRaw(const Slice &key) = 0; - virtual Keys Enumerate(const Slice &range) const = 0; - void SetRawWithEvent(const Slice &key, const std::string &value); - virtual ~Config(); - - friend class std::default_delete; - friend class LayeredConfig; - -private: - mutable Mutex mutex_; - bool events_enabled_{false}; -}; -} // namespace util -} // namespace tile - -#endif // TILE_BASE_CONFIG_CONFIG_H diff --git a/tile/base/config/configurable.h b/tile/base/config/configurable.h index 6896afd..99918b2 100644 --- a/tile/base/config/configurable.h +++ b/tile/base/config/configurable.h @@ -2,18 +2,16 @@ #define TILE_BASE_CONFIG_CONFIGURABLE_H #pragma once -#include "tile/base/string.h" +#include "tile/base/slice.h" namespace tile { -namespace util { class Configurable { - Configurable(); - virtual ~Configurable() = default; - virtual void SetProperty(const Slice &name, const Slice &value) = 0; - virtual std::string GetProperty(const Slice &name) const = 0; + Configurable(); + virtual ~Configurable() = default; + virtual void SetProperty(const Slice &name, const Slice &value) = 0; + virtual std::string GetProperty(const Slice &name) const = 0; }; -} // namespace util -} // namespace tile +}// namespace tile -#endif // TILE_BASE_CONFIG_CONFIGURABLE_H +#endif// TILE_BASE_CONFIG_CONFIGURABLE_H diff --git a/tile/base/config/configuration.cc b/tile/base/config/configuration.cc new file mode 100644 index 0000000..6f2999b --- /dev/null +++ b/tile/base/config/configuration.cc @@ -0,0 +1,138 @@ +#include "tile/base/config/configuration.h" + +#include "tile/base/string.h" +#include "tile/base/thread/unique_lock.h" + +namespace tile { +bool +Configuration::Has(const Slice &key) const +{ + UniqueLock lock(mutex_); + std::string value; + return GetRaw(key, &value); +} + +void +Configuration::Remove(const Slice &key) +{ + UniqueLock lock(mutex_); + if (events_enabled_) { OnRemoving(key); } + RemoveRaw(key); + if (events_enabled_) { OnRemoved(key); } +} + +void +Configuration::EnableEvents(bool enable) +{ + events_enabled_ = enable; +} + +bool +Configuration::EventsEnabled() const +{ + return events_enabled_; +} + +Configuration::Keys +Configuration::keys(const Slice &root) const +{ + UniqueLock lock(mutex_); + return Enumerate(root); +} + +#define TILE_DEFINE_CONFIG_GETTER(type, name) \ + std::optional Configuration::Get##name(const Slice &key) const \ + { \ + std::string value; \ + if (GetRaw(key, &value)) { \ + auto opt = TryParseTraits::TryParse(value); \ + if (opt.has_value()) { \ + return *opt; \ + } else { \ + return std::nullopt; \ + } \ + } else { \ + return std::nullopt; \ + } \ + } \ + type Configuration::Get##name(const Slice &key, type default_value) const \ + { \ + auto opt = Get##name(key); \ + if (opt.has_value()) { \ + return *opt; \ + } else { \ + return default_value; \ + } \ + } + +#define TILE_DEFINE_CONFIG_SETTER(type, name) \ + void Configuration::Set##name(const Slice &key, type value) { SetRawWithEvent(key, Format("{}", value)); } + +std::optional +Configuration::GetString(const Slice &key) const +{ + std::string value; + UniqueLock lock(mutex_); + if (GetRaw(key, &value)) { + return value; + } else { + return std::nullopt; + } +} + +std::string +Configuration::GetString(const Slice &key, std::string default_value) const +{ + auto opt = GetString(key); + return opt.has_value() ? *opt : default_value; +} + +void +Configuration::SetString(const Slice &key, const std::string &value) +{ + SetRawWithEvent(key, value); +} + +void +Configuration::SetBool(const Slice &key, bool value) +{ + SetRawWithEvent(key, value ? "true" : "false"); +} + +TILE_DEFINE_CONFIG_GETTER(int, Int) +TILE_DEFINE_CONFIG_GETTER(unsigned int, UInt) +TILE_DEFINE_CONFIG_GETTER(int16_t, Int16) +TILE_DEFINE_CONFIG_GETTER(int32_t, Int32) +TILE_DEFINE_CONFIG_GETTER(int64_t, Int64) +TILE_DEFINE_CONFIG_GETTER(uint16_t, UInt16) +TILE_DEFINE_CONFIG_GETTER(uint32_t, UInt32) +TILE_DEFINE_CONFIG_GETTER(uint64_t, UInt64) +TILE_DEFINE_CONFIG_GETTER(double, Double) +TILE_DEFINE_CONFIG_GETTER(bool, Bool) + +TILE_DEFINE_CONFIG_SETTER(int, Int) +TILE_DEFINE_CONFIG_SETTER(unsigned int, UInt) +TILE_DEFINE_CONFIG_SETTER(int16_t, Int16) +TILE_DEFINE_CONFIG_SETTER(int32_t, Int32) +TILE_DEFINE_CONFIG_SETTER(int64_t, Int64) +TILE_DEFINE_CONFIG_SETTER(uint16_t, UInt16) +TILE_DEFINE_CONFIG_SETTER(uint32_t, UInt32) +TILE_DEFINE_CONFIG_SETTER(uint64_t, UInt64) +TILE_DEFINE_CONFIG_SETTER(double, Double) + +void +Configuration::SetRawWithEvent(const Slice &key, const std::string &value) +{ + if (events_enabled_) { OnChanging(key, value); } + + { + UniqueLock lock(mutex_); + SetRaw(key, value); + } + + if (events_enabled_) { OnChanged(key, value); } +} + +Configuration::~Configuration() {} + +}// namespace tile diff --git a/tile/base/config/configuration.h b/tile/base/config/configuration.h new file mode 100644 index 0000000..c595e3b --- /dev/null +++ b/tile/base/config/configuration.h @@ -0,0 +1,122 @@ +#ifndef TILE_BASE_CONFIG_CONFIGURATION_H +#define TILE_BASE_CONFIG_CONFIGURATION_H + +#pragma once +#include "tile/base/optional.h" +#include "tile/base/ref_ptr.h" +#include "tile/base/slice.h" +#include "tile/base/thread/mutex.h" +#include "tile/sigslot/sigslot.h" +#include + +namespace tile { +class Configuration : public RefCounted { +public: + using Keys = std::vector; + using Ptr = RefPtr; + + // events + // Key, Value + sigslot::signal2 OnChanging; + // Key, Value + sigslot::signal2 OnChanged; + // Key + sigslot::signal1 OnRemoving; + // Key + sigslot::signal1 OnRemoved; + + bool Has(const Slice &key) const; + void Remove(const Slice &key); + void EnableEvents(bool enable = true); + bool EventsEnabled() const; + + Keys keys(const Slice &root = "") const; + +#define TILE_DECLARE_CONFIG_GETTER(type, name) \ + std::optional Get##name(const Slice &key) const; \ + type Get##name(const Slice &key, type default_value) const; + +#define TILE_DECLARE_CONFIG_SETTER(type, name) virtual void Set##name(const Slice &key, type value); + + std::string GetRawString(const Slice &key, const Slice &default_value) const; + // std::stirng GetString() + // int GetInt() + // unsigned int GetUInt() + // int16_t GetInt16() + // int32_t GetInt32() + // int64_t GetInt64() + // uint16_t GetUInt16() + // uint32_t GetUInt32() + // uint64_t GetUInt64() + // double GetDouble() + // bool GetBool() + + // getters + TILE_DECLARE_CONFIG_GETTER(std::string, String) + TILE_DECLARE_CONFIG_GETTER(int, Int) + TILE_DECLARE_CONFIG_GETTER(unsigned int, UInt) + TILE_DECLARE_CONFIG_GETTER(int16_t, Int16) + TILE_DECLARE_CONFIG_GETTER(int32_t, Int32) + TILE_DECLARE_CONFIG_GETTER(int64_t, Int64) + TILE_DECLARE_CONFIG_GETTER(uint16_t, UInt16) + TILE_DECLARE_CONFIG_GETTER(uint32_t, UInt32) + TILE_DECLARE_CONFIG_GETTER(uint64_t, UInt64) + TILE_DECLARE_CONFIG_GETTER(double, Double) + TILE_DECLARE_CONFIG_GETTER(bool, Bool) + +protected: + // setters + // SetString(const Slice &key, const std::string &value) + // SetInt(const Slice &key, int value) + // SetUInt(const Slice &key, unsigned int value) + // SetInt16(const Slice &key, int16_t value) + // SetInt32(const Slice &key, int32_t value) + // SetInt64(const Slice &key, int64_t value) + // SetUInt16(const Slice &key, uint16_t value) + // SetUInt32(const Slice &key, uint32_t value) + // SetUInt64(const Slice &key, uint64_t value) + // SetDouble(const Slice &key, double value) + // SetBool(const Slice &key, bool value) + TILE_DECLARE_CONFIG_SETTER(const std::string &, String) + TILE_DECLARE_CONFIG_SETTER(int, Int) + TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt) + TILE_DECLARE_CONFIG_SETTER(int16_t, Int16) + TILE_DECLARE_CONFIG_SETTER(int32_t, Int32) + TILE_DECLARE_CONFIG_SETTER(int64_t, Int64) + TILE_DECLARE_CONFIG_SETTER(uint16_t, UInt16) + TILE_DECLARE_CONFIG_SETTER(uint32_t, UInt32) + TILE_DECLARE_CONFIG_SETTER(uint64_t, UInt64) + TILE_DECLARE_CONFIG_SETTER(double, Double) + TILE_DECLARE_CONFIG_SETTER(bool, Bool) + +#undef TILE_DECLARE_CONFIG_GETTER +#undef TILE_DECLARE_CONFIG_SETTER + +protected: + class ScopedLock { + public: + explicit ScopedLock(const Configuration &config) : config_(config) { config_.mutex_.Lock(); } + + ~ScopedLock() { config_.mutex_.Unlock(); } + + private: + const Configuration &config_; + }; + + virtual bool GetRaw(const Slice &key, std::string *value) const = 0; + virtual bool SetRaw(const Slice &key, const Slice &value) = 0; + virtual void RemoveRaw(const Slice &key) = 0; + virtual Keys Enumerate(const Slice &range) const = 0; + void SetRawWithEvent(const Slice &key, const std::string &value); + virtual ~Configuration(); + + friend class std::default_delete; + friend class LayeredConfiguration; + +private: + mutable Mutex mutex_; + bool events_enabled_{false}; +}; +}// namespace tile + +#endif// TILE_BASE_CONFIG_CONFIGURATION_H diff --git a/tile/base/config/ini_file_config.cc b/tile/base/config/ini_file_config.cc deleted file mode 100644 index c3838c5..0000000 --- a/tile/base/config/ini_file_config.cc +++ /dev/null @@ -1,145 +0,0 @@ -#include "tile/base/config/ini_file_config.h" -#include "tile/base/thread/scoped_lock.h" - -namespace tile { -namespace util { - -IniFileConfig::IniFileConfig() {} -// IniFileConfig::IniFileConfig(std::istream &istr) { load(istr); } -// IniFileConfig::IniFileConfig(const std::string &path) { load(path); } -IniFileConfig::~IniFileConfig() {} - -bool IniFileConfig::load(std::istream &istr) { - Config::ScopedLock lock(*this); - map_.clear(); - section_key_.clear(); - while (!istr.eof()) { - ParseLine(istr); - } - return true; -} -bool IniFileConfig::load(const std::string &path) { - std::ifstream istr(path); - if (istr.good()) { - return load(istr); - } else { - return false; - } -} -bool IniFileConfig::GetRaw(const Slice &key, std::string *value) const { - auto iter = map_.find(key.ToString()); - if (iter != map_.end()) { - *value = iter->second; - return true; - } else { - return false; - } -} -bool IniFileConfig::SetRaw(const Slice &key, const Slice &value) { - map_[key] = value; - return true; -} - -void IniFileConfig::RemoveRaw(const Slice &key) { - std::string prefix = key; - if (!prefix.empty()) { - prefix.push_back('.'); - } - IStringMap::iterator it = map_.begin(); - IStringMap::iterator cur; - while (it != map_.end()) { - if (EqualsIgnoreCase(cur->first, key) || - EqualsIgnoreCase(cur->first, prefix)) { - it = map_.erase(cur); - } else { - ++it; - } - } -} - -Config::Keys IniFileConfig::Enumerate(const Slice &key) const { - Config::Keys range; - std::set keys; - std::string prefix = key.ToString(); - if (prefix.empty()) { - prefix.push_back('.'); - } - - std::string::size_type psize = prefix.size(); - for (const auto &p : map_) { - auto &key = p.first; - if (EqualsIgnoreCase(key, prefix, psize)) { - std::string::size_type end = key.find('.', psize); - std::string subkey; - if (end == std::string::npos) { - subkey = key.substr(psize); - } else { - subkey = key.substr(psize, end - psize); - } - - if (keys.find(subkey) == keys.end()) { - keys.insert(subkey); - range.push_back(subkey); - } - } - } - return range; -} - -void IniFileConfig::ParseLine(std::istream &istr) { - static const int eof = std::char_traits::eof(); - auto ReadLine = [&](std::string *line) { - line->clear(); - while (true) { - int c = istr.get(); - if (c == eof || c == '\n') { - return c != eof; - } else { - *line += (char)c; - } - } - }; - - std::string raw_line; - while (ReadLine(&raw_line)) { - if (raw_line.empty()) { - continue; - } - Slice line = TrimLeft(raw_line); - if (line.empty() || line[0] == ';' || line[0] == '#') { - // skip empty line - // skip comment line ; or # - continue; - } - - // parse section - if (line[0] == '[') { - auto pos = line.find(']'); - if (pos == Slice::npos) { - section_key_ = Trim(line.substr(1)); - } else { - section_key_ = Trim(line.substr(1, pos - 1)); - } - } else { - auto strs = Split(line, "=", true, 2); - std::string full_key = section_key_; - if (!full_key.empty()) { - full_key.push_back('.'); - } - full_key.append(Trim(strs[0])); - if (strs.size() > 1) { - map_[full_key] = Trim(strs[1]); - } else { - map_[full_key] = ""; - } - } - } -} -bool IniFileConfig::ICompare::operator()(const std::string &s1, - const std::string &s2) const { - auto len = std::min(s1.size(), s2.size()); - return strncmp(s1.c_str(), s2.c_str(), len) < 0; -} - -} // namespace util -} // namespace tile diff --git a/tile/base/config/ini_file_config.h b/tile/base/config/ini_file_config.h deleted file mode 100644 index fe3e1a9..0000000 --- a/tile/base/config/ini_file_config.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef TILE_BASE_CONFIG_INI_FILE_CONFIG_H -#define TILE_BASE_CONFIG_INI_FILE_CONFIG_H - -#pragma once - -#include "tile/base/config/config.h" -#include - -namespace tile { -namespace util { -class IniFileConfig : public Config { -public: - using Ptr = RefPtr; - - IniFileConfig(); - ~IniFileConfig() override; - // IniFileConfig(std::istream &istr); - // IniFileConfig(const std::string &path); - bool load(std::istream &istr); - bool load(const std::string &path); - -protected: - bool GetRaw(const Slice &key, std::string *value) const override; - bool SetRaw(const Slice &key, const Slice &value) override; - void RemoveRaw(const Slice &key) override; - Keys Enumerate(const Slice &range) const override; - -private: - void ParseLine(std::istream &istr); - struct ICompare { - bool operator()(const std::string &s1, const std::string &s2) const; - }; - typedef std::map IStringMap; - IStringMap map_; - std::string section_key_; -}; -} // namespace util -} // namespace tile - -#endif // TILE_BASE_CONFIG_INI_FILE_CONFIG_H diff --git a/tile/base/config/ini_file_config_test.cc b/tile/base/config/ini_file_config_test.cc deleted file mode 100644 index e110933..0000000 --- a/tile/base/config/ini_file_config_test.cc +++ /dev/null @@ -1,46 +0,0 @@ -#include "tile/base/config/ini_file_config.h" -#include "gtest/gtest.h" - -const char *kIniFileConfig = R"( -# comment 1 -; comment 2 - # commet 3 - ; commment 4 -a=1 -[sec1] -a=2 -[sec3] -a=3 -[sec2.kk] -a=4 -)"; - -namespace tile { -namespace util { - -static_assert(!detail::HasClassofImpl::value, ""); - -TEST(IniFileConfig, LoadFromIStream) { - std::stringstream ss(kIniFileConfig); - Config::Ptr config = MakeRefCounted(); - ASSERT_FALSE(config->Has("a")); - ASSERT_FALSE(config->Has("sec1.a")); - ASSERT_FALSE(config->Has("sec3.a")); - if (config.Is()) { - IniFileConfig::Ptr ini = config.As(); - ASSERT_TRUE(ini->load(ss)); - } - ASSERT_TRUE(config->Has("a")); - ASSERT_TRUE(config->Has("sec1.a")); - ASSERT_TRUE(config->Has("sec3.a")); - ASSERT_TRUE(config->Has("sec2.kk.a")); - - ASSERT_TRUE(config->GetInt("a")); - ASSERT_TRUE(config->GetInt("sec1.a")); - ASSERT_EQ(1, *config->GetInt("a")); - ASSERT_EQ(2, *config->GetInt("sec1.a")); - ASSERT_EQ(3, *config->GetInt("sec3.a")); - ASSERT_EQ(4, *config->GetInt("sec2.kk.a")); -} -} // namespace util -} // namespace tile diff --git a/tile/base/config/ini_file_configuration.cc b/tile/base/config/ini_file_configuration.cc new file mode 100644 index 0000000..fa66366 --- /dev/null +++ b/tile/base/config/ini_file_configuration.cc @@ -0,0 +1,152 @@ +#include "tile/base/config/ini_file_configuration.h" + +#include "tile/base/string.h" +#include "tile/base/thread/scoped_lock.h" + +namespace tile { + +IniFileConfiguration::IniFileConfiguration() {} + +// IniFileConfig::IniFileConfig(std::istream &istr) { load(istr); } +// IniFileConfig::IniFileConfig(const std::string &path) { load(path); } +IniFileConfiguration::~IniFileConfiguration() {} + +bool +IniFileConfiguration::load(std::istream &istr) +{ + if (!istr.good()) { return false; } + + Configuration::ScopedLock lock(*this); + map_.clear(); + section_key_.clear(); + while (!istr.eof()) { ParseLine(istr); } + return true; +} + +bool +IniFileConfiguration::load(const std::string &path) +{ + std::ifstream istr(path); + return load(istr); +} + +bool +IniFileConfiguration::GetRaw(const Slice &key, std::string *value) const +{ + auto iter = map_.find(key.ToString()); + if (iter != map_.end()) { + *value = iter->second; + return true; + } else { + return false; + } +} + +bool +IniFileConfiguration::SetRaw(const Slice &key, const Slice &value) +{ + map_[key] = value; + return true; +} + +void +IniFileConfiguration::RemoveRaw(const Slice &key) +{ + std::string prefix = key; + if (!prefix.empty()) { prefix.push_back('.'); } + IStringMap::iterator it = map_.begin(); + IStringMap::iterator cur; + while (it != map_.end()) { + if (EqualsIgnoreCase(cur->first, key) || EqualsIgnoreCase(cur->first, prefix)) { + it = map_.erase(cur); + } else { + ++it; + } + } +} + +Configuration::Keys +IniFileConfiguration::Enumerate(const Slice &key) const +{ + Configuration::Keys range; + std::set keys; + std::string prefix = key.ToString(); + if (prefix.empty()) { prefix.push_back('.'); } + + std::string::size_type psize = prefix.size(); + for (const auto &p : map_) { + auto &key = p.first; + if (EqualsIgnoreCase(key, prefix, psize)) { + std::string::size_type end = key.find('.', psize); + std::string subkey; + if (end == std::string::npos) { + subkey = key.substr(psize); + } else { + subkey = key.substr(psize, end - psize); + } + + if (keys.find(subkey) == keys.end()) { + keys.insert(subkey); + range.push_back(subkey); + } + } + } + return range; +} + +void +IniFileConfiguration::ParseLine(std::istream &istr) +{ + static const int eof = std::char_traits::eof(); + auto ReadLine = [&](std::string *line) { + line->clear(); + while (true) { + int c = istr.get(); + if (c == eof || c == '\n') { + return c != eof; + } else { + *line += (char) c; + } + } + }; + + std::string raw_line; + while (ReadLine(&raw_line)) { + if (raw_line.empty()) { continue; } + Slice line = TrimLeft(raw_line); + if (line.empty() || line[0] == ';' || line[0] == '#') { + // skip empty line + // skip comment line ; or # + continue; + } + + // parse section + if (line[0] == '[') { + auto pos = line.find(']'); + if (pos == Slice::npos) { + section_key_ = Trim(line.substr(1)); + } else { + section_key_ = Trim(line.substr(1, pos - 1)); + } + } else { + auto strs = Split(line, "=", true, 2); + std::string full_key = section_key_; + if (!full_key.empty()) { full_key.push_back('.'); } + full_key.append(Trim(strs[0])); + if (strs.size() > 1) { + map_[full_key] = Trim(strs[1]); + } else { + map_[full_key] = ""; + } + } + } +} + +bool +IniFileConfiguration::ICompare::operator()(const std::string &s1, const std::string &s2) const +{ + auto len = std::min(s1.size(), s2.size()); + return strncmp(s1.c_str(), s2.c_str(), len) < 0; +} + +}// namespace tile diff --git a/tile/base/config/ini_file_configuration.h b/tile/base/config/ini_file_configuration.h new file mode 100644 index 0000000..d71b05b --- /dev/null +++ b/tile/base/config/ini_file_configuration.h @@ -0,0 +1,40 @@ +#ifndef TILE_BASE_CONFIG_INI_FILE_CONFIG_H +#define TILE_BASE_CONFIG_INI_FILE_CONFIG_H + +#pragma once + +#include "tile/base/config/configuration.h" +#include + +namespace tile { +class IniFileConfiguration : public Configuration { +public: + using Ptr = RefPtr; + + IniFileConfiguration(); + ~IniFileConfiguration() override; + // IniFileConfig(std::istream &istr); + // IniFileConfig(const std::string &path); + bool load(std::istream &istr); + bool load(const std::string &path); + +protected: + bool GetRaw(const Slice &key, std::string *value) const override; + bool SetRaw(const Slice &key, const Slice &value) override; + void RemoveRaw(const Slice &key) override; + Keys Enumerate(const Slice &range) const override; + +private: + void ParseLine(std::istream &istr); + + struct ICompare { + bool operator()(const std::string &s1, const std::string &s2) const; + }; + + typedef std::map IStringMap; + IStringMap map_; + std::string section_key_; +}; +}// namespace tile + +#endif// TILE_BASE_CONFIG_INI_FILE_CONFIG_H diff --git a/tile/base/config/ini_file_configuration_test.cc b/tile/base/config/ini_file_configuration_test.cc new file mode 100644 index 0000000..d789c84 --- /dev/null +++ b/tile/base/config/ini_file_configuration_test.cc @@ -0,0 +1,45 @@ +#include "tile/base/config/ini_file_configuration.h" +#include "gtest/gtest.h" + +const char *kIniFileConfig = R"( +# comment 1 +; comment 2 + # commet 3 + ; commment 4 +a=1 +[sec1] +a=2 +[sec3] +a=3 +[sec2.kk] +a=4 +)"; + +namespace tile { + +static_assert(!detail::HasClassofImpl::value, ""); + +TEST(IniFileConfig, LoadFromIStream) +{ + std::stringstream ss(kIniFileConfig); + Configuration::Ptr config = MakeRefCounted(); + ASSERT_FALSE(config->Has("a")); + ASSERT_FALSE(config->Has("sec1.a")); + ASSERT_FALSE(config->Has("sec3.a")); + if (config.Is()) { + IniFileConfiguration::Ptr ini = config.As(); + ASSERT_TRUE(ini->load(ss)); + } + ASSERT_TRUE(config->Has("a")); + ASSERT_TRUE(config->Has("sec1.a")); + ASSERT_TRUE(config->Has("sec3.a")); + ASSERT_TRUE(config->Has("sec2.kk.a")); + + ASSERT_TRUE(config->GetInt("a")); + ASSERT_TRUE(config->GetInt("sec1.a")); + ASSERT_EQ(1, *config->GetInt("a")); + ASSERT_EQ(2, *config->GetInt("sec1.a")); + ASSERT_EQ(3, *config->GetInt("sec3.a")); + ASSERT_EQ(4, *config->GetInt("sec2.kk.a")); +} +}// namespace tile diff --git a/tile/base/config/json_configuration.cc b/tile/base/config/json_configuration.cc new file mode 100644 index 0000000..64db9d4 --- /dev/null +++ b/tile/base/config/json_configuration.cc @@ -0,0 +1,153 @@ +#include "tile/base/config/json_configuration.h" + +#include "tile/base/logging.h" +#include "tile/base/string.h" + +namespace tile { +JSONConfiguration::JSONConfiguration() : object_() {} + +JSONConfiguration::~JSONConfiguration() {} + +bool +JSONConfiguration::load(const std::string &path) +{ + std::ifstream istr(path); + return load(istr); +} + +bool +JSONConfiguration::load(std::istream &istr) +{ + if (!istr.good()) { return false; } + + Configuration::ScopedLock _(*this); + object_.clear(); + Json::Reader reader; + return reader.parse(istr, object_, true); +} + +void +JSONConfiguration::SetInt(const Slice &key, int value) +{ + Configuration::ScopedLock _(*this); + SetValue(key, value); +} + +void +JSONConfiguration::SetBool(const Slice &key, bool value) +{ + Configuration::ScopedLock _(*this); + SetValue(key, value); +} + +void +JSONConfiguration::SetDouble(const Slice &key, double value) +{ + Configuration::ScopedLock _(*this); + SetValue(key, value); +} + +void +JSONConfiguration::SetString(const Slice &key, const std::string &value) +{ + Configuration::ScopedLock _(*this); + SetValue(key, value); +} + +void +JSONConfiguration::RemoveRaw(const Slice &key) +{ + Json::Value *root; + Slice last_part; + if (!FindStart(key, &last_part, &root)) { return; } + root->removeMember(last_part); +} + +std::string +JSONConfiguration::Dump() const +{ + Configuration::ScopedLock _(*this); + return object_.toStyledString(); +} + +bool +JSONConfiguration::GetRaw(const Slice &key, std::string *value) const +{ + auto keys = Split(key, '.'); + auto root = object_; + for (std::size_t i = 0; i < keys.size(); ++i) { + const auto &cur_key = keys[i]; + if (cur_key.empty()) { + TILE_LOG_ERROR("Invalid key: {}", key); + return false; + } + if (!root.isMember(cur_key)) { return false; } + root = root[cur_key]; + } + + if (root.isConvertibleTo(Json::stringValue)) { + *value = root.asString(); + } else { + *value = root.toStyledString(); + } + return true; +} + +bool +JSONConfiguration::SetRaw(const Slice &key, const Slice &value) +{ + return SetValue(key, value.ToString()); +} + +Configuration::Keys +JSONConfiguration::Enumerate(const Slice &range) const +{ + Configuration::Keys key_set; + std::string prefix = range; + if (!prefix.empty()) { prefix += "."; } + + auto keys = Split(range, '.'); + auto root = object_; + for (std::size_t i = 0; i < keys.size(); ++i) { + const auto &cur_key = keys[i]; + if (cur_key.empty()) { + TILE_LOG_ERROR("Invalid range: {}", range); + return key_set; + } + if (!root.isMember(cur_key)) { return key_set; } + root = root[cur_key]; + } + + for (const auto &key : root.getMemberNames()) { key_set.push_back(prefix + key); } + return key_set; +} + +bool +JSONConfiguration::FindStart(const Slice &key, Slice *last_part, Json::Value **parent_obj) +{ + auto keys = Split(key, '.'); + if (keys.empty()) { return false; } + + Json::Value *root = &object_; + for (std::size_t i = 0; i < keys.size() - 1; ++i) { + const auto &cur_key = keys[i]; + if (cur_key.empty()) { + TILE_LOG_ERROR("Invalid key: {}", key); + return false; + } + + if (!root->isMember(cur_key)) { + (*root)[cur_key] = Json::Value(Json::objectValue); + } else if (!(*root)[cur_key].isObject()) { + TILE_LOG_ERROR("only leaf nodes can be set: key: {}, cur_key(idx={}): {}", key, i, cur_key); + return false; + } + root = &(*root)[cur_key]; + } + + *last_part = keys.back(); + *parent_obj = root; + return true; +} + +}// namespace tile diff --git a/tile/base/config/json_configuration.h b/tile/base/config/json_configuration.h new file mode 100644 index 0000000..f9c774e --- /dev/null +++ b/tile/base/config/json_configuration.h @@ -0,0 +1,50 @@ +#ifndef TILE_BASE_CONFIG_JSON_CONFIGURATION_H +#define TILE_BASE_CONFIG_JSON_CONFIGURATION_H + +#pragma once + +#include "tile/base/config/configuration.h" +#include "json/json.h" + +namespace tile { +class JSONConfiguration : public Configuration { +public: + using Ptr = RefPtr; + JSONConfiguration(); + ~JSONConfiguration() override; + bool load(const std::string &path); + bool load(std::istream &istr); + + void SetInt(const Slice &key, int value) override; + void SetBool(const Slice &key, bool value) override; + void SetDouble(const Slice &key, double value) override; + void SetString(const Slice &key, const std::string &value) override; + void RemoveRaw(const Slice &key) override; + + std::string Dump() const; + +protected: + bool GetRaw(const Slice &key, std::string *value) const override; + bool SetRaw(const Slice &key, const Slice &value) override; + Keys Enumerate(const Slice &range) const override; + +private: + bool FindStart(const Slice &key, Slice *last_prt, Json::Value **parent_obj); + template + bool SetValue(const Slice &key, T value); + Json::Value object_; +}; + +template +bool +JSONConfiguration::SetValue(const Slice &key, T value) +{ + Slice last_part; + Json::Value *root; + if (!FindStart(key, &last_part, &root)) { return false; } + (*root)[last_part] = Json::Value(value); + return true; +} +}// namespace tile + +#endif// TILE_BASE_CONFIG_JSON_CONFIGURATION_H diff --git a/tile/base/config/json_configuration_test.cc b/tile/base/config/json_configuration_test.cc new file mode 100644 index 0000000..1a26478 --- /dev/null +++ b/tile/base/config/json_configuration_test.cc @@ -0,0 +1,100 @@ +#include "tile/base/config/json_configuration.h" + +#include "gmock/gmock-matchers.h" +#include "gtest/gtest.h" + +namespace tile { +namespace { +const char *kJsonConfig = R"( + { + "key1": "value1", + "key2": 2, + "key3": 3.0, + "key4": true, + "key5": { + "key51": "value51", + "key52": 52 + } + } + )"; +} + +TEST(JSONConfiguration, Load) +{ + JSONConfiguration config; + std::istringstream istr(kJsonConfig); + ASSERT_TRUE(config.load(istr)); +} + +TEST(JSONConfiguration, Has) +{ + JSONConfiguration config; + std::istringstream istr(kJsonConfig); + ASSERT_TRUE(config.load(istr)); + + ASSERT_TRUE(config.Has("key1")); + ASSERT_TRUE(config.Has("key2")); + ASSERT_TRUE(config.Has("key3")); + ASSERT_TRUE(config.Has("key4")); + ASSERT_TRUE(config.Has("key5")); + ASSERT_TRUE(config.Has("key5.key51")); + ASSERT_TRUE(config.Has("key5.key52")); +} + +TEST(JSONConfiguration, SampleSet) +{ + JSONConfiguration config; + std::istringstream istr(kJsonConfig); + ASSERT_TRUE(config.load(istr)); + ASSERT_TRUE(config.GetInt32("key2")); + ASSERT_EQ(*config.GetInt32("key2"), 2); + + config.SetInt("key2", 20); + ASSERT_TRUE(config.GetInt32("key2")); + ASSERT_EQ(20, *config.GetInt32("key2")) << config.Dump(); + + config.SetDouble("key3", 30.0); + ASSERT_TRUE(config.GetDouble("key3")); + ASSERT_NEAR(30.0, *config.GetDouble("key3"), 0.0001) << config.Dump(); + + config.SetBool("key4", false); + ASSERT_TRUE(config.GetBool("key4")); + ASSERT_FALSE(*config.GetBool("key4")) << config.Dump(); +} + +TEST(JSONConfiguration, LayeredSet) +{ + JSONConfiguration config; + std::istringstream istr(kJsonConfig); + ASSERT_TRUE(config.load(istr)); + + // ASSERT_TRUE(config.GetInt32("key5.key52")); + config.SetInt("key5.key52", 520); + ASSERT_TRUE(config.GetInt32("key5.key52")) << config.Dump(); + ASSERT_EQ(520, *config.GetInt32("key5.key52")) << config.Dump(); + + // override by other type + config.SetBool("key5.key52", false); + ASSERT_TRUE(config.GetBool("key5.key52")) << config.Dump(); + ASSERT_FALSE(*config.GetBool("key5.key52")) << config.Dump(); +} + +TEST(json_configuration, Enumerate) +{ + JSONConfiguration config; + std::istringstream istr(kJsonConfig); + ASSERT_TRUE(config.load(istr)); + auto keys = config.keys(""); + ASSERT_EQ(5, keys.size()); + ASSERT_THAT(keys, ::testing::UnorderedElementsAre("key1", "key2", "key3", "key4", "key5")); + + keys = config.keys("key5"); + ASSERT_EQ(2, keys.size()); + ASSERT_THAT(keys, ::testing::UnorderedElementsAre("key5.key51", "key5.key52")); + + config.Remove("key5.key51"); + keys = config.keys("key5"); + ASSERT_EQ("key5.key52", keys[0]); +} + +}// namespace tile diff --git a/tile/base/config/layered_config.cc b/tile/base/config/layered_config.cc deleted file mode 100644 index 3b90173..0000000 --- a/tile/base/config/layered_config.cc +++ /dev/null @@ -1,129 +0,0 @@ -#include "tile/base/config/layered_config.h" - -namespace tile { -namespace util { - -LayeredConfig::LayeredConfig() {} -LayeredConfig::~LayeredConfig() {} - -void LayeredConfig::Add(Config::Ptr cfg) { Add(cfg, highest(), false); } - -void LayeredConfig::Add(Config::Ptr cfg, int priority) { - Add(cfg, priority, false); -} - -void LayeredConfig::Add(Config::Ptr cfg, const std::string &label) { - Add(cfg, label, highest(), false); -} - -void LayeredConfig::Add(Config::Ptr cfg, const std::string &label, - int priority) { - Add(cfg, label, priority, false); -} - -void LayeredConfig::Add(Config::Ptr cfg, const std::string &label, - bool writeable) { - Add(cfg, label, highest(), writeable); -} - -void LayeredConfig::Add(Config::Ptr cfg, const std::string &label, int priority, - bool writeable) { - Config::ScopedLock lock(*this); - ConfigItem item; - item.cfg = cfg; - item.priority = priority; - item.writeable = writeable; - item.label = label; - auto it = configs_.begin(); - while (it != configs_.end() && it->priority < priority) { - ++it; - } - configs_.insert(it, item); -} - -void LayeredConfig::Add(Config::Ptr cfg, int priority, bool writeable) { - Add(cfg, std::string(), priority, writeable); -} -void LayeredConfig::AddWriteable(Config::Ptr cfg, int priority) { - Add(cfg, priority, true); -} - -Config::Ptr LayeredConfig::Find(const Slice &label) const { - Config::ScopedLock lock(*this); - - for (const auto &conf : configs_) { - if (conf.label == label) { - return conf.cfg; - } - } - return 0; -} - -void LayeredConfig::RemoveConfig(Config::Ptr cfg) { - Config::ScopedLock lock(*this); - for (auto it = configs_.begin(); it != configs_.end();) { - if (it->cfg == cfg) { - it = configs_.erase(it); - } else { - ++it; - } - } -} - -bool LayeredConfig::GetRaw(const Slice &key, std::string *value) const { - for (const auto &conf : configs_) { - if (conf.cfg->GetRaw(key, value)) { - return true; - } - } - return false; -} - -bool LayeredConfig::SetRaw(const Slice &key, const Slice &value) { - for (const auto &conf : configs_) { - if (conf.writeable && conf.cfg->SetRaw(key, value)) { - return true; - } - } - return false; -} - -Config::Keys LayeredConfig::Enumerate(const Slice &key) const { - Config::Keys keys; - std::set key_set; - for (const auto &conf : configs_) { - auto conf_keys = conf.cfg->Enumerate(key); - for (const auto &k : conf_keys) { - if (key_set.insert(k).second) { - keys.push_back(k); - } - } - } - return keys; -} - -void LayeredConfig::RemoveRaw(const std::string &key) { - for (auto &conf : configs_) { - if (conf.writeable) { - conf.cfg->RemoveRaw(key); - } - } -} - -int LayeredConfig::lowest() const { - if (configs_.empty()) { - return 0; - } else { - return configs_.front().priority - 1; - } -} -int LayeredConfig::highest() const { - if (configs_.empty()) { - return 0; - } else { - return configs_.back().priority + 1; - } -} - -} // namespace util -} // namespace tile diff --git a/tile/base/config/layered_config.h b/tile/base/config/layered_config.h deleted file mode 100644 index e481515..0000000 --- a/tile/base/config/layered_config.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef TILE_BASE_CONFIG_LAYERED_CONFIG_H -#define TILE_BASE_CONFIG_LAYERED_CONFIG_H - -#pragma once - -#include "tile/base/config/config.h" -#include - -namespace tile { -namespace util { -class LayeredConfig : public Config { -public: - using Ptr = RefPtr; - LayeredConfig(); - LayeredConfig(const LayeredConfig &) = delete; - LayeredConfig &operator=(const LayeredConfig &) = delete; - - void Add(Config::Ptr cfg); - void Add(Config::Ptr cfg, int priority); - void Add(Config::Ptr cfg, const std::string &label); - void Add(Config::Ptr cfg, const std::string &label, int priority); - void Add(Config::Ptr cfg, const std::string &label, bool writeable); - void Add(Config::Ptr cfg, const std::string &label, int priority, - bool writeable); - void Add(Config::Ptr cfg, int priority, bool writeable); - void AddWriteable(Config::Ptr cfg, int priority); - Config::Ptr Find(const Slice &label) const; - void RemoveConfig(Config::Ptr cfg); - -protected: - struct ConfigItem { - Config::Ptr cfg; - // ... > -2 > -1 > 0 > 1 > 2 > 3 > 4 ... - int priority; - // can remove or set new? - bool writeable; - std::string label; - }; - - bool GetRaw(const Slice &key, std::string *value) const override; - bool SetRaw(const Slice &key, const Slice &value) override; - Config::Keys Enumerate(const Slice &key) const override; - void RemoveRaw(const std::string &key); - - int lowest() const; - int highest() const; - - ~LayeredConfig(); - -private: - using ConfigList = std::list; - ConfigList configs_; -}; -} // namespace util -} // namespace tile - -#endif // TILE_BASE_CONFIG_LAYERED_CONFIG_H diff --git a/tile/base/config/layered_configuration.cc b/tile/base/config/layered_configuration.cc new file mode 100644 index 0000000..d07e380 --- /dev/null +++ b/tile/base/config/layered_configuration.cc @@ -0,0 +1,149 @@ +#include "tile/base/config/layered_configuration.h" + +namespace tile { + +LayeredConfiguration::LayeredConfiguration() {} + +LayeredConfiguration::~LayeredConfiguration() {} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg) +{ + Add(cfg, highest(), false); +} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg, int priority) +{ + Add(cfg, priority, false); +} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label) +{ + Add(cfg, label, highest(), false); +} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, int priority) +{ + Add(cfg, label, priority, false); +} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, bool writeable) +{ + Add(cfg, label, highest(), writeable); +} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, int priority, bool writeable) +{ + Configuration::ScopedLock lock(*this); + ConfigItem item; + item.cfg = cfg; + item.priority = priority; + item.writeable = writeable; + item.label = label; + auto it = configs_.begin(); + while (it != configs_.end() && it->priority < priority) { ++it; } + configs_.insert(it, item); +} + +void +LayeredConfiguration::Add(Configuration::Ptr cfg, int priority, bool writeable) +{ + Add(cfg, std::string(), priority, writeable); +} + +void +LayeredConfiguration::AddWriteable(Configuration::Ptr cfg, int priority) +{ + Add(cfg, priority, true); +} + +Configuration::Ptr +LayeredConfiguration::Find(const Slice &label) const +{ + Configuration::ScopedLock lock(*this); + + for (const auto &conf : configs_) { + if (conf.label == label) { return conf.cfg; } + } + return 0; +} + +void +LayeredConfiguration::RemoveConfig(Configuration::Ptr cfg) +{ + Configuration::ScopedLock lock(*this); + for (auto it = configs_.begin(); it != configs_.end();) { + if (it->cfg == cfg) { + it = configs_.erase(it); + } else { + ++it; + } + } +} + +bool +LayeredConfiguration::GetRaw(const Slice &key, std::string *value) const +{ + for (const auto &conf : configs_) { + if (conf.cfg->GetRaw(key, value)) { return true; } + } + return false; +} + +bool +LayeredConfiguration::SetRaw(const Slice &key, const Slice &value) +{ + for (const auto &conf : configs_) { + if (conf.writeable && conf.cfg->SetRaw(key, value)) { return true; } + } + return false; +} + +Configuration::Keys +LayeredConfiguration::Enumerate(const Slice &key) const +{ + Configuration::Keys keys; + std::set key_set; + for (const auto &conf : configs_) { + auto conf_keys = conf.cfg->Enumerate(key); + for (const auto &k : conf_keys) { + if (key_set.insert(k).second) { keys.push_back(k); } + } + } + return keys; +} + +void +LayeredConfiguration::RemoveRaw(const std::string &key) +{ + for (auto &conf : configs_) { + if (conf.writeable) { conf.cfg->RemoveRaw(key); } + } +} + +int +LayeredConfiguration::lowest() const +{ + if (configs_.empty()) { + return 0; + } else { + return configs_.front().priority - 1; + } +} + +int +LayeredConfiguration::highest() const +{ + if (configs_.empty()) { + return 0; + } else { + return configs_.back().priority + 1; + } +} + +}// namespace tile diff --git a/tile/base/config/layered_configuration.h b/tile/base/config/layered_configuration.h new file mode 100644 index 0000000..aba8f4a --- /dev/null +++ b/tile/base/config/layered_configuration.h @@ -0,0 +1,54 @@ +#ifndef TILE_BASE_CONFIG_LAYERED_CONFIGURATION_H +#define TILE_BASE_CONFIG_LAYERED_CONFIGURATION_H + +#pragma once + +#include "tile/base/config/configuration.h" +#include + +namespace tile { +class LayeredConfiguration : public Configuration { +public: + using Ptr = RefPtr; + LayeredConfiguration(); + LayeredConfiguration(const LayeredConfiguration &) = delete; + LayeredConfiguration &operator=(const LayeredConfiguration &) = delete; + + void Add(Configuration::Ptr cfg); + void Add(Configuration::Ptr cfg, int priority); + void Add(Configuration::Ptr cfg, const std::string &label); + void Add(Configuration::Ptr cfg, const std::string &label, int priority); + void Add(Configuration::Ptr cfg, const std::string &label, bool writeable); + void Add(Configuration::Ptr cfg, const std::string &label, int priority, bool writeable); + void Add(Configuration::Ptr cfg, int priority, bool writeable); + void AddWriteable(Configuration::Ptr cfg, int priority); + Configuration::Ptr Find(const Slice &label) const; + void RemoveConfig(Configuration::Ptr cfg); + +protected: + struct ConfigItem { + Configuration::Ptr cfg; + // ... > -2 > -1 > 0 > 1 > 2 > 3 > 4 ... + int priority; + // can remove or set new? + bool writeable; + std::string label; + }; + + bool GetRaw(const Slice &key, std::string *value) const override; + bool SetRaw(const Slice &key, const Slice &value) override; + Configuration::Keys Enumerate(const Slice &key) const override; + void RemoveRaw(const std::string &key); + + int lowest() const; + int highest() const; + + ~LayeredConfiguration(); + +private: + using ConfigList = std::list; + ConfigList configs_; +}; +}// namespace tile + +#endif// TILE_BASE_CONFIG_LAYERED_CONFIGURATION_H diff --git a/tile/base/config/toml_configuration.cc b/tile/base/config/toml_configuration.cc new file mode 100644 index 0000000..87ea4f3 --- /dev/null +++ b/tile/base/config/toml_configuration.cc @@ -0,0 +1,219 @@ +#include "tile/base/config/toml_configuration.h" + +#include "tile/base/logging.h" +#include "tile/base/string.h" +#include "toml.hpp" + +namespace tile { +class TomlConfiguration::Impl { +public: + Impl() {} + + ~Impl() = default; + + bool load(std::istream &istr) + { + try { + value_ = toml::parse(istr); + return true; + } catch (...) { + return false; + } + } + + bool load(const std::string &path) + { + try { + value_ = toml::parse(path); + return true; + } catch (...) { + return false; + } + } + + toml::value *operator->() { return &value_; } + + toml::value &operator*() { return value_; } + +private: + toml::value value_; +}; + +TomlConfiguration::TomlConfiguration() : impl_(new Impl) {} + +TomlConfiguration::~TomlConfiguration() {} + +bool +TomlConfiguration::load(std::istream &istr) +{ + return impl_->load(istr); +} + +bool +TomlConfiguration::load(const std::string &path) +{ + return impl_->load(path); +} + +void +TomlConfiguration::SetInt(const Slice &key, int value) +{ + Configuration::SetInt(key, value); +} + +void +TomlConfiguration::SetBool(const Slice &key, bool value) +{ + Configuration::SetBool(key, value); +} + +void +TomlConfiguration::SetDouble(const Slice &key, double value) +{ + Configuration::SetDouble(key, value); +} + +void +TomlConfiguration::SetString(const Slice &key, const std::string &value) +{ + Configuration::SetString(key, value); +} + +std::string +TomlConfiguration::Dump() const +{ + try { + return toml::format(**impl_); + } catch (const std::exception &e) { + TILE_LOG_WARNING_EVERY_SECOND("{}", e.what()); + return ""; + } +} + +bool +TomlConfiguration::GetRaw(const Slice &key, std::string *value) const +{ + if (key.empty()) { return false; } + auto keys = Split(key, '.'); + + try { + auto cur = **impl_; + for (const auto &k : keys) { + auto next = toml::find(cur, k); + cur = next; + } + std::stringstream ss; + if (cur.is_string()) { + ss << cur.as_string(); + } else if (cur.is_integer()) { + ss << cur.as_integer(); + } else if (cur.is_boolean()) { + ss << cur.as_boolean(); + } else if (cur.is_floating()) { + ss << cur.as_floating(); + } else if (cur.is_empty()) { + ss << ""; + } else { + ss << toml::format(cur); + } + *value = ss.str(); + return true; + } catch (const std::exception &e) { + TILE_LOG_WARNING_EVERY_SECOND("{}", e.what()); + return false; + } +} + +bool +TomlConfiguration::SetRaw(const Slice &key, const Slice &value) +{ + if (key.empty()) { return false; } + auto keys = Split(key, '.'); + try { + auto &root = **impl_; + for (size_t i = 0; i < keys.size() - 1; ++i) { + auto &next = toml::find(root, keys[i]); + root = next; + } + root[keys.back()] = value.ToString(); + return true; + } catch (const std::exception &e) { + TILE_LOG_WARNING_EVERY_SECOND("{}", e.what()); + return false; + } +} + +void +TomlConfiguration::RemoveRaw(const Slice &key) +{ + if (key.empty()) { return; } + auto keys = Split(key, '.'); + while (!keys.empty() && keys.back() == "") { keys.pop_back(); } + try { + if (keys.empty()) { + auto &root = **impl_; + root = toml::table(); + return; + } + + auto root = **impl_; + for (size_t i = 0; i + 1 < keys.size(); ++i) { + if (!root.contains(keys[i])) { return; } + root = root.at(keys[i]); + } + + if (!root.contains(keys.back())) { return; } + auto last_root = root.at(key.back()); + + // remove the last key + toml::value new_node = toml::table(); + for (const auto &p : last_root.as_table()) { + if (p.first != keys.back()) { new_node[p.first] = p.second; } + } + last_root = new_node; + } catch (...) {} +} + +Configuration::Keys +TomlConfiguration::Enumerate(const Slice &range) const +{ + Configuration::Keys keys; + auto split_keys = Split(range, '.'); + auto last_key = split_keys.empty() ? "" : split_keys.back(); + std::string prefix = range; + if (!prefix.empty() && prefix.back() != '.') { prefix += "."; } + + auto root = **impl_; + try { + // if (!root.is_empty()) { + for (int i = 0; i < split_keys.size(); ++i) { + auto next = toml::find(root, split_keys[i]); + root = next; + } + // } + + // if (root.is_empty()) { return keys; } + + // for (const auto &k : keys_) { + // auto next = toml::find(root, k); + // root = next; + // } + + auto visitor = [&](const toml::value &node) { + if (node.is_table()) { + for (const auto &p : node.as_table()) { + /*if (EndsWith(p.first, prefix)) */ { + keys.push_back(prefix + p.first); + } + } + } + }; + toml::visit(visitor, root); + // TILE_LOG_INFO("{}", toml::format(root)); + } catch (const std::exception &e) { + TILE_LOG_WARNING_EVERY_SECOND("{}\n{}\n", e.what(), Dump()); + } + return keys; +} + +}// namespace tile diff --git a/tile/base/config/toml_configuration.h b/tile/base/config/toml_configuration.h new file mode 100644 index 0000000..20d9b22 --- /dev/null +++ b/tile/base/config/toml_configuration.h @@ -0,0 +1,40 @@ +#ifndef TILE_BASE_CONFIG_TOML_CONFIGURATION_H +#define TILE_BASE_CONFIG_TOML_CONFIGURATION_H + +#pragma once + +#include "tile/base/config/configuration.h" +#include + +namespace tile { +class TomlConfiguration : public Configuration { +public: + using Ptr = RefPtr; + + TomlConfiguration(); + ~TomlConfiguration() override; + + bool load(std::istream &istr); + bool load(const std::string &path); + + void SetInt(const Slice &key, int value) override; + void SetBool(const Slice &key, bool value) override; + void SetDouble(const Slice &key, double value) override; + void SetString(const Slice &key, const std::string &value) override; + + void RemoveRaw(const Slice &key) override; + std::string Dump() const; + +protected: + bool GetRaw(const Slice &key, std::string *value) const override; + bool SetRaw(const Slice &key, const Slice &value) override; + Keys Enumerate(const Slice &range) const override; + +private: + class Impl; + + std::unique_ptr impl_; +}; +}// namespace tile + +#endif// TILE_BASE_CONFIG_TOML_CONFIGURATION_H diff --git a/tile/base/config/toml_configuration_test.cc b/tile/base/config/toml_configuration_test.cc new file mode 100644 index 0000000..21511e8 --- /dev/null +++ b/tile/base/config/toml_configuration_test.cc @@ -0,0 +1,71 @@ +#include "tile/base/config/toml_configuration.h" + +#include "gmock/gmock-matchers.h" +#include "gtest/gtest.h" + +namespace tile { +namespace { +const char *kTomlConfig = R"( +key1=1 +key2=2 +key3=1.9 +key4=true + +[key5] +key51="value51" +key52=52 +)"; +} + +TEST(TomlConfiguration, Load) +{ + TomlConfiguration config; + std::istringstream istr(kTomlConfig); + ASSERT_TRUE(config.load(istr)); +} + +TEST(TomlConfiguration, Has) +{ + TomlConfiguration config; + std::istringstream istr(kTomlConfig); + ASSERT_TRUE(config.load(istr)); + ASSERT_TRUE(config.Has("key1")); + ASSERT_TRUE(config.Has("key2")); + ASSERT_TRUE(config.Has("key3")); + ASSERT_TRUE(config.Has("key4")); + ASSERT_TRUE(config.Has("key5")); + ASSERT_TRUE(config.Has("key5.key51")); + ASSERT_TRUE(config.Has("key5.key52")); +} + +TEST(TomlConfiguration, SampleSet) +{ + TomlConfiguration config; + std::istringstream istr(kTomlConfig); + ASSERT_TRUE(config.load(istr)); + ASSERT_TRUE(config.GetInt32("key2")); + ASSERT_EQ(*config.GetInt32("key2"), 2); + + config.SetInt("key2", 20); +} + +TEST(TomlConfiguration, Enumerate) +{ + TomlConfiguration config; + std::istringstream istr(kTomlConfig); + ASSERT_TRUE(config.load(istr)); + auto keys = config.keys(""); + ASSERT_EQ(5, keys.size()); + ASSERT_THAT(keys, ::testing::UnorderedElementsAre("key1", "key2", "key3", "key4", "key5")); + + keys = config.keys("key5"); + // for (const auto &key : keys) { TILE_LOG_INFO("key5: {}", key); } + ASSERT_EQ(2, keys.size()); + ASSERT_THAT(keys, ::testing::UnorderedElementsAre("key5.key51", "key5.key52")); + + config.Remove("key5.key51"); + keys = config.keys("key5"); + ASSERT_EQ("key5.key52", keys[0]); +} + +}// namespace tile diff --git a/tile/base/configuration.h b/tile/base/configuration.h new file mode 100644 index 0000000..af53510 --- /dev/null +++ b/tile/base/configuration.h @@ -0,0 +1,12 @@ +#ifndef TILE_BASE_CONFIGURATION_H +#define TILE_BASE_CONFIGURATION_H + +#pragma once + +#include "tile/base/config/configurable.h" +#include "tile/base/config/configuration.h" +#include "tile/base/config/ini_file_configuration.h" +#include "tile/base/config/layered_configuration.h" +#include "tile/base/config/toml_configuration.h" + +#endif// TILE_BASE_CONFIGURATION_H diff --git a/tile/base/data.h b/tile/base/data.h index 77a321d..8f17b7d 100644 --- a/tile/base/data.h +++ b/tile/base/data.h @@ -4,4 +4,4 @@ #pragma once #include "tile/base/data/json.h" -#endif // TILE_BASE_DATA_H +#endif// TILE_BASE_DATA_H diff --git a/tile/base/data/json.h b/tile/base/data/json.h index 21dec79..3c69b7e 100644 --- a/tile/base/data/json.h +++ b/tile/base/data/json.h @@ -11,28 +11,34 @@ namespace tile { -template -auto ToJson(const T &value) - -> enable_if_t::value, Json::Value> { - return static_cast(value); +template +auto +ToJson(const T &value) -> enable_if_t::value, Json::Value> +{ + return static_cast(value); } -template -auto ToJson(const T &value) - -> enable_if_t::value, Json::Value> { - return static_cast(value); -} -template -auto ToJson(const T &value) - -> enable_if_t::value && !std::is_unsigned::value, - Json::Value> { - return Format("{}", value); +template +auto +ToJson(const T &value) -> enable_if_t::value, Json::Value> +{ + return static_cast(value); } -template Json::Value ToJson(const std::atomic &v) { - return ToJson(v.load(std::memory_order_relaxed)); +template +auto +ToJson(const T &value) -> enable_if_t::value && !std::is_unsigned::value, Json::Value> +{ + return Format("{}", value); } -} // namespace tile +template +Json::Value +ToJson(const std::atomic &v) +{ + return ToJson(v.load(std::memory_order_relaxed)); +} -#endif // TILE_BASE_DATA_JSON_H +}// namespace tile + +#endif// TILE_BASE_DATA_JSON_H diff --git a/tile/base/deferred.h b/tile/base/deferred.h index cf5a054..ce0297e 100644 --- a/tile/base/deferred.h +++ b/tile/base/deferred.h @@ -9,56 +9,57 @@ namespace tile { class ScopedDeferred { public: - template - explicit ScopedDeferred(F &&f) : action_(std::move(f)) {} + template + explicit ScopedDeferred(F &&f) : action_(std::move(f)) + {} - ~ScopedDeferred() { action_(); } - ScopedDeferred(const ScopedDeferred &) = delete; - ScopedDeferred &operator=(const ScopedDeferred &) = delete; + ~ScopedDeferred() { action_(); } + + ScopedDeferred(const ScopedDeferred &) = delete; + ScopedDeferred &operator=(const ScopedDeferred &) = delete; private: - std::function action_; + std::function action_; }; class Deferred { public: - Deferred() = default; - template - explicit Deferred(F &&f) : action_(std::forward(f)) {} + Deferred() = default; - Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) { - other.action_ = nullptr; - } - Deferred &operator=(Deferred &&other) noexcept { - if (&other == this) { - return *this; + template + explicit Deferred(F &&f) : action_(std::forward(f)) + {} + + Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) { other.action_ = nullptr; } + + Deferred &operator=(Deferred &&other) noexcept + { + if (&other == this) { return *this; } + + Fire(); + action_ = std::move(other.action_); + other.action_ = nullptr; + return *this; } - Fire(); - action_ = std::move(other.action_); - other.action_ = nullptr; - return *this; - } - ~Deferred() { - if (action_) { - action_(); + ~Deferred() + { + if (action_) { action_(); } } - } - explicit operator bool() const noexcept { return !!action_; } + explicit operator bool() const noexcept { return !!action_; } - void Fire() noexcept { - if (auto op = std::move(action_)) { - op(); + void Fire() noexcept + { + if (auto op = std::move(action_)) { op(); } } - } - void Dimiss() noexcept { action_ = nullptr; } + void Dimiss() noexcept { action_ = nullptr; } private: - std::function action_; + std::function action_; }; -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_DEFERED_H +#endif// _TILE_BASE_DEFERED_H diff --git a/tile/base/deferred_test.cc b/tile/base/deferred_test.cc index 373768a..5a73ce5 100644 --- a/tile/base/deferred_test.cc +++ b/tile/base/deferred_test.cc @@ -2,49 +2,51 @@ #include "gtest/gtest.h" namespace tile { -TEST(ScopedDefered, All) { - bool f = false; - { - ScopedDeferred scoped_defered([&] { f = true; }); - ASSERT_FALSE(f); - } - ASSERT_TRUE(f); +TEST(ScopedDefered, All) +{ + bool f = false; + { + ScopedDeferred scoped_defered([&] { f = true; }); + ASSERT_FALSE(f); + } + ASSERT_TRUE(f); } -TEST(Defered, All) { - bool f1 = false; - bool f2 = false; - { - Deferred defer([&] { f1 = true; }); - ASSERT_FALSE(f1); - Deferred defer2([&] { f2 = true; }); - defer2.Dimiss(); +TEST(Defered, All) +{ + bool f1 = false; + bool f2 = false; + { + Deferred defer([&] { f1 = true; }); + ASSERT_FALSE(f1); + Deferred defer2([&] { f2 = true; }); + defer2.Dimiss(); + ASSERT_FALSE(f2); + } + ASSERT_TRUE(f1); ASSERT_FALSE(f2); - } - ASSERT_TRUE(f1); - ASSERT_FALSE(f2); - bool f3 = false; - Deferred defer3([&] { f3 = true; }); - ASSERT_FALSE(f3); - defer3.Fire(); - ASSERT_TRUE(f3); + bool f3 = false; + Deferred defer3([&] { f3 = true; }); + ASSERT_FALSE(f3); + defer3.Fire(); + ASSERT_TRUE(f3); - bool f4 = false; - Deferred defer5; - { - Deferred defer4([&] { f4 = true; }); + bool f4 = false; + Deferred defer5; + { + Deferred defer4([&] { f4 = true; }); + ASSERT_FALSE(f4); + defer5 = std::move(defer4); + } ASSERT_FALSE(f4); - defer5 = std::move(defer4); - } - ASSERT_FALSE(f4); - auto defer6 = std::move(defer5); - defer5.Fire(); - ASSERT_FALSE(defer5); - defer6.Fire(); - ASSERT_TRUE(f4); + auto defer6 = std::move(defer5); + defer5.Fire(); + ASSERT_FALSE(defer5); + defer6.Fire(); + ASSERT_TRUE(f4); - Deferred().Fire(); - Deferred().Dimiss(); + Deferred().Fire(); + Deferred().Dimiss(); } -} // namespace tile +}// namespace tile diff --git a/tile/base/demangle.cc b/tile/base/demangle.cc index 7d68f68..97fbbf9 100644 --- a/tile/base/demangle.cc +++ b/tile/base/demangle.cc @@ -10,21 +10,21 @@ namespace tile { -std::string Demangle(const char *s) { +std::string +Demangle(const char *s) +{ #ifdef _MSC_VER - return s; -#elif defined(__GNUC__) || defined(__clang__) - int status; - char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status); - ScopedDeferred _([&] { free(demangled); }); - if (!demangled) { return s; - } - return demangled; +#elif defined(__GNUC__) || defined(__clang__) + int status; + char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status); + ScopedDeferred _([&] { free(demangled); }); + if (!demangled) { return s; } + return demangled; #else #error "Demangle not supported on current compiler." #endif } -} // namespace tile +}// namespace tile diff --git a/tile/base/demangle.h b/tile/base/demangle.h index 8df72b8..00109ce 100644 --- a/tile/base/demangle.h +++ b/tile/base/demangle.h @@ -11,8 +11,11 @@ namespace tile { std::string Demangle(const char *s); -template std::string GetTypeName() { - return Demangle(typeid(T).name()); +template +std::string +GetTypeName() +{ + return Demangle(typeid(T).name()); } #if __GNUC__ >= 12 @@ -20,14 +23,17 @@ template std::string GetTypeName() { #pragma GCC diagnostic ignored "-Wnonnull-compare" #endif -template std::string GetTypeName(T &&o) { - return Demangle(typeid(std::forward(o)).name()); +template +std::string +GetTypeName(T &&o) +{ + return Demangle(typeid(std::forward(o)).name()); } #if __GNUC__ >= 12 #pragma GCC diagnostic pop #endif -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_DEMANGLE_H +#endif// _TILE_BASE_DEMANGLE_H diff --git a/tile/base/demangle_test.cc b/tile/base/demangle_test.cc index 68ae2b0..81970de 100644 --- a/tile/base/demangle_test.cc +++ b/tile/base/demangle_test.cc @@ -3,16 +3,16 @@ namespace tile { struct C { - struct D { - struct E {}; - }; + struct D { + struct E {}; + }; }; -TEST(Demangle, All) { - ASSERT_EQ("tile::C::D::E", GetTypeName()); - ASSERT_NE(GetTypeName(), typeid(C::D::E).name()); - ASSERT_EQ("invalid function name !@#$", - Demangle("invalid function name !@#$")); +TEST(Demangle, All) +{ + ASSERT_EQ("tile::C::D::E", GetTypeName()); + ASSERT_NE(GetTypeName(), typeid(C::D::E).name()); + ASSERT_EQ("invalid function name !@#$", Demangle("invalid function name !@#$")); } -} // namespace tile +}// namespace tile diff --git a/tile/base/dependency_registry.h b/tile/base/dependency_registry.h index ac23ae6..14a3db8 100644 --- a/tile/base/dependency_registry.h +++ b/tile/base/dependency_registry.h @@ -21,186 +21,174 @@ #include // class dependency -#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \ - extern ::tile::detail::dependency_registry::ClassRegistry< \ - struct Registry, Interface, ##__VA_ARGS__> &Registry +#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \ + extern ::tile::detail::dependency_registry::ClassRegistry &Registry -#define TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \ - ::tile::detail::dependency_registry::ClassRegistry< \ - struct Registry, Interface, ##__VA_ARGS__> &Registry = \ - **::tile::internal::LazyInit<::tile::NeverDestroyed< \ - ::tile::detail::dependency_registry::ClassRegistry< \ - struct Registry, Interface, ##__VA_ARGS__>>>() +#define TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \ + ::tile::detail::dependency_registry::ClassRegistry &Registry = \ + **::tile::internal::LazyInit<::tile::NeverDestroyed< \ + ::tile::detail::dependency_registry::ClassRegistry>>() -#define TILE_REGISTER_CLASS_DEPENDENCY(Registry, ImplementationName, \ - ImplementationClassName, ...) \ - TILE_REGISTER_CLASS_DEPENDENCY_FACTORY( \ - Registry, ImplementationName, \ - TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE( \ - ImplementationClassName, ##__VA_ARGS__)) +#define TILE_REGISTER_CLASS_DEPENDENCY(Registry, ImplementationName, ImplementationClassName, ...) \ + TILE_REGISTER_CLASS_DEPENDENCY_FACTORY( \ + Registry, ImplementationName, \ + TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(ImplementationClassName, ##__VA_ARGS__)) -#define TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(Registry, Name, Factory) \ - __attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \ - tile_reserved_registry_dependency_class_register_, __COUNTER__)() { \ - auto registry = ::tile::internal::LazyInit<::tile::NeverDestroyed< \ - typename std::decay::type>>() \ - ->Get(); \ - ::tile::detail::dependency_registry::AddToRegistry(registry, Name, \ - Factory); \ - } +#define TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(Registry, Name, Factory) \ + __attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \ + tile_reserved_registry_dependency_class_register_, __COUNTER__)() \ + { \ + auto registry = \ + ::tile::internal::LazyInit<::tile::NeverDestroyed::type>>() \ + ->Get(); \ + ::tile::detail::dependency_registry::AddToRegistry(registry, Name, Factory); \ + } // object dependency -#define TILE_DECLARE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \ - extern ::tile::detail::dependency_registry::ObjectRegistry< \ - struct Registry, Interface> &Registry +#define TILE_DECLARE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \ + extern ::tile::detail::dependency_registry::ObjectRegistry &Registry -#define TILE_DEFINE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \ - ::tile::detail::dependency_registry::ObjectRegistry &Registry = \ - **::tile::internal::LazyInit<::tile::NeverDestroyed< \ - ::tile::detail::dependency_registry::ObjectRegistry>>() +#define TILE_DEFINE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \ + ::tile::detail::dependency_registry::ObjectRegistry &Registry = \ + **::tile::internal::LazyInit< \ + ::tile::NeverDestroyed<::tile::detail::dependency_registry::ObjectRegistry>>() -#define TILE_REGISTER_OBJECT_DEPENDENCY(Registry, ObjectName, \ - PointerOrFactory) \ - __attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \ - tile_reserved_registry_dependency_object_register_, __COUNTER__)() { \ - auto registry = ::tile::internal::LazyInit<::tile::NeverDestroyed< \ - typename std::decay::type>>() \ - ->Get(); \ - ::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, \ - PointerOrFactory); \ - } +#define TILE_REGISTER_OBJECT_DEPENDENCY(Registry, ObjectName, PointerOrFactory) \ + __attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \ + tile_reserved_registry_dependency_object_register_, __COUNTER__)() \ + { \ + auto registry = \ + ::tile::internal::LazyInit<::tile::NeverDestroyed::type>>() \ + ->Get(); \ + ::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, PointerOrFactory); \ + } namespace tile { namespace detail { namespace dependency_registry { -#define TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(Interface, ...) \ - ::tile::detail::dependency_registry::FactoryAux -template -std::unique_ptr FactoryAux(Args &&...args) { - return make_unique(std::forward(args)...); +#define TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(Interface, ...) \ + ::tile::detail::dependency_registry::FactoryAux + +template +std::unique_ptr +FactoryAux(Args &&...args) +{ + return make_unique(std::forward(args)...); } -template -void AddToRegistry(T *registry, Args &&...args) { - registry->Register(std::forward(args)...); +template +void +AddToRegistry(T *registry, Args &&...args) +{ + registry->Register(std::forward(args)...); } -template +template class ClassRegistry { - using Factory = std::function(FactoryArgs...)>; + using Factory = std::function(FactoryArgs...)>; public: - Factory TryGetFactory(Slice name) const noexcept { - auto iter = factories_.find(name.ToString()); - if (iter == factories_.end()) { - return nullptr; + Factory TryGetFactory(Slice name) const noexcept + { + auto iter = factories_.find(name.ToString()); + if (iter == factories_.end()) { return nullptr; } + + auto constructor_ptr = iter->second.get(); + return + [constructor_ptr](FactoryArgs... args) { return (*constructor_ptr)(std::forward(args)...); }; } - auto constructor_ptr = iter->second.get(); - return [constructor_ptr](FactoryArgs... args) { - return (*constructor_ptr)(std::forward(args)...); + Factory GetFactory(const std::string &name) const noexcept + { + auto result = TryGetFactory(name); + TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName()); + + return result; + } + + std::unique_ptr TryNew(Slice name, FactoryArgs... args) const + { + auto iter = factories_.find(name.ToString()); + if (iter == factories_.end()) { return nullptr; } + + auto &constructor = iter->second; + return (*constructor)(std::forward(args)...); + } + + std::unique_ptr New(const std::string &name, FactoryArgs... args) const + { + auto result = TryNew(name, std::forward(args)...); + TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName()); + return result; + } + +private: + template + friend void AddToRegistry(T *registry, Args &&...args); + + void Register(const std::string &name, Factory factory) + { + TILE_CHECK(factories_.find(name) == factories_.end(), "Duplicate class dependency: "); + factories_[name] = make_unique(std::move(factory)); + } + +private: + std::unordered_map> factories_; +}; + +template +class ObjectRegistry { +public: + Interface *TryGet(Slice name) const + { + auto iter = objects_.find(name.ToString()); + if (iter == objects_.end()) { return nullptr; } + auto &&e = *iter->second; + std::call_once(e.flag, [&] { e.object = e.initializer(); }); + return e.object.Get(); + } + + Interface *Get(Slice name) const + { + auto result = TryGet(name); + TILE_CHECK(result, "Object dependency [{}] implementing interface [{}] is not found", name, + GetTypeName()); + return result; + } + +private: + template + friend void AddToRegistry(T *registry, Args &&...args); + + void Register(const std::string &name, Interface *object) + { + TILE_DCHECK(objects_.find(name) == objects_.end()); + auto &&e = objects_[name]; + e = make_unique(); + std::call_once(e->flag, [&] { e->object = MaybeOwning(non_owning, object); }); + } + + void Register(const std::string &name, std::function()> initializer) + { + TILE_CHECK(objects_.find(name) == objects_.end(), "Double registration of object dependency [{}]", name); + + objects_[name] = make_unique(); + objects_[name]->initializer = std::move(initializer); + } + +private: + struct LazilyInstantiatedObject { + std::once_flag flag; + MaybeOwning object; + std::function()> initializer; }; - } - Factory GetFactory(const std::string &name) const noexcept { - auto result = TryGetFactory(name); - TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", - name, GetTypeName()); - - return result; - } - - std::unique_ptr TryNew(Slice name, FactoryArgs... args) const { - auto iter = factories_.find(name.ToString()); - if (iter == factories_.end()) { - return nullptr; - } - - auto &constructor = iter->second; - return (*constructor)(std::forward(args)...); - } - - std::unique_ptr New(const std::string &name, - FactoryArgs... args) const { - auto result = TryNew(name, std::forward(args)...); - TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", - name, GetTypeName()); - return result; - } - -private: - template - friend void AddToRegistry(T *registry, Args &&...args); - - void Register(const std::string &name, Factory factory) { - TILE_CHECK(factories_.find(name) == factories_.end(), - "Duplicate class dependency: "); - factories_[name] = make_unique(std::move(factory)); - } - -private: - std::unordered_map> factories_; + std::unordered_map> objects_; }; -template class ObjectRegistry { -public: - Interface *TryGet(Slice name) const { - auto iter = objects_.find(name.ToString()); - if (iter == objects_.end()) { - return nullptr; - } - auto &&e = *iter->second; - std::call_once(e.flag, [&] { e.object = e.initializer(); }); - return e.object.Get(); - } +}// namespace dependency_registry +}// namespace detail +}// namespace tile - Interface *Get(Slice name) const { - auto result = TryGet(name); - TILE_CHECK( - result, - "Object dependency [{}] implementing interface [{}] is not found", name, - GetTypeName()); - return result; - } - -private: - template - friend void AddToRegistry(T *registry, Args &&...args); - - void Register(const std::string &name, Interface *object) { - TILE_DCHECK(objects_.find(name) == objects_.end()); - auto &&e = objects_[name]; - e = make_unique(); - std::call_once(e->flag, [&] { - e->object = MaybeOwning(non_owning, object); - }); - } - - void Register(const std::string &name, - std::function()> initializer) { - TILE_CHECK(objects_.find(name) == objects_.end(), - "Double registration of object dependency [{}]", name); - - objects_[name] = make_unique(); - objects_[name]->initializer = std::move(initializer); - } - -private: - struct LazilyInstantiatedObject { - std::once_flag flag; - MaybeOwning object; - std::function()> initializer; - }; - - std::unordered_map> - objects_; -}; - -} // namespace dependency_registry -} // namespace detail -} // namespace tile - -#endif // TILE_BASE_DEPENDENCY_REGISTRY_H +#endif// TILE_BASE_DEPENDENCY_REGISTRY_H diff --git a/tile/base/dependency_registry_test.cc b/tile/base/dependency_registry_test.cc index 89f7164..df2c3ff 100644 --- a/tile/base/dependency_registry_test.cc +++ b/tile/base/dependency_registry_test.cc @@ -1,77 +1,84 @@ #include "tile/base/dependency_registry.h" #include "gtest/gtest.h" + namespace tile { struct Destroyer { - virtual ~Destroyer() = default; + virtual ~Destroyer() = default; }; struct FastDestroyer : Destroyer { - FastDestroyer() { ++instances; } - ~FastDestroyer() override { --instances; } - static int instances; + FastDestroyer() { ++instances; } + + ~FastDestroyer() override { --instances; } + + static int instances; }; + int FastDestroyer::instances = 0; struct GentleDestroyer : Destroyer { - GentleDestroyer() { ++instances; } - ~GentleDestroyer() override { --instances; } - static int instances; + GentleDestroyer() { ++instances; } + + ~GentleDestroyer() override { --instances; } + + static int instances; }; + int GentleDestroyer::instances = 0; struct SpeedDestroyer : Destroyer { - explicit SpeedDestroyer(int) {} + explicit SpeedDestroyer(int) {} }; + struct SpeedDestroyer2 : Destroyer {}; TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer); TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer); -TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer", - FastDestroyer); +TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer", FastDestroyer); TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(world_destroyer, "gentle-destroyer", [] { - return make_unique(); + return make_unique(); }); GentleDestroyer global_gentle_destroyer; TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int); TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int); -TILE_REGISTER_CLASS_DEPENDENCY(with_arg_destroyer, "speed-destroyer", - SpeedDestroyer, int); -TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2", - [](int speed) { - return make_unique(); - }); +TILE_REGISTER_CLASS_DEPENDENCY(with_arg_destroyer, "speed-destroyer", SpeedDestroyer, int); +TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2", [](int speed) { + return make_unique(); +}); -TEST(DependencyRegistry, Class) { - EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer")); - EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer")); - EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer")); - EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer")); - EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer")); - EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer")); +TEST(DependencyRegistry, Class) +{ + EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer")); + EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer")); + EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer")); + EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer")); + EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer")); + EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer")); - EXPECT_EQ(1, GentleDestroyer::instances); // The global one. - EXPECT_EQ(0, FastDestroyer::instances); - - { - auto gentle = world_destroyer.TryNew("gentle-destroyer"); - EXPECT_EQ(2, GentleDestroyer::instances); + EXPECT_EQ(1, GentleDestroyer::instances);// The global one. + EXPECT_EQ(0, FastDestroyer::instances); + + { + auto gentle = world_destroyer.TryNew("gentle-destroyer"); + EXPECT_EQ(2, GentleDestroyer::instances); + EXPECT_EQ(0, FastDestroyer::instances); + auto fast = world_destroyer.TryNew("fast-destroyer"); + EXPECT_EQ(2, GentleDestroyer::instances); + EXPECT_EQ(1, FastDestroyer::instances); + } + EXPECT_EQ(1, GentleDestroyer::instances); EXPECT_EQ(0, FastDestroyer::instances); - auto fast = world_destroyer.TryNew("fast-destroyer"); - EXPECT_EQ(2, GentleDestroyer::instances); - EXPECT_EQ(1, FastDestroyer::instances); - } - EXPECT_EQ(1, GentleDestroyer::instances); - EXPECT_EQ(0, FastDestroyer::instances); } -TEST(DependencyRegistry, ClassWithArgs) { - EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer")); - EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2")); - EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3")); - EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456)); - EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer-2", 456)); - EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456)); +TEST(DependencyRegistry, ClassWithArgs) +{ + EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer")); + EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2")); + EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3")); + EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456)); + EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer-2", 456)); + EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456)); } -} // namespace tile +}// namespace tile diff --git a/tile/base/down_cast.h b/tile/base/down_cast.h index fa5ed7a..bde1421 100644 --- a/tile/base/down_cast.h +++ b/tile/base/down_cast.h @@ -8,18 +8,22 @@ #include namespace tile { -template ::value, - const To *, To *>::type> -inline R down_cast(From *ptr) { - TILE_DCHECK(dynamic_cast(ptr)); - return static_cast(ptr); +template::value, const To *, To *>::type> +inline R +down_cast(From *ptr) +{ + TILE_DCHECK(dynamic_cast(ptr)); + return static_cast(ptr); } -template -inline auto down_cast(From &ref) -> decltype(down_cast(&ref)) { - return down_cast(&ref); +template +inline auto +down_cast(From &ref) -> decltype(down_cast(&ref)) +{ + return down_cast(&ref); } -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_DOWN_CAST_H +#endif// _TILE_BASE_DOWN_CAST_H diff --git a/tile/base/down_cast_test.cc b/tile/base/down_cast_test.cc index afde504..57b135d 100644 --- a/tile/base/down_cast_test.cc +++ b/tile/base/down_cast_test.cc @@ -3,22 +3,25 @@ namespace tile { struct A { - virtual ~A() = default; + virtual ~A() = default; }; + struct B : A {}; -TEST(DownCast, All) { - B b; - A *ptr = &b; - ASSERT_NE(nullptr, down_cast(ptr)); - ASSERT_NE(nullptr, down_cast(const_cast(ptr))); +TEST(DownCast, All) +{ + B b; + A *ptr = &b; + ASSERT_NE(nullptr, down_cast(ptr)); + ASSERT_NE(nullptr, down_cast(const_cast(ptr))); } #ifndef NDEBUG -TEST(CastDeathTest, DownCast) { - A a; - A *ptr = &a; - ASSERT_DEATH(down_cast(ptr), ""); +TEST(CastDeathTest, DownCast) +{ + A a; + A *ptr = &a; + ASSERT_DEATH(down_cast(ptr), ""); } #endif -} // namespace tile +}// namespace tile diff --git a/tile/base/encoding.h b/tile/base/encoding.h index 827dabf..698e337 100644 --- a/tile/base/encoding.h +++ b/tile/base/encoding.h @@ -7,4 +7,4 @@ #include "tile/base/encoding/hex.h" #include "tile/base/encoding/percent.h" -#endif // _TILE_BASE_ENCODING_H +#endif// _TILE_BASE_ENCODING_H diff --git a/tile/base/encoding/base64.cc b/tile/base/encoding/base64.cc index 98f536c..490645d 100644 --- a/tile/base/encoding/base64.cc +++ b/tile/base/encoding/base64.cc @@ -19,137 +19,132 @@ static constexpr std::size_t kBufferSize = BLOCK_SIZE_1M; #undef BLOCK_SIZE_64K #undef BLOCK_SIZE_1M -} // namespace +}// namespace -std::string EncodeBase64(Slice from) { - std::string result; - EncodeBase64(from, &result); - return result; -} -std::optional DecodeBase64(Slice from) { - std::string result; - if (!DecodeBase64(from, &result)) { - return std::nullopt; - } - return result; +std::string +EncodeBase64(Slice from) +{ + std::string result; + EncodeBase64(from, &result); + return result; } -void EncodeBase64(Slice from, std::string *to) { - TILE_CHECK(from.size() < std::numeric_limits::max(), - "Not implemented: Source bytes too long. {} > max_size({})", - from.size(), std::numeric_limits::max()); - - const auto size = 4 * ((from.size() + 2) / 3); - to->resize(size); - base64_encodestate state; - base64_init_encodestate(&state); - - std::size_t used = 0; - std::size_t input_len; - while (!from.empty()) { - input_len = std::min(kBufferSize, from.size()); - used += base64_encode_block( - reinterpret_cast(from.data()), input_len, - reinterpret_cast(internal::RemoveConstPtr(to->data())) + used, - &state); - from.RemovePrefix(input_len); - }; - base64_encode_blockend( - reinterpret_cast(internal::RemoveConstPtr(to->data())) + used, - &state); - - // bool success = - // Base64::Encode(from.data(), from.size(), - // internal::RemoveConstPtr(to->data()), to->size()); - // TILE_CHECK(success, "Unexpected: Failed to do base64-encode."); +std::optional +DecodeBase64(Slice from) +{ + std::string result; + if (!DecodeBase64(from, &result)) { return std::nullopt; } + return result; } -bool DecodeBase64(Slice from, std::string *to) { - if (TILE_UNLIKELY(from.empty())) { - to->clear(); - return true; - } +void +EncodeBase64(Slice from, std::string *to) +{ + TILE_CHECK(from.size() < std::numeric_limits::max(), + "Not implemented: Source bytes too long. {} > max_size({})", from.size(), + std::numeric_limits::max()); - std::string safe_from_str; - // auto padding - auto padding = (4 - from.size() % 4) % 4; - if (padding > 2) { - return false; - } else if (padding != 0) { - TILE_LOG_WARNING("`from.size()` [{}] Can not be divisible by 4. " - "auto add padding [{}] `=`", - from.size(), padding); - safe_from_str = from.ToString() + std::string(padding, '='); - from = safe_from_str; - } + const auto size = 4 * ((from.size() + 2) / 3); + to->resize(size); + base64_encodestate state; + base64_init_encodestate(&state); - auto size = 3 * from.size() / 4; - to->resize(size); - - { - Slice input = from; - base64_decodestate state; - base64_init_decodestate(&state); std::size_t used = 0; - while (!input.empty()) { - auto input_len = std::min(kBufferSize, from.size()); - auto encoded_len = base64_decode_block( - input.data(), input_len, - reinterpret_cast(internal::RemoveConstPtr(to->data())) + used, - &state); - if (TILE_UNLIKELY(encoded_len == 0)) { - return false; - } - used += encoded_len; - input.RemovePrefix(input_len); - } - } + std::size_t input_len; + while (!from.empty()) { + input_len = std::min(kBufferSize, from.size()); + used += base64_encode_block(reinterpret_cast(from.data()), input_len, + reinterpret_cast(internal::RemoveConstPtr(to->data())) + used, &state); + from.RemovePrefix(input_len); + }; + base64_encode_blockend(reinterpret_cast(internal::RemoveConstPtr(to->data())) + used, &state); - // === Sample - // { - // bool success = - // Base64::Decode(from.data(), from.size(), - // internal::RemoveConstPtr(to->data()), to->size()); - // if (!success) { - // return false; - // } - // } - - // === OpenSSL - // int rc; - // rc = EVP_DecodeBlock( - // reinterpret_cast(internal::RemoveConstPtr(to->data())), reinterpret_cast(from.data()), from.size()); - // if (rc != size) { - // return false; - // } - // - // @sa: https://tools.ietf.org/html/rfc4648#section-4 - // - // (1) The final quantum of encoding input is an integral multiple of 24 - // bits; here, the final unit of encoded output will be an integral - // multiple of 4 characters with no "=" padding. - // - // (2) The final quantum of encoding input is exactly 8 bits; here, the - // final unit of encoded output will be two characters followed by - // two "=" padding characters. - // - // (3) The final quantum of encoding input is exactly 16 bits; here, the - // final unit of encoded output will be three characters followed by - // one "=" padding character. - TILE_CHECK(from.size() >= 2 && to->size() >= 2); - if (from[from.size() - 1] == '=') { - to->pop_back(); - } - - if (from[from.size() - 2] == '=') { - to->pop_back(); - } - - // to->pop_back(); // Remove Terminating null - - return true; + // bool success = + // Base64::Encode(from.data(), from.size(), + // internal::RemoveConstPtr(to->data()), to->size()); + // TILE_CHECK(success, "Unexpected: Failed to do base64-encode."); } -} // namespace tile +bool +DecodeBase64(Slice from, std::string *to) +{ + if (TILE_UNLIKELY(from.empty())) { + to->clear(); + return true; + } + + std::string safe_from_str; + // auto padding + auto padding = (4 - from.size() % 4) % 4; + if (padding > 2) { + return false; + } else if (padding != 0) { + TILE_LOG_WARNING("`from.size()` [{}] Can not be divisible by 4. " + "auto add padding [{}] `=`", + from.size(), padding); + safe_from_str = from.ToString() + std::string(padding, '='); + from = safe_from_str; + } + + auto size = 3 * from.size() / 4; + to->resize(size); + + { + Slice input = from; + base64_decodestate state; + base64_init_decodestate(&state); + std::size_t used = 0; + while (!input.empty()) { + auto input_len = std::min(kBufferSize, from.size()); + auto encoded_len = base64_decode_block( + input.data(), input_len, reinterpret_cast(internal::RemoveConstPtr(to->data())) + used, &state); + if (TILE_UNLIKELY(encoded_len == 0)) { return false; } + used += encoded_len; + input.RemovePrefix(input_len); + } + } + + // === Sample + // { + // bool success = + // Base64::Decode(from.data(), from.size(), + // internal::RemoveConstPtr(to->data()), to->size()); + // if (!success) { + // return false; + // } + // } + + // === OpenSSL + // int rc; + // rc = EVP_DecodeBlock( + // reinterpret_cast(internal::RemoveConstPtr(to->data())), reinterpret_cast(from.data()), from.size()); + // if (rc != size) { + // return false; + // } + // + // @sa: https://tools.ietf.org/html/rfc4648#section-4 + // + // (1) The final quantum of encoding input is an integral multiple of 24 + // bits; here, the final unit of encoded output will be an integral + // multiple of 4 characters with no "=" padding. + // + // (2) The final quantum of encoding input is exactly 8 bits; here, the + // final unit of encoded output will be two characters followed by + // two "=" padding characters. + // + // (3) The final quantum of encoding input is exactly 16 bits; here, the + // final unit of encoded output will be three characters followed by + // one "=" padding character. + TILE_CHECK(from.size() >= 2 && to->size() >= 2); + if (from[from.size() - 1] == '=') { to->pop_back(); } + + if (from[from.size() - 2] == '=') { to->pop_back(); } + + // to->pop_back(); // Remove Terminating null + + return true; +} + +}// namespace tile diff --git a/tile/base/encoding/base64.h b/tile/base/encoding/base64.h index 09a19ae..87a3c68 100644 --- a/tile/base/encoding/base64.h +++ b/tile/base/encoding/base64.h @@ -13,6 +13,6 @@ std::optional DecodeBase64(Slice from); void EncodeBase64(Slice from, std::string *to); bool DecodeBase64(Slice from, std::string *to); -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_ENCODING_BASE64_H +#endif// _TILE_BASE_ENCODING_BASE64_H diff --git a/tile/base/encoding/base64_test.cc b/tile/base/encoding/base64_test.cc index 7996f52..b851bbf 100644 --- a/tile/base/encoding/base64_test.cc +++ b/tile/base/encoding/base64_test.cc @@ -9,70 +9,75 @@ namespace tile { -static constexpr auto kText = ".<>@????????"; -static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/"; -static constexpr auto kText2 = ".<>@???????"; +static constexpr auto kText = ".<>@????????"; +static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/"; +static constexpr auto kText2 = ".<>@???????"; static constexpr auto kBase64Text2 = "Ljw+QD8/Pz8/Pz8="; -TEST(Base64, Default) { - EXPECT_EQ(kBase64Text, EncodeBase64(kText)); - auto decoded = DecodeBase64(kBase64Text); - ASSERT_TRUE(decoded); - EXPECT_EQ(kText, *decoded); - EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!")); -} - -TEST(Base64, Padding) { - EXPECT_EQ(kBase64Text2, EncodeBase64(kText2)); - - auto decoded = DecodeBase64(kBase64Text2); - ASSERT_TRUE(decoded); - EXPECT_EQ(kText2, *decoded); -} - -TEST(Base64, Empty) { - EXPECT_EQ("", EncodeBase64("")); - auto decoded = DecodeBase64(""); - ASSERT_TRUE(decoded); - EXPECT_EQ("", *decoded); -} - -TEST(Base64, AutoPadding) { - static constexpr auto kChar = "A"; - - EXPECT_EQ("QQ==", EncodeBase64(kChar)); - - { - auto decoded = DecodeBase64("QQ=="); +TEST(Base64, Default) +{ + EXPECT_EQ(kBase64Text, EncodeBase64(kText)); + auto decoded = DecodeBase64(kBase64Text); ASSERT_TRUE(decoded); - EXPECT_EQ(kChar, *decoded); - } - - // auto padding, 1 - { - auto decoded1 = DecodeBase64("QQ="); - ASSERT_TRUE(decoded1); - EXPECT_EQ(kChar, *decoded1); - } - - // auto padding, 2 - { - auto decoded2 = DecodeBase64("QQ"); - ASSERT_TRUE(decoded2); - EXPECT_EQ(kChar, *decoded2); - } + EXPECT_EQ(kText, *decoded); + EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!")); } -TEST(Base64, Torture) { - std::string random_str; - std::string encoded; - std::string decoded; - for (int i = 0; i != 1000; ++i) { - testing::RandomString(&random_str, 10000, 10000); - EncodeBase64(random_str, &encoded); - ASSERT_TRUE(DecodeBase64(encoded, &decoded)); - EXPECT_EQ(random_str, decoded); - } +TEST(Base64, Padding) +{ + EXPECT_EQ(kBase64Text2, EncodeBase64(kText2)); + + auto decoded = DecodeBase64(kBase64Text2); + ASSERT_TRUE(decoded); + EXPECT_EQ(kText2, *decoded); } -} // namespace tile +TEST(Base64, Empty) +{ + EXPECT_EQ("", EncodeBase64("")); + auto decoded = DecodeBase64(""); + ASSERT_TRUE(decoded); + EXPECT_EQ("", *decoded); +} + +TEST(Base64, AutoPadding) +{ + static constexpr auto kChar = "A"; + + EXPECT_EQ("QQ==", EncodeBase64(kChar)); + + { + auto decoded = DecodeBase64("QQ=="); + ASSERT_TRUE(decoded); + EXPECT_EQ(kChar, *decoded); + } + + // auto padding, 1 + { + auto decoded1 = DecodeBase64("QQ="); + ASSERT_TRUE(decoded1); + EXPECT_EQ(kChar, *decoded1); + } + + // auto padding, 2 + { + auto decoded2 = DecodeBase64("QQ"); + ASSERT_TRUE(decoded2); + EXPECT_EQ(kChar, *decoded2); + } +} + +TEST(Base64, Torture) +{ + std::string random_str; + std::string encoded; + std::string decoded; + for (int i = 0; i != 1000; ++i) { + testing::RandomString(&random_str, 10000, 10000); + EncodeBase64(random_str, &encoded); + ASSERT_TRUE(DecodeBase64(encoded, &decoded)); + EXPECT_EQ(random_str, decoded); + } +} + +}// namespace tile diff --git a/tile/base/encoding/detail/base64_impl.h b/tile/base/encoding/detail/base64_impl.h index 6e8034f..e3ba4cd 100644 --- a/tile/base/encoding/detail/base64_impl.h +++ b/tile/base/encoding/detail/base64_impl.h @@ -3,286 +3,247 @@ #include -const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; +const char kBase64Alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; namespace { -static inline unsigned char b64_lookup_aux(unsigned char c) { - if (c >= 'A' && c <= 'Z') - return c - 'A'; - if (c >= 'a' && c <= 'z') - return c - 71; - if (c >= '0' && c <= '9') - return c + 4; - if (c == '+') - return 62; - if (c == '/') - return 63; - return 255; +static inline unsigned char +b64_lookup_aux(unsigned char c) +{ + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 71; + if (c >= '0' && c <= '9') return c + 4; + if (c == '+') return 62; + if (c == '/') return 63; + return 255; } -struct Base64LookupInitializer { - Base64LookupInitializer() { - for (int i = 0; i < 256; i++) { - kBase64Lookup[i] = b64_lookup_aux(i); - } - } - int kBase64Lookup[256]; +struct Base64LookupInitializer { + Base64LookupInitializer() + { + for (int i = 0; i < 256; i++) { kBase64Lookup[i] = b64_lookup_aux(i); } + } + + int kBase64Lookup[256]; }; -static inline unsigned char b64_lookup_aux1(unsigned char c) { - static Base64LookupInitializer init; - return init.kBase64Lookup[c]; +static inline unsigned char +b64_lookup_aux1(unsigned char c) +{ + static Base64LookupInitializer init; + return init.kBase64Lookup[c]; +} +}// namespace + +inline unsigned char +b64_lookup(unsigned char c) +{ + return b64_lookup_aux1(c); } -} // namespace -inline unsigned char b64_lookup(unsigned char c) { return b64_lookup_aux1(c); } class Base64 { public: - static bool Encode(const std::string &in, std::string *out) { - int i = 0, j = 0; - size_t enc_len = 0; - unsigned char a3[3]; - unsigned char a4[4]; + static bool Encode(const std::string &in, std::string *out) + { + int i = 0, j = 0; + size_t enc_len = 0; + unsigned char a3[3]; + unsigned char a4[4]; - out->resize(EncodedLength(in)); + out->resize(EncodedLength(in)); - size_t input_len = in.size(); - std::string::const_iterator input = in.begin(); + size_t input_len = in.size(); + std::string::const_iterator input = in.begin(); - while (input_len--) { - a3[i++] = *(input++); - if (i == 3) { - a3_to_a4(a4, a3); + while (input_len--) { + a3[i++] = *(input++); + if (i == 3) { + a3_to_a4(a4, a3); - for (i = 0; i < 4; i++) { - (*out)[enc_len++] = kBase64Alphabet[a4[i]]; + for (i = 0; i < 4; i++) { (*out)[enc_len++] = kBase64Alphabet[a4[i]]; } + + i = 0; + } } - i = 0; - } - } + if (i) { + for (j = i; j < 3; j++) { a3[j] = '\0'; } - if (i) { - for (j = i; j < 3; j++) { - a3[j] = '\0'; - } + a3_to_a4(a4, a3); - a3_to_a4(a4, a3); + for (j = 0; j < i + 1; j++) { (*out)[enc_len++] = kBase64Alphabet[a4[j]]; } - for (j = 0; j < i + 1; j++) { - (*out)[enc_len++] = kBase64Alphabet[a4[j]]; - } - - while ((i++ < 3)) { - (*out)[enc_len++] = '='; - } - } - - return (enc_len == out->size()); - } - - static bool Encode(const char *input, size_t input_length, char *out, - size_t out_length) { - int i = 0, j = 0; - char *out_begin = out; - unsigned char a3[3]; - unsigned char a4[4]; - - size_t encoded_length = EncodedLength(input_length); - - if (out_length < encoded_length) - return false; - - while (input_length--) { - a3[i++] = *input++; - if (i == 3) { - a3_to_a4(a4, a3); - - for (i = 0; i < 4; i++) { - *out++ = kBase64Alphabet[a4[i]]; - } - // out[0] = kBase64Alphabet[a4[0]]; - // out[1] = kBase64Alphabet[a4[1]]; - // out[2] = kBase64Alphabet[a4[2]]; - // out[3] = kBase64Alphabet[a4[3]]; - // out += 4; - - i = 0; - } - } - - if (i) { - for (j = i; j < 3; j++) { - a3[j] = '\0'; - } - - a3_to_a4(a4, a3); - - for (j = 0; j < i + 1; j++) { - *out++ = kBase64Alphabet[a4[j]]; - } - - while ((i++ < 3)) { - *out++ = '='; - } - } - - return (out == (out_begin + encoded_length)); - } - - static bool Decode(const std::string &in, std::string *out) { - int i = 0, j = 0; - size_t dec_len = 0; - unsigned char a3[3]; - unsigned char a4[4]; - - size_t input_len = in.size(); - std::string::const_iterator input = in.begin(); - - out->resize(DecodedLength(in)); - - while (input_len--) { - if (*input == '=') { - break; - } - - a4[i++] = *(input++); - if (i == 4) { - for (i = 0; i < 4; i++) { - a4[i] = b64_lookup(a4[i]); + while ((i++ < 3)) { (*out)[enc_len++] = '='; } } - a4_to_a3(a3, a4); + return (enc_len == out->size()); + } - for (i = 0; i < 3; i++) { - (*out)[dec_len++] = a3[i]; + static bool Encode(const char *input, size_t input_length, char *out, size_t out_length) + { + int i = 0, j = 0; + char *out_begin = out; + unsigned char a3[3]; + unsigned char a4[4]; + + size_t encoded_length = EncodedLength(input_length); + + if (out_length < encoded_length) return false; + + while (input_length--) { + a3[i++] = *input++; + if (i == 3) { + a3_to_a4(a4, a3); + + for (i = 0; i < 4; i++) { *out++ = kBase64Alphabet[a4[i]]; } + // out[0] = kBase64Alphabet[a4[0]]; + // out[1] = kBase64Alphabet[a4[1]]; + // out[2] = kBase64Alphabet[a4[2]]; + // out[3] = kBase64Alphabet[a4[3]]; + // out += 4; + + i = 0; + } } - i = 0; - } - } + if (i) { + for (j = i; j < 3; j++) { a3[j] = '\0'; } - if (i) { - for (j = i; j < 4; j++) { - a4[j] = '\0'; - } + a3_to_a4(a4, a3); - for (j = 0; j < 4; j++) { - a4[j] = b64_lookup(a4[j]); - } + for (j = 0; j < i + 1; j++) { *out++ = kBase64Alphabet[a4[j]]; } - a4_to_a3(a3, a4); - - for (j = 0; j < i - 1; j++) { - (*out)[dec_len++] = a3[j]; - } - } - - return (dec_len == out->size()); - } - - static bool Decode(const char *input, size_t input_length, char *out, - size_t out_length) { - int i = 0, j = 0; - char *out_begin = out; - unsigned char a3[3]; - unsigned char a4[4]; - - size_t decoded_length = DecodedLength(input, input_length); - - if (out_length < decoded_length) - return false; - - while (input_length--) { - if (*input == '=') { - break; - } - - a4[i++] = *(input++); - if (i == 4) { - for (i = 0; i < 4; i++) { - a4[i] = b64_lookup(a4[i]); + while ((i++ < 3)) { *out++ = '='; } } - a4_to_a3(a3, a4); + return (out == (out_begin + encoded_length)); + } - for (i = 0; i < 3; i++) { - *out++ = a3[i]; + static bool Decode(const std::string &in, std::string *out) + { + int i = 0, j = 0; + size_t dec_len = 0; + unsigned char a3[3]; + unsigned char a4[4]; + + size_t input_len = in.size(); + std::string::const_iterator input = in.begin(); + + out->resize(DecodedLength(in)); + + while (input_len--) { + if (*input == '=') { break; } + + a4[i++] = *(input++); + if (i == 4) { + for (i = 0; i < 4; i++) { a4[i] = b64_lookup(a4[i]); } + + a4_to_a3(a3, a4); + + for (i = 0; i < 3; i++) { (*out)[dec_len++] = a3[i]; } + + i = 0; + } } - i = 0; - } + if (i) { + for (j = i; j < 4; j++) { a4[j] = '\0'; } + + for (j = 0; j < 4; j++) { a4[j] = b64_lookup(a4[j]); } + + a4_to_a3(a3, a4); + + for (j = 0; j < i - 1; j++) { (*out)[dec_len++] = a3[j]; } + } + + return (dec_len == out->size()); } - if (i) { - for (j = i; j < 4; j++) { - a4[j] = '\0'; - } + static bool Decode(const char *input, size_t input_length, char *out, size_t out_length) + { + int i = 0, j = 0; + char *out_begin = out; + unsigned char a3[3]; + unsigned char a4[4]; - for (j = 0; j < 4; j++) { - a4[j] = b64_lookup(a4[j]); - } + size_t decoded_length = DecodedLength(input, input_length); - a4_to_a3(a3, a4); + if (out_length < decoded_length) return false; - for (j = 0; j < i - 1; j++) { - *out++ = a3[j]; - } + while (input_length--) { + if (*input == '=') { break; } + + a4[i++] = *(input++); + if (i == 4) { + for (i = 0; i < 4; i++) { a4[i] = b64_lookup(a4[i]); } + + a4_to_a3(a3, a4); + + for (i = 0; i < 3; i++) { *out++ = a3[i]; } + + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) { a4[j] = '\0'; } + + for (j = 0; j < 4; j++) { a4[j] = b64_lookup(a4[j]); } + + a4_to_a3(a3, a4); + + for (j = 0; j < i - 1; j++) { *out++ = a3[j]; } + } + + return (out == (out_begin + decoded_length)); } - return (out == (out_begin + decoded_length)); - } + static size_t DecodedLength(const char *in, size_t in_length) + { + int numEq = 0; - static size_t DecodedLength(const char *in, size_t in_length) { - int numEq = 0; + const char *in_end = in + in_length; + while (*--in_end == '=') ++numEq; - const char *in_end = in + in_length; - while (*--in_end == '=') - ++numEq; - - return ((6 * in_length) / 8) - numEq; - } - - static size_t DecodedLength(const std::string &in) { - int numEq = 0; - size_t n = in.size(); - - for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; - ++it) { - ++numEq; + return ((6 * in_length) / 8) - numEq; } - return ((6 * n) / 8) - numEq; - } + static size_t DecodedLength(const std::string &in) + { + int numEq = 0; + size_t n = in.size(); - inline static size_t EncodedLength(size_t length) { - return (length + 2 - ((length + 2) % 3)) / 3 * 4; - } + for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; } - inline static size_t EncodedLength(const std::string &in) { - return EncodedLength(in.length()); - } + return ((6 * n) / 8) - numEq; + } - inline static void StripPadding(std::string *in) { - while (!in->empty() && *(in->rbegin()) == '=') - in->resize(in->size() - 1); - } + inline static size_t EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; } + + inline static size_t EncodedLength(const std::string &in) { return EncodedLength(in.length()); } + + inline static void StripPadding(std::string *in) + { + while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1); + } private: - static inline void a3_to_a4(unsigned char *a4, unsigned char *a3) { - a4[0] = (a3[0] & 0xfc) >> 2; - a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); - a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); - a4[3] = (a3[2] & 0x3f); - } + static inline void a3_to_a4(unsigned char *a4, unsigned char *a3) + { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = (a3[2] & 0x3f); + } - static inline void a4_to_a3(unsigned char *a3, unsigned char *a4) { - a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); - a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); - a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; - } + static inline void a4_to_a3(unsigned char *a3, unsigned char *a4) + { + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; + } }; -#endif // BASE64_H +#endif// BASE64_H diff --git a/tile/base/encoding/detail/hex_chars.cc b/tile/base/encoding/detail/hex_chars.cc index de6cdab..222c74e 100644 --- a/tile/base/encoding/detail/hex_chars.cc +++ b/tile/base/encoding/detail/hex_chars.cc @@ -6,80 +6,88 @@ namespace detail { namespace { static constexpr auto kCharMin = std::numeric_limits::min(); static constexpr auto kCharMax = std::numeric_limits::max(); -static constexpr auto kSize = kCharMax - kCharMin + 1; +static constexpr auto kSize = kCharMax - kCharMin + 1; -int AsciiCodeFromCharPairSlow(char a, char b) { - a = ToLower(a); - b = ToLower(b); - auto ToNum = [](char x) { - if (x >= '0' && x <= '9') { - return x - '0'; - } else if (x >= 'a' && x <= 'f') { - return x - 'a' + 10; +int +AsciiCodeFromCharPairSlow(char a, char b) +{ + a = ToLower(a); + b = ToLower(b); + auto ToNum = [](char x) { + if (x >= '0' && x <= '9') { + return x - '0'; + } else if (x >= 'a' && x <= 'f') { + return x - 'a' + 10; + } else { + return -1; + } + }; + + int x = ToNum(a); + int y = ToNum(b); + if (x == -1 || y == -1) { return -1; } + + return (x << 4) | y; +} + +std::pair +AsciiCodeToCharPairSlow(std::uint8_t value, bool uppercase) +{ + if (uppercase) { + return std::make_pair(kHexCharsUpper[value >> 4], kHexCharsUpper[value & 0x0f]); } else { - return -1; + return std::make_pair(kHexCharsLower[value >> 4], kHexCharsLower[value & 0x0f]); } - }; - - int x = ToNum(a); - int y = ToNum(b); - if (x == -1 || y == -1) { - return -1; - } - - return (x << 4) | y; } -std::pair AsciiCodeToCharPairSlow(std::uint8_t value, - bool uppercase) { - if (uppercase) { - return std::make_pair(kHexCharsUpper[value >> 4], - kHexCharsUpper[value & 0x0f]); - } else { - return std::make_pair(kHexCharsLower[value >> 4], - kHexCharsLower[value & 0x0f]); - } -} - -std::array, 256> GenAsciiCodeFromCharPairTable() { - std::array, 256> table; - for (char i = kCharMin; i != kCharMax; ++i) { - for (char j = kCharMin; j != kCharMax; ++j) { - table[i - kCharMin][j - kCharMin] = AsciiCodeFromCharPairSlow(i, j); +std::array, 256> +GenAsciiCodeFromCharPairTable() +{ + std::array, 256> table; + for (char i = kCharMin; i != kCharMax; ++i) { + for (char j = kCharMin; j != kCharMax; ++j) { + table[i - kCharMin][j - kCharMin] = AsciiCodeFromCharPairSlow(i, j); + } } - } - return table; + return table; } std::array, 256> -GenAsciiCodeToCharPairTable(bool uppercase) { - std::array, 256> table; - for (int i = 0; i != 256; ++i) { - table[i] = AsciiCodeToCharPairSlow(i, uppercase); - } - return table; +GenAsciiCodeToCharPairTable(bool uppercase) +{ + std::array, 256> table; + for (int i = 0; i != 256; ++i) { table[i] = AsciiCodeToCharPairSlow(i, uppercase); } + return table; } -} // namespace +}// namespace -int AsciiCodeFromCharPair(char a, char b) { - static auto table = GenAsciiCodeFromCharPairTable(); - return table[a - kCharMin][b - kCharMin]; +int +AsciiCodeFromCharPair(char a, char b) +{ + static auto table = GenAsciiCodeFromCharPairTable(); + return table[a - kCharMin][b - kCharMin]; } -inline std::pair AsciiCodeToUpperCharPair(std::uint8_t value) { - static auto table = GenAsciiCodeToCharPairTable(true); - return table[value]; -} -inline std::pair AsciiCodeToLowerCharPair(std::uint8_t value) { - static auto table = GenAsciiCodeToCharPairTable(false); - return table[value]; +inline std::pair +AsciiCodeToUpperCharPair(std::uint8_t value) +{ + static auto table = GenAsciiCodeToCharPairTable(true); + return table[value]; } -std::pair AsciiCodeToCharPair(std::uint8_t value, bool uppercase) { - return uppercase ? AsciiCodeToUpperCharPair(value) - : AsciiCodeToLowerCharPair(value); +inline std::pair +AsciiCodeToLowerCharPair(std::uint8_t value) +{ + static auto table = GenAsciiCodeToCharPairTable(false); + return table[value]; } -} // namespace detail -} // namespace tile +std::pair +AsciiCodeToCharPair(std::uint8_t value, bool uppercase) +{ + return uppercase ? AsciiCodeToUpperCharPair(value) : AsciiCodeToLowerCharPair(value); +} + +}// namespace detail +}// namespace tile diff --git a/tile/base/encoding/detail/hex_chars.h b/tile/base/encoding/detail/hex_chars.h index f29db0a..eca32c7 100644 --- a/tile/base/encoding/detail/hex_chars.h +++ b/tile/base/encoding/detail/hex_chars.h @@ -13,7 +13,7 @@ namespace tile { namespace detail { struct CharPair { - char a, b; + char a, b; }; static constexpr char kHexCharsLower[] = "0123456789abcdef"; @@ -23,10 +23,9 @@ static constexpr char kHexCharsUpper[] = "0123456789ABCDEF"; int AsciiCodeFromCharPair(char a, char b); -std::pair AsciiCodeToCharPair(std::uint8_t value, - bool uppercase = true); +std::pair AsciiCodeToCharPair(std::uint8_t value, bool uppercase = true); -} // namespace detail -} // namespace tile +}// namespace detail +}// namespace tile -#endif // _TILE_BASE_ENCODING_DETAIL_HEX_CHARS_H +#endif// _TILE_BASE_ENCODING_DETAIL_HEX_CHARS_H diff --git a/tile/base/encoding/hex.cc b/tile/base/encoding/hex.cc index 3ed8f36..60ba213 100644 --- a/tile/base/encoding/hex.cc +++ b/tile/base/encoding/hex.cc @@ -6,48 +6,49 @@ namespace tile { -std::string EncodeHex(Slice from, bool uppercase) { - std::string result; - EncodeHex(from, &result, uppercase); - return result; +std::string +EncodeHex(Slice from, bool uppercase) +{ + std::string result; + EncodeHex(from, &result, uppercase); + return result; } -std::optional DecodeHex(Slice from) { - std::string result; - if (!DecodeHex(from, &result)) { - return std::nullopt; - } - return result; +std::optional +DecodeHex(Slice from) +{ + std::string result; + if (!DecodeHex(from, &result)) { return std::nullopt; } + return result; } -void EncodeHex(Slice from, std::string *to, bool uppercase) { - to->clear(); - to->reserve(from.size() * 2); - for (auto &&e : from) { - // auto index = static_cast(e); - auto pair = - detail::AsciiCodeToCharPair(static_cast(e), uppercase); - to->append({pair.first, pair.second}); - } +void +EncodeHex(Slice from, std::string *to, bool uppercase) +{ + to->clear(); + to->reserve(from.size() * 2); + for (auto &&e : from) { + // auto index = static_cast(e); + auto pair = detail::AsciiCodeToCharPair(static_cast(e), uppercase); + to->append({pair.first, pair.second}); + } } -bool DecodeHex(Slice from, std::string *to) { - if (from.size() % 2 != 0) { - return false; - } +bool +DecodeHex(Slice from, std::string *to) +{ + if (from.size() % 2 != 0) { return false; } - to->clear(); - to->reserve(from.size() / 2); - for (size_t i = 0; i != from.size(); i += 2) { - auto v = detail::AsciiCodeFromCharPair(from[i], from[i + 1]); - if (v == -1) { - return false; + to->clear(); + to->reserve(from.size() / 2); + for (size_t i = 0; i != from.size(); i += 2) { + auto v = detail::AsciiCodeFromCharPair(from[i], from[i + 1]); + if (v == -1) { return false; } + + TILE_CHECK(v >= 0 && v <= 255); + to->push_back(v); } - TILE_CHECK(v >= 0 && v <= 255); - to->push_back(v); - } - - return true; + return true; } -} // namespace tile +}// namespace tile diff --git a/tile/base/encoding/hex.h b/tile/base/encoding/hex.h index 3ecacc8..98d916e 100644 --- a/tile/base/encoding/hex.h +++ b/tile/base/encoding/hex.h @@ -11,6 +11,6 @@ std::optional DecodeHex(Slice from); void EncodeHex(Slice from, std::string *to, bool uppercase = false); bool DecodeHex(Slice from, std::string *to); -} // namespace tile +}// namespace tile -#endif // TILE_BASE_ENCODING_HEX_H +#endif// TILE_BASE_ENCODING_HEX_H diff --git a/tile/base/encoding/hex_test.cc b/tile/base/encoding/hex_test.cc index 1ecf190..879bef7 100644 --- a/tile/base/encoding/hex_test.cc +++ b/tile/base/encoding/hex_test.cc @@ -7,23 +7,25 @@ namespace tile { const char Hex123456FF[] = "\x12\x34\x56\xFF"; -TEST(Hex, Default) { - EXPECT_EQ("123456ff", EncodeHex(Hex123456FF)); - EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true)); - EXPECT_EQ(Hex123456FF, *DecodeHex("123456ff")); - EXPECT_EQ(Hex123456FF, *DecodeHex("123456FF")); +TEST(Hex, Default) +{ + EXPECT_EQ("123456ff", EncodeHex(Hex123456FF)); + EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true)); + EXPECT_EQ(Hex123456FF, *DecodeHex("123456ff")); + EXPECT_EQ(Hex123456FF, *DecodeHex("123456FF")); } -TEST(Hex, Random) { - std::string random_str; - std::string encoded; - std::string decoded; - for (int i = 0; i != 1000; ++i) { - testing::RandomString(&random_str); - EncodeHex(random_str, &encoded); - ASSERT_TRUE(DecodeHex(encoded, &decoded)); - EXPECT_EQ(random_str, decoded); - } +TEST(Hex, Random) +{ + std::string random_str; + std::string encoded; + std::string decoded; + for (int i = 0; i != 1000; ++i) { + testing::RandomString(&random_str); + EncodeHex(random_str, &encoded); + ASSERT_TRUE(DecodeHex(encoded, &decoded)); + EXPECT_EQ(random_str, decoded); + } } -} // namespace tile +}// namespace tile diff --git a/tile/base/encoding/percent.cc b/tile/base/encoding/percent.cc index 6efa3f8..a1c86f5 100644 --- a/tile/base/encoding/percent.cc +++ b/tile/base/encoding/percent.cc @@ -7,112 +7,107 @@ namespace tile { // [0-9a-zA-Z] namespace { -std::array GenerateUnescapedCharBitmap(Slice unescaped_chars) { - std::array result{}; - for (auto &&e : unescaped_chars) { - result[e] = true; - } +std::array +GenerateUnescapedCharBitmap(Slice unescaped_chars) +{ + std::array result{}; + for (auto &&e : unescaped_chars) { result[e] = true; } - for (int i = 0; i != 10; ++i) { - result[i + '0'] = true; - } + for (int i = 0; i != 10; ++i) { result[i + '0'] = true; } - for (int i = 0; i != 26; ++i) { - result[i + 'A'] = true; - result[i + 'a'] = true; - } + for (int i = 0; i != 26; ++i) { + result[i + 'A'] = true; + result[i + 'a'] = true; + } - return result; + return result; } const std::array, 2> & -GetUnescapedCharBitmap(const PercentEncodingStyle &style) { - static const std::array, 2> kUnescapedChars[] = { - /* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), - GenerateUnescapedCharBitmap("_-!.*~'()")}, +GetUnescapedCharBitmap(const PercentEncodingStyle &style) +{ + static const std::array, 2> kUnescapedChars[] = { + /* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), GenerateUnescapedCharBitmap("_-!.*~'()") }, - /* rfc3986 = */ - {GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"), - GenerateUnescapedCharBitmap("_-.~") + /* rfc3986 = */ + {GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"), GenerateUnescapedCharBitmap("_-.~") - }, - /* rfc5987 = */ - {GenerateUnescapedCharBitmap("!#$&+-.^_`|~"), - GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}}; + }, + /* rfc5987 = */ + {GenerateUnescapedCharBitmap("!#$&+-.^_`|~"), GenerateUnescapedCharBitmap("!#$&+-.^_`|~")} + }; - return kUnescapedChars[static_cast(style)]; + return kUnescapedChars[static_cast(style)]; } -} // namespace +}// namespace -PercentEncodingOptions::PercentEncodingOptions(PercentEncodingStyle s, bool er) - : style(s), escape_reserved(er) {} +PercentEncodingOptions::PercentEncodingOptions(PercentEncodingStyle s, bool er) : style(s), escape_reserved(er) {} -std::string EncodePercent(Slice from, const PercentEncodingOptions &options) { - std::string result; - EncodePercent(from, &result, options); - return result; -} -std::optional DecodePercent(Slice from, - bool decode_plus_sign_as_whitespace) { - std::string result; - if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) { +std::string +EncodePercent(Slice from, const PercentEncodingOptions &options) +{ + std::string result; + EncodePercent(from, &result, options); return result; - } - return std::nullopt; } -void EncodePercent(Slice from, std::string *to, - const PercentEncodingOptions &options) { - - auto &&unescaped = - GetUnescapedCharBitmap(options.style)[options.escape_reserved]; - - int escape_char_count = 0; - for (auto &&e : from) { - if (!unescaped[static_cast(e)]) { - ++escape_char_count; - } - } - - to->clear(); - to->reserve(from.size() + escape_char_count * 2); - - for (auto &&e : from) { - if (TILE_UNLIKELY(unescaped[static_cast(e)])) { - to->push_back(e); - } else { - auto hex_pair = detail::AsciiCodeToCharPair(static_cast(e)); - to->append({'%', hex_pair.first, hex_pair.second}); - } - } +std::optional +DecodePercent(Slice from, bool decode_plus_sign_as_whitespace) +{ + std::string result; + if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) { return result; } + return std::nullopt; } -bool DecodePercent(Slice from, std::string *to, - bool decode_plus_sign_as_whitespace) { - to->clear(); - to->reserve(from.size()); - for (auto iter = from.begin(); iter != from.end();) { - if (*iter == '%') { - if (iter + 3 > from.end()) { - return false; - } - auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2)); - if (TILE_LIKELY(v != -1)) { - to->push_back(v); - iter += 3; - } else { - // invalid char - return false; - } - } else { - if (decode_plus_sign_as_whitespace && *iter == '+') { - to->push_back(' '); - ++iter; - } else { - to->push_back(*iter++); - } +void +EncodePercent(Slice from, std::string *to, const PercentEncodingOptions &options) +{ + + auto &&unescaped = GetUnescapedCharBitmap(options.style)[options.escape_reserved]; + + int escape_char_count = 0; + for (auto &&e : from) { + if (!unescaped[static_cast(e)]) { ++escape_char_count; } + } + + to->clear(); + to->reserve(from.size() + escape_char_count * 2); + + for (auto &&e : from) { + if (TILE_UNLIKELY(unescaped[static_cast(e)])) { + to->push_back(e); + } else { + auto hex_pair = detail::AsciiCodeToCharPair(static_cast(e)); + to->append({'%', hex_pair.first, hex_pair.second}); + } } - } - return true; } -} // namespace tile + +bool +DecodePercent(Slice from, std::string *to, bool decode_plus_sign_as_whitespace) +{ + to->clear(); + to->reserve(from.size()); + for (auto iter = from.begin(); iter != from.end();) { + if (*iter == '%') { + if (iter + 3 > from.end()) { return false; } + auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2)); + if (TILE_LIKELY(v != -1)) { + to->push_back(v); + iter += 3; + } else { + // invalid char + return false; + } + } else { + if (decode_plus_sign_as_whitespace && *iter == '+') { + to->push_back(' '); + ++iter; + } else { + to->push_back(*iter++); + } + } + } + return true; +} +}// namespace tile diff --git a/tile/base/encoding/percent.h b/tile/base/encoding/percent.h index cd9343d..f029a8b 100644 --- a/tile/base/encoding/percent.h +++ b/tile/base/encoding/percent.h @@ -9,32 +9,28 @@ namespace tile { enum class PercentEncodingStyle { - Ecma262 = 0, - Rfc3986 = 1, - Rfc5987 = 2, + Ecma262 = 0, + Rfc3986 = 1, + Rfc5987 = 2, }; struct PercentEncodingOptions { - PercentEncodingStyle style = PercentEncodingStyle::Rfc3986; - bool escape_reserved = true; + PercentEncodingStyle style = PercentEncodingStyle::Rfc3986; + bool escape_reserved = true; - PercentEncodingOptions(PercentEncodingStyle s = PercentEncodingStyle::Rfc3986, - bool escape_reserved = true); + PercentEncodingOptions(PercentEncodingStyle s = PercentEncodingStyle::Rfc3986, bool escape_reserved = true); }; std::string EncodePercent(Slice from, - const PercentEncodingOptions &options = - internal::EarlyInitConstant()); -std::optional -DecodePercent(Slice from, bool decode_plus_sign_as_whitespace = false); + const PercentEncodingOptions &options = internal::EarlyInitConstant()); +std::optional DecodePercent(Slice from, bool decode_plus_sign_as_whitespace = false); -void EncodePercent(Slice from, std::string *to, - const PercentEncodingOptions &options = - internal::EarlyInitConstant()); -bool DecodePercent(Slice from, std::string *to, - bool decode_plus_sign_as_whitespace = false); +void EncodePercent(Slice from, + std::string *to, + const PercentEncodingOptions &options = internal::EarlyInitConstant()); +bool DecodePercent(Slice from, std::string *to, bool decode_plus_sign_as_whitespace = false); -} // namespace tile +}// namespace tile -#endif // TILE_BASE_ENCODING_PERCENT_H +#endif// TILE_BASE_ENCODING_PERCENT_H diff --git a/tile/base/encoding/percent_test.cc b/tile/base/encoding/percent_test.cc index 94edabe..488fc6a 100644 --- a/tile/base/encoding/percent_test.cc +++ b/tile/base/encoding/percent_test.cc @@ -6,103 +6,97 @@ namespace tile { -TEST(PercentEncoding, Emca262) { - // Shamelessly copied from: - // - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent - Slice set1 = ";,/?:@&=+$"; // Reserved Characters - Slice set2 = "-_.!~*'()"; // Unescaped Characters - Slice set3 = "#"; // Number Sign - Slice set4 = "ABC abc 123"; // Alphanumeric Characters + Space +TEST(PercentEncoding, Emca262) +{ + // Shamelessly copied from: + // + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + Slice set1 = ";,/?:@&=+$"; // Reserved Characters + Slice set2 = "-_.!~*'()"; // Unescaped Characters + Slice set3 = "#"; // Number Sign + Slice set4 = "ABC abc 123";// Alphanumeric Characters + Space - // Reserved chars are escaped. - const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262, - true); + // Reserved chars are escaped. + const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262, true); - EXPECT_EQ("%3B%2C%2F%3F%3A%40%26%3D%2B%24", - EncodePercent(set1, ecma262_reversed)); - EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262_reversed)); - EXPECT_EQ("%23", EncodePercent(set3, ecma262_reversed)); - EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262_reversed)); + EXPECT_EQ("%3B%2C%2F%3F%3A%40%26%3D%2B%24", EncodePercent(set1, ecma262_reversed)); + EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262_reversed)); + EXPECT_EQ("%23", EncodePercent(set3, ecma262_reversed)); + EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262_reversed)); - EXPECT_EQ(set1, DecodePercent("%3B%2C%2F%3F%3A%40%26%3D%2B%24")); - EXPECT_EQ(set2, DecodePercent("-_.!~*'()")); - EXPECT_EQ(set3, DecodePercent("%23")); - EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123")); + EXPECT_EQ(set1, DecodePercent("%3B%2C%2F%3F%3A%40%26%3D%2B%24")); + EXPECT_EQ(set2, DecodePercent("-_.!~*'()")); + EXPECT_EQ(set3, DecodePercent("%23")); + EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123")); - // Reserved chars are kept. - const PercentEncodingOptions ecma262(PercentEncodingStyle::Ecma262, false); - EXPECT_EQ(";,/?:@&=+$", EncodePercent(set1, ecma262)); - EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262)); - EXPECT_EQ("#", EncodePercent(set3, ecma262)); - EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262)); + // Reserved chars are kept. + const PercentEncodingOptions ecma262(PercentEncodingStyle::Ecma262, false); + EXPECT_EQ(";,/?:@&=+$", EncodePercent(set1, ecma262)); + EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262)); + EXPECT_EQ("#", EncodePercent(set3, ecma262)); + EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262)); - EXPECT_EQ(set1, DecodePercent(";,/?:@&=+$")); - EXPECT_EQ(set2, DecodePercent("-_.!~*'()")); - EXPECT_EQ(set3, DecodePercent("#")); - EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123")); + EXPECT_EQ(set1, DecodePercent(";,/?:@&=+$")); + EXPECT_EQ(set2, DecodePercent("-_.!~*'()")); + EXPECT_EQ(set3, DecodePercent("#")); + EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123")); } -TEST(PercentEncoding, Rfc3986) { - Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123"; +TEST(PercentEncoding, Rfc3986) +{ + Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123"; - const PercentEncodingOptions rfc3986_reversed(PercentEncodingStyle::Rfc3986, - true); - const PercentEncodingOptions rfc3986(PercentEncodingStyle::Rfc3986, false); - EXPECT_EQ( - "_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%" - "20123", - EncodePercent(str, rfc3986_reversed)); - EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123", - EncodePercent(str, rfc3986)); - EXPECT_EQ(str, - DecodePercent("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%" - "2B%3D~%24ABC%20abc%20123")); - EXPECT_EQ(str, DecodePercent("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123")); + const PercentEncodingOptions rfc3986_reversed(PercentEncodingStyle::Rfc3986, true); + const PercentEncodingOptions rfc3986(PercentEncodingStyle::Rfc3986, false); + EXPECT_EQ("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%" + "20123", + EncodePercent(str, rfc3986_reversed)); + EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123", EncodePercent(str, rfc3986)); + EXPECT_EQ(str, + DecodePercent("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%" + "2B%3D~%24ABC%20abc%20123")); + EXPECT_EQ(str, DecodePercent("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123")); } -TEST(PercentEncoding, Rfc5987) { - // `encodeRFC5987ValueChars from MDN does not seems quite right (in that it - // does escape `#` `$` ..., which is not required.): - // - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent - // - // auto encode_rfc5987_value_chars = [](Slice str) { - // auto result = - // EncodePercent(str, PercentEncodingOptions{.style = - // PercentEncodingStyle::Ecma262}); - // result = Replace(result, "'", "%" + EncodeHex("'")); - // result = Replace(result, "(", "%" + EncodeHex("(")); - // result = Replace(result, ")", "%" + EncodeHex(")")); - // result = Replace(result, "*", "%" + EncodeHex("*")); - // result = Replace(result, "%7C", "|"); - // result = Replace(result, "%60", "`"); - // result = Replace(result, "%5E", "^"); - // return result; - // }; - // - // EXPECT_EQ( - // encode_rfc5987_value_chars(str), - // EncodePercent(str, PercentEncodingOptions{.style = - // PercentEncodingStyle::Rfc5987})); +TEST(PercentEncoding, Rfc5987) +{ + // `encodeRFC5987ValueChars from MDN does not seems quite right (in that it + // does escape `#` `$` ..., which is not required.): + // + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + // + // auto encode_rfc5987_value_chars = [](Slice str) { + // auto result = + // EncodePercent(str, PercentEncodingOptions{.style = + // PercentEncodingStyle::Ecma262}); + // result = Replace(result, "'", "%" + EncodeHex("'")); + // result = Replace(result, "(", "%" + EncodeHex("(")); + // result = Replace(result, ")", "%" + EncodeHex(")")); + // result = Replace(result, "*", "%" + EncodeHex("*")); + // result = Replace(result, "%7C", "|"); + // result = Replace(result, "%60", "`"); + // result = Replace(result, "%5E", "^"); + // return result; + // }; + // + // EXPECT_EQ( + // encode_rfc5987_value_chars(str), + // EncodePercent(str, PercentEncodingOptions{.style = + // PercentEncodingStyle::Rfc5987})); - const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false); - const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987, - true); + const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false); + const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987, true); - Slice str = "!123'-!#$&()*,./:;?@[]^_`|~+=ABC abc"; + Slice str = "!123'-!#$&()*,./:;?@[]^_`|~+=ABC abc"; - EXPECT_EQ("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc", - EncodePercent(str, rfc5987)); - EXPECT_EQ( - str, - DecodePercent( - "!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc")); + EXPECT_EQ("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc", EncodePercent(str, rfc5987)); + EXPECT_EQ(str, DecodePercent("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc")); } -TEST(PercentEncoding, DecodePlusSignAsWhitespace) { - EXPECT_EQ("a+b", DecodePercent("a+b")); - EXPECT_EQ("a b", DecodePercent("a+b", true)); +TEST(PercentEncoding, DecodePlusSignAsWhitespace) +{ + EXPECT_EQ("a+b", DecodePercent("a+b")); + EXPECT_EQ("a b", DecodePercent("a+b", true)); } -} // namespace tile +}// namespace tile diff --git a/tile/base/encoding_benchmark.cc b/tile/base/encoding_benchmark.cc index e51f216..dcbd05d 100644 --- a/tile/base/encoding_benchmark.cc +++ b/tile/base/encoding_benchmark.cc @@ -7,77 +7,90 @@ namespace tile { -void Benchmark_Base64Encode(benchmark::State &state) { - std::string random_str; - std::string out; - while (state.KeepRunning()) { - state.PauseTiming(); - testing::RandomString(&random_str); - state.ResumeTiming(); +void +Benchmark_Base64Encode(benchmark::State &state) +{ + std::string random_str; + std::string out; + while (state.KeepRunning()) { + state.PauseTiming(); + testing::RandomString(&random_str); + state.ResumeTiming(); - EncodeBase64(random_str, &out); - } -} -void Benchmark_Base64Decode(benchmark::State &state) { - std::string random_str; - std::string base64; - std::string plain_text; - while (state.KeepRunning()) { - state.PauseTiming(); - testing::RandomString(&random_str); - EncodeBase64(random_str, &base64); - state.ResumeTiming(); - - DecodeBase64(base64, &plain_text); - } + EncodeBase64(random_str, &out); + } } -void Benchmark_PercentEncode(benchmark::State &state) { - std::string random_str; - std::string out; - while (state.KeepRunning()) { - state.PauseTiming(); - testing::RandomString(&random_str); - state.ResumeTiming(); - EncodeBase64(random_str, &out); - } +void +Benchmark_Base64Decode(benchmark::State &state) +{ + std::string random_str; + std::string base64; + std::string plain_text; + while (state.KeepRunning()) { + state.PauseTiming(); + testing::RandomString(&random_str); + EncodeBase64(random_str, &base64); + state.ResumeTiming(); + + DecodeBase64(base64, &plain_text); + } } -void Benchmark_PercentDecode(benchmark::State &state) { - std::string random_str; - std::string percent; - std::string plain_text; - while (state.KeepRunning()) { - state.PauseTiming(); - testing::RandomString(&random_str); - EncodePercent(random_str, &percent); - state.ResumeTiming(); - DecodePercent(percent, &plain_text); - } +void +Benchmark_PercentEncode(benchmark::State &state) +{ + std::string random_str; + std::string out; + while (state.KeepRunning()) { + state.PauseTiming(); + testing::RandomString(&random_str); + state.ResumeTiming(); + EncodeBase64(random_str, &out); + } } -void Benchmark_HexEncode(benchmark::State &state) { - std::string random_str; - std::string percent; - while (state.KeepRunning()) { - state.PauseTiming(); - testing::RandomString(&random_str); - state.ResumeTiming(); - EncodeHex(random_str, &percent); - } +void +Benchmark_PercentDecode(benchmark::State &state) +{ + std::string random_str; + std::string percent; + std::string plain_text; + while (state.KeepRunning()) { + state.PauseTiming(); + testing::RandomString(&random_str); + EncodePercent(random_str, &percent); + state.ResumeTiming(); + DecodePercent(percent, &plain_text); + } } -void Benchmark_HexDecode(benchmark::State &state) { - std::string random_str; - std::string percent; - std::string plain_text; - while (state.KeepRunning()) { - state.PauseTiming(); - testing::RandomString(&random_str); - EncodeHex(random_str, &percent); - state.ResumeTiming(); - DecodeHex(percent, &plain_text); - } +void +Benchmark_HexEncode(benchmark::State &state) +{ + std::string random_str; + std::string percent; + while (state.KeepRunning()) { + state.PauseTiming(); + testing::RandomString(&random_str); + state.ResumeTiming(); + EncodeHex(random_str, &percent); + } +} + +void +Benchmark_HexDecode(benchmark::State &state) +{ + std::string random_str; + std::string percent; + std::string plain_text; + while (state.KeepRunning()) { + state.PauseTiming(); + testing::RandomString(&random_str); + EncodeHex(random_str, &percent); + state.ResumeTiming(); + DecodeHex(percent, &plain_text); + } } BENCHMARK(Benchmark_Base64Encode); @@ -86,4 +99,4 @@ BENCHMARK(Benchmark_PercentEncode); BENCHMARK(Benchmark_PercentDecode); BENCHMARK(Benchmark_HexEncode); BENCHMARK(Benchmark_HexDecode); -} // namespace tile +}// namespace tile diff --git a/tile/base/enum.h b/tile/base/enum.h index cc8de79..c55ba51 100644 --- a/tile/base/enum.h +++ b/tile/base/enum.h @@ -6,35 +6,33 @@ #include "tile/base/internal/meta.h" namespace tile { -template ::value>> -constexpr auto underlying_value(T v) -> typename std::underlying_type::type { - return static_cast::type>(v); +template::value>> +constexpr auto +underlying_value(T v) -> typename std::underlying_type::type +{ + return static_cast::type>(v); } -#define TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, Op) \ - constexpr Type operator Op(Type left, Type right) { \ - return static_cast(::tile::underlying_value(left) \ - Op ::tile::underlying_value(right)); \ - } -#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \ - constexpr Type operator Op(Type v) { \ - return static_cast(Op ::tile::underlying_value(v)); \ - } -#define TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, Op) \ - constexpr Type &operator Op(Type & left, Type right) { return left Op right; } +#define TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, Op) \ + constexpr Type operator Op(Type left, Type right) \ + { \ + return static_cast(::tile::underlying_value(left) Op ::tile::underlying_value(right)); \ + } +#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \ + constexpr Type operator Op(Type v) { return static_cast(Op ::tile::underlying_value(v)); } +#define TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, Op) \ + constexpr Type &operator Op(Type & left, Type right) { return left Op right; } -#define TILE_DEFINE_ENUM_BITMASK_OPS(Type) \ - TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, |) \ - TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, &) \ - TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, ^) \ - TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, |=) \ - TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, &=) \ - TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, ^=) \ - TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, ~) \ - constexpr bool operator!(Type value) { \ - return !::tile::underlying_value(value); \ - } +#define TILE_DEFINE_ENUM_BITMASK_OPS(Type) \ + TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, |) \ + TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, &) \ + TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, ^) \ + TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, |=) \ + TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, &=) \ + TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, ^=) \ + TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, ~) \ + constexpr bool operator!(Type value) { return !::tile::underlying_value(value); } -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_ENUM_H +#endif// _TILE_BASE_ENUM_H diff --git a/tile/base/erased_ptr.h b/tile/base/erased_ptr.h index 878fd28..49cea40 100644 --- a/tile/base/erased_ptr.h +++ b/tile/base/erased_ptr.h @@ -12,58 +12,69 @@ namespace tile { // RAII wrapper ptr class ErasedPtr final { public: - using Deleter = void (*)(void *); - constexpr ErasedPtr(std::nullptr_t = nullptr) - : ptr_(nullptr), deleter_(nullptr) {} - template - constexpr ErasedPtr(T *ptr) noexcept - : ptr_(ptr), deleter_([](void *ptr) { delete static_cast(ptr); }) {} - template - constexpr ErasedPtr(T *ptr, D deleter) noexcept - : ptr_(ptr), deleter_(deleter) {} - ErasedPtr(ErasedPtr &&ptr) noexcept : ptr_(ptr.ptr_), deleter_(ptr.deleter_) { - ptr.ptr_ = nullptr; - } + using Deleter = void (*)(void *); - ErasedPtr &operator=(ErasedPtr &&ptr) noexcept { - if (TILE_LIKELY(this != &ptr)) { - Reset(); + constexpr ErasedPtr(std::nullptr_t = nullptr) : ptr_(nullptr), deleter_(nullptr) {} + + template + constexpr ErasedPtr(T *ptr) noexcept + : ptr_(ptr), + deleter_([](void *ptr) { delete static_cast(ptr); }) + {} + + template + constexpr ErasedPtr(T *ptr, D deleter) noexcept + : ptr_(ptr), + deleter_(deleter) + {} + + ErasedPtr(ErasedPtr &&ptr) noexcept : ptr_(ptr.ptr_), deleter_(ptr.deleter_) { ptr.ptr_ = nullptr; } + + ErasedPtr &operator=(ErasedPtr &&ptr) noexcept + { + if (TILE_LIKELY(this != &ptr)) { Reset(); } + return *this; } - return *this; - } - ErasedPtr(const ErasedPtr &) = delete; - ErasedPtr &operator=(const ErasedPtr &) = delete; + ErasedPtr(const ErasedPtr &) = delete; + ErasedPtr &operator=(const ErasedPtr &) = delete; - ~ErasedPtr() { - if (ptr_) { - deleter_(ptr_); + ~ErasedPtr() + { + if (ptr_) { deleter_(ptr_); } } - } - constexpr void *Get() const noexcept { return ptr_; } - template T *UncheckedGet() const noexcept { - return static_cast(ptr_); - } - constexpr explicit operator bool() const noexcept { return !!ptr_; } - void Reset(std::nullptr_t = nullptr) noexcept { - if (ptr_) { - deleter_(ptr_); - ptr_ = nullptr; + constexpr void *Get() const noexcept { return ptr_; } + + template + T *UncheckedGet() const noexcept + { + return static_cast(ptr_); } - } - TILE_NODISCARD void *Leak() noexcept { - void *rc = nullptr; - std::swap(rc, ptr_); - return rc; - } - constexpr Deleter GetDeleter() const noexcept { return deleter_; } + constexpr explicit operator bool() const noexcept { return !!ptr_; } + + void Reset(std::nullptr_t = nullptr) noexcept + { + if (ptr_) { + deleter_(ptr_); + ptr_ = nullptr; + } + } + + TILE_NODISCARD void *Leak() noexcept + { + void *rc = nullptr; + std::swap(rc, ptr_); + return rc; + } + + constexpr Deleter GetDeleter() const noexcept { return deleter_; } private: - void *ptr_; - Deleter deleter_; + void *ptr_; + Deleter deleter_; }; -} // namespace tile +}// namespace tile -#endif // _TILE_BASE_ERASED_PTR_H +#endif// _TILE_BASE_ERASED_PTR_H diff --git a/tile/base/expected.h b/tile/base/expected.h index c7057f4..1c24faa 100644 --- a/tile/base/expected.h +++ b/tile/base/expected.h @@ -17,9 +17,9 @@ #define expected_lite_MINOR 6 #define expected_lite_PATCH 3 -#define expected_lite_VERSION \ - expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY( \ - expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH) +#define expected_lite_VERSION \ + expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY( \ + expected_lite_PATCH) #define expected_STRINGIFY(x) expected_STRINGIFY_(x) #define expected_STRINGIFY_(x) #x @@ -45,8 +45,7 @@ // expected selection and configuration: #if !defined(nsel_CONFIG_SELECT_EXPECTED) -#define nsel_CONFIG_SELECT_EXPECTED \ - (nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_NONSTD) +#define nsel_CONFIG_SELECT_EXPECTED (nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_NONSTD) #endif // Proposal revisions: @@ -89,7 +88,7 @@ #ifndef nsel_CONFIG_NO_EXCEPTIONS #if defined(_MSC_VER) -#include // for _HAS_EXCEPTIONS +#include // for _HAS_EXCEPTIONS #endif #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) #define nsel_CONFIG_NO_EXCEPTIONS 0 @@ -134,10 +133,9 @@ #define nsel_HAVE_STD_EXPECTED 0 #endif -#define nsel_USES_STD_EXPECTED \ - ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) || \ - ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && \ - nsel_HAVE_STD_EXPECTED)) +#define nsel_USES_STD_EXPECTED \ + ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) \ + || ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && nsel_HAVE_STD_EXPECTED)) // // in_place: code duplicated in any-lite, expected-lite, expected-lite, @@ -166,71 +164,74 @@ using std::in_place_type_t; #define nonstd_lite_in_place_type_t(T) std::in_place_type_t #define nonstd_lite_in_place_index_t(K) std::in_place_index_t -#define nonstd_lite_in_place(T) \ - std::in_place_t {} -#define nonstd_lite_in_place_type(T) \ - std::in_place_type_t {} -#define nonstd_lite_in_place_index(K) \ - std::in_place_index_t {} +#define nonstd_lite_in_place(T) \ + std::in_place_t {} +#define nonstd_lite_in_place_type(T) \ + std::in_place_type_t {} +#define nonstd_lite_in_place_index(K) \ + std::in_place_index_t {} -} // namespace nonstd +}// namespace nonstd -#else // nsel_CPP17_OR_GREATER +#else// nsel_CPP17_OR_GREATER #include namespace nonstd { namespace detail { -template struct in_place_type_tag {}; +template +struct in_place_type_tag {}; -template struct in_place_index_tag {}; +template +struct in_place_index_tag {}; -} // namespace detail +}// namespace detail struct in_place_t {}; -template +template inline in_place_t -in_place(detail::in_place_type_tag = detail::in_place_type_tag()) { - return in_place_t(); +in_place(detail::in_place_type_tag = detail::in_place_type_tag()) +{ + return in_place_t(); } -template +template inline in_place_t -in_place(detail::in_place_index_tag = detail::in_place_index_tag()) { - return in_place_t(); +in_place(detail::in_place_index_tag = detail::in_place_index_tag()) +{ + return in_place_t(); } -template +template inline in_place_t -in_place_type(detail::in_place_type_tag = detail::in_place_type_tag()) { - return in_place_t(); +in_place_type(detail::in_place_type_tag = detail::in_place_type_tag()) +{ + return in_place_t(); } -template -inline in_place_t in_place_index( - detail::in_place_index_tag = detail::in_place_index_tag()) { - return in_place_t(); +template +inline in_place_t +in_place_index(detail::in_place_index_tag = detail::in_place_index_tag()) +{ + return in_place_t(); } // mimic templated typedef: -#define nonstd_lite_in_place_t(T) \ - nonstd::in_place_t (&)(nonstd::detail::in_place_type_tag) -#define nonstd_lite_in_place_type_t(T) \ - nonstd::in_place_t (&)(nonstd::detail::in_place_type_tag) -#define nonstd_lite_in_place_index_t(K) \ - nonstd::in_place_t (&)(nonstd::detail::in_place_index_tag) +#define nonstd_lite_in_place_t(T) nonstd::in_place_t (&)(nonstd::detail::in_place_type_tag) +#define nonstd_lite_in_place_type_t(T) nonstd::in_place_t (&)(nonstd::detail::in_place_type_tag) +#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t (&)(nonstd::detail::in_place_index_tag) #define nonstd_lite_in_place(T) nonstd::in_place_type #define nonstd_lite_in_place_type(T) nonstd::in_place_type #define nonstd_lite_in_place_index(K) nonstd::in_place_index -} // namespace nonstd +}// namespace nonstd -#endif // nsel_CPP17_OR_GREATER -#endif // nonstd_lite_HAVE_IN_PLACE_TYPES +#endif// nsel_CPP17_OR_GREATER +#endif// nonstd_lite_HAVE_IN_PLACE_TYPES // // Using std::expected: @@ -244,9 +245,9 @@ namespace nonstd { using std::expected; // ... -} // namespace nonstd +}// namespace nonstd -#else // nsel_USES_STD_EXPECTED +#else// nsel_USES_STD_EXPECTED #include #include @@ -262,7 +263,7 @@ using std::expected; #if nsel_CONFIG_NO_EXCEPTIONS #if nsel_CONFIG_NO_EXCEPTIONS_SEH -#include // for ExceptionCodes +#include // for ExceptionCodes #else // already included: #endif @@ -309,26 +310,22 @@ using std::expected; #if defined(_MSC_VER) && !defined(__clang__) #define nsel_COMPILER_MSVC_VER (_MSC_VER) -#define nsel_COMPILER_MSVC_VERSION \ - (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) +#define nsel_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) #else #define nsel_COMPILER_MSVC_VER 0 #define nsel_COMPILER_MSVC_VERSION 0 #endif -#define nsel_COMPILER_VERSION(major, minor, patch) \ - (10 * (10 * (major) + (minor)) + (patch)) +#define nsel_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch)) #if defined(__clang__) -#define nsel_COMPILER_CLANG_VERSION \ - nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#define nsel_COMPILER_CLANG_VERSION nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else #define nsel_COMPILER_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) -#define nsel_COMPILER_GNUC_VERSION \ - nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#define nsel_COMPILER_GNUC_VERSION nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else #define nsel_COMPILER_GNUC_VERSION 0 #endif @@ -338,16 +335,13 @@ using std::expected; // Method enabling -#define nsel_REQUIRES_0(...) \ - template ::type = 0> +#define nsel_REQUIRES_0(...) template::type = 0> -#define nsel_REQUIRES_T(...) \ - , typename std::enable_if<(__VA_ARGS__), int>::type = 0 +#define nsel_REQUIRES_T(...) , typename std::enable_if<(__VA_ARGS__), int>::type = 0 #define nsel_REQUIRES_R(R, ...) typename std::enable_if<(__VA_ARGS__), R>::type -#define nsel_REQUIRES_A(...) \ - , typename std::enable_if<(__VA_ARGS__), void *>::type = nullptr +#define nsel_REQUIRES_A(...) , typename std::enable_if<(__VA_ARGS__), void *>::type = nullptr // Presence of language and library features: @@ -366,7 +360,7 @@ using std::expected; #pragma clang diagnostic push #elif defined __GNUC__ #pragma GCC diagnostic push -#endif // __clang__ +#endif// __clang__ #if nsel_COMPILER_MSVC_VERSION >= 140 #pragma warning(push) @@ -395,2643 +389,2478 @@ nsel_DISABLE_MSVC_WARNINGS(26409) // expected: // - namespace nonstd { - namespace expected_lite { + namespace nonstd +{ + namespace expected_lite { - // type traits C++17: + // type traits C++17: - namespace std17 { + namespace std17 { #if nsel_CPP17_OR_GREATER - using std::conjunction; - using std::is_nothrow_swappable; - using std::is_swappable; + using std::conjunction; + using std::is_nothrow_swappable; + using std::is_swappable; -#else // nsel_CPP17_OR_GREATER +#else// nsel_CPP17_OR_GREATER - namespace detail { + namespace detail { - using std::swap; + using std::swap; - struct is_swappable { - template (), - std::declval()))> - static std::true_type test(int /* unused */); + struct is_swappable { + template(), std::declval()))> + static std::true_type test(int /* unused */); - template static std::false_type test(...); - }; + template + static std::false_type test(...); + }; - struct is_nothrow_swappable { - // wrap noexcept(expr) in separate function as work-around for VC140 - // (VS2015): + struct is_nothrow_swappable { + // wrap noexcept(expr) in separate function as work-around for VC140 + // (VS2015): - template static constexpr bool satisfies() { - return noexcept(swap(std::declval(), std::declval())); - } + template + static constexpr bool satisfies() + { + return noexcept(swap(std::declval(), std::declval())); + } - template - static auto test(int) -> std::integral_constant()> {} + template + static auto test(int) -> std::integral_constant()> + {} - template static auto test(...) -> std::false_type; - }; - } // namespace detail + template + static auto test(...) -> std::false_type; + }; + }// namespace detail - // is [nothrow] swappable: + // is [nothrow] swappable: - template - struct is_swappable : decltype(detail::is_swappable::test(0)) {}; + template + struct is_swappable : decltype(detail::is_swappable::test(0)) {}; - template - struct is_nothrow_swappable - : decltype(detail::is_nothrow_swappable::test(0)) {}; + template + struct is_nothrow_swappable : decltype(detail::is_nothrow_swappable::test(0)) {}; - // conjunction: + // conjunction: - template struct conjunction : std::true_type {}; - template struct conjunction : B1 {}; + template + struct conjunction : std::true_type {}; - template - struct conjunction - : std::conditional, B1>::type {}; + template + struct conjunction : B1 {}; -#endif // nsel_CPP17_OR_GREATER + template + struct conjunction : std::conditional, B1>::type {}; - } // namespace std17 +#endif// nsel_CPP17_OR_GREATER - // type traits C++20: + }// namespace std17 - namespace std20 { + // type traits C++20: + + namespace std20 { #if defined(__cpp_lib_remove_cvref) - using std::remove_cvref; + using std::remove_cvref; #else - template struct remove_cvref { - typedef - typename std::remove_cv::type>::type - type; - }; + template + struct remove_cvref { + typedef typename std::remove_cv::type>::type type; + }; #endif - } // namespace std20 + }// namespace std20 - // forward declaration: + // forward declaration: - template class expected; + template + class expected; - namespace detail { + namespace detail { #if nsel_P2505R >= 3 - template struct is_expected : std::false_type {}; + template + struct is_expected : std::false_type {}; - template - struct is_expected> : std::true_type {}; -#endif // nsel_P2505R >= 3 + template + struct is_expected> : std::true_type {}; +#endif// nsel_P2505R >= 3 - /// discriminated union to hold value or 'error'. + /// discriminated union to hold value or 'error'. - template class storage_t_noncopy_nonmove_impl { - template friend class nonstd::expected_lite::expected; + template + class storage_t_noncopy_nonmove_impl { + template + friend class nonstd::expected_lite::expected; - public: - using value_type = T; - using error_type = E; + public: + using value_type = T; + using error_type = E; - // no-op construction - storage_t_noncopy_nonmove_impl() {} - ~storage_t_noncopy_nonmove_impl() {} + // no-op construction + storage_t_noncopy_nonmove_impl() {} - explicit storage_t_noncopy_nonmove_impl(bool has_value) - : m_has_value(has_value) {} + ~storage_t_noncopy_nonmove_impl() {} - void construct_value() { new (&m_value) value_type(); } + explicit storage_t_noncopy_nonmove_impl(bool has_value) : m_has_value(has_value) {} - // void construct_value( value_type const & e ) - // { - // new( &m_value ) value_type( e ); - // } + void construct_value() { new (&m_value) value_type(); } - // void construct_value( value_type && e ) - // { - // new( &m_value ) value_type( std::move( e ) ); - // } + // void construct_value( value_type const & e ) + // { + // new( &m_value ) value_type( e ); + // } - template void emplace_value(Args &&...args) { - new (&m_value) value_type(std::forward(args)...); - } + // void construct_value( value_type && e ) + // { + // new( &m_value ) value_type( std::move( e ) ); + // } - template - void emplace_value(std::initializer_list il, Args &&...args) { - new (&m_value) value_type(il, std::forward(args)...); - } + template + void emplace_value(Args &&...args) + { + new (&m_value) value_type(std::forward(args)...); + } - void destruct_value() { m_value.~value_type(); } + template + void emplace_value(std::initializer_list il, Args &&...args) + { + new (&m_value) value_type(il, std::forward(args)...); + } - // void construct_error( error_type const & e ) - // { - // // new( &m_error ) error_type( e ); - // } + void destruct_value() { m_value.~value_type(); } - // void construct_error( error_type && e ) - // { - // // new( &m_error ) error_type( std::move( e ) ); - // } + // void construct_error( error_type const & e ) + // { + // // new( &m_error ) error_type( e ); + // } - template void emplace_error(Args &&...args) { - new (&m_error) error_type(std::forward(args)...); - } + // void construct_error( error_type && e ) + // { + // // new( &m_error ) error_type( std::move( e ) ); + // } - template - void emplace_error(std::initializer_list il, Args &&...args) { - new (&m_error) error_type(il, std::forward(args)...); - } + template + void emplace_error(Args &&...args) + { + new (&m_error) error_type(std::forward(args)...); + } - void destruct_error() { m_error.~error_type(); } + template + void emplace_error(std::initializer_list il, Args &&...args) + { + new (&m_error) error_type(il, std::forward(args)...); + } - constexpr value_type const &value() const & { return m_value; } + void destruct_error() { m_error.~error_type(); } - value_type &value() & { return m_value; } + constexpr value_type const &value() const & { return m_value; } - constexpr value_type const &&value() const && { return std::move(m_value); } + value_type &value() & { return m_value; } - nsel_constexpr14 value_type &&value() && { return std::move(m_value); } + constexpr value_type const &&value() const && { return std::move(m_value); } - value_type const *value_ptr() const { return &m_value; } + nsel_constexpr14 value_type &&value() && { return std::move(m_value); } - value_type *value_ptr() { return &m_value; } + value_type const *value_ptr() const { return &m_value; } - error_type const &error() const & { return m_error; } + value_type *value_ptr() { return &m_value; } - error_type &error() & { return m_error; } + error_type const &error() const & { return m_error; } - constexpr error_type const &&error() const && { return std::move(m_error); } + error_type &error() & { return m_error; } - nsel_constexpr14 error_type &&error() && { return std::move(m_error); } + constexpr error_type const &&error() const && { return std::move(m_error); } - bool has_value() const { return m_has_value; } + nsel_constexpr14 error_type &&error() && { return std::move(m_error); } - void set_has_value(bool v) { m_has_value = v; } + bool has_value() const { return m_has_value; } - private: - union { - value_type m_value; - error_type m_error; + void set_has_value(bool v) { m_has_value = v; } + + private: + union { + value_type m_value; + error_type m_error; + }; + + bool m_has_value = false; }; - bool m_has_value = false; - }; + template + class storage_t_impl { + template + friend class nonstd::expected_lite::expected; - template class storage_t_impl { - template friend class nonstd::expected_lite::expected; + public: + using value_type = T; + using error_type = E; - public: - using value_type = T; - using error_type = E; + // no-op construction + storage_t_impl() {} - // no-op construction - storage_t_impl() {} - ~storage_t_impl() {} + ~storage_t_impl() {} - explicit storage_t_impl(bool has_value) : m_has_value(has_value) {} + explicit storage_t_impl(bool has_value) : m_has_value(has_value) {} - void construct_value() { new (&m_value) value_type(); } + void construct_value() { new (&m_value) value_type(); } - void construct_value(value_type const &e) { new (&m_value) value_type(e); } + void construct_value(value_type const &e) { new (&m_value) value_type(e); } - void construct_value(value_type &&e) { - new (&m_value) value_type(std::move(e)); - } + void construct_value(value_type &&e) { new (&m_value) value_type(std::move(e)); } - template void emplace_value(Args &&...args) { - new (&m_value) value_type(std::forward(args)...); - } + template + void emplace_value(Args &&...args) + { + new (&m_value) value_type(std::forward(args)...); + } - template - void emplace_value(std::initializer_list il, Args &&...args) { - new (&m_value) value_type(il, std::forward(args)...); - } + template + void emplace_value(std::initializer_list il, Args &&...args) + { + new (&m_value) value_type(il, std::forward(args)...); + } - void destruct_value() { m_value.~value_type(); } + void destruct_value() { m_value.~value_type(); } - void construct_error(error_type const &e) { new (&m_error) error_type(e); } + void construct_error(error_type const &e) { new (&m_error) error_type(e); } - void construct_error(error_type &&e) { - new (&m_error) error_type(std::move(e)); - } + void construct_error(error_type &&e) { new (&m_error) error_type(std::move(e)); } - template void emplace_error(Args &&...args) { - new (&m_error) error_type(std::forward(args)...); - } + template + void emplace_error(Args &&...args) + { + new (&m_error) error_type(std::forward(args)...); + } - template - void emplace_error(std::initializer_list il, Args &&...args) { - new (&m_error) error_type(il, std::forward(args)...); - } + template + void emplace_error(std::initializer_list il, Args &&...args) + { + new (&m_error) error_type(il, std::forward(args)...); + } - void destruct_error() { m_error.~error_type(); } + void destruct_error() { m_error.~error_type(); } - constexpr value_type const &value() const & { return m_value; } + constexpr value_type const &value() const & { return m_value; } - value_type &value() & { return m_value; } + value_type &value() & { return m_value; } - constexpr value_type const &&value() const && { return std::move(m_value); } + constexpr value_type const &&value() const && { return std::move(m_value); } - nsel_constexpr14 value_type &&value() && { return std::move(m_value); } + nsel_constexpr14 value_type &&value() && { return std::move(m_value); } - value_type const *value_ptr() const { return &m_value; } + value_type const *value_ptr() const { return &m_value; } - value_type *value_ptr() { return &m_value; } + value_type *value_ptr() { return &m_value; } - error_type const &error() const & { return m_error; } + error_type const &error() const & { return m_error; } - error_type &error() & { return m_error; } + error_type &error() & { return m_error; } - constexpr error_type const &&error() const && { return std::move(m_error); } + constexpr error_type const &&error() const && { return std::move(m_error); } - nsel_constexpr14 error_type &&error() && { return std::move(m_error); } + nsel_constexpr14 error_type &&error() && { return std::move(m_error); } - bool has_value() const { return m_has_value; } + bool has_value() const { return m_has_value; } - void set_has_value(bool v) { m_has_value = v; } + void set_has_value(bool v) { m_has_value = v; } - private: - union { - value_type m_value; - error_type m_error; + private: + union { + value_type m_value; + error_type m_error; + }; + + bool m_has_value = false; }; - bool m_has_value = false; - }; + /// discriminated union to hold only 'error'. - /// discriminated union to hold only 'error'. + template + struct storage_t_impl { + template + friend class nonstd::expected_lite::expected; - template struct storage_t_impl { - template friend class nonstd::expected_lite::expected; + public: + using value_type = void; + using error_type = E; - public: - using value_type = void; - using error_type = E; + // no-op construction + storage_t_impl() {} - // no-op construction - storage_t_impl() {} - ~storage_t_impl() {} + ~storage_t_impl() {} - explicit storage_t_impl(bool has_value) : m_has_value(has_value) {} + explicit storage_t_impl(bool has_value) : m_has_value(has_value) {} - void construct_error(error_type const &e) { new (&m_error) error_type(e); } + void construct_error(error_type const &e) { new (&m_error) error_type(e); } - void construct_error(error_type &&e) { - new (&m_error) error_type(std::move(e)); - } + void construct_error(error_type &&e) { new (&m_error) error_type(std::move(e)); } - template void emplace_error(Args &&...args) { - new (&m_error) error_type(std::forward(args)...); - } + template + void emplace_error(Args &&...args) + { + new (&m_error) error_type(std::forward(args)...); + } - template - void emplace_error(std::initializer_list il, Args &&...args) { - new (&m_error) error_type(il, std::forward(args)...); - } + template + void emplace_error(std::initializer_list il, Args &&...args) + { + new (&m_error) error_type(il, std::forward(args)...); + } - void destruct_error() { m_error.~error_type(); } + void destruct_error() { m_error.~error_type(); } - error_type const &error() const & { return m_error; } + error_type const &error() const & { return m_error; } - error_type &error() & { return m_error; } + error_type &error() & { return m_error; } - constexpr error_type const &&error() const && { return std::move(m_error); } + constexpr error_type const &&error() const && { return std::move(m_error); } - nsel_constexpr14 error_type &&error() && { return std::move(m_error); } + nsel_constexpr14 error_type &&error() && { return std::move(m_error); } - bool has_value() const { return m_has_value; } + bool has_value() const { return m_has_value; } - void set_has_value(bool v) { m_has_value = v; } + void set_has_value(bool v) { m_has_value = v; } - private: - union { - char m_dummy; - error_type m_error; + private: + union { + char m_dummy; + error_type m_error; + }; + + bool m_has_value = false; }; - bool m_has_value = false; - }; + template + class storage_t { + public: + }; - template - class storage_t { - public: - }; + template + class storage_t : public storage_t_noncopy_nonmove_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t - : public storage_t_noncopy_nonmove_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_noncopy_nonmove_impl(has_value) {} - explicit storage_t(bool has_value) - : storage_t_noncopy_nonmove_impl(has_value) {} + storage_t(storage_t const &other) = delete; + storage_t(storage_t &&other) = delete; + }; - storage_t(storage_t const &other) = delete; - storage_t(storage_t &&other) = delete; - }; + template + class storage_t : public storage_t_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t : public storage_t_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_impl(has_value) {} - explicit storage_t(bool has_value) : storage_t_impl(has_value) {} + storage_t(storage_t const &other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + this->construct_value(other.value()); + else + this->construct_error(other.error()); + } - storage_t(storage_t const &other) - : storage_t_impl(other.has_value()) { - if (this->has_value()) - this->construct_value(other.value()); - else - this->construct_error(other.error()); - } + storage_t(storage_t &&other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + this->construct_value(std::move(other.value())); + else + this->construct_error(std::move(other.error())); + } + }; - storage_t(storage_t &&other) : storage_t_impl(other.has_value()) { - if (this->has_value()) - this->construct_value(std::move(other.value())); - else - this->construct_error(std::move(other.error())); - } - }; + template + class storage_t : public storage_t_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t : public storage_t_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_impl(has_value) {} - explicit storage_t(bool has_value) : storage_t_impl(has_value) {} + storage_t(storage_t const &other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + ; + else + this->construct_error(other.error()); + } - storage_t(storage_t const &other) - : storage_t_impl(other.has_value()) { - if (this->has_value()) - ; - else - this->construct_error(other.error()); - } + storage_t(storage_t &&other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + ; + else + this->construct_error(std::move(other.error())); + } + }; - storage_t(storage_t &&other) : storage_t_impl(other.has_value()) { - if (this->has_value()) - ; - else - this->construct_error(std::move(other.error())); - } - }; + template + class storage_t : public storage_t_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t : public storage_t_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_impl(has_value) {} - explicit storage_t(bool has_value) : storage_t_impl(has_value) {} + storage_t(storage_t const &other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + this->construct_value(other.value()); + else + this->construct_error(other.error()); + } - storage_t(storage_t const &other) - : storage_t_impl(other.has_value()) { - if (this->has_value()) - this->construct_value(other.value()); - else - this->construct_error(other.error()); - } + storage_t(storage_t &&other) = delete; + }; - storage_t(storage_t &&other) = delete; - }; + template + class storage_t : public storage_t_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t : public storage_t_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_impl(has_value) {} - explicit storage_t(bool has_value) : storage_t_impl(has_value) {} + storage_t(storage_t const &other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + ; + else + this->construct_error(other.error()); + } - storage_t(storage_t const &other) - : storage_t_impl(other.has_value()) { - if (this->has_value()) - ; - else - this->construct_error(other.error()); - } + storage_t(storage_t &&other) = delete; + }; - storage_t(storage_t &&other) = delete; - }; + template + class storage_t : public storage_t_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t : public storage_t_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_impl(has_value) {} - explicit storage_t(bool has_value) : storage_t_impl(has_value) {} + storage_t(storage_t const &other) = delete; - storage_t(storage_t const &other) = delete; + storage_t(storage_t &&other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + this->construct_value(std::move(other.value())); + else + this->construct_error(std::move(other.error())); + } + }; - storage_t(storage_t &&other) : storage_t_impl(other.has_value()) { - if (this->has_value()) - this->construct_value(std::move(other.value())); - else - this->construct_error(std::move(other.error())); - } - }; + template + class storage_t : public storage_t_impl { + public: + storage_t() = default; + ~storage_t() = default; - template - class storage_t : public storage_t_impl { - public: - storage_t() = default; - ~storage_t() = default; + explicit storage_t(bool has_value) : storage_t_impl(has_value) {} - explicit storage_t(bool has_value) : storage_t_impl(has_value) {} + storage_t(storage_t const &other) = delete; - storage_t(storage_t const &other) = delete; - - storage_t(storage_t &&other) : storage_t_impl(other.has_value()) { - if (this->has_value()) - ; - else - this->construct_error(std::move(other.error())); - } - }; + storage_t(storage_t &&other) : storage_t_impl(other.has_value()) + { + if (this->has_value()) + ; + else + this->construct_error(std::move(other.error())); + } + }; #if nsel_P2505R >= 3 - // C++11 invoke implementation - template struct is_reference_wrapper : std::false_type {}; - template - struct is_reference_wrapper> : std::true_type {}; + // C++11 invoke implementation + template + struct is_reference_wrapper : std::false_type {}; - template < - typename FnT, typename ClassT, typename ObjectT, - typename... Args nsel_REQUIRES_T( - std::is_function::value && - (std::is_same::type>::value || - std::is_base_of< - ClassT, typename std20::remove_cvref::type>::value))> - nsel_constexpr auto invoke_member_function_impl( - FnT ClassT::*memfnptr, ObjectT &&obj, - Args &&...args) noexcept(noexcept((std::forward(obj).* - memfnptr)(std::forward( - args)...))) -> decltype((std::forward(obj).* - memfnptr)(std::forward(args)...)) { - return (std::forward(obj).*memfnptr)(std::forward(args)...); - } + template + struct is_reference_wrapper> : std::true_type {}; - template ::value &&is_reference_wrapper< - typename std20::remove_cvref::type>::value)> - nsel_constexpr auto - invoke_member_function_impl(FnT ClassT::*memfnptr, ObjectT &&obj, - Args &&...args) noexcept(noexcept((obj.get().* - memfnptr)( - std::forward(args)...))) - -> decltype((obj.get().*memfnptr)(std::forward(args)...)) { - return (obj.get().*memfnptr)(std::forward(args)...); - } + template::value + && (std::is_same::type>::value + || std::is_base_of::type>::value))> + nsel_constexpr auto invoke_member_function_impl(FnT ClassT::*memfnptr, ObjectT &&obj, Args &&...args) noexcept( + noexcept((std::forward(obj).*memfnptr)(std::forward(args)...))) + -> decltype((std::forward(obj).*memfnptr)(std::forward(args)...)) + { + return (std::forward(obj).*memfnptr)(std::forward(args)...); + } - template < - typename FnT, typename ClassT, typename ObjectT, - typename... Args nsel_REQUIRES_T( - std::is_function::value && - !std::is_same::type>::value && - !std::is_base_of< - ClassT, typename std20::remove_cvref::type>::value && - !is_reference_wrapper< - typename std20::remove_cvref::type>::value)> - nsel_constexpr auto invoke_member_function_impl( - FnT ClassT::*memfnptr, ObjectT &&obj, - Args &&...args) noexcept(noexcept(((*std::forward(obj)).* - memfnptr)(std::forward( - args)...))) -> decltype(((*std::forward(obj)).* - memfnptr)(std::forward(args)...)) { - return ((*std::forward(obj)).* - memfnptr)(std::forward(args)...); - } + template::value &&is_reference_wrapper< + typename std20::remove_cvref::type>::value)> + nsel_constexpr auto invoke_member_function_impl(FnT ClassT::*memfnptr, ObjectT &&obj, Args &&...args) noexcept( + noexcept((obj.get().*memfnptr)(std::forward(args)...))) + -> decltype((obj.get().*memfnptr)(std::forward(args)...)) + { + return (obj.get().*memfnptr)(std::forward(args)...); + } - template < - typename MemberT, typename ClassT, - typename ObjectT nsel_REQUIRES_T( - std::is_same::type>::value || - std::is_base_of::type>::value)> - nsel_constexpr auto - invoke_member_object_impl(MemberT ClassT::*memobjptr, ObjectT &&obj) noexcept( - noexcept(std::forward(obj).* - memobjptr)) -> decltype(std::forward(obj).*memobjptr) { - return std::forward(obj).*memobjptr; - } + template::value + && !std::is_same::type>::value + && !std::is_base_of::type>::value + && !is_reference_wrapper::type>::value)> + nsel_constexpr auto invoke_member_function_impl(FnT ClassT::*memfnptr, ObjectT &&obj, Args &&...args) noexcept( + noexcept(((*std::forward(obj)).*memfnptr)(std::forward(args)...))) + -> decltype(((*std::forward(obj)).*memfnptr)(std::forward(args)...)) + { + return ((*std::forward(obj)).*memfnptr)(std::forward(args)...); + } - template ::type>::value)> - nsel_constexpr auto - invoke_member_object_impl(MemberT ClassT::*memobjptr, ObjectT &&obj) noexcept( - noexcept(obj.get().*memobjptr)) -> decltype(obj.get().*memobjptr) { - return obj.get().*memobjptr; - } + template::type>::value + || std::is_base_of::type>::value)> + nsel_constexpr auto invoke_member_object_impl(MemberT ClassT::*memobjptr, ObjectT &&obj) noexcept( + noexcept(std::forward(obj).*memobjptr)) -> decltype(std::forward(obj).*memobjptr) + { + return std::forward(obj).*memobjptr; + } - template < - typename MemberT, typename ClassT, - typename ObjectT nsel_REQUIRES_T( - !std::is_same::type>::value && - !std::is_base_of< - ClassT, typename std20::remove_cvref::type>::value && - !is_reference_wrapper< - typename std20::remove_cvref::type>::value)> - nsel_constexpr auto - invoke_member_object_impl(MemberT ClassT::*memobjptr, ObjectT &&obj) noexcept( - noexcept((*std::forward(obj)).*memobjptr)) - -> decltype((*std::forward(obj)).*memobjptr) { - return (*std::forward(obj)).*memobjptr; - } + template::type>::value)> + nsel_constexpr auto invoke_member_object_impl(MemberT ClassT::*memobjptr, ObjectT &&obj) noexcept( + noexcept(obj.get().*memobjptr)) -> decltype(obj.get().*memobjptr) + { + return obj.get().*memobjptr; + } - template ::type>::value)> - nsel_constexpr auto invoke(F &&f, Args &&...args) noexcept( - noexcept(invoke_member_function_impl(std::forward(f), - std::forward(args)...))) - -> decltype(invoke_member_function_impl(std::forward(f), - std::forward(args)...)) { - return invoke_member_function_impl(std::forward(f), - std::forward(args)...); - } + template::type>::value + && !std::is_base_of::type>::value + && !is_reference_wrapper::type>::value)> + nsel_constexpr auto invoke_member_object_impl(MemberT ClassT::*memobjptr, ObjectT &&obj) noexcept( + noexcept((*std::forward(obj)).*memobjptr)) -> decltype((*std::forward(obj)).*memobjptr) + { + return (*std::forward(obj)).*memobjptr; + } - template ::type>::value)> - nsel_constexpr auto invoke(F &&f, Args &&...args) noexcept( - noexcept(invoke_member_object_impl(std::forward(f), - std::forward(args)...))) - -> decltype(invoke_member_object_impl(std::forward(f), - std::forward(args)...)) { - return invoke_member_object_impl(std::forward(f), - std::forward(args)...); - } + template< + typename F, + typename... Args nsel_REQUIRES_T(std::is_member_function_pointer::type>::value)> + nsel_constexpr auto invoke(F &&f, Args &&...args) noexcept( + noexcept(invoke_member_function_impl(std::forward(f), std::forward(args)...))) + -> decltype(invoke_member_function_impl(std::forward(f), std::forward(args)...)) + { + return invoke_member_function_impl(std::forward(f), std::forward(args)...); + } - template ::type>::value && - !std::is_member_object_pointer< - typename std20::remove_cvref::type>::value)> - nsel_constexpr auto invoke(F &&f, Args &&...args) noexcept( - noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); - } + template< + typename F, + typename... Args nsel_REQUIRES_T(std::is_member_object_pointer::type>::value)> + nsel_constexpr auto invoke(F &&f, Args &&...args) noexcept( + noexcept(invoke_member_object_impl(std::forward(f), std::forward(args)...))) + -> decltype(invoke_member_object_impl(std::forward(f), std::forward(args)...)) + { + return invoke_member_object_impl(std::forward(f), std::forward(args)...); + } - template - using invoke_result_nocvref_t = typename std20::remove_cvref(), std::declval()...))>::type; + template::type>::value + && !std::is_member_object_pointer::type>::value)> + nsel_constexpr auto invoke(F &&f, Args &&...args) noexcept(noexcept( + std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) + { + return std::forward(f)(std::forward(args)...); + } + + template + using invoke_result_nocvref_t = + typename std20::remove_cvref(), std::declval()...))>::type; #if nsel_P2505R >= 5 - template - using transform_invoke_result_t = typename std::remove_cv(), std::declval()...))>::type; + template + using transform_invoke_result_t = + typename std::remove_cv(), std::declval()...))>::type; #else - template - using transform_invoke_result_t = invoke_result_nocvref_t -#endif // nsel_P2505R >= 5 + template + using transform_invoke_result_t = invoke_result_nocvref_t +#endif// nsel_P2505R >= 5 - template - struct valid_expected_value_type - : std::integral_constant::value && - !std::is_reference::value && - !std::is_array::value> {}; + template + struct valid_expected_value_type + : std::integral_constant::value && !std::is_reference::value + && !std::is_array::value> {}; -#endif // nsel_P2505R >= 3 - } // namespace detail +#endif// nsel_P2505R >= 3 + }// namespace detail - /// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also - /// use aliased type unexpected. + /// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also + /// use aliased type unexpected. #if nsel_P0323R <= 2 - template - class unexpected_type + template + class unexpected_type #else - template - class unexpected_type -#endif // nsel_P0323R - { - public: - using error_type = E; + template + class unexpected_type +#endif// nsel_P0323R + { + public: + using error_type = E; - // x.x.5.2.1 Constructors + // x.x.5.2.1 Constructors - // unexpected_type() = delete; + // unexpected_type() = delete; - constexpr unexpected_type(unexpected_type const &) = default; - constexpr unexpected_type(unexpected_type &&) = default; + constexpr unexpected_type(unexpected_type const &) = default; + constexpr unexpected_type(unexpected_type &&) = default; - template ::value)> - constexpr explicit unexpected_type(nonstd_lite_in_place_t(E), - Args &&...args) - : m_error(std::forward(args)...) {} + template::value)> + constexpr explicit unexpected_type(nonstd_lite_in_place_t(E), Args &&...args) + : m_error(std::forward(args)...) + {} - template , - Args &&...>::value)> - constexpr explicit unexpected_type(nonstd_lite_in_place_t(E), - std::initializer_list il, - Args &&...args) - : m_error(il, std::forward(args)...) {} + template< + typename U, + typename... Args nsel_REQUIRES_T(std::is_constructible, Args &&...>::value)> + constexpr explicit unexpected_type(nonstd_lite_in_place_t(E), std::initializer_list il, Args &&...args) + : m_error(il, std::forward(args)...) + {} - template ::value && - !std::is_same::type, - nonstd_lite_in_place_t(E2)>::value && - !std::is_same::type, - unexpected_type>::value)> - constexpr explicit unexpected_type(E2 &&error) - : m_error(std::forward(error)) {} + template::value + && !std::is_same::type, nonstd_lite_in_place_t(E2)>::value + && !std::is_same::type, unexpected_type>::value)> + constexpr explicit unexpected_type(E2 &&error) : m_error(std::forward(error)) + {} - template ::value && - !std::is_constructible &>::value && - !std::is_constructible>::value && - !std::is_constructible const &>::value && - !std::is_constructible const>::value && - !std::is_convertible &, E>::value && - !std::is_convertible, E>::value && - !std::is_convertible const &, E>::value && - !std::is_convertible const, E>::value && - !std::is_convertible::value /*=> explicit */ - )> - constexpr explicit unexpected_type(unexpected_type const &error) - : m_error(E{error.value()}) {} + template::value && !std::is_constructible &>::value + && !std::is_constructible>::value + && !std::is_constructible const &>::value + && !std::is_constructible const>::value + && !std::is_convertible &, E>::value + && !std::is_convertible, E>::value + && !std::is_convertible const &, E>::value + && !std::is_convertible const, E>::value + && !std::is_convertible::value /*=> explicit */ + )> + constexpr explicit unexpected_type(unexpected_type const &error) : m_error(E{error.value()}) + {} - template ::value && - !std::is_constructible &>::value && - !std::is_constructible>::value && - !std::is_constructible const &>::value && - !std::is_constructible const>::value && - !std::is_convertible &, E>::value && - !std::is_convertible, E>::value && - !std::is_convertible const &, E>::value && - !std::is_convertible const, E>::value && - std::is_convertible::value /*=> explicit */ - )> - constexpr /*non-explicit*/ unexpected_type(unexpected_type const &error) - : m_error(error.value()) {} + template::value && !std::is_constructible &>::value + && !std::is_constructible>::value + && !std::is_constructible const &>::value + && !std::is_constructible const>::value + && !std::is_convertible &, E>::value + && !std::is_convertible, E>::value + && !std::is_convertible const &, E>::value + && !std::is_convertible const, E>::value + && std::is_convertible::value /*=> explicit */ + )> + constexpr /*non-explicit*/ unexpected_type(unexpected_type const &error) : m_error(error.value()) + {} - template ::value && - !std::is_constructible &>::value && - !std::is_constructible>::value && - !std::is_constructible const &>::value && - !std::is_constructible const>::value && - !std::is_convertible &, E>::value && - !std::is_convertible, E>::value && - !std::is_convertible const &, E>::value && - !std::is_convertible const, E>::value && - !std::is_convertible::value /*=> explicit */ - )> - constexpr explicit unexpected_type(unexpected_type &&error) - : m_error(E{std::move(error.value())}) {} + template::value && !std::is_constructible &>::value + && !std::is_constructible>::value + && !std::is_constructible const &>::value + && !std::is_constructible const>::value + && !std::is_convertible &, E>::value + && !std::is_convertible, E>::value + && !std::is_convertible const &, E>::value + && !std::is_convertible const, E>::value + && !std::is_convertible::value /*=> explicit */ + )> + constexpr explicit unexpected_type(unexpected_type &&error) : m_error(E{std::move(error.value())}) + {} - template ::value && - !std::is_constructible &>::value && - !std::is_constructible>::value && - !std::is_constructible const &>::value && - !std::is_constructible const>::value && - !std::is_convertible &, E>::value && - !std::is_convertible, E>::value && - !std::is_convertible const &, E>::value && - !std::is_convertible const, E>::value && - std::is_convertible::value /*=> non-explicit */ - )> - constexpr /*non-explicit*/ unexpected_type(unexpected_type &&error) - : m_error(std::move(error.value())) {} + template::value && !std::is_constructible &>::value + && !std::is_constructible>::value + && !std::is_constructible const &>::value + && !std::is_constructible const>::value + && !std::is_convertible &, E>::value + && !std::is_convertible, E>::value + && !std::is_convertible const &, E>::value + && !std::is_convertible const, E>::value + && std::is_convertible::value /*=> non-explicit */ + )> + constexpr /*non-explicit*/ unexpected_type(unexpected_type &&error) : m_error(std::move(error.value())) + {} - // x.x.5.2.2 Assignment + // x.x.5.2.2 Assignment - nsel_constexpr14 unexpected_type & - operator=(unexpected_type const &) = default; - nsel_constexpr14 unexpected_type &operator=(unexpected_type &&) = default; + nsel_constexpr14 unexpected_type &operator=(unexpected_type const &) = default; + nsel_constexpr14 unexpected_type &operator=(unexpected_type &&) = default; - template - nsel_constexpr14 unexpected_type & - operator=(unexpected_type const &other) { - unexpected_type{other.value()}.swap(*this); - return *this; - } + template + nsel_constexpr14 unexpected_type &operator=(unexpected_type const &other) + { + unexpected_type{other.value()}.swap(*this); + return *this; + } - template - nsel_constexpr14 unexpected_type &operator=(unexpected_type &&other) { - unexpected_type{std::move(other.value())}.swap(*this); - return *this; - } + template + nsel_constexpr14 unexpected_type &operator=(unexpected_type &&other) + { + unexpected_type{std::move(other.value())}.swap(*this); + return *this; + } - // x.x.5.2.3 Observers + // x.x.5.2.3 Observers - nsel_constexpr14 E &value() & noexcept { return m_error; } + nsel_constexpr14 E &value() & noexcept { return m_error; } - constexpr E const &value() const & noexcept { return m_error; } + constexpr E const &value() const & noexcept { return m_error; } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - nsel_constexpr14 E &&value() && noexcept { return std::move(m_error); } + nsel_constexpr14 E &&value() && noexcept { return std::move(m_error); } - constexpr E const &&value() const && noexcept { return std::move(m_error); } + constexpr E const &&value() const && noexcept { return std::move(m_error); } #endif - // x.x.5.2.4 Swap + // x.x.5.2.4 Swap - template - nsel_REQUIRES_R(void, std17::is_swappable::value) - swap(unexpected_type &other) noexcept( - std17::is_nothrow_swappable::value) { - using std::swap; - swap(m_error, other.m_error); - } + template + nsel_REQUIRES_R(void, std17::is_swappable::value) + swap(unexpected_type &other) noexcept(std17::is_nothrow_swappable::value) + { + using std::swap; + swap(m_error, other.m_error); + } - // TODO: ??? unexpected_type: in-class friend operator==, != + // TODO: ??? unexpected_type: in-class friend operator==, != - private: - error_type m_error; - }; + private: + error_type m_error; + }; #if nsel_CPP17_OR_GREATER - /// template deduction guide: + /// template deduction guide: - template unexpected_type(E) -> unexpected_type; + template + unexpected_type(E) -> unexpected_type; #endif - /// class unexpected_type, std::exception_ptr specialization (P0323R2) + /// class unexpected_type, std::exception_ptr specialization (P0323R2) #if !nsel_CONFIG_NO_EXCEPTIONS #if nsel_P0323R <= 2 - // TODO: Should expected be specialized for particular E types such as - // exception_ptr and how? - // See p0323r7 2.1. Ergonomics, http://wg21.link/p0323 - template <> class unexpected_type { - public: - using error_type = std::exception_ptr; + // TODO: Should expected be specialized for particular E types such as + // exception_ptr and how? + // See p0323r7 2.1. Ergonomics, http://wg21.link/p0323 + template<> + class unexpected_type { + public: + using error_type = std::exception_ptr; - unexpected_type() = delete; + unexpected_type() = delete; - ~unexpected_type() {} + ~unexpected_type() {} - explicit unexpected_type(std::exception_ptr const &error) - : m_error(error) {} + explicit unexpected_type(std::exception_ptr const &error) : m_error(error) {} - explicit unexpected_type(std::exception_ptr &&error) - : m_error(std::move(error)) {} + explicit unexpected_type(std::exception_ptr &&error) : m_error(std::move(error)) {} - template - explicit unexpected_type(E error) - : m_error(std::make_exception_ptr(error)) {} + template + explicit unexpected_type(E error) : m_error(std::make_exception_ptr(error)) + {} - std::exception_ptr const &value() const { return m_error; } + std::exception_ptr const &value() const { return m_error; } - std::exception_ptr &value() { return m_error; } + std::exception_ptr &value() { return m_error; } - private: - std::exception_ptr m_error; - }; + private: + std::exception_ptr m_error; + }; -#endif // nsel_P0323R -#endif // !nsel_CONFIG_NO_EXCEPTIONS +#endif// nsel_P0323R +#endif// !nsel_CONFIG_NO_EXCEPTIONS - /// x.x.4, Unexpected equality operators + /// x.x.4, Unexpected equality operators - template - constexpr bool operator==(unexpected_type const &x, - unexpected_type const &y) { - return x.value() == y.value(); - } - - template - constexpr bool operator!=(unexpected_type const &x, - unexpected_type const &y) { - return !(x == y); - } - -#if nsel_P0323R <= 2 - - template - constexpr bool operator<(unexpected_type const &x, - unexpected_type const &y) { - return x.value() < y.value(); - } - - template - constexpr bool operator>(unexpected_type const &x, - unexpected_type const &y) { - return (y < x); - } - - template - constexpr bool operator<=(unexpected_type const &x, - unexpected_type const &y) { - return !(y < x); - } - - template - constexpr bool operator>=(unexpected_type const &x, - unexpected_type const &y) { - return !(x < y); - } - -#endif // nsel_P0323R - - /// x.x.5 Specialized algorithms - - template ::value)> - void swap(unexpected_type &x, - unexpected_type &y) noexcept(noexcept(x.swap(y))) { - x.swap(y); - } - -#if nsel_P0323R <= 2 - - // unexpected: relational operators for std::exception_ptr: - - inline constexpr bool - operator<(unexpected_type const & /*x*/, - unexpected_type const & /*y*/) { - return false; - } - - inline constexpr bool - operator>(unexpected_type const & /*x*/, - unexpected_type const & /*y*/) { - return false; - } - - inline constexpr bool - operator<=(unexpected_type const &x, - unexpected_type const &y) { - return (x == y); - } - - inline constexpr bool - operator>=(unexpected_type const &x, - unexpected_type const &y) { - return (x == y); - } - -#endif // nsel_P0323R - - // unexpected: traits - -#if nsel_P0323R <= 3 - - template struct is_unexpected : std::false_type {}; - - template - struct is_unexpected> : std::true_type {}; - -#endif // nsel_P0323R - - // unexpected: factory - - // keep make_unexpected() removed in p0323r2 for pre-C++17: - - template - nsel_constexpr14 auto - make_unexpected(E &&value) -> unexpected_type::type> { - return unexpected_type::type>( - std::forward(value)); - } - -#if nsel_P0323R <= 3 - - /*nsel_constexpr14*/ auto inline make_unexpected_from_current_exception() - -> unexpected_type { - return unexpected_type(std::current_exception()); - } - -#endif // nsel_P0323R - - /// x.x.6, x.x.7 expected access error - - template class bad_expected_access; - - /// x.x.7 bad_expected_access: expected access error - - template <> class bad_expected_access : public std::exception { - public: - explicit bad_expected_access() : std::exception() {} - }; - - /// x.x.6 bad_expected_access: expected access error - -#if !nsel_CONFIG_NO_EXCEPTIONS - - template - class bad_expected_access : public bad_expected_access { - public: - using error_type = E; - - explicit bad_expected_access(error_type error) : m_error(error) {} - - virtual char const *what() const noexcept override { - return "bad_expected_access"; + template + constexpr bool operator==(unexpected_type const &x, unexpected_type const &y) + { + return x.value() == y.value(); } - nsel_constexpr14 error_type &error() & { return m_error; } + template + constexpr bool operator!=(unexpected_type const &x, unexpected_type const &y) + { + return !(x == y); + } - constexpr error_type const &error() const & { return m_error; } +#if nsel_P0323R <= 2 + + template + constexpr bool operator<(unexpected_type const &x, unexpected_type const &y) + { + return x.value() < y.value(); + } + + template + constexpr bool operator>(unexpected_type const &x, unexpected_type const &y) + { + return (y < x); + } + + template + constexpr bool operator<=(unexpected_type const &x, unexpected_type const &y) + { + return !(y < x); + } + + template + constexpr bool operator>=(unexpected_type const &x, unexpected_type const &y) + { + return !(x < y); + } + +#endif// nsel_P0323R + + /// x.x.5 Specialized algorithms + + template::value)> + void swap(unexpected_type &x, unexpected_type &y) noexcept(noexcept(x.swap(y))) + { + x.swap(y); + } + +#if nsel_P0323R <= 2 + + // unexpected: relational operators for std::exception_ptr: + + inline constexpr bool operator<(unexpected_type const & /*x*/, + unexpected_type const & /*y*/) + { + return false; + } + + inline constexpr bool operator>(unexpected_type const & /*x*/, + unexpected_type const & /*y*/) + { + return false; + } + + inline constexpr bool operator<=(unexpected_type const &x, + unexpected_type const &y) + { + return (x == y); + } + + inline constexpr bool operator>=(unexpected_type const &x, + unexpected_type const &y) + { + return (x == y); + } + +#endif// nsel_P0323R + + // unexpected: traits + +#if nsel_P0323R <= 3 + + template + struct is_unexpected : std::false_type {}; + + template + struct is_unexpected> : std::true_type {}; + +#endif// nsel_P0323R + + // unexpected: factory + + // keep make_unexpected() removed in p0323r2 for pre-C++17: + + template + nsel_constexpr14 auto make_unexpected(E &&value) -> unexpected_type::type> + { + return unexpected_type::type>(std::forward(value)); + } + +#if nsel_P0323R <= 3 + + /*nsel_constexpr14*/ auto inline make_unexpected_from_current_exception() -> unexpected_type + { + return unexpected_type(std::current_exception()); + } + +#endif// nsel_P0323R + + /// x.x.6, x.x.7 expected access error + + template + class bad_expected_access; + + /// x.x.7 bad_expected_access: expected access error + + template<> + class bad_expected_access : public std::exception { + public: + explicit bad_expected_access() : std::exception() {} + }; + + /// x.x.6 bad_expected_access: expected access error + +#if !nsel_CONFIG_NO_EXCEPTIONS + + template + class bad_expected_access : public bad_expected_access { + public: + using error_type = E; + + explicit bad_expected_access(error_type error) : m_error(error) {} + + virtual char const *what() const noexcept override { return "bad_expected_access"; } + + nsel_constexpr14 error_type &error() & { return m_error; } + + constexpr error_type const &error() const & { return m_error; } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - nsel_constexpr14 error_type &&error() && { return std::move(m_error); } + nsel_constexpr14 error_type &&error() && { return std::move(m_error); } - constexpr error_type const &&error() const && { return std::move(m_error); } + constexpr error_type const &&error() const && { return std::move(m_error); } #endif - private: - error_type m_error; - }; + private: + error_type m_error; + }; -#endif // nsel_CONFIG_NO_EXCEPTIONS +#endif// nsel_CONFIG_NO_EXCEPTIONS - /// x.x.8 unexpect tag, in_place_unexpected tag: construct an error + /// x.x.8 unexpect tag, in_place_unexpected tag: construct an error - struct unexpect_t {}; - using in_place_unexpected_t = unexpect_t; + struct unexpect_t {}; - nsel_inline17 constexpr unexpect_t unexpect{}; - nsel_inline17 constexpr unexpect_t in_place_unexpected{}; + using in_place_unexpected_t = unexpect_t; - /// class error_traits + nsel_inline17 constexpr unexpect_t unexpect{}; + nsel_inline17 constexpr unexpect_t in_place_unexpected{}; + + /// class error_traits #if nsel_CONFIG_NO_EXCEPTIONS - namespace detail { - inline bool text(char const * /*text*/) { return true; } - } // namespace detail + namespace detail { + inline bool text(char const * /*text*/) { return true; } + }// namespace detail - template struct error_traits { - static void rethrow(Error const & /*e*/) { + template + struct error_traits { + static void rethrow(Error const & /*e*/) + { #if nsel_CONFIG_NO_EXCEPTIONS_SEH - RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, - NULL); + RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL); #else - assert(false && detail::text("throw bad_expected_access{ e };")); + assert(false && detail::text("throw bad_expected_access{ e };")); #endif - } - }; - - template <> struct error_traits { - static void rethrow(std::exception_ptr const & /*e*/) { -#if nsel_CONFIG_NO_EXCEPTIONS_SEH - RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, - NULL); -#else - assert( - false && - detail::text("throw bad_expected_access{ e };")); -#endif - } - }; - - template <> struct error_traits { - static void rethrow(std::error_code const & /*e*/) { -#if nsel_CONFIG_NO_EXCEPTIONS_SEH - RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, - NULL); -#else - assert(false && detail::text("throw std::system_error( e );")); -#endif - } - }; - -#else // nsel_CONFIG_NO_EXCEPTIONS - - template struct error_traits { - static void rethrow(Error const &e) { throw bad_expected_access{e}; } - }; - - template <> struct error_traits { - static void rethrow(std::exception_ptr const &e) { - std::rethrow_exception(e); - } - }; - - template <> struct error_traits { - static void rethrow(std::error_code const &e) { - throw std::system_error(e); - } - }; - -#endif // nsel_CONFIG_NO_EXCEPTIONS - -#if nsel_P2505R >= 3 - namespace detail { - - // from https://en.cppreference.com/w/cpp/utility/expected/unexpected: - // "the type of the unexpected value. The type must not be an array type, a - // non-object type, a specialization of std::unexpected, or a cv-qualified - // type." - template - struct valid_unexpected_type - : std::integral_constant< - bool, - std::is_same::type>::value && - std::is_object::value && !std::is_array::value> {}; - - template - struct valid_unexpected_type> : std::false_type {}; - - } // namespace detail -#endif // nsel_P2505R >= 3 - - } // namespace expected_lite - - // provide nonstd::unexpected_type: - - using expected_lite::unexpected_type; - - namespace expected_lite { - - /// class expected - -#if nsel_P0323R <= 2 - template - class expected -#else - template - class expected -#endif // nsel_P0323R - { - private: - template friend class expected; - - public: - using value_type = T; - using error_type = E; - using unexpected_type = nonstd::unexpected_type; - - template struct rebind { - using type = expected; + } }; - // x.x.4.1 constructors + template<> + struct error_traits { + static void rethrow(std::exception_ptr const & /*e*/) + { +#if nsel_CONFIG_NO_EXCEPTIONS_SEH + RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL); +#else + assert(false && detail::text("throw bad_expected_access{ e };")); +#endif + } + }; - nsel_REQUIRES_0(std::is_default_constructible::value) nsel_constexpr14 - expected() - : contained(true) { - contained.construct_value(); - } + template<> + struct error_traits { + static void rethrow(std::error_code const & /*e*/) + { +#if nsel_CONFIG_NO_EXCEPTIONS_SEH + RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL); +#else + assert(false && detail::text("throw std::system_error( e );")); +#endif + } + }; - nsel_constexpr14 expected(expected const &) = default; - nsel_constexpr14 expected(expected &&) = default; +#else// nsel_CONFIG_NO_EXCEPTIONS - template ::value - &&std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible const &>::value && - !std::is_constructible const &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible const &, T>::value && - !std::is_convertible const &&, T>::value && - (!std::is_convertible::value || - !std::is_convertible::value) /*=> explicit */ - )> - nsel_constexpr14 explicit expected(expected const &other) - : contained(other.has_value()) { - if (has_value()) - contained.construct_value(T{other.contained.value()}); - else - contained.construct_error(E{other.contained.error()}); - } + template + struct error_traits { + static void rethrow(Error const &e) { throw bad_expected_access{e}; } + }; - template ::value - &&std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible const &>::value && - !std::is_constructible const &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible const &, T>::value && - !std::is_convertible const &&, T>::value && - !(!std::is_convertible::value || - !std::is_convertible::value) /*=> non-explicit - */ - )> - nsel_constexpr14 /*non-explicit*/ expected(expected const &other) - : contained(other.has_value()) { - if (has_value()) - contained.construct_value(other.contained.value()); - else - contained.construct_error(other.contained.error()); - } + template<> + struct error_traits { + static void rethrow(std::exception_ptr const &e) { std::rethrow_exception(e); } + }; - template ::value - &&std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible const &>::value && - !std::is_constructible const &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible const &, T>::value && - !std::is_convertible const &&, T>::value && - (!std::is_convertible::value || - !std::is_convertible::value) /*=> explicit */ - )> - nsel_constexpr14 explicit expected(expected &&other) - : contained(other.has_value()) { - if (has_value()) - contained.construct_value(T{std::move(other.contained.value())}); - else - contained.construct_error(E{std::move(other.contained.error())}); - } + template<> + struct error_traits { + static void rethrow(std::error_code const &e) { throw std::system_error(e); } + }; - template ::value - &&std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible const &>::value && - !std::is_constructible const &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible const &, T>::value && - !std::is_convertible const &&, T>::value && - !(!std::is_convertible::value || - !std::is_convertible::value) /*=> non-explicit */ - )> - nsel_constexpr14 /*non-explicit*/ expected(expected &&other) - : contained(other.has_value()) { - if (has_value()) - contained.construct_value(std::move(other.contained.value())); - else - contained.construct_error(std::move(other.contained.error())); - } +#endif// nsel_CONFIG_NO_EXCEPTIONS - template < - typename U = T nsel_REQUIRES_T(std::is_copy_constructible::value)> - nsel_constexpr14 expected(value_type const &value) : contained(true) { - contained.construct_value(value); - } +#if nsel_P2505R >= 3 + namespace detail { - template ::value && - !std::is_same::type, - nonstd_lite_in_place_t(U)>::value && - !std::is_same, - typename std20::remove_cvref::type>::value && - !std::is_same, - typename std20::remove_cvref::type>::value && - !std::is_convertible::value /*=> explicit */ - )> - nsel_constexpr14 explicit expected(U &&value) noexcept( - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_constructible::value) - : contained(true) { - contained.construct_value(T{std::forward(value)}); - } + // from https://en.cppreference.com/w/cpp/utility/expected/unexpected: + // "the type of the unexpected value. The type must not be an array type, a + // non-object type, a specialization of std::unexpected, or a cv-qualified + // type." + template + struct valid_unexpected_type + : std::integral_constant::type>::value + && std::is_object::value && !std::is_array::value> {}; - template ::value && - !std::is_same::type, - nonstd_lite_in_place_t(U)>::value && - !std::is_same, - typename std20::remove_cvref::type>::value && - !std::is_same, - typename std20::remove_cvref::type>::value && - std::is_convertible::value /*=> non-explicit */ - )> - nsel_constexpr14 /*non-explicit*/ - expected(U &&value) noexcept(std::is_nothrow_move_constructible::value && - std::is_nothrow_move_constructible::value) - : contained(true) { - contained.construct_value(std::forward(value)); - } + template + struct valid_unexpected_type> : std::false_type {}; - // construct error: + }// namespace detail +#endif// nsel_P2505R >= 3 - template ::value && - !std::is_convertible::value /*=> explicit */ - )> - nsel_constexpr14 explicit expected(nonstd::unexpected_type const &error) - : contained(false) { - contained.construct_error(E{error.value()}); - } + }// namespace expected_lite - template ::value && - std::is_convertible::value /*=> non-explicit - */ - )> - nsel_constexpr14 /*non-explicit*/ - expected(nonstd::unexpected_type const &error) - : contained(false) { - contained.construct_error(error.value()); - } + // provide nonstd::unexpected_type: - template ::value && - !std::is_convertible::value /*=> explicit */ - )> - nsel_constexpr14 explicit expected(nonstd::unexpected_type &&error) - : contained(false) { - contained.construct_error(E{std::move(error.value())}); - } + using expected_lite::unexpected_type; - template ::value - &&std::is_convertible::value /*=> non-explicit */ - )> - nsel_constexpr14 /*non-explicit*/ - expected(nonstd::unexpected_type &&error) - : contained(false) { - contained.construct_error(std::move(error.value())); - } + namespace expected_lite { - // in-place construction, value + /// class expected - template ::value)> - nsel_constexpr14 explicit expected(nonstd_lite_in_place_t(T), - Args &&...args) - : contained(true) { - contained.emplace_value(std::forward(args)...); - } - - template , - Args &&...>::value)> - nsel_constexpr14 explicit expected(nonstd_lite_in_place_t(T), - std::initializer_list il, - Args &&...args) - : contained(true) { - contained.emplace_value(il, std::forward(args)...); - } - - // in-place construction, error - - template ::value)> - nsel_constexpr14 explicit expected(unexpect_t, Args &&...args) - : contained(false) { - contained.emplace_error(std::forward(args)...); - } - - template , - Args &&...>::value)> - nsel_constexpr14 explicit expected(unexpect_t, std::initializer_list il, - Args &&...args) - : contained(false) { - contained.emplace_error(il, std::forward(args)...); - } - - // x.x.4.2 destructor - - // TODO: ~expected: triviality - // Effects: If T is not cv void and is_trivially_destructible_v is false - // and bool(*this), calls val.~T(). If is_trivially_destructible_v is - // false and !bool(*this), calls unexpect.~unexpected(). Remarks: If - // either T is cv void or is_trivially_destructible_v is true, and - // is_trivially_destructible_v is true, then this destructor shall be a - // trivial destructor. - - ~expected() { - if (has_value()) - contained.destruct_value(); - else - contained.destruct_error(); - } - - // x.x.4.3 assignment - - expected &operator=(expected const &other) { - expected(other).swap(*this); - return *this; - } - - expected &operator=(expected &&other) noexcept( - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_assignable::value && - std::is_nothrow_move_constructible::value // added for missing - && std::is_nothrow_move_assignable::value) // nothrow above +#if nsel_P0323R <= 2 + template + class expected +#else + template + class expected +#endif// nsel_P0323R { - expected(std::move(other)).swap(*this); - return *this; - } + private: + template + friend class expected; - template , - typename std20::remove_cvref::type>::value && - std17::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> - expected &operator=(U &&value) { - expected(std::forward(value)).swap(*this); - return *this; - } + public: + using value_type = T; + using error_type = E; + using unexpected_type = nonstd::unexpected_type; - template ::value - &&std::is_copy_constructible:: - value // TODO: std::is_nothrow_copy_constructible - &&std::is_copy_assignable::value)> - expected &operator=(nonstd::unexpected_type const &error) { - expected(unexpect, error.value()).swap(*this); - return *this; - } + template + struct rebind { + using type = expected; + }; - template < - typename G = E nsel_REQUIRES_T( - std::is_constructible::value &&std::is_move_constructible< - G>::value // TODO: std::is_nothrow_move_constructible - &&std::is_move_assignable::value)> - expected &operator=(nonstd::unexpected_type &&error) { - expected(unexpect, std::move(error.value())).swap(*this); - return *this; - } + // x.x.4.1 constructors - template ::value)> - value_type &emplace(Args &&...args) { - expected(nonstd_lite_in_place(T), std::forward(args)...) - .swap(*this); - return value(); - } + nsel_REQUIRES_0(std::is_default_constructible::value) nsel_constexpr14 expected() : contained(true) + { + contained.construct_value(); + } - template &, - Args &&...>::value)> - value_type &emplace(std::initializer_list il, Args &&...args) { - expected(nonstd_lite_in_place(T), il, std::forward(args)...) - .swap(*this); - return value(); - } + nsel_constexpr14 expected(expected const &) = default; + nsel_constexpr14 expected(expected &&) = default; - // x.x.4.4 swap + template::value &&std::is_constructible::value + && !std::is_constructible &>::value + && !std::is_constructible &&>::value + && !std::is_constructible const &>::value + && !std::is_constructible const &&>::value + && !std::is_convertible &, T>::value + && !std::is_convertible &&, T>::value + && !std::is_convertible const &, T>::value + && !std::is_convertible const &&, T>::value + && (!std::is_convertible::value + || !std::is_convertible::value) /*=> explicit */ + )> + nsel_constexpr14 explicit expected(expected const &other) : contained(other.has_value()) + { + if (has_value()) + contained.construct_value(T{other.contained.value()}); + else + contained.construct_error(E{other.contained.error()}); + } - template - nsel_REQUIRES_R( - void, std17::is_swappable::value &&std17::is_swappable::value && - (std::is_move_constructible::value || - std::is_move_constructible::value)) - swap(expected &other) noexcept( - std::is_nothrow_move_constructible::value && - std17::is_nothrow_swappable::value && - std::is_nothrow_move_constructible::value && - std17::is_nothrow_swappable::value) { - using std::swap; + template::value &&std::is_constructible::value + && !std::is_constructible &>::value + && !std::is_constructible &&>::value + && !std::is_constructible const &>::value + && !std::is_constructible const &&>::value + && !std::is_convertible &, T>::value + && !std::is_convertible &&, T>::value + && !std::is_convertible const &, T>::value + && !std::is_convertible const &&, T>::value + && !(!std::is_convertible::value + || !std::is_convertible::value) /*=> non-explicit + */ + )> + nsel_constexpr14 /*non-explicit*/ expected(expected const &other) : contained(other.has_value()) + { + if (has_value()) + contained.construct_value(other.contained.value()); + else + contained.construct_error(other.contained.error()); + } - if (bool(*this) && bool(other)) { - swap(contained.value(), other.contained.value()); - } else if (!bool(*this) && !bool(other)) { - swap(contained.error(), other.contained.error()); - } else if (bool(*this) && !bool(other)) { - error_type t(std::move(other.error())); - other.contained.destruct_error(); - other.contained.construct_value(std::move(contained.value())); - contained.destruct_value(); - contained.construct_error(std::move(t)); - bool has_value = contained.has_value(); - bool other_has_value = other.has_value(); - other.contained.set_has_value(has_value); - contained.set_has_value(other_has_value); - } else if (!bool(*this) && bool(other)) { - other.swap(*this); - } - } + template::value &&std::is_constructible::value + && !std::is_constructible &>::value + && !std::is_constructible &&>::value + && !std::is_constructible const &>::value + && !std::is_constructible const &&>::value + && !std::is_convertible &, T>::value + && !std::is_convertible &&, T>::value + && !std::is_convertible const &, T>::value + && !std::is_convertible const &&, T>::value + && (!std::is_convertible::value || !std::is_convertible::value) /*=> explicit */ + )> + nsel_constexpr14 explicit expected(expected &&other) : contained(other.has_value()) + { + if (has_value()) + contained.construct_value(T{std::move(other.contained.value())}); + else + contained.construct_error(E{std::move(other.contained.error())}); + } - // x.x.4.5 observers + template::value &&std::is_constructible::value + && !std::is_constructible &>::value + && !std::is_constructible &&>::value + && !std::is_constructible const &>::value + && !std::is_constructible const &&>::value + && !std::is_convertible &, T>::value + && !std::is_convertible &&, T>::value + && !std::is_convertible const &, T>::value + && !std::is_convertible const &&, T>::value + && !(!std::is_convertible::value || !std::is_convertible::value) /*=> non-explicit */ + )> + nsel_constexpr14 /*non-explicit*/ expected(expected &&other) : contained(other.has_value()) + { + if (has_value()) + contained.construct_value(std::move(other.contained.value())); + else + contained.construct_error(std::move(other.contained.error())); + } - constexpr value_type const *operator->() const { - return assert(has_value()), contained.value_ptr(); - } + template::value)> + nsel_constexpr14 expected(value_type const &value) : contained(true) + { + contained.construct_value(value); + } - value_type *operator->() { - return assert(has_value()), contained.value_ptr(); - } + template::value + && !std::is_same::type, nonstd_lite_in_place_t(U)>::value + && !std::is_same, typename std20::remove_cvref::type>::value + && !std::is_same, typename std20::remove_cvref::type>::value + && !std::is_convertible::value /*=> explicit */ + )> + nsel_constexpr14 explicit expected(U &&value) noexcept(std::is_nothrow_move_constructible::value + && std::is_nothrow_move_constructible::value) + : contained(true) + { + contained.construct_value(T{std::forward(value)}); + } - constexpr value_type const &operator*() const & { - return assert(has_value()), contained.value(); - } + template::value + && !std::is_same::type, nonstd_lite_in_place_t(U)>::value + && !std::is_same, typename std20::remove_cvref::type>::value + && !std::is_same, typename std20::remove_cvref::type>::value + && std::is_convertible::value /*=> non-explicit */ + )> + nsel_constexpr14 /*non-explicit*/ + expected(U &&value) noexcept(std::is_nothrow_move_constructible::value + && std::is_nothrow_move_constructible::value) + : contained(true) + { + contained.construct_value(std::forward(value)); + } - value_type &operator*() & { return assert(has_value()), contained.value(); } + // construct error: + + template::value + && !std::is_convertible::value /*=> explicit */ + )> + nsel_constexpr14 explicit expected(nonstd::unexpected_type const &error) : contained(false) + { + contained.construct_error(E{error.value()}); + } + + template::value + &&std::is_convertible::value /*=> non-explicit + */ + )> + nsel_constexpr14 /*non-explicit*/ + expected(nonstd::unexpected_type const &error) + : contained(false) + { + contained.construct_error(error.value()); + } + + template::value + && !std::is_convertible::value /*=> explicit */ + )> + nsel_constexpr14 explicit expected(nonstd::unexpected_type &&error) : contained(false) + { + contained.construct_error(E{std::move(error.value())}); + } + + template::value &&std::is_convertible::value /*=> non-explicit */ + )> + nsel_constexpr14 /*non-explicit*/ + expected(nonstd::unexpected_type &&error) + : contained(false) + { + contained.construct_error(std::move(error.value())); + } + + // in-place construction, value + + template::value)> + nsel_constexpr14 explicit expected(nonstd_lite_in_place_t(T), Args &&...args) : contained(true) + { + contained.emplace_value(std::forward(args)...); + } + + template< + typename U, + typename... Args nsel_REQUIRES_T(std::is_constructible, Args &&...>::value)> + nsel_constexpr14 explicit expected(nonstd_lite_in_place_t(T), std::initializer_list il, Args &&...args) + : contained(true) + { + contained.emplace_value(il, std::forward(args)...); + } + + // in-place construction, error + + template::value)> + nsel_constexpr14 explicit expected(unexpect_t, Args &&...args) : contained(false) + { + contained.emplace_error(std::forward(args)...); + } + + template< + typename U, + typename... Args nsel_REQUIRES_T(std::is_constructible, Args &&...>::value)> + nsel_constexpr14 explicit expected(unexpect_t, std::initializer_list il, Args &&...args) : contained(false) + { + contained.emplace_error(il, std::forward(args)...); + } + + // x.x.4.2 destructor + + // TODO: ~expected: triviality + // Effects: If T is not cv void and is_trivially_destructible_v is false + // and bool(*this), calls val.~T(). If is_trivially_destructible_v is + // false and !bool(*this), calls unexpect.~unexpected(). Remarks: If + // either T is cv void or is_trivially_destructible_v is true, and + // is_trivially_destructible_v is true, then this destructor shall be a + // trivial destructor. + + ~expected() + { + if (has_value()) + contained.destruct_value(); + else + contained.destruct_error(); + } + + // x.x.4.3 assignment + + expected &operator=(expected const &other) + { + expected(other).swap(*this); + return *this; + } + + expected &operator=(expected &&other) noexcept( + std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value + && std::is_nothrow_move_constructible::value// added for missing + && std::is_nothrow_move_assignable::value) // nothrow above + { + expected(std::move(other)).swap(*this); + return *this; + } + + template, typename std20::remove_cvref::type>::value + && std17::conjunction, std::is_same>>::value + && std::is_constructible::value && std::is_assignable::value + && std::is_nothrow_move_constructible::value)> + expected &operator=(U &&value) + { + expected(std::forward(value)).swap(*this); + return *this; + } + + template::value &&std::is_copy_constructible< + G>::value// TODO: std::is_nothrow_copy_constructible + &&std::is_copy_assignable::value)> + expected &operator=(nonstd::unexpected_type const &error) + { + expected(unexpect, error.value()).swap(*this); + return *this; + } + + template::value &&std::is_move_constructible< + G>::value// TODO: std::is_nothrow_move_constructible + &&std::is_move_assignable::value)> + expected &operator=(nonstd::unexpected_type &&error) + { + expected(unexpect, std::move(error.value())).swap(*this); + return *this; + } + + template::value)> + value_type &emplace(Args &&...args) + { + expected(nonstd_lite_in_place(T), std::forward(args)...).swap(*this); + return value(); + } + + template &, Args &&...>::value)> + value_type &emplace(std::initializer_list il, Args &&...args) + { + expected(nonstd_lite_in_place(T), il, std::forward(args)...).swap(*this); + return value(); + } + + // x.x.4.4 swap + + template + nsel_REQUIRES_R(void, + std17::is_swappable::value &&std17::is_swappable::value + && (std::is_move_constructible::value || std::is_move_constructible::value)) + swap(expected &other) noexcept( + std::is_nothrow_move_constructible::value && std17::is_nothrow_swappable::value + && std::is_nothrow_move_constructible::value && std17::is_nothrow_swappable::value) + { + using std::swap; + + if (bool(*this) && bool(other)) { + swap(contained.value(), other.contained.value()); + } else if (!bool(*this) && !bool(other)) { + swap(contained.error(), other.contained.error()); + } else if (bool(*this) && !bool(other)) { + error_type t(std::move(other.error())); + other.contained.destruct_error(); + other.contained.construct_value(std::move(contained.value())); + contained.destruct_value(); + contained.construct_error(std::move(t)); + bool has_value = contained.has_value(); + bool other_has_value = other.has_value(); + other.contained.set_has_value(has_value); + contained.set_has_value(other_has_value); + } else if (!bool(*this) && bool(other)) { + other.swap(*this); + } + } + + // x.x.4.5 observers + + constexpr value_type const *operator->() const { return assert(has_value()), contained.value_ptr(); } + + value_type *operator->() { return assert(has_value()), contained.value_ptr(); } + + constexpr value_type const &operator*() const & { return assert(has_value()), contained.value(); } + + value_type &operator*() & { return assert(has_value()), contained.value(); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - constexpr value_type const &&operator*() const && { - return std::move((assert(has_value()), contained.value())); - } + constexpr value_type const &&operator*() const && + { + return std::move((assert(has_value()), contained.value())); + } - nsel_constexpr14 value_type &&operator*() && { - return std::move((assert(has_value()), contained.value())); - } + nsel_constexpr14 value_type &&operator*() && { return std::move((assert(has_value()), contained.value())); } #endif - constexpr explicit operator bool() const noexcept { return has_value(); } + constexpr explicit operator bool() const noexcept { return has_value(); } - constexpr bool has_value() const noexcept { return contained.has_value(); } + constexpr bool has_value() const noexcept { return contained.has_value(); } - constexpr value_type const &value() const & { - return has_value() - ? (contained.value()) - : (error_traits::rethrow(contained.error()), - contained.value()); - } + constexpr value_type const &value() const & + { + return has_value() ? (contained.value()) + : (error_traits::rethrow(contained.error()), contained.value()); + } - value_type &value() & { - return has_value() - ? (contained.value()) - : (error_traits::rethrow(contained.error()), - contained.value()); - } + value_type &value() & + { + return has_value() ? (contained.value()) + : (error_traits::rethrow(contained.error()), contained.value()); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - constexpr value_type const &&value() const && { - return std::move( - has_value() ? (contained.value()) - : (error_traits::rethrow(contained.error()), - contained.value())); - } + constexpr value_type const &&value() const && + { + return std::move(has_value() ? (contained.value()) + : (error_traits::rethrow(contained.error()), contained.value())); + } - nsel_constexpr14 value_type &&value() && { - return std::move( - has_value() ? (contained.value()) - : (error_traits::rethrow(contained.error()), - contained.value())); - } + nsel_constexpr14 value_type &&value() && + { + return std::move(has_value() ? (contained.value()) + : (error_traits::rethrow(contained.error()), contained.value())); + } #endif - constexpr error_type const &error() const & { - return assert(!has_value()), contained.error(); - } + constexpr error_type const &error() const & { return assert(!has_value()), contained.error(); } - error_type &error() & { return assert(!has_value()), contained.error(); } + error_type &error() & { return assert(!has_value()), contained.error(); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - constexpr error_type const &&error() const && { - return std::move((assert(!has_value()), contained.error())); - } + constexpr error_type const &&error() const && { return std::move((assert(!has_value()), contained.error())); } - error_type &&error() && { - return std::move((assert(!has_value()), contained.error())); - } + error_type &&error() && { return std::move((assert(!has_value()), contained.error())); } #endif - constexpr unexpected_type get_unexpected() const { - return make_unexpected(contained.error()); - } + constexpr unexpected_type get_unexpected() const { return make_unexpected(contained.error()); } - template bool has_exception() const { - using ContainedEx = typename std::remove_reference< - decltype(get_unexpected().value())>::type; - return !has_value() && std::is_base_of::value; - } + template + bool has_exception() const + { + using ContainedEx = typename std::remove_reference::type; + return !has_value() && std::is_base_of::value; + } - template < - typename U nsel_REQUIRES_T(std::is_copy_constructible::value - &&std::is_convertible::value)> - value_type value_or(U &&v) const & { - return has_value() ? contained.value() - : static_cast(std::forward(v)); - } + template::value &&std::is_convertible::value)> + value_type value_or(U &&v) const & + { + return has_value() ? contained.value() : static_cast(std::forward(v)); + } - template < - typename U nsel_REQUIRES_T(std::is_move_constructible::value - &&std::is_convertible::value)> - value_type value_or(U &&v) && { - return has_value() ? std::move(contained.value()) - : static_cast(std::forward(v)); - } + template::value &&std::is_convertible::value)> + value_type value_or(U &&v) && + { + return has_value() ? std::move(contained.value()) : static_cast(std::forward(v)); + } #if nsel_P2505R >= 4 - template < - typename G = E nsel_REQUIRES_T(std::is_copy_constructible::value - &&std::is_convertible::value)> - nsel_constexpr error_type error_or(G &&e) const & { - return has_value() ? static_cast(std::forward(e)) - : contained.error(); - } + template< + typename G = E nsel_REQUIRES_T(std::is_copy_constructible::value &&std::is_convertible::value)> + nsel_constexpr error_type error_or(G &&e) const & + { + return has_value() ? static_cast(std::forward(e)) : contained.error(); + } - template < - typename G = E nsel_REQUIRES_T(std::is_move_constructible::value - &&std::is_convertible::value)> - nsel_constexpr14 error_type error_or(G &&e) && { - return has_value() ? static_cast(std::forward(e)) - : std::move(contained.error()); - } -#endif // nsel_P2505R >= 4 + template< + typename G = E nsel_REQUIRES_T(std::is_move_constructible::value &&std::is_convertible::value)> + nsel_constexpr14 error_type error_or(G &&e) && + { + return has_value() ? static_cast(std::forward(e)) : std::move(contained.error()); + } +#endif// nsel_P2505R >= 4 #if nsel_P2505R >= 3 - // Monadic operations (P2505) - template >:: - value &&std::is_same::error_type, - error_type>::value - &&std::is_constructible::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t - and_then(F &&f) & { - return has_value() ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), value())) - : detail::invoke_result_nocvref_t( - unexpect, error()); - } + // Monadic operations (P2505) + template>::value + &&std::is_same::error_type, error_type>::value + &&std::is_constructible::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t and_then(F &&f) & + { + return has_value() + ? detail::invoke_result_nocvref_t(detail::invoke(std::forward(f), value())) + : detail::invoke_result_nocvref_t(unexpect, error()); + } - template >::value - &&std::is_same::error_type, - error_type>::value - &&std::is_constructible::value)> - nsel_constexpr detail::invoke_result_nocvref_t - and_then(F &&f) const & { - return has_value() - ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), value())) - : detail::invoke_result_nocvref_t( - unexpect, error()); - } + template>::value + &&std::is_same::error_type, + error_type>::value &&std::is_constructible::value)> + nsel_constexpr detail::invoke_result_nocvref_t and_then(F &&f) const & + { + return has_value() + ? detail::invoke_result_nocvref_t(detail::invoke(std::forward(f), value())) + : detail::invoke_result_nocvref_t(unexpect, error()); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template >:: - value &&std::is_same::error_type, - error_type>::value - &&std::is_constructible::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t - and_then(F &&f) && { - return has_value() - ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), std::move(value()))) - : detail::invoke_result_nocvref_t( - unexpect, std::move(error())); - } + template>::value && + std::is_same::error_type, error_type>::value + &&std::is_constructible::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t and_then(F &&f) && + { + return has_value() + ? detail::invoke_result_nocvref_t( + detail::invoke(std::forward(f), std::move(value()))) + : detail::invoke_result_nocvref_t(unexpect, std::move(error())); + } - template >::value - &&std::is_same::error_type, - error_type>::value && - std::is_constructible::value)> - nsel_constexpr detail::invoke_result_nocvref_t - and_then(F &&f) const && { - return has_value() - ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), std::move(value()))) - : detail::invoke_result_nocvref_t( - unexpect, std::move(error())); - } + template>::value + &&std::is_same::error_type, + error_type>::value &&std::is_constructible::value)> + nsel_constexpr detail::invoke_result_nocvref_t and_then(F &&f) const && + { + return has_value() + ? detail::invoke_result_nocvref_t( + detail::invoke(std::forward(f), std::move(value()))) + : detail::invoke_result_nocvref_t(unexpect, std::move(error())); + } #endif - template >:: - value &&std::is_same::value_type, - value_type>::value + template>::value + &&std::is_same::value_type, value_type>::value + &&std::is_constructible::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t or_else(F &&f) & + { + return has_value() + ? detail::invoke_result_nocvref_t(value()) + : detail::invoke_result_nocvref_t(detail::invoke(std::forward(f), error())); + } + + template>::value + &&std::is_same::value_type, + value_type>::value &&std::is_constructible::value)> + nsel_constexpr detail::invoke_result_nocvref_t or_else(F &&f) const & + { + return has_value() + ? detail::invoke_result_nocvref_t(value()) + : detail::invoke_result_nocvref_t(detail::invoke(std::forward(f), error())); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template>::value && + std::is_same::value_type, value_type>::value + &&std::is_constructible::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t or_else(F &&f) && + { + return has_value() + ? detail::invoke_result_nocvref_t(std::move(value())) + : detail::invoke_result_nocvref_t( + detail::invoke(std::forward(f), std::move(error()))); + } + + template>::value + &&std::is_same::value_type, + value_type>::value &&std::is_constructible::value)> + nsel_constexpr detail::invoke_result_nocvref_t or_else(F &&f) const && + { + return has_value() + ? detail::invoke_result_nocvref_t(std::move(value())) + : detail::invoke_result_nocvref_t( + detail::invoke(std::forward(f), std::move(error()))); + } +#endif + + template::value + && !std::is_void>::value + && detail::valid_expected_value_type>::value)> + nsel_constexpr14 expected, error_type> transform(F &&f) & + { + return has_value() + ? expected, error_type>( + detail::invoke(std::forward(f), **this)) + : make_unexpected(error()); + } + + template::value &&std::is_void< + detail::transform_invoke_result_t>::value)> + nsel_constexpr14 expected transform(F &&f) & + { + return has_value() ? (detail::invoke(std::forward(f), **this), expected()) + : make_unexpected(error()); + } + + template::value + && !std::is_void>::value + && detail::valid_expected_value_type>::value)> + nsel_constexpr expected, error_type> + transform(F &&f) const & + { + return has_value() + ? expected, error_type>( + detail::invoke(std::forward(f), **this)) + : make_unexpected(error()); + } + + template::value &&std::is_void< + detail::transform_invoke_result_t>::value)> + nsel_constexpr expected transform(F &&f) const & + { + return has_value() ? (detail::invoke(std::forward(f), **this), expected()) + : make_unexpected(error()); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template::value + && !std::is_void>::value + && detail::valid_expected_value_type>::value)> + nsel_constexpr14 expected, error_type> transform(F &&f) && + { + return has_value() + ? expected, error_type>( + detail::invoke(std::forward(f), std::move(**this))) + : make_unexpected(std::move(error())); + } + + template::value &&std::is_void< + detail::transform_invoke_result_t>::value)> + nsel_constexpr14 expected transform(F &&f) && + { + return has_value() ? (detail::invoke(std::forward(f), **this), expected()) + : make_unexpected(std::move(error())); + } + + template::value + && !std::is_void>::value + && detail::valid_expected_value_type>::value)> + nsel_constexpr expected, error_type> + transform(F &&f) const && + { + return has_value() + ? expected, error_type>( + detail::invoke(std::forward(f), std::move(**this))) + : make_unexpected(std::move(error())); + } + + template::value + &&std::is_void>::value)> + nsel_constexpr expected transform(F &&f) const && + { + return has_value() ? (detail::invoke(std::forward(f), **this), expected()) + : make_unexpected(std::move(error())); + } +#endif + + template>::value &&std::is_constructible::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t - or_else(F &&f) & { - return has_value() - ? detail::invoke_result_nocvref_t(value()) - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), error())); - } + nsel_constexpr14 expected> + transform_error(F &&f) & + { + return has_value() + ? expected>(in_place, **this) + : make_unexpected(detail::invoke(std::forward(f), error())); + } - template >::value - &&std::is_same::value_type, - value_type>::value + template>::value &&std::is_constructible::value)> - nsel_constexpr detail::invoke_result_nocvref_t - or_else(F &&f) const & { - return has_value() - ? detail::invoke_result_nocvref_t( - value()) - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), error())); - } + nsel_constexpr expected> + transform_error(F &&f) const & + { + return has_value() + ? expected>(in_place, **this) + : make_unexpected(detail::invoke(std::forward(f), error())); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template >:: - value &&std::is_same::value_type, - value_type>::value + template>::value &&std::is_constructible::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t - or_else(F &&f) && { - return has_value() - ? detail::invoke_result_nocvref_t( - std::move(value())) - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), std::move(error()))); - } + nsel_constexpr14 expected> + transform_error(F &&f) && + { + return has_value() + ? expected>(in_place, std::move(**this)) + : make_unexpected(detail::invoke(std::forward(f), std::move(error()))); + } - template >::value - &&std::is_same::value_type, - value_type>::value && - std::is_constructible::value)> - nsel_constexpr detail::invoke_result_nocvref_t - or_else(F &&f) const && { - return has_value() - ? detail::invoke_result_nocvref_t( - std::move(value())) - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), std::move(error()))); - } + template>::value + &&std::is_constructible::value)> + nsel_constexpr expected> + transform_error(F &&f) const && + { + return has_value() + ? expected>( + in_place, std::move(**this)) + : make_unexpected(detail::invoke(std::forward(f), std::move(error()))); + } #endif +#endif// nsel_P2505R >= 3 \ + // unwrap() - template ::value && - !std::is_void< - detail::transform_invoke_result_t>::value && - detail::valid_expected_value_type< - detail::transform_invoke_result_t>::value)> - nsel_constexpr14 - expected, error_type> - transform(F &&f) & { - return has_value() - ? expected, - error_type>( - detail::invoke(std::forward(f), **this)) - : make_unexpected(error()); - } + // template + // constexpr expected expected,E>::unwrap() const&; - template ::value &&std::is_void< - detail::transform_invoke_result_t>::value)> - nsel_constexpr14 expected transform(F &&f) & { - return has_value() ? (detail::invoke(std::forward(f), **this), - expected()) - : make_unexpected(error()); - } + // template + // constexpr expected expected::unwrap() const&; - template ::value && - !std::is_void< - detail::transform_invoke_result_t>::value && - detail::valid_expected_value_type< - detail::transform_invoke_result_t>::value)> - nsel_constexpr - expected, - error_type> - transform(F &&f) const & { - return has_value() - ? expected< - detail::transform_invoke_result_t, - error_type>(detail::invoke(std::forward(f), **this)) - : make_unexpected(error()); - } + // template + // expected expected, E>::unwrap() &&; - template ::value - &&std::is_void>::value)> - nsel_constexpr expected transform(F &&f) const & { - return has_value() ? (detail::invoke(std::forward(f), **this), - expected()) - : make_unexpected(error()); - } + // template + // template expected expected::unwrap() &&; -#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template ::value && - !std::is_void< - detail::transform_invoke_result_t>::value && - detail::valid_expected_value_type< - detail::transform_invoke_result_t>::value)> - nsel_constexpr14 - expected, - error_type> - transform(F &&f) && { - return has_value() - ? expected, - error_type>( - detail::invoke(std::forward(f), std::move(**this))) - : make_unexpected(std::move(error())); - } + // factories - template ::value &&std::is_void< - detail::transform_invoke_result_t>::value)> - nsel_constexpr14 expected transform(F &&f) && { - return has_value() ? (detail::invoke(std::forward(f), **this), - expected()) - : make_unexpected(std::move(error())); - } + // template< typename Ex, typename F> + // expected catch_exception(F&& f); - template ::value && - !std::is_void< - detail::transform_invoke_result_t>::value && - detail::valid_expected_value_type< - detail::transform_invoke_result_t>::value)> - nsel_constexpr - expected, - error_type> - transform(F &&f) const && { - return has_value() ? expected, - error_type>(detail::invoke( - std::forward(f), std::move(**this))) - : make_unexpected(std::move(error())); - } + // template< typename F> + // expected())),E> map(F&& func) ; - template ::value - &&std::is_void>::value)> - nsel_constexpr expected transform(F &&f) const && { - return has_value() ? (detail::invoke(std::forward(f), **this), - expected()) - : make_unexpected(std::move(error())); - } -#endif + // template< typename F> + // 'see below' bind(F&& func); - template >::value - &&std::is_constructible::value)> - nsel_constexpr14 - expected> - transform_error(F &&f) & { - return has_value() - ? expected>( - in_place, **this) - : make_unexpected(detail::invoke(std::forward(f), error())); - } + // template< typename F> + // expected catch_error(F&& f); - template >::value - &&std::is_constructible::value)> - nsel_constexpr - expected> - transform_error(F &&f) const & { - return has_value() - ? expected>(in_place, - **this) - : make_unexpected(detail::invoke(std::forward(f), error())); - } + // template< typename F> + // 'see below' then(F&& func); -#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template >::value - &&std::is_constructible::value)> - nsel_constexpr14 - expected> - transform_error(F &&f) && { - return has_value() - ? expected>( - in_place, std::move(**this)) - : make_unexpected( - detail::invoke(std::forward(f), std::move(error()))); - } + private: + detail::storage_t::value && std::is_copy_constructible::value, + std::is_move_constructible::value && std::is_move_constructible::value> + contained; + }; - template >::value - &&std::is_constructible::value)> - nsel_constexpr - expected> - transform_error(F &&f) const && { - return has_value() - ? expected>( - in_place, std::move(**this)) - : make_unexpected( - detail::invoke(std::forward(f), std::move(error()))); - } -#endif -#endif // nsel_P2505R >= 3 - // unwrap() + /// class expected, void specialization - // template - // constexpr expected expected,E>::unwrap() const&; + template + class expected { + private: + template + friend class expected; - // template - // constexpr expected expected::unwrap() const&; + public: + using value_type = void; + using error_type = E; + using unexpected_type = nonstd::unexpected_type; - // template - // expected expected, E>::unwrap() &&; + // x.x.4.1 constructors - // template - // template expected expected::unwrap() &&; + constexpr expected() noexcept : contained(true) {} - // factories + nsel_constexpr14 expected(expected const &other) = default; + nsel_constexpr14 expected(expected &&other) = default; - // template< typename Ex, typename F> - // expected catch_exception(F&& f); + constexpr explicit expected(nonstd_lite_in_place_t(void)) : contained(true) {} - // template< typename F> - // expected())),E> map(F&& func) ; + template::value /*=> explicit */ + )> + nsel_constexpr14 explicit expected(nonstd::unexpected_type const &error) : contained(false) + { + contained.construct_error(E{error.value()}); + } - // template< typename F> - // 'see below' bind(F&& func); + template::value /*=> non-explicit */ + )> + nsel_constexpr14 /*non-explicit*/ + expected(nonstd::unexpected_type const &error) + : contained(false) + { + contained.construct_error(error.value()); + } - // template< typename F> - // expected catch_error(F&& f); + template::value /*=> explicit */ + )> + nsel_constexpr14 explicit expected(nonstd::unexpected_type &&error) : contained(false) + { + contained.construct_error(E{std::move(error.value())}); + } - // template< typename F> - // 'see below' then(F&& func); + template::value /*=> non-explicit */ + )> + nsel_constexpr14 /*non-explicit*/ + expected(nonstd::unexpected_type &&error) + : contained(false) + { + contained.construct_error(std::move(error.value())); + } - private: - detail::storage_t::value && - std::is_copy_constructible::value, - std::is_move_constructible::value && - std::is_move_constructible::value> - contained; - }; + template::value)> + nsel_constexpr14 explicit expected(unexpect_t, Args &&...args) : contained(false) + { + contained.emplace_error(std::forward(args)...); + } - /// class expected, void specialization + template< + typename U, + typename... Args nsel_REQUIRES_T(std::is_constructible, Args &&...>::value)> + nsel_constexpr14 explicit expected(unexpect_t, std::initializer_list il, Args &&...args) : contained(false) + { + contained.emplace_error(il, std::forward(args)...); + } - template class expected { - private: - template friend class expected; + // destructor - public: - using value_type = void; - using error_type = E; - using unexpected_type = nonstd::unexpected_type; + ~expected() + { + if (!has_value()) { contained.destruct_error(); } + } - // x.x.4.1 constructors + // x.x.4.3 assignment - constexpr expected() noexcept : contained(true) {} + expected &operator=(expected const &other) + { + expected(other).swap(*this); + return *this; + } - nsel_constexpr14 expected(expected const &other) = default; - nsel_constexpr14 expected(expected &&other) = default; + expected &operator=(expected &&other) noexcept(std::is_nothrow_move_assignable::value + && std::is_nothrow_move_constructible::value) + { + expected(std::move(other)).swap(*this); + return *this; + } - constexpr explicit expected(nonstd_lite_in_place_t(void)) - : contained(true) {} + void emplace() { expected().swap(*this); } - template ::value /*=> explicit */ - )> - nsel_constexpr14 explicit expected(nonstd::unexpected_type const &error) - : contained(false) { - contained.construct_error(E{error.value()}); - } + // x.x.4.4 swap - template ::value /*=> non-explicit */ - )> - nsel_constexpr14 /*non-explicit*/ - expected(nonstd::unexpected_type const &error) - : contained(false) { - contained.construct_error(error.value()); - } + template + nsel_REQUIRES_R(void, std17::is_swappable::value &&std::is_move_constructible::value) + swap(expected &other) noexcept(std::is_nothrow_move_constructible::value + && std17::is_nothrow_swappable::value) + { + using std::swap; - template ::value /*=> explicit */ - )> - nsel_constexpr14 explicit expected(nonstd::unexpected_type &&error) - : contained(false) { - contained.construct_error(E{std::move(error.value())}); - } + if (!bool(*this) && !bool(other)) { + swap(contained.error(), other.contained.error()); + } else if (bool(*this) && !bool(other)) { + contained.construct_error(std::move(other.error())); + bool has_value = contained.has_value(); + bool other_has_value = other.has_value(); + other.contained.set_has_value(has_value); + contained.set_has_value(other_has_value); + } else if (!bool(*this) && bool(other)) { + other.swap(*this); + } + } - template ::value /*=> non-explicit */ - )> - nsel_constexpr14 /*non-explicit*/ - expected(nonstd::unexpected_type &&error) - : contained(false) { - contained.construct_error(std::move(error.value())); - } + // x.x.4.5 observers - template ::value)> - nsel_constexpr14 explicit expected(unexpect_t, Args &&...args) - : contained(false) { - contained.emplace_error(std::forward(args)...); - } + constexpr explicit operator bool() const noexcept { return has_value(); } - template , - Args &&...>::value)> - nsel_constexpr14 explicit expected(unexpect_t, std::initializer_list il, - Args &&...args) - : contained(false) { - contained.emplace_error(il, std::forward(args)...); - } + constexpr bool has_value() const noexcept { return contained.has_value(); } - // destructor + void value() const + { + if (!has_value()) { error_traits::rethrow(contained.error()); } + } - ~expected() { - if (!has_value()) { - contained.destruct_error(); - } - } + constexpr error_type const &error() const & { return assert(!has_value()), contained.error(); } - // x.x.4.3 assignment - - expected &operator=(expected const &other) { - expected(other).swap(*this); - return *this; - } - - expected &operator=(expected &&other) noexcept( - std::is_nothrow_move_assignable::value && - std::is_nothrow_move_constructible::value) { - expected(std::move(other)).swap(*this); - return *this; - } - - void emplace() { expected().swap(*this); } - - // x.x.4.4 swap - - template - nsel_REQUIRES_R( - void, - std17::is_swappable::value &&std::is_move_constructible::value) - swap(expected &other) noexcept( - std::is_nothrow_move_constructible::value && - std17::is_nothrow_swappable::value) { - using std::swap; - - if (!bool(*this) && !bool(other)) { - swap(contained.error(), other.contained.error()); - } else if (bool(*this) && !bool(other)) { - contained.construct_error(std::move(other.error())); - bool has_value = contained.has_value(); - bool other_has_value = other.has_value(); - other.contained.set_has_value(has_value); - contained.set_has_value(other_has_value); - } else if (!bool(*this) && bool(other)) { - other.swap(*this); - } - } - - // x.x.4.5 observers - - constexpr explicit operator bool() const noexcept { return has_value(); } - - constexpr bool has_value() const noexcept { return contained.has_value(); } - - void value() const { - if (!has_value()) { - error_traits::rethrow(contained.error()); - } - } - - constexpr error_type const &error() const & { - return assert(!has_value()), contained.error(); - } - - error_type &error() & { return assert(!has_value()), contained.error(); } + error_type &error() & { return assert(!has_value()), contained.error(); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - constexpr error_type const &&error() const && { - return std::move((assert(!has_value()), contained.error())); - } + constexpr error_type const &&error() const && { return std::move((assert(!has_value()), contained.error())); } - error_type &&error() && { - return std::move((assert(!has_value()), contained.error())); - } + error_type &&error() && { return std::move((assert(!has_value()), contained.error())); } #endif - constexpr unexpected_type get_unexpected() const { - return make_unexpected(contained.error()); - } + constexpr unexpected_type get_unexpected() const { return make_unexpected(contained.error()); } - template bool has_exception() const { - using ContainedEx = typename std::remove_reference< - decltype(get_unexpected().value())>::type; - return !has_value() && std::is_base_of::value; - } + template + bool has_exception() const + { + using ContainedEx = typename std::remove_reference::type; + return !has_value() && std::is_base_of::value; + } #if nsel_P2505R >= 4 - template < - typename G = E nsel_REQUIRES_T(std::is_copy_constructible::value - &&std::is_convertible::value)> - nsel_constexpr error_type error_or(G &&e) const & { - return has_value() ? static_cast(std::forward(e)) - : contained.error(); - } + template< + typename G = E nsel_REQUIRES_T(std::is_copy_constructible::value &&std::is_convertible::value)> + nsel_constexpr error_type error_or(G &&e) const & + { + return has_value() ? static_cast(std::forward(e)) : contained.error(); + } - template < - typename G = E nsel_REQUIRES_T(std::is_move_constructible::value - &&std::is_convertible::value)> - nsel_constexpr14 error_type error_or(G &&e) && { - return has_value() ? static_cast(std::forward(e)) - : std::move(contained.error()); - } -#endif // nsel_P2505R >= 4 + template< + typename G = E nsel_REQUIRES_T(std::is_move_constructible::value &&std::is_convertible::value)> + nsel_constexpr14 error_type error_or(G &&e) && + { + return has_value() ? static_cast(std::forward(e)) : std::move(contained.error()); + } +#endif// nsel_P2505R >= 4 #if nsel_P2505R >= 3 - // Monadic operations (P2505) - template >::value &&std:: - is_same::error_type, - error_type>::value - &&std::is_constructible::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t and_then(F &&f) & { - return has_value() - ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f))) - : detail::invoke_result_nocvref_t(unexpect, error()); - } + // Monadic operations (P2505) + template>::value + &&std::is_same::error_type, error_type>::value + &&std::is_constructible::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t and_then(F &&f) & + { + return has_value() ? detail::invoke_result_nocvref_t(detail::invoke(std::forward(f))) + : detail::invoke_result_nocvref_t(unexpect, error()); + } - template >::value &&std:: - is_same::error_type, - error_type>::value - &&std::is_constructible::value)> - nsel_constexpr detail::invoke_result_nocvref_t and_then(F &&f) const & { - return has_value() - ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f))) - : detail::invoke_result_nocvref_t(unexpect, error()); - } + template>::value + &&std::is_same::error_type, error_type>::value + &&std::is_constructible::value)> + nsel_constexpr detail::invoke_result_nocvref_t and_then(F &&f) const & + { + return has_value() ? detail::invoke_result_nocvref_t(detail::invoke(std::forward(f))) + : detail::invoke_result_nocvref_t(unexpect, error()); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template >::value &&std:: - is_same::error_type, - error_type>::value - &&std::is_constructible::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t and_then(F &&f) && { - return has_value() ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f))) - : detail::invoke_result_nocvref_t( - unexpect, std::move(error())); - } + template>::value + &&std::is_same::error_type, error_type>::value + &&std::is_constructible::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t and_then(F &&f) && + { + return has_value() ? detail::invoke_result_nocvref_t(detail::invoke(std::forward(f))) + : detail::invoke_result_nocvref_t(unexpect, std::move(error())); + } - template >::value &&std:: - is_same::error_type, - error_type>::value && - std::is_constructible::value)> - nsel_constexpr detail::invoke_result_nocvref_t and_then(F &&f) const && { - return has_value() ? detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f))) - : detail::invoke_result_nocvref_t( - unexpect, std::move(error())); - } + template>::value + &&std::is_same::error_type, error_type>::value + &&std::is_constructible::value)> + nsel_constexpr detail::invoke_result_nocvref_t and_then(F &&f) const && + { + return has_value() ? detail::invoke_result_nocvref_t(detail::invoke(std::forward(f))) + : detail::invoke_result_nocvref_t(unexpect, std::move(error())); + } #endif - template >:: - value &&std::is_void::value_type>::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t - or_else(F &&f) & { - return has_value() ? detail::invoke_result_nocvref_t() - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), error())); - } + template>::value + &&std::is_void::value_type>::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t or_else(F &&f) & + { + return has_value() + ? detail::invoke_result_nocvref_t() + : detail::invoke_result_nocvref_t(detail::invoke(std::forward(f), error())); + } - template >::value - &&std::is_void::value_type>::value)> - nsel_constexpr detail::invoke_result_nocvref_t - or_else(F &&f) const & { - return has_value() - ? detail::invoke_result_nocvref_t() - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), error())); - } + template>::value + &&std::is_void::value_type>::value)> + nsel_constexpr detail::invoke_result_nocvref_t or_else(F &&f) const & + { + return has_value() + ? detail::invoke_result_nocvref_t() + : detail::invoke_result_nocvref_t(detail::invoke(std::forward(f), error())); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template >:: - value &&std::is_void::value_type>::value)> - nsel_constexpr14 detail::invoke_result_nocvref_t - or_else(F &&f) && { - return has_value() - ? detail::invoke_result_nocvref_t() - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), std::move(error()))); - } + template>::value + &&std::is_void::value_type>::value)> + nsel_constexpr14 detail::invoke_result_nocvref_t or_else(F &&f) && + { + return has_value() + ? detail::invoke_result_nocvref_t() + : detail::invoke_result_nocvref_t( + detail::invoke(std::forward(f), std::move(error()))); + } - template >::value - &&std::is_void::value_type>::value)> - nsel_constexpr detail::invoke_result_nocvref_t - or_else(F &&f) const && { - return has_value() - ? detail::invoke_result_nocvref_t() - : detail::invoke_result_nocvref_t( - detail::invoke(std::forward(f), std::move(error()))); - } + template>::value + &&std::is_void::value_type>::value)> + nsel_constexpr detail::invoke_result_nocvref_t or_else(F &&f) const && + { + return has_value() + ? detail::invoke_result_nocvref_t() + : detail::invoke_result_nocvref_t( + detail::invoke(std::forward(f), std::move(error()))); + } #endif - template ::value && - !std::is_void>::value)> - nsel_constexpr14 expected, error_type> - transform(F &&f) & { - return has_value() - ? expected, error_type>( - detail::invoke(std::forward(f))) - : make_unexpected(error()); - } + template::value + && !std::is_void>::value)> + nsel_constexpr14 expected, error_type> transform(F &&f) & + { + return has_value() + ? expected, error_type>(detail::invoke(std::forward(f))) + : make_unexpected(error()); + } - template ::value - &&std::is_void>::value)> - nsel_constexpr14 expected transform(F &&f) & { - return has_value() ? (detail::invoke(std::forward(f)), - expected()) - : make_unexpected(error()); - } + template::value + &&std::is_void>::value)> + nsel_constexpr14 expected transform(F &&f) & + { + return has_value() ? (detail::invoke(std::forward(f)), expected()) + : make_unexpected(error()); + } - template ::value && - !std::is_void>::value)> - nsel_constexpr expected, error_type> - transform(F &&f) const & { - return has_value() - ? expected, error_type>( - detail::invoke(std::forward(f))) - : make_unexpected(error()); - } + template::value + && !std::is_void>::value)> + nsel_constexpr expected, error_type> transform(F &&f) const & + { + return has_value() + ? expected, error_type>(detail::invoke(std::forward(f))) + : make_unexpected(error()); + } - template ::value - &&std::is_void>::value)> - nsel_constexpr expected transform(F &&f) const & { - return has_value() ? (detail::invoke(std::forward(f)), - expected()) - : make_unexpected(error()); - } + template::value + &&std::is_void>::value)> + nsel_constexpr expected transform(F &&f) const & + { + return has_value() ? (detail::invoke(std::forward(f)), expected()) + : make_unexpected(error()); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template ::value && - !std::is_void>::value)> - nsel_constexpr14 expected, error_type> - transform(F &&f) && { - return has_value() - ? expected, error_type>( - detail::invoke(std::forward(f))) - : make_unexpected(error()); - } + template::value + && !std::is_void>::value)> + nsel_constexpr14 expected, error_type> transform(F &&f) && + { + return has_value() + ? expected, error_type>(detail::invoke(std::forward(f))) + : make_unexpected(error()); + } - template ::value - &&std::is_void>::value)> - nsel_constexpr14 expected transform(F &&f) && { - return has_value() ? (detail::invoke(std::forward(f)), - expected()) - : make_unexpected(error()); - } + template::value + &&std::is_void>::value)> + nsel_constexpr14 expected transform(F &&f) && + { + return has_value() ? (detail::invoke(std::forward(f)), expected()) + : make_unexpected(error()); + } - template ::value && - !std::is_void>::value)> - nsel_constexpr expected, error_type> - transform(F &&f) const && { - return has_value() - ? expected, error_type>( - detail::invoke(std::forward(f))) - : make_unexpected(error()); - } + template::value + && !std::is_void>::value)> + nsel_constexpr expected, error_type> transform(F &&f) const && + { + return has_value() + ? expected, error_type>(detail::invoke(std::forward(f))) + : make_unexpected(error()); + } - template ::value - &&std::is_void>::value)> - nsel_constexpr expected transform(F &&f) const && { - return has_value() ? (detail::invoke(std::forward(f)), - expected()) - : make_unexpected(error()); - } + template::value + &&std::is_void>::value)> + nsel_constexpr expected transform(F &&f) const && + { + return has_value() ? (detail::invoke(std::forward(f)), expected()) + : make_unexpected(error()); + } #endif - template >::value)> - nsel_constexpr14 - expected> - transform_error(F &&f) & { - return has_value() - ? expected>() - : make_unexpected(detail::invoke(std::forward(f), error())); - } + template>::value)> + nsel_constexpr14 expected> transform_error(F &&f) & + { + return has_value() ? expected>() + : make_unexpected(detail::invoke(std::forward(f), error())); + } - template >::value)> - nsel_constexpr - expected> - transform_error(F &&f) const & { - return has_value() - ? expected>() - : make_unexpected(detail::invoke(std::forward(f), error())); - } + template>::value)> + nsel_constexpr expected> + transform_error(F &&f) const & + { + return has_value() ? expected>() + : make_unexpected(detail::invoke(std::forward(f), error())); + } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 - template >::value)> - nsel_constexpr14 - expected> - transform_error(F &&f) && { - return has_value() ? expected>() - : make_unexpected(detail::invoke(std::forward(f), - std::move(error()))); - } + template>::value)> + nsel_constexpr14 expected> transform_error(F &&f) && + { + return has_value() ? expected>() + : make_unexpected(detail::invoke(std::forward(f), std::move(error()))); + } - template >::value)> - nsel_constexpr - expected> - transform_error(F &&f) const && { - return has_value() ? expected>() - : make_unexpected(detail::invoke(std::forward(f), - std::move(error()))); - } + template>::value)> + nsel_constexpr expected> + transform_error(F &&f) const && + { + return has_value() ? expected>() + : make_unexpected(detail::invoke(std::forward(f), std::move(error()))); + } #endif -#endif // nsel_P2505R >= 3 +#endif// nsel_P2505R >= 3 - // template constexpr 'see below' unwrap() const&; - // - // template 'see below' unwrap() &&; + // template constexpr 'see below' unwrap() const&; + // + // template 'see below' unwrap() &&; - // factories + // factories - // template< typename Ex, typename F> - // expected catch_exception(F&& f); - // - // template< typename F> - // expected map(F&& func) ; - // - // template< typename F> - // 'see below' bind(F&& func) ; - // - // template< typename F> - // expected catch_error(F&& f); - // - // template< typename F> - // 'see below' then(F&& func); + // template< typename Ex, typename F> + // expected catch_exception(F&& f); + // + // template< typename F> + // expected map(F&& func) ; + // + // template< typename F> + // 'see below' bind(F&& func) ; + // + // template< typename F> + // expected catch_error(F&& f); + // + // template< typename F> + // 'see below' then(F&& func); - private: - detail::storage_t::value, - std::is_move_constructible::value> - contained; - }; + private: + detail::storage_t::value, std::is_move_constructible::value> + contained; + }; - // x.x.4.6 expected<>: comparison operators + // x.x.4.6 expected<>: comparison operators - template ::value && - !std::is_void::value)> - constexpr bool operator==(expected const &x, - expected const &y) { - return bool(x) != bool(y) ? false - : bool(x) ? *x == *y - : x.error() == y.error(); - } + template::value && !std::is_void::value)> + constexpr bool operator==(expected const &x, expected const &y) + { + return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error(); + } - template ::value &&std::is_void::value)> - constexpr bool operator==(expected const &x, - expected const &y) { - return bool(x) != bool(y) - ? false - : bool(x) || static_cast(x.error() == y.error()); - } + template::value &&std::is_void::value)> + constexpr bool operator==(expected const &x, expected const &y) + { + return bool(x) != bool(y) ? false : bool(x) || static_cast(x.error() == y.error()); + } - template - constexpr bool operator!=(expected const &x, - expected const &y) { - return !(x == y); - } + template + constexpr bool operator!=(expected const &x, expected const &y) + { + return !(x == y); + } #if nsel_P0323R <= 2 - template - constexpr bool operator<(expected const &x, expected const &y) { - return (!y) ? false : (!x) ? true : *x < *y; - } + template + constexpr bool operator<(expected const &x, expected const &y) + { + return (!y) ? false : (!x) ? true : *x < *y; + } - template - constexpr bool operator>(expected const &x, expected const &y) { - return (y < x); - } + template + constexpr bool operator>(expected const &x, expected const &y) + { + return (y < x); + } - template - constexpr bool operator<=(expected const &x, expected const &y) { - return !(y < x); - } + template + constexpr bool operator<=(expected const &x, expected const &y) + { + return !(y < x); + } - template - constexpr bool operator>=(expected const &x, expected const &y) { - return !(x < y); - } + template + constexpr bool operator>=(expected const &x, expected const &y) + { + return !(x < y); + } #endif - // x.x.4.7 expected: comparison with T + // x.x.4.7 expected: comparison with T - template ::value)> - constexpr bool operator==(expected const &x, T2 const &v) { - return bool(x) ? *x == v : false; - } + template::value)> + constexpr bool operator==(expected const &x, T2 const &v) + { + return bool(x) ? *x == v : false; + } - template ::value)> - constexpr bool operator==(T2 const &v, expected const &x) { - return bool(x) ? v == *x : false; - } + template::value)> + constexpr bool operator==(T2 const &v, expected const &x) + { + return bool(x) ? v == *x : false; + } - template - constexpr bool operator!=(expected const &x, T2 const &v) { - return bool(x) ? *x != v : true; - } + template + constexpr bool operator!=(expected const &x, T2 const &v) + { + return bool(x) ? *x != v : true; + } - template - constexpr bool operator!=(T2 const &v, expected const &x) { - return bool(x) ? v != *x : true; - } + template + constexpr bool operator!=(T2 const &v, expected const &x) + { + return bool(x) ? v != *x : true; + } #if nsel_P0323R <= 2 - template - constexpr bool operator<(expected const &x, T const &v) { - return bool(x) ? *x < v : true; - } + template + constexpr bool operator<(expected const &x, T const &v) + { + return bool(x) ? *x < v : true; + } - template - constexpr bool operator<(T const &v, expected const &x) { - return bool(x) ? v < *x : false; - } + template + constexpr bool operator<(T const &v, expected const &x) + { + return bool(x) ? v < *x : false; + } - template - constexpr bool operator>(T const &v, expected const &x) { - return bool(x) ? *x < v : false; - } + template + constexpr bool operator>(T const &v, expected const &x) + { + return bool(x) ? *x < v : false; + } - template - constexpr bool operator>(expected const &x, T const &v) { - return bool(x) ? v < *x : false; - } + template + constexpr bool operator>(expected const &x, T const &v) + { + return bool(x) ? v < *x : false; + } - template - constexpr bool operator<=(T const &v, expected const &x) { - return bool(x) ? !(*x < v) : false; - } + template + constexpr bool operator<=(T const &v, expected const &x) + { + return bool(x) ? !(*x < v) : false; + } - template - constexpr bool operator<=(expected const &x, T const &v) { - return bool(x) ? !(v < *x) : true; - } + template + constexpr bool operator<=(expected const &x, T const &v) + { + return bool(x) ? !(v < *x) : true; + } - template - constexpr bool operator>=(expected const &x, T const &v) { - return bool(x) ? !(*x < v) : false; - } + template + constexpr bool operator>=(expected const &x, T const &v) + { + return bool(x) ? !(*x < v) : false; + } - template - constexpr bool operator>=(T const &v, expected const &x) { - return bool(x) ? !(v < *x) : true; - } + template + constexpr bool operator>=(T const &v, expected const &x) + { + return bool(x) ? !(v < *x) : true; + } -#endif // nsel_P0323R +#endif// nsel_P0323R - // x.x.4.8 expected: comparison with unexpected_type + // x.x.4.8 expected: comparison with unexpected_type - template - constexpr bool operator==(expected const &x, - unexpected_type const &u) { - return (!x) ? x.get_unexpected() == u : false; - } + template + constexpr bool operator==(expected const &x, unexpected_type const &u) + { + return (!x) ? x.get_unexpected() == u : false; + } - template - constexpr bool operator==(unexpected_type const &u, - expected const &x) { - return (x == u); - } + template + constexpr bool operator==(unexpected_type const &u, expected const &x) + { + return (x == u); + } - template - constexpr bool operator!=(expected const &x, - unexpected_type const &u) { - return !(x == u); - } + template + constexpr bool operator!=(expected const &x, unexpected_type const &u) + { + return !(x == u); + } - template - constexpr bool operator!=(unexpected_type const &u, - expected const &x) { - return !(x == u); - } + template + constexpr bool operator!=(unexpected_type const &u, expected const &x) + { + return !(x == u); + } #if nsel_P0323R <= 2 - template - constexpr bool operator<(expected const &x, - unexpected_type const &u) { - return (!x) ? (x.get_unexpected() < u) : false; - } + template + constexpr bool operator<(expected const &x, unexpected_type const &u) + { + return (!x) ? (x.get_unexpected() < u) : false; + } - template - constexpr bool operator<(unexpected_type const &u, - expected const &x) { - return (!x) ? (u < x.get_unexpected()) : true; - } + template + constexpr bool operator<(unexpected_type const &u, expected const &x) + { + return (!x) ? (u < x.get_unexpected()) : true; + } - template - constexpr bool operator>(expected const &x, - unexpected_type const &u) { - return (u < x); - } + template + constexpr bool operator>(expected const &x, unexpected_type const &u) + { + return (u < x); + } - template - constexpr bool operator>(unexpected_type const &u, - expected const &x) { - return (x < u); - } + template + constexpr bool operator>(unexpected_type const &u, expected const &x) + { + return (x < u); + } - template - constexpr bool operator<=(expected const &x, - unexpected_type const &u) { - return !(u < x); - } + template + constexpr bool operator<=(expected const &x, unexpected_type const &u) + { + return !(u < x); + } - template - constexpr bool operator<=(unexpected_type const &u, - expected const &x) { - return !(x < u); - } + template + constexpr bool operator<=(unexpected_type const &u, expected const &x) + { + return !(x < u); + } - template - constexpr bool operator>=(expected const &x, - unexpected_type const &u) { - return !(u > x); - } + template + constexpr bool operator>=(expected const &x, unexpected_type const &u) + { + return !(u > x); + } - template - constexpr bool operator>=(unexpected_type const &u, - expected const &x) { - return !(x > u); - } + template + constexpr bool operator>=(unexpected_type const &u, expected const &x) + { + return !(x > u); + } -#endif // nsel_P0323R +#endif// nsel_P0323R - /// x.x.x Specialized algorithms + /// x.x.x Specialized algorithms - template ::value || - std::is_move_constructible::value) && - std::is_move_constructible::value && - std17::is_swappable::value && - std17::is_swappable::value)> - void swap(expected &x, - expected &y) noexcept(noexcept(x.swap(y))) { - x.swap(y); - } + template::value || std::is_move_constructible::value) + && std::is_move_constructible::value && std17::is_swappable::value + && std17::is_swappable::value)> + void swap(expected &x, expected &y) noexcept(noexcept(x.swap(y))) + { + x.swap(y); + } #if nsel_P0323R <= 3 - template - constexpr auto - make_expected(T &&v) -> expected::type> { - return expected::type>(std::forward(v)); - } - - // expected specialization: - - auto inline make_expected() -> expected { - return expected(in_place); - } - - template - constexpr auto make_expected_from_current_exception() -> expected { - return expected(make_unexpected_from_current_exception()); - } - - template - auto make_expected_from_exception(std::exception_ptr v) -> expected { - return expected(unexpected_type( - std::forward(v))); - } - - template - constexpr auto - make_expected_from_error(E e) -> expected::type> { - return expected::type>(make_unexpected(e)); - } - - template ::type, void>::value)> - /*nsel_constexpr14*/ - auto - make_expected_from_call(F f) -> expected::type> { - try { - return make_expected(f()); - } catch (...) { - return make_unexpected_from_current_exception(); + template + constexpr auto make_expected(T &&v) -> expected::type> + { + return expected::type>(std::forward(v)); } - } - template ::type, void>::value)> - /*nsel_constexpr14*/ - auto make_expected_from_call(F f) -> expected { - try { - f(); - return make_expected(); - } catch (...) { - return make_unexpected_from_current_exception(); + // expected specialization: + + auto inline make_expected() -> expected { return expected(in_place); } + + template + constexpr auto make_expected_from_current_exception() -> expected + { + return expected(make_unexpected_from_current_exception()); } - } -#endif // nsel_P0323R + template + auto make_expected_from_exception(std::exception_ptr v) -> expected + { + return expected(unexpected_type(std::forward(v))); + } - } // namespace expected_lite + template + constexpr auto make_expected_from_error(E e) -> expected::type> + { + return expected::type>(make_unexpected(e)); + } - using namespace expected_lite; + template::type, void>::value)> + /*nsel_constexpr14*/ + auto make_expected_from_call(F f) -> expected::type> + { + try { + return make_expected(f()); + } catch (...) { + return make_unexpected_from_current_exception(); + } + } - // using expected_lite::expected; - // using ... + template::type, void>::value)> + /*nsel_constexpr14*/ + auto make_expected_from_call(F f) -> expected + { + try { + f(); + return make_expected(); + } catch (...) { + return make_unexpected_from_current_exception(); + } + } -} // namespace nonstd +#endif// nsel_P0323R + + }// namespace expected_lite + + using namespace expected_lite; + + // using expected_lite::expected; + // using ... + +}// namespace nonstd namespace std { // expected: hash support -template struct hash> { - using result_type = std::size_t; - using argument_type = nonstd::expected; +template +struct hash> { + using result_type = std::size_t; + using argument_type = nonstd::expected; - constexpr result_type operator()(argument_type const &arg) const { - return arg ? std::hash{}(*arg) : result_type{}; - } + constexpr result_type operator()(argument_type const &arg) const + { + return arg ? std::hash{}(*arg) : result_type{}; + } }; // TBD - ?? remove? see spec. -template struct hash> { - using result_type = std::size_t; - using argument_type = nonstd::expected; +template +struct hash> { + using result_type = std::size_t; + using argument_type = nonstd::expected; - constexpr result_type operator()(argument_type const &arg) const { - return arg ? std::hash{}(*arg) : result_type{}; - } + constexpr result_type operator()(argument_type const &arg) const + { + return arg ? std::hash{}(*arg) : result_type{}; + } }; // TBD - implement @@ -3039,19 +2868,21 @@ template struct hash> { // otherwise it evaluates to an unspecified value if E is exception_ptr or // a combination of hashing false and hash()(e.error()). -template struct hash> {}; +template +struct hash> {}; -} // namespace std +}// namespace std namespace nonstd { // void unexpected() is deprecated && removed in C++17 #if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141 -template using unexpected = unexpected_type; +template +using unexpected = unexpected_type; #endif -} // namespace nonstd +}// namespace nonstd #undef nsel_REQUIRES #undef nsel_REQUIRES_0 @@ -3059,9 +2890,9 @@ template using unexpected = unexpected_type; nsel_RESTORE_WARNINGS() -#endif // nsel_USES_STD_EXPECTED +#endif// nsel_USES_STD_EXPECTED namespace std { using namespace nonstd; } -#endif // NONSTD_EXPECTED_LITE_HPP +#endif// NONSTD_EXPECTED_LITE_HPP diff --git a/tile/base/exposed_var.cc b/tile/base/exposed_var.cc index cd032e2..a33bc7f 100644 --- a/tile/base/exposed_var.cc +++ b/tile/base/exposed_var.cc @@ -8,293 +8,294 @@ namespace tile { // namespace exposed_var { namespace { -#define CHECK_PATH(path) \ - TILE_CHECK((path).size() <= 1 || (path).back() != '/', \ - "Invalid path: [{}].", path); \ - TILE_CHECK((path).find("//") == tile::Slice::npos, "Invalid path: [{}].", \ - path); +#define CHECK_PATH(path) \ + TILE_CHECK((path).size() <= 1 || (path).back() != '/', "Invalid path: [{}].", path); \ + TILE_CHECK((path).find("//") == tile::Slice::npos, "Invalid path: [{}].", path); -#define CHECK_RELATIVE_PATH(path) \ - CHECK_PATH(path); \ - TILE_CHECK((path).empty() || (path).front() != '/', "Invalid path: [{}].", \ - path); +#define CHECK_RELATIVE_PATH(path) \ + CHECK_PATH(path); \ + TILE_CHECK((path).empty() || (path).front() != '/', "Invalid path: [{}].", path); -#define CHECK_ABSOLUTE_PATH(path) \ - CHECK_PATH(path); \ - TILE_CHECK((path).empty() || (path).front() == '/', "Invalid path: [{}]", \ - path); +#define CHECK_ABSOLUTE_PATH(path) \ + CHECK_PATH(path); \ + TILE_CHECK((path).empty() || (path).front() == '/', "Invalid path: [{}]", path); + +std::pair +SplitFirstPart(Slice path) +{ + auto pos = path.find_first_of('/'); + if (pos == tile::Slice::npos) { + return std::make_pair(path, ""); + } else { + return std::make_pair(path.substr(0, pos), path.substr(pos + 1)); + } +} + +std::pair +SplitLastPart(Slice path) +{ + auto pos = path.find_last_of('/'); + if (pos == tile::Slice::npos) { return std::make_pair("", path); } -std::pair SplitFirstPart(Slice path) { - auto pos = path.find_first_of('/'); - if (pos == tile::Slice::npos) { - return std::make_pair(path, ""); - } else { return std::make_pair(path.substr(0, pos), path.substr(pos + 1)); - } } -std::pair SplitLastPart(Slice path) { - auto pos = path.find_last_of('/'); - if (pos == tile::Slice::npos) { - return std::make_pair("", path); - } +std::string +JoinPath(Slice a, Slice b) +{ + if (EndsWith(b, "/")) { b.RemoveSuffix(1); } - return std::make_pair(path.substr(0, pos), path.substr(pos + 1)); + if (EndsWith(a, "/")) { a.RemoveSuffix(1); } + + if (b.empty()) { return a.ToString(); } + if (a.empty()) { return b.ToString(); } + + return a.ToString() + "/" + b.ToString(); } -std::string JoinPath(Slice a, Slice b) { - if (EndsWith(b, "/")) { - b.RemoveSuffix(1); - } - - if (EndsWith(a, "/")) { - a.RemoveSuffix(1); - } - - if (b.empty()) { - return a.ToString(); - } - if (a.empty()) { - return b.ToString(); - } - - return a.ToString() + "/" + b.ToString(); +std::string +SubstituteEscapedSlashForZero(Slice path) +{ + TILE_CHECK(path.find('\0') == tile::Slice::npos); + return Replace(path, "\\/", '\0'); } -std::string SubstituteEscapedSlashForZero(Slice path) { - TILE_CHECK(path.find('\0') == tile::Slice::npos); - return Replace(path, "\\/", '\0'); +std::string +SubstituteZeroForEscapedSlash(Slice path) +{ + return Replace(path, '\0', "\\/"); } -std::string SubstituteZeroForEscapedSlash(Slice path) { - return Replace(path, '\0', "\\/"); +std::string +UnescapeZeroToPlainSlash(Slice path) +{ + return Replace(path, '\0', '/'); } -std::string UnescapeZeroToPlainSlash(Slice path) { - return Replace(path, '\0', '/'); -} - -} // namespace +}// namespace // } // namespace exposed_var ExposedVarGroup::Handle -ExposedVarGroup::Add(Slice rel_path, std::function value) { +ExposedVarGroup::Add(Slice rel_path, std::function value) +{ - auto real_path = SubstituteEscapedSlashForZero(rel_path); - CHECK_RELATIVE_PATH(rel_path); + auto real_path = SubstituteEscapedSlashForZero(rel_path); + CHECK_RELATIVE_PATH(rel_path); - auto path_and_name = SplitLastPart(real_path); - auto name = path_and_name.second.ToString(); + auto path_and_name = SplitLastPart(real_path); + auto name = path_and_name.second.ToString(); - auto moved_func = tile::MakeMoveOnCopy(value); - return CreateUpto(path_and_name.first) - ->AddDirect( - name, - [name, moved_func](Slice expected) -> std::optional { + auto moved_func = tile::MakeMoveOnCopy(value); + return CreateUpto(path_and_name.first) + ->AddDirect(name, [name, moved_func](Slice expected) -> std::optional { auto jsv = moved_func.Ref()(); - if (expected.empty()) { - return jsv; - } + if (expected.empty()) { return jsv; } - auto real_path = SubstituteEscapedSlashForZero(expected); + auto real_path = SubstituteEscapedSlashForZero(expected); Json::Value *ptr = &jsv; - auto pieces = Split(real_path, '/'); + auto pieces = Split(real_path, '/'); for (auto &&e : pieces) { - auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()}); - if (ptr->isObject()) { - if (ptr->isMember(unescaped)) { - ptr = &(*ptr)[unescaped]; + auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()}); + if (ptr->isObject()) { + if (ptr->isMember(unescaped)) { + ptr = &(*ptr)[unescaped]; + } else { + return {}; + } + } else if (ptr->isArray()) { + auto index = TryParse(unescaped); + if (index && *index < ptr->size()) { + ptr = &(*ptr)[static_cast(*index)]; + } else { + return {}; + } } else { - return {}; + return {}; } - } else if (ptr->isArray()) { - auto index = TryParse(unescaped); - if (index && *index < ptr->size()) { - ptr = &(*ptr)[static_cast(*index)]; - } else { - return {}; - } - } else { - return {}; - } } return *ptr; - }); + }); } ExposedVarGroup::Handle -ExposedVarGroup::Add(Slice rel_path, ExposedVarDynamicTree *dynamic_tree) { - auto real_path = SubstituteEscapedSlashForZero(rel_path); - CHECK_RELATIVE_PATH(real_path); - auto path_and_name = SplitLastPart(real_path); - auto name = path_and_name.second.ToString(); +ExposedVarGroup::Add(Slice rel_path, ExposedVarDynamicTree *dynamic_tree) +{ + auto real_path = SubstituteEscapedSlashForZero(rel_path); + CHECK_RELATIVE_PATH(real_path); + auto path_and_name = SplitLastPart(real_path); + auto name = path_and_name.second.ToString(); - return CreateUpto(path_and_name.first) - ->AddDirect(name, [dynamic_tree](Slice inner_path) { + return CreateUpto(path_and_name.first)->AddDirect(name, [dynamic_tree](Slice inner_path) { return dynamic_tree->TryGet(inner_path); - }); + }); } -ExposedVarGroup *ExposedVarGroup::FindOrCreate(Slice abs_path) { - auto real_path = SubstituteEscapedSlashForZero(abs_path); - CHECK_ABSOLUTE_PATH(real_path); +ExposedVarGroup * +ExposedVarGroup::FindOrCreate(Slice abs_path) +{ + auto real_path = SubstituteEscapedSlashForZero(abs_path); + CHECK_ABSOLUTE_PATH(real_path); - return Root()->CreateUpto(real_path.substr(1)); + return Root()->CreateUpto(real_path.substr(1)); } -std::optional ExposedVarGroup::TryGet(Slice abs_path) { - auto real_path = SubstituteEscapedSlashForZero(abs_path); - TILE_CHECK(!real_path.empty()); - CHECK_ABSOLUTE_PATH(real_path); - if (real_path == "/") { - return Root()->Dump(); - } +std::optional +ExposedVarGroup::TryGet(Slice abs_path) +{ + auto real_path = SubstituteEscapedSlashForZero(abs_path); + TILE_CHECK(!real_path.empty()); + CHECK_ABSOLUTE_PATH(real_path); - Slice left_path; - auto rel_path = real_path.substr(1); - auto parent = Root()->FindLowest(rel_path, &left_path); + if (real_path == "/") { return Root()->Dump(); } - auto name_and_rest = SplitFirstPart(left_path); - if (name_and_rest.first.empty()) { - return parent->Dump(); - } + Slice left_path; + auto rel_path = real_path.substr(1); + auto parent = Root()->FindLowest(rel_path, &left_path); - auto s = name_and_rest.first.ToString(); - std::lock_guard lk(parent->lock_); - auto iter = parent->leaves_.find(s); - if (iter != parent->leaves_.end()) { - return iter->second(SubstituteZeroForEscapedSlash(name_and_rest.second)); - } else { - auto iter = parent->nodes_.find(s); - if (iter != parent->nodes_.end()) { - return iter->second->Dump(); + auto name_and_rest = SplitFirstPart(left_path); + if (name_and_rest.first.empty()) { return parent->Dump(); } + + auto s = name_and_rest.first.ToString(); + std::lock_guard lk(parent->lock_); + auto iter = parent->leaves_.find(s); + if (iter != parent->leaves_.end()) { + return iter->second(SubstituteZeroForEscapedSlash(name_and_rest.second)); } else { - return {}; + auto iter = parent->nodes_.find(s); + if (iter != parent->nodes_.end()) { + return iter->second->Dump(); + } else { + return {}; + } } - } } -ExposedVarGroup::ExposedVarGroup(std::string abs_path) - : abs_path_(std::move(abs_path)) { - CHECK_ABSOLUTE_PATH(abs_path); +ExposedVarGroup::ExposedVarGroup(std::string abs_path) : abs_path_(std::move(abs_path)) +{ + CHECK_ABSOLUTE_PATH(abs_path); } -ExposedVarGroup *ExposedVarGroup::Root() { - static ExposedVarGroup evg("/"); - return &evg; +ExposedVarGroup * +ExposedVarGroup::Root() +{ + static ExposedVarGroup evg("/"); + return &evg; } -const std::string &ExposedVarGroup::AbsolutePath() const { return abs_path_; } -ExposedVarGroup *ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) { - CHECK_RELATIVE_PATH(rel_path); - auto current = this; +const std::string & +ExposedVarGroup::AbsolutePath() const +{ + return abs_path_; +} - while (!rel_path.empty()) { - auto name_and_rest = SplitFirstPart(rel_path); +ExposedVarGroup * +ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) +{ + CHECK_RELATIVE_PATH(rel_path); + auto current = this; - std::lock_guard lk(current->lock_); - auto iter = current->nodes_.find(name_and_rest.first.ToString()); - if (iter == current->nodes_.end()) { - break; - } else { - current = &*iter->second; + while (!rel_path.empty()) { + auto name_and_rest = SplitFirstPart(rel_path); + + std::lock_guard lk(current->lock_); + auto iter = current->nodes_.find(name_and_rest.first.ToString()); + if (iter == current->nodes_.end()) { + break; + } else { + current = &*iter->second; + } + + rel_path = name_and_rest.second; } - rel_path = name_and_rest.second; - } - - if (left) { - *left = rel_path; - } - return current; + if (left) { *left = rel_path; } + return current; } -ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) { - CHECK_RELATIVE_PATH(rel_path); +ExposedVarGroup * +ExposedVarGroup::CreateUpto(Slice rel_path) +{ + CHECK_RELATIVE_PATH(rel_path); - Slice left_path; - auto current = FindLowest(rel_path, &left_path); + Slice left_path; + auto current = FindLowest(rel_path, &left_path); - auto pieces = Split(left_path, '/'); + auto pieces = Split(left_path, '/'); - for (auto &&e : pieces) { - auto s = e.ToString(); + for (auto &&e : pieces) { + auto s = e.ToString(); - std::lock_guard lk(current->lock_); - auto p = JoinPath(current->AbsolutePath(), s); + std::lock_guard lk(current->lock_); + auto p = JoinPath(current->AbsolutePath(), s); - TILE_CHECK( - current->leaves_.find(s) == current->leaves_.end(), - "Path [{}] has already been used: A value is registered at [{}].", - rel_path, p); + TILE_CHECK(current->leaves_.find(s) == current->leaves_.end(), + "Path [{}] has already been used: A value is registered at [{}].", rel_path, p); - TILE_CHECK(current->nodes_.find(s) == current->nodes_.end()); - auto evg = std::unique_ptr(new ExposedVarGroup(p)); - current->nodes_[s] = std::move(evg); - current = &*(current->nodes_[s]); - } + TILE_CHECK(current->nodes_.find(s) == current->nodes_.end()); + auto evg = std::unique_ptr(new ExposedVarGroup(p)); + current->nodes_[s] = std::move(evg); + current = &*(current->nodes_[s]); + } - TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]", - current->AbsolutePath(), rel_path); - return current; + TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]", current->AbsolutePath(), rel_path); + return current; } -ExposedVarGroup::Handle ExposedVarGroup::AddDirect(Slice name, Getter value) { - auto s = name.ToString(); - std::lock_guard lk(lock_); - - TILE_CHECK(leaves_.find(s) == leaves_.end(), - "Value [{}] has already been registered at [{}].", name, - AbsolutePath()); - TILE_CHECK(nodes_.find(s) == nodes_.end(), "Path [{}] has already been used.", - JoinPath(AbsolutePath(), name)); - leaves_[s] = std::move(value); - - return Deferred([this, s] { +ExposedVarGroup::Handle +ExposedVarGroup::AddDirect(Slice name, Getter value) +{ + auto s = name.ToString(); std::lock_guard lk(lock_); - TILE_CHECK_EQ(leaves_.erase(s), 1); - }); + + TILE_CHECK(leaves_.find(s) == leaves_.end(), "Value [{}] has already been registered at [{}].", name, + AbsolutePath()); + TILE_CHECK(nodes_.find(s) == nodes_.end(), "Path [{}] has already been used.", JoinPath(AbsolutePath(), name)); + leaves_[s] = std::move(value); + + return Deferred([this, s] { + std::lock_guard lk(lock_); + TILE_CHECK_EQ(leaves_.erase(s), 1); + }); } -Json::Value ExposedVarGroup::Dump() const { - std::lock_guard lk(lock_); - Json::Value jsv; - for (auto &&node : nodes_) { - jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump(); - } +Json::Value +ExposedVarGroup::Dump() const +{ + std::lock_guard lk(lock_); + Json::Value jsv; + for (auto &&node : nodes_) { jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump(); } - for (auto &&leave : leaves_) { - jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second(""); - } + for (auto &&leave : leaves_) { jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second(""); } - return jsv; + return jsv; } -ExposedVarDynamicTree::ExposedVarDynamicTree( - Slice rel_path, std::function getter, - ExposedVarGroup *parent) - : getter_(std::move(getter)) { - handle_ = parent->Add(rel_path, this); +ExposedVarDynamicTree::ExposedVarDynamicTree(Slice rel_path, + std::function getter, + ExposedVarGroup *parent) + : getter_(std::move(getter)) +{ + handle_ = parent->Add(rel_path, this); } -std::optional ExposedVarDynamicTree::TryGet(Slice rel_path) const { - auto real_path = SubstituteEscapedSlashForZero(rel_path); - auto jsv = getter_(); - Json::Value *ptr = &jsv; +std::optional +ExposedVarDynamicTree::TryGet(Slice rel_path) const +{ + auto real_path = SubstituteEscapedSlashForZero(rel_path); + auto jsv = getter_(); + Json::Value *ptr = &jsv; - auto pieces = Split(real_path, '/'); - for (auto &&e : pieces) { - auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()}); - ptr = &(*ptr)[unescaped]; - } + auto pieces = Split(real_path, '/'); + for (auto &&e : pieces) { + auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()}); + ptr = &(*ptr)[unescaped]; + } - if (ptr->isNull()) { - return {}; - } + if (ptr->isNull()) { return {}; } - return *ptr; + return *ptr; } -} // namespace tile +}// namespace tile diff --git a/tile/base/exposed_var.h b/tile/base/exposed_var.h index 70d9267..4621b4f 100644 --- a/tile/base/exposed_var.h +++ b/tile/base/exposed_var.h @@ -15,180 +15,189 @@ namespace tile { namespace exposed_var { -template -auto ToJsonValue(const T &t) - -> enable_if_t::value, Json::Value> { - return t; +template +auto +ToJsonValue(const T &t) -> enable_if_t::value, Json::Value> +{ + return t; } -template -auto ToJsonValue(const T &t) - -> enable_if_t::value && std::is_unsigned::value, - Json::Value> { - return Json::Value(static_cast(t)); +template +auto +ToJsonValue(const T &t) -> enable_if_t::value && std::is_unsigned::value, Json::Value> +{ + return Json::Value(static_cast(t)); } -template -auto ToJsonValue(const T &t) - -> enable_if_t::value && !std::is_unsigned::value, - Json::Value> { - return Json::Value(static_cast(t)); +template +auto +ToJsonValue(const T &t) -> enable_if_t::value && !std::is_unsigned::value, Json::Value> +{ + return Json::Value(static_cast(t)); } -template -auto ToJsonValue(const T &t) - -> enable_if_t::value || - std::is_same::value || - std::is_same::value || - std::is_same::value, - Json::Value> { - return Json::Value(t); +template +auto +ToJsonValue(const T &t) -> enable_if_t::value || std::is_same::value + || std::is_same::value || std::is_same::value, + Json::Value> +{ + return Json::Value(t); } -template -auto ToJsonValue(const T &t) - -> enable_if_t< - !std::is_integral::value && !std::is_floating_point::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - std::is_same()))>::value, - Json::Value> { - return Json::Value(format_as(t)); +template +auto +ToJsonValue(const T &t) -> enable_if_t::value && !std::is_floating_point::value + && !std::is_same::value + && !std::is_same::value && !std::is_same::value + && std::is_same()))>::value, + Json::Value> +{ + return Json::Value(format_as(t)); } -template Json::Value ToJsonValue(const std::atomic &v) { - return ToJsonValue(v.load(std::memory_order_relaxed)); +template +Json::Value +ToJsonValue(const std::atomic &v) +{ + return ToJsonValue(v.load(std::memory_order_relaxed)); } -} // namespace exposed_var +}// namespace exposed_var class ExposedVarDynamicTree; class ExposedVarGroup { public: - using Handle = Deferred; + using Handle = Deferred; - Handle Add(Slice rel_path, std::function value); - Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree); - static ExposedVarGroup *FindOrCreate(Slice abs_path); - static std::optional TryGet(Slice abs_path); + Handle Add(Slice rel_path, std::function value); + Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree); + static ExposedVarGroup *FindOrCreate(Slice abs_path); + static std::optional TryGet(Slice abs_path); private: - using Getter = std::function(Slice)>; + using Getter = std::function(Slice)>; - explicit ExposedVarGroup(std::string abs_path); - const std::string &AbsolutePath() const; + explicit ExposedVarGroup(std::string abs_path); + const std::string &AbsolutePath() const; - ExposedVarGroup *FindLowest(Slice rel_path, Slice *left); - ExposedVarGroup *CreateUpto(Slice rel_path); - Handle AddDirect(Slice name, Getter getter); - Json::Value Dump() const; - static ExposedVarGroup *Root(); + ExposedVarGroup *FindLowest(Slice rel_path, Slice *left); + ExposedVarGroup *CreateUpto(Slice rel_path); + Handle AddDirect(Slice name, Getter getter); + Json::Value Dump() const; + static ExposedVarGroup *Root(); private: - std::string abs_path_; - mutable std::mutex lock_; + std::string abs_path_; + mutable std::mutex lock_; - std::unordered_map> nodes_; - std::unordered_map leaves_; + std::unordered_map> nodes_; + std::unordered_map leaves_; }; -template class ExposedVar { +template +class ExposedVar { public: - ExposedVar(Slice rel_path) { - LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); - } + ExposedVar(Slice rel_path) { LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); } - template - ExposedVar(Slice rel_path, U &&initial_value, - ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/")) - : obj_(std::forward(initial_value)) { - LinkToParent(rel_path, parent); - } + template + ExposedVar(Slice rel_path, U &&initial_value, ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/")) + : obj_(std::forward(initial_value)) + { + LinkToParent(rel_path, parent); + } - T *operator->() noexcept { return &obj_; } - T &operator*() noexcept { return obj_; } + T *operator->() noexcept { return &obj_; } + + T &operator*() noexcept { return obj_; } private: - void LinkToParent(Slice rel_path, ExposedVarGroup *parent) { - handle_ = parent->Add(rel_path, - [this] { return exposed_var::ToJsonValue(obj_); }); - } + void LinkToParent(Slice rel_path, ExposedVarGroup *parent) + { + handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(obj_); }); + } private: - T obj_{}; - ExposedVarGroup::Handle handle_; + T obj_{}; + ExposedVarGroup::Handle handle_; }; -template class ExposedVarDynamic { +template +class ExposedVarDynamic { public: - ExposedVarDynamic( - Slice rel_path, std::function getter, - ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/")) - : getter_(std::move(getter)) { - handle_ = parent->Add( - rel_path, [this] { return exposed_var::ToJsonValue(getter_()); }); - } + ExposedVarDynamic(Slice rel_path, + std::function getter, + ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/")) + : getter_(std::move(getter)) + { + handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(getter_()); }); + } private: - std::function getter_; - ExposedVarGroup::Handle handle_; + std::function getter_; + ExposedVarGroup::Handle handle_; }; class ExposedVarDynamicTree { public: - ExposedVarDynamicTree( - Slice rel_path, std::function getter, - ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/")); + ExposedVarDynamicTree(Slice rel_path, + std::function getter, + ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/")); - std::optional TryGet(Slice rel_path) const; + std::optional TryGet(Slice rel_path) const; private: - std::function getter_; - ExposedVarGroup::Handle handle_; + std::function getter_; + ExposedVarGroup::Handle handle_; }; namespace detail { -template struct IdentityTime { - std::uint64_t operator()(const T &val) const { - return (ReadSteadyClock() - std::chrono::steady_clock::time_point()) / - std::chrono::nanoseconds(1); - } +template +struct IdentityTime { + std::uint64_t operator()(const T &val) const + { + return (ReadSteadyClock() - std::chrono::steady_clock::time_point()) / std::chrono::nanoseconds(1); + } }; -} // namespace detail +}// namespace detail -template > +template> class ExposedMetrics { public: - explicit ExposedMetrics(Slice rel_path) { - LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); - } + explicit ExposedMetrics(Slice rel_path) { LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); } private: - Json::Value ToJsonValue(const T &v) { - Json::Value result; - std::unordered_map m = {{"1s", 1}}; - for (auto &&item : m) { + Json::Value ToJsonValue(const T &v) + { + Json::Value result; + std::unordered_map m = { + {"1s", 1} + }; + for (auto &&item : m) {} + return result; } - return result; - } - void LinkToParent(Slice rel_path, ExposedVarGroup *parent) { - handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); }); - } + void LinkToParent(Slice rel_path, ExposedVarGroup *parent) + { + handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); }); + } private: - T obj_{}; - ExposedVarGroup::Handle handle_; + T obj_{}; + ExposedVarGroup::Handle handle_; }; -template using ExposedCounter = ExposedVar; -template using ExposedGauge = ExposedVar; -template using ExposedMiner = ExposedVar; -template using ExposedMaxer = ExposedVar; -template using ExposedAverager = ExposedVar; +template +using ExposedCounter = ExposedVar; +template +using ExposedGauge = ExposedVar; +template +using ExposedMiner = ExposedVar; +template +using ExposedMaxer = ExposedVar; +template +using ExposedAverager = ExposedVar; -} // namespace tile +}// namespace tile -#endif // TILE_BASE_EXPOSED_VAR_H +#endif// TILE_BASE_EXPOSED_VAR_H diff --git a/tile/base/exposed_var_test.cc b/tile/base/exposed_var_test.cc index 2c078c7..d1bc6c4 100644 --- a/tile/base/exposed_var_test.cc +++ b/tile/base/exposed_var_test.cc @@ -4,14 +4,19 @@ #include "json/json.h" namespace tile { -ExposedVarGroup *GetFancyGroup() { - return ExposedVarGroup::FindOrCreate("/a/b"); +ExposedVarGroup * +GetFancyGroup() +{ + return ExposedVarGroup::FindOrCreate("/a/b"); } -Json::Value GetTree() { - Json::Value jsv; - jsv["dir"]["sub-dir"]["key"] = 5; - jsv["key"] = "6"; - return jsv; + +Json::Value +GetTree() +{ + Json::Value jsv; + jsv["dir"]["sub-dir"]["key"] = 5; + jsv["key"] = "6"; + return jsv; } ExposedVar v1("v1", 5); @@ -21,18 +26,18 @@ ExposedVar f1("f1", 6.2, ExposedVarGroup::FindOrCreate("/x/y/z")); auto GreatGroup = ExposedVarGroup::FindOrCreate("/a/b"); // `/a/b/ds1` -ExposedVarDynamic - ds1("ds1", [] { return "test_str"; }, GetFancyGroup()); +ExposedVarDynamic ds1("ds1", [] { return "test_str"; }, GetFancyGroup()); -TEST(ExposedVar, Mutate) { - auto opt = ExposedVarGroup::TryGet("/"); - ASSERT_TRUE(opt); - auto &&jsv = *opt; - ASSERT_EQ(5, jsv["v1"].asInt()); - *v1 = 6; - jsv = *ExposedVarGroup::TryGet("/"); - ASSERT_EQ(6, jsv["v1"].asInt()); - *v1 = 5; +TEST(ExposedVar, Mutate) +{ + auto opt = ExposedVarGroup::TryGet("/"); + ASSERT_TRUE(opt); + auto &&jsv = *opt; + ASSERT_EQ(5, jsv["v1"].asInt()); + *v1 = 6; + jsv = *ExposedVarGroup::TryGet("/"); + ASSERT_EQ(6, jsv["v1"].asInt()); + *v1 = 5; } -} // namespace tile +}// namespace tile diff --git a/tile/base/future.h b/tile/base/future.h index 605ef06..a48cb99 100644 --- a/tile/base/future.h +++ b/tile/base/future.h @@ -22,6 +22,6 @@ using future::Split; using future::WhenAll; using future::WhenAny; -} // namespace tile +}// namespace tile -#endif // TILE_BASE_FUTURE_H +#endif// TILE_BASE_FUTURE_H diff --git a/tile/base/future/basic.h b/tile/base/future/basic.h index a07c501..1880930 100644 --- a/tile/base/future/basic.h +++ b/tile/base/future/basic.h @@ -10,54 +10,63 @@ namespace tile { namespace future { -template -struct are_rvalue_refs - : internal::conjunction...> {}; +template +struct are_rvalue_refs : internal::conjunction...> {}; + static_assert(are_rvalue_refs::value, ""); static_assert(!are_rvalue_refs::value, ""); // Rebinds `Ts...` in `TT<....>` to `UT<...>` // Example: // rebind_t, Promise<>> -> Promise -template class To> struct rebind; -template