feat/update_config #10

Merged
tqcq merged 26 commits from feat/update_config into master 2024-10-14 10:15:01 +08:00
306 changed files with 42001 additions and 22939 deletions

78
.clang-format Normal file
View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -0,0 +1,3 @@
# Library
- [toml11](https://github.com/ToruNiina/toml11/archive/refs/tags/v4.2.0.tar.gz)

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -9,23 +9,24 @@
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()); const char *data() const noexcept override { return reinterpret_cast<const char *>(storage_.data()); }
}
std::size_t size() const noexcept override { return storage_.size(); } 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"); TILE_CHECK_GT(nb.ByteSize(), size, "No enough data");
std::size_t copied = 0; std::size_t copied = 0;
for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) { for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) {
@ -34,10 +35,10 @@ void FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer,
copied += 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();
@ -47,10 +48,9 @@ NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other)
} }
NoncontiguousBuffer & NoncontiguousBuffer &
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) { NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other)
if (TILE_UNLIKELY(&other == this)) { {
return *this; if (TILE_UNLIKELY(&other == this)) { return *this; }
}
Clear(); Clear();
byte_size_ = other.byte_size_; byte_size_ = other.byte_size_;
@ -63,7 +63,9 @@ NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) {
return *this; return *this;
} }
void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept { void
NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept
{
TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size"); TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size");
byte_size_ -= bytes; byte_size_ -= bytes;
@ -80,17 +82,18 @@ void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept {
} }
} }
void NoncontiguousBuffer::ClearSlow() noexcept { void
NoncontiguousBuffer::ClearSlow() noexcept
{
byte_size_ = 0; byte_size_ = 0;
while (!buffers_.empty()) { while (!buffers_.empty()) { object_pool::Put<buffer_t>(buffers_.pop_front()); }
object_pool::Put<buffer_t>(buffers_.pop_front());
}
} }
void NoncontiguousBufferBuilder::InitializeNextBlock() { void
NoncontiguousBufferBuilder::InitializeNextBlock()
{
if (current_) { if (current_) {
TILE_CHECK(SizeAvailable(), TILE_CHECK(SizeAvailable(), "You should flush current block by `FlushCurrentBlock()`");
"You should flush current block by `FlushCurrentBlock()`");
return; return;
} }
@ -98,7 +101,9 @@ void NoncontiguousBufferBuilder::InitializeNextBlock() {
used_ = 0; used_ = 0;
} }
void NoncontiguousBufferBuilder::FlushCurrentBlock() { void
NoncontiguousBufferBuilder::FlushCurrentBlock()
{
if (!used_) { if (!used_) {
// current block is empty // current block is empty
return; return;
@ -109,8 +114,9 @@ void NoncontiguousBufferBuilder::FlushCurrentBlock() {
current_ = nullptr; current_ = nullptr;
} }
void NoncontiguousBufferBuilder::AppendSlow(const void *ptr, void
std::size_t length) { NoncontiguousBufferBuilder::AppendSlow(const void *ptr, std::size_t length)
{
while (length) { while (length) {
auto copying = std::min(length, SizeAvailable()); auto copying = std::min(length, SizeAvailable());
memcpy(data(), ptr, copying); memcpy(data(), ptr, copying);
@ -120,24 +126,31 @@ void NoncontiguousBufferBuilder::AppendSlow(const void *ptr,
} }
} }
void NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer) { void
for (auto &&e : buffer) { NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer)
Append(e.data(), e.size()); {
} for (auto &&e : buffer) { Append(e.data(), e.size()); }
} }
NoncontiguousBuffer CreateBufferSlow(Slice s) { NoncontiguousBuffer
CreateBufferSlow(Slice s)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(s); nbb.Append(s);
return nbb.DestructiveGet(); return nbb.DestructiveGet();
} }
NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size) {
NoncontiguousBuffer
CreateBufferSlow(const void *ptr, std::size_t size)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(ptr, size); nbb.Append(ptr, size);
return nbb.DestructiveGet(); return nbb.DestructiveGet();
} }
std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes) { std::string
FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes)
{
max_bytes = std::min(max_bytes, nb.ByteSize()); max_bytes = std::min(max_bytes, nb.ByteSize());
std::string rc; std::string rc;
std::size_t left = max_bytes; std::size_t left = max_bytes;
@ -150,25 +163,22 @@ std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes) {
return rc; return rc;
} }
std::string FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim, std::string
std::size_t max_bytes) { FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim, std::size_t max_bytes)
if (nb.Empty()) { {
return {}; if (nb.Empty()) { return {}; }
}
{ {
Slice current(nb.FirstContiguous().data(), nb.FirstContiguous().size()); Slice current(nb.FirstContiguous().data(), nb.FirstContiguous().size());
auto pos = current.find(delim); auto pos = current.find(delim);
if (pos != Slice::npos) { if (pos != Slice::npos) {
auto expected_bytes = std::min(pos + delim.size(), max_bytes); auto expected_bytes = std::min(pos + delim.size(), max_bytes);
return std::string(nb.FirstContiguous().data(), return std::string(nb.FirstContiguous().data(), nb.FirstContiguous().data() + expected_bytes);
nb.FirstContiguous().data() + expected_bytes);
} }
} }
std::string rc; std::string rc;
for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes; for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes; ++iter) {
++iter) {
auto old_len = rc.size(); auto old_len = rc.size();
rc.append(iter->data(), iter->size()); rc.append(iter->data(), iter->size());
// find delim // find delim
@ -181,33 +191,32 @@ std::string FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim,
} }
} }
} }
if (rc.size() > max_bytes) { if (rc.size() > max_bytes) { rc.erase(rc.begin() + max_bytes, rc.end()); }
rc.erase(rc.begin() + max_bytes, rc.end());
}
return rc; return rc;
} }
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size) { PolymorphicBuffer
MakeReferencingBuffer(const void *ptr, std::size_t size)
{
return MakeReferencingBuffer(ptr, size, [] {}); return MakeReferencingBuffer(ptr, size, [] {});
} }
PolymorphicBuffer MakeForeignBuffer(std::string buffer) { PolymorphicBuffer
MakeForeignBuffer(std::string buffer)
{
auto size = buffer.size(); auto size = buffer.size();
return PolymorphicBuffer( return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0, size);
MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0,
size);
} }
template <typename T> template<typename T>
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer) { PolymorphicBuffer
MakeForeignBuffer(std::vector<T> buffer)
{
auto size = buffer.size() * sizeof(T); auto size = buffer.size() * sizeof(T);
return PolymorphicBuffer( return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::vector<T>>>(std::move(buffer)), 0, size);
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,17 +233,15 @@ 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); buffer.Skip(min_len);
prefix.RemovePrefix(min_len); prefix.RemovePrefix(min_len);
@ -242,24 +249,23 @@ bool StartsWith(NoncontiguousBuffer buffer, Slice prefix) {
return prefix.empty(); return prefix.empty();
} }
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix) { bool
EndsWith(NoncontiguousBuffer buffer, Slice suffix)
{
TILE_NOT_IMPLEMENTED(""); TILE_NOT_IMPLEMENTED("");
return false; return false;
} }
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) { bool
if (buffer.ByteSize() < prefix.size()) { StartsWithIgnoreCase(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 (!EqualsIgnoreCase(first.substr(0, min_len), if (!EqualsIgnoreCase(first.substr(0, min_len), prefix.substr(0, min_len))) { return false; }
prefix.substr(0, min_len))) {
return false;
}
buffer.Skip(min_len); buffer.Skip(min_len);
prefix.RemovePrefix(min_len); prefix.RemovePrefix(min_len);
@ -267,8 +273,10 @@ bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) {
return prefix.empty(); return prefix.empty();
} }
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix) { bool
EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix)
{
TILE_NOT_IMPLEMENTED(""); TILE_NOT_IMPLEMENTED("");
} }
} // namespace tile }// namespace tile

View File

