Merge branch 'master' into bg_bindings

This commit is contained in:
Josh Slocum 2021-12-01 16:55:28 -06:00
commit a82845af43
216 changed files with 18782 additions and 2426 deletions

5
.gitignore vendored
View File

@ -9,7 +9,9 @@ bindings/java/foundationdb-tests*.jar
bindings/java/fdb-java-*-sources.jar
packaging/msi/FDBInstaller.msi
build/
cmake-build-debug*
cmake-build-debug/
cmake-build-release/
# Generated source, build, and packaging files
*.g.cpp
*.g.h
@ -105,3 +107,4 @@ temp/
/compile_commands.json
/.ccls-cache
.clangd/
.stignore

View File

@ -85,11 +85,20 @@ message(STATUS "Current git version ${CURRENT_GIT_VERSION}")
################################################################################
# Version information
################################################################################
set(FDB_PACKAGE_NAME "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
set(FDB_VERSION ${PROJECT_VERSION})
if (FDB_RELEASE_CANDIDATE)
set(FDB_RELEASE_CANDIDATE_VERSION 1 CACHE STRING "release candidate version")
set(FDB_VERSION ${PROJECT_VERSION}-rc${FDB_RELEASE_CANDIDATE_VERSION})
else()
set(FDB_VERSION ${PROJECT_VERSION})
endif()
set(FDB_VERSION_PLAIN ${FDB_VERSION})
configure_file(${CMAKE_SOURCE_DIR}/versions.target.cmake ${CMAKE_SOURCE_DIR}/versions.target)
string(REPLACE "." ";" FDB_VERSION_LIST ${FDB_VERSION_PLAIN})
list(GET FDB_VERSION_LIST 0 FDB_MAJOR)
list(GET FDB_VERSION_LIST 1 FDB_MINOR)
list(GET FDB_VERSION_LIST 2 FDB_PATCH)
set(FDB_PACKAGE_NAME "${FDB_MAJOR}.${FDB_MINOR}")
configure_file(${CMAKE_SOURCE_DIR}/versions.target.cmake ${CMAKE_CURRENT_BINARY_DIR}/versions.target)
file(WRITE ${CMAKE_BINARY_DIR}/version.txt ${FDB_VERSION})
message(STATUS "FDB version is ${FDB_VERSION}")
message(STATUS "FDB package name is ${FDB_PACKAGE_NAME}")
@ -198,7 +207,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fdbclient/BuildFlags.h.in ${CMAKE_CUR
################################################################################
# process compile commands for IDE
################################################################################
if (CMAKE_EXPORT_COMPILE_COMMANDS AND WITH_PYTHON)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json

View File

@ -117,8 +117,8 @@ should simulate it using an anonymous transaction. Remember that set and clear
operations must immediately commit (with appropriate retry behavior!).
Any error that bubbles out of these operations must be caught. In the event of
an error, you must push the packed tuple of the string `"ERROR"` and the error
code (as a string, not an integer).
an error, you must push the packed tuple of the byte string `"ERROR"` and the
error code (as a byte string, not an integer).
Some operations may allow you to push future values onto the stack. When popping
objects from the stack, the future MUST BE waited on and errors caught before

View File

@ -7,18 +7,26 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/foundationdb)
set(asm_file ${CMAKE_CURRENT_BINARY_DIR}/fdb_c.g.S)
set(platform "linux")
set(os "linux")
set(cpu "intel")
if(APPLE)
set(platform "osx")
set(os "osx")
elseif(WIN32)
set(platform "windows")
set(os "windows")
set(asm_file ${CMAKE_CURRENT_BINARY_DIR}/fdb_c.g.asm)
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(platform "linux-aarch64")
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "arm64")
set(cpu "aarch64")
endif()
set(IS_ARM_MAC NO)
if(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(IS_ARM_MAC YES)
endif()
add_custom_command(OUTPUT ${asm_file} ${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h
COMMAND $<TARGET_FILE:Python::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${platform}
COMMAND $<TARGET_FILE:Python::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${os} ${cpu}
${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp
${asm_file}
${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h
@ -66,8 +74,10 @@ if(WIN32)
set_property(SOURCE ${asm_file} PROPERTY LANGUAGE ASM_MASM)
endif()
# The tests don't build on windows
if(NOT WIN32)
# The tests don't build on windows and ARM macs
# doctest doesn't seem to compile on ARM macs, we should
# check later whether this works
if(NOT WIN32 AND NOT IS_ARM_MAC)
set(MAKO_SRCS
test/mako/mako.c
test/mako/mako.h

View File

@ -23,7 +23,7 @@
import re
import sys
(platform, source, asm, h) = sys.argv[1:]
(os, cpu, source, asm, h) = sys.argv[1:]
functions = {}
@ -59,17 +59,18 @@ def write_windows_asm(asmfile, functions):
def write_unix_asm(asmfile, functions, prefix):
if platform != "linux-aarch64":
if cpu != "aarch64":
asmfile.write(".intel_syntax noprefix\n")
if platform.startswith('linux') or platform == "freebsd":
if cpu == 'aarch64' or os == 'linux' or os == 'freebsd':
asmfile.write("\n.data\n")
for f in functions:
asmfile.write("\t.extern fdb_api_ptr_%s\n" % f)
asmfile.write("\n.text\n")
for f in functions:
asmfile.write("\t.global %s\n\t.type %s, @function\n" % (f, f))
if os == 'linux' or os == 'freebsd':
asmfile.write("\n.text\n")
for f in functions:
asmfile.write("\t.global %s\n\t.type %s, @function\n" % (f, f))
for f in functions:
asmfile.write("\n.globl %s%s\n" % (prefix, f))
@ -104,10 +105,16 @@ def write_unix_asm(asmfile, functions, prefix):
# .size g, .-g
# .ident "GCC: (GNU) 8.3.1 20190311 (Red Hat 8.3.1-3)"
if platform == "linux-aarch64":
asmfile.write("\tadrp x8, :got:fdb_api_ptr_%s\n" % (f))
asmfile.write("\tldr x8, [x8, #:got_lo12:fdb_api_ptr_%s]\n" % (f))
asmfile.write("\tldr x8, [x8]\n")
p = ''
if os == 'osx':
p = '_'
if cpu == "aarch64":
asmfile.write("\tldr x16, =%sfdb_api_ptr_%s\n" % (p, f))
if os == 'osx':
asmfile.write("\tldr x16, [x16]\n")
else:
asmfile.write("\tldr x8, [x8, #:got_lo12:fdb_api_ptr_%s]\n" % (f))
asmfile.write("\tldr x8, [x8]\n")
asmfile.write("\tbr x8\n")
else:
asmfile.write(
@ -123,15 +130,15 @@ with open(asm, 'w') as asmfile:
hfile.write(
"void fdb_api_ptr_removed() { fprintf(stderr, \"REMOVED FDB API FUNCTION\\n\"); abort(); }\n\n")
if platform.startswith('linux'):
if os == 'linux':
write_unix_asm(asmfile, functions, '')
elif platform == "osx":
elif os == "osx":
write_unix_asm(asmfile, functions, '_')
elif platform == "windows":
elif os == "windows":
write_windows_asm(asmfile, functions)
for f in functions:
if platform == "windows":
if os == "windows":
hfile.write("extern \"C\" ")
hfile.write("void* fdb_api_ptr_%s = (void*)&fdb_api_ptr_unimpl;\n" % f)
for v in functions[f]:

View File

@ -1085,7 +1085,7 @@ int run_workload(FDBTransaction* transaction,
if (tracetimer == dotrace) {
fdb_error_t err;
tracetimer = 0;
snprintf(traceid, 32, "makotrace%019lld", total_xacts);
snprintf(traceid, 32, "makotrace%019ld", total_xacts);
fprintf(debugme, "DEBUG: txn tracing %s\n", traceid);
err = fdb_transaction_set_option(transaction,
FDB_TR_OPTION_DEBUG_TRANSACTION_IDENTIFIER,
@ -1246,7 +1246,7 @@ void* worker_thread(void* thread_args) {
}
fprintf(debugme,
"DEBUG: worker_id:%d (%d) thread_id:%d (%d) database_index:%d (tid:%lld)\n",
"DEBUG: worker_id:%d (%d) thread_id:%d (%d) database_index:%lu (tid:%lu)\n",
worker_id,
args->num_processes,
thread_id,
@ -1446,7 +1446,7 @@ int worker_process_main(mako_args_t* args, int worker_id, mako_shmhdr_t* shm, pi
if (err) {
fprintf(stderr,
"ERROR: fdb_network_set_option (FDB_NET_OPTION_CLIENT_THREADS_PER_VERSION) (%d): %s\n",
(uint8_t*)&args->client_threads_per_version,
args->client_threads_per_version,
fdb_get_error(err));
// let's exit here since we do not want to confuse users
// that mako is running with multi-threaded client enabled
@ -2198,9 +2198,9 @@ void print_stats(mako_args_t* args, mako_stats_t* stats, struct timespec* now, s
for (op = 0; op < MAX_OP; op++) {
if (args->txnspec.ops[op][OP_COUNT] > 0) {
uint64_t ops_total_diff = ops_total[op] - ops_total_prev[op];
printf("%" STR(STATS_FIELD_WIDTH) "lld ", ops_total_diff);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", ops_total_diff);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), ops_total_diff);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), ops_total_diff);
}
errors_diff[op] = errors_total[op] - errors_total_prev[op];
print_err = (errors_diff[op] > 0);
@ -2228,7 +2228,7 @@ void print_stats(mako_args_t* args, mako_stats_t* stats, struct timespec* now, s
printf("%" STR(STATS_TITLE_WIDTH) "s ", "Errors");
for (op = 0; op < MAX_OP; op++) {
if (args->txnspec.ops[op][OP_COUNT] > 0) {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", errors_diff[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", errors_diff[op]);
if (fp) {
fprintf(fp, "\"errors\": %.2f", conflicts_diff);
}
@ -2373,10 +2373,10 @@ void print_report(mako_args_t* args,
break;
}
}
printf("Total Xacts: %8lld\n", totalxacts);
printf("Total Conflicts: %8lld\n", conflicts);
printf("Total Errors: %8lld\n", totalerrors);
printf("Overall TPS: %8lld\n\n", totalxacts * 1000000000 / duration_nsec);
printf("Total Xacts: %8lu\n", totalxacts);
printf("Total Conflicts: %8lu\n", conflicts);
printf("Total Errors: %8lu\n", totalerrors);
printf("Overall TPS: %8lu\n\n", totalxacts * 1000000000 / duration_nsec);
if (fp) {
fprintf(fp, "\"results\": {");
@ -2384,10 +2384,10 @@ void print_report(mako_args_t* args,
fprintf(fp, "\"totalProcesses\": %d,", args->num_processes);
fprintf(fp, "\"totalThreads\": %d,", args->num_threads);
fprintf(fp, "\"targetTPS\": %d,", args->tpsmax);
fprintf(fp, "\"totalXacts\": %lld,", totalxacts);
fprintf(fp, "\"totalConflicts\": %lld,", conflicts);
fprintf(fp, "\"totalErrors\": %lld,", totalerrors);
fprintf(fp, "\"overallTPS\": %lld,", totalxacts * 1000000000 / duration_nsec);
fprintf(fp, "\"totalXacts\": %lu,", totalxacts);
fprintf(fp, "\"totalConflicts\": %lu,", conflicts);
fprintf(fp, "\"totalErrors\": %lu,", totalerrors);
fprintf(fp, "\"overallTPS\": %lu,", totalxacts * 1000000000 / duration_nsec);
}
/* per-op stats */
@ -2400,9 +2400,9 @@ void print_report(mako_args_t* args,
}
for (op = 0; op < MAX_OP; op++) {
if ((args->txnspec.ops[op][OP_COUNT] > 0 && op != OP_TRANSACTION) || op == OP_COMMIT) {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", ops_total[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", ops_total[op]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), ops_total[op]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), ops_total[op]);
}
}
}
@ -2423,9 +2423,9 @@ void print_report(mako_args_t* args,
printf("%-" STR(STATS_TITLE_WIDTH) "s ", "Errors");
for (op = 0; op < MAX_OP; op++) {
if (args->txnspec.ops[op][OP_COUNT] > 0 && op != OP_TRANSACTION) {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", errors_total[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", errors_total[op]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), errors_total[op]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), errors_total[op]);
}
}
}
@ -2442,12 +2442,12 @@ void print_report(mako_args_t* args,
for (op = 0; op < MAX_OP; op++) {
if (args->txnspec.ops[op][OP_COUNT] > 0 || op == OP_TRANSACTION || op == OP_COMMIT) {
if (lat_total[op]) {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", lat_samples[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", lat_samples[op]);
} else {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
}
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), lat_samples[op]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), lat_samples[op]);
}
}
}
@ -2463,9 +2463,9 @@ void print_report(mako_args_t* args,
if (lat_min[op] == -1) {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
} else {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", lat_min[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", lat_min[op]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), lat_min[op]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), lat_min[op]);
}
}
}
@ -2480,9 +2480,9 @@ void print_report(mako_args_t* args,
for (op = 0; op < MAX_OP; op++) {
if (args->txnspec.ops[op][OP_COUNT] > 0 || op == OP_TRANSACTION || op == OP_COMMIT) {
if (lat_total[op]) {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", lat_total[op] / lat_samples[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", lat_total[op] / lat_samples[op]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), lat_total[op] / lat_samples[op]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), lat_total[op] / lat_samples[op]);
}
} else {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
@ -2501,9 +2501,9 @@ void print_report(mako_args_t* args,
if (lat_max[op] == 0) {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
} else {
printf("%" STR(STATS_FIELD_WIDTH) "lld ", lat_max[op]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", lat_max[op]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), lat_max[op]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), lat_max[op]);
}
}
}
@ -2553,9 +2553,9 @@ void print_report(mako_args_t* args,
} else {
median = (dataPoints[op][num_points[op] / 2] + dataPoints[op][num_points[op] / 2 - 1]) >> 1;
}
printf("%" STR(STATS_FIELD_WIDTH) "lld ", median);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", median);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), median);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), median);
}
} else {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
@ -2577,9 +2577,9 @@ void print_report(mako_args_t* args,
}
if (lat_total[op]) {
point_95pct = ((float)(num_points[op]) * 0.95) - 1;
printf("%" STR(STATS_FIELD_WIDTH) "lld ", dataPoints[op][point_95pct]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", dataPoints[op][point_95pct]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), dataPoints[op][point_95pct]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), dataPoints[op][point_95pct]);
}
} else {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
@ -2601,9 +2601,9 @@ void print_report(mako_args_t* args,
}
if (lat_total[op]) {
point_99pct = ((float)(num_points[op]) * 0.99) - 1;
printf("%" STR(STATS_FIELD_WIDTH) "lld ", dataPoints[op][point_99pct]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", dataPoints[op][point_99pct]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), dataPoints[op][point_99pct]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), dataPoints[op][point_99pct]);
}
} else {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
@ -2625,9 +2625,9 @@ void print_report(mako_args_t* args,
}
if (lat_total[op]) {
point_99_9pct = ((float)(num_points[op]) * 0.999) - 1;
printf("%" STR(STATS_FIELD_WIDTH) "lld ", dataPoints[op][point_99_9pct]);
printf("%" STR(STATS_FIELD_WIDTH) "lu ", dataPoints[op][point_99_9pct]);
if (fp) {
fprintf(fp, "\"%s\": %lld,", get_ops_name(op), dataPoints[op][point_99_9pct]);
fprintf(fp, "\"%s\": %lu,", get_ops_name(op), dataPoints[op][point_99_9pct]);
}
} else {
printf("%" STR(STATS_FIELD_WIDTH) "s ", "N/A");
@ -2689,7 +2689,7 @@ int stats_process_main(mako_args_t* args,
fprintf(fp, "\"value_length\": %d,", args->value_length);
fprintf(fp, "\"commit_get\": %d,", args->commit_get);
fprintf(fp, "\"verbose\": %d,", args->verbose);
fprintf(fp, "\"cluster_files\": \"%s\",", args->cluster_files);
fprintf(fp, "\"cluster_files\": \"%s\",", args->cluster_files[0]);
fprintf(fp, "\"log_group\": \"%s\",", args->log_group);
fprintf(fp, "\"prefixpadding\": %d,", args->prefixpadding);
fprintf(fp, "\"trace\": %d,", args->trace);
@ -2978,6 +2978,7 @@ failExit:
if (shmfd) {
close(shmfd);
shm_unlink(shmpath);
unlink(shmpath);
}
return 0;

View File

@ -67,25 +67,25 @@ void runTests(struct ResultSet* rs) {
fdb_transaction_set(tr, keys[i], KEY_SIZE, valueStr, VALUE_SIZE);
e = getSize(rs, tr, sizes + i);
checkError(e, "transaction get size", rs);
printf("size %d: %u\n", i, sizes[i]);
printf("size %d: %ld\n", i, sizes[i]);
i++;
fdb_transaction_set(tr, keys[i], KEY_SIZE, valueStr, VALUE_SIZE);
e = getSize(rs, tr, sizes + i);
checkError(e, "transaction get size", rs);
printf("size %d: %u\n", i, sizes[i]);
printf("size %d: %ld\n", i, sizes[i]);
i++;
fdb_transaction_clear(tr, keys[i], KEY_SIZE);
e = getSize(rs, tr, sizes + i);
checkError(e, "transaction get size", rs);
printf("size %d: %u\n", i, sizes[i]);
printf("size %d: %ld\n", i, sizes[i]);
i++;
fdb_transaction_clear_range(tr, keys[i], KEY_SIZE, keys[i + 1], KEY_SIZE);
e = getSize(rs, tr, sizes + i);
checkError(e, "transaction get size", rs);
printf("size %d: %u\n", i, sizes[i]);
printf("size %d: %ld\n", i, sizes[i]);
i++;
for (j = 0; j + 1 < i; j++) {

View File

@ -252,7 +252,8 @@ public:
fdb_bool_t snapshot,
fdb_bool_t reverse);
// Returns a future which will be set to an FDBKeyValue array.
// WARNING: This feature is considered experimental at this time. It is only allowed when using snapshot isolation
// AND disabling read-your-writes. Returns a future which will be set to an FDBKeyValue array.
KeyValueArrayFuture get_range_and_flat_map(const uint8_t* begin_key_name,
int begin_key_name_length,
fdb_bool_t begin_or_equal,

View File

@ -876,17 +876,17 @@ TEST_CASE("fdb_transaction_set_read_version future_version") {
const std::string EMPTY = Tuple().pack().toString();
const KeyRef RECORD = "RECORD"_sr;
const KeyRef INDEX = "INDEX"_sr;
static KeyRef primaryKey(const int i) {
return KeyRef(format("primary-key-of-record-%08d", i));
static Key primaryKey(const int i) {
return Key(format("primary-key-of-record-%08d", i));
}
static KeyRef indexKey(const int i) {
return KeyRef(format("index-key-of-record-%08d", i));
static Key indexKey(const int i) {
return Key(format("index-key-of-record-%08d", i));
}
static ValueRef dataOfRecord(const int i) {
return KeyRef(format("data-of-record-%08d", i));
static Value dataOfRecord(const int i) {
return Value(format("data-of-record-%08d", i));
}
static std::string indexEntryKey(const int i) {
return Tuple().append(prefix).append(INDEX).append(indexKey(i)).append(primaryKey(i)).pack().toString();
return Tuple().append(StringRef(prefix)).append(INDEX).append(indexKey(i)).append(primaryKey(i)).pack().toString();
}
static std::string recordKey(const int i) {
return Tuple().append(prefix).append(RECORD).append(primaryKey(i)).pack().toString();
@ -922,7 +922,7 @@ TEST_CASE("fdb_transaction_get_range_and_flat_map") {
/* target_bytes */ 0,
/* FDBStreamingMode */ FDB_STREAMING_MODE_WANT_ALL,
/* iteration */ 0,
/* snapshot */ false,
/* snapshot */ true,
/* reverse */ 0);
if (result.err) {
@ -953,6 +953,44 @@ TEST_CASE("fdb_transaction_get_range_and_flat_map") {
}
}
TEST_CASE("fdb_transaction_get_range_and_flat_map_restricted_to_snapshot") {
std::string mapper = Tuple().append(prefix).append(RECORD).append("{K[3]}"_sr).pack().toString();
fdb::Transaction tr(db);
fdb_check(tr.set_option(FDB_TR_OPTION_READ_YOUR_WRITES_DISABLE, nullptr, 0));
auto result = get_range_and_flat_map(
tr,
FDB_KEYSEL_FIRST_GREATER_OR_EQUAL((const uint8_t*)indexEntryKey(0).c_str(), indexEntryKey(0).size()),
FDB_KEYSEL_FIRST_GREATER_THAN((const uint8_t*)indexEntryKey(1).c_str(), indexEntryKey(1).size()),
(const uint8_t*)mapper.c_str(),
mapper.size(),
/* limit */ 0,
/* target_bytes */ 0,
/* FDBStreamingMode */ FDB_STREAMING_MODE_WANT_ALL,
/* iteration */ 0,
/* snapshot */ false, // Set snapshot to false
/* reverse */ 0);
ASSERT(result.err == error_code_client_invalid_operation);
}
TEST_CASE("fdb_transaction_get_range_and_flat_map_restricted_to_ryw_disable") {
std::string mapper = Tuple().append(prefix).append(RECORD).append("{K[3]}"_sr).pack().toString();
fdb::Transaction tr(db);
// Not set FDB_TR_OPTION_READ_YOUR_WRITES_DISABLE.
auto result = get_range_and_flat_map(
tr,
FDB_KEYSEL_FIRST_GREATER_OR_EQUAL((const uint8_t*)indexEntryKey(0).c_str(), indexEntryKey(0).size()),
FDB_KEYSEL_FIRST_GREATER_THAN((const uint8_t*)indexEntryKey(1).c_str(), indexEntryKey(1).size()),
(const uint8_t*)mapper.c_str(),
mapper.size(),
/* limit */ 0,
/* target_bytes */ 0,
/* FDBStreamingMode */ FDB_STREAMING_MODE_WANT_ALL,
/* iteration */ 0,
/* snapshot */ true,
/* reverse */ 0);
ASSERT(result.err == error_code_client_invalid_operation);
}
TEST_CASE("fdb_transaction_get_range reverse") {
std::map<std::string, std::string> data = create_data({ { "a", "1" }, { "b", "2" }, { "c", "3" }, { "d", "4" } });
insert_data(db, data);

View File

@ -36,15 +36,15 @@ if(NOT OPEN_FOR_IDE)
endforeach()
if(NOT FDB_RELEASE)
set(prerelease_string "-PRERELEASE")
set(not_fdb_release_string "-SNAPSHOT")
else()
set(prerelease_string "")
set(not_fdb_release_string "")
endif()
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/packages)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/packages)
set(package_dir ${CMAKE_CURRENT_BINARY_DIR}/packages/fdb-flow-${CMAKE_PROJECT_VERSION})
set(tar_file ${CMAKE_BINARY_DIR}/packages/fdb-flow-${CMAKE_PROJECT_VERSION}${prerelease_string}-x86_64.tar.gz)
set(package_dir ${CMAKE_CURRENT_BINARY_DIR}/packages/fdb-flow-${FDB_VERSION})
set(tar_file ${CMAKE_BINARY_DIR}/packages/fdb-flow-${FDB_VERSION}${not_fdb_release_string}-x86_64.tar.gz)
add_custom_command(OUTPUT ${tar_file}
COMMAND
${CMAKE_COMMAND} -E make_directory ${package_dir} &&

View File

@ -24,6 +24,7 @@
#include <stdio.h>
#include <cinttypes>
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "flow/DeterministicRandom.h"
#include "flow/SystemMonitor.h"
#include "flow/TLSConfig.actor.h"
@ -44,7 +45,7 @@ ACTOR Future<Void> _test() {
// tr->setVersion(1);
Version ver = wait(tr->getReadVersion());
printf("%" PRId64 "\n", ver);
fmt::print("{}\n", ver);
state std::vector<Future<Version>> versions;

View File

@ -182,7 +182,7 @@ set(MANIFEST_FILE ${CMAKE_CURRENT_BINARY_DIR}/resources/META-INF/MANIFEST.MF)
file(WRITE ${MANIFEST_FILE} ${MANIFEST_TEXT})
add_jar(fdb-java ${JAVA_BINDING_SRCS} ${GENERATED_JAVA_FILES} ${CMAKE_SOURCE_DIR}/LICENSE
OUTPUT_DIR ${PROJECT_BINARY_DIR}/lib VERSION ${CMAKE_PROJECT_VERSION} MANIFEST ${MANIFEST_FILE})
OUTPUT_DIR ${PROJECT_BINARY_DIR}/lib VERSION ${FDB_VERSION} MANIFEST ${MANIFEST_FILE})
add_dependencies(fdb-java fdb_java_options fdb_java)
if(NOT OPEN_FOR_IDE)
@ -238,11 +238,11 @@ if(NOT OPEN_FOR_IDE)
add_custom_target(copy_lib DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lib_copied)
add_dependencies(copy_lib unpack_jar)
if(NOT FDB_RELEASE)
set(prerelease_string "-PRERELEASE")
set(not_fdb_release_string "-SNAPSHOT")
else()
set(prerelease_string "")
set(not_fdb_release_string "")
endif()
set(target_jar ${jar_destination}/fdb-java-${CMAKE_PROJECT_VERSION}${prerelease_string}.jar)
set(target_jar ${jar_destination}/fdb-java-${FDB_VERSION}${not_fdb_release_string}.jar)
add_custom_command(OUTPUT ${target_jar}
COMMAND ${Java_JAR_EXECUTABLE} cfm ${target_jar} ${unpack_dir}/META-INF/MANIFEST.MF .
WORKING_DIRECTORY ${unpack_dir}
@ -250,7 +250,7 @@ if(NOT OPEN_FOR_IDE)
COMMENT "Build ${target_jar}")
add_jar(foundationdb-tests SOURCES ${JAVA_TESTS_SRCS} INCLUDE_JARS fdb-java)
add_dependencies(foundationdb-tests fdb_java_options)
set(tests_jar ${jar_destination}/fdb-java-${CMAKE_PROJECT_VERSION}${prerelease_string}-tests.jar)
set(tests_jar ${jar_destination}/fdb-java-${FDB_VERSION}${not_fdb_release_string}-tests.jar)
add_custom_command(OUTPUT ${tests_jar}
COMMAND ${CMAKE_COMMAND} -E copy foundationdb-tests.jar "${tests_jar}"
WORKING_DIRECTORY .

View File

@ -180,9 +180,10 @@ class RangeAndFlatMapQueryIntegrationTest {
try {
tr.options().setReadYourWritesDisable();
List<KeyValue> kvs =
tr.getRangeAndFlatMap(KeySelector.firstGreaterOrEqual(indexEntryKey(begin)),
KeySelector.firstGreaterOrEqual(indexEntryKey(end)), MAPPER,
ReadTransaction.ROW_LIMIT_UNLIMITED, false, StreamingMode.WANT_ALL)
tr.snapshot()
.getRangeAndFlatMap(KeySelector.firstGreaterOrEqual(indexEntryKey(begin)),
KeySelector.firstGreaterOrEqual(indexEntryKey(end)), MAPPER,
ReadTransaction.ROW_LIMIT_UNLIMITED, false, StreamingMode.WANT_ALL)
.asList()
.get();
Assertions.assertEquals(end - begin, kvs.size());
@ -219,10 +220,12 @@ class RangeAndFlatMapQueryIntegrationTest {
// getRangeAndFlatMap is only support without RYW. This is a must!!!
tr.options().setReadYourWritesDisable();
// getRangeAndFlatMap is only supported with snapshot.
Iterator<KeyValue> kvs =
tr.getRangeAndFlatMap(KeySelector.firstGreaterOrEqual(indexEntryKey(0)),
KeySelector.firstGreaterThan(indexEntryKey(1)), MAPPER,
ReadTransaction.ROW_LIMIT_UNLIMITED, false, StreamingMode.WANT_ALL)
tr.snapshot()
.getRangeAndFlatMap(KeySelector.firstGreaterOrEqual(indexEntryKey(0)),
KeySelector.firstGreaterThan(indexEntryKey(1)), MAPPER,
ReadTransaction.ROW_LIMIT_UNLIMITED, false, StreamingMode.WANT_ALL)
.iterator();
Iterator<byte[]> expected_data_of_records_iter = expected_data_of_records.iterator();
while (expected_data_of_records_iter.hasNext()) {

View File

@ -350,10 +350,7 @@ class FDBTransaction extends NativeObjectWrapper implements Transaction, OptionC
@Override
public AsyncIterable<KeyValue> getRangeAndFlatMap(KeySelector begin, KeySelector end, byte[] mapper, int limit,
boolean reverse, StreamingMode mode) {
if (mapper == null) {
throw new IllegalArgumentException("Mapper must be non-null");
}
return new RangeQuery(this, false, begin, end, mapper, limit, reverse, mode, eventKeeper);
throw new UnsupportedOperationException("getRangeAndFlatMap is only supported in snapshot");
}
///////////////////

View File

@ -425,9 +425,8 @@ public interface ReadTransaction extends ReadTransactionContext {
int limit, boolean reverse, StreamingMode mode);
/**
* Gets an ordered range of keys and values from the database. The begin
* and end keys are specified by {@code KeySelector}s, with the begin
* {@code KeySelector} inclusive and the end {@code KeySelector} exclusive.
* WARNING: This feature is considered experimental at this time. It is only allowed when using snapshot isolation
* AND disabling read-your-writes.
*
* @see KeySelector
* @see AsyncIterator

View File

@ -60,12 +60,12 @@ else()
add_custom_target(fdb_python_check COMMAND ${CMAKE_COMMAND} -E echo "Skipped Python style check! Missing: pycodestyle")
endif()
if(NOT FDB_RELEASE)
set(prerelease_string "a1")
set(not_fdb_release_string "a1")
else()
set(prerelease_string "")
set(not_fdb_release_string "")
endif()
set(setup_file_name foundationdb-${FDB_VERSION}.tar.gz)
set(package_file ${CMAKE_BINARY_DIR}/packages/foundationdb-${FDB_VERSION}${prerelease_string}.tar.gz)
set(package_file ${CMAKE_BINARY_DIR}/packages/foundationdb-${FDB_VERSION}${not_fdb_release_string}.tar.gz)
add_custom_command(OUTPUT ${package_file}
COMMAND $<TARGET_FILE:Python::Interpreter> setup.py sdist --formats=gztar &&
${CMAKE_COMMAND} -E copy dist/${setup_file_name} ${package_file}

View File

@ -5,15 +5,15 @@ vexillographer_compile(TARGET ruby_options LANG ruby
configure_file(fdb.gemspec.cmake fdb.gemspec)
if(NOT FDB_RELEASE)
set(prerelease_string "PRERELEASE")
set(not_fdb_release_string "SNAPSHOT")
else()
set(prerelease_string "")
set(not_fdb_release_string "")
endif()
set(setup_file fdb-${FDB_VERSION}.gem)
set(gem_target ${CMAKE_BINARY_DIR}/packages/fdb-${FDB_VERSION}${prerelease_string}.gem)
set(gem_target ${CMAKE_BINARY_DIR}/packages/fdb-${FDB_VERSION}${not_fdb_release_string}.gem)
add_custom_command(OUTPUT ${gem_target}
COMMAND ${GEM_COMMAND} build fdb.gemspec &&
COMMAND ${GEM_COMMAND} build fdb.gemspec --output ${setup_file} &&
${CMAKE_COMMAND} -E copy ${setup_file} ${gem_target}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Building ruby gem")

View File

@ -250,7 +250,7 @@ function(create_correctness_package)
endif()
set(out_dir "${CMAKE_BINARY_DIR}/correctness")
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "correctness" OUT_FILES package_files)
set(tar_file ${CMAKE_BINARY_DIR}/packages/correctness-${CMAKE_PROJECT_VERSION}.tar.gz)
set(tar_file ${CMAKE_BINARY_DIR}/packages/correctness-${FDB_VERSION}.tar.gz)
add_custom_command(
OUTPUT ${tar_file}
DEPENDS ${package_files}
@ -285,7 +285,7 @@ function(create_valgrind_correctness_package)
if(USE_VALGRIND)
set(out_dir "${CMAKE_BINARY_DIR}/valgrind_correctness")
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "valgrind correctness" OUT_FILES package_files)
set(tar_file ${CMAKE_BINARY_DIR}/packages/valgrind-${CMAKE_PROJECT_VERSION}.tar.gz)
set(tar_file ${CMAKE_BINARY_DIR}/packages/valgrind-${FDB_VERSION}.tar.gz)
add_custom_command(
OUTPUT ${tar_file}
DEPENDS ${package_files}
@ -355,14 +355,14 @@ function(package_bindingtester)
set(generated_binding_files python/fdb/fdboptions.py)
if(WITH_JAVA_BINDING)
if(NOT FDB_RELEASE)
set(prerelease_string "-PRERELEASE")
set(not_fdb_release_string "-SNAPSHOT")
else()
set(prerelease_string "")
set(not_fdb_release_string "")
endif()
add_custom_command(
TARGET copy_binding_output_files
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_BINARY_DIR}/packages/fdb-java-${CMAKE_PROJECT_VERSION}${prerelease_string}.jar
${CMAKE_BINARY_DIR}/packages/fdb-java-${FDB_VERSION}${not_fdb_release_string}.jar
${bdir}/tests/java/foundationdb-client.jar
COMMENT "Copy Java bindings for bindingtester")
add_dependencies(copy_binding_output_files fat-jar)
@ -391,7 +391,7 @@ function(package_bindingtester)
add_custom_target(copy_bindingtester_binaries
DEPENDS ${outfiles} "${CMAKE_BINARY_DIR}/bindingtester.touch" copy_binding_output_files)
add_dependencies(copy_bindingtester_binaries strip_only_fdbserver strip_only_fdbcli strip_only_fdb_c)
set(tar_file ${CMAKE_BINARY_DIR}/packages/bindingtester-${CMAKE_PROJECT_VERSION}.tar.gz)
set(tar_file ${CMAKE_BINARY_DIR}/packages/bindingtester-${FDB_VERSION}.tar.gz)
add_custom_command(
OUTPUT ${tar_file}
COMMAND ${CMAKE_COMMAND} -E tar czf ${tar_file} *

View File

@ -49,8 +49,8 @@ function(compile_boost)
include(ExternalProject)
set(BOOST_INSTALL_DIR "${CMAKE_BINARY_DIR}/boost_install")
ExternalProject_add("${COMPILE_BOOST_TARGET}Project"
URL "https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.bz2"
URL_HASH SHA256=59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722
URL "https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2"
URL_HASH SHA256=fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854
CONFIGURE_COMMAND ${BOOTSTRAP_COMMAND} ${BOOTSTRAP_ARGS} --with-libraries=${BOOTSTRAP_LIBRARIES} --with-toolset=${BOOST_TOOLSET}
BUILD_COMMAND ${B2_COMMAND} link=static ${COMPILE_BOOST_BUILD_ARGS} --prefix=${BOOST_INSTALL_DIR} ${USER_CONFIG_FLAG} install
BUILD_IN_SOURCE ON
@ -113,7 +113,7 @@ if(WIN32)
return()
endif()
find_package(Boost 1.72.0 EXACT QUIET COMPONENTS context CONFIG PATHS ${BOOST_HINT_PATHS})
find_package(Boost 1.77.0 EXACT QUIET COMPONENTS context CONFIG PATHS ${BOOST_HINT_PATHS})
set(FORCE_BOOST_BUILD OFF CACHE BOOL "Forces cmake to build boost and ignores any installed boost")
if(Boost_FOUND AND NOT FORCE_BOOST_BUILD)

View File

@ -10,6 +10,7 @@ env_set(USE_GCOV OFF BOOL "Compile with gcov instrumentation")
env_set(USE_MSAN OFF BOOL "Compile with memory sanitizer. To avoid false positives you need to dynamically link to a msan-instrumented libc++ and libc++abi, which you must compile separately. See https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo#instrumented-libc.")
env_set(USE_TSAN OFF BOOL "Compile with thread sanitizer. It is recommended to dynamically link to a tsan-instrumented libc++ and libc++abi, which you can compile separately.")
env_set(USE_UBSAN OFF BOOL "Compile with undefined behavior sanitizer")
env_set(FDB_RELEASE_CANDIDATE OFF BOOL "This is a building of a release candidate")
env_set(FDB_RELEASE OFF BOOL "This is a building of a final release")
env_set(USE_CCACHE OFF BOOL "Use ccache for compilation if available")
env_set(RELATIVE_DEBUG_PATHS OFF BOOL "Use relative file paths in debug info")
@ -283,11 +284,11 @@ else()
-Wshift-sign-overflow
# Here's the current set of warnings we need to explicitly disable to compile warning-free with clang 11
-Wno-delete-non-virtual-dtor
-Wno-format
-Wno-sign-compare
-Wno-undefined-var-template
-Wno-unknown-warning-option
-Wno-unused-parameter
-Wno-constant-logical-operand
)
if (USE_CCACHE)
add_compile_options(

View File

@ -202,6 +202,21 @@ else()
target_include_directories(toml11_target SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/toml11/include)
endif()
################################################################################
# Coroutine implementation
################################################################################
set(DEFAULT_COROUTINE_IMPL boost)
if(WIN32)
# boost coroutine not available in windows build environment for now.
set(DEFAULT_COROUTINE_IMPL libcoro)
elseif(NOT APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^x86")
# revert to libcoro for x86 linux while we investigate a performance regression
set(DEFAULT_COROUTINE_IMPL libcoro)
endif()
set(COROUTINE_IMPL ${DEFAULT_COROUTINE_IMPL} CACHE STRING "Which coroutine implementation to use. Options are boost and libcoro")
################################################################################
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/packages)

View File

@ -105,7 +105,7 @@ install_destinations(TGZ
ETC etc/foundationdb
LOG log/foundationdb
DATA lib/foundationdb)
copy_install_destinations(TGZ VERSIONED PREFIX "usr/lib/foundationdb-${PROJECT_VERSION}/")
copy_install_destinations(TGZ VERSIONED PREFIX "usr/lib/foundationdb-${FDB_VERSION}/")
install_destinations(DEB
BIN usr/bin
SBIN usr/sbin
@ -141,21 +141,12 @@ set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_BINARY_DIR}/packaging/CPackConfig.cmake")
set(GENERATE_DEBUG_PACKAGES "${FDB_RELEASE}" CACHE BOOL "Build debug rpm/deb packages (default: only ON for FDB_RELEASE)")
################################################################################
# Version information
################################################################################
string(REPLACE "." ";" FDB_VERSION_LIST ${FDB_VERSION_PLAIN})
list(GET FDB_VERSION_LIST 0 FDB_MAJOR)
list(GET FDB_VERSION_LIST 1 FDB_MINOR)
list(GET FDB_VERSION_LIST 2 FDB_PATCH)
################################################################################
# Alternatives config
################################################################################
set(mv_packaging_dir ${PROJECT_SOURCE_DIR}/packaging/multiversion)
# USE PROJECT_VERSION_* HERE AS THEY ARE GUARANTEED TO BE INTEGERS ONLY
math(EXPR ALTERNATIVES_PRIORITY "(${PROJECT_VERSION_MAJOR} * 1000) + (${PROJECT_VERSION_MINOR} * 100) + ${PROJECT_VERSION_PATCH}")
set(script_dir "${PROJECT_BINARY_DIR}/packaging/multiversion/")
file(MAKE_DIRECTORY "${script_dir}/server" "${script_dir}/clients")
@ -185,12 +176,6 @@ install(DIRECTORY "${script_dir}/clients/usr/lib/cmake"
DESTINATION usr/lib
COMPONENT clients-versioned)
################################################################################
# Move Docker Setup
################################################################################
file(COPY "${PROJECT_SOURCE_DIR}/packaging/docker" DESTINATION "${PROJECT_BINARY_DIR}/packages/")
################################################################################
# General CPack configuration
################################################################################
@ -215,20 +200,20 @@ set(CPACK_COMPONENT_SERVER-DEB_DEPENDS clients-deb)
set(CPACK_COMPONENT_SERVER-TGZ_DEPENDS clients-tgz)
set(CPACK_COMPONENT_SERVER-VERSIONED_DEPENDS clients-versioned)
set(CPACK_RPM_SERVER-VERSIONED_PACKAGE_REQUIRES
"foundationdb${PROJECT_VERSION}-clients")
"foundationdb${FDB_VERSION}-clients")
set(CPACK_COMPONENT_SERVER-EL7_DISPLAY_NAME "foundationdb-server")
set(CPACK_COMPONENT_SERVER-DEB_DISPLAY_NAME "foundationdb-server")
set(CPACK_COMPONENT_SERVER-TGZ_DISPLAY_NAME "foundationdb-server")
set(CPACK_COMPONENT_SERVER-VERSIONED_DISPLAY_NAME "foundationdb${PROJECT_VERSION}-server")
set(CPACK_COMPONENT_SERVER-VERSIONED_DISPLAY_NAME "foundationdb${FDB_VERSION}-server")
set(CPACK_COMPONENT_CLIENTS-EL7_DISPLAY_NAME "foundationdb-clients")
set(CPACK_COMPONENT_CLIENTS-DEB_DISPLAY_NAME "foundationdb-clients")
set(CPACK_COMPONENT_CLIENTS-TGZ_DISPLAY_NAME "foundationdb-clients")
set(CPACK_COMPONENT_CLIENTS-VERSIONED_DISPLAY_NAME "foundationdb${PROJECT_VERSION}-clients")
set(CPACK_COMPONENT_CLIENTS-VERSIONED_DISPLAY_NAME "foundationdb${FDB_VERSION}-clients")
# MacOS needs a file exiension for the LICENSE file
# MacOS needs a file extension for the LICENSE file
configure_file(${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_BINARY_DIR}/License.txt COPYONLY)
################################################################################
@ -240,27 +225,27 @@ if(NOT FDB_RELEASE)
set(git_string ".${CURRENT_GIT_VERSION}")
endif()
set(CPACK_RPM_PACKAGE_RELEASE 0)
set(prerelease_string "-0${git_string}.PRERELEASE")
set(not_fdb_release_string "-0${git_string}.SNAPSHOT")
else()
set(CPACK_RPM_PACKAGE_RELEASE 1)
set(prerelease_string "-1")
set(not_fdb_release_string "-1")
endif()
#############
# Filenames #
#############
set(unversioned_postfix "${PROJECT_VERSION}${prerelease_string}")
set(unversioned_postfix "${FDB_VERSION}${not_fdb_release_string}")
# RPM filenames
set(rpm-clients-filename "foundationdb-clients-${unversioned_postfix}")
set(rpm-server-filename "foundationdb-server-${unversioned_postfix}")
set(rpm-clients-versioned-filename "foundationdb${PROJECT_VERSION}-clients${prerelease_string}")
set(rpm-server-versioned-filename "foundationdb${PROJECT_VERSION}-server${prerelease_string}")
set(rpm-clients-versioned-filename "foundationdb${FDB_VERSION}-clients${prerelease_string}")
set(rpm-server-versioned-filename "foundationdb${FDB_VERSION}-server${prerelease_string}")
# Deb filenames
set(deb-clients-filename "foundationdb-clients_${unversioned_postfix}")
set(deb-server-filename "foundationdb-server_${unversioned_postfix}")
set(deb-clients-versioned-filename "foundationdb${PROJECT_VERSION}-clients${prerelease_string}")
set(deb-server-versioned-filename "foundationdb${PROJECT_VERSION}-server${prerelease_string}")
set(deb-clients-versioned-filename "foundationdb${FDB_VERSION}-clients${prerelease_string}")
set(deb-server-versioned-filename "foundationdb${FDB_VERSION}-server${prerelease_string}")
################################################################################
# Configuration for RPM
@ -271,8 +256,8 @@ set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0")
set(CPACK_RPM_PACKAGE_NAME "foundationdb")
set(CPACK_RPM_CLIENTS-EL7_PACKAGE_NAME "foundationdb-clients")
set(CPACK_RPM_SERVER-EL7_PACKAGE_NAME "foundationdb-server")
set(CPACK_RPM_SERVER-VERSIONED_PACKAGE_NAME "foundationdb${PROJECT_VERSION}-server")
set(CPACK_RPM_CLIENTS-VERSIONED_PACKAGE_NAME "foundationdb${PROJECT_VERSION}-clients")
set(CPACK_RPM_SERVER-VERSIONED_PACKAGE_NAME "foundationdb${FDB_VERSION}-server")
set(CPACK_RPM_CLIENTS-VERSIONED_PACKAGE_NAME "foundationdb${FDB_VERSION}-clients")
set(CPACK_RPM_CLIENTS-EL7_FILE_NAME "${rpm-clients-filename}.el7.${CMAKE_SYSTEM_PROCESSOR}.rpm")
set(CPACK_RPM_CLIENTS-VERSIONED_FILE_NAME "${rpm-clients-versioned-filename}.versioned.${CMAKE_SYSTEM_PROCESSOR}.rpm")
@ -370,8 +355,8 @@ set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
set(CPACK_DEBIAN_SERVER-DEB_PACKAGE_NAME "foundationdb-server")
set(CPACK_DEBIAN_CLIENTS-DEB_PACKAGE_NAME "foundationdb-clients")
set(CPACK_DEBIAN_SERVER-VERSIONED_PACKAGE_NAME "foundationdb${PROJECT_VERSION}-server")
set(CPACK_DEBIAN_CLIENTS-VERSIONED_PACKAGE_NAME "foundationdb${PROJECT_VERSION}-clients")
set(CPACK_DEBIAN_SERVER-VERSIONED_PACKAGE_NAME "foundationdb${FDB_VERSION}-server")
set(CPACK_DEBIAN_CLIENTS-VERSIONED_PACKAGE_NAME "foundationdb${FDB_VERSION}-clients")
set(CPACK_DEBIAN_SERVER-DEB_PACKAGE_DEPENDS "adduser, libc6 (>= 2.12), foundationdb-clients (= ${FDB_VERSION})")
set(CPACK_DEBIAN_SERVER-DEB_PACKAGE_RECOMMENDS "python (>= 2.6)")
@ -425,10 +410,10 @@ if(NOT WIN32)
RENAME "foundationdb"
COMPONENT server-deb)
install(FILES ${CMAKE_SOURCE_DIR}/packaging/rpm/foundationdb.service
DESTINATION "usr/lib/foundationdb-${PROJECT_VERSION}/lib/systemd/system"
DESTINATION "usr/lib/foundationdb-${FDB_VERSION}/lib/systemd/system"
COMPONENT server-versioned)
install(PROGRAMS ${CMAKE_SOURCE_DIR}/packaging/deb/foundationdb-init
DESTINATION "usr/lib/foundationdb-${PROJECT_VERSION}/etc/init.d"
DESTINATION "usr/lib/foundationdb-${FDB_VERSION}/etc/init.d"
RENAME "foundationdb"
COMPONENT server-versioned)
endif()

View File

@ -3,7 +3,7 @@ includedir=/@INCLUDE_DIR@
Name: foundationdb-client
Description: FoundationDB c client
Version: @PROJECT_VERSION@
Version: @FDB_VERSION@
Libs: -L${libdir} -lfdb_c
Cflags: -I${includedir}

View File

@ -1,3 +1,4 @@
add_subdirectory(fmt-8.0.1)
if(NOT WIN32)
add_subdirectory(monitoring)
add_subdirectory(TraceLogHelper)

View File

@ -1636,8 +1636,8 @@ namespace SummarizeTest
{
Console.WriteLine("Version: 1.02");
Console.WriteLine("FDB Project Ver: " + "${CMAKE_PROJECT_VERSION}");
Console.WriteLine("FDB Version: " + "${CMAKE_PROJECT_VERSION_MAJOR}" + "." + "${CMAKE_PROJECT_VERSION_MINOR}");
Console.WriteLine("FDB Project Ver: " + "${FDB_VERSION}");
Console.WriteLine("FDB Version: " + "${FDB_VERSION_MAJOR}" + "." + "${FDB_VERSION_MINOR}");
Console.WriteLine("Source Version: " + "${CURRENT_GIT_VERSION}");
return 1;
}

View File

@ -0,0 +1 @@
DisableFormat: true

View File

@ -0,0 +1,410 @@
cmake_minimum_required(VERSION 3.1...3.18)
# Fallback for using newer policies on CMake <3.12.
if(${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
if (NOT DEFINED FMT_MASTER_PROJECT)
set(FMT_MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(FMT_MASTER_PROJECT ON)
message(STATUS "CMake version: ${CMAKE_VERSION}")
endif ()
endif ()
# Joins arguments and places the results in ${result_var}.
function(join result_var)
set(result "")
foreach (arg ${ARGN})
set(result "${result}${arg}")
endforeach ()
set(${result_var} "${result}" PARENT_SCOPE)
endfunction()
function(enable_module target)
if (MSVC)
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI})
endif ()
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
endfunction()
include(CMakeParseArguments)
# Sets a cache variable with a docstring joined from multiple arguments:
# set(<variable> <value>... CACHE <type> <docstring>...)
# This allows splitting a long docstring for readability.
function(set_verbose)
# cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
# list instead.
list(GET ARGN 0 var)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 val)
list(REMOVE_AT ARGN 0)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 type)
list(REMOVE_AT ARGN 0)
join(doc ${ARGN})
set(${var} ${val} CACHE ${type} ${doc})
endfunction()
# Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake).
if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif ()
project(FMT CXX)
include(GNUInstallDirs)
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
"Installation directory for include files, a relative path that "
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
set(FMT_CAN_MODULE OFF)
if (CMAKE_CXX_STANDARD GREATER 17 AND
# msvc 16.10-pre4
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
set(FMT_CAN_MODULE ON)
endif ()
if (NOT FMT_CAN_MODULE)
set(FMT_MODULE OFF)
message(STATUS "Module support is disabled.")
endif ()
if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.")
endif ()
# Get version from core.h
file(READ include/fmt/core.h core_h)
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.")
endif ()
# Use math to skip leading zeros if any.
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
${CPACK_PACKAGE_VERSION_PATCH})
message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
# compatibility with older CMake versions.
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
endif ()
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING
"Preset for the export of private symbols")
set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS
hidden default)
endif ()
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL
"Whether to add a compile flag to hide symbols of inline functions")
endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
-Wold-style-cast -Wundef
-Wredundant-decls -Wwrite-strings -Wpointer-arith
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
-Wcast-align
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion -Wswitch-enum -Wundef
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
-Wno-dangling-else -Wno-unused-local-typedefs)
endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
-Wvector-operation-performance -Wsized-deallocation -Wshadow)
endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
-Wnull-dereference -Wduplicated-cond)
endif ()
set(WERROR_FLAG -Werror)
endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
-Wdeprecated -Wweak-vtables -Wshadow
-Wno-gnu-zero-variadic-macro-arguments)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
-Wzero-as-null-pointer-constant)
endif ()
set(WERROR_FLAG -Werror)
endif ()
if (MSVC)
set(PEDANTIC_COMPILE_FLAGS /W3)
set(WERROR_FLAG /WX)
endif ()
if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
# If Microsoft SDK is installed create script run-msbuild.bat that
# calls SetEnv.cmd to set up build environment and runs msbuild.
# It is useful when building Visual Studio projects with the SDK
# toolchain rather than Visual Studio.
include(FindSetEnv)
if (WINSDK_SETENV)
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
endif ()
# Set FrameworkPathOverride to get rid of MSB3644 warnings.
join(netfxpath
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
".NETFramework\\v4.0")
file(WRITE run-msbuild.bat "
${MSBUILD_SETUP}
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
function(add_headers VAR)
set(headers ${${VAR}})
foreach (header ${ARGN})
set(headers ${headers} include/fmt/${header})
endforeach()
set(${VAR} ${headers} PARENT_SCOPE)
endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
format-inl.h locale.h os.h ostream.h printf.h ranges.h
xchar.h)
if (FMT_MODULE)
set(FMT_SOURCES src/fmt.cc)
elseif (FMT_OS)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS})
add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (MINGW)
check_cxx_compiler_flag("Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
if (${FMT_HAS_MBIG_OBJ})
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif()
endif ()
if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif ()
if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (FMT_MODULE)
enable_module(fmt)
endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
# property because it's not set by default.
set(FMT_LIB_NAME fmt)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
endif ()
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND
NOT EMSCRIPTEN)
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed)
endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif ()
if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
endif()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
# Install targets.
if (FMT_INSTALL)
include(CMakePackageConfigHelpers)
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, a relative path that "
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
"path.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets)
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, a relative path that "
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, a relative "
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
"absolute path.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
${version_config}
VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion)
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}"
@ONLY)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(
FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR})
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif ()
if (FMT_DOC)
add_subdirectory(doc)
endif ()
if (FMT_TEST)
enable_testing()
add_subdirectory(test)
endif ()
# Control fuzzing independent of the unit tests.
if (FMT_FUZZ)
add_subdirectory(test/fuzzing)
# The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing
# mode and make fuzzing practically possible. It is similar to
# FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to
# avoid interfering with fuzzing of projects that use {fmt}.
# See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode.
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore.
file (STRINGS ${gitignore} lines)
list(REMOVE_ITEM lines /doc/html)
foreach (line ${lines})
string(REPLACE "." "[.]" line "${line}")
string(REPLACE "*" ".*" line "${line}")
set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach ()
set(ignored_files ${ignored_files}
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
set(CPACK_SOURCE_GENERATOR ZIP)
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})
set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION})
set(CPACK_PACKAGE_NAME fmt)
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst)
include(CPack)
endif ()

View File

@ -0,0 +1,27 @@
Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

View File

@ -0,0 +1,232 @@
// Formatting library for C++ - dynamic format arguments
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#include "core.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};
template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value &&
!has_formatter<T, Context>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> named_info_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
detail::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg));
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) {
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
data_.insert(data_.begin(), {zero_ptr, 0});
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}
/** Erase all elements from the store */
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
};
FMT_END_NAMESPACE
#endif // FMT_ARGS_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,627 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
const text_style& rhs) {
return and_assign(rhs);
}
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
operator&(text_style lhs, const text_style& rhs) {
return lhs.and_assign(rhs);
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
// DEPRECATED!
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
/** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(false, background);
}
FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(L"\x1b[0m", stream);
}
template <typename Char>
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
detail::vformat_to(buf, format_str, args, {});
if (has_style) detail::reset_color<Char>(buf);
}
FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
\rst
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View File

@ -0,0 +1,639 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type =
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#ifdef __cpp_if_constexpr
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1};
}
};
#endif
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
}
};
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
return write<Char>(out, get_arg_checked<T, N>(args...));
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument with name.
template <typename Char> struct runtime_named_field {
using char_type = Char;
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
return true;
}
}
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
throw format_error("argument with specified name is not found");
}
return out;
}
};
template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
constexpr int manual_indexing_id = -1;
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int operator()() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) { throw format_error(message); }
};
template <typename Char> struct parse_arg_id_result {
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
} else if constexpr (c == ':') {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
}
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
return result;
}
}
#endif // __cpp_if_constexpr
} // namespace detail
FMT_MODULE_EXPORT_BEGIN
#ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
const auto& first = detail::first(args...);
if constexpr (detail::is_named_arg<
remove_cvref_t<decltype(first)>>::value) {
return fmt::to_string(first.value);
} else {
return fmt::to_string(first);
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return format(compiled, std::forward<Args>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format_to(out,
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return format_to(out, compiled, std::forward<Args>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
std::forward<Args>(args)...);
return {it.base(), it.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) {
return format_to(detail::counting_iterator(), format_str, args...).count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer;
format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals {
template <detail_exported::fixed_string Str>
constexpr detail::udl_compiled_string<
remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
operator""_cf() {
return {};
}
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
#include "xchar.h"
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead

View File

@ -0,0 +1,515 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include <cerrno>
#include <clocale> // locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT;
FMT_END_DETAIL_NAMESPACE
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args);
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
return vwindows_error(error_code, message, fmt::make_format_args(args...));
}
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
const char* message) FMT_NOEXCEPT;
#else
inline const std::error_category& system_category() FMT_NOEXCEPT {
return std::system_category();
}
#endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
FMT_BEGIN_DETAIL_NAMESPACE
struct buffer_size {
buffer_size() = default;
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
oflag = new_oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
FMT_END_DETAIL_NAMESPACE
constexpr detail::buffer_size buffer_size;
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
private:
file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
/**
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
}
};
/**
\rst
Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@ -0,0 +1,181 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Write the content of buf to os.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace detail
FMT_MODULE_EXPORT
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_MODULE_EXPORT
template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View File

@ -0,0 +1,652 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_format_args<basic_printf_context> args_;
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
// The ``printf`` argument formatter.
template <typename OutputIt, typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_;
OutputIt write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = 0;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs;
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
}
return base::operator()(value);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) {
return base::operator()(value);
}
/** Formats a null-terminated C string. */
OutputIt operator()(const char* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != 'p');
}
/** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != 'p');
}
OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value);
}
/** Formats a pointer. */
OutputIt operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx =
basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_);
return this->out;
}
};
template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ':
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end,
basic_format_specs<Char>& specs, GetArg get_arg) {
int arg_index = -1;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
int value = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill[0] = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big"));
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<Char>(specs), get_arg(-1)));
}
}
return arg_index;
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using OutputIt = buffer_appender<Char>;
auto out = OutputIt(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!detail::find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
break;
}
Char c = *it++;
if (it != end && *it == c) {
out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it;
continue;
}
out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs;
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
} else {
specs.precision = 0;
}
}
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (specs.type) {
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
break;
}
}
start = it;
// Format argument.
out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
}
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
FMT_END_DETAIL_NAMESPACE
template <typename Char>
using basic_printf_context_t =
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline auto vsprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vfprintf(
std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(fmt),
fmt::make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
return vfprintf(stdout, to_string_view(fmt), args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
inline auto printf(const S& fmt, const T&... args) -> int {
return vprintf(
to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
}
template <typename S, typename Char = char_t<S>>
FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
template <typename S, typename... T, typename Char = char_t<S>>
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
const T&... args) -> int {
return vfprintf(os, to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View File

@ -0,0 +1,468 @@
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
template <typename Char, typename Enable = void> struct formatting_range {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = '[';
Char postfix = ']';
#endif
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void> struct formatting_tuple {
Char prefix = '(';
Char postfix = ')';
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
template <typename OutputIterator>
OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overload
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
// ADL overload. Only participates in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T, void_t<decltype(detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_begin(
std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())),
decltype(detail::range_begin(std::declval<T>())),
enable_if_t<std::is_copy_constructible<T>::value>>>
: std::true_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
template <typename T, typename Enable = void> struct range_to_view;
template <typename T>
struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
struct view_t {
const T* m_range_ptr;
auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
};
static auto view(const T& range) -> view_t { return {&range}; }
};
template <typename T>
struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
has_mutable_begin_end<T>::value>> {
struct view_t {
T m_range_copy;
auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
};
static auto view(const T& range) -> view_t { return {range}; }
};
# undef FMT_DECLTYPE_RETURN
#endif
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ',';
*out++ = ' ';
return out;
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
*out++ = '"';
out = write<Char>(out, v);
*out++ = '"';
return out;
}
template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\'';
*out++ = v;
*out++ = '\'';
return out;
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
!std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
}
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, v);
++i;
}
formatting_tuple<Char>& formatting;
size_t& i;
typename std::add_lvalue_reference<
decltype(std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
size_t i = 0;
detail::copy(formatting.prefix, out);
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<
fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&& (has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto view = detail::range_to_view<T>::view(values);
auto it = view.begin();
auto end = view.end();
for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it);
++i;
}
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>;
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
typename FormatContext::iterator {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
detail::index_sequence<N...>) ->
typename FormatContext::iterator {
using std::get;
return format_args(value, ctx, get<N>(value.tuple)...);
}
template <typename FormatContext>
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
typename FormatContext::iterator {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) ->
typename FormatContext::iterator {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = base().format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
FMT_MODULE_EXPORT_BEGIN
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@ -0,0 +1,236 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_WCHAR_H_
#define FMT_WCHAR_H_
#include <cwchar>
#include <tuple>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
}
inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
#endif
} // namespace literals
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range>
auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat(to_string_view(format_str), vargs);
}
template <typename Locale, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(
const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, to_string_view(format_str), args);
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to(out, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
return detail::buffer_appender<Char>(buf);
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <
typename OutputIt, typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to_n(out, n, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
detail::vformat_to(buf, to_string_view(fmt), vargs);
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
inline void vprint(wstring_view fmt, wformat_args args) {
vprint(stdout, fmt, args);
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), make_wformat_args(args...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_WCHAR_H_

View File

@ -0,0 +1,100 @@
module;
#ifndef __cpp_modules
# error Module not supported.
#endif
// put all implementation-provided headers into the global module fragment
// to prevent attachment to this module
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
#endif
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <chrono>
#include <climits>
#include <clocale>
#include <cmath>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwchar>
#include <exception>
#include <functional>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <type_traits>
#include <utility>
#include <vector>
#if _MSC_VER
# include <intrin.h>
#endif
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h>
#endif
#if __has_include(<winapifamily.h>)
# include <winapifamily.h>
#endif
#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# include <io.h>
# endif
#endif
#ifdef _WIN32
# include <windows.h>
#endif
export module fmt;
#define FMT_MODULE_EXPORT export
#define FMT_MODULE_EXPORT_BEGIN export {
#define FMT_MODULE_EXPORT_END }
#define FMT_BEGIN_DETAIL_NAMESPACE \
} \
namespace detail {
#define FMT_END_DETAIL_NAMESPACE \
} \
export {
// all library-provided declarations and definitions
// must be in the module purview to be exported
#include "fmt/args.h"
#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/compile.h"
#include "fmt/format.h"
#include "fmt/os.h"
#include "fmt/printf.h"
#include "fmt/xchar.h"
// gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION
module : private;
#endif
#include "format.cc"
#include "os.cc"

View File

@ -0,0 +1,78 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT;
} // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API auto detail::thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API auto detail::thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template struct detail::basic_data<void>;
FMT_END_NAMESPACE

360
contrib/fmt-8.0.1/src/os.cc Normal file
View File

@ -0,0 +1,360 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/os.h"
#include <climits>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
#endif
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
using rwresult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
}
namespace detail {
class system_message {
system_message(const system_message&) = delete;
void operator=(const system_message&) = delete;
unsigned long result_;
wchar_t* message_;
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT {
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
}
public:
explicit system_message(unsigned long error_code)
: result_(0), message_(nullptr) {
result_ = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
if (result_ != 0) {
while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
--result_;
}
}
}
~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
return basic_string_view<wchar_t>(message_, result_);
}
};
class utf8_system_category final : public std::error_category {
public:
const char* name() const FMT_NOEXCEPT override { return "system"; }
std::string message(int error_code) const override {
system_message msg(error_code);
if (msg) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
return utf8_message.str();
}
}
return "unknown error";
}
};
} // namespace detail
FMT_API const std::error_category& system_category() FMT_NOEXCEPT {
static const detail::utf8_system_category category;
return category;
}
std::system_error vwindows_error(int err_code, string_view format_str,
format_args args) {
auto ec = std::error_code(err_code, system_category());
return std::system_error(ec, vformat(format_str, args));
}
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT {
FMT_TRY {
system_message msg(error_code);
if (msg) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
return;
}
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
# if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
# else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
# endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void file::close() {
if (fd_ == -1) return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
# else
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
# endif
}
std::size_t file::read(void* buffer, std::size_t count) {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return detail::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return detail::to_unsigned(result);
}
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return file(new_fd);
}
void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = std::error_code(errno, std::generic_category());
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
# else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
# endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long getpagesize() {
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
# else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
# endif
}
FMT_API void ostream::grow(size_t) {
if (this->size() == this->capacity()) flush();
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@ -0,0 +1,7 @@
# A CMake script to find SetEnv.cmd.
find_program(WINSDK_SETENV NAMES SetEnv.cmd
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin")
if (WINSDK_SETENV AND PRINT_PATH)
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}")
endif ()

View File

@ -0,0 +1,26 @@
# This module provides function for joining paths
# known from from most languages
#
# Original license:
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Explicit permission given to distribute this module under
# the terms of the project as described in /LICENSE.rst.
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

View File

@ -0,0 +1,70 @@
# C++14 feature support detection
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
if (CMAKE_CXX_STANDARD EQUAL 20)
check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
if (has_std_20_flag)
set(CXX_STANDARD_FLAG -std=c++20)
elseif (has_std_2a_flag)
set(CXX_STANDARD_FLAG -std=c++2a)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 17)
check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
if (has_std_17_flag)
set(CXX_STANDARD_FLAG -std=c++17)
elseif (has_std_1z_flag)
set(CXX_STANDARD_FLAG -std=c++1z)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 14)
check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
if (has_std_14_flag)
set(CXX_STANDARD_FLAG -std=c++14)
elseif (has_std_1y_flag)
set(CXX_STANDARD_FLAG -std=c++1y)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 11)
check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
if (has_std_11_flag)
set(CXX_STANDARD_FLAG -std=c++11)
elseif (has_std_0x_flag)
set(CXX_STANDARD_FLAG -std=c++0x)
endif ()
endif ()
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif ()
# Check if <variant> is available
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
check_cxx_source_compiles("
#include <variant>
int main() {}"
FMT_HAS_VARIANT)
if (NOT FMT_HAS_VARIANT)
set (FMT_HAS_VARIANT OFF)
endif ()
set(CMAKE_REQUIRED_FLAGS )

