Compare commits
27 Commits
b8cfbb3c72
...
88dfe5d323
Author | SHA1 | Date | |
---|---|---|---|
88dfe5d323 | |||
|
c4fd274e66 | ||
|
f5d3fcdf63 | ||
|
7c4395e897 | ||
|
6f68f497f4 | ||
|
471fcca5b7 | ||
|
791125454a | ||
|
dc02ceb9b0 | ||
|
80eb00711b | ||
|
64b25815a2 | ||
|
729ee8b86c | ||
|
eda5a20286 | ||
|
748262d48b | ||
|
f03d0bd931 | ||
|
25998dde64 | ||
|
fa04f9c23a | ||
|
635db57230 | ||
|
30576240a2 | ||
|
481015e1c6 | ||
|
64200d42cc | ||
|
dc2bb3567b | ||
|
0ff288d0bc | ||
|
a09f8cc68b | ||
|
28c22939ce | ||
|
5a02a4c750 | ||
|
04d41aa566 | ||
|
34f0461a68 |
78
.clang-format
Normal file
78
.clang-format
Normal file
@ -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
|
@ -36,6 +36,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y cmake make
|
||||||
|
|
||||||
- name: armeabi-v7a
|
- name: armeabi-v7a
|
||||||
run: |
|
run: |
|
||||||
|
@ -41,7 +41,7 @@ jobs:
|
|||||||
- name: install-tools
|
- name: install-tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
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
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
@ -36,10 +36,10 @@ jobs:
|
|||||||
build_type: ["Debug", "Release"]
|
build_type: ["Debug", "Release"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# - name: install-tools
|
- name: install-tools
|
||||||
# run: |
|
run: |
|
||||||
# sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
# sudo apt-get install -y g++-arm-linux-gnueabi qemu-user-binfmt
|
sudo apt-get install -y cmake g++-arm-linux-gnueabi qemu-user-binfmt
|
||||||
- name: configure
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@ -51,8 +51,8 @@ jobs:
|
|||||||
- name: test
|
- name: test
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
#sudo ln -sf /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib/ld-linux.so.3
|
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
|
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabi/lib
|
||||||
ctest --output-on-failure -j $(nproc)
|
ctest --output-on-failure -j $(nproc)
|
||||||
|
|
||||||
linux-gcc-armhf:
|
linux-gcc-armhf:
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
|||||||
- name: install-tools
|
- name: install-tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
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
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
@ -39,7 +39,7 @@ jobs:
|
|||||||
- name: install-tools
|
- name: install-tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
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
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
@ -40,7 +40,7 @@ jobs:
|
|||||||
- name: install-tools
|
- name: install-tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
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
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
@ -32,7 +32,7 @@ jobs:
|
|||||||
- name: install-tools
|
- name: install-tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y cmake make
|
sudo apt-get install -y cmake make clang-tools
|
||||||
- name: configure
|
- name: configure
|
||||||
env:
|
env:
|
||||||
CC: clang
|
CC: clang
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
|||||||
- name: install-tools
|
- name: install-tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
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
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -19,11 +19,14 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
|||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
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_TESTS "Build tests" OFF)
|
||||||
option(TILE_BUILD_BENCHMARKS "Build tests" OFF)
|
option(TILE_BUILD_BENCHMARKS "Build tests" OFF)
|
||||||
option(TILE_WITH_OPENSSL "Build with openssl" OFF)
|
option(TILE_WITH_OPENSSL "Build with openssl" OFF)
|
||||||
option(TILE_BUILD_SHARED "Build shared library" ON)
|
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)
|
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
set(TILE_BUILD_TESTS ON)
|
set(TILE_BUILD_TESTS ON)
|
||||||
@ -34,34 +37,34 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE "Release")
|
set(CMAKE_BUILD_TYPE "Release")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
# if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
|
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
|
||||||
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
|
# # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS
|
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS
|
||||||
# "${CMAKE_CXX_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}
|
# # "${CMAKE_CXX_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}
|
||||||
# -fsanitize=address ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address
|
# # -fsanitize=address ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address
|
||||||
# ")
|
# # ")
|
||||||
|
#
|
||||||
set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load")
|
# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load")
|
||||||
# set(NO_WHOLE_ARCHIVE_PREFIX "")
|
# # set(NO_WHOLE_ARCHIVE_PREFIX "")
|
||||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
|
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
|
||||||
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
|
# # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS
|
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS
|
||||||
# "${CMAKE_CXX_FLAGS} -static")
|
# # "${CMAKE_CXX_FLAGS} -static")
|
||||||
|
#
|
||||||
set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,")
|
# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,")
|
||||||
# set(NO_WHOLE_ARCHIVE_PREFIX "")
|
# # set(NO_WHOLE_ARCHIVE_PREFIX "")
|
||||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
|
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
|
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
|
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
|
||||||
# set(CMAKE_C_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(STATIC_BINARY_FLAGS "-static-libgcc -static-libstdc++")
|
||||||
set(WHOLE_ARCHIVE_PREFIX "-Wl,--whole-archive")
|
# set(WHOLE_ARCHIVE_PREFIX "-Wl,--whole-archive")
|
||||||
set(WHOLE_ARCHIVE_SUFFIX "-Wl,--no-whole-archive")
|
# set(WHOLE_ARCHIVE_SUFFIX "-Wl,--no-whole-archive")
|
||||||
endif()
|
# endif()
|
||||||
|
|
||||||
# extern int getifaddrs(struct ifaddrs **ifap); extern void freeifaddrs(struct
|
# extern int getifaddrs(struct ifaddrs **ifap); extern void freeifaddrs(struct
|
||||||
# ifaddrs *ifa);
|
# ifaddrs *ifa);
|
||||||
@ -71,11 +74,11 @@ include(cmake/BuildInfo.cmake)
|
|||||||
check_symbol_exists("getifaddrs" "ifaddrs.h" TILE_HAVE_GETIFADDRS)
|
check_symbol_exists("getifaddrs" "ifaddrs.h" TILE_HAVE_GETIFADDRS)
|
||||||
check_symbol_exists("freeifaddrs" "ifaddrs.h" TILE_HAVE_FREEIFADDRS)
|
check_symbol_exists("freeifaddrs" "ifaddrs.h" TILE_HAVE_FREEIFADDRS)
|
||||||
|
|
||||||
find_package(Git REQUIRED)
|
# find_package(Git REQUIRED)
|
||||||
|
#
|
||||||
get_git_commit_hash(GIT_COMMIT_HASH)
|
# get_git_commit_hash(GIT_COMMIT_HASH)
|
||||||
get_git_commit_date(GIT_COMMIT_DATE)
|
# get_git_commit_date(GIT_COMMIT_DATE)
|
||||||
get_git_commit_subject(GIT_COMMIT_SUBJECT)
|
# get_git_commit_subject(GIT_COMMIT_SUBJECT)
|
||||||
|
|
||||||
set(THIRD_PARTY_INCLUDE_DIRS
|
set(THIRD_PARTY_INCLUDE_DIRS
|
||||||
# "third_party/json" "third_party/inja"
|
# "third_party/json" "third_party/inja"
|
||||||
@ -211,16 +214,22 @@ set(TILE_SRCS
|
|||||||
"tile/rpc/protocol/http/buffer_io.cc"
|
"tile/rpc/protocol/http/buffer_io.cc"
|
||||||
"tile/rpc/protocol/message.cc"
|
"tile/rpc/protocol/message.cc"
|
||||||
# "tile/rpc/server.cc"
|
# "tile/rpc/server.cc"
|
||||||
"tile/base/config/config.cc"
|
"tile/base/config/configuration.cc"
|
||||||
"tile/base/config/ini_file_config.cc"
|
"tile/base/config/ini_file_configuration.cc"
|
||||||
"tile/base/config/layered_config.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))
|
if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
|
||||||
list(APPEND TILE_SRCS "tile/base/net/detail/android/ifaddrs.c")
|
list(APPEND TILE_SRCS "tile/base/net/detail/android/ifaddrs.c")
|
||||||
endif()
|
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}
|
set_target_properties(tile PROPERTIES VERSION ${PROJECT_VERSION}
|
||||||
target_precompile_headers(tile PUBLIC inja/inja.h)
|
target_precompile_headers(tile PUBLIC inja/inja.h)
|
||||||
target_precompile_headers(tile PUBLIC inja/string_view.h)
|
target_precompile_headers(tile PUBLIC inja/string_view.h)
|
||||||
@ -238,6 +247,7 @@ target_include_directories(
|
|||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
${THIRD_PARTY_INCLUDE_DIRS}
|
${THIRD_PARTY_INCLUDE_DIRS}
|
||||||
RPIVATE
|
RPIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/header_only/"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
|
"${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
|
3
third_party/README.md
vendored
Normal file
3
third_party/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Library
|
||||||
|
|
||||||
|
- [toml11](https://github.com/ToruNiina/toml11/archive/refs/tags/v4.2.0.tar.gz)
|
2
third_party/curl/CMakeLists.txt
vendored
2
third_party/curl/CMakeLists.txt
vendored
@ -107,7 +107,7 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
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"
|
cmake_dependent_option(ENABLE_THREADED_RESOLVER "Set to ON to enable threaded DNS lookup"
|
||||||
ON "NOT ENABLE_ARES"
|
ON "NOT ENABLE_ARES"
|
||||||
|
17241
third_party/header_only/toml.hpp
vendored
Normal file
17241
third_party/header_only/toml.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
// @sa: https://github.com/facebook/folly/blob/master/folly/lang/Align.h
|
||||||
//
|
//
|
||||||
// Update at 20201124: Well, AMD's Zen 3 does the same.
|
// 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;
|
constexpr std::size_t hardware_constructive_interference_size = 64;
|
||||||
|
|
||||||
#elif defined(__powerpc64__)
|
#elif defined(__powerpc64__)
|
||||||
|
|
||||||
// These values are read from
|
// These values are read from
|
||||||
// `/sys/devices/system/cpu/cpu0/cache/index*/coherency_line_size`
|
// `/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;
|
constexpr std::size_t hardware_constructive_interference_size = 128;
|
||||||
|
|
||||||
#elif defined(__ARM_ARCH_5T__)
|
#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;
|
constexpr std::size_t hardware_constructive_interference_size = 32;
|
||||||
|
|
||||||
#elif defined(__arm__)
|
#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
|
// are even implementations with cache line sizes configurable at boot
|
||||||
// time.
|
// 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;
|
constexpr std::size_t hardware_constructive_interference_size = 64;
|
||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
@ -47,13 +47,13 @@ constexpr std::size_t hardware_constructive_interference_size = 64;
|
|||||||
// Let's ignore those CPUs for now.
|
// Let's ignore those CPUs for now.
|
||||||
//
|
//
|
||||||
// @sa: https://www.mono-project.com/news/2016/09/12/arm64-icache/
|
// @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;
|
constexpr std::size_t hardware_constructive_interference_size = 64;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
constexpr std::size_t hardware_constructive_interference_size = max_align_v;
|
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.
|
// #error Unsupported architecture.
|
||||||
// #pragma message("Unsupported architecture, use max_align_v")
|
// #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");
|
"hardware constructive interference size is too small");
|
||||||
static_assert(hardware_destructive_interference_size >= max_align_v,
|
static_assert(hardware_destructive_interference_size >= max_align_v,
|
||||||
"hardware destructive interference size is too small");
|
"hardware destructive interference size is too small");
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_ALIGN_H
|
#endif// _TILE_BASE_ALIGN_H
|
||||||
|
@ -9,205 +9,214 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace {
|
namespace {
|
||||||
template <typename T> class OwningBufferBlock : public PolymorphicBufferBlock {
|
template<typename T>
|
||||||
|
class OwningBufferBlock : public PolymorphicBufferBlock {
|
||||||
public:
|
public:
|
||||||
explicit OwningBufferBlock(T storage) : storage_(std::move(storage)) {}
|
explicit OwningBufferBlock(T storage) : storage_(std::move(storage)) {}
|
||||||
const char *data() const noexcept override {
|
|
||||||
return reinterpret_cast<const char *>(storage_.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t size() const noexcept override { return storage_.size(); }
|
const char *data() const noexcept override { return reinterpret_cast<const char *>(storage_.data()); }
|
||||||
|
|
||||||
|
std::size_t size() const noexcept override { return storage_.size(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T storage_;
|
T storage_;
|
||||||
};
|
};
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
void FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer,
|
void
|
||||||
std::size_t size) {
|
FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t size)
|
||||||
TILE_CHECK_GT(nb.ByteSize(), size, "No enough data");
|
{
|
||||||
std::size_t copied = 0;
|
TILE_CHECK_GT(nb.ByteSize(), size, "No enough data");
|
||||||
for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) {
|
std::size_t copied = 0;
|
||||||
auto len = std::min(size - copied, iter->size());
|
for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) {
|
||||||
memcpy(reinterpret_cast<char *>(buffer) + copied, iter->data(), len);
|
auto len = std::min(size - copied, iter->size());
|
||||||
copied += len;
|
memcpy(reinterpret_cast<char *>(buffer) + copied, iter->data(), len);
|
||||||
}
|
copied += len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other)
|
NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other) : byte_size_(other.byte_size_)
|
||||||
: byte_size_(other.byte_size_) {
|
{
|
||||||
for (auto &&e : other.buffers_) {
|
for (auto &&e : other.buffers_) {
|
||||||
|
|
||||||
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
|
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
|
||||||
*b = e;
|
*b = e;
|
||||||
buffers_.push_back(std::move(b));
|
buffers_.push_back(std::move(b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NoncontiguousBuffer &
|
NoncontiguousBuffer &
|
||||||
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) {
|
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other)
|
||||||
if (TILE_UNLIKELY(&other == this)) {
|
{
|
||||||
|
if (TILE_UNLIKELY(&other == this)) { return *this; }
|
||||||
|
|
||||||
|
Clear();
|
||||||
|
byte_size_ = other.byte_size_;
|
||||||
|
for (auto &&e : other.buffers_) {
|
||||||
|
|
||||||
|
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
|
||||||
|
*b = e;
|
||||||
|
buffers_.push_back(std::move(b));
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
byte_size_ = other.byte_size_;
|
|
||||||
for (auto &&e : other.buffers_) {
|
|
||||||
|
|
||||||
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
|
|
||||||
*b = e;
|
|
||||||
buffers_.push_back(std::move(b));
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept {
|
void
|
||||||
TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size");
|
NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept
|
||||||
byte_size_ -= bytes;
|
{
|
||||||
|
TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size");
|
||||||
|
byte_size_ -= bytes;
|
||||||
|
|
||||||
while (bytes) {
|
while (bytes) {
|
||||||
auto &&first = buffers_.front();
|
auto &&first = buffers_.front();
|
||||||
auto min_size = std::min(bytes, first.size());
|
auto min_size = std::min(bytes, first.size());
|
||||||
if (min_size == first.size()) {
|
if (min_size == first.size()) {
|
||||||
object_pool::Put<buffer_t>(buffers_.pop_front());
|
object_pool::Put<buffer_t>(buffers_.pop_front());
|
||||||
} else {
|
} else {
|
||||||
TILE_DCHECK_LT(min_size, first.size());
|
TILE_DCHECK_LT(min_size, first.size());
|
||||||
first.Skip(min_size);
|
first.Skip(min_size);
|
||||||
|
}
|
||||||
|
bytes -= min_size;
|
||||||
}
|
}
|
||||||
bytes -= min_size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoncontiguousBuffer::ClearSlow() noexcept {
|
void
|
||||||
byte_size_ = 0;
|
NoncontiguousBuffer::ClearSlow() noexcept
|
||||||
while (!buffers_.empty()) {
|
{
|
||||||
object_pool::Put<buffer_t>(buffers_.pop_front());
|
byte_size_ = 0;
|
||||||
}
|
while (!buffers_.empty()) { object_pool::Put<buffer_t>(buffers_.pop_front()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoncontiguousBufferBuilder::InitializeNextBlock() {
|
void
|
||||||
if (current_) {
|
NoncontiguousBufferBuilder::InitializeNextBlock()
|
||||||
TILE_CHECK(SizeAvailable(),
|
{
|
||||||
"You should flush current block by `FlushCurrentBlock()`");
|
if (current_) {
|
||||||
return;
|
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<const char *>(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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::string rc;
|
current_ = MakeNativeBufferBlock();
|
||||||
for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes;
|
used_ = 0;
|
||||||
++iter) {
|
}
|
||||||
auto old_len = rc.size();
|
|
||||||
rc.append(iter->data(), iter->size());
|
void
|
||||||
// find delim
|
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<const char *>(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
|
Slice current(nb.FirstContiguous().data(), nb.FirstContiguous().size());
|
||||||
auto pos = rc.find(delim, old_len - std::min(old_len, delim.size()));
|
auto pos = current.find(delim);
|
||||||
if (pos != Slice::npos) {
|
if (pos != Slice::npos) {
|
||||||
rc.erase(rc.begin() + pos + delim.size(), rc.end());
|
auto expected_bytes = std::min(pos + delim.size(), max_bytes);
|
||||||
break;
|
return std::string(nb.FirstContiguous().data(), nb.FirstContiguous().data() + expected_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (rc.size() > max_bytes) {
|
std::string rc;
|
||||||
rc.erase(rc.begin() + max_bytes, rc.end());
|
for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes; ++iter) {
|
||||||
}
|
auto old_len = rc.size();
|
||||||
return rc;
|
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) {
|
PolymorphicBuffer
|
||||||
return MakeReferencingBuffer(ptr, size, [] {});
|
MakeReferencingBuffer(const void *ptr, std::size_t size)
|
||||||
|
{
|
||||||
|
return MakeReferencingBuffer(ptr, size, [] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
PolymorphicBuffer MakeForeignBuffer(std::string buffer) {
|
PolymorphicBuffer
|
||||||
auto size = buffer.size();
|
MakeForeignBuffer(std::string buffer)
|
||||||
return PolymorphicBuffer(
|
{
|
||||||
MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0,
|
auto size = buffer.size();
|
||||||
size);
|
return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer) {
|
PolymorphicBuffer
|
||||||
auto size = buffer.size() * sizeof(T);
|
MakeForeignBuffer(std::vector<T> buffer)
|
||||||
return PolymorphicBuffer(
|
{
|
||||||
MakeRefCounted<OwningBufferBlock<std::vector<T>>>(std::move(buffer)), 0,
|
auto size = buffer.size() * sizeof(T);
|
||||||
size);
|
return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::vector<T>>>(std::move(buffer)), 0, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(type) \
|
#define TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(type) template PolymorphicBuffer MakeForeignBuffer(std::vector<type> buffer)
|
||||||
template PolymorphicBuffer MakeForeignBuffer(std::vector<type> buffer)
|
|
||||||
|
|
||||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(char);
|
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(char);
|
||||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(signed 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(double);
|
||||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(long double);
|
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(long double);
|
||||||
|
|
||||||
bool StartsWith(NoncontiguousBuffer buffer, Slice prefix) {
|
bool
|
||||||
if (buffer.ByteSize() < prefix.size()) {
|
StartsWith(NoncontiguousBuffer buffer, Slice prefix)
|
||||||
return false;
|
{
|
||||||
}
|
if (buffer.ByteSize() < prefix.size()) { return false; }
|
||||||
|
|
||||||
while (!buffer.Empty() && prefix.empty()) {
|
while (!buffer.Empty() && prefix.empty()) {
|
||||||
auto first = buffer.FirstContiguous();
|
auto first = buffer.FirstContiguous();
|
||||||
auto min_len = std::min(first.size(), prefix.size());
|
auto min_len = std::min(first.size(), prefix.size());
|
||||||
if (memcmp(first.data(), prefix.data(), min_len) != 0) {
|
if (memcmp(first.data(), prefix.data(), min_len) != 0) { return false; }
|
||||||
return false;
|
|
||||||
|
buffer.Skip(min_len);
|
||||||
|
prefix.RemovePrefix(min_len);
|
||||||
}
|
}
|
||||||
|
return prefix.empty();
|
||||||
buffer.Skip(min_len);
|
|
||||||
prefix.RemovePrefix(min_len);
|
|
||||||
}
|
|
||||||
return prefix.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix) {
|
bool
|
||||||
TILE_NOT_IMPLEMENTED("");
|
EndsWith(NoncontiguousBuffer buffer, Slice suffix)
|
||||||
return false;
|
{
|
||||||
}
|
TILE_NOT_IMPLEMENTED("");
|
||||||
|
|
||||||
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) {
|
|
||||||
if (buffer.ByteSize() < prefix.size()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!buffer.Empty() && prefix.empty()) {
|
bool
|
||||||
auto first = buffer.FirstContiguous();
|
StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix)
|
||||||
auto min_len = std::min(first.size(), prefix.size());
|
{
|
||||||
|
if (buffer.ByteSize() < prefix.size()) { return false; }
|
||||||
|
|
||||||
if (!EqualsIgnoreCase(first.substr(0, min_len),
|
while (!buffer.Empty() && prefix.empty()) {
|
||||||
prefix.substr(0, min_len))) {
|
auto first = buffer.FirstContiguous();
|
||||||
return false;
|
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);
|
||||||
}
|
}
|
||||||
|
return prefix.empty();
|
||||||
buffer.Skip(min_len);
|
|
||||||
prefix.RemovePrefix(min_len);
|
|
||||||
}
|
|
||||||
return prefix.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix) {
|
bool
|
||||||
TILE_NOT_IMPLEMENTED("");
|
EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix)
|
||||||
|
{
|
||||||
|
TILE_NOT_IMPLEMENTED("");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -19,355 +19,376 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename T>
|
template<typename T>
|
||||||
constexpr auto data(const T &c)
|
constexpr auto
|
||||||
-> enable_if_t<std::is_convertible<decltype(c.data()), const char *>::value,
|
data(const T &c) -> enable_if_t<std::is_convertible<decltype(c.data()), const char *>::value, const char *>
|
||||||
const char *> {
|
{
|
||||||
return c.data();
|
return c.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N>
|
template<std::size_t N>
|
||||||
constexpr auto data(const char (&c)[N]) -> decltype(c) {
|
constexpr auto
|
||||||
return c;
|
data(const char (&c)[N]) -> decltype(c)
|
||||||
|
{
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> constexpr std::size_t size(const T &c) {
|
template<typename T>
|
||||||
return c.size();
|
constexpr std::size_t
|
||||||
|
size(const T &c)
|
||||||
|
{
|
||||||
|
return c.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N> struct size_impl {
|
template<std::size_t N>
|
||||||
static constexpr std::size_t size(const char (&c)[N]) {
|
struct size_impl {
|
||||||
return N - (c[N - 1] == '\0');
|
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 <std::size_t N> constexpr std::size_t size(const char (&c)[N]) {
|
template<>
|
||||||
return size_impl<N>::size(c);
|
struct size_impl<0> {
|
||||||
|
static constexpr std::size_t size(const char (&c)[0]) { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
constexpr std::size_t
|
||||||
|
size(const char (&c)[N])
|
||||||
|
{
|
||||||
|
return size_impl<N>::size(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A1> constexpr std::size_t total_size(const A1 &a1) {
|
template<typename A1>
|
||||||
return size(a1);
|
constexpr std::size_t
|
||||||
|
total_size(const A1 &a1)
|
||||||
|
{
|
||||||
|
return size(a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A1, typename... Args>
|
template<typename A1, typename... Args>
|
||||||
constexpr std::size_t total_size(const A1 &a1, const Args &...args) {
|
constexpr std::size_t
|
||||||
return size(a1) + total_size(args...);
|
total_size(const A1 &a1, const Args &...args)
|
||||||
|
{
|
||||||
|
return size(a1) + total_size(args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
class NoncontiguousBuffer {
|
class NoncontiguousBuffer {
|
||||||
// <buffer_t, buffer_t::chain>
|
// <buffer_t, buffer_t::chain>
|
||||||
using LinkedBuffers =
|
using LinkedBuffers = internal::SinglyLinkedList<PolymorphicBuffer, &PolymorphicBuffer::chain>;
|
||||||
internal::SinglyLinkedList<PolymorphicBuffer, &PolymorphicBuffer::chain>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using iterator = LinkedBuffers::iterator;
|
using iterator = LinkedBuffers::iterator;
|
||||||
using const_iterator = LinkedBuffers::const_iterator;
|
using const_iterator = LinkedBuffers::const_iterator;
|
||||||
using buffer_t = PolymorphicBuffer;
|
using buffer_t = PolymorphicBuffer;
|
||||||
|
|
||||||
constexpr NoncontiguousBuffer() = default;
|
constexpr NoncontiguousBuffer() = default;
|
||||||
|
|
||||||
// Copyable & moveable
|
// Copyable & moveable
|
||||||
NoncontiguousBuffer(const NoncontiguousBuffer &other);
|
NoncontiguousBuffer(const NoncontiguousBuffer &other);
|
||||||
NoncontiguousBuffer &operator=(const NoncontiguousBuffer &other);
|
NoncontiguousBuffer &operator=(const NoncontiguousBuffer &other);
|
||||||
NoncontiguousBuffer(NoncontiguousBuffer &&other) noexcept
|
|
||||||
: byte_size_(internal::Exchange(other.byte_size_, 0)),
|
NoncontiguousBuffer(NoncontiguousBuffer &&other) noexcept
|
||||||
buffers_(std::move(other.buffers_)) {}
|
: byte_size_(internal::Exchange(other.byte_size_, 0)),
|
||||||
NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept {
|
buffers_(std::move(other.buffers_))
|
||||||
if (TILE_UNLIKELY(&other == this)) {
|
{}
|
||||||
return *this;
|
|
||||||
|
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();
|
~NoncontiguousBuffer() { Clear(); }
|
||||||
std::swap(byte_size_, other.byte_size_);
|
|
||||||
buffers_ = std::move(other.buffers_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~NoncontiguousBuffer() { Clear(); }
|
Slice FirstContiguous() const noexcept
|
||||||
|
{
|
||||||
Slice FirstContiguous() const noexcept {
|
TILE_DCHECK(!Empty());
|
||||||
TILE_DCHECK(!Empty());
|
auto &&first = buffers_.front();
|
||||||
auto &&first = buffers_.front();
|
return Slice(first.data(), first.size());
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TILE_LIKELY(left > 0)) {
|
void Skip(size_t bytes) noexcept
|
||||||
auto ncb = object_pool::Get<PolymorphicBuffer>().Leak();
|
{
|
||||||
*ncb = buffers_.front();
|
TILE_DCHECK_LE(bytes, ByteSize());
|
||||||
ncb->set_size(left);
|
if (TILE_UNLIKELY(bytes == 0)) {
|
||||||
rc.buffers_.push_back(ncb);
|
} else if (bytes < buffers_.front().size()) {
|
||||||
buffers_.front().Skip(left);
|
buffers_.front().Skip(bytes);
|
||||||
}
|
byte_size_ -= bytes;
|
||||||
rc.byte_size_ = bytes;
|
} else {
|
||||||
byte_size_ -= bytes;
|
SkipSlow(bytes);
|
||||||
return rc;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Append(PolymorphicBuffer buffer) {
|
|
||||||
if (TILE_UNLIKELY(buffer.size() == 0)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto block = object_pool::Get<PolymorphicBuffer>();
|
NoncontiguousBuffer Cut(std::size_t bytes)
|
||||||
*block = std::move(buffer);
|
{
|
||||||
byte_size_ += block->size();
|
TILE_DCHECK_LE(bytes, ByteSize());
|
||||||
buffers_.push_back(block.Leak());
|
TILE_DCHECK_GE(bytes, 0);
|
||||||
}
|
|
||||||
|
|
||||||
void Append(NoncontiguousBuffer buffer) {
|
NoncontiguousBuffer rc;
|
||||||
byte_size_ += internal::Exchange(buffer.byte_size_, 0);
|
auto left = bytes;
|
||||||
buffers_.splice(std::move(buffer.buffers_));
|
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_; }
|
if (TILE_LIKELY(left > 0)) {
|
||||||
|
auto ncb = object_pool::Get<PolymorphicBuffer>().Leak();
|
||||||
bool Empty() const noexcept {
|
*ncb = buffers_.front();
|
||||||
TILE_CHECK_EQ(buffers_.empty(), !byte_size_);
|
ncb->set_size(left);
|
||||||
return !byte_size_;
|
rc.buffers_.push_back(ncb);
|
||||||
}
|
buffers_.front().Skip(left);
|
||||||
|
}
|
||||||
void Clear() noexcept {
|
rc.byte_size_ = bytes;
|
||||||
if (!Empty()) {
|
byte_size_ -= bytes;
|
||||||
ClearSlow();
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin() noexcept { return buffers_.begin(); }
|
void Append(PolymorphicBuffer buffer)
|
||||||
iterator end() noexcept { return buffers_.end(); }
|
{
|
||||||
const_iterator begin() const noexcept { return buffers_.begin(); }
|
if (TILE_UNLIKELY(buffer.size() == 0)) { return; }
|
||||||
const_iterator end() const noexcept { return buffers_.end(); }
|
|
||||||
|
auto block = object_pool::Get<PolymorphicBuffer>();
|
||||||
|
*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:
|
private:
|
||||||
void SkipSlow(size_t bytes) noexcept;
|
void SkipSlow(size_t bytes) noexcept;
|
||||||
void ClearSlow() noexcept;
|
void ClearSlow() noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t byte_size_{};
|
std::size_t byte_size_{};
|
||||||
LinkedBuffers buffers_;
|
LinkedBuffers buffers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NoncontiguousBufferBuilder {
|
class NoncontiguousBufferBuilder {
|
||||||
static constexpr auto kAppendViaCopyThreshold = 128;
|
static constexpr auto kAppendViaCopyThreshold = 128;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NoncontiguousBufferBuilder() { InitializeNextBlock(); }
|
NoncontiguousBufferBuilder() { InitializeNextBlock(); }
|
||||||
|
|
||||||
// current write ptr
|
// current write ptr
|
||||||
// It's size is available at `SizeAvailable()`
|
// It's size is available at `SizeAvailable()`
|
||||||
char *data() const noexcept { return current_->mutable_data() + used_; }
|
char *data() const noexcept { return current_->mutable_data() + used_; }
|
||||||
|
|
||||||
std::size_t SizeAvailable() const noexcept {
|
std::size_t SizeAvailable() const noexcept { return current_->size() - used_; }
|
||||||
return current_->size() - used_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkWritten(std::size_t bytes) {
|
void MarkWritten(std::size_t bytes)
|
||||||
TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer.");
|
{
|
||||||
used_ += bytes;
|
TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer.");
|
||||||
// Is fulled ?
|
used_ += bytes;
|
||||||
if (TILE_UNLIKELY(!SizeAvailable())) {
|
// Is fulled ?
|
||||||
FlushCurrentBlock();
|
if (TILE_UNLIKELY(!SizeAvailable())) {
|
||||||
InitializeNextBlock();
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ptr = data();
|
// Allocate new block
|
||||||
MarkWritten(bytes);
|
// return write ptr
|
||||||
return 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
|
auto ptr = data();
|
||||||
std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; }
|
MarkWritten(bytes);
|
||||||
|
return ptr;
|
||||||
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<void *>(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (used_) {
|
// Total number of bytes written
|
||||||
FlushCurrentBlock();
|
std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; }
|
||||||
InitializeNextBlock();
|
|
||||||
|
NoncontiguousBuffer DestructiveGet() noexcept
|
||||||
|
{
|
||||||
|
FlushCurrentBlock();
|
||||||
|
return std::move(nb_);
|
||||||
}
|
}
|
||||||
|
|
||||||
nb_.Append(std::move(buffer));
|
void Append(const void *ptr, std::size_t length)
|
||||||
}
|
{
|
||||||
|
auto current = data();
|
||||||
void Append(NoncontiguousBuffer buffer) {
|
used_ += length;
|
||||||
if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) {
|
if (TILE_LIKELY(used_ < current_->size())) {
|
||||||
AppendCopy(buffer);
|
memcpy(static_cast<void *>(current), ptr, length);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
used_ -= length;
|
||||||
|
AppendSlow(ptr, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (used_) {
|
void Append(Slice s) { return Append(s.data(), s.size()); }
|
||||||
FlushCurrentBlock();
|
|
||||||
InitializeNextBlock();
|
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<unsigned char>(c)); }
|
void Append(NoncontiguousBuffer buffer)
|
||||||
|
{
|
||||||
|
if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) {
|
||||||
|
AppendCopy(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void Append(unsigned char c) {
|
if (used_) {
|
||||||
TILE_DCHECK(SizeAvailable());
|
FlushCurrentBlock();
|
||||||
*reinterpret_cast<unsigned char *>(data()) = c;
|
InitializeNextBlock();
|
||||||
MarkWritten(1);
|
}
|
||||||
}
|
nb_.Append(std::move(buffer));
|
||||||
|
|
||||||
template <
|
|
||||||
typename... Ts,
|
|
||||||
typename =
|
|
||||||
internal::void_t<decltype(detail::data(std::declval<Ts &>()))...>,
|
|
||||||
typename =
|
|
||||||
internal::void_t<decltype(detail::size(std::declval<Ts &>()))...>>
|
|
||||||
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 <typename T>
|
void Append(char c) { Append(static_cast<unsigned char>(c)); }
|
||||||
auto Append(T) -> enable_if_t<std::is_same<T, int>::value> {
|
|
||||||
static_assert(sizeof(T) == 0, "Please use Append(char) instead.");
|
void Append(unsigned char c)
|
||||||
}
|
{
|
||||||
|
TILE_DCHECK(SizeAvailable());
|
||||||
|
*reinterpret_cast<unsigned char *>(data()) = c;
|
||||||
|
MarkWritten(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Ts,
|
||||||
|
typename = internal::void_t<decltype(detail::data(std::declval<Ts &>()))...>,
|
||||||
|
typename = internal::void_t<decltype(detail::size(std::declval<Ts &>()))...>>
|
||||||
|
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<typename T>
|
||||||
|
auto Append(T) -> enable_if_t<std::is_same<T, int>::value>
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == 0, "Please use Append(char) instead.");
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Allocate a new buffer
|
// Allocate a new buffer
|
||||||
void InitializeNextBlock();
|
void InitializeNextBlock();
|
||||||
|
|
||||||
void FlushCurrentBlock();
|
void FlushCurrentBlock();
|
||||||
|
|
||||||
void AppendSlow(const void *ptr, std::size_t length);
|
void AppendSlow(const void *ptr, std::size_t length);
|
||||||
void AppendCopy(const NoncontiguousBuffer &buffer);
|
void AppendCopy(const NoncontiguousBuffer &buffer);
|
||||||
|
|
||||||
template <typename T, typename...>
|
template<typename T, typename...>
|
||||||
inline void UncheckedAppend(char *ptr, const T &slice) {
|
inline void UncheckedAppend(char *ptr, const T &slice)
|
||||||
memcpy(ptr, detail::data(slice), detail::size(slice));
|
{
|
||||||
}
|
memcpy(ptr, detail::data(slice), detail::size(slice));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename... Ts>
|
template<typename T, typename... Ts>
|
||||||
inline void UncheckedAppend(char *ptr, const T &slice, const Ts &...slices) {
|
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...);
|
memcpy(ptr, detail::data(slice), detail::size(slice));
|
||||||
}
|
UncheckedAppend(ptr + detail::size(slice), slices...);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NoncontiguousBuffer nb_;
|
NoncontiguousBuffer nb_;
|
||||||
std::size_t used_;
|
std::size_t used_;
|
||||||
RefPtr<NativeBufferBlock> current_;
|
RefPtr<NativeBufferBlock> current_;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
void FlattenToSlowSlow(const NoncontiguousBuffer &ncb, void *buffer,
|
void FlattenToSlowSlow(const NoncontiguousBuffer &ncb, void *buffer, std::size_t size);
|
||||||
std::size_t size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NoncontiguousBuffer CreateBufferSlow(Slice s);
|
NoncontiguousBuffer CreateBufferSlow(Slice s);
|
||||||
NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size);
|
NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size);
|
||||||
|
|
||||||
std::string
|
std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
||||||
FlattenSlow(const NoncontiguousBuffer &nb,
|
std::string FlattenSlowUntil(const NoncontiguousBuffer &nb,
|
||||||
std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
Slice delim,
|
||||||
std::string FlattenSlowUntil(
|
std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
||||||
const NoncontiguousBuffer &nb, Slice delim,
|
|
||||||
std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
inline void
|
||||||
inline void FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer,
|
FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t max_bytes)
|
||||||
std::size_t max_bytes) {
|
{
|
||||||
if (TILE_LIKELY(max_bytes <= nb.FirstContiguous().size())) {
|
if (TILE_LIKELY(max_bytes <= nb.FirstContiguous().size())) {
|
||||||
std::memcpy(buffer, nb.FirstContiguous().data(), max_bytes);
|
std::memcpy(buffer, nb.FirstContiguous().data(), max_bytes);
|
||||||
}
|
}
|
||||||
return detail::FlattenToSlowSlow(nb, buffer, max_bytes);
|
return detail::FlattenToSlowSlow(nb, buffer, max_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size);
|
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size);
|
||||||
|
|
||||||
template <typename F>
|
template<typename F>
|
||||||
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size,
|
PolymorphicBuffer
|
||||||
F &&completion_cb) {
|
MakeReferencingBuffer(const void *ptr, std::size_t size, F &&completion_cb)
|
||||||
using BufferBlock =
|
{
|
||||||
ReferencingBufferBlock<typename std::remove_reference<F>::type>;
|
using BufferBlock = ReferencingBufferBlock<typename std::remove_reference<F>::type>;
|
||||||
return PolymorphicBuffer(
|
return PolymorphicBuffer(
|
||||||
MakeRefCounted<BufferBlock>(
|
MakeRefCounted<BufferBlock>(ptr, size, std::forward<typename std::remove_reference<F>::type>(completion_cb)), 0,
|
||||||
ptr, size,
|
size);
|
||||||
std::forward<typename std::remove_reference<F>::type>(completion_cb)),
|
|
||||||
0, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PolymorphicBuffer MakeForeignBuffer(std::string buffer);
|
PolymorphicBuffer MakeForeignBuffer(std::string buffer);
|
||||||
|
|
||||||
// `T` in (`std::byte`, integral types, floating types)
|
// `T` in (`std::byte`, integral types, floating types)
|
||||||
template <typename T>
|
template<typename T>
|
||||||
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer);
|
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer);
|
||||||
|
|
||||||
bool StartsWith(NoncontiguousBuffer buffer, Slice prefix);
|
bool StartsWith(NoncontiguousBuffer buffer, Slice prefix);
|
||||||
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix);
|
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix);
|
||||||
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix);
|
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix);
|
||||||
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix);
|
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix);
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_BUFFER_H
|
#endif// TILE_BASE_BUFFER_H
|
||||||
|
@ -19,104 +19,108 @@ namespace tile {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <std::size_t kSize>
|
template<std::size_t kSize>
|
||||||
struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock
|
struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock : NativeBufferBlock {
|
||||||
: NativeBufferBlock {
|
|
||||||
public:
|
public:
|
||||||
static RefPtr<FixedNativeBufferBlock> New() {
|
static RefPtr<FixedNativeBufferBlock> New()
|
||||||
// WARNING: don't use adopt_ptr
|
{
|
||||||
return RefPtr<FixedNativeBufferBlock<kSize>>(
|
// WARNING: don't use adopt_ptr
|
||||||
adopt_ptr, object_pool::Get<FixedNativeBufferBlock<kSize>>().Leak());
|
return RefPtr<FixedNativeBufferBlock<kSize>>(
|
||||||
}
|
adopt_ptr, object_pool::Get<FixedNativeBufferBlock<kSize>>().Leak());
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: add ref ensure `RefCount<Base>`.
|
// HACK: add ref ensure `RefCount<Base>`.
|
||||||
static void Delete(FixedNativeBufferBlock *ptr) {
|
static void Delete(FixedNativeBufferBlock *ptr)
|
||||||
ptr->Save();
|
{
|
||||||
object_pool::Put<FixedNativeBufferBlock<kSize>>(ptr);
|
ptr->Save();
|
||||||
}
|
object_pool::Put<FixedNativeBufferBlock<kSize>>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
char *mutable_data() noexcept override { return buffer.data(); }
|
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); }
|
|
||||||
|
|
||||||
static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock);
|
const char *data() const noexcept override { return buffer.data(); }
|
||||||
std::array<char, kBufferSize> buffer;
|
|
||||||
|
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<char, kBufferSize> buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t kSize>
|
template<std::size_t kSize>
|
||||||
RefPtr<NativeBufferBlock> MakeNativeBufferBlockOfBytes() {
|
RefPtr<NativeBufferBlock>
|
||||||
return FixedNativeBufferBlock<kSize>::New();
|
MakeNativeBufferBlockOfBytes()
|
||||||
|
{
|
||||||
|
return FixedNativeBufferBlock<kSize>::New();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<NativeBufferBlock> (*make_native_buffer_block)() =
|
RefPtr<NativeBufferBlock> (*make_native_buffer_block)() = MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K>;
|
||||||
MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K>;
|
|
||||||
|
|
||||||
void InitializeMakeNativeBufferBlock() {
|
void
|
||||||
static const std::unordered_map<std::string, RefPtr<NativeBufferBlock> (*)()>
|
InitializeMakeNativeBufferBlock()
|
||||||
kMakers = {{"4K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K>},
|
{
|
||||||
{"64K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_64K>},
|
static const std::unordered_map<std::string, RefPtr<NativeBufferBlock> (*)()> kMakers = {
|
||||||
{"1M", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_1M>}
|
{"4K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K> },
|
||||||
|
{"64K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_64K>},
|
||||||
};
|
{"1M", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_1M> }
|
||||||
auto iter = kMakers.find(FLAGS_tile_buffer_block_size);
|
};
|
||||||
if (iter != kMakers.end()) {
|
auto iter = kMakers.find(FLAGS_tile_buffer_block_size);
|
||||||
make_native_buffer_block = iter->second;
|
if (iter != kMakers.end()) {
|
||||||
} else {
|
make_native_buffer_block = iter->second;
|
||||||
TILE_UNEXPECTED(
|
} else {
|
||||||
"Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block "
|
TILE_UNEXPECTED("Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block "
|
||||||
"is supported.",
|
"is supported.",
|
||||||
FLAGS_tile_buffer_block_size);
|
FLAGS_tile_buffer_block_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TILE_ON_INIT(0, InitializeMakeNativeBufferBlock);
|
TILE_ON_INIT(0, InitializeMakeNativeBufferBlock);
|
||||||
|
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
RefPtr<NativeBufferBlock> MakeNativeBufferBlock() {
|
RefPtr<NativeBufferBlock>
|
||||||
return make_native_buffer_block();
|
MakeNativeBufferBlock()
|
||||||
|
{
|
||||||
|
return make_native_buffer_block();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>> {
|
template<>
|
||||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>> {
|
||||||
static constexpr auto kLowWaterMark = 16384; // 64M per node.
|
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||||
static constexpr auto kHighWaterMark =
|
static constexpr auto kLowWaterMark = 16384;// 64M per node.
|
||||||
std::numeric_limits<std::size_t>::max();
|
static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||||
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
||||||
static constexpr auto kMinimumThreadCacheSize = 4096; // 16M per thread.
|
static constexpr auto kMinimumThreadCacheSize = 4096;// 16M per thread.
|
||||||
static constexpr auto kTransferBatchSize = 1024; // Extra 4M.
|
static constexpr auto kTransferBatchSize = 1024;// Extra 4M.
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>> {
|
template<>
|
||||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>> {
|
||||||
static constexpr auto kLowWaterMark = 1024; // 64M per node.
|
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||||
static constexpr auto kHighWaterMark =
|
static constexpr auto kLowWaterMark = 1024;// 64M per node.
|
||||||
std::numeric_limits<std::size_t>::max();
|
static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||||
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
||||||
static constexpr auto kMinimumThreadCacheSize = 256; // 16M per thread.
|
static constexpr auto kMinimumThreadCacheSize = 256;// 16M per thread.
|
||||||
static constexpr auto kTransferBatchSize = 64; // Extra 4M.
|
static constexpr auto kTransferBatchSize = 64; // Extra 4M.
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>> {
|
template<>
|
||||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>> {
|
||||||
static constexpr auto kLowWaterMark = 128; // 128M per node.
|
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||||
static constexpr auto kHighWaterMark =
|
static constexpr auto kLowWaterMark = 128;// 128M per node.
|
||||||
std::numeric_limits<std::size_t>::max();
|
static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||||
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
||||||
static constexpr auto kMinimumThreadCacheSize = 64; // 64M per thread.
|
static constexpr auto kMinimumThreadCacheSize = 64;// 64M per thread.
|
||||||
static constexpr auto kTransferBatchSize = 16; // Extra 16M.
|
static constexpr auto kTransferBatchSize = 16;// Extra 16M.
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kType;
|
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kType;
|
||||||
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kType;
|
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kType;
|
||||||
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kType;
|
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kType;
|
||||||
|
|
||||||
constexpr std::chrono::seconds
|
constexpr std::chrono::seconds PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kMaxIdle;
|
||||||
PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kMaxIdle;
|
constexpr std::chrono::seconds PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kMaxIdle;
|
||||||
constexpr std::chrono::seconds
|
constexpr std::chrono::seconds PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kMaxIdle;
|
||||||
PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kMaxIdle;
|
|
||||||
constexpr std::chrono::seconds
|
|
||||||
PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kMaxIdle;
|
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -8,34 +8,32 @@
|
|||||||
#include "tile/base/ref_ptr.h"
|
#include "tile/base/ref_ptr.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
class alignas(hardware_destructive_interference_size) NativeBufferBlock
|
class alignas(hardware_destructive_interference_size) NativeBufferBlock : public PolymorphicBufferBlock {
|
||||||
: public PolymorphicBufferBlock {
|
|
||||||
public:
|
public:
|
||||||
virtual char *mutable_data() noexcept = 0;
|
virtual char *mutable_data() noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
RefPtr<NativeBufferBlock> MakeNativeBufferBlock();
|
RefPtr<NativeBufferBlock> MakeNativeBufferBlock();
|
||||||
|
|
||||||
template <typename F>
|
template<typename F>
|
||||||
class ReferencingBufferBlock : public PolymorphicBufferBlock,
|
class ReferencingBufferBlock : public PolymorphicBufferBlock, private F /* Empty Base Optimize */ {
|
||||||
private F /* Empty Base Optimize */ {
|
|
||||||
public:
|
public:
|
||||||
explicit ReferencingBufferBlock(const void *ptr, std::size_t size,
|
explicit ReferencingBufferBlock(const void *ptr, std::size_t size, F &&completion_cb)
|
||||||
F &&completion_cb)
|
: F(std::move(completion_cb)),
|
||||||
: F(std::move(completion_cb)), ptr_(ptr), size_(size) {}
|
ptr_(ptr),
|
||||||
|
size_(size)
|
||||||
|
{}
|
||||||
|
|
||||||
~ReferencingBufferBlock() override { (*this)(); }
|
~ReferencingBufferBlock() override { (*this)(); }
|
||||||
|
|
||||||
const char *data() const noexcept override {
|
const char *data() const noexcept override { return reinterpret_cast<const char *>(ptr_); }
|
||||||
return reinterpret_cast<const char *>(ptr_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t size() const noexcept override { return size_; }
|
std::size_t size() const noexcept override { return size_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const void *ptr_;
|
const void *ptr_;
|
||||||
std::size_t size_;
|
std::size_t size_;
|
||||||
}; // namespace tile
|
};// namespace tile
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_BUFFER_BUILTIN_BUFFER_BLOCK_H
|
#endif// TILE_BASE_BUFFER_BUILTIN_BUFFER_BLOCK_H
|
||||||
|
@ -9,59 +9,63 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
template <typename T> class CircularBuffer {
|
template<typename T>
|
||||||
class UninitializedObject;
|
class CircularBuffer {
|
||||||
|
class UninitializedObject;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CircularBuffer(std::size_t capacity);
|
explicit CircularBuffer(std::size_t capacity);
|
||||||
|
|
||||||
template <typename... Ts> bool Emplace(Ts &&...args) {
|
template<typename... Ts>
|
||||||
auto head = head_.load(std::memory_order_relaxed);
|
bool Emplace(Ts &&...args)
|
||||||
auto next = NormalizeIndex(head + 1);
|
{
|
||||||
|
auto head = head_.load(std::memory_order_relaxed);
|
||||||
|
auto next = NormalizeIndex(head + 1);
|
||||||
|
|
||||||
if (next == tail_.load(std::memory_order_acquire)) {
|
if (next == tail_.load(std::memory_order_acquire)) { return false; }
|
||||||
return false;
|
|
||||||
|
objects_[head].Initialize(std::forward<Ts>(args)...);
|
||||||
|
head_.store(next, std::memory_order_release);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
objects_[head].Initialize(std::forward<Ts>(args)...);
|
void Pop(std::vector<T> *objects)
|
||||||
head_.store(next, std::memory_order_release);
|
{
|
||||||
return true;
|
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<T> *objects) {
|
tail_.store(current, std::memory_order_release);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class UninitializedObject {
|
class UninitializedObject {
|
||||||
public:
|
public:
|
||||||
T *Get() noexcept { return reinterpret_cast<T *>(&storage_); }
|
T *Get() noexcept { return reinterpret_cast<T *>(&storage_); }
|
||||||
template <typename... Ts> void Initialize(Ts &&...args) {
|
|
||||||
new (Get()) T(std::forward<Ts>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Destroy() { Get()->~T(); }
|
template<typename... Ts>
|
||||||
|
void Initialize(Ts &&...args)
|
||||||
|
{
|
||||||
|
new (Get()) T(std::forward<Ts>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
void Destroy() { Get()->~T(); }
|
||||||
std::aligned_storage<sizeof(T), alignof(T)> storage_;
|
|
||||||
};
|
private:
|
||||||
std::size_t NormalizeIndex(std::size_t x) {
|
std::aligned_storage<sizeof(T), alignof(T)> storage_;
|
||||||
return (x < capacity_) ? x : x - capacity_;
|
};
|
||||||
}
|
|
||||||
|
std::size_t NormalizeIndex(std::size_t x) { return (x < capacity_) ? x : x - capacity_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t capacity_;
|
std::size_t capacity_;
|
||||||
std::unique_ptr<UninitializedObject[]> objects_;
|
std::unique_ptr<UninitializedObject[]> objects_;
|
||||||
std::atomic<std::size_t> head_{}, tail_{};
|
std::atomic<std::size_t> head_{}, tail_{};
|
||||||
};
|
};
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_BUFFER_CIRCULAR_BUFFER_H
|
#endif// _TILE_BASE_BUFFER_CIRCULAR_BUFFER_H
|
||||||
|
@ -2,42 +2,39 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
NoncontiguousBufferCompressionOutputStream::
|
NoncontiguousBufferCompressionOutputStream::NoncontiguousBufferCompressionOutputStream(
|
||||||
NoncontiguousBufferCompressionOutputStream(
|
NoncontiguousBufferBuilder *builder)
|
||||||
NoncontiguousBufferBuilder *builder)
|
: builder_(builder)
|
||||||
: builder_(builder) {}
|
{}
|
||||||
|
|
||||||
NoncontiguousBufferCompressionOutputStream::
|
NoncontiguousBufferCompressionOutputStream::~NoncontiguousBufferCompressionOutputStream() { Flush(); }
|
||||||
~NoncontiguousBufferCompressionOutputStream() {
|
|
||||||
Flush();
|
void
|
||||||
|
NoncontiguousBufferCompressionOutputStream::Flush()
|
||||||
|
{
|
||||||
|
if (using_bytes_ > 0) {
|
||||||
|
builder_->MarkWritten(using_bytes_);
|
||||||
|
using_bytes_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoncontiguousBufferCompressionOutputStream::Flush() {
|
bool
|
||||||
if (using_bytes_ > 0) {
|
NoncontiguousBufferCompressionOutputStream::Next(void **data, std::size_t *size) noexcept
|
||||||
builder_->MarkWritten(using_bytes_);
|
{
|
||||||
using_bytes_ = 0;
|
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
|
||||||
void **data, std::size_t *size) noexcept {
|
NoncontiguousBufferCompressionOutputStream::BackUp(std::size_t count) noexcept
|
||||||
if (!builder_) {
|
{
|
||||||
return false;
|
using_bytes_ -= count;
|
||||||
}
|
|
||||||
|
|
||||||
if (using_bytes_) {
|
|
||||||
builder_->MarkWritten(using_bytes_);
|
|
||||||
}
|
|
||||||
|
|
||||||
*data = builder_->data();
|
|
||||||
*size = builder_->SizeAvailable();
|
|
||||||
using_bytes_ = *size;
|
|
||||||
TILE_CHECK(*size > 0);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
}// namespace tile
|
||||||
void NoncontiguousBufferCompressionOutputStream::BackUp(
|
|
||||||
std::size_t count) noexcept {
|
|
||||||
using_bytes_ -= count;
|
|
||||||
}
|
|
||||||
} // namespace tile
|
|
||||||
|
@ -6,22 +6,20 @@
|
|||||||
#include "tile/base/compression/compression.h"
|
#include "tile/base/compression/compression.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
class NoncontiguousBufferCompressionOutputStream
|
class NoncontiguousBufferCompressionOutputStream : public CompressionOutputStream {
|
||||||
: public CompressionOutputStream {
|
|
||||||
public:
|
public:
|
||||||
explicit NoncontiguousBufferCompressionOutputStream(
|
explicit NoncontiguousBufferCompressionOutputStream(NoncontiguousBufferBuilder *builder);
|
||||||
NoncontiguousBufferBuilder *builder);
|
~NoncontiguousBufferCompressionOutputStream() override;
|
||||||
~NoncontiguousBufferCompressionOutputStream() override;
|
|
||||||
|
|
||||||
void Flush();
|
void Flush();
|
||||||
|
|
||||||
bool Next(void **data, std::size_t *size) noexcept override;
|
bool Next(void **data, std::size_t *size) noexcept override;
|
||||||
void BackUp(std::size_t count) noexcept override;
|
void BackUp(std::size_t count) noexcept override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t using_bytes_{};
|
std::size_t using_bytes_{};
|
||||||
NoncontiguousBufferBuilder *builder_;
|
NoncontiguousBufferBuilder *builder_;
|
||||||
};
|
};
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_BUFFER_COMPRESSION_OUTPUT_STREAM_H
|
#endif// TILE_BASE_BUFFER_COMPRESSION_OUTPUT_STREAM_H
|
||||||
|
@ -8,14 +8,18 @@ constexpr std::chrono::seconds PoolTraits<PolymorphicBuffer>::kMaxIdle;
|
|||||||
constexpr std::size_t PoolTraits<PolymorphicBuffer>::kMinimumThreadCacheSize;
|
constexpr std::size_t PoolTraits<PolymorphicBuffer>::kMinimumThreadCacheSize;
|
||||||
constexpr std::size_t PoolTraits<PolymorphicBuffer>::kTransferBatchSize;
|
constexpr std::size_t PoolTraits<PolymorphicBuffer>::kTransferBatchSize;
|
||||||
|
|
||||||
void PoolTraits<PolymorphicBuffer>::OnPut(PolymorphicBuffer *bb) {
|
void
|
||||||
bb->Clear();
|
PoolTraits<PolymorphicBuffer>::OnPut(PolymorphicBuffer *bb)
|
||||||
|
{
|
||||||
|
bb->Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
void PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p) {
|
void
|
||||||
TILE_DCHECK_EQ(p->UnsafeRefCount(), 0);
|
PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p)
|
||||||
p->Destroy();
|
{
|
||||||
|
TILE_DCHECK_EQ(p->UnsafeRefCount(), 0);
|
||||||
|
p->Destroy();
|
||||||
}
|
}
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -19,102 +19,111 @@ namespace detail {
|
|||||||
struct PolymorphicBufferBlockDeleter;
|
struct PolymorphicBufferBlockDeleter;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PolymorphicBufferBlock
|
class PolymorphicBufferBlock : public RefCounted<PolymorphicBufferBlock, detail::PolymorphicBufferBlockDeleter> {
|
||||||
: public RefCounted<PolymorphicBufferBlock,
|
|
||||||
detail::PolymorphicBufferBlockDeleter> {
|
|
||||||
public:
|
public:
|
||||||
virtual ~PolymorphicBufferBlock() = default;
|
virtual ~PolymorphicBufferBlock() = default;
|
||||||
virtual const char *data() const noexcept = 0;
|
virtual const char *data() const noexcept = 0;
|
||||||
virtual std::size_t size() const noexcept = 0;
|
virtual std::size_t size() const noexcept = 0;
|
||||||
virtual void Destroy() noexcept { delete this; }
|
|
||||||
|
virtual void Destroy() noexcept { delete this; }
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(!std::is_same<detail::as_ref_counted_t<PolymorphicBufferBlock>,
|
static_assert(!std::is_same<detail::as_ref_counted_t<PolymorphicBufferBlock>, PolymorphicBufferBlock>::value, "");
|
||||||
PolymorphicBufferBlock>::value,
|
static_assert(detail::is_default_ref_traits_safe<PolymorphicBufferBlock>::value, "");
|
||||||
"");
|
|
||||||
static_assert(detail::is_default_ref_traits_safe<PolymorphicBufferBlock>::value,
|
|
||||||
"");
|
|
||||||
class PolymorphicBuffer {
|
class PolymorphicBuffer {
|
||||||
public:
|
public:
|
||||||
PolymorphicBuffer() = default;
|
PolymorphicBuffer() = default;
|
||||||
PolymorphicBuffer(const PolymorphicBuffer &) = default;
|
PolymorphicBuffer(const PolymorphicBuffer &) = default;
|
||||||
PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default;
|
PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default;
|
||||||
|
|
||||||
PolymorphicBuffer(PolymorphicBuffer &&other) noexcept
|
PolymorphicBuffer(PolymorphicBuffer &&other) noexcept
|
||||||
: ptr_(other.ptr_), size_(other.size_), ref_(std::move(other.ref_)) {
|
: ptr_(other.ptr_),
|
||||||
other.Clear();
|
size_(other.size_),
|
||||||
}
|
ref_(std::move(other.ref_))
|
||||||
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept {
|
{
|
||||||
if (this != &other) {
|
other.Clear();
|
||||||
ptr_ = other.ptr_;
|
|
||||||
size_ = other.size_;
|
|
||||||
ref_ = other.ref_;
|
|
||||||
other.Clear();
|
|
||||||
}
|
}
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start,
|
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept
|
||||||
std::size_t size)
|
{
|
||||||
: ptr_(data->data() + start), size_(size), ref_(std::move(data)) {}
|
if (this != &other) {
|
||||||
|
ptr_ = other.ptr_;
|
||||||
|
size_ = other.size_;
|
||||||
|
ref_ = other.ref_;
|
||||||
|
other.Clear();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
const char *data() const noexcept { return ptr_; }
|
PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
|
||||||
std::size_t size() const noexcept { return size_; }
|
: ptr_(data->data() + start),
|
||||||
|
size_(size),
|
||||||
|
ref_(std::move(data))
|
||||||
|
{}
|
||||||
|
|
||||||
void Skip(std::size_t bytes) {
|
const char *data() const noexcept { return ptr_; }
|
||||||
TILE_CHECK_LT(bytes, size_);
|
|
||||||
size_ -= bytes;
|
|
||||||
ptr_ += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_size(std::size_t size) {
|
std::size_t size() const noexcept { return size_; }
|
||||||
TILE_DCHECK_LE(size, size_);
|
|
||||||
size_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start,
|
void Skip(std::size_t bytes)
|
||||||
std::size_t size) {
|
{
|
||||||
TILE_DCHECK_LE(start, size);
|
TILE_CHECK_LT(bytes, size_);
|
||||||
TILE_DCHECK_LE(size, data->size());
|
size_ -= bytes;
|
||||||
|
ptr_ += bytes;
|
||||||
|
}
|
||||||
|
|
||||||
ref_ = std::move(data);
|
void set_size(std::size_t size)
|
||||||
ptr_ = data->data() + start;
|
{
|
||||||
size_ = size;
|
TILE_DCHECK_LE(size, size_);
|
||||||
}
|
size_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
void Clear() {
|
void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
|
||||||
ptr_ = nullptr;
|
{
|
||||||
size_ = 0;
|
TILE_DCHECK_LE(start, size);
|
||||||
ref_ = nullptr;
|
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:
|
private:
|
||||||
friend class NoncontiguousBuffer;
|
friend class NoncontiguousBuffer;
|
||||||
|
|
||||||
internal::SinglyLinkedListEntry chain;
|
internal::SinglyLinkedListEntry chain;
|
||||||
const char *ptr_{};
|
const char *ptr_{};
|
||||||
std::size_t size_{};
|
std::size_t size_{};
|
||||||
RefPtr<PolymorphicBufferBlock> ref_;
|
RefPtr<PolymorphicBufferBlock> ref_;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct PolymorphicBufferBlockDeleter {
|
struct PolymorphicBufferBlockDeleter {
|
||||||
void operator()(PolymorphicBufferBlock *p);
|
void operator()(PolymorphicBufferBlock *p);
|
||||||
};
|
};
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
template <> struct PoolTraits<PolymorphicBuffer> {
|
template<>
|
||||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
struct PoolTraits<PolymorphicBuffer> {
|
||||||
static constexpr std::size_t kLowWaterMark = 32768;
|
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||||
static constexpr std::size_t kHighWaterMark =
|
static constexpr std::size_t kLowWaterMark = 32768;
|
||||||
std::numeric_limits<std::size_t>::max();
|
static constexpr std::size_t kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||||
static constexpr std::chrono::seconds kMaxIdle = std::chrono::seconds(10);
|
static constexpr std::chrono::seconds kMaxIdle = std::chrono::seconds(10);
|
||||||
static constexpr std::size_t kMinimumThreadCacheSize = 8192;
|
static constexpr std::size_t kMinimumThreadCacheSize = 8192;
|
||||||
static constexpr std::size_t kTransferBatchSize = 1024;
|
static constexpr std::size_t kTransferBatchSize = 1024;
|
||||||
|
|
||||||
static void OnPut(PolymorphicBuffer *bb);
|
static void OnPut(PolymorphicBuffer *bb);
|
||||||
};
|
};
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
#endif // TILE_BASE_BUFFER_POLYMORPHIC_BUFFER_H
|
#endif// TILE_BASE_BUFFER_POLYMORPHIC_BUFFER_H
|
||||||
|
@ -14,280 +14,308 @@ namespace tile {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
PolymorphicBuffer MakeNativeBuffer(Slice s) {
|
PolymorphicBuffer
|
||||||
auto buffer = MakeNativeBufferBlock();
|
MakeNativeBuffer(Slice s)
|
||||||
memcpy(buffer->mutable_data(), s.data(), s.size());
|
{
|
||||||
return PolymorphicBuffer(buffer, 0, s.size());
|
auto buffer = MakeNativeBufferBlock();
|
||||||
|
memcpy(buffer->mutable_data(), s.data(), s.size());
|
||||||
|
return PolymorphicBuffer(buffer, 0, s.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
TEST(CreateBufferSlow, All) {
|
TEST(CreateBufferSlow, All)
|
||||||
static const auto kData = Slice("sadfas234sadf-/+8sdaf sd f~!#");
|
{
|
||||||
auto nb = CreateBufferSlow(kData);
|
static const auto kData = Slice("sadfas234sadf-/+8sdaf sd f~!#");
|
||||||
ASSERT_EQ(kData, nb.FirstContiguous().data());
|
auto nb = CreateBufferSlow(kData);
|
||||||
ASSERT_EQ(kData, FlattenSlow(nb));
|
ASSERT_EQ(kData, nb.FirstContiguous().data());
|
||||||
|
ASSERT_EQ(kData, FlattenSlow(nb));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Cut) {
|
TEST(NoncontiguousBuffer, Cut)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(CreateBufferSlow("asdf"));
|
NoncontiguousBuffer nb;
|
||||||
auto r = nb.Cut(3);
|
nb.Append(CreateBufferSlow("asdf"));
|
||||||
ASSERT_EQ(1, nb.ByteSize());
|
auto r = nb.Cut(3);
|
||||||
ASSERT_EQ("f", FlattenSlow(nb));
|
ASSERT_EQ(1, nb.ByteSize());
|
||||||
ASSERT_EQ(3, r.ByteSize());
|
ASSERT_EQ("f", FlattenSlow(nb));
|
||||||
ASSERT_EQ("asd", FlattenSlow(r));
|
ASSERT_EQ(3, r.ByteSize());
|
||||||
|
ASSERT_EQ("asd", FlattenSlow(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Cut1) {
|
TEST(NoncontiguousBuffer, Cut1)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(CreateBufferSlow("asdf"));
|
NoncontiguousBuffer nb;
|
||||||
auto r = nb.Cut(4);
|
nb.Append(CreateBufferSlow("asdf"));
|
||||||
ASSERT_TRUE(nb.Empty());
|
auto r = nb.Cut(4);
|
||||||
ASSERT_EQ(4, r.ByteSize());
|
ASSERT_TRUE(nb.Empty());
|
||||||
|
ASSERT_EQ(4, r.ByteSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Cut2) {
|
TEST(NoncontiguousBuffer, Cut2)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(MakeNativeBuffer("asdf"));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer("asdf"));
|
nb.Append(MakeNativeBuffer("asdf"));
|
||||||
auto r = nb.Cut(4);
|
nb.Append(MakeNativeBuffer("asdf"));
|
||||||
ASSERT_EQ(4, nb.ByteSize());
|
auto r = nb.Cut(4);
|
||||||
ASSERT_EQ(4, r.ByteSize());
|
ASSERT_EQ(4, nb.ByteSize());
|
||||||
|
ASSERT_EQ(4, r.ByteSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Cut3) {
|
TEST(NoncontiguousBuffer, Cut3)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(MakeNativeBuffer("asdf"));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer("asdf"));
|
nb.Append(MakeNativeBuffer("asdf"));
|
||||||
auto r = nb.Cut(8);
|
nb.Append(MakeNativeBuffer("asdf"));
|
||||||
ASSERT_TRUE(nb.Empty());
|
auto r = nb.Cut(8);
|
||||||
ASSERT_EQ(8, r.ByteSize());
|
ASSERT_TRUE(nb.Empty());
|
||||||
|
ASSERT_EQ(8, r.ByteSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Cut4) {
|
TEST(NoncontiguousBuffer, Cut4)
|
||||||
auto nb = CreateBufferSlow("asdfasf2345sfsdfdf");
|
{
|
||||||
auto nb2 = nb;
|
auto nb = CreateBufferSlow("asdfasf2345sfsdfdf");
|
||||||
ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2));
|
auto nb2 = nb;
|
||||||
NoncontiguousBuffer splited;
|
ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2));
|
||||||
splited.Append(nb.Cut(1));
|
NoncontiguousBuffer splited;
|
||||||
splited.Append(nb.Cut(2));
|
splited.Append(nb.Cut(1));
|
||||||
splited.Append(nb.Cut(3));
|
splited.Append(nb.Cut(2));
|
||||||
splited.Append(nb.Cut(4));
|
splited.Append(nb.Cut(3));
|
||||||
splited.Append(std::move(nb));
|
splited.Append(nb.Cut(4));
|
||||||
ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited));
|
splited.Append(std::move(nb));
|
||||||
|
ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Skip) {
|
TEST(NoncontiguousBuffer, Skip)
|
||||||
NoncontiguousBuffer splited;
|
{
|
||||||
splited.Append(CreateBufferSlow("asdf"));
|
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.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);
|
splited.Append(CreateBufferSlow("asdf"));
|
||||||
ASSERT_EQ(0, splited.ByteSize());
|
splited.Skip(32);
|
||||||
|
ASSERT_EQ(0, splited.ByteSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, Skip2) {
|
TEST(NoncontiguousBuffer, Skip2)
|
||||||
NoncontiguousBuffer buffer;
|
{
|
||||||
EXPECT_TRUE(buffer.Empty());
|
NoncontiguousBuffer buffer;
|
||||||
buffer.Skip(0); // Don't crash.
|
EXPECT_TRUE(buffer.Empty());
|
||||||
EXPECT_TRUE(buffer.Empty());
|
buffer.Skip(0);// Don't crash.
|
||||||
|
EXPECT_TRUE(buffer.Empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, FlattenSlow) {
|
TEST(NoncontiguousBuffer, FlattenSlow)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(MakeNativeBuffer("asd4234"));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
nb.Append(MakeNativeBuffer("asd4234"));
|
||||||
ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10));
|
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
||||||
|
ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, FlattenToSlow) {
|
TEST(NoncontiguousBuffer, FlattenToSlow)
|
||||||
struct C {
|
{
|
||||||
std::uint64_t ll;
|
struct C {
|
||||||
int i;
|
std::uint64_t ll;
|
||||||
bool f;
|
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)));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer(Slice("\x1", 1)));
|
nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78\x9a\xbc\xde\xf0", 8)));
|
||||||
nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3))); // Padding
|
nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78", 4)));
|
||||||
C c;
|
nb.Append(MakeNativeBuffer(Slice("\x1", 1)));
|
||||||
FlattenToSlow(nb, &c, sizeof(C));
|
nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3)));// Padding
|
||||||
ASSERT_EQ(0xf0debc9a78563412, c.ll);
|
C c;
|
||||||
ASSERT_EQ(0x78563412, c.i);
|
FlattenToSlow(nb, &c, sizeof(C));
|
||||||
ASSERT_EQ(true, c.f);
|
ASSERT_EQ(0xf0debc9a78563412, c.ll);
|
||||||
|
ASSERT_EQ(0x78563412, c.i);
|
||||||
|
ASSERT_EQ(true, c.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, FlattenSlowUntil) {
|
TEST(NoncontiguousBuffer, FlattenSlowUntil)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(MakeNativeBuffer("asd4234"));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
nb.Append(MakeNativeBuffer("asd4234"));
|
||||||
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
|
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
||||||
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
|
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
|
||||||
ASSERT_EQ("asd4234aXsdfsadfasdf2342", FlattenSlowUntil(nb, "2342"));
|
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
|
||||||
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "z", 5));
|
ASSERT_EQ("asd4234aXsdfsadfasdf2342", FlattenSlowUntil(nb, "2342"));
|
||||||
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "3", 5));
|
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "z", 5));
|
||||||
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "2", 5));
|
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "3", 5));
|
||||||
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5));
|
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "2", 5));
|
||||||
|
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, FlattenSlowUntil2) {
|
TEST(NoncontiguousBuffer, FlattenSlowUntil2)
|
||||||
auto nb = CreateBufferSlow(
|
{
|
||||||
"HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
|
auto nb = CreateBufferSlow(
|
||||||
"0\r\nRpc-Error-Reason: The operation completed "
|
"HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
|
||||||
"successfully.\r\nContent-Type: "
|
"0\r\nRpc-Error-Reason: The operation completed "
|
||||||
"application/x-protobuf\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 "
|
"successfully.\r\nContent-Type: "
|
||||||
"OK\r\nRpc-Seq");
|
"application/x-protobuf\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 "
|
||||||
ASSERT_EQ("HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
|
"OK\r\nRpc-Seq");
|
||||||
"0\r\nRpc-Error-Reason: The operation completed "
|
ASSERT_EQ("HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
|
||||||
"successfully.\r\nContent-Type: "
|
"0\r\nRpc-Error-Reason: The operation completed "
|
||||||
"application/x-protobuf\r\nContent-Length: 0\r\n\r\n",
|
"successfully.\r\nContent-Type: "
|
||||||
FlattenSlowUntil(nb, "\r\n\r\n"));
|
"application/x-protobuf\r\nContent-Length: 0\r\n\r\n",
|
||||||
|
FlattenSlowUntil(nb, "\r\n\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, FlattenSlowUntil3) {
|
TEST(NoncontiguousBuffer, FlattenSlowUntil3)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(MakeNativeBuffer("asd4234"));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
nb.Append(MakeNativeBuffer("asd4234"));
|
||||||
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
|
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
||||||
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
|
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
|
||||||
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX"));
|
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
|
||||||
|
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBuffer, FlattenSlowUntil4) {
|
TEST(NoncontiguousBuffer, FlattenSlowUntil4)
|
||||||
NoncontiguousBuffer nb;
|
{
|
||||||
nb.Append(MakeNativeBuffer("AB"));
|
NoncontiguousBuffer nb;
|
||||||
nb.Append(MakeNativeBuffer("CDEFGGGGHHHH"));
|
nb.Append(MakeNativeBuffer("AB"));
|
||||||
ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG"));
|
nb.Append(MakeNativeBuffer("CDEFGGGGHHHH"));
|
||||||
|
ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, Append) {
|
TEST(NoncontiguousBufferBuilder, Append)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append(MakeForeignBuffer(""));
|
NoncontiguousBufferBuilder nbb;
|
||||||
nbb.Append(MakeForeignBuffer("small"));
|
nbb.Append(MakeForeignBuffer(""));
|
||||||
nbb.Append(MakeForeignBuffer(std::string(8192, 'a')));
|
nbb.Append(MakeForeignBuffer("small"));
|
||||||
nbb.Append(CreateBufferSlow(""));
|
nbb.Append(MakeForeignBuffer(std::string(8192, 'a')));
|
||||||
nbb.Append(CreateBufferSlow("small"));
|
nbb.Append(CreateBufferSlow(""));
|
||||||
nbb.Append(CreateBufferSlow(std::string(8192, 'a')));
|
nbb.Append(CreateBufferSlow("small"));
|
||||||
auto nb = nbb.DestructiveGet();
|
nbb.Append(CreateBufferSlow(std::string(8192, 'a')));
|
||||||
EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'),
|
auto nb = nbb.DestructiveGet();
|
||||||
FlattenSlow(nb));
|
EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'), FlattenSlow(nb));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, Reserve) {
|
TEST(NoncontiguousBufferBuilder, Reserve)
|
||||||
auto temp_block = MakeNativeBufferBlock();
|
{
|
||||||
auto max_bytes = temp_block->size();
|
auto temp_block = MakeNativeBufferBlock();
|
||||||
NoncontiguousBufferBuilder nbb;
|
auto max_bytes = temp_block->size();
|
||||||
auto ptr = nbb.data();
|
NoncontiguousBufferBuilder nbb;
|
||||||
auto ptr2 = nbb.Reserve(10);
|
auto ptr = nbb.data();
|
||||||
ASSERT_EQ(ptr, ptr2);
|
auto ptr2 = nbb.Reserve(10);
|
||||||
ASSERT_EQ(ptr + 10, nbb.data());
|
ASSERT_EQ(ptr, ptr2);
|
||||||
nbb.Append(std::string(max_bytes - 10 - 1, 'a'));
|
ASSERT_EQ(ptr + 10, nbb.data());
|
||||||
ptr = nbb.data();
|
nbb.Append(std::string(max_bytes - 10 - 1, 'a'));
|
||||||
ptr2 = nbb.Reserve(1); // Last byte in the block.
|
ptr = nbb.data();
|
||||||
ASSERT_EQ(ptr, ptr2);
|
ptr2 = nbb.Reserve(1);// Last byte in the block.
|
||||||
ASSERT_EQ(max_bytes, nbb.SizeAvailable());
|
ASSERT_EQ(ptr, ptr2);
|
||||||
|
ASSERT_EQ(max_bytes, nbb.SizeAvailable());
|
||||||
|
|
||||||
nbb.Append(std::string(max_bytes - 1, 'a'));
|
nbb.Append(std::string(max_bytes - 1, 'a'));
|
||||||
ptr = nbb.data();
|
ptr = nbb.data();
|
||||||
ptr2 = nbb.Reserve(2);
|
ptr2 = nbb.Reserve(2);
|
||||||
ASSERT_NE(ptr, ptr2);
|
ASSERT_NE(ptr, ptr2);
|
||||||
ASSERT_EQ(ptr2 + 2, nbb.data());
|
ASSERT_EQ(ptr2 + 2, nbb.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, DestructiveGet1) {
|
TEST(NoncontiguousBufferBuilder, DestructiveGet1)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append("asdf1234", 6);
|
NoncontiguousBufferBuilder nbb;
|
||||||
nbb.Append("1122", 4);
|
nbb.Append("asdf1234", 6);
|
||||||
ASSERT_EQ("asdf12"
|
nbb.Append("1122", 4);
|
||||||
"1122",
|
ASSERT_EQ("asdf12"
|
||||||
FlattenSlow(nbb.DestructiveGet()));
|
"1122",
|
||||||
|
FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, DestructiveGet2) {
|
TEST(NoncontiguousBufferBuilder, DestructiveGet2)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append("aabbccd");
|
NoncontiguousBufferBuilder nbb;
|
||||||
ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet()));
|
nbb.Append("aabbccd");
|
||||||
|
ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, DestructiveGet3) {
|
TEST(NoncontiguousBufferBuilder, DestructiveGet3)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append(std::string(1000000, 'A'));
|
NoncontiguousBufferBuilder nbb;
|
||||||
ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet()));
|
nbb.Append(std::string(1000000, 'A'));
|
||||||
|
ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, DestructiveGet4) {
|
TEST(NoncontiguousBufferBuilder, DestructiveGet4)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append('c');
|
NoncontiguousBufferBuilder nbb;
|
||||||
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
|
nbb.Append('c');
|
||||||
|
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, DestructiveGet5) {
|
TEST(NoncontiguousBufferBuilder, DestructiveGet5)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append(CreateBufferSlow("c"));
|
NoncontiguousBufferBuilder nbb;
|
||||||
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
|
nbb.Append(CreateBufferSlow("c"));
|
||||||
|
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NoncontiguousBufferBuilder, DestructiveGet6) {
|
TEST(NoncontiguousBufferBuilder, DestructiveGet6)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"),
|
NoncontiguousBufferBuilder nbb;
|
||||||
Slice("6"));
|
nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"), Slice("6"));
|
||||||
nbb.Append("1122", 4);
|
nbb.Append("1122", 4);
|
||||||
ASSERT_EQ("11234561122", FlattenSlow(nbb.DestructiveGet()));
|
ASSERT_EQ("11234561122", FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MakeReferencingBuffer, Simple) {
|
TEST(MakeReferencingBuffer, Simple)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append(MakeReferencingBuffer("abcdefg", 7));
|
NoncontiguousBufferBuilder nbb;
|
||||||
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
nbb.Append(MakeReferencingBuffer("abcdefg", 7));
|
||||||
|
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) {
|
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized)
|
||||||
int x = 0;
|
{
|
||||||
|
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;
|
NoncontiguousBufferBuilder nbb;
|
||||||
nbb.Append("aaa", 3);
|
nbb.Append("aaa", 3);
|
||||||
nbb.Append(MakeReferencingBuffer(kBuffer.data(), 1024, [&] { ++x; }));
|
// Small buffers are copied by `Append` and freed immediately.
|
||||||
EXPECT_EQ(0, x);
|
nbb.Append(MakeReferencingBuffer("abcdefg", 7, [&] { ++x; }));
|
||||||
|
// Therefore the callback should have fired on return of `Append`.
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
auto buffer = nbb.DestructiveGet();
|
auto buffer = nbb.DestructiveGet();
|
||||||
EXPECT_EQ(0, x);
|
EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer));
|
||||||
EXPECT_EQ("aaa" + kBuffer.substr(0, 1024), FlattenSlow(buffer));
|
|
||||||
}
|
|
||||||
EXPECT_EQ(1, x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MakeForeignBuffer, String) {
|
TEST(MakeReferencingBuffer, WithCallback)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
nbb.Append(MakeForeignBuffer(std::string("abcdefg")));
|
static const std::string kBuffer(12345, 'a');
|
||||||
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
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) {
|
TEST(MakeForeignBuffer, String)
|
||||||
std::vector<char> data{'a', 'b', 'c', 'd', 'e', 'f', 'g'};
|
{
|
||||||
NoncontiguousBufferBuilder nbb;
|
NoncontiguousBufferBuilder nbb;
|
||||||
nbb.Append(MakeForeignBuffer(std::move(data)));
|
nbb.Append(MakeForeignBuffer(std::string("abcdefg")));
|
||||||
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MakeForeignBuffer, VectorOfChar)
|
||||||
|
{
|
||||||
|
std::vector<char> 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) {
|
// TEST(MakeForeignBuffer, VectorOfBytes) {
|
||||||
@ -299,13 +327,14 @@ TEST(MakeForeignBuffer, VectorOfChar) {
|
|||||||
// EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
// EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
TEST(MakeForeignBuffer, VectorOfUInt8) {
|
TEST(MakeForeignBuffer, VectorOfUInt8)
|
||||||
std::vector<std::uint8_t> data;
|
{
|
||||||
data.resize(7);
|
std::vector<std::uint8_t> data;
|
||||||
memcpy(data.data(), "abcdefg", 7);
|
data.resize(7);
|
||||||
NoncontiguousBufferBuilder nbb;
|
memcpy(data.data(), "abcdefg", 7);
|
||||||
nbb.Append(MakeForeignBuffer(std::move(data)));
|
NoncontiguousBufferBuilder nbb;
|
||||||
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
nbb.Append(MakeForeignBuffer(std::move(data)));
|
||||||
|
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -41,86 +41,116 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
inline uint16_t ByteSwap16(uint16_t val) { return __builtin_bswap16(val); }
|
inline uint16_t
|
||||||
inline uint32_t ByteSwap32(uint32_t val) { return __builtin_bswap32(val); }
|
ByteSwap16(uint16_t val)
|
||||||
inline uint64_t ByteSwap64(uint64_t val) { return __builtin_bswap64(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
|
#if TILE_LITTLE_ENDIAN == TILE_BYTE_ORDER
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsHostBigEndian() {
|
inline bool
|
||||||
|
IsHostBigEndian()
|
||||||
|
{
|
||||||
#if TILE_BIG_ENDIAN == TILE_BYTE_ORDER
|
#if TILE_BIG_ENDIAN == TILE_BYTE_ORDER
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
return val_in_host;
|
return val_in_host;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
return ByteSwap16(val_in_host);
|
return ByteSwap16(val_in_host);
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
return val_in_host;
|
return val_in_host;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
return ByteSwap32(val_in_host);
|
return ByteSwap32(val_in_host);
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
return val_in_host;
|
return val_in_host;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
return ByteSwap64(val_in_host);
|
return ByteSwap64(val_in_host);
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
return val_in_network;
|
return val_in_network;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
return ByteSwap16(val_in_network);
|
return ByteSwap16(val_in_network);
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
return val_in_network;
|
return val_in_network;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
return ByteSwap32(val_in_network);
|
return ByteSwap32(val_in_network);
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
return val_in_network;
|
return val_in_network;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
return ByteSwap64(val_in_network);
|
return ByteSwap64(val_in_network);
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_BYTE_ORDER_H
|
#endif// TILE_BASE_BYTE_ORDER_H
|
||||||
|
@ -8,109 +8,123 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
static union EndianHelper {
|
static union EndianHelper {
|
||||||
uint16_t v16;
|
uint16_t v16;
|
||||||
uint32_t v32;
|
uint32_t v32;
|
||||||
uint32_t v64;
|
uint32_t v64;
|
||||||
uint8_t bytes[8];
|
uint8_t bytes[8];
|
||||||
} endian_helper = {.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
|
} endian_helper = {
|
||||||
|
.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}
|
||||||
|
};
|
||||||
|
|
||||||
TEST(ByteOrder, ByteSwap16) {
|
TEST(ByteOrder, ByteSwap16)
|
||||||
const uint16_t val = 0x1234;
|
{
|
||||||
const uint16_t expected = 0x3412;
|
const uint16_t val = 0x1234;
|
||||||
ASSERT_EQ(expected, ByteSwap16(val));
|
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, IsHostByteOrder) {
|
TEST(ByteOrder, ByteSwap32)
|
||||||
ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian());
|
{
|
||||||
|
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
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
ASSERT_TRUE(IsHostBigEndian());
|
ASSERT_TRUE(IsHostBigEndian());
|
||||||
ASSERT_FALSE(IsHostLittleEndian());
|
ASSERT_FALSE(IsHostLittleEndian());
|
||||||
#else
|
#else
|
||||||
ASSERT_TRUE(IsHostLittleEndian());
|
ASSERT_TRUE(IsHostLittleEndian());
|
||||||
ASSERT_FALSE(IsHostBigEndian());
|
ASSERT_FALSE(IsHostBigEndian());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ByteOrder, HostToNetwork16) {
|
TEST(ByteOrder, HostToNetwork16)
|
||||||
const uint16_t val = 0x1234;
|
{
|
||||||
|
const uint16_t val = 0x1234;
|
||||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
const uint16_t expected = val;
|
const uint16_t expected = val;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
const uint16_t expected = 0x3412;
|
const uint16_t expected = 0x3412;
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
ASSERT_EQ(expected, HostToNetwork16(val));
|
ASSERT_EQ(expected, HostToNetwork16(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ByteOrder, HostToNetwork32) {
|
TEST(ByteOrder, HostToNetwork32)
|
||||||
const uint32_t val = 0x12345678;
|
{
|
||||||
|
const uint32_t val = 0x12345678;
|
||||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
const uint32_t expected = val;
|
const uint32_t expected = val;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
const uint32_t expected = 0x78563412;
|
const uint32_t expected = 0x78563412;
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
ASSERT_EQ(expected, HostToNetwork32(val));
|
ASSERT_EQ(expected, HostToNetwork32(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ByteOrder, HostToNetwork64) {
|
TEST(ByteOrder, HostToNetwork64)
|
||||||
const uint64_t val = 0x1234567890abcdef;
|
{
|
||||||
|
const uint64_t val = 0x1234567890abcdef;
|
||||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
const uint64_t expected = val;
|
const uint64_t expected = val;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
const uint64_t expected = 0xefcdab9078563412;
|
const uint64_t expected = 0xefcdab9078563412;
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
ASSERT_EQ(expected, HostToNetwork64(val));
|
ASSERT_EQ(expected, HostToNetwork64(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ByteOrder, NetworkToHost16) {
|
TEST(ByteOrder, NetworkToHost16)
|
||||||
const uint16_t val = 0x1234;
|
{
|
||||||
|
const uint16_t val = 0x1234;
|
||||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
const uint16_t expected = val;
|
const uint16_t expected = val;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
const uint16_t expected = 0x3412;
|
const uint16_t expected = 0x3412;
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
ASSERT_EQ(expected, NetworkToHost16(val));
|
ASSERT_EQ(expected, NetworkToHost16(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ByteOrder, NetworkToHost32) {
|
TEST(ByteOrder, NetworkToHost32)
|
||||||
const uint32_t val = 0x12345678;
|
{
|
||||||
|
const uint32_t val = 0x12345678;
|
||||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
const uint32_t expected = val;
|
const uint32_t expected = val;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
const uint32_t expected = 0x78563412;
|
const uint32_t expected = 0x78563412;
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
ASSERT_EQ(expected, NetworkToHost32(val));
|
ASSERT_EQ(expected, NetworkToHost32(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ByteOrder, NetworkToHost64) {
|
TEST(ByteOrder, NetworkToHost64)
|
||||||
const uint64_t val = 0x1234567890abcdef;
|
{
|
||||||
|
const uint64_t val = 0x1234567890abcdef;
|
||||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||||
const uint64_t expected = val;
|
const uint64_t expected = val;
|
||||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||||
const uint64_t expected = 0xefcdab9078563412;
|
const uint64_t expected = 0xefcdab9078563412;
|
||||||
#else
|
#else
|
||||||
#error "Unsupported byte order"
|
#error "Unsupported byte order"
|
||||||
#endif
|
#endif
|
||||||
ASSERT_EQ(expected, NetworkToHost64(val));
|
ASSERT_EQ(expected, NetworkToHost64(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -12,178 +12,200 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// Has classof
|
// Has classof
|
||||||
template <typename T, typename Base> struct HasClassofImpl {
|
template<typename T, typename Base>
|
||||||
template <
|
struct HasClassofImpl {
|
||||||
typename TT, typename TBase,
|
template<typename TT,
|
||||||
typename = enable_if_t<std::is_same<
|
typename TBase,
|
||||||
decltype(TT::classof(std::declval<const TBase &>())), bool>::value>>
|
typename = enable_if_t<std::is_same<decltype(TT::classof(std::declval<const TBase &>())), bool>::value>>
|
||||||
static std::true_type test(int);
|
static std::true_type test(int);
|
||||||
template <typename TT, typename TBase> static std::false_type test(...);
|
template<typename TT, typename TBase>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
static constexpr bool value = decltype(test<T, Base>(0))::value;
|
static constexpr bool value = decltype(test<T, Base>(0))::value;
|
||||||
};
|
};
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
template <typename T, typename = void> struct CastingTraits {
|
template<typename T, typename = void>
|
||||||
template <typename Base>
|
struct CastingTraits {
|
||||||
static auto RuntimeTypeCheck(const Base &val)
|
template<typename Base>
|
||||||
-> enable_if_t<detail::HasClassofImpl<T, Base>::value, bool> {
|
static auto RuntimeTypeCheck(const Base &val) -> enable_if_t<detail::HasClassofImpl<T, Base>::value, bool>
|
||||||
return T::classof(val);
|
{
|
||||||
}
|
return T::classof(val);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Base>
|
template<typename Base>
|
||||||
static auto RuntimeTypeCheck(const Base &val)
|
static auto RuntimeTypeCheck(const Base &val) -> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool>
|
||||||
-> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool> {
|
{
|
||||||
return dynamic_cast<const T *>(&val) != nullptr;
|
return dynamic_cast<const T *>(&val) != nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Castable {
|
class Castable {
|
||||||
class Dummy {};
|
class Dummy {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
friend void SetRuntimeType(Castable *ptr, int type, Dummy = {}) {
|
friend void SetRuntimeType(Castable *ptr, int type, Dummy = {}) { ptr->type_ = type; }
|
||||||
ptr->type_ = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend int GetRuntimeType(const Castable &val, Dummy = {}) {
|
friend int GetRuntimeType(const Castable &val, Dummy = {}) { return val.type_; }
|
||||||
return val.type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename, typename> friend struct CastingTraits;
|
template<typename, typename>
|
||||||
~Castable() = default;
|
friend struct CastingTraits;
|
||||||
|
~Castable() = default;
|
||||||
|
|
||||||
template <typename T> void SetRuntimeTypeTo() {
|
template<typename T>
|
||||||
SetRuntimeType(this, GetRuntimeTypeId<T>());
|
void SetRuntimeTypeTo()
|
||||||
}
|
{
|
||||||
|
SetRuntimeType(this, GetRuntimeTypeId<T>());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> int GetRuntimeType() const {
|
template<typename T>
|
||||||
return GetRuntimeTypeId<T>();
|
int GetRuntimeType() const
|
||||||
}
|
{
|
||||||
|
return GetRuntimeTypeId<T>();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> static int GetRuntimeTypeId() {
|
template<typename T>
|
||||||
static const int value = internal::IndexAlloc::For<Castable>()->Next();
|
static int GetRuntimeTypeId()
|
||||||
return value;
|
{
|
||||||
}
|
static const int value = internal::IndexAlloc::For<Castable>()->Next();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::int_fast32_t type_ = GetRuntimeTypeId<Castable>();
|
std::int_fast32_t type_ = GetRuntimeTypeId<Castable>();
|
||||||
};
|
};
|
||||||
|
|
||||||
// ExactMatchCastable
|
// ExactMatchCastable
|
||||||
class ExactMatchCastable : public Castable {
|
class ExactMatchCastable : public Castable {
|
||||||
protected:
|
protected:
|
||||||
~ExactMatchCastable() = default;
|
~ExactMatchCastable() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
struct CastingTraits<
|
struct CastingTraits<T, enable_if_t<std::is_base_of<ExactMatchCastable, T>::value>> {
|
||||||
T, enable_if_t<std::is_base_of<ExactMatchCastable, T>::value>> {
|
static bool RuntimeTypeCheck(const ExactMatchCastable &object)
|
||||||
static bool RuntimeTypeCheck(const ExactMatchCastable &object) {
|
{
|
||||||
return GetRuntimeType(object) == Castable::GetRuntimeTypeId<T>();
|
return GetRuntimeType(object) == Castable::GetRuntimeTypeId<T>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace casting {
|
namespace casting {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename T, typename Base,
|
template<typename T,
|
||||||
typename = enable_if_t<std::is_base_of<Base, T>::value &&
|
typename Base,
|
||||||
!std::is_same<Base, T>::value>>
|
typename = enable_if_t<std::is_base_of<Base, T>::value && !std::is_same<Base, T>::value>>
|
||||||
inline bool RuntimeTypeCheck(const Base &val) {
|
inline bool
|
||||||
return CastingTraits<T>::RuntimeTypeCheck(val);
|
RuntimeTypeCheck(const Base &val)
|
||||||
|
{
|
||||||
|
return CastingTraits<T>::RuntimeTypeCheck(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> inline bool RuntimeTypeCheck(const T &val) {
|
template<typename T>
|
||||||
return true;
|
inline bool
|
||||||
|
RuntimeTypeCheck(const T &val)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base>
|
template<typename T, typename Base>
|
||||||
using casted_type_t =
|
using casted_type_t = internal::conditional_t<std::is_const<Base>::value, const T *, T *>;
|
||||||
internal::conditional_t<std::is_const<Base>::value, const T *, T *>;
|
|
||||||
|
|
||||||
template <typename T, typename Base>
|
template<typename T, typename Base>
|
||||||
auto ReallyCastable(Base *ptr)
|
auto
|
||||||
-> enable_if_t<std::has_virtual_destructor<T>::value, bool> {
|
ReallyCastable(Base *ptr) -> enable_if_t<std::has_virtual_destructor<T>::value, bool>
|
||||||
return dynamic_cast<const T *>(ptr) != nullptr;
|
{
|
||||||
}
|
return dynamic_cast<const T *>(ptr) != nullptr;
|
||||||
template <typename T, typename Base>
|
|
||||||
auto ReallyCastable(Base *ptr)
|
|
||||||
-> enable_if_t<!std::has_virtual_destructor<T>::value, bool> {
|
|
||||||
return typeid(T) == typeid(*ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base> void InvalidCast(Base *ptr) {
|
template<typename T, typename Base>
|
||||||
if (ReallyCastable<T, Base>(ptr)) {
|
auto
|
||||||
TILE_LOG_FATAL(
|
ReallyCastable(Base *ptr) -> enable_if_t<!std::has_virtual_destructor<T>::value, bool>
|
||||||
"Casting to type [{}] failed. However, the C++ runtime reports that "
|
{
|
||||||
"you're indeed casting to the (right) runtime type of the object. This "
|
return typeid(T) == typeid(*ptr);
|
||||||
"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<T>());
|
|
||||||
} 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<T>(), GetTypeName(*ptr));
|
|
||||||
}
|
|
||||||
TILE_UNREACHABLE("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
template<typename T, typename Base>
|
||||||
} // namespace casting
|
void
|
||||||
|
InvalidCast(Base *ptr)
|
||||||
template <typename T, typename Base,
|
{
|
||||||
typename = enable_if_t<!std::is_pointer<Base>::value>>
|
if (ReallyCastable<T, Base>(ptr)) {
|
||||||
inline bool isa(const Base &val) {
|
TILE_LOG_FATAL("Casting to type [{}] failed. However, the C++ runtime reports that "
|
||||||
return casting::detail::RuntimeTypeCheck<T>(val);
|
"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<T>());
|
||||||
|
} 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<T>(), GetTypeName(*ptr));
|
||||||
|
}
|
||||||
|
TILE_UNREACHABLE("");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base> inline bool isa(const Base *val) {
|
}// namespace detail
|
||||||
return isa<T>(*val);
|
}// namespace casting
|
||||||
|
|
||||||
|
template<typename T, typename Base, typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||||
|
inline bool
|
||||||
|
isa(const Base &val)
|
||||||
|
{
|
||||||
|
return casting::detail::RuntimeTypeCheck<T>(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base,
|
template<typename T, typename Base>
|
||||||
typename R = casting::detail::casted_type_t<T, Base>>
|
inline bool
|
||||||
R dyn_cast(Base *ptr) {
|
isa(const Base *val)
|
||||||
return isa<T>(ptr) ? static_cast<R>(ptr) : nullptr;
|
{
|
||||||
|
return isa<T>(*val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base,
|
template<typename T, typename Base, typename R = casting::detail::casted_type_t<T, Base>>
|
||||||
typename = enable_if_t<!std::is_pointer<Base>::value>>
|
R
|
||||||
|
dyn_cast(Base *ptr)
|
||||||
|
{
|
||||||
|
return isa<T>(ptr) ? static_cast<R>(ptr) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename Base, typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||||
inline auto
|
inline auto
|
||||||
dyn_cast(Base &val) -> decltype(dyn_cast<T>(std::declval<Base *>())) {
|
dyn_cast(Base &val) -> decltype(dyn_cast<T>(std::declval<Base *>()))
|
||||||
return dyn_cast<T>(&val);
|
{
|
||||||
|
return dyn_cast<T>(&val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base>
|
template<typename T, typename Base>
|
||||||
inline auto dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast<T>(ptr)) {
|
inline auto
|
||||||
return ptr ? dyn_cast<T>(ptr) : nullptr;
|
dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast<T>(ptr))
|
||||||
|
{
|
||||||
|
return ptr ? dyn_cast<T>(ptr) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base,
|
template<typename T, typename Base, typename R = casting::detail::casted_type_t<T, Base>>
|
||||||
typename R = casting::detail::casted_type_t<T, Base>>
|
inline R
|
||||||
inline R cast(Base *ptr) {
|
cast(Base *ptr)
|
||||||
if (TILE_LIKELY(isa<T>(ptr))) {
|
{
|
||||||
return static_cast<R>(ptr);
|
if (TILE_LIKELY(isa<T>(ptr))) { return static_cast<R>(ptr); }
|
||||||
}
|
casting::detail::InvalidCast<T>(ptr);
|
||||||
casting::detail::InvalidCast<T>(ptr);
|
return nullptr;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base,
|
template<typename T, typename Base, typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||||
typename = enable_if_t<!std::is_pointer<Base>::value>>
|
inline auto
|
||||||
inline auto cast(Base &val) -> decltype(cast<T>(std::declval<Base *>())) {
|
cast(Base &val) -> decltype(cast<T>(std::declval<Base *>()))
|
||||||
return cast<T>(&val);
|
{
|
||||||
|
return cast<T>(&val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base>
|
template<typename T, typename Base>
|
||||||
inline auto cast_or_null(Base *ptr) -> decltype(cast<T>(ptr)) {
|
inline auto
|
||||||
return ptr ? cast<T>(ptr) : nullptr;
|
cast_or_null(Base *ptr) -> decltype(cast<T>(ptr))
|
||||||
|
{
|
||||||
|
return ptr ? cast<T>(ptr) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_CASTING_H
|
#endif// TILE_BASE_CASTING_H
|
||||||
|
@ -7,64 +7,63 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
struct Base {
|
struct Base {
|
||||||
virtual ~Base() = default;
|
virtual ~Base() = default;
|
||||||
enum { kA, kB, kC [[maybe_unused]] } type;
|
|
||||||
|
enum { kA, kB, kC [[maybe_unused]] } type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct A : Base {
|
struct A : Base {
|
||||||
A() { type = kA; }
|
A() { type = kA; }
|
||||||
|
|
||||||
static bool classof(const Base &val) {
|
static bool classof(const Base &val) { return val.type == kA || val.type == kB; }
|
||||||
return val.type == kA || val.type == kB;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct B : A {
|
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<B>();
|
auto pb = make_unique<B>();
|
||||||
Base *ptr = pb.get();
|
Base *ptr = pb.get();
|
||||||
volatile A *converted_ptr;
|
volatile A *converted_ptr;
|
||||||
|
|
||||||
struct C1 : ExactMatchCastable {};
|
struct C1 : ExactMatchCastable {};
|
||||||
|
|
||||||
struct C2 : C1 {
|
struct C2 : C1 {
|
||||||
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
|
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct C3 : C1 {
|
struct C3 : C1 {
|
||||||
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
|
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
auto pc2 = make_unique<C2>();
|
auto pc2 = make_unique<C2>();
|
||||||
C1 *pc1 = pc2.get();
|
C1 *pc1 = pc2.get();
|
||||||
volatile C3 *pc3;
|
volatile C3 *pc3;
|
||||||
|
|
||||||
void Benchmark_BuiltinDynamicCast(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_BuiltinDynamicCast(benchmark::State &state)
|
||||||
converted_ptr = dynamic_cast<A *>(ptr);
|
{
|
||||||
}
|
while (state.KeepRunning()) { converted_ptr = dynamic_cast<A *>(ptr); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_BuiltinDynamicCast);
|
BENCHMARK(Benchmark_BuiltinDynamicCast);
|
||||||
|
|
||||||
void Benchmark_DynCast(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_DynCast(benchmark::State &state)
|
||||||
converted_ptr = dyn_cast<A>(ptr);
|
{
|
||||||
}
|
while (state.KeepRunning()) { converted_ptr = dyn_cast<A>(ptr); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_DynCast);
|
BENCHMARK(Benchmark_DynCast);
|
||||||
|
|
||||||
void Benchmark_ExactMatchCastableDynCast(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_ExactMatchCastableDynCast(benchmark::State &state)
|
||||||
pc3 = dyn_cast<C3>(pc1);
|
{
|
||||||
}
|
while (state.KeepRunning()) { pc3 = dyn_cast<C3>(pc1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_ExactMatchCastableDynCast);
|
BENCHMARK(Benchmark_ExactMatchCastableDynCast);
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -9,95 +9,99 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
struct Base {
|
struct Base {
|
||||||
enum { kA, kB, kC } type;
|
enum { kA, kB, kC } type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct A : Base {
|
struct A : Base {
|
||||||
A() { type = kA; }
|
A() { type = kA; }
|
||||||
|
|
||||||
static bool classof(const Base &val) {
|
static bool classof(const Base &val) { return val.type == kA || val.type == kB; }
|
||||||
return val.type == kA || val.type == kB;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct B : A {
|
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 {
|
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 C1 : ExactMatchCastable {};
|
||||||
|
|
||||||
struct C2 : C1 {
|
struct C2 : C1 {
|
||||||
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
|
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct C3 : C1 {
|
struct C3 : C1 {
|
||||||
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
|
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(CastingDeathTest, InvalidCast) {
|
TEST(CastingDeathTest, InvalidCast)
|
||||||
auto pa = make_unique<A>();
|
{
|
||||||
ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast");
|
auto pa = make_unique<A>();
|
||||||
|
ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Casting, Nullptr) {
|
TEST(Casting, Nullptr)
|
||||||
B *pb = nullptr;
|
{
|
||||||
ASSERT_EQ(nullptr, dyn_cast_or_null<A>(pb));
|
B *pb = nullptr;
|
||||||
ASSERT_EQ(nullptr, cast_or_null<A>(pb));
|
ASSERT_EQ(nullptr, dyn_cast_or_null<A>(pb));
|
||||||
|
ASSERT_EQ(nullptr, cast_or_null<A>(pb));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Casting, DownCastFailure) {
|
TEST(Casting, DownCastFailure)
|
||||||
auto pa = make_unique<A>();
|
{
|
||||||
ASSERT_EQ(nullptr, dyn_cast<B>(pa.get()));
|
auto pa = make_unique<A>();
|
||||||
|
ASSERT_EQ(nullptr, dyn_cast<B>(pa.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Casting, Cast) {
|
TEST(Casting, Cast)
|
||||||
auto pb = make_unique<B>();
|
{
|
||||||
Base *ptr = pb.get();
|
auto pb = make_unique<B>();
|
||||||
|
Base *ptr = pb.get();
|
||||||
|
|
||||||
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
|
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
|
||||||
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
|
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
|
||||||
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
|
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
|
||||||
|
|
||||||
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
|
||||||
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
|
||||||
ASSERT_NE(nullptr, cast<A>(*ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<A>(*ptr));// Casting pointer.
|
||||||
ASSERT_NE(nullptr, cast<B>(*ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<B>(*ptr));// Casting pointer.
|
||||||
|
|
||||||
ASSERT_NE(nullptr, dyn_cast<B>(pb.get())); // Cast to self.
|
ASSERT_NE(nullptr, dyn_cast<B>(pb.get()));// Cast to self.
|
||||||
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
|
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Casting, CastWithConst) {
|
TEST(Casting, CastWithConst)
|
||||||
auto pb = make_unique<const B>();
|
{
|
||||||
const Base *ptr = pb.get();
|
auto pb = make_unique<const B>();
|
||||||
|
const Base *ptr = pb.get();
|
||||||
|
|
||||||
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
|
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
|
||||||
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
|
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
|
||||||
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
|
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
|
||||||
|
|
||||||
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
|
||||||
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
|
||||||
ASSERT_NE(nullptr, cast<A>(*ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<A>(*ptr));// Casting pointer.
|
||||||
ASSERT_NE(nullptr, cast<B>(*ptr)); // Casting pointer.
|
ASSERT_NE(nullptr, cast<B>(*ptr));// Casting pointer.
|
||||||
|
|
||||||
ASSERT_NE(nullptr, dyn_cast<B>(pb.get())); // Cast to self.
|
ASSERT_NE(nullptr, dyn_cast<B>(pb.get()));// Cast to self.
|
||||||
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
|
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Casting, ExactMatchCastable) {
|
TEST(Casting, ExactMatchCastable)
|
||||||
auto pc2 = make_unique<C2>();
|
{
|
||||||
C1 *p = pc2.get();
|
auto pc2 = make_unique<C2>();
|
||||||
ASSERT_NE(nullptr, dyn_cast<C1>(p)); // Success.
|
C1 *p = pc2.get();
|
||||||
ASSERT_NE(nullptr, dyn_cast<C2>(p)); // Success.
|
ASSERT_NE(nullptr, dyn_cast<C1>(p));// Success.
|
||||||
ASSERT_EQ(nullptr, dyn_cast<C3>(p)); // Failure.
|
ASSERT_NE(nullptr, dyn_cast<C2>(p));// Success.
|
||||||
|
ASSERT_EQ(nullptr, dyn_cast<C3>(p));// Failure.
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -15,132 +15,150 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <typename T> struct Epoch {};
|
template<typename T>
|
||||||
template <> struct Epoch<std::chrono::steady_clock> {
|
struct Epoch {};
|
||||||
static constexpr auto value =
|
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
template<>
|
||||||
std::chrono::seconds(0));
|
struct Epoch<std::chrono::steady_clock> {
|
||||||
|
static constexpr auto value = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(0));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct Epoch<std::chrono::system_clock> {
|
template<>
|
||||||
static constexpr auto value =
|
struct Epoch<std::chrono::system_clock> {
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
static constexpr auto value =
|
||||||
std::chrono::seconds(1716543273));
|
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1716543273));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Clock>
|
template<typename Clock>
|
||||||
inline typename Clock::time_point ReadClock(clockid_t type) {
|
inline typename Clock::time_point
|
||||||
timespec ts;
|
ReadClock(clockid_t type)
|
||||||
clock_gettime(type, &ts);
|
{
|
||||||
const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec;
|
timespec ts;
|
||||||
auto duration = std::chrono::duration_cast<typename Clock::duration>(
|
clock_gettime(type, &ts);
|
||||||
std::chrono::nanoseconds(ns));
|
const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec;
|
||||||
return typename Clock::time_point(duration);
|
auto duration = std::chrono::duration_cast<typename Clock::duration>(std::chrono::nanoseconds(ns));
|
||||||
|
return typename Clock::time_point(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sleep(long ns) {
|
void
|
||||||
struct timeval timeout;
|
Sleep(long ns)
|
||||||
timeout.tv_sec = 0;
|
{
|
||||||
timeout.tv_usec = (ns + 999) / 1000;
|
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 detail {
|
||||||
namespace chrono {
|
namespace chrono {
|
||||||
struct alignas(hardware_destructive_interference_size)
|
struct alignas(hardware_destructive_interference_size) AsynchronouslyUpdatedTimestamps {
|
||||||
AsynchronouslyUpdatedTimestamps {
|
std::atomic<std::chrono::nanoseconds> steady_clock_time_since_epoch;
|
||||||
std::atomic<std::chrono::nanoseconds> steady_clock_time_since_epoch;
|
std::atomic<std::chrono::nanoseconds> system_clock_time_since_epoch;
|
||||||
std::atomic<std::chrono::nanoseconds> system_clock_time_since_epoch;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static AsynchronouslyUpdatedTimestamps async_updated_timestamps;
|
static AsynchronouslyUpdatedTimestamps async_updated_timestamps;
|
||||||
|
|
||||||
void UpdateCoarseTimestamps() {
|
void
|
||||||
async_updated_timestamps.steady_clock_time_since_epoch.store(
|
UpdateCoarseTimestamps()
|
||||||
ReadSteadyClock().time_since_epoch(),
|
{
|
||||||
// std::chrono::steady_clock::now().time_since_epoch(),
|
async_updated_timestamps.steady_clock_time_since_epoch.store(
|
||||||
std::memory_order_relaxed);
|
ReadSteadyClock().time_since_epoch(),
|
||||||
async_updated_timestamps.system_clock_time_since_epoch.store(
|
// std::chrono::steady_clock::now().time_since_epoch(),
|
||||||
ReadSystemClock().time_since_epoch(),
|
std::memory_order_relaxed);
|
||||||
// std::chrono::system_clock::now().time_since_epoch(),
|
async_updated_timestamps.system_clock_time_since_epoch.store(
|
||||||
std::memory_order_relaxed);
|
ReadSystemClock().time_since_epoch(),
|
||||||
|
// std::chrono::system_clock::now().time_since_epoch(),
|
||||||
|
std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy;
|
constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy;
|
||||||
CoarseClockInitializer *CoarseClockInitializer::Instance() {
|
|
||||||
static NeverDestroyedSingleton<CoarseClockInitializer> cci;
|
CoarseClockInitializer *
|
||||||
return cci.Get();
|
CoarseClockInitializer::Instance()
|
||||||
|
{
|
||||||
|
static NeverDestroyedSingleton<CoarseClockInitializer> cci;
|
||||||
|
return cci.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoarseClockInitializer::Start() {
|
void
|
||||||
if (!internal::TestAndSet(running_, false, true)) {
|
CoarseClockInitializer::Start()
|
||||||
TILE_LOG_WARNING("CoarseClockInitializer is already running");
|
{
|
||||||
return;
|
if (!internal::TestAndSet(running_, false, true)) {
|
||||||
}
|
TILE_LOG_WARNING("CoarseClockInitializer is already running");
|
||||||
|
return;
|
||||||
UpdateCoarseTimestamps();
|
|
||||||
|
|
||||||
worker_ = make_unique<std::thread>([this] {
|
|
||||||
const auto accuracy_as_ns =
|
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(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);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
UpdateCoarseTimestamps();
|
||||||
|
|
||||||
|
worker_ = make_unique<std::thread>([this] {
|
||||||
|
const auto accuracy_as_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(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() {
|
void
|
||||||
running_.store(false, std::memory_order_relaxed);
|
CoarseClockInitializer::Stop()
|
||||||
|
{
|
||||||
|
running_.store(false, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoarseClockInitializer::Join() {
|
void
|
||||||
if (worker_) {
|
CoarseClockInitializer::Join()
|
||||||
worker_->join();
|
{
|
||||||
worker_.reset();
|
if (worker_) {
|
||||||
}
|
worker_->join();
|
||||||
|
worker_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoarseClockInitializer::CoarseClockInitializer() {}
|
CoarseClockInitializer::CoarseClockInitializer() {}
|
||||||
|
|
||||||
CoarseClockInitializer::~CoarseClockInitializer() {}
|
CoarseClockInitializer::~CoarseClockInitializer() {}
|
||||||
|
|
||||||
} // namespace chrono
|
}// namespace chrono
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point ReadSteadyClock() {
|
std::chrono::steady_clock::time_point
|
||||||
// return ReadClock<std::chrono::steady_clock>(CLOCK_MONOTONIC);
|
ReadSteadyClock()
|
||||||
return std::chrono::steady_clock::now();
|
{
|
||||||
}
|
// return ReadClock<std::chrono::steady_clock>(CLOCK_MONOTONIC);
|
||||||
std::chrono::system_clock::time_point ReadSystemClock() {
|
return std::chrono::steady_clock::now();
|
||||||
// return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME);
|
|
||||||
return std::chrono::system_clock::now();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point ReadCoarseSteadyClock() {
|
std::chrono::system_clock::time_point
|
||||||
auto coarse_duration =
|
ReadSystemClock()
|
||||||
detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch
|
{
|
||||||
.load(std::memory_order_relaxed);
|
// return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME);
|
||||||
auto target_duration =
|
return std::chrono::system_clock::now();
|
||||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
|
|
||||||
coarse_duration);
|
|
||||||
return std::chrono::steady_clock::time_point(target_duration);
|
|
||||||
}
|
}
|
||||||
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::steady_clock::time_point
|
||||||
std::chrono::duration_cast<std::chrono::system_clock::duration>(
|
ReadCoarseSteadyClock()
|
||||||
coarse_duration);
|
{
|
||||||
|
auto coarse_duration =
|
||||||
return std::chrono::system_clock::time_point(target_duration);
|
detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch.load(std::memory_order_relaxed);
|
||||||
|
auto target_duration = std::chrono::duration_cast<std::chrono::steady_clock::duration>(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<std::chrono::system_clock::duration>(coarse_duration);
|
||||||
|
|
||||||
|
return std::chrono::system_clock::time_point(target_duration);
|
||||||
|
}
|
||||||
|
}// namespace tile
|
||||||
|
@ -17,26 +17,26 @@ namespace chrono {
|
|||||||
|
|
||||||
class CoarseClockInitializer {
|
class CoarseClockInitializer {
|
||||||
public:
|
public:
|
||||||
static constexpr auto kAccuracy = std::chrono::microseconds(500);
|
static constexpr auto kAccuracy = std::chrono::microseconds(500);
|
||||||
static CoarseClockInitializer *Instance();
|
static CoarseClockInitializer *Instance();
|
||||||
|
|
||||||
// for `tile::Start()`
|
// for `tile::Start()`
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
void Join();
|
void Join();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CoarseClockInitializer();
|
~CoarseClockInitializer();
|
||||||
CoarseClockInitializer();
|
CoarseClockInitializer();
|
||||||
friend NeverDestroyedSingleton<CoarseClockInitializer>;
|
friend NeverDestroyedSingleton<CoarseClockInitializer>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<std::thread> worker_;
|
std::unique_ptr<std::thread> worker_;
|
||||||
std::atomic<bool> running_{false};
|
std::atomic<bool> running_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chrono
|
}// namespace chrono
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point ReadSteadyClock();
|
std::chrono::steady_clock::time_point ReadSteadyClock();
|
||||||
std::chrono::system_clock::time_point ReadSystemClock();
|
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();
|
std::chrono::steady_clock::time_point ReadCoarseSteadyClock();
|
||||||
// accuracy: 10us
|
// accuracy: 10us
|
||||||
std::chrono::system_clock::time_point ReadCoarseSystemClock();
|
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
|
||||||
|
@ -8,63 +8,67 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
void Benchmark_GetTimeOfDay(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_GetTimeOfDay(benchmark::State &state)
|
||||||
struct timeval tv;
|
{
|
||||||
gettimeofday(&tv, nullptr);
|
while (state.KeepRunning()) {
|
||||||
}
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_GetTimeOfDay);
|
BENCHMARK(Benchmark_GetTimeOfDay);
|
||||||
|
|
||||||
void Benchmark_StdSteadyClock(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_StdSteadyClock(benchmark::State &state)
|
||||||
(void)std::chrono::steady_clock::now();
|
{
|
||||||
}
|
while (state.KeepRunning()) { (void) std::chrono::steady_clock::now(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_StdSteadyClock);
|
BENCHMARK(Benchmark_StdSteadyClock);
|
||||||
|
|
||||||
void Benchmark_StdSystemClock(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_StdSystemClock(benchmark::State &state)
|
||||||
(void)std::chrono::system_clock::now();
|
{
|
||||||
}
|
while (state.KeepRunning()) { (void) std::chrono::system_clock::now(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_StdSystemClock);
|
BENCHMARK(Benchmark_StdSystemClock);
|
||||||
|
|
||||||
void Benchmark_ReadSteadyClock(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_ReadSteadyClock(benchmark::State &state)
|
||||||
ReadSteadyClock();
|
{
|
||||||
}
|
while (state.KeepRunning()) { ReadSteadyClock(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_ReadSteadyClock);
|
BENCHMARK(Benchmark_ReadSteadyClock);
|
||||||
|
|
||||||
void Benchmark_ReadSystemClock(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_ReadSystemClock(benchmark::State &state)
|
||||||
ReadSystemClock();
|
{
|
||||||
}
|
while (state.KeepRunning()) { ReadSystemClock(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_ReadSystemClock);
|
BENCHMARK(Benchmark_ReadSystemClock);
|
||||||
|
|
||||||
void Benchmark_ReadCoarseSteadyClock(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_ReadCoarseSteadyClock(benchmark::State &state)
|
||||||
// ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch`
|
{
|
||||||
// ... <+30>: mov (%rax),%rax
|
while (state.KeepRunning()) {
|
||||||
ReadCoarseSteadyClock();
|
// ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch`
|
||||||
}
|
// ... <+30>: mov (%rax),%rax
|
||||||
|
ReadCoarseSteadyClock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_ReadCoarseSteadyClock);
|
BENCHMARK(Benchmark_ReadCoarseSteadyClock);
|
||||||
|
|
||||||
void Benchmark_ReadCoarseSystemClock(benchmark::State &state) {
|
void
|
||||||
while (state.KeepRunning()) {
|
Benchmark_ReadCoarseSystemClock(benchmark::State &state)
|
||||||
ReadCoarseSystemClock();
|
{
|
||||||
}
|
while (state.KeepRunning()) { ReadCoarseSystemClock(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(Benchmark_ReadCoarseSystemClock);
|
BENCHMARK(Benchmark_ReadCoarseSystemClock);
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -7,42 +7,38 @@ namespace tile {
|
|||||||
static constexpr auto one_ms = std::chrono::milliseconds(1);
|
static constexpr auto one_ms = std::chrono::milliseconds(1);
|
||||||
static constexpr auto kTestN = 1000;
|
static constexpr auto kTestN = 1000;
|
||||||
|
|
||||||
long AvageTime(std::function<long()> f, std::size_t n = kTestN) {
|
long
|
||||||
long double total = 0;
|
AvageTime(std::function<long()> f, std::size_t n = kTestN)
|
||||||
for (std::size_t i = 0; i != n; ++i) {
|
{
|
||||||
total += 1.0f / n * f();
|
long double total = 0;
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
for (std::size_t i = 0; i != n; ++i) {
|
||||||
}
|
total += 1.0f / n * f();
|
||||||
return static_cast<long>(total);
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
|
return static_cast<long>(total);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SystemClock, Compare) {
|
TEST(SystemClock, Compare)
|
||||||
auto diff = AvageTime([] {
|
{
|
||||||
return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms;
|
auto diff = AvageTime([] { return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms; });
|
||||||
});
|
ASSERT_NEAR(diff, 0, 5);
|
||||||
ASSERT_NEAR(diff, 0, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SteadyClock, Compare) {
|
TEST(SteadyClock, Compare)
|
||||||
auto diff = AvageTime([] {
|
{
|
||||||
return (ReadSteadyClock() - std::chrono::steady_clock::now()) / one_ms;
|
auto diff = AvageTime([] { return (ReadSteadyClock() - std::chrono::steady_clock::now()) / one_ms; });
|
||||||
});
|
ASSERT_NEAR(diff, 0, 5);
|
||||||
ASSERT_NEAR(diff, 0, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CoarseSystemClock, Compare) {
|
TEST(CoarseSystemClock, Compare)
|
||||||
auto diff = AvageTime([] {
|
{
|
||||||
return (ReadCoarseSystemClock() - std::chrono::system_clock::now()) /
|
auto diff = AvageTime([] { return (ReadCoarseSystemClock() - std::chrono::system_clock::now()) / one_ms; });
|
||||||
one_ms;
|
ASSERT_NEAR(diff, 0, kTestN);
|
||||||
});
|
|
||||||
ASSERT_NEAR(diff, 0, kTestN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CoarseSteadyClock, Compare) {
|
TEST(CoarseSteadyClock, Compare)
|
||||||
auto diff = AvageTime([] {
|
{
|
||||||
return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) /
|
auto diff = AvageTime([] { return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) / one_ms; });
|
||||||
one_ms;
|
ASSERT_NEAR(diff, 0, kTestN);
|
||||||
});
|
|
||||||
ASSERT_NEAR(diff, 0, kTestN);
|
|
||||||
}
|
}
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -4,89 +4,96 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
std::unique_ptr<Decompressor> MakeDecompressor(Slice name) {
|
std::unique_ptr<Decompressor>
|
||||||
return decompressor_registry.TryNew(name);
|
MakeDecompressor(Slice name)
|
||||||
|
{
|
||||||
|
return decompressor_registry.TryNew(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Compressor> MakeCompressor(Slice name) {
|
std::unique_ptr<Compressor>
|
||||||
return compressor_registry.TryNew(name);
|
MakeCompressor(Slice name)
|
||||||
|
{
|
||||||
|
return compressor_registry.TryNew(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
|
std::optional<NoncontiguousBuffer>
|
||||||
const NoncontiguousBuffer &body) {
|
Compress(Compressor *compressor, const NoncontiguousBuffer &body)
|
||||||
NoncontiguousBufferBuilder builder;
|
{
|
||||||
if (!Compress(compressor, body, &builder)) {
|
NoncontiguousBufferBuilder builder;
|
||||||
return std::nullopt;
|
if (!Compress(compressor, body, &builder)) { return std::nullopt; }
|
||||||
}
|
return builder.DestructiveGet();
|
||||||
return builder.DestructiveGet();
|
|
||||||
}
|
|
||||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
|
|
||||||
Slice body) {
|
|
||||||
NoncontiguousBufferBuilder builder;
|
|
||||||
if (!Compress(compressor, body, &builder)) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return builder.DestructiveGet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb,
|
std::optional<NoncontiguousBuffer>
|
||||||
NoncontiguousBufferBuilder *builder) {
|
Compress(Compressor *compressor, Slice body)
|
||||||
if (!compressor) {
|
{
|
||||||
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
|
NoncontiguousBufferBuilder builder;
|
||||||
return false;
|
if (!Compress(compressor, body, &builder)) { return std::nullopt; }
|
||||||
}
|
return builder.DestructiveGet();
|
||||||
|
|
||||||
NoncontiguousBufferCompressionOutputStream out(builder);
|
|
||||||
return compressor->Compress(nb, &out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compress(Compressor *compressor, Slice body,
|
bool
|
||||||
NoncontiguousBufferBuilder *builder) {
|
Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder)
|
||||||
if (!compressor) {
|
{
|
||||||
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
|
if (!compressor) {
|
||||||
return false;
|
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
NoncontiguousBufferCompressionOutputStream out(builder);
|
NoncontiguousBufferCompressionOutputStream out(builder);
|
||||||
return compressor->Compress(body.data(), body.size(), &out);
|
return compressor->Compress(nb, &out);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
bool
|
||||||
const NoncontiguousBuffer &body) {
|
Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder)
|
||||||
NoncontiguousBufferBuilder builder;
|
{
|
||||||
if (!Decompress(decompressor, body, &builder)) {
|
if (!compressor) {
|
||||||
return std::nullopt;
|
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
|
||||||
}
|
return false;
|
||||||
return builder.DestructiveGet();
|
}
|
||||||
}
|
|
||||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
NoncontiguousBufferCompressionOutputStream out(builder);
|
||||||
Slice body) {
|
return compressor->Compress(body.data(), body.size(), &out);
|
||||||
NoncontiguousBufferBuilder builder;
|
|
||||||
if (!Decompress(decompressor, body, &builder)) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return builder.DestructiveGet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
|
std::optional<NoncontiguousBuffer>
|
||||||
NoncontiguousBufferBuilder *builder) {
|
Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body)
|
||||||
if (!decompressor) {
|
{
|
||||||
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
|
NoncontiguousBufferBuilder builder;
|
||||||
return false;
|
if (!Decompress(decompressor, body, &builder)) { return std::nullopt; }
|
||||||
}
|
return builder.DestructiveGet();
|
||||||
|
|
||||||
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
|
std::optional<NoncontiguousBuffer>
|
||||||
|
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
|
||||||
|
@ -11,26 +11,19 @@ namespace tile {
|
|||||||
std::unique_ptr<Decompressor> MakeDecompressor(Slice name);
|
std::unique_ptr<Decompressor> MakeDecompressor(Slice name);
|
||||||
std::unique_ptr<Compressor> MakeCompressor(Slice name);
|
std::unique_ptr<Compressor> MakeCompressor(Slice name);
|
||||||
|
|
||||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
|
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor, const NoncontiguousBuffer &nb);
|
||||||
const NoncontiguousBuffer &nb);
|
|
||||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor, Slice body);
|
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor, Slice body);
|
||||||
|
|
||||||
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb,
|
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder);
|
||||||
NoncontiguousBufferBuilder *builder);
|
|
||||||
|
|
||||||
bool Compress(Compressor *compressor, Slice body,
|
bool Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder);
|
||||||
NoncontiguousBufferBuilder *builder);
|
|
||||||
|
|
||||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body);
|
||||||
const NoncontiguousBuffer &body);
|
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor, Slice body);
|
||||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
|
||||||
Slice body);
|
|
||||||
|
|
||||||
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
|
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder);
|
||||||
NoncontiguousBufferBuilder *builder);
|
bool Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder);
|
||||||
bool Decompress(Decompressor *decompressor, Slice body,
|
|
||||||
NoncontiguousBufferBuilder *builder);
|
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_COMPRESSION_H
|
#endif// TILE_BASE_COMPRESSION_H
|
||||||
|
@ -4,25 +4,33 @@ namespace tile {
|
|||||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(compressor_registry, Compressor);
|
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(compressor_registry, Compressor);
|
||||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
|
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
|
||||||
|
|
||||||
TestCompressionOutputStream::TestCompressionOutputStream(std::string *s,
|
TestCompressionOutputStream::TestCompressionOutputStream(std::string *s, std::size_t every_size)
|
||||||
std::size_t every_size)
|
: buffer_(s),
|
||||||
: buffer_(s), every_size_(every_size) {}
|
every_size_(every_size)
|
||||||
|
{}
|
||||||
|
|
||||||
bool TestCompressionOutputStream::Next(void **data,
|
bool
|
||||||
std::size_t *size) noexcept {
|
TestCompressionOutputStream::Next(void **data, std::size_t *size) noexcept
|
||||||
if (buffer_->size() < using_bytes_ + every_size_) {
|
{
|
||||||
buffer_->resize(using_bytes_ + every_size_);
|
if (buffer_->size() < using_bytes_ + every_size_) { buffer_->resize(using_bytes_ + every_size_); }
|
||||||
}
|
|
||||||
|
|
||||||
*data = internal::RemoveConstPtr(buffer_->data()) + using_bytes_;
|
*data = internal::RemoveConstPtr(buffer_->data()) + using_bytes_;
|
||||||
*size = every_size_;
|
*size = every_size_;
|
||||||
|
|
||||||
using_bytes_ += every_size_;
|
using_bytes_ += every_size_;
|
||||||
return true;
|
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
|
||||||
|
@ -9,57 +9,50 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
class CompressionOutputStream {
|
class CompressionOutputStream {
|
||||||
public:
|
public:
|
||||||
virtual ~CompressionOutputStream() = default;
|
virtual ~CompressionOutputStream() = default;
|
||||||
// Get a buffer to write to. The buffer may be backed by multiple
|
// 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 bool Next(void **data, std::size_t *size) noexcept = 0;
|
||||||
virtual void BackUp(std::size_t count) noexcept = 0;
|
virtual void BackUp(std::size_t count) noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Compressor {
|
class Compressor {
|
||||||
public:
|
public:
|
||||||
virtual ~Compressor() = default;
|
virtual ~Compressor() = default;
|
||||||
virtual bool Compress(const void *src, std::size_t size,
|
virtual bool Compress(const void *src, std::size_t size, CompressionOutputStream *out) = 0;
|
||||||
CompressionOutputStream *out) = 0;
|
virtual bool Compress(const NoncontiguousBuffer &src, CompressionOutputStream *out) = 0;
|
||||||
virtual bool Compress(const NoncontiguousBuffer &src,
|
|
||||||
CompressionOutputStream *out) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Decompressor {
|
class Decompressor {
|
||||||
public:
|
public:
|
||||||
~Decompressor() = default;
|
~Decompressor() = default;
|
||||||
virtual bool Decompress(const void *src, std::size_t size,
|
virtual bool Decompress(const void *src, std::size_t size, CompressionOutputStream *out) = 0;
|
||||||
CompressionOutputStream *out) = 0;
|
virtual bool Decompress(const NoncontiguousBuffer &src, 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(compressor_registry, Compressor);
|
||||||
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
|
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#define TILE_COMPRESSION_REGISTER_COMPRESSOR(Name, Implementation) \
|
#define TILE_COMPRESSION_REGISTER_COMPRESSOR(Name, Implementation) \
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY(::tile::compressor_registry, Name, \
|
TILE_REGISTER_CLASS_DEPENDENCY(::tile::compressor_registry, Name, Implementation)
|
||||||
Implementation)
|
#define TILE_COMPRESSION_REGISTER_DECOMPRESSOR(Name, Implementation) \
|
||||||
#define TILE_COMPRESSION_REGISTER_DECOMPRESSOR(Name, Implementation) \
|
TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, Implementation)
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, \
|
|
||||||
Implementation)
|
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
class TestCompressionOutputStream : public CompressionOutputStream {
|
class TestCompressionOutputStream : public CompressionOutputStream {
|
||||||
public:
|
public:
|
||||||
explicit TestCompressionOutputStream(std::string *s,
|
explicit TestCompressionOutputStream(std::string *s, std::size_t every_size = 2);
|
||||||
std::size_t every_size = 2);
|
|
||||||
|
|
||||||
bool Next(void **data, std::size_t *size) noexcept override;
|
bool Next(void **data, std::size_t *size) noexcept override;
|
||||||
void BackUp(std::size_t count) noexcept override;
|
void BackUp(std::size_t count) noexcept override;
|
||||||
void Flush();
|
void Flush();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t using_bytes_{};
|
std::size_t using_bytes_{};
|
||||||
std::size_t every_size_;
|
std::size_t every_size_;
|
||||||
std::string *buffer_;
|
std::string *buffer_;
|
||||||
};
|
};
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_COMPRESSION_COMPRESSION_H
|
#endif// TILE_BASE_COMPRESSION_COMPRESSION_H
|
||||||
|
@ -11,24 +11,28 @@ TILE_COMPRESSION_REGISTER_DECOMPRESSOR("gzip", GzipDecompressor);
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct ZStream {
|
struct ZStream {
|
||||||
z_stream stream;
|
z_stream stream;
|
||||||
};
|
};
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
inline double EstimateCompressionRate(const z_stream *stream,
|
inline double
|
||||||
double default_value) {
|
EstimateCompressionRate(const z_stream *stream, double default_value)
|
||||||
if (stream->total_in > 0) {
|
{
|
||||||
double rate = 1.0f * stream->total_out / stream->total_in;
|
if (stream->total_in > 0) {
|
||||||
constexpr double kMinRate = 1.1;
|
double rate = 1.0f * stream->total_out / stream->total_in;
|
||||||
return rate * kMinRate;
|
constexpr double kMinRate = 1.1;
|
||||||
}
|
return rate * kMinRate;
|
||||||
|
}
|
||||||
|
|
||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t RestrictAvailSize(size_t size) {
|
inline uint32_t
|
||||||
return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX)));
|
RestrictAvailSize(size_t size)
|
||||||
|
{
|
||||||
|
return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// See document of inflateInit2 in zlib.h
|
// 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.
|
// he size to increase every time Z_BUF_ERROR returns.
|
||||||
constexpr int kOutBufferIncreaseSize = 32;
|
constexpr int kOutBufferIncreaseSize = 32;
|
||||||
|
|
||||||
bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
|
bool
|
||||||
const void *input_buffer, std::size_t input_size, bool is_deflate,
|
DoAppend(z_stream *stream,
|
||||||
bool finish) {
|
CompressionOutputStream *out_stream,
|
||||||
TILE_CHECK(stream && 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)) {
|
if (!finish && (input_buffer == nullptr || input_size == 0)) {
|
||||||
// no input data
|
// no input data
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->next_in = reinterpret_cast<Bytef *>(const_cast<void *>(input_buffer));
|
stream->next_in = reinterpret_cast<Bytef *>(const_cast<void *>(input_buffer));
|
||||||
int code = Z_OK;
|
int code = Z_OK;
|
||||||
// the number of `buffer too small error`
|
// the number of `buffer too small error`
|
||||||
int need_more_space_cnt = 0;
|
int need_more_space_cnt = 0;
|
||||||
std::string tmp_buffer;
|
std::string tmp_buffer;
|
||||||
size_t left_size = input_size;
|
size_t left_size = input_size;
|
||||||
while (left_size > 0 || need_more_space_cnt > 0 || finish) {
|
while (left_size > 0 || need_more_space_cnt > 0 || finish) {
|
||||||
stream->avail_in = RestrictAvailSize(left_size);
|
stream->avail_in = RestrictAvailSize(left_size);
|
||||||
const auto current_avail_in = stream->avail_in;
|
const auto current_avail_in = stream->avail_in;
|
||||||
std::size_t out_size;
|
std::size_t out_size;
|
||||||
if (!need_more_space_cnt) {
|
if (!need_more_space_cnt) {
|
||||||
void *out_data;
|
void *out_data;
|
||||||
if (!out_stream->Next(&out_data, &out_size)) {
|
if (!out_stream->Next(&out_data, &out_size)) {
|
||||||
// out buffer is full
|
// out buffer is full
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->next_out = reinterpret_cast<Bytef *>(out_data);
|
stream->next_out = reinterpret_cast<Bytef *>(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<Bytef *>(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<detail::ZStream>();
|
||||||
|
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 {
|
} else {
|
||||||
double rate = EstimateCompressionRate(stream, 0.5);
|
out_ = out;
|
||||||
tmp_buffer.resize(left_size * rate +
|
|
||||||
need_more_space_cnt * kOutBufferIncreaseSize);
|
|
||||||
stream->next_out = reinterpret_cast<Bytef *>(
|
|
||||||
internal::RemoveConstPtr(tmp_buffer.data()));
|
|
||||||
out_size = tmp_buffer.size();
|
|
||||||
}
|
}
|
||||||
|
return code == Z_OK;
|
||||||
|
}
|
||||||
|
|
||||||
stream->avail_out = RestrictAvailSize(out_size);
|
bool
|
||||||
const std::size_t current_avail_out = stream->avail_out;
|
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,
|
bool
|
||||||
"Avail_out should never be zero before the call.");
|
GzipDecompressor::Decompress(const void *src, std::size_t size, CompressionOutputStream *out)
|
||||||
int flush_option = finish ? Z_FINISH : Z_NO_FLUSH;
|
{
|
||||||
if (is_deflate) {
|
bool ok = Init(out);
|
||||||
code = deflate(stream, flush_option);
|
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<detail::ZStream>();
|
||||||
|
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 {
|
} else {
|
||||||
code = inflate(stream, flush_option);
|
out_ = out;
|
||||||
}
|
}
|
||||||
|
return code == Z_OK;
|
||||||
// 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) {
|
bool
|
||||||
TILE_CHECK(!out_);
|
GzipDecompressor::Release()
|
||||||
stream_ = make_unique<detail::ZStream>();
|
{
|
||||||
int code =
|
TILE_CHECK(stream_);
|
||||||
deflateInit2(&stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
int code = inflateEnd(&stream_->stream);
|
||||||
MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY);
|
if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); }
|
||||||
if (code != Z_OK) {
|
return 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::Decompress(const void *src, std::size_t size,
|
}// namespace compression
|
||||||
CompressionOutputStream *out) {
|
}// namespace tile
|
||||||
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<detail::ZStream>();
|
|
||||||
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
|
|
||||||
|
@ -5,48 +5,43 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
#include "tile/base/compression/compression.h"
|
#include "tile/base/compression/compression.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace compression {
|
namespace compression {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct ZStream;
|
struct ZStream;
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
class GzipCompressor : public Compressor {
|
class GzipCompressor : public Compressor {
|
||||||
public:
|
public:
|
||||||
bool Compress(const void *src, std::size_t size,
|
bool Compress(const void *src, std::size_t size, CompressionOutputStream *out) override;
|
||||||
CompressionOutputStream *out) override;
|
bool Compress(const NoncontiguousBuffer &bytes, CompressionOutputStream *out) override;
|
||||||
bool Compress(const NoncontiguousBuffer &bytes,
|
|
||||||
CompressionOutputStream *out) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Append(const void *buffer, std::size_t size);
|
bool Append(const void *buffer, std::size_t size);
|
||||||
bool Flush();
|
bool Flush();
|
||||||
bool Init(CompressionOutputStream *out);
|
bool Init(CompressionOutputStream *out);
|
||||||
bool Release();
|
bool Release();
|
||||||
std::unique_ptr<detail::ZStream> stream_;
|
std::unique_ptr<detail::ZStream> stream_;
|
||||||
CompressionOutputStream *out_ = nullptr;
|
CompressionOutputStream *out_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GzipDecompressor : public Decompressor {
|
class GzipDecompressor : public Decompressor {
|
||||||
public:
|
public:
|
||||||
bool Decompress(const void *src, std::size_t size,
|
bool Decompress(const void *src, std::size_t size, CompressionOutputStream *out) override;
|
||||||
CompressionOutputStream *out) override;
|
bool Decompress(const NoncontiguousBuffer &compressed, CompressionOutputStream *out) override;
|
||||||
bool Decompress(const NoncontiguousBuffer &compressed,
|
|
||||||
CompressionOutputStream *out) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Append(const void *buffer, std::size_t size);
|
bool Append(const void *buffer, std::size_t size);
|
||||||
bool Flush();
|
bool Flush();
|
||||||
bool Init(CompressionOutputStream *out);
|
bool Init(CompressionOutputStream *out);
|
||||||
bool Release();
|
bool Release();
|
||||||
std::unique_ptr<detail::ZStream> stream_;
|
std::unique_ptr<detail::ZStream> stream_;
|
||||||
CompressionOutputStream *out_ = nullptr;
|
CompressionOutputStream *out_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace compression
|
}// namespace compression
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_COMPRESSION_GZIP_H
|
#endif// TILE_BASE_COMPRESSION_GZIP_H
|
||||||
|
@ -1,37 +1,33 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace compression {
|
namespace compression {
|
||||||
bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
|
bool
|
||||||
const void *data, std::size_t size) {
|
CopyDataToCompressionOutputStream(CompressionOutputStream *out, const void *data, std::size_t size)
|
||||||
|
{
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t current_pos = 0;
|
std::size_t current_pos = 0;
|
||||||
std::size_t left_to_copy = size;
|
std::size_t left_to_copy = size;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
void *next_data;
|
void *next_data;
|
||||||
std::size_t next_size;
|
std::size_t next_size;
|
||||||
if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) {
|
if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) { return false; }
|
||||||
return false;
|
|
||||||
|
if (left_to_copy <= next_size) {
|
||||||
|
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, left_to_copy);
|
||||||
|
out->BackUp(next_size - left_to_copy);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, next_size);
|
||||||
|
current_pos += next_size;
|
||||||
|
left_to_copy -= next_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
TILE_UNREACHABLE("");
|
||||||
if (left_to_copy <= next_size) {
|
return false;
|
||||||
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos,
|
|
||||||
left_to_copy);
|
|
||||||
out->BackUp(next_size - left_to_copy);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos,
|
|
||||||
next_size);
|
|
||||||
current_pos += next_size;
|
|
||||||
left_to_copy -= next_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TILE_UNREACHABLE("");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} // namespace compression
|
}// namespace compression
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace compression {
|
namespace compression {
|
||||||
bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
|
bool CopyDataToCompressionOutputStream(CompressionOutputStream *out, const void *data, std::size_t size);
|
||||||
const void *data, std::size_t size);
|
|
||||||
}
|
}
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_COMPRESSION_UTIL_H
|
#endif// TILE_BASE_COMPRESSION_UTIL_H
|
||||||
|
@ -4,19 +4,20 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace compression {
|
namespace compression {
|
||||||
TEST(CopyDataToCompressionOutputStream, All) {
|
TEST(CopyDataToCompressionOutputStream, All)
|
||||||
std::string s;
|
{
|
||||||
std::string a = "123456789+";
|
std::string s;
|
||||||
|
std::string a = "123456789+";
|
||||||
|
|
||||||
TestCompressionOutputStream out1(&s, 2);
|
TestCompressionOutputStream out1(&s, 2);
|
||||||
CopyDataToCompressionOutputStream(&out1, a.data(), 1);
|
CopyDataToCompressionOutputStream(&out1, a.data(), 1);
|
||||||
CopyDataToCompressionOutputStream(&out1, a.data() + 1, 2);
|
CopyDataToCompressionOutputStream(&out1, a.data() + 1, 2);
|
||||||
CopyDataToCompressionOutputStream(&out1, a.data() + 3, 3);
|
CopyDataToCompressionOutputStream(&out1, a.data() + 3, 3);
|
||||||
CopyDataToCompressionOutputStream(&out1, a.data() + 6, 4);
|
CopyDataToCompressionOutputStream(&out1, a.data() + 6, 4);
|
||||||
out1.Flush();
|
out1.Flush();
|
||||||
|
|
||||||
ASSERT_EQ(s, a);
|
ASSERT_EQ(s, a);
|
||||||
}
|
}
|
||||||
} // namespace compression
|
}// namespace compression
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -11,62 +11,67 @@ static const char *algos[] = {
|
|||||||
"gzip",
|
"gzip",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
TEST(MakeCompressor, All) {
|
|
||||||
auto &&c = MakeCompressor("gzip");
|
TEST(MakeCompressor, All)
|
||||||
EXPECT_TRUE(c);
|
{
|
||||||
c = MakeCompressor("??");
|
auto &&c = MakeCompressor("gzip");
|
||||||
EXPECT_FALSE(c);
|
EXPECT_TRUE(c);
|
||||||
|
c = MakeCompressor("??");
|
||||||
|
EXPECT_FALSE(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MakeDecompressor, All) {
|
TEST(MakeDecompressor, All)
|
||||||
auto &&c = MakeDecompressor("gzip");
|
{
|
||||||
EXPECT_TRUE(c);
|
auto &&c = MakeDecompressor("gzip");
|
||||||
c = MakeDecompressor("??");
|
EXPECT_TRUE(c);
|
||||||
EXPECT_FALSE(c);
|
c = MakeDecompressor("??");
|
||||||
|
EXPECT_FALSE(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompressString, All) {
|
TEST(CompressString, All)
|
||||||
std::string original(1000, 'A');
|
{
|
||||||
auto c = Compress(MakeCompressor("gzip").get(), original);
|
std::string original(1000, 'A');
|
||||||
EXPECT_TRUE(c);
|
auto c = Compress(MakeCompressor("gzip").get(), original);
|
||||||
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
|
EXPECT_TRUE(c);
|
||||||
EXPECT_TRUE(d);
|
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
|
||||||
EXPECT_EQ(FlattenSlow(*d), original);
|
EXPECT_TRUE(d);
|
||||||
|
EXPECT_EQ(FlattenSlow(*d), original);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompressNoncontiguousBuffer, All) {
|
TEST(CompressNoncontiguousBuffer, All)
|
||||||
NoncontiguousBufferBuilder nbb;
|
{
|
||||||
std::string original(1000, 'A');
|
NoncontiguousBufferBuilder nbb;
|
||||||
nbb.Append(original.data(), original.size());
|
std::string original(1000, 'A');
|
||||||
auto &&nb = nbb.DestructiveGet();
|
nbb.Append(original.data(), original.size());
|
||||||
auto c = Compress(MakeCompressor("gzip").get(), nb);
|
auto &&nb = nbb.DestructiveGet();
|
||||||
EXPECT_TRUE(c);
|
auto c = Compress(MakeCompressor("gzip").get(), nb);
|
||||||
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
|
EXPECT_TRUE(c);
|
||||||
EXPECT_TRUE(d);
|
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
|
||||||
EXPECT_EQ(FlattenSlow(*d), original);
|
EXPECT_TRUE(d);
|
||||||
|
EXPECT_EQ(FlattenSlow(*d), original);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Decompressor, Empty) {
|
TEST(Decompressor, Empty)
|
||||||
for (auto &&algo : algos) {
|
{
|
||||||
auto res = Decompress(MakeDecompressor(algo).get(), "");
|
for (auto &&algo : algos) {
|
||||||
|
auto res = Decompress(MakeDecompressor(algo).get(), "");
|
||||||
|
|
||||||
// Failure in decompressing empty buffer is not an error. So long as
|
// Failure in decompressing empty buffer is not an error. So long as
|
||||||
// decompressing a buffer produced by `Compress(..., "")` works correctly,
|
// decompressing a buffer produced by `Compress(..., "")` works correctly,
|
||||||
// we're fine.
|
// we're fine.
|
||||||
if (res) {
|
if (res) {
|
||||||
// However, if the decompression does succeed, the resulting buffer should
|
// However, if the decompression does succeed, the resulting buffer should
|
||||||
// be empty.
|
// be empty.
|
||||||
EXPECT_TRUE(res->Empty());
|
EXPECT_TRUE(res->Empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Decompressor, Invalid) {
|
TEST(Decompressor, Invalid)
|
||||||
for (auto &&algo : algos) {
|
{
|
||||||
auto res =
|
for (auto &&algo : algos) {
|
||||||
Decompress(MakeDecompressor(algo).get(),
|
auto res = Decompress(MakeDecompressor(algo).get(), "this buffer is likely an invalid compressed buffer.");
|
||||||
"this buffer is likely an invalid compressed buffer.");
|
EXPECT_FALSE(res);
|
||||||
EXPECT_FALSE(res);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -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<tile::Mutex> 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<tile::Mutex> lock(mutex_);
|
|
||||||
return Enumerate(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
|
|
||||||
std::optional<type> Config::Get##name(const Slice &key) const { \
|
|
||||||
std::string value; \
|
|
||||||
if (GetRaw(key, &value)) { \
|
|
||||||
auto opt = TryParseTraits<type>::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<std::string> Config::GetString(const Slice &key) const {
|
|
||||||
std::string value;
|
|
||||||
UniqueLock<tile::Mutex> 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<tile::Mutex> lock(mutex_);
|
|
||||||
SetRaw(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (events_enabled_) {
|
|
||||||
OnChanged(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::~Config() {}
|
|
||||||
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
} // namespace tile
|
|
@ -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 <cstdint>
|
|
||||||
|
|
||||||
namespace tile {
|
|
||||||
namespace util {
|
|
||||||
class Config : public RefCounted<Config> {
|
|
||||||
public:
|
|
||||||
using Keys = std::vector<std::string>;
|
|
||||||
using Ptr = RefPtr<Config>;
|
|
||||||
|
|
||||||
// events
|
|
||||||
// Key, Value
|
|
||||||
sigslot::signal2<const Slice &, const Slice &> OnChanging;
|
|
||||||
// Key, Value
|
|
||||||
sigslot::signal2<const Slice &, const Slice &> OnChanged;
|
|
||||||
// Key
|
|
||||||
sigslot::signal1<const Slice &> OnRemoving;
|
|
||||||
// Key
|
|
||||||
sigslot::signal1<const Slice &> 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<type> 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<Config>;
|
|
||||||
friend class LayeredConfig;
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable Mutex mutex_;
|
|
||||||
bool events_enabled_{false};
|
|
||||||
};
|
|
||||||
} // namespace util
|
|
||||||
} // namespace tile
|
|
||||||
|
|
||||||
#endif // TILE_BASE_CONFIG_CONFIG_H
|
|
@ -2,18 +2,16 @@
|
|||||||
#define TILE_BASE_CONFIG_CONFIGURABLE_H
|
#define TILE_BASE_CONFIG_CONFIGURABLE_H
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "tile/base/string.h"
|
#include "tile/base/slice.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace util {
|
|
||||||
class Configurable {
|
class Configurable {
|
||||||
Configurable();
|
Configurable();
|
||||||
virtual ~Configurable() = default;
|
virtual ~Configurable() = default;
|
||||||
virtual void SetProperty(const Slice &name, const Slice &value) = 0;
|
virtual void SetProperty(const Slice &name, const Slice &value) = 0;
|
||||||
virtual std::string GetProperty(const Slice &name) const = 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
|
||||||
|
138
tile/base/config/configuration.cc
Normal file
138
tile/base/config/configuration.cc
Normal file
@ -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<tile::Mutex> lock(mutex_);
|
||||||
|
std::string value;
|
||||||
|
return GetRaw(key, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Configuration::Remove(const Slice &key)
|
||||||
|
{
|
||||||
|
UniqueLock<tile::Mutex> 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<tile::Mutex> lock(mutex_);
|
||||||
|
return Enumerate(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
|
||||||
|
std::optional<type> Configuration::Get##name(const Slice &key) const \
|
||||||
|
{ \
|
||||||
|
std::string value; \
|
||||||
|
if (GetRaw(key, &value)) { \
|
||||||
|
auto opt = TryParseTraits<type>::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<std::string>
|
||||||
|
Configuration::GetString(const Slice &key) const
|
||||||
|
{
|
||||||
|
std::string value;
|
||||||
|
UniqueLock<tile::Mutex> 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<tile::Mutex> lock(mutex_);
|
||||||
|
SetRaw(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events_enabled_) { OnChanged(key, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration::~Configuration() {}
|
||||||
|
|
||||||
|
}// namespace tile
|
122
tile/base/config/configuration.h
Normal file
122
tile/base/config/configuration.h
Normal file
@ -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 <cstdint>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
class Configuration : public RefCounted<Configuration> {
|
||||||
|
public:
|
||||||
|
using Keys = std::vector<std::string>;
|
||||||
|
using Ptr = RefPtr<Configuration>;
|
||||||
|
|
||||||
|
// events
|
||||||
|
// Key, Value
|
||||||
|
sigslot::signal2<const Slice &, const Slice &> OnChanging;
|
||||||
|
// Key, Value
|
||||||
|
sigslot::signal2<const Slice &, const Slice &> OnChanged;
|
||||||
|
// Key
|
||||||
|
sigslot::signal1<const Slice &> OnRemoving;
|
||||||
|
// Key
|
||||||
|
sigslot::signal1<const Slice &> 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<type> 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<Configuration>;
|
||||||
|
friend class LayeredConfiguration;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable Mutex mutex_;
|
||||||
|
bool events_enabled_{false};
|
||||||
|
};
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_CONFIG_CONFIGURATION_H
|
@ -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<Slice> 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<char>::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
|
|
@ -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 <map>
|
|
||||||
|
|
||||||
namespace tile {
|
|
||||||
namespace util {
|
|
||||||
class IniFileConfig : public Config {
|
|
||||||
public:
|
|
||||||
using Ptr = RefPtr<IniFileConfig>;
|
|
||||||
|
|
||||||
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<std::string, std::string, ICompare> IStringMap;
|
|
||||||
IStringMap map_;
|
|
||||||
std::string section_key_;
|
|
||||||
};
|
|
||||||
} // namespace util
|
|
||||||
} // namespace tile
|
|
||||||
|
|
||||||
#endif // TILE_BASE_CONFIG_INI_FILE_CONFIG_H
|
|
@ -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<IniFileConfig, Config>::value, "");
|
|
||||||
|
|
||||||
TEST(IniFileConfig, LoadFromIStream) {
|
|
||||||
std::stringstream ss(kIniFileConfig);
|
|
||||||
Config::Ptr config = MakeRefCounted<IniFileConfig>();
|
|
||||||
ASSERT_FALSE(config->Has("a"));
|
|
||||||
ASSERT_FALSE(config->Has("sec1.a"));
|
|
||||||
ASSERT_FALSE(config->Has("sec3.a"));
|
|
||||||
if (config.Is<IniFileConfig>()) {
|
|
||||||
IniFileConfig::Ptr ini = config.As<IniFileConfig>();
|
|
||||||
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
|
|
152
tile/base/config/ini_file_configuration.cc
Normal file
152
tile/base/config/ini_file_configuration.cc
Normal file
@ -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<Slice> 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<char>::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
|
40
tile/base/config/ini_file_configuration.h
Normal file
40
tile/base/config/ini_file_configuration.h
Normal file
@ -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 <map>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
class IniFileConfiguration : public Configuration {
|
||||||
|
public:
|
||||||
|
using Ptr = RefPtr<IniFileConfiguration>;
|
||||||
|
|
||||||
|
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<std::string, std::string, ICompare> IStringMap;
|
||||||
|
IStringMap map_;
|
||||||
|
std::string section_key_;
|
||||||
|
};
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_CONFIG_INI_FILE_CONFIG_H
|
45
tile/base/config/ini_file_configuration_test.cc
Normal file
45
tile/base/config/ini_file_configuration_test.cc
Normal file
@ -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<IniFileConfiguration, Configuration>::value, "");
|
||||||
|
|
||||||
|
TEST(IniFileConfig, LoadFromIStream)
|
||||||
|
{
|
||||||
|
std::stringstream ss(kIniFileConfig);
|
||||||
|
Configuration::Ptr config = MakeRefCounted<IniFileConfiguration>();
|
||||||
|
ASSERT_FALSE(config->Has("a"));
|
||||||
|
ASSERT_FALSE(config->Has("sec1.a"));
|
||||||
|
ASSERT_FALSE(config->Has("sec3.a"));
|
||||||
|
if (config.Is<IniFileConfiguration>()) {
|
||||||
|
IniFileConfiguration::Ptr ini = config.As<IniFileConfiguration>();
|
||||||
|
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
|
153
tile/base/config/json_configuration.cc
Normal file
153
tile/base/config/json_configuration.cc
Normal file
@ -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
|
50
tile/base/config/json_configuration.h
Normal file
50
tile/base/config/json_configuration.h
Normal file
@ -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();
|
||||||
|
~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<typename T>
|
||||||
|
bool SetValue(const Slice &key, T value);
|
||||||
|
Json::Value object_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
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
|
100
tile/base/config/json_configuration_test.cc
Normal file
100
tile/base/config/json_configuration_test.cc
Normal file
@ -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
|
@ -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<Slice> 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
|
|
@ -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 <list>
|
|
||||||
|
|
||||||
namespace tile {
|
|
||||||
namespace util {
|
|
||||||
class LayeredConfig : public Config {
|
|
||||||
public:
|
|
||||||
using Ptr = RefPtr<LayeredConfig>;
|
|
||||||
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<ConfigItem>;
|
|
||||||
ConfigList configs_;
|
|
||||||
};
|
|
||||||
} // namespace util
|
|
||||||
} // namespace tile
|
|
||||||
|
|
||||||
#endif // TILE_BASE_CONFIG_LAYERED_CONFIG_H
|
|
149
tile/base/config/layered_configuration.cc
Normal file
149
tile/base/config/layered_configuration.cc
Normal file
@ -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<Slice> 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
|
54
tile/base/config/layered_configuration.h
Normal file
54
tile/base/config/layered_configuration.h
Normal file
@ -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 <list>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
class LayeredConfiguration : public Configuration {
|
||||||
|
public:
|
||||||
|
using Ptr = RefPtr<LayeredConfiguration>;
|
||||||
|
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<ConfigItem>;
|
||||||
|
ConfigList configs_;
|
||||||
|
};
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_CONFIG_LAYERED_CONFIGURATION_H
|
219
tile/base/config/toml_configuration.cc
Normal file
219
tile/base/config/toml_configuration.cc
Normal file
@ -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
|
40
tile/base/config/toml_configuration.h
Normal file
40
tile/base/config/toml_configuration.h
Normal file
@ -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 <map>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
class TomlConfiguration : public Configuration {
|
||||||
|
public:
|
||||||
|
using Ptr = RefPtr<TomlConfiguration>;
|
||||||
|
|
||||||
|
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> impl_;
|
||||||
|
};
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_CONFIG_TOML_CONFIGURATION_H
|
71
tile/base/config/toml_configuration_test.cc
Normal file
71
tile/base/config/toml_configuration_test.cc
Normal file
@ -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
|
12
tile/base/configuration.h
Normal file
12
tile/base/configuration.h
Normal file
@ -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
|
@ -4,4 +4,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "tile/base/data/json.h"
|
#include "tile/base/data/json.h"
|
||||||
|
|
||||||
#endif // TILE_BASE_DATA_H
|
#endif// TILE_BASE_DATA_H
|
||||||
|
@ -11,28 +11,34 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJson(const T &value)
|
auto
|
||||||
-> enable_if_t<std::is_unsigned<T>::value, Json::Value> {
|
ToJson(const T &value) -> enable_if_t<std::is_unsigned<T>::value, Json::Value>
|
||||||
return static_cast<std::uint64_t>(value);
|
{
|
||||||
|
return static_cast<std::uint64_t>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJson(const T &value)
|
auto
|
||||||
-> enable_if_t<std::is_signed<T>::value, Json::Value> {
|
ToJson(const T &value) -> enable_if_t<std::is_signed<T>::value, Json::Value>
|
||||||
return static_cast<std::int64_t>(value);
|
{
|
||||||
}
|
return static_cast<std::int64_t>(value);
|
||||||
template <typename T>
|
|
||||||
auto ToJson(const T &value)
|
|
||||||
-> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value,
|
|
||||||
Json::Value> {
|
|
||||||
return Format("{}", value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> Json::Value ToJson(const std::atomic<T> &v) {
|
template<typename T>
|
||||||
return ToJson(v.load(std::memory_order_relaxed));
|
auto
|
||||||
|
ToJson(const T &value) -> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value, Json::Value>
|
||||||
|
{
|
||||||
|
return Format("{}", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
template<typename T>
|
||||||
|
Json::Value
|
||||||
|
ToJson(const std::atomic<T> &v)
|
||||||
|
{
|
||||||
|
return ToJson(v.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
#endif // TILE_BASE_DATA_JSON_H
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_DATA_JSON_H
|
||||||
|
@ -9,56 +9,57 @@ namespace tile {
|
|||||||
|
|
||||||
class ScopedDeferred {
|
class ScopedDeferred {
|
||||||
public:
|
public:
|
||||||
template <typename F>
|
template<typename F>
|
||||||
explicit ScopedDeferred(F &&f) : action_(std::move(f)) {}
|
explicit ScopedDeferred(F &&f) : action_(std::move(f))
|
||||||
|
{}
|
||||||
|
|
||||||
~ScopedDeferred() { action_(); }
|
~ScopedDeferred() { action_(); }
|
||||||
ScopedDeferred(const ScopedDeferred &) = delete;
|
|
||||||
ScopedDeferred &operator=(const ScopedDeferred &) = delete;
|
ScopedDeferred(const ScopedDeferred &) = delete;
|
||||||
|
ScopedDeferred &operator=(const ScopedDeferred &) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void()> action_;
|
std::function<void()> action_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Deferred {
|
class Deferred {
|
||||||
public:
|
public:
|
||||||
Deferred() = default;
|
Deferred() = default;
|
||||||
template <typename F>
|
|
||||||
explicit Deferred(F &&f) : action_(std::forward<F>(f)) {}
|
|
||||||
|
|
||||||
Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) {
|
template<typename F>
|
||||||
other.action_ = nullptr;
|
explicit Deferred(F &&f) : action_(std::forward<F>(f))
|
||||||
}
|
{}
|
||||||
Deferred &operator=(Deferred &&other) noexcept {
|
|
||||||
if (&other == this) {
|
Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) { other.action_ = nullptr; }
|
||||||
return *this;
|
|
||||||
|
Deferred &operator=(Deferred &&other) noexcept
|
||||||
|
{
|
||||||
|
if (&other == this) { return *this; }
|
||||||
|
|
||||||
|
Fire();
|
||||||
|
action_ = std::move(other.action_);
|
||||||
|
other.action_ = nullptr;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Fire();
|
~Deferred()
|
||||||
action_ = std::move(other.action_);
|
{
|
||||||
other.action_ = nullptr;
|
if (action_) { action_(); }
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
~Deferred() {
|
|
||||||
if (action_) {
|
|
||||||
action_();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const noexcept { return !!action_; }
|
explicit operator bool() const noexcept { return !!action_; }
|
||||||
|
|
||||||
void Fire() noexcept {
|
void Fire() noexcept
|
||||||
if (auto op = std::move(action_)) {
|
{
|
||||||
op();
|
if (auto op = std::move(action_)) { op(); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Dimiss() noexcept { action_ = nullptr; }
|
void Dimiss() noexcept { action_ = nullptr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void()> action_;
|
std::function<void()> action_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_DEFERED_H
|
#endif// _TILE_BASE_DEFERED_H
|
||||||
|
@ -2,49 +2,51 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
TEST(ScopedDefered, All) {
|
TEST(ScopedDefered, All)
|
||||||
bool f = false;
|
{
|
||||||
{
|
bool f = false;
|
||||||
ScopedDeferred scoped_defered([&] { f = true; });
|
{
|
||||||
ASSERT_FALSE(f);
|
ScopedDeferred scoped_defered([&] { f = true; });
|
||||||
}
|
ASSERT_FALSE(f);
|
||||||
ASSERT_TRUE(f);
|
}
|
||||||
|
ASSERT_TRUE(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Defered, All) {
|
TEST(Defered, All)
|
||||||
bool f1 = false;
|
{
|
||||||
bool f2 = false;
|
bool f1 = false;
|
||||||
{
|
bool f2 = false;
|
||||||
Deferred defer([&] { f1 = true; });
|
{
|
||||||
ASSERT_FALSE(f1);
|
Deferred defer([&] { f1 = true; });
|
||||||
Deferred defer2([&] { f2 = true; });
|
ASSERT_FALSE(f1);
|
||||||
defer2.Dimiss();
|
Deferred defer2([&] { f2 = true; });
|
||||||
|
defer2.Dimiss();
|
||||||
|
ASSERT_FALSE(f2);
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(f1);
|
||||||
ASSERT_FALSE(f2);
|
ASSERT_FALSE(f2);
|
||||||
}
|
|
||||||
ASSERT_TRUE(f1);
|
|
||||||
ASSERT_FALSE(f2);
|
|
||||||
|
|
||||||
bool f3 = false;
|
bool f3 = false;
|
||||||
Deferred defer3([&] { f3 = true; });
|
Deferred defer3([&] { f3 = true; });
|
||||||
ASSERT_FALSE(f3);
|
ASSERT_FALSE(f3);
|
||||||
defer3.Fire();
|
defer3.Fire();
|
||||||
ASSERT_TRUE(f3);
|
ASSERT_TRUE(f3);
|
||||||
|
|
||||||
bool f4 = false;
|
bool f4 = false;
|
||||||
Deferred defer5;
|
Deferred defer5;
|
||||||
{
|
{
|
||||||
Deferred defer4([&] { f4 = true; });
|
Deferred defer4([&] { f4 = true; });
|
||||||
|
ASSERT_FALSE(f4);
|
||||||
|
defer5 = std::move(defer4);
|
||||||
|
}
|
||||||
ASSERT_FALSE(f4);
|
ASSERT_FALSE(f4);
|
||||||
defer5 = std::move(defer4);
|
auto defer6 = std::move(defer5);
|
||||||
}
|
defer5.Fire();
|
||||||
ASSERT_FALSE(f4);
|
ASSERT_FALSE(defer5);
|
||||||
auto defer6 = std::move(defer5);
|
defer6.Fire();
|
||||||
defer5.Fire();
|
ASSERT_TRUE(f4);
|
||||||
ASSERT_FALSE(defer5);
|
|
||||||
defer6.Fire();
|
|
||||||
ASSERT_TRUE(f4);
|
|
||||||
|
|
||||||
Deferred().Fire();
|
Deferred().Fire();
|
||||||
Deferred().Dimiss();
|
Deferred().Dimiss();
|
||||||
}
|
}
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -10,21 +10,21 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
std::string Demangle(const char *s) {
|
std::string
|
||||||
|
Demangle(const char *s)
|
||||||
|
{
|
||||||
#ifdef _MSC_VER
|
#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 s;
|
||||||
}
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
return demangled;
|
int status;
|
||||||
|
char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status);
|
||||||
|
ScopedDeferred _([&] { free(demangled); });
|
||||||
|
if (!demangled) { return s; }
|
||||||
|
return demangled;
|
||||||
#else
|
#else
|
||||||
#error "Demangle not supported on current compiler."
|
#error "Demangle not supported on current compiler."
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -11,8 +11,11 @@ namespace tile {
|
|||||||
|
|
||||||
std::string Demangle(const char *s);
|
std::string Demangle(const char *s);
|
||||||
|
|
||||||
template <class T> std::string GetTypeName() {
|
template<class T>
|
||||||
return Demangle(typeid(T).name());
|
std::string
|
||||||
|
GetTypeName()
|
||||||
|
{
|
||||||
|
return Demangle(typeid(T).name());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __GNUC__ >= 12
|
#if __GNUC__ >= 12
|
||||||
@ -20,14 +23,17 @@ template <class T> std::string GetTypeName() {
|
|||||||
#pragma GCC diagnostic ignored "-Wnonnull-compare"
|
#pragma GCC diagnostic ignored "-Wnonnull-compare"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <class T> std::string GetTypeName(T &&o) {
|
template<class T>
|
||||||
return Demangle(typeid(std::forward<T>(o)).name());
|
std::string
|
||||||
|
GetTypeName(T &&o)
|
||||||
|
{
|
||||||
|
return Demangle(typeid(std::forward<T>(o)).name());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __GNUC__ >= 12
|
#if __GNUC__ >= 12
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_DEMANGLE_H
|
#endif// _TILE_BASE_DEMANGLE_H
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
struct C {
|
struct C {
|
||||||
struct D {
|
struct D {
|
||||||
struct E {};
|
struct E {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(Demangle, All) {
|
TEST(Demangle, All)
|
||||||
ASSERT_EQ("tile::C::D::E", GetTypeName<C::D::E>());
|
{
|
||||||
ASSERT_NE(GetTypeName<C::D>(), typeid(C::D::E).name());
|
ASSERT_EQ("tile::C::D::E", GetTypeName<C::D::E>());
|
||||||
ASSERT_EQ("invalid function name !@#$",
|
ASSERT_NE(GetTypeName<C::D>(), typeid(C::D::E).name());
|
||||||
Demangle("invalid function name !@#$"));
|
ASSERT_EQ("invalid function name !@#$", Demangle("invalid function name !@#$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -21,186 +21,174 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
// class dependency
|
// class dependency
|
||||||
#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
|
#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
|
||||||
extern ::tile::detail::dependency_registry::ClassRegistry< \
|
extern ::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__> &Registry
|
||||||
struct Registry, Interface, ##__VA_ARGS__> &Registry
|
|
||||||
|
|
||||||
#define TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
|
#define TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
|
||||||
::tile::detail::dependency_registry::ClassRegistry< \
|
::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__> &Registry = \
|
||||||
struct Registry, Interface, ##__VA_ARGS__> &Registry = \
|
**::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
||||||
**::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__>>>()
|
||||||
::tile::detail::dependency_registry::ClassRegistry< \
|
|
||||||
struct Registry, Interface, ##__VA_ARGS__>>>()
|
|
||||||
|
|
||||||
#define TILE_REGISTER_CLASS_DEPENDENCY(Registry, ImplementationName, \
|
#define TILE_REGISTER_CLASS_DEPENDENCY(Registry, ImplementationName, ImplementationClassName, ...) \
|
||||||
ImplementationClassName, ...) \
|
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY( \
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY( \
|
Registry, ImplementationName, \
|
||||||
Registry, ImplementationName, \
|
TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(ImplementationClassName, ##__VA_ARGS__))
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE( \
|
|
||||||
ImplementationClassName, ##__VA_ARGS__))
|
|
||||||
|
|
||||||
#define TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(Registry, Name, Factory) \
|
#define TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(Registry, Name, Factory) \
|
||||||
__attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \
|
__attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \
|
||||||
tile_reserved_registry_dependency_class_register_, __COUNTER__)() { \
|
tile_reserved_registry_dependency_class_register_, __COUNTER__)() \
|
||||||
auto registry = ::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
{ \
|
||||||
typename std::decay<decltype(Registry)>::type>>() \
|
auto registry = \
|
||||||
->Get(); \
|
::tile::internal::LazyInit<::tile::NeverDestroyed<typename std::decay<decltype(Registry)>::type>>() \
|
||||||
::tile::detail::dependency_registry::AddToRegistry(registry, Name, \
|
->Get(); \
|
||||||
Factory); \
|
::tile::detail::dependency_registry::AddToRegistry(registry, Name, Factory); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// object dependency
|
// object dependency
|
||||||
#define TILE_DECLARE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
|
#define TILE_DECLARE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
|
||||||
extern ::tile::detail::dependency_registry::ObjectRegistry< \
|
extern ::tile::detail::dependency_registry::ObjectRegistry<struct Registry, Interface> &Registry
|
||||||
struct Registry, Interface> &Registry
|
|
||||||
|
|
||||||
#define TILE_DEFINE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
|
#define TILE_DEFINE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
|
||||||
::tile::detail::dependency_registry::ObjectRegistry<struct Registry, \
|
::tile::detail::dependency_registry::ObjectRegistry<struct Registry, Interface> &Registry = \
|
||||||
Interface> &Registry = \
|
**::tile::internal::LazyInit< \
|
||||||
**::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
::tile::NeverDestroyed<::tile::detail::dependency_registry::ObjectRegistry<struct Registry, Interface>>>()
|
||||||
::tile::detail::dependency_registry::ObjectRegistry<struct Registry, \
|
|
||||||
Interface>>>()
|
|
||||||
|
|
||||||
#define TILE_REGISTER_OBJECT_DEPENDENCY(Registry, ObjectName, \
|
#define TILE_REGISTER_OBJECT_DEPENDENCY(Registry, ObjectName, PointerOrFactory) \
|
||||||
PointerOrFactory) \
|
__attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \
|
||||||
__attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \
|
tile_reserved_registry_dependency_object_register_, __COUNTER__)() \
|
||||||
tile_reserved_registry_dependency_object_register_, __COUNTER__)() { \
|
{ \
|
||||||
auto registry = ::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
auto registry = \
|
||||||
typename std::decay<decltype(Registry)>::type>>() \
|
::tile::internal::LazyInit<::tile::NeverDestroyed<typename std::decay<decltype(Registry)>::type>>() \
|
||||||
->Get(); \
|
->Get(); \
|
||||||
::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, \
|
::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, PointerOrFactory); \
|
||||||
PointerOrFactory); \
|
}
|
||||||
}
|
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
namespace dependency_registry {
|
namespace dependency_registry {
|
||||||
|
|
||||||
#define TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(Interface, ...) \
|
#define TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(Interface, ...) \
|
||||||
::tile::detail::dependency_registry::FactoryAux<Interface, ##__VA_ARGS__>
|
::tile::detail::dependency_registry::FactoryAux<Interface, ##__VA_ARGS__>
|
||||||
template <typename Interface, typename... Args>
|
|
||||||
std::unique_ptr<Interface> FactoryAux(Args &&...args) {
|
template<typename Interface, typename... Args>
|
||||||
return make_unique<Interface>(std::forward<Args>(args)...);
|
std::unique_ptr<Interface>
|
||||||
|
FactoryAux(Args &&...args)
|
||||||
|
{
|
||||||
|
return make_unique<Interface>(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
void AddToRegistry(T *registry, Args &&...args) {
|
void
|
||||||
registry->Register(std::forward<Args>(args)...);
|
AddToRegistry(T *registry, Args &&...args)
|
||||||
|
{
|
||||||
|
registry->Register(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Tag, typename Interface, typename... FactoryArgs>
|
template<typename Tag, typename Interface, typename... FactoryArgs>
|
||||||
class ClassRegistry {
|
class ClassRegistry {
|
||||||
using Factory = std::function<std::unique_ptr<Interface>(FactoryArgs...)>;
|
using Factory = std::function<std::unique_ptr<Interface>(FactoryArgs...)>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Factory TryGetFactory(Slice name) const noexcept {
|
Factory TryGetFactory(Slice name) const noexcept
|
||||||
auto iter = factories_.find(name.ToString());
|
{
|
||||||
if (iter == factories_.end()) {
|
auto iter = factories_.find(name.ToString());
|
||||||
return nullptr;
|
if (iter == factories_.end()) { return nullptr; }
|
||||||
|
|
||||||
|
auto constructor_ptr = iter->second.get();
|
||||||
|
return
|
||||||
|
[constructor_ptr](FactoryArgs... args) { return (*constructor_ptr)(std::forward<FactoryArgs>(args)...); };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto constructor_ptr = iter->second.get();
|
Factory GetFactory(const std::string &name) const noexcept
|
||||||
return [constructor_ptr](FactoryArgs... args) {
|
{
|
||||||
return (*constructor_ptr)(std::forward<FactoryArgs>(args)...);
|
auto result = TryGetFactory(name);
|
||||||
|
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Interface> 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<FactoryArgs>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Interface> New(const std::string &name, FactoryArgs... args) const
|
||||||
|
{
|
||||||
|
auto result = TryNew(name, std::forward<FactoryArgs>(args)...);
|
||||||
|
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
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<Factory>(std::move(factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Factory>> factories_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Tag, typename Interface>
|
||||||
|
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<Interface>());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
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<LazilyInstantiatedObject>();
|
||||||
|
std::call_once(e->flag, [&] { e->object = MaybeOwning<Interface>(non_owning, object); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Register(const std::string &name, std::function<MaybeOwning<Interface>()> initializer)
|
||||||
|
{
|
||||||
|
TILE_CHECK(objects_.find(name) == objects_.end(), "Double registration of object dependency [{}]", name);
|
||||||
|
|
||||||
|
objects_[name] = make_unique<LazilyInstantiatedObject>();
|
||||||
|
objects_[name]->initializer = std::move(initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LazilyInstantiatedObject {
|
||||||
|
std::once_flag flag;
|
||||||
|
MaybeOwning<Interface> object;
|
||||||
|
std::function<MaybeOwning<Interface>()> initializer;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Factory GetFactory(const std::string &name) const noexcept {
|
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>> objects_;
|
||||||
auto result = TryGetFactory(name);
|
|
||||||
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.",
|
|
||||||
name, GetTypeName<Interface>());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Interface> 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<FactoryArgs>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Interface> New(const std::string &name,
|
|
||||||
FactoryArgs... args) const {
|
|
||||||
auto result = TryNew(name, std::forward<FactoryArgs>(args)...);
|
|
||||||
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.",
|
|
||||||
name, GetTypeName<Interface>());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
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<Factory>(std::move(factory));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<Factory>> factories_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Tag, typename Interface> class ObjectRegistry {
|
}// namespace dependency_registry
|
||||||
public:
|
}// namespace detail
|
||||||
Interface *TryGet(Slice name) const {
|
}// namespace tile
|
||||||
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 {
|
#endif// TILE_BASE_DEPENDENCY_REGISTRY_H
|
||||||
auto result = TryGet(name);
|
|
||||||
TILE_CHECK(
|
|
||||||
result,
|
|
||||||
"Object dependency [{}] implementing interface [{}] is not found", name,
|
|
||||||
GetTypeName<Interface>());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
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<LazilyInstantiatedObject>();
|
|
||||||
std::call_once(e->flag, [&] {
|
|
||||||
e->object = MaybeOwning<Interface>(non_owning, object);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Register(const std::string &name,
|
|
||||||
std::function<MaybeOwning<Interface>()> initializer) {
|
|
||||||
TILE_CHECK(objects_.find(name) == objects_.end(),
|
|
||||||
"Double registration of object dependency [{}]", name);
|
|
||||||
|
|
||||||
objects_[name] = make_unique<LazilyInstantiatedObject>();
|
|
||||||
objects_[name]->initializer = std::move(initializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct LazilyInstantiatedObject {
|
|
||||||
std::once_flag flag;
|
|
||||||
MaybeOwning<Interface> object;
|
|
||||||
std::function<MaybeOwning<Interface>()> initializer;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>>
|
|
||||||
objects_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace dependency_registry
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace tile
|
|
||||||
|
|
||||||
#endif // TILE_BASE_DEPENDENCY_REGISTRY_H
|
|
||||||
|
@ -1,77 +1,84 @@
|
|||||||
#include "tile/base/dependency_registry.h"
|
#include "tile/base/dependency_registry.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
struct Destroyer {
|
struct Destroyer {
|
||||||
virtual ~Destroyer() = default;
|
virtual ~Destroyer() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FastDestroyer : Destroyer {
|
struct FastDestroyer : Destroyer {
|
||||||
FastDestroyer() { ++instances; }
|
FastDestroyer() { ++instances; }
|
||||||
~FastDestroyer() override { --instances; }
|
|
||||||
static int instances;
|
~FastDestroyer() override { --instances; }
|
||||||
|
|
||||||
|
static int instances;
|
||||||
};
|
};
|
||||||
|
|
||||||
int FastDestroyer::instances = 0;
|
int FastDestroyer::instances = 0;
|
||||||
|
|
||||||
struct GentleDestroyer : Destroyer {
|
struct GentleDestroyer : Destroyer {
|
||||||
GentleDestroyer() { ++instances; }
|
GentleDestroyer() { ++instances; }
|
||||||
~GentleDestroyer() override { --instances; }
|
|
||||||
static int instances;
|
~GentleDestroyer() override { --instances; }
|
||||||
|
|
||||||
|
static int instances;
|
||||||
};
|
};
|
||||||
|
|
||||||
int GentleDestroyer::instances = 0;
|
int GentleDestroyer::instances = 0;
|
||||||
|
|
||||||
struct SpeedDestroyer : Destroyer {
|
struct SpeedDestroyer : Destroyer {
|
||||||
explicit SpeedDestroyer(int) {}
|
explicit SpeedDestroyer(int) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpeedDestroyer2 : Destroyer {};
|
struct SpeedDestroyer2 : Destroyer {};
|
||||||
|
|
||||||
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
|
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
|
||||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
|
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer",
|
TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer", FastDestroyer);
|
||||||
FastDestroyer);
|
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(world_destroyer, "gentle-destroyer", [] {
|
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(world_destroyer, "gentle-destroyer", [] {
|
||||||
return make_unique<GentleDestroyer>();
|
return make_unique<GentleDestroyer>();
|
||||||
});
|
});
|
||||||
GentleDestroyer global_gentle_destroyer;
|
GentleDestroyer global_gentle_destroyer;
|
||||||
|
|
||||||
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int);
|
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int);
|
||||||
TILE_DEFINE_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",
|
TILE_REGISTER_CLASS_DEPENDENCY(with_arg_destroyer, "speed-destroyer", SpeedDestroyer, int);
|
||||||
SpeedDestroyer, int);
|
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2", [](int speed) {
|
||||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2",
|
return make_unique<SpeedDestroyer2>();
|
||||||
[](int speed) {
|
});
|
||||||
return make_unique<SpeedDestroyer2>();
|
|
||||||
});
|
|
||||||
|
|
||||||
TEST(DependencyRegistry, Class) {
|
TEST(DependencyRegistry, Class)
|
||||||
EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
|
{
|
||||||
EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
|
EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
|
||||||
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
|
EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
|
||||||
EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer"));
|
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
|
||||||
EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer"));
|
EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer"));
|
||||||
EXPECT_FALSE(world_destroyer.TryGetFactory("404-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(1, GentleDestroyer::instances);// The global one.
|
||||||
EXPECT_EQ(0, FastDestroyer::instances);
|
EXPECT_EQ(0, FastDestroyer::instances);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto gentle = world_destroyer.TryNew("gentle-destroyer");
|
auto gentle = world_destroyer.TryNew("gentle-destroyer");
|
||||||
EXPECT_EQ(2, GentleDestroyer::instances);
|
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);
|
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) {
|
TEST(DependencyRegistry, ClassWithArgs)
|
||||||
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer"));
|
{
|
||||||
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2"));
|
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer"));
|
||||||
EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3"));
|
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2"));
|
||||||
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456));
|
EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3"));
|
||||||
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer-2", 456));
|
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456));
|
||||||
EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 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
|
||||||
|
@ -8,18 +8,22 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
template <typename To, typename From,
|
template<typename To,
|
||||||
typename R = typename std::conditional<std::is_const<From>::value,
|
typename From,
|
||||||
const To *, To *>::type>
|
typename R = typename std::conditional<std::is_const<From>::value, const To *, To *>::type>
|
||||||
inline R down_cast(From *ptr) {
|
inline R
|
||||||
TILE_DCHECK(dynamic_cast<R>(ptr));
|
down_cast(From *ptr)
|
||||||
return static_cast<R>(ptr);
|
{
|
||||||
|
TILE_DCHECK(dynamic_cast<R>(ptr));
|
||||||
|
return static_cast<R>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename To, typename From>
|
template<typename To, typename From>
|
||||||
inline auto down_cast(From &ref) -> decltype(down_cast(&ref)) {
|
inline auto
|
||||||
return down_cast<To>(&ref);
|
down_cast(From &ref) -> decltype(down_cast(&ref))
|
||||||
|
{
|
||||||
|
return down_cast<To>(&ref);
|
||||||
}
|
}
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_DOWN_CAST_H
|
#endif// _TILE_BASE_DOWN_CAST_H
|
||||||
|
@ -3,22 +3,25 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
struct A {
|
struct A {
|
||||||
virtual ~A() = default;
|
virtual ~A() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct B : A {};
|
struct B : A {};
|
||||||
|
|
||||||
TEST(DownCast, All) {
|
TEST(DownCast, All)
|
||||||
B b;
|
{
|
||||||
A *ptr = &b;
|
B b;
|
||||||
ASSERT_NE(nullptr, down_cast<B>(ptr));
|
A *ptr = &b;
|
||||||
ASSERT_NE(nullptr, down_cast<B>(const_cast<const A *>(ptr)));
|
ASSERT_NE(nullptr, down_cast<B>(ptr));
|
||||||
|
ASSERT_NE(nullptr, down_cast<B>(const_cast<const A *>(ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
TEST(CastDeathTest, DownCast) {
|
TEST(CastDeathTest, DownCast)
|
||||||
A a;
|
{
|
||||||
A *ptr = &a;
|
A a;
|
||||||
ASSERT_DEATH(down_cast<B>(ptr), "");
|
A *ptr = &a;
|
||||||
|
ASSERT_DEATH(down_cast<B>(ptr), "");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -7,4 +7,4 @@
|
|||||||
#include "tile/base/encoding/hex.h"
|
#include "tile/base/encoding/hex.h"
|
||||||
#include "tile/base/encoding/percent.h"
|
#include "tile/base/encoding/percent.h"
|
||||||
|
|
||||||
#endif // _TILE_BASE_ENCODING_H
|
#endif// _TILE_BASE_ENCODING_H
|
||||||
|
@ -19,137 +19,132 @@ static constexpr std::size_t kBufferSize = BLOCK_SIZE_1M;
|
|||||||
#undef BLOCK_SIZE_64K
|
#undef BLOCK_SIZE_64K
|
||||||
#undef BLOCK_SIZE_1M
|
#undef BLOCK_SIZE_1M
|
||||||
|
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
std::string EncodeBase64(Slice from) {
|
std::string
|
||||||
std::string result;
|
EncodeBase64(Slice from)
|
||||||
EncodeBase64(from, &result);
|
{
|
||||||
return result;
|
std::string result;
|
||||||
}
|
EncodeBase64(from, &result);
|
||||||
std::optional<std::string> DecodeBase64(Slice from) {
|
return result;
|
||||||
std::string result;
|
|
||||||
if (!DecodeBase64(from, &result)) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodeBase64(Slice from, std::string *to) {
|
std::optional<std::string>
|
||||||
TILE_CHECK(from.size() < std::numeric_limits<int>::max(),
|
DecodeBase64(Slice from)
|
||||||
"Not implemented: Source bytes too long. {} > max_size({})",
|
{
|
||||||
from.size(), std::numeric_limits<int>::max());
|
std::string result;
|
||||||
|
if (!DecodeBase64(from, &result)) { return std::nullopt; }
|
||||||
const auto size = 4 * ((from.size() + 2) / 3);
|
return result;
|
||||||
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<const char *>(from.data()), input_len,
|
|
||||||
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
|
|
||||||
&state);
|
|
||||||
from.RemovePrefix(input_len);
|
|
||||||
};
|
|
||||||
base64_encode_blockend(
|
|
||||||
reinterpret_cast<char *>(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.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecodeBase64(Slice from, std::string *to) {
|
void
|
||||||
if (TILE_UNLIKELY(from.empty())) {
|
EncodeBase64(Slice from, std::string *to)
|
||||||
to->clear();
|
{
|
||||||
return true;
|
TILE_CHECK(from.size() < std::numeric_limits<int>::max(),
|
||||||
}
|
"Not implemented: Source bytes too long. {} > max_size({})", from.size(),
|
||||||
|
std::numeric_limits<int>::max());
|
||||||
|
|
||||||
std::string safe_from_str;
|
const auto size = 4 * ((from.size() + 2) / 3);
|
||||||
// auto padding
|
to->resize(size);
|
||||||
auto padding = (4 - from.size() % 4) % 4;
|
base64_encodestate state;
|
||||||
if (padding > 2) {
|
base64_init_encodestate(&state);
|
||||||
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;
|
std::size_t used = 0;
|
||||||
while (!input.empty()) {
|
std::size_t input_len;
|
||||||
auto input_len = std::min(kBufferSize, from.size());
|
while (!from.empty()) {
|
||||||
auto encoded_len = base64_decode_block(
|
input_len = std::min(kBufferSize, from.size());
|
||||||
input.data(), input_len,
|
used += base64_encode_block(reinterpret_cast<const char *>(from.data()), input_len,
|
||||||
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
|
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
|
||||||
&state);
|
from.RemovePrefix(input_len);
|
||||||
if (TILE_UNLIKELY(encoded_len == 0)) {
|
};
|
||||||
return false;
|
base64_encode_blockend(reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
|
||||||
}
|
|
||||||
used += encoded_len;
|
|
||||||
input.RemovePrefix(input_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Sample
|
// bool success =
|
||||||
// {
|
// Base64::Encode(from.data(), from.size(),
|
||||||
// bool success =
|
// internal::RemoveConstPtr(to->data()), to->size());
|
||||||
// Base64::Decode(from.data(), from.size(),
|
// TILE_CHECK(success, "Unexpected: Failed to do base64-encode.");
|
||||||
// internal::RemoveConstPtr(to->data()), to->size());
|
|
||||||
// if (!success) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// === OpenSSL
|
|
||||||
// int rc;
|
|
||||||
// rc = EVP_DecodeBlock(
|
|
||||||
// reinterpret_cast<unsigned char
|
|
||||||
// *>(internal::RemoveConstPtr(to->data())), reinterpret_cast<const
|
|
||||||
// unsigned char *>(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
|
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<char *>(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<unsigned char
|
||||||
|
// *>(internal::RemoveConstPtr(to->data())), reinterpret_cast<const
|
||||||
|
// unsigned char *>(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
|
||||||
|
@ -13,6 +13,6 @@ std::optional<std::string> DecodeBase64(Slice from);
|
|||||||
|
|
||||||
void EncodeBase64(Slice from, std::string *to);
|
void EncodeBase64(Slice from, std::string *to);
|
||||||
bool DecodeBase64(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
|
||||||
|
@ -9,70 +9,75 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
static constexpr auto kText = ".<>@????????";
|
static constexpr auto kText = ".<>@????????";
|
||||||
static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/";
|
static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/";
|
||||||
static constexpr auto kText2 = ".<>@???????";
|
static constexpr auto kText2 = ".<>@???????";
|
||||||
static constexpr auto kBase64Text2 = "Ljw+QD8/Pz8/Pz8=";
|
static constexpr auto kBase64Text2 = "Ljw+QD8/Pz8/Pz8=";
|
||||||
|
|
||||||
TEST(Base64, Default) {
|
TEST(Base64, Default)
|
||||||
EXPECT_EQ(kBase64Text, EncodeBase64(kText));
|
{
|
||||||
auto decoded = DecodeBase64(kBase64Text);
|
EXPECT_EQ(kBase64Text, EncodeBase64(kText));
|
||||||
ASSERT_TRUE(decoded);
|
auto decoded = DecodeBase64(kBase64Text);
|
||||||
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==");
|
|
||||||
ASSERT_TRUE(decoded);
|
ASSERT_TRUE(decoded);
|
||||||
EXPECT_EQ(kChar, *decoded);
|
EXPECT_EQ(kText, *decoded);
|
||||||
}
|
EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!"));
|
||||||
|
|
||||||
// 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) {
|
TEST(Base64, Padding)
|
||||||
std::string random_str;
|
{
|
||||||
std::string encoded;
|
EXPECT_EQ(kBase64Text2, EncodeBase64(kText2));
|
||||||
std::string decoded;
|
|
||||||
for (int i = 0; i != 1000; ++i) {
|
auto decoded = DecodeBase64(kBase64Text2);
|
||||||
testing::RandomString(&random_str, 10000, 10000);
|
ASSERT_TRUE(decoded);
|
||||||
EncodeBase64(random_str, &encoded);
|
EXPECT_EQ(kText2, *decoded);
|
||||||
ASSERT_TRUE(DecodeBase64(encoded, &decoded));
|
|
||||||
EXPECT_EQ(random_str, 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
|
||||||
|
@ -3,286 +3,247 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
const char kBase64Alphabet[] =
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"0123456789+/";
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static inline unsigned char b64_lookup_aux(unsigned char c) {
|
static inline unsigned char
|
||||||
if (c >= 'A' && c <= 'Z')
|
b64_lookup_aux(unsigned char c)
|
||||||
return c - 'A';
|
{
|
||||||
if (c >= 'a' && c <= 'z')
|
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||||
return c - 71;
|
if (c >= 'a' && c <= 'z') return c - 71;
|
||||||
if (c >= '0' && c <= '9')
|
if (c >= '0' && c <= '9') return c + 4;
|
||||||
return c + 4;
|
if (c == '+') return 62;
|
||||||
if (c == '+')
|
if (c == '/') return 63;
|
||||||
return 62;
|
return 255;
|
||||||
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 inline unsigned char
|
||||||
static Base64LookupInitializer init;
|
b64_lookup_aux1(unsigned char c)
|
||||||
return init.kBase64Lookup[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 {
|
class Base64 {
|
||||||
public:
|
public:
|
||||||
static bool Encode(const std::string &in, std::string *out) {
|
static bool Encode(const std::string &in, std::string *out)
|
||||||
int i = 0, j = 0;
|
{
|
||||||
size_t enc_len = 0;
|
int i = 0, j = 0;
|
||||||
unsigned char a3[3];
|
size_t enc_len = 0;
|
||||||
unsigned char a4[4];
|
unsigned char a3[3];
|
||||||
|
unsigned char a4[4];
|
||||||
|
|
||||||
out->resize(EncodedLength(in));
|
out->resize(EncodedLength(in));
|
||||||
|
|
||||||
size_t input_len = in.size();
|
size_t input_len = in.size();
|
||||||
std::string::const_iterator input = in.begin();
|
std::string::const_iterator input = in.begin();
|
||||||
|
|
||||||
while (input_len--) {
|
while (input_len--) {
|
||||||
a3[i++] = *(input++);
|
a3[i++] = *(input++);
|
||||||
if (i == 3) {
|
if (i == 3) {
|
||||||
a3_to_a4(a4, a3);
|
a3_to_a4(a4, a3);
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) { (*out)[enc_len++] = kBase64Alphabet[a4[i]]; }
|
||||||
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
if (i) {
|
||||||
}
|
for (j = i; j < 3; j++) { a3[j] = '\0'; }
|
||||||
}
|
|
||||||
|
|
||||||
if (i) {
|
a3_to_a4(a4, a3);
|
||||||
for (j = i; j < 3; j++) {
|
|
||||||
a3[j] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
while ((i++ < 3)) { (*out)[enc_len++] = '='; }
|
||||||
(*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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a4_to_a3(a3, a4);
|
return (enc_len == out->size());
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
static bool Encode(const char *input, size_t input_length, char *out, size_t out_length)
|
||||||
(*out)[dec_len++] = a3[i];
|
{
|
||||||
|
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) {
|
a3_to_a4(a4, a3);
|
||||||
for (j = i; j < 4; j++) {
|
|
||||||
a4[j] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 4; j++) {
|
for (j = 0; j < i + 1; j++) { *out++ = kBase64Alphabet[a4[j]]; }
|
||||||
a4[j] = b64_lookup(a4[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
a4_to_a3(a3, a4);
|
while ((i++ < 3)) { *out++ = '='; }
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a4_to_a3(a3, a4);
|
return (out == (out_begin + encoded_length));
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
static bool Decode(const std::string &in, std::string *out)
|
||||||
*out++ = a3[i];
|
{
|
||||||
|
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) {
|
static bool Decode(const char *input, size_t input_length, char *out, size_t out_length)
|
||||||
for (j = i; j < 4; j++) {
|
{
|
||||||
a4[j] = '\0';
|
int i = 0, j = 0;
|
||||||
}
|
char *out_begin = out;
|
||||||
|
unsigned char a3[3];
|
||||||
|
unsigned char a4[4];
|
||||||
|
|
||||||
for (j = 0; j < 4; j++) {
|
size_t decoded_length = DecodedLength(input, input_length);
|
||||||
a4[j] = b64_lookup(a4[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
a4_to_a3(a3, a4);
|
if (out_length < decoded_length) return false;
|
||||||
|
|
||||||
for (j = 0; j < i - 1; j++) {
|
while (input_length--) {
|
||||||
*out++ = a3[j];
|
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) {
|
const char *in_end = in + in_length;
|
||||||
int numEq = 0;
|
while (*--in_end == '=') ++numEq;
|
||||||
|
|
||||||
const char *in_end = in + in_length;
|
return ((6 * in_length) / 8) - numEq;
|
||||||
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 * 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) {
|
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; }
|
||||||
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static size_t EncodedLength(const std::string &in) {
|
return ((6 * n) / 8) - numEq;
|
||||||
return EncodedLength(in.length());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inline static void StripPadding(std::string *in) {
|
inline static size_t EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; }
|
||||||
while (!in->empty() && *(in->rbegin()) == '=')
|
|
||||||
in->resize(in->size() - 1);
|
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:
|
private:
|
||||||
static inline void a3_to_a4(unsigned char *a4, unsigned char *a3) {
|
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[0] = (a3[0] & 0xfc) >> 2;
|
||||||
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
|
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
|
||||||
a4[3] = (a3[2] & 0x3f);
|
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) {
|
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[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
|
||||||
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
|
||||||
}
|
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BASE64_H
|
#endif// BASE64_H
|
||||||
|
@ -6,80 +6,88 @@ namespace detail {
|
|||||||
namespace {
|
namespace {
|
||||||
static constexpr auto kCharMin = std::numeric_limits<char>::min();
|
static constexpr auto kCharMin = std::numeric_limits<char>::min();
|
||||||
static constexpr auto kCharMax = std::numeric_limits<char>::max();
|
static constexpr auto kCharMax = std::numeric_limits<char>::max();
|
||||||
static constexpr auto kSize = kCharMax - kCharMin + 1;
|
static constexpr auto kSize = kCharMax - kCharMin + 1;
|
||||||
|
|
||||||
int AsciiCodeFromCharPairSlow(char a, char b) {
|
int
|
||||||
a = ToLower(a);
|
AsciiCodeFromCharPairSlow(char a, char b)
|
||||||
b = ToLower(b);
|
{
|
||||||
auto ToNum = [](char x) {
|
a = ToLower(a);
|
||||||
if (x >= '0' && x <= '9') {
|
b = ToLower(b);
|
||||||
return x - '0';
|
auto ToNum = [](char x) {
|
||||||
} else if (x >= 'a' && x <= 'f') {
|
if (x >= '0' && x <= '9') {
|
||||||
return x - 'a' + 10;
|
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<char, char>
|
||||||
|
AsciiCodeToCharPairSlow(std::uint8_t value, bool uppercase)
|
||||||
|
{
|
||||||
|
if (uppercase) {
|
||||||
|
return std::make_pair(kHexCharsUpper[value >> 4], kHexCharsUpper[value & 0x0f]);
|
||||||
} else {
|
} 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<char, char> AsciiCodeToCharPairSlow(std::uint8_t value,
|
std::array<std::array<int, 256>, 256>
|
||||||
bool uppercase) {
|
GenAsciiCodeFromCharPairTable()
|
||||||
if (uppercase) {
|
{
|
||||||
return std::make_pair(kHexCharsUpper[value >> 4],
|
std::array<std::array<int, 256>, 256> table;
|
||||||
kHexCharsUpper[value & 0x0f]);
|
for (char i = kCharMin; i != kCharMax; ++i) {
|
||||||
} else {
|
for (char j = kCharMin; j != kCharMax; ++j) {
|
||||||
return std::make_pair(kHexCharsLower[value >> 4],
|
table[i - kCharMin][j - kCharMin] = AsciiCodeFromCharPairSlow(i, j);
|
||||||
kHexCharsLower[value & 0x0f]);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<std::array<int, 256>, 256> GenAsciiCodeFromCharPairTable() {
|
|
||||||
std::array<std::array<int, 256>, 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<std::pair<char, char>, 256>
|
std::array<std::pair<char, char>, 256>
|
||||||
GenAsciiCodeToCharPairTable(bool uppercase) {
|
GenAsciiCodeToCharPairTable(bool uppercase)
|
||||||
std::array<std::pair<char, char>, 256> table;
|
{
|
||||||
for (int i = 0; i != 256; ++i) {
|
std::array<std::pair<char, char>, 256> table;
|
||||||
table[i] = AsciiCodeToCharPairSlow(i, uppercase);
|
for (int i = 0; i != 256; ++i) { table[i] = AsciiCodeToCharPairSlow(i, uppercase); }
|
||||||
}
|
return table;
|
||||||
return table;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
int AsciiCodeFromCharPair(char a, char b) {
|
int
|
||||||
static auto table = GenAsciiCodeFromCharPairTable();
|
AsciiCodeFromCharPair(char a, char b)
|
||||||
return table[a - kCharMin][b - kCharMin];
|
{
|
||||||
|
static auto table = GenAsciiCodeFromCharPairTable();
|
||||||
|
return table[a - kCharMin][b - kCharMin];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::pair<char, char> AsciiCodeToUpperCharPair(std::uint8_t value) {
|
inline std::pair<char, char>
|
||||||
static auto table = GenAsciiCodeToCharPairTable(true);
|
AsciiCodeToUpperCharPair(std::uint8_t value)
|
||||||
return table[value];
|
{
|
||||||
}
|
static auto table = GenAsciiCodeToCharPairTable(true);
|
||||||
inline std::pair<char, char> AsciiCodeToLowerCharPair(std::uint8_t value) {
|
return table[value];
|
||||||
static auto table = GenAsciiCodeToCharPairTable(false);
|
|
||||||
return table[value];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value, bool uppercase) {
|
inline std::pair<char, char>
|
||||||
return uppercase ? AsciiCodeToUpperCharPair(value)
|
AsciiCodeToLowerCharPair(std::uint8_t value)
|
||||||
: AsciiCodeToLowerCharPair(value);
|
{
|
||||||
|
static auto table = GenAsciiCodeToCharPairTable(false);
|
||||||
|
return table[value];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
std::pair<char, char>
|
||||||
} // namespace tile
|
AsciiCodeToCharPair(std::uint8_t value, bool uppercase)
|
||||||
|
{
|
||||||
|
return uppercase ? AsciiCodeToUpperCharPair(value) : AsciiCodeToLowerCharPair(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace detail
|
||||||
|
}// namespace tile
|
||||||
|
@ -13,7 +13,7 @@ namespace tile {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
struct CharPair {
|
struct CharPair {
|
||||||
char a, b;
|
char a, b;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr char kHexCharsLower[] = "0123456789abcdef";
|
static constexpr char kHexCharsLower[] = "0123456789abcdef";
|
||||||
@ -23,10 +23,9 @@ static constexpr char kHexCharsUpper[] = "0123456789ABCDEF";
|
|||||||
|
|
||||||
int AsciiCodeFromCharPair(char a, char b);
|
int AsciiCodeFromCharPair(char a, char b);
|
||||||
|
|
||||||
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value,
|
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value, bool uppercase = true);
|
||||||
bool uppercase = true);
|
|
||||||
|
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_ENCODING_DETAIL_HEX_CHARS_H
|
#endif// _TILE_BASE_ENCODING_DETAIL_HEX_CHARS_H
|
||||||
|
@ -6,48 +6,49 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
std::string EncodeHex(Slice from, bool uppercase) {
|
std::string
|
||||||
std::string result;
|
EncodeHex(Slice from, bool uppercase)
|
||||||
EncodeHex(from, &result, uppercase);
|
{
|
||||||
return result;
|
std::string result;
|
||||||
|
EncodeHex(from, &result, uppercase);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> DecodeHex(Slice from) {
|
std::optional<std::string>
|
||||||
std::string result;
|
DecodeHex(Slice from)
|
||||||
if (!DecodeHex(from, &result)) {
|
{
|
||||||
return std::nullopt;
|
std::string result;
|
||||||
}
|
if (!DecodeHex(from, &result)) { return std::nullopt; }
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodeHex(Slice from, std::string *to, bool uppercase) {
|
void
|
||||||
to->clear();
|
EncodeHex(Slice from, std::string *to, bool uppercase)
|
||||||
to->reserve(from.size() * 2);
|
{
|
||||||
for (auto &&e : from) {
|
to->clear();
|
||||||
// auto index = static_cast<std::uint8_t>(e);
|
to->reserve(from.size() * 2);
|
||||||
auto pair =
|
for (auto &&e : from) {
|
||||||
detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e), uppercase);
|
// auto index = static_cast<std::uint8_t>(e);
|
||||||
to->append({pair.first, pair.second});
|
auto pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e), uppercase);
|
||||||
}
|
to->append({pair.first, pair.second});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecodeHex(Slice from, std::string *to) {
|
bool
|
||||||
if (from.size() % 2 != 0) {
|
DecodeHex(Slice from, std::string *to)
|
||||||
return false;
|
{
|
||||||
}
|
if (from.size() % 2 != 0) { return false; }
|
||||||
|
|
||||||
to->clear();
|
to->clear();
|
||||||
to->reserve(from.size() / 2);
|
to->reserve(from.size() / 2);
|
||||||
for (size_t i = 0; i != from.size(); i += 2) {
|
for (size_t i = 0; i != from.size(); i += 2) {
|
||||||
auto v = detail::AsciiCodeFromCharPair(from[i], from[i + 1]);
|
auto v = detail::AsciiCodeFromCharPair(from[i], from[i + 1]);
|
||||||
if (v == -1) {
|
if (v == -1) { return false; }
|
||||||
return false;
|
|
||||||
|
TILE_CHECK(v >= 0 && v <= 255);
|
||||||
|
to->push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
TILE_CHECK(v >= 0 && v <= 255);
|
return true;
|
||||||
to->push_back(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -11,6 +11,6 @@ std::optional<std::string> DecodeHex(Slice from);
|
|||||||
|
|
||||||
void EncodeHex(Slice from, std::string *to, bool uppercase = false);
|
void EncodeHex(Slice from, std::string *to, bool uppercase = false);
|
||||||
bool DecodeHex(Slice from, std::string *to);
|
bool DecodeHex(Slice from, std::string *to);
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_ENCODING_HEX_H
|
#endif// TILE_BASE_ENCODING_HEX_H
|
||||||
|
@ -7,23 +7,25 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
const char Hex123456FF[] = "\x12\x34\x56\xFF";
|
const char Hex123456FF[] = "\x12\x34\x56\xFF";
|
||||||
|
|
||||||
TEST(Hex, Default) {
|
TEST(Hex, Default)
|
||||||
EXPECT_EQ("123456ff", EncodeHex(Hex123456FF));
|
{
|
||||||
EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true));
|
EXPECT_EQ("123456ff", EncodeHex(Hex123456FF));
|
||||||
EXPECT_EQ(Hex123456FF, *DecodeHex("123456ff"));
|
EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true));
|
||||||
EXPECT_EQ(Hex123456FF, *DecodeHex("123456FF"));
|
EXPECT_EQ(Hex123456FF, *DecodeHex("123456ff"));
|
||||||
|
EXPECT_EQ(Hex123456FF, *DecodeHex("123456FF"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Hex, Random) {
|
TEST(Hex, Random)
|
||||||
std::string random_str;
|
{
|
||||||
std::string encoded;
|
std::string random_str;
|
||||||
std::string decoded;
|
std::string encoded;
|
||||||
for (int i = 0; i != 1000; ++i) {
|
std::string decoded;
|
||||||
testing::RandomString(&random_str);
|
for (int i = 0; i != 1000; ++i) {
|
||||||
EncodeHex(random_str, &encoded);
|
testing::RandomString(&random_str);
|
||||||
ASSERT_TRUE(DecodeHex(encoded, &decoded));
|
EncodeHex(random_str, &encoded);
|
||||||
EXPECT_EQ(random_str, decoded);
|
ASSERT_TRUE(DecodeHex(encoded, &decoded));
|
||||||
}
|
EXPECT_EQ(random_str, decoded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -7,112 +7,107 @@ namespace tile {
|
|||||||
|
|
||||||
// [0-9a-zA-Z]
|
// [0-9a-zA-Z]
|
||||||
namespace {
|
namespace {
|
||||||
std::array<bool, 256> GenerateUnescapedCharBitmap(Slice unescaped_chars) {
|
std::array<bool, 256>
|
||||||
std::array<bool, 256> result{};
|
GenerateUnescapedCharBitmap(Slice unescaped_chars)
|
||||||
for (auto &&e : unescaped_chars) {
|
{
|
||||||
result[e] = true;
|
std::array<bool, 256> result{};
|
||||||
}
|
for (auto &&e : unescaped_chars) { result[e] = true; }
|
||||||
|
|
||||||
for (int i = 0; i != 10; ++i) {
|
for (int i = 0; i != 10; ++i) { result[i + '0'] = true; }
|
||||||
result[i + '0'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i != 26; ++i) {
|
for (int i = 0; i != 26; ++i) {
|
||||||
result[i + 'A'] = true;
|
result[i + 'A'] = true;
|
||||||
result[i + 'a'] = true;
|
result[i + 'a'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<std::array<bool, 256>, 2> &
|
const std::array<std::array<bool, 256>, 2> &
|
||||||
GetUnescapedCharBitmap(const PercentEncodingStyle &style) {
|
GetUnescapedCharBitmap(const PercentEncodingStyle &style)
|
||||||
static const std::array<std::array<bool, 256>, 2> kUnescapedChars[] = {
|
{
|
||||||
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"),
|
static const std::array<std::array<bool, 256>, 2> kUnescapedChars[] = {
|
||||||
GenerateUnescapedCharBitmap("_-!.*~'()")},
|
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), GenerateUnescapedCharBitmap("_-!.*~'()") },
|
||||||
|
|
||||||
/* rfc3986 = */
|
/* rfc3986 = */
|
||||||
{GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"),
|
{GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"), GenerateUnescapedCharBitmap("_-.~")
|
||||||
GenerateUnescapedCharBitmap("_-.~")
|
|
||||||
|
|
||||||
},
|
},
|
||||||
/* rfc5987 = */
|
/* rfc5987 = */
|
||||||
{GenerateUnescapedCharBitmap("!#$&+-.^_`|~"),
|
{GenerateUnescapedCharBitmap("!#$&+-.^_`|~"), GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}
|
||||||
GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}};
|
};
|
||||||
|
|
||||||
return kUnescapedChars[static_cast<int>(style)];
|
return kUnescapedChars[static_cast<int>(style)];
|
||||||
}
|
}
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
PercentEncodingOptions::PercentEncodingOptions(PercentEncodingStyle s, bool er)
|
PercentEncodingOptions::PercentEncodingOptions(PercentEncodingStyle s, bool er) : style(s), escape_reserved(er) {}
|
||||||
: style(s), escape_reserved(er) {}
|
|
||||||
|
|
||||||
std::string EncodePercent(Slice from, const PercentEncodingOptions &options) {
|
std::string
|
||||||
std::string result;
|
EncodePercent(Slice from, const PercentEncodingOptions &options)
|
||||||
EncodePercent(from, &result, options);
|
{
|
||||||
return result;
|
std::string result;
|
||||||
}
|
EncodePercent(from, &result, options);
|
||||||
std::optional<std::string> DecodePercent(Slice from,
|
|
||||||
bool decode_plus_sign_as_whitespace) {
|
|
||||||
std::string result;
|
|
||||||
if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) {
|
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodePercent(Slice from, std::string *to,
|
std::optional<std::string>
|
||||||
const PercentEncodingOptions &options) {
|
DecodePercent(Slice from, bool decode_plus_sign_as_whitespace)
|
||||||
|
{
|
||||||
auto &&unescaped =
|
std::string result;
|
||||||
GetUnescapedCharBitmap(options.style)[options.escape_reserved];
|
if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) { return result; }
|
||||||
|
return std::nullopt;
|
||||||
int escape_char_count = 0;
|
|
||||||
for (auto &&e : from) {
|
|
||||||
if (!unescaped[static_cast<std::uint8_t>(e)]) {
|
|
||||||
++escape_char_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
to->clear();
|
|
||||||
to->reserve(from.size() + escape_char_count * 2);
|
|
||||||
|
|
||||||
for (auto &&e : from) {
|
|
||||||
if (TILE_UNLIKELY(unescaped[static_cast<std::uint8_t>(e)])) {
|
|
||||||
to->push_back(e);
|
|
||||||
} else {
|
|
||||||
auto hex_pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e));
|
|
||||||
to->append({'%', hex_pair.first, hex_pair.second});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecodePercent(Slice from, std::string *to,
|
void
|
||||||
bool decode_plus_sign_as_whitespace) {
|
EncodePercent(Slice from, std::string *to, const PercentEncodingOptions &options)
|
||||||
to->clear();
|
{
|
||||||
to->reserve(from.size());
|
|
||||||
for (auto iter = from.begin(); iter != from.end();) {
|
auto &&unescaped = GetUnescapedCharBitmap(options.style)[options.escape_reserved];
|
||||||
if (*iter == '%') {
|
|
||||||
if (iter + 3 > from.end()) {
|
int escape_char_count = 0;
|
||||||
return false;
|
for (auto &&e : from) {
|
||||||
}
|
if (!unescaped[static_cast<std::uint8_t>(e)]) { ++escape_char_count; }
|
||||||
auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2));
|
}
|
||||||
if (TILE_LIKELY(v != -1)) {
|
|
||||||
to->push_back(v);
|
to->clear();
|
||||||
iter += 3;
|
to->reserve(from.size() + escape_char_count * 2);
|
||||||
} else {
|
|
||||||
// invalid char
|
for (auto &&e : from) {
|
||||||
return false;
|
if (TILE_UNLIKELY(unescaped[static_cast<std::uint8_t>(e)])) {
|
||||||
}
|
to->push_back(e);
|
||||||
} else {
|
} else {
|
||||||
if (decode_plus_sign_as_whitespace && *iter == '+') {
|
auto hex_pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e));
|
||||||
to->push_back(' ');
|
to->append({'%', hex_pair.first, hex_pair.second});
|
||||||
++iter;
|
}
|
||||||
} else {
|
|
||||||
to->push_back(*iter++);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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
|
||||||
|
@ -9,32 +9,28 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
enum class PercentEncodingStyle {
|
enum class PercentEncodingStyle {
|
||||||
Ecma262 = 0,
|
Ecma262 = 0,
|
||||||
Rfc3986 = 1,
|
Rfc3986 = 1,
|
||||||
Rfc5987 = 2,
|
Rfc5987 = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PercentEncodingOptions {
|
struct PercentEncodingOptions {
|
||||||
PercentEncodingStyle style = PercentEncodingStyle::Rfc3986;
|
PercentEncodingStyle style = PercentEncodingStyle::Rfc3986;
|
||||||
bool escape_reserved = true;
|
bool escape_reserved = true;
|
||||||
|
|
||||||
PercentEncodingOptions(PercentEncodingStyle s = PercentEncodingStyle::Rfc3986,
|
PercentEncodingOptions(PercentEncodingStyle s = PercentEncodingStyle::Rfc3986, bool escape_reserved = true);
|
||||||
bool escape_reserved = true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
EncodePercent(Slice from,
|
EncodePercent(Slice from,
|
||||||
const PercentEncodingOptions &options =
|
const PercentEncodingOptions &options = internal::EarlyInitConstant<PercentEncodingOptions>());
|
||||||
internal::EarlyInitConstant<PercentEncodingOptions>());
|
std::optional<std::string> DecodePercent(Slice from, bool decode_plus_sign_as_whitespace = false);
|
||||||
std::optional<std::string>
|
|
||||||
DecodePercent(Slice from, bool decode_plus_sign_as_whitespace = false);
|
|
||||||
|
|
||||||
void EncodePercent(Slice from, std::string *to,
|
void EncodePercent(Slice from,
|
||||||
const PercentEncodingOptions &options =
|
std::string *to,
|
||||||
internal::EarlyInitConstant<PercentEncodingOptions>());
|
const PercentEncodingOptions &options = internal::EarlyInitConstant<PercentEncodingOptions>());
|
||||||
bool DecodePercent(Slice from, std::string *to,
|
bool DecodePercent(Slice from, std::string *to, bool decode_plus_sign_as_whitespace = false);
|
||||||
bool decode_plus_sign_as_whitespace = false);
|
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_ENCODING_PERCENT_H
|
#endif// TILE_BASE_ENCODING_PERCENT_H
|
||||||
|
@ -6,103 +6,97 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
TEST(PercentEncoding, Emca262) {
|
TEST(PercentEncoding, Emca262)
|
||||||
// Shamelessly copied from:
|
{
|
||||||
//
|
// Shamelessly copied from:
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
//
|
||||||
Slice set1 = ";,/?:@&=+$"; // Reserved Characters
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
||||||
Slice set2 = "-_.!~*'()"; // Unescaped Characters
|
Slice set1 = ";,/?:@&=+$"; // Reserved Characters
|
||||||
Slice set3 = "#"; // Number Sign
|
Slice set2 = "-_.!~*'()"; // Unescaped Characters
|
||||||
Slice set4 = "ABC abc 123"; // Alphanumeric Characters + Space
|
Slice set3 = "#"; // Number Sign
|
||||||
|
Slice set4 = "ABC abc 123";// Alphanumeric Characters + Space
|
||||||
|
|
||||||
// Reserved chars are escaped.
|
// Reserved chars are escaped.
|
||||||
const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262,
|
const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262, true);
|
||||||
true);
|
|
||||||
|
|
||||||
EXPECT_EQ("%3B%2C%2F%3F%3A%40%26%3D%2B%24",
|
EXPECT_EQ("%3B%2C%2F%3F%3A%40%26%3D%2B%24", EncodePercent(set1, ecma262_reversed));
|
||||||
EncodePercent(set1, ecma262_reversed));
|
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262_reversed));
|
||||||
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262_reversed));
|
EXPECT_EQ("%23", EncodePercent(set3, ecma262_reversed));
|
||||||
EXPECT_EQ("%23", EncodePercent(set3, ecma262_reversed));
|
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, 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(set1, DecodePercent("%3B%2C%2F%3F%3A%40%26%3D%2B%24"));
|
||||||
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
|
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
|
||||||
EXPECT_EQ(set3, DecodePercent("%23"));
|
EXPECT_EQ(set3, DecodePercent("%23"));
|
||||||
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
|
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
|
||||||
|
|
||||||
// Reserved chars are kept.
|
// Reserved chars are kept.
|
||||||
const PercentEncodingOptions ecma262(PercentEncodingStyle::Ecma262, false);
|
const PercentEncodingOptions ecma262(PercentEncodingStyle::Ecma262, false);
|
||||||
EXPECT_EQ(";,/?:@&=+$", EncodePercent(set1, ecma262));
|
EXPECT_EQ(";,/?:@&=+$", EncodePercent(set1, ecma262));
|
||||||
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262));
|
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262));
|
||||||
EXPECT_EQ("#", EncodePercent(set3, ecma262));
|
EXPECT_EQ("#", EncodePercent(set3, ecma262));
|
||||||
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262));
|
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262));
|
||||||
|
|
||||||
EXPECT_EQ(set1, DecodePercent(";,/?:@&=+$"));
|
EXPECT_EQ(set1, DecodePercent(";,/?:@&=+$"));
|
||||||
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
|
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
|
||||||
EXPECT_EQ(set3, DecodePercent("#"));
|
EXPECT_EQ(set3, DecodePercent("#"));
|
||||||
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
|
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PercentEncoding, Rfc3986) {
|
TEST(PercentEncoding, Rfc3986)
|
||||||
Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123";
|
{
|
||||||
|
Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123";
|
||||||
|
|
||||||
const PercentEncodingOptions rfc3986_reversed(PercentEncodingStyle::Rfc3986,
|
const PercentEncodingOptions rfc3986_reversed(PercentEncodingStyle::Rfc3986, true);
|
||||||
true);
|
const PercentEncodingOptions rfc3986(PercentEncodingStyle::Rfc3986, false);
|
||||||
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%"
|
||||||
EXPECT_EQ(
|
"20123",
|
||||||
"_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%"
|
EncodePercent(str, rfc3986_reversed));
|
||||||
"20123",
|
EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123", EncodePercent(str, rfc3986));
|
||||||
EncodePercent(str, rfc3986_reversed));
|
EXPECT_EQ(str,
|
||||||
EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123",
|
DecodePercent("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%"
|
||||||
EncodePercent(str, rfc3986));
|
"2B%3D~%24ABC%20abc%20123"));
|
||||||
EXPECT_EQ(str,
|
EXPECT_EQ(str, DecodePercent("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123"));
|
||||||
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) {
|
TEST(PercentEncoding, Rfc5987)
|
||||||
// `encodeRFC5987ValueChars from MDN does not seems quite right (in that it
|
{
|
||||||
// does escape `#` `$` ..., which is not required.):
|
// `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
|
//
|
||||||
//
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
||||||
// auto encode_rfc5987_value_chars = [](Slice str) {
|
//
|
||||||
// auto result =
|
// auto encode_rfc5987_value_chars = [](Slice str) {
|
||||||
// EncodePercent(str, PercentEncodingOptions{.style =
|
// auto result =
|
||||||
// PercentEncodingStyle::Ecma262});
|
// EncodePercent(str, PercentEncodingOptions{.style =
|
||||||
// result = Replace(result, "'", "%" + EncodeHex("'"));
|
// PercentEncodingStyle::Ecma262});
|
||||||
// result = Replace(result, "(", "%" + EncodeHex("("));
|
// result = Replace(result, "'", "%" + EncodeHex("'"));
|
||||||
// result = Replace(result, ")", "%" + EncodeHex(")"));
|
// result = Replace(result, "(", "%" + EncodeHex("("));
|
||||||
// result = Replace(result, "*", "%" + EncodeHex("*"));
|
// result = Replace(result, ")", "%" + EncodeHex(")"));
|
||||||
// result = Replace(result, "%7C", "|");
|
// result = Replace(result, "*", "%" + EncodeHex("*"));
|
||||||
// result = Replace(result, "%60", "`");
|
// result = Replace(result, "%7C", "|");
|
||||||
// result = Replace(result, "%5E", "^");
|
// result = Replace(result, "%60", "`");
|
||||||
// return result;
|
// result = Replace(result, "%5E", "^");
|
||||||
// };
|
// return result;
|
||||||
//
|
// };
|
||||||
// EXPECT_EQ(
|
//
|
||||||
// encode_rfc5987_value_chars(str),
|
// EXPECT_EQ(
|
||||||
// EncodePercent(str, PercentEncodingOptions{.style =
|
// encode_rfc5987_value_chars(str),
|
||||||
// PercentEncodingStyle::Rfc5987}));
|
// EncodePercent(str, PercentEncodingOptions{.style =
|
||||||
|
// PercentEncodingStyle::Rfc5987}));
|
||||||
|
|
||||||
const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false);
|
const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false);
|
||||||
const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987,
|
const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987, true);
|
||||||
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",
|
EXPECT_EQ("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc", EncodePercent(str, rfc5987));
|
||||||
EncodePercent(str, rfc5987));
|
EXPECT_EQ(str, DecodePercent("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc"));
|
||||||
EXPECT_EQ(
|
|
||||||
str,
|
|
||||||
DecodePercent(
|
|
||||||
"!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PercentEncoding, DecodePlusSignAsWhitespace) {
|
TEST(PercentEncoding, DecodePlusSignAsWhitespace)
|
||||||
EXPECT_EQ("a+b", DecodePercent("a+b"));
|
{
|
||||||
EXPECT_EQ("a b", DecodePercent("a+b", true));
|
EXPECT_EQ("a+b", DecodePercent("a+b"));
|
||||||
|
EXPECT_EQ("a b", DecodePercent("a+b", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -7,77 +7,90 @@
|
|||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
void Benchmark_Base64Encode(benchmark::State &state) {
|
void
|
||||||
std::string random_str;
|
Benchmark_Base64Encode(benchmark::State &state)
|
||||||
std::string out;
|
{
|
||||||
while (state.KeepRunning()) {
|
std::string random_str;
|
||||||
state.PauseTiming();
|
std::string out;
|
||||||
testing::RandomString(&random_str);
|
while (state.KeepRunning()) {
|
||||||
state.ResumeTiming();
|
state.PauseTiming();
|
||||||
|
testing::RandomString(&random_str);
|
||||||
|
state.ResumeTiming();
|
||||||
|
|
||||||
EncodeBase64(random_str, &out);
|
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_PercentEncode(benchmark::State &state) {
|
void
|
||||||
std::string random_str;
|
Benchmark_Base64Decode(benchmark::State &state)
|
||||||
std::string out;
|
{
|
||||||
while (state.KeepRunning()) {
|
std::string random_str;
|
||||||
state.PauseTiming();
|
std::string base64;
|
||||||
testing::RandomString(&random_str);
|
std::string plain_text;
|
||||||
state.ResumeTiming();
|
while (state.KeepRunning()) {
|
||||||
EncodeBase64(random_str, &out);
|
state.PauseTiming();
|
||||||
}
|
testing::RandomString(&random_str);
|
||||||
|
EncodeBase64(random_str, &base64);
|
||||||
|
state.ResumeTiming();
|
||||||
|
|
||||||
|
DecodeBase64(base64, &plain_text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Benchmark_PercentDecode(benchmark::State &state) {
|
void
|
||||||
std::string random_str;
|
Benchmark_PercentEncode(benchmark::State &state)
|
||||||
std::string percent;
|
{
|
||||||
std::string plain_text;
|
std::string random_str;
|
||||||
while (state.KeepRunning()) {
|
std::string out;
|
||||||
state.PauseTiming();
|
while (state.KeepRunning()) {
|
||||||
testing::RandomString(&random_str);
|
state.PauseTiming();
|
||||||
EncodePercent(random_str, &percent);
|
testing::RandomString(&random_str);
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
DecodePercent(percent, &plain_text);
|
EncodeBase64(random_str, &out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Benchmark_HexEncode(benchmark::State &state) {
|
void
|
||||||
std::string random_str;
|
Benchmark_PercentDecode(benchmark::State &state)
|
||||||
std::string percent;
|
{
|
||||||
while (state.KeepRunning()) {
|
std::string random_str;
|
||||||
state.PauseTiming();
|
std::string percent;
|
||||||
testing::RandomString(&random_str);
|
std::string plain_text;
|
||||||
state.ResumeTiming();
|
while (state.KeepRunning()) {
|
||||||
EncodeHex(random_str, &percent);
|
state.PauseTiming();
|
||||||
}
|
testing::RandomString(&random_str);
|
||||||
|
EncodePercent(random_str, &percent);
|
||||||
|
state.ResumeTiming();
|
||||||
|
DecodePercent(percent, &plain_text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Benchmark_HexDecode(benchmark::State &state) {
|
void
|
||||||
std::string random_str;
|
Benchmark_HexEncode(benchmark::State &state)
|
||||||
std::string percent;
|
{
|
||||||
std::string plain_text;
|
std::string random_str;
|
||||||
while (state.KeepRunning()) {
|
std::string percent;
|
||||||
state.PauseTiming();
|
while (state.KeepRunning()) {
|
||||||
testing::RandomString(&random_str);
|
state.PauseTiming();
|
||||||
EncodeHex(random_str, &percent);
|
testing::RandomString(&random_str);
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
DecodeHex(percent, &plain_text);
|
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);
|
BENCHMARK(Benchmark_Base64Encode);
|
||||||
@ -86,4 +99,4 @@ BENCHMARK(Benchmark_PercentEncode);
|
|||||||
BENCHMARK(Benchmark_PercentDecode);
|
BENCHMARK(Benchmark_PercentDecode);
|
||||||
BENCHMARK(Benchmark_HexEncode);
|
BENCHMARK(Benchmark_HexEncode);
|
||||||
BENCHMARK(Benchmark_HexDecode);
|
BENCHMARK(Benchmark_HexDecode);
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -6,35 +6,33 @@
|
|||||||
#include "tile/base/internal/meta.h"
|
#include "tile/base/internal/meta.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
template <class T, class = enable_if_t<std::is_enum<T>::value>>
|
template<class T, class = enable_if_t<std::is_enum<T>::value>>
|
||||||
constexpr auto underlying_value(T v) -> typename std::underlying_type<T>::type {
|
constexpr auto
|
||||||
return static_cast<typename std::underlying_type<T>::type>(v);
|
underlying_value(T v) -> typename std::underlying_type<T>::type
|
||||||
|
{
|
||||||
|
return static_cast<typename std::underlying_type<T>::type>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, Op) \
|
#define TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, Op) \
|
||||||
constexpr Type operator Op(Type left, Type right) { \
|
constexpr Type operator Op(Type left, Type right) \
|
||||||
return static_cast<Type>(::tile::underlying_value(left) \
|
{ \
|
||||||
Op ::tile::underlying_value(right)); \
|
return static_cast<Type>(::tile::underlying_value(left) Op ::tile::underlying_value(right)); \
|
||||||
}
|
}
|
||||||
#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \
|
#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \
|
||||||
constexpr Type operator Op(Type v) { \
|
constexpr Type operator Op(Type v) { return static_cast<Type>(Op ::tile::underlying_value(v)); }
|
||||||
return static_cast<Type>(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_ASSIGN_OP(Type, Op) \
|
|
||||||
constexpr Type &operator Op(Type & left, Type right) { return left Op right; }
|
|
||||||
|
|
||||||
#define TILE_DEFINE_ENUM_BITMASK_OPS(Type) \
|
#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_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_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, ~) \
|
TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, ~) \
|
||||||
constexpr bool operator!(Type value) { \
|
constexpr bool operator!(Type value) { return !::tile::underlying_value(value); }
|
||||||
return !::tile::underlying_value(value); \
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_ENUM_H
|
#endif// _TILE_BASE_ENUM_H
|
||||||
|
@ -12,58 +12,69 @@ namespace tile {
|
|||||||
// RAII wrapper ptr
|
// RAII wrapper ptr
|
||||||
class ErasedPtr final {
|
class ErasedPtr final {
|
||||||
public:
|
public:
|
||||||
using Deleter = void (*)(void *);
|
using Deleter = void (*)(void *);
|
||||||
constexpr ErasedPtr(std::nullptr_t = nullptr)
|
|
||||||
: ptr_(nullptr), deleter_(nullptr) {}
|
|
||||||
template <typename T>
|
|
||||||
constexpr ErasedPtr(T *ptr) noexcept
|
|
||||||
: ptr_(ptr), deleter_([](void *ptr) { delete static_cast<T *>(ptr); }) {}
|
|
||||||
template <typename T, typename D>
|
|
||||||
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 {
|
constexpr ErasedPtr(std::nullptr_t = nullptr) : ptr_(nullptr), deleter_(nullptr) {}
|
||||||
if (TILE_LIKELY(this != &ptr)) {
|
|
||||||
Reset();
|
template<typename T>
|
||||||
|
constexpr ErasedPtr(T *ptr) noexcept
|
||||||
|
: ptr_(ptr),
|
||||||
|
deleter_([](void *ptr) { delete static_cast<T *>(ptr); })
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename T, typename D>
|
||||||
|
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(const ErasedPtr &) = delete;
|
||||||
ErasedPtr &operator=(const ErasedPtr &) = delete;
|
ErasedPtr &operator=(const ErasedPtr &) = delete;
|
||||||
|
|
||||||
~ErasedPtr() {
|
~ErasedPtr()
|
||||||
if (ptr_) {
|
{
|
||||||
deleter_(ptr_);
|
if (ptr_) { deleter_(ptr_); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void *Get() const noexcept { return ptr_; }
|
constexpr void *Get() const noexcept { return ptr_; }
|
||||||
template <typename T> T *UncheckedGet() const noexcept {
|
|
||||||
return static_cast<T *>(ptr_);
|
template<typename T>
|
||||||
}
|
T *UncheckedGet() const noexcept
|
||||||
constexpr explicit operator bool() const noexcept { return !!ptr_; }
|
{
|
||||||
void Reset(std::nullptr_t = nullptr) noexcept {
|
return static_cast<T *>(ptr_);
|
||||||
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_; }
|
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:
|
private:
|
||||||
void *ptr_;
|
void *ptr_;
|
||||||
Deleter deleter_;
|
Deleter deleter_;
|
||||||
};
|
};
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // _TILE_BASE_ERASED_PTR_H
|
#endif// _TILE_BASE_ERASED_PTR_H
|
||||||
|
4305
tile/base/expected.h
4305
tile/base/expected.h
File diff suppressed because it is too large
Load Diff
@ -8,293 +8,294 @@ namespace tile {
|
|||||||
// namespace exposed_var {
|
// namespace exposed_var {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#define CHECK_PATH(path) \
|
#define CHECK_PATH(path) \
|
||||||
TILE_CHECK((path).size() <= 1 || (path).back() != '/', \
|
TILE_CHECK((path).size() <= 1 || (path).back() != '/', "Invalid path: [{}].", path); \
|
||||||
"Invalid path: [{}].", path); \
|
TILE_CHECK((path).find("//") == tile::Slice::npos, "Invalid path: [{}].", path);
|
||||||
TILE_CHECK((path).find("//") == tile::Slice::npos, "Invalid path: [{}].", \
|
|
||||||
path);
|
|
||||||
|
|
||||||
#define CHECK_RELATIVE_PATH(path) \
|
#define CHECK_RELATIVE_PATH(path) \
|
||||||
CHECK_PATH(path); \
|
CHECK_PATH(path); \
|
||||||
TILE_CHECK((path).empty() || (path).front() != '/', "Invalid path: [{}].", \
|
TILE_CHECK((path).empty() || (path).front() != '/', "Invalid path: [{}].", path);
|
||||||
path);
|
|
||||||
|
|
||||||
#define CHECK_ABSOLUTE_PATH(path) \
|
#define CHECK_ABSOLUTE_PATH(path) \
|
||||||
CHECK_PATH(path); \
|
CHECK_PATH(path); \
|
||||||
TILE_CHECK((path).empty() || (path).front() == '/', "Invalid path: [{}]", \
|
TILE_CHECK((path).empty() || (path).front() == '/', "Invalid path: [{}]", path);
|
||||||
path);
|
|
||||||
|
std::pair<Slice, Slice>
|
||||||
|
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<Slice, Slice>
|
||||||
|
SplitLastPart(Slice path)
|
||||||
|
{
|
||||||
|
auto pos = path.find_last_of('/');
|
||||||
|
if (pos == tile::Slice::npos) { return std::make_pair("", path); }
|
||||||
|
|
||||||
std::pair<Slice, Slice> 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));
|
return std::make_pair(path.substr(0, pos), path.substr(pos + 1));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Slice, Slice> SplitLastPart(Slice path) {
|
std::string
|
||||||
auto pos = path.find_last_of('/');
|
JoinPath(Slice a, Slice b)
|
||||||
if (pos == tile::Slice::npos) {
|
{
|
||||||
return std::make_pair("", path);
|
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) {
|
std::string
|
||||||
if (EndsWith(b, "/")) {
|
SubstituteEscapedSlashForZero(Slice path)
|
||||||
b.RemoveSuffix(1);
|
{
|
||||||
}
|
TILE_CHECK(path.find('\0') == tile::Slice::npos);
|
||||||
|
return Replace(path, "\\/", '\0');
|
||||||
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) {
|
std::string
|
||||||
TILE_CHECK(path.find('\0') == tile::Slice::npos);
|
SubstituteZeroForEscapedSlash(Slice path)
|
||||||
return Replace(path, "\\/", '\0');
|
{
|
||||||
|
return Replace(path, '\0', "\\/");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SubstituteZeroForEscapedSlash(Slice path) {
|
std::string
|
||||||
return Replace(path, '\0', "\\/");
|
UnescapeZeroToPlainSlash(Slice path)
|
||||||
|
{
|
||||||
|
return Replace(path, '\0', '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UnescapeZeroToPlainSlash(Slice path) {
|
}// namespace
|
||||||
return Replace(path, '\0', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// } // namespace exposed_var
|
// } // namespace exposed_var
|
||||||
ExposedVarGroup::Handle
|
ExposedVarGroup::Handle
|
||||||
ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
|
ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value)
|
||||||
|
{
|
||||||
|
|
||||||
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
||||||
CHECK_RELATIVE_PATH(rel_path);
|
CHECK_RELATIVE_PATH(rel_path);
|
||||||
|
|
||||||
auto path_and_name = SplitLastPart(real_path);
|
auto path_and_name = SplitLastPart(real_path);
|
||||||
auto name = path_and_name.second.ToString();
|
auto name = path_and_name.second.ToString();
|
||||||
|
|
||||||
auto moved_func = tile::MakeMoveOnCopy(value);
|
auto moved_func = tile::MakeMoveOnCopy(value);
|
||||||
return CreateUpto(path_and_name.first)
|
return CreateUpto(path_and_name.first)
|
||||||
->AddDirect(
|
->AddDirect(name, [name, moved_func](Slice expected) -> std::optional<Json::Value> {
|
||||||
name,
|
|
||||||
[name, moved_func](Slice expected) -> std::optional<Json::Value> {
|
|
||||||
auto jsv = moved_func.Ref()();
|
auto jsv = moved_func.Ref()();
|
||||||
if (expected.empty()) {
|
if (expected.empty()) { return jsv; }
|
||||||
return jsv;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto real_path = SubstituteEscapedSlashForZero(expected);
|
auto real_path = SubstituteEscapedSlashForZero(expected);
|
||||||
Json::Value *ptr = &jsv;
|
Json::Value *ptr = &jsv;
|
||||||
auto pieces = Split(real_path, '/');
|
auto pieces = Split(real_path, '/');
|
||||||
|
|
||||||
for (auto &&e : pieces) {
|
for (auto &&e : pieces) {
|
||||||
auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()});
|
auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()});
|
||||||
if (ptr->isObject()) {
|
if (ptr->isObject()) {
|
||||||
if (ptr->isMember(unescaped)) {
|
if (ptr->isMember(unescaped)) {
|
||||||
ptr = &(*ptr)[unescaped];
|
ptr = &(*ptr)[unescaped];
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} else if (ptr->isArray()) {
|
||||||
|
auto index = TryParse<std::size_t>(unescaped);
|
||||||
|
if (index && *index < ptr->size()) {
|
||||||
|
ptr = &(*ptr)[static_cast<Json::ArrayIndex>(*index)];
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
} else if (ptr->isArray()) {
|
|
||||||
auto index = TryParse<std::size_t>(unescaped);
|
|
||||||
if (index && *index < ptr->size()) {
|
|
||||||
ptr = &(*ptr)[static_cast<Json::ArrayIndex>(*index)];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return *ptr;
|
return *ptr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedVarGroup::Handle
|
ExposedVarGroup::Handle
|
||||||
ExposedVarGroup::Add(Slice rel_path, ExposedVarDynamicTree *dynamic_tree) {
|
ExposedVarGroup::Add(Slice rel_path, ExposedVarDynamicTree *dynamic_tree)
|
||||||
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
{
|
||||||
CHECK_RELATIVE_PATH(real_path);
|
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
||||||
auto path_and_name = SplitLastPart(real_path);
|
CHECK_RELATIVE_PATH(real_path);
|
||||||
auto name = path_and_name.second.ToString();
|
auto path_and_name = SplitLastPart(real_path);
|
||||||
|
auto name = path_and_name.second.ToString();
|
||||||
|
|
||||||
return CreateUpto(path_and_name.first)
|
return CreateUpto(path_and_name.first)->AddDirect(name, [dynamic_tree](Slice inner_path) {
|
||||||
->AddDirect(name, [dynamic_tree](Slice inner_path) {
|
|
||||||
return dynamic_tree->TryGet(inner_path);
|
return dynamic_tree->TryGet(inner_path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedVarGroup *ExposedVarGroup::FindOrCreate(Slice abs_path) {
|
ExposedVarGroup *
|
||||||
auto real_path = SubstituteEscapedSlashForZero(abs_path);
|
ExposedVarGroup::FindOrCreate(Slice abs_path)
|
||||||
CHECK_ABSOLUTE_PATH(real_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<Json::Value> ExposedVarGroup::TryGet(Slice abs_path) {
|
|
||||||
auto real_path = SubstituteEscapedSlashForZero(abs_path);
|
|
||||||
TILE_CHECK(!real_path.empty());
|
|
||||||
CHECK_ABSOLUTE_PATH(real_path);
|
|
||||||
|
|
||||||
if (real_path == "/") {
|
std::optional<Json::Value>
|
||||||
return Root()->Dump();
|
ExposedVarGroup::TryGet(Slice abs_path)
|
||||||
}
|
{
|
||||||
|
auto real_path = SubstituteEscapedSlashForZero(abs_path);
|
||||||
|
TILE_CHECK(!real_path.empty());
|
||||||
|
CHECK_ABSOLUTE_PATH(real_path);
|
||||||
|
|
||||||
Slice left_path;
|
if (real_path == "/") { return Root()->Dump(); }
|
||||||
auto rel_path = real_path.substr(1);
|
|
||||||
auto parent = Root()->FindLowest(rel_path, &left_path);
|
|
||||||
|
|
||||||
auto name_and_rest = SplitFirstPart(left_path);
|
Slice left_path;
|
||||||
if (name_and_rest.first.empty()) {
|
auto rel_path = real_path.substr(1);
|
||||||
return parent->Dump();
|
auto parent = Root()->FindLowest(rel_path, &left_path);
|
||||||
}
|
|
||||||
|
|
||||||
auto s = name_and_rest.first.ToString();
|
auto name_and_rest = SplitFirstPart(left_path);
|
||||||
std::lock_guard<std::mutex> lk(parent->lock_);
|
if (name_and_rest.first.empty()) { return parent->Dump(); }
|
||||||
auto iter = parent->leaves_.find(s);
|
|
||||||
if (iter != parent->leaves_.end()) {
|
auto s = name_and_rest.first.ToString();
|
||||||
return iter->second(SubstituteZeroForEscapedSlash(name_and_rest.second));
|
std::lock_guard<std::mutex> lk(parent->lock_);
|
||||||
} else {
|
auto iter = parent->leaves_.find(s);
|
||||||
auto iter = parent->nodes_.find(s);
|
if (iter != parent->leaves_.end()) {
|
||||||
if (iter != parent->nodes_.end()) {
|
return iter->second(SubstituteZeroForEscapedSlash(name_and_rest.second));
|
||||||
return iter->second->Dump();
|
|
||||||
} else {
|
} 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)
|
ExposedVarGroup::ExposedVarGroup(std::string abs_path) : abs_path_(std::move(abs_path))
|
||||||
: abs_path_(std::move(abs_path)) {
|
{
|
||||||
CHECK_ABSOLUTE_PATH(abs_path);
|
CHECK_ABSOLUTE_PATH(abs_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedVarGroup *ExposedVarGroup::Root() {
|
ExposedVarGroup *
|
||||||
static ExposedVarGroup evg("/");
|
ExposedVarGroup::Root()
|
||||||
return &evg;
|
{
|
||||||
|
static ExposedVarGroup evg("/");
|
||||||
|
return &evg;
|
||||||
}
|
}
|
||||||
const std::string &ExposedVarGroup::AbsolutePath() const { return abs_path_; }
|
|
||||||
|
|
||||||
ExposedVarGroup *ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) {
|
const std::string &
|
||||||
CHECK_RELATIVE_PATH(rel_path);
|
ExposedVarGroup::AbsolutePath() const
|
||||||
auto current = this;
|
{
|
||||||
|
return abs_path_;
|
||||||
|
}
|
||||||
|
|
||||||
while (!rel_path.empty()) {
|
ExposedVarGroup *
|
||||||
auto name_and_rest = SplitFirstPart(rel_path);
|
ExposedVarGroup::FindLowest(Slice rel_path, Slice *left)
|
||||||
|
{
|
||||||
|
CHECK_RELATIVE_PATH(rel_path);
|
||||||
|
auto current = this;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lk(current->lock_);
|
while (!rel_path.empty()) {
|
||||||
auto iter = current->nodes_.find(name_and_rest.first.ToString());
|
auto name_and_rest = SplitFirstPart(rel_path);
|
||||||
if (iter == current->nodes_.end()) {
|
|
||||||
break;
|
std::lock_guard<std::mutex> lk(current->lock_);
|
||||||
} else {
|
auto iter = current->nodes_.find(name_and_rest.first.ToString());
|
||||||
current = &*iter->second;
|
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) {
|
ExposedVarGroup *
|
||||||
CHECK_RELATIVE_PATH(rel_path);
|
ExposedVarGroup::CreateUpto(Slice rel_path)
|
||||||
|
{
|
||||||
|
CHECK_RELATIVE_PATH(rel_path);
|
||||||
|
|
||||||
Slice left_path;
|
Slice left_path;
|
||||||
auto current = FindLowest(rel_path, &left_path);
|
auto current = FindLowest(rel_path, &left_path);
|
||||||
|
|
||||||
auto pieces = Split(left_path, '/');
|
auto pieces = Split(left_path, '/');
|
||||||
|
|
||||||
for (auto &&e : pieces) {
|
for (auto &&e : pieces) {
|
||||||
auto s = e.ToString();
|
auto s = e.ToString();
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lk(current->lock_);
|
std::lock_guard<std::mutex> lk(current->lock_);
|
||||||
auto p = JoinPath(current->AbsolutePath(), s);
|
auto p = JoinPath(current->AbsolutePath(), s);
|
||||||
|
|
||||||
TILE_CHECK(
|
TILE_CHECK(current->leaves_.find(s) == current->leaves_.end(),
|
||||||
current->leaves_.find(s) == current->leaves_.end(),
|
"Path [{}] has already been used: A value is registered at [{}].", rel_path, p);
|
||||||
"Path [{}] has already been used: A value is registered at [{}].",
|
|
||||||
rel_path, p);
|
|
||||||
|
|
||||||
TILE_CHECK(current->nodes_.find(s) == current->nodes_.end());
|
TILE_CHECK(current->nodes_.find(s) == current->nodes_.end());
|
||||||
auto evg = std::unique_ptr<ExposedVarGroup>(new ExposedVarGroup(p));
|
auto evg = std::unique_ptr<ExposedVarGroup>(new ExposedVarGroup(p));
|
||||||
current->nodes_[s] = std::move(evg);
|
current->nodes_[s] = std::move(evg);
|
||||||
current = &*(current->nodes_[s]);
|
current = &*(current->nodes_[s]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]",
|
TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]", current->AbsolutePath(), rel_path);
|
||||||
current->AbsolutePath(), rel_path);
|
return current;
|
||||||
return current;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedVarGroup::Handle ExposedVarGroup::AddDirect(Slice name, Getter value) {
|
ExposedVarGroup::Handle
|
||||||
auto s = name.ToString();
|
ExposedVarGroup::AddDirect(Slice name, Getter value)
|
||||||
std::lock_guard<std::mutex> lk(lock_);
|
{
|
||||||
|
auto s = name.ToString();
|
||||||
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<std::mutex> lk(lock_);
|
std::lock_guard<std::mutex> 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<std::mutex> lk(lock_);
|
||||||
|
TILE_CHECK_EQ(leaves_.erase(s), 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value ExposedVarGroup::Dump() const {
|
Json::Value
|
||||||
std::lock_guard<std::mutex> lk(lock_);
|
ExposedVarGroup::Dump() const
|
||||||
Json::Value jsv;
|
{
|
||||||
for (auto &&node : nodes_) {
|
std::lock_guard<std::mutex> lk(lock_);
|
||||||
jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump();
|
Json::Value jsv;
|
||||||
}
|
for (auto &&node : nodes_) { jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump(); }
|
||||||
|
|
||||||
for (auto &&leave : leaves_) {
|
for (auto &&leave : leaves_) { jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second(""); }
|
||||||
jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsv;
|
return jsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedVarDynamicTree::ExposedVarDynamicTree(
|
ExposedVarDynamicTree::ExposedVarDynamicTree(Slice rel_path,
|
||||||
Slice rel_path, std::function<Json::Value()> getter,
|
std::function<Json::Value()> getter,
|
||||||
ExposedVarGroup *parent)
|
ExposedVarGroup *parent)
|
||||||
: getter_(std::move(getter)) {
|
: getter_(std::move(getter))
|
||||||
handle_ = parent->Add(rel_path, this);
|
{
|
||||||
|
handle_ = parent->Add(rel_path, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Json::Value> ExposedVarDynamicTree::TryGet(Slice rel_path) const {
|
std::optional<Json::Value>
|
||||||
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
ExposedVarDynamicTree::TryGet(Slice rel_path) const
|
||||||
auto jsv = getter_();
|
{
|
||||||
Json::Value *ptr = &jsv;
|
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
||||||
|
auto jsv = getter_();
|
||||||
|
Json::Value *ptr = &jsv;
|
||||||
|
|
||||||
auto pieces = Split(real_path, '/');
|
auto pieces = Split(real_path, '/');
|
||||||
for (auto &&e : pieces) {
|
for (auto &&e : pieces) {
|
||||||
auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()});
|
auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()});
|
||||||
ptr = &(*ptr)[unescaped];
|
ptr = &(*ptr)[unescaped];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr->isNull()) {
|
if (ptr->isNull()) { return {}; }
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
@ -15,180 +15,189 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
namespace exposed_var {
|
namespace exposed_var {
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJsonValue(const T &t)
|
auto
|
||||||
-> enable_if_t<std::is_same<T, Json::Value>::value, Json::Value> {
|
ToJsonValue(const T &t) -> enable_if_t<std::is_same<T, Json::Value>::value, Json::Value>
|
||||||
return t;
|
{
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJsonValue(const T &t)
|
auto
|
||||||
-> enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
ToJsonValue(const T &t) -> enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, Json::Value>
|
||||||
Json::Value> {
|
{
|
||||||
return Json::Value(static_cast<Json::UInt64>(t));
|
return Json::Value(static_cast<Json::UInt64>(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJsonValue(const T &t)
|
auto
|
||||||
-> enable_if_t<std::is_integral<T>::value && !std::is_unsigned<T>::value,
|
ToJsonValue(const T &t) -> enable_if_t<std::is_integral<T>::value && !std::is_unsigned<T>::value, Json::Value>
|
||||||
Json::Value> {
|
{
|
||||||
return Json::Value(static_cast<Json::Int64>(t));
|
return Json::Value(static_cast<Json::Int64>(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJsonValue(const T &t)
|
auto
|
||||||
-> enable_if_t<std::is_floating_point<T>::value ||
|
ToJsonValue(const T &t) -> enable_if_t<std::is_floating_point<T>::value || std::is_same<T, std::string>::value
|
||||||
std::is_same<T, std::string>::value ||
|
|| std::is_same<T, const char *>::value || std::is_same<T, bool>::value,
|
||||||
std::is_same<T, const char *>::value ||
|
Json::Value>
|
||||||
std::is_same<T, bool>::value,
|
{
|
||||||
Json::Value> {
|
return Json::Value(t);
|
||||||
return Json::Value(t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
auto ToJsonValue(const T &t)
|
auto
|
||||||
-> enable_if_t<
|
ToJsonValue(const T &t) -> enable_if_t<!std::is_integral<T>::value && !std::is_floating_point<T>::value
|
||||||
!std::is_integral<T>::value && !std::is_floating_point<T>::value &&
|
&& !std::is_same<T, std::string>::value
|
||||||
!std::is_same<T, std::string>::value &&
|
&& !std::is_same<T, const char *>::value && !std::is_same<T, bool>::value
|
||||||
!std::is_same<T, const char *>::value &&
|
&& std::is_same<std::string, decltype(format_as(std::declval<T>()))>::value,
|
||||||
!std::is_same<T, bool>::value &&
|
Json::Value>
|
||||||
std::is_same<std::string,
|
{
|
||||||
decltype(format_as(std::declval<T>()))>::value,
|
return Json::Value(format_as(t));
|
||||||
Json::Value> {
|
|
||||||
return Json::Value(format_as(t));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> Json::Value ToJsonValue(const std::atomic<T> &v) {
|
template<class T>
|
||||||
return ToJsonValue(v.load(std::memory_order_relaxed));
|
Json::Value
|
||||||
|
ToJsonValue(const std::atomic<T> &v)
|
||||||
|
{
|
||||||
|
return ToJsonValue(v.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
} // namespace exposed_var
|
}// namespace exposed_var
|
||||||
|
|
||||||
class ExposedVarDynamicTree;
|
class ExposedVarDynamicTree;
|
||||||
|
|
||||||
class ExposedVarGroup {
|
class ExposedVarGroup {
|
||||||
public:
|
public:
|
||||||
using Handle = Deferred;
|
using Handle = Deferred;
|
||||||
|
|
||||||
Handle Add(Slice rel_path, std::function<Json::Value()> value);
|
Handle Add(Slice rel_path, std::function<Json::Value()> value);
|
||||||
Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree);
|
Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree);
|
||||||
static ExposedVarGroup *FindOrCreate(Slice abs_path);
|
static ExposedVarGroup *FindOrCreate(Slice abs_path);
|
||||||
static std::optional<Json::Value> TryGet(Slice abs_path);
|
static std::optional<Json::Value> TryGet(Slice abs_path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Getter = std::function<std::optional<Json::Value>(Slice)>;
|
using Getter = std::function<std::optional<Json::Value>(Slice)>;
|
||||||
|
|
||||||
explicit ExposedVarGroup(std::string abs_path);
|
explicit ExposedVarGroup(std::string abs_path);
|
||||||
const std::string &AbsolutePath() const;
|
const std::string &AbsolutePath() const;
|
||||||
|
|
||||||
ExposedVarGroup *FindLowest(Slice rel_path, Slice *left);
|
ExposedVarGroup *FindLowest(Slice rel_path, Slice *left);
|
||||||
ExposedVarGroup *CreateUpto(Slice rel_path);
|
ExposedVarGroup *CreateUpto(Slice rel_path);
|
||||||
Handle AddDirect(Slice name, Getter getter);
|
Handle AddDirect(Slice name, Getter getter);
|
||||||
Json::Value Dump() const;
|
Json::Value Dump() const;
|
||||||
static ExposedVarGroup *Root();
|
static ExposedVarGroup *Root();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string abs_path_;
|
std::string abs_path_;
|
||||||
mutable std::mutex lock_;
|
mutable std::mutex lock_;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<ExposedVarGroup>> nodes_;
|
std::unordered_map<std::string, std::unique_ptr<ExposedVarGroup>> nodes_;
|
||||||
std::unordered_map<std::string, Getter> leaves_;
|
std::unordered_map<std::string, Getter> leaves_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> class ExposedVar {
|
template<typename T>
|
||||||
|
class ExposedVar {
|
||||||
public:
|
public:
|
||||||
ExposedVar(Slice rel_path) {
|
ExposedVar(Slice rel_path) { LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); }
|
||||||
LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
template<typename U>
|
||||||
ExposedVar(Slice rel_path, U &&initial_value,
|
ExposedVar(Slice rel_path, U &&initial_value, ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
|
||||||
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
|
: obj_(std::forward<U>(initial_value))
|
||||||
: obj_(std::forward<U>(initial_value)) {
|
{
|
||||||
LinkToParent(rel_path, parent);
|
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:
|
private:
|
||||||
void LinkToParent(Slice rel_path, ExposedVarGroup *parent) {
|
void LinkToParent(Slice rel_path, ExposedVarGroup *parent)
|
||||||
handle_ = parent->Add(rel_path,
|
{
|
||||||
[this] { return exposed_var::ToJsonValue(obj_); });
|
handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(obj_); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T obj_{};
|
T obj_{};
|
||||||
ExposedVarGroup::Handle handle_;
|
ExposedVarGroup::Handle handle_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> class ExposedVarDynamic {
|
template<typename T>
|
||||||
|
class ExposedVarDynamic {
|
||||||
public:
|
public:
|
||||||
ExposedVarDynamic(
|
ExposedVarDynamic(Slice rel_path,
|
||||||
Slice rel_path, std::function<T()> getter,
|
std::function<T()> getter,
|
||||||
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
|
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
|
||||||
: getter_(std::move(getter)) {
|
: getter_(std::move(getter))
|
||||||
handle_ = parent->Add(
|
{
|
||||||
rel_path, [this] { return exposed_var::ToJsonValue(getter_()); });
|
handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(getter_()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<T()> getter_;
|
std::function<T()> getter_;
|
||||||
ExposedVarGroup::Handle handle_;
|
ExposedVarGroup::Handle handle_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExposedVarDynamicTree {
|
class ExposedVarDynamicTree {
|
||||||
public:
|
public:
|
||||||
ExposedVarDynamicTree(
|
ExposedVarDynamicTree(Slice rel_path,
|
||||||
Slice rel_path, std::function<Json::Value()> getter,
|
std::function<Json::Value()> getter,
|
||||||
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"));
|
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"));
|
||||||
|
|
||||||
std::optional<Json::Value> TryGet(Slice rel_path) const;
|
std::optional<Json::Value> TryGet(Slice rel_path) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<Json::Value()> getter_;
|
std::function<Json::Value()> getter_;
|
||||||
ExposedVarGroup::Handle handle_;
|
ExposedVarGroup::Handle handle_;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename T> struct IdentityTime {
|
template<typename T>
|
||||||
std::uint64_t operator()(const T &val) const {
|
struct IdentityTime {
|
||||||
return (ReadSteadyClock() - std::chrono::steady_clock::time_point()) /
|
std::uint64_t operator()(const T &val) const
|
||||||
std::chrono::nanoseconds(1);
|
{
|
||||||
}
|
return (ReadSteadyClock() - std::chrono::steady_clock::time_point()) / std::chrono::nanoseconds(1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
template <typename T, typename F = detail::IdentityTime<T>>
|
template<typename T, typename F = detail::IdentityTime<T>>
|
||||||
class ExposedMetrics {
|
class ExposedMetrics {
|
||||||
public:
|
public:
|
||||||
explicit ExposedMetrics(Slice rel_path) {
|
explicit ExposedMetrics(Slice rel_path) { LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); }
|
||||||
LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Json::Value ToJsonValue(const T &v) {
|
Json::Value ToJsonValue(const T &v)
|
||||||
Json::Value result;
|
{
|
||||||
std::unordered_map<std::string, T> m = {{"1s", 1}};
|
Json::Value result;
|
||||||
for (auto &&item : m) {
|
std::unordered_map<std::string, T> m = {
|
||||||
|
{"1s", 1}
|
||||||
|
};
|
||||||
|
for (auto &&item : m) {}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LinkToParent(Slice rel_path, ExposedVarGroup *parent) {
|
void LinkToParent(Slice rel_path, ExposedVarGroup *parent)
|
||||||
handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); });
|
{
|
||||||
}
|
handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); });
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T obj_{};
|
T obj_{};
|
||||||
ExposedVarGroup::Handle handle_;
|
ExposedVarGroup::Handle handle_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> using ExposedCounter = ExposedVar<T>;
|
template<typename T>
|
||||||
template <typename T> using ExposedGauge = ExposedVar<T>;
|
using ExposedCounter = ExposedVar<T>;
|
||||||
template <typename T> using ExposedMiner = ExposedVar<T>;
|
template<typename T>
|
||||||
template <typename T> using ExposedMaxer = ExposedVar<T>;
|
using ExposedGauge = ExposedVar<T>;
|
||||||
template <typename T> using ExposedAverager = ExposedVar<T>;
|
template<typename T>
|
||||||
|
using ExposedMiner = ExposedVar<T>;
|
||||||
|
template<typename T>
|
||||||
|
using ExposedMaxer = ExposedVar<T>;
|
||||||
|
template<typename T>
|
||||||
|
using ExposedAverager = ExposedVar<T>;
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
#endif // TILE_BASE_EXPOSED_VAR_H
|
#endif// TILE_BASE_EXPOSED_VAR_H
|
||||||
|
@ -4,14 +4,19 @@
|
|||||||
#include "json/json.h"
|
#include "json/json.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
ExposedVarGroup *GetFancyGroup() {
|
ExposedVarGroup *
|
||||||
return ExposedVarGroup::FindOrCreate("/a/b");
|
GetFancyGroup()
|
||||||
|
{
|
||||||
|
return ExposedVarGroup::FindOrCreate("/a/b");
|
||||||
}
|
}
|
||||||
Json::Value GetTree() {
|
|
||||||
Json::Value jsv;
|
Json::Value
|
||||||
jsv["dir"]["sub-dir"]["key"] = 5;
|
GetTree()
|
||||||
jsv["key"] = "6";
|
{
|
||||||
return jsv;
|
Json::Value jsv;
|
||||||
|
jsv["dir"]["sub-dir"]["key"] = 5;
|
||||||
|
jsv["key"] = "6";
|
||||||
|
return jsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedVar<int> v1("v1", 5);
|
ExposedVar<int> v1("v1", 5);
|
||||||
@ -21,18 +26,18 @@ ExposedVar<double> f1("f1", 6.2, ExposedVarGroup::FindOrCreate("/x/y/z"));
|
|||||||
auto GreatGroup = ExposedVarGroup::FindOrCreate("/a/b");
|
auto GreatGroup = ExposedVarGroup::FindOrCreate("/a/b");
|
||||||
|
|
||||||
// `/a/b/ds1`
|
// `/a/b/ds1`
|
||||||
ExposedVarDynamic<std::string>
|
ExposedVarDynamic<std::string> ds1("ds1", [] { return "test_str"; }, GetFancyGroup());
|
||||||
ds1("ds1", [] { return "test_str"; }, GetFancyGroup());
|
|
||||||
|
|
||||||
TEST(ExposedVar, Mutate) {
|
TEST(ExposedVar, Mutate)
|
||||||
auto opt = ExposedVarGroup::TryGet("/");
|
{
|
||||||
ASSERT_TRUE(opt);
|
auto opt = ExposedVarGroup::TryGet("/");
|
||||||
auto &&jsv = *opt;
|
ASSERT_TRUE(opt);
|
||||||
ASSERT_EQ(5, jsv["v1"].asInt());
|
auto &&jsv = *opt;
|
||||||
*v1 = 6;
|
ASSERT_EQ(5, jsv["v1"].asInt());
|
||||||
jsv = *ExposedVarGroup::TryGet("/");
|
*v1 = 6;
|
||||||
ASSERT_EQ(6, jsv["v1"].asInt());
|
jsv = *ExposedVarGroup::TryGet("/");
|
||||||
*v1 = 5;
|
ASSERT_EQ(6, jsv["v1"].asInt());
|
||||||
|
*v1 = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user