@ -19,50 +19,63 @@
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
data(const char (&c)[N]) -> decltype(c)
{
return c; return c;
} }
template <typename T> constexpr std::size_t size(const T &c) { template<typename T>
constexpr std::size_t
size(const T &c)
{
return c.size(); 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> {
template<>
struct size_impl<0> {
static constexpr std::size_t size(const char (&c)[0]) { return 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<std::size_t N>
constexpr std::size_t
size(const char (&c)[N])
{
return size_impl<N>::size(c); return size_impl<N>::size(c);
} }
template <typename A1> constexpr std::size_t total_size(const A1 &a1) { template<typename A1>
constexpr std::size_t
total_size(const A1 &a1)
{
return size(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
total_size(const A1 &a1, const Args &...args)
{
return size(a1) + total_size(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;
@ -74,13 +87,15 @@ public:
// 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 NoncontiguousBuffer(NoncontiguousBuffer &&other) noexcept
: byte_size_(internal::Exchange(other.byte_size_, 0)), : byte_size_(internal::Exchange(other.byte_size_, 0)),
buffers_(std::move(other.buffers_)) {} buffers_(std::move(other.buffers_))
NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept { {}
if (TILE_UNLIKELY(&other == this)) {
return *this; NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept
} {
if (TILE_UNLIKELY(&other == this)) { return *this; }
Clear(); Clear();
std::swap(byte_size_, other.byte_size_); std::swap(byte_size_, other.byte_size_);
@ -90,13 +105,15 @@ public:
~NoncontiguousBuffer() { Clear(); } ~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 { void Skip(size_t bytes) noexcept
{
TILE_DCHECK_LE(bytes, ByteSize()); TILE_DCHECK_LE(bytes, ByteSize());
if (TILE_UNLIKELY(bytes == 0)) { if (TILE_UNLIKELY(bytes == 0)) {
} else if (bytes < buffers_.front().size()) { } else if (bytes < buffers_.front().size()) {
@ -107,7 +124,8 @@ public:
} }
} }
NoncontiguousBuffer Cut(std::size_t bytes) { NoncontiguousBuffer Cut(std::size_t bytes)
{
TILE_DCHECK_LE(bytes, ByteSize()); TILE_DCHECK_LE(bytes, ByteSize());
TILE_DCHECK_GE(bytes, 0); TILE_DCHECK_GE(bytes, 0);
@ -130,10 +148,9 @@ public:
return rc; return rc;
} }
void Append(PolymorphicBuffer buffer) { void Append(PolymorphicBuffer buffer)
if (TILE_UNLIKELY(buffer.size() == 0)) { {
return; if (TILE_UNLIKELY(buffer.size() == 0)) { return; }
}
auto block = object_pool::Get<PolymorphicBuffer>(); auto block = object_pool::Get<PolymorphicBuffer>();
*block = std::move(buffer); *block = std::move(buffer);
@ -141,27 +158,31 @@ public:
buffers_.push_back(block.Leak()); buffers_.push_back(block.Leak());
} }
void Append(NoncontiguousBuffer buffer) { void Append(NoncontiguousBuffer buffer)
{
byte_size_ += internal::Exchange(buffer.byte_size_, 0); byte_size_ += internal::Exchange(buffer.byte_size_, 0);
buffers_.splice(std::move(buffer.buffers_)); buffers_.splice(std::move(buffer.buffers_));
} }
std::size_t ByteSize() const noexcept { return byte_size_; } std::size_t ByteSize() const noexcept { return byte_size_; }
bool Empty() const noexcept { bool Empty() const noexcept
{
TILE_CHECK_EQ(buffers_.empty(), !byte_size_); TILE_CHECK_EQ(buffers_.empty(), !byte_size_);
return !byte_size_; return !byte_size_;
} }
void Clear() noexcept { void Clear() noexcept
if (!Empty()) { {
ClearSlow(); if (!Empty()) { ClearSlow(); }
}
} }
iterator begin() noexcept { return buffers_.begin(); } iterator begin() noexcept { return buffers_.begin(); }
iterator end() noexcept { return buffers_.end(); } iterator end() noexcept { return buffers_.end(); }
const_iterator begin() const noexcept { return buffers_.begin(); } const_iterator begin() const noexcept { return buffers_.begin(); }
const_iterator end() const noexcept { return buffers_.end(); } const_iterator end() const noexcept { return buffers_.end(); }
private: private:
@ -183,11 +204,10 @@ public:
// 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."); TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer.");
used_ += bytes; used_ += bytes;
// Is fulled ? // Is fulled ?
@ -199,11 +219,10 @@ public:
// Allocate new block // Allocate new block
// return write ptr // return write ptr
char *Reserve(std::size_t bytes) { char *Reserve(std::size_t bytes)
{
static const auto kMaxBytes = 1024; static const auto kMaxBytes = 1024;
TILE_CHECK_LE(bytes, kMaxBytes, TILE_CHECK_LE(bytes, kMaxBytes, "At most [{}] bytes may be reserved in single call.", kMaxBytes);
"At most [{}] bytes may be reserved in single call.",
kMaxBytes);
if (SizeAvailable() < bytes) { if (SizeAvailable() < bytes) {
FlushCurrentBlock(); FlushCurrentBlock();
InitializeNextBlock(); InitializeNextBlock();
@ -217,12 +236,14 @@ public:
// Total number of bytes written // Total number of bytes written
std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; } std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; }
NoncontiguousBuffer DestructiveGet() noexcept { NoncontiguousBuffer DestructiveGet() noexcept
{
FlushCurrentBlock(); FlushCurrentBlock();
return std::move(nb_); return std::move(nb_);
} }
void Append(const void *ptr, std::size_t length) { void Append(const void *ptr, std::size_t length)
{
auto current = data(); auto current = data();
used_ += length; used_ += length;
if (TILE_LIKELY(used_ < current_->size())) { if (TILE_LIKELY(used_ < current_->size())) {
@ -237,9 +258,9 @@ public:
void Append(const std::string &s) { Append(s.data(), s.size()); } void Append(const std::string &s) { Append(s.data(), s.size()); }
void Append(PolymorphicBuffer buffer) { void Append(PolymorphicBuffer buffer)
if (buffer.size() < kAppendViaCopyThreshold && {
SizeAvailable() >= buffer.size()) { if (buffer.size() < kAppendViaCopyThreshold && SizeAvailable() >= buffer.size()) {
Append(buffer.data(), buffer.size()); Append(buffer.data(), buffer.size());
return; return;
} }
@ -252,7 +273,8 @@ public:
nb_.Append(std::move(buffer)); nb_.Append(std::move(buffer));
} }
void Append(NoncontiguousBuffer buffer) { void Append(NoncontiguousBuffer buffer)
{
if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) { if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) {
AppendCopy(buffer); AppendCopy(buffer);
return; return;
@ -267,19 +289,18 @@ public:
void Append(char c) { Append(static_cast<unsigned char>(c)); } void Append(char c) { Append(static_cast<unsigned char>(c)); }
void Append(unsigned char c) { void Append(unsigned char c)
{
TILE_DCHECK(SizeAvailable()); TILE_DCHECK(SizeAvailable());
*reinterpret_cast<unsigned char *>(data()) = c; *reinterpret_cast<unsigned char *>(data()) = c;
MarkWritten(1); MarkWritten(1);
} }
template < template<typename... Ts,
typename... Ts, typename = internal::void_t<decltype(detail::data(std::declval<Ts &>()))...>,
typename = typename = internal::void_t<decltype(detail::size(std::declval<Ts &>()))...>>
internal::void_t<decltype(detail::data(std::declval<Ts &>()))...>, void Append(const Ts &...buffers)
typename = {
internal::void_t<decltype(detail::size(std::declval<Ts &>()))...>>
void Append(const Ts &...buffers) {
auto current = data(); auto current = data();
auto total = detail::total_size(buffers...); auto total = detail::total_size(buffers...);
used_ += total; used_ += total;
@ -291,8 +312,9 @@ public:
int dummy[] = {(Append(buffers), 0)...}; int dummy[] = {(Append(buffers), 0)...};
} }
template <typename T> template<typename T>
auto Append(T) -> enable_if_t<std::is_same<T, int>::value> { auto Append(T) -> enable_if_t<std::is_same<T, int>::value>
{
static_assert(sizeof(T) == 0, "Please use Append(char) instead."); static_assert(sizeof(T) == 0, "Please use Append(char) instead.");
} }
@ -305,13 +327,15 @@ private:
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)); memcpy(ptr, detail::data(slice), detail::size(slice));
UncheckedAppend(ptr + detail::size(slice), slices...); UncheckedAppend(ptr + detail::size(slice), slices...);
} }
@ -323,21 +347,20 @@ private:
}; };
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,
Slice delim,
std::size_t max_bytes = std::numeric_limits<std::size_t>::max()); std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
std::string FlattenSlowUntil(
const NoncontiguousBuffer &nb, Slice delim, inline void
std::size_t max_bytes = std::numeric_limits<std::size_t>::max()); FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t max_bytes)
inline void FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer, {
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);
} }
@ -346,28 +369,26 @@ inline void FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer,
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

View File

@ -19,52 +19,57 @@ 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 // WARNING: don't use adopt_ptr
return RefPtr<FixedNativeBufferBlock<kSize>>( return RefPtr<FixedNativeBufferBlock<kSize>>(
adopt_ptr, object_pool::Get<FixedNativeBufferBlock<kSize>>().Leak()); 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(); ptr->Save();
object_pool::Put<FixedNativeBufferBlock<kSize>>(ptr); 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(); } const char *data() const noexcept override { return buffer.data(); }
std::size_t size() const noexcept override { return buffer.size(); } std::size_t size() const noexcept override { return buffer.size(); }
void Destroy() noexcept override { Delete(this); } void Destroy() noexcept override { Delete(this); }
static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock); static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock);
std::array<char, kBufferSize> buffer; std::array<char, kBufferSize> buffer;
}; };
template <std::size_t kSize> template<std::size_t kSize>
RefPtr<NativeBufferBlock> MakeNativeBufferBlockOfBytes() { RefPtr<NativeBufferBlock>
MakeNativeBufferBlockOfBytes()
{
return FixedNativeBufferBlock<kSize>::New(); 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>}, {
static const std::unordered_map<std::string, RefPtr<NativeBufferBlock> (*)()> kMakers = {
{"4K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K> },
{"64K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_64K>}, {"64K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_64K>},
{"1M", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_1M>} {"1M", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_1M> }
}; };
auto iter = kMakers.find(FLAGS_tile_buffer_block_size); auto iter = kMakers.find(FLAGS_tile_buffer_block_size);
if (iter != kMakers.end()) { if (iter != kMakers.end()) {
make_native_buffer_block = iter->second; make_native_buffer_block = iter->second;
} else { } else {
TILE_UNEXPECTED( TILE_UNEXPECTED("Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block "
"Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block "
"is supported.", "is supported.",
FLAGS_tile_buffer_block_size); FLAGS_tile_buffer_block_size);
} }
@ -72,51 +77,50 @@ void InitializeMakeNativeBufferBlock() {
TILE_ON_INIT(0, InitializeMakeNativeBufferBlock); TILE_ON_INIT(0, InitializeMakeNativeBufferBlock);
} // namespace }// namespace
RefPtr<NativeBufferBlock> MakeNativeBufferBlock() { RefPtr<NativeBufferBlock>
MakeNativeBufferBlock()
{
return make_native_buffer_block(); return make_native_buffer_block();
} }
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>> { template<>
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>> {
static constexpr auto kType = PoolType::MemoryNodeShared; static constexpr auto kType = PoolType::MemoryNodeShared;
static constexpr auto kLowWaterMark = 16384; // 64M per node. static constexpr auto kLowWaterMark = 16384;// 64M per node.
static constexpr auto kHighWaterMark = static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
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<>
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>> {
static constexpr auto kType = PoolType::MemoryNodeShared; static constexpr auto kType = PoolType::MemoryNodeShared;
static constexpr auto kLowWaterMark = 1024; // 64M per node. static constexpr auto kLowWaterMark = 1024;// 64M per node.
static constexpr auto kHighWaterMark = static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
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<>
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>> {
static constexpr auto kType = PoolType::MemoryNodeShared; static constexpr auto kType = PoolType::MemoryNodeShared;
static constexpr auto kLowWaterMark = 128; // 128M per node. static constexpr auto kLowWaterMark = 128;// 128M per node.
static constexpr auto kHighWaterMark = static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
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

View File

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

View File

@ -9,26 +9,28 @@
#include <vector> #include <vector>
namespace tile { namespace tile {
template <typename T> class CircularBuffer { template<typename T>
class CircularBuffer {
class UninitializedObject; 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>
bool Emplace(Ts &&...args)
{
auto head = head_.load(std::memory_order_relaxed); auto head = head_.load(std::memory_order_relaxed);
auto next = NormalizeIndex(head + 1); 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)...); objects_[head].Initialize(std::forward<Ts>(args)...);
head_.store(next, std::memory_order_release); head_.store(next, std::memory_order_release);
return true; return true;
} }
void Pop(std::vector<T> *objects) { void Pop(std::vector<T> *objects)
{
auto upto = head_.load(std::memory_order_acquire); auto upto = head_.load(std::memory_order_acquire);
auto current = tail_.load(std::memory_order_relaxed); auto current = tail_.load(std::memory_order_relaxed);
while (current != upto) { while (current != upto) {
@ -44,7 +46,10 @@ 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) {
template<typename... Ts>
void Initialize(Ts &&...args)
{
new (Get()) T(std::forward<Ts>(args)...); new (Get()) T(std::forward<Ts>(args)...);
} }
@ -53,15 +58,14 @@ private:
private: private:
std::aligned_storage<sizeof(T), alignof(T)> storage_; std::aligned_storage<sizeof(T), alignof(T)> storage_;
}; };
std::size_t NormalizeIndex(std::size_t x) {
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

View File

@ -2,32 +2,28 @@
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() { void
NoncontiguousBufferCompressionOutputStream::Flush()
{
if (using_bytes_ > 0) { if (using_bytes_ > 0) {
builder_->MarkWritten(using_bytes_); builder_->MarkWritten(using_bytes_);
using_bytes_ = 0; using_bytes_ = 0;
} }
} }
bool NoncontiguousBufferCompressionOutputStream::Next( bool
void **data, std::size_t *size) noexcept { NoncontiguousBufferCompressionOutputStream::Next(void **data, std::size_t *size) noexcept
if (!builder_) { {
return false; if (!builder_) { return false; }
}
if (using_bytes_) { if (using_bytes_) { builder_->MarkWritten(using_bytes_); }
builder_->MarkWritten(using_bytes_);
}
*data = builder_->data(); *data = builder_->data();
*size = builder_->SizeAvailable(); *size = builder_->SizeAvailable();
@ -36,8 +32,9 @@ bool NoncontiguousBufferCompressionOutputStream::Next(
return true; return true;
} }
void NoncontiguousBufferCompressionOutputStream::BackUp( void
std::size_t count) noexcept { NoncontiguousBufferCompressionOutputStream::BackUp(std::size_t count) noexcept
{
using_bytes_ -= count; using_bytes_ -= count;
} }
} // namespace tile }// namespace tile

View File

@ -6,11 +6,9 @@
#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();
@ -22,6 +20,6 @@ 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

View File

@ -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
PoolTraits<PolymorphicBuffer>::OnPut(PolymorphicBuffer *bb)
{
bb->Clear(); bb->Clear();
} }
namespace detail { namespace detail {
void PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p) { void
PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p)
{
TILE_DCHECK_EQ(p->UnsafeRefCount(), 0); TILE_DCHECK_EQ(p->UnsafeRefCount(), 0);
p->Destroy(); p->Destroy();
} }
} // namespace detail }// namespace detail
} // namespace tile }// namespace tile

View File

@ -19,21 +19,18 @@ 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;
@ -41,10 +38,15 @@ public:
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_),
size_(other.size_),
ref_(std::move(other.ref_))
{
other.Clear(); other.Clear();
} }
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept {
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept
{
if (this != &other) { if (this != &other) {
ptr_ = other.ptr_; ptr_ = other.ptr_;
size_ = other.size_; size_ = other.size_;
@ -54,26 +56,31 @@ public:
return *this; return *this;
} }
PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start, PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
std::size_t size) : ptr_(data->data() + start),
: ptr_(data->data() + start), size_(size), ref_(std::move(data)) {} size_(size),
ref_(std::move(data))
{}
const char *data() const noexcept { return ptr_; } const char *data() const noexcept { return ptr_; }
std::size_t size() const noexcept { return size_; } std::size_t size() const noexcept { return size_; }
void Skip(std::size_t bytes) { void Skip(std::size_t bytes)
{
TILE_CHECK_LT(bytes, size_); TILE_CHECK_LT(bytes, size_);
size_ -= bytes; size_ -= bytes;
ptr_ += bytes; ptr_ += bytes;
} }
void set_size(std::size_t size) { void set_size(std::size_t size)
{
TILE_DCHECK_LE(size, size_); TILE_DCHECK_LE(size, size_);
size_ = size; size_ = size;
} }
void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start, void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
std::size_t size) { {
TILE_DCHECK_LE(start, size); TILE_DCHECK_LE(start, size);
TILE_DCHECK_LE(size, data->size()); TILE_DCHECK_LE(size, data->size());
@ -82,7 +89,8 @@ public:
size_ = size; size_ = size;
} }
void Clear() { void Clear()
{
ptr_ = nullptr; ptr_ = nullptr;
size_ = 0; size_ = 0;
ref_ = nullptr; ref_ = nullptr;
@ -96,25 +104,26 @@ private:
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<>
struct PoolTraits<PolymorphicBuffer> {
static constexpr auto kType = PoolType::MemoryNodeShared; static constexpr auto kType = PoolType::MemoryNodeShared;
static constexpr std::size_t kLowWaterMark = 32768; static constexpr std::size_t kLowWaterMark = 32768;
static constexpr std::size_t kHighWaterMark = static constexpr std::size_t kHighWaterMark = std::numeric_limits<std::size_t>::max();
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

View File

@ -14,22 +14,26 @@ namespace tile {
namespace { namespace {
PolymorphicBuffer MakeNativeBuffer(Slice s) { PolymorphicBuffer
MakeNativeBuffer(Slice s)
{
auto buffer = MakeNativeBufferBlock(); auto buffer = MakeNativeBufferBlock();
memcpy(buffer->mutable_data(), s.data(), s.size()); memcpy(buffer->mutable_data(), s.data(), s.size());
return PolymorphicBuffer(buffer, 0, 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~!#"); static const auto kData = Slice("sadfas234sadf-/+8sdaf sd f~!#");
auto nb = CreateBufferSlow(kData); auto nb = CreateBufferSlow(kData);
ASSERT_EQ(kData, nb.FirstContiguous().data()); ASSERT_EQ(kData, nb.FirstContiguous().data());
ASSERT_EQ(kData, FlattenSlow(nb)); ASSERT_EQ(kData, FlattenSlow(nb));
} }
TEST(NoncontiguousBuffer, Cut) { TEST(NoncontiguousBuffer, Cut)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(CreateBufferSlow("asdf")); nb.Append(CreateBufferSlow("asdf"));
auto r = nb.Cut(3); auto r = nb.Cut(3);
@ -39,7 +43,8 @@ TEST(NoncontiguousBuffer, Cut) {
ASSERT_EQ("asd", FlattenSlow(r)); ASSERT_EQ("asd", FlattenSlow(r));
} }
TEST(NoncontiguousBuffer, Cut1) { TEST(NoncontiguousBuffer, Cut1)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(CreateBufferSlow("asdf")); nb.Append(CreateBufferSlow("asdf"));
auto r = nb.Cut(4); auto r = nb.Cut(4);
@ -47,7 +52,8 @@ TEST(NoncontiguousBuffer, Cut1) {
ASSERT_EQ(4, r.ByteSize()); ASSERT_EQ(4, r.ByteSize());
} }
TEST(NoncontiguousBuffer, Cut2) { TEST(NoncontiguousBuffer, Cut2)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asdf")); nb.Append(MakeNativeBuffer("asdf"));
nb.Append(MakeNativeBuffer("asdf")); nb.Append(MakeNativeBuffer("asdf"));
@ -56,7 +62,8 @@ TEST(NoncontiguousBuffer, Cut2) {
ASSERT_EQ(4, r.ByteSize()); ASSERT_EQ(4, r.ByteSize());
} }
TEST(NoncontiguousBuffer, Cut3) { TEST(NoncontiguousBuffer, Cut3)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asdf")); nb.Append(MakeNativeBuffer("asdf"));
nb.Append(MakeNativeBuffer("asdf")); nb.Append(MakeNativeBuffer("asdf"));
@ -65,7 +72,8 @@ TEST(NoncontiguousBuffer, Cut3) {
ASSERT_EQ(8, r.ByteSize()); ASSERT_EQ(8, r.ByteSize());
} }
TEST(NoncontiguousBuffer, Cut4) { TEST(NoncontiguousBuffer, Cut4)
{
auto nb = CreateBufferSlow("asdfasf2345sfsdfdf"); auto nb = CreateBufferSlow("asdfasf2345sfsdfdf");
auto nb2 = nb; auto nb2 = nb;
ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2)); ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2));
@ -78,7 +86,8 @@ TEST(NoncontiguousBuffer, Cut4) {
ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited)); ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited));
} }
TEST(NoncontiguousBuffer, Skip) { TEST(NoncontiguousBuffer, Skip)
{
NoncontiguousBuffer splited; NoncontiguousBuffer splited;
splited.Append(CreateBufferSlow("asdf")); splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf")); splited.Append(CreateBufferSlow("asdf"));
@ -92,31 +101,35 @@ TEST(NoncontiguousBuffer, Skip) {
ASSERT_EQ(0, splited.ByteSize()); ASSERT_EQ(0, splited.ByteSize());
} }
TEST(NoncontiguousBuffer, Skip2) { TEST(NoncontiguousBuffer, Skip2)
{
NoncontiguousBuffer buffer; NoncontiguousBuffer buffer;
EXPECT_TRUE(buffer.Empty()); EXPECT_TRUE(buffer.Empty());
buffer.Skip(0); // Don't crash. buffer.Skip(0);// Don't crash.
EXPECT_TRUE(buffer.Empty()); EXPECT_TRUE(buffer.Empty());
} }
TEST(NoncontiguousBuffer, FlattenSlow) { TEST(NoncontiguousBuffer, FlattenSlow)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234")); nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10)); ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10));
} }
TEST(NoncontiguousBuffer, FlattenToSlow) { TEST(NoncontiguousBuffer, FlattenToSlow)
{
struct C { struct C {
std::uint64_t ll; std::uint64_t ll;
int i; int i;
bool f; bool f;
}; };
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78\x9a\xbc\xde\xf0", 8))); nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78\x9a\xbc\xde\xf0", 8)));
nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78", 4))); nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78", 4)));
nb.Append(MakeNativeBuffer(Slice("\x1", 1))); nb.Append(MakeNativeBuffer(Slice("\x1", 1)));
nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3))); // Padding nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3)));// Padding
C c; C c;
FlattenToSlow(nb, &c, sizeof(C)); FlattenToSlow(nb, &c, sizeof(C));
ASSERT_EQ(0xf0debc9a78563412, c.ll); ASSERT_EQ(0xf0debc9a78563412, c.ll);
@ -124,7 +137,8 @@ TEST(NoncontiguousBuffer, FlattenToSlow) {
ASSERT_EQ(true, c.f); ASSERT_EQ(true, c.f);
} }
TEST(NoncontiguousBuffer, FlattenSlowUntil) { TEST(NoncontiguousBuffer, FlattenSlowUntil)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234")); nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
@ -137,7 +151,8 @@ TEST(NoncontiguousBuffer, FlattenSlowUntil) {
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5)); ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5));
} }
TEST(NoncontiguousBuffer, FlattenSlowUntil2) { TEST(NoncontiguousBuffer, FlattenSlowUntil2)
{
auto nb = CreateBufferSlow( auto nb = CreateBufferSlow(
"HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: " "HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
"0\r\nRpc-Error-Reason: The operation completed " "0\r\nRpc-Error-Reason: The operation completed "
@ -151,7 +166,8 @@ TEST(NoncontiguousBuffer, FlattenSlowUntil2) {
FlattenSlowUntil(nb, "\r\n\r\n")); FlattenSlowUntil(nb, "\r\n\r\n"));
} }
TEST(NoncontiguousBuffer, FlattenSlowUntil3) { TEST(NoncontiguousBuffer, FlattenSlowUntil3)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234")); nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342")); nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
@ -160,14 +176,16 @@ TEST(NoncontiguousBuffer, FlattenSlowUntil3) {
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX")); ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX"));
} }
TEST(NoncontiguousBuffer, FlattenSlowUntil4) { TEST(NoncontiguousBuffer, FlattenSlowUntil4)
{
NoncontiguousBuffer nb; NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("AB")); nb.Append(MakeNativeBuffer("AB"));
nb.Append(MakeNativeBuffer("CDEFGGGGHHHH")); nb.Append(MakeNativeBuffer("CDEFGGGGHHHH"));
ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG")); ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG"));
} }
TEST(NoncontiguousBufferBuilder, Append) { TEST(NoncontiguousBufferBuilder, Append)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer("")); nbb.Append(MakeForeignBuffer(""));
nbb.Append(MakeForeignBuffer("small")); nbb.Append(MakeForeignBuffer("small"));
@ -176,11 +194,11 @@ TEST(NoncontiguousBufferBuilder, Append) {
nbb.Append(CreateBufferSlow("small")); nbb.Append(CreateBufferSlow("small"));
nbb.Append(CreateBufferSlow(std::string(8192, 'a'))); nbb.Append(CreateBufferSlow(std::string(8192, 'a')));
auto nb = nbb.DestructiveGet(); auto nb = nbb.DestructiveGet();
EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'), EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'), FlattenSlow(nb));
FlattenSlow(nb));
} }
TEST(NoncontiguousBufferBuilder, Reserve) { TEST(NoncontiguousBufferBuilder, Reserve)
{
auto temp_block = MakeNativeBufferBlock(); auto temp_block = MakeNativeBufferBlock();
auto max_bytes = temp_block->size(); auto max_bytes = temp_block->size();
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
@ -190,7 +208,7 @@ TEST(NoncontiguousBufferBuilder, Reserve) {
ASSERT_EQ(ptr + 10, nbb.data()); ASSERT_EQ(ptr + 10, nbb.data());
nbb.Append(std::string(max_bytes - 10 - 1, 'a')); nbb.Append(std::string(max_bytes - 10 - 1, 'a'));
ptr = nbb.data(); ptr = nbb.data();
ptr2 = nbb.Reserve(1); // Last byte in the block. ptr2 = nbb.Reserve(1);// Last byte in the block.
ASSERT_EQ(ptr, ptr2); ASSERT_EQ(ptr, ptr2);
ASSERT_EQ(max_bytes, nbb.SizeAvailable()); ASSERT_EQ(max_bytes, nbb.SizeAvailable());
@ -201,7 +219,8 @@ TEST(NoncontiguousBufferBuilder, Reserve) {
ASSERT_EQ(ptr2 + 2, nbb.data()); ASSERT_EQ(ptr2 + 2, nbb.data());
} }
TEST(NoncontiguousBufferBuilder, DestructiveGet1) { TEST(NoncontiguousBufferBuilder, DestructiveGet1)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append("asdf1234", 6); nbb.Append("asdf1234", 6);
nbb.Append("1122", 4); nbb.Append("1122", 4);
@ -210,45 +229,51 @@ TEST(NoncontiguousBufferBuilder, DestructiveGet1) {
FlattenSlow(nbb.DestructiveGet())); FlattenSlow(nbb.DestructiveGet()));
} }
TEST(NoncontiguousBufferBuilder, DestructiveGet2) { TEST(NoncontiguousBufferBuilder, DestructiveGet2)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append("aabbccd"); nbb.Append("aabbccd");
ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet())); ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet()));
} }
TEST(NoncontiguousBufferBuilder, DestructiveGet3) { TEST(NoncontiguousBufferBuilder, DestructiveGet3)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(std::string(1000000, 'A')); nbb.Append(std::string(1000000, 'A'));
ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet())); ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet()));
} }
TEST(NoncontiguousBufferBuilder, DestructiveGet4) { TEST(NoncontiguousBufferBuilder, DestructiveGet4)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append('c'); nbb.Append('c');
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet())); ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
} }
TEST(NoncontiguousBufferBuilder, DestructiveGet5) { TEST(NoncontiguousBufferBuilder, DestructiveGet5)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(CreateBufferSlow("c")); nbb.Append(CreateBufferSlow("c"));
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet())); ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
} }
TEST(NoncontiguousBufferBuilder, DestructiveGet6) { TEST(NoncontiguousBufferBuilder, DestructiveGet6)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"), nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"), Slice("6"));
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; NoncontiguousBufferBuilder nbb;
nbb.Append(MakeReferencingBuffer("abcdefg", 7)); nbb.Append(MakeReferencingBuffer("abcdefg", 7));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
} }
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) { TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized)
{
int x = 0; int x = 0;
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
@ -261,7 +286,8 @@ TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) {
EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer)); EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer));
} }
TEST(MakeReferencingBuffer, WithCallback) { TEST(MakeReferencingBuffer, WithCallback)
{
static const std::string kBuffer(12345, 'a'); static const std::string kBuffer(12345, 'a');
int x = 0; int x = 0;
@ -277,13 +303,15 @@ TEST(MakeReferencingBuffer, WithCallback) {
EXPECT_EQ(1, x); EXPECT_EQ(1, x);
} }
TEST(MakeForeignBuffer, String) { TEST(MakeForeignBuffer, String)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::string("abcdefg"))); nbb.Append(MakeForeignBuffer(std::string("abcdefg")));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
} }
TEST(MakeForeignBuffer, VectorOfChar) { TEST(MakeForeignBuffer, VectorOfChar)
{
std::vector<char> data{'a', 'b', 'c', 'd', 'e', 'f', 'g'}; 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::move(data)));
@ -299,7 +327,8 @@ 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; std::vector<std::uint8_t> data;
data.resize(7); data.resize(7);
memcpy(data.data(), "abcdefg", 7); memcpy(data.data(), "abcdefg", 7);
@ -308,4 +337,4 @@ TEST(MakeForeignBuffer, VectorOfUInt8) {
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet())); EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
} }
} // namespace tile }// namespace tile