View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
check_required_components(fmt)

View File

@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@libdir_for_pc_file@
includedir=@includedir_for_pc_file@
Name: fmt
Description: A modern formatting library
Version: @FMT_VERSION@
Libs: -L${libdir} -l@FMT_LIB_NAME@
Cflags: -I${includedir}

View File

@ -53,7 +53,7 @@ copyright = u'2013-2021 Apple, Inc and the FoundationDB project authors'
# Load the version information from 'versions.target'
import xml.etree.ElementTree as ET
version_path = os.path.join(os.path.dirname(__file__), '..', '..', 'versions.target')
version_path = os.path.join(os.path.dirname(sys.executable), '..', '..', '..', 'versions.target')
tree = ET.parse(version_path)
root = tree.getroot()

View File

@ -700,7 +700,7 @@
"ssd",
"ssd-1",
"ssd-2",
"ssd-redwood-experimental",
"ssd-redwood-1-experimental",
"ssd-rocksdb-experimental",
"memory",
"memory-1",
@ -713,7 +713,7 @@
"ssd",
"ssd-1",
"ssd-2",
"ssd-redwood-experimental",
"ssd-redwood-1-experimental",
"ssd-rocksdb-experimental",
"memory",
"memory-1",

View File

@ -31,7 +31,7 @@ Because TSS recruitment only pairs *new* storage processes, you must add process
Example commands
----------------
Set the desired TSS processes count to 4, using the redwood storage engine: ``configure tss ssd-redwood-experimental count=4``.
Set the desired TSS processes count to 4, using the redwood storage engine: ``configure tss ssd-redwood-1-experimental count=4``.
Change the desired TSS process count to 2: ``configure tss count=2``.

