Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
758e9e2d94 | ||
|
|
a5f86ed839 | ||
|
|
dbfdf59bfe | ||
|
|
abacf29b85 | ||
|
|
5e7a31e5c5 |
No files matched your search
+20
-23
@@ -16,6 +16,7 @@ set(
|
||||
set(SPARK_BIN "${EXTERNALS_ROOT}/bin")
|
||||
set(SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
|
||||
set(SPARK_LIB "${EXTERNALS_ROOT}/lib/$<CONFIG>")
|
||||
set(BUILD_SPARK_CONFIG OFF CACHE BOOL "Build lib_spark_config")
|
||||
set(BUILD_API_SERVER OFF CACHE BOOL "Build the api server")
|
||||
set(BUILD_DOCS OFF CACHE BOOL "Build documentation")
|
||||
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
|
||||
@@ -30,33 +31,23 @@ include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zli
|
||||
link_directories(${SPARK_LIB})
|
||||
|
||||
if(WIN32)
|
||||
# flags
|
||||
set(
|
||||
CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} \
|
||||
/MP \
|
||||
/WX /wd4251 /wd4250 /wd4251 /wd4244 /wd4267 /wd4355 /wd4275 /wd4345 \
|
||||
/bigobj \
|
||||
/DCURL_STATICLIB \
|
||||
/D_WINSOCK_DEPRECATED_NO_WARNINGS \
|
||||
/D__TBB_NO_IMPLICIT_LINKAGE \
|
||||
/DBOOST_ALL_NO_LIB \
|
||||
/std:c++17 \
|
||||
/D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} /MT")
|
||||
set(
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} \
|
||||
/DEBUG \
|
||||
/OPT:REF \
|
||||
/OPT:ICF \
|
||||
/LTCG")
|
||||
|
||||
add_compile_definitions(SHARED_MEMORY_PREFIX="spark")
|
||||
add_compile_definitions(APP_DATA_DIR="\$\{APPDATA\}/bts/")
|
||||
# subdirs
|
||||
add_subdirectory(src/lib_expression)
|
||||
add_subdirectory(src/lib)
|
||||
elseif(BUILD_SPARK_CONFIG)
|
||||
add_compile_definitions(SHARED_MEMORY_PREFIX="sparkd")
|
||||
add_compile_definitions(APP_DATA_DIR="\$\{HOME\}/sparkdata/")
|
||||
add_subdirectory(src/lib_core)
|
||||
add_subdirectory(src/lib_logging)
|
||||
add_subdirectory(src/lib_io)
|
||||
add_subdirectory(src/lib_domain)
|
||||
add_subdirectory(src/lib_protobuf)
|
||||
add_subdirectory(src/lib_threading)
|
||||
add_subdirectory(src/lib_expression)
|
||||
add_subdirectory(src/lib_md)
|
||||
add_subdirectory(src/lib_spark_config)
|
||||
else()
|
||||
# make sparkd look for libspark.so in its directory
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
@@ -118,6 +109,12 @@ else()
|
||||
if (BUILD_SPARKD)
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/data_files)
|
||||
# add_subdirectory(src/lib_md)
|
||||
add_subdirectory(src/lib_core)
|
||||
add_subdirectory(src/lib_logging)
|
||||
add_subdirectory(src/lib_threading)
|
||||
add_subdirectory(src/lib_expression)
|
||||
add_subdirectory(src/lib_spark_config)
|
||||
endif()
|
||||
add_subdirectory(src/lib)
|
||||
if (BUILD_ADMIN_WEB_APP)
|
||||
|
||||
@@ -145,6 +145,47 @@
|
||||
"inherits": "sparkd-release-gcc",
|
||||
"displayName": "Publish",
|
||||
"description": "Production build compiled w/ gcc and ready to ship"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "spark-config",
|
||||
"displayName": "Spark Config",
|
||||
"description": "Test build for lib_spark_config",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"generator": "Unix Makefiles",
|
||||
"cacheVariables": {
|
||||
"platform": "linux",
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_BUILD_WITH_INSTALL_RPATH": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"CMAKE_INSTALL_RPATH": ".",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
|
||||
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
|
||||
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
|
||||
"CMAKE_CXX_STANDARD": "17",
|
||||
"BUILD_SPARKD": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_DOCS": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_SDK": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"BUILD_SPARK_CONFIG": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"BUILD_ADMIN_WEB_APP": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -224,6 +265,13 @@
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "spark-config",
|
||||
"configurePreset": "spark-config",
|
||||
"displayName": "Spark Config",
|
||||
"inherits": "sparkd"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sparkd-publish",
|
||||
"configurePreset": "sparkd-publish",
|
||||
|
||||
+23
-10
@@ -16,13 +16,26 @@ function(Protobuf_Generate_Cpp SRCS HDRS)
|
||||
|
||||
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc")
|
||||
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")
|
||||
|
||||
set(PROTOBUF_INCLUDE_DIR_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS " -I ")
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
foreach(INC ${PROTOBUF_INCLUDE_DIRS})
|
||||
set(PROTOBUF_INCLUDE_DIR_ARGS ${PROTOBUF_INCLUDE_DIR_ARGS} -I ${INC})
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS " -I ")
|
||||
# list(APPEND PROTOBUF_INCLUDE_DIR_ARGS ${INC})
|
||||
endforeach()
|
||||
set(PROTOBUF_INCLUDE_DIR_ARGS ${PROTOBUF_INCLUDE_DIR_ARGS} -I ${CMAKE_CURRENT_BINARY_DIR})
|
||||
# string(JOIN " -I " PROTOBUF_INCLUDE_DIR_ARGS_ ${PROTOBUF_INCLUDE_DIR_ARGS})
|
||||
message("pb inc: ${PROTOBUF_INCLUDE_DIR_ARGS}")
|
||||
# set(cmd_text "${Protobuf_PROTOC_EXECUTABLE} --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${PROTOBUF_INCLUDE_DIR_ARGS_} -I ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL}")
|
||||
message("CMD: ${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${PROTOBUF_INCLUDE_DIR_ARGS} ${ABS_FIL}")
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
|
||||
COMMAND
|
||||
${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_BINARY_DIR}/src -I
|
||||
${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_SOURCE_DIR}/src ${ABS_FIL}
|
||||
COMMAND ${Protobuf_PROTOC_EXECUTABLE} --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${PROTOBUF_INCLUDE_DIR_ARGS} ${ABS_FIL}
|
||||
# COMMAND
|
||||
# ${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I
|
||||
# ${PROTOBUF_INCLUDE_DIR_ARGS} ${ABS_FIL}
|
||||
DEPENDS ${ABS_FIL}
|
||||
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
|
||||
VERBATIM)
|
||||
@@ -90,10 +103,10 @@ function(Service_Compiler_Generate_Cpp SRCS HDRS)
|
||||
COMMAND SET PYTHONPATH=${SPARK_PYTHON}
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/.venv/Scripts/activate.bat
|
||||
COMMAND
|
||||
${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_BINARY_DIR}/src
|
||||
${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR}
|
||||
--plugin=protoc-gen-service=${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.cmd
|
||||
--service_out=lang=cpp:${CMAKE_BINARY_DIR}/src -I
|
||||
${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_SOURCE_DIR}/src ${ABS_FIL}
|
||||
--service_out=lang=cpp:${CMAKE_CURRENT_BINARY_DIR} -I
|
||||
${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL}
|
||||
DEPENDS ${ABS_FIL} ${TEMPLATES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/api/service_compiler/plugin.py
|
||||
COMMENT "Running C++ service compiler on ${FIL}"
|
||||
@@ -109,10 +122,10 @@ function(Service_Compiler_Generate_Cpp SRCS HDRS)
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tools/venv_exec.sh
|
||||
ARGS
|
||||
${Protobuf_PROTOC_EXECUTABLE}
|
||||
--cpp_out=${CMAKE_BINARY_DIR}/src
|
||||
--cpp_out=${CMAKE_CURRENT_BINARY_DIR}
|
||||
--plugin=protoc-gen-service=${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.py
|
||||
--service_out=lang=cpp,make_file_name=${ABS_FIL}:${CMAKE_BINARY_DIR}/src
|
||||
-I ${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_SOURCE_DIR}/src ${ABS_FIL}
|
||||
--service_out=lang=cpp,make_file_name=${ABS_FIL}:${CMAKE_CURRENT_BINARY_DIR}
|
||||
-I ${PROTOBUF_INCLUDE_DIRS} -I ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL}
|
||||
DEPENDS ${ABS_FIL} ${TEMPLATES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/api/service_compiler/plugin.py
|
||||
COMMENT "Running C++ service compiler on ${FIL}"
|
||||
|
||||
+18
-70
@@ -2,31 +2,8 @@ cmake_minimum_required(VERSION 3.5)
|
||||
project(libspark)
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/safe_dep_glob.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Protobuf.cmake)
|
||||
|
||||
# DEPENDENCIES
|
||||
file(
|
||||
GLOB_RECURSE Config_Protos
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
config/protos/*.proto
|
||||
api/domain/SecurityMetadataCollection.proto
|
||||
api/md/MarketDataConfig.proto)
|
||||
file(
|
||||
GLOB_RECURSE Service_Protos
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
api/services/*.proto)
|
||||
file(
|
||||
GLOB_RECURSE NonConfig_Protos
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
*.proto)
|
||||
list(REMOVE_ITEM NonConfig_Protos ${Service_Protos} ${Config_Protos})
|
||||
|
||||
set(CONFIG_DEFAULTS_PATH "config/profiles/default/*.conf" CACHE STRING "Path(s) to config defaults")
|
||||
file(
|
||||
GLOB_RECURSE Conf_Defaults
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CONFIG_DEFAULTS_PATH})
|
||||
|
||||
if(WIN32 OR NOT ${BUILD_SPARKD})
|
||||
file(
|
||||
GLOB_RECURSE SparkLib_Sources
|
||||
@@ -35,7 +12,6 @@ if(WIN32 OR NOT ${BUILD_SPARKD})
|
||||
config/*.cpp
|
||||
core/*.cpp
|
||||
domain_model/*.cpp
|
||||
expression/*.cpp
|
||||
logging/*.cpp
|
||||
io/*.cpp
|
||||
md/*.cpp
|
||||
@@ -51,53 +27,9 @@ else()
|
||||
*.cpp)
|
||||
endif()
|
||||
|
||||
update_deps_file("${SparkLib_Sources};${Config_Protos};${Service_Protos};${NonConfig_Protos};${Conf_Defaults}")
|
||||
update_deps_file(${SparkLib_Sources})
|
||||
# END DEPENDENCIES
|
||||
|
||||
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${NonConfig_Protos} ${Config_Protos})
|
||||
service_compiler_generate_cpp(SERVICE_COMPILER_SRCS SERVICE_COMPILER_HDRS ${Service_Protos})
|
||||
|
||||
# generate list of config protos
|
||||
set(config_proto_file ${CMAKE_CURRENT_BINARY_DIR}/config_proto_files.h)
|
||||
|
||||
# generate a string with the current config files
|
||||
set(config_proto_str "const char * config_proto_files[] = { // generated file\n")
|
||||
foreach(protopath ${Config_Protos})
|
||||
get_filename_component(protoname ${protopath} NAME)
|
||||
string(APPEND config_proto_str "\"${protoname}\",\n")
|
||||
endforeach(protopath)
|
||||
string(APPEND config_proto_str "} ;\n")
|
||||
|
||||
# compare with file and if there is any difference update the file
|
||||
set(old_config_proto_str "")
|
||||
if(EXISTS "${config_proto_file}")
|
||||
file(READ "${config_proto_file}" old_config_proto_str)
|
||||
endif()
|
||||
if(NOT old_config_proto_str STREQUAL config_proto_str)
|
||||
file(WRITE "${config_proto_file}" "${config_proto_str}")
|
||||
endif()
|
||||
|
||||
# generate static defaults
|
||||
set(config_defaults_file ${CMAKE_CURRENT_BINARY_DIR}/config_defaults.h)
|
||||
# Defaults cannot be a single string because the compiler complains about a string longer than 65k
|
||||
set(config_defaults_str "const char * config_static_defaults[] = { ")
|
||||
foreach(cfg_file_path ${Conf_Defaults})
|
||||
file(READ ${cfg_file_path} cfg_file_content)
|
||||
string(REPLACE "\"" "\\\"" cfg_file_content_escaped ${cfg_file_content})
|
||||
string(REPLACE "\n" ";" cfg_lines ${cfg_file_content_escaped})
|
||||
foreach(line ${cfg_lines})
|
||||
string(APPEND config_defaults_str "\n \"${line}\",")
|
||||
endforeach(line)
|
||||
endforeach(cfg_file_path)
|
||||
string(APPEND config_defaults_str "};")
|
||||
set(old_defaults_str "")
|
||||
if(EXISTS ${config_defaults_file})
|
||||
file(READ ${config_defaults_file} old_defaults_str)
|
||||
endif()
|
||||
if(NOT config_defaults_str STREQUAL old_defaults_str)
|
||||
file(WRITE "${config_defaults_file}" "${config_defaults_str}")
|
||||
endif()
|
||||
|
||||
# lib
|
||||
if(WIN32)
|
||||
add_library(libspark SHARED ${SparkLib_Sources} ${PROTO_SRCS} ${SERVICE_COMPILER_SRCS})
|
||||
@@ -120,9 +52,15 @@ if(WIN32)
|
||||
# Link libs to support the front end (Spark.exe), as well as the api server
|
||||
# (spark_api_server.exe)
|
||||
target_link_libraries(libspark
|
||||
lib_core
|
||||
lib_spark_config
|
||||
lib_expression
|
||||
lib_logging
|
||||
lib_threading
|
||||
crypt32
|
||||
debug fmtd
|
||||
optimized fmt
|
||||
lib_expression
|
||||
libcrypto
|
||||
libcurl
|
||||
libboost_system
|
||||
@@ -140,6 +78,11 @@ else()
|
||||
# Linux ONLY
|
||||
if(BUILD_API_SERVER)
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
lib_core
|
||||
lib_spark_config
|
||||
lib_expression
|
||||
lib_logging
|
||||
lib_threading
|
||||
curl
|
||||
tbb
|
||||
boost_system
|
||||
@@ -162,7 +105,12 @@ else()
|
||||
|
||||
if(BUILD_SPARKD)
|
||||
target_link_libraries(
|
||||
libspark
|
||||
${PROJECT_NAME}
|
||||
lib_core
|
||||
lib_spark_config
|
||||
lib_expression
|
||||
lib_logging
|
||||
lib_threading
|
||||
protobuf
|
||||
double-conversion
|
||||
boost_system
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package bts.api.domain;
|
||||
|
||||
option csharp_namespace = "bts.api.domain";
|
||||
|
||||
enum Currency {
|
||||
BTC = 0;
|
||||
ETH = 1;
|
||||
USD = 3;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#include "ConfigLog.h"
|
||||
|
||||
#include "lib/logging/logging.h"
|
||||
|
||||
namespace bts {
|
||||
namespace config {
|
||||
namespace internal {
|
||||
|
||||
struct ConfigLog::PImpl {
|
||||
PImpl()
|
||||
: print_errors(true), logger_hub("config_logger"),
|
||||
logger(logger_hub.make_logger()) {}
|
||||
|
||||
bool print_errors;
|
||||
log::LoggerHub logger_hub;
|
||||
log::Logger &logger;
|
||||
};
|
||||
|
||||
ConfigLog::ConfigLog() : me(new PImpl()) {
|
||||
log::LoggingConfig cfg;
|
||||
cfg.level_names = {{Diag, "Diag"}, {Info, "Info"}, {Warn, "Warn"},
|
||||
{Error, "Error"}, {Fatal, "Fatal"}, {Sys, "Sys"}};
|
||||
cfg.subsystem_names = {{log::Gen, "Gen"}, {log::Log, "Log"}};
|
||||
|
||||
cfg.logger_initial_queue_size = 10;
|
||||
|
||||
log::Filter f;
|
||||
for (auto &l : f)
|
||||
l = Warn;
|
||||
|
||||
cfg.deployment_type = "Config";
|
||||
f[log::Cfg.ordinal] = Warn;
|
||||
cfg.sinks["console"] = std::make_pair(log::console_sink(cfg), f);
|
||||
f[log::Cfg.ordinal] = Info;
|
||||
cfg.sinks["syslog"] = std::make_pair(log::syslog_sink(cfg), f);
|
||||
me->logger_hub.config(cfg);
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
void ConfigLog::start() { me->logger_hub.start(); }
|
||||
|
||||
ConfigLog::~ConfigLog() { me->logger.mark_inactive(); }
|
||||
|
||||
bool ConfigLog::print_errors() const { return me->print_errors; }
|
||||
void ConfigLog::set_print_errors(bool value) { me->print_errors = value; }
|
||||
|
||||
void ConfigLog::info(const std::string &msg) {
|
||||
if (me->print_errors)
|
||||
me->logger.enqueue(
|
||||
log::log_ts(), log::Cfg, Info, __FILE__, __LINE__, "{0}", msg);
|
||||
}
|
||||
|
||||
void ConfigLog::error(const std::string &msg) {
|
||||
if (me->print_errors)
|
||||
me->logger.enqueue(
|
||||
log::log_ts(), log::Cfg, Error, __FILE__, __LINE__, "{0}", msg);
|
||||
}
|
||||
|
||||
void ConfigLog::warning(const std::string &msg) {
|
||||
if (me->print_errors)
|
||||
me->logger.enqueue(
|
||||
log::log_ts(), log::Cfg, Warn, __FILE__, __LINE__, "{0}", msg);
|
||||
}
|
||||
|
||||
//
|
||||
// Log Aggregator
|
||||
|
||||
LogAggregator::LogAggregator(IConfigLog &log) : _log(log), _buffer() {}
|
||||
|
||||
void LogAggregator::info(const std::string &msg) {
|
||||
_buffer << msg;
|
||||
#ifdef _WIN32
|
||||
_buffer << std::endl;
|
||||
#else
|
||||
_buffer << " ";
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogAggregator::error(const std::string &msg) { _log.error(msg); }
|
||||
|
||||
void LogAggregator::warning(const std::string &msg) {
|
||||
_log.warning(msg);
|
||||
_buffer << "> Warning: " << msg << std::endl;
|
||||
#ifdef _WIN32
|
||||
_buffer << std::endl;
|
||||
#else
|
||||
_buffer << " ";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string LogAggregator::text() { return _buffer.str(); }
|
||||
|
||||
} // namespace internal
|
||||
} // namespace config
|
||||
} // namespace bts
|
||||
@@ -1,182 +0,0 @@
|
||||
#include "ConfigQuerier.h"
|
||||
#include "config.h"
|
||||
#include "lib/collections/Contains.h"
|
||||
#include "lib/config/ConfigQueryErrors.h"
|
||||
#include "lib/config/text_format/text_format.h"
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
#include "lib/utils/bts_export.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/generated_message_reflection.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace proto = google::protobuf;
|
||||
namespace bts::config::interop {
|
||||
std::string get_macro_string(const char *name);
|
||||
} // namespace bts::config::interop
|
||||
|
||||
namespace bts::config {
|
||||
|
||||
using namespace bts;
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
static vector<string> argv_to_vector(const char **argv, int argc) {
|
||||
vector<string> vec;
|
||||
vec.reserve(argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
vec.emplace_back(argv[i]);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void fill_in_defaults(google::protobuf::Message *msg) {
|
||||
const proto::Descriptor *d = msg->GetDescriptor();
|
||||
const proto::Reflection *r = msg->GetReflection();
|
||||
for (int x = 0; x < d->field_count(); ++x) {
|
||||
auto *fd = d->field(x);
|
||||
auto type = fd->cpp_type();
|
||||
if (!fd->is_repeated()) {
|
||||
if (type == proto::FieldDescriptor::CPPTYPE_MESSAGE) {
|
||||
fill_in_defaults(r->MutableMessage(msg, fd));
|
||||
} else if (fd->has_default_value() && !r->HasField(*msg, fd)) {
|
||||
switch (type) {
|
||||
case proto::FieldDescriptor::CPPTYPE_INT32:
|
||||
r->SetInt32(msg, fd, fd->default_value_int32());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_INT64:
|
||||
r->SetInt64(msg, fd, fd->default_value_int64());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_UINT32:
|
||||
r->SetUInt32(msg, fd, fd->default_value_uint32());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_UINT64:
|
||||
r->SetUInt64(msg, fd, fd->default_value_uint64());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_DOUBLE:
|
||||
r->SetDouble(msg, fd, fd->default_value_double());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_FLOAT:
|
||||
r->SetFloat(msg, fd, fd->default_value_float());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_BOOL:
|
||||
r->SetBool(msg, fd, fd->default_value_bool());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_ENUM:
|
||||
r->SetEnum(msg, fd, fd->default_value_enum());
|
||||
break;
|
||||
case proto::FieldDescriptor::CPPTYPE_STRING:
|
||||
r->SetString(msg, fd, fd->default_value_string());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run_config_query(
|
||||
const char **keysv,
|
||||
int keysc,
|
||||
const char **excluded_keysv,
|
||||
int excluded_keysc,
|
||||
bool all,
|
||||
bool list_keys,
|
||||
bool verbose) {
|
||||
const auto keys = argv_to_vector(keysv, keysc);
|
||||
const auto excluded_keys = argv_to_vector(excluded_keysv, excluded_keysc);
|
||||
ConfigQuerier::run(keys, excluded_keys, all, list_keys, verbose);
|
||||
}
|
||||
|
||||
void ConfigQuerier::init_(bool verbose) {
|
||||
char buffer[4096] = {0};
|
||||
utils::TimeStamp start = utils::TimeStamp::now();
|
||||
bool success = config::initialize_nothrow(buffer, sizeof(buffer));
|
||||
double time = (utils::TimeStamp::now() - start).seconds();
|
||||
|
||||
if (success) {
|
||||
success = config::internal::check_rules(buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
std::cout << buffer << std::endl;
|
||||
}
|
||||
if (verbose) {
|
||||
std::cout << "Load time> " << std::floor(time * 1000) << " ms"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int ConfigQuerier::run(
|
||||
vector<string> keys,
|
||||
const vector<string> &excluded_keys,
|
||||
bool all,
|
||||
bool list_keys,
|
||||
bool verbose) {
|
||||
auto errors = ConfigQueryErrors::NONE;
|
||||
init_(verbose);
|
||||
if (list_keys) {
|
||||
const auto fields = config::get_fields();
|
||||
for (const auto *f : fields) {
|
||||
std::cout << f->name() << "\n";
|
||||
}
|
||||
}
|
||||
if (keys.empty() && excluded_keys.empty() && !all) {
|
||||
return 0;
|
||||
}
|
||||
std::vector<std::string> macro_keys;
|
||||
for (auto it = keys.begin(); it != keys.end();) {
|
||||
if (it->empty()) {
|
||||
it = keys.erase(it);
|
||||
continue;
|
||||
}
|
||||
if ((*it)[0] == '$') {
|
||||
macro_keys.push_back(*it);
|
||||
it = keys.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
for (const auto &mk : macro_keys) {
|
||||
std::string result = config::interop::get_macro_string(mk.c_str());
|
||||
std::cout << mk << ": " << result << "\n";
|
||||
}
|
||||
|
||||
// If output is verbose, print unknown keys
|
||||
vector<string> unknown_keys;
|
||||
const auto msgs = config::get_messages(keys, excluded_keys, &unknown_keys);
|
||||
if (!unknown_keys.empty()) {
|
||||
errors |= ConfigQueryErrors::UNKNOWN_KEYS;
|
||||
std::cerr << "The following keys were not found:\n";
|
||||
for (const auto &k : unknown_keys) {
|
||||
std::cerr << k << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Print all messages to console
|
||||
google::protobuf::TextFormat::Printer printer;
|
||||
printer.SetInitialIndentLevel(2);
|
||||
for (const auto *m : msgs) {
|
||||
google::protobuf::Message *mut = m->New();
|
||||
mut->CopyFrom(*m);
|
||||
fill_in_defaults(mut);
|
||||
string str;
|
||||
printer.PrintToString(*mut, &str);
|
||||
std::cout << "\n" << m->GetTypeName();
|
||||
if (str.empty()) {
|
||||
std::cout << " { }";
|
||||
} else {
|
||||
std::cout << " {\n" << str << "}";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
return static_cast<int>(errors);
|
||||
}
|
||||
|
||||
} // namespace bts::config
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/utils/bts_export.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::config {
|
||||
|
||||
extern "C" BTS_EXPORT void run_config_query(
|
||||
const char **keysv,
|
||||
int keysc,
|
||||
const char **excluded_keysv,
|
||||
int excluded_keysc,
|
||||
bool all = false,
|
||||
bool list_keys = false,
|
||||
bool verbose = false);
|
||||
|
||||
class ConfigQuerier {
|
||||
public:
|
||||
static int run(
|
||||
std::vector<std::string> keys,
|
||||
const std::vector<std::string> &excluded_keys,
|
||||
bool all = false,
|
||||
bool list_keys = false,
|
||||
bool verbose = false);
|
||||
|
||||
private:
|
||||
static void init_(bool verbose);
|
||||
};
|
||||
|
||||
} // namespace bts::config
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/core/EnumMacros.h"
|
||||
|
||||
namespace bts::config {
|
||||
|
||||
enum class ConfigQueryErrors {
|
||||
NONE = 0,
|
||||
UNKNOWN_KEYS = 1,
|
||||
};
|
||||
|
||||
ENUM_BITWISE_OPS(ConfigQueryErrors, int)
|
||||
|
||||
} // namespace bts::config
|
||||
@@ -1,30 +0,0 @@
|
||||
# Config Profiles
|
||||
- A **Config Profile** is simply a folder in this directory containing *.conf*
|
||||
files, which can be used to override the default generated config.
|
||||
- Profiles can be used individually, or in combination.
|
||||
## To override the config defaults
|
||||
1. Make a new folder in this folder (name it anything you like).
|
||||
1. Add *.conf* files to the new folder that contain the desired config.
|
||||
1. Build with the new config profiles
|
||||
- Multiple profiles can be specified. If a particular field is overridden in
|
||||
multiple profiles, the last profile specified wins out
|
||||
- Example A) Running **build_sparkd.sh**
|
||||
|
||||
# This script runs cmake internally to configure the build, then builds it
|
||||
tools/build_sparkd.sh --build-type release --compiler gcc --config-profiles <profile1> <profile2...>
|
||||
|
||||
|
||||
- Example B) Running **cmake** directly
|
||||
|
||||
# Configure the build (only needs to be done once)
|
||||
cmake --preset sparkd-release-gcc -DCONFIG_DEFAULTS_PATH="config/profiles/default/*.conf;config/profiles/<profile1>/*.conf"
|
||||
# Now build it the usual way
|
||||
cd build/sparkd-release-gcc
|
||||
ninja
|
||||
|
||||
>- Multiple profiles (paths) can be specified as a semi-colon
|
||||
separated list.
|
||||
>- The new profile's path is preceeded by *config/profiles/default/\*.conf*,
|
||||
so omitted fields retain their original values. *This isn't necessary in
|
||||
the *build_sparkd.sh* example because that script internally converts the
|
||||
--config-profiles argument to **DCONFIG_DEFAULTS_PATH***.
|
||||
@@ -1,49 +0,0 @@
|
||||
MarketDataConfig {
|
||||
value {
|
||||
# binance {
|
||||
# num_depth_levels: 5
|
||||
# url_rest_base: "https://testnet.binance.vision"
|
||||
# url_ws_base: "wss://testnet.binance.vision"
|
||||
# security_criteria {
|
||||
# clauses {
|
||||
# security_types: SPOT
|
||||
# symbols: "BTC"
|
||||
# symbols: "ETH"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
# binance_us {
|
||||
# num_depth_levels: 5
|
||||
# url_rest_base: "https://api.binance.us"
|
||||
# url_ws_base: "wss://stream.binance.us:9443"
|
||||
# security_criteria {
|
||||
# clauses {
|
||||
# security_types: SPOT
|
||||
# symbols: "BTC"
|
||||
# symbols: "ETH"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
# binance_usds_futures {
|
||||
# num_depth_levels: 5
|
||||
# url_rest_base: "https://testnet.binancefuture.com"
|
||||
# url_ws_base: "wss://fstream.binance.com"
|
||||
# security_criteria {
|
||||
# clauses {
|
||||
# security_types: FUTURE
|
||||
# symbols: "BTC"
|
||||
# symbols: "ETH"
|
||||
# }
|
||||
# }
|
||||
# security_desc_formattings {
|
||||
# expression: "^(.+)(USD)(?=_)(.*)"
|
||||
# format: "$1USDT$3"
|
||||
# }
|
||||
# }
|
||||
deribit {
|
||||
json_conf_file_name: "deribit_demo_config.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
|
||||
SymbolList {
|
||||
value {
|
||||
# This key is here for backwards compatability ONLY.
|
||||
# If you need to update symbols you should update SymbolLists (plural)
|
||||
symbol: "GC"
|
||||
symbol: "SI"
|
||||
symbol: "ES"
|
||||
symbol: "BNB"
|
||||
symbol: "BTC"
|
||||
symbol: "ETH"
|
||||
}
|
||||
}
|
||||
|
||||
SymbolLists {
|
||||
value {
|
||||
list {
|
||||
key: "ICE"
|
||||
}
|
||||
list {
|
||||
key: "GLBX"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "DERIBIT"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "FTX"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "BINANCE"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "BINANCE_US"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "BINANCE_USDS_FUTURES"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbols {
|
||||
rule: "DeploymentType.type == 'DEMO' and System.platform == 'windows'"
|
||||
value {
|
||||
sim {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
simice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Symbols {
|
||||
rule: "DeploymentType.type == 'PROD' and System.platform == 'windows'"
|
||||
value {
|
||||
glbx {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
option: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
ice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Symbols {
|
||||
value {
|
||||
glbx {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
option: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
sim {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
option: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
ice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
simice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
deribit {
|
||||
future: @SymbolLists.list[DERIBIT].symbol
|
||||
option: @SymbolLists.list[DERIBIT].symbol
|
||||
spread: @SymbolLists.list[DERIBIT].symbol
|
||||
}
|
||||
ftx {
|
||||
future: @SymbolLists.list[FTX].symbol
|
||||
option: @SymbolLists.list[FTX].symbol
|
||||
spread: @SymbolLists.list[FTX].symbol
|
||||
}
|
||||
binance {
|
||||
future: @SymbolLists.list[BINANCE].symbol
|
||||
option: @SymbolLists.list[BINANCE].symbol
|
||||
spread: @SymbolLists.list[BINANCE].symbol
|
||||
}
|
||||
binance_us {
|
||||
future: @SymbolLists.list[BINANCE_US].symbol
|
||||
option: @SymbolLists.list[BINANCE_US].symbol
|
||||
spread: @SymbolLists.list[BINANCE_US].symbol
|
||||
}
|
||||
binance_usds_futures {
|
||||
future: @SymbolLists.list[BINANCE_USDS_FUTURES].symbol
|
||||
option: @SymbolLists.list[BINANCE_USDS_FUTURES].symbol
|
||||
spread: @SymbolLists.list[BINANCE_USDS_FUTURES].symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
Mercury {
|
||||
value {
|
||||
queue_size: 1000
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
|
||||
SymbolList {
|
||||
value {
|
||||
# This key is here for backwards compatability ONLY.
|
||||
# If you need to update symbols you should update SymbolLists (plural)
|
||||
symbol: "GC"
|
||||
symbol: "SI"
|
||||
symbol: "ES"
|
||||
symbol: "BNB"
|
||||
symbol: "BTC"
|
||||
symbol: "ETH"
|
||||
}
|
||||
}
|
||||
|
||||
SymbolLists {
|
||||
value {
|
||||
list {
|
||||
key: "ICE"
|
||||
}
|
||||
list {
|
||||
key: "GLBX"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "DERIBIT"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "FTX"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "BINANCE"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "BINANCE_US"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
list {
|
||||
key: "BINANCE_USDS_FUTURES"
|
||||
symbol: @SymbolList.symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbols {
|
||||
rule: "DeploymentType.type == 'DEMO' and System.platform == 'windows'"
|
||||
value {
|
||||
sim {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
simice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Symbols {
|
||||
rule: "DeploymentType.type == 'PROD' and System.platform == 'windows'"
|
||||
value {
|
||||
glbx {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
option: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
ice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Symbols {
|
||||
value {
|
||||
glbx {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
option: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
sim {
|
||||
future: @SymbolLists.list[GLBX].symbol
|
||||
spread: @SymbolLists.list[GLBX].symbol
|
||||
option: @SymbolLists.list[GLBX].symbol
|
||||
}
|
||||
ice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
simice {
|
||||
future: @SymbolLists.list[ICE].symbol
|
||||
spread: @SymbolLists.list[ICE].symbol
|
||||
}
|
||||
deribit {
|
||||
future: @SymbolLists.list[DERIBIT].symbol
|
||||
option: @SymbolLists.list[DERIBIT].symbol
|
||||
spread: @SymbolLists.list[DERIBIT].symbol
|
||||
}
|
||||
ftx {
|
||||
future: @SymbolLists.list[FTX].symbol
|
||||
option: @SymbolLists.list[FTX].symbol
|
||||
spread: @SymbolLists.list[FTX].symbol
|
||||
}
|
||||
binance {
|
||||
future: @SymbolLists.list[BINANCE].symbol
|
||||
option: @SymbolLists.list[BINANCE].symbol
|
||||
spread: @SymbolLists.list[BINANCE].symbol
|
||||
}
|
||||
binance_us {
|
||||
future: @SymbolLists.list[BINANCE_US].symbol
|
||||
option: @SymbolLists.list[BINANCE_US].symbol
|
||||
spread: @SymbolLists.list[BINANCE_US].symbol
|
||||
}
|
||||
binance_usds_futures {
|
||||
future: @SymbolLists.list[BINANCE_USDS_FUTURES].symbol
|
||||
option: @SymbolLists.list[BINANCE_USDS_FUTURES].symbol
|
||||
spread: @SymbolLists.list[BINANCE_USDS_FUTURES].symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
MarketDataConfig {
|
||||
value {
|
||||
binance {
|
||||
num_depth_levels: 5
|
||||
url_rest_base: "https://testnet.binance.vision"
|
||||
url_ws_base: "wss://testnet.binance.vision"
|
||||
security_criteria {
|
||||
clauses {
|
||||
security_types: SPOT
|
||||
symbols: "BTC"
|
||||
symbols: "ETH"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binance_us {
|
||||
num_depth_levels: 5
|
||||
url_rest_base: "https://api.binance.us"
|
||||
url_ws_base: "wss://stream.binance.us:9443"
|
||||
security_criteria {
|
||||
clauses {
|
||||
security_types: SPOT
|
||||
symbols: "BTC"
|
||||
symbols: "ETH"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binance_usds_futures {
|
||||
num_depth_levels: 5
|
||||
url_rest_base: "https://testnet.binancefuture.com"
|
||||
url_ws_base: "wss://fstream.binance.com"
|
||||
security_criteria {
|
||||
clauses {
|
||||
security_types: FUTURE
|
||||
symbols: "BTC"
|
||||
symbols: "ETH"
|
||||
}
|
||||
}
|
||||
security_desc_formattings {
|
||||
expression: "^(.+)(USD)(?=_)(.*)"
|
||||
format: "$1USDT$3"
|
||||
}
|
||||
}
|
||||
deribit {
|
||||
json_conf_file_name: "deribit_demo_config.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
MarketDataConfig {
|
||||
value {
|
||||
binance {
|
||||
num_depth_levels: 5
|
||||
url_rest_base: "https://api.binance.com"
|
||||
url_ws_base: "wss://stream.binance.com:9443"
|
||||
security_criteria {
|
||||
clauses {
|
||||
security_types: SPOT
|
||||
symbols: "BTC"
|
||||
symbols: "ETH"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binance_usds_futures {
|
||||
num_depth_levels: 5
|
||||
url_rest_base: "https://dapi.binance.com"
|
||||
url_ws_base: "wss://fstream.binance.com"
|
||||
security_criteria {
|
||||
clauses {
|
||||
security_types: FUTURE
|
||||
symbols: "BTC"
|
||||
symbols: "ETH"
|
||||
}
|
||||
}
|
||||
security_desc_formattings {
|
||||
expression: "^(.+)(USD)(?=_)(.*)"
|
||||
format: "$1USDT$3"
|
||||
}
|
||||
}
|
||||
deribit {
|
||||
json_conf_file_name: "deribit_demo_config.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
#include "HTTPServer.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace bts {
|
||||
namespace config {
|
||||
namespace internal {
|
||||
|
||||
HTTPServer::HTTPServer()
|
||||
: _io_service(),
|
||||
_acceptor(
|
||||
_io_service,
|
||||
tcp::endpoint(boost::asio::ip::address_v4::loopback(), 0)),
|
||||
_cur_socket(), _request_response_pairs(), _thread(), _done(false) {}
|
||||
|
||||
unsigned int HTTPServer::port() const {
|
||||
return _acceptor.local_endpoint().port();
|
||||
}
|
||||
|
||||
void HTTPServer::add_response(
|
||||
const std::string &path,
|
||||
const std::string &response,
|
||||
const std::string &content) {
|
||||
_request_response_pairs[path] = response + content;
|
||||
}
|
||||
|
||||
void HTTPServer::start() {
|
||||
_thread.reset(new boost::thread(boost::bind(&HTTPServer::worker, this)));
|
||||
}
|
||||
|
||||
void HTTPServer::stop() {
|
||||
_done = true;
|
||||
_io_service.stop();
|
||||
|
||||
if (_thread) {
|
||||
_thread->join();
|
||||
_thread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::worker() {
|
||||
start_accept();
|
||||
|
||||
_io_service.run();
|
||||
}
|
||||
|
||||
void HTTPServer::start_accept() {
|
||||
_cur_socket.reset(new boost::asio::ip::tcp::socket(_io_service));
|
||||
_acceptor.async_accept(
|
||||
*_cur_socket.get(),
|
||||
boost::bind(
|
||||
&HTTPServer::handle_accept, this, _cur_socket.get(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void HTTPServer::handle_accept(
|
||||
boost::asio::ip::tcp::socket *socket,
|
||||
const boost::system::error_code &error) {
|
||||
if (!error) {
|
||||
handle_connect(socket);
|
||||
}
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void HTTPServer::handle_connect(boost::asio::ip::tcp::socket *socket) {
|
||||
boost::asio::streambuf buffer;
|
||||
size_t size = boost::asio::read_until(*socket, buffer, "\r\n\r\n");
|
||||
std::string request(size, 0);
|
||||
buffer.sgetn(&request[0], size);
|
||||
|
||||
size_t start = request.find("GET ") + 4;
|
||||
size_t end = request.find(" HTTP/1.1\r\n");
|
||||
size_t url_args = request.find("?");
|
||||
if (url_args != std::string::npos)
|
||||
end = std::min(end, url_args);
|
||||
std::string request_path = request.substr(start, end - start);
|
||||
|
||||
std::map<std::string, std::string>::iterator request_match =
|
||||
_request_response_pairs.find(request_path);
|
||||
|
||||
if (request_match == _request_response_pairs.end()) {
|
||||
std::string response = "HTTP/1.0 404 NOT FOUND\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"not found\r\n";
|
||||
boost::asio::write(*socket, boost::asio::buffer(response));
|
||||
} else {
|
||||
boost::asio::write(*socket, boost::asio::buffer(request_match->second));
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
socket->shutdown(tcp::socket::shutdown_both, ec);
|
||||
socket->close(ec);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace config
|
||||
} // namespace bts
|
||||
@@ -1,58 +0,0 @@
|
||||
#ifndef HTTP_TEST_SERVER_H_
|
||||
#define HTTP_TEST_SERVER_H_
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/read_until.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace config {
|
||||
namespace internal {
|
||||
|
||||
class HTTPServer {
|
||||
public:
|
||||
HTTPServer();
|
||||
unsigned int port() const;
|
||||
|
||||
void start();
|
||||
void add_response(
|
||||
const std::string &path,
|
||||
const std::string &response,
|
||||
const std::string &content);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void worker();
|
||||
void start_accept();
|
||||
void handle_accept(
|
||||
boost::asio::ip::tcp::socket *, const boost::system::error_code &error);
|
||||
void handle_connect(boost::asio::ip::tcp::socket *);
|
||||
|
||||
boost::asio::io_service _io_service;
|
||||
boost::asio::ip::tcp::acceptor _acceptor;
|
||||
boost::scoped_ptr<boost::asio::ip::tcp::socket> _cur_socket;
|
||||
std::map<std::string, std::string> _request_response_pairs;
|
||||
boost::scoped_ptr<boost::thread> _thread;
|
||||
bool _done;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace config
|
||||
} // namespace bts
|
||||
#endif
|
||||
@@ -1,890 +0,0 @@
|
||||
#include "lib/utils/TempDir.h"
|
||||
#include "lib/utils/TempFile.h"
|
||||
|
||||
#include "../ConfigurationException.h"
|
||||
#include "../ConfigurationState.h"
|
||||
#include "../config.h"
|
||||
#include "lib/config/invalid/invalid.pb.h"
|
||||
#include "lib/config/protos/testkeys.pb.h"
|
||||
#include "lib/expression/ParserExceptions.h"
|
||||
|
||||
#include "HTTPServer.h"
|
||||
#include "catch/catch.hpp"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
using namespace bts::config;
|
||||
using namespace bts::config::internal;
|
||||
namespace proto = google::protobuf;
|
||||
|
||||
using bts::config::test::Foo;
|
||||
using bts::config::test::FooGroup;
|
||||
using bts::config::test::FooList;
|
||||
|
||||
namespace bts {
|
||||
int config_ref_tests = 0;
|
||||
TEST_CASE("Configuration Reference Tests", "[config]") {
|
||||
|
||||
SECTION("basic_value") {
|
||||
initialize_for_test("TestSite {"
|
||||
" value {"
|
||||
" name: 'ref value'"
|
||||
" }"
|
||||
"}"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: @TestSite.name"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.name() == "ref value");
|
||||
}
|
||||
|
||||
SECTION("scalar_to_repeat") {
|
||||
initialize_for_test("TestSite {"
|
||||
" value {"
|
||||
" name: 'a@example.com' "
|
||||
" }"
|
||||
"}"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" email: \"before@example.com\" \n"
|
||||
" email: @TestSite.name \n"
|
||||
" email: \"after@example.com\" \n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
|
||||
REQUIRE(foo.email_size() == 3);
|
||||
REQUIRE(foo.email(0) == "before@example.com");
|
||||
REQUIRE(foo.email(1) == "a@example.com");
|
||||
REQUIRE(foo.email(2) == "after@example.com");
|
||||
}
|
||||
|
||||
SECTION("repeat_to_repeat ") {
|
||||
initialize_for_test("StringTable {"
|
||||
" value {"
|
||||
" entries: 'a@example.com' "
|
||||
" entries: 'b@example.com' "
|
||||
" }"
|
||||
"}"
|
||||
"TestSite {"
|
||||
" value {"
|
||||
" name: 'c@example.com' "
|
||||
" }"
|
||||
"}"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" email: \"before@example.com\" \n"
|
||||
" email: @StringTable.entries \n"
|
||||
" email: @TestSite.name \n"
|
||||
" email: \"after@example.com\" \n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
|
||||
REQUIRE(foo.email_size() == 5);
|
||||
REQUIRE(foo.email(0) == "before@example.com");
|
||||
REQUIRE(foo.email(1) == "a@example.com");
|
||||
REQUIRE(foo.email(2) == "b@example.com");
|
||||
REQUIRE(foo.email(3) == "c@example.com");
|
||||
REQUIRE(foo.email(4) == "after@example.com");
|
||||
}
|
||||
|
||||
SECTION("message_value") {
|
||||
initialize_for_test("Foo { \n"
|
||||
" rule: 'false'"
|
||||
" value {\n"
|
||||
" name: \"bad\"\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: \"good\"\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooGroup {"
|
||||
" value { "
|
||||
" group: @Foo.name "
|
||||
" foo: @Foo "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const FooGroup &g = get<FooGroup>();
|
||||
REQUIRE(g.group() == "good");
|
||||
REQUIRE(g.foo().name() == "good");
|
||||
}
|
||||
|
||||
SECTION("nested_message_value") {
|
||||
initialize_for_test("NamedFoo { \n"
|
||||
" value {\n"
|
||||
" key: 'good foo' "
|
||||
" foo { "
|
||||
" name: \"good\"\n"
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooGroup {"
|
||||
" value { "
|
||||
" group: @NamedFoo.key "
|
||||
" foo: @NamedFoo.foo "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const FooGroup &g = get<FooGroup>();
|
||||
REQUIRE(g.group() == "good foo");
|
||||
REQUIRE(g.foo().name() == "good");
|
||||
}
|
||||
|
||||
SECTION("macro_value") {
|
||||
initialize_for_test("macro {"
|
||||
" name: 'macro1' "
|
||||
" expression: \"'m1'\" "
|
||||
"}"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: $macro1"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.name() == "m1");
|
||||
}
|
||||
|
||||
SECTION("enum_value") {
|
||||
initialize_for_test("ConsoleColor {"
|
||||
" value {"
|
||||
" color: Blue "
|
||||
" }"
|
||||
"}"
|
||||
"ConsoleColors {"
|
||||
" value {"
|
||||
" color: @ConsoleColor.color"
|
||||
" colors: Green"
|
||||
" colors: @ConsoleColor.color"
|
||||
" colors: DarkRed"
|
||||
" colors: @ConsoleColor.color"
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const test::ConsoleColor &c1 = get<test::ConsoleColor>();
|
||||
const test::ConsoleColors &c2 = get<test::ConsoleColors>();
|
||||
REQUIRE(c2.color() == c1.color());
|
||||
REQUIRE(c2.colors_size() == 4);
|
||||
REQUIRE(c2.colors(0) == test::ConsoleColor::Green);
|
||||
REQUIRE(c2.colors(1) == c1.color());
|
||||
REQUIRE(c2.colors(2) == test::ConsoleColor::DarkRed);
|
||||
REQUIRE(c2.colors(3) == c1.color());
|
||||
}
|
||||
|
||||
SECTION("named_key") {
|
||||
initialize_for_test("Foo { \n"
|
||||
" value {\n"
|
||||
" name: @FooList.item[foo2].foo.name "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" item { "
|
||||
" key: 'foo1' "
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo2' "
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo3' "
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" }"
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
REQUIRE(get<Foo>().name() == "foo2");
|
||||
}
|
||||
|
||||
SECTION("named_key_rule") {
|
||||
initialize_for_test("Foo { \n"
|
||||
" value {\n"
|
||||
" name: @FooList.item[foo2].foo.name "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" item { "
|
||||
" key: 'foo1' "
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo2' "
|
||||
" rule: '1=0' "
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo3' "
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" }"
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
// NOTE that a false rule will still create a message with defaults, so
|
||||
// the list contains the same number of elements
|
||||
REQUIRE(get<Foo>().name() == "Blue Foo");
|
||||
REQUIRE(get<FooList>().item_size() == 3);
|
||||
}
|
||||
|
||||
SECTION("named_key2") {
|
||||
initialize_for_test("Foo { \n"
|
||||
" value: @FooList.foo[foo2] "
|
||||
"}\n"
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" id: 1 "
|
||||
" }"
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" id: 2 "
|
||||
" }"
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" id: 3 "
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
REQUIRE(get<Foo>().name() == "foo2");
|
||||
REQUIRE(get<Foo>().id() == 2);
|
||||
}
|
||||
|
||||
SECTION("named_key_syntax_error") {
|
||||
bool error = false;
|
||||
try {
|
||||
initialize_for_test(
|
||||
"Foo { \n"
|
||||
" value: @FooList[foo2] " // FooList doesn't have a key or a
|
||||
// property named foo2
|
||||
"}\n"
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" id: 1 "
|
||||
" }"
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" id: 2 "
|
||||
" }"
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" id: 3 "
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n");
|
||||
} catch (...) {
|
||||
error = true;
|
||||
}
|
||||
REQUIRE(error);
|
||||
}
|
||||
|
||||
SECTION("named_struture") {
|
||||
initialize_for_test("FooGroup { \n"
|
||||
" value {\n"
|
||||
" foo: @FooList.item[foo3].foo "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" item { "
|
||||
" key: 'foo1' "
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" id: 1 "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo2' "
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" id: 2 "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo3' "
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" id: 3 "
|
||||
" }"
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n");
|
||||
REQUIRE(get<FooGroup>().foo().name() == "foo3");
|
||||
REQUIRE(get<FooGroup>().foo().id() == 3);
|
||||
}
|
||||
|
||||
SECTION("named_structure_rule") {
|
||||
initialize_for_test("FooGroup { \n"
|
||||
" value {\n"
|
||||
" foo: @FooList.item[foo3].foo "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" item { "
|
||||
" key: 'foo1' "
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" id: 1 "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo2' "
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" id: 2 "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo3' "
|
||||
" rule: 'false'"
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" id: 3 "
|
||||
" }"
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n");
|
||||
// NOTE that a false rule will still create a message with defaults, so
|
||||
// the list contains the same number of elements
|
||||
REQUIRE(get<FooGroup>().foo().name() == "Blue Foo");
|
||||
REQUIRE(get<FooGroup>().foo().id() == 1234);
|
||||
REQUIRE(get<FooList>().item_size() == 3);
|
||||
}
|
||||
|
||||
SECTION("named_value_rule") {
|
||||
initialize_for_test(
|
||||
"FooList { \n"
|
||||
" value {\n"
|
||||
" item { "
|
||||
" key: 'foo1' "
|
||||
" foo { "
|
||||
" name: 'foo1' "
|
||||
" id: 1 "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo2' "
|
||||
" foo { "
|
||||
" name: 'foo2' "
|
||||
" id: 2 "
|
||||
" }"
|
||||
" }"
|
||||
" item { "
|
||||
" key: 'foo3' "
|
||||
" foo { "
|
||||
" name: 'foo3' "
|
||||
" id: 3 "
|
||||
" }"
|
||||
" }"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
// Adding [] to BooleanGrammer.h is very difficult because
|
||||
// boost::spirit is crap also it risks making list syntax [1,2,3]
|
||||
// ambiguous so this is a workaround syntax
|
||||
" rule: 'FooList.item.foo1.foo.id = 2' "
|
||||
" value {\n"
|
||||
" name: 'bad' "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" rule: 'FooList.item.foo2.foo.id = 2' "
|
||||
" value {\n"
|
||||
" name: 'good' "
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.name() == "good");
|
||||
}
|
||||
|
||||
SECTION("recursive_reference_good") {
|
||||
initialize_for_test("TestSite { \n"
|
||||
" value {\n"
|
||||
" name: \"good\"\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: @TestSite.name "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooGroup {"
|
||||
" value { "
|
||||
" group: @Foo.name "
|
||||
" foo: @Foo "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
REQUIRE(get<FooGroup>().group() == "good");
|
||||
REQUIRE(get<FooGroup>().foo().name() == "good");
|
||||
}
|
||||
|
||||
SECTION("recursive_reference_bad") {
|
||||
bool error = false;
|
||||
try {
|
||||
|
||||
initialize_for_test("TestSite { \n"
|
||||
" value {\n"
|
||||
" name: @Foo.name "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: @TestSite.name "
|
||||
" }\n"
|
||||
"}\n"
|
||||
"FooGroup {"
|
||||
" value { "
|
||||
" group: @Foo.name "
|
||||
" foo: @Foo "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
} catch (...) {
|
||||
error = true;
|
||||
}
|
||||
REQUIRE(error);
|
||||
}
|
||||
|
||||
SECTION("recursive_order_independent") {
|
||||
initialize_for_test("FooGroup {"
|
||||
" value { "
|
||||
" group: @Foo.name "
|
||||
" foo: @Foo "
|
||||
" }"
|
||||
"}"
|
||||
"TestSite { \n"
|
||||
" value {\n"
|
||||
" name: \"good\"\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: @TestSite.name "
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
// In this test we mighe expect @Foo.name or @Foo to be evaluated first
|
||||
// But Foo is incomplete until @TestSite.name is evaluated.
|
||||
REQUIRE(get<FooGroup>().group() == "good");
|
||||
REQUIRE(get<FooGroup>().foo().name() == "good");
|
||||
}
|
||||
|
||||
SECTION("loop_test") {
|
||||
bool error = false;
|
||||
try {
|
||||
initialize_for_test("Foo { \n"
|
||||
" value {\n"
|
||||
" name: @Foo.name "
|
||||
" }\n"
|
||||
"}\n");
|
||||
} catch (...) {
|
||||
error = true;
|
||||
}
|
||||
REQUIRE(error);
|
||||
}
|
||||
|
||||
SECTION("merge_test") {
|
||||
// Config used to go through a merge step for each file in a directory.
|
||||
// This would invalidate the memory held by the deferred value queue and
|
||||
// cause crashes and instability. This is to verify that we don't regress
|
||||
// the new behavior.
|
||||
|
||||
HTTPServer server;
|
||||
std::string port = boost::lexical_cast<std::string>(server.port());
|
||||
std::string config_url = "http://localhost:" + port + "/";
|
||||
std::string response = "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n";
|
||||
|
||||
server.add_response(
|
||||
"/spark.conf", response,
|
||||
"Foo {\n"
|
||||
" value: {\n"
|
||||
" name: \"included_value\"\n"
|
||||
" id: 4096\n"
|
||||
" }}\n"
|
||||
"TestSite {"
|
||||
" value { "
|
||||
" name: @FooGroup.group" // reference to testkeys.conf
|
||||
" }"
|
||||
"}");
|
||||
server.add_response(
|
||||
"/testkeys.conf", response,
|
||||
"FooGroup {\n"
|
||||
" value {\n"
|
||||
" group: \"http_group\"\n"
|
||||
" foo {\n"
|
||||
" name: @Foo.name " // reference to spark.conf
|
||||
" id: 80\n"
|
||||
"}}}\n");
|
||||
|
||||
server.start();
|
||||
initialize_for_test("include: \"" + config_url + "\"");
|
||||
server.stop();
|
||||
|
||||
const FooGroup &group = get<FooGroup>();
|
||||
const test::TestSite &site = get<test::TestSite>();
|
||||
|
||||
REQUIRE(group.foo().name() == "included_value");
|
||||
REQUIRE(site.name() == "http_group");
|
||||
}
|
||||
|
||||
SECTION("test_conversion") {
|
||||
initialize_for_test("Foo {\n"
|
||||
" value: {\n"
|
||||
" name: @RuleRef.a_int \n"
|
||||
" id: @RuleRef.a_string "
|
||||
" }}\n"
|
||||
"Foo {\n"
|
||||
" rule: 'false' "
|
||||
" value: {\n"
|
||||
" name: @RuleRef.a_string \n"
|
||||
" id: @RuleRef.a_int "
|
||||
" }}\n"
|
||||
"RuleRef { "
|
||||
" value { "
|
||||
" a_string: '42' "
|
||||
" a_int: 99 "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.name() == "99");
|
||||
REQUIRE(foo.id() == 42);
|
||||
}
|
||||
|
||||
SECTION("test_int") {
|
||||
initialize_for_test("Foo {\n"
|
||||
" value: {\n"
|
||||
" id: @RuleRef.a_int "
|
||||
" }}\n"
|
||||
"RuleRef { "
|
||||
" value { "
|
||||
" a_string: 'good' "
|
||||
" a_int: 99 "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.id() == 99);
|
||||
}
|
||||
|
||||
SECTION("test_repeated_to_repeated") {
|
||||
initialize_for_test("FooList {\n"
|
||||
" value: {\n"
|
||||
" foo {\n"
|
||||
" name: 'good1' \n"
|
||||
" }\n"
|
||||
" foo {\n"
|
||||
" name: 'good2' \n"
|
||||
" }\n"
|
||||
" }}\n"
|
||||
"RuleRef { "
|
||||
" value { "
|
||||
" foox { "
|
||||
" name: 'original' "
|
||||
" }"
|
||||
" foox: @FooList.foo "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const test::RuleRef &result = get<test::RuleRef>();
|
||||
|
||||
REQUIRE(result.foox_size() == 3);
|
||||
REQUIRE(result.foox(0).name() == "original");
|
||||
REQUIRE(result.foox(1).name() == "good1");
|
||||
REQUIRE(result.foox(2).name() == "good2");
|
||||
}
|
||||
|
||||
SECTION("test_repeated_after_key_rule") {
|
||||
initialize_for_test("StringTable { \n"
|
||||
" value { \n"
|
||||
" entries: 'a' \n"
|
||||
" entries: 'b' \n"
|
||||
" } \n"
|
||||
"} \n"
|
||||
"FooList {\n"
|
||||
" value: {\n"
|
||||
" item {\n"
|
||||
" key: 'one' \n"
|
||||
" rule: \" 'a' in StringTable.entries\" \n"
|
||||
" foox { \n"
|
||||
" name: 'good1' \n"
|
||||
" }\n"
|
||||
" foox { \n"
|
||||
" name: 'good2' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" item {\n"
|
||||
" key: 'two' \n"
|
||||
" foox { \n"
|
||||
" name: 'bad1' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }}\n"
|
||||
"RuleRef { "
|
||||
" value { "
|
||||
" foox { "
|
||||
" name: 'original' "
|
||||
" }"
|
||||
" foox: @FooList.item[one].foox "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const test::RuleRef &result = get<test::RuleRef>();
|
||||
REQUIRE(result.foox_size() == 3);
|
||||
REQUIRE(result.foox(0).name() == "original");
|
||||
REQUIRE(result.foox(1).name() == "good1");
|
||||
REQUIRE(result.foox(2).name() == "good2");
|
||||
}
|
||||
|
||||
SECTION("test_repeated_after_key_rule2") {
|
||||
// This test fails on purpose to illustrate the frailty of the rule
|
||||
// evaluation feature in 'keyed' list keys
|
||||
initialize_for_test("TestSite {"
|
||||
" value {"
|
||||
" name: 'a' "
|
||||
" }"
|
||||
"}"
|
||||
"StringTable {"
|
||||
" value {"
|
||||
" entries: @TestSite.name "
|
||||
" entries: 'b' "
|
||||
" }"
|
||||
"}"
|
||||
"FooList {\n"
|
||||
" value: {\n"
|
||||
" item {\n"
|
||||
" key: 'one' \n"
|
||||
" rule: \"'a' in StringTable.entries\" \n"
|
||||
" foox { \n"
|
||||
" name: 'good1' \n"
|
||||
" }\n"
|
||||
" foox { \n"
|
||||
" name: 'good2' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" item {\n"
|
||||
" key: 'two' \n"
|
||||
" foox { \n"
|
||||
" name: 'bad1' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }}\n"
|
||||
"RuleRef { "
|
||||
" value { "
|
||||
" foox { "
|
||||
" name: 'original' "
|
||||
" }"
|
||||
" foox: @FooList.item[one].foox "
|
||||
" }"
|
||||
"}");
|
||||
|
||||
const test::RuleRef &result = get<test::RuleRef>();
|
||||
REQUIRE(result.foox_size() == 3);
|
||||
REQUIRE(result.foox(0).name() == "original");
|
||||
REQUIRE(result.foox(1).name() == "good1");
|
||||
REQUIRE(result.foox(2).name() == "good2");
|
||||
}
|
||||
|
||||
SECTION("test_repeated_after_key_rule3") {
|
||||
// This test fails on purpose to illustrate the frailty of the rule
|
||||
// evaluation feature in 'keyed' list keys
|
||||
initialize_for_test("RuleRef { "
|
||||
" value { "
|
||||
" foox { "
|
||||
" name: 'original' "
|
||||
" }"
|
||||
" foox: @FooList.item[one].foox "
|
||||
" }"
|
||||
"}"
|
||||
"TestSite {"
|
||||
" value {"
|
||||
" name: 'a' "
|
||||
" }"
|
||||
"}"
|
||||
"StringTable {"
|
||||
" value {"
|
||||
" entries: @TestSite.name "
|
||||
" entries: 'b' "
|
||||
" }"
|
||||
"}"
|
||||
"FooList {\n"
|
||||
" value: {\n"
|
||||
" item {\n"
|
||||
" key: 'one' \n"
|
||||
" rule: \"'a' in StringTable.entries\" \n"
|
||||
" foox { \n"
|
||||
" name: 'good1' \n"
|
||||
" }\n"
|
||||
" foox { \n"
|
||||
" name: 'good2' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" item {\n"
|
||||
" key: 'two' \n"
|
||||
" foox { \n"
|
||||
" name: 'bad1' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }}\n"
|
||||
|
||||
);
|
||||
|
||||
const test::RuleRef &result = get<test::RuleRef>();
|
||||
// the order matters here, but seems to be deterministic
|
||||
REQUIRE(result.foox_size() == 1);
|
||||
}
|
||||
|
||||
// SECTION("test_repeated_after_key_rule_should_fail") {
|
||||
// //This test fails on purpose to illustrate the frailty of the rule
|
||||
// evaluation feature in 'keyed' list keys initialize_for_test(
|
||||
// "RuleRef { "
|
||||
// " value { "
|
||||
// " foox { "
|
||||
// " name: 'original' "
|
||||
// " }"
|
||||
// " foox: @FooList.item[one].foox "
|
||||
// " }"
|
||||
// "}"
|
||||
// "TestSite {"
|
||||
// " value {"
|
||||
// " name: 'a' "
|
||||
// " }"
|
||||
// "}"
|
||||
// "StringTable {"
|
||||
// " value {"
|
||||
// " entries: @TestSite.name "
|
||||
// " entries: 'b' "
|
||||
// " }"
|
||||
// "}"
|
||||
// "FooList {\n"
|
||||
// " value: {\n"
|
||||
// " item {\n"
|
||||
// " key: 'one' \n"
|
||||
// " rule: \"'a' in StringTable.entries\" \n"
|
||||
// " foox { \n"
|
||||
// " name: 'good1' \n"
|
||||
// " }\n"
|
||||
// " foox { \n"
|
||||
// " name: 'good2' \n"
|
||||
// " }\n"
|
||||
// " }\n"
|
||||
// " item {\n"
|
||||
// " key: 'two' \n"
|
||||
// " foox { \n"
|
||||
// " name: 'bad1' \n"
|
||||
// " }\n"
|
||||
// " }\n"
|
||||
// " }}\n"
|
||||
|
||||
// );
|
||||
|
||||
// const test::RuleRef& result = get<test::RuleRef>();
|
||||
// //this should fail
|
||||
// REQUIRE(result.foox_size() == 3);
|
||||
// }
|
||||
|
||||
SECTION("test_irrelevant_lookup") {
|
||||
// If a key references another key which is not present it should only be
|
||||
// an error if the resulting key will ever be used.
|
||||
initialize_for_test(
|
||||
"FooList {\n"
|
||||
" value: {\n"
|
||||
" item {\n"
|
||||
" key: 'one' \n"
|
||||
" foox { \n"
|
||||
" name: 'good1' \n"
|
||||
" }\n"
|
||||
" foox { \n"
|
||||
" name: 'good2' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" item {\n"
|
||||
" key: 'two' \n"
|
||||
" foox { \n"
|
||||
" name: 'bad1' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }}\n"
|
||||
"RuleRef { "
|
||||
" rule: 'false' "
|
||||
" value { "
|
||||
" foox { "
|
||||
" name: 'never used' "
|
||||
" }"
|
||||
" foox: @FooList.item[XXXXX].foox " // Invalid lookup
|
||||
" }"
|
||||
"}"
|
||||
"RuleRef { "
|
||||
" value { "
|
||||
" foox { "
|
||||
" name: 'original' "
|
||||
" }"
|
||||
" foox: @FooList.item[one].foox "
|
||||
" }"
|
||||
"}");
|
||||
const test::RuleRef &result = get<test::RuleRef>();
|
||||
REQUIRE(result.foox_size() == 3);
|
||||
}
|
||||
|
||||
SECTION("macro_definition_order_test_hard") {
|
||||
// The $is_crusible test hard version
|
||||
// $macro1 tests Value2.x with rule about $macro2 before $macro2 is
|
||||
// defined Value2.x is an @reference of a 4th location
|
||||
initialize_for_test("TestSite {"
|
||||
" value { name: 'cme_1u7_cert' }"
|
||||
"}"
|
||||
"Foo {"
|
||||
" rule: '$is_crucible'"
|
||||
" value { name: @TestSite.name }"
|
||||
"}"
|
||||
""
|
||||
"Foo {"
|
||||
" value { name: '' }"
|
||||
"}"
|
||||
""
|
||||
"macro {"
|
||||
" name: 'o2_cme_enabled'"
|
||||
" expression: \"Foo.name != ''\" "
|
||||
"}"
|
||||
""
|
||||
"macro {"
|
||||
" name: 'is_crucible'"
|
||||
" expression: 'true'"
|
||||
"}");
|
||||
|
||||
REQUIRE(get<Foo>().name() == "cme_1u7_cert");
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
} // namespace bts
|
||||
@@ -1,533 +0,0 @@
|
||||
#include "lib/utils/TempFile.h"
|
||||
|
||||
#include "../ConfigurationException.h"
|
||||
#include "../ConfigurationState.h"
|
||||
#include "../config.h"
|
||||
#include "lib/expression/ParserExceptions.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/is_unittest_environment.h"
|
||||
|
||||
#include "HTTPServer.h"
|
||||
|
||||
#include "lib/config/invalid/invalid.pb.h"
|
||||
#include "lib/config/protos/system_config.pb.h"
|
||||
#include "lib/config/protos/testkeys.pb.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#define URDL_HEADER_ONLY 1
|
||||
#define URDL_DISALE_SSL 1
|
||||
#include <catch/catch.hpp>
|
||||
#include <urdl/test/http_server.hpp>
|
||||
#include <urdl/url.hpp>
|
||||
|
||||
using namespace bts::config;
|
||||
using namespace bts::config::internal;
|
||||
namespace proto = google::protobuf;
|
||||
|
||||
using bts::config::test::Foo;
|
||||
using bts::config::test::FooGroup;
|
||||
using bts::config::test::StringTable;
|
||||
|
||||
namespace bts {
|
||||
int config_tests = 0;
|
||||
|
||||
TEST_CASE("Configuration Tests", "[config]") {
|
||||
|
||||
bts::utils::internal::force_unittest_environment();
|
||||
|
||||
SECTION("basic_get_value_test") {
|
||||
initialize_for_test("Foo { \n"
|
||||
" rule: \"true\"\n"
|
||||
" value {\n"
|
||||
" name: \"Name A\"\n"
|
||||
" id: 65\n"
|
||||
" email: \"test@example.com\"\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
|
||||
REQUIRE(foo.name() == "Name A");
|
||||
REQUIRE(foo.id() == 65);
|
||||
REQUIRE(foo.email().Get(0) == "test@example.com");
|
||||
}
|
||||
|
||||
SECTION("default_value_test") {
|
||||
initialize_for_test(""); // Only loads defaults.conf
|
||||
|
||||
// Should load a Foo from testkeys.conf
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.name() == "Blue Foo"); // from testkeys.proto
|
||||
REQUIRE(foo.id() == 1234); // from testkeys.proto
|
||||
REQUIRE(foo.email().size() > 0);
|
||||
REQUIRE(
|
||||
foo.email().Get(0) == "default@example.com"); // from testkeys.conf
|
||||
}
|
||||
|
||||
SECTION("default_include_test") {
|
||||
// test defaults.conf with an include
|
||||
}
|
||||
|
||||
SECTION("invalid_key_type_test") {
|
||||
// Load an initialize with no files - OK all values default
|
||||
|
||||
const Foo &foo = get<Foo>(); // OK
|
||||
REQUIRE(foo.name() == "Blue Foo");
|
||||
|
||||
bool error = false;
|
||||
try {
|
||||
get<config::test::InvalidMessageType>(); // BAD
|
||||
} catch (...) {
|
||||
error = true;
|
||||
}
|
||||
REQUIRE(error == true);
|
||||
}
|
||||
|
||||
SECTION("invalid_config_test2") {
|
||||
utils::TempFile config_b;
|
||||
|
||||
*config_b.out() << "Foo {\n"
|
||||
" value { \n"
|
||||
" name: \"bob\"\n"
|
||||
" id: 7319 \n"
|
||||
" }";
|
||||
|
||||
bool error = false;
|
||||
try {
|
||||
initialize_for_test("include: \"" + config_b.path() + "\"", false);
|
||||
} catch (const ConfigurationException &err) {
|
||||
std::string what = err.what();
|
||||
bts_assert(what.find("Expected identifier.") != std::string::npos);
|
||||
error = true;
|
||||
}
|
||||
REQUIRE(error == true);
|
||||
}
|
||||
|
||||
// Tests that we can load using a File:/// url
|
||||
// Tests complex value load
|
||||
SECTION("file_url_test") {
|
||||
utils::TempFile config_a;
|
||||
utils::TempFile config_b;
|
||||
|
||||
*config_a.out() << "include: \"" << config_b.path() << "\"" << std::endl
|
||||
<< "Foo { \n"
|
||||
" rule: \"false\"\n"
|
||||
" value {\n"
|
||||
" name: \"Name A\"\n"
|
||||
" id: 65\n"
|
||||
" email: \"A@bluecg.com\"\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
*config_b.out() << "Foo {\n"
|
||||
" rule: \"true\"\n"
|
||||
" value {\n"
|
||||
" name: \"Name B\"\n"
|
||||
" id: 66 "
|
||||
" email: \"A@bluecg.com\" \n"
|
||||
" }}";
|
||||
|
||||
// Get key Foo without loading any config files - this will show the
|
||||
// default value
|
||||
Foo foo = bts::config::get<Foo>();
|
||||
|
||||
REQUIRE(foo.name() == "Blue Foo");
|
||||
REQUIRE(foo.id() == 1234);
|
||||
|
||||
// Parse the config file and file:/// include
|
||||
initialize_for_test("include: \"" + config_a.path() + "\"");
|
||||
|
||||
// Get the new value of Foo - this time it will come from config_b
|
||||
foo = bts::config::get<Foo>();
|
||||
REQUIRE(foo.name() == "Name B");
|
||||
REQUIRE(foo.id() == 66);
|
||||
}
|
||||
|
||||
SECTION("http_test") {
|
||||
HTTPServer server;
|
||||
std::string port = boost::lexical_cast<std::string>(server.port());
|
||||
std::string path = "/spark.conf";
|
||||
std::string response = "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n";
|
||||
std::string content = "FooGroup {\n"
|
||||
" rule: \"true\"\n"
|
||||
" value {\n"
|
||||
" group: \"http_group\"\n"
|
||||
" foo {\n"
|
||||
" name: \"http\"\n"
|
||||
" id: 80\n"
|
||||
"}}}\n";
|
||||
|
||||
server.add_response(path, response, content);
|
||||
server.start();
|
||||
std::string config_url = "http://127.0.0.1:" + port;
|
||||
|
||||
// Initialize with a configuration which references the http url
|
||||
initialize_for_test("include: \"" + config_url + "\"");
|
||||
|
||||
server.stop();
|
||||
|
||||
const FooGroup &foogrp = get<FooGroup>();
|
||||
|
||||
REQUIRE(foogrp.group() == "http_group");
|
||||
REQUIRE(foogrp.foo().name() == "http");
|
||||
REQUIRE(foogrp.foo().id() == 80);
|
||||
REQUIRE(get<SystemConfig>().config_host() == "127.0.0.1");
|
||||
}
|
||||
|
||||
SECTION("http_folder_test") {
|
||||
HTTPServer server;
|
||||
std::string port = boost::lexical_cast<std::string>(server.port());
|
||||
std::string config_url = "http://localhost:" + port + "/";
|
||||
std::string response = "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n";
|
||||
|
||||
server.add_response(
|
||||
"/spark.conf", response, "include: \"" + config_url + "other/\"");
|
||||
server.add_response(
|
||||
"/testkeys.conf", response,
|
||||
"FooGroup {\n"
|
||||
" rule: \"true\"\n"
|
||||
" value {\n"
|
||||
" group: \"http_group\"\n"
|
||||
" foo {\n"
|
||||
" name: \"http\"\n"
|
||||
" id: 80\n"
|
||||
"}}}\n");
|
||||
|
||||
server.add_response(
|
||||
"/other/spark.conf", response,
|
||||
"Foo {\n"
|
||||
" rule: \"true\"\n"
|
||||
" value: {\n"
|
||||
" name: \"included_value\"\n"
|
||||
" id: 4096\n"
|
||||
" }}\n");
|
||||
|
||||
server.start();
|
||||
initialize_for_test("include: \"" + config_url + "\"");
|
||||
|
||||
server.stop();
|
||||
|
||||
const FooGroup &foogrp = get<FooGroup>();
|
||||
|
||||
REQUIRE(foogrp.group() == "http_group");
|
||||
REQUIRE(foogrp.foo().name() == "http");
|
||||
REQUIRE(foogrp.foo().id() == 80);
|
||||
|
||||
REQUIRE(get<Foo>().name() == "included_value");
|
||||
REQUIRE(get<Foo>().id() == 4096);
|
||||
}
|
||||
|
||||
SECTION("test_rules") {
|
||||
initialize_for_test("StringTable {\n"
|
||||
" value { \n"
|
||||
" entries: [\"foo\", \"bar\", \"baz\"] \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"RuleRef { \n"
|
||||
" # use this rule of StringTable has a cow \n"
|
||||
" rule: \"'cow' in StringTable.entries\" \n"
|
||||
" value { \n"
|
||||
" a_string: \"AAA\"\n"
|
||||
" a_int: -1\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"RuleRef { \n"
|
||||
" rule: \"'baz' in StringTable.entries\" \n"
|
||||
" value { \n"
|
||||
" a_string: \"BBB\"\n"
|
||||
" a_int: 99\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" rule: \"RuleRef.a_int == 789\"\n"
|
||||
" value {\n"
|
||||
" name: \"A\"\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" rule: \"RuleRef.a_string == 'BBB'\"\n"
|
||||
" value {\n"
|
||||
" name: \"B\"\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
// What should happen:
|
||||
// Foo A
|
||||
// eval rule
|
||||
// find RuleRef AAA
|
||||
// eval rule
|
||||
// fail - StringTable.entries does not have 'cow'
|
||||
// find RuleRef BBB
|
||||
// eval rule
|
||||
// success
|
||||
// Fail - RuleRef.a_int != 789
|
||||
// Foo B
|
||||
// eval rule
|
||||
// - same as above
|
||||
// success a_string == 'BBB'
|
||||
// return Foo B
|
||||
|
||||
const Foo &foo = get<Foo>();
|
||||
REQUIRE(foo.name() == "B");
|
||||
}
|
||||
|
||||
SECTION("test_recursive_rules") {
|
||||
initialize_for_test("StringTable {\n"
|
||||
" rule: \"Foo.name == 'foo'\"\n"
|
||||
" value { \n"
|
||||
" entries: [\"foo\", \"bar\", \"baz\"] \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"RuleRef { \n"
|
||||
" rule: \"'foo' in StringTable.entries\" \n"
|
||||
" value { \n"
|
||||
" a_string: \"AAA\"\n"
|
||||
" a_int: -1\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" rule: \"RuleRef.a_string == 'BBB'\"\n"
|
||||
" value {\n"
|
||||
" name: \"foo\"\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
// Foo cannot be queried because RuleRef's rule requests StringTable which
|
||||
// requests RuleRef
|
||||
bool error = false;
|
||||
try {
|
||||
get<Foo>();
|
||||
} catch (const expression::RecursivePropertyGetException &) {
|
||||
error = true;
|
||||
}
|
||||
REQUIRE(error);
|
||||
}
|
||||
|
||||
SECTION("test_nested_properties") {
|
||||
initialize_for_test("RuleRef { \n"
|
||||
" value { \n"
|
||||
" nested {\n"
|
||||
" list_int: [7,8,9] \n"
|
||||
" nested {\n"
|
||||
" a_string: 'ABC' \n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"Foo { \n"
|
||||
" rule: \"RuleRef.nested.nested.a_string == 'ABC' "
|
||||
"and 9 in RuleRef.nested.list_int\"\n"
|
||||
" value {\n"
|
||||
" name: \"foo\"\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
REQUIRE(get<Foo>().name() == "foo");
|
||||
}
|
||||
|
||||
SECTION("built_in_properties_test") {
|
||||
initialize_for_test("Foo { \n"
|
||||
" rule: \"SystemConfig.unittest_environment == true \"\n"
|
||||
" value {\n"
|
||||
" name: \"unit tests\"\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
REQUIRE(get<Foo>().name() == "unit tests");
|
||||
}
|
||||
|
||||
SECTION("nonbreaking_space_test") {
|
||||
bool parse_error = false;
|
||||
try {
|
||||
initialize_for_test(
|
||||
"Foo { "
|
||||
" value {\xA0\xA0 "
|
||||
" name: \"valid name\""
|
||||
" \xA0 }"
|
||||
"} ",
|
||||
false);
|
||||
} catch (const bts::config::ConfigurationException &) {
|
||||
parse_error = true;
|
||||
}
|
||||
REQUIRE(parse_error);
|
||||
}
|
||||
|
||||
SECTION("macro_bool_test") {
|
||||
initialize_for_test("macro"
|
||||
"{"
|
||||
" name: \"bool\""
|
||||
" expression: \"'c' in ['a', 'b', 'c', 'd']\""
|
||||
"}"
|
||||
""
|
||||
"Foo"
|
||||
"{"
|
||||
" rule: \"$bool\""
|
||||
" value {"
|
||||
" name: 'good'"
|
||||
" }"
|
||||
"}"
|
||||
""
|
||||
"Foo"
|
||||
"{"
|
||||
" value {"
|
||||
" name: 'bad'"
|
||||
" }"
|
||||
"}");
|
||||
|
||||
REQUIRE(config::get<Foo>().name() == "good");
|
||||
}
|
||||
|
||||
SECTION("macro_list_test") {
|
||||
initialize_for_test("macro"
|
||||
"{"
|
||||
" name: \"list\""
|
||||
" expression: \"['a', 'b', 'c', 'd']\""
|
||||
"}"
|
||||
""
|
||||
"Foo"
|
||||
"{"
|
||||
" rule: \"'a' in $list\""
|
||||
" value {"
|
||||
" name: 'good'"
|
||||
" }"
|
||||
"}"
|
||||
""
|
||||
"Foo"
|
||||
"{"
|
||||
" value {"
|
||||
" name: 'bad'"
|
||||
" }"
|
||||
"}");
|
||||
|
||||
REQUIRE(config::get<Foo>().name() == "good");
|
||||
}
|
||||
|
||||
SECTION("fallback_test") {
|
||||
// HTTPServer main;
|
||||
HTTPServer fallback;
|
||||
// std::string main_url = "http://localhost:" +
|
||||
// boost::lexical_cast<std::string>(main.port()) + "/";
|
||||
std::string main_url = "http://does_not_exist:12345/";
|
||||
// main.stop();
|
||||
std::string fallback_url =
|
||||
"http://127.0.0.1:" +
|
||||
boost::lexical_cast<std::string>(fallback.port()) + "/";
|
||||
std::string response = "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n";
|
||||
|
||||
std::string content = "FooGroup {\n"
|
||||
" value {\n"
|
||||
" group: \"http_group\"\n"
|
||||
" foo {\n"
|
||||
" name: \"http\"\n"
|
||||
" id: 80\n"
|
||||
"}}}\n";
|
||||
|
||||
fallback.add_response("/spark.conf", response, content);
|
||||
|
||||
fallback.start();
|
||||
initialize_for_test(
|
||||
"include: '" + main_url +
|
||||
"'\n"
|
||||
"fallback: '" +
|
||||
fallback_url + "'\n",
|
||||
false);
|
||||
fallback.stop();
|
||||
|
||||
REQUIRE(get<FooGroup>().foo().name() == "http");
|
||||
REQUIRE(get<FooGroup>().foo().id() == 80);
|
||||
}
|
||||
|
||||
SECTION("macro_definition_order_test") {
|
||||
// The $is_crusible test
|
||||
// o2_cme_enabled depends on Foo which has a rule which depends on
|
||||
// is_crucible. Eager evaluation caused a bug which would cause loading
|
||||
// o2_cme_enabled to set is_crucible false. Case 3993
|
||||
initialize_for_test("Foo {"
|
||||
" rule: '$is_crucible'"
|
||||
" value { name: 'cme_1u7_cert' }"
|
||||
"}"
|
||||
""
|
||||
"Foo {"
|
||||
" value { name: '' }"
|
||||
"}"
|
||||
""
|
||||
"macro {"
|
||||
" name: 'o2_cme_enabled'"
|
||||
" expression: \"Foo.name != ''\" "
|
||||
"}"
|
||||
""
|
||||
"macro {"
|
||||
" name: 'is_crucible'"
|
||||
" expression: 'true'"
|
||||
"}");
|
||||
|
||||
REQUIRE(get<Foo>().name() == "cme_1u7_cert");
|
||||
}
|
||||
|
||||
SECTION("repeated_value_order_preserved") {
|
||||
initialize_for_test("Foo {"
|
||||
" value { name: 'edge-eharding' id: 80 }"
|
||||
"}"
|
||||
"StringTable {"
|
||||
" value {"
|
||||
" entries: 'http://'"
|
||||
" entries: @Foo.name"
|
||||
" entries: ':'"
|
||||
" entries: @Foo.id"
|
||||
" entries: '/risk'"
|
||||
" }"
|
||||
"}");
|
||||
std::string result;
|
||||
for (const auto &part : get<StringTable>().entries()) {
|
||||
result += part;
|
||||
}
|
||||
REQUIRE(result == "http://edge-eharding:80/risk");
|
||||
}
|
||||
|
||||
SECTION("string_concat_test") {
|
||||
initialize_for_test("Ports {\n"
|
||||
" value { \n"
|
||||
" config_port: 80 \n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"RuleRef { \n"
|
||||
" value { \n"
|
||||
" a_string: \"localhost\"\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"macro{\n"
|
||||
" name: 'list'\n"
|
||||
" expression: \"['http://', RuleRef.a_string, ':', "
|
||||
"Ports.config_port, '/risk']\"\n"
|
||||
"}"
|
||||
"StringTable {\n"
|
||||
"value {\n"
|
||||
" entries: 'http://'\n"
|
||||
" entries: @RuleRef.a_string\n"
|
||||
" entries: ':'\n"
|
||||
" entries: @Ports.config_port\n"
|
||||
" entries: '/risk'\n"
|
||||
"}}\n"
|
||||
"Foo { \n"
|
||||
" value {\n"
|
||||
" name: $list\n"
|
||||
" url: @StringTable.entries \n"
|
||||
" }\n"
|
||||
"}");
|
||||
|
||||
auto foo = config::get<Foo>();
|
||||
auto expected = "http://localhost:80/risk";
|
||||
REQUIRE(foo.name() == expected);
|
||||
REQUIRE(foo.url() == expected);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
} // namespace bts
|
||||
@@ -1,10 +1,2 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
using AllSameType =
|
||||
std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>>;
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/AllSameType.h"
|
||||
+1
-38
@@ -1,39 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class BoolName {
|
||||
public:
|
||||
explicit constexpr BoolName(bool value);
|
||||
|
||||
[[nodiscard]] static constexpr const char *true_false(bool value);
|
||||
[[nodiscard]] static constexpr const char *yes_no(bool value);
|
||||
[[nodiscard]] static constexpr const char *y_n(bool value);
|
||||
[[nodiscard]] static constexpr const char *on_off(bool value);
|
||||
|
||||
private:
|
||||
bool value_;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
constexpr const char *BoolName::true_false(bool value) {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
constexpr const char *BoolName::yes_no(bool value) {
|
||||
return value ? "yes" : "no";
|
||||
}
|
||||
|
||||
constexpr const char *BoolName::y_n(bool value) {
|
||||
// NOLINTNEXTLINE
|
||||
return value ? "y" : "n";
|
||||
}
|
||||
|
||||
constexpr const char *BoolName::on_off(bool value) {
|
||||
return value ? "on" : "off";
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/BoolName.h"
|
||||
+1
-153
@@ -1,154 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#define ENUM_BASIC_MEMBERS(type) \
|
||||
Value value; \
|
||||
\
|
||||
type() = default; \
|
||||
/*NOLINTNEXTLINE*/ \
|
||||
constexpr type(Value value) noexcept : value(value) {} \
|
||||
\
|
||||
type(const type &) noexcept = default; \
|
||||
type(type &&) noexcept = default; \
|
||||
type &operator=(const type &) noexcept = default; \
|
||||
type &operator=(type &&) noexcept = default; \
|
||||
\
|
||||
~type() = default; \
|
||||
\
|
||||
static constexpr bool parse(const char *name, type *value) noexcept; \
|
||||
static bool parse(const std::string &name, type *value) noexcept; \
|
||||
\
|
||||
[[nodiscard]] constexpr const char *name() const noexcept;
|
||||
|
||||
#define ENUM_VALUE_TO_NAME_CASE(type, v) \
|
||||
case type::v: \
|
||||
return #v;
|
||||
|
||||
#define ENUM_NAME_TO_VALUE_CLAUSE(type, v) \
|
||||
if (name == #v) { \
|
||||
*value = type::v; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define ENUM_NAMEC_TO_VALUE_CLAUSE(type, v) \
|
||||
if (std::string_view(name) == #v) { \
|
||||
*value = type::v; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define ENUM_NAME_DEF(type, for_all) \
|
||||
constexpr const char *type::name() const noexcept { \
|
||||
switch (value) { \
|
||||
for_all(ENUM_VALUE_TO_NAME_CASE) \
|
||||
default: return "ENUM_UNKNOWN"; \
|
||||
} \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
#define ENUM_PARSE_DEF(type, for_all) \
|
||||
constexpr bool type::parse(const char *name, type *value) noexcept { \
|
||||
for_all(ENUM_NAMEC_TO_VALUE_CLAUSE) return false; \
|
||||
} \
|
||||
inline bool type::parse(const std::string &name, type *value) noexcept { \
|
||||
for_all(ENUM_NAME_TO_VALUE_CLAUSE) return false; \
|
||||
}
|
||||
|
||||
#define ENUM_EQUALITY_OPS(type) \
|
||||
constexpr bool operator==(type lhs, type rhs) noexcept { \
|
||||
return lhs.value == rhs.value; \
|
||||
} \
|
||||
constexpr bool operator!=(type lhs, type rhs) noexcept { \
|
||||
return lhs.value != rhs.value; \
|
||||
} \
|
||||
constexpr bool operator==(type lhs, type::Value rhs) noexcept { \
|
||||
return lhs.value == rhs; \
|
||||
} \
|
||||
constexpr bool operator!=(type lhs, type::Value rhs) noexcept { \
|
||||
return lhs.value != rhs; \
|
||||
}
|
||||
|
||||
#define ENUM_OUTPUT_OPS(type) \
|
||||
inline std::ostream &operator<<(std::ostream &out, type value) noexcept { \
|
||||
out << value.name(); \
|
||||
return out; \
|
||||
} \
|
||||
\
|
||||
inline std::ostream &operator<<( \
|
||||
std::ostream &out, type::Value value) noexcept { \
|
||||
out << type{value}.name(); \
|
||||
return out; \
|
||||
}
|
||||
|
||||
#define ENUM_FROM_JSON(type) \
|
||||
inline void from_json(const nlohmann::json &j, type &value) { \
|
||||
std::string value_str; \
|
||||
j.get_to(value_str); \
|
||||
type::parse(value_str, &value); \
|
||||
}
|
||||
|
||||
#define ENUM_TO_JSON(type) \
|
||||
inline void to_json(nlohmann::json &j, const type &value) { \
|
||||
j = value.name(); \
|
||||
}
|
||||
|
||||
#define ENUM_DECLARE_VALUE(t, x) x,
|
||||
|
||||
#define ENUM_CLASS_DEF(type, for_all) \
|
||||
class type { \
|
||||
public: \
|
||||
enum Value { for_all(ENUM_DECLARE_VALUE) }; \
|
||||
\
|
||||
ENUM_BASIC_MEMBERS(type) \
|
||||
}; \
|
||||
\
|
||||
ENUM_OUTPUT_OPS(type) \
|
||||
ENUM_NAME_DEF(type, for_all) \
|
||||
ENUM_PARSE_DEF(type, for_all) \
|
||||
ENUM_FROM_JSON(type) \
|
||||
ENUM_TO_JSON(type) \
|
||||
ENUM_EQUALITY_OPS(type)
|
||||
|
||||
#define ENUM_BITWISE_COMPARISON_OP(enum_type, numeric_type, op) \
|
||||
constexpr enum_type operator op(enum_type lhs, enum_type rhs) noexcept { \
|
||||
return static_cast<enum_type>(static_cast<numeric_type>(lhs) \
|
||||
op static_cast<numeric_type>(rhs)); \
|
||||
}
|
||||
|
||||
#define ENUM_BITWISE_COMPARISON_OPS(enum_type, numeric_type) \
|
||||
ENUM_BITWISE_COMPARISON_OP(enum_type, numeric_type, |) \
|
||||
ENUM_BITWISE_COMPARISON_OP(enum_type, numeric_type, &) \
|
||||
ENUM_BITWISE_COMPARISON_OP(enum_type, numeric_type, ^)
|
||||
|
||||
#define ENUM_BITWISE_ASSIGNMENT_OP(enum_type, op) \
|
||||
inline enum_type &operator op##=(enum_type &lhs, enum_type rhs) noexcept { \
|
||||
lhs = lhs op rhs; \
|
||||
return lhs; \
|
||||
}
|
||||
|
||||
#define ENUM_BITWISE_ASSIGNMENT_OPS(enum_type) \
|
||||
ENUM_BITWISE_ASSIGNMENT_OP(enum_type, |) \
|
||||
ENUM_BITWISE_ASSIGNMENT_OP(enum_type, &) \
|
||||
ENUM_BITWISE_ASSIGNMENT_OP(enum_type, ^)
|
||||
|
||||
#define ENUM_BITWISE_OPS(enum_type, numeric_type) \
|
||||
ENUM_BITWISE_COMPARISON_OPS(enum_type, numeric_type) \
|
||||
ENUM_BITWISE_ASSIGNMENT_OPS(enum_type)
|
||||
|
||||
#define ENUM_HAS_FLAG(enum_value, flag) (enum_value & flag) == flag
|
||||
#define ENUM_STRIP_FLAG(enum_value, flag) (enum_value | flag) ^ flag
|
||||
|
||||
#define PB_ENUM_FROM_JSON(enum_type) \
|
||||
inline void from_json(const nlohmann::json &j, enum_type &value) { \
|
||||
io::JsonReader jr{&j}; \
|
||||
std::string name; \
|
||||
bool success = jr.read(name) && enum_type##_Parse(name, &value); \
|
||||
if (!success) { \
|
||||
const auto j_str = j.dump(2); \
|
||||
LOG(Gen, Diag, "Failed to parse json to " #enum_type ":\n{}", j_str); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PB_ENUM_TO_JSON(enum_type) \
|
||||
inline void to_json(nlohmann::json &j, const enum_type &value) { \
|
||||
j = enum_type##_Name(value); \
|
||||
}
|
||||
#include "core/EnumMacros.h"
|
||||
+1
-4
@@ -1,5 +1,2 @@
|
||||
#pragma once
|
||||
#include <fmt/format.h>
|
||||
|
||||
#define FMT(template_str, ...) fmt::format(template_str, __VA_ARGS__)
|
||||
#define FMT_JOIN(range, delimiter) fmt::to_string(fmt::join(range, delimiter))
|
||||
#include "core/FMT.h"
|
||||
+1
-32
@@ -1,33 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#define FMT_IMPL(Type, var_name, ...) \
|
||||
template <> \
|
||||
struct fmt::formatter<Type> { \
|
||||
constexpr auto parse(fmt::format_parse_context &ctx) \
|
||||
-> decltype(ctx.end()) { \
|
||||
return ctx.end(); \
|
||||
} \
|
||||
\
|
||||
template <typename FormatContext> \
|
||||
auto format(const Type &var_name, FormatContext &ctx) const \
|
||||
-> decltype(ctx.out()) { \
|
||||
return fmt::format_to(ctx.out(), __VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FMT_IMPL_OSTREAM(T) \
|
||||
template <> \
|
||||
struct fmt::formatter<T> { \
|
||||
constexpr auto parse(fmt::format_parse_context &ctx) \
|
||||
-> decltype(ctx.end()) { \
|
||||
return ctx.end(); \
|
||||
} \
|
||||
\
|
||||
template <typename FormatContext> \
|
||||
auto format(const T &t, FormatContext &ctx) const \
|
||||
-> decltype(ctx.out()) { \
|
||||
std::stringstream ss; \
|
||||
ss << t; \
|
||||
return fmt::format_to(ctx.out(), "{}", ss.str()); \
|
||||
} \
|
||||
}
|
||||
#include "core/FMT_IMPL.h"
|
||||
+1
-223
@@ -1,224 +1,2 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
#ifndef __CHAR_BIT__
|
||||
#define __CHAR_BIT__ 8
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Provides facilities to combine multiple hash codes into a single hash code.
|
||||
*
|
||||
* @remarks Employs techniques, such as modular arithmetic and bitwise shifts,
|
||||
* to minimize and evenly distribute information loss to the [multiple] input
|
||||
* hash codes in the [singular] output hash code.
|
||||
*/
|
||||
class HashPhaser {
|
||||
public:
|
||||
/**
|
||||
* Combines the hash codes of several parameters into a single hash code.
|
||||
*
|
||||
* @param value One of the parameters whose hash to store
|
||||
* @param next The other parameters whose hash to store
|
||||
*
|
||||
* @remarks A well-chosen modulus of each parameter's hash is stored in
|
||||
* equally sized blocks of bits.
|
||||
*/
|
||||
template <class T, class... NextT>
|
||||
static std::size_t auto_phase(T value, NextT... next);
|
||||
|
||||
/**
|
||||
* Combines the hash codes of a limited number of elements into a single hash
|
||||
* code.
|
||||
*
|
||||
* @param first Iterator pointing to the first element whose hash to store
|
||||
* @param last Iterator pointing just after the last element whose hash to
|
||||
* store
|
||||
*
|
||||
* @remarks A well-chosen modulus of each value's hash is stored in equally
|
||||
* sized blocks of bits
|
||||
*/
|
||||
template <class Iter>
|
||||
static std::size_t auto_phase_range(Iter first, Iter last);
|
||||
|
||||
/**
|
||||
* Combines the hash codes of a limited number of elements into a single hash
|
||||
* code.
|
||||
*
|
||||
* @param first Iterator pointing to the first element whose hash to store
|
||||
* @param last Iterator pointing just after the last element whose hash to
|
||||
* store
|
||||
* @param hash The hash op to use
|
||||
*
|
||||
* @overload
|
||||
*/
|
||||
template <class Iter, class Hash>
|
||||
static std::size_t auto_phase_range(Iter first, Iter last, Hash hash);
|
||||
|
||||
/**
|
||||
* Combines the hash codes of several parameters into a single composite hash
|
||||
* code.
|
||||
*
|
||||
* @param value One of the parameters whose hash to store
|
||||
* @param hash_op The hash code operator to use for the 'value' parameter
|
||||
* @param next The other parameters whose hash to store
|
||||
*
|
||||
* @remarks A well-chosen modulus of each parameter's hash is stored in
|
||||
* equally sized blocks of bits.
|
||||
*/
|
||||
template <class T, class HashOp, class... NextT>
|
||||
static std::size_t custom_phase(T value, HashOp hash_op, NextT... next);
|
||||
|
||||
private:
|
||||
template <class T, class... NextT>
|
||||
static void _auto_phase(
|
||||
std::size_t &hash, std::uint16_t &depth, T value, NextT... next);
|
||||
|
||||
template <class T>
|
||||
static void _auto_phase(std::size_t &hash, std::uint16_t &depth, T value);
|
||||
|
||||
template <class T, class HashOp, class... NextT>
|
||||
static void _custom_phase(
|
||||
std::size_t &hash, std::uint16_t &depth, T value, HashOp, NextT... next);
|
||||
|
||||
template <class T, class HashOp>
|
||||
static void _custom_phase(
|
||||
std::size_t &hash, std::uint16_t &depth, T value, HashOp);
|
||||
|
||||
/**
|
||||
* Get the result of std::hash<T>()(value) without having to declare the
|
||||
* template parameter T explicitly (helpful when using 'auto' variables).
|
||||
*/
|
||||
template <class T>
|
||||
static std::size_t _default_hash(T value);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
template <class T, class HashOp, class... NextT>
|
||||
std::size_t HashPhaser::custom_phase(T value, HashOp hash_op, NextT... next) {
|
||||
std::size_t res = 0;
|
||||
std::uint16_t depth = 0;
|
||||
HashPhaser::_custom_phase(res, depth, value, hash_op, next...);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T, class... NextT>
|
||||
std::size_t HashPhaser::auto_phase(T value, NextT... next) {
|
||||
std::size_t res = 0;
|
||||
std::uint16_t depth = 0;
|
||||
HashPhaser::_auto_phase(res, depth, value, next...);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline std::size_t HashPhaser::_default_hash(T value) {
|
||||
return std::hash<T>()(value);
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
std::size_t HashPhaser::auto_phase_range(Iter first, Iter last) {
|
||||
if (first == last) {
|
||||
return 0;
|
||||
}
|
||||
const std::uint16_t max_els = 8;
|
||||
auto num_vals = std::distance(first, last);
|
||||
std::uint16_t el_count =
|
||||
num_vals > (std::numeric_limits<std::uint16_t>::max)()
|
||||
? max_els
|
||||
: std::max(static_cast<std::uint16_t>(num_vals), max_els);
|
||||
auto bits_per_hash = (sizeof(std::size_t) * __CHAR_BIT__) / el_count;
|
||||
std::size_t phase = 1U << (bits_per_hash - 1);
|
||||
std::size_t composite_hash = 0;
|
||||
auto el_rem = el_count;
|
||||
while (first != last) {
|
||||
std::size_t val_hash = _default_hash(*first);
|
||||
composite_hash |= (val_hash % phase) << ((el_count - 1) * bits_per_hash);
|
||||
if (--el_rem == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return composite_hash;
|
||||
}
|
||||
|
||||
template <class Iter, class Hash>
|
||||
std::size_t HashPhaser::auto_phase_range(Iter first, Iter last, Hash hash) {
|
||||
if (first == last) {
|
||||
return 0;
|
||||
}
|
||||
const std::uint16_t max_els = 8;
|
||||
auto num_vals = std::distance(first, last);
|
||||
std::uint16_t el_count =
|
||||
num_vals > (std::numeric_limits<std::uint16_t>::max)()
|
||||
? max_els
|
||||
: std::max(static_cast<std::uint16_t>(num_vals), max_els);
|
||||
auto bits_per_hash = (sizeof(std::size_t) * __CHAR_BIT__) / el_count;
|
||||
std::size_t phase = 1U << (bits_per_hash - 1);
|
||||
std::size_t composite_hash = 0;
|
||||
auto el_rem = el_count;
|
||||
while (first != last) {
|
||||
std::size_t val_hash = hash(*first);
|
||||
composite_hash |= (val_hash % phase) << ((el_count - 1) * bits_per_hash);
|
||||
if (--el_rem == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return composite_hash;
|
||||
}
|
||||
|
||||
template <class T, class... NextT>
|
||||
void HashPhaser::_auto_phase(
|
||||
std::size_t &composite_hash, std::uint16_t &depth, T value, NextT... next) {
|
||||
auto m_depth = depth++;
|
||||
_auto_phase(composite_hash, depth, next...);
|
||||
auto val_hash = std::hash<T>()(value);
|
||||
std::size_t bits_per_hash = (sizeof(std::size_t) * __CHAR_BIT__) / depth;
|
||||
std::size_t phase = std::size_t(1) << static_cast<std::size_t>(bits_per_hash - 1);
|
||||
composite_hash |= (val_hash % phase) << (m_depth * bits_per_hash);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void HashPhaser::_auto_phase(
|
||||
std::size_t &composite_hash, std::uint16_t &depth, T value) {
|
||||
depth++;
|
||||
std::size_t val_hash = std::hash<T>()(value);
|
||||
std::size_t bits_per_hash = (sizeof(std::size_t) * __CHAR_BIT__) / depth;
|
||||
std::size_t phase = std::size_t(1) << static_cast<std::size_t>(bits_per_hash - 1);
|
||||
composite_hash |= (val_hash % phase) << ((depth - 1) * bits_per_hash);
|
||||
}
|
||||
|
||||
template <class T, class HashOp, class... NextT>
|
||||
void HashPhaser::_custom_phase(
|
||||
std::size_t &composite_hash,
|
||||
std::uint16_t &depth,
|
||||
T value,
|
||||
HashOp hash_op,
|
||||
NextT... next) {
|
||||
auto m_depth = depth++;
|
||||
_custom_phase(composite_hash, depth, next...);
|
||||
auto val_hash = hash_op(value);
|
||||
auto bits_per_hash = (sizeof(std::size_t) * __CHAR_BIT__) / depth;
|
||||
std::size_t phase = 1U << (bits_per_hash - 1);
|
||||
composite_hash |= (val_hash % phase) << (m_depth * bits_per_hash);
|
||||
}
|
||||
|
||||
template <class T, class HashOp>
|
||||
void HashPhaser::_custom_phase(
|
||||
std::size_t &composite_hash,
|
||||
std::uint16_t &depth,
|
||||
T value,
|
||||
HashOp hash_op) {
|
||||
depth++;
|
||||
std::size_t val_hash = hash_op(value);
|
||||
auto bits_per_hash = (sizeof(std::size_t) * __CHAR_BIT__) / depth;
|
||||
std::size_t phase = 1U << (bits_per_hash - 1);
|
||||
composite_hash |= (val_hash % phase) << ((depth - 1) * bits_per_hash);
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/HashPhaser.h"
|
||||
+1
-23
@@ -1,24 +1,2 @@
|
||||
#pragma once
|
||||
#include <limits>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class NaN {
|
||||
public:
|
||||
static constexpr double value();
|
||||
static constexpr float valuef();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
constexpr double NaN::value() {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
constexpr float NaN::valuef() {
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/NaN.h"
|
||||
@@ -1,70 +1,2 @@
|
||||
#pragma once
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class StringContains {
|
||||
public:
|
||||
template <typename T, typename... Ts>
|
||||
using AllSame = std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>>;
|
||||
|
||||
template <typename... Ts>
|
||||
using AllString = AllSame<std::string, Ts...>;
|
||||
|
||||
constexpr bool contains(const std::string &str, const std::string &substr);
|
||||
constexpr bool contains(const std::string &str, const char *substr);
|
||||
constexpr bool contains(const char *str, const char *substr);
|
||||
|
||||
template <typename... Ts>
|
||||
constexpr bool contains(
|
||||
const std::string &str, const std::string &substr, Ts... substrs);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
constexpr bool StringContains::contains(
|
||||
const std::string &str, const std::string &substr) {
|
||||
return str.find(substr) != std::string::npos;
|
||||
}
|
||||
|
||||
constexpr bool StringContains::contains(
|
||||
const std::string &str, const char *substr) {
|
||||
return str.find(substr) != std::string::npos;
|
||||
}
|
||||
|
||||
constexpr bool StringContains::contains(const char *str, const char *substr) {
|
||||
if (str[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
if (substr[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; str[i] != '\0'; i++) {
|
||||
bool is_match = true;
|
||||
for (std::size_t j = 0; substr[j] < '\0'; j++) {
|
||||
if (str[i + j] != substr[j]) {
|
||||
is_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_match) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
constexpr bool StringContains::contains(
|
||||
const std::string &str, const std::string &substr, Ts... substrs) {
|
||||
return contains(str, substr) || contains(str, substrs...);
|
||||
}
|
||||
|
||||
#define STRING_CONTAINS(x, ...) (bts::StringContains(x, __VA_ARGS__))
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/StringContains.h"
|
||||
@@ -1,24 +1,2 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class StringFiller {
|
||||
public:
|
||||
static bool fill_if_empty(std::string &target, const std::string &fill);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
inline bool StringFiller::fill_if_empty(
|
||||
std::string &target, const std::string &fill) {
|
||||
if (!target.empty()) {
|
||||
return false;
|
||||
}
|
||||
target = fill;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/StringFiller.h"
|
||||
@@ -1,32 +1,2 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class StringFinder {
|
||||
public:
|
||||
static std::size_t find(std::string str, char ch, int occurrence = 1);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
inline std::size_t StringFinder::find(
|
||||
std::string str, char ch, int occurrence) {
|
||||
int occur = 0;
|
||||
|
||||
// Loop to find the Nth
|
||||
// occurrence of the character
|
||||
for (std::size_t i = 0; i < str.length(); i++) {
|
||||
if (str[i] == ch) {
|
||||
occur += 1;
|
||||
}
|
||||
if (occur == occurrence)
|
||||
return i;
|
||||
}
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/StringFinder.h"
|
||||
+1
-177
@@ -1,178 +1,2 @@
|
||||
#pragma once
|
||||
#include <fmt/format.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class StringJoiner {
|
||||
public:
|
||||
template <class Arg>
|
||||
static std::ostream &join(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg);
|
||||
|
||||
template <class Arg>
|
||||
static std::string join(const std::string &delimiter, Arg arg);
|
||||
|
||||
template <class Arg, class... Args>
|
||||
static std::ostream &join(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg, Args... args);
|
||||
|
||||
template <class Arg, class... Args>
|
||||
static std::string join(const std::string &delimiter, Arg arg, Args... args);
|
||||
|
||||
template <class Iter>
|
||||
static std::ostream &join(
|
||||
Iter first, Iter last, const std::string &delimiter, std::ostream &out);
|
||||
|
||||
template <class Iter>
|
||||
static std::string join(Iter first, Iter last, const std::string &delimiter);
|
||||
|
||||
/**
|
||||
* Converts the args to strings (using the standard output operator), then
|
||||
* joins the non-empty ones with the specified delimiter.
|
||||
*/
|
||||
template <class Arg>
|
||||
static std::string join_non_empties(const std::string &delimiter, Arg arg);
|
||||
|
||||
/**
|
||||
* Converts the args to strings (using the standard output operator), then
|
||||
* joins the non-empty ones with the specified delimiter.
|
||||
*/
|
||||
template <class Arg, class... Args>
|
||||
static std::string join_non_empties(
|
||||
const std::string &delimiter, Arg arg, Args... args);
|
||||
|
||||
/**
|
||||
* Converts the args to strings (using the standard output operator), then
|
||||
* joins the non-empty ones with the specified delimiter.
|
||||
*/
|
||||
template <class Arg>
|
||||
static std::ostream &join_non_empties(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg);
|
||||
|
||||
/**
|
||||
* Converts the args to strings (using the standard output operator), then
|
||||
* joins the non-empty ones with the specified delimiter.
|
||||
*/
|
||||
template <class Arg, class... Args>
|
||||
static std::ostream &join_non_empties(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg, Args... args);
|
||||
|
||||
private:
|
||||
template <class Arg, class... Args>
|
||||
static std::ostream &join_non_empties_(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg, Args... args);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
template <class Arg>
|
||||
std::ostream &StringJoiner::join(
|
||||
const std::string & /*delimiter*/, std::ostream &out, Arg arg) {
|
||||
// special case for single item (don't use delimiter)
|
||||
out << arg;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
std::string StringJoiner::join(const std::string & /*delimiter*/, Arg arg) {
|
||||
// special case for single item (don't use delimiter)
|
||||
std::stringstream out;
|
||||
out << arg;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
std::ostream &StringJoiner::join(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg, Args... args) {
|
||||
out << arg << delimiter;
|
||||
return join(delimiter, out, args...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
std::string StringJoiner::join(
|
||||
const std::string &delimiter, Arg arg, Args... args) {
|
||||
std::stringstream out;
|
||||
out << arg << delimiter;
|
||||
join<Args...>(delimiter, out, args...);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
std::ostream &StringJoiner::join(
|
||||
Iter it, Iter last, const std::string &delimiter, std::ostream &out) {
|
||||
if (it == last) {
|
||||
return out;
|
||||
}
|
||||
out << *it;
|
||||
it++;
|
||||
for (; it != last; it++) {
|
||||
out << delimiter << *it;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
std::string StringJoiner::join(
|
||||
Iter it, Iter last, const std::string &delimiter) {
|
||||
std::stringstream out;
|
||||
join(it, last, delimiter, out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
std::string StringJoiner::join_non_empties(
|
||||
const std::string & /*delimiter*/, Arg arg) {
|
||||
std::stringstream ss;
|
||||
ss << arg;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
std::string StringJoiner::join_non_empties(
|
||||
const std::string &delimiter, Arg arg, Args... args) {
|
||||
std::stringstream ss;
|
||||
std::ostream &out = ss;
|
||||
join_non_empties(delimiter, out, arg, args...);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
std::ostream &StringJoiner::join_non_empties(
|
||||
const std::string & /*delimiter*/, std::ostream &out, Arg arg) {
|
||||
out << arg;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
std::ostream &StringJoiner::join_non_empties(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg, Args... args) {
|
||||
std::stringstream ss;
|
||||
ss << arg;
|
||||
auto arg_str = ss.str();
|
||||
if (arg_str.empty()) {
|
||||
return join_non_empties(delimiter, out, args...);
|
||||
}
|
||||
out << arg;
|
||||
return join_non_empties_(delimiter, out, args...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
std::ostream &StringJoiner::join_non_empties_(
|
||||
const std::string &delimiter, std::ostream &out, Arg arg, Args... args) {
|
||||
std::stringstream ss;
|
||||
ss << arg;
|
||||
auto arg_str = ss.str();
|
||||
if (arg_str.empty()) {
|
||||
return join_non_empties_(delimiter, out, args...);
|
||||
}
|
||||
out << delimiter << arg_str;
|
||||
join(delimiter, out, args...);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/StringJoiner.h"
|
||||
@@ -1,172 +1,2 @@
|
||||
#pragma once
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class TableFormatter {
|
||||
public:
|
||||
template <class T>
|
||||
using Schema =
|
||||
std::vector<std::tuple<std::string, int, std::function<std::string(T)>>>;
|
||||
|
||||
template <class T, class Range>
|
||||
static std::ostream &format(
|
||||
std::ostream &out, const Range &range, const Schema<T> &schema);
|
||||
|
||||
template <class T, class TIter>
|
||||
static std::ostream &format(
|
||||
std::ostream &out, TIter first, TIter last, const Schema<T> &schema);
|
||||
|
||||
template <class T>
|
||||
static std::ostream &format_record(
|
||||
std::ostream &out,
|
||||
const T &record,
|
||||
const Schema<T> &schema,
|
||||
bool headers = false);
|
||||
|
||||
template <class TRow>
|
||||
class SchemaBuilder {
|
||||
public:
|
||||
Schema<TRow> result;
|
||||
|
||||
template <class GetCell>
|
||||
void add(std::string name, GetCell get_cell, int width = 0);
|
||||
};
|
||||
};
|
||||
|
||||
template <class TRow>
|
||||
template <class GetCell>
|
||||
inline void TableFormatter::SchemaBuilder<TRow>::add(
|
||||
std::string name, GetCell get_cell, int width) {
|
||||
std::function<std::string(TRow)> get_cell_str = [&get_cell](const TRow &r) {
|
||||
std::stringstream ss;
|
||||
ss << get_cell(r);
|
||||
return ss.str();
|
||||
};
|
||||
result.emplace_back(std::move(name), width, get_cell_str);
|
||||
}
|
||||
|
||||
template <class T, class Range>
|
||||
std::ostream &TableFormatter::format(
|
||||
std::ostream &out, const Range &range, const Schema<T> &schema) {
|
||||
return format(out, range.begin(), range.end(), schema);
|
||||
}
|
||||
|
||||
template <class T, class TIter>
|
||||
inline std::ostream &TableFormatter::format(
|
||||
std::ostream &out,
|
||||
TIter first,
|
||||
TIter last,
|
||||
const typename TableFormatter::Schema<T> &schema) {
|
||||
if (first == last || schema.size() == 0)
|
||||
return out;
|
||||
int total_width = 0;
|
||||
for (auto col : schema) {
|
||||
total_width += std::get<1>(col);
|
||||
}
|
||||
// top border of table
|
||||
for (int i = 0; i < total_width + static_cast<int>(schema.size()); i++) {
|
||||
out << "-";
|
||||
}
|
||||
out << std::endl;
|
||||
// column headers themselves
|
||||
out << "|";
|
||||
for (auto col : schema) {
|
||||
out << std::setw(std::get<1>(col)) << std::get<0>(col) << "|";
|
||||
}
|
||||
// line between column headers and rows
|
||||
out << std::endl;
|
||||
for (int i = 0; i < total_width + static_cast<int>(schema.size()); i++) {
|
||||
out << "-";
|
||||
}
|
||||
// rows
|
||||
while (first != last) {
|
||||
out << std::endl;
|
||||
out << "|";
|
||||
for (uint32_t i = 0; i < schema.size(); i++) {
|
||||
out << std::setw(std::get<1>(schema[i]))
|
||||
<< std::get<2>(schema[i])(*first) << "|";
|
||||
}
|
||||
first++;
|
||||
}
|
||||
// bottom border of table
|
||||
out << std::endl;
|
||||
for (int i = 0; i < total_width + static_cast<int>(schema.size()); i++)
|
||||
out << "-";
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline std::ostream &TableFormatter::format_record(
|
||||
std::ostream &out,
|
||||
const T &record,
|
||||
const typename TableFormatter::Schema<T> &schema,
|
||||
bool headers) {
|
||||
constexpr const char *const field_header = "Field";
|
||||
constexpr const char *const value_header = "Value";
|
||||
|
||||
int ulabel_width = headers ? strlen(field_header) : 0;
|
||||
for (const auto &field : schema) {
|
||||
auto label_width = std::get<1>(field);
|
||||
if (label_width == 0) {
|
||||
label_width = static_cast<int>(std::get<0>(field).size());
|
||||
}
|
||||
if (label_width > ulabel_width) {
|
||||
ulabel_width = label_width;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> field_vals;
|
||||
for (const auto &field : schema) {
|
||||
field_vals.push_back(std::get<2>(field)(record));
|
||||
}
|
||||
|
||||
int uvalue_width = headers ? strlen(value_header) : 0;
|
||||
for (const auto &v : field_vals) {
|
||||
auto value_width = static_cast<int>(v.size());
|
||||
if (value_width > uvalue_width) {
|
||||
uvalue_width = value_width;
|
||||
}
|
||||
}
|
||||
|
||||
int table_width = ulabel_width + uvalue_width + 4;
|
||||
if (headers) {
|
||||
// top border of table
|
||||
for (int i = 0; i < table_width + 3; i++) {
|
||||
out << "-";
|
||||
}
|
||||
out << std::endl;
|
||||
// column headers
|
||||
out << "| " << std::setw(ulabel_width) << field_header << " | "
|
||||
<< std::setw(uvalue_width) << value_header << " |\n";
|
||||
}
|
||||
|
||||
// border between col headers and rows (or top border of table, if 'headers'
|
||||
// is false)
|
||||
for (int i = 0; i < table_width + 3; i++) {
|
||||
out << "-";
|
||||
}
|
||||
|
||||
// rows
|
||||
for (std::size_t i = 0; i < field_vals.size(); i++) {
|
||||
out << "\n";
|
||||
const auto &label = std::get<0>(schema.at(i));
|
||||
const auto &val = field_vals.at(i);
|
||||
out << "| " << std::setw(ulabel_width) << label << " | "
|
||||
<< std::setw(uvalue_width) << val << " |";
|
||||
}
|
||||
out << "\n";
|
||||
|
||||
// bottom border of table
|
||||
for (int i = 0; i < table_width + 3; i++) {
|
||||
out << "-";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/TableFormatter.h"
|
||||
@@ -1,43 +0,0 @@
|
||||
#include "TimeStamp.h"
|
||||
#include "lib/utils/datetime.h"
|
||||
#include <boost/date_time/posix_time/time_formatters.hpp>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
TimeStamp::TimeStamp(uint32_t year, uint32_t month, uint32_t day) {
|
||||
bts_assert(year >= 1970);
|
||||
bts_assert(month >= 1 && month <= 12);
|
||||
bts_assert(day >= 1 && day <= 31);
|
||||
std::tm date = {};
|
||||
date.tm_year = year - 1900;
|
||||
date.tm_mon = month - 1;
|
||||
date.tm_mday = day;
|
||||
auto time = std::chrono::system_clock::from_time_t(mktime(&date));
|
||||
auto duration = time.time_since_epoch();
|
||||
value_ =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
|
||||
}
|
||||
|
||||
TimeStamp TimeStamp::from_iso_string(const std::string &iso_string) {
|
||||
auto ptime = datetime::parse_iso_date_time_string(iso_string);
|
||||
return from_ptime(ptime);
|
||||
}
|
||||
|
||||
std::string TimeStamp::to_iso_string() const {
|
||||
return boost::posix_time::to_iso_string(to_ptime());
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const TimeStamp &ts) noexcept {
|
||||
out << ts.to_iso_string();
|
||||
return out;
|
||||
}
|
||||
|
||||
TimeStamp TimeStamp::from_ptime(boost::posix_time::ptime t) {
|
||||
// time_t is Unix time: number of seconds since epoch Jan 1, 1970
|
||||
auto epoch_pt =
|
||||
t - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1));
|
||||
auto epoch_nanos = epoch_pt.total_nanoseconds();
|
||||
return TimeStamp{static_cast<std::uint64_t>(epoch_nanos)};
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
+1
-166
@@ -1,167 +1,2 @@
|
||||
#pragma once
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/datetime.h"
|
||||
#include <boost/date_time/posix_time/conversion.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_duration.hpp>
|
||||
#include <boost/date_time/posix_time/ptime.hpp>
|
||||
#include <boost/date_time/time.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
/**
|
||||
* @brief Represents time as nano-seconds since the unix epoch (Jan 1, 1970)
|
||||
*/
|
||||
struct TimeStamp {
|
||||
public:
|
||||
constexpr TimeStamp() = default;
|
||||
explicit constexpr TimeStamp(std::uint64_t value);
|
||||
// constructs a TimeStamp from a UTC date
|
||||
explicit TimeStamp(uint32_t year, uint32_t month, uint32_t day);
|
||||
|
||||
static constexpr TimeStamp from_int64(std::int64_t value);
|
||||
static constexpr TimeStamp from_nanos(std::int64_t value);
|
||||
static constexpr TimeStamp from_micros(std::int64_t value);
|
||||
static constexpr TimeStamp from_millis(std::int64_t value);
|
||||
static constexpr TimeStamp from_seconds(std::int64_t value);
|
||||
static TimeStamp from_ptime(boost::posix_time::ptime t);
|
||||
static TimeStamp from_iso_string(const std::string &iso_string);
|
||||
|
||||
constexpr std::uint64_t nanos() const;
|
||||
constexpr std::uint64_t micros() const;
|
||||
constexpr std::uint64_t millis() const;
|
||||
constexpr std::uint64_t seconds() const;
|
||||
|
||||
constexpr double total_millis() const;
|
||||
constexpr double total_seconds() const;
|
||||
|
||||
constexpr std::int64_t to_int64();
|
||||
inline void to_date(uint32_t &year, uint32_t &month, uint32_t &day);
|
||||
constexpr boost::posix_time::ptime to_ptime() const;
|
||||
std::string to_iso_string() const;
|
||||
// current epoch ns
|
||||
static uint64_t now_ns() {
|
||||
#ifndef _WIN32
|
||||
return datetime::unix_nano_now();
|
||||
#else
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
now.time_since_epoch())
|
||||
.count();
|
||||
return ns;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
// posix time
|
||||
std::uint64_t value_{};
|
||||
};
|
||||
|
||||
constexpr bool operator==(TimeStamp lhs, TimeStamp rhs) noexcept;
|
||||
constexpr bool operator!=(TimeStamp lhs, TimeStamp rhs) noexcept;
|
||||
constexpr bool operator<(TimeStamp lhs, TimeStamp rhs) noexcept;
|
||||
constexpr bool operator>(TimeStamp lhs, TimeStamp rhs) noexcept;
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const TimeStamp &ts) noexcept;
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
constexpr TimeStamp::TimeStamp(std::uint64_t value) : value_(value) {}
|
||||
|
||||
constexpr TimeStamp TimeStamp::from_int64(std::int64_t value) {
|
||||
bts_assert(value >= 0);
|
||||
return TimeStamp{static_cast<std::uint64_t>(value)};
|
||||
}
|
||||
|
||||
constexpr TimeStamp TimeStamp::from_nanos(std::int64_t value) {
|
||||
bts_assert(value >= 0);
|
||||
return TimeStamp{static_cast<std::uint64_t>(value)};
|
||||
}
|
||||
|
||||
constexpr TimeStamp TimeStamp::from_micros(std::int64_t value) {
|
||||
bts_assert(value >= 0);
|
||||
constexpr const auto nanos_per_micro = 1'000;
|
||||
return TimeStamp{static_cast<std::uint64_t>(value * nanos_per_micro)};
|
||||
}
|
||||
|
||||
constexpr TimeStamp TimeStamp::from_millis(std::int64_t value) {
|
||||
bts_assert(value >= 0);
|
||||
constexpr const auto nanos_per_milli = 1'000'000;
|
||||
return TimeStamp{static_cast<std::uint64_t>(value * nanos_per_milli)};
|
||||
}
|
||||
|
||||
constexpr TimeStamp TimeStamp::from_seconds(std::int64_t value) {
|
||||
bts_assert(value >= 0);
|
||||
constexpr const auto nanos_per_second = 1'000'000'000;
|
||||
return TimeStamp{static_cast<std::uint64_t>(value * nanos_per_second)};
|
||||
}
|
||||
|
||||
constexpr std::uint64_t TimeStamp::nanos() const { return value_; }
|
||||
|
||||
constexpr std::uint64_t TimeStamp::micros() const { return value_ / 1'000; }
|
||||
|
||||
constexpr std::uint64_t TimeStamp::millis() const { return value_ / 1'000'000; }
|
||||
|
||||
constexpr std::uint64_t TimeStamp::seconds() const {
|
||||
return value_ / 1'000'000'000;
|
||||
}
|
||||
|
||||
constexpr double TimeStamp::total_millis() const {
|
||||
return static_cast<double>(value_) / 1000'000;
|
||||
}
|
||||
|
||||
constexpr double TimeStamp::total_seconds() const {
|
||||
return static_cast<double>(value_) / 1000'000'000;
|
||||
}
|
||||
|
||||
constexpr std::int64_t TimeStamp::to_int64() {
|
||||
bts_assert(
|
||||
value_ <=
|
||||
static_cast<std::int64_t>(std::numeric_limits<std::int64_t>::max()));
|
||||
return static_cast<std::int64_t>(value_);
|
||||
}
|
||||
void TimeStamp::to_date(uint32_t &year, uint32_t &month, uint32_t &day) {
|
||||
std::uint64_t epoch_secs = value_ / (1000 * 1000 * 1000);
|
||||
time_t ts_tt(epoch_secs);
|
||||
struct tm *ptm = gmtime(&ts_tt);
|
||||
year = ptm->tm_year + 1900;
|
||||
month = ptm->tm_mon + 1;
|
||||
day = ptm->tm_mday;
|
||||
}
|
||||
|
||||
constexpr boost::posix_time::ptime TimeStamp::to_ptime() const {
|
||||
constexpr const auto nanos_per_sec = 1'000'000'000;
|
||||
constexpr const auto nanos_per_micro = 1'000;
|
||||
// time_t is Unix time: number of seconds since epoch Jan 1, 1970
|
||||
std::time_t ts = value_ / nanos_per_sec;
|
||||
const auto nanos_rem = value_ % nanos_per_sec;
|
||||
return boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1)) +
|
||||
boost::posix_time::seconds(static_cast<long>(ts)) +
|
||||
boost::posix_time::microseconds(nanos_rem / nanos_per_micro);
|
||||
}
|
||||
|
||||
constexpr bool operator==(TimeStamp lhs, TimeStamp rhs) noexcept {
|
||||
return lhs.nanos() == rhs.nanos();
|
||||
}
|
||||
|
||||
constexpr bool operator!=(TimeStamp lhs, TimeStamp rhs) noexcept {
|
||||
return lhs.nanos() != rhs.nanos();
|
||||
}
|
||||
|
||||
constexpr bool operator<(TimeStamp lhs, TimeStamp rhs) noexcept {
|
||||
return lhs.nanos() < rhs.nanos();
|
||||
}
|
||||
|
||||
constexpr bool operator>(TimeStamp lhs, TimeStamp rhs) noexcept {
|
||||
return lhs.nanos() > rhs.nanos();
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
|
||||
FMT_IMPL(bts::core::TimeStamp, t, "{}", t.to_iso_string());
|
||||
#include "core/TimeStamp.h"
|
||||
@@ -1,9 +1,2 @@
|
||||
#pragma once
|
||||
#include "lib/utils/ValueWrapper.h"
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
template <class Tag, class ValueT>
|
||||
using ValueWrapper = utils::ValueWrapper<Tag, ValueT>;
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/ValueWrapper.h"
|
||||
@@ -1,56 +1,2 @@
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::core {
|
||||
|
||||
class VectorAdapter {
|
||||
public:
|
||||
template <class InT, class OutT, class Op>
|
||||
static std::vector<OutT> to(const std::vector<InT> &source, Op op);
|
||||
|
||||
template <class T>
|
||||
static std::vector<const T *> to_const_pointers(
|
||||
const std::vector<T> &source);
|
||||
|
||||
template <class K, class V>
|
||||
static std::vector<V> copy_values(const std::unordered_map<K, V> &source);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Definition
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
template <class InT, class OutT, class Op>
|
||||
std::vector<OutT> VectorAdapter::to(const std::vector<InT> &source, Op op) {
|
||||
std::vector<OutT> result;
|
||||
result.reserve(source.size());
|
||||
for (const auto &value : source) {
|
||||
result.push_back(op(value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<const T *> VectorAdapter::to_const_pointers(
|
||||
const std::vector<T> &source) {
|
||||
std::vector<const T *> ptrs;
|
||||
ptrs.reserve(source.size());
|
||||
for (const auto &value : source) {
|
||||
ptrs.push_back(&value);
|
||||
}
|
||||
return ptrs;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
std::vector<V> VectorAdapter::copy_values(
|
||||
const std::unordered_map<K, V> &source) {
|
||||
std::vector<V> values;
|
||||
values.reserve(source.size());
|
||||
for (const auto &p : source) {
|
||||
values.push_back(p.second);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
} // namespace bts::core
|
||||
#include "core/VectorAdapter.h"
|
||||
@@ -1,181 +0,0 @@
|
||||
// #include "core/bts_core/utest/utest.h"
|
||||
|
||||
// #include "../IPropertyEvaluator.h"
|
||||
// #include "../ExpressionEvaluator.h"
|
||||
// #include "../ParserExceptions.h"
|
||||
|
||||
// #include "core/bts_core/utils/BTS_SharedPtr.h"
|
||||
// #include <utility>
|
||||
|
||||
// using namespace bts::expression;
|
||||
|
||||
// namespace bts {
|
||||
|
||||
// // Simple property evaluator for testing
|
||||
// class EvaluatesToFoo : public IPropertyEvaluator
|
||||
// {
|
||||
// public:
|
||||
// EvaluatesToFoo()
|
||||
// { }
|
||||
// ~EvaluatesToFoo()
|
||||
// { }
|
||||
// BTS_NO_COPY(EvaluatesToFoo);
|
||||
|
||||
// bool get_value(const std::string& prop, Value* out_value) OVERRIDE
|
||||
// {
|
||||
// bool valid = prop.substr(0,3) == "Foo";
|
||||
// if (valid && out_value != nullptr)
|
||||
// {
|
||||
// if (prop == "Foo.number")
|
||||
// {
|
||||
// *out_value = Value(42.0);
|
||||
// }
|
||||
// else if (prop == "Foo.list")
|
||||
// {
|
||||
// std::vector<Value> values;
|
||||
// values.push_back(Value(std::string("foo")));
|
||||
// values.push_back(Value(std::string("bar")));
|
||||
// values.push_back(Value(std::string("baz")));
|
||||
// *out_value = Value(values);
|
||||
// }
|
||||
// else if (prop == "Foo.bool")
|
||||
// {
|
||||
// *out_value = Value(true);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Foo.*
|
||||
// *out_value = Value(std::string("Foo"));
|
||||
// }
|
||||
// }
|
||||
|
||||
// return valid;
|
||||
// }
|
||||
// };
|
||||
|
||||
// class EvaluatesRecursively : public IPropertyEvaluator
|
||||
// {
|
||||
// public:
|
||||
// EvaluatesRecursively()
|
||||
// : _e(nullptr)
|
||||
// { }
|
||||
// ~EvaluatesRecursively()
|
||||
// { }
|
||||
// BTS_NO_COPY(EvaluatesRecursively);
|
||||
|
||||
// void set_evaluator(ExpressionEvaluator* e)
|
||||
// {
|
||||
// _e = e;
|
||||
// }
|
||||
|
||||
// bool get_value(const std::string& prop, Value* out_value) OVERRIDE
|
||||
// {
|
||||
// std::string recur = "42 == " + prop;
|
||||
// if (out_value)
|
||||
// {
|
||||
// *out_value = Value(_e->eval(recur));
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// ExpressionEvaluator* _e;
|
||||
// };
|
||||
|
||||
// struct expression_tests : public TestSuite
|
||||
// {
|
||||
// expression_tests() { }
|
||||
// ~expression_tests() { }
|
||||
// BTS_NO_COPY(expression_tests);
|
||||
|
||||
// void setup()
|
||||
// {
|
||||
// }
|
||||
|
||||
// void teardown()
|
||||
// {
|
||||
// }
|
||||
|
||||
// void simple_operator_test()
|
||||
// {
|
||||
// ExpressionEvaluator e(BTS_SharedPtr<IPropertyEvaluator> (new
|
||||
// EvaluatesToFoo())); test_assert(e.eval("!!true"));
|
||||
// test_assert(e.eval("3 == 3"));
|
||||
// test_assert(e.eval("4 > 3"));
|
||||
// test_assert(e.eval("3 < 6"));
|
||||
// test_assert(e.eval("3 <= 3.5"));
|
||||
// test_assert(e.eval("3 in [1,2,3]"));
|
||||
// test_assert(!e.eval("3 in [1,2,4]"));
|
||||
// test_assert(e.eval("'oo' in Foo.foo"));
|
||||
// test_assert(e.eval("'oo' == 'oo'"));
|
||||
// test_assert(e.eval("'afoobar' ~= 'foo.*'"));
|
||||
// test_assert(!e.eval("'afoobar' ~= '^foo.*'"));
|
||||
// }
|
||||
|
||||
// void test_bool_conversion()
|
||||
// {
|
||||
// ExpressionEvaluator e(BTS_SharedPtr<IPropertyEvaluator> (new
|
||||
// EvaluatesToFoo())); test_assert(e.eval("true"));
|
||||
// test_assert(e.eval("1"));
|
||||
// test_assert(e.eval("1.5"));
|
||||
// test_assert(e.eval("'true string'"));
|
||||
// //test_assert(e.eval("[1,2,3]")); // list not valid here - only
|
||||
// after "in" test_assert(e.eval("Foo.x"));
|
||||
// test_assert(e.eval("Foo.list"));
|
||||
// test_assert(!e.eval("''"));
|
||||
// }
|
||||
|
||||
// void property_validator_test()
|
||||
// {
|
||||
// ExpressionEvaluator e(BTS_SharedPtr<IPropertyEvaluator> (new
|
||||
// EvaluatesToFoo())); test_assert(e.check_valid("Foo.bar == 'Foo' or
|
||||
// 'x' in [Foo.x, Foo.y, 'z']", nullptr)); std::string bad_property;
|
||||
// test_assert(!e.check_valid("Foo.bar == 'Foo' or 'x' in [Fuu.x,
|
||||
// Foo.y, 'z']", &bad_property)); test_assert(bad_property == "Fuu.x");
|
||||
// }
|
||||
|
||||
// void more_complex_test()
|
||||
// {
|
||||
// ExpressionEvaluator e(BTS_SharedPtr<IPropertyEvaluator> (new
|
||||
// EvaluatesToFoo()));
|
||||
|
||||
// test_assert(e.eval("Foo.bar == 'Foo' and 'b' in ['a', 'b', 'c',
|
||||
// Foo.bar]")); test_assert(e.eval("Foo.number > 2 or (3.5 <= 7 and
|
||||
// Foo.number == 3) or 'x' in ['a', 'b', 'c']"));
|
||||
// test_assert(!e.eval("(3.5 <= 7 and Foo.number == 3) or 'x' in ['a',
|
||||
// 'b', 'c']")); test_assert(e.eval("'foo' in Foo.list and Foo.number
|
||||
// == 42.0")); test_assert(e.eval("!!('oo' in Foo.foo)"));
|
||||
// test_assert(e.eval("!('ooo' in Foo.foo)"));
|
||||
// test_assert(e.eval("!false in [false, false, false, false, true,
|
||||
// Foo.bool]"));
|
||||
// }
|
||||
|
||||
// void recursive_test()
|
||||
// {
|
||||
// BTS_SharedPtr<EvaluatesRecursively> badprops (new
|
||||
// EvaluatesRecursively()); ExpressionEvaluator e(badprops);
|
||||
// badprops->set_evaluator(&e);
|
||||
|
||||
// // Try to get the 'fuzzy'property recursively
|
||||
// bool success = false;
|
||||
// try
|
||||
// {
|
||||
// test_assert(e.eval("fuzzy == 42"));
|
||||
// }
|
||||
// catch(RecursivePropertyGetException)
|
||||
// {
|
||||
// success = true;
|
||||
// }
|
||||
// test_assert(success);
|
||||
// }
|
||||
|
||||
// };
|
||||
|
||||
// BEGIN_TEST_SUITE(expression_tests)
|
||||
// ADD_TEST(simple_operator_test);
|
||||
// ADD_TEST(test_bool_conversion);
|
||||
// ADD_TEST(property_validator_test);
|
||||
// ADD_TEST(more_complex_test);
|
||||
// ADD_TEST(recursive_test);
|
||||
// END_TEST_SUITE
|
||||
|
||||
// }
|
||||
@@ -1,49 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/utils/windows_wrapper.h"
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
|
||||
struct Level {
|
||||
using Rep = uint8_t;
|
||||
constexpr static auto Max = (std::numeric_limits<Rep>::max)();
|
||||
Rep ordinal;
|
||||
bool operator<(const Level &rhs) const { return ordinal < rhs.ordinal; }
|
||||
};
|
||||
|
||||
struct Subsystem {
|
||||
using Rep = uint8_t;
|
||||
constexpr static auto Max = (std::numeric_limits<Rep>::max)();
|
||||
Rep ordinal;
|
||||
bool operator<(const Subsystem &rhs) const { return ordinal < rhs.ordinal; }
|
||||
};
|
||||
|
||||
using ArgBuffer = std::array<uint8_t, 156>;
|
||||
using MsgFormatter = std::string (*)(ArgBuffer const &, const char *);
|
||||
using Filter = std::array<Level, Subsystem::Max + 1>;
|
||||
|
||||
inline uint64_t log_ts() {
|
||||
uint64_t result;
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER qpcnt;
|
||||
QueryPerformanceCounter(&qpcnt);
|
||||
result = qpcnt.QuadPart;
|
||||
#else
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
result =
|
||||
static_cast<int64_t>(1000000000UL) * static_cast<int64_t>(ts.tv_sec) +
|
||||
static_cast<int64_t>(ts.tv_nsec);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/Common.h"
|
||||
@@ -1,42 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "MsgPacking.h"
|
||||
#include "lib/utils/warn_ctrl.h"
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
|
||||
using ArgUnpacker =
|
||||
std::string (*)(ArgBuffer const &b, const char *msg_template);
|
||||
|
||||
GCC_DIAG_OFF("-Weffc++")
|
||||
struct LogRec {
|
||||
uint64_t ts;
|
||||
const char *file;
|
||||
const char *msg_template;
|
||||
ArgUnpacker unpacker;
|
||||
uint16_t line;
|
||||
Level level;
|
||||
Subsystem subsystem;
|
||||
ArgBuffer args;
|
||||
|
||||
template <typename... Args>
|
||||
void pack_msg(const char *msg_t, Args &&... msg_args) {
|
||||
msg_template = msg_t;
|
||||
|
||||
unpacker = unpack_msg_args<
|
||||
sizeof(ArgBuffer), typename std::remove_reference<Args>::type...>;
|
||||
pack_msg_args(args, std::forward<Args>(msg_args)...);
|
||||
}
|
||||
|
||||
std::string unpack_msg() { return unpacker(args, msg_template); }
|
||||
};
|
||||
GCC_DIAG_RESTORE()
|
||||
|
||||
#ifndef _WIN32
|
||||
// static_assert(sizeof(LogRec) == 256, "LogRec should be 128 bytes");
|
||||
#endif
|
||||
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/LogRec.h"
|
||||
+1
-173
@@ -1,174 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "LogRec.h"
|
||||
#include "MsgPacking.h"
|
||||
#include "lib/os/os.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <atomic>
|
||||
#include <numeric>
|
||||
#include <thread>
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
Logger(const Logger &) = delete;
|
||||
Logger &operator=(const Logger &) = delete;
|
||||
|
||||
Logger(
|
||||
size_t tid,
|
||||
Filter const &f,
|
||||
size_t initial_queue_size,
|
||||
size_t max_queue_growth)
|
||||
: _mutex(), _read_q(&_queue_b), _write_q(&_queue_a), _wcursor(0),
|
||||
_filter(f), _drop_count(0), _queue_a(initial_queue_size),
|
||||
_queue_b(initial_queue_size), _tid(tid), _active(true),
|
||||
_max_queue_size(initial_queue_size * max_queue_growth),
|
||||
_thread_name() {
|
||||
os::get_thread_name(&_thread_name);
|
||||
}
|
||||
|
||||
bool is_loggable(Subsystem subsystem, Level lv) {
|
||||
lock();
|
||||
bool result = lv.ordinal >= _filter[subsystem.ordinal].ordinal;
|
||||
unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool enqueue(
|
||||
uint64_t ts,
|
||||
Subsystem subsystem,
|
||||
Level lv,
|
||||
const char *file,
|
||||
int line,
|
||||
const char *msg_template,
|
||||
Args &&... msg_args) {
|
||||
assert(_active);
|
||||
lock();
|
||||
if (_wcursor == _write_q->size())
|
||||
_drop_count++;
|
||||
else {
|
||||
auto &lrec = (*_write_q)[_wcursor++];
|
||||
lrec.ts = ts;
|
||||
lrec.file = file;
|
||||
lrec.line = line;
|
||||
lrec.level = lv;
|
||||
lrec.subsystem = subsystem;
|
||||
lrec.pack_msg(msg_template, std::forward<Args>(msg_args)...);
|
||||
}
|
||||
unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t aggregate_hash() { return 0; }
|
||||
template <typename Arg1, typename... Args>
|
||||
size_t aggregate_hash(Arg1 &arg1, Args &&... args2) {
|
||||
return std::hash<Arg1>()(arg1) + aggregate_hash(args2...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool enqueue_once_maybe(
|
||||
uint64_t ts,
|
||||
Subsystem subsystem,
|
||||
Level lv,
|
||||
const char *file,
|
||||
int line,
|
||||
const char *msg_template,
|
||||
Args &&... msg_args) {
|
||||
const size_t filter_size = 1024 * 8; // 1kb
|
||||
static std::vector<bool> s_bloom(filter_size);
|
||||
auto arg_hash = aggregate_hash(msg_args...);
|
||||
size_t hash =
|
||||
(arg_hash + std::hash<const char *>()(msg_template)) % filter_size;
|
||||
if (s_bloom[hash])
|
||||
return false;
|
||||
else
|
||||
enqueue(
|
||||
ts, subsystem, lv, file, line, msg_template,
|
||||
std::forward<Args>(msg_args)...);
|
||||
s_bloom[hash] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class S> void unqueue(S const &s) {
|
||||
size_t logged_msg_count = 0;
|
||||
size_t dropped_msg_count = 0;
|
||||
bool active = true;
|
||||
|
||||
lock();
|
||||
std::swap(_read_q, _write_q);
|
||||
logged_msg_count = _wcursor;
|
||||
dropped_msg_count = _drop_count;
|
||||
active = _active;
|
||||
_wcursor = 0;
|
||||
_drop_count = 0;
|
||||
unlock();
|
||||
|
||||
s(_tid, _thread_name, active, dropped_msg_count, logged_msg_count,
|
||||
*_read_q);
|
||||
|
||||
if ((logged_msg_count > _read_q->size() / 2) &&
|
||||
_read_q->size() < _max_queue_size)
|
||||
_read_q->resize(_read_q->size() * 2);
|
||||
}
|
||||
|
||||
void update_filter(Filter const &new_filter) {
|
||||
lock();
|
||||
_filter = new_filter;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void
|
||||
update_queue_limits(size_t initial_queue_size, size_t max_queue_growth) {
|
||||
lock();
|
||||
if (_wcursor == 0) {
|
||||
// we can only do this if the queue is empty because each log
|
||||
// record contains a tuple with possibly non trivially
|
||||
// constructuable objects and cannot be moved
|
||||
_write_q->resize(initial_queue_size);
|
||||
}
|
||||
_read_q->resize(initial_queue_size);
|
||||
_max_queue_size = initial_queue_size * max_queue_growth;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void mark_inactive() {
|
||||
lock();
|
||||
assert(_active);
|
||||
_active = false;
|
||||
unlock();
|
||||
}
|
||||
|
||||
~Logger() {
|
||||
#ifdef false // This breaks .net core and windows
|
||||
assert(!_active);
|
||||
assert(_wcursor == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void lock() {
|
||||
while (_mutex.test_and_set(std::memory_order_acquire))
|
||||
;
|
||||
}
|
||||
|
||||
void unlock() { _mutex.clear(std::memory_order_release); }
|
||||
|
||||
std::atomic_flag _mutex = ATOMIC_FLAG_INIT;
|
||||
std::vector<LogRec> *_read_q;
|
||||
std::vector<LogRec> *_write_q;
|
||||
size_t _wcursor;
|
||||
Filter _filter;
|
||||
size_t _drop_count;
|
||||
std::vector<LogRec> _queue_a;
|
||||
std::vector<LogRec> _queue_b;
|
||||
size_t _tid;
|
||||
bool _active;
|
||||
size_t _max_queue_size;
|
||||
std::string _thread_name;
|
||||
};
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/Logger.h"
|
||||
@@ -1,37 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "LoggingConfig.h"
|
||||
#include "Sink.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
|
||||
class Logger;
|
||||
struct LogRec;
|
||||
|
||||
class LoggerHub {
|
||||
public:
|
||||
LoggerHub(std::string const &);
|
||||
~LoggerHub();
|
||||
void config(const LoggingConfig &cfg);
|
||||
void start();
|
||||
Logger &make_logger();
|
||||
|
||||
private:
|
||||
struct SinkRec;
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
void do_work();
|
||||
void update_logger_queue_limits();
|
||||
size_t consume_log_recs();
|
||||
void rebuild_logger_filters();
|
||||
void dispatch_to_sinks(size_t tid, std::string const &thread_name, LogRec &);
|
||||
Filter get_base_filter() const;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/LoggerHub.h"
|
||||
@@ -1,28 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Sink.h"
|
||||
#include "lib/config/protos/threads.pb.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
|
||||
struct LoggingConfig {
|
||||
LoggingConfig()
|
||||
: logger_initial_queue_size(120000), logger_max_queue_growth(4), sinks(),
|
||||
level_names(), subsystem_names(), deployment_type("dev"),
|
||||
thread_cfg() {}
|
||||
|
||||
size_t logger_initial_queue_size = 0;
|
||||
size_t logger_max_queue_growth = 0;
|
||||
std::map<std::string, std::pair<Sink, Filter>> sinks;
|
||||
std::map<Level, std::string> level_names;
|
||||
std::map<Subsystem, std::string> subsystem_names;
|
||||
std::string deployment_type;
|
||||
config::ThreadGroupCfg thread_cfg;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/LoggingConfig.h"
|
||||
@@ -1,64 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <fmt/ostream.h>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
|
||||
template <class Tuple, std::size_t... Is>
|
||||
std::string format_tuple(
|
||||
const char *msg_template, Tuple const &t, std::index_sequence<Is...>) {
|
||||
try {
|
||||
return fmt::format(msg_template, std::get<Is>(t)...);
|
||||
} catch (fmt::format_error const &e) {
|
||||
return fmt::format(
|
||||
"Bad log message template: '{}' Error: '{}'", msg_template, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t SZ, typename... Args>
|
||||
std::string unpack_msg_args(
|
||||
std::array<uint8_t, SZ> const &buffer, const char *msg_template) {
|
||||
std::string result;
|
||||
using Store = std::tuple<Args...>;
|
||||
if (sizeof(Store) <= SZ) {
|
||||
auto args = (Store *)&buffer[0];
|
||||
result =
|
||||
format_tuple(msg_template, *args, std::index_sequence_for<Args...>{});
|
||||
args->~Store();
|
||||
} else {
|
||||
auto indirect_store = (Store **)&buffer[0];
|
||||
auto args = *indirect_store;
|
||||
result =
|
||||
format_tuple(msg_template, *args, std::index_sequence_for<Args...>{});
|
||||
delete args;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args> struct CheckPtr { const static auto value = true; };
|
||||
|
||||
template <typename Head, typename... Tail>
|
||||
struct CheckPtr<Head, Tail...> : public CheckPtr<Tail...> {
|
||||
static_assert(!std::is_pointer<Head>::value, "Pointers cannot be logged");
|
||||
};
|
||||
|
||||
template <size_t SZ, typename... Args>
|
||||
void pack_msg_args(std::array<uint8_t, SZ> &buffer, Args &&... args) {
|
||||
using Store = std::tuple<typename std::remove_reference<Args>::type...>;
|
||||
static_assert(
|
||||
CheckPtr<typename std::remove_reference<Args>::type...>::value,
|
||||
"Illegal type logged");
|
||||
if (sizeof(Store) <= SZ)
|
||||
new (&buffer[0]) Store(std::forward<Args>(args)...);
|
||||
else {
|
||||
auto indirect_store = new Store(std::forward<Args>(args)...);
|
||||
*((Store **)&buffer[0]) = indirect_store;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/MsgPackaging.h"
|
||||
+1
-36
@@ -1,37 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "lib/utils/warn_ctrl.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace bts {
|
||||
namespace log {
|
||||
struct LoggingConfig;
|
||||
GCC_DIAG_OFF("-Weffc++")
|
||||
struct LogMsg {
|
||||
const uint64_t ts;
|
||||
const std::string &msg;
|
||||
|
||||
const char *file;
|
||||
const uint16_t line;
|
||||
|
||||
const Level level;
|
||||
const std::string &level_name;
|
||||
|
||||
const Subsystem subsystem;
|
||||
const std::string &subsystem_name;
|
||||
const size_t tid;
|
||||
const std::string &thread_name;
|
||||
};
|
||||
GCC_DIAG_RESTORE()
|
||||
|
||||
using Sink = std::function<void(LogMsg const &)>;
|
||||
Sink console_sink(LoggingConfig const &cfg);
|
||||
Sink syslog_sink(LoggingConfig const &cfg);
|
||||
Sink file_sink(LoggingConfig const &cfg);
|
||||
Sink null_sink();
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/Sink.h"
|
||||
+1
-117
@@ -1,118 +1,2 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Logger.h"
|
||||
#include "LoggerHub.h"
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <thread>
|
||||
|
||||
namespace bts {
|
||||
namespace config {
|
||||
class Logging;
|
||||
class ThreadGroupCfg;
|
||||
} // namespace config
|
||||
|
||||
// Please keep this list in sync with Log.cs
|
||||
constexpr log::Level NoLog{0};
|
||||
constexpr log::Level Diag{NoLog.ordinal + 1};
|
||||
constexpr log::Level Info{Diag.ordinal + 1};
|
||||
constexpr log::Level Warn{Info.ordinal + 1};
|
||||
constexpr log::Level Error{Warn.ordinal + 1};
|
||||
constexpr log::Level Fatal{Error.ordinal + 1};
|
||||
constexpr log::Level Sys{log::Level::Max - 1};
|
||||
|
||||
namespace log {
|
||||
|
||||
// Please keep this list in sync with Log.cs
|
||||
constexpr log::Subsystem Gen{0};
|
||||
constexpr log::Subsystem Cfg{Gen.ordinal + 1};
|
||||
constexpr log::Subsystem QBus{Cfg.ordinal + 1};
|
||||
constexpr log::Subsystem Log{QBus.ordinal + 1};
|
||||
constexpr log::Subsystem Jetfix{Log.ordinal + 1};
|
||||
constexpr log::Subsystem md{Jetfix.ordinal + 1};
|
||||
constexpr log::Subsystem Api{md.ordinal + 1};
|
||||
constexpr log::Subsystem persist{Api.ordinal + 1};
|
||||
constexpr log::Subsystem User{persist.ordinal + 1};
|
||||
constexpr log::Subsystem OrderMan{User.ordinal + 1};
|
||||
constexpr log::Subsystem Assert{OrderMan.ordinal + 1};
|
||||
constexpr log::Subsystem Engine{Assert.ordinal + 1};
|
||||
constexpr log::Subsystem Ui{Engine.ordinal + 1};
|
||||
constexpr log::Subsystem ExSim{Ui.ordinal + 1};
|
||||
constexpr log::Subsystem Mercury{ExSim.ordinal + 1};
|
||||
constexpr log::Subsystem Testing{Mercury.ordinal + 1};
|
||||
constexpr log::Subsystem Latency{Testing.ordinal + 1};
|
||||
constexpr log::Subsystem DataFlow{Latency.ordinal + 1};
|
||||
constexpr log::Subsystem Plugin{DataFlow.ordinal + 1};
|
||||
constexpr log::Subsystem Sdk{Plugin.ordinal + 1};
|
||||
constexpr log::Subsystem Http{Sdk.ordinal + 1};
|
||||
constexpr log::Subsystem Spartan{Http.ordinal + 1};
|
||||
constexpr log::Subsystem iLink3{Spartan.ordinal + 1};
|
||||
|
||||
struct LoggerHandle {
|
||||
LoggerHandle(LoggerHandle const &) = delete;
|
||||
LoggerHandle &operator=(LoggerHandle const &) = delete;
|
||||
|
||||
LoggerHandle() : logger(nullptr) {}
|
||||
LoggerHandle(Logger &l) : logger(&l) {}
|
||||
~LoggerHandle() {
|
||||
if (logger)
|
||||
logger->mark_inactive();
|
||||
}
|
||||
Logger *logger;
|
||||
};
|
||||
|
||||
struct CharPtrWrapper {
|
||||
const char *ptr;
|
||||
};
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &s, CharPtrWrapper const &ptr) {
|
||||
s << ptr.ptr;
|
||||
return s;
|
||||
}
|
||||
|
||||
extern thread_local LoggerHandle _thread_logger;
|
||||
void configure_logging(LoggingConfig const &cfg);
|
||||
void configure_logging(
|
||||
bts::config::Logging const &cfg, bts::config::ThreadGroupCfg const &);
|
||||
void configure_logging(bts::config::Logging const &cfg);
|
||||
void start_default_logging_thread();
|
||||
|
||||
class LoggingConfigPoller {
|
||||
public:
|
||||
LoggingConfigPoller();
|
||||
LoggingConfigPoller(
|
||||
bts::config::Logging const &cfg,
|
||||
bts::config::ThreadGroupCfg const &thread_cfg);
|
||||
~LoggingConfigPoller();
|
||||
|
||||
private:
|
||||
void init(
|
||||
bts::config::Logging const &cfg,
|
||||
bts::config::ThreadGroupCfg const &thread_cfg);
|
||||
void poll_once();
|
||||
utils::DispatcherTimer _poller;
|
||||
std::time_t _last_ts;
|
||||
boost::filesystem::path _log_cache_file;
|
||||
std::atomic<bool> _done;
|
||||
};
|
||||
|
||||
boost::filesystem::path log_path();
|
||||
|
||||
#define LOG(subsystem, level, ...) \
|
||||
bts::log::_thread_logger.logger->is_loggable(bts::log::subsystem, level) && \
|
||||
bts::log::_thread_logger.logger->enqueue( \
|
||||
bts::log::log_ts(), bts::log::subsystem, level, __FILE__, __LINE__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
#define LOG_ONCE_MAYBE(subsystem, level, ...) \
|
||||
bts::log::_thread_logger.logger->is_loggable(bts::log::subsystem, level) && \
|
||||
bts::log::_thread_logger.logger->enqueue_once_maybe( \
|
||||
bts::log::log_ts(), log::subsystem, level, __FILE__, __LINE__, \
|
||||
__VA_ARGS__)
|
||||
|
||||
} // namespace log
|
||||
} // namespace bts
|
||||
#include "logging/logging.h"
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinanceMiniTickerAdaptor.h"
|
||||
#include "lib/domain/Security.h"
|
||||
#include "lib/domain/SecurityMarketDataStatistics.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/md/ICCApiMessageHandler.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/api/domain/SecurityType.pb.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinanceMiniTickerUpdate.h"
|
||||
#include "lib/core/TimeStamp.h"
|
||||
#include "lib/domain/Price.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/TimeStamp.h"
|
||||
#include "lib/domain/SecurityMarketDataStatistics.h"
|
||||
#include "lib/md/binance/BinanceMiniTickerEventType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinancePriceStatistics.h"
|
||||
#include "lib/core/TimeStamp.h"
|
||||
#include "lib/domain/Price.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/core/TimeStamp.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinancePriceStatisticsAdaptor.h"
|
||||
#include "lib/domain/Security.h"
|
||||
#include "lib/domain/SecurityID.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/md/ICCApiMessageHandler.h"
|
||||
#include <string>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/api/domain/Currency.pb.h"
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinanceSecurityRequestor.h"
|
||||
#include "lib/api/domain/SecurityType.pb.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/utils/Delegate.h"
|
||||
#include <initializer_list>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinanceSpot.h"
|
||||
#include "lib/api/domain/Currency.pb.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/api/domain/Venue.h"
|
||||
#include "lib/md/binance/BinanceContractType.h"
|
||||
#include "lib/md/binance/BinanceFilterTuple.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "BinanceSubscription.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "lib/api/md/BinanceMarketDataConfig.pb.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "BinanceUsPriceStatistics.h"
|
||||
#include "lib/core/TimeStamp.h"
|
||||
#include "lib/domain/Price.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "lib/core/TimeStamp.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "BinanceUsPriceStatisticsAdaptor.h"
|
||||
#include "lib/domain/Security.h"
|
||||
#include "lib/domain/SecurityID.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/md/ICCApiMessageHandler.h"
|
||||
#include <string>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "BinanceUsSecurityRequestor.h"
|
||||
#include "lib/api/domain/SecurityType.pb.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/utils/Delegate.h"
|
||||
#include <initializer_list>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include "BinanceUsSubscription.h"
|
||||
#include "lib/api/domain/Venue.pb.h"
|
||||
#include "lib/api/md/BinanceUsMarketDataConfig.pb.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "BinanceUsdsFuture.h"
|
||||
#include "lib/api/domain/Currency.h"
|
||||
#include "lib/api/domain/Venue.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/md/binance/BinanceInstrumentStatus.h"
|
||||
#include "lib/md/binance/BinanceQuoteAsset.h"
|
||||
#include "lib/md/binance_usds_futures/BinanceUsdsFuturesContractType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/core/EnumMacros.h"
|
||||
#include "lib/core/FMT_IMPL.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "lib/io/JsonDumper.h"
|
||||
#include "lib/io/JsonReader.h"
|
||||
#include "lib/md/binance/BinanceFilterType.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "BinanceUsdsFuturesPriceStatistics.h"
|
||||
#include "lib/domain/Price.h"
|
||||
#include "lib/domain/Quantity.h"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
|
||||
#include "lib/core/TimeStamp.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -48,3 +50,4 @@ class BinanceUsdsFuturesPriceStatistics {
|
||||
};
|
||||
|
||||
} // namespace bts::md::binance_usds_futures
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef SPARK_ENABLE_CCAPI
|
||||
#if defined(SPARK_ENABLE_CCAPI) && defined(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
|
||||
#include "BinanceUsdsFuturesPriceStatisticsAdaptor.h"
|
||||
#include "lib/domain/SecurityID.h"
|
||||
#include "lib/domain/SecurityMarketDataStatistics.h"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/utils/bts_no_copy.h"
|
||||
|
||||
#include "datetime.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/utils/bts_no_copy.h"
|
||||
#include "logging/logging.h"
|
||||
#include <atomic>
|
||||
#include <tbb/tick_count.h>
|
||||
|
||||
@@ -22,7 +21,8 @@
|
||||
namespace bts {
|
||||
namespace utils {
|
||||
|
||||
template <uint32_t T> class CBTimer {
|
||||
template <uint32_t T>
|
||||
class CBTimer {
|
||||
public:
|
||||
CBTimer(
|
||||
double &sum,
|
||||
|
||||
Loaded 100 of 471 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user