View File

@ -41,11 +41,27 @@
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
@ -53,7 +69,9 @@ inline bool IsHostLittleEndian() {
#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
@ -61,7 +79,9 @@ inline bool IsHostBigEndian() {
#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
@ -71,7 +91,9 @@ inline uint16_t HostToNetwork16(uint16_t val_in_host) {
#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
@ -81,7 +103,9 @@ inline uint32_t HostToNetwork32(uint32_t val_in_host) {
#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
@ -91,7 +115,9 @@ inline uint64_t HostToNetwork64(uint64_t val_in_host) {
#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
@ -101,7 +127,9 @@ inline uint16_t NetworkToHost16(uint16_t val_in_network) {
#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
@ -111,7 +139,9 @@ inline uint32_t NetworkToHost32(uint32_t val_in_network) {
#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
@ -121,6 +151,6 @@ inline uint64_t NetworkToHost64(uint64_t val_in_network) {
#endif #endif
} }
} // namespace tile }// namespace tile
#endif // TILE_BASE_BYTE_ORDER_H #endif// TILE_BASE_BYTE_ORDER_H

View File

@ -12,25 +12,33 @@ static union EndianHelper {
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 val = 0x1234;
const uint16_t expected = 0x3412; const uint16_t expected = 0x3412;
ASSERT_EQ(expected, ByteSwap16(val)); ASSERT_EQ(expected, ByteSwap16(val));
} }
TEST(ByteOrder, ByteSwap32) {
TEST(ByteOrder, ByteSwap32)
{
const uint32_t val = 0x12345678; const uint32_t val = 0x12345678;
const uint32_t expected = 0x78563412; const uint32_t expected = 0x78563412;
ASSERT_EQ(expected, ByteSwap32(val)); ASSERT_EQ(expected, ByteSwap32(val));
} }
TEST(ByteOrder, ByteSwap64) {
TEST(ByteOrder, ByteSwap64)
{
const uint64_t val = 0x1234567890abcdef; const uint64_t val = 0x1234567890abcdef;
const uint64_t expected = 0xefcdab9078563412; const uint64_t expected = 0xefcdab9078563412;
ASSERT_EQ(expected, ByteSwap64(val)); ASSERT_EQ(expected, ByteSwap64(val));
} }
TEST(ByteOrder, IsHostByteOrder) { TEST(ByteOrder, IsHostByteOrder)
{
ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian()); ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian());
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN #if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
ASSERT_TRUE(IsHostBigEndian()); ASSERT_TRUE(IsHostBigEndian());
@ -41,7 +49,8 @@ TEST(ByteOrder, IsHostByteOrder) {
#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;
@ -53,7 +62,8 @@ TEST(ByteOrder, HostToNetwork16) {
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;
@ -65,7 +75,8 @@ TEST(ByteOrder, HostToNetwork32) {
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;
@ -77,7 +88,8 @@ TEST(ByteOrder, HostToNetwork64) {
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;
@ -89,7 +101,8 @@ TEST(ByteOrder, NetworkToHost16) {
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;
@ -101,7 +114,8 @@ TEST(ByteOrder, NetworkToHost32) {
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;
@ -113,4 +127,4 @@ TEST(ByteOrder, NetworkToHost64) {
ASSERT_EQ(expected, NetworkToHost64(val)); ASSERT_EQ(expected, NetworkToHost64(val));
} }
} // namespace tile }// namespace tile

View File

@ -12,28 +12,30 @@
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;
} }
}; };
@ -42,27 +44,30 @@ 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>
friend struct CastingTraits;
~Castable() = default; ~Castable() = default;
template <typename T> void SetRuntimeTypeTo() { template<typename T>
void SetRuntimeTypeTo()
{
SetRuntimeType(this, GetRuntimeTypeId<T>()); SetRuntimeType(this, GetRuntimeTypeId<T>());
} }
template <typename T> int GetRuntimeType() const { template<typename T>
int GetRuntimeType() const
{
return GetRuntimeTypeId<T>(); return GetRuntimeTypeId<T>();
} }
template <typename T> static int GetRuntimeTypeId() { template<typename T>
static int GetRuntimeTypeId()
{
static const int value = internal::IndexAlloc::For<Castable>()->Next(); static const int value = internal::IndexAlloc::For<Castable>()->Next();
return value; return value;
} }
@ -77,46 +82,55 @@ 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
RuntimeTypeCheck(const Base &val)
{
return CastingTraits<T>::RuntimeTypeCheck(val); return CastingTraits<T>::RuntimeTypeCheck(val);
} }
template <typename T> inline bool RuntimeTypeCheck(const T &val) { template<typename T>
inline bool
RuntimeTypeCheck(const T &val)
{
return true; 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) template<typename T, typename Base>
-> enable_if_t<!std::has_virtual_destructor<T>::value, bool> { auto
ReallyCastable(Base *ptr) -> enable_if_t<!std::has_virtual_destructor<T>::value, bool>
{
return typeid(T) == typeid(*ptr); return typeid(T) == typeid(*ptr);
} }
template <typename T, typename Base> void InvalidCast(Base *ptr) { template<typename T, typename Base>
void
InvalidCast(Base *ptr)
{
if (ReallyCastable<T, Base>(ptr)) { if (ReallyCastable<T, Base>(ptr)) {
TILE_LOG_FATAL( TILE_LOG_FATAL("Casting to type [{}] failed. However, the C++ runtime reports that "
"Casting to type [{}] failed. However, the C++ runtime reports that "
"you're indeed casting to the (right) runtime type of the object. This " "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 " "can happen when either: 1) you haven't initialize object's runtime "
"type correctly (e.g. via `SetRuntimeTypeTo` if you're inheriting from " "type correctly (e.g. via `SetRuntimeTypeTo` if you're inheriting from "
@ -124,66 +138,74 @@ template <typename T, typename Base> void InvalidCast(Base *ptr) {
"incorrect.", "incorrect.",
GetTypeName<T>()); GetTypeName<T>());
} else { } else {
TILE_LOG_FATAL( TILE_LOG_FATAL("Invalid cast: Runtime type [{}] expected, got [{}]. If you believe "
"Invalid cast: Runtime type [{}] expected, got [{}]. If you believe "
"this is an error, check if your `classof` is implemented correctly", "this is an error, check if your `classof` is implemented correctly",
GetTypeName<T>(), GetTypeName(*ptr)); GetTypeName<T>(), GetTypeName(*ptr));
} }
TILE_UNREACHABLE(""); TILE_UNREACHABLE("");
} }
} // namespace detail }// namespace detail
} // namespace casting }// namespace casting
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 bool
inline bool isa(const Base &val) { isa(const Base &val)
{
return casting::detail::RuntimeTypeCheck<T>(val); return casting::detail::RuntimeTypeCheck<T>(val);
} }
template <typename T, typename Base> inline bool isa(const Base *val) { template<typename T, typename Base>
inline bool
isa(const Base *val)
{
return isa<T>(*val); return isa<T>(*val);
} }
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>> R
R dyn_cast(Base *ptr) { dyn_cast(Base *ptr)
{
return isa<T>(ptr) ? static_cast<R>(ptr) : nullptr; return isa<T>(ptr) ? static_cast<R>(ptr) : 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
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
dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast<T>(ptr))
{
return ptr ? dyn_cast<T>(ptr) : nullptr; 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
cast_or_null(Base *ptr) -> decltype(cast<T>(ptr))
{
return ptr ? cast<T>(ptr) : nullptr; return ptr ? cast<T>(ptr) : nullptr;
} }
} // namespace tile }// namespace tile
#endif // TILE_BASE_CASTING_H #endif// TILE_BASE_CASTING_H

View File

@ -8,15 +8,14 @@ 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 {
@ -43,28 +42,28 @@ 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

View File

@ -15,9 +15,7 @@ struct Base {
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 {
@ -42,23 +40,27 @@ struct C3 : C1 {
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); } C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
}; };
TEST(CastingDeathTest, InvalidCast) { TEST(CastingDeathTest, InvalidCast)
{
auto pa = make_unique<A>(); auto pa = make_unique<A>();
ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast"); ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast");
} }
TEST(Casting, Nullptr) { TEST(Casting, Nullptr)
{
B *pb = nullptr; B *pb = nullptr;
ASSERT_EQ(nullptr, dyn_cast_or_null<A>(pb)); ASSERT_EQ(nullptr, dyn_cast_or_null<A>(pb));
ASSERT_EQ(nullptr, cast_or_null<A>(pb)); ASSERT_EQ(nullptr, cast_or_null<A>(pb));
} }
TEST(Casting, DownCastFailure) { TEST(Casting, DownCastFailure)
{
auto pa = make_unique<A>(); auto pa = make_unique<A>();
ASSERT_EQ(nullptr, dyn_cast<B>(pa.get())); ASSERT_EQ(nullptr, dyn_cast<B>(pa.get()));
} }
TEST(Casting, Cast) { TEST(Casting, Cast)
{
auto pb = make_unique<B>(); auto pb = make_unique<B>();
Base *ptr = pb.get(); Base *ptr = pb.get();
@ -68,14 +70,15 @@ TEST(Casting, Cast) {
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>(); auto pb = make_unique<const B>();
const Base *ptr = pb.get(); const Base *ptr = pb.get();
@ -85,19 +88,20 @@ TEST(Casting, CastWithConst) {
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>(); auto pc2 = make_unique<C2>();
C1 *p = pc2.get(); C1 *p = pc2.get();
ASSERT_NE(nullptr, dyn_cast<C1>(p)); // Success. ASSERT_NE(nullptr, dyn_cast<C1>(p));// Success.
ASSERT_NE(nullptr, dyn_cast<C2>(p)); // Success. ASSERT_NE(nullptr, dyn_cast<C2>(p));// Success.
ASSERT_EQ(nullptr, dyn_cast<C3>(p)); // Failure. ASSERT_EQ(nullptr, dyn_cast<C3>(p));// Failure.
} }
} // namespace tile }// namespace tile

View File

@ -15,30 +15,34 @@
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<>
struct Epoch<std::chrono::system_clock> {
static constexpr auto value = static constexpr auto value =
std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1716543273));
std::chrono::seconds(1716543273));
}; };
template <typename Clock> template<typename Clock>
inline typename Clock::time_point ReadClock(clockid_t type) { inline typename Clock::time_point
ReadClock(clockid_t type)
{
timespec ts; timespec ts;
clock_gettime(type, &ts); clock_gettime(type, &ts);
const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec; const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec;
auto duration = std::chrono::duration_cast<typename Clock::duration>( auto duration = std::chrono::duration_cast<typename Clock::duration>(std::chrono::nanoseconds(ns));
std::chrono::nanoseconds(ns));
return typename Clock::time_point(duration); return typename Clock::time_point(duration);
} }
void Sleep(long ns) { void
Sleep(long ns)
{
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = (ns + 999) / 1000; timeout.tv_usec = (ns + 999) / 1000;
@ -46,17 +50,20 @@ void Sleep(long ns) {
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
UpdateCoarseTimestamps()
{
async_updated_timestamps.steady_clock_time_since_epoch.store( async_updated_timestamps.steady_clock_time_since_epoch.store(
ReadSteadyClock().time_since_epoch(), ReadSteadyClock().time_since_epoch(),
// std::chrono::steady_clock::now().time_since_epoch(), // std::chrono::steady_clock::now().time_since_epoch(),
@ -66,13 +73,19 @@ void UpdateCoarseTimestamps() {
// std::chrono::system_clock::now().time_since_epoch(), // std::chrono::system_clock::now().time_since_epoch(),
std::memory_order_relaxed); std::memory_order_relaxed);
} }
constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy; constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy;
CoarseClockInitializer *CoarseClockInitializer::Instance() {
CoarseClockInitializer *
CoarseClockInitializer::Instance()
{
static NeverDestroyedSingleton<CoarseClockInitializer> cci; static NeverDestroyedSingleton<CoarseClockInitializer> cci;
return cci.Get(); return cci.Get();
} }
void CoarseClockInitializer::Start() { void
CoarseClockInitializer::Start()
{
if (!internal::TestAndSet(running_, false, true)) { if (!internal::TestAndSet(running_, false, true)) {
TILE_LOG_WARNING("CoarseClockInitializer is already running"); TILE_LOG_WARNING("CoarseClockInitializer is already running");
return; return;
@ -81,11 +94,8 @@ void CoarseClockInitializer::Start() {
UpdateCoarseTimestamps(); UpdateCoarseTimestamps();
worker_ = make_unique<std::thread>([this] { worker_ = make_unique<std::thread>([this] {
const auto accuracy_as_ns = const auto accuracy_as_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(kAccuracy).count();
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);
TILE_CHECK_GE(accuracy_as_ns, 2000,
"accuracy is too small, MUST GE 2000ns, current is {}",
kAccuracy);
while (running_.load(std::memory_order_relaxed)) { while (running_.load(std::memory_order_relaxed)) {
// std::this_thread::sleep_for(std::chrono::nanoseconds(500)); // std::this_thread::sleep_for(std::chrono::nanoseconds(500));
@ -96,11 +106,15 @@ void CoarseClockInitializer::Start() {
}); });
} }
void CoarseClockInitializer::Stop() { void
CoarseClockInitializer::Stop()
{
running_.store(false, std::memory_order_relaxed); running_.store(false, std::memory_order_relaxed);
} }
void CoarseClockInitializer::Join() { void
CoarseClockInitializer::Join()
{
if (worker_) { if (worker_) {
worker_->join(); worker_->join();
worker_.reset(); worker_.reset();
@ -111,36 +125,40 @@ 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
ReadSteadyClock()
{
// return ReadClock<std::chrono::steady_clock>(CLOCK_MONOTONIC); // return ReadClock<std::chrono::steady_clock>(CLOCK_MONOTONIC);
return std::chrono::steady_clock::now(); return std::chrono::steady_clock::now();
} }
std::chrono::system_clock::time_point ReadSystemClock() {
std::chrono::system_clock::time_point
ReadSystemClock()
{
// return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME); // return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME);
return std::chrono::system_clock::now(); return std::chrono::system_clock::now();
} }
std::chrono::steady_clock::time_point ReadCoarseSteadyClock() { std::chrono::steady_clock::time_point
ReadCoarseSteadyClock()
{
auto coarse_duration = auto coarse_duration =
detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch.load(std::memory_order_relaxed);
.load(std::memory_order_relaxed); auto target_duration = std::chrono::duration_cast<std::chrono::steady_clock::duration>(coarse_duration);
auto target_duration =
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
coarse_duration);
return std::chrono::steady_clock::time_point(target_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::system_clock::time_point
std::chrono::duration_cast<std::chrono::system_clock::duration>( ReadCoarseSystemClock()
coarse_duration); {
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); return std::chrono::system_clock::time_point(target_duration);
} }
} // namespace tile }// namespace tile

View File

@ -35,8 +35,8 @@ private:
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() {
inline std::int64_t
ReadUnixTimestamp()
{
return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1); return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1);
} }
} // namespace tile }// namespace tile
#endif // _TILE_BASE_CHRONO_H #endif// _TILE_BASE_CHRONO_H

View File

@ -8,7 +8,9 @@
namespace tile { namespace tile {
void Benchmark_GetTimeOfDay(benchmark::State &state) { void
Benchmark_GetTimeOfDay(benchmark::State &state)
{
while (state.KeepRunning()) { while (state.KeepRunning()) {
struct timeval tv; struct timeval tv;
gettimeofday(&tv, nullptr); gettimeofday(&tv, nullptr);
@ -17,39 +19,41 @@ void Benchmark_GetTimeOfDay(benchmark::State &state) {
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
Benchmark_ReadCoarseSteadyClock(benchmark::State &state)
{
while (state.KeepRunning()) { while (state.KeepRunning()) {
// ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch` // ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch`
// ... <+30>: mov (%rax),%rax // ... <+30>: mov (%rax),%rax
@ -59,12 +63,12 @@ void Benchmark_ReadCoarseSteadyClock(benchmark::State &state) {
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

View File

@ -7,7 +7,9 @@ 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
AvageTime(std::function<long()> f, std::size_t n = kTestN)
{
long double total = 0; long double total = 0;
for (std::size_t i = 0; i != n; ++i) { for (std::size_t i = 0; i != n; ++i) {
total += 1.0f / n * f(); total += 1.0f / n * f();
@ -16,33 +18,27 @@ long AvageTime(std::function<long()> f, std::size_t n = kTestN) {
return static_cast<long>(total); 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

View File

@ -4,33 +4,37 @@
namespace tile { namespace tile {
std::unique_ptr<Decompressor> MakeDecompressor(Slice name) { std::unique_ptr<Decompressor>
MakeDecompressor(Slice name)
{
return decompressor_registry.TryNew(name); return decompressor_registry.TryNew(name);
} }
std::unique_ptr<Compressor> MakeCompressor(Slice name) { std::unique_ptr<Compressor>
MakeCompressor(Slice name)
{
return compressor_registry.TryNew(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; NoncontiguousBufferBuilder builder;
if (!Compress(compressor, body, &builder)) { if (!Compress(compressor, body, &builder)) { return std::nullopt; }
return std::nullopt;
}
return builder.DestructiveGet();
}
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
Slice body) {
NoncontiguousBufferBuilder builder;
if (!Compress(compressor, body, &builder)) {
return std::nullopt;
}
return builder.DestructiveGet(); return builder.DestructiveGet();
} }
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb, std::optional<NoncontiguousBuffer>
NoncontiguousBufferBuilder *builder) { 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, NoncontiguousBufferBuilder *builder)
{
if (!compressor) { if (!compressor) {
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr"); TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
return false; return false;
@ -40,8 +44,9 @@ bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb,
return compressor->Compress(nb, &out); return compressor->Compress(nb, &out);
} }
bool Compress(Compressor *compressor, Slice body, bool
NoncontiguousBufferBuilder *builder) { Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder)
{
if (!compressor) { if (!compressor) {
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr"); TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
return false; return false;
@ -51,25 +56,25 @@ bool Compress(Compressor *compressor, Slice body,
return compressor->Compress(body.data(), body.size(), &out); return compressor->Compress(body.data(), body.size(), &out);
} }
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor, std::optional<NoncontiguousBuffer>
const NoncontiguousBuffer &body) { Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body)
{
NoncontiguousBufferBuilder builder; NoncontiguousBufferBuilder builder;
if (!Decompress(decompressor, body, &builder)) { if (!Decompress(decompressor, body, &builder)) { return std::nullopt; }
return std::nullopt;
}
return builder.DestructiveGet();
}
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
Slice body) {
NoncontiguousBufferBuilder builder;
if (!Decompress(decompressor, body, &builder)) {
return std::nullopt;
}
return builder.DestructiveGet(); return builder.DestructiveGet();
} }
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, std::optional<NoncontiguousBuffer>
NoncontiguousBufferBuilder *builder) { 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) { if (!decompressor) {
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr"); TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
return false; return false;
@ -78,8 +83,10 @@ bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
NoncontiguousBufferCompressionOutputStream out(builder); NoncontiguousBufferCompressionOutputStream out(builder);
return decompressor->Decompress(nb, &out); return decompressor->Decompress(nb, &out);
} }
bool Decompress(Decompressor *decompressor, Slice body,
NoncontiguousBufferBuilder *builder) { bool
Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder)
{
if (!decompressor) { if (!decompressor) {
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr"); TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
return false; return false;
@ -89,4 +96,4 @@ bool Decompress(Decompressor *decompressor, Slice body,
return decompressor->Decompress(body.data(), body.size(), &out); return decompressor->Decompress(body.data(), body.size(), &out);
} }
} // namespace tile }// namespace tile

View File

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

View File

@ -4,15 +4,15 @@ 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_;
@ -20,9 +20,17 @@ bool TestCompressionOutputStream::Next(void **data,
using_bytes_ += every_size_; using_bytes_ += every_size_;
return true; return true;
} }
void TestCompressionOutputStream::BackUp(std::size_t count) noexcept {
void
TestCompressionOutputStream::BackUp(std::size_t count) noexcept
{
using_bytes_ -= count; using_bytes_ -= count;
} }
void TestCompressionOutputStream::Flush() { buffer_->resize(using_bytes_); }
} // namespace tile void
TestCompressionOutputStream::Flush()
{
buffer_->resize(using_bytes_);
}
}// namespace tile

View File

@ -18,38 +18,31 @@ public:
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, \ TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, Implementation)
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;
@ -60,6 +53,6 @@ private:
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

View File

@ -13,11 +13,13 @@ 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) { if (stream->total_in > 0) {
double rate = 1.0f * stream->total_out / stream->total_in; double rate = 1.0f * stream->total_out / stream->total_in;
constexpr double kMinRate = 1.1; constexpr double kMinRate = 1.1;
@ -27,7 +29,9 @@ inline double EstimateCompressionRate(const z_stream *stream,
return default_value; return default_value;
} }
inline uint32_t RestrictAvailSize(size_t size) { inline uint32_t
RestrictAvailSize(size_t size)
{
return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX))); return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX)));
} }
@ -37,9 +41,14 @@ 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,
const void *input_buffer,
std::size_t input_size,
bool is_deflate,
bool finish)
{
TILE_CHECK(stream && out_stream); TILE_CHECK(stream && out_stream);
if (!finish && (input_buffer == nullptr || input_size == 0)) { if (!finish && (input_buffer == nullptr || input_size == 0)) {
@ -67,18 +76,15 @@ bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
stream->next_out = reinterpret_cast<Bytef *>(out_data); stream->next_out = reinterpret_cast<Bytef *>(out_data);
} else { } else {
double rate = EstimateCompressionRate(stream, 0.5); double rate = EstimateCompressionRate(stream, 0.5);
tmp_buffer.resize(left_size * rate + tmp_buffer.resize(left_size * rate + need_more_space_cnt * kOutBufferIncreaseSize);
need_more_space_cnt * kOutBufferIncreaseSize); stream->next_out = reinterpret_cast<Bytef *>(internal::RemoveConstPtr(tmp_buffer.data()));
stream->next_out = reinterpret_cast<Bytef *>(
internal::RemoveConstPtr(tmp_buffer.data()));
out_size = tmp_buffer.size(); out_size = tmp_buffer.size();
} }
stream->avail_out = RestrictAvailSize(out_size); stream->avail_out = RestrictAvailSize(out_size);
const std::size_t current_avail_out = stream->avail_out; const std::size_t current_avail_out = stream->avail_out;
TILE_CHECK_GT(stream->avail_out, 0, TILE_CHECK_GT(stream->avail_out, 0, "Avail_out should never be zero before the call.");
"Avail_out should never be zero before the call.");
int flush_option = finish ? Z_FINISH : Z_NO_FLUSH; int flush_option = finish ? Z_FINISH : Z_NO_FLUSH;
if (is_deflate) { if (is_deflate) {
code = deflate(stream, flush_option); code = deflate(stream, flush_option);
@ -89,9 +95,7 @@ bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
// https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-inflate-1.html // 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. // No progress is possible; either avail_in or avail_out was zero.
if (code == Z_BUF_ERROR) { if (code == Z_BUF_ERROR) {
if (need_more_space_cnt == 0) { if (need_more_space_cnt == 0) { out_stream->BackUp(out_size); }
out_stream->BackUp(out_size);
}
if (stream->avail_in == 0) { if (stream->avail_in == 0) {
// if not finish, we need more input data // if not finish, we need more input data
@ -115,32 +119,32 @@ bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
out_stream->BackUp(stream->avail_out); out_stream->BackUp(stream->avail_out);
} else { } else {
if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream( if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream(
out_stream, tmp_buffer.data(), out_stream, tmp_buffer.data(), current_avail_out - stream->avail_out))) {
current_avail_out - stream->avail_out))) {
return false; return false;
} }
need_more_space_cnt = 0; need_more_space_cnt = 0;
} }
if (code == Z_STREAM_END) { if (code == Z_STREAM_END) { return true; }
return true;
}
} }
return code == Z_OK; return code == Z_OK;
} }
} // namespace }// namespace
bool GzipCompressor::Compress(const void *src, std::size_t size, bool
CompressionOutputStream *out) { GzipCompressor::Compress(const void *src, std::size_t size, CompressionOutputStream *out)
{
bool ok = Init(out); bool ok = Init(out);
ok &= Append(src, size); ok &= Append(src, size);
ok &= Flush(); ok &= Flush();
out_ = nullptr; out_ = nullptr;
return ok; return ok;
} }
bool GzipCompressor::Compress(const NoncontiguousBuffer &bytes,
CompressionOutputStream *out) { bool
GzipCompressor::Compress(const NoncontiguousBuffer &bytes, CompressionOutputStream *out)
{
bool ok = Init(out); bool ok = Init(out);
std::size_t left = bytes.ByteSize(); std::size_t left = bytes.ByteSize();
for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) { for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) {
@ -152,23 +156,28 @@ bool GzipCompressor::Compress(const NoncontiguousBuffer &bytes,
out_ = nullptr; out_ = nullptr;
return ok; return ok;
} }
bool GzipCompressor::Append(const void *buffer, std::size_t size) {
bool
GzipCompressor::Append(const void *buffer, std::size_t size)
{
return DoAppend(&stream_->stream, out_, buffer, size, true, false); return DoAppend(&stream_->stream, out_, buffer, size, true, false);
} }
bool GzipCompressor::Flush() {
bool
GzipCompressor::Flush()
{
TILE_CHECK(out_); TILE_CHECK(out_);
if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) { if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) { return false; }
return false;
}
return Release(); return Release();
} }
bool GzipCompressor::Init(CompressionOutputStream *out) { bool
GzipCompressor::Init(CompressionOutputStream *out)
{
TILE_CHECK(!out_); TILE_CHECK(!out_);
stream_ = make_unique<detail::ZStream>(); stream_ = make_unique<detail::ZStream>();
int code = int code = deflateInit2(
deflateInit2(&stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, &stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY);
MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY);
if (code != Z_OK) { if (code != Z_OK) {
TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code); TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code);
stream_ = nullptr; stream_ = nullptr;
@ -177,25 +186,29 @@ bool GzipCompressor::Init(CompressionOutputStream *out) {
} }
return code == Z_OK; return code == Z_OK;
} }
bool GzipCompressor::Release() {
bool
GzipCompressor::Release()
{
TILE_CHECK(stream_); TILE_CHECK(stream_);
int code = deflateEnd(&stream_->stream); int code = deflateEnd(&stream_->stream);
if (code != Z_OK) { if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); }
TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code);
}
return code == Z_OK; return code == Z_OK;
} }
bool GzipDecompressor::Decompress(const void *src, std::size_t size, bool
CompressionOutputStream *out) { GzipDecompressor::Decompress(const void *src, std::size_t size, CompressionOutputStream *out)
{
bool ok = Init(out); bool ok = Init(out);
ok &= Append(src, size); ok &= Append(src, size);
ok &= Flush(); ok &= Flush();
out_ = nullptr; out_ = nullptr;
return ok; return ok;
} }
bool GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed,
CompressionOutputStream *out) { bool
GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed, CompressionOutputStream *out)
{
bool ok = Init(out); bool ok = Init(out);
std::size_t left = compressed.ByteSize(); std::size_t left = compressed.ByteSize();
for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) { for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) {
@ -208,17 +221,23 @@ bool GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed,
return ok; return ok;
} }
bool GzipDecompressor::Append(const void *buffer, std::size_t size) { bool
GzipDecompressor::Append(const void *buffer, std::size_t size)
{
return DoAppend(&stream_->stream, out_, buffer, size, false, false); return DoAppend(&stream_->stream, out_, buffer, size, false, false);
} }
bool GzipDecompressor::Flush() {
bool
GzipDecompressor::Flush()
{
TILE_CHECK(out_); TILE_CHECK(out_);
if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) { if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) { return false; }
return false;
}
return Release(); return Release();
} }
bool GzipDecompressor::Init(CompressionOutputStream *out) {
bool
GzipDecompressor::Init(CompressionOutputStream *out)
{
TILE_CHECK(!out_); TILE_CHECK(!out_);
stream_ = make_unique<detail::ZStream>(); stream_ = make_unique<detail::ZStream>();
int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP); int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP);
@ -230,14 +249,15 @@ bool GzipDecompressor::Init(CompressionOutputStream *out) {
} }
return code == Z_OK; return code == Z_OK;
} }
bool GzipDecompressor::Release() {
bool
GzipDecompressor::Release()
{
TILE_CHECK(stream_); TILE_CHECK(stream_);
int code = inflateEnd(&stream_->stream); int code = inflateEnd(&stream_->stream);
if (code != Z_OK) { if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); }
TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code);
}
return code == Z_OK; return code == Z_OK;
} }
} // namespace compression }// namespace compression
} // namespace tile }// namespace tile