View File

@ -18,6 +18,7 @@
* limitations under the License.
*/
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbbackup/BackupTLSConfig.h"
#include "fdbclient/JsonBuilder.h"
#include "flow/Arena.h"
@ -2309,7 +2310,7 @@ ACTOR Future<Void> runRestore(Database db,
origDb = Database::createDatabase(originalClusterFile, Database::API_VERSION_LATEST);
Version v = wait(timeKeeperVersionFromDatetime(targetTimestamp, origDb.get()));
printf("Timestamp '%s' resolves to version %" PRId64 "\n", targetTimestamp.c_str(), v);
fmt::print("Timestamp '{0}' resolves to version {1}\n", targetTimestamp, v);
targetVersion = v;
}
@ -2336,8 +2337,9 @@ ACTOR Future<Void> runRestore(Database db,
throw restore_error();
}
if (verbose)
printf("Using target restore version %" PRId64 "\n", targetVersion);
if (verbose) {
fmt::print("Ussing target restore version {}\n", targetVersion);
}
}
if (performRestore) {
@ -2359,19 +2361,19 @@ ACTOR Future<Void> runRestore(Database db,
if (waitForDone && verbose) {
// If restore is now complete then report version restored
printf("Restored to version %" PRId64 "\n", restoredVersion);
fmt::print("Restored to version {}\n", restoredVersion);
}
} else {
state Optional<RestorableFileSet> rset = wait(bc->getRestoreSet(targetVersion, ranges));
if (!rset.present()) {
fprintf(stderr,
"Insufficient data to restore to version %" PRId64 ". Describe backup for more information.\n",
targetVersion);
fmt::print(stderr,
"Insufficient data to restore to version {}. Describe backup for more information.\n",
targetVersion);
throw restore_invalid_version();
}
printf("Backup can be used to restore to version %" PRId64 "\n", targetVersion);
fmt::print("Backup can be used to restore to version {}\n", targetVersion);
}
} catch (Error& e) {
@ -2472,20 +2474,20 @@ ACTOR Future<Void> runFastRestoreTool(Database db,
state Optional<RestorableFileSet> rset = wait(bc->getRestoreSet(restoreVersion));
if (!rset.present()) {
fprintf(stderr, "Insufficient data to restore to version %" PRId64 "\n", restoreVersion);
fmt::print(stderr, "Insufficient data to restore to version {}\n", restoreVersion);
throw restore_invalid_version();
}
// Display the restore information, if requested
if (verbose) {
printf("[DRY RUN] Restoring backup to version: %" PRId64 "\n", restoreVersion);
printf("%s\n", description.toString().c_str());
fmt::print("[DRY RUN] Restoring backup to version: {}\n", restoreVersion);
fmt::print("{}\n", description.toString());
}
}
if (waitForDone && verbose) {
// If restore completed then report version restored
printf("Restored to version %" PRId64 "%s\n", restoreVersion, (performRestore) ? "" : " (DRY RUN)");
fmt::print("Restored to version {0}{1}\n", restoreVersion, (performRestore) ? "" : " (DRY RUN)");
}
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled)
@ -2520,7 +2522,7 @@ ACTOR Future<Void> dumpBackupData(const char* name,
}
}
printf("Scanning version range %" PRId64 " to %" PRId64 "\n", beginVersion, endVersion);
fmt::print("Scanning version range {0} to {1}\n", beginVersion, endVersion);
BackupFileList files = wait(c->dumpFileList(beginVersion, endVersion));
files.toStream(stdout);
@ -2579,12 +2581,12 @@ ACTOR Future<Void> expireBackupData(const char* name,
printf("\r%s%s\n", p.c_str(), (spaces > 0 ? std::string(spaces, ' ').c_str() : ""));
if (endVersion < 0)
printf("All data before %" PRId64 " versions (%" PRId64
" days) prior to latest backup log has been deleted.\n",
-endVersion,
-endVersion / ((int64_t)24 * 3600 * CLIENT_KNOBS->CORE_VERSIONSPERSECOND));
fmt::print("All data before {0} versions ({1}"
" days) prior to latest backup log has been deleted.\n",
-endVersion,
-endVersion / ((int64_t)24 * 3600 * CLIENT_KNOBS->CORE_VERSIONSPERSECOND));
else
printf("All data before version %" PRId64 " has been deleted.\n", endVersion);
fmt::print("All data before version {} has been deleted.\n", endVersion);
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled)
throw;
@ -3762,35 +3764,6 @@ int main(int argc, char* argv[]) {
}
}
IKnobCollection::setGlobalKnobCollection(IKnobCollection::Type::CLIENT, Randomize::False, IsSimulated::False);
auto& g_knobs = IKnobCollection::getMutableGlobalKnobCollection();
for (const auto& [knobName, knobValueString] : knobs) {
try {
auto knobValue = g_knobs.parseKnobValue(knobName, knobValueString);
g_knobs.setKnob(knobName, knobValue);
} catch (Error& e) {
if (e.code() == error_code_invalid_option_value) {
fprintf(stderr,
"WARNING: Invalid value '%s' for knob option '%s'\n",
knobValueString.c_str(),
knobName.c_str());
TraceEvent(SevWarnAlways, "InvalidKnobValue")
.detail("Knob", printable(knobName))
.detail("Value", printable(knobValueString));
} else {
fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", knobName.c_str(), e.what());
TraceEvent(SevError, "FailedToSetKnob")
.detail("Knob", printable(knobName))
.detail("Value", printable(knobValueString))
.error(e);
throw;
}
}
}
// Reinitialize knobs in order to update knobs that are dependent on explicitly set knobs
g_knobs.initialize(Randomize::False, IsSimulated::False);
if (trace) {
if (!traceLogGroup.empty())
setNetworkOption(FDBNetworkOptions::TRACE_LOG_GROUP, StringRef(traceLogGroup));
@ -3831,6 +3804,34 @@ int main(int argc, char* argv[]) {
return FDB_EXIT_ERROR;
}
auto& g_knobs = IKnobCollection::getMutableGlobalKnobCollection();
for (const auto& [knobName, knobValueString] : knobs) {
try {
auto knobValue = g_knobs.parseKnobValue(knobName, knobValueString);
g_knobs.setKnob(knobName, knobValue);
} catch (Error& e) {
if (e.code() == error_code_invalid_option_value) {
fprintf(stderr,
"WARNING: Invalid value '%s' for knob option '%s'\n",
knobValueString.c_str(),
knobName.c_str());
TraceEvent(SevWarnAlways, "InvalidKnobValue")
.detail("Knob", printable(knobName))
.detail("Value", printable(knobValueString));
} else {
fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", knobName.c_str(), e.what());
TraceEvent(SevError, "FailedToSetKnob")
.detail("Knob", printable(knobName))
.detail("Value", printable(knobValueString))
.error(e);
throw;
}
}
}
// Reinitialize knobs in order to update knobs that are dependent on explicitly set knobs
g_knobs.initialize(Randomize::False, IsSimulated::False);
TraceEvent("ProgramStart")
.setMaxEventLength(12000)
.detail("SourceVersion", getSourceVersion())

View File

@ -18,6 +18,8 @@
* limitations under the License.
*/
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbcli/fdbcli.actor.h"
#include "fdbclient/FDBOptions.g.h"
@ -62,6 +64,17 @@ ACTOR Future<Void> changeFeedList(Database db) {
namespace fdb_cli {
ACTOR Future<Void> requestVersionUpdate(Database localDb, Reference<ChangeFeedData> feedData) {
loop {
wait(delay(5.0));
Transaction tr(localDb);
state Version ver = wait(tr.getReadVersion());
fmt::print("Requesting version {}\n", ver);
wait(feedData->whenAtLeast(ver));
fmt::print("Feed at version {}\n", ver);
}
}
ACTOR Future<bool> changeFeedCommandActor(Database localDb, std::vector<StringRef> tokens, Future<Void> warn) {
if (tokens.size() == 1) {
printUsage(tokens[0]);
@ -109,7 +122,7 @@ ACTOR Future<bool> changeFeedCommandActor(Database localDb, std::vector<StringRe
}
if (tokens.size() > 4) {
int n = 0;
if (sscanf(tokens[4].toString().c_str(), "%ld%n", &end, &n) != 1 || n != tokens[4].size()) {
if (sscanf(tokens[4].toString().c_str(), "%" PRId64 "%n", &end, &n) != 1 || n != tokens[4].size()) {
printUsage(tokens[0]);
return false;
}
@ -117,24 +130,26 @@ ACTOR Future<bool> changeFeedCommandActor(Database localDb, std::vector<StringRe
if (warn.isValid()) {
warn.cancel();
}
state PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>> feedResults;
state Future<Void> feed = localDb->getChangeFeedStream(feedResults, tokens[2], begin, end);
state Reference<ChangeFeedData> feedData = makeReference<ChangeFeedData>();
state Future<Void> feed = localDb->getChangeFeedStream(feedData, tokens[2], begin, end);
state Future<Void> versionUpdates = requestVersionUpdate(localDb, feedData);
printf("\n");
try {
state Future<Void> feedInterrupt = LineNoise::onKeyboardInterrupt();
loop {
choose {
when(Standalone<VectorRef<MutationsAndVersionRef>> res = waitNext(feedResults.getFuture())) {
when(Standalone<VectorRef<MutationsAndVersionRef>> res =
waitNext(feedData->mutations.getFuture())) {
for (auto& it : res) {
for (auto& it2 : it.mutations) {
printf("%lld %s\n", it.version, it2.toString().c_str());
fmt::print("{0} {1}\n", it.version, it2.toString());
}
}
}
when(wait(feedInterrupt)) {
feedInterrupt = Future<Void>();
feed.cancel();
feedResults = PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>>();
feedData = makeReference<ChangeFeedData>();
break;
}
}

View File

@ -21,6 +21,7 @@
#include <cinttypes>
#include "boost/lexical_cast.hpp"
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbcli/fdbcli.actor.h"
@ -55,7 +56,7 @@ ACTOR Future<Void> printHealthyZone(Reference<IDatabase> db) {
} else {
std::string zoneId = res[0].key.removePrefix(fdb_cli::maintenanceSpecialKeyRange.begin).toString();
int64_t seconds = static_cast<int64_t>(boost::lexical_cast<double>(res[0].value.toString()));
printf("Maintenance for zone %s will continue for %" PRId64 " seconds.\n", zoneId.c_str(), seconds);
fmt::print("Maintenance for zone {0} will continue for {1} seconds.\n", zoneId, seconds);
}
return Void();
} catch (Error& e) {

View File

@ -18,6 +18,8 @@
* limitations under the License.
*/
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbcli/fdbcli.actor.h"
#include "fdbclient/FDBOptions.g.h"
@ -48,7 +50,7 @@ ACTOR Future<Void> printProcessClass(Reference<IDatabase> db) {
ASSERT(processSourceList.size() == processTypeList.size());
if (!processTypeList.size())
printf("No processes are registered in the database.\n");
printf("There are currently %zu processes in the database:\n", processTypeList.size());
fmt::print("There are currently {} processes in the database:\n", processTypeList.size());
for (int index = 0; index < processTypeList.size(); index++) {
std::string address =
processTypeList[index].key.removePrefix(fdb_cli::processClassTypeSpecialKeyRange.begin).toString();

View File

@ -19,6 +19,7 @@
*/
#include "boost/lexical_cast.hpp"
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbclient/ClusterConnectionFile.h"
#include "fdbclient/NativeAPI.actor.h"
#include "fdbclient/FDBTypes.h"
@ -631,9 +632,9 @@ ACTOR Future<Void> commitTransaction(Reference<ITransaction> tr) {
wait(makeInterruptable(safeThreadFutureToFuture(tr->commit())));
auto ver = tr->getCommittedVersion();
if (ver != invalidVersion)
printf("Committed (%" PRId64 ")\n", ver);
fmt::print("Committed ({})\n", ver);
else
printf("Nothing to commit\n");
fmt::print("Nothing to commit\n");
return Void();
}
@ -1402,7 +1403,9 @@ struct CLIOptions {
exit_code = FDB_EXIT_ERROR;
return;
}
}
void setupKnobs() {
auto& g_knobs = IKnobCollection::getMutableGlobalKnobCollection();
for (const auto& [knobName, knobValueString] : knobs) {
try {
@ -2432,8 +2435,6 @@ int main(int argc, char** argv) {
registerCrashHandler();
IKnobCollection::setGlobalKnobCollection(IKnobCollection::Type::CLIENT, Randomize::False, IsSimulated::False);
#ifdef __unixish__
struct sigaction act;
@ -2534,6 +2535,10 @@ int main(int argc, char** argv) {
try {
API->selectApiVersion(opt.api_version);
API->setupNetwork();
opt.setupKnobs();
if (opt.exit_code != -1) {
return opt.exit_code;
}
Future<int> cliFuture = runCli(opt);
Future<Void> timeoutFuture = opt.exit_timeout ? timeExit(opt.exit_timeout) : Never();
auto f = stopNetworkAfter(success(cliFuture) || timeoutFuture);
@ -2548,4 +2553,4 @@ int main(int argc, char** argv) {
fprintf(stderr, "ERROR: %s (%d)\n", e.what(), e.code());
return 1;
}
}
}

View File

@ -22,6 +22,7 @@
#include <ostream>
// FIXME: Trim this down
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "flow/Platform.actor.h"
#include "fdbclient/AsyncTaskThread.h"
#include "fdbclient/BackupContainer.h"
@ -75,13 +76,13 @@ std::string IBackupContainer::ExpireProgress::toString() const {
void BackupFileList::toStream(FILE* fout) const {
for (const RangeFile& f : ranges) {
fprintf(fout, "range %" PRId64 " %s\n", f.fileSize, f.fileName.c_str());
fmt::print(fout, "range {0} {1}\n", f.fileSize, f.fileName);
}
for (const LogFile& f : logs) {
fprintf(fout, "log %" PRId64 " %s\n", f.fileSize, f.fileName.c_str());
fmt::print(fout, "log {0} {1}\n", f.fileSize, f.fileName);
}
for (const KeyspaceSnapshotFile& f : snapshots) {
fprintf(fout, "snapshotManifest %" PRId64 " %s\n", f.totalSize, f.fileName.c_str());
fmt::print(fout, "snapshotManifest {0} {1}\n", f.totalSize, f.fileName);
}
}

View File

@ -1757,7 +1757,7 @@ ACTOR Future<Void> testBackupContainer(std::string url, Optional<std::string> en
state Version expireVersion = listing.snapshots[i].endVersion;
// Expire everything up to but not including the snapshot end version
printf("EXPIRE TO %" PRId64 "\n", expireVersion);
fmt::print("EXPIRE TO {}\n", expireVersion);
state Future<Void> f = c->expireData(expireVersion);
wait(ready(f));

View File

@ -22,6 +22,7 @@
#define FDBCLIENT_BACKUP_CONTAINER_FILESYSTEM_H
#pragma once
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbclient/BackupContainer.h"
#include "fdbclient/FDBTypes.h"
#include "flow/Trace.h"

View File

@ -18,6 +18,7 @@
* limitations under the License.
*/
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "flow/serialize.h"
#include "fdbclient/BlobGranuleFiles.h"
#include "fdbclient/SystemData.h" // for allKeys unit test - could remove
@ -70,10 +71,10 @@ static Arena loadSnapshotFile(const StringRef& snapshotData,
}
}*/
if (BG_READ_DEBUG) {
printf("Started with %d rows from snapshot after pruning to [%s - %s)\n",
dataMap.size(),
keyRange.begin.printable().c_str(),
keyRange.end.printable().c_str());
fmt::print("Started with {0} rows from snapshot after pruning to [{1} - {2})\n",
dataMap.size(),
keyRange.begin.printable(),
keyRange.end.printable());
}
return parseArena;
@ -150,14 +151,14 @@ static Arena loadDeltaFile(StringRef deltaData,
reader.deserialize(FileIdentifierFor<GranuleDeltas>::value, deltas, parseArena);
if (BG_READ_DEBUG) {
printf("Parsed %d deltas from file\n", deltas.size());
fmt::print("Parsed {}} deltas from file\n", deltas.size());
}
// TODO REMOVE sanity check
for (int i = 0; i < deltas.size() - 1; i++) {
if (deltas[i].version > deltas[i + 1].version) {
printf(
"BG VERSION ORDER VIOLATION IN DELTA FILE: '%lld', '%lld'\n", deltas[i].version, deltas[i + 1].version);
fmt::print(
"BG VERSION ORDER VIOLATION IN DELTA FILE: '{0}', '{1}'\n", deltas[i].version, deltas[i + 1].version);
}
ASSERT(deltas[i].version <= deltas[i + 1].version);
}
@ -191,14 +192,14 @@ RangeResult materializeBlobGranule(const BlobGranuleChunkRef& chunk,
}
if (BG_READ_DEBUG) {
printf("Applying %d delta files\n", chunk.deltaFiles.size());
fmt::print("Applying {} delta files\n", chunk.deltaFiles.size());
}
for (int deltaIdx = 0; deltaIdx < chunk.deltaFiles.size(); deltaIdx++) {
Arena deltaArena = loadDeltaFile(deltaFileData[deltaIdx], keyRange, readVersion, lastFileEndVersion, dataMap);
arena.dependsOn(deltaArena);
}
if (BG_READ_DEBUG) {
printf("Applying %d memory deltas\n", chunk.newDeltas.size());
fmt::print("Applying {} memory deltas\n", chunk.newDeltas.size());
}
applyDeltas(chunk.newDeltas, keyRange, readVersion, lastFileEndVersion, dataMap);

View File

@ -21,6 +21,7 @@
#include <map>
#include <vector>
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbclient/AsyncFileS3BlobStore.actor.h"
#include "fdbclient/BlobGranuleCommon.h"
#include "fdbclient/BlobGranuleFiles.h"

View File

@ -100,6 +100,7 @@ void ClientKnobs::initialize(Randomize randomize) {
init( RANGESTREAM_FRAGMENT_SIZE, 1e6 );
init( RANGESTREAM_BUFFERED_FRAGMENTS_LIMIT, 20 );
init( QUARANTINE_TSS_ON_MISMATCH, true ); if( randomize && BUGGIFY ) QUARANTINE_TSS_ON_MISMATCH = false; // if true, a tss mismatch will put the offending tss in quarantine. If false, it will just be killed
init( CHANGE_FEED_EMPTY_BATCH_TIME, 0.005 );
//KeyRangeMap
init( KRM_GET_RANGE_LIMIT, 1e5 ); if( randomize && BUGGIFY ) KRM_GET_RANGE_LIMIT = 10;

View File

@ -100,6 +100,7 @@ public:
int64_t RANGESTREAM_FRAGMENT_SIZE;
int RANGESTREAM_BUFFERED_FRAGMENTS_LIMIT;
bool QUARANTINE_TSS_ON_MISMATCH;
double CHANGE_FEED_EMPTY_BATCH_TIME;
// KeyRangeMap
int KRM_GET_RANGE_LIMIT;

View File

@ -31,8 +31,10 @@
// A ClientWorkerInterface is embedded as the first element of a WorkerInterface.
struct ClientWorkerInterface {
constexpr static FileIdentifier file_identifier = 12418152;
RequestStream<struct RebootRequest> reboot;
RequestStream<struct ProfilerRequest> profiler;
RequestStream<struct SetFailureInjection> setFailureInjection;
bool operator==(ClientWorkerInterface const& r) const { return id() == r.id(); }
bool operator!=(ClientWorkerInterface const& r) const { return id() != r.id(); }
@ -43,7 +45,7 @@ struct ClientWorkerInterface {
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, reboot, profiler);
serializer(ar, reboot, profiler, setFailureInjection);
}
};
@ -88,4 +90,39 @@ struct ProfilerRequest {
}
};
struct SetFailureInjection {
constexpr static FileIdentifier file_identifier = 15439864;
ReplyPromise<Void> reply;
struct DiskFailureCommand {
// how often should the disk be stalled (0 meaning once, 10 meaning every 10 secs)
double stallInterval;
// Period of time disk stalls will be injected for
double stallPeriod;
// Period of time the disk will be slowed down for
double throttlePeriod;
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, stallInterval, stallPeriod, throttlePeriod);
}
};
struct FlipBitsCommand {
// percent of bits to flip in the given file
double percentBitFlips;
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, percentBitFlips);
}
};
Optional<DiskFailureCommand> diskFailure;
Optional<FlipBitsCommand> flipBits;
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, reply, diskFailure, flipBits);
}
};
#endif

View File

@ -39,6 +39,7 @@ struct ClusterInterface {
RequestStream<struct ForceRecoveryRequest> forceRecovery;
RequestStream<struct MoveShardRequest> moveShard;
RequestStream<struct RepairSystemDataRequest> repairSystemData;
RequestStream<struct SplitShardRequest> splitShard;
bool operator==(ClusterInterface const& r) const { return id() == r.id(); }
bool operator!=(ClusterInterface const& r) const { return id() != r.id(); }
@ -49,7 +50,8 @@ struct ClusterInterface {
return openDatabase.getFuture().isReady() || failureMonitoring.getFuture().isReady() ||
databaseStatus.getFuture().isReady() || ping.getFuture().isReady() ||
getClientWorkers.getFuture().isReady() || forceRecovery.getFuture().isReady() ||
moveShard.getFuture().isReady() || repairSystemData.getFuture().isReady();
moveShard.getFuture().isReady() || repairSystemData.getFuture().isReady() ||
splitShard.getFuture().isReady();
}
void initEndpoints() {
@ -61,6 +63,7 @@ struct ClusterInterface {
forceRecovery.getEndpoint(TaskPriority::ClusterController);
moveShard.getEndpoint(TaskPriority::ClusterController);
repairSystemData.getEndpoint(TaskPriority::ClusterController);
splitShard.getEndpoint(TaskPriority::ClusterController);
}
template <class Ar>
@ -73,7 +76,8 @@ struct ClusterInterface {
getClientWorkers,
forceRecovery,
moveShard,
repairSystemData);
repairSystemData,
splitShard);
}
};
@ -288,4 +292,35 @@ struct RepairSystemDataRequest {
serializer(ar, reply);
}
};
// Returns the actual shards generated by the SplitShardRequest.
struct SplitShardReply {
constexpr static FileIdentifier file_identifier = 1384440;
std::vector<KeyRange> shards;
SplitShardReply() {}
explicit SplitShardReply(std::vector<KeyRange> shards) : shards{ std::move(shards) } {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, shards);
}
};
// Split keyrange [shard.begin, shard.end) into num shards.
// Split points are chosen as the arithmeticlly equal division points of the given range.
struct SplitShardRequest {
constexpr static FileIdentifier file_identifier = 1384443;
KeyRange shard;
int num;
ReplyPromise<SplitShardReply> reply;
SplitShardRequest() : num(0) {}
SplitShardRequest(KeyRange shard, int num) : shard{ std::move(shard) }, num(num) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, shard, num, reply);
}
};
#endif

View File

@ -20,6 +20,9 @@
#include "fdbclient/DatabaseConfiguration.h"
#include "fdbclient/SystemData.h"
#include "flow/ITrace.h"
#include "flow/Trace.h"
#include "flow/genericactors.actor.h"
DatabaseConfiguration::DatabaseConfiguration() {
resetInternal();
@ -49,6 +52,10 @@ void DatabaseConfiguration::resetInternal() {
storageMigrationType = StorageMigrationType::DEFAULT;
}
int toInt(ValueRef const& v) {
return atoi(v.toString().c_str());
}
void parse(int* i, ValueRef const& v) {
// FIXME: Sanity checking
*i = atoi(v.toString().c_str());
@ -288,7 +295,7 @@ StatusObject DatabaseConfiguration::toJSON(bool noPolicies) const {
result["storage_engine"] = "ssd-2";
} else if (tLogDataStoreType == KeyValueStoreType::SSD_BTREE_V2 &&
storageServerStoreType == KeyValueStoreType::SSD_REDWOOD_V1) {
result["storage_engine"] = "ssd-redwood-experimental";
result["storage_engine"] = "ssd-redwood-1-experimental";
} else if (tLogDataStoreType == KeyValueStoreType::SSD_BTREE_V2 &&
storageServerStoreType == KeyValueStoreType::SSD_ROCKSDB_V1) {
result["storage_engine"] = "ssd-rocksdb-experimental";
@ -311,7 +318,7 @@ StatusObject DatabaseConfiguration::toJSON(bool noPolicies) const {
} else if (testingStorageServerStoreType == KeyValueStoreType::SSD_BTREE_V2) {
result["tss_storage_engine"] = "ssd-2";
} else if (testingStorageServerStoreType == KeyValueStoreType::SSD_REDWOOD_V1) {
result["tss_storage_engine"] = "ssd-redwood-experimental";
result["tss_storage_engine"] = "ssd-redwood-1-experimental";
} else if (testingStorageServerStoreType == KeyValueStoreType::SSD_ROCKSDB_V1) {
result["tss_storage_engine"] = "ssd-rocksdb-experimental";
} else if (testingStorageServerStoreType == KeyValueStoreType::MEMORY_RADIXTREE) {
@ -466,6 +473,64 @@ std::string DatabaseConfiguration::toString() const {
return json_spirit::write_string(json_spirit::mValue(toJSON()), json_spirit::Output_options::none);
}
Key getKeyWithPrefix(std::string const& k) {
return StringRef(k).withPrefix(configKeysPrefix);
}
void DatabaseConfiguration::overwriteProxiesCount() {
Key commitProxiesKey = getKeyWithPrefix("commit_proxies");
Key grvProxiesKey = getKeyWithPrefix("grv_proxies");
Key proxiesKey = getKeyWithPrefix("proxies");
Optional<ValueRef> optCommitProxies = DatabaseConfiguration::get(commitProxiesKey);
Optional<ValueRef> optGrvProxies = DatabaseConfiguration::get(grvProxiesKey);
Optional<ValueRef> optProxies = DatabaseConfiguration::get(proxiesKey);
const int mutableGrvProxyCount = optGrvProxies.present() ? toInt(optGrvProxies.get()) : 0;
const int mutableCommitProxyCount = optCommitProxies.present() ? toInt(optCommitProxies.get()) : 0;
const int mutableProxiesCount = optProxies.present() ? toInt(optProxies.get()) : 0;
if (mutableProxiesCount > 1) {
TraceEvent(SevDebug, "OverwriteProxiesCount")
.detail("CPCount", commitProxyCount)
.detail("MutableCPCount", mutableCommitProxyCount)
.detail("GrvCount", grvProxyCount)
.detail("MutableGrvCPCount", mutableGrvProxyCount)
.detail("MutableProxiesCount", mutableProxiesCount);
if (grvProxyCount == -1 && commitProxyCount > 0) {
if (mutableProxiesCount > commitProxyCount) {
grvProxyCount = mutableProxiesCount - commitProxyCount;
} else {
// invalid configuration; provision min GrvProxies
grvProxyCount = 1;
commitProxyCount = mutableProxiesCount - 1;
}
} else if (grvProxyCount > 0 && commitProxyCount == -1) {
if (mutableProxiesCount > grvProxyCount) {
commitProxyCount = mutableProxiesCount - grvProxyCount;
} else {
// invalid configuration; provision min CommitProxies
commitProxyCount = 1;
grvProxyCount = mutableProxiesCount - 1;
}
} else if (grvProxyCount == -1 && commitProxyCount == -1) {
// Use DEFAULT_COMMIT_GRV_PROXIES_RATIO to split proxies between Grv & Commit proxies
const int derivedGrvProxyCount =
std::max(1,
std::min(CLIENT_KNOBS->DEFAULT_MAX_GRV_PROXIES,
mutableProxiesCount / (CLIENT_KNOBS->DEFAULT_COMMIT_GRV_PROXIES_RATIO + 1)));
grvProxyCount = derivedGrvProxyCount;
commitProxyCount = mutableProxiesCount - grvProxyCount;
}
TraceEvent(SevDebug, "OverwriteProxiesCountResult")
.detail("CommitProxyCount", commitProxyCount)
.detail("GrvProxyCount", grvProxyCount)
.detail("ProxyCount", mutableProxiesCount);
}
}
bool DatabaseConfiguration::setInternal(KeyRef key, ValueRef value) {
KeyRef ck = key.removePrefix(configKeysPrefix);
int type;
@ -473,9 +538,13 @@ bool DatabaseConfiguration::setInternal(KeyRef key, ValueRef value) {
if (ck == LiteralStringRef("initialized")) {
initialized = true;
} else if (ck == LiteralStringRef("commit_proxies")) {
parse(&commitProxyCount, value);
commitProxyCount = toInt(value);
if (commitProxyCount == -1)
overwriteProxiesCount();
} else if (ck == LiteralStringRef("grv_proxies")) {
parse(&grvProxyCount, value);
grvProxyCount = toInt(value);
if (grvProxyCount == -1)
overwriteProxiesCount();
} else if (ck == LiteralStringRef("resolvers")) {
parse(&resolverCount, value);
} else if (ck == LiteralStringRef("logs")) {
@ -557,21 +626,7 @@ bool DatabaseConfiguration::setInternal(KeyRef key, ValueRef value) {
parse((&type), value);
storageMigrationType = (StorageMigrationType::MigrationType)type;
} else if (ck == LiteralStringRef("proxies")) {
int proxiesCount;
parse(&proxiesCount, value);
if (proxiesCount > 1) {
int derivedGrvProxyCount =
std::max(1,
std::min(CLIENT_KNOBS->DEFAULT_MAX_GRV_PROXIES,
proxiesCount / (CLIENT_KNOBS->DEFAULT_COMMIT_GRV_PROXIES_RATIO + 1)));
int derivedCommitProxyCount = proxiesCount - derivedGrvProxyCount;
if (grvProxyCount == -1) {
grvProxyCount = derivedGrvProxyCount;
}
if (commitProxyCount == -1) {
commitProxyCount = derivedCommitProxyCount;
}
}
overwriteProxiesCount();
} else {
return false;
}

View File

@ -333,6 +333,8 @@ private:
/// Check if the key is overridden by either mutableConfiguration or rawConfiguration
bool isOverridden(std::string key) const;
// Overwrite commitProxyCount and/or grvProxyCount if set to -1
void overwriteProxiesCount();
};
#endif

View File

@ -20,6 +20,7 @@
#ifndef DatabaseContext_h
#define DatabaseContext_h
#include "fdbclient/Notified.h"
#include "flow/FastAlloc.h"
#include "flow/FastRef.h"
#include "fdbclient/StorageServerInterface.h"
@ -146,6 +147,37 @@ public:
WatchMetadata(Key key, Optional<Value> value, Version version, TransactionInfo info, TagSet tags);
};
struct MutationAndVersionStream {
Standalone<MutationsAndVersionRef> next;
PromiseStream<Standalone<MutationsAndVersionRef>> results;
bool operator<(MutationAndVersionStream const& rhs) const { return next.version > rhs.next.version; }
};
struct ChangeFeedStorageData : ReferenceCounted<ChangeFeedStorageData> {
UID id;
Future<Void> updater;
NotifiedVersion version;
NotifiedVersion desired;
Promise<Void> destroyed;
~ChangeFeedStorageData() { destroyed.send(Void()); }
};
struct ChangeFeedData : ReferenceCounted<ChangeFeedData> {
PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>> mutations;
std::vector<ReplyPromiseStream<ChangeFeedStreamReply>> streams;
Version getVersion();
Future<Void> whenAtLeast(Version version);
NotifiedVersion lastReturnedVersion;
std::vector<Reference<ChangeFeedStorageData>> storageData;
AsyncVar<int> notAtLatest;
Promise<Void> refresh;
ChangeFeedData() : notAtLatest(1) {}
};
class DatabaseContext : public ReferenceCounted<DatabaseContext>, public FastAllocated<DatabaseContext>, NonCopyable {
public:
static DatabaseContext* allocateOnForeignThread() {
@ -253,7 +285,7 @@ public:
// Management API, create snapshot
Future<Void> createSnapshot(StringRef uid, StringRef snapshot_command);
Future<Void> getChangeFeedStream(const PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>>& results,
Future<Void> getChangeFeedStream(Reference<ChangeFeedData> results,
Key rangeID,
Version begin = 0,
Version end = std::numeric_limits<Version>::max(),
@ -341,6 +373,9 @@ public:
std::unordered_map<UID, Reference<TSSMetrics>> tssMetrics;
// map from changeFeedId -> changeFeedRange
std::unordered_map<Key, KeyRange> changeFeedCache;
std::unordered_map<UID, Reference<ChangeFeedStorageData>> changeFeedUpdaters;
Reference<ChangeFeedStorageData> getStorageData(StorageServerInterface interf);
UID dbId;
IsInternal internal; // Only contexts created through the C client and fdbcli are non-internal
@ -460,6 +495,8 @@ public:
using TransactionT = ReadYourWritesTransaction;
Reference<TransactionT> createTransaction();
EventCacheHolder connectToDatabaseEventCacheHolder;
private:
std::unordered_map<KeyRef, Reference<WatchMetadata>> watchMap;
};

View File

@ -706,7 +706,7 @@ struct KeyValueStoreType {
case SSD_BTREE_V2:
return "ssd-2";
case SSD_REDWOOD_V1:
return "ssd-redwood-experimental";
return "ssd-redwood-1-experimental";
case SSD_ROCKSDB_V1:
return "ssd-rocksdb-experimental";
case MEMORY:

View File

@ -18,6 +18,7 @@
* limitations under the License.
*/
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbclient/BackupAgent.actor.h"
#include "fdbclient/BackupContainer.h"
#include "fdbclient/DatabaseContext.h"
@ -5342,10 +5343,7 @@ public:
.detail("BackupContainer", bc->getURL())
.detail("BeginVersion", beginVersion)
.detail("TargetVersion", targetVersion);
fprintf(stderr,
"ERROR: Restore version %" PRId64 " is not possible from %s\n",
targetVersion,
bc->getURL().c_str());
fmt::print(stderr, "ERROR: Restore version {0} is not possible from {1}\n", targetVersion, bc->getURL());
throw restore_invalid_version();
}

View File

@ -73,20 +73,26 @@ KnobValue IKnobCollection::parseKnobValue(std::string const& knobName, std::stri
UNSTOPPABLE_ASSERT(false);
}
std::unique_ptr<IKnobCollection> IKnobCollection::globalKnobCollection =
IKnobCollection::create(IKnobCollection::Type::CLIENT, Randomize::False, IsSimulated::False);
std::unique_ptr<IKnobCollection>& IKnobCollection::globalKnobCollection() {
static std::unique_ptr<IKnobCollection> res;
if (!res) {
res = IKnobCollection::create(IKnobCollection::Type::CLIENT, Randomize::False, IsSimulated::False);
}
return res;
}
void IKnobCollection::setGlobalKnobCollection(Type type, Randomize randomize, IsSimulated isSimulated) {
globalKnobCollection = create(type, randomize, isSimulated);
FLOW_KNOBS = &globalKnobCollection->getFlowKnobs();
globalKnobCollection() = create(type, randomize, isSimulated);
ASSERT(FLOW_KNOBS == &bootstrapGlobalFlowKnobs);
FLOW_KNOBS = &globalKnobCollection()->getFlowKnobs();
}
IKnobCollection const& IKnobCollection::getGlobalKnobCollection() {
return *globalKnobCollection;
return *globalKnobCollection();
}
IKnobCollection& IKnobCollection::getMutableGlobalKnobCollection() {
return *globalKnobCollection;
return *globalKnobCollection();
}
ConfigMutationRef IKnobCollection::createSetMutation(Arena arena, KeyRef key, ValueRef value) {

View File

@ -36,7 +36,7 @@
* - TestKnobs
*/
class IKnobCollection {
static std::unique_ptr<IKnobCollection> globalKnobCollection;
static std::unique_ptr<IKnobCollection>& globalKnobCollection();
public:
virtual ~IKnobCollection() = default;

View File

@ -22,6 +22,7 @@
#include <string>
#include <vector>
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbclient/Knobs.h"
#include "flow/Arena.h"
#include "fdbclient/ClusterConnectionMemoryRecord.h"
@ -184,7 +185,7 @@ std::map<std::string, std::string> configForToken(std::string const& mode) {
} else if (mode == "ssd" || mode == "ssd-2") {
logType = KeyValueStoreType::SSD_BTREE_V2;
storeType = KeyValueStoreType::SSD_BTREE_V2;
} else if (mode == "ssd-redwood-experimental") {
} else if (mode == "ssd-redwood-1-experimental") {
logType = KeyValueStoreType::SSD_BTREE_V2;
storeType = KeyValueStoreType::SSD_REDWOOD_V1;
} else if (mode == "ssd-rocksdb-experimental") {
@ -1677,9 +1678,9 @@ ACTOR Future<Void> printHealthyZone(Database cx) {
printf("No ongoing maintenance.\n");
} else {
auto healthyZone = decodeHealthyZoneValue(val.get());
printf("Maintenance for zone %s will continue for %" PRId64 " seconds.\n",
healthyZone.first.toString().c_str(),
(healthyZone.second - tr.getReadVersion().get()) / CLIENT_KNOBS->CORE_VERSIONSPERSECOND);
fmt::print("Maintenance for zone {0} will continue for {1} seconds.\n",
healthyZone.first.toString(),
(healthyZone.second - tr.getReadVersion().get()) / CLIENT_KNOBS->CORE_VERSIONSPERSECOND);
}
return Void();
} catch (Error& e) {

View File

@ -481,7 +481,7 @@ ThreadFuture<ProtocolVersion> DLDatabase::getServerProtocol(Optional<ProtocolVer
// requireFunction - Determines the behavior if the function is not present. If true, an error is thrown. If false,
// the function pointer will be set to nullptr.
template <class T>
void loadClientFunction(T* fp, void* lib, std::string libPath, const char* functionName, bool requireFunction = true) {
void loadClientFunction(T* fp, void* lib, std::string libPath, const char* functionName, bool requireFunction) {
*(void**)(fp) = loadFunction(lib, functionName);
if (*fp == nullptr && requireFunction) {
TraceEvent(SevError, "ErrorLoadingFunction").detail("LibraryPath", libPath).detail("Function", functionName);
@ -511,16 +511,17 @@ void DLApi::init() {
}
}
loadClientFunction(&api->selectApiVersion, lib, fdbCPath, "fdb_select_api_version_impl");
loadClientFunction(&api->selectApiVersion, lib, fdbCPath, "fdb_select_api_version_impl", headerVersion >= 0);
loadClientFunction(&api->getClientVersion, lib, fdbCPath, "fdb_get_client_version", headerVersion >= 410);
loadClientFunction(&api->setNetworkOption, lib, fdbCPath, "fdb_network_set_option");
loadClientFunction(&api->setupNetwork, lib, fdbCPath, "fdb_setup_network");
loadClientFunction(&api->runNetwork, lib, fdbCPath, "fdb_run_network");
loadClientFunction(&api->stopNetwork, lib, fdbCPath, "fdb_stop_network");
loadClientFunction(&api->setNetworkOption, lib, fdbCPath, "fdb_network_set_option", headerVersion >= 0);
loadClientFunction(&api->setupNetwork, lib, fdbCPath, "fdb_setup_network", headerVersion >= 0);
loadClientFunction(&api->runNetwork, lib, fdbCPath, "fdb_run_network", headerVersion >= 0);
loadClientFunction(&api->stopNetwork, lib, fdbCPath, "fdb_stop_network", headerVersion >= 0);
loadClientFunction(&api->createDatabase, lib, fdbCPath, "fdb_create_database", headerVersion >= 610);
loadClientFunction(&api->databaseCreateTransaction, lib, fdbCPath, "fdb_database_create_transaction");
loadClientFunction(&api->databaseSetOption, lib, fdbCPath, "fdb_database_set_option");
loadClientFunction(
&api->databaseCreateTransaction, lib, fdbCPath, "fdb_database_create_transaction", headerVersion >= 0);
loadClientFunction(&api->databaseSetOption, lib, fdbCPath, "fdb_database_set_option", headerVersion >= 0);
loadClientFunction(&api->databaseGetMainThreadBusyness,
lib,
fdbCPath,
@ -528,7 +529,7 @@ void DLApi::init() {
headerVersion >= 700);
loadClientFunction(
&api->databaseGetServerProtocol, lib, fdbCPath, "fdb_database_get_server_protocol", headerVersion >= 700);
loadClientFunction(&api->databaseDestroy, lib, fdbCPath, "fdb_database_destroy");
loadClientFunction(&api->databaseDestroy, lib, fdbCPath, "fdb_database_destroy", headerVersion >= 0);
loadClientFunction(&api->databaseRebootWorker, lib, fdbCPath, "fdb_database_reboot_worker", headerVersion >= 700);
loadClientFunction(&api->databaseForceRecoveryWithDataLoss,
lib,
@ -538,33 +539,48 @@ void DLApi::init() {
loadClientFunction(
&api->databaseCreateSnapshot, lib, fdbCPath, "fdb_database_create_snapshot", headerVersion >= 700);
loadClientFunction(&api->transactionSetOption, lib, fdbCPath, "fdb_transaction_set_option");
loadClientFunction(&api->transactionDestroy, lib, fdbCPath, "fdb_transaction_destroy");
loadClientFunction(&api->transactionSetReadVersion, lib, fdbCPath, "fdb_transaction_set_read_version");
loadClientFunction(&api->transactionGetReadVersion, lib, fdbCPath, "fdb_transaction_get_read_version");
loadClientFunction(&api->transactionGet, lib, fdbCPath, "fdb_transaction_get");
loadClientFunction(&api->transactionGetKey, lib, fdbCPath, "fdb_transaction_get_key");
loadClientFunction(&api->transactionGetAddressesForKey, lib, fdbCPath, "fdb_transaction_get_addresses_for_key");
loadClientFunction(&api->transactionGetRange, lib, fdbCPath, "fdb_transaction_get_range");
loadClientFunction(&api->transactionGetRangeAndFlatMap, lib, fdbCPath, "fdb_transaction_get_range_and_flat_map");
loadClientFunction(&api->transactionSetOption, lib, fdbCPath, "fdb_transaction_set_option", headerVersion >= 0);
loadClientFunction(&api->transactionDestroy, lib, fdbCPath, "fdb_transaction_destroy", headerVersion >= 0);
loadClientFunction(
&api->transactionSetReadVersion, lib, fdbCPath, "fdb_transaction_set_read_version", headerVersion >= 0);
loadClientFunction(
&api->transactionGetReadVersion, lib, fdbCPath, "fdb_transaction_get_read_version", headerVersion >= 0);
loadClientFunction(&api->transactionGet, lib, fdbCPath, "fdb_transaction_get", headerVersion >= 0);
loadClientFunction(&api->transactionGetKey, lib, fdbCPath, "fdb_transaction_get_key", headerVersion >= 0);
loadClientFunction(&api->transactionGetAddressesForKey,
lib,
fdbCPath,
"fdb_transaction_get_addresses_for_key",
headerVersion >= 0);
loadClientFunction(&api->transactionGetRange, lib, fdbCPath, "fdb_transaction_get_range", headerVersion >= 0);
loadClientFunction(&api->transactionGetRangeAndFlatMap,
lib,
fdbCPath,
"fdb_transaction_get_range_and_flat_map",
headerVersion >= 700);
loadClientFunction(
&api->transactionGetVersionstamp, lib, fdbCPath, "fdb_transaction_get_versionstamp", headerVersion >= 410);
loadClientFunction(&api->transactionSet, lib, fdbCPath, "fdb_transaction_set");
loadClientFunction(&api->transactionClear, lib, fdbCPath, "fdb_transaction_clear");
loadClientFunction(&api->transactionClearRange, lib, fdbCPath, "fdb_transaction_clear_range");
loadClientFunction(&api->transactionAtomicOp, lib, fdbCPath, "fdb_transaction_atomic_op");
loadClientFunction(&api->transactionCommit, lib, fdbCPath, "fdb_transaction_commit");
loadClientFunction(&api->transactionGetCommittedVersion, lib, fdbCPath, "fdb_transaction_get_committed_version");
loadClientFunction(&api->transactionSet, lib, fdbCPath, "fdb_transaction_set", headerVersion >= 0);
loadClientFunction(&api->transactionClear, lib, fdbCPath, "fdb_transaction_clear", headerVersion >= 0);
loadClientFunction(&api->transactionClearRange, lib, fdbCPath, "fdb_transaction_clear_range", headerVersion >= 0);
loadClientFunction(&api->transactionAtomicOp, lib, fdbCPath, "fdb_transaction_atomic_op", headerVersion >= 0);
loadClientFunction(&api->transactionCommit, lib, fdbCPath, "fdb_transaction_commit", headerVersion >= 0);
loadClientFunction(&api->transactionGetCommittedVersion,
lib,
fdbCPath,
"fdb_transaction_get_committed_version",
headerVersion >= 0);
loadClientFunction(&api->transactionGetApproximateSize,
lib,
fdbCPath,
"fdb_transaction_get_approximate_size",
headerVersion >= 620);
loadClientFunction(&api->transactionWatch, lib, fdbCPath, "fdb_transaction_watch");
loadClientFunction(&api->transactionOnError, lib, fdbCPath, "fdb_transaction_on_error");
loadClientFunction(&api->transactionReset, lib, fdbCPath, "fdb_transaction_reset");
loadClientFunction(&api->transactionCancel, lib, fdbCPath, "fdb_transaction_cancel");
loadClientFunction(&api->transactionAddConflictRange, lib, fdbCPath, "fdb_transaction_add_conflict_range");
loadClientFunction(&api->transactionWatch, lib, fdbCPath, "fdb_transaction_watch", headerVersion >= 0);
loadClientFunction(&api->transactionOnError, lib, fdbCPath, "fdb_transaction_on_error", headerVersion >= 0);
loadClientFunction(&api->transactionReset, lib, fdbCPath, "fdb_transaction_reset", headerVersion >= 0);
loadClientFunction(&api->transactionCancel, lib, fdbCPath, "fdb_transaction_cancel", headerVersion >= 0);
loadClientFunction(
&api->transactionAddConflictRange, lib, fdbCPath, "fdb_transaction_add_conflict_range", headerVersion >= 0);
loadClientFunction(&api->transactionGetEstimatedRangeSizeBytes,
lib,
fdbCPath,
@ -583,20 +599,24 @@ void DLApi::init() {
headerVersion >= 710);
loadClientFunction(
&api->transactionReadBlobGranules, lib, fdbCPath, "fdb_transaction_read_blob_granules", headerVersion >= 710);
loadClientFunction(
&api->futureGetInt64, lib, fdbCPath, headerVersion >= 620 ? "fdb_future_get_int64" : "fdb_future_get_version");
loadClientFunction(&api->futureGetInt64,
lib,
fdbCPath,
headerVersion >= 620 ? "fdb_future_get_int64" : "fdb_future_get_version",
headerVersion >= 0);
loadClientFunction(&api->futureGetUInt64, lib, fdbCPath, "fdb_future_get_uint64", headerVersion >= 700);
loadClientFunction(&api->futureGetError, lib, fdbCPath, "fdb_future_get_error");
loadClientFunction(&api->futureGetKey, lib, fdbCPath, "fdb_future_get_key");
loadClientFunction(&api->futureGetValue, lib, fdbCPath, "fdb_future_get_value");
loadClientFunction(&api->futureGetStringArray, lib, fdbCPath, "fdb_future_get_string_array");
loadClientFunction(&api->futureGetError, lib, fdbCPath, "fdb_future_get_error", headerVersion >= 0);
loadClientFunction(&api->futureGetKey, lib, fdbCPath, "fdb_future_get_key", headerVersion >= 0);
loadClientFunction(&api->futureGetValue, lib, fdbCPath, "fdb_future_get_value", headerVersion >= 0);
loadClientFunction(&api->futureGetStringArray, lib, fdbCPath, "fdb_future_get_string_array", headerVersion >= 0);
loadClientFunction(
&api->futureGetKeyRangeArray, lib, fdbCPath, "fdb_future_get_keyrange_array", headerVersion >= 710);
loadClientFunction(&api->futureGetKeyArray, lib, fdbCPath, "fdb_future_get_key_array", headerVersion >= 700);
loadClientFunction(&api->futureGetKeyValueArray, lib, fdbCPath, "fdb_future_get_keyvalue_array");
loadClientFunction(&api->futureSetCallback, lib, fdbCPath, "fdb_future_set_callback");
loadClientFunction(&api->futureCancel, lib, fdbCPath, "fdb_future_cancel");
loadClientFunction(&api->futureDestroy, lib, fdbCPath, "fdb_future_destroy");
loadClientFunction(
&api->futureGetKeyValueArray, lib, fdbCPath, "fdb_future_get_keyvalue_array", headerVersion >= 0);
loadClientFunction(&api->futureSetCallback, lib, fdbCPath, "fdb_future_set_callback", headerVersion >= 0);
loadClientFunction(&api->futureCancel, lib, fdbCPath, "fdb_future_cancel", headerVersion >= 0);
loadClientFunction(&api->futureDestroy, lib, fdbCPath, "fdb_future_destroy", headerVersion >= 0);
loadClientFunction(
&api->resultGetKeyValueArray, lib, fdbCPath, "fdb_result_get_keyvalue_array", headerVersion >= 710);
@ -1839,6 +1859,8 @@ void MultiVersionApi::setNetworkOption(FDBNetworkOptions::Option option, Optiona
}
void MultiVersionApi::setNetworkOptionInternal(FDBNetworkOptions::Option option, Optional<StringRef> value) {
bool forwardOption = false;
auto itr = FDBNetworkOptions::optionInfo.find(option);
if (itr != FDBNetworkOptions::optionInfo.end()) {
TraceEvent("SetNetworkOption").detail("Option", itr->second.name);
@ -1870,20 +1892,24 @@ void MultiVersionApi::setNetworkOptionInternal(FDBNetworkOptions::Option option,
ASSERT(!value.present() && !networkStartSetup);
externalClient = true;
bypassMultiClientApi = true;
forwardOption = true;
} else if (option == FDBNetworkOptions::CLIENT_THREADS_PER_VERSION) {
MutexHolder holder(lock);
validateOption(value, true, false, false);
ASSERT(!networkStartSetup);
if (networkStartSetup) {
throw invalid_option();
}
#if defined(__unixish__)
threadCount = extractIntOption(value, 1, 1024);
#else
// multiple client threads are not supported on windows.
threadCount = extractIntOption(value, 1, 1);
#endif
if (threadCount > 1) {
disableLocalClient();
}
} else {
forwardOption = true;
}
if (forwardOption) {
MutexHolder holder(lock);
localClient->api->setNetworkOption(option, value);
@ -1910,6 +1936,10 @@ void MultiVersionApi::setupNetwork() {
throw network_already_setup();
}
if (threadCount > 1) {
disableLocalClient();
}
for (auto i : externalClientDescriptions) {
std::string path = i.second.libPath;
std::string filename = basename(path);
@ -1953,13 +1983,13 @@ void MultiVersionApi::setupNetwork() {
localClient->api->setupNetwork();
}
localClient->loadProtocolVersion();
localClient->loadVersion();
if (!bypassMultiClientApi) {
runOnExternalClientsAllThreads([this](Reference<ClientInfo> client) {
TraceEvent("InitializingExternalClient").detail("LibraryPath", client->libPath);
client->api->selectApiVersion(apiVersion);
client->loadProtocolVersion();
client->loadVersion();
});
MutexHolder holder(lock);
@ -2007,11 +2037,21 @@ void MultiVersionApi::runNetwork() {
std::vector<THREAD_HANDLE> handles;
if (!bypassMultiClientApi) {
runOnExternalClientsAllThreads([&handles](Reference<ClientInfo> client) {
if (client->external) {
handles.push_back(g_network->startThread(&runNetworkThread, client.getPtr()));
}
});
for (int threadNum = 0; threadNum < threadCount; threadNum++) {
runOnExternalClients(threadNum, [&handles, threadNum](Reference<ClientInfo> client) {
if (client->external) {
std::string threadName = format("fdb-%s-%d", client->releaseVersion.c_str(), threadNum);
if (threadName.size() > 15) {
threadName = format("fdb-%s", client->releaseVersion.c_str());
if (threadName.size() > 15) {
threadName = "fdb-external";
}
}
handles.push_back(
g_network->startThread(&runNetworkThread, client.getPtr(), 0, threadName.c_str()));
}
});
}
}
localClient->api->runNetwork();
@ -2215,19 +2255,24 @@ MultiVersionApi::MultiVersionApi()
MultiVersionApi* MultiVersionApi::api = new MultiVersionApi();
// ClientInfo
void ClientInfo::loadProtocolVersion() {
void ClientInfo::loadVersion() {
std::string version = api->getClientVersion();
if (version == "unknown") {
protocolVersion = ProtocolVersion(0);
releaseVersion = "unknown";
return;
}
Standalone<ClientVersionRef> clientVersion = ClientVersionRef(StringRef(version));
char* next;
std::string protocolVersionStr = ClientVersionRef(StringRef(version)).protocolVersion.toString();
std::string protocolVersionStr = clientVersion.protocolVersion.toString();
protocolVersion = ProtocolVersion(strtoull(protocolVersionStr.c_str(), &next, 16));
ASSERT(protocolVersion.version() != 0 && protocolVersion.version() != ULLONG_MAX);
ASSERT_EQ(next, &protocolVersionStr[protocolVersionStr.length()]);
releaseVersion = clientVersion.clientVersion.toString();
}
bool ClientInfo::canReplace(Reference<ClientInfo> other) const {

View File

@ -521,6 +521,7 @@ struct ClientDesc {
struct ClientInfo : ClientDesc, ThreadSafeReferenceCounted<ClientInfo> {
ProtocolVersion protocolVersion;
std::string releaseVersion = "unknown";
IClientApi* api;
bool failed;
std::atomic_bool initialized;
@ -533,7 +534,7 @@ struct ClientInfo : ClientDesc, ThreadSafeReferenceCounted<ClientInfo> {
ClientInfo(IClientApi* api, std::string libPath)
: ClientDesc(libPath, true), protocolVersion(0), api(api), failed(false), initialized(false) {}
void loadProtocolVersion();
void loadVersion();
bool canReplace(Reference<ClientInfo> other) const;
};

View File

@ -28,6 +28,8 @@
#include <utility>
#include <vector>
#include "contrib/fmt-8.0.1/include/fmt/format.h"
#include "fdbclient/FDBTypes.h"
#include "fdbrpc/FailureMonitor.h"
#include "fdbrpc/MultiInterface.h"
@ -65,6 +67,7 @@
#include "flow/ActorCollection.h"
#include "flow/DeterministicRandom.h"
#include "flow/Error.h"
#include "flow/FastRef.h"
#include "flow/IRandom.h"
#include "flow/flow.h"
#include "flow/genericactors.actor.h"
@ -124,8 +127,8 @@ TLSConfig tlsConfig(TLSEndpointType::CLIENT);
NetworkOptions::NetworkOptions()
: traceRollSize(TRACE_DEFAULT_ROLL_SIZE), traceMaxLogsSize(TRACE_DEFAULT_MAX_LOGS_SIZE), traceLogGroup("default"),
traceFormat("xml"), traceClockSource("now"),
supportedVersions(new ReferencedObject<Standalone<VectorRef<ClientVersionRef>>>()), runLoopProfilingEnabled(false) {
}
supportedVersions(new ReferencedObject<Standalone<VectorRef<ClientVersionRef>>>()), runLoopProfilingEnabled(false),
primaryClient(true) {}
static const Key CLIENT_LATENCY_INFO_PREFIX = LiteralStringRef("client_latency/");
static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = LiteralStringRef("client_latency_counter/");
@ -1226,7 +1229,8 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<IClusterConnection
clientInfo(clientInfo), clientInfoMonitor(clientInfoMonitor), coordinator(coordinator), apiVersion(apiVersion),
mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0),
smoothMidShardSize(CLIENT_KNOBS->SHARD_STAT_SMOOTH_AMOUNT),
specialKeySpace(std::make_unique<SpecialKeySpace>(specialKeys.begin, specialKeys.end, /* test */ false)) {
specialKeySpace(std::make_unique<SpecialKeySpace>(specialKeys.begin, specialKeys.end, /* test */ false)),
connectToDatabaseEventCacheHolder(format("ConnectToDatabase/%s", dbId.toString().c_str())) {
dbId = deterministicRandom()->randomUniqueID();
connected = (clientInfo->get().commitProxies.size() && clientInfo->get().grvProxies.size())
? Void()
@ -1478,7 +1482,8 @@ DatabaseContext::DatabaseContext(const Error& err)
transactionsExpensiveClearCostEstCount("ExpensiveClearCostEstCount", cc),
transactionGrvFullBatches("NumGrvFullBatches", cc), transactionGrvTimedOutBatches("NumGrvTimedOutBatches", cc),
latencies(1000), readLatencies(1000), commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000),
bytesPerCommit(1000), transactionTracingSample(false), smoothMidShardSize(CLIENT_KNOBS->SHARD_STAT_SMOOTH_AMOUNT) {}
bytesPerCommit(1000), transactionTracingSample(false), smoothMidShardSize(CLIENT_KNOBS->SHARD_STAT_SMOOTH_AMOUNT),
connectToDatabaseEventCacheHolder(format("ConnectToDatabase/%s", dbId.toString().c_str())) {}
// Static constructor used by server processes to create a DatabaseContext
// For internal (fdbserver) use only
@ -1792,6 +1797,8 @@ Database Database::createDatabase(Reference<IClusterConnectionRecord> connRecord
if (!g_network)
throw network_not_setup();
platform::ImageInfo imageInfo = platform::getImageInfo();
if (connRecord) {
if (networkOptions.traceDirectory.present() && !traceFileIsOpen()) {
g_network->initMetrics();
@ -1814,11 +1821,11 @@ Database Database::createDatabase(Reference<IClusterConnectionRecord> connRecord
.detail("SourceVersion", getSourceVersion())
.detail("Version", FDB_VT_VERSION)
.detail("PackageName", FDB_VT_PACKAGE_NAME)
.detail("ClusterFile", connRecord->toString())
.detail("ConnectionString", connRecord->getConnectionString().toString())
.detailf("ActualTime", "%lld", DEBUG_DETERMINISM ? 0 : time(nullptr))
.detail("ApiVersion", apiVersion)
.detailf("ImageOffset", "%p", platform::getImageOffset())
.detail("ClientLibrary", imageInfo.fileName)
.detailf("ImageOffset", "%p", imageInfo.offset)
.detail("Primary", networkOptions.primaryClient)
.trackLatest("ClientStart");
initializeSystemMonitorMachineState(SystemMonitorMachineState(IPAddress(publicIP)));
@ -1872,6 +1879,16 @@ Database Database::createDatabase(Reference<IClusterConnectionRecord> connRecord
database, Reference<AsyncVar<ClientDBInfo> const>(clientInfo), std::addressof(clientInfo->get()));
GlobalConfig::globalConfig().trigger(samplingFrequency, samplingProfilerUpdateFrequency);
GlobalConfig::globalConfig().trigger(samplingWindow, samplingProfilerUpdateWindow);
TraceEvent("ConnectToDatabase", database->dbId)
.detail("Version", FDB_VT_VERSION)
.detail("ClusterFile", connRecord ? connRecord->toString() : "None")
.detail("ConnectionString", connRecord ? connRecord->getConnectionString().toString() : "None")
.detail("ClientLibrary", imageInfo.fileName)
.detail("Primary", networkOptions.primaryClient)
.detail("Internal", internal)
.trackLatest(database->connectToDatabaseEventCacheHolder.trackingKey);
return database;
}
@ -1989,7 +2006,11 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional<StringRef> valu
try {
auto knobValue = IKnobCollection::parseKnobValue(knobName, knobValueString, IKnobCollection::Type::CLIENT);
IKnobCollection::getMutableGlobalKnobCollection().setKnob(knobName, knobValue);
if (g_network) {
IKnobCollection::getMutableGlobalKnobCollection().setKnob(knobName, knobValue);
} else {
networkOptions.knobs[knobName] = knobValue;
}
} catch (Error& e) {
TraceEvent(SevWarnAlways, "UnrecognizedKnob").detail("Knob", knobName.c_str());
fprintf(stderr, "FoundationDB client ignoring unrecognized knob option '%s'\n", knobName.c_str());
@ -2090,6 +2111,9 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional<StringRef> valu
}
break;
}
case FDBNetworkOptions::EXTERNAL_CLIENT:
networkOptions.primaryClient = false;
break;
default:
break;
}
@ -2135,6 +2159,13 @@ ACTOR Future<Void> monitorNetworkBusyness() {
}
}
static void setupGlobalKnobs() {
IKnobCollection::setGlobalKnobCollection(IKnobCollection::Type::CLIENT, Randomize::False, IsSimulated::False);
for (const auto& [knobName, knobValue] : networkOptions.knobs) {
IKnobCollection::getMutableGlobalKnobCollection().setKnob(knobName, knobValue);
}
}
// Setup g_network and start monitoring for network busyness
void setupNetwork(uint64_t transportId, UseMetrics useMetrics) {
if (g_network)
@ -2143,6 +2174,7 @@ void setupNetwork(uint64_t transportId, UseMetrics useMetrics) {
if (!networkOptions.logClientInfo.present())
networkOptions.logClientInfo = true;
setupGlobalKnobs();
TLS::DisableOpenSSLAtExitHandler();
g_network = newNet2(tlsConfig, false, useMetrics || networkOptions.traceDirectory.present());
g_network->addStopCallback(Net2FileSystem::stop);
@ -4125,7 +4157,6 @@ ACTOR Future<Void> getRangeStream(PromiseStream<RangeResult> _results,
Reverse reverse,
TransactionInfo info,
TagSet tags) {
state ParallelStream<RangeResult> results(_results, CLIENT_KNOBS->RANGESTREAM_BUFFERED_FRAGMENTS_LIMIT);
// FIXME: better handling to disable row limits
@ -4596,7 +4627,6 @@ Future<RangeResult> Transaction::getRangeAndFlatMap(const KeySelector& begin,
GetRangeLimits limits,
Snapshot snapshot,
Reverse reverse) {
return getRangeInternal<GetKeyValuesAndFlatMapRequest, GetKeyValuesAndFlatMapReply>(
begin, end, mapper, limits, snapshot, reverse);
}
@ -6014,7 +6044,6 @@ ACTOR Future<ProtocolVersion> getCoordinatorProtocol(NetworkAddressList coordina
ACTOR Future<Optional<ProtocolVersion>> getCoordinatorProtocolFromConnectPacket(
NetworkAddress coordinatorAddress,
Optional<ProtocolVersion> expectedVersion) {
state Reference<AsyncVar<Optional<ProtocolVersion>> const> protocolVersion =
FlowTransport::transport().getPeerProtocolAsyncVar(coordinatorAddress);
@ -6042,7 +6071,6 @@ ACTOR Future<Optional<ProtocolVersion>> getCoordinatorProtocolFromConnectPacket(
ACTOR Future<ProtocolVersion> getClusterProtocolImpl(
Reference<AsyncVar<Optional<ClientLeaderRegInterface>> const> coordinator,
Optional<ProtocolVersion> expectedVersion) {
state bool needToConnect = true;
state Future<ProtocolVersion> protocolVersion = Never();
@ -6521,193 +6549,6 @@ struct BWLocationInfo : MultiInterface<ReferencedInterface<BlobWorkerInterface>>
explicit BWLocationInfo(const std::vector<Reference<ReferencedInterface<BlobWorkerInterface>>>& v) : Locations(v) {}
};
/*
ACTOR Future<Void> readBlobGranulesStreamActor(Reference<DatabaseContext> db,
PromiseStream<Standalone<BlobGranuleChunkRef>> results,
KeyRange range,
Version begin,
Optional<Version> read) { // end not present is just latest
state Database cx(db);
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
state RangeResult blobGranuleMapping;
state Version readVersion;
state Key granuleStartKey;
state Key granuleEndKey;
state KeyRange keyRange = range;
state int i, loopCounter = 0;
state UID workerId;
loop {
try {
// FIXME: Use streaming parallelism?
// Read mapping and worker interfaces from DB
loopCounter++;
loop {
try {
tr->reset();
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
if (loopCounter == 1) {
// if retrying, use new version for mapping but original version for read version
if (read.present()) {
readVersion = read.get();
} else {
Version _end = wait(tr->getReadVersion());
readVersion = _end;
}
}
// Right now just read whole blob range assignments from DB
// FIXME: eventually we probably want to cache this and invalidate similarly to storage servers.
// Cache misses could still read from the DB, or we could add it to the Transaction State Store and
// have proxies serve it from memory.
RangeResult _bgMapping = wait(krmGetRanges(
tr, blobGranuleMappingKeys.begin, keyRange, 1000, GetRangeLimits::BYTE_LIMIT_UNLIMITED));
blobGranuleMapping = _bgMapping;
if (blobGranuleMapping.more) {
if (BG_REQUEST_DEBUG) {
printf("BG Mapping for [%s - %s) too large!\n");
}
throw unsupported_operation();
}
ASSERT(!blobGranuleMapping.more && blobGranuleMapping.size() < CLIENT_KNOBS->TOO_MANY);
if (blobGranuleMapping.size() == 0) {
if (BG_REQUEST_DEBUG) {
printf("no blob worker assignments yet \n");
}
throw transaction_too_old();
}
if (BG_REQUEST_DEBUG) {
printf("Doing blob granule request @ %lld\n", readVersion);
printf("blob worker assignments:\n");
}
for (i = 0; i < blobGranuleMapping.size() - 1; i++) {
granuleStartKey = blobGranuleMapping[i].key;
granuleEndKey = blobGranuleMapping[i + 1].key;
if (!blobGranuleMapping[i].value.size()) {
if (BG_REQUEST_DEBUG) {
printf("Key range [%s - %s) missing worker assignment!\n",
granuleStartKey.printable().c_str(),
granuleEndKey.printable().c_str());
// TODO probably new exception type instead
}
throw transaction_too_old();
}
workerId = decodeBlobGranuleMappingValue(blobGranuleMapping[i].value);
if (BG_REQUEST_DEBUG) {
printf(" [%s - %s): %s\n",
granuleStartKey.printable().c_str(),
granuleEndKey.printable().c_str(),
workerId.toString().c_str());
}
if (!cx->blobWorker_interf.count(workerId)) {
Optional<Value> workerInterface = wait(tr->get(blobWorkerListKeyFor(workerId)));
ASSERT(workerInterface.present());
cx->blobWorker_interf[workerId] = decodeBlobWorkerListValue(workerInterface.get());
if (BG_REQUEST_DEBUG) {
printf(" decoded worker interface for %s\n", workerId.toString().c_str());
}
}
}
break;
} catch (Error& e) {
wait(tr->onError(e));
}
}
// Make request for each granule
for (i = 0; i < blobGranuleMapping.size() - 1; i++) {
granuleStartKey = blobGranuleMapping[i].key;
granuleEndKey = blobGranuleMapping[i + 1].key;
// if this was a time travel and the request returned larger bounds, skip this chunk
if (granuleEndKey <= keyRange.begin) {
continue;
}
workerId = decodeBlobGranuleMappingValue(blobGranuleMapping[i].value);
// prune first/last granules to requested range
if (keyRange.begin > granuleStartKey) {
granuleStartKey = keyRange.begin;
}
if (keyRange.end < granuleEndKey) {
granuleEndKey = keyRange.end;
}
state BlobGranuleFileRequest req;
req.keyRange = KeyRangeRef(StringRef(req.arena, granuleStartKey), StringRef(req.arena, granuleEndKey));
req.beginVersion = begin;
req.readVersion = readVersion;
std::vector<Reference<ReferencedInterface<BlobWorkerInterface>>> v;
v.push_back(makeReference<ReferencedInterface<BlobWorkerInterface>>(cx->blobWorker_interf[workerId]));
state Reference<MultiInterface<ReferencedInterface<BlobWorkerInterface>>> location =
makeReference<BWLocationInfo>(v);
// use load balance with one option for now for retry and error handling
BlobGranuleFileReply rep = wait(loadBalance(location,
&BlobWorkerInterface::blobGranuleFileRequest,
req,
TaskPriority::DefaultPromiseEndpoint,
AtMostOnce::False,
nullptr));
if (BG_REQUEST_DEBUG) {
printf("Blob granule request for [%s - %s) @ %lld - %lld got reply from %s:\n",
granuleStartKey.printable().c_str(),
granuleEndKey.printable().c_str(),
begin,
readVersion,
workerId.toString().c_str());
}
for (auto& chunk : rep.chunks) {
if (BG_REQUEST_DEBUG) {
printf("[%s - %s)\n",
chunk.keyRange.begin.printable().c_str(),
chunk.keyRange.end.printable().c_str());
printf(" SnapshotFile:\n %s\n",
chunk.snapshotFile.present() ? chunk.snapshotFile.get().toString().c_str() : "<none>");
printf(" DeltaFiles:\n");
for (auto& df : chunk.deltaFiles) {
printf(" %s\n", df.toString().c_str());
}
printf(" Deltas: (%d)", chunk.newDeltas.size());
if (chunk.newDeltas.size() > 0) {
printf(" with version [%lld - %lld]",
chunk.newDeltas[0].version,
chunk.newDeltas[chunk.newDeltas.size() - 1].version);
}
printf(" IncludedVersion: %lld\n", chunk.includedVersion);
printf("\n\n");
}
Arena a;
a.dependsOn(rep.arena);
results.send(Standalone<BlobGranuleChunkRef>(chunk, a));
keyRange = KeyRangeRef(std::min(chunk.keyRange.end, keyRange.end), keyRange.end);
}
}
results.sendError(end_of_stream());
return Void();
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw;
}
if (e.code() == error_code_wrong_shard_server || e.code() == error_code_all_alternatives_failed ||
e.code() == error_code_connection_failed) {
// TODO would invalidate mapping cache here if we had it
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY));
} else {
if (BG_REQUEST_DEBUG) {
printf("blob granule file request got unexpected error %s\n", e.name());
}
results.sendError(e);
return Void();
}
}
}
}*/
ACTOR Future<Standalone<VectorRef<BlobGranuleChunkRef>>> readBlobGranulesActor(
Database cx,
Transaction* self,
@ -6756,8 +6597,8 @@ ACTOR Future<Standalone<VectorRef<BlobGranuleChunkRef>>> readBlobGranulesActor(
}
if (BG_REQUEST_DEBUG) {
printf("Doing blob granule request @ %lld\n", *readVersionOut);
printf("blob worker assignments:\n");
fmt::print("Doing blob granule request @ {}\n", endVersion);
fmt::print("blob worker assignments:\n");
}
for (i = 0; i < blobGranuleMapping.size() - 1; i++) {
@ -6829,32 +6670,31 @@ ACTOR Future<Standalone<VectorRef<BlobGranuleChunkRef>>> readBlobGranulesActor(
nullptr));
if (BG_REQUEST_DEBUG) {
printf("Blob granule request for [%s - %s) @ %lld - %lld got reply from %s:\n",
granuleStartKey.printable().c_str(),
granuleEndKey.printable().c_str(),
begin,
*readVersionOut,
workerId.toString().c_str());
fmt::print("Blob granule request for [{0} - {1}) @ {2} - {3} got reply from {4}:\n",
granuleStartKey.printable(),
granuleEndKey.printable(),
begin,
*readVersionOut,
workerId.toString());
}
results.arena().dependsOn(rep.arena);
for (auto& chunk : rep.chunks) {
if (BG_REQUEST_DEBUG) {
printf("[%s - %s)\n", chunk.keyRange.begin.printable().c_str(), chunk.keyRange.end.printable().c_str());
printf(" SnapshotFile:\n %s\n",
printf(" SnapshotFile:\n \n",
chunk.snapshotFile.present() ? chunk.snapshotFile.get().toString().c_str() : "<none>");
printf(" DeltaFiles:\n");
for (auto& df : chunk.deltaFiles) {
printf(" %s\n", df.toString().c_str());
fmt::print(" {0}\n", df.toString());
}
printf(" Deltas: (%d)", chunk.newDeltas.size());
fmt::print(" Deltas: ({0})", chunk.newDeltas.size());
if (chunk.newDeltas.size() > 0) {
printf(" with version [%lld - %lld]",
chunk.newDeltas[0].version,
chunk.newDeltas[chunk.newDeltas.size() - 1].version);
fmt::print(" with version [{0} - {1}]",
chunk.newDeltas[0].version,
chunk.newDeltas[chunk.newDeltas.size() - 1].version);
}
printf(" IncludedVersion: %lld\n", chunk.includedVersion);
printf("\n\n");
fmt::print(" IncludedVersion: {}\n\n\n", chunk.includedVersion);
}
results.push_back(results.arena(), chunk);
@ -7173,70 +7013,210 @@ Future<Void> DatabaseContext::createSnapshot(StringRef uid, StringRef snapshot_c
return createSnapshotActor(this, UID::fromString(uid_str), snapshot_command);
}
ACTOR Future<Void> singleChangeFeedStream(StorageServerInterface interf,
PromiseStream<Standalone<MutationsAndVersionRef>> results,
Key rangeID,
Version begin,
Version end,
KeyRange range) {
ACTOR Future<Void> storageFeedVersionUpdater(StorageServerInterface interf, ChangeFeedStorageData* self) {
state Promise<Void> destroyed = self->destroyed;
loop {
try {
state Version lastEmpty = invalidVersion;
state ChangeFeedStreamRequest req;
req.rangeID = rangeID;
req.begin = begin;
req.end = end;
req.range = range;
state ReplyPromiseStream<ChangeFeedStreamReply> replyStream = interf.changeFeedStream.getReplyStream(req);
loop {
state ChangeFeedStreamReply rep = waitNext(replyStream.getFuture());
begin = rep.mutations.back().version + 1;
state int resultLoc = 0;
// FIXME: handle empty versions properly
while (resultLoc < rep.mutations.size()) {
if (rep.mutations[resultLoc].mutations.size() || rep.mutations[resultLoc].version + 1 == end ||
(rep.mutations[resultLoc].mutations.empty() &&
rep.mutations[resultLoc].version >= lastEmpty + 5000000)) {
wait(results.onEmpty());
results.send(rep.mutations[resultLoc]);
}
resultLoc++;
}
if (begin == end) {
results.sendError(end_of_stream());
return Void();
}
}
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw;
}
results.sendError(e);
if (destroyed.isSet()) {
return Void();
}
if (self->version.get() < self->desired.get()) {
wait(delay(CLIENT_KNOBS->CHANGE_FEED_EMPTY_BATCH_TIME) || self->version.whenAtLeast(self->desired.get()));
if (destroyed.isSet()) {
return Void();
}
if (self->version.get() < self->desired.get()) {
ChangeFeedVersionUpdateReply rep = wait(brokenPromiseToNever(
interf.changeFeedVersionUpdate.getReply(ChangeFeedVersionUpdateRequest(self->desired.get()))));
if (rep.version > self->version.get()) {
self->version.set(rep.version);
}
}
} else {
wait(self->desired.whenAtLeast(self->version.get() + 1));
}
}
}
struct MutationAndVersionStream {
Standalone<MutationsAndVersionRef> next;
PromiseStream<Standalone<MutationsAndVersionRef>> results;
bool operator<(MutationAndVersionStream const& rhs) const { return next.version > rhs.next.version; }
};
Reference<ChangeFeedStorageData> DatabaseContext::getStorageData(StorageServerInterface interf) {
auto it = changeFeedUpdaters.find(interf.id());
if (it == changeFeedUpdaters.end()) {
Reference<ChangeFeedStorageData> newStorageUpdater = makeReference<ChangeFeedStorageData>();
newStorageUpdater->id = interf.id();
newStorageUpdater->updater = storageFeedVersionUpdater(interf, newStorageUpdater.getPtr());
changeFeedUpdaters[interf.id()] = newStorageUpdater;
return newStorageUpdater;
}
return it->second;
}
ACTOR Future<Void> mergeChangeFeedStream(std::vector<std::pair<StorageServerInterface, KeyRange>> interfs,
PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>> results,
Version ChangeFeedData::getVersion() {
if (notAtLatest.get() == 0 && mutations.isEmpty()) {
Version v = storageData[0]->version.get();
for (int i = 1; i < storageData.size(); i++) {
if (storageData[i]->version.get() < v) {
v = storageData[i]->version.get();
}
}
return std::max(v, lastReturnedVersion.get());
}
return lastReturnedVersion.get();
}
ACTOR Future<Void> changeFeedWhenAtLatest(ChangeFeedData* self, Version version) {
state Future<Void> lastReturned = self->lastReturnedVersion.whenAtLeast(version);
loop {
if (self->notAtLatest.get() == 0) {
std::vector<Future<Void>> allAtLeast;
for (auto& it : self->storageData) {
if (it->version.get() < version) {
if (version > it->desired.get()) {
it->desired.set(version);
}
allAtLeast.push_back(it->version.whenAtLeast(version));
}
}
choose {
when(wait(lastReturned)) { return Void(); }
when(wait(waitForAll(allAtLeast))) {
std::vector<Future<Void>> onEmpty;
if (!self->mutations.isEmpty()) {
onEmpty.push_back(self->mutations.onEmpty());
}
for (auto& it : self->streams) {
if (!it.isEmpty()) {
onEmpty.push_back(it.onEmpty());
}
}
if (!onEmpty.size()) {
return Void();
}
choose {
when(wait(waitForAll(onEmpty))) {
wait(delay(0));
return Void();
}
when(wait(lastReturned)) { return Void(); }
when(wait(self->refresh.getFuture())) {}
when(wait(self->notAtLatest.onChange())) {}
}
}
when(wait(self->refresh.getFuture())) {}
when(wait(self->notAtLatest.onChange())) {}
}
} else {
choose {
when(wait(lastReturned)) { return Void(); }
when(wait(self->notAtLatest.onChange())) {}
when(wait(self->refresh.getFuture())) {}
}
}
}
}
Future<Void> ChangeFeedData::whenAtLeast(Version version) {
return changeFeedWhenAtLatest(this, version);
}
ACTOR Future<Void> singleChangeFeedStream(StorageServerInterface interf,
PromiseStream<Standalone<MutationsAndVersionRef>> results,
ReplyPromiseStream<ChangeFeedStreamReply> replyStream,
Version end,
Reference<ChangeFeedData> feedData,
Reference<ChangeFeedStorageData> storageData) {
state bool atLatestVersion = false;
state Version nextVersion = 0;
try {
loop {
if (nextVersion >= end) {
results.sendError(end_of_stream());
return Void();
}
choose {
when(state ChangeFeedStreamReply rep = waitNext(replyStream.getFuture())) {
state int resultLoc = 0;
while (resultLoc < rep.mutations.size()) {
wait(results.onEmpty());
if (rep.mutations[resultLoc].version >= nextVersion) {
results.send(rep.mutations[resultLoc]);
} else {
ASSERT(rep.mutations[resultLoc].mutations.empty());
}
resultLoc++;
}
nextVersion = rep.mutations.back().version + 1;
if (!atLatestVersion && rep.atLatestVersion) {
atLatestVersion = true;
feedData->notAtLatest.set(feedData->notAtLatest.get() - 1);
}
if (rep.minStreamVersion > storageData->version.get()) {
storageData->version.set(rep.minStreamVersion);
}
for (auto& it : feedData->storageData) {
if (rep.mutations.back().version > it->desired.get()) {
it->desired.set(rep.mutations.back().version);
}
}
}
when(wait(atLatestVersion && replyStream.isEmpty() && results.isEmpty()
? storageData->version.whenAtLeast(nextVersion)
: Future<Void>(Never()))) {
MutationsAndVersionRef empty;
empty.version = storageData->version.get();
results.send(empty);
nextVersion = storageData->version.get() + 1;
}
when(wait(atLatestVersion && replyStream.isEmpty() && !results.isEmpty() ? results.onEmpty()
: Future<Void>(Never()))) {}
}
}
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw;
}
results.sendError(e);
return Void();
}
}
ACTOR Future<Void> mergeChangeFeedStream(Reference<DatabaseContext> db,
std::vector<std::pair<StorageServerInterface, KeyRange>> interfs,
Reference<ChangeFeedData> results,
Key rangeID,
Version* begin,
Version end) {
state std::priority_queue<MutationAndVersionStream, std::vector<MutationAndVersionStream>> mutations;
state std::vector<Future<Void>> fetchers(interfs.size());
state std::vector<MutationAndVersionStream> streams(interfs.size());
results->streams.clear();
for (auto& it : interfs) {
ChangeFeedStreamRequest req;
req.rangeID = rangeID;
req.begin = *begin;
req.end = end;
req.range = it.second;
results->streams.push_back(it.first.changeFeedStream.getReplyStream(req));
}
for (auto& it : results->storageData) {
if (it->debugGetReferenceCount() == 2) {
db->changeFeedUpdaters.erase(it->id);
}
}
results->storageData.clear();
Promise<Void> refresh = results->refresh;
results->refresh = Promise<Void>();
for (int i = 0; i < interfs.size(); i++) {
fetchers[i] =
singleChangeFeedStream(interfs[i].first, streams[i].results, rangeID, *begin, end, interfs[i].second);
results->storageData.push_back(db->getStorageData(interfs[i].first));
}
results->notAtLatest.set(interfs.size());
refresh.send(Void());
for (int i = 0; i < interfs.size(); i++) {
fetchers[i] = singleChangeFeedStream(
interfs[i].first, streams[i].results, results->streams[i], end, results, results->storageData[i]);
}
state int interfNum = 0;
while (interfNum < interfs.size()) {
@ -7260,7 +7240,8 @@ ACTOR Future<Void> mergeChangeFeedStream(std::vector<std::pair<StorageServerInte
if (nextStream.next.version != checkVersion) {
if (nextOut.size()) {
*begin = checkVersion + 1;
results.send(nextOut);
results->mutations.send(nextOut);
results->lastReturnedVersion.set(nextOut.back().version);
nextOut = Standalone<VectorRef<MutationsAndVersionRef>>();
}
checkVersion = nextStream.next.version;
@ -7285,7 +7266,8 @@ ACTOR Future<Void> mergeChangeFeedStream(std::vector<std::pair<StorageServerInte
}
}
if (nextOut.size()) {
results.send(nextOut);
results->mutations.send(nextOut);
results->lastReturnedVersion.set(nextOut.back().version);
}
throw end_of_stream();
}
@ -7324,7 +7306,7 @@ ACTOR Future<KeyRange> getChangeFeedRange(Reference<DatabaseContext> db, Databas
}
ACTOR Future<Void> getChangeFeedStreamActor(Reference<DatabaseContext> db,
PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>> results,
Reference<ChangeFeedData> results,
Key rangeID,
Version begin,
Version end,
@ -7397,34 +7379,65 @@ ACTOR Future<Void> getChangeFeedStreamActor(Reference<DatabaseContext> db,
interfs.push_back(std::make_pair(locations[i].second->getInterface(chosenLocations[i]),
locations[i].first & range));
}
wait(mergeChangeFeedStream(interfs, results, rangeID, &begin, end) || cx->connectionFileChanged());
wait(mergeChangeFeedStream(db, interfs, results, rangeID, &begin, end) || cx->connectionFileChanged());
} else {
state ChangeFeedStreamRequest req;
req.rangeID = rangeID;
req.begin = begin;
req.end = end;
req.range = range;
StorageServerInterface interf = locations[0].second->getInterface(chosenLocations[0]);
state ReplyPromiseStream<ChangeFeedStreamReply> replyStream =
locations[0]
.second->get(chosenLocations[0], &StorageServerInterface::changeFeedStream)
.getReplyStream(req);
interf.changeFeedStream.getReplyStream(req);
for (auto& it : results->storageData) {
if (it->debugGetReferenceCount() == 2) {
db->changeFeedUpdaters.erase(it->id);
}
}
results->streams.clear();
results->storageData.clear();
results->storageData.push_back(db->getStorageData(interf));
Promise<Void> refresh = results->refresh;
results->refresh = Promise<Void>();
results->notAtLatest.set(1);
refresh.send(Void());
state bool atLatest = false;
loop {
wait(results.onEmpty());
wait(results->mutations.onEmpty());
choose {
when(wait(cx->connectionFileChanged())) { break; }
when(ChangeFeedStreamReply rep = waitNext(replyStream.getFuture())) {
begin = rep.mutations.back().version + 1;
results.send(Standalone<VectorRef<MutationsAndVersionRef>>(rep.mutations, rep.arena));
results->mutations.send(
Standalone<VectorRef<MutationsAndVersionRef>>(rep.mutations, rep.arena));
results->lastReturnedVersion.set(rep.mutations.back().version);
if (!atLatest && rep.atLatestVersion) {
atLatest = true;
results->notAtLatest.set(0);
}
if (rep.minStreamVersion > results->storageData[0]->version.get()) {
results->storageData[0]->version.set(rep.minStreamVersion);
}
}
}
}
}
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
for (auto& it : results->storageData) {
if (it->debugGetReferenceCount() == 2) {
db->changeFeedUpdaters.erase(it->id);
}
}
results->streams.clear();
results->storageData.clear();
results->refresh.sendError(change_feed_cancelled());
throw;
}
if (results->notAtLatest.get() == 0) {
results->notAtLatest.set(1);
}
if (e.code() == error_code_wrong_shard_server || e.code() == error_code_all_alternatives_failed ||
e.code() == error_code_connection_failed || e.code() == error_code_unknown_change_feed ||
e.code() == error_code_broken_promise) {
@ -7432,19 +7445,26 @@ ACTOR Future<Void> getChangeFeedStreamActor(Reference<DatabaseContext> db,
cx->invalidateCache(keys);
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY));
} else {
results.sendError(e);
results->mutations.sendError(e);
results->refresh.sendError(change_feed_cancelled());
for (auto& it : results->storageData) {
if (it->debugGetReferenceCount() == 2) {
db->changeFeedUpdaters.erase(it->id);
}
}
results->streams.clear();
results->storageData.clear();
return Void();
}
}
}
}
Future<Void> DatabaseContext::getChangeFeedStream(
const PromiseStream<Standalone<VectorRef<MutationsAndVersionRef>>>& results,
Key rangeID,
Version begin,
Version end,
KeyRange range) {
Future<Void> DatabaseContext::getChangeFeedStream(Reference<ChangeFeedData> results,
Key rangeID,
Version begin,
Version end,
KeyRange range) {
return getChangeFeedStreamActor(Reference<DatabaseContext>::addRef(this), results, rangeID, begin, end, range);
}
@ -7587,16 +7607,15 @@ ACTOR Future<Void> popChangeFeedMutationsActor(Reference<DatabaseContext> db, Ke
return Void();
}
// FIXME: lookup both the src and dest shards as of the pop version to ensure all locations are popped
state std::vector<Future<Void>> popRequests;
for (int i = 0; i < locations.size(); i++) {
for (int j = 0; j < locations[i].second->size(); j++) {
popRequests.push_back(locations[i].second->getInterface(j).changeFeedPop.getReply(
ChangeFeedPopRequest(rangeID, version, locations[i].first)));
}
}
try {
// FIXME: lookup both the src and dest shards as of the pop version to ensure all locations are popped
std::vector<Future<Void>> popRequests;
for (int i = 0; i < locations.size(); i++) {
for (int j = 0; j < locations[i].second->size(); j++) {
popRequests.push_back(locations[i].second->getInterface(j).changeFeedPop.getReply(
ChangeFeedPopRequest(rangeID, version, locations[i].first)));
}
}
choose {
when(wait(waitForAll(popRequests))) {}
when(wait(delay(CLIENT_KNOBS->CHANGE_FEED_POP_TIMEOUT))) {

View File

@ -72,6 +72,8 @@ struct NetworkOptions {
Optional<bool> logClientInfo;
Reference<ReferencedObject<Standalone<VectorRef<ClientVersionRef>>>> supportedVersions;
bool runLoopProfilingEnabled;
bool primaryClient;
std::map<std::string, KnobValue> knobs;
NetworkOptions();
};

View File

@ -397,11 +397,10 @@ public:
static inline Future<typename Req::Result> readWithConflictRangeAndFlatMap(ReadYourWritesTransaction* ryw,
Req const& req,
Snapshot snapshot) {
if (ryw->options.readYourWritesDisabled) {
// For now, getRangeAndFlatMap is only supported if transaction use snapshot isolation AND read-your-writes is
// disabled.
if (snapshot && ryw->options.readYourWritesDisabled) {
return readWithConflictRangeThroughAndFlatMap(ryw, req, snapshot);
} else if (snapshot && ryw->options.snapshotRywEnabled <= 0) {
TEST(true); // readWithConflictRangeSnapshot not supported for getRangeAndFlatMap
throw client_invalid_operation();
}
TEST(true); // readWithConflictRangeRYW not supported for getRangeAndFlatMap
throw client_invalid_operation();

View File

@ -728,7 +728,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
"ssd",
"ssd-1",
"ssd-2",
"ssd-redwood-experimental",
"ssd-redwood-1-experimental",
"ssd-rocksdb-experimental",
"memory",
"memory-1",
@ -741,7 +741,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
"ssd",
"ssd-1",
"ssd-2",
"ssd-redwood-experimental",
"ssd-redwood-1-experimental",
"ssd-rocksdb-experimental",
"memory",
"memory-1",

View File

@ -63,7 +63,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( MAX_MESSAGE_SIZE, std::max<int>(LOG_SYSTEM_PUSHED_DATA_BLOCK_SIZE, 1e5 + 2e4 + 1) + 8 ); // VALUE_SIZE_LIMIT + SYSTEM_KEY_SIZE_LIMIT + 9 bytes (4 bytes for length, 4 bytes for sequence number, and 1 byte for mutation type)
init( TLOG_MESSAGE_BLOCK_BYTES, 10e6 );
init( TLOG_MESSAGE_BLOCK_OVERHEAD_FACTOR, double(TLOG_MESSAGE_BLOCK_BYTES) / (TLOG_MESSAGE_BLOCK_BYTES - MAX_MESSAGE_SIZE) ); //1.0121466709838096006362758832473
init( PEEK_TRACKER_EXPIRATION_TIME, 600 ); if( randomize && BUGGIFY ) PEEK_TRACKER_EXPIRATION_TIME = deterministicRandom()->coinflip() ? 0.1 : 120;
init( PEEK_TRACKER_EXPIRATION_TIME, 600 ); if( randomize && BUGGIFY ) PEEK_TRACKER_EXPIRATION_TIME = 120; // Cannot be buggified lower without changing the following assert in LogSystemPeekCursor.actor.cpp: ASSERT_WE_THINK(e.code() == error_code_operation_obsolete || SERVER_KNOBS->PEEK_TRACKER_EXPIRATION_TIME < 10);
init( PEEK_USING_STREAMING, true ); if( randomize && BUGGIFY ) PEEK_USING_STREAMING = false;
init( PARALLEL_GET_MORE_REQUESTS, 32 ); if( randomize && BUGGIFY ) PARALLEL_GET_MORE_REQUESTS = 2;
init( MULTI_CURSOR_PRE_FETCH_LIMIT, 10 );
@ -771,8 +771,10 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( REDWOOD_LAZY_CLEAR_MAX_PAGES, 1e6 );
init( REDWOOD_REMAP_CLEANUP_WINDOW, 50 );
init( REDWOOD_REMAP_CLEANUP_LAG, 0.1 );
init( REDWOOD_PAGEFILE_GROWTH_SIZE_PAGES, 20000 ); if( randomize && BUGGIFY ) { REDWOOD_PAGEFILE_GROWTH_SIZE_PAGES = deterministicRandom()->randomInt(200, 1000); }
init( REDWOOD_METRICS_INTERVAL, 5.0 );
init( REDWOOD_HISTOGRAM_INTERVAL, 30.0 );
init( REDWOOD_EVICT_UPDATED_PAGES, true ); if( randomize && BUGGIFY ) { REDWOOD_EVICT_UPDATED_PAGES = false; }
// Server request latency measurement
init( LATENCY_SAMPLE_SIZE, 100000 );

View File

@ -724,8 +724,10 @@ public:
int64_t REDWOOD_REMAP_CLEANUP_WINDOW; // Remap remover lag interval in which to coalesce page writes
double REDWOOD_REMAP_CLEANUP_LAG; // Maximum allowed remap remover lag behind the cleanup window as a multiple of
// the window size
int REDWOOD_PAGEFILE_GROWTH_SIZE_PAGES; // Number of pages to grow page file by
double REDWOOD_METRICS_INTERVAL;
double REDWOOD_HISTOGRAM_INTERVAL;
bool REDWOOD_EVICT_UPDATED_PAGES; // Whether to prioritize eviction of updated pages from cache.
// Server request latency measurement
int LATENCY_SAMPLE_SIZE;

View File

@ -83,6 +83,7 @@ struct StorageServerInterface {
RequestStream<struct ChangeFeedStreamRequest> changeFeedStream;
RequestStream<struct OverlappingChangeFeedsRequest> overlappingChangeFeeds;
RequestStream<struct ChangeFeedPopRequest> changeFeedPop;
RequestStream<struct ChangeFeedVersionUpdateRequest> changeFeedVersionUpdate;
explicit StorageServerInterface(UID uid) : uniqueID(uid) {}
StorageServerInterface() : uniqueID(deterministicRandom()->randomUniqueID()) {}
@ -125,14 +126,16 @@ struct StorageServerInterface {
RequestStream<struct SplitRangeRequest>(getValue.getEndpoint().getAdjustedEndpoint(12));
getKeyValuesStream =
RequestStream<struct GetKeyValuesStreamRequest>(getValue.getEndpoint().getAdjustedEndpoint(13));
changeFeedStream =
RequestStream<struct ChangeFeedStreamRequest>(getValue.getEndpoint().getAdjustedEndpoint(14));
overlappingChangeFeeds =
RequestStream<struct OverlappingChangeFeedsRequest>(getValue.getEndpoint().getAdjustedEndpoint(15));
changeFeedPop =
RequestStream<struct ChangeFeedPopRequest>(getValue.getEndpoint().getAdjustedEndpoint(16));
getKeyValuesAndFlatMap =
RequestStream<struct GetKeyValuesAndFlatMapRequest>(getValue.getEndpoint().getAdjustedEndpoint(17));
RequestStream<struct GetKeyValuesAndFlatMapRequest>(getValue.getEndpoint().getAdjustedEndpoint(14));
changeFeedStream =
RequestStream<struct ChangeFeedStreamRequest>(getValue.getEndpoint().getAdjustedEndpoint(15));
overlappingChangeFeeds =
RequestStream<struct OverlappingChangeFeedsRequest>(getValue.getEndpoint().getAdjustedEndpoint(16));
changeFeedPop =
RequestStream<struct ChangeFeedPopRequest>(getValue.getEndpoint().getAdjustedEndpoint(17));
changeFeedVersionUpdate = RequestStream<struct ChangeFeedVersionUpdateRequest>(
getValue.getEndpoint().getAdjustedEndpoint(18));
}
} else {
ASSERT(Ar::isDeserializing);
@ -175,10 +178,11 @@ struct StorageServerInterface {
streams.push_back(getReadHotRanges.getReceiver());
streams.push_back(getRangeSplitPoints.getReceiver());
streams.push_back(getKeyValuesStream.getReceiver(TaskPriority::LoadBalancedEndpoint));
streams.push_back(getKeyValuesAndFlatMap.getReceiver(TaskPriority::LoadBalancedEndpoint));
streams.push_back(changeFeedStream.getReceiver());
streams.push_back(overlappingChangeFeeds.getReceiver());
streams.push_back(changeFeedPop.getReceiver());
streams.push_back(getKeyValuesAndFlatMap.getReceiver(TaskPriority::LoadBalancedEndpoint));
streams.push_back(changeFeedVersionUpdate.getReceiver());
FlowTransport::transport().addEndpoints(streams);
}
};
@ -684,6 +688,8 @@ struct ChangeFeedStreamReply : public ReplyPromiseStreamReply {
constexpr static FileIdentifier file_identifier = 1783066;
Arena arena;
VectorRef<MutationsAndVersionRef> mutations;
bool atLatestVersion = false;
Version minStreamVersion = invalidVersion;
ChangeFeedStreamReply() {}
@ -691,7 +697,13 @@ struct ChangeFeedStreamReply : public ReplyPromiseStreamReply {
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, ReplyPromiseStreamReply::acknowledgeToken, ReplyPromiseStreamReply::sequence, mutations, arena);
serializer(ar,
ReplyPromiseStreamReply::acknowledgeToken,
ReplyPromiseStreamReply::sequence,
mutations,
atLatestVersion,
minStreamVersion,
arena);
}
};
@ -779,6 +791,33 @@ struct OverlappingChangeFeedsRequest {
}
};
struct ChangeFeedVersionUpdateReply {
constexpr static FileIdentifier file_identifier = 11815134;
Version version = 0;
ChangeFeedVersionUpdateReply() {}
explicit ChangeFeedVersionUpdateReply(Version version) : version(version) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, version);
}
};
struct ChangeFeedVersionUpdateRequest {
constexpr static FileIdentifier file_identifier = 6795746;
Version minVersion;
ReplyPromise<ChangeFeedVersionUpdateReply> reply;
ChangeFeedVersionUpdateRequest() {}
explicit ChangeFeedVersionUpdateRequest(Version minVersion) : minVersion(minVersion) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, minVersion, reply);
}
};
struct GetStorageMetricsReply {
constexpr static FileIdentifier file_identifier = 15491478;
StorageMetrics load;

View File

@ -213,6 +213,8 @@ const KeyRangeRef writeConflictRangeKeysRange =
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/write_conflict_range/"),
LiteralStringRef("\xff\xff/transaction/write_conflict_range/\xff\xff"));
const KeyRef clusterIdKey = LiteralStringRef("\xff/clusterId");
// "\xff/cacheServer/[[UID]] := StorageServerInterface"
const KeyRangeRef storageCacheServerKeys(LiteralStringRef("\xff/cacheServer/"), LiteralStringRef("\xff/cacheServer0"));
const KeyRef storageCacheServersPrefix = storageCacheServerKeys.begin;

View File

@ -67,6 +67,8 @@ void decodeKeyServersValue(std::map<Tag, UID> const& tag_uid,
std::vector<UID>& src,
std::vector<UID>& dest);
extern const KeyRef clusterIdKey;
// "\xff/storageCacheServer/[[UID]] := StorageServerInterface"
// This will be added by the cache server on initialization and removed by DD
// TODO[mpilman]: We will need a way to map uint16_t ids to UIDs in a future

View File

@ -15,7 +15,7 @@
//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread
#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
#include <boost/function.hpp>
#include <boost/version.hpp>
@ -366,18 +366,25 @@ public:
typedef boost::function<void(boost::int64_t)> Int_action;
typedef boost::function<void(boost::uint64_t)> Uint64_action;
Char_action begin_obj(boost::bind(&Semantic_actions_t::begin_obj, &self.actions_, _1));
Char_action end_obj(boost::bind(&Semantic_actions_t::end_obj, &self.actions_, _1));
Char_action begin_array(boost::bind(&Semantic_actions_t::begin_array, &self.actions_, _1));
Char_action end_array(boost::bind(&Semantic_actions_t::end_array, &self.actions_, _1));
Str_action new_name(boost::bind(&Semantic_actions_t::new_name, &self.actions_, _1, _2));
Str_action new_str(boost::bind(&Semantic_actions_t::new_str, &self.actions_, _1, _2));
Str_action new_true(boost::bind(&Semantic_actions_t::new_true, &self.actions_, _1, _2));
Str_action new_false(boost::bind(&Semantic_actions_t::new_false, &self.actions_, _1, _2));
Str_action new_null(boost::bind(&Semantic_actions_t::new_null, &self.actions_, _1, _2));
Real_action new_real(boost::bind(&Semantic_actions_t::new_real, &self.actions_, _1));
Int_action new_int(boost::bind(&Semantic_actions_t::new_int, &self.actions_, _1));
Uint64_action new_uint64(boost::bind(&Semantic_actions_t::new_uint64, &self.actions_, _1));
Char_action begin_obj(boost::bind(&Semantic_actions_t::begin_obj, &self.actions_, boost::placeholders::_1));
Char_action end_obj(boost::bind(&Semantic_actions_t::end_obj, &self.actions_, boost::placeholders::_1));
Char_action begin_array(
boost::bind(&Semantic_actions_t::begin_array, &self.actions_, boost::placeholders::_1));
Char_action end_array(boost::bind(&Semantic_actions_t::end_array, &self.actions_, boost::placeholders::_1));
Str_action new_name(boost::bind(
&Semantic_actions_t::new_name, &self.actions_, boost::placeholders::_1, boost::placeholders::_2));
Str_action new_str(boost::bind(
&Semantic_actions_t::new_str, &self.actions_, boost::placeholders::_1, boost::placeholders::_2));
Str_action new_true(boost::bind(
&Semantic_actions_t::new_true, &self.actions_, boost::placeholders::_1, boost::placeholders::_2));
Str_action new_false(boost::bind(
&Semantic_actions_t::new_false, &self.actions_, boost::placeholders::_1, boost::placeholders::_2));
Str_action new_null(boost::bind(
&Semantic_actions_t::new_null, &self.actions_, boost::placeholders::_1, boost::placeholders::_2));
Real_action new_real(boost::bind(&Semantic_actions_t::new_real, &self.actions_, boost::placeholders::_1));
Int_action new_int(boost::bind(&Semantic_actions_t::new_int, &self.actions_, boost::placeholders::_1));
Uint64_action new_uint64(
boost::bind(&Semantic_actions_t::new_uint64, &self.actions_, boost::placeholders::_1));
// actual grammer

View File

@ -5,12 +5,12 @@ To test this, run the following commands from the root of the FoundationDB
repository:
```bash
docker build -t foundationdb/foundationdb-kubernetes:6.3.13-local --build-arg FDB_VERSION=6.3.13 --build-arg FDB_LIBRARY_VERSIONS="6.3.13 6.2.30 6.1.13" -f packaging/docker/kubernetes/Dockerfile .
docker build -t foundationdb/foundationdb-kubernetes:6.3.15-local --build-arg FDB_VERSION=6.3.15 --build-arg FDB_LIBRARY_VERSIONS="6.3.15 6.2.30 6.1.13" -f packaging/docker/kubernetes/Dockerfile .
docker build -t foundationdb/foundationdb-kubernetes:6.3.13-local --target fdb-kubernetes-monitor --build-arg FDB_VERSION=6.3.13 --build-arg FDB_LIBRARY_VERSIONS="6.3.13 6.2.30 6.1.13" -f packaging/docker/Dockerfile .
docker build -t foundationdb/foundationdb-kubernetes:6.3.15-local --target fdb-kubernetes-monitor --build-arg FDB_VERSION=6.3.15 --build-arg FDB_LIBRARY_VERSIONS="6.3.15 6.2.30 6.1.13" -f packaging/docker/Dockerfile .
kubectl apply -f packaging/docker/kubernetes/test_config.yaml
# Wait for the pods to become ready
ips=$(kubectl get pod -l app=fdb-kubernetes-example -o json | jq -j '[[.items|.[]|select(.status.podIP!="")]|limit(3;.[])|.status.podIP+":4501"]|join(",")')
sed -e "s/fdb.cluster: \"\"/fdb.cluster: \"test:test@$ips\"/" -e "s/\"serverCount\": 0/\"serverCount\": 1/" packaging/docker/kubernetes/test_config.yaml | kubectl apply -f -
sed -e "s/fdb.cluster: \"\"/fdb.cluster: \"test:test@$ips\"/" -e "s/\"runProcesses\": false/\"runProcesses\": true/" packaging/docker/kubernetes/test_config.yaml | kubectl apply -f -
kubectl get pod -l app=fdb-kubernetes-example -o name | xargs -I {} kubectl annotate {} foundationdb.org/outdated-config-map-seen=$(date +%s) --overwrite
# Watch the logs for the fdb-kubernetes-example pods to confirm that they have launched the fdbserver processes.
kubectl exec -it sts/fdb-kubernetes-example -- fdbcli --exec "configure new double ssd"
@ -21,7 +21,7 @@ This will set up a cluster in your Kubernetes environment using a statefulset, t
You can then make changes to the data in the config map and update the fdbserver processes:
```bash
sed -e "s/fdb.cluster: \"\"/fdb.cluster: \"test:test@$ips\"/" -e "s/\"serverCount\": 0/\"serverCount\": 1/" packaging/docker/kubernetes/test_config.yaml | kubectl apply -f -
sed -e "s/fdb.cluster: \"\"/fdb.cluster: \"test:test@$ips\"/" -e "s/\"runProcesses\": false/\"runProcesses\": true/" packaging/docker/kubernetes/test_config.yaml | kubectl apply -f -
# You can apply an annotation to speed up the propagation of config
kubectl get pod -l app=fdb-kubernetes-example -o name | xargs -I {} kubectl annotate {} foundationdb.org/outdated-config-map-seen=$(date +%s) --overwrite

View File

@ -1,6 +1,5 @@
{
"version": "6.3.15",
"serverCount": 1,
"arguments": [
{"value": "--cluster_file"},
{"value": ".testdata/fdb.cluster"},

View File

@ -17,7 +17,7 @@
// limitations under the License.
//
package main
package api
import (
"fmt"
@ -31,8 +31,10 @@ type ProcessConfiguration struct {
// Version provides the version of FoundationDB the process should run.
Version string `json:"version"`
// ServerCount defines the number of processes to start.
ServerCount int `json:"serverCount,omitempty"`
// RunServers defines whether we should run the server processes.
// This defaults to true, but you can set it to false to prevent starting
// new fdbserver processes.
RunServers *bool `json:"runServers,omitempty"`
// BinaryPath provides the path to the binary to launch.
BinaryPath string `json:"-"`

View File

@ -17,7 +17,7 @@
// limitations under the License.
//
package main
package api
import (
"encoding/json"

View File

@ -24,8 +24,10 @@ import (
"encoding/json"
"fmt"
"os"
"path"
"strconv"
"github.com/apple/foundationdb/fdbkubernetesmonitor/api"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -67,7 +69,7 @@ type PodClient struct {
}
// CreatePodClient creates a new client for working with the pod object.
func CreatePodClient() (*PodClient, error) {
func CreatePodClient(logger logr.Logger) (*PodClient, error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, err
@ -83,7 +85,7 @@ func CreatePodClient() (*PodClient, error) {
return nil, err
}
podClient := &PodClient{podApi: podApi, pod: pod, TimestampFeed: make(chan int64, 10)}
podClient := &PodClient{podApi: podApi, pod: pod, TimestampFeed: make(chan int64, 10), Logger: logger}
err = podClient.watchPod()
if err != nil {
return nil, err
@ -94,7 +96,7 @@ func CreatePodClient() (*PodClient, error) {
// retrieveEnvironmentVariables extracts the environment variables we have for
// an argument into a map.
func retrieveEnvironmentVariables(argument Argument, target map[string]string) {
func retrieveEnvironmentVariables(argument api.Argument, target map[string]string) {
if argument.Source != "" {
target[argument.Source] = os.Getenv(argument.Source)
}
@ -112,6 +114,7 @@ func (client *PodClient) UpdateAnnotations(monitor *Monitor) error {
for _, argument := range monitor.ActiveConfiguration.Arguments {
retrieveEnvironmentVariables(argument, environment)
}
environment["BINARY_DIR"] = path.Dir(monitor.ActiveConfiguration.BinaryPath)
jsonEnvironment, err := json.Marshal(environment)
if err != nil {
return err
@ -180,7 +183,7 @@ func (client *PodClient) processPodUpdate(pod *corev1.Pod) {
}
timestamp, err := strconv.ParseInt(annotation, 10, 64)
if err != nil {
client.Logger.Error(err, "Error parsing annotation", "key", OutdatedConfigMapAnnotation, "rawAnnotation", annotation, err)
client.Logger.Error(err, "Error parsing annotation", "key", OutdatedConfigMapAnnotation, "rawAnnotation", annotation)
return
}

View File

@ -51,6 +51,7 @@ var (
mainContainerVersion string
currentContainerVersion string
additionalEnvFile string
processCount int
)
type executionMode string
@ -78,6 +79,7 @@ func main() {
pflag.StringArrayVar(&requiredCopyFiles, "require-not-empty", nil, "When copying this file, exit with an error if the file is empty")
pflag.StringVar(&mainContainerVersion, "main-container-version", "", "For sidecar mode, this specifies the version of the main container. If this is equal to the current container version, no files will be copied")
pflag.StringVar(&additionalEnvFile, "additional-env-file", "", "A file with additional environment variables to use when interpreting the monitor configuration")
pflag.IntVar(&processCount, "process-count", 1, "The number of processes to start")
pflag.Parse()
zapConfig := zap.NewProductionConfig()
@ -110,7 +112,7 @@ func main() {
logger.Error(err, "Error loading additional environment")
os.Exit(1)
}
StartMonitor(logger, fmt.Sprintf("%s/%s", inputDir, monitorConfFile), customEnvironment)
StartMonitor(logger, fmt.Sprintf("%s/%s", inputDir, monitorConfFile), customEnvironment, processCount)
case executionModeInit:
err = CopyFiles(logger, outputDir, copyDetails, requiredCopies)
if err != nil {
@ -124,9 +126,10 @@ func main() {
logger.Error(err, "Error copying files")
os.Exit(1)
}
done := make(chan bool)
<-done
}
logger.Info("Waiting for process to be terminated")
done := make(chan bool)
<-done
default:
logger.Error(nil, "Unknown execution mode", "mode", mode)
os.Exit(1)

View File

@ -32,6 +32,7 @@ import (
"syscall"
"time"
"github.com/apple/foundationdb/fdbkubernetesmonitor/api"
"github.com/fsnotify/fsnotify"
"github.com/go-logr/logr"
)
@ -52,7 +53,7 @@ type Monitor struct {
CustomEnvironment map[string]string
// ActiveConfiguration defines the active process configuration.
ActiveConfiguration *ProcessConfiguration
ActiveConfiguration *api.ProcessConfiguration
// ActiveConfigurationBytes defines the source data for the active process
// configuration.
@ -62,6 +63,9 @@ type Monitor struct {
// configuration file.
LastConfigurationTime time.Time
// ProcessCount defines how many processes the
ProcessCount int
// ProcessIDs stores the PIDs of the processes that are running. A PID of
// zero will indicate that a process does not have a run loop. A PID of -1
// will indicate that a process has a run loop but is not currently running
@ -82,8 +86,8 @@ type Monitor struct {
}
// StartMonitor starts the monitor loop.
func StartMonitor(logger logr.Logger, configFile string, customEnvironment map[string]string) {
podClient, err := CreatePodClient()
func StartMonitor(logger logr.Logger, configFile string, customEnvironment map[string]string, processCount int) {
podClient, err := CreatePodClient(logger)
if err != nil {
panic(err)
}
@ -93,6 +97,7 @@ func StartMonitor(logger logr.Logger, configFile string, customEnvironment map[s
PodClient: podClient,
Logger: logger,
CustomEnvironment: customEnvironment,
ProcessCount: processCount,
}
go func() { monitor.WatchPodTimestamps() }()
@ -107,7 +112,7 @@ func (monitor *Monitor) LoadConfiguration() {
return
}
defer file.Close()
configuration := &ProcessConfiguration{}
configuration := &api.ProcessConfiguration{}
configurationBytes, err := io.ReadAll(file)
if err != nil {
monitor.Logger.Error(err, "Error reading monitor configuration", "monitorConfigPath", monitor.ConfigFile)
@ -154,15 +159,15 @@ func checkOwnerExecutable(path string) error {
// acceptConfiguration is called when the monitor process parses and accepts
// a configuration from the local config file.
func (monitor *Monitor) acceptConfiguration(configuration *ProcessConfiguration, configurationBytes []byte) {
func (monitor *Monitor) acceptConfiguration(configuration *api.ProcessConfiguration, configurationBytes []byte) {
monitor.Mutex.Lock()
defer monitor.Mutex.Unlock()
monitor.Logger.Info("Received new configuration file", "configuration", configuration)
if monitor.ProcessIDs == nil {
monitor.ProcessIDs = make([]int, configuration.ServerCount+1)
monitor.ProcessIDs = make([]int, monitor.ProcessCount+1)
} else {
for len(monitor.ProcessIDs) <= configuration.ServerCount {
for len(monitor.ProcessIDs) <= monitor.ProcessCount {
monitor.ProcessIDs = append(monitor.ProcessIDs, 0)
}
}
@ -171,7 +176,7 @@ func (monitor *Monitor) acceptConfiguration(configuration *ProcessConfiguration,
monitor.ActiveConfigurationBytes = configurationBytes
monitor.LastConfigurationTime = time.Now()
for processNumber := 1; processNumber <= configuration.ServerCount; processNumber++ {
for processNumber := 1; processNumber <= monitor.ProcessCount; processNumber++ {
if monitor.ProcessIDs[processNumber] == 0 {
monitor.ProcessIDs[processNumber] = -1
tempNumber := processNumber
@ -284,7 +289,8 @@ func (monitor *Monitor) checkProcessRequired(processNumber int) bool {
monitor.Mutex.Lock()
defer monitor.Mutex.Unlock()
logger := monitor.Logger.WithValues("processNumber", processNumber, "area", "checkProcessRequired")
if monitor.ActiveConfiguration.ServerCount < processNumber {
runProcesses := monitor.ActiveConfiguration.RunServers
if monitor.ProcessCount < processNumber || (runProcesses != nil && !*runProcesses) {
logger.Info("Terminating run loop")
monitor.ProcessIDs[processNumber] = 0
return false

View File

@ -0,0 +1,155 @@
/*
* AsyncFileChaos.actor.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "flow/flow.h"
#include "flow/serialize.h"
#include "flow/genericactors.actor.h"
#include "fdbrpc/IAsyncFile.h"
#include "flow/network.h"
#include "flow/ActorCollection.h"
#include "flow/actorcompiler.h"
// template <class AsyncFileType>
class AsyncFileChaos final : public IAsyncFile, public ReferenceCounted<AsyncFileChaos> {
private:
Reference<IAsyncFile> file;
bool enabled;
public:
explicit AsyncFileChaos(Reference<IAsyncFile> file) : file(file) {
// We only allow chaos events on storage files
enabled = (file->getFilename().find("storage-") != std::string::npos);
}
void addref() override { ReferenceCounted<AsyncFileChaos>::addref(); }
void delref() override { ReferenceCounted<AsyncFileChaos>::delref(); }
double getDelay() const {
double delayFor = 0.0;
if (!enabled)
return delayFor;
auto res = g_network->global(INetwork::enDiskFailureInjector);
if (res) {
DiskFailureInjector* delayInjector = static_cast<DiskFailureInjector*>(res);
delayFor = delayInjector->getDiskDelay();
// increment the metric for disk delays
if (delayFor > 0.0) {
auto res = g_network->global(INetwork::enChaosMetrics);
if (res) {
ChaosMetrics* chaosMetrics = static_cast<ChaosMetrics*>(res);
chaosMetrics->diskDelays++;
}
}
}
return delayFor;
}
Future<int> read(void* data, int length, int64_t offset) override {
double diskDelay = getDelay();
if (diskDelay == 0.0)
return file->read(data, length, offset);
// Wait for diskDelay before submitting the I/O
// Template types are being provided explicitly because they can't be automatically deduced for some reason.
return mapAsync<Void, std::function<Future<int>(Void)>, int>(
delay(diskDelay), [=](Void _) -> Future<int> { return file->read(data, length, offset); });
}
Future<Void> write(void const* data, int length, int64_t offset) override {
Arena arena;
char* pdata = nullptr;
// Check if a bit flip event was injected, if so, copy the buffer contents
// with a random bit flipped in a new buffer and use that for the write
auto res = g_network->global(INetwork::enBitFlipper);
if (enabled && res) {
auto bitFlipPercentage = static_cast<BitFlipper*>(res)->getBitFlipPercentage();
if (bitFlipPercentage > 0.0) {
auto bitFlipProb = bitFlipPercentage / 100;
if (deterministicRandom()->random01() < bitFlipProb) {
pdata = (char*)arena.allocate4kAlignedBuffer(length);
memcpy(pdata, data, length);
// flip a random bit in the copied buffer
pdata[deterministicRandom()->randomInt(0, length)] ^= (1 << deterministicRandom()->randomInt(0, 8));
// increment the metric for bit flips
auto res = g_network->global(INetwork::enChaosMetrics);
if (res) {
ChaosMetrics* chaosMetrics = static_cast<ChaosMetrics*>(res);
chaosMetrics->bitFlips++;
}
}
}
}
double diskDelay = getDelay();
if (diskDelay == 0.0) {
if (pdata)
return holdWhile(arena, file->write(pdata, length, offset));
return file->write(data, length, offset);
}
// Wait for diskDelay before submitting the I/O
return mapAsync<Void, std::function<Future<Void>(Void)>, Void>(delay(diskDelay), [=](Void _) -> Future<Void> {
if (pdata)
return holdWhile(arena, file->write(pdata, length, offset));
return file->write(data, length, offset);
});
}
Future<Void> truncate(int64_t size) override {
double diskDelay = getDelay();
if (diskDelay == 0.0)
return file->truncate(size);
// Wait for diskDelay before submitting the I/O
return mapAsync<Void, std::function<Future<Void>(Void)>, Void>(
delay(diskDelay), [=](Void _) -> Future<Void> { return file->truncate(size); });
}
Future<Void> sync() override {
double diskDelay = getDelay();
if (diskDelay == 0.0)
return file->sync();
// Wait for diskDelay before submitting the I/O
return mapAsync<Void, std::function<Future<Void>(Void)>, Void>(
delay(diskDelay), [=](Void _) -> Future<Void> { return file->sync(); });
}
Future<int64_t> size() const override {
double diskDelay = getDelay();
if (diskDelay == 0.0)
return file->size();
// Wait for diskDelay before submitting the I/O
return mapAsync<Void, std::function<Future<int64_t>(Void)>, int64_t>(
delay(diskDelay), [=](Void _) -> Future<int64_t> { return file->size(); });
}
int64_t debugFD() const override { return file->debugFD(); }
std::string getFilename() const override { return file->getFilename(); }
};

View File

@ -195,7 +195,6 @@ public:
void addref() override { ReferenceCounted<AsyncFileKAIO>::addref(); }
void delref() override { ReferenceCounted<AsyncFileKAIO>::delref(); }
Future<int> read(void* data, int length, int64_t offset) override {
++countFileLogicalReads;
++countLogicalReads;

View File

@ -439,7 +439,6 @@ private:
try {
state int rep = wait(self->onRead(self, data, length, offset));
wait(g_simulator.onProcess(currentProcess, currentTaskID));
return rep;
} catch (Error& e) {
state Error err = e;

View File

@ -33,6 +33,7 @@
#define FLOW_ASYNCFILEWINASIO_ACTOR_H
#include <Windows.h>
#include <boost/bind/bind.hpp>
#undef min
#undef max

View File

@ -86,8 +86,23 @@ if(COMPILE_EIO)
target_link_libraries(fdbrpc PRIVATE eio)
target_link_libraries(fdbrpc_sampling PRIVATE eio)
endif()
if(WIN32)
add_library(coro STATIC libcoroutine/Common.c libcoroutine/Coro.c)
target_link_libraries(fdbrpc PRIVATE coro)
target_link_libraries(fdbrpc_sampling PRIVATE coro)
set(CORO_SRCS libcoroutine/Common.c libcoroutine/Coro.c)
if(APPLE)
list(APPEND CORO_SRCS libcoroutine/asm.S)
endif()
if(NOT WIN32)
list(APPEND CORO_SRCS libcoroutine/context.c)
endif()
add_library(coro STATIC ${CORO_SRCS})
if(WIN32)
target_compile_definitions(coro PRIVATE USE_FIBERS)
else()
target_compile_definitions(coro PRIVATE USE_UCONTEXT)
target_compile_options(coro BEFORE PRIVATE -w) # disable warnings for third party
endif()
if(USE_VALGRIND)
target_link_libraries(coro PUBLIC Valgrind)
endif()
target_link_libraries(fdbrpc PRIVATE coro)
target_link_libraries(fdbrpc_sampling PRIVATE coro)

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