diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 925c7fbd..2b701265 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -1735,6 +1735,7 @@ toolset tooltip torquem TOUPPER +transcoder transcoding treeview tribudubois diff --git a/mdbx.h++ b/mdbx.h++ index 349f185c..654cee8b 100644 --- a/mdbx.h++ +++ b/mdbx.h++ @@ -191,6 +191,24 @@ #endif #endif /* MDBX_CXX20_UNLIKELY */ +#ifndef MDBX_CXX20_CONCEPT +#if defined(DOXYGEN) || (defined(__cpp_concepts) && __cpp_concepts >= 201907L) +#define MDBX_CXX20_CONCEPT(CONCEPT, NAME) CONCEPT NAME +#else +#define MDBX_CXX20_CONCEPT(CONCEPT, NAME) typename NAME +#endif +#endif /* MDBX_CXX20_CONCEPT */ + +#ifndef MDBX_ASSERT_CXX20_CONCEPT_SATISFIED +#if defined(DOXYGEN) || (defined(__cpp_concepts) && __cpp_concepts >= 201907L) +#define MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(CONCEPT, TYPE) \ + static_assert(CONCEPT) +#else +#define MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(CONCEPT, NAME) \ + static_assert(true, MDBX_STRINGIFY(CONCEPT) "<" MDBX_STRINGIFY(TYPE) ">") +#endif +#endif /* MDBX_ASSERT_CXX20_CONCEPT_SATISFIED */ + #ifdef _MSC_VER #pragma warning(push, 4) #pragma warning(disable : 4127) /* conditional expression is constant */ @@ -247,7 +265,15 @@ MDBX_CXX11_CONSTEXPR const build_info &get_build() noexcept; /// \brief constexpr-compatible strlen(). static MDBX_CXX17_CONSTEXPR size_t strlen(const char *c_str) noexcept; +/// \brief Legacy default allocator +/// but it is recommended to use \ref polymorphic_allocator. +using legacy_allocator = ::std::string::allocator_type; + struct slice; +struct default_capacity_policy; +template +class buffer; class env; class env_managed; class txn; @@ -255,10 +281,6 @@ class txn_managed; class cursor; class cursor_managed; -/// \brief Legacy default allocator -/// but it is recommended to use \ref polymorphic_allocator. -using legacy_allocator = ::std::string::allocator_type; - #if defined(DOXYGEN) || \ defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L /// \brief Default polymorphic allocator for modern code. @@ -444,6 +466,60 @@ static MDBX_CXX20_CONSTEXPR void *memcpy(void *dest, const void *src, size_t bytes) noexcept; //------------------------------------------------------------------------------ +#if defined(DOXYGEN) || (defined(__cpp_concepts) && __cpp_concepts >= 201907L) + +template +concept MutableByteProducer = requires(T a, char array[42]) { + { a.is_empty() } + ->std::same_as; + { a.envisage_result_length() } + ->std::same_as; + { a.write_bytes(&array[0], size_t(42)) } + ->std::same_as; +}; + +template +concept ImmutableByteProducer = requires(const T &a, char array[42]) { + { a.is_empty() } + ->std::same_as; + { a.envisage_result_length() } + ->std::same_as; + { a.write_bytes(&array[0], size_t(42)) } + ->std::same_as; +}; + +template +concept SliceTranscoder = + ImmutableByteProducer &&requires(const slice &source, const T &a) { + T(source); + { a.is_erroneous() } + ->std::same_as; +}; + +#endif /* __cpp_concepts >= 201907L*/ + +template +inline buffer +make_buffer(PRODUCER &producer, const ALLOCATOR &allocator = ALLOCATOR()); + +template +inline buffer +make_buffer(const PRODUCER &producer, const ALLOCATOR &allocator = ALLOCATOR()); + +template +inline string make_string(PRODUCER &producer, + const ALLOCATOR &allocator = ALLOCATOR()); + +template +inline string make_string(const PRODUCER &producer, + const ALLOCATOR &allocator = ALLOCATOR()); + /// \brief References a data located outside the slice. /// /// The `slice` is similar in many ways to `std::string_view`, but it @@ -478,24 +554,25 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// \brief Create a slice that refers to the contents of "str". /// \note 'explicit' to avoid reference to the temporary std::string instance. - template - explicit MDBX_CXX20_CONSTEXPR slice(const ::std::basic_string &str) - : slice(str.data(), str.length() * sizeof(C)) {} + template + explicit MDBX_CXX20_CONSTEXPR + slice(const ::std::basic_string &str) + : slice(str.data(), str.length() * sizeof(CHAR)) {} MDBX_CXX14_CONSTEXPR slice(const MDBX_val &src); MDBX_CXX11_CONSTEXPR slice(const slice &) noexcept = default; - inline slice(MDBX_val &&src); - inline slice(slice &&src) noexcept; + MDBX_CXX14_CONSTEXPR slice(MDBX_val &&src); + MDBX_CXX14_CONSTEXPR slice(slice &&src) noexcept; #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) /// \brief Create a slice that refers to the same contents as "string_view" - template - MDBX_CXX14_CONSTEXPR slice(const ::std::basic_string_view &sv) + template + MDBX_CXX14_CONSTEXPR slice(const ::std::basic_string_view &sv) : slice(sv.data(), sv.data() + sv.length()) {} - template - slice(::std::basic_string_view &&sv) : slice(sv) { + template + slice(::std::basic_string_view &&sv) : slice(sv) { sv = {}; } #endif /* __cpp_lib_string_view >= 201606L */ @@ -519,19 +596,19 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { inline slice &assign(slice &&src) noexcept; inline slice &assign(::MDBX_val &&src); inline slice &assign(const void *begin, const void *end); - template - slice &assign(const ::std::basic_string &str) { - return assign(str.data(), str.length() * sizeof(C)); + template + slice &assign(const ::std::basic_string &str) { + return assign(str.data(), str.length() * sizeof(CHAR)); } inline slice &assign(const char *c_str); #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template - slice &assign(const ::std::basic_string_view &view) { + template + slice &assign(const ::std::basic_string_view &view) { return assign(view.begin(), view.end()); } - template - slice &assign(::std::basic_string_view &&view) { + template + slice &assign(::std::basic_string_view &&view) { assign(view); view = {}; return *this; @@ -544,155 +621,112 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template - slice &operator=(const ::std::basic_string_view &view) { + template + slice &operator=(const ::std::basic_string_view &view) { return assign(view); } - template - slice &operator=(::std::basic_string_view &&view) { + template + slice &operator=(::std::basic_string_view &&view) { return assign(view); } /// \brief Return a string_view that references the same data as this slice. - template > - MDBX_CXX11_CONSTEXPR ::std::basic_string_view + template > + MDBX_CXX11_CONSTEXPR ::std::basic_string_view string_view() const noexcept { - static_assert(sizeof(C) == 1, "Must be single byte characters"); - return ::std::basic_string_view(char_ptr(), length()); + static_assert(sizeof(CHAR) == 1, "Must be single byte characters"); + return ::std::basic_string_view(char_ptr(), length()); } /// \brief Return a string_view that references the same data as this slice. - template + template MDBX_CXX11_CONSTEXPR explicit - operator ::std::basic_string_view() const noexcept { - return this->string_view(); + operator ::std::basic_string_view() const noexcept { + return this->string_view(); } #endif /* __cpp_lib_string_view >= 201606L */ - template , - class A = legacy_allocator> - MDBX_CXX20_CONSTEXPR ::std::basic_string - string(const A &allocator = A()) const { - static_assert(sizeof(C) == 1, "Must be single byte characters"); - return ::std::basic_string(char_ptr(), length(), allocator); + template , + class ALLOCATOR = legacy_allocator> + MDBX_CXX20_CONSTEXPR ::std::basic_string + as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + static_assert(sizeof(CHAR) == 1, "Must be single byte characters"); + return ::std::basic_string(char_ptr(), length(), + allocator); } - template - MDBX_CXX20_CONSTEXPR explicit operator ::std::basic_string() const { - return this->string(); - } - - /// \brief Fills the destination by hexadecimal data dump of slice content. - /// \throws std::length_error if given buffer is too small. - char *to_hex(char *dest, size_t dest_size, bool uppercase = false, - unsigned wrap_width = 0) const; - - /// \brief Returns the number of bytes needed for hexadecimal data dump - /// of slice content. - MDBX_CXX11_CONSTEXPR size_t - envisage_to_hex_length(unsigned wrap_width = 0) const noexcept { - const size_t bytes = length() << 1; - return wrap_width ? bytes + bytes / wrap_width : bytes; - } - - /// \brief Fills the destination with data converted from hexadecimal dump - /// from slice content. - /// \throws std::length_error if given buffer is too small. - byte *from_hex(byte *dest, size_t dest_size, - bool ignore_spaces = false) const; - - /// \brief Returns the number of bytes needed for conversion - /// hexadecimal dump from slice content to data. - MDBX_CXX11_CONSTEXPR size_t envisage_from_hex_length() const noexcept { - return length() >> 1; - } - - /// \brief Fills the destination by - /// [Base58](https://en.wikipedia.org/wiki/Base58) data dump of slice content. - /// \throws std::length_error if given buffer is too small. - char *to_base58(char *dest, size_t dest_size, unsigned wrap_width = 0) const; - - /// \brief Returns the number of bytes needed for - /// [Base58](https://en.wikipedia.org/wiki/Base58) data dump of slice content. - MDBX_CXX11_CONSTEXPR size_t - envisage_to_base58_length(unsigned wrap_width = 0) const noexcept { - const size_t bytes = length() / 8 * 11 + (length() % 8 * 43 + 31) / 32; - return wrap_width ? bytes + bytes / wrap_width : bytes; - } - - /// \brief Fills the destination with data converted from - /// [Base58](https://en.wikipedia.org/wiki/Base58) dump from slice content. - /// \throws std::length_error if given buffer is too small. - byte *from_base58(byte *dest, size_t dest_size, - bool ignore_spaces = false) const; - - /// \brief Returns the number of bytes needed for conversion - /// [Base58](https://en.wikipedia.org/wiki/Base58) dump to data. - MDBX_CXX11_CONSTEXPR size_t envisage_from_base58_length() const noexcept { - return length() / 11 * 8 + length() % 11 * 32 / 43; - } - - /// \brief Fills the destination by - /// [Base64](https://en.wikipedia.org/wiki/Base64) data dump. \throws - /// std::length_error if given buffer is too small. - char *to_base64(char *dest, size_t dest_size, unsigned wrap_width = 0) const; - - /// \brief Returns the number of bytes needed for - /// [Base64](https://en.wikipedia.org/wiki/Base64) data dump. - MDBX_CXX11_CONSTEXPR size_t - envisage_to_base64_length(unsigned wrap_width = 0) const noexcept { - const size_t bytes = (length() + 2) / 3 * 4; - return wrap_width ? bytes + bytes / wrap_width : bytes; - } - - /// \brief Fills the destination with data converted from - /// [Base64](https://en.wikipedia.org/wiki/Base64) dump. - /// \throws std::length_error if given buffer is too small. - byte *from_base64(byte *dest, size_t dest_size, - bool ignore_spaces = false) const; - - /// \brief Returns the number of bytes needed for conversion - /// [Base64](https://en.wikipedia.org/wiki/Base64) dump to data. - MDBX_CXX11_CONSTEXPR size_t envisage_from_base64_length() const noexcept { - return (length() + 3) / 4 * 3; + template + MDBX_CXX20_CONSTEXPR explicit + operator ::std::basic_string() const { + return as_string(); } /// \brief Returns a string with a hexadecimal dump of the slice content. template - inline ::mdbx::string - hex_encode(bool uppercase = false, - const ALLOCATOR &allocator = ALLOCATOR()) const; - - /// \brief Decodes hexadecimal dump from the slice content into returned data - /// string. - template - inline ::mdbx::string - hex_decode(const ALLOCATOR &allocator = ALLOCATOR()) const; + inline string + as_hex_string(bool uppercase = false, unsigned wrap_width = 0, + const ALLOCATOR &allocator = ALLOCATOR()) const; /// \brief Returns a string with a /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content. template - inline ::mdbx::string - base58_encode(const ALLOCATOR &allocator = ALLOCATOR()) const; - - /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump - /// from the slice content into returned data string. - template - inline ::mdbx::string - base58_decode(const ALLOCATOR &allocator = ALLOCATOR()) const; + inline string + as_base58_string(unsigned wrap_width = 0, + const ALLOCATOR &allocator = ALLOCATOR()) const; /// \brief Returns a string with a - /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content. + /// [Base58](https://en.wikipedia.org/wiki/Base64) dump of the slice content. template - inline ::mdbx::string - base64_encode(const ALLOCATOR &allocator = ALLOCATOR()) const; + inline string + as_base64_string(unsigned wrap_width = 0, + const ALLOCATOR &allocator = ALLOCATOR()) const; + + /// \brief Returns a buffer with a hexadecimal dump of the slice content. + template + inline buffer + encode_hex(bool uppercase = false, unsigned wrap_width = 0, + const ALLOCATOR &allocator = ALLOCATOR()) const; + + /// \brief Returns a buffer with a + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content. + template + inline buffer + encode_base58(unsigned wrap_width = 0, + const ALLOCATOR &allocator = ALLOCATOR()) const; + + /// \brief Returns a buffer with a + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content. + template + inline buffer + encode_base64(unsigned wrap_width = 0, + const ALLOCATOR &allocator = ALLOCATOR()) const; + + /// \brief Decodes hexadecimal dump from the slice content to returned buffer. + template + inline buffer + hex_decode(bool ignore_spaces = false, + const ALLOCATOR &allocator = ALLOCATOR()) const; + + /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump + /// from the slice content to returned buffer. + template + inline buffer + base58_decode(bool ignore_spaces = false, + const ALLOCATOR &allocator = ALLOCATOR()) const; /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump - /// from the slice content into returned data string. - template - inline ::mdbx::string - base64_decode(const ALLOCATOR &allocator = ALLOCATOR()) const; + /// from the slice content to returned buffer. + template + inline buffer + base64_decode(bool ignore_spaces = false, + const ALLOCATOR &allocator = ALLOCATOR()) const; /// \brief Checks whether the content of the slice is printable. /// \param [in] disable_utf8 By default if `disable_utf8` is `false` function @@ -706,32 +740,33 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// \param [in] ignore_spaces If `true` function will skips spaces surrounding /// (before, between and after) a encoded bytes. However, spaces should not /// break a pair of characters encoding a single byte. - MDBX_NOTHROW_PURE_FUNCTION bool + inline MDBX_NOTHROW_PURE_FUNCTION bool is_hex(bool ignore_spaces = false) const noexcept; - MDBX_NOTHROW_PURE_FUNCTION bool /// \brief Checks whether the content of the slice is a /// [Base58](https://en.wikipedia.org/wiki/Base58) dump. /// \param [in] ignore_spaces If `true` function will skips spaces surrounding /// (before, between and after) a encoded bytes. However, spaces should not /// break a code group of characters. + inline MDBX_NOTHROW_PURE_FUNCTION bool is_base58(bool ignore_spaces = false) const noexcept; - MDBX_NOTHROW_PURE_FUNCTION bool /// \brief Checks whether the content of the slice is a /// [Base64](https://en.wikipedia.org/wiki/Base64) dump. /// \param [in] ignore_spaces If `true` function will skips spaces surrounding /// (before, between and after) a encoded bytes. However, spaces should not /// break a code group of characters. + inline MDBX_NOTHROW_PURE_FUNCTION bool is_base64(bool ignore_spaces = false) const noexcept; inline void swap(slice &other) noexcept; + #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template - void swap(::std::basic_string_view &view) noexcept { - static_assert(sizeof(C) == 1, "Must be single byte characters"); - const auto temp = ::std::basic_string_view(*this); + template + void swap(::std::basic_string_view &view) noexcept { + static_assert(sizeof(CHAR) == 1, "Must be single byte characters"); + const auto temp = ::std::basic_string_view(*this); *this = view; view = temp; } @@ -741,17 +776,35 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { MDBX_CXX11_CONSTEXPR const byte *byte_ptr() const noexcept; MDBX_CXX11_CONSTEXPR byte *byte_ptr() noexcept; + /// \brief Returns casted to pointer to byte an end of data. + MDBX_CXX11_CONSTEXPR const byte *end_byte_ptr() const noexcept; + MDBX_CXX11_CONSTEXPR byte *end_byte_ptr() noexcept; + /// \brief Returns casted to pointer to char an address of data. MDBX_CXX11_CONSTEXPR const char *char_ptr() const noexcept; MDBX_CXX11_CONSTEXPR char *char_ptr() noexcept; + /// \brief Returns casted to pointer to char an end of data. + MDBX_CXX11_CONSTEXPR const char *end_char_ptr() const noexcept; + MDBX_CXX11_CONSTEXPR char *end_char_ptr() noexcept; + /// \brief Return a pointer to the beginning of the referenced data. MDBX_CXX11_CONSTEXPR const void *data() const noexcept; MDBX_CXX11_CONSTEXPR void *data() noexcept; + /// \brief Return a pointer to the ending of the referenced data. + MDBX_CXX11_CONSTEXPR const void *end() const noexcept; + MDBX_CXX11_CONSTEXPR void *end() noexcept; + /// \brief Returns the number of bytes. MDBX_CXX11_CONSTEXPR size_t length() const noexcept; + /// \brief Set slice length. + MDBX_CXX14_CONSTEXPR slice &set_length(size_t bytes); + + /// \brief Sets the length by specifying the end of the slice data. + MDBX_CXX14_CONSTEXPR slice &set_end(const void *ptr); + /// \brief Checks whether the slice is empty. MDBX_CXX11_CONSTEXPR bool empty() const noexcept; @@ -765,10 +818,10 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { MDBX_CXX11_CONSTEXPR operator bool() const noexcept; /// \brief Depletes content of slice and make it invalid. - inline void invalidate() noexcept; + MDBX_CXX14_CONSTEXPR void invalidate() noexcept; /// \brief Makes the slice empty and referencing to nothing. - inline void clear() noexcept; + MDBX_CXX14_CONSTEXPR void clear() noexcept; /// \brief Drops the first "n" bytes from this slice. /// \pre REQUIRES: `n <= size()` @@ -787,44 +840,44 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { inline void safe_remove_suffix(size_t n); /// \brief Checks if the data starts with the given prefix. - MDBX_NOTHROW_PURE_FUNCTION inline bool + MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool starts_with(const slice &prefix) const noexcept; /// \brief Checks if the data ends with the given suffix. - MDBX_NOTHROW_PURE_FUNCTION inline bool + MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool ends_with(const slice &suffix) const noexcept; /// \brief Returns the nth byte in the referenced data. /// \pre REQUIRES: `n < size()` - inline byte operator[](size_t n) const noexcept; + MDBX_CXX11_CONSTEXPR byte operator[](size_t n) const noexcept; /// \brief Returns the nth byte in the referenced data with bounds checking. /// \throws std::out_of_range if `n >= size()` - inline byte at(size_t n) const; + MDBX_CXX11_CONSTEXPR byte at(size_t n) const; /// \brief Returns the first "n" bytes of the slice. /// \pre REQUIRES: `n <= size()` - inline slice head(size_t n) const noexcept; + MDBX_CXX14_CONSTEXPR slice head(size_t n) const noexcept; /// \brief Returns the last "n" bytes of the slice. /// \pre REQUIRES: `n <= size()` - inline slice tail(size_t n) const noexcept; + MDBX_CXX14_CONSTEXPR slice tail(size_t n) const noexcept; /// \brief Returns the middle "n" bytes of the slice. /// \pre REQUIRES: `from + n <= size()` - inline slice middle(size_t from, size_t n) const noexcept; + MDBX_CXX14_CONSTEXPR slice middle(size_t from, size_t n) const noexcept; /// \brief Returns the first "n" bytes of the slice. /// \throws std::out_of_range if `n >= size()` - inline slice safe_head(size_t n) const; + MDBX_CXX14_CONSTEXPR slice safe_head(size_t n) const; /// \brief Returns the last "n" bytes of the slice. /// \throws std::out_of_range if `n >= size()` - inline slice safe_tail(size_t n) const; + MDBX_CXX14_CONSTEXPR slice safe_tail(size_t n) const; /// \brief Returns the middle "n" bytes of the slice. /// \throws std::out_of_range if `from + n >= size()` - inline slice safe_middle(size_t from, size_t n) const; + MDBX_CXX14_CONSTEXPR slice safe_middle(size_t from, size_t n) const; /// \brief Returns the hash value of referenced data. /// \attention Function implementation and returned hash values may changed @@ -840,7 +893,7 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// > 0 if "a" longer than "b", /// < 0 if "a" length-equal and lexicographically less than "b", /// > 0 if "a" length-equal and lexicographically great than "b". - MDBX_NOTHROW_PURE_FUNCTION static inline intptr_t + MDBX_NOTHROW_PURE_FUNCTION static MDBX_CXX14_CONSTEXPR intptr_t compare_fast(const slice &a, const slice &b) noexcept; /// \brief Three-way lexicographically comparison. @@ -848,14 +901,20 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// < 0 if "a" < "b", /// == 0 if "a" == "b", /// > 0 if "a" > "b". - MDBX_NOTHROW_PURE_FUNCTION static inline intptr_t + MDBX_NOTHROW_PURE_FUNCTION static MDBX_CXX14_CONSTEXPR intptr_t compare_lexicographically(const slice &a, const slice &b) noexcept; - friend inline bool operator==(const slice &a, const slice &b) noexcept; - friend inline bool operator<(const slice &a, const slice &b) noexcept; - friend inline bool operator>(const slice &a, const slice &b) noexcept; - friend inline bool operator<=(const slice &a, const slice &b) noexcept; - friend inline bool operator>=(const slice &a, const slice &b) noexcept; - friend inline bool operator!=(const slice &a, const slice &b) noexcept; + friend MDBX_CXX14_CONSTEXPR bool operator==(const slice &a, + const slice &b) noexcept; + friend MDBX_CXX14_CONSTEXPR bool operator<(const slice &a, + const slice &b) noexcept; + friend MDBX_CXX14_CONSTEXPR bool operator>(const slice &a, + const slice &b) noexcept; + friend MDBX_CXX14_CONSTEXPR bool operator<=(const slice &a, + const slice &b) noexcept; + friend MDBX_CXX14_CONSTEXPR bool operator>=(const slice &a, + const slice &b) noexcept; + friend MDBX_CXX14_CONSTEXPR bool operator!=(const slice &a, + const slice &b) noexcept; /// \brief Checks the slice is not refers to null address or has zero length. MDBX_CXX11_CONSTEXPR bool is_valid() const noexcept { @@ -864,7 +923,9 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// \brief Build an invalid slice which non-zero length and refers to null /// address. - inline static slice invalid() noexcept { return slice(size_t(-1)); } + MDBX_CXX14_CONSTEXPR static slice invalid() noexcept { + return slice(size_t(-1)); + } protected: MDBX_CXX11_CONSTEXPR slice(size_t invalid_length) noexcept @@ -1036,10 +1097,304 @@ struct default_capacity_policy { } }; +/// \brief Hexadecimal encoder which satisfy \ref SliceTranscoder concept. +struct LIBMDBX_API to_hex { + const slice source; + const bool uppercase = false; + const unsigned wrap_width = 0; + MDBX_CXX11_CONSTEXPR to_hex(const slice &source, bool uppercase = false, + unsigned wrap_width = 0) noexcept + : source(source), uppercase(uppercase), wrap_width(wrap_width) { + MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, to_hex); + } + + /// \brief Returns a string with a hexadecimal dump of a passed slice. + template + string as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_string(*this, allocator); + } + + /// \brief Returns a buffer with a hexadecimal dump of a passed slice. + template + buffer + as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_buffer(*this, allocator); + } + + /// \brief Returns the buffer size in bytes needed for hexadecimal + /// dump of a passed slice. + MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { + const size_t bytes = source.length() << 1; + return wrap_width ? bytes + bytes / wrap_width : bytes; + } + + /// \brief Fills the buffer by hexadecimal dump of a passed slice. + /// \throws std::length_error if given buffer is too small. + char *write_bytes(char *dest, size_t dest_size) const; + + /// \brief Checks whether a passed slice is empty, + /// and therefore there will be no output bytes. + bool is_empty() const noexcept { return source.empty(); } + + /// \brief Checks whether the content of a passed slice is a valid data + /// and could be encoded or unexpectedly not. + bool is_erroneous() const noexcept { return false; } +}; + +/// \brief [Base58](https://en.wikipedia.org/wiki/Base58) encoder which satisfy +/// \ref SliceTranscoder concept. +struct LIBMDBX_API to_base58 { + const slice source; + const unsigned wrap_width = 0; + MDBX_CXX11_CONSTEXPR + to_base58(const slice &source, unsigned wrap_width = 0) noexcept + : source(source), wrap_width(wrap_width) { + MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, to_base58); + } + + /// \brief Returns a string with a + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of a passed slice. + template + string as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_string(*this, allocator); + } + + /// \brief Returns a buffer with a + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of a passed slice. + template + buffer + as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_buffer(*this, allocator); + } + + /// \brief Returns the buffer size in bytes needed for + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice. + MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { + const size_t bytes = + source.length() / 8 * 11 + (source.length() % 8 * 43 + 31) / 32; + return wrap_width ? bytes + bytes / wrap_width : bytes; + } + + /// \brief Fills the buffer by [Base58](https://en.wikipedia.org/wiki/Base58) + /// dump of passed slice. + /// \throws std::length_error if given buffer is too small. + char *write_bytes(char *dest, size_t dest_size) const; + + /// \brief Checks whether a passed slice is empty, + /// and therefore there will be no output bytes. + bool is_empty() const noexcept { return source.empty(); } + + /// \brief Checks whether the content of a passed slice is a valid data + /// and could be encoded or unexpectedly not. + bool is_erroneous() const noexcept { return false; } +}; + +/// \brief [Base64](https://en.wikipedia.org/wiki/Base64) encoder which satisfy +/// \ref SliceTranscoder concept. +struct LIBMDBX_API to_base64 { + const slice source; + const unsigned wrap_width = 0; + MDBX_CXX11_CONSTEXPR + to_base64(const slice &source, unsigned wrap_width = 0) noexcept + : source(source), wrap_width(wrap_width) { + MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, to_base64); + } + + /// \brief Returns a string with a + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of a passed slice. + template + string as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_string(*this, allocator); + } + + /// \brief Returns a buffer with a + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of a passed slice. + template + buffer + as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_buffer(*this, allocator); + } + + /// \brief Returns the buffer size in bytes needed for + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of passed slice. + MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { + const size_t bytes = (source.length() + 2) / 3 * 4; + return wrap_width ? bytes + bytes / wrap_width : bytes; + } + + /// \brief Fills the buffer by [Base64](https://en.wikipedia.org/wiki/Base64) + /// dump of passed slice. + /// \throws std::length_error if given buffer is too small. + char *write_bytes(char *dest, size_t dest_size) const; + + /// \brief Checks whether a passed slice is empty, + /// and therefore there will be no output bytes. + bool is_empty() const noexcept { return source.empty(); } + + /// \brief Checks whether the content of a passed slice is a valid data + /// and could be encoded or unexpectedly not. + bool is_erroneous() const noexcept { return false; } +}; + +inline ::std::ostream &operator<<(::std::ostream out, const to_hex &wrapper) { + return out << wrapper.as_string(); +} +inline ::std::ostream &operator<<(::std::ostream out, + const to_base58 &wrapper) { + return out << wrapper.as_string(); +} +inline ::std::ostream &operator<<(::std::ostream out, + const to_base64 &wrapper) { + return out << wrapper.as_string(); +} + +/// \brief Hexadecimal decoder which satisfy \ref SliceTranscoder concept. +struct LIBMDBX_API from_hex { + const slice source; + const bool ignore_spaces = false; + MDBX_CXX11_CONSTEXPR from_hex(const slice &source, + bool ignore_spaces = false) noexcept + : source(source), ignore_spaces(ignore_spaces) { + MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, from_hex); + } + + /// \brief Decodes hexadecimal dump from a passed slice to returned string. + template + string as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_string(*this, allocator); + } + + /// \brief Decodes hexadecimal dump from a passed slice to returned buffer. + template + buffer + as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_buffer(*this, allocator); + } + + /// \brief Returns the number of bytes needed for conversion + /// hexadecimal dump from a passed slice to decoded data. + MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { + return source.length() >> 1; + } + + /// \brief Fills the destination with data decoded from hexadecimal dump + /// from a passed slice. + /// \throws std::length_error if given buffer is too small. + char *write_bytes(char *dest, size_t dest_size) const; + + /// \brief Checks whether a passed slice is empty, + /// and therefore there will be no output bytes. + bool is_empty() const noexcept { return source.empty(); } + + /// \brief Checks whether the content of a passed slice is a valid hexadecimal + /// dump, and therefore there could be decoded or not. + bool is_erroneous() const noexcept; +}; + +/// \brief [Base58](https://en.wikipedia.org/wiki/Base58) decoder which satisfy +/// \ref SliceTranscoder concept. +struct LIBMDBX_API from_base58 { + const slice source; + const bool ignore_spaces = false; + MDBX_CXX11_CONSTEXPR from_base58(const slice &source, + bool ignore_spaces = false) noexcept + : source(source), ignore_spaces(ignore_spaces) { + MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, from_base58); + } + + /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump from a + /// passed slice to returned string. + template + string as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_string(*this, allocator); + } + + /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump from a + /// passed slice to returned buffer. + template + buffer + as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_buffer(*this, allocator); + } + + /// \brief Returns the number of bytes needed for conversion + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to + /// decoded data. + MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { + return source.length() / 11 * 8 + source.length() % 11 * 32 / 43; + } + + /// \brief Fills the destination with data decoded from + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice. + /// \throws std::length_error if given buffer is too small. + char *write_bytes(char *dest, size_t dest_size) const; + + /// \brief Checks whether a passed slice is empty, + /// and therefore there will be no output bytes. + bool is_empty() const noexcept { return source.empty(); } + + /// \brief Checks whether the content of a passed slice is a valid + /// [Base58](https://en.wikipedia.org/wiki/Base58) dump, and therefore there + /// could be decoded or not. + bool is_erroneous() const noexcept; +}; + +/// \brief [Base64](https://en.wikipedia.org/wiki/Base64) decoder which satisfy +/// \ref SliceTranscoder concept. +struct LIBMDBX_API from_base64 { + const slice source; + const bool ignore_spaces = false; + MDBX_CXX11_CONSTEXPR from_base64(const slice &source, + bool ignore_spaces = false) noexcept + : source(source), ignore_spaces(ignore_spaces) { + MDBX_ASSERT_CXX20_CONCEPT_SATISFIED(SliceTranscoder, from_base64); + } + + /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump from a + /// passed slice to returned string. + template + string as_string(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_string(*this, allocator); + } + + /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump from a + /// passed slice to returned buffer. + template + buffer + as_buffer(const ALLOCATOR &allocator = ALLOCATOR()) const { + return make_buffer(*this, allocator); + } + + /// \brief Returns the number of bytes needed for conversion + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump from a passed slice to + /// decoded data. + MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept { + return (source.length() + 3) / 4 * 3; + } + + /// \brief Fills the destination with data decoded from + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump from a passed slice. + /// \throws std::length_error if given buffer is too small. + char *write_bytes(char *dest, size_t dest_size) const; + + /// \brief Checks whether a passed slice is empty, + /// and therefore there will be no output bytes. + bool is_empty() const noexcept { return source.empty(); } + + /// \brief Checks whether the content of a passed slice is a valid + /// [Base64](https://en.wikipedia.org/wiki/Base64) dump, and therefore there + /// could be decoded or not. + bool is_erroneous() const noexcept; +}; + /// \brief The chunk of data stored inside the buffer or located outside it. -template -class buffer { +template class buffer { public: #if !defined(_MSC_VER) || _MSC_VER > 1900 using allocator_type = typename ::std::allocator_traits< @@ -1600,42 +1955,79 @@ public: return slice_.byte_ptr(); } + /// \brief Returns casted to const pointer to byte an end of data. + MDBX_CXX11_CONSTEXPR const byte *end_byte_ptr() const noexcept { + return slice_.end_byte_ptr(); + } + /// \brief Returns casted to pointer to byte an address of data. /// \pre REQUIRES: The buffer should store data chunk, but not referenced to /// an external one. MDBX_CXX11_CONSTEXPR byte *byte_ptr() noexcept { - assert(is_freestanding()); + MDBX_CONSTEXPR_ASSERT(is_freestanding()); return const_cast(slice_.byte_ptr()); } + /// \brief Returns casted to pointer to byte an end of data. + /// \pre REQUIRES: The buffer should store data chunk, but not referenced to + /// an external one. + MDBX_CXX11_CONSTEXPR byte *end_byte_ptr() noexcept { + MDBX_CONSTEXPR_ASSERT(is_freestanding()); + return const_cast(slice_.end_byte_ptr()); + } + /// \brief Returns casted to const pointer to char an address of data. MDBX_CXX11_CONSTEXPR const char *char_ptr() const noexcept { return slice_.char_ptr(); } + /// \brief Returns casted to const pointer to char an end of data. + MDBX_CXX11_CONSTEXPR const char *end_char_ptr() const noexcept { + return slice_.end_char_ptr(); + } + /// \brief Returns casted to pointer to char an address of data. /// \pre REQUIRES: The buffer should store data chunk, but not referenced to /// an external one. MDBX_CXX11_CONSTEXPR char *char_ptr() noexcept { - assert(is_freestanding()); + MDBX_CONSTEXPR_ASSERT(is_freestanding()); return const_cast(slice_.char_ptr()); } + /// \brief Returns casted to pointer to char an end of data. + /// \pre REQUIRES: The buffer should store data chunk, but not referenced to + /// an external one. + MDBX_CXX11_CONSTEXPR char *end_char_ptr() noexcept { + MDBX_CONSTEXPR_ASSERT(is_freestanding()); + return const_cast(slice_.end_char_ptr()); + } + /// \brief Return a const pointer to the beginning of the referenced data. MDBX_CXX11_CONSTEXPR const void *data() const noexcept { return slice_.data(); } + /// \brief Return a const pointer to the end of the referenced data. + MDBX_CXX11_CONSTEXPR const void *end() const noexcept { return slice_.end(); } + /// \brief Return a pointer to the beginning of the referenced data. /// \pre REQUIRES: The buffer should store data chunk, but not referenced to /// an external one. MDBX_CXX11_CONSTEXPR void *data() noexcept { - assert(is_freestanding()); + MDBX_CONSTEXPR_ASSERT(is_freestanding()); return const_cast(slice_.data()); } + /// \brief Return a pointer to the end of the referenced data. + /// \pre REQUIRES: The buffer should store data chunk, but not referenced to + /// an external one. + MDBX_CXX11_CONSTEXPR void *end() noexcept { + MDBX_CONSTEXPR_ASSERT(is_freestanding()); + return const_cast(slice_.end()); + } + /// \brief Returns the number of bytes. - MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR size_t + MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR size_t length() const noexcept { return MDBX_CONSTEXPR_ASSERT(is_reference() || slice_.length() + headroom() <= @@ -1643,6 +2035,23 @@ public: slice_.length(); } + /// \brief Set length of data. + MDBX_CXX14_CONSTEXPR buffer &set_length(size_t bytes) { + MDBX_CONSTEXPR_ASSERT(is_reference() || + bytes + headroom() <= silo_.capacity()); + slice_.set_length(bytes); + return *this; + } + + /// \brief Sets the length by specifying the end of the data. + MDBX_CXX14_CONSTEXPR buffer &set_end(const void *ptr) { + MDBX_CONSTEXPR_ASSERT(static_cast(ptr) >= char_ptr()); + return set_length(static_cast(ptr) - char_ptr()); + } + + /// \brief Makes buffer owning the data. + /// \details If buffer refers to an external data, then makes it the owner + /// of clone by allocating storage and copying the data. void make_freestanding() { if (is_reference()) insulate(); @@ -1652,7 +2061,7 @@ public: MDBX_CXX20_CONSTEXPR buffer(const allocator_type &allocator) noexcept : silo_(allocator) {} - buffer(const ::mdbx::slice &src, bool make_reference, + buffer(const struct slice &src, bool make_reference, const allocator_type &allocator = allocator_type()) : silo_(allocator), slice_(src) { if (!make_reference) @@ -1667,10 +2076,10 @@ public: const allocator_type &allocator = allocator_type()) : buffer(::mdbx::slice(ptr, bytes), make_reference, allocator) {} - template - buffer(const ::std::basic_string &) = delete; - template - buffer(const ::std::basic_string &&) = delete; + template + buffer(const ::std::basic_string &) = delete; + template + buffer(const ::std::basic_string &&) = delete; buffer(const char *c_str, bool make_reference, const allocator_type &allocator = allocator_type()) @@ -1678,14 +2087,14 @@ public: #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template - buffer(const ::std::basic_string_view &view, bool make_reference, + template + buffer(const ::std::basic_string_view &view, bool make_reference, const allocator_type &allocator = allocator_type()) : buffer(::mdbx::slice(view), make_reference, allocator) {} #endif /* __cpp_lib_string_view >= 201606L */ MDBX_CXX20_CONSTEXPR - buffer(const ::mdbx::slice &src, + buffer(const struct slice &src, const allocator_type &allocator = allocator_type()) : silo_(src.data(), src.length(), allocator), slice_(silo_.data(), src.length()) {} @@ -1699,9 +2108,9 @@ public: const allocator_type &allocator = allocator_type()) : buffer(::mdbx::slice(ptr, bytes), allocator) {} - template + template MDBX_CXX20_CONSTEXPR - buffer(const ::std::basic_string &str, + buffer(const ::std::basic_string &str, const allocator_type &allocator = allocator_type()) : buffer(::mdbx::slice(str), allocator) {} @@ -1711,9 +2120,9 @@ public: #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template + template MDBX_CXX20_CONSTEXPR - buffer(const ::std::basic_string_view &view, + buffer(const ::std::basic_string_view &view, const allocator_type &allocator = allocator_type()) : buffer(::mdbx::slice(view), allocator) {} #endif /* __cpp_lib_string_view >= 201606L */ @@ -1731,7 +2140,7 @@ public: assert(slice_.iov_len == 0); } - buffer(size_t head_room, const ::mdbx::slice &src, size_t tail_room, + buffer(size_t head_room, const struct slice &src, size_t tail_room, const allocator_type &allocator = allocator_type()) : silo_(allocator) { slice_.iov_base = @@ -1744,17 +2153,17 @@ public: const allocator_type &allocator = allocator_type()) : buffer(head_room, src.slice_, tail_room, allocator) {} - inline buffer(const ::mdbx::txn &txn, const ::mdbx::slice &src, + inline buffer(const ::mdbx::txn &txn, const struct slice &src, const allocator_type &allocator = allocator_type()); buffer(buffer &&src) noexcept(move_assign_alloc::is_nothrow()) : silo_(::std::move(src.silo_)), slice_(::std::move(src.slice_)) {} - MDBX_CXX11_CONSTEXPR const ::mdbx::slice &slice() const noexcept { + MDBX_CXX11_CONSTEXPR const struct slice &slice() const noexcept { return slice_; } - MDBX_CXX11_CONSTEXPR operator const ::mdbx::slice &() const noexcept { + MDBX_CXX11_CONSTEXPR operator const struct slice &() const noexcept { return slice_; } @@ -1818,7 +2227,7 @@ public: : assign_freestanding(ptr, bytes); } - buffer &assign(const ::mdbx::slice &src, bool make_reference = false) { + buffer &assign(const struct slice &src, bool make_reference = false) { return assign(src.data(), src.length(), make_reference); } @@ -1826,7 +2235,7 @@ public: return assign(src.iov_base, src.iov_len, make_reference); } - buffer &assign(::mdbx::slice &&src, bool make_reference = false) { + buffer &assign(struct slice &&src, bool make_reference = false) { assign(src.data(), src.length(), make_reference); src.invalidate(); return *this; @@ -1846,8 +2255,8 @@ public: make_reference); } - template - buffer &assign(const ::std::basic_string &str, + template + buffer &assign(const ::std::basic_string &str, bool make_reference = false) { return assign(str.data(), str.length(), make_reference); } @@ -1857,14 +2266,14 @@ public: } #if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L - template - buffer &assign(const ::std::basic_string_view &view, + template + buffer &assign(const ::std::basic_string_view &view, bool make_reference = false) { return assign(view.data(), view.length(), make_reference); } - template - buffer &assign(::std::basic_string_view &&view, + template + buffer &assign(::std::basic_string_view &&view, bool make_reference = false) { assign(view.data(), view.length(), make_reference); view = {}; @@ -1878,77 +2287,30 @@ public: return assign(::std::move(src)); } - buffer &operator=(const ::mdbx::slice &src) { return assign(src); } + buffer &operator=(const struct slice &src) { return assign(src); } - buffer &operator=(::mdbx::slice &&src) { return assign(::std::move(src)); } + buffer &operator=(struct slice &&src) { return assign(::std::move(src)); } #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template - buffer &operator=(const ::std::basic_string_view &view) noexcept { + template + buffer &operator=(const ::std::basic_string_view &view) noexcept { return assign(view); } /// \brief Return a string_view that references the data of this buffer. - template > - ::std::basic_string_view string_view() const noexcept { - return slice_.string_view(); + template > + ::std::basic_string_view string_view() const noexcept { + return slice_.string_view(); } /// \brief Return a string_view that references the data of this buffer. - template - operator ::std::basic_string_view() const noexcept { - return string_view(); + template + operator ::std::basic_string_view() const noexcept { + return string_view(); } #endif /* __cpp_lib_string_view >= 201606L */ -#if 0 /* temporary disabled */ - /// \brief Decodes hexadecimal dump from the given slice to the returned - /// buffer. - static buffer decode_hex(const ::mdbx::slice &hex, - const allocator_type &allocator = allocator_type()) { - return buffer(::std::move(hex.hex_decode(allocator))); - } - - /// \brief Returns a buffer with a hexadecimal dump of the given slice. - static buffer encode_hex(const ::mdbx::slice &data, bool uppercase = false, - const allocator_type &allocator = allocator_type()) { - return buffer(::std::move(data.hex_encode(uppercase, allocator))); - } - - /// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump from - /// the given slice to the returned buffer. - static buffer - decode_base58(const ::mdbx::slice &base58, - const allocator_type &allocator = allocator_type()) { - return buffer(::std::move(base58.base58_decode(allocator))); - } - - /// \brief Returns a buffer with a - /// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the given slice. - static buffer - encode_base58(const ::mdbx::slice &data, - const allocator_type &allocator = allocator_type()) { - return buffer(::std::move(data.base58_encode(allocator))); - } - - /// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump from - /// the given slice to the returned buffer. - static buffer - decode_base64(const ::mdbx::slice &base64, - const allocator_type &allocator = allocator_type()) { - return buffer(::std::move(base64.base64_decode(allocator))); - } - - /// \brief Returns a buffer with a - /// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the given slice. - static buffer - encode_base64(const ::mdbx::slice &data, - const allocator_type &allocator = allocator_type()) { - return buffer(::std::move(data.base64_encode(allocator))); - } -#endif /* temporary disabled */ - /// \brief Checks whether the string is empty. MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX20_CONSTEXPR bool empty() const noexcept { return length() == 0; @@ -1973,27 +2335,28 @@ public: return slice_.hash_value(); } - template , + template , class A = legacy_allocator> - MDBX_CXX20_CONSTEXPR ::std::basic_string - string(const A &allocator = A()) const { - return slice_.string(allocator); + MDBX_CXX20_CONSTEXPR ::std::basic_string + as_string(const A &allocator = A()) const { + return slice_.as_string(allocator); } - template - MDBX_CXX20_CONSTEXPR operator ::std::basic_string() const { - return this->string(); + template + MDBX_CXX20_CONSTEXPR explicit + operator ::std::basic_string() const { + return as_string(); } /// \brief Checks if the data starts with the given prefix. MDBX_NOTHROW_PURE_FUNCTION bool - starts_with(const ::mdbx::slice &prefix) const noexcept { + starts_with(const struct slice &prefix) const noexcept { return slice_.starts_with(prefix); } /// \brief Checks if the data ends with the given suffix. MDBX_NOTHROW_PURE_FUNCTION bool - ends_with(const ::mdbx::slice &suffix) const noexcept { + ends_with(const struct slice &suffix) const noexcept { return slice_.ends_with(suffix); } @@ -2024,22 +2387,25 @@ public: /// \brief Accesses the specified byte of data chunk. /// \pre REQUIRES: `n < size()` - byte operator[](size_t n) const noexcept { return slice_[n]; } + MDBX_CXX11_CONSTEXPR byte operator[](size_t n) const noexcept { + MDBX_CONSTEXPR_ASSERT(n < size()); + return slice_[n]; + } /// \brief Accesses the specified byte of data chunk. /// \pre REQUIRES: `n < size()` - byte &operator[](size_t n) noexcept { - assert(n < size()); + MDBX_CXX11_CONSTEXPR byte &operator[](size_t n) noexcept { + MDBX_CONSTEXPR_ASSERT(n < size()); return byte_ptr()[n]; } /// \brief Accesses the specified byte of data chunk with bounds checking. /// \throws std::out_of_range if `n >= size()` - byte at(size_t n) const { return slice_.at(n); } + MDBX_CXX14_CONSTEXPR byte at(size_t n) const { return slice_.at(n); } /// \brief Accesses the specified byte of data chunk with bounds checking. /// \throws std::out_of_range if `n >= size()` - byte &at(size_t n) { + MDBX_CXX14_CONSTEXPR byte &at(size_t n) { if (MDBX_UNLIKELY(n >= size())) MDBX_CXX20_UNLIKELY throw_out_range(); return byte_ptr()[n]; @@ -2047,29 +2413,38 @@ public: /// \brief Returns the first "n" bytes of the data chunk. /// \pre REQUIRES: `n <= size()` - ::mdbx::slice head(size_t n) const noexcept { return slice_.head(n); } + MDBX_CXX14_CONSTEXPR struct slice head(size_t n) const noexcept { + return slice_.head(n); + } /// \brief Returns the last "n" bytes of the data chunk. /// \pre REQUIRES: `n <= size()` - ::mdbx::slice tail(size_t n) const noexcept { return slice_.tail(n); } + MDBX_CXX14_CONSTEXPR struct slice tail(size_t n) const noexcept { + return slice_.tail(n); + } /// \brief Returns the middle "n" bytes of the data chunk. /// \pre REQUIRES: `from + n <= size()` - ::mdbx::slice middle(size_t from, size_t n) const noexcept { + MDBX_CXX14_CONSTEXPR struct slice middle(size_t from, + size_t n) const noexcept { return slice_.middle(from, n); } /// \brief Returns the first "n" bytes of the data chunk. /// \throws std::out_of_range if `n >= size()` - ::mdbx::slice safe_head(size_t n) const { return slice_.safe_head(n); } + MDBX_CXX14_CONSTEXPR struct slice safe_head(size_t n) const { + return slice_.safe_head(n); + } /// \brief Returns the last "n" bytes of the data chunk. /// \throws std::out_of_range if `n >= size()` - ::mdbx::slice safe_tail(size_t n) const { return slice_.safe_tail(n); } + MDBX_CXX14_CONSTEXPR struct slice safe_tail(size_t n) const { + return slice_.safe_tail(n); + } /// \brief Returns the middle "n" bytes of the data chunk. /// \throws std::out_of_range if `from + n >= size()` - ::mdbx::slice safe_middle(size_t from, size_t n) const { + MDBX_CXX14_CONSTEXPR struct slice safe_middle(size_t from, size_t n) const { return slice_.safe_middle(from, n); } @@ -2081,7 +2456,7 @@ public: return *this; } - buffer &append(const ::mdbx::slice &chunk) { + buffer &append(const struct slice &chunk) { return append(chunk.data(), chunk.size()); } @@ -2094,10 +2469,54 @@ public: return *this; } - buffer &add_header(const ::mdbx::slice &chunk) { + buffer &add_header(const struct slice &chunk) { return add_header(chunk.data(), chunk.size()); } + template + buffer &append_producer(PRODUCER &producer) { + const size_t wanna_bytes = producer.envisage_result_length(); + if (MDBX_UNLIKELY(tailroom() < check_length(wanna_bytes))) + MDBX_CXX20_UNLIKELY reserve_tailroom(wanna_bytes); + return set_end(producer.write_bytes(end_char_ptr(), tailroom())); + } + + template + buffer &append_producer(const PRODUCER &producer) { + const size_t wanna_bytes = producer.envisage_result_length(); + if (MDBX_UNLIKELY(tailroom() < check_length(wanna_bytes))) + MDBX_CXX20_UNLIKELY reserve_tailroom(wanna_bytes); + return set_end(producer.write_bytes(end_char_ptr(), tailroom())); + } + + buffer &append_hex(const struct slice &data, bool uppercase = false, + unsigned wrap_width = 0) { + return append_producer(to_hex(data, uppercase, wrap_width)); + } + + buffer &append_base58(const struct slice &data, unsigned wrap_width = 0) { + return append_producer(to_base58(data, wrap_width)); + } + + buffer &append_base64(const struct slice &data, unsigned wrap_width = 0) { + return append_producer(to_base64(data, wrap_width)); + } + + buffer &append_decoded_hex(const struct slice &data, + bool ignore_spaces = false) { + return append_producer(from_hex(data, ignore_spaces)); + } + + buffer &append_decoded_base58(const struct slice &data, + bool ignore_spaces = false) { + return append_producer(from_base58(data, ignore_spaces)); + } + + buffer &append_decoded_base64(const struct slice &data, + bool ignore_spaces = false) { + return append_producer(from_base64(data, ignore_spaces)); + } + //---------------------------------------------------------------------------- template @@ -2107,8 +2526,8 @@ public: #if defined(DOXYGEN) || \ (defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L) - template - static buffer key_from(const ::std::basic_string_view &src, + template + static buffer key_from(const ::std::basic_string_view &src, bool make_reference = false) { return buffer(src, make_reference); } @@ -2118,8 +2537,8 @@ public: return buffer(src, make_reference); } - template - static buffer key_from(const ::std::basic_string &src, + template + static buffer key_from(const ::std::basic_string &src, bool make_reference = false) { return buffer(src, make_reference); } @@ -2165,6 +2584,64 @@ public: } }; +template +inline buffer +make_buffer(PRODUCER &producer, const ALLOCATOR &allocator) { + if (MDBX_LIKELY(!producer.is_empty())) + MDBX_CXX20_LIKELY { + buffer result( + producer.envisage_result_length(), allocator); + result.set_end( + producer.write_bytes(result.end_char_ptr(), result.tailroom())); + return result; + } + return buffer(allocator); +} + +template +inline buffer +make_buffer(const PRODUCER &producer, const ALLOCATOR &allocator) { + if (MDBX_LIKELY(!producer.is_empty())) + MDBX_CXX20_LIKELY { + buffer result( + producer.envisage_result_length(), allocator); + result.set_end( + producer.write_bytes(result.end_char_ptr(), result.tailroom())); + return result; + } + return buffer(allocator); +} + +template +inline string make_string(PRODUCER &producer, + const ALLOCATOR &allocator) { + string result(allocator); + if (MDBX_LIKELY(!producer.is_empty())) + MDBX_CXX20_LIKELY { + result.resize(producer.envisage_result_length()); + result.resize(producer.write_bytes(const_cast(result.data()), + result.capacity()) - + result.data()); + } + return result; +} + +template +inline string make_string(const PRODUCER &producer, + const ALLOCATOR &allocator) { + string result(allocator); + if (MDBX_LIKELY(!producer.is_empty())) + MDBX_CXX20_LIKELY { + result.resize(producer.envisage_result_length()); + result.resize(producer.write_bytes(const_cast(result.data()), + result.capacity()) - + result.data()); + } + return result; +} + /// \brief Combines data slice with boolean flag to represent result of certain /// operations. struct value_result { @@ -3495,6 +3972,23 @@ static MDBX_CXX20_CONSTEXPR void *memcpy(void *dest, const void *src, return ::std::memcpy(dest, src, bytes); } +static MDBX_CXX20_CONSTEXPR int memcmp(const void *a, const void *b, + size_t bytes) noexcept { +#if defined(__cpp_lib_is_constant_evaluated) && \ + __cpp_lib_is_constant_evaluated >= 201811L + if (::std::is_constant_evaluated()) { + for (size_t i = 0; i < bytes; ++i) { + const int diff = + static_cast(a)[i] - static_cast(b)[i]; + if (diff) + return diff; + } + return 0; + } else +#endif /* __cpp_lib_is_constant_evaluated >= 201811 */ + return ::std::memcmp(a, b, bytes); +} + static MDBX_CXX14_CONSTEXPR size_t check_length(size_t bytes) { if (MDBX_UNLIKELY(bytes > size_t(MDBX_MAXDATASIZE))) MDBX_CXX20_UNLIKELY throw_max_length_exceeded(); @@ -3662,9 +4156,13 @@ MDBX_CXX17_CONSTEXPR slice::slice(const char *c_str) MDBX_CXX14_CONSTEXPR slice::slice(const MDBX_val &src) : slice(src.iov_base, src.iov_len) {} -inline slice::slice(MDBX_val &&src) : slice(src) { src.iov_base = nullptr; } +MDBX_CXX14_CONSTEXPR slice::slice(MDBX_val &&src) : slice(src) { + src.iov_base = nullptr; +} -inline slice::slice(slice &&src) noexcept : slice(src) { src.invalidate(); } +MDBX_CXX14_CONSTEXPR slice::slice(slice &&src) noexcept : slice(src) { + src.invalidate(); +} inline slice &slice::assign(const void *ptr, size_t bytes) { iov_base = const_cast(ptr); @@ -3721,26 +4219,60 @@ MDBX_CXX11_CONSTEXPR const ::mdbx::byte *slice::byte_ptr() const noexcept { return static_cast(iov_base); } +MDBX_CXX11_CONSTEXPR const ::mdbx::byte *slice::end_byte_ptr() const noexcept { + return byte_ptr() + length(); +} + MDBX_CXX11_CONSTEXPR ::mdbx::byte *slice::byte_ptr() noexcept { return static_cast(iov_base); } +MDBX_CXX11_CONSTEXPR ::mdbx::byte *slice::end_byte_ptr() noexcept { + return byte_ptr() + length(); +} + MDBX_CXX11_CONSTEXPR const char *slice::char_ptr() const noexcept { return static_cast(iov_base); } +MDBX_CXX11_CONSTEXPR const char *slice::end_char_ptr() const noexcept { + return char_ptr() + length(); +} + MDBX_CXX11_CONSTEXPR char *slice::char_ptr() noexcept { return static_cast(iov_base); } +MDBX_CXX11_CONSTEXPR char *slice::end_char_ptr() noexcept { + return char_ptr() + length(); +} + MDBX_CXX11_CONSTEXPR const void *slice::data() const noexcept { return iov_base; } +MDBX_CXX11_CONSTEXPR const void *slice::end() const noexcept { + return static_cast(end_byte_ptr()); +} + MDBX_CXX11_CONSTEXPR void *slice::data() noexcept { return iov_base; } +MDBX_CXX11_CONSTEXPR void *slice::end() noexcept { + return static_cast(end_byte_ptr()); +} + MDBX_CXX11_CONSTEXPR size_t slice::length() const noexcept { return iov_len; } +MDBX_CXX14_CONSTEXPR slice &slice::set_length(size_t bytes) { + iov_len = check_length(bytes); + return *this; +} + +MDBX_CXX14_CONSTEXPR slice &slice::set_end(const void *ptr) { + MDBX_CONSTEXPR_ASSERT(static_cast(ptr) >= char_ptr()); + return set_length(static_cast(ptr) - char_ptr()); +} + MDBX_CXX11_CONSTEXPR bool slice::empty() const noexcept { return length() == 0; } @@ -3755,9 +4287,9 @@ MDBX_CXX11_CONSTEXPR slice::operator bool() const noexcept { return !is_null(); } -inline void slice::invalidate() noexcept { iov_base = nullptr; } +MDBX_CXX14_CONSTEXPR void slice::invalidate() noexcept { iov_base = nullptr; } -inline void slice::clear() noexcept { +MDBX_CXX14_CONSTEXPR void slice::clear() noexcept { iov_base = nullptr; iov_len = 0; } @@ -3785,15 +4317,16 @@ inline void slice::safe_remove_suffix(size_t n) { remove_suffix(n); } -inline bool slice::starts_with(const slice &prefix) const noexcept { +MDBX_CXX14_CONSTEXPR bool +slice::starts_with(const slice &prefix) const noexcept { return length() >= prefix.length() && - ::std::memcmp(data(), prefix.data(), prefix.length()) == 0; + memcmp(data(), prefix.data(), prefix.length()) == 0; } -inline bool slice::ends_with(const slice &suffix) const noexcept { +MDBX_CXX14_CONSTEXPR bool slice::ends_with(const slice &suffix) const noexcept { return length() >= suffix.length() && - ::std::memcmp(byte_ptr() + length() - suffix.length(), suffix.data(), - suffix.length()) == 0; + memcmp(byte_ptr() + length() - suffix.length(), suffix.data(), + suffix.length()) == 0; } MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR size_t @@ -3804,45 +4337,45 @@ slice::hash_value() const noexcept { return h ^ 3863194411 * (h >> 11); } -inline byte slice::operator[](size_t n) const noexcept { - assert(n < size()); +MDBX_CXX11_CONSTEXPR byte slice::operator[](size_t n) const noexcept { + MDBX_CONSTEXPR_ASSERT(n < size()); return byte_ptr()[n]; } -inline byte slice::at(size_t n) const { +MDBX_CXX11_CONSTEXPR byte slice::at(size_t n) const { if (MDBX_UNLIKELY(n >= size())) MDBX_CXX20_UNLIKELY throw_out_range(); return byte_ptr()[n]; } -inline slice slice::head(size_t n) const noexcept { - assert(n <= size()); +MDBX_CXX14_CONSTEXPR slice slice::head(size_t n) const noexcept { + MDBX_CONSTEXPR_ASSERT(n <= size()); return slice(data(), n); } -inline slice slice::tail(size_t n) const noexcept { - assert(n <= size()); +MDBX_CXX14_CONSTEXPR slice slice::tail(size_t n) const noexcept { + MDBX_CONSTEXPR_ASSERT(n <= size()); return slice(char_ptr() + size() - n, n); } -inline slice slice::middle(size_t from, size_t n) const noexcept { - assert(from + n <= size()); +MDBX_CXX14_CONSTEXPR slice slice::middle(size_t from, size_t n) const noexcept { + MDBX_CONSTEXPR_ASSERT(from + n <= size()); return slice(char_ptr() + from, n); } -inline slice slice::safe_head(size_t n) const { +MDBX_CXX14_CONSTEXPR slice slice::safe_head(size_t n) const { if (MDBX_UNLIKELY(n > size())) MDBX_CXX20_UNLIKELY throw_out_range(); return head(n); } -inline slice slice::safe_tail(size_t n) const { +MDBX_CXX14_CONSTEXPR slice slice::safe_tail(size_t n) const { if (MDBX_UNLIKELY(n > size())) MDBX_CXX20_UNLIKELY throw_out_range(); return tail(n); } -inline slice slice::safe_middle(size_t from, size_t n) const { +MDBX_CXX14_CONSTEXPR slice slice::safe_middle(size_t from, size_t n) const { if (MDBX_UNLIKELY(n > max_length)) MDBX_CXX20_UNLIKELY throw_max_length_exceeded(); if (MDBX_UNLIKELY(from + n > size())) @@ -3850,145 +4383,139 @@ inline slice slice::safe_middle(size_t from, size_t n) const { return middle(from, n); } -inline intptr_t slice::compare_fast(const slice &a, const slice &b) noexcept { +MDBX_CXX14_CONSTEXPR intptr_t slice::compare_fast(const slice &a, + const slice &b) noexcept { const intptr_t diff = intptr_t(a.length()) - intptr_t(b.length()); return diff ? diff : MDBX_UNLIKELY(a.length() == 0 || a.data() == b.data()) ? 0 - : ::std::memcmp(a.data(), b.data(), a.length()); + : memcmp(a.data(), b.data(), a.length()); } -inline intptr_t slice::compare_lexicographically(const slice &a, - const slice &b) noexcept { +MDBX_CXX14_CONSTEXPR intptr_t +slice::compare_lexicographically(const slice &a, const slice &b) noexcept { const size_t shortest = ::std::min(a.length(), b.length()); if (MDBX_LIKELY(shortest > 0)) MDBX_CXX20_LIKELY { - const intptr_t diff = ::std::memcmp(a.data(), b.data(), shortest); + const intptr_t diff = memcmp(a.data(), b.data(), shortest); if (MDBX_LIKELY(diff != 0)) MDBX_CXX20_LIKELY return diff; } return intptr_t(a.length()) - intptr_t(b.length()); } -MDBX_NOTHROW_PURE_FUNCTION inline bool operator==(const slice &a, - const slice &b) noexcept { +MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool +operator==(const slice &a, const slice &b) noexcept { return slice::compare_fast(a, b) == 0; } -MDBX_NOTHROW_PURE_FUNCTION inline bool operator<(const slice &a, - const slice &b) noexcept { +MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool +operator<(const slice &a, const slice &b) noexcept { return slice::compare_lexicographically(a, b) < 0; } -MDBX_NOTHROW_PURE_FUNCTION inline bool operator>(const slice &a, - const slice &b) noexcept { +MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool +operator>(const slice &a, const slice &b) noexcept { return slice::compare_lexicographically(a, b) > 0; } -MDBX_NOTHROW_PURE_FUNCTION inline bool operator<=(const slice &a, - const slice &b) noexcept { +MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool +operator<=(const slice &a, const slice &b) noexcept { return slice::compare_lexicographically(a, b) <= 0; } -MDBX_NOTHROW_PURE_FUNCTION inline bool operator>=(const slice &a, - const slice &b) noexcept { +MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool +operator>=(const slice &a, const slice &b) noexcept { return slice::compare_lexicographically(a, b) >= 0; } -MDBX_NOTHROW_PURE_FUNCTION inline bool operator!=(const slice &a, - const slice &b) noexcept { +MDBX_NOTHROW_PURE_FUNCTION MDBX_CXX14_CONSTEXPR bool +operator!=(const slice &a, const slice &b) noexcept { return slice::compare_fast(a, b) != 0; } template -inline ::mdbx::string -slice::hex_encode(bool uppercase, const ALLOCATOR &allocator) const { - ::mdbx::string result(allocator); - if (MDBX_LIKELY(length() > 0)) { - result.resize(envisage_to_hex_length()); - result.resize(to_hex(const_cast(result.data()), result.capacity()) - - result.data(), - uppercase); - } - return result; +inline string +slice::as_hex_string(bool uppercase, unsigned wrap_width, + const ALLOCATOR &allocator) const { + return to_hex(*this, uppercase, wrap_width).as_string(allocator); } template -inline ::mdbx::string -slice::hex_decode(const ALLOCATOR &allocator) const { - ::mdbx::string result(allocator); - if (MDBX_LIKELY(length() > 0)) { - result.resize(envisage_from_hex_length()); - result.resize( - from_hex(static_cast( - static_cast(const_cast(result.data()))), - result.capacity()) - - static_cast(static_cast(result.data()))); - } - return result; +inline string +slice::as_base58_string(unsigned wrap_width, const ALLOCATOR &allocator) const { + return to_base58(*this, wrap_width).as_string(allocator); } template -inline ::mdbx::string -slice::base58_encode(const ALLOCATOR &allocator) const { - ::mdbx::string result(allocator); - if (MDBX_LIKELY(length() > 0)) { - result.resize(envisage_to_base58_length()); - result.resize( - to_base58(const_cast(result.data()), result.capacity()) - - result.data()); - } - return result; +inline string +slice::as_base64_string(unsigned wrap_width, const ALLOCATOR &allocator) const { + return to_base64(*this, wrap_width).as_string(allocator); } -template -inline ::mdbx::string -slice::base58_decode(const ALLOCATOR &allocator) const { - ::mdbx::string result(allocator); - if (MDBX_LIKELY(length() > 0)) { - result.resize(envisage_from_base58_length()); - result.resize( - from_base58(static_cast( - static_cast(const_cast(result.data()))), - result.capacity()) - - static_cast(static_cast(result.data()))); - } - return result; +template +inline buffer +slice::encode_hex(bool uppercase, unsigned wrap_width, + const ALLOCATOR &allocator) const { + return to_hex(*this, uppercase, wrap_width) + .as_buffer(allocator); } -template -inline ::mdbx::string -slice::base64_encode(const ALLOCATOR &allocator) const { - ::mdbx::string result(allocator); - if (MDBX_LIKELY(length() > 0)) { - result.resize(envisage_to_base64_length()); - result.resize( - to_base64(const_cast(result.data()), result.capacity()) - - result.data()); - } - return result; +template +inline buffer +slice::encode_base58(unsigned wrap_width, const ALLOCATOR &allocator) const { + return to_base58(*this, wrap_width) + .as_buffer(allocator); } -template -inline ::mdbx::string -slice::base64_decode(const ALLOCATOR &allocator) const { - ::mdbx::string result(allocator); - if (MDBX_LIKELY(length() > 0)) { - result.resize(envisage_from_base64_length()); - result.resize( - from_base64(static_cast( - static_cast(const_cast(result.data()))), - result.capacity()) - - static_cast(static_cast(result.data()))); - } - return result; +template +inline buffer +slice::encode_base64(unsigned wrap_width, const ALLOCATOR &allocator) const { + return to_base64(*this, wrap_width) + .as_buffer(allocator); +} + +template +inline buffer +slice::hex_decode(bool ignore_spaces, const ALLOCATOR &allocator) const { + return from_hex(*this, ignore_spaces) + .as_buffer(allocator); +} + +template +inline buffer +slice::base58_decode(bool ignore_spaces, const ALLOCATOR &allocator) const { + return from_base58(*this, ignore_spaces) + .as_buffer(allocator); +} + +template +inline buffer +slice::base64_decode(bool ignore_spaces, const ALLOCATOR &allocator) const { + return from_base64(*this, ignore_spaces) + .as_buffer(allocator); +} + +inline MDBX_NOTHROW_PURE_FUNCTION bool +slice::is_hex(bool ignore_spaces) const noexcept { + return !from_hex(*this, ignore_spaces).is_erroneous(); +} + +inline MDBX_NOTHROW_PURE_FUNCTION bool +slice::is_base58(bool ignore_spaces) const noexcept { + return !from_base58(*this, ignore_spaces).is_erroneous(); +} + +inline MDBX_NOTHROW_PURE_FUNCTION bool +slice::is_base64(bool ignore_spaces) const noexcept { + return !from_base64(*this, ignore_spaces).is_erroneous(); } //------------------------------------------------------------------------------ template inline buffer::buffer( - const txn &txn, const ::mdbx::slice &src, const allocator_type &allocator) + const txn &txn, const struct slice &src, const allocator_type &allocator) : buffer(src, !txn.is_dirty(src.data()), allocator) {} //------------------------------------------------------------------------------ diff --git a/src/mdbx.c++ b/src/mdbx.c++ index 4b0cd9a1..82770c69 100644 --- a/src/mdbx.c++ +++ b/src/mdbx.c++ @@ -538,15 +538,14 @@ bool slice::is_printable(bool disable_utf8) const noexcept { //------------------------------------------------------------------------------ -char *slice::to_hex(char *__restrict dest, size_t dest_size, bool uppercase, - unsigned wrap_width) const { - if (MDBX_UNLIKELY(envisage_to_hex_length(wrap_width) > dest_size)) +char *to_hex::write_bytes(char *__restrict dest, size_t dest_size) const { + if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto src = byte_ptr(); + auto src = source.byte_ptr(); const char alphabase = (uppercase ? 'A' : 'a') - 10; auto line = dest; - for (const auto end = src + length(); src != end; ++src) { + for (const auto end = source.end_byte_ptr(); src != end; ++src) { const int8_t hi = *src >> 4; const int8_t lo = *src & 15; dest[0] = char(alphabase + hi + (((hi - 10) >> 7) & -7)); @@ -560,16 +559,15 @@ char *slice::to_hex(char *__restrict dest, size_t dest_size, bool uppercase, return dest; } -byte *slice::from_hex(byte *__restrict dest, size_t dest_size, - bool ignore_spaces) const { - if (MDBX_UNLIKELY(length() % 2 && !ignore_spaces)) +char *from_hex::write_bytes(char *__restrict dest, size_t dest_size) const { + if (MDBX_UNLIKELY(source.length() % 2 && !ignore_spaces)) MDBX_CXX20_UNLIKELY throw std::domain_error( "mdbx::from_hex:: odd length of hexadecimal string"); - if (MDBX_UNLIKELY(envisage_from_hex_length() > dest_size)) + if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto src = byte_ptr(); - for (auto left = length(); left > 0;) { + auto src = source.byte_ptr(); + for (auto left = source.length(); left > 0;) { if (MDBX_UNLIKELY(*src <= ' ') && MDBX_LIKELY(ignore_spaces && isspace(*src))) { ++src; @@ -596,13 +594,13 @@ byte *slice::from_hex(byte *__restrict dest, size_t dest_size, return dest; } -bool slice::is_hex(bool ignore_spaces) const noexcept { - if (MDBX_UNLIKELY(length() % 2 && !ignore_spaces)) - MDBX_CXX20_UNLIKELY return false; +bool from_hex::is_erroneous() const noexcept { + if (MDBX_UNLIKELY(source.length() % 2 && !ignore_spaces)) + MDBX_CXX20_UNLIKELY return true; bool got = false; - auto src = byte_ptr(); - for (auto left = length(); left > 0;) { + auto src = source.byte_ptr(); + for (auto left = source.length(); left > 0;) { if (MDBX_UNLIKELY(*src <= ' ') && MDBX_LIKELY(ignore_spaces && isspace(*src))) { ++src; @@ -611,13 +609,13 @@ bool slice::is_hex(bool ignore_spaces) const noexcept { } if (MDBX_UNLIKELY(left < 1 || !isxdigit(src[0]) || !isxdigit(src[1]))) - MDBX_CXX20_UNLIKELY return false; + MDBX_CXX20_UNLIKELY return true; got = true; src += 2; left -= 2; } - return got; + return !got; } //------------------------------------------------------------------------------ @@ -665,13 +663,12 @@ static inline char b58_8to11(uint64_t &v) noexcept { return b58_alphabet[i]; } -char *slice::to_base58(char *__restrict dest, size_t dest_size, - unsigned wrap_width) const { - if (MDBX_UNLIKELY(envisage_to_base58_length(wrap_width) > dest_size)) +char *to_base58::write_bytes(char *__restrict dest, size_t dest_size) const { + if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto src = byte_ptr(); - size_t left = length(); + auto src = source.byte_ptr(); + size_t left = source.length(); auto line = dest; while (MDBX_LIKELY(left > 7)) { left -= 8; @@ -748,13 +745,12 @@ static inline signed char b58_11to8(uint64_t &v, const byte c) noexcept { return m; } -byte *slice::from_base58(byte *__restrict dest, size_t dest_size, - bool ignore_spaces) const { - if (MDBX_UNLIKELY(envisage_from_base58_length() > dest_size)) +char *from_base58::write_bytes(char *__restrict dest, size_t dest_size) const { + if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto src = byte_ptr(); - for (auto left = length(); left > 0;) { + auto src = source.byte_ptr(); + for (auto left = source.length(); left > 0;) { if (MDBX_UNLIKELY(isspace(*src)) && ignore_spaces) { ++src; --left; @@ -808,10 +804,10 @@ bailout: throw std::domain_error("mdbx::from_base58:: invalid base58 string"); } -bool slice::is_base58(bool ignore_spaces) const noexcept { +bool from_base58::is_erroneous() const noexcept { bool got = false; - auto src = byte_ptr(); - for (auto left = length(); left > 0;) { + auto src = source.byte_ptr(); + for (auto left = source.length(); left > 0;) { if (MDBX_UNLIKELY(*src <= ' ') && MDBX_LIKELY(ignore_spaces && isspace(*src))) { ++src; @@ -824,7 +820,7 @@ bool slice::is_base58(bool ignore_spaces) const noexcept { b58_map[src[3]] | b58_map[src[4]] | b58_map[src[5]] | b58_map[src[6]] | b58_map[src[7]] | b58_map[src[8]] | b58_map[src[9]] | b58_map[src[10]]) < 0)) - return false; + MDBX_CXX20_UNLIKELY return true; src += 11; left -= 11; got = true; @@ -837,12 +833,12 @@ bool slice::is_base58(bool ignore_spaces) const noexcept { do if (MDBX_UNLIKELY(b58_map[*src++] < 0)) - MDBX_CXX20_UNLIKELY return false; + MDBX_CXX20_UNLIKELY return true; while (--left); got = true; break; } - return got; + return !got; } //------------------------------------------------------------------------------ @@ -861,13 +857,12 @@ static inline void b64_3to4(const byte x, const byte y, const byte z, dest[3] = alphabet[z & 0x3f]; } -char *slice::to_base64(char *__restrict dest, size_t dest_size, - unsigned wrap_width) const { - if (MDBX_UNLIKELY(envisage_to_base64_length(wrap_width) > dest_size)) +char *to_base64::write_bytes(char *__restrict dest, size_t dest_size) const { + if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto src = byte_ptr(); - size_t left = length(); + auto src = source.byte_ptr(); + size_t left = source.length(); auto line = dest; while (true) { switch (left) { @@ -917,23 +912,22 @@ static const signed char b64_map[256] = { static inline signed char b64_4to3(signed char a, signed char b, signed char c, signed char d, - byte *__restrict dest) noexcept { + char *__restrict dest) noexcept { dest[0] = byte((a << 2) + ((b & 0x30) >> 4)); dest[1] = byte(((b & 0xf) << 4) + ((c & 0x3c) >> 2)); dest[2] = byte(((c & 0x3) << 6) + d); return a | b | c | d; } -byte *slice::from_base64(byte *__restrict dest, size_t dest_size, - bool ignore_spaces) const { - if (MDBX_UNLIKELY(length() % 4 && !ignore_spaces)) +char *from_base64::write_bytes(char *__restrict dest, size_t dest_size) const { + if (MDBX_UNLIKELY(source.length() % 4 && !ignore_spaces)) MDBX_CXX20_UNLIKELY throw std::domain_error( "mdbx::from_base64:: odd length of base64 string"); - if (MDBX_UNLIKELY(envisage_from_base64_length() > dest_size)) + if (MDBX_UNLIKELY(envisage_result_length() > dest_size)) MDBX_CXX20_UNLIKELY throw_too_small_target_buffer(); - auto src = byte_ptr(); - for (auto left = length(); left > 0;) { + auto src = source.byte_ptr(); + for (auto left = source.length(); left > 0;) { if (MDBX_UNLIKELY(*src <= ' ') && MDBX_LIKELY(ignore_spaces && isspace(*src))) { ++src; @@ -963,13 +957,13 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size, return dest; } -bool slice::is_base64(bool ignore_spaces) const noexcept { - if (MDBX_UNLIKELY(length() % 4 && !ignore_spaces)) - MDBX_CXX20_UNLIKELY return false; +bool from_base64::is_erroneous() const noexcept { + if (MDBX_UNLIKELY(source.length() % 4 && !ignore_spaces)) + MDBX_CXX20_UNLIKELY return true; bool got = false; - auto src = byte_ptr(); - for (auto left = length(); left > 0;) { + auto src = source.byte_ptr(); + for (auto left = source.length(); left > 0;) { if (MDBX_UNLIKELY(*src <= ' ') && MDBX_LIKELY(ignore_spaces && isspace(*src))) { ++src; @@ -984,14 +978,14 @@ bool slice::is_base64(bool ignore_spaces) const noexcept { if (MDBX_UNLIKELY((a | b | c | d) < 0)) MDBX_CXX20_UNLIKELY { if (left == 4 && (a | b) >= 0 && d == EQ && (c >= 0 || c == d)) - return true; - return false; + return false; + return true; } got = true; src += 4; left -= 4; } - return got; + return !got; } //------------------------------------------------------------------------------ @@ -1406,7 +1400,7 @@ __cold ::std::ostream &operator<<(::std::ostream &out, const slice &it) { if (root.is_printable()) (out << "\"").write(root.char_ptr(), root.length()) << "\""; else - out << root.base58_encode(); + out << root.encode_base58(); if (root.length() < it.length()) out << "..."; }