View File

@ -5,21 +5,18 @@
#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);
@ -32,10 +29,8 @@ private:
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);
@ -46,7 +41,7 @@ private:
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

View File

@ -1,12 +1,12 @@
#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;
@ -14,18 +14,14 @@ bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
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) { if (left_to_copy <= next_size) {
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, left_to_copy);
left_to_copy);
out->BackUp(next_size - left_to_copy); out->BackUp(next_size - left_to_copy);
return true; return true;
} else { } else {
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, next_size);
next_size);
current_pos += next_size; current_pos += next_size;
left_to_copy -= next_size; left_to_copy -= next_size;
} }
@ -33,5 +29,5 @@ bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
TILE_UNREACHABLE(""); TILE_UNREACHABLE("");
return false; return false;
} }
} // namespace compression }// namespace compression
} // namespace tile }// namespace tile

View File

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

View File

@ -4,7 +4,8 @@
namespace tile { namespace tile {
namespace compression { namespace compression {
TEST(CopyDataToCompressionOutputStream, All) { TEST(CopyDataToCompressionOutputStream, All)
{
std::string s; std::string s;
std::string a = "123456789+"; std::string a = "123456789+";
@ -17,6 +18,6 @@ TEST(CopyDataToCompressionOutputStream, All) {
ASSERT_EQ(s, a); ASSERT_EQ(s, a);
} }
} // namespace compression }// namespace compression
} // namespace tile }// namespace tile

View File

@ -11,21 +11,25 @@ static const char *algos[] = {
"gzip", "gzip",
}; };
} }
TEST(MakeCompressor, All) {
TEST(MakeCompressor, All)
{
auto &&c = MakeCompressor("gzip"); auto &&c = MakeCompressor("gzip");
EXPECT_TRUE(c); EXPECT_TRUE(c);
c = MakeCompressor("??"); c = MakeCompressor("??");
EXPECT_FALSE(c); EXPECT_FALSE(c);
} }
TEST(MakeDecompressor, All) { TEST(MakeDecompressor, All)
{
auto &&c = MakeDecompressor("gzip"); auto &&c = MakeDecompressor("gzip");
EXPECT_TRUE(c); EXPECT_TRUE(c);
c = MakeDecompressor("??"); c = MakeDecompressor("??");
EXPECT_FALSE(c); EXPECT_FALSE(c);
} }
TEST(CompressString, All) { TEST(CompressString, All)
{
std::string original(1000, 'A'); std::string original(1000, 'A');
auto c = Compress(MakeCompressor("gzip").get(), original); auto c = Compress(MakeCompressor("gzip").get(), original);
EXPECT_TRUE(c); EXPECT_TRUE(c);
@ -34,7 +38,8 @@ TEST(CompressString, All) {
EXPECT_EQ(FlattenSlow(*d), original); EXPECT_EQ(FlattenSlow(*d), original);
} }
TEST(CompressNoncontiguousBuffer, All) { TEST(CompressNoncontiguousBuffer, All)
{
NoncontiguousBufferBuilder nbb; NoncontiguousBufferBuilder nbb;
std::string original(1000, 'A'); std::string original(1000, 'A');
nbb.Append(original.data(), original.size()); nbb.Append(original.data(), original.size());
@ -46,7 +51,8 @@ TEST(CompressNoncontiguousBuffer, All) {
EXPECT_EQ(FlattenSlow(*d), original); EXPECT_EQ(FlattenSlow(*d), original);
} }
TEST(Decompressor, Empty) { TEST(Decompressor, Empty)
{
for (auto &&algo : algos) { for (auto &&algo : algos) {
auto res = Decompress(MakeDecompressor(algo).get(), ""); auto res = Decompress(MakeDecompressor(algo).get(), "");
@ -61,12 +67,11 @@ TEST(Decompressor, Empty) {
} }
} }
TEST(Decompressor, Invalid) { TEST(Decompressor, Invalid)
{
for (auto &&algo : algos) { for (auto &&algo : algos) {
auto res = auto res = Decompress(MakeDecompressor(algo).get(), "this buffer is likely an invalid compressed buffer.");
Decompress(MakeDecompressor(algo).get(),
"this buffer is likely an invalid compressed buffer.");
EXPECT_FALSE(res); EXPECT_FALSE(res);
} }
} }
} // namespace tile }// namespace tile

View File

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

View File

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

View File

@ -2,10 +2,9 @@
#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;
@ -13,7 +12,6 @@ class Configurable {
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

View 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

View 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

View File

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

View File

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

View File

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

View 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

View 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

View 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

View 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

View 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

View 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

View File

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

View File

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

View 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

View 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

View 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

View 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

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

View File

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

View File

@ -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) template<typename T>
-> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value, auto
Json::Value> { ToJson(const T &value) -> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value, Json::Value>
{
return Format("{}", value); return Format("{}", value);
} }
template <typename T> Json::Value ToJson(const std::atomic<T> &v) { template<typename T>
Json::Value
ToJson(const std::atomic<T> &v)
{
return ToJson(v.load(std::memory_order_relaxed)); return ToJson(v.load(std::memory_order_relaxed));
} }
} // namespace tile }// namespace tile
#endif // TILE_BASE_DATA_JSON_H #endif// TILE_BASE_DATA_JSON_H

View File

@ -9,10 +9,12 @@ 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(const ScopedDeferred &) = delete;
ScopedDeferred &operator=(const ScopedDeferred &) = delete; ScopedDeferred &operator=(const ScopedDeferred &) = delete;
@ -23,34 +25,33 @@ private:
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(); Fire();
action_ = std::move(other.action_); action_ = std::move(other.action_);
other.action_ = nullptr; other.action_ = nullptr;
return *this; return *this;
} }
~Deferred() {
if (action_) { ~Deferred()
action_(); {
} 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; }
@ -59,6 +60,6 @@ private:
std::function<void()> action_; std::function<void()> action_;
}; };
} // namespace tile }// namespace tile
#endif // _TILE_BASE_DEFERED_H #endif// _TILE_BASE_DEFERED_H

