#include "src/lib/types.cpp" #include "src/lib/dvalue.cpp" #include "src/lib/functionlib.cpp" #include "src/lib/fastcgi_request_spec.h" #include #include #include struct DValueBeforeNumeric { char type = 'S'; String _String; f64 _float = 0; s64 _array_index = 0; bool _bool = false; bool _list_mode = false; void* _ptr = 0; u32 refs = 1; std::map _map; }; static_assert(sizeof(DValue) == offsetof(DValueBeforeNumeric, _map) + sizeof(DValueEntries)); static_assert(alignof(DValue) == alignof(DValueBeforeNumeric)); static_assert(offsetof(DValue, _float) == offsetof(DValueBeforeNumeric, _float)); static_assert(offsetof(DValue, _map) == offsetof(DValueBeforeNumeric, _map)); static void same_numeric(const DValue& source, const DValue& value) { assert(value.type == source.type && value.numeric_equal(source)); assert(value.to_string() == source.to_string()); assert(value.to_stringmap()["value"] == source.to_string()); assert(json_encode(value) == source.to_string()); } static void copies_and_codecs(const DValue& source) { DValue copied(source); same_numeric(source, copied); DValue moved(std::move(copied)); same_numeric(source, moved); DValue assigned; assigned.set(source); same_numeric(source, assigned); assigned.set(std::move(moved)); same_numeric(source, assigned); assigned.set("replace type"); assigned.assign_shared(source); same_numeric(source, assigned); DValue tree; DValue* shared = new DValue(source); tree.put_node("shared", shared); DValue::unref(shared); DValue shared_copy; shared_copy.assign_shared(tree); assert(shared_copy.key("shared") == tree.key("shared")); same_numeric(source, shared_copy["shared"]); tree["number"] = source; tree["text"] = source.to_string(); tree["list"].set_array(); tree["list"].push(source); DValue clone(tree); same_numeric(source, clone["number"]); same_numeric(source, clone["list"][0]); assert(clone["text"].type == 'S'); for(bool local : {false, true}) { String wire = local ? brb_encode_local(tree) : brb_encode(tree); DValue decoded; String error; assert(local ? brb_decode_local(wire, decoded, &error) : brb_decode(wire, decoded, &error)); same_numeric(source, decoded["number"]); same_numeric(source, decoded["list"][0]); assert(decoded["text"].type == 'S'); assert(!decoded.is_list() && decoded["list"].is_list()); } assert(yaml_encode(source) == source.to_string() + "\n"); assert(xml_encode(source, "number") == "" + source.to_string() + ""); } int main() { std::cout << "DValue baseline=" << sizeof(DValueBeforeNumeric) << " numeric=" << sizeof(DValue) << " align=" << alignof(DValue) << " payload_offset=" << offsetof(DValue, _float) << " map_offset=" << offsetof(DValue, _map) << "\n"; for(s64 value : {std::numeric_limits::min(), (s64)-9007199254740993LL, (s64)-1, (s64)0, (s64)1, (s64)9007199254740991LL, (s64)9007199254740992LL, (s64)9007199254740993LL, std::numeric_limits::max()}) { DValue number; number.set_s64(value); assert(number.get_type_name() == "s64" && number.to_s64() == value); assert(DValue(" " + std::to_string(value) + " ").to_s64(7) == value); assert(number.to_u64() == (value < 0 ? 0 : (u64)value)); assert(number.to_bool() == (value != 0)); copies_and_codecs(number); DValue floating; floating.set((f64)value); bool exact = value != std::numeric_limits::max() && (s64)(f64)value == value; assert(number.numeric_equal(floating) == exact); assert(floating.numeric_equal(number) == exact); } for(u64 value : {0ULL, 1ULL, 9007199254740991ULL, 9007199254740992ULL, 9007199254740993ULL, 9223372036854775807ULL, 9223372036854775808ULL, 18446744073709551615ULL}) { DValue number; number.set_u64(value); assert(number.get_type_name() == "u64" && number.to_u64() == value); assert(DValue("+" + std::to_string(value)).to_u64(7) == value); assert(number.to_s64() == (value > (u64)std::numeric_limits::max() ? std::numeric_limits::max() : (s64)value)); assert(number.to_bool() == (value != 0)); copies_and_codecs(number); DValue floating; floating.set((f64)value); bool exact = value != std::numeric_limits::max() && (u64)(f64)value == value; assert(number.numeric_equal(floating) == exact); assert(floating.numeric_equal(number) == exact); } for(f64 value : {0.12345678901234567, 1.0000000000000002, 1e-200, 1e200}) { DValue number; number.set(value); String text = number.to_string(); assert(strtod(text.c_str(), nullptr) == value); assert(number.to_stringmap()["value"] == text); DValue record; record["number"].set(value); assert(record.to_stringmap()["number"] == text); } assert(DValue("1.9").to_s64() == 1 && DValue("1e3").to_u64() == 1000); assert(DValue("invalid").to_s64(7) == 7 && DValue("-1").to_u64(7) == 0); DValue native(std::numeric_limits::max()); assert(native.type == 'U' && native.to_u64() == std::numeric_limits::max()); native = std::numeric_limits::min(); assert(native.type == 'I' && native.to_s64() == std::numeric_limits::min()); native.set(true); assert(native.type == 'B' && native.to_bool()); assert(native.to_string() == "true" && native.to_stringmap()["value"] == "true"); native = false; assert(native.type == 'B' && !native.to_bool()); assert(native.to_string() == "false" && native.to_stringmap()["value"] == "false"); DValue metadata; metadata["insert_id"] = std::numeric_limits::max(); assert(json_encode(metadata) == "{\"insert_id\": 18446744073709551615}"); DValue left, right; left.set_type('I'); right.set_type('U'); assert(left.to_s64() == 0 && right.to_u64() == 0 && left.numeric_equal(right)); right.set(-0.0); assert(left.numeric_equal(right) && right.numeric_equal(left)); left.set_s64(-1); right.set_u64(std::numeric_limits::max()); assert(!left.numeric_equal(right) && !right.numeric_equal(left)); left.set_s64(9007199254740993LL); right.set_u64(9007199254740993ULL); assert(left.numeric_equal(right) && right.numeric_equal(left)); right.set_u64(9007199254740992ULL); assert(!left.numeric_equal(right)); for(f64 number : {0.5, std::numeric_limits::infinity(), -std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}) { right.set(number); assert(!left.numeric_equal(right) && !right.numeric_equal(left)); } right.set("9007199254740993"); assert(!left.numeric_equal(right)); for(char tag : {'I', 'U'}) { for(const String text : std::initializer_list{"", "+1", "1.5", "1e2", " 1", "1 ", "18446744073709551616", "-9223372036854775809", String("1\0", 2)}) { String wire("BRRB\2\0", 6); wire += tag; wire += (char)text.size(); wire += text; wire += '\0'; DValue out; String error; assert(!brb_decode(wire, out, &error) && !error.empty()); } } DValue decoded; String error; assert(!brb_decode(String("BRRB\2\0U\2-1\0", 11), decoded, &error)); assert(!brb_decode(String("BRRB\2\0I\23", 8) + "9223372036854775808" + '\0', decoded, &error)); DValue options, clean, option_error; options["port"].set_u64(9000); options["timeout_ms"].set_s64(100); assert(fastcgi_request_spec(options, clean, option_error)); assert(clean["port"].to_u64() == 9000 && clean["timeout_ms"].to_s64() == 100); assert(json_decode("9007199254740993").type == 'I'); assert(json_decode("9007199254740993").to_s64() == 9007199254740993LL); }