diff --git a/DEPENDENCIES b/DEPENDENCIES index 69d3884..59f083c 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -3,4 +3,4 @@ noa https://github.com/sourcemeta/noa 517e88aef5981b88ac6bb8caff15d17dffcb4320 jsontoolkit https://github.com/sourcemeta/jsontoolkit b5c8f63fbc4b4b7a9cd4bdd71774d89db6ee2a99 hydra https://github.com/sourcemeta/hydra 3c53d3fdef79e9ba603d48470a508cc45472a0dc alterschema https://github.com/sourcemeta/alterschema 358df64771979da64e043a416cf340d83a5382ca -jsonbinpack https://github.com/sourcemeta/jsonbinpack 0c8caafaa27f74c7124cba5663f2083b79a3ef7d +jsonbinpack https://github.com/sourcemeta/jsonbinpack 3046d0b9820b7da7f10645ca5fae6b0ffb749422 diff --git a/vendor/jsonbinpack/src/runtime/CMakeLists.txt b/vendor/jsonbinpack/src/runtime/CMakeLists.txt index a62fc05..81db0a8 100644 --- a/vendor/jsonbinpack/src/runtime/CMakeLists.txt +++ b/vendor/jsonbinpack/src/runtime/CMakeLists.txt @@ -1,11 +1,40 @@ noa_library(NAMESPACE sourcemeta PROJECT jsonbinpack NAME runtime FOLDER "JSON BinPack/Runtime" PRIVATE_HEADERS - decoder.h decoder_basic.h - encoder.h encoder_basic.h encoder_context.h - plan.h plan_wrap.h parser.h + decoder.h + encoder.h + input_stream.h + output_stream.h + encoder_cache.h + encoding.h + SOURCES + input_stream.cc + output_stream.cc varint.h - SOURCES runtime_parser.cc runtime_parser_v1.h) + unreachable.h + cache.cc + + loader.cc + loader_v1_any.h + loader_v1_array.h + loader_v1_integer.h + loader_v1_number.h + loader_v1_string.h + + decoder_any.cc + decoder_array.cc + decoder_common.cc + decoder_integer.cc + decoder_number.cc + decoder_object.cc + decoder_string.cc + encoder_any.cc + encoder_array.cc + encoder_common.cc + encoder_integer.cc + encoder_number.cc + encoder_object.cc + encoder_string.cc) if(JSONBINPACK_INSTALL) noa_library_install(NAMESPACE sourcemeta PROJECT jsonbinpack NAME runtime) diff --git a/vendor/jsonbinpack/src/runtime/cache.cc b/vendor/jsonbinpack/src/runtime/cache.cc new file mode 100644 index 0000000..0b09cb2 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/cache.cc @@ -0,0 +1,66 @@ +#include + +namespace sourcemeta::jsonbinpack { + +auto Cache::record(const sourcemeta::jsontoolkit::JSON::String &value, + const std::uint64_t offset, const Type type) -> void { + // Encoding a shared string has some overhead, such as the + // shared string marker + the offset, so its not worth + // doing for strings that are too small. + constexpr auto MINIMUM_STRING_LENGTH{3}; + + // We don't want to allow the context to grow + // forever, otherwise an attacker could force the + // program to exhaust memory given an input + // document that contains a high number of large strings. + constexpr auto MAXIMUM_BYTE_SIZE{20971520}; + + const auto value_size{value.size()}; + if (value_size < MINIMUM_STRING_LENGTH || value_size >= MAXIMUM_BYTE_SIZE) { + return; + } + + // Remove the oldest entries to make space if needed + while (!this->data.empty() && + this->byte_size + value_size >= MAXIMUM_BYTE_SIZE) { + this->remove_oldest(); + } + + auto result{this->data.insert({std::make_pair(value, type), offset})}; + if (result.second) { + this->byte_size += value_size; + this->order.emplace(offset, result.first->first); + } else if (offset > result.first->second) { + this->order.erase(result.first->second); + // If the string already exists, we want to + // bump the offset for locality purposes. + result.first->second = offset; + this->order.emplace(offset, result.first->first); + } + + // Otherwise we are doing something wrong + assert(this->order.size() == this->data.size()); +} + +auto Cache::remove_oldest() -> void { + assert(!this->data.empty()); + // std::map are by definition ordered by key, + // so the begin iterator points to the entry + // with the lowest offset, a.k.a. the oldest. + const auto iterator{this->order.cbegin()}; + this->byte_size -= iterator->second.get().first.size(); + this->data.erase(iterator->second.get()); + this->order.erase(iterator); +} + +auto Cache::find(const sourcemeta::jsontoolkit::JSON::String &value, + const Type type) const -> std::optional { + const auto result{this->data.find(std::make_pair(value, type))}; + if (result == this->data.cend()) { + return std::nullopt; + } + + return result->second; +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_any.cc b/vendor/jsonbinpack/src/runtime/decoder_any.cc new file mode 100644 index 0000000..44445b7 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_any.cc @@ -0,0 +1,158 @@ +#include +#include + +#include "unreachable.h" + +#include // assert +#include // std::uint8_t, std::uint16_t, std::uint64_t +#include // std::make_shared + +namespace sourcemeta::jsonbinpack { + +auto Decoder::BYTE_CHOICE_INDEX(const struct BYTE_CHOICE_INDEX &options) + -> sourcemeta::jsontoolkit::JSON { + assert(!options.choices.empty()); + assert(is_byte(options.choices.size())); + const std::uint8_t index{this->get_byte()}; + assert(options.choices.size() > index); + return options.choices[index]; +} + +auto Decoder::LARGE_CHOICE_INDEX(const struct LARGE_CHOICE_INDEX &options) + -> sourcemeta::jsontoolkit::JSON { + assert(!options.choices.empty()); + const std::uint64_t index{this->get_varint()}; + assert(options.choices.size() > index); + return options.choices[index]; +} + +auto Decoder::TOP_LEVEL_BYTE_CHOICE_INDEX( + const struct TOP_LEVEL_BYTE_CHOICE_INDEX &options) + -> sourcemeta::jsontoolkit::JSON { + assert(!options.choices.empty()); + assert(is_byte(options.choices.size())); + if (!this->has_more_data()) { + return options.choices.front(); + } else { + const std::uint16_t index{static_cast(this->get_byte() + 1)}; + assert(options.choices.size() > index); + return options.choices[index]; + } +} + +auto Decoder::CONST_NONE(const struct CONST_NONE &options) + -> sourcemeta::jsontoolkit::JSON { + return options.value; +} + +auto Decoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( + const struct ANY_PACKED_TYPE_TAG_BYTE_PREFIX &) + -> sourcemeta::jsontoolkit::JSON { + using namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX; + const std::uint8_t byte{this->get_byte()}; + const std::uint8_t type{ + static_cast(byte & (0xff >> subtype_size))}; + const std::uint8_t subtype{static_cast(byte >> type_size)}; + + if (type == TYPE_OTHER) { + switch (subtype) { + case SUBTYPE_NULL: + return sourcemeta::jsontoolkit::JSON{nullptr}; + case SUBTYPE_FALSE: + return sourcemeta::jsontoolkit::JSON{false}; + case SUBTYPE_TRUE: + return sourcemeta::jsontoolkit::JSON{true}; + case SUBTYPE_NUMBER: + return this->DOUBLE_VARINT_TUPLE({}); + case SUBTYPE_POSITIVE_REAL_INTEGER_BYTE: + return sourcemeta::jsontoolkit::JSON{ + static_cast(this->get_byte())}; + case SUBTYPE_POSITIVE_INTEGER: + return sourcemeta::jsontoolkit::JSON{ + static_cast(this->get_varint())}; + case SUBTYPE_NEGATIVE_INTEGER: + return sourcemeta::jsontoolkit::JSON{ + -static_cast(this->get_varint()) - 1}; + case SUBTYPE_LONG_STRING_BASE_EXPONENT_7: + return sourcemeta::jsontoolkit::JSON{ + this->get_string_utf8(this->get_varint() + 128)}; + case SUBTYPE_LONG_STRING_BASE_EXPONENT_8: + return sourcemeta::jsontoolkit::JSON{ + this->get_string_utf8(this->get_varint() + 256)}; + case SUBTYPE_LONG_STRING_BASE_EXPONENT_9: + return sourcemeta::jsontoolkit::JSON{ + this->get_string_utf8(this->get_varint() + 512)}; + case SUBTYPE_LONG_STRING_BASE_EXPONENT_10: + return sourcemeta::jsontoolkit::JSON{ + this->get_string_utf8(this->get_varint() + 1024)}; + } + + // We should never get here. If so, it is definitely a bug + unreachable(); + } else { + switch (type) { + case TYPE_POSITIVE_INTEGER_BYTE: + return sourcemeta::jsontoolkit::JSON{subtype > 0 ? subtype - 1 + : this->get_byte()}; + case TYPE_NEGATIVE_INTEGER_BYTE: + return sourcemeta::jsontoolkit::JSON{ + subtype > 0 ? static_cast(-subtype) + : static_cast(-this->get_byte() - 1)}; + case TYPE_SHARED_STRING: { + const auto length = subtype == 0 + ? this->get_varint() - 1 + uint_max<5> * 2 + : subtype - 1; + const std::uint64_t position{this->position()}; + const std::uint64_t current{this->rewind(this->get_varint(), position)}; + const sourcemeta::jsontoolkit::JSON value{ + this->get_string_utf8(length)}; + this->seek(current); + return value; + }; + case TYPE_STRING: + return subtype == 0 ? this->FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( + {uint_max<5> * 2}) + : sourcemeta::jsontoolkit::JSON{ + this->get_string_utf8(subtype - 1)}; + case TYPE_LONG_STRING: + return sourcemeta::jsontoolkit::JSON{ + this->get_string_utf8(subtype + uint_max<5>)}; + case TYPE_ARRAY: + return subtype == 0 ? this->FIXED_TYPED_ARRAY( + {this->get_varint() + uint_max<5>, + std::make_shared( + sourcemeta::jsonbinpack:: + ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}), + {}}) + : this->FIXED_TYPED_ARRAY( + {static_cast(subtype - 1), + std::make_shared( + sourcemeta::jsonbinpack:: + ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}), + {}}); + case TYPE_OBJECT: + return subtype == 0 + ? this->FIXED_TYPED_ARBITRARY_OBJECT( + {this->get_varint() + uint_max<5>, + std::make_shared( + sourcemeta::jsonbinpack:: + PREFIX_VARINT_LENGTH_STRING_SHARED{}), + std::make_shared( + sourcemeta::jsonbinpack:: + ANY_PACKED_TYPE_TAG_BYTE_PREFIX{})}) + : this->FIXED_TYPED_ARBITRARY_OBJECT( + {static_cast(subtype - 1), + std::make_shared( + sourcemeta::jsonbinpack:: + PREFIX_VARINT_LENGTH_STRING_SHARED{}), + std::make_shared( + sourcemeta::jsonbinpack:: + ANY_PACKED_TYPE_TAG_BYTE_PREFIX{})}); + } + + // We should never get here. If so, it is definitely a bug + unreachable(); + } +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_array.cc b/vendor/jsonbinpack/src/runtime/decoder_array.cc new file mode 100644 index 0000000..0f151c6 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_array.cc @@ -0,0 +1,57 @@ +#include +#include + +#include // assert +#include // std::uint8_t, std::uint64_t +#include // std::move + +namespace sourcemeta::jsonbinpack { + +auto Decoder::FIXED_TYPED_ARRAY(const struct FIXED_TYPED_ARRAY &options) + -> sourcemeta::jsontoolkit::JSON { + const auto prefix_encodings{options.prefix_encodings.size()}; + sourcemeta::jsontoolkit::JSON result = + sourcemeta::jsontoolkit::JSON::make_array(); + for (std::size_t index = 0; index < options.size; index++) { + const Encoding &encoding{prefix_encodings > index + ? options.prefix_encodings[index] + : *(options.encoding)}; + result.push_back(this->read(encoding)); + } + + assert(result.size() == options.size); + return result; +}; + +auto Decoder::BOUNDED_8BITS_TYPED_ARRAY( + const struct BOUNDED_8BITS_TYPED_ARRAY &options) + -> sourcemeta::jsontoolkit::JSON { + assert(options.maximum >= options.minimum); + assert(is_byte(options.maximum - options.minimum)); + const std::uint8_t byte{this->get_byte()}; + const std::uint64_t size{byte + options.minimum}; + assert(is_within(size, options.minimum, options.maximum)); + return this->FIXED_TYPED_ARRAY( + {size, std::move(options.encoding), std::move(options.prefix_encodings)}); +}; + +auto Decoder::FLOOR_TYPED_ARRAY(const struct FLOOR_TYPED_ARRAY &options) + -> sourcemeta::jsontoolkit::JSON { + const std::uint64_t value{this->get_varint()}; + const std::uint64_t size{value + options.minimum}; + assert(size >= value); + assert(size >= options.minimum); + return this->FIXED_TYPED_ARRAY( + {size, std::move(options.encoding), std::move(options.prefix_encodings)}); +}; + +auto Decoder::ROOF_TYPED_ARRAY(const struct ROOF_TYPED_ARRAY &options) + -> sourcemeta::jsontoolkit::JSON { + const std::uint64_t value{this->get_varint()}; + const std::uint64_t size{options.maximum - value}; + assert(size <= options.maximum); + return this->FIXED_TYPED_ARRAY( + {size, std::move(options.encoding), std::move(options.prefix_encodings)}); +}; + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_common.cc b/vendor/jsonbinpack/src/runtime/decoder_common.cc new file mode 100644 index 0000000..47b7092 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_common.cc @@ -0,0 +1,46 @@ +#include + +#include "unreachable.h" + +#include // assert +#include // std::get + +namespace sourcemeta::jsonbinpack { + +Decoder::Decoder(Stream &input) : InputStream{input} {} + +auto Decoder::read(const Encoding &encoding) -> sourcemeta::jsontoolkit::JSON { + switch (encoding.index()) { +#define HANDLE_DECODING(index, name) \ + case (index): \ + return this->name(std::get(encoding)); + HANDLE_DECODING(0, BOUNDED_MULTIPLE_8BITS_ENUM_FIXED) + HANDLE_DECODING(1, FLOOR_MULTIPLE_ENUM_VARINT) + HANDLE_DECODING(2, ROOF_MULTIPLE_MIRROR_ENUM_VARINT) + HANDLE_DECODING(3, ARBITRARY_MULTIPLE_ZIGZAG_VARINT) + HANDLE_DECODING(4, DOUBLE_VARINT_TUPLE) + HANDLE_DECODING(5, BYTE_CHOICE_INDEX) + HANDLE_DECODING(6, LARGE_CHOICE_INDEX) + HANDLE_DECODING(7, TOP_LEVEL_BYTE_CHOICE_INDEX) + HANDLE_DECODING(8, CONST_NONE) + HANDLE_DECODING(9, ANY_PACKED_TYPE_TAG_BYTE_PREFIX) + HANDLE_DECODING(10, UTF8_STRING_NO_LENGTH) + HANDLE_DECODING(11, FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) + HANDLE_DECODING(12, ROOF_VARINT_PREFIX_UTF8_STRING_SHARED) + HANDLE_DECODING(13, BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED) + HANDLE_DECODING(14, RFC3339_DATE_INTEGER_TRIPLET) + HANDLE_DECODING(15, PREFIX_VARINT_LENGTH_STRING_SHARED) + HANDLE_DECODING(16, FIXED_TYPED_ARRAY) + HANDLE_DECODING(17, BOUNDED_8BITS_TYPED_ARRAY) + HANDLE_DECODING(18, FLOOR_TYPED_ARRAY) + HANDLE_DECODING(19, ROOF_TYPED_ARRAY) + HANDLE_DECODING(20, FIXED_TYPED_ARBITRARY_OBJECT) + HANDLE_DECODING(21, VARINT_TYPED_ARBITRARY_OBJECT) +#undef HANDLE_DECODING + } + + // We should never get here. If so, it is definitely a bug + unreachable(); +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_integer.cc b/vendor/jsonbinpack/src/runtime/decoder_integer.cc new file mode 100644 index 0000000..71c2eca --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_integer.cc @@ -0,0 +1,92 @@ +#include +#include + +#include // assert +#include // std::uint8_t, std::uint32_t, std::int64_t, std::uint64_t + +namespace sourcemeta::jsonbinpack { + +auto Decoder::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( + const struct BOUNDED_MULTIPLE_8BITS_ENUM_FIXED &options) + -> sourcemeta::jsontoolkit::JSON { + assert(options.multiplier > 0); + const std::uint8_t byte{this->get_byte()}; + const std::int64_t closest_minimum{ + divide_ceil(options.minimum, options.multiplier)}; + if (closest_minimum >= 0) { + const std::uint64_t closest_minimum_multiple{ + static_cast(closest_minimum) * options.multiplier}; + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{static_cast( + (byte * options.multiplier) + closest_minimum_multiple)}; + } else { + const std::uint64_t closest_minimum_multiple{abs(closest_minimum) * + options.multiplier}; + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{static_cast( + (byte * options.multiplier) - closest_minimum_multiple)}; + } +} + +auto Decoder::FLOOR_MULTIPLE_ENUM_VARINT( + const struct FLOOR_MULTIPLE_ENUM_VARINT &options) + -> sourcemeta::jsontoolkit::JSON { + assert(options.multiplier > 0); + const std::int64_t closest_minimum{ + divide_ceil(options.minimum, options.multiplier)}; + if (closest_minimum >= 0) { + const std::uint64_t closest_minimum_multiple{ + static_cast(closest_minimum) * options.multiplier}; + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{static_cast( + (this->get_varint() * options.multiplier) + closest_minimum_multiple)}; + } else { + const std::uint64_t closest_minimum_multiple{abs(closest_minimum) * + options.multiplier}; + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{static_cast( + (this->get_varint() * options.multiplier) - closest_minimum_multiple)}; + } +} + +auto Decoder::ROOF_MULTIPLE_MIRROR_ENUM_VARINT( + const struct ROOF_MULTIPLE_MIRROR_ENUM_VARINT &options) + -> sourcemeta::jsontoolkit::JSON { + assert(options.multiplier > 0); + const std::int64_t closest_maximum{ + divide_floor(options.maximum, options.multiplier)}; + if (closest_maximum >= 0) { + const std::uint64_t closest_maximum_multiple{ + static_cast(closest_maximum) * options.multiplier}; + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{static_cast( + -(static_cast(this->get_varint() * options.multiplier)) + + static_cast(closest_maximum_multiple))}; + } else { + const std::uint64_t closest_maximum_multiple{abs(closest_maximum) * + options.multiplier}; + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{static_cast( + -(static_cast(this->get_varint() * options.multiplier)) - + static_cast(closest_maximum_multiple))}; + } +} + +auto Decoder::ARBITRARY_MULTIPLE_ZIGZAG_VARINT( + const struct ARBITRARY_MULTIPLE_ZIGZAG_VARINT &options) + -> sourcemeta::jsontoolkit::JSON { + assert(options.multiplier > 0); + // We trust the encoder that the data we are seeing + // corresponds to a valid 64-bit signed integer. + return sourcemeta::jsontoolkit::JSON{ + static_cast(this->get_varint_zigzag() * + static_cast(options.multiplier))}; +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_number.cc b/vendor/jsonbinpack/src/runtime/decoder_number.cc new file mode 100644 index 0000000..2d369e4 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_number.cc @@ -0,0 +1,16 @@ +#include + +#include // std::pow +#include // std::int64_t, std::uint64_t + +namespace sourcemeta::jsonbinpack { + +auto Decoder::DOUBLE_VARINT_TUPLE(const struct DOUBLE_VARINT_TUPLE &) + -> sourcemeta::jsontoolkit::JSON { + const std::int64_t digits{this->get_varint_zigzag()}; + const std::uint64_t point{this->get_varint()}; + const double divisor{std::pow(10, static_cast(point))}; + return sourcemeta::jsontoolkit::JSON{static_cast(digits) / divisor}; +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_object.cc b/vendor/jsonbinpack/src/runtime/decoder_object.cc new file mode 100644 index 0000000..d1eee16 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_object.cc @@ -0,0 +1,41 @@ +#include + +#include // assert +#include // std::uint64_t + +namespace sourcemeta::jsonbinpack { + +auto Decoder::FIXED_TYPED_ARBITRARY_OBJECT( + const struct FIXED_TYPED_ARBITRARY_OBJECT &options) + -> sourcemeta::jsontoolkit::JSON { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::JSON::make_object(); + for (std::size_t index = 0; index < options.size; index++) { + const sourcemeta::jsontoolkit::JSON key = + this->read(*(options.key_encoding)); + assert(key.is_string()); + document.assign(key.to_string(), this->read(*(options.encoding))); + } + + assert(document.size() == options.size); + return document; +}; + +auto Decoder::VARINT_TYPED_ARBITRARY_OBJECT( + const struct VARINT_TYPED_ARBITRARY_OBJECT &options) + -> sourcemeta::jsontoolkit::JSON { + const std::uint64_t size{this->get_varint()}; + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::JSON::make_object(); + for (std::size_t index = 0; index < size; index++) { + const sourcemeta::jsontoolkit::JSON key = + this->read(*(options.key_encoding)); + assert(key.is_string()); + document.assign(key.to_string(), this->read(*(options.encoding))); + } + + assert(document.size() == size); + return document; +}; + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/decoder_string.cc b/vendor/jsonbinpack/src/runtime/decoder_string.cc new file mode 100644 index 0000000..f9b8d37 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/decoder_string.cc @@ -0,0 +1,119 @@ +#include + +#include // assert +#include // std::uint8_t, std::uint16_t, std::uint64_t +#include // std::setw, std::setfill +#include // std::basic_ostringstream + +namespace sourcemeta::jsonbinpack { + +auto Decoder::UTF8_STRING_NO_LENGTH(const struct UTF8_STRING_NO_LENGTH &options) + -> sourcemeta::jsontoolkit::JSON { + return sourcemeta::jsontoolkit::JSON{this->get_string_utf8(options.size)}; +} + +auto Decoder::FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( + const struct FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED &options) + -> sourcemeta::jsontoolkit::JSON { + const std::uint64_t prefix{this->get_varint()}; + const bool is_shared{prefix == 0}; + const std::uint64_t length{(is_shared ? this->get_varint() : prefix) + + options.minimum - 1}; + assert(length >= options.minimum); + + if (is_shared) { + const std::uint64_t position{this->position()}; + const std::uint64_t current{this->rewind(this->get_varint(), position)}; + const sourcemeta::jsontoolkit::JSON value{this->get_string_utf8(length)}; + this->seek(current); + return value; + } else { + return UTF8_STRING_NO_LENGTH({length}); + } +} + +auto Decoder::ROOF_VARINT_PREFIX_UTF8_STRING_SHARED( + const struct ROOF_VARINT_PREFIX_UTF8_STRING_SHARED &options) + -> sourcemeta::jsontoolkit::JSON { + const std::uint64_t prefix{this->get_varint()}; + const bool is_shared{prefix == 0}; + const std::uint64_t length{options.maximum - + (is_shared ? this->get_varint() : prefix) + 1}; + assert(length <= options.maximum); + + if (is_shared) { + const std::uint64_t position{this->position()}; + const std::uint64_t current{this->rewind(this->get_varint(), position)}; + const sourcemeta::jsontoolkit::JSON value{UTF8_STRING_NO_LENGTH({length})}; + this->seek(current); + return value; + } else { + return UTF8_STRING_NO_LENGTH({length}); + } +} + +auto Decoder::BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( + const struct BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED &options) + -> sourcemeta::jsontoolkit::JSON { + assert(options.minimum <= options.maximum); + assert(is_byte(options.maximum - options.minimum)); + const std::uint8_t prefix{this->get_byte()}; + const bool is_shared{prefix == 0}; + const std::uint64_t length{(is_shared ? this->get_byte() : prefix) + + options.minimum - 1}; + assert(is_within(length, options.minimum, options.maximum)); + + if (is_shared) { + const std::uint64_t position{this->position()}; + const std::uint64_t current{this->rewind(this->get_varint(), position)}; + const sourcemeta::jsontoolkit::JSON value{UTF8_STRING_NO_LENGTH({length})}; + this->seek(current); + return value; + } else { + return UTF8_STRING_NO_LENGTH({length}); + } +} + +auto Decoder::RFC3339_DATE_INTEGER_TRIPLET( + const struct RFC3339_DATE_INTEGER_TRIPLET &) + -> sourcemeta::jsontoolkit::JSON { + const std::uint16_t year{this->get_word()}; + const std::uint8_t month{this->get_byte()}; + const std::uint8_t day{this->get_byte()}; + + assert(year <= 9999); + assert(month >= 1 && month <= 12); + assert(day >= 1 && day <= 31); + + std::basic_ostringstream + output; + output << std::setfill('0'); + output << std::setw(4) << year; + output << "-"; + // Cast the bytes to a larger integer, otherwise + // they will be interpreted as characters. + output << std::setw(2) << static_cast(month); + output << "-"; + output << std::setw(2) << static_cast(day); + + return sourcemeta::jsontoolkit::JSON{output.str()}; +} + +auto Decoder::PREFIX_VARINT_LENGTH_STRING_SHARED( + const struct PREFIX_VARINT_LENGTH_STRING_SHARED &options) + -> sourcemeta::jsontoolkit::JSON { + const std::uint64_t prefix{this->get_varint()}; + if (prefix == 0) { + const std::uint64_t position{this->position()}; + const std::uint64_t current{this->rewind(this->get_varint(), position)}; + const sourcemeta::jsontoolkit::JSON value{ + PREFIX_VARINT_LENGTH_STRING_SHARED(options)}; + this->seek(current); + return value; + } else { + return sourcemeta::jsontoolkit::JSON{this->get_string_utf8(prefix - 1)}; + } +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_any.cc b/vendor/jsonbinpack/src/runtime/encoder_any.cc new file mode 100644 index 0000000..8ce4a4b --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_any.cc @@ -0,0 +1,198 @@ +#include +#include + +#include "unreachable.h" + +#include // std::find_if +#include // assert +#include // std::uint8_t, std::int64_t, std::uint64_t +#include // std::cbegin, std::cend, std::distance +#include // std::make_shared +#include // std::move + +namespace sourcemeta::jsonbinpack { + +auto Encoder::BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &document, + const struct BYTE_CHOICE_INDEX &options) + -> void { + assert(!options.choices.empty()); + assert(is_byte(options.choices.size())); + const auto iterator{std::find_if( + std::cbegin(options.choices), std::cend(options.choices), + [&document](const auto &choice) { return choice == document; })}; + assert(iterator != std::cend(options.choices)); + const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; + assert( + is_within(cursor, 0, static_cast(options.choices.size()))); + this->put_byte(static_cast(cursor)); +} + +auto Encoder::LARGE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &document, + const struct LARGE_CHOICE_INDEX &options) + -> void { + assert(options.choices.size() > 0); + const auto iterator{std::find_if( + std::cbegin(options.choices), std::cend(options.choices), + [&document](const auto &choice) { return choice == document; })}; + assert(iterator != std::cend(options.choices)); + const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; + assert(is_within(cursor, static_cast(0), + options.choices.size() - 1)); + this->put_varint(static_cast(cursor)); +} + +auto Encoder::TOP_LEVEL_BYTE_CHOICE_INDEX( + const sourcemeta::jsontoolkit::JSON &document, + const struct TOP_LEVEL_BYTE_CHOICE_INDEX &options) -> void { + assert(options.choices.size() > 0); + assert(is_byte(options.choices.size())); + const auto iterator{std::find_if( + std::cbegin(options.choices), std::cend(options.choices), + [&document](auto const &choice) { return choice == document; })}; + assert(iterator != std::cend(options.choices)); + const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; + assert(is_within(cursor, 0, + static_cast(options.choices.size()) - 1)); + // This encoding encodes the first option of the enum as "no data" + if (cursor > 0) { + this->put_byte(static_cast(cursor - 1)); + } +} + +auto Encoder::CONST_NONE( +#ifndef NDEBUG + const sourcemeta::jsontoolkit::JSON &document, + const struct CONST_NONE &options) +#else + const sourcemeta::jsontoolkit::JSON &, const struct CONST_NONE &) +#endif + -> void { + assert(document == options.value); +} + +auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( + const sourcemeta::jsontoolkit::JSON &document, + const struct ANY_PACKED_TYPE_TAG_BYTE_PREFIX &) -> void { + using namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX; + if (document.is_null()) { + this->put_byte(TYPE_OTHER | (SUBTYPE_NULL << type_size)); + } else if (document.is_boolean()) { + const std::uint8_t subtype{document.to_boolean() ? SUBTYPE_TRUE + : SUBTYPE_FALSE}; + this->put_byte(TYPE_OTHER | + static_cast(subtype << type_size)); + } else if (document.is_integer_real()) { + const auto value{document.as_integer()}; + if (value >= 0 && is_byte(value)) { + this->put_byte(TYPE_OTHER | SUBTYPE_POSITIVE_REAL_INTEGER_BYTE + << type_size); + this->put_byte(static_cast(value)); + } else { + this->put_byte(TYPE_OTHER | SUBTYPE_NUMBER << type_size); + this->DOUBLE_VARINT_TUPLE(document, {}); + } + } else if (document.is_real()) { + this->put_byte(TYPE_OTHER | SUBTYPE_NUMBER << type_size); + this->DOUBLE_VARINT_TUPLE(document, {}); + } else if (document.is_integer()) { + const std::int64_t value{document.to_integer()}; + const bool is_positive{value >= 0}; + const std::uint64_t absolute{is_positive ? static_cast(value) + : abs(value) - 1}; + if (is_byte(absolute)) { + const std::uint8_t type{is_positive ? TYPE_POSITIVE_INTEGER_BYTE + : TYPE_NEGATIVE_INTEGER_BYTE}; + const std::uint8_t absolute_byte{static_cast(absolute)}; + if (absolute < uint_max<5>) { + this->put_byte( + type | static_cast((absolute_byte + 1) << type_size)); + } else { + this->put_byte(type); + this->put_byte(absolute_byte); + } + } else { + const std::uint8_t subtype{is_positive ? SUBTYPE_POSITIVE_INTEGER + : SUBTYPE_NEGATIVE_INTEGER}; + this->put_byte(TYPE_OTHER | + static_cast(subtype << type_size)); + this->put_varint(absolute); + } + } else if (document.is_string()) { + const sourcemeta::jsontoolkit::JSON::String value{document.to_string()}; + const auto size{document.byte_size()}; + const auto shared{this->cache_.find(value, Cache::Type::Standalone)}; + if (size < uint_max<5>) { + const std::uint8_t type{shared.has_value() ? TYPE_SHARED_STRING + : TYPE_STRING}; + this->put_byte( + static_cast(type | ((size + 1) << type_size))); + if (shared.has_value()) { + this->put_varint(this->position() - shared.value()); + } else { + this->cache_.record(value, this->position(), Cache::Type::Standalone); + this->put_string_utf8(value, size); + } + } else if (size >= uint_max<5> && size < uint_max<5> * 2 && + !shared.has_value()) { + this->put_byte(static_cast( + TYPE_LONG_STRING | ((size - uint_max<5>) << type_size))); + this->put_string_utf8(value, size); + } else if (size >= 2 << (SUBTYPE_LONG_STRING_BASE_EXPONENT_7 - 1) && + !shared.has_value()) { + const std::uint8_t exponent{closest_smallest_exponent( + size, 2, SUBTYPE_LONG_STRING_BASE_EXPONENT_7, + SUBTYPE_LONG_STRING_BASE_EXPONENT_10)}; + this->put_byte( + static_cast(TYPE_OTHER | (exponent << type_size))); + this->put_varint(size - static_cast(2 << (exponent - 1))); + this->put_string_utf8(value, size); + } else { + // Exploit the fact that a shared string always starts + // with an impossible length marker (0) to avoid having + // to encode an additional tag + if (!shared.has_value()) { + this->put_byte(TYPE_STRING); + } + + // If we got this far, the string is at least a certain length + return FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED(document, + {uint_max<5> * 2}); + } + } else if (document.is_array()) { + const auto size{document.size()}; + if (size >= uint_max<5>) { + this->put_byte(TYPE_ARRAY); + this->put_varint(size - uint_max<5>); + } else { + this->put_byte( + static_cast(TYPE_ARRAY | ((size + 1) << type_size))); + } + + Encoding encoding{ + sourcemeta::jsonbinpack::ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}}; + this->FIXED_TYPED_ARRAY( + document, {size, std::make_shared(std::move(encoding)), {}}); + } else if (document.is_object()) { + const auto size{document.size()}; + if (size >= uint_max<5>) { + this->put_byte(TYPE_OBJECT); + this->put_varint(size - uint_max<5>); + } else { + this->put_byte( + static_cast(TYPE_OBJECT | ((size + 1) << type_size))); + } + + Encoding key_encoding{ + sourcemeta::jsonbinpack::PREFIX_VARINT_LENGTH_STRING_SHARED{}}; + Encoding value_encoding{ + sourcemeta::jsonbinpack::ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}}; + this->FIXED_TYPED_ARBITRARY_OBJECT( + document, {size, std::make_shared(std::move(key_encoding)), + std::make_shared(std::move(value_encoding))}); + } else { + // We should never get here + unreachable(); + } +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_array.cc b/vendor/jsonbinpack/src/runtime/encoder_array.cc new file mode 100644 index 0000000..f3b748d --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_array.cc @@ -0,0 +1,56 @@ +#include +#include + +#include // assert +#include // std::uint8_t +#include // std::move + +namespace sourcemeta::jsonbinpack { + +auto Encoder::FIXED_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, + const struct FIXED_TYPED_ARRAY &options) + -> void { + assert(document.is_array()); + assert(document.size() == options.size); + const auto prefix_encodings{options.prefix_encodings.size()}; + assert(prefix_encodings <= document.size()); + for (std::size_t index = 0; index < options.size; index++) { + const Encoding &encoding{prefix_encodings > index + ? options.prefix_encodings[index] + : *(options.encoding)}; + this->write(document.at(index), encoding); + } +} + +auto Encoder::BOUNDED_8BITS_TYPED_ARRAY( + const sourcemeta::jsontoolkit::JSON &document, + const struct BOUNDED_8BITS_TYPED_ARRAY &options) -> void { + assert(options.maximum >= options.minimum); + const auto size{document.size()}; + assert(is_within(size, options.minimum, options.maximum)); + assert(is_byte(options.maximum - options.minimum)); + this->put_byte(static_cast(size - options.minimum)); + this->FIXED_TYPED_ARRAY(document, {size, std::move(options.encoding), + std::move(options.prefix_encodings)}); +} + +auto Encoder::FLOOR_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, + const struct FLOOR_TYPED_ARRAY &options) + -> void { + const auto size{document.size()}; + assert(size >= options.minimum); + this->put_varint(size - options.minimum); + this->FIXED_TYPED_ARRAY(document, {size, std::move(options.encoding), + std::move(options.prefix_encodings)}); +} + +auto Encoder::ROOF_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, + const struct ROOF_TYPED_ARRAY &options) -> void { + const auto size{document.size()}; + assert(size <= options.maximum); + this->put_varint(options.maximum - size); + this->FIXED_TYPED_ARRAY(document, {size, std::move(options.encoding), + std::move(options.prefix_encodings)}); +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_common.cc b/vendor/jsonbinpack/src/runtime/encoder_common.cc new file mode 100644 index 0000000..9972d29 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_common.cc @@ -0,0 +1,48 @@ +#include + +#include "unreachable.h" + +#include // assert +#include // std::get + +namespace sourcemeta::jsonbinpack { + +Encoder::Encoder(Stream &output) : OutputStream{output} {} + +auto Encoder::write(const sourcemeta::jsontoolkit::JSON &document, + const Encoding &encoding) -> void { + switch (encoding.index()) { +#define HANDLE_ENCODING(index, name) \ + case (index): \ + return this->name(document, \ + std::get(encoding)); + HANDLE_ENCODING(0, BOUNDED_MULTIPLE_8BITS_ENUM_FIXED) + HANDLE_ENCODING(1, FLOOR_MULTIPLE_ENUM_VARINT) + HANDLE_ENCODING(2, ROOF_MULTIPLE_MIRROR_ENUM_VARINT) + HANDLE_ENCODING(3, ARBITRARY_MULTIPLE_ZIGZAG_VARINT) + HANDLE_ENCODING(4, DOUBLE_VARINT_TUPLE) + HANDLE_ENCODING(5, BYTE_CHOICE_INDEX) + HANDLE_ENCODING(6, LARGE_CHOICE_INDEX) + HANDLE_ENCODING(7, TOP_LEVEL_BYTE_CHOICE_INDEX) + HANDLE_ENCODING(8, CONST_NONE) + HANDLE_ENCODING(9, ANY_PACKED_TYPE_TAG_BYTE_PREFIX) + HANDLE_ENCODING(10, UTF8_STRING_NO_LENGTH) + HANDLE_ENCODING(11, FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) + HANDLE_ENCODING(12, ROOF_VARINT_PREFIX_UTF8_STRING_SHARED) + HANDLE_ENCODING(13, BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED) + HANDLE_ENCODING(14, RFC3339_DATE_INTEGER_TRIPLET) + HANDLE_ENCODING(15, PREFIX_VARINT_LENGTH_STRING_SHARED) + HANDLE_ENCODING(16, FIXED_TYPED_ARRAY) + HANDLE_ENCODING(17, BOUNDED_8BITS_TYPED_ARRAY) + HANDLE_ENCODING(18, FLOOR_TYPED_ARRAY) + HANDLE_ENCODING(19, ROOF_TYPED_ARRAY) + HANDLE_ENCODING(20, FIXED_TYPED_ARBITRARY_OBJECT) + HANDLE_ENCODING(21, VARINT_TYPED_ARBITRARY_OBJECT) +#undef HANDLE_ENCODING + } + + // We should never get here. If so, it is definitely a bug + unreachable(); +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_integer.cc b/vendor/jsonbinpack/src/runtime/encoder_integer.cc new file mode 100644 index 0000000..ddbc386 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_integer.cc @@ -0,0 +1,77 @@ +#include +#include + +#include // assert +#include // std::uint8_t, std::int64_t, std::uint64_t + +namespace sourcemeta::jsonbinpack { + +auto Encoder::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( + const sourcemeta::jsontoolkit::JSON &document, + const struct BOUNDED_MULTIPLE_8BITS_ENUM_FIXED &options) -> void { + assert(document.is_integer()); + const std::int64_t value{document.to_integer()}; + assert(is_within(value, options.minimum, options.maximum)); + assert(options.multiplier > 0); + assert(abs(value) % options.multiplier == 0); + const std::int64_t enum_minimum{ + divide_ceil(options.minimum, options.multiplier)}; +#ifndef NDEBUG + const std::int64_t enum_maximum{ + divide_floor(options.maximum, options.multiplier)}; +#endif + assert(is_byte(enum_maximum - enum_minimum)); + this->put_byte(static_cast( + (value / static_cast(options.multiplier)) - enum_minimum)); +} + +auto Encoder::FLOOR_MULTIPLE_ENUM_VARINT( + const sourcemeta::jsontoolkit::JSON &document, + const struct FLOOR_MULTIPLE_ENUM_VARINT &options) -> void { + assert(document.is_integer()); + const std::int64_t value{document.to_integer()}; + assert(options.minimum <= value); + assert(options.multiplier > 0); + assert(abs(value) % options.multiplier == 0); + if (options.multiplier == 1) { + return this->put_varint( + static_cast(value - options.minimum)); + } + + return this->put_varint( + (static_cast(value) / options.multiplier) - + static_cast(divide_ceil( + options.minimum, static_cast(options.multiplier)))); +} + +auto Encoder::ROOF_MULTIPLE_MIRROR_ENUM_VARINT( + const sourcemeta::jsontoolkit::JSON &document, + const struct ROOF_MULTIPLE_MIRROR_ENUM_VARINT &options) -> void { + assert(document.is_integer()); + const std::int64_t value{document.to_integer()}; + assert(value <= options.maximum); + assert(options.multiplier > 0); + assert(abs(value) % options.multiplier == 0); + if (options.multiplier == 1) { + return this->put_varint( + static_cast(options.maximum - value)); + } + + return this->put_varint( + static_cast( + divide_floor(options.maximum, options.multiplier)) - + (static_cast(value) / options.multiplier)); +} + +auto Encoder::ARBITRARY_MULTIPLE_ZIGZAG_VARINT( + const sourcemeta::jsontoolkit::JSON &document, + const struct ARBITRARY_MULTIPLE_ZIGZAG_VARINT &options) -> void { + assert(document.is_integer()); + const std::int64_t value{document.to_integer()}; + assert(options.multiplier > 0); + assert(abs(value) % options.multiplier == 0); + this->put_varint_zigzag(value / + static_cast(options.multiplier)); +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_number.cc b/vendor/jsonbinpack/src/runtime/encoder_number.cc new file mode 100644 index 0000000..2688770 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_number.cc @@ -0,0 +1,20 @@ +#include +#include + +#include // assert +#include // std::uint64_t + +namespace sourcemeta::jsonbinpack { + +auto Encoder::DOUBLE_VARINT_TUPLE(const sourcemeta::jsontoolkit::JSON &document, + const struct DOUBLE_VARINT_TUPLE &) -> void { + assert(document.is_real()); + const auto value{document.to_real()}; + std::uint64_t point_position; + const std::int64_t integral{ + real_digits(value, &point_position)}; + this->put_varint_zigzag(integral); + this->put_varint(point_position); +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_object.cc b/vendor/jsonbinpack/src/runtime/encoder_object.cc new file mode 100644 index 0000000..f809f5e --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_object.cc @@ -0,0 +1,32 @@ +#include + +#include // assert + +namespace sourcemeta::jsonbinpack { + +auto Encoder::FIXED_TYPED_ARBITRARY_OBJECT( + const sourcemeta::jsontoolkit::JSON &document, + const struct FIXED_TYPED_ARBITRARY_OBJECT &options) -> void { + assert(document.is_object()); + assert(document.size() == options.size); + + for (const auto &[key, value] : document.as_object()) { + this->write(sourcemeta::jsontoolkit::JSON{key}, *(options.key_encoding)); + this->write(value, *(options.encoding)); + } +} + +auto Encoder::VARINT_TYPED_ARBITRARY_OBJECT( + const sourcemeta::jsontoolkit::JSON &document, + const struct VARINT_TYPED_ARBITRARY_OBJECT &options) -> void { + assert(document.is_object()); + const auto size{document.size()}; + this->put_varint(size); + + for (const auto &[key, value] : document.as_object()) { + this->write(sourcemeta::jsontoolkit::JSON{key}, *(options.key_encoding)); + this->write(value, *(options.encoding)); + } +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/encoder_string.cc b/vendor/jsonbinpack/src/runtime/encoder_string.cc new file mode 100644 index 0000000..4ff6e79 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/encoder_string.cc @@ -0,0 +1,150 @@ +#include + +#include // assert +#include // std::uint8_t, std::uint16_t +#include // std::stoul + +namespace sourcemeta::jsonbinpack { + +auto Encoder::UTF8_STRING_NO_LENGTH( + const sourcemeta::jsontoolkit::JSON &document, + const struct UTF8_STRING_NO_LENGTH &options) -> void { + assert(document.is_string()); + const sourcemeta::jsontoolkit::JSON::String value{document.to_string()}; + this->put_string_utf8(value, options.size); +} + +auto Encoder::FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &document, + const struct FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED &options) -> void { + assert(document.is_string()); + const sourcemeta::jsontoolkit::JSON::String value{document.to_string()}; + const auto size{value.size()}; + assert(document.size() == size); + const auto shared{this->cache_.find(value, Cache::Type::Standalone)}; + + // (1) Write 0x00 if shared, else do nothing + if (shared.has_value()) { + this->put_byte(0); + } + + // (2) Write length of the string + 1 (so it will never be zero) + this->put_varint(size - options.minimum + 1); + + // (3) Write relative offset if shared, else write plain string + if (shared.has_value()) { + this->put_varint(this->position() - shared.value()); + } else { + this->cache_.record(value, this->position(), Cache::Type::Standalone); + this->put_string_utf8(value, size); + } +} + +auto Encoder::ROOF_VARINT_PREFIX_UTF8_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &document, + const struct ROOF_VARINT_PREFIX_UTF8_STRING_SHARED &options) -> void { + assert(document.is_string()); + const sourcemeta::jsontoolkit::JSON::String value{document.to_string()}; + const auto size{value.size()}; + assert(document.size() == size); + assert(size <= options.maximum); + const auto shared{this->cache_.find(value, Cache::Type::Standalone)}; + + // (1) Write 0x00 if shared, else do nothing + if (shared.has_value()) { + this->put_byte(0); + } + + // (2) Write length of the string + 1 (so it will never be zero) + this->put_varint(options.maximum - size + 1); + + // (3) Write relative offset if shared, else write plain string + if (shared.has_value()) { + this->put_varint(this->position() - shared.value()); + } else { + this->cache_.record(value, this->position(), Cache::Type::Standalone); + this->put_string_utf8(value, size); + } +} + +auto Encoder::BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &document, + const struct BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED &options) -> void { + assert(document.is_string()); + const sourcemeta::jsontoolkit::JSON::String value{document.to_string()}; + const auto size{value.size()}; + assert(document.size() == size); + assert(options.minimum <= options.maximum); + assert(is_byte(options.maximum - options.minimum + 1)); + assert(is_within(size, options.minimum, options.maximum)); + const auto shared{this->cache_.find(value, Cache::Type::Standalone)}; + + // (1) Write 0x00 if shared, else do nothing + if (shared.has_value()) { + this->put_byte(0); + } + + // (2) Write length of the string + 1 (so it will never be zero) + this->put_byte(static_cast(size - options.minimum + 1)); + + // (3) Write relative offset if shared, else write plain string + if (shared.has_value()) { + this->put_varint(this->position() - shared.value()); + } else { + this->cache_.record(value, this->position(), Cache::Type::Standalone); + this->put_string_utf8(value, size); + } +} + +auto Encoder::RFC3339_DATE_INTEGER_TRIPLET( + const sourcemeta::jsontoolkit::JSON &document, + const struct RFC3339_DATE_INTEGER_TRIPLET &) -> void { + assert(document.is_string()); + const auto &value{document.to_string()}; + assert(value.size() == 10); + assert(document.size() == value.size()); + + // As according to RFC3339: Internet Protocols MUST + // generate four digit years in dates. + const std::uint16_t year{ + static_cast(std::stoul(value.substr(0, 4)))}; + const std::uint8_t month{ + static_cast(std::stoul(value.substr(5, 2)))}; + const std::uint8_t day{ + static_cast(std::stoul(value.substr(8, 2)))}; + assert(month >= 1 && month <= 12); + assert(day >= 1 && day <= 31); + + this->put_bytes(year); + this->put_byte(month); + this->put_byte(day); +} + +auto Encoder::PREFIX_VARINT_LENGTH_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &document, + const struct PREFIX_VARINT_LENGTH_STRING_SHARED &) -> void { + assert(document.is_string()); + const sourcemeta::jsontoolkit::JSON::String value{document.to_string()}; + + const auto shared{ + this->cache_.find(value, Cache::Type::PrefixLengthVarintPlusOne)}; + if (shared.has_value()) { + const auto new_offset{this->position()}; + this->put_byte(0); + this->put_varint(this->position() - shared.value()); + // Bump the context cache for locality purposes + this->cache_.record(value, new_offset, + Cache::Type::PrefixLengthVarintPlusOne); + } else { + const auto size{value.size()}; + assert(document.size() == size); + this->cache_.record(value, this->position(), + Cache::Type::PrefixLengthVarintPlusOne); + this->put_varint(size + 1); + // Also record a standalone variant of it + this->cache_.record(value, this->position(), Cache::Type::Standalone); + this->put_string_utf8(value, size); + } +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime.h index 8e64f51..63d4666 100644 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime.h +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime.h @@ -10,9 +10,50 @@ /// #include /// ``` +#include "runtime_export.h" + +#include + #include #include -#include -#include +#include + +#include // std::exception +#include // std::move + +namespace sourcemeta::jsonbinpack { + +/// @ingroup runtime +SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT +auto load(const sourcemeta::jsontoolkit::JSON &input) -> Encoding; + +// Exporting symbols that depends on the standard C++ library is considered +// safe. +// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN +#if defined(_MSC_VER) +#pragma warning(disable : 4251 4275) +#endif + +/// @ingroup runtime +/// This class represents an encoding error +class SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT EncodingError + : public std::exception { +public: + EncodingError(sourcemeta::jsontoolkit::JSON::String message) + : message_{std::move(message)} {} + + [[nodiscard]] auto what() const noexcept -> const char * override { + return this->message_.c_str(); + } + +private: + const sourcemeta::jsontoolkit::JSON::String message_; +}; + +#if defined(_MSC_VER) +#pragma warning(default : 4251 4275) +#endif + +} // namespace sourcemeta::jsonbinpack #endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder.h index 7118841..4f1fc4c 100644 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder.h +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder.h @@ -1,493 +1,65 @@ #ifndef SOURCEMETA_JSONBINPACK_RUNTIME_DECODER_H_ #define SOURCEMETA_JSONBINPACK_RUNTIME_DECODER_H_ -#include +#include "runtime_export.h" -#include -#include -#include +#include +#include #include -#include // assert -#include // std::pow -#include // std::uint8_t, std::uint16_t, std::uint32_t, std::int64_t, std::uint64_t -#include // std::abort -#include // std::setw, std::setfill -#include // std::basic_istream -#include // std::basic_ostringstream - namespace sourcemeta::jsonbinpack { /// @ingroup runtime -template -class Decoder : private BasicDecoder { +class SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT Decoder : private InputStream { public: - Decoder(std::basic_istream &input) - : BasicDecoder{input} {} - - auto decode(const Plan &encoding) -> sourcemeta::jsontoolkit::JSON { - switch (encoding.index()) { -#define HANDLE_DECODING(index, name) \ - case (index): \ - return this->name(std::get(encoding)); - HANDLE_DECODING(0, BOUNDED_MULTIPLE_8BITS_ENUM_FIXED) - HANDLE_DECODING(1, FLOOR_MULTIPLE_ENUM_VARINT) - HANDLE_DECODING(2, ROOF_MULTIPLE_MIRROR_ENUM_VARINT) - HANDLE_DECODING(3, ARBITRARY_MULTIPLE_ZIGZAG_VARINT) - HANDLE_DECODING(4, DOUBLE_VARINT_TUPLE) - HANDLE_DECODING(5, BYTE_CHOICE_INDEX) - HANDLE_DECODING(6, LARGE_CHOICE_INDEX) - HANDLE_DECODING(7, TOP_LEVEL_BYTE_CHOICE_INDEX) - HANDLE_DECODING(8, CONST_NONE) - HANDLE_DECODING(9, UTF8_STRING_NO_LENGTH) - HANDLE_DECODING(10, FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) - HANDLE_DECODING(11, ROOF_VARINT_PREFIX_UTF8_STRING_SHARED) - HANDLE_DECODING(12, BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED) - HANDLE_DECODING(13, RFC3339_DATE_INTEGER_TRIPLET) - HANDLE_DECODING(14, PREFIX_VARINT_LENGTH_STRING_SHARED) - HANDLE_DECODING(15, FIXED_TYPED_ARRAY) - HANDLE_DECODING(16, BOUNDED_8BITS_TYPED_ARRAY) - HANDLE_DECODING(17, FLOOR_TYPED_ARRAY) - HANDLE_DECODING(18, ROOF_TYPED_ARRAY) - HANDLE_DECODING(19, FIXED_TYPED_ARBITRARY_OBJECT) - HANDLE_DECODING(20, VARINT_TYPED_ARBITRARY_OBJECT) - HANDLE_DECODING(21, ANY_PACKED_TYPE_TAG_BYTE_PREFIX) -#undef HANDLE_DECODING - } - - // We should never get here. If so, it is definitely a bug - assert(false); - std::abort(); - } + Decoder(Stream &input); + auto read(const Encoding &encoding) -> sourcemeta::jsontoolkit::JSON; // The methods that implement individual encodings as considered private #ifndef DOXYGEN - - auto BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( - const BOUNDED_MULTIPLE_8BITS_ENUM_FIXED &options) - -> sourcemeta::jsontoolkit::JSON { - assert(options.multiplier > 0); - const std::uint8_t byte{this->get_byte()}; - const std::int64_t closest_minimum{ - divide_ceil(options.minimum, options.multiplier)}; - if (closest_minimum >= 0) { - const std::uint64_t closest_minimum_multiple{ - static_cast(closest_minimum) * options.multiplier}; - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{static_cast( - (byte * options.multiplier) + closest_minimum_multiple)}; - } else { - const std::uint64_t closest_minimum_multiple{abs(closest_minimum) * - options.multiplier}; - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{static_cast( - (byte * options.multiplier) - closest_minimum_multiple)}; - } - } - - auto FLOOR_MULTIPLE_ENUM_VARINT(const FLOOR_MULTIPLE_ENUM_VARINT &options) - -> sourcemeta::jsontoolkit::JSON { - assert(options.multiplier > 0); - const std::int64_t closest_minimum{ - divide_ceil(options.minimum, options.multiplier)}; - if (closest_minimum >= 0) { - const std::uint64_t closest_minimum_multiple{ - static_cast(closest_minimum) * options.multiplier}; - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{ - static_cast((this->get_varint() * options.multiplier) + - closest_minimum_multiple)}; - } else { - const std::uint64_t closest_minimum_multiple{abs(closest_minimum) * - options.multiplier}; - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{ - static_cast((this->get_varint() * options.multiplier) - - closest_minimum_multiple)}; - } - } - - auto ROOF_MULTIPLE_MIRROR_ENUM_VARINT( - const ROOF_MULTIPLE_MIRROR_ENUM_VARINT &options) - -> sourcemeta::jsontoolkit::JSON { - assert(options.multiplier > 0); - const std::int64_t closest_maximum{ - divide_floor(options.maximum, options.multiplier)}; - if (closest_maximum >= 0) { - const std::uint64_t closest_maximum_multiple{ - static_cast(closest_maximum) * options.multiplier}; - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{static_cast( - -(static_cast(this->get_varint() * - options.multiplier)) + - static_cast(closest_maximum_multiple))}; - } else { - const std::uint64_t closest_maximum_multiple{abs(closest_maximum) * - options.multiplier}; - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{static_cast( - -(static_cast(this->get_varint() * - options.multiplier)) - - static_cast(closest_maximum_multiple))}; - } - } - - auto ARBITRARY_MULTIPLE_ZIGZAG_VARINT( - const ARBITRARY_MULTIPLE_ZIGZAG_VARINT &options) - -> sourcemeta::jsontoolkit::JSON { - assert(options.multiplier > 0); - // We trust the encoder that the data we are seeing - // corresponds to a valid 64-bit signed integer. - return sourcemeta::jsontoolkit::JSON{static_cast( - this->get_varint_zigzag() * - static_cast(options.multiplier))}; - } - - auto DOUBLE_VARINT_TUPLE(const DOUBLE_VARINT_TUPLE &) - -> sourcemeta::jsontoolkit::JSON { - const std::int64_t digits{this->get_varint_zigzag()}; - const std::uint64_t point{this->get_varint()}; - const double divisor{std::pow(10, static_cast(point))}; - return sourcemeta::jsontoolkit::JSON{static_cast(digits) / divisor}; - } - - auto BYTE_CHOICE_INDEX(const BYTE_CHOICE_INDEX &options) - -> sourcemeta::jsontoolkit::JSON { - assert(!options.choices.empty()); - assert(is_byte(options.choices.size())); - const std::uint8_t index{this->get_byte()}; - assert(options.choices.size() > index); - return options.choices[index]; - } - - auto LARGE_CHOICE_INDEX(const LARGE_CHOICE_INDEX &options) - -> sourcemeta::jsontoolkit::JSON { - assert(!options.choices.empty()); - const std::uint64_t index{this->get_varint()}; - assert(options.choices.size() > index); - return options.choices[index]; - } - - auto TOP_LEVEL_BYTE_CHOICE_INDEX(const TOP_LEVEL_BYTE_CHOICE_INDEX &options) - -> sourcemeta::jsontoolkit::JSON { - assert(!options.choices.empty()); - assert(is_byte(options.choices.size())); - if (!this->has_more_data()) { - return options.choices.front(); - } else { - const std::uint16_t index{ - static_cast(this->get_byte() + 1)}; - assert(options.choices.size() > index); - return options.choices[index]; - } - } - - auto CONST_NONE(const CONST_NONE &options) -> sourcemeta::jsontoolkit::JSON { - return options.value; - } - - auto UTF8_STRING_NO_LENGTH(const UTF8_STRING_NO_LENGTH &options) - -> sourcemeta::jsontoolkit::JSON { - return sourcemeta::jsontoolkit::JSON{this->get_string_utf8(options.size)}; - } - - auto FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( - const FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED &options) - -> sourcemeta::jsontoolkit::JSON { - const std::uint64_t prefix{this->get_varint()}; - const bool is_shared{prefix == 0}; - const std::uint64_t length{(is_shared ? this->get_varint() : prefix) + - options.minimum - 1}; - assert(length >= options.minimum); - - if (is_shared) { - const std::uint64_t position{this->position()}; - const std::uint64_t current{this->rewind(this->get_varint(), position)}; - const sourcemeta::jsontoolkit::JSON value{this->get_string_utf8(length)}; - this->seek(current); - return value; - } else { - return UTF8_STRING_NO_LENGTH({length}); - } - } - - auto ROOF_VARINT_PREFIX_UTF8_STRING_SHARED( - const ROOF_VARINT_PREFIX_UTF8_STRING_SHARED &options) - -> sourcemeta::jsontoolkit::JSON { - const std::uint64_t prefix{this->get_varint()}; - const bool is_shared{prefix == 0}; - const std::uint64_t length{options.maximum - - (is_shared ? this->get_varint() : prefix) + 1}; - assert(length <= options.maximum); - - if (is_shared) { - const std::uint64_t position{this->position()}; - const std::uint64_t current{this->rewind(this->get_varint(), position)}; - const sourcemeta::jsontoolkit::JSON value{ - UTF8_STRING_NO_LENGTH({length})}; - this->seek(current); - return value; - } else { - return UTF8_STRING_NO_LENGTH({length}); - } - } - - auto BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( - const BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED &options) - -> sourcemeta::jsontoolkit::JSON { - assert(options.minimum <= options.maximum); - assert(is_byte(options.maximum - options.minimum)); - const std::uint8_t prefix{this->get_byte()}; - const bool is_shared{prefix == 0}; - const std::uint64_t length{(is_shared ? this->get_byte() : prefix) + - options.minimum - 1}; - assert(is_within(length, options.minimum, options.maximum)); - - if (is_shared) { - const std::uint64_t position{this->position()}; - const std::uint64_t current{this->rewind(this->get_varint(), position)}; - const sourcemeta::jsontoolkit::JSON value{ - UTF8_STRING_NO_LENGTH({length})}; - this->seek(current); - return value; - } else { - return UTF8_STRING_NO_LENGTH({length}); - } - } - - auto RFC3339_DATE_INTEGER_TRIPLET(const RFC3339_DATE_INTEGER_TRIPLET &) - -> sourcemeta::jsontoolkit::JSON { - const std::uint16_t year{this->get_word()}; - const std::uint8_t month{this->get_byte()}; - const std::uint8_t day{this->get_byte()}; - - assert(year <= 9999); - assert(month >= 1 && month <= 12); - assert(day >= 1 && day <= 31); - - std::basic_ostringstream output; - output << std::setfill('0'); - output << std::setw(4) << year; - output << "-"; - // Cast the bytes to a larger integer, otherwise - // they will be interpreted as characters. - output << std::setw(2) << static_cast(month); - output << "-"; - output << std::setw(2) << static_cast(day); - - return sourcemeta::jsontoolkit::JSON{output.str()}; - } - - auto PREFIX_VARINT_LENGTH_STRING_SHARED( - const PREFIX_VARINT_LENGTH_STRING_SHARED &options) - -> sourcemeta::jsontoolkit::JSON { - const std::uint64_t prefix{this->get_varint()}; - if (prefix == 0) { - const std::uint64_t position{this->position()}; - const std::uint64_t current{this->rewind(this->get_varint(), position)}; - const sourcemeta::jsontoolkit::JSON value{ - PREFIX_VARINT_LENGTH_STRING_SHARED(options)}; - this->seek(current); - return value; - } else { - return sourcemeta::jsontoolkit::JSON{this->get_string_utf8(prefix - 1)}; - } - } - +#define DECLARE_ENCODING(name) \ + auto name(const name &) -> sourcemeta::jsontoolkit::JSON; + + // Integer + DECLARE_ENCODING(BOUNDED_MULTIPLE_8BITS_ENUM_FIXED) + DECLARE_ENCODING(FLOOR_MULTIPLE_ENUM_VARINT) + DECLARE_ENCODING(ROOF_MULTIPLE_MIRROR_ENUM_VARINT) + DECLARE_ENCODING(ARBITRARY_MULTIPLE_ZIGZAG_VARINT) + + // Number + DECLARE_ENCODING(DOUBLE_VARINT_TUPLE) + + // Any + DECLARE_ENCODING(BYTE_CHOICE_INDEX) + DECLARE_ENCODING(LARGE_CHOICE_INDEX) + DECLARE_ENCODING(TOP_LEVEL_BYTE_CHOICE_INDEX) + DECLARE_ENCODING(CONST_NONE) + DECLARE_ENCODING(ANY_PACKED_TYPE_TAG_BYTE_PREFIX) + + // String + DECLARE_ENCODING(UTF8_STRING_NO_LENGTH) + DECLARE_ENCODING(FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) + DECLARE_ENCODING(ROOF_VARINT_PREFIX_UTF8_STRING_SHARED) + DECLARE_ENCODING(BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED) + DECLARE_ENCODING(RFC3339_DATE_INTEGER_TRIPLET) + DECLARE_ENCODING(PREFIX_VARINT_LENGTH_STRING_SHARED) // TODO: Implement STRING_BROTLI encoding // TODO: Implement STRING_DICTIONARY_COMPRESSOR encoding // TODO: Implement STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH encoding // TODO: Implement URL_PROTOCOL_HOST_REST encoding - auto FIXED_TYPED_ARRAY(const FIXED_TYPED_ARRAY &options) - -> sourcemeta::jsontoolkit::JSON { - const auto prefix_encodings{options.prefix_encodings.size()}; - sourcemeta::jsontoolkit::JSON result = - sourcemeta::jsontoolkit::JSON::make_array(); - for (std::size_t index = 0; index < options.size; index++) { - const Plan &encoding{prefix_encodings > index - ? options.prefix_encodings[index].value - : options.encoding->value}; - result.push_back(this->decode(encoding)); - } - - assert(result.size() == options.size); - return result; - }; - - auto BOUNDED_8BITS_TYPED_ARRAY(const BOUNDED_8BITS_TYPED_ARRAY &options) - -> sourcemeta::jsontoolkit::JSON { - assert(options.maximum >= options.minimum); - assert(is_byte(options.maximum - options.minimum)); - const std::uint8_t byte{this->get_byte()}; - const std::uint64_t size{byte + options.minimum}; - assert(is_within(size, options.minimum, options.maximum)); - return this->FIXED_TYPED_ARRAY({size, std::move(options.encoding), - std::move(options.prefix_encodings)}); - }; - - auto FLOOR_TYPED_ARRAY(const FLOOR_TYPED_ARRAY &options) - -> sourcemeta::jsontoolkit::JSON { - const std::uint64_t value{this->get_varint()}; - const std::uint64_t size{value + options.minimum}; - assert(size >= value); - assert(size >= options.minimum); - return this->FIXED_TYPED_ARRAY({size, std::move(options.encoding), - std::move(options.prefix_encodings)}); - }; - - auto ROOF_TYPED_ARRAY(const ROOF_TYPED_ARRAY &options) - -> sourcemeta::jsontoolkit::JSON { - const std::uint64_t value{this->get_varint()}; - const std::uint64_t size{options.maximum - value}; - assert(size <= options.maximum); - return this->FIXED_TYPED_ARRAY({size, std::move(options.encoding), - std::move(options.prefix_encodings)}); - }; - - auto FIXED_TYPED_ARBITRARY_OBJECT(const FIXED_TYPED_ARBITRARY_OBJECT &options) - -> sourcemeta::jsontoolkit::JSON { - sourcemeta::jsontoolkit::JSON document = - sourcemeta::jsontoolkit::JSON::make_object(); - for (std::size_t index = 0; index < options.size; index++) { - const sourcemeta::jsontoolkit::JSON key = - this->decode(options.key_encoding->value); - assert(key.is_string()); - document.assign(key.to_string(), this->decode(options.encoding->value)); - } - - assert(document.size() == options.size); - return document; - }; - - auto - VARINT_TYPED_ARBITRARY_OBJECT(const VARINT_TYPED_ARBITRARY_OBJECT &options) - -> sourcemeta::jsontoolkit::JSON { - const std::uint64_t size{this->get_varint()}; - sourcemeta::jsontoolkit::JSON document = - sourcemeta::jsontoolkit::JSON::make_object(); - for (std::size_t index = 0; index < size; index++) { - const sourcemeta::jsontoolkit::JSON key = - this->decode(options.key_encoding->value); - assert(key.is_string()); - document.assign(key.to_string(), this->decode(options.encoding->value)); - } - - assert(document.size() == size); - return document; - }; - - auto ANY_PACKED_TYPE_TAG_BYTE_PREFIX(const ANY_PACKED_TYPE_TAG_BYTE_PREFIX &) - -> sourcemeta::jsontoolkit::JSON { - using namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX; - const std::uint8_t byte{this->get_byte()}; - const std::uint8_t type{ - static_cast(byte & (0xff >> subtype_size))}; - const std::uint8_t subtype{static_cast(byte >> type_size)}; - - if (type == TYPE_OTHER) { - switch (subtype) { - case SUBTYPE_NULL: - return sourcemeta::jsontoolkit::JSON{nullptr}; - case SUBTYPE_FALSE: - return sourcemeta::jsontoolkit::JSON{false}; - case SUBTYPE_TRUE: - return sourcemeta::jsontoolkit::JSON{true}; - case SUBTYPE_NUMBER: - return this->DOUBLE_VARINT_TUPLE({}); - case SUBTYPE_POSITIVE_INTEGER: - return sourcemeta::jsontoolkit::JSON{ - static_cast(this->get_varint())}; - case SUBTYPE_NEGATIVE_INTEGER: - return sourcemeta::jsontoolkit::JSON{ - -static_cast(this->get_varint()) - 1}; - case SUBTYPE_LONG_STRING_BASE_EXPONENT_7: - return sourcemeta::jsontoolkit::JSON{ - this->get_string_utf8(this->get_varint() + 128)}; - case SUBTYPE_LONG_STRING_BASE_EXPONENT_8: - return sourcemeta::jsontoolkit::JSON{ - this->get_string_utf8(this->get_varint() + 256)}; - case SUBTYPE_LONG_STRING_BASE_EXPONENT_9: - return sourcemeta::jsontoolkit::JSON{ - this->get_string_utf8(this->get_varint() + 512)}; - case SUBTYPE_LONG_STRING_BASE_EXPONENT_10: - return sourcemeta::jsontoolkit::JSON{ - this->get_string_utf8(this->get_varint() + 1024)}; - } - - // We should never get here. If so, it is definitely a bug - assert(false); - std::abort(); - } else { - switch (type) { - case TYPE_POSITIVE_INTEGER_BYTE: - return sourcemeta::jsontoolkit::JSON{subtype > 0 ? subtype - 1 - : this->get_byte()}; - case TYPE_NEGATIVE_INTEGER_BYTE: - return sourcemeta::jsontoolkit::JSON{ - subtype > 0 ? static_cast(-subtype) - : static_cast(-this->get_byte() - 1)}; - case TYPE_SHARED_STRING: { - const auto length = subtype == 0 - ? this->get_varint() - 1 + uint_max<5> * 2 - : subtype - 1; - const std::uint64_t position{this->position()}; - const std::uint64_t current{ - this->rewind(this->get_varint(), position)}; - const sourcemeta::jsontoolkit::JSON value{ - this->get_string_utf8(length)}; - this->seek(current); - return value; - }; - case TYPE_STRING: - return subtype == 0 ? this->FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( - {uint_max<5> * 2}) - : sourcemeta::jsontoolkit::JSON{ - this->get_string_utf8(subtype - 1)}; - case TYPE_LONG_STRING: - return sourcemeta::jsontoolkit::JSON{ - this->get_string_utf8(subtype + uint_max<5>)}; - case TYPE_ARRAY: - return subtype == 0 - ? this->FIXED_TYPED_ARRAY( - {this->get_varint() + uint_max<5>, - wrap(sourcemeta::jsonbinpack:: - ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}), - {}}) - : this->FIXED_TYPED_ARRAY( - {static_cast(subtype - 1), - wrap(sourcemeta::jsonbinpack:: - ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}), - {}}); - case TYPE_OBJECT: - return subtype == 0 - ? this->FIXED_TYPED_ARBITRARY_OBJECT( - {this->get_varint() + uint_max<5>, - wrap(sourcemeta::jsonbinpack:: - PREFIX_VARINT_LENGTH_STRING_SHARED{}), - wrap(sourcemeta::jsonbinpack:: - ANY_PACKED_TYPE_TAG_BYTE_PREFIX{})}) - : this->FIXED_TYPED_ARBITRARY_OBJECT( - {static_cast(subtype - 1), - wrap(sourcemeta::jsonbinpack:: - PREFIX_VARINT_LENGTH_STRING_SHARED{}), - wrap(sourcemeta::jsonbinpack:: - ANY_PACKED_TYPE_TAG_BYTE_PREFIX{})}); - } + // Array + DECLARE_ENCODING(FIXED_TYPED_ARRAY) + DECLARE_ENCODING(BOUNDED_8BITS_TYPED_ARRAY) + DECLARE_ENCODING(FLOOR_TYPED_ARRAY) + DECLARE_ENCODING(ROOF_TYPED_ARRAY) - // We should never get here. If so, it is definitely a bug - assert(false); - std::abort(); - } - } + // Object + DECLARE_ENCODING(FIXED_TYPED_ARBITRARY_OBJECT) + DECLARE_ENCODING(VARINT_TYPED_ARBITRARY_OBJECT) +#undef DECLARE_ENCODING #endif }; diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder_basic.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder_basic.h deleted file mode 100644 index 74914d0..0000000 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_decoder_basic.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_DECODER_BASIC_H_ -#define SOURCEMETA_JSONBINPACK_RUNTIME_DECODER_BASIC_H_ -#ifndef DOXYGEN - -#include - -#include - -#include // assert -#include // std::ceil -#include // std::uint8_t, std::int64_t, std::uint64_t -#include // std::ios_base -#include // std::basic_istream - -namespace sourcemeta::jsonbinpack { - -template class BasicDecoder { -public: - BasicDecoder(std::basic_istream &input) : stream{input} { - this->stream.exceptions(std::ios_base::badbit | std::ios_base::failbit | - std::ios_base::eofbit); - } - - // Prevent copying, as this class is tied to a stream resource - BasicDecoder(const BasicDecoder &) = delete; - auto operator=(const BasicDecoder &) -> BasicDecoder & = delete; - - inline auto position() const noexcept -> std::uint64_t { - return static_cast(this->stream.tellg()); - } - - inline auto seek(const std::uint64_t offset) -> void { - this->stream.seekg(static_cast(offset)); - } - - // Seek backwards given a relative offset - inline auto rewind(const std::uint64_t relative_offset, - const std::uint64_t position) -> std::uint64_t { - assert(position >= relative_offset); - const std::uint64_t offset{position - relative_offset}; - assert(offset < position); - const std::uint64_t current{this->position()}; - this->seek(offset); - return current; - } - - inline auto get_byte() -> std::uint8_t { - return static_cast(this->stream.get()); - } - - // A "word" corresponds to two bytes - // See https://stackoverflow.com/questions/28066462/how-many-bits-is-a-word - inline auto get_word() -> std::uint16_t { - std::uint16_t word; - this->stream.read(reinterpret_cast(&word), sizeof word); - return word; - } - - inline auto get_varint() -> std::uint64_t { - return varint_decode(this->stream); - } - - inline auto get_varint_zigzag() -> std::int64_t { - const std::uint64_t value = varint_decode(this->stream); - return zigzag_decode(value); - } - - inline auto has_more_data() const noexcept -> bool { - // A way to check if the stream is empty without using `.peek()`, - // which throws given we set exceptions on the EOF bit. - // However, `in_avail()` works on characters and will return zero - // if all that's remaining is 0x00 (null), so we need to handle - // that case separately. - return this->stream.rdbuf()->in_avail() > 0 || - this->stream.rdbuf()->sgetc() == '\0'; - } - - inline auto get_string_utf8(const std::uint64_t length) - -> std::basic_string { - std::basic_string result; - result.reserve(length); - std::uint64_t counter = 0; - while (counter < length) { - result += static_cast(this->get_byte()); - counter += 1; - } - - assert(counter == length); - assert(result.size() == length); - return result; - } - -private: - std::basic_istream &stream; -}; - -} // namespace sourcemeta::jsonbinpack - -#endif -#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder.h index 1dd8aa7..b1ecde9 100644 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder.h +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder.h @@ -1,547 +1,72 @@ #ifndef SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_H_ #define SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_H_ -#include +#include "runtime_export.h" -#include -#include -#include +#include +#include +#include #include -#include // std::uint8_t, std::uint16_t, std::int64_t, std::uint64_t -#include // std::abort -#include // std::basic_ostream -#include // std::basic_string, std::stoul -#include // std::move - namespace sourcemeta::jsonbinpack { /// @ingroup runtime -template -class Encoder : private BasicEncoder { +class SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT Encoder : private OutputStream { public: - Encoder(std::basic_ostream &output) - : BasicEncoder{output} {} - - auto encode(const sourcemeta::jsontoolkit::JSON &document, - const Plan &encoding) -> void { - switch (encoding.index()) { -#define HANDLE_ENCODING(index, name) \ - case (index): \ - return this->name(document, \ - std::get(encoding)); - HANDLE_ENCODING(0, BOUNDED_MULTIPLE_8BITS_ENUM_FIXED) - HANDLE_ENCODING(1, FLOOR_MULTIPLE_ENUM_VARINT) - HANDLE_ENCODING(2, ROOF_MULTIPLE_MIRROR_ENUM_VARINT) - HANDLE_ENCODING(3, ARBITRARY_MULTIPLE_ZIGZAG_VARINT) - HANDLE_ENCODING(4, DOUBLE_VARINT_TUPLE) - HANDLE_ENCODING(5, BYTE_CHOICE_INDEX) - HANDLE_ENCODING(6, LARGE_CHOICE_INDEX) - HANDLE_ENCODING(7, TOP_LEVEL_BYTE_CHOICE_INDEX) - HANDLE_ENCODING(8, CONST_NONE) - HANDLE_ENCODING(9, UTF8_STRING_NO_LENGTH) - HANDLE_ENCODING(10, FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) - HANDLE_ENCODING(11, ROOF_VARINT_PREFIX_UTF8_STRING_SHARED) - HANDLE_ENCODING(12, BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED) - HANDLE_ENCODING(13, RFC3339_DATE_INTEGER_TRIPLET) - HANDLE_ENCODING(14, PREFIX_VARINT_LENGTH_STRING_SHARED) - HANDLE_ENCODING(15, FIXED_TYPED_ARRAY) - HANDLE_ENCODING(16, BOUNDED_8BITS_TYPED_ARRAY) - HANDLE_ENCODING(17, FLOOR_TYPED_ARRAY) - HANDLE_ENCODING(18, ROOF_TYPED_ARRAY) - HANDLE_ENCODING(19, FIXED_TYPED_ARBITRARY_OBJECT) - HANDLE_ENCODING(20, VARINT_TYPED_ARBITRARY_OBJECT) - HANDLE_ENCODING(21, ANY_PACKED_TYPE_TAG_BYTE_PREFIX) -#undef HANDLE_ENCODING - } - - // We should never get here. If so, it is definitely a bug - assert(false); - std::abort(); - } + Encoder(Stream &output); + auto write(const sourcemeta::jsontoolkit::JSON &document, + const Encoding &encoding) -> void; // The methods that implement individual encodings as considered private #ifndef DOXYGEN - - auto BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( - const sourcemeta::jsontoolkit::JSON &document, - const BOUNDED_MULTIPLE_8BITS_ENUM_FIXED &options) -> void { - assert(document.is_integer()); - const std::int64_t value{document.to_integer()}; - assert(is_within(value, options.minimum, options.maximum)); - assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); - const std::int64_t enum_minimum{ - divide_ceil(options.minimum, options.multiplier)}; -#ifndef NDEBUG - const std::int64_t enum_maximum{ - divide_floor(options.maximum, options.multiplier)}; -#endif - assert(is_byte(enum_maximum - enum_minimum)); - this->put_byte(static_cast( - (value / static_cast(options.multiplier)) - - enum_minimum)); - } - - auto FLOOR_MULTIPLE_ENUM_VARINT(const sourcemeta::jsontoolkit::JSON &document, - const FLOOR_MULTIPLE_ENUM_VARINT &options) - -> void { - assert(document.is_integer()); - const std::int64_t value{document.to_integer()}; - assert(options.minimum <= value); - assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); - if (options.multiplier == 1) { - return this->put_varint( - static_cast(value - options.minimum)); - } - - return this->put_varint( - (static_cast(value) / options.multiplier) - - static_cast(divide_ceil( - options.minimum, static_cast(options.multiplier)))); - } - - auto ROOF_MULTIPLE_MIRROR_ENUM_VARINT( - const sourcemeta::jsontoolkit::JSON &document, - const ROOF_MULTIPLE_MIRROR_ENUM_VARINT &options) -> void { - assert(document.is_integer()); - const std::int64_t value{document.to_integer()}; - assert(value <= options.maximum); - assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); - if (options.multiplier == 1) { - return this->put_varint( - static_cast(options.maximum - value)); - } - - return this->put_varint( - static_cast( - divide_floor(options.maximum, options.multiplier)) - - (static_cast(value) / options.multiplier)); - } - - auto ARBITRARY_MULTIPLE_ZIGZAG_VARINT( - const sourcemeta::jsontoolkit::JSON &document, - const ARBITRARY_MULTIPLE_ZIGZAG_VARINT &options) -> void { - assert(document.is_integer()); - const std::int64_t value{document.to_integer()}; - assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); - this->put_varint_zigzag(value / - static_cast(options.multiplier)); - } - - auto DOUBLE_VARINT_TUPLE(const sourcemeta::jsontoolkit::JSON &document, - const DOUBLE_VARINT_TUPLE &) -> void { - assert(document.is_real()); - const auto value{document.to_real()}; - std::uint64_t point_position; - const std::int64_t integral{ - real_digits(value, &point_position)}; - this->put_varint_zigzag(integral); - this->put_varint(point_position); - } - - auto BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &document, - const BYTE_CHOICE_INDEX &options) -> void { - assert(!options.choices.empty()); - assert(is_byte(options.choices.size())); - const auto iterator{std::find_if( - std::cbegin(options.choices), std::cend(options.choices), - [&document](const auto &choice) { return choice == document; })}; - assert(iterator != std::cend(options.choices)); - const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; - assert(is_within(cursor, 0, - static_cast(options.choices.size()))); - this->put_byte(static_cast(cursor)); - } - - auto LARGE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &document, - const LARGE_CHOICE_INDEX &options) -> void { - assert(options.choices.size() > 0); - const auto iterator{std::find_if( - std::cbegin(options.choices), std::cend(options.choices), - [&document](const auto &choice) { return choice == document; })}; - assert(iterator != std::cend(options.choices)); - const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; - assert(is_within(cursor, static_cast(0), - options.choices.size() - 1)); - this->put_varint(static_cast(cursor)); - } - - auto - TOP_LEVEL_BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &document, - const TOP_LEVEL_BYTE_CHOICE_INDEX &options) - -> void { - assert(options.choices.size() > 0); - assert(is_byte(options.choices.size())); - const auto iterator{std::find_if( - std::cbegin(options.choices), std::cend(options.choices), - [&document](auto const &choice) { return choice == document; })}; - assert(iterator != std::cend(options.choices)); - const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; - assert(is_within(cursor, 0, - static_cast(options.choices.size()) - 1)); - // This encoding encodes the first option of the enum as "no data" - if (cursor > 0) { - this->put_byte(static_cast(cursor - 1)); - } - } - - auto CONST_NONE( -#ifndef NDEBUG - const sourcemeta::jsontoolkit::JSON &document, const CONST_NONE &options) -#else - const sourcemeta::jsontoolkit::JSON &, const CONST_NONE &) -#endif - -> void { - assert(document == options.value); - } - - auto UTF8_STRING_NO_LENGTH(const sourcemeta::jsontoolkit::JSON &document, - const UTF8_STRING_NO_LENGTH &options) -> void { - assert(document.is_string()); - const std::basic_string value{document.to_string()}; - this->put_string_utf8(value, options.size); - } - - auto FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &document, - const FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED &options) -> void { - assert(document.is_string()); - const std::basic_string value{document.to_string()}; - const auto size{value.size()}; - assert(document.size() == size); - const bool is_shared{this->context().has(value, ContextType::Standalone)}; - - // (1) Write 0x00 if shared, else do nothing - if (is_shared) { - this->put_byte(0); - } - - // (2) Write length of the string + 1 (so it will never be zero) - this->put_varint(size - options.minimum + 1); - - // (3) Write relative offset if shared, else write plain string - if (is_shared) { - this->put_varint(this->position() - - this->context().offset(value, ContextType::Standalone)); - } else { - this->context().record(value, this->position(), ContextType::Standalone); - this->put_string_utf8(value, size); - } - } - - auto ROOF_VARINT_PREFIX_UTF8_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &document, - const ROOF_VARINT_PREFIX_UTF8_STRING_SHARED &options) -> void { - assert(document.is_string()); - const std::basic_string value{document.to_string()}; - const auto size{value.size()}; - assert(document.size() == size); - assert(size <= options.maximum); - const bool is_shared{this->context().has(value, ContextType::Standalone)}; - - // (1) Write 0x00 if shared, else do nothing - if (is_shared) { - this->put_byte(0); - } - - // (2) Write length of the string + 1 (so it will never be zero) - this->put_varint(options.maximum - size + 1); - - // (3) Write relative offset if shared, else write plain string - if (is_shared) { - this->put_varint(this->position() - - this->context().offset(value, ContextType::Standalone)); - } else { - this->context().record(value, this->position(), ContextType::Standalone); - this->put_string_utf8(value, size); - } - } - - auto BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &document, - const BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED &options) -> void { - assert(document.is_string()); - const std::basic_string value{document.to_string()}; - const auto size{value.size()}; - assert(document.size() == size); - assert(options.minimum <= options.maximum); - assert(is_byte(options.maximum - options.minimum + 1)); - assert(is_within(size, options.minimum, options.maximum)); - const bool is_shared{this->context().has(value, ContextType::Standalone)}; - - // (1) Write 0x00 if shared, else do nothing - if (is_shared) { - this->put_byte(0); - } - - // (2) Write length of the string + 1 (so it will never be zero) - this->put_byte(static_cast(size - options.minimum + 1)); - - // (3) Write relative offset if shared, else write plain string - if (is_shared) { - this->put_varint(this->position() - - this->context().offset(value, ContextType::Standalone)); - } else { - this->context().record(value, this->position(), ContextType::Standalone); - this->put_string_utf8(value, size); - } - } - - auto - RFC3339_DATE_INTEGER_TRIPLET(const sourcemeta::jsontoolkit::JSON &document, - const RFC3339_DATE_INTEGER_TRIPLET &) -> void { - assert(document.is_string()); - const auto &value{document.to_string()}; - assert(value.size() == 10); - assert(document.size() == value.size()); - - // As according to RFC3339: Internet Protocols MUST - // generate four digit years in dates. - const std::uint16_t year{ - static_cast(std::stoul(value.substr(0, 4)))}; - const std::uint8_t month{ - static_cast(std::stoul(value.substr(5, 2)))}; - const std::uint8_t day{ - static_cast(std::stoul(value.substr(8, 2)))}; - assert(month >= 1 && month <= 12); - assert(day >= 1 && day <= 31); - - this->put_bytes(year); - this->put_byte(month); - this->put_byte(day); - } - - auto PREFIX_VARINT_LENGTH_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &document, - const PREFIX_VARINT_LENGTH_STRING_SHARED &) -> void { - assert(document.is_string()); - const std::basic_string value{document.to_string()}; - - if (this->context().has(value, ContextType::PrefixLengthVarintPlusOne)) { - const auto new_offset{this->position()}; - this->put_byte(0); - this->put_varint(this->position() - - this->context().offset( - value, ContextType::PrefixLengthVarintPlusOne)); - // Bump the context cache for locality purposes - this->context().record(value, new_offset, - ContextType::PrefixLengthVarintPlusOne); - } else { - const auto size{value.size()}; - assert(document.size() == size); - this->context().record(value, this->position(), - ContextType::PrefixLengthVarintPlusOne); - this->put_varint(size + 1); - // Also record a standalone variant of it - this->context().record(value, this->position(), ContextType::Standalone); - this->put_string_utf8(value, size); - } - } - +#define DECLARE_ENCODING(name) \ + auto name(const sourcemeta::jsontoolkit::JSON &document, const name &) \ + -> void; + + // Integer + DECLARE_ENCODING(BOUNDED_MULTIPLE_8BITS_ENUM_FIXED) + DECLARE_ENCODING(FLOOR_MULTIPLE_ENUM_VARINT) + DECLARE_ENCODING(ROOF_MULTIPLE_MIRROR_ENUM_VARINT) + DECLARE_ENCODING(ARBITRARY_MULTIPLE_ZIGZAG_VARINT) + + // Number + DECLARE_ENCODING(DOUBLE_VARINT_TUPLE) + + // Any + DECLARE_ENCODING(BYTE_CHOICE_INDEX) + DECLARE_ENCODING(LARGE_CHOICE_INDEX) + DECLARE_ENCODING(TOP_LEVEL_BYTE_CHOICE_INDEX) + DECLARE_ENCODING(CONST_NONE) + DECLARE_ENCODING(ANY_PACKED_TYPE_TAG_BYTE_PREFIX) + + // String + DECLARE_ENCODING(UTF8_STRING_NO_LENGTH) + DECLARE_ENCODING(FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) + DECLARE_ENCODING(ROOF_VARINT_PREFIX_UTF8_STRING_SHARED) + DECLARE_ENCODING(BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED) + DECLARE_ENCODING(RFC3339_DATE_INTEGER_TRIPLET) + DECLARE_ENCODING(PREFIX_VARINT_LENGTH_STRING_SHARED) // TODO: Implement STRING_BROTLI encoding // TODO: Implement STRING_DICTIONARY_COMPRESSOR encoding // TODO: Implement STRING_UNBOUNDED_SCOPED_PREFIX_LENGTH encoding // TODO: Implement URL_PROTOCOL_HOST_REST encoding - auto FIXED_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, - const FIXED_TYPED_ARRAY &options) -> void { - assert(document.is_array()); - assert(document.size() == options.size); - const auto prefix_encodings{options.prefix_encodings.size()}; - assert(prefix_encodings <= document.size()); - for (std::size_t index = 0; index < options.size; index++) { - const Plan &encoding{prefix_encodings > index - ? options.prefix_encodings[index].value - : options.encoding->value}; - this->encode(document.at(index), encoding); - } - } - - auto BOUNDED_8BITS_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, - const BOUNDED_8BITS_TYPED_ARRAY &options) - -> void { - assert(options.maximum >= options.minimum); - const auto size{document.size()}; - assert(is_within(size, options.minimum, options.maximum)); - assert(is_byte(options.maximum - options.minimum)); - this->put_byte(static_cast(size - options.minimum)); - this->FIXED_TYPED_ARRAY(document, {size, std::move(options.encoding), - std::move(options.prefix_encodings)}); - } - - auto FLOOR_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, - const FLOOR_TYPED_ARRAY &options) -> void { - const auto size{document.size()}; - assert(size >= options.minimum); - this->put_varint(size - options.minimum); - this->FIXED_TYPED_ARRAY(document, {size, std::move(options.encoding), - std::move(options.prefix_encodings)}); - } - - auto ROOF_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &document, - const ROOF_TYPED_ARRAY &options) -> void { - const auto size{document.size()}; - assert(size <= options.maximum); - this->put_varint(options.maximum - size); - this->FIXED_TYPED_ARRAY(document, {size, std::move(options.encoding), - std::move(options.prefix_encodings)}); - } - - auto - FIXED_TYPED_ARBITRARY_OBJECT(const sourcemeta::jsontoolkit::JSON &document, - const FIXED_TYPED_ARBITRARY_OBJECT &options) - -> void { - assert(document.is_object()); - assert(document.size() == options.size); - - for (const auto &[key, value] : document.as_object()) { - this->encode(sourcemeta::jsontoolkit::JSON{key}, - options.key_encoding->value); - this->encode(value, options.encoding->value); - } - } - - auto - VARINT_TYPED_ARBITRARY_OBJECT(const sourcemeta::jsontoolkit::JSON &document, - const VARINT_TYPED_ARBITRARY_OBJECT &options) - -> void { - assert(document.is_object()); - const auto size{document.size()}; - this->put_varint(size); - - for (const auto &[key, value] : document.as_object()) { - this->encode(sourcemeta::jsontoolkit::JSON{key}, - options.key_encoding->value); - this->encode(value, options.encoding->value); - } - } - - auto - ANY_PACKED_TYPE_TAG_BYTE_PREFIX(const sourcemeta::jsontoolkit::JSON &document, - const ANY_PACKED_TYPE_TAG_BYTE_PREFIX &) - -> void { - using namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX; - if (document.is_null()) { - this->put_byte(TYPE_OTHER | (SUBTYPE_NULL << type_size)); - } else if (document.is_boolean()) { - const std::uint8_t subtype{document.to_boolean() ? SUBTYPE_TRUE - : SUBTYPE_FALSE}; - this->put_byte(TYPE_OTHER | - static_cast(subtype << type_size)); - } else if (document.is_real()) { - this->put_byte(TYPE_OTHER | SUBTYPE_NUMBER << type_size); - this->DOUBLE_VARINT_TUPLE(document, {}); - } else if (document.is_integer()) { - const std::int64_t value{document.to_integer()}; - const bool is_positive{value >= 0}; - const std::uint64_t absolute{ - is_positive ? static_cast(value) : abs(value) - 1}; - if (is_byte(absolute)) { - const std::uint8_t type{is_positive ? TYPE_POSITIVE_INTEGER_BYTE - : TYPE_NEGATIVE_INTEGER_BYTE}; - const std::uint8_t absolute_byte{static_cast(absolute)}; - if (absolute < uint_max<5>) { - this->put_byte(type | static_cast((absolute_byte + 1) - << type_size)); - } else { - this->put_byte(type); - this->put_byte(absolute_byte); - } - } else { - const std::uint8_t subtype{is_positive ? SUBTYPE_POSITIVE_INTEGER - : SUBTYPE_NEGATIVE_INTEGER}; - this->put_byte(TYPE_OTHER | - static_cast(subtype << type_size)); - this->put_varint(absolute); - } - } else if (document.is_string()) { - const std::basic_string value{document.to_string()}; - const auto size{document.byte_size()}; - const bool is_shared{this->context().has(value, ContextType::Standalone)}; - if (size < uint_max<5>) { - const std::uint8_t type{is_shared ? TYPE_SHARED_STRING : TYPE_STRING}; - this->put_byte( - static_cast(type | ((size + 1) << type_size))); - if (is_shared) { - this->put_varint( - this->position() - - this->context().offset(value, ContextType::Standalone)); - } else { - this->context().record(value, this->position(), - ContextType::Standalone); - this->put_string_utf8(value, size); - } - } else if (size >= uint_max<5> && size < uint_max<5> * 2 && !is_shared) { - this->put_byte(static_cast( - TYPE_LONG_STRING | ((size - uint_max<5>) << type_size))); - this->put_string_utf8(value, size); - } else if (size >= 2 << (SUBTYPE_LONG_STRING_BASE_EXPONENT_7 - 1) && - !is_shared) { - const std::uint8_t exponent{closest_smallest_exponent( - size, 2, SUBTYPE_LONG_STRING_BASE_EXPONENT_7, - SUBTYPE_LONG_STRING_BASE_EXPONENT_10)}; - this->put_byte( - static_cast(TYPE_OTHER | (exponent << type_size))); - this->put_varint(size - - static_cast(2 << (exponent - 1))); - this->put_string_utf8(value, size); - } else { - // Exploit the fact that a shared string always starts - // with an impossible length marker (0) to avoid having - // to encode an additional tag - if (!is_shared) { - this->put_byte(TYPE_STRING); - } - - // If we got this far, the string is at least a certain length - return FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED(document, - {uint_max<5> * 2}); - } - } else if (document.is_array()) { - const auto size{document.size()}; - if (size >= uint_max<5>) { - this->put_byte(TYPE_ARRAY); - this->put_varint(size - uint_max<5>); - } else { - this->put_byte( - static_cast(TYPE_ARRAY | ((size + 1) << type_size))); - } - - Plan encoding{sourcemeta::jsonbinpack::ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}}; - this->FIXED_TYPED_ARRAY(document, {size, wrap(std::move(encoding)), {}}); - } else if (document.is_object()) { - const auto size{document.size()}; - if (size >= uint_max<5>) { - this->put_byte(TYPE_OBJECT); - this->put_varint(size - uint_max<5>); - } else { - this->put_byte( - static_cast(TYPE_OBJECT | ((size + 1) << type_size))); - } + // Array + DECLARE_ENCODING(FIXED_TYPED_ARRAY) + DECLARE_ENCODING(BOUNDED_8BITS_TYPED_ARRAY) + DECLARE_ENCODING(FLOOR_TYPED_ARRAY) + DECLARE_ENCODING(ROOF_TYPED_ARRAY) - Plan key_encoding{ - sourcemeta::jsonbinpack::PREFIX_VARINT_LENGTH_STRING_SHARED{}}; - Plan value_encoding{ - sourcemeta::jsonbinpack::ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}}; - this->FIXED_TYPED_ARBITRARY_OBJECT(document, - {size, wrap(std::move(key_encoding)), - wrap(std::move(value_encoding))}); - } else { - // We should never get here. - assert(false); - std::abort(); - } - } + // Object + DECLARE_ENCODING(FIXED_TYPED_ARBITRARY_OBJECT) + DECLARE_ENCODING(VARINT_TYPED_ARBITRARY_OBJECT) +#undef DECLARE_ENCODING #endif private: - using ContextType = typename Context::Type; + Cache cache_; }; } // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_basic.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_basic.h deleted file mode 100644 index 636ac4b..0000000 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_basic.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_BASIC_H_ -#define SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_BASIC_H_ -#ifndef DOXYGEN - -#include - -#include -#include - -#include // std::find_if -#include // assert -#include // std::int8_t, std::uint8_t, std::int64_t -#include // std::ios_base -#include // std::cbegin, std::cend, std::distance -#include // std::basic_ostream -#include // std::basic_string - -namespace sourcemeta::jsonbinpack { - -template class BasicEncoder { -public: - BasicEncoder(std::basic_ostream &output) : stream{output} { - this->stream.exceptions(std::ios_base::badbit | std::ios_base::failbit | - std::ios_base::eofbit); - } - - // Prevent copying, as this class is tied to a stream resource - BasicEncoder(const BasicEncoder &) = delete; - auto operator=(const BasicEncoder &) -> BasicEncoder & = delete; - - inline auto position() const noexcept -> std::uint64_t { - return static_cast(this->stream.tellp()); - } - - inline auto put_byte(const std::uint8_t byte) -> void { - this->stream.put(static_cast(byte)); - } - - inline auto put_bytes(const std::uint16_t bytes) -> void { - this->stream.write(reinterpret_cast(&bytes), sizeof bytes); - } - - inline auto put_varint(const std::uint64_t value) -> void { - varint_encode(this->stream, value); - } - - inline auto put_varint_zigzag(const std::int64_t value) -> void { - varint_encode(this->stream, zigzag_encode(value)); - } - - inline auto put_string_utf8(const std::basic_string &string, - const std::uint64_t length) -> void { - assert(string.size() == length); - // Do a manual for-loop based on the provided length instead of a range - // loop based on the string value to avoid accidental overflows - for (std::uint64_t index = 0; index < length; index++) { - this->put_byte(static_cast(string[index])); - } - } - - inline auto context() -> Context & { return this->context_; } - -private: - std::basic_ostream &stream; - Context context_; -}; - -} // namespace sourcemeta::jsonbinpack - -#endif -#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_cache.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_cache.h new file mode 100644 index 0000000..7d730bc --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_cache.h @@ -0,0 +1,48 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_CACHE_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_CACHE_H_ +#ifndef DOXYGEN + +#include "runtime_export.h" + +#include + +#include // std::reference_wrapper +#include // std::map +#include // std::optional +#include // std::pair + +namespace sourcemeta::jsonbinpack { + +class SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT Cache { +public: + enum class Type { Standalone, PrefixLengthVarintPlusOne }; + auto record(const sourcemeta::jsontoolkit::JSON::String &value, + const std::uint64_t offset, const Type type) -> void; + auto find(const sourcemeta::jsontoolkit::JSON::String &value, + const Type type) const -> std::optional; + +#ifndef DOXYGEN + // This method is considered private. We only expose it for testing purposes + auto remove_oldest() -> void; +#endif + +private: +// Exporting symbols that depends on the standard C++ library is considered +// safe. +// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN +#if defined(_MSC_VER) +#pragma warning(disable : 4251 4275) +#endif + std::uint64_t byte_size{0}; + using Entry = std::pair; + std::map data; + std::map> order; +#if defined(_MSC_VER) +#pragma warning(default : 4251 4275) +#endif +}; + +} // namespace sourcemeta::jsonbinpack + +#endif +#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_context.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_context.h deleted file mode 100644 index c908a7a..0000000 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoder_context.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_CONTEXT_H_ -#define SOURCEMETA_JSONBINPACK_RUNTIME_ENCODER_CONTEXT_H_ -#ifndef DOXYGEN - -#include // assert -#include // std::cbegin, std::cend -#include // std::map -#include // std::logic_error -#include // std::basic_string -#include // std::pair, std::make_pair - -// Encoding a shared string has some overhead, such as the -// shared string marker + the offset, so its not worth -// doing for strings that are too small. -static constexpr auto MINIMUM_STRING_LENGTH{3}; - -// We don't want to allow the context to grow -// forever, otherwise an attacker could force the -// program to exhaust memory given an input -// document that contains a high number of large strings. -static constexpr auto MAXIMUM_BYTE_SIZE{20971520}; - -namespace sourcemeta::jsonbinpack { - -template class Context { -public: - enum class Type { Standalone, PrefixLengthVarintPlusOne }; - - auto record(const std::basic_string &value, const std::uint64_t offset, - const Type type) -> void { - const auto value_size{value.size()}; - if (value_size < MINIMUM_STRING_LENGTH) { - return; - // The value is too big for the context to start with - } else if (value_size >= MAXIMUM_BYTE_SIZE) { - return; - } - - // Remove the oldest entries to make space if needed - while (!this->strings.empty() && - this->byte_size + value_size >= MAXIMUM_BYTE_SIZE) { - this->remove_oldest(); - } - - // If the string already exists, we want to - // bump the offset for locality purposes. - if (this->has(value, type)) { - const auto key{std::make_pair(value, type)}; - const auto previous_offset{this->strings[key]}; - if (offset > previous_offset) { - this->strings[key] = offset; - this->offsets.erase(previous_offset); - this->offsets.insert({offset, std::make_pair(value, type)}); - } - } else { - const auto result{ - this->offsets.insert({offset, std::make_pair(value, type)})}; - // Prevent recording two strings to the same offset - assert(result.second); - if (result.second) { - this->strings.insert({std::make_pair(value, type), offset}); - this->byte_size += value_size; - } - } - } - - auto remove_oldest() -> void { - assert(!this->strings.empty()); - // std::map are by definition ordered by key, - // so the begin iterator points to the entry - // with the lowest offset, a.k.a. the oldest. - const auto iterator{std::cbegin(this->offsets)}; - this->strings.erase( - std::make_pair(iterator->second.first, iterator->second.second)); - this->byte_size -= iterator->second.first.size(); - this->offsets.erase(iterator); - } - - auto has(const std::basic_string &value, const Type type) const - -> bool { - return this->strings.contains(std::make_pair(value, type)); - } - - auto offset(const std::basic_string &value, const Type type) const - -> std::uint64_t { - // This method assumes the value indeed exists for performance reasons - assert(this->has(value, type)); - return this->strings.at(std::make_pair(value, type)); - } - -private: - std::map, Type>, std::uint64_t> strings; - // A mirror of the above map to be able to sort by offset. - // While this means we need 2x the amount of memory to keep track - // of strings, it allows us to efficiently put an upper bound - // on the amount of memory being consumed by this class. - std::map, Type>> offsets; - std::uint64_t byte_size = 0; -}; - -} // namespace sourcemeta::jsonbinpack - -#endif -#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_plan.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h similarity index 94% rename from vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_plan.h rename to vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h index c2ef0b5..2e225e8 100644 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_plan.h +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h @@ -12,16 +12,49 @@ namespace sourcemeta::jsonbinpack { -// We cannot directly create an Plan variant type whose values potentially -// include other Plan instances. As a workaround, we have a helper -// encoding wrapper that we can use as an incomplete type -struct __internal_encoding_wrapper; -// Use these alias types. Never use the internal wrapper type directly -using SinglePlan = std::shared_ptr<__internal_encoding_wrapper>; -using MultiplePlans = std::vector<__internal_encoding_wrapper>; +// Forward declarations for the sole purpose of being bale to define circular +// structures +#ifndef DOXYGEN +struct BOUNDED_MULTIPLE_8BITS_ENUM_FIXED; +struct FLOOR_MULTIPLE_ENUM_VARINT; +struct ROOF_MULTIPLE_MIRROR_ENUM_VARINT; +struct ARBITRARY_MULTIPLE_ZIGZAG_VARINT; +struct DOUBLE_VARINT_TUPLE; +struct BYTE_CHOICE_INDEX; +struct LARGE_CHOICE_INDEX; +struct TOP_LEVEL_BYTE_CHOICE_INDEX; +struct CONST_NONE; +struct ANY_PACKED_TYPE_TAG_BYTE_PREFIX; +struct UTF8_STRING_NO_LENGTH; +struct FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED; +struct ROOF_VARINT_PREFIX_UTF8_STRING_SHARED; +struct BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED; +struct RFC3339_DATE_INTEGER_TRIPLET; +struct PREFIX_VARINT_LENGTH_STRING_SHARED; +struct FIXED_TYPED_ARRAY; +struct BOUNDED_8BITS_TYPED_ARRAY; +struct FLOOR_TYPED_ARRAY; +struct ROOF_TYPED_ARRAY; +struct FIXED_TYPED_ARBITRARY_OBJECT; +struct VARINT_TYPED_ARBITRARY_OBJECT; +#endif /// @ingroup runtime -/// @defgroup plan_integer Integer Encodings +/// Represents an encoding +using Encoding = std::variant< + BOUNDED_MULTIPLE_8BITS_ENUM_FIXED, FLOOR_MULTIPLE_ENUM_VARINT, + ROOF_MULTIPLE_MIRROR_ENUM_VARINT, ARBITRARY_MULTIPLE_ZIGZAG_VARINT, + DOUBLE_VARINT_TUPLE, BYTE_CHOICE_INDEX, LARGE_CHOICE_INDEX, + TOP_LEVEL_BYTE_CHOICE_INDEX, CONST_NONE, ANY_PACKED_TYPE_TAG_BYTE_PREFIX, + UTF8_STRING_NO_LENGTH, FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED, + ROOF_VARINT_PREFIX_UTF8_STRING_SHARED, + BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED, RFC3339_DATE_INTEGER_TRIPLET, + PREFIX_VARINT_LENGTH_STRING_SHARED, FIXED_TYPED_ARRAY, + BOUNDED_8BITS_TYPED_ARRAY, FLOOR_TYPED_ARRAY, ROOF_TYPED_ARRAY, + FIXED_TYPED_ARBITRARY_OBJECT, VARINT_TYPED_ARBITRARY_OBJECT>; + +/// @ingroup runtime +/// @defgroup encoding_integer Integer Encodings /// @{ // clang-format off @@ -179,7 +212,7 @@ struct ARBITRARY_MULTIPLE_ZIGZAG_VARINT { /// @} /// @ingroup runtime -/// @defgroup plan_number Number Encodings +/// @defgroup encoding_number Number Encodings /// @{ // clang-format off @@ -226,7 +259,7 @@ struct DOUBLE_VARINT_TUPLE {}; /// @} /// @ingroup runtime -/// @defgroup plan_enum Enumeration Encodings +/// @defgroup encoding_any Any Encodings /// @{ // clang-format off @@ -367,10 +400,65 @@ struct CONST_NONE { const sourcemeta::jsontoolkit::JSON value; }; +// TODO: Write brief description +struct ANY_PACKED_TYPE_TAG_BYTE_PREFIX {}; +#ifndef DOXYGEN +namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX { +constexpr auto type_size = 3; +constexpr std::uint8_t TYPE_SHARED_STRING = 0b00000000; +constexpr std::uint8_t TYPE_STRING = 0b00000001; +constexpr std::uint8_t TYPE_LONG_STRING = 0b00000010; +constexpr std::uint8_t TYPE_OBJECT = 0b00000011; +constexpr std::uint8_t TYPE_ARRAY = 0b00000100; +constexpr std::uint8_t TYPE_POSITIVE_INTEGER_BYTE = 0b00000101; +constexpr std::uint8_t TYPE_NEGATIVE_INTEGER_BYTE = 0b00000110; +constexpr std::uint8_t TYPE_OTHER = 0b00000111; +static_assert(TYPE_SHARED_STRING <= uint_max); +static_assert(TYPE_STRING <= uint_max); +static_assert(TYPE_LONG_STRING <= uint_max); +static_assert(TYPE_OBJECT <= uint_max); +static_assert(TYPE_ARRAY <= uint_max); +static_assert(TYPE_POSITIVE_INTEGER_BYTE <= uint_max); +static_assert(TYPE_NEGATIVE_INTEGER_BYTE <= uint_max); +static_assert(TYPE_OTHER <= uint_max); + +constexpr auto subtype_size = 5; +constexpr std::uint8_t SUBTYPE_FALSE = 0b00000000; +constexpr std::uint8_t SUBTYPE_TRUE = 0b00000001; +constexpr std::uint8_t SUBTYPE_NULL = 0b00000010; +constexpr std::uint8_t SUBTYPE_POSITIVE_INTEGER = 0b00000011; +constexpr std::uint8_t SUBTYPE_NEGATIVE_INTEGER = 0b00000100; +constexpr std::uint8_t SUBTYPE_NUMBER = 0b00000101; +constexpr std::uint8_t SUBTYPE_POSITIVE_REAL_INTEGER_BYTE = 0b00000110; +constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_7 = 0b00000111; +constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_8 = 0b00001000; +constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_9 = 0b00001001; +constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_10 = 0b00001010; + +static_assert(SUBTYPE_FALSE <= uint_max); +static_assert(SUBTYPE_TRUE <= uint_max); +static_assert(SUBTYPE_NULL <= uint_max); +static_assert(SUBTYPE_POSITIVE_INTEGER <= uint_max); +static_assert(SUBTYPE_NEGATIVE_INTEGER <= uint_max); +static_assert(SUBTYPE_NUMBER <= uint_max); +static_assert(SUBTYPE_POSITIVE_REAL_INTEGER_BYTE <= uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 <= uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_8 <= uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_9 <= uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_10 <= uint_max); + +// Note that the binary values actually match the declared exponents +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 == 7); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_8 == 8); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_9 == 9); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_10 == 10); +} // namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX +#endif + /// @} /// @ingroup runtime -/// @defgroup plan_string String Encodings +/// @defgroup encoding_string String Encodings /// @{ // clang-format off @@ -661,7 +749,7 @@ struct PREFIX_VARINT_LENGTH_STRING_SHARED {}; /// @} /// @ingroup runtime -/// @defgroup plan_array Array Encodings +/// @defgroup encoding_array Array Encodings /// @{ // clang-format off @@ -703,9 +791,9 @@ struct FIXED_TYPED_ARRAY { /// The array length const std::uint64_t size; /// Element encoding - const SinglePlan encoding; + const std::shared_ptr encoding; /// Positional encodings - const MultiplePlans prefix_encodings; + const std::vector prefix_encodings; }; // clang-format off @@ -754,9 +842,9 @@ struct BOUNDED_8BITS_TYPED_ARRAY { /// The maximum length of the array const std::uint64_t maximum; /// Element encoding - const SinglePlan encoding; + const std::shared_ptr encoding; /// Positional encodings - const MultiplePlans prefix_encodings; + const std::vector prefix_encodings; }; // clang-format off @@ -800,9 +888,9 @@ struct FLOOR_TYPED_ARRAY { /// The minimum length of the array const std::uint64_t minimum; /// Element encoding - const SinglePlan encoding; + const std::shared_ptr encoding; /// Positional encodings - const MultiplePlans prefix_encodings; + const std::vector prefix_encodings; }; // clang-format off @@ -845,15 +933,15 @@ struct ROOF_TYPED_ARRAY { /// The maximum length of the array const std::uint64_t maximum; /// Element encoding - const SinglePlan encoding; + const std::shared_ptr encoding; /// Positional encodings - const MultiplePlans prefix_encodings; + const std::vector prefix_encodings; }; /// @} /// @ingroup runtime -/// @defgroup plan_object Object Encodings +/// @defgroup encoding_object Object Encodings /// @{ // clang-format off @@ -902,9 +990,9 @@ struct FIXED_TYPED_ARBITRARY_OBJECT { /// The object size const std::uint64_t size; /// Key encoding - const SinglePlan key_encoding; + const std::shared_ptr key_encoding; /// Value encoding - const SinglePlan encoding; + const std::shared_ptr encoding; }; // clang-format off @@ -946,96 +1034,13 @@ struct FIXED_TYPED_ARBITRARY_OBJECT { // clang-format on struct VARINT_TYPED_ARBITRARY_OBJECT { /// Key encoding - const SinglePlan key_encoding; + const std::shared_ptr key_encoding; /// Value encoding - const SinglePlan encoding; + const std::shared_ptr encoding; }; /// @} -/// @ingroup runtime -/// @defgroup plan_any Any Encodings -/// @{ - -// TODO: Write brief description -struct ANY_PACKED_TYPE_TAG_BYTE_PREFIX {}; -#ifndef DOXYGEN -namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX { -constexpr auto type_size = 3; -constexpr std::uint8_t TYPE_SHARED_STRING = 0b00000000; -constexpr std::uint8_t TYPE_STRING = 0b00000001; -constexpr std::uint8_t TYPE_LONG_STRING = 0b00000010; -constexpr std::uint8_t TYPE_OBJECT = 0b00000011; -constexpr std::uint8_t TYPE_ARRAY = 0b00000100; -constexpr std::uint8_t TYPE_POSITIVE_INTEGER_BYTE = 0b00000101; -constexpr std::uint8_t TYPE_NEGATIVE_INTEGER_BYTE = 0b00000110; -constexpr std::uint8_t TYPE_OTHER = 0b00000111; -static_assert(TYPE_SHARED_STRING <= uint_max); -static_assert(TYPE_STRING <= uint_max); -static_assert(TYPE_LONG_STRING <= uint_max); -static_assert(TYPE_OBJECT <= uint_max); -static_assert(TYPE_ARRAY <= uint_max); -static_assert(TYPE_POSITIVE_INTEGER_BYTE <= uint_max); -static_assert(TYPE_NEGATIVE_INTEGER_BYTE <= uint_max); -static_assert(TYPE_OTHER <= uint_max); - -constexpr auto subtype_size = 5; -constexpr std::uint8_t SUBTYPE_FALSE = 0b00000000; -constexpr std::uint8_t SUBTYPE_TRUE = 0b00000001; -constexpr std::uint8_t SUBTYPE_NULL = 0b00000010; -constexpr std::uint8_t SUBTYPE_POSITIVE_INTEGER = 0b00000011; -constexpr std::uint8_t SUBTYPE_NEGATIVE_INTEGER = 0b00000100; -constexpr std::uint8_t SUBTYPE_NUMBER = 0b00000101; -constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_7 = 0b00000111; -constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_8 = 0b00001000; -constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_9 = 0b00001001; -constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_10 = 0b00001010; - -static_assert(SUBTYPE_FALSE <= uint_max); -static_assert(SUBTYPE_TRUE <= uint_max); -static_assert(SUBTYPE_NULL <= uint_max); -static_assert(SUBTYPE_POSITIVE_INTEGER <= uint_max); -static_assert(SUBTYPE_NEGATIVE_INTEGER <= uint_max); -static_assert(SUBTYPE_NUMBER <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_8 <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_9 <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_10 <= uint_max); - -// Note that the binary values actually match the declared exponents -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 == 7); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_8 == 8); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_9 == 9); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_10 == 10); -} // namespace internal::ANY_PACKED_TYPE_TAG_BYTE_PREFIX -#endif - -/// @} -// clang-format on - -/// @ingroup runtime -/// Represents an encoding plan -using Plan = std::variant< - BOUNDED_MULTIPLE_8BITS_ENUM_FIXED, FLOOR_MULTIPLE_ENUM_VARINT, - ROOF_MULTIPLE_MIRROR_ENUM_VARINT, ARBITRARY_MULTIPLE_ZIGZAG_VARINT, - DOUBLE_VARINT_TUPLE, BYTE_CHOICE_INDEX, LARGE_CHOICE_INDEX, - TOP_LEVEL_BYTE_CHOICE_INDEX, CONST_NONE, UTF8_STRING_NO_LENGTH, - FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED, - ROOF_VARINT_PREFIX_UTF8_STRING_SHARED, - BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED, RFC3339_DATE_INTEGER_TRIPLET, - PREFIX_VARINT_LENGTH_STRING_SHARED, FIXED_TYPED_ARRAY, - BOUNDED_8BITS_TYPED_ARRAY, FLOOR_TYPED_ARRAY, ROOF_TYPED_ARRAY, - FIXED_TYPED_ARBITRARY_OBJECT, VARINT_TYPED_ARBITRARY_OBJECT, - ANY_PACKED_TYPE_TAG_BYTE_PREFIX>; - -// Helper definitions that rely on the Plan data type -#ifndef DOXYGEN -// Ignore this definition on the documentation -struct __internal_encoding_wrapper { - const Plan value; -}; -#endif - } // namespace sourcemeta::jsonbinpack #endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_input_stream.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_input_stream.h new file mode 100644 index 0000000..dc95f2b --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_input_stream.h @@ -0,0 +1,44 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_INPUT_STREAM_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_INPUT_STREAM_H_ + +#include "runtime_export.h" + +#include + +#include // std::uint8_t, std::uint16_t, std::uint64_t +#include // std::basic_istream + +namespace sourcemeta::jsonbinpack { + +/// @ingroup runtime +class SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT InputStream { +public: + using Stream = std::basic_istream; + InputStream(Stream &input); + // Prevent copying, as this class is tied to a stream resource + InputStream(const InputStream &) = delete; + auto operator=(const InputStream &) -> InputStream & = delete; + + auto position() const noexcept -> std::uint64_t; + auto seek(const std::uint64_t offset) -> void; + // Seek backwards given a relative offset + auto rewind(const std::uint64_t relative_offset, const std::uint64_t position) + -> std::uint64_t; + auto get_byte() -> std::uint8_t; + // A "word" corresponds to two bytes + // See https://stackoverflow.com/questions/28066462/how-many-bits-is-a-word + auto get_word() -> std::uint16_t; + auto get_varint() -> std::uint64_t; + auto get_varint_zigzag() -> std::int64_t; + auto has_more_data() const noexcept -> bool; + auto get_string_utf8(const std::uint64_t length) + -> sourcemeta::jsontoolkit::JSON::String; + +private: + Stream &stream; +}; + +} // namespace sourcemeta::jsonbinpack + +#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_output_stream.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_output_stream.h new file mode 100644 index 0000000..fe58681 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_output_stream.h @@ -0,0 +1,38 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_OUTPUT_STREAM_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_OUTPUT_STREAM_H_ + +#include "runtime_export.h" + +#include + +#include // std::uint8_t, std::uint16_t, std::uint64_t +#include // std::basic_ostream + +namespace sourcemeta::jsonbinpack { + +/// @ingroup runtime +class SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT OutputStream { +public: + using Stream = std::basic_ostream; + OutputStream(Stream &output); + + // Prevent copying, as this class is tied to a stream resource + OutputStream(const OutputStream &) = delete; + auto operator=(const OutputStream &) -> OutputStream & = delete; + + auto position() const noexcept -> std::uint64_t; + auto put_byte(const std::uint8_t byte) -> void; + auto put_bytes(const std::uint16_t bytes) -> void; + auto put_varint(const std::uint64_t value) -> void; + auto put_varint_zigzag(const std::int64_t value) -> void; + auto put_string_utf8(const sourcemeta::jsontoolkit::JSON::String &string, + const std::uint64_t length) -> void; + +private: + Stream &stream; +}; + +} // namespace sourcemeta::jsonbinpack + +#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_parser.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_parser.h deleted file mode 100644 index d8a0d82..0000000 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_parser.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_PARSER_H_ -#define SOURCEMETA_JSONBINPACK_RUNTIME_PARSER_H_ - -#include "runtime_export.h" - -#include -#include - -namespace sourcemeta::jsonbinpack { - -// TODO: Give this a better name -/// @ingroup runtime -SOURCEMETA_JSONBINPACK_RUNTIME_EXPORT -auto parse(const sourcemeta::jsontoolkit::JSON &input) -> Plan; - -} // namespace sourcemeta::jsonbinpack - -#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_plan_wrap.h b/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_plan_wrap.h deleted file mode 100644 index 6d3d149..0000000 --- a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_plan_wrap.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_ENCODING_WRAP_H_ -#define SOURCEMETA_JSONBINPACK_RUNTIME_ENCODING_WRAP_H_ - -#include - -#include // std::transform -#include // std::initializer_list -#include // std::back_inserter, std::forward_iterator -#include // std::make_shared -#include // std::move, std::forward -#include // std::vector - -namespace { - -// Adapter to make smart pointers using struct aggregate initialization -// From https://stackoverflow.com/a/35300172/1641422 -template struct aggregate_adapter : public T { - template - aggregate_adapter(Args &&...args) : T{std::forward(args)...} {} -}; - -} // namespace - -namespace sourcemeta::jsonbinpack { - -// We define all wrap helper functions inline in this header file, -// as we want to avoid including their definitions in if the user -// doesn't import this header. - -inline auto wrap(Plan &&encoding) -> SinglePlan { - return std::make_shared>( - std::move(encoding)); -} - -// clang-format off -template -requires std::forward_iterator -// clang-format on -inline auto wrap(Iterator begin, Iterator end) -> MultiplePlans { - MultiplePlans result; - std::transform(begin, end, std::back_inserter(result), [](Plan encoding) { - return __internal_encoding_wrapper{std::move(encoding)}; - }); - return result; -} - -inline auto wrap(std::initializer_list encodings) -> MultiplePlans { - return wrap(encodings.begin(), encodings.end()); -} - -} // namespace sourcemeta::jsonbinpack - -#endif diff --git a/vendor/jsonbinpack/src/runtime/input_stream.cc b/vendor/jsonbinpack/src/runtime/input_stream.cc new file mode 100644 index 0000000..ef89b97 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/input_stream.cc @@ -0,0 +1,79 @@ +#include +#include + +#include "varint.h" + +#include // assert +#include // std::ios_base + +namespace sourcemeta::jsonbinpack { + +InputStream::InputStream(Stream &input) : stream{input} { + this->stream.exceptions(std::ios_base::badbit | std::ios_base::failbit | + std::ios_base::eofbit); +} + +auto InputStream::position() const noexcept -> std::uint64_t { + return static_cast(this->stream.tellg()); +} + +auto InputStream::seek(const std::uint64_t offset) -> void { + this->stream.seekg(static_cast(offset)); +} + +auto InputStream::rewind(const std::uint64_t relative_offset, + const std::uint64_t position) -> std::uint64_t { + assert(position >= relative_offset); + const std::uint64_t offset{position - relative_offset}; + assert(offset < position); + const std::uint64_t current{this->position()}; + this->seek(offset); + return current; +} + +auto InputStream::get_byte() -> std::uint8_t { + return static_cast(this->stream.get()); +} + +auto InputStream::get_word() -> std::uint16_t { + std::uint16_t word; + this->stream.read(reinterpret_cast(&word), sizeof word); + return word; +} + +auto InputStream::get_varint() -> std::uint64_t { + return varint_decode(this->stream); +} + +auto InputStream::get_varint_zigzag() -> std::int64_t { + const std::uint64_t value = varint_decode(this->stream); + return zigzag_decode(value); +} + +auto InputStream::has_more_data() const noexcept -> bool { + // A way to check if the stream is empty without using `.peek()`, + // which throws given we set exceptions on the EOF bit. + // However, `in_avail()` works on characters and will return zero + // if all that's remaining is 0x00 (null), so we need to handle + // that case separately. + return this->stream.rdbuf()->in_avail() > 0 || + this->stream.rdbuf()->sgetc() == '\0'; +} + +auto InputStream::get_string_utf8(const std::uint64_t length) + -> sourcemeta::jsontoolkit::JSON::String { + sourcemeta::jsontoolkit::JSON::String result; + result.reserve(length); + std::uint64_t counter = 0; + while (counter < length) { + result += + static_cast(this->get_byte()); + counter += 1; + } + + assert(counter == length); + assert(result.size() == length); + return result; +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/runtime_parser.cc b/vendor/jsonbinpack/src/runtime/loader.cc similarity index 84% rename from vendor/jsonbinpack/src/runtime/runtime_parser.cc rename to vendor/jsonbinpack/src/runtime/loader.cc index ff3b657..9736a26 100644 --- a/vendor/jsonbinpack/src/runtime/runtime_parser.cc +++ b/vendor/jsonbinpack/src/runtime/loader.cc @@ -1,6 +1,10 @@ -#include +#include -#include "runtime_parser_v1.h" +#include "loader_v1_any.h" +#include "loader_v1_array.h" +#include "loader_v1_integer.h" +#include "loader_v1_number.h" +#include "loader_v1_string.h" #include // assert #include // std::ostringstream @@ -8,7 +12,7 @@ namespace sourcemeta::jsonbinpack { -auto parse(const sourcemeta::jsontoolkit::JSON &input) -> Plan { +auto load(const sourcemeta::jsontoolkit::JSON &input) -> Encoding { assert(input.defines("binpackEncoding")); assert(input.defines("binpackOptions")); const auto encoding{input.at("binpackEncoding").to_string()}; @@ -25,11 +29,12 @@ auto parse(const sourcemeta::jsontoolkit::JSON &input) -> Plan { PARSE_ENCODING(v1, ARBITRARY_MULTIPLE_ZIGZAG_VARINT) // Numbers PARSE_ENCODING(v1, DOUBLE_VARINT_TUPLE) - // Enumerations + // Any PARSE_ENCODING(v1, BYTE_CHOICE_INDEX) PARSE_ENCODING(v1, LARGE_CHOICE_INDEX) PARSE_ENCODING(v1, TOP_LEVEL_BYTE_CHOICE_INDEX) PARSE_ENCODING(v1, CONST_NONE) + PARSE_ENCODING(v1, ANY_PACKED_TYPE_TAG_BYTE_PREFIX) // Strings PARSE_ENCODING(v1, UTF8_STRING_NO_LENGTH) PARSE_ENCODING(v1, FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED) @@ -42,15 +47,14 @@ auto parse(const sourcemeta::jsontoolkit::JSON &input) -> Plan { PARSE_ENCODING(v1, BOUNDED_8BITS_TYPED_ARRAY) PARSE_ENCODING(v1, FLOOR_TYPED_ARRAY) PARSE_ENCODING(v1, ROOF_TYPED_ARRAY) - // Any - PARSE_ENCODING(v1, ANY_PACKED_TYPE_TAG_BYTE_PREFIX) + + // TODO: Handle object encodings #undef PARSE_ENCODING - // TODO: Have a custom error for this std::ostringstream error; error << "Unrecognized encoding: " << encoding; - throw std::runtime_error(error.str()); + throw EncodingError(error.str()); } } // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/loader_v1_any.h b/vendor/jsonbinpack/src/runtime/loader_v1_any.h new file mode 100644 index 0000000..9062bbd --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/loader_v1_any.h @@ -0,0 +1,60 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_ANY_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_ANY_H_ + +#include + +#include + +#include // assert +#include // std::move +#include // std::vector + +namespace sourcemeta::jsonbinpack::v1 { + +auto BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("choices")); + const auto &choices{options.at("choices")}; + assert(choices.is_array()); + const auto &array{choices.as_array()}; + std::vector elements{array.cbegin(), + array.cend()}; + return sourcemeta::jsonbinpack::BYTE_CHOICE_INDEX({std::move(elements)}); +} + +auto LARGE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("choices")); + const auto &choices{options.at("choices")}; + assert(choices.is_array()); + const auto &array{choices.as_array()}; + std::vector elements{array.cbegin(), + array.cend()}; + return sourcemeta::jsonbinpack::LARGE_CHOICE_INDEX({std::move(elements)}); +} + +auto TOP_LEVEL_BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("choices")); + const auto &choices{options.at("choices")}; + assert(choices.is_array()); + const auto &array{choices.as_array()}; + std::vector elements{array.cbegin(), + array.cend()}; + return sourcemeta::jsonbinpack::TOP_LEVEL_BYTE_CHOICE_INDEX( + {std::move(elements)}); +} + +auto CONST_NONE(const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("value")); + return sourcemeta::jsonbinpack::CONST_NONE({options.at("value")}); +} + +auto ANY_PACKED_TYPE_TAG_BYTE_PREFIX(const sourcemeta::jsontoolkit::JSON &) + -> Encoding { + return sourcemeta::jsonbinpack::ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}; +} + +} // namespace sourcemeta::jsonbinpack::v1 + +#endif diff --git a/vendor/jsonbinpack/src/runtime/loader_v1_array.h b/vendor/jsonbinpack/src/runtime/loader_v1_array.h new file mode 100644 index 0000000..8c0b750 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/loader_v1_array.h @@ -0,0 +1,116 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_ARRAY_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_ARRAY_H_ + +#include + +#include + +#include // std::transform +#include // assert +#include // std::uint64_t +#include // std::back_inserter +#include // std::make_shared +#include // std::vector + +namespace sourcemeta::jsonbinpack::v1 { + +auto FIXED_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("size")); + assert(options.defines("encoding")); + assert(options.defines("prefixEncodings")); + const auto &size{options.at("size")}; + const auto &array_encoding{options.at("encoding")}; + const auto &prefix_encodings{options.at("prefixEncodings")}; + assert(size.is_integer()); + assert(size.is_positive()); + assert(array_encoding.is_object()); + assert(prefix_encodings.is_array()); + std::vector encodings; + std::transform(prefix_encodings.as_array().cbegin(), + prefix_encodings.as_array().cend(), + std::back_inserter(encodings), + [](const auto &element) { return load(element); }); + assert(encodings.size() == prefix_encodings.size()); + return sourcemeta::jsonbinpack::FIXED_TYPED_ARRAY{ + static_cast(size.to_integer()), + std::make_shared(load(array_encoding)), std::move(encodings)}; +} + +auto BOUNDED_8BITS_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("minimum")); + assert(options.defines("maximum")); + assert(options.defines("encoding")); + assert(options.defines("prefixEncodings")); + const auto &minimum{options.at("minimum")}; + const auto &maximum{options.at("maximum")}; + const auto &array_encoding{options.at("encoding")}; + const auto &prefix_encodings{options.at("prefixEncodings")}; + assert(minimum.is_integer()); + assert(maximum.is_integer()); + assert(minimum.is_positive()); + assert(maximum.is_positive()); + assert(array_encoding.is_object()); + assert(prefix_encodings.is_array()); + std::vector encodings; + std::transform(prefix_encodings.as_array().cbegin(), + prefix_encodings.as_array().cend(), + std::back_inserter(encodings), + [](const auto &element) { return load(element); }); + assert(encodings.size() == prefix_encodings.size()); + return sourcemeta::jsonbinpack::BOUNDED_8BITS_TYPED_ARRAY{ + static_cast(minimum.to_integer()), + static_cast(maximum.to_integer()), + std::make_shared(load(array_encoding)), std::move(encodings)}; +} + +auto FLOOR_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("minimum")); + assert(options.defines("encoding")); + assert(options.defines("prefixEncodings")); + const auto &minimum{options.at("minimum")}; + const auto &array_encoding{options.at("encoding")}; + const auto &prefix_encodings{options.at("prefixEncodings")}; + assert(minimum.is_integer()); + assert(minimum.is_positive()); + assert(array_encoding.is_object()); + assert(prefix_encodings.is_array()); + std::vector encodings; + std::transform(prefix_encodings.as_array().cbegin(), + prefix_encodings.as_array().cend(), + std::back_inserter(encodings), + [](const auto &element) { return load(element); }); + assert(encodings.size() == prefix_encodings.size()); + return sourcemeta::jsonbinpack::FLOOR_TYPED_ARRAY{ + static_cast(minimum.to_integer()), + std::make_shared(load(array_encoding)), std::move(encodings)}; +} + +auto ROOF_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("maximum")); + assert(options.defines("encoding")); + assert(options.defines("prefixEncodings")); + const auto &maximum{options.at("maximum")}; + const auto &array_encoding{options.at("encoding")}; + const auto &prefix_encodings{options.at("prefixEncodings")}; + assert(maximum.is_integer()); + assert(maximum.is_positive()); + assert(array_encoding.is_object()); + assert(prefix_encodings.is_array()); + std::vector encodings; + std::transform(prefix_encodings.as_array().cbegin(), + prefix_encodings.as_array().cend(), + std::back_inserter(encodings), + [](const auto &element) { return load(element); }); + assert(encodings.size() == prefix_encodings.size()); + return sourcemeta::jsonbinpack::ROOF_TYPED_ARRAY{ + static_cast(maximum.to_integer()), + std::make_shared(load(array_encoding)), std::move(encodings)}; +} + +} // namespace sourcemeta::jsonbinpack::v1 + +#endif diff --git a/vendor/jsonbinpack/src/runtime/loader_v1_integer.h b/vendor/jsonbinpack/src/runtime/loader_v1_integer.h new file mode 100644 index 0000000..32e4619 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/loader_v1_integer.h @@ -0,0 +1,70 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_INTEGER_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_INTEGER_H_ + +#include + +#include + +#include // assert +#include // std::uint64_t + +namespace sourcemeta::jsonbinpack::v1 { + +auto BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( + const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("minimum")); + assert(options.defines("maximum")); + assert(options.defines("multiplier")); + const auto &minimum{options.at("minimum")}; + const auto &maximum{options.at("maximum")}; + const auto &multiplier{options.at("multiplier")}; + assert(minimum.is_integer()); + assert(maximum.is_integer()); + assert(multiplier.is_integer()); + assert(multiplier.is_positive()); + return sourcemeta::jsonbinpack::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED{ + minimum.to_integer(), maximum.to_integer(), + static_cast(multiplier.to_integer())}; +} + +auto FLOOR_MULTIPLE_ENUM_VARINT(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("minimum")); + assert(options.defines("multiplier")); + const auto &minimum{options.at("minimum")}; + const auto &multiplier{options.at("multiplier")}; + assert(minimum.is_integer()); + assert(multiplier.is_integer()); + assert(multiplier.is_positive()); + return sourcemeta::jsonbinpack::FLOOR_MULTIPLE_ENUM_VARINT{ + minimum.to_integer(), + static_cast(multiplier.to_integer())}; +} + +auto ROOF_MULTIPLE_MIRROR_ENUM_VARINT( + const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("maximum")); + assert(options.defines("multiplier")); + const auto &maximum{options.at("maximum")}; + const auto &multiplier{options.at("multiplier")}; + assert(maximum.is_integer()); + assert(multiplier.is_integer()); + assert(multiplier.is_positive()); + return sourcemeta::jsonbinpack::ROOF_MULTIPLE_MIRROR_ENUM_VARINT{ + maximum.to_integer(), + static_cast(multiplier.to_integer())}; +} + +auto ARBITRARY_MULTIPLE_ZIGZAG_VARINT( + const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("multiplier")); + const auto &multiplier{options.at("multiplier")}; + assert(multiplier.is_integer()); + assert(multiplier.is_positive()); + return sourcemeta::jsonbinpack::ARBITRARY_MULTIPLE_ZIGZAG_VARINT{ + static_cast(multiplier.to_integer())}; +} + +} // namespace sourcemeta::jsonbinpack::v1 + +#endif diff --git a/vendor/jsonbinpack/src/runtime/loader_v1_number.h b/vendor/jsonbinpack/src/runtime/loader_v1_number.h new file mode 100644 index 0000000..50f508f --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/loader_v1_number.h @@ -0,0 +1,16 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_NUMBER_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_NUMBER_H_ + +#include + +#include + +namespace sourcemeta::jsonbinpack::v1 { + +auto DOUBLE_VARINT_TUPLE(const sourcemeta::jsontoolkit::JSON &) -> Encoding { + return sourcemeta::jsonbinpack::DOUBLE_VARINT_TUPLE{}; +} + +} // namespace sourcemeta::jsonbinpack::v1 + +#endif diff --git a/vendor/jsonbinpack/src/runtime/loader_v1_string.h b/vendor/jsonbinpack/src/runtime/loader_v1_string.h new file mode 100644 index 0000000..c9ff959 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/loader_v1_string.h @@ -0,0 +1,70 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_STRING_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_LOADER_V1_STRING_H_ + +#include + +#include + +#include // assert +#include // std::uint64_t + +namespace sourcemeta::jsonbinpack::v1 { + +auto UTF8_STRING_NO_LENGTH(const sourcemeta::jsontoolkit::JSON &options) + -> Encoding { + assert(options.defines("size")); + const auto &size{options.at("size")}; + assert(size.is_integer()); + assert(size.is_positive()); + return sourcemeta::jsonbinpack::UTF8_STRING_NO_LENGTH{ + static_cast(size.to_integer())}; +} + +auto FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("minimum")); + const auto &minimum{options.at("minimum")}; + assert(minimum.is_integer()); + assert(minimum.is_positive()); + return sourcemeta::jsonbinpack::FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED{ + static_cast(minimum.to_integer())}; +} + +auto ROOF_VARINT_PREFIX_UTF8_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("maximum")); + const auto &maximum{options.at("maximum")}; + assert(maximum.is_integer()); + assert(maximum.is_positive()); + return sourcemeta::jsonbinpack::ROOF_VARINT_PREFIX_UTF8_STRING_SHARED{ + static_cast(maximum.to_integer())}; +} + +auto BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( + const sourcemeta::jsontoolkit::JSON &options) -> Encoding { + assert(options.defines("minimum")); + assert(options.defines("maximum")); + const auto &minimum{options.at("minimum")}; + const auto &maximum{options.at("maximum")}; + assert(minimum.is_integer()); + assert(maximum.is_integer()); + assert(minimum.is_positive()); + assert(maximum.is_positive()); + return sourcemeta::jsonbinpack::BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED{ + static_cast(minimum.to_integer()), + static_cast(maximum.to_integer())}; +} + +auto RFC3339_DATE_INTEGER_TRIPLET(const sourcemeta::jsontoolkit::JSON &) + -> Encoding { + return sourcemeta::jsonbinpack::RFC3339_DATE_INTEGER_TRIPLET{}; +} + +auto PREFIX_VARINT_LENGTH_STRING_SHARED(const sourcemeta::jsontoolkit::JSON &) + -> Encoding { + return sourcemeta::jsonbinpack::PREFIX_VARINT_LENGTH_STRING_SHARED{}; +} + +} // namespace sourcemeta::jsonbinpack::v1 + +#endif diff --git a/vendor/jsonbinpack/src/runtime/output_stream.cc b/vendor/jsonbinpack/src/runtime/output_stream.cc new file mode 100644 index 0000000..24fdf36 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/output_stream.cc @@ -0,0 +1,49 @@ +#include +#include + +#include "varint.h" + +#include // assert +#include // std::ios_base + +namespace sourcemeta::jsonbinpack { + +OutputStream::OutputStream(Stream &output) : stream{output} { + this->stream.exceptions(std::ios_base::badbit | std::ios_base::failbit | + std::ios_base::eofbit); +} + +auto OutputStream::position() const noexcept -> std::uint64_t { + return static_cast(this->stream.tellp()); +} + +auto OutputStream::put_byte(const std::uint8_t byte) -> void { + this->stream.put(static_cast(byte)); +} + +auto OutputStream::put_bytes(const std::uint16_t bytes) -> void { + this->stream.write( + reinterpret_cast(&bytes), + sizeof bytes); +} + +auto OutputStream::put_varint(const std::uint64_t value) -> void { + varint_encode(this->stream, value); +} + +auto OutputStream::put_varint_zigzag(const std::int64_t value) -> void { + varint_encode(this->stream, zigzag_encode(value)); +} + +auto OutputStream::put_string_utf8( + const sourcemeta::jsontoolkit::JSON::String &string, + const std::uint64_t length) -> void { + assert(string.size() == length); + // Do a manual for-loop based on the provided length instead of a range + // loop based on the string value to avoid accidental overflows + for (std::uint64_t index = 0; index < length; index++) { + this->put_byte(static_cast(string[index])); + } +} + +} // namespace sourcemeta::jsonbinpack diff --git a/vendor/jsonbinpack/src/runtime/runtime_parser_v1.h b/vendor/jsonbinpack/src/runtime/runtime_parser_v1.h deleted file mode 100644 index 2cef4ae..0000000 --- a/vendor/jsonbinpack/src/runtime/runtime_parser_v1.h +++ /dev/null @@ -1,281 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_PARSER_V1_H_ -#define SOURCEMETA_JSONBINPACK_PARSER_V1_H_ - -#include -#include -#include - -#include // std::transform -#include // assert -#include // std::uint64_t -#include // std::back_inserter -#include // std::move -#include // std::vector - -namespace sourcemeta::jsonbinpack::v1 { - -// Integers - -auto BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( - const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("minimum")); - assert(options.defines("maximum")); - assert(options.defines("multiplier")); - const auto &minimum{options.at("minimum")}; - const auto &maximum{options.at("maximum")}; - const auto &multiplier{options.at("multiplier")}; - assert(minimum.is_integer()); - assert(maximum.is_integer()); - assert(multiplier.is_integer()); - assert(multiplier.is_positive()); - return sourcemeta::jsonbinpack::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED{ - minimum.to_integer(), maximum.to_integer(), - static_cast(multiplier.to_integer())}; -} - -auto FLOOR_MULTIPLE_ENUM_VARINT(const sourcemeta::jsontoolkit::JSON &options) - -> Plan { - assert(options.defines("minimum")); - assert(options.defines("multiplier")); - const auto &minimum{options.at("minimum")}; - const auto &multiplier{options.at("multiplier")}; - assert(minimum.is_integer()); - assert(multiplier.is_integer()); - assert(multiplier.is_positive()); - return sourcemeta::jsonbinpack::FLOOR_MULTIPLE_ENUM_VARINT{ - minimum.to_integer(), - static_cast(multiplier.to_integer())}; -} - -auto ROOF_MULTIPLE_MIRROR_ENUM_VARINT( - const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("maximum")); - assert(options.defines("multiplier")); - const auto &maximum{options.at("maximum")}; - const auto &multiplier{options.at("multiplier")}; - assert(maximum.is_integer()); - assert(multiplier.is_integer()); - assert(multiplier.is_positive()); - return sourcemeta::jsonbinpack::ROOF_MULTIPLE_MIRROR_ENUM_VARINT{ - maximum.to_integer(), - static_cast(multiplier.to_integer())}; -} - -auto ARBITRARY_MULTIPLE_ZIGZAG_VARINT( - const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("multiplier")); - const auto &multiplier{options.at("multiplier")}; - assert(multiplier.is_integer()); - assert(multiplier.is_positive()); - return sourcemeta::jsonbinpack::ARBITRARY_MULTIPLE_ZIGZAG_VARINT{ - static_cast(multiplier.to_integer())}; -} - -// Numbers - -auto DOUBLE_VARINT_TUPLE(const sourcemeta::jsontoolkit::JSON &) -> Plan { - return sourcemeta::jsonbinpack::DOUBLE_VARINT_TUPLE{}; -} - -// Enumerations - -auto BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("choices")); - const auto &choices{options.at("choices")}; - assert(choices.is_array()); - const auto &array{choices.as_array()}; - std::vector elements{array.cbegin(), - array.cend()}; - return sourcemeta::jsonbinpack::BYTE_CHOICE_INDEX({std::move(elements)}); -} - -auto LARGE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("choices")); - const auto &choices{options.at("choices")}; - assert(choices.is_array()); - const auto &array{choices.as_array()}; - std::vector elements{array.cbegin(), - array.cend()}; - return sourcemeta::jsonbinpack::LARGE_CHOICE_INDEX({std::move(elements)}); -} - -auto TOP_LEVEL_BYTE_CHOICE_INDEX(const sourcemeta::jsontoolkit::JSON &options) - -> Plan { - assert(options.defines("choices")); - const auto &choices{options.at("choices")}; - assert(choices.is_array()); - const auto &array{choices.as_array()}; - std::vector elements{array.cbegin(), - array.cend()}; - return sourcemeta::jsonbinpack::TOP_LEVEL_BYTE_CHOICE_INDEX( - {std::move(elements)}); -} - -auto CONST_NONE(const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("value")); - return sourcemeta::jsonbinpack::CONST_NONE({options.at("value")}); -} - -// Strings - -auto UTF8_STRING_NO_LENGTH(const sourcemeta::jsontoolkit::JSON &options) - -> Plan { - assert(options.defines("size")); - const auto &size{options.at("size")}; - assert(size.is_integer()); - assert(size.is_positive()); - return sourcemeta::jsonbinpack::UTF8_STRING_NO_LENGTH{ - static_cast(size.to_integer())}; -} - -auto FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("minimum")); - const auto &minimum{options.at("minimum")}; - assert(minimum.is_integer()); - assert(minimum.is_positive()); - return sourcemeta::jsonbinpack::FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED{ - static_cast(minimum.to_integer())}; -} - -auto ROOF_VARINT_PREFIX_UTF8_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("maximum")); - const auto &maximum{options.at("maximum")}; - assert(maximum.is_integer()); - assert(maximum.is_positive()); - return sourcemeta::jsonbinpack::ROOF_VARINT_PREFIX_UTF8_STRING_SHARED{ - static_cast(maximum.to_integer())}; -} - -auto BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( - const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("minimum")); - assert(options.defines("maximum")); - const auto &minimum{options.at("minimum")}; - const auto &maximum{options.at("maximum")}; - assert(minimum.is_integer()); - assert(maximum.is_integer()); - assert(minimum.is_positive()); - assert(maximum.is_positive()); - return sourcemeta::jsonbinpack::BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED{ - static_cast(minimum.to_integer()), - static_cast(maximum.to_integer())}; -} - -auto RFC3339_DATE_INTEGER_TRIPLET(const sourcemeta::jsontoolkit::JSON &) - -> Plan { - return sourcemeta::jsonbinpack::RFC3339_DATE_INTEGER_TRIPLET{}; -} - -auto PREFIX_VARINT_LENGTH_STRING_SHARED(const sourcemeta::jsontoolkit::JSON &) - -> Plan { - return sourcemeta::jsonbinpack::PREFIX_VARINT_LENGTH_STRING_SHARED{}; -} - -// Arrays - -auto FIXED_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("size")); - assert(options.defines("encoding")); - assert(options.defines("prefixEncodings")); - const auto &size{options.at("size")}; - const auto &array_encoding{options.at("encoding")}; - const auto &prefix_encodings{options.at("prefixEncodings")}; - assert(size.is_integer()); - assert(size.is_positive()); - assert(array_encoding.is_object()); - assert(prefix_encodings.is_array()); - std::vector encodings; - std::transform(prefix_encodings.as_array().cbegin(), - prefix_encodings.as_array().cend(), - std::back_inserter(encodings), - [](const auto &element) { return parse(element); }); - assert(encodings.size() == prefix_encodings.size()); - return sourcemeta::jsonbinpack::FIXED_TYPED_ARRAY{ - static_cast(size.to_integer()), - wrap(parse(array_encoding)), wrap(encodings.begin(), encodings.end())}; -} - -auto BOUNDED_8BITS_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) - -> Plan { - assert(options.defines("minimum")); - assert(options.defines("maximum")); - assert(options.defines("encoding")); - assert(options.defines("prefixEncodings")); - const auto &minimum{options.at("minimum")}; - const auto &maximum{options.at("maximum")}; - const auto &array_encoding{options.at("encoding")}; - const auto &prefix_encodings{options.at("prefixEncodings")}; - assert(minimum.is_integer()); - assert(maximum.is_integer()); - assert(minimum.is_positive()); - assert(maximum.is_positive()); - assert(array_encoding.is_object()); - assert(prefix_encodings.is_array()); - std::vector encodings; - std::transform(prefix_encodings.as_array().cbegin(), - prefix_encodings.as_array().cend(), - std::back_inserter(encodings), - [](const auto &element) { return parse(element); }); - assert(encodings.size() == prefix_encodings.size()); - return sourcemeta::jsonbinpack::BOUNDED_8BITS_TYPED_ARRAY{ - static_cast(minimum.to_integer()), - static_cast(maximum.to_integer()), - wrap(parse(array_encoding)), wrap(encodings.begin(), encodings.end())}; -} - -auto FLOOR_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("minimum")); - assert(options.defines("encoding")); - assert(options.defines("prefixEncodings")); - const auto &minimum{options.at("minimum")}; - const auto &array_encoding{options.at("encoding")}; - const auto &prefix_encodings{options.at("prefixEncodings")}; - assert(minimum.is_integer()); - assert(minimum.is_positive()); - assert(array_encoding.is_object()); - assert(prefix_encodings.is_array()); - std::vector encodings; - std::transform(prefix_encodings.as_array().cbegin(), - prefix_encodings.as_array().cend(), - std::back_inserter(encodings), - [](const auto &element) { return parse(element); }); - assert(encodings.size() == prefix_encodings.size()); - return sourcemeta::jsonbinpack::FLOOR_TYPED_ARRAY{ - static_cast(minimum.to_integer()), - wrap(parse(array_encoding)), wrap(encodings.begin(), encodings.end())}; -} - -auto ROOF_TYPED_ARRAY(const sourcemeta::jsontoolkit::JSON &options) -> Plan { - assert(options.defines("maximum")); - assert(options.defines("encoding")); - assert(options.defines("prefixEncodings")); - const auto &maximum{options.at("maximum")}; - const auto &array_encoding{options.at("encoding")}; - const auto &prefix_encodings{options.at("prefixEncodings")}; - assert(maximum.is_integer()); - assert(maximum.is_positive()); - assert(array_encoding.is_object()); - assert(prefix_encodings.is_array()); - std::vector encodings; - std::transform(prefix_encodings.as_array().cbegin(), - prefix_encodings.as_array().cend(), - std::back_inserter(encodings), - [](const auto &element) { return parse(element); }); - assert(encodings.size() == prefix_encodings.size()); - return sourcemeta::jsonbinpack::ROOF_TYPED_ARRAY{ - static_cast(maximum.to_integer()), - wrap(parse(array_encoding)), wrap(encodings.begin(), encodings.end())}; -} - -// Any - -auto ANY_PACKED_TYPE_TAG_BYTE_PREFIX(const sourcemeta::jsontoolkit::JSON &) - -> Plan { - return sourcemeta::jsonbinpack::ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}; -} - -} // namespace sourcemeta::jsonbinpack::v1 - -#endif diff --git a/vendor/jsonbinpack/src/runtime/unreachable.h b/vendor/jsonbinpack/src/runtime/unreachable.h new file mode 100644 index 0000000..21f9515 --- /dev/null +++ b/vendor/jsonbinpack/src/runtime/unreachable.h @@ -0,0 +1,17 @@ +#ifndef SOURCEMETA_JSONBINPACK_RUNTIME_UNREACHABLE_H_ +#define SOURCEMETA_JSONBINPACK_RUNTIME_UNREACHABLE_H_ + +#include // assert + +// Until we are on C++23 and can use std::unreachable +// See https://en.cppreference.com/w/cpp/utility/unreachable +[[noreturn]] inline void unreachable() { + assert(false); +#if defined(_MSC_VER) && !defined(__clang__) + __assume(false); +#else + __builtin_unreachable(); +#endif +} + +#endif diff --git a/vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_varint.h b/vendor/jsonbinpack/src/runtime/varint.h similarity index 100% rename from vendor/jsonbinpack/src/runtime/include/sourcemeta/jsonbinpack/runtime_varint.h rename to vendor/jsonbinpack/src/runtime/varint.h