View File

@ -2,7 +2,8 @@
#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; }); ScopedDeferred scoped_defered([&] { f = true; });
@ -11,7 +12,8 @@ TEST(ScopedDefered, All) {
ASSERT_TRUE(f); ASSERT_TRUE(f);
} }
TEST(Defered, All) { TEST(Defered, All)
{
bool f1 = false; bool f1 = false;
bool f2 = false; bool f2 = false;
{ {
@ -47,4 +49,4 @@ TEST(Defered, All) {
Deferred().Fire(); Deferred().Fire();
Deferred().Dimiss(); Deferred().Dimiss();
} }
} // namespace tile }// namespace tile

View File

@ -10,16 +10,16 @@
namespace tile { namespace tile {
std::string Demangle(const char *s) { std::string
Demangle(const char *s)
{
#ifdef _MSC_VER #ifdef _MSC_VER
return s; return s;
#elif defined(__GNUC__) || defined(__clang__) #elif defined(__GNUC__) || defined(__clang__)
int status; int status;
char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status); char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status);
ScopedDeferred _([&] { free(demangled); }); ScopedDeferred _([&] { free(demangled); });
if (!demangled) { if (!demangled) { return s; }
return s;
}
return demangled; return demangled;
#else #else
#error "Demangle not supported on current compiler." #error "Demangle not supported on current compiler."
@ -27,4 +27,4 @@ std::string Demangle(const char *s) {
#endif #endif
} }
} // namespace tile }// namespace tile

View File

@ -11,7 +11,10 @@ namespace tile {
std::string Demangle(const char *s); std::string Demangle(const char *s);
template <class T> std::string GetTypeName() { template<class T>
std::string
GetTypeName()
{
return Demangle(typeid(T).name()); return Demangle(typeid(T).name());
} }
@ -20,7 +23,10 @@ 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>
std::string
GetTypeName(T &&o)
{
return Demangle(typeid(std::forward<T>(o)).name()); return Demangle(typeid(std::forward<T>(o)).name());
} }
@ -28,6 +34,6 @@ template <class T> std::string GetTypeName(T &&o) {
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
} // namespace tile }// namespace tile
#endif // _TILE_BASE_DEMANGLE_H #endif// _TILE_BASE_DEMANGLE_H

View File

@ -8,11 +8,11 @@ struct C {
}; };
}; };
TEST(Demangle, All) { TEST(Demangle, All)
{
ASSERT_EQ("tile::C::D::E", GetTypeName<C::D::E>()); ASSERT_EQ("tile::C::D::E", GetTypeName<C::D::E>());
ASSERT_NE(GetTypeName<C::D>(), typeid(C::D::E).name()); ASSERT_NE(GetTypeName<C::D>(), typeid(C::D::E).name());
ASSERT_EQ("invalid function name !@#$", ASSERT_EQ("invalid function name !@#$", Demangle("invalid function name !@#$"));
Demangle("invalid function name !@#$"));
} }
} // namespace tile }// namespace tile

View File

