Compare commits

...
471 changed files with 14353 additions and 5092 deletions

No files matched your search

+20 -23
View File
@@ -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)
+48
View File
@@ -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
View File
@@ -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
View File
@@ -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
-10
View File
@@ -1,10 +0,0 @@
syntax = "proto3";
package bts.api.domain;
option csharp_namespace = "bts.api.domain";
enum Currency {
BTC = 0;
ETH = 1;
USD = 3;
}
-96
View File
@@ -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
-182
View File
@@ -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
-31
View File
@@ -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
-13
View File
@@ -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
-30
View File
@@ -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"
}
}
}
-99
View File
@@ -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
-58
View File
@@ -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
-890
View File
@@ -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
-533
View File
@@ -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 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 -69
View File
@@ -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 -23
View File
@@ -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 -31
View File
@@ -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
View File
@@ -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 -171
View File
@@ -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"
-43
View File
@@ -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
View File
@@ -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 -8
View File
@@ -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 -55
View File
@@ -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 -48
View File
@@ -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 -41
View File
@@ -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
View File
@@ -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 -36
View File
@@ -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 -27
View File
@@ -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 -63
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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"
+4 -4
View File
@@ -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