@ -22,54 +22,45 @@
// 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< \ ::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__>>>()
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( \ TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(ImplementationClassName, ##__VA_ARGS__))
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 = \
::tile::internal::LazyInit<::tile::NeverDestroyed<typename std::decay<decltype(Registry)>::type>>() \
->Get(); \ ->Get(); \
::tile::detail::dependency_registry::AddToRegistry(registry, Name, \ ::tile::detail::dependency_registry::AddToRegistry(registry, Name, Factory); \
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< \ { \
typename std::decay<decltype(Registry)>::type>>() \ auto registry = \
::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 {
@ -78,66 +69,67 @@ 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>
std::unique_ptr<Interface>
FactoryAux(Args &&...args)
{
return make_unique<Interface>(std::forward<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
AddToRegistry(T *registry, Args &&...args)
{
registry->Register(std::forward<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()); auto iter = factories_.find(name.ToString());
if (iter == factories_.end()) { if (iter == factories_.end()) { return nullptr; }
return nullptr;
}
auto constructor_ptr = iter->second.get(); auto constructor_ptr = iter->second.get();
return [constructor_ptr](FactoryArgs... args) { return
return (*constructor_ptr)(std::forward<FactoryArgs>(args)...); [constructor_ptr](FactoryArgs... args) { return (*constructor_ptr)(std::forward<FactoryArgs>(args)...); };
};
} }
Factory GetFactory(const std::string &name) const noexcept { Factory GetFactory(const std::string &name) const noexcept
{
auto result = TryGetFactory(name); auto result = TryGetFactory(name);
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
name, GetTypeName<Interface>());
return result; return result;
} }
std::unique_ptr<Interface> TryNew(Slice name, FactoryArgs... args) const { std::unique_ptr<Interface> TryNew(Slice name, FactoryArgs... args) const
{
auto iter = factories_.find(name.ToString()); auto iter = factories_.find(name.ToString());
if (iter == factories_.end()) { if (iter == factories_.end()) { return nullptr; }
return nullptr;
}
auto &constructor = iter->second; auto &constructor = iter->second;
return (*constructor)(std::forward<FactoryArgs>(args)...); return (*constructor)(std::forward<FactoryArgs>(args)...);
} }
std::unique_ptr<Interface> New(const std::string &name, std::unique_ptr<Interface> New(const std::string &name, FactoryArgs... args) const
FactoryArgs... args) const { {
auto result = TryNew(name, std::forward<FactoryArgs>(args)...); auto result = TryNew(name, std::forward<FactoryArgs>(args)...);
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
name, GetTypeName<Interface>());
return result; return result;
} }
private: private:
template <typename T, typename... Args> template<typename T, typename... Args>
friend void AddToRegistry(T *registry, Args &&...args); friend void AddToRegistry(T *registry, Args &&...args);
void Register(const std::string &name, Factory factory) { void Register(const std::string &name, Factory factory)
TILE_CHECK(factories_.find(name) == factories_.end(), {
"Duplicate class dependency: "); TILE_CHECK(factories_.find(name) == factories_.end(), "Duplicate class dependency: ");
factories_[name] = make_unique<Factory>(std::move(factory)); factories_[name] = make_unique<Factory>(std::move(factory));
} }
@ -145,44 +137,41 @@ private:
std::unordered_map<std::string, std::unique_ptr<Factory>> factories_; std::unordered_map<std::string, std::unique_ptr<Factory>> factories_;
}; };
template <typename Tag, typename Interface> class ObjectRegistry { template<typename Tag, typename Interface>
class ObjectRegistry {
public: public:
Interface *TryGet(Slice name) const { Interface *TryGet(Slice name) const
{
auto iter = objects_.find(name.ToString()); auto iter = objects_.find(name.ToString());
if (iter == objects_.end()) { if (iter == objects_.end()) { return nullptr; }
return nullptr;
}
auto &&e = *iter->second; auto &&e = *iter->second;
std::call_once(e.flag, [&] { e.object = e.initializer(); }); std::call_once(e.flag, [&] { e.object = e.initializer(); });
return e.object.Get(); return e.object.Get();
} }
Interface *Get(Slice name) const { Interface *Get(Slice name) const
{
auto result = TryGet(name); auto result = TryGet(name);
TILE_CHECK( TILE_CHECK(result, "Object dependency [{}] implementing interface [{}] is not found", name,
result,
"Object dependency [{}] implementing interface [{}] is not found", name,
GetTypeName<Interface>()); GetTypeName<Interface>());
return result; return result;
} }
private: private:
template <typename T, typename... Args> template<typename T, typename... Args>
friend void AddToRegistry(T *registry, Args &&...args); friend void AddToRegistry(T *registry, Args &&...args);
void Register(const std::string &name, Interface *object) { void Register(const std::string &name, Interface *object)
{
TILE_DCHECK(objects_.find(name) == objects_.end()); TILE_DCHECK(objects_.find(name) == objects_.end());
auto &&e = objects_[name]; auto &&e = objects_[name];
e = make_unique<LazilyInstantiatedObject>(); e = make_unique<LazilyInstantiatedObject>();
std::call_once(e->flag, [&] { std::call_once(e->flag, [&] { e->object = MaybeOwning<Interface>(non_owning, object); });
e->object = MaybeOwning<Interface>(non_owning, object);
});
} }
void Register(const std::string &name, void Register(const std::string &name, std::function<MaybeOwning<Interface>()> initializer)
std::function<MaybeOwning<Interface>()> initializer) { {
TILE_CHECK(objects_.find(name) == objects_.end(), TILE_CHECK(objects_.find(name) == objects_.end(), "Double registration of object dependency [{}]", name);
"Double registration of object dependency [{}]", name);
objects_[name] = make_unique<LazilyInstantiatedObject>(); objects_[name] = make_unique<LazilyInstantiatedObject>();
objects_[name]->initializer = std::move(initializer); objects_[name]->initializer = std::move(initializer);
@ -195,12 +184,11 @@ private:
std::function<MaybeOwning<Interface>()> initializer; std::function<MaybeOwning<Interface>()> initializer;
}; };
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>> std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>> objects_;
objects_;
}; };
} // namespace dependency_registry }// namespace dependency_registry
} // namespace detail }// namespace detail
} // namespace tile }// namespace tile
#endif // TILE_BASE_DEPENDENCY_REGISTRY_H #endif// TILE_BASE_DEPENDENCY_REGISTRY_H

View File

@ -1,5 +1,6 @@
#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;
@ -7,27 +8,33 @@ struct Destroyer {
struct FastDestroyer : Destroyer { struct FastDestroyer : Destroyer {
FastDestroyer() { ++instances; } FastDestroyer() { ++instances; }
~FastDestroyer() override { --instances; } ~FastDestroyer() override { --instances; }
static int 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; } ~GentleDestroyer() override { --instances; }
static int 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>();
}); });
@ -35,14 +42,13 @@ 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",
[](int speed) {
return make_unique<SpeedDestroyer2>(); return make_unique<SpeedDestroyer2>();
}); });
TEST(DependencyRegistry, Class) { TEST(DependencyRegistry, Class)
{
EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer")); EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer")); EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer")); EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
@ -50,7 +56,7 @@ TEST(DependencyRegistry, Class) {
EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer")); EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer"));
EXPECT_FALSE(world_destroyer.TryGetFactory("404-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);
{ {
@ -65,7 +71,8 @@ TEST(DependencyRegistry, Class) {
EXPECT_EQ(0, FastDestroyer::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"));
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2")); EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2"));
EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3")); EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3"));
@ -74,4 +81,4 @@ TEST(DependencyRegistry, ClassWithArgs) {
EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456)); EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456));
} }
} // namespace tile }// namespace tile

View File

@ -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
down_cast(From *ptr)
{
TILE_DCHECK(dynamic_cast<R>(ptr)); TILE_DCHECK(dynamic_cast<R>(ptr));
return static_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
down_cast(From &ref) -> decltype(down_cast(&ref))
{
return down_cast<To>(&ref); return down_cast<To>(&ref);
} }
} // namespace tile }// namespace tile
#endif // _TILE_BASE_DOWN_CAST_H #endif// _TILE_BASE_DOWN_CAST_H

View File

@ -5,9 +5,11 @@ 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; B b;
A *ptr = &b; A *ptr = &b;
ASSERT_NE(nullptr, down_cast<B>(ptr)); ASSERT_NE(nullptr, down_cast<B>(ptr));
@ -15,10 +17,11 @@ TEST(DownCast, All) {
} }
#ifndef NDEBUG #ifndef NDEBUG
TEST(CastDeathTest, DownCast) { TEST(CastDeathTest, DownCast)
{
A a; A a;
A *ptr = &a; A *ptr = &a;
ASSERT_DEATH(down_cast<B>(ptr), ""); ASSERT_DEATH(down_cast<B>(ptr), "");
} }
#endif #endif
} // namespace tile }// namespace tile

View File

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

View File

@ -19,25 +19,30 @@ 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
EncodeBase64(Slice from)
{
std::string result; std::string result;
EncodeBase64(from, &result); EncodeBase64(from, &result);
return result; return result;
} }
std::optional<std::string> DecodeBase64(Slice from) {
std::optional<std::string>
DecodeBase64(Slice from)
{
std::string result; std::string result;
if (!DecodeBase64(from, &result)) { if (!DecodeBase64(from, &result)) { return std::nullopt; }
return std::nullopt;
}
return result; return result;
} }
void EncodeBase64(Slice from, std::string *to) { void
EncodeBase64(Slice from, std::string *to)
{
TILE_CHECK(from.size() < std::numeric_limits<int>::max(), TILE_CHECK(from.size() < std::numeric_limits<int>::max(),
"Not implemented: Source bytes too long. {} > max_size({})", "Not implemented: Source bytes too long. {} > max_size({})", from.size(),
from.size(), std::numeric_limits<int>::max()); std::numeric_limits<int>::max());
const auto size = 4 * ((from.size() + 2) / 3); const auto size = 4 * ((from.size() + 2) / 3);
to->resize(size); to->resize(size);
@ -48,15 +53,11 @@ void EncodeBase64(Slice from, std::string *to) {
std::size_t input_len; std::size_t input_len;
while (!from.empty()) { while (!from.empty()) {
input_len = std::min(kBufferSize, from.size()); input_len = std::min(kBufferSize, from.size());
used += base64_encode_block( used += base64_encode_block(reinterpret_cast<const char *>(from.data()), input_len,
reinterpret_cast<const char *>(from.data()), input_len, reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
&state);
from.RemovePrefix(input_len); from.RemovePrefix(input_len);
}; };
base64_encode_blockend( base64_encode_blockend(reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
&state);
// bool success = // bool success =
// Base64::Encode(from.data(), from.size(), // Base64::Encode(from.data(), from.size(),
@ -64,7 +65,9 @@ void EncodeBase64(Slice from, std::string *to) {
// TILE_CHECK(success, "Unexpected: Failed to do base64-encode."); // TILE_CHECK(success, "Unexpected: Failed to do base64-encode.");
} }
bool DecodeBase64(Slice from, std::string *to) { bool
DecodeBase64(Slice from, std::string *to)
{
if (TILE_UNLIKELY(from.empty())) { if (TILE_UNLIKELY(from.empty())) {
to->clear(); to->clear();
return true; return true;
@ -94,12 +97,8 @@ bool DecodeBase64(Slice from, std::string *to) {
while (!input.empty()) { while (!input.empty()) {
auto input_len = std::min(kBufferSize, from.size()); auto input_len = std::min(kBufferSize, from.size());
auto encoded_len = base64_decode_block( auto encoded_len = base64_decode_block(
input.data(), input_len, input.data(), input_len, reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, if (TILE_UNLIKELY(encoded_len == 0)) { return false; }
&state);
if (TILE_UNLIKELY(encoded_len == 0)) {
return false;
}
used += encoded_len; used += encoded_len;
input.RemovePrefix(input_len); input.RemovePrefix(input_len);
} }
@ -139,17 +138,13 @@ bool DecodeBase64(Slice from, std::string *to) {
// final unit of encoded output will be three characters followed by // final unit of encoded output will be three characters followed by
// one "=" padding character. // one "=" padding character.
TILE_CHECK(from.size() >= 2 && to->size() >= 2); TILE_CHECK(from.size() >= 2 && to->size() >= 2);
if (from[from.size() - 1] == '=') { if (from[from.size() - 1] == '=') { to->pop_back(); }
to->pop_back();
}
if (from[from.size() - 2] == '=') { if (from[from.size() - 2] == '=') { to->pop_back(); }
to->pop_back();
}
// to->pop_back(); // Remove Terminating null // to->pop_back(); // Remove Terminating null
return true; return true;
} }
} // namespace tile }// namespace tile

View File

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

View File

@ -14,7 +14,8 @@ 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)); EXPECT_EQ(kBase64Text, EncodeBase64(kText));
auto decoded = DecodeBase64(kBase64Text); auto decoded = DecodeBase64(kBase64Text);
ASSERT_TRUE(decoded); ASSERT_TRUE(decoded);
@ -22,7 +23,8 @@ TEST(Base64, Default) {
EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!")); EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!"));
} }
TEST(Base64, Padding) { TEST(Base64, Padding)
{
EXPECT_EQ(kBase64Text2, EncodeBase64(kText2)); EXPECT_EQ(kBase64Text2, EncodeBase64(kText2));
auto decoded = DecodeBase64(kBase64Text2); auto decoded = DecodeBase64(kBase64Text2);
@ -30,14 +32,16 @@ TEST(Base64, Padding) {
EXPECT_EQ(kText2, *decoded); EXPECT_EQ(kText2, *decoded);
} }
TEST(Base64, Empty) { TEST(Base64, Empty)
{
EXPECT_EQ("", EncodeBase64("")); EXPECT_EQ("", EncodeBase64(""));
auto decoded = DecodeBase64(""); auto decoded = DecodeBase64("");
ASSERT_TRUE(decoded); ASSERT_TRUE(decoded);
EXPECT_EQ("", *decoded); EXPECT_EQ("", *decoded);
} }
TEST(Base64, AutoPadding) { TEST(Base64, AutoPadding)
{
static constexpr auto kChar = "A"; static constexpr auto kChar = "A";
EXPECT_EQ("QQ==", EncodeBase64(kChar)); EXPECT_EQ("QQ==", EncodeBase64(kChar));
@ -63,7 +67,8 @@ TEST(Base64, AutoPadding) {
} }
} }
TEST(Base64, Torture) { TEST(Base64, Torture)
{
std::string random_str; std::string random_str;
std::string encoded; std::string encoded;
std::string decoded; std::string decoded;
@ -75,4 +80,4 @@ TEST(Base64, Torture) {
} }
} }
} // namespace tile }// namespace tile

View File

@ -3,44 +3,50 @@
#include <string> #include <string>
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" const char kBase64Alphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "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;
if (c == '/')
return 63;
return 255; return 255;
} }
struct Base64LookupInitializer { struct Base64LookupInitializer {
Base64LookupInitializer() { Base64LookupInitializer()
for (int i = 0; i < 256; i++) { {
kBase64Lookup[i] = b64_lookup_aux(i); for (int i = 0; i < 256; i++) { kBase64Lookup[i] = b64_lookup_aux(i); }
}
} }
int kBase64Lookup[256]; int kBase64Lookup[256];
}; };
static inline unsigned char b64_lookup_aux1(unsigned char c) { static inline unsigned char
b64_lookup_aux1(unsigned char c)
{
static Base64LookupInitializer init; static Base64LookupInitializer init;
return init.kBase64Lookup[c]; return init.kBase64Lookup[c];
} }
} // namespace }// namespace
inline unsigned char b64_lookup(unsigned char c) { return b64_lookup_aux1(c); }
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; int i = 0, j = 0;
size_t enc_len = 0; size_t enc_len = 0;
unsigned char a3[3]; unsigned char a3[3];
@ -56,35 +62,27 @@ public:
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) { if (i) {
for (j = i; j < 3; j++) { for (j = i; j < 3; j++) { a3[j] = '\0'; }
a3[j] = '\0';
}
a3_to_a4(a4, a3); a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++) { for (j = 0; j < i + 1; j++) { (*out)[enc_len++] = kBase64Alphabet[a4[j]]; }
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
}
while ((i++ < 3)) { while ((i++ < 3)) { (*out)[enc_len++] = '='; }
(*out)[enc_len++] = '=';
}
} }
return (enc_len == out->size()); return (enc_len == out->size());
} }
static bool Encode(const char *input, size_t input_length, char *out, static bool Encode(const char *input, size_t input_length, char *out, size_t out_length)
size_t out_length) { {
int i = 0, j = 0; int i = 0, j = 0;
char *out_begin = out; char *out_begin = out;
unsigned char a3[3]; unsigned char a3[3];
@ -92,17 +90,14 @@ public:
size_t encoded_length = EncodedLength(input_length); size_t encoded_length = EncodedLength(input_length);
if (out_length < encoded_length) if (out_length < encoded_length) return false;
return false;
while (input_length--) { while (input_length--) {
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++ = kBase64Alphabet[a4[i]]; }
*out++ = kBase64Alphabet[a4[i]];
}
// out[0] = kBase64Alphabet[a4[0]]; // out[0] = kBase64Alphabet[a4[0]];
// out[1] = kBase64Alphabet[a4[1]]; // out[1] = kBase64Alphabet[a4[1]];
// out[2] = kBase64Alphabet[a4[2]]; // out[2] = kBase64Alphabet[a4[2]];
@ -114,25 +109,20 @@ public:
} }
if (i) { if (i) {
for (j = i; j < 3; j++) { for (j = i; j < 3; j++) { a3[j] = '\0'; }
a3[j] = '\0';
}
a3_to_a4(a4, a3); a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++) { for (j = 0; j < i + 1; j++) { *out++ = kBase64Alphabet[a4[j]]; }
*out++ = kBase64Alphabet[a4[j]];
}
while ((i++ < 3)) { while ((i++ < 3)) { *out++ = '='; }
*out++ = '=';
}
} }
return (out == (out_begin + encoded_length)); return (out == (out_begin + encoded_length));
} }
static bool Decode(const std::string &in, std::string *out) { static bool Decode(const std::string &in, std::string *out)
{
int i = 0, j = 0; int i = 0, j = 0;
size_t dec_len = 0; size_t dec_len = 0;
unsigned char a3[3]; unsigned char a3[3];
@ -144,47 +134,35 @@ public:
out->resize(DecodedLength(in)); out->resize(DecodedLength(in));
while (input_len--) { while (input_len--) {
if (*input == '=') { if (*input == '=') { break; }
break;
}
a4[i++] = *(input++); a4[i++] = *(input++);
if (i == 4) { if (i == 4) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) { a4[i] = b64_lookup(a4[i]); }
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3, a4); a4_to_a3(a3, a4);
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) { (*out)[dec_len++] = a3[i]; }
(*out)[dec_len++] = a3[i];
}
i = 0; i = 0;
} }
} }
if (i) { if (i) {
for (j = i; j < 4; j++) { for (j = i; j < 4; j++) { a4[j] = '\0'; }
a4[j] = '\0';
}
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) { a4[j] = b64_lookup(a4[j]); }
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3, a4); a4_to_a3(a3, a4);
for (j = 0; j < i - 1; j++) { for (j = 0; j < i - 1; j++) { (*out)[dec_len++] = a3[j]; }
(*out)[dec_len++] = a3[j];
}
} }
return (dec_len == out->size()); return (dec_len == out->size());
} }
static bool Decode(const char *input, size_t input_length, char *out, static bool Decode(const char *input, size_t input_length, char *out, size_t out_length)
size_t out_length) { {
int i = 0, j = 0; int i = 0, j = 0;
char *out_begin = out; char *out_begin = out;
unsigned char a3[3]; unsigned char a3[3];
@ -192,97 +170,80 @@ public:
size_t decoded_length = DecodedLength(input, input_length); size_t decoded_length = DecodedLength(input, input_length);
if (out_length < decoded_length) if (out_length < decoded_length) return false;
return false;
while (input_length--) { while (input_length--) {
if (*input == '=') { if (*input == '=') { break; }
break;
}
a4[i++] = *(input++); a4[i++] = *(input++);
if (i == 4) { if (i == 4) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) { a4[i] = b64_lookup(a4[i]); }
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3, a4); a4_to_a3(a3, a4);
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) { *out++ = a3[i]; }
*out++ = a3[i];
}
i = 0; i = 0;
} }
} }
if (i) { if (i) {
for (j = i; j < 4; j++) { for (j = i; j < 4; j++) { a4[j] = '\0'; }
a4[j] = '\0';
}
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) { a4[j] = b64_lookup(a4[j]); }
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3, a4); a4_to_a3(a3, a4);
for (j = 0; j < i - 1; j++) { for (j = 0; j < i - 1; j++) { *out++ = a3[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) { static size_t DecodedLength(const char *in, size_t in_length)
{
int numEq = 0; int numEq = 0;
const char *in_end = in + in_length; const char *in_end = in + in_length;
while (*--in_end == '=') while (*--in_end == '=') ++numEq;
++numEq;
return ((6 * in_length) / 8) - numEq; return ((6 * in_length) / 8) - numEq;
} }
static size_t DecodedLength(const std::string &in) { static size_t DecodedLength(const std::string &in)
{
int numEq = 0; int numEq = 0;
size_t n = in.size(); size_t n = in.size();
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; }
++it) {
++numEq;
}
return ((6 * n) / 8) - numEq; return ((6 * n) / 8) - numEq;
} }
inline static size_t EncodedLength(size_t length) { inline static size_t EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; }
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
}
inline static size_t EncodedLength(const std::string &in) { inline static size_t EncodedLength(const std::string &in) { return EncodedLength(in.length()); }
return EncodedLength(in.length());
}
inline static void StripPadding(std::string *in) { inline static void StripPadding(std::string *in)
while (!in->empty() && *(in->rbegin()) == '=') {
in->resize(in->size() - 1); 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[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f); 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[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
} }
}; };
#endif // BASE64_H #endif// BASE64_H

View File

@ -8,7 +8,9 @@ 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
AsciiCodeFromCharPairSlow(char a, char b)
{
a = ToLower(a); a = ToLower(a);
b = ToLower(b); b = ToLower(b);
auto ToNum = [](char x) { auto ToNum = [](char x) {
@ -23,25 +25,24 @@ int AsciiCodeFromCharPairSlow(char a, char b) {
int x = ToNum(a); int x = ToNum(a);
int y = ToNum(b); int y = ToNum(b);
if (x == -1 || y == -1) { if (x == -1 || y == -1) { return -1; }
return -1;
}
return (x << 4) | y; return (x << 4) | y;
} }
std::pair<char, char> AsciiCodeToCharPairSlow(std::uint8_t value, std::pair<char, char>
bool uppercase) { AsciiCodeToCharPairSlow(std::uint8_t value, bool uppercase)
{
if (uppercase) { if (uppercase) {
return std::make_pair(kHexCharsUpper[value >> 4], return std::make_pair(kHexCharsUpper[value >> 4], kHexCharsUpper[value & 0x0f]);
kHexCharsUpper[value & 0x0f]);
} else { } else {
return std::make_pair(kHexCharsLower[value >> 4], return std::make_pair(kHexCharsLower[value >> 4], kHexCharsLower[value & 0x0f]);
kHexCharsLower[value & 0x0f]);
} }
} }
std::array<std::array<int, 256>, 256> GenAsciiCodeFromCharPairTable() { std::array<std::array<int, 256>, 256>
GenAsciiCodeFromCharPairTable()
{
std::array<std::array<int, 256>, 256> table; std::array<std::array<int, 256>, 256> table;
for (char i = kCharMin; i != kCharMax; ++i) { for (char i = kCharMin; i != kCharMax; ++i) {
for (char j = kCharMin; j != kCharMax; ++j) { for (char j = kCharMin; j != kCharMax; ++j) {
@ -52,34 +53,41 @@ std::array<std::array<int, 256>, 256> GenAsciiCodeFromCharPairTable() {
} }
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; std::array<std::pair<char, char>, 256> table;
for (int i = 0; i != 256; ++i) { for (int i = 0; i != 256; ++i) { table[i] = AsciiCodeToCharPairSlow(i, uppercase); }
table[i] = AsciiCodeToCharPairSlow(i, uppercase);
}
return table; return table;
} }
} // namespace }// namespace
int AsciiCodeFromCharPair(char a, char b) { int
AsciiCodeFromCharPair(char a, char b)
{
static auto table = GenAsciiCodeFromCharPairTable(); static auto table = GenAsciiCodeFromCharPairTable();
return table[a - kCharMin][b - kCharMin]; return table[a - kCharMin][b - kCharMin];
} }
inline std::pair<char, char> AsciiCodeToUpperCharPair(std::uint8_t value) { inline std::pair<char, char>
AsciiCodeToUpperCharPair(std::uint8_t value)
{
static auto table = GenAsciiCodeToCharPairTable(true); static auto table = GenAsciiCodeToCharPairTable(true);
return table[value]; return table[value];
} }
inline std::pair<char, char> AsciiCodeToLowerCharPair(std::uint8_t value) {
inline std::pair<char, char>
AsciiCodeToLowerCharPair(std::uint8_t value)
{
static auto table = GenAsciiCodeToCharPairTable(false); static auto table = GenAsciiCodeToCharPairTable(false);
return table[value]; return table[value];
} }
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value, bool uppercase) { std::pair<char, char>
return uppercase ? AsciiCodeToUpperCharPair(value) AsciiCodeToCharPair(std::uint8_t value, bool uppercase)
: AsciiCodeToLowerCharPair(value); {
return uppercase ? AsciiCodeToUpperCharPair(value) : AsciiCodeToLowerCharPair(value);
} }
} // namespace detail }// namespace detail
} // namespace tile }// namespace tile

View File

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

View File

@ -6,43 +6,44 @@
namespace tile { namespace tile {
std::string EncodeHex(Slice from, bool uppercase) { std::string
EncodeHex(Slice from, bool uppercase)
{
std::string result; std::string result;
EncodeHex(from, &result, uppercase); EncodeHex(from, &result, uppercase);
return result; return result;
} }
std::optional<std::string> DecodeHex(Slice from) { std::optional<std::string>
DecodeHex(Slice from)
{
std::string result; std::string result;
if (!DecodeHex(from, &result)) { if (!DecodeHex(from, &result)) { return std::nullopt; }
return std::nullopt;
}
return result; return result;
} }
void EncodeHex(Slice from, std::string *to, bool uppercase) { void
EncodeHex(Slice from, std::string *to, bool uppercase)
{
to->clear(); to->clear();
to->reserve(from.size() * 2); to->reserve(from.size() * 2);
for (auto &&e : from) { for (auto &&e : from) {
// auto index = static_cast<std::uint8_t>(e); // auto index = static_cast<std::uint8_t>(e);
auto pair = auto pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e), uppercase);
detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e), uppercase);
to->append({pair.first, pair.second}); 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); TILE_CHECK(v >= 0 && v <= 255);
to->push_back(v); to->push_back(v);
@ -50,4 +51,4 @@ bool DecodeHex(Slice from, std::string *to) {
return true; return true;
} }
} // namespace tile }// namespace tile

View File

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

View File

@ -7,14 +7,16 @@
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));
EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true)); EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true));
EXPECT_EQ(Hex123456FF, *DecodeHex("123456ff")); 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 random_str;
std::string encoded; std::string encoded;
std::string decoded; std::string decoded;
@ -26,4 +28,4 @@ TEST(Hex, Random) {
} }
} }
} // namespace tile }// namespace tile

View File

@ -7,15 +7,13 @@ namespace tile {
// [0-9a-zA-Z] // [0-9a-zA-Z]
namespace { namespace {
std::array<bool, 256> GenerateUnescapedCharBitmap(Slice unescaped_chars) { std::array<bool, 256>
GenerateUnescapedCharBitmap(Slice unescaped_chars)
{
std::array<bool, 256> result{}; std::array<bool, 256> result{};
for (auto &&e : unescaped_chars) { for (auto &&e : unescaped_chars) { result[e] = true; }
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;
@ -26,52 +24,50 @@ std::array<bool, 256> GenerateUnescapedCharBitmap(Slice unescaped_chars) {
} }
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[] = { static const std::array<std::array<bool, 256>, 2> kUnescapedChars[] = {
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), /* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), 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
EncodePercent(Slice from, const PercentEncodingOptions &options)
{
std::string result; std::string result;
EncodePercent(from, &result, options); EncodePercent(from, &result, options);
return result; return result;
} }
std::optional<std::string> DecodePercent(Slice from,
bool decode_plus_sign_as_whitespace) { std::optional<std::string>
DecodePercent(Slice from, bool decode_plus_sign_as_whitespace)
{
std::string result; std::string result;
if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) { if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) { return result; }
return result;
}
return std::nullopt; return std::nullopt;
} }
void EncodePercent(Slice from, std::string *to, void
const PercentEncodingOptions &options) { EncodePercent(Slice from, std::string *to, const PercentEncodingOptions &options)
{
auto &&unescaped = auto &&unescaped = GetUnescapedCharBitmap(options.style)[options.escape_reserved];
GetUnescapedCharBitmap(options.style)[options.escape_reserved];
int escape_char_count = 0; int escape_char_count = 0;
for (auto &&e : from) { for (auto &&e : from) {
if (!unescaped[static_cast<std::uint8_t>(e)]) { if (!unescaped[static_cast<std::uint8_t>(e)]) { ++escape_char_count; }
++escape_char_count;
}
} }
to->clear(); to->clear();
@ -87,15 +83,14 @@ void EncodePercent(Slice from, std::string *to,
} }
} }
bool DecodePercent(Slice from, std::string *to, bool
bool decode_plus_sign_as_whitespace) { DecodePercent(Slice from, std::string *to, bool decode_plus_sign_as_whitespace)
{
to->clear(); to->clear();
to->reserve(from.size()); to->reserve(from.size());
for (auto iter = from.begin(); iter != from.end();) { for (auto iter = from.begin(); iter != from.end();) {
if (*iter == '%') { if (*iter == '%') {
if (iter + 3 > from.end()) { if (iter + 3 > from.end()) { return false; }
return false;
}
auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2)); auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2));
if (TILE_LIKELY(v != -1)) { if (TILE_LIKELY(v != -1)) {
to->push_back(v); to->push_back(v);
@ -115,4 +110,4 @@ bool DecodePercent(Slice from, std::string *to,
} }
return true; return true;
} }
} // namespace tile }// namespace tile

View File

@ -18,23 +18,19 @@ 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

View File

@ -6,21 +6,20 @@
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 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
Slice set1 = ";,/?:@&=+$"; // Reserved Characters Slice set1 = ";,/?:@&=+$"; // Reserved Characters
Slice set2 = "-_.!~*'()"; // Unescaped Characters Slice set2 = "-_.!~*'()"; // Unescaped Characters
Slice set3 = "#"; // Number Sign Slice set3 = "#"; // Number Sign
Slice set4 = "ABC abc 123"; // Alphanumeric Characters + Space 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));
@ -43,25 +42,24 @@ TEST(PercentEncoding, Emca262) {
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( EXPECT_EQ("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%"
"_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%"
"20123", "20123",
EncodePercent(str, rfc3986_reversed)); EncodePercent(str, rfc3986_reversed));
EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123", EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123", EncodePercent(str, rfc3986));
EncodePercent(str, rfc3986));
EXPECT_EQ(str, EXPECT_EQ(str,
DecodePercent("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%" DecodePercent("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%"
"2B%3D~%24ABC%20abc%20123")); "2B%3D~%24ABC%20abc%20123"));
EXPECT_EQ(str, DecodePercent("_-,;:!?.'()[]@*/&#+=~$ABC%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 // `encodeRFC5987ValueChars from MDN does not seems quite right (in that it
// does escape `#` `$` ..., which is not required.): // does escape `#` `$` ..., which is not required.):
// //
@ -87,22 +85,18 @@ TEST(PercentEncoding, Rfc5987) {
// PercentEncodingStyle::Rfc5987})); // 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"));
EXPECT_EQ("a b", DecodePercent("a+b", true)); EXPECT_EQ("a b", DecodePercent("a+b", true));
} }
} // namespace tile }// namespace tile

View File

@ -7,7 +7,9 @@
namespace tile { namespace tile {
void Benchmark_Base64Encode(benchmark::State &state) { void
Benchmark_Base64Encode(benchmark::State &state)
{
std::string random_str; std::string random_str;
std::string out; std::string out;
while (state.KeepRunning()) { while (state.KeepRunning()) {
@ -18,7 +20,10 @@ void Benchmark_Base64Encode(benchmark::State &state) {
EncodeBase64(random_str, &out); EncodeBase64(random_str, &out);
} }
} }
void Benchmark_Base64Decode(benchmark::State &state) {
void
Benchmark_Base64Decode(benchmark::State &state)
{
std::string random_str; std::string random_str;
std::string base64; std::string base64;
std::string plain_text; std::string plain_text;
@ -32,7 +37,9 @@ void Benchmark_Base64Decode(benchmark::State &state) {
} }
} }
void Benchmark_PercentEncode(benchmark::State &state) { void
Benchmark_PercentEncode(benchmark::State &state)
{
std::string random_str; std::string random_str;
std::string out; std::string out;
while (state.KeepRunning()) { while (state.KeepRunning()) {
@ -43,7 +50,9 @@ void Benchmark_PercentEncode(benchmark::State &state) {
} }
} }
void Benchmark_PercentDecode(benchmark::State &state) { void
Benchmark_PercentDecode(benchmark::State &state)
{
std::string random_str; std::string random_str;
std::string percent; std::string percent;
std::string plain_text; std::string plain_text;
@ -56,7 +65,9 @@ void Benchmark_PercentDecode(benchmark::State &state) {
} }
} }
void Benchmark_HexEncode(benchmark::State &state) { void
Benchmark_HexEncode(benchmark::State &state)
{
std::string random_str; std::string random_str;
std::string percent; std::string percent;
while (state.KeepRunning()) { while (state.KeepRunning()) {
@ -67,7 +78,9 @@ void Benchmark_HexEncode(benchmark::State &state) {
} }
} }
void Benchmark_HexDecode(benchmark::State &state) { void
Benchmark_HexDecode(benchmark::State &state)
{
std::string random_str; std::string random_str;
std::string percent; std::string percent;
std::string plain_text; std::string plain_text;
@ -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

View File

@ -6,20 +6,20 @@
#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
underlying_value(T v) -> typename std::underlying_type<T>::type
{
return static_cast<typename std::underlying_type<T>::type>(v); 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) \ #define TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, Op) \
constexpr Type &operator Op(Type & left, Type right) { return left Op right; } constexpr Type &operator Op(Type & left, Type right) { return left Op right; }
@ -31,10 +31,8 @@ constexpr auto underlying_value(T v) -> typename std::underlying_type<T>::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

View File

@ -13,46 +13,57 @@ namespace tile {
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 {
template<typename T>
T *UncheckedGet() const noexcept
{
return static_cast<T *>(ptr_); return static_cast<T *>(ptr_);
} }
constexpr explicit operator bool() const noexcept { return !!ptr_; } constexpr explicit operator bool() const noexcept { return !!ptr_; }
void Reset(std::nullptr_t = nullptr) noexcept {
void Reset(std::nullptr_t = nullptr) noexcept
{
if (ptr_) { if (ptr_) {
deleter_(ptr_); deleter_(ptr_);
ptr_ = nullptr; ptr_ = nullptr;
} }
} }
TILE_NODISCARD void *Leak() noexcept {
TILE_NODISCARD void *Leak() noexcept
{
void *rc = nullptr; void *rc = nullptr;
std::swap(rc, ptr_); std::swap(rc, ptr_);
return rc; return rc;
@ -64,6 +75,6 @@ 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

File diff suppressed because it is too large Load Diff

View File

@ -9,22 +9,20 @@ namespace tile {
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) { std::pair<Slice, Slice>
SplitFirstPart(Slice path)
{
auto pos = path.find_first_of('/'); auto pos = path.find_first_of('/');
if (pos == tile::Slice::npos) { if (pos == tile::Slice::npos) {
return std::make_pair(path, ""); return std::make_pair(path, "");
@ -33,52 +31,53 @@ std::pair<Slice, Slice> SplitFirstPart(Slice path) {
} }
} }
std::pair<Slice, Slice> SplitLastPart(Slice path) { std::pair<Slice, Slice>
SplitLastPart(Slice path)
{
auto pos = path.find_last_of('/'); auto pos = path.find_last_of('/');
if (pos == tile::Slice::npos) { if (pos == tile::Slice::npos) { return std::make_pair("", path); }
return std::make_pair("", path);
}
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::string JoinPath(Slice a, Slice b) { std::string
if (EndsWith(b, "/")) { JoinPath(Slice a, Slice b)
b.RemoveSuffix(1); {
} if (EndsWith(b, "/")) { b.RemoveSuffix(1); }
if (EndsWith(a, "/")) { if (EndsWith(a, "/")) { a.RemoveSuffix(1); }
a.RemoveSuffix(1);
}
if (b.empty()) { if (b.empty()) { return a.ToString(); }
return a.ToString(); if (a.empty()) { return b.ToString(); }
}
if (a.empty()) {
return b.ToString();
}
return a.ToString() + "/" + b.ToString(); return a.ToString() + "/" + b.ToString();
} }
std::string SubstituteEscapedSlashForZero(Slice path) { std::string
SubstituteEscapedSlashForZero(Slice path)
{
TILE_CHECK(path.find('\0') == tile::Slice::npos); TILE_CHECK(path.find('\0') == tile::Slice::npos);
return Replace(path, "\\/", '\0'); return Replace(path, "\\/", '\0');
} }
std::string SubstituteZeroForEscapedSlash(Slice path) { std::string
SubstituteZeroForEscapedSlash(Slice path)
{
return Replace(path, '\0', "\\/"); return Replace(path, '\0', "\\/");
} }
std::string UnescapeZeroToPlainSlash(Slice path) { std::string
UnescapeZeroToPlainSlash(Slice path)
{
return Replace(path, '\0', '/'); return Replace(path, '\0', '/');
} }
} // namespace }// 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);
@ -88,13 +87,9 @@ ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
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;
@ -125,41 +120,42 @@ ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
} }
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); auto real_path = SubstituteEscapedSlashForZero(rel_path);
CHECK_RELATIVE_PATH(real_path); CHECK_RELATIVE_PATH(real_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();
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 *
ExposedVarGroup::FindOrCreate(Slice abs_path)
{
auto real_path = SubstituteEscapedSlashForZero(abs_path); auto real_path = SubstituteEscapedSlashForZero(abs_path);
CHECK_ABSOLUTE_PATH(real_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) {
std::optional<Json::Value>
ExposedVarGroup::TryGet(Slice abs_path)
{
auto real_path = SubstituteEscapedSlashForZero(abs_path); auto real_path = SubstituteEscapedSlashForZero(abs_path);
TILE_CHECK(!real_path.empty()); TILE_CHECK(!real_path.empty());
CHECK_ABSOLUTE_PATH(real_path); CHECK_ABSOLUTE_PATH(real_path);
if (real_path == "/") { if (real_path == "/") { return Root()->Dump(); }
return Root()->Dump();
}
Slice left_path; Slice left_path;
auto rel_path = real_path.substr(1); auto rel_path = real_path.substr(1);
auto parent = Root()->FindLowest(rel_path, &left_path); auto parent = Root()->FindLowest(rel_path, &left_path);
auto name_and_rest = SplitFirstPart(left_path); auto name_and_rest = SplitFirstPart(left_path);
if (name_and_rest.first.empty()) { if (name_and_rest.first.empty()) { return parent->Dump(); }
return parent->Dump();
}
auto s = name_and_rest.first.ToString(); auto s = name_and_rest.first.ToString();
std::lock_guard<std::mutex> lk(parent->lock_); std::lock_guard<std::mutex> lk(parent->lock_);
@ -176,18 +172,27 @@ std::optional<Json::Value> ExposedVarGroup::TryGet(Slice abs_path) {
} }
} }
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 *
ExposedVarGroup::Root()
{
static ExposedVarGroup evg("/"); static ExposedVarGroup evg("/");
return &evg; return &evg;
} }
const std::string &ExposedVarGroup::AbsolutePath() const { return abs_path_; }
ExposedVarGroup *ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) { const std::string &
ExposedVarGroup::AbsolutePath() const
{
return abs_path_;
}
ExposedVarGroup *
ExposedVarGroup::FindLowest(Slice rel_path, Slice *left)
{
CHECK_RELATIVE_PATH(rel_path); CHECK_RELATIVE_PATH(rel_path);
auto current = this; auto current = this;
@ -205,13 +210,13 @@ ExposedVarGroup *ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) {
rel_path = name_and_rest.second; rel_path = name_and_rest.second;
} }
if (left) { if (left) { *left = rel_path; }
*left = rel_path;
}
return current; return current;
} }
ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) { ExposedVarGroup *
ExposedVarGroup::CreateUpto(Slice rel_path)
{
CHECK_RELATIVE_PATH(rel_path); CHECK_RELATIVE_PATH(rel_path);
Slice left_path; Slice left_path;
@ -225,10 +230,8 @@ ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) {
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));
@ -236,20 +239,19 @@ ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) {
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
ExposedVarGroup::AddDirect(Slice name, Getter value)
{
auto s = name.ToString(); auto s = name.ToString();
std::lock_guard<std::mutex> lk(lock_); std::lock_guard<std::mutex> lk(lock_);
TILE_CHECK(leaves_.find(s) == leaves_.end(), TILE_CHECK(leaves_.find(s) == leaves_.end(), "Value [{}] has already been registered at [{}].", name,
"Value [{}] has already been registered at [{}].", name,
AbsolutePath()); AbsolutePath());
TILE_CHECK(nodes_.find(s) == nodes_.end(), "Path [{}] has already been used.", TILE_CHECK(nodes_.find(s) == nodes_.end(), "Path [{}] has already been used.", JoinPath(AbsolutePath(), name));
JoinPath(AbsolutePath(), name));
leaves_[s] = std::move(value); leaves_[s] = std::move(value);
return Deferred([this, s] { return Deferred([this, s] {
@ -258,28 +260,29 @@ ExposedVarGroup::Handle ExposedVarGroup::AddDirect(Slice name, Getter value) {
}); });
} }
Json::Value ExposedVarGroup::Dump() const { Json::Value
ExposedVarGroup::Dump() const
{
std::lock_guard<std::mutex> lk(lock_); std::lock_guard<std::mutex> lk(lock_);
Json::Value jsv; Json::Value jsv;
for (auto &&node : nodes_) { for (auto &&node : nodes_) { jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump(); }
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>
ExposedVarDynamicTree::TryGet(Slice rel_path) const
{
auto real_path = SubstituteEscapedSlashForZero(rel_path); auto real_path = SubstituteEscapedSlashForZero(rel_path);
auto jsv = getter_(); auto jsv = getter_();
Json::Value *ptr = &jsv; Json::Value *ptr = &jsv;
@ -290,11 +293,9 @@ std::optional<Json::Value> ExposedVarDynamicTree::TryGet(Slice rel_path) const {
ptr = &(*ptr)[unescaped]; ptr = &(*ptr)[unescaped];
} }
if (ptr->isNull()) { if (ptr->isNull()) { return {}; }
return {};
}
return *ptr; return *ptr;
} }
} // namespace tile }// namespace tile

View File

@ -15,53 +15,54 @@
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,
Json::Value> {
return Json::Value(format_as(t)); return Json::Value(format_as(t));
} }
template <class T> Json::Value ToJsonValue(const std::atomic<T> &v) { template<class T>
Json::Value
ToJsonValue(const std::atomic<T> &v)
{
return ToJsonValue(v.load(std::memory_order_relaxed)); return ToJsonValue(v.load(std::memory_order_relaxed));
} }
} // namespace exposed_var }// namespace exposed_var
class ExposedVarDynamicTree; class ExposedVarDynamicTree;
@ -94,26 +95,26 @@ private:
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:
@ -121,14 +122,15 @@ private:
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:
@ -138,8 +140,8 @@ private:
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;
@ -150,31 +152,33 @@ private:
}; };
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; Json::Value result;
std::unordered_map<std::string, T> m = {{"1s", 1}}; std::unordered_map<std::string, T> m = {
for (auto &&item : 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_); });
} }
@ -183,12 +187,17 @@ private:
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

View File

@ -4,10 +4,15 @@
#include "json/json.h" #include "json/json.h"
namespace tile { namespace tile {
ExposedVarGroup *GetFancyGroup() { ExposedVarGroup *
GetFancyGroup()
{
return ExposedVarGroup::FindOrCreate("/a/b"); return ExposedVarGroup::FindOrCreate("/a/b");
} }
Json::Value GetTree() {
Json::Value
GetTree()
{
Json::Value jsv; Json::Value jsv;
jsv["dir"]["sub-dir"]["key"] = 5; jsv["dir"]["sub-dir"]["key"] = 5;
jsv["key"] = "6"; jsv["key"] = "6";
@ -21,10 +26,10 @@ 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("/"); auto opt = ExposedVarGroup::TryGet("/");
ASSERT_TRUE(opt); ASSERT_TRUE(opt);
auto &&jsv = *opt; auto &&jsv = *opt;
@ -35,4 +40,4 @@ TEST(ExposedVar, Mutate) {
*v1 = 5; *v1 = 5;
} }
} // namespace tile }// namespace tile

Some files were not shown because too many files have changed in this diff Show More