diff options
Diffstat (limited to 'include/jsoncons_ext/bson')
-rw-r--r-- | include/jsoncons_ext/bson/bson.hpp | 23 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_cursor.hpp | 320 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_decimal128.hpp | 865 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_decimal128.hpp.bak | 816 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_encoder.hpp | 585 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_error.hpp | 103 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_oid.hpp | 245 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_options.hpp | 75 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_parser.hpp | 645 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_reader.hpp | 92 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/bson_type.hpp | 44 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/decode_bson.hpp | 201 | ||||
-rw-r--r-- | include/jsoncons_ext/bson/encode_bson.hpp | 144 |
13 files changed, 4158 insertions, 0 deletions
diff --git a/include/jsoncons_ext/bson/bson.hpp b/include/jsoncons_ext/bson/bson.hpp new file mode 100644 index 0000000..ec3192d --- /dev/null +++ b/include/jsoncons_ext/bson/bson.hpp @@ -0,0 +1,23 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_HPP +#define JSONCONS_BSON_BSON_HPP + +#include <string> +#include <vector> +#include <memory> +#include <type_traits> // std::enable_if +#include <istream> // std::basic_istream +#include <jsoncons/json.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons_ext/bson/bson_encoder.hpp> +#include <jsoncons_ext/bson/bson_reader.hpp> +#include <jsoncons_ext/bson/bson_cursor.hpp> +#include <jsoncons_ext/bson/encode_bson.hpp> +#include <jsoncons_ext/bson/decode_bson.hpp> + +#endif diff --git a/include/jsoncons_ext/bson/bson_cursor.hpp b/include/jsoncons_ext/bson/bson_cursor.hpp new file mode 100644 index 0000000..8baee53 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_cursor.hpp @@ -0,0 +1,320 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_CURSOR_HPP +#define JSONCONS_BSON_BSON_CURSOR_HPP + +#include <memory> // std::allocator +#include <string> +#include <vector> +#include <stdexcept> +#include <system_error> +#include <ios> +#include <istream> // std::basic_istream +#include <jsoncons/byte_string.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons/json_visitor.hpp> +#include <jsoncons/json_exception.hpp> +#include <jsoncons/staj_cursor.hpp> +#include <jsoncons/source.hpp> +#include <jsoncons_ext/bson/bson_parser.hpp> + +namespace jsoncons { +namespace bson { + +template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>> +class basic_bson_cursor : public basic_staj_cursor<char>, private virtual ser_context +{ + using super_type = basic_staj_cursor<char>; +public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; +private: + basic_bson_parser<Source,Allocator> parser_; + basic_staj_visitor<char_type> cursor_visitor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_bson_cursor(const basic_bson_cursor&) = delete; + basic_bson_cursor& operator=(const basic_bson_cursor&) = delete; + +public: + using string_view_type = string_view; + + template <class Sourceable> + basic_bson_cursor(Sourceable&& source, + const bson_decode_options& options = bson_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward<Sourceable>(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template <class Sourceable> + basic_bson_cursor(Sourceable&& source, + std::error_code& ec) + : basic_bson_cursor(std::allocator_arg, Allocator(), + std::forward<Sourceable>(source), + bson_decode_options(), + ec) + { + } + + template <class Sourceable> + basic_bson_cursor(Sourceable&& source, + const bson_decode_options& options, + std::error_code& ec) + : basic_bson_cursor(std::allocator_arg, Allocator(), + std::forward<Sourceable>(source), + options, + ec) + { + } + + template <class Sourceable> + basic_bson_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const bson_decode_options& options, + std::error_code& ec) + : parser_(std::forward<Sourceable>(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template <class Sourceable> + void reset(Sourceable&& source) + { + parser_.reset(std::forward<Sourceable>(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template <class Sourceable> + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward<Sourceable>(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + void array_expected(std::error_code& ec) override + { + if (cursor_visitor_.event().event_type() == staj_event_type::begin_object) + { + parser_.array_expected(cursor_visitor_, ec); + } + else + { + super_type::array_expected(ec); + } + } + + const staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor<char_type>& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor<char_type>& visitor, + std::error_code& ec) override + { + if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj_filter_view operator|(basic_bson_cursor& cursor, + std::function<bool(const staj_event&, const ser_context&)> pred) + { + return staj_filter_view(cursor, pred); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + template <class Sourceable> + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_bson_cursor(Sourceable&& source, + std::function<bool(const staj_event&, const ser_context&)> filter, + std::error_code& ec) + : basic_bson_cursor(std::allocator_arg, Allocator(), + std::forward<Sourceable>(source), filter, ec) + { + } + + template <class Sourceable> + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_bson_cursor(Sourceable&& source, + std::function<bool(const staj_event&, const ser_context&)> filter, + const bson_decode_options& options = bson_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward<Sourceable>(source), options, alloc), + cursor_visitor_(filter), + eof_(false) + { + if (!done()) + { + next(); + } + } + + template <class Sourceable> + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_bson_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + std::function<bool(const staj_event&, const ser_context&)> filter, + std::error_code& ec) + : parser_(std::forward<Sourceable>(source),alloc), + cursor_visitor_(filter), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&)") + void read(basic_json_visitor<char_type>& visitor) + { + read_to(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&, std::error_code&)") + void read(basic_json_visitor<char_type>& visitor, + std::error_code& ec) + { + read_to(visitor, ec); + } +#endif +private: + static bool accept_all(const staj_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_visitor_, ec); + if (ec) return; + } + } + + void read_next(basic_json_visitor<char_type>& visitor, std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(visitor, ec); + if (ec) return; + } + } +}; + +using bson_stream_cursor = basic_bson_cursor<jsoncons::binary_stream_source>; +using bson_bytes_cursor = basic_bson_cursor<jsoncons::bytes_source>; + +} // namespace bson +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp b/include/jsoncons_ext/bson/bson_decimal128.hpp new file mode 100644 index 0000000..b487a04 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_decimal128.hpp @@ -0,0 +1,865 @@ +#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP +#define JSONCONS_BSON_BSON_DECIMAL128_HPP + +/* + * Implements decimal128_to_chars and decimal128_from_chars + * + * Based on the libjson functions bson_decimal128_to_string + * and bson_decimal128_from_string_w_len, available at + * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.h + * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.c + * +*/ + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <cstring> +#include <ctype.h> +#include <system_error> +#include <algorithm> +#include <jsoncons/config/jsoncons_config.hpp> + +namespace jsoncons { namespace bson { + + struct decimal128_to_chars_result + { + char* ptr; + std::errc ec; + }; + + struct decimal128_from_chars_result + { + const char* ptr; + std::errc ec; + }; + +/** + * BSON_DECIMAL128_STRING: + * + * The length of a decimal128 string (with null terminator). + * + * 1 for the sign + * 35 for digits and radix + * 2 for exponent indicator and sign + * 4 for exponent digits + */ +#define BSON_DECIMAL128_STRING 43 + + struct TP1 + { + uint64_t low; + uint64_t high; + + constexpr TP1() : low(0), high(0) {} + constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {} + }; + struct TP2 + { + uint64_t high; + uint64_t low; + + constexpr TP2() : high(0), low(0) {} + constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {} + }; + + typedef std::conditional< + jsoncons::endian::native == jsoncons::endian::little, + TP1, + TP2 + >::type decimal128_t; + + inline + bool operator==(const decimal128_t& lhs, const decimal128_t& rhs) + { + return lhs.high == rhs.high && lhs.low == rhs.low; + } + + inline + bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs) + { + return !(lhs == rhs); + } + + struct decimal128_limits + { + // The length of a decimal128 string (without null terminator). + // + // 1 for the sign + // 35 for digits and radix + // 2 for exponent indicator and sign + // 4 for exponent digits + static constexpr int buf_size = 42; + static constexpr int exponent_max = 6111; + static constexpr int exponent_min = -6176; + static constexpr int exponent_bias = 6176; + static constexpr int max_digits = 34; + + static constexpr decimal128_t nan() {return decimal128_t(0x7c00000000000000ull, 0);} + static constexpr decimal128_t infinity() {return decimal128_t(0x7800000000000000ull, 0);} + static constexpr decimal128_t neg_infinity() {return decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0);} + }; + + inline + bool is_nan(decimal128_t dec) { return dec == decimal128_limits::nan(); } + + inline + bool is_inf(decimal128_t dec) { return dec == decimal128_limits::infinity(); } + + inline + bool is_neg_inf(decimal128_t dec) { return dec == decimal128_limits::neg_infinity(); } + + /** + * bson_uint128_t: + * + * This struct represents a 128 bit integer. + */ + typedef struct { + uint32_t parts[4]; /* 32-bit words stored high to low. */ + } bson_uint128_t; + + typedef struct { + uint64_t high, low; + } bson_uint128_6464_t; + + namespace detail { + + /** + *------------------------------------------------------------------------------ + * + * bson_uint128_divide1B -- + * + * This function divides a #bson_uint128_t by 1000000000 (1 billion) and + * computes the quotient and remainder. + * + * The remainder will contain 9 decimal digits for conversion to string. + * + * @value The #bson_uint128_t operand. + * @quotient A pointer to store the #bson_uint128_t quotient. + * @rem A pointer to store the #uint64_t remainder. + * + * Returns: + * The quotient at @quotient and the remainder at @rem. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void bson_uint128_divide1B (bson_uint128_t value, /* IN */ + bson_uint128_t *quotient, /* OUT */ + uint32_t *rem) /* OUT */ + { + const uint32_t DIVISOR = 1000 * 1000 * 1000; + uint64_t _rem = 0; + int i = 0; + + if (!value.parts[0] && !value.parts[1] && !value.parts[2] && + !value.parts[3]) { + *quotient = value; + *rem = 0; + return; + } + + for (i = 0; i <= 3; i++) { + _rem <<= 32; /* Adjust remainder to match value of next dividend */ + _rem += value.parts[i]; /* Add the divided to _rem */ + value.parts[i] = (uint32_t) (_rem / DIVISOR); + _rem %= DIVISOR; /* Store the remainder */ + } + + *quotient = value; + *rem = (uint32_t) _rem; + } + + /** + *------------------------------------------------------------------------- + * + * mul64x64 -- + * + * This function multiplies two &uint64_t into a &bson_uint128_6464_t. + * + * Returns: + * The product of @left and @right. + * + * Side Effects: + * None. + * + *------------------------------------------------------------------------- + */ + + inline + void mul_64x64 (uint64_t left, /* IN */ + uint64_t right, /* IN */ + bson_uint128_6464_t *product) /* OUT */ + { + uint64_t left_high, left_low, right_high, right_low, product_high, + product_mid, product_mid2, product_low; + bson_uint128_6464_t rt = {0,0}; + + if (!left && !right) { + *product = rt; + return; + } + + left_high = left >> 32; + left_low = (uint32_t) left; + right_high = right >> 32; + right_low = (uint32_t) right; + + product_high = left_high * right_high; + product_mid = left_high * right_low; + product_mid2 = left_low * right_high; + product_low = left_low * right_low; + + product_high += product_mid >> 32; + product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32); + + product_high = product_high + (product_mid >> 32); + product_low = (product_mid << 32) + (uint32_t) product_low; + + rt.high = product_high; + rt.low = product_low; + *product = rt; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_tolower -- + * + * This function converts the ASCII character @c to lowercase. It is locale + * insensitive (unlike the stdlib tolower). + * + * Returns: + * The lowercased character. + */ + + inline + char dec128_tolower (char c) + { + if (isupper (c)) { + c += 32; + } + + return c; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_istreq -- + * + * This function compares the null-terminated *ASCII* strings @a and @b + * for case-insensitive equality. + * + * Returns: + * true if the strings are equal, false otherwise. + */ + + inline + bool dec128_istreq (const char* a, const char* lasta, + const char* b, const char* lastb) + { + while (!(a == lasta && b == lastb)) + { + // strings are different lengths + if (a == lasta || b == lastb) + { + return false; + } + + if (dec128_tolower (*a) != dec128_tolower (*b)) { + return false; + } + + a++; + b++; + } + + return true; + } + + } // namespace detail + + + /** + *------------------------------------------------------------------------------ + * + * decimal128_to_chars -- + * + * This function converts a BID formatted decimal128 value to string, + * accepting a &decimal128_t as @dec. The string is stored at @str. + * + * @dec : The BID formatted decimal to convert. + * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING + *characters. + * + * Returns: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + decimal128_to_chars_result decimal128_to_chars(char* first, char* last, const decimal128_t& dec) + { + const std::string bson_decimal128_inf = "Infinity"; + const std::string bson_decimal128_nan = "NaN"; + + const uint32_t combination_mask = 0x1f; /* Extract least significant 5 bits */ + const uint32_t exponent_mask = 0x3fff; /* Extract least significant 14 bits */ + const uint32_t combination_infinity = 30; /* Value of combination field for Inf */ + const uint32_t combination_nan = 31; /* Value of combination field for NaN */ + const uint32_t exponent_bias = 6176; /* decimal128 exponent bias */ + + char* str_out = first; /* output pointer in string */ + char significand_str[35]; /* decoded significand digits */ + + /* Note: bits in this routine are referred to starting at 0, */ + /* from the sign bit, towards the coefficient. */ + uint32_t high; /* bits 0 - 31 */ + uint32_t midh; /* bits 32 - 63 */ + uint32_t midl; /* bits 64 - 95 */ + uint32_t low; /* bits 96 - 127 */ + uint32_t combination; /* bits 1 - 5 */ + uint32_t biased_exponent; /* decoded biased exponent (14 bits) */ + uint32_t significand_digits = 0; /* the number of significand digits */ + uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ + uint32_t *significand_read = significand; /* read pointer into significand */ + int32_t exponent; /* unbiased exponent */ + int32_t scientific_exponent; /* the exponent if scientific notation is + * used */ + bool is_zero = false; /* true if the number is zero */ + + uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ + bson_uint128_t + significand128; /* temporary storage for significand decoding */ + + memset (significand_str, 0, sizeof (significand_str)); + + if ((int64_t) dec.high < 0) { /* negative */ + *(str_out++) = '-'; + } + + low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32), + midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32); + + /* Decode combination field and exponent */ + combination = (high >> 26) & combination_mask; + + if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) { + /* Check for 'special' values */ + if (combination == combination_infinity) { /* Infinity */ + if (last-str_out >= static_cast<ptrdiff_t >(bson_decimal128_inf.size())) + { + std::memcpy(str_out, bson_decimal128_inf.data(), bson_decimal128_inf.size()); + str_out += bson_decimal128_inf.size(); + } + *str_out = 0; + //strcpy_s (str_out, last-str_out, bson_decimal128_inf.c_str()); + return decimal128_to_chars_result{str_out, std::errc()}; + } else if (combination == combination_nan) { /* NaN */ + /* first, not str_out, to erase the sign */ + str_out = first; + if (last-str_out >= static_cast<ptrdiff_t >(bson_decimal128_nan.size())) + { + std::memcpy(str_out, bson_decimal128_nan.data(), bson_decimal128_nan.size()); + str_out += bson_decimal128_nan.size(); + } + *str_out = 0; + //strcpy_s (first, last-first, bson_decimal128_nan.c_str()); + /* we don't care about the NaN payload. */ + return decimal128_to_chars_result{str_out, std::errc()}; + } else { + biased_exponent = (high >> 15) & exponent_mask; + significand_msb = 0x8 + ((high >> 14) & 0x1); + } + } else { + significand_msb = (high >> 14) & 0x7; + biased_exponent = (high >> 17) & exponent_mask; + } + + exponent = biased_exponent - exponent_bias; + /* Create string of significand digits */ + + /* Convert the 114-bit binary number represented by */ + /* (high, midh, midl, low) to at most 34 decimal */ + /* digits through modulo and division. */ + significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); + significand128.parts[1] = midh; + significand128.parts[2] = midl; + significand128.parts[3] = low; + + if (significand128.parts[0] == 0 && significand128.parts[1] == 0 && + significand128.parts[2] == 0 && significand128.parts[3] == 0) { + is_zero = true; + } else if (significand128.parts[0] >= (1 << 17)) { + /* The significand is non-canonical or zero. + * In order to preserve compatibility with the densely packed decimal + * format, the maximum value for the significand of decimal128 is + * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754 + * standard dictates that the significand is interpreted as zero. + */ + is_zero = true; + } else { + for (int k = 3; k >= 0; k--) { + uint32_t least_digits = 0; + detail::bson_uint128_divide1B ( + significand128, &significand128, &least_digits); + + /* We now have the 9 least significant digits (in base 2). */ + /* Convert and output to string. */ + if (!least_digits) { + continue; + } + + for (int j = 8; j >= 0; j--) { + significand[k * 9 + j] = least_digits % 10; + least_digits /= 10; + } + } + } + + /* Output format options: */ + /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ + /* Regular - ddd.ddd */ + + if (is_zero) { + significand_digits = 1; + *significand_read = 0; + } else { + significand_digits = 36; + while (!(*significand_read)) { + significand_digits--; + significand_read++; + } + } + + scientific_exponent = significand_digits - 1 + exponent; + + /* The scientific exponent checks are dictated by the string conversion + * specification and are somewhat arbitrary cutoffs. + * + * We must check exponent > 0, because if this is the case, the number + * has trailing zeros. However, we *cannot* output these trailing zeros, + * because doing so would change the precision of the value, and would + * change stored data if the string converted number is round tripped. + */ + if (scientific_exponent < -6 || exponent > 0) { + /* Scientific format */ + *(str_out++) = char(*(significand_read++)) + '0'; + significand_digits--; + + if (significand_digits) { + *(str_out++) = '.'; + } + + for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + /* Exponent */ + *(str_out++) = 'E'; + + std::string s; + if (scientific_exponent >= 0) { + s.push_back('+'); + } + jsoncons::detail::from_integer(scientific_exponent, s); + if (str_out + s.size() < last) + { + std::memcpy(str_out, s.data(), s.size()); + } + else + { + return decimal128_to_chars_result{str_out, std::errc::value_too_large}; + } + str_out += s.size(); + } else { + /* Regular format with no decimal place */ + if (exponent >= 0) { + for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } else { + int32_t radix_position = significand_digits + exponent; + + if (radix_position > 0) { /* non-zero digits before radix */ + for (int32_t i = 0; + i < radix_position && (str_out < last); + i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } else { /* leading zero before radix point */ + *(str_out++) = '0'; + } + + *(str_out++) = '.'; + while (radix_position++ < 0) { /* add leading zeros after radix */ + *(str_out++) = '0'; + } + + for (std::size_t i = 0; + (i < significand_digits - (std::max) (radix_position - 1, 0)) && + (str_out < last); + i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } + } + return decimal128_to_chars_result{str_out, std::errc()}; + } + + + + /** + *------------------------------------------------------------------------------ + * + * bson_decimal128_from_string_w_len -- + * + * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to + * decimal128. Out of range values are converted to +/-Infinity. Invalid + * strings are converted to NaN. @len is the length of the string, or -1 + * meaning the string is null-terminated. + * + * If more digits are provided than the available precision allows, + * round to the nearest expressable decimal128 with ties going to even will + * occur. + * + * Note: @string must be ASCII only! + * + * Returns: + * true on success, or false on failure. @dec will be NaN if @str was invalid + * The &decimal128_t converted from @string at @dec. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + decimal128_from_chars_result decimal128_from_chars(const char* first, const char* last, decimal128_t& dec) + { + const string_view inf_str = "inf"; + const string_view infinity_str = "infinity"; + const string_view nan_str = "nan"; + + ptrdiff_t len = last - first; + + bson_uint128_6464_t significand = {0,0}; + + const char* str_read = first; /* Read pointer for consuming str. */ + + /* Parsing state tracking */ + bool is_negative = false; + bool saw_radix = false; + bool includes_sign = false; /* True if the input first contains a sign. */ + bool found_nonzero = false; + + size_t significant_digits = 0; /* Total number of significant digits + * (no leading or trailing zero) */ + size_t ndigits_read = 0; /* Total number of significand digits read */ + size_t ndigits = 0; /* Total number of digits (no leading zeros) */ + size_t radix_position = 0; /* The number of the digits after radix */ + size_t first_nonzero = 0; /* The index of the first non-zero in *str* */ + + uint16_t digits[decimal128_limits::max_digits] = {0}; + uint16_t ndigits_stored = 0; /* The number of digits in digits */ + uint16_t *digits_insert = digits; /* Insertion pointer for digits */ + size_t first_digit = 0; /* The index of the first non-zero digit */ + size_t last_digit = 0; /* The index of the last digit */ + + int32_t exponent = 0; + uint64_t significand_high = 0; /* The high 17 digits of the significand */ + uint64_t significand_low = 0; /* The low 17 digits of the significand */ + uint16_t biased_exponent = 0; /* The biased exponent */ + + dec.high = 0; + dec.low = 0; + + if (*str_read == '+' || *str_read == '-') { + is_negative = *(str_read++) == '-'; + includes_sign = true; + } + + /* Check for Infinity or NaN */ + if (!isdigit (*str_read) && *str_read != '.') { + if (detail::dec128_istreq (str_read, last, inf_str.data(), inf_str.data()+inf_str.length()) || + detail::dec128_istreq (str_read, last, infinity_str.data(), infinity_str.data()+infinity_str.length())) + { + dec = is_negative ? decimal128_limits::neg_infinity() : decimal128_limits::infinity(); + return decimal128_from_chars_result{str_read,std::errc()}; + } else if (detail::dec128_istreq (str_read, last, nan_str.data(), nan_str.data()+nan_str.length())) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc()}; + } + + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Read digits */ + while (((isdigit (*str_read) || *str_read == '.')) && + (len == -1 || str_read < first + len)) { + if (*str_read == '.') { + if (saw_radix) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + saw_radix = true; + str_read++; + continue; + } + + if (ndigits_stored < 34) { + if (*str_read != '0' || found_nonzero) { + if (!found_nonzero) { + first_nonzero = ndigits_read; + } + + found_nonzero = true; + *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */ + ndigits_stored++; + } + } + + if (found_nonzero) { + ndigits++; + } + + if (saw_radix) { + radix_position++; + } + + ndigits_read++; + str_read++; + } + + if (saw_radix && !ndigits_read) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Read exponent if exists */ + if (*str_read == 'e' || *str_read == 'E') { + ++str_read; + if (*str_read == '+') { + ++str_read; + } + auto result = jsoncons::detail::to_integer(str_read, last - str_read, exponent); + if (result.ec != jsoncons::detail::to_integer_errc()) + { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + str_read = result.ptr; + } + + if ((len == -1 || str_read < first + len) && *str_read) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Done reading input. */ + /* Find first non-zero digit in digits */ + first_digit = 0; + + if (!ndigits_stored) { /* value is zero */ + first_digit = 0; + last_digit = 0; + digits[0] = 0; + ndigits = 1; + ndigits_stored = 1; + significant_digits = 0; + } else { + last_digit = ndigits_stored - 1; + significant_digits = ndigits; + /* Mark trailing zeros as non-significant */ + while (first[first_nonzero + significant_digits - 1 + includes_sign + + saw_radix] == '0') { + significant_digits--; + } + } + + + /* Normalization of exponent */ + /* Correct exponent based on radix position, and shift significand as needed + */ + /* to represent user input */ + + /* Overflow prevention */ + if (exponent <= static_cast<int32_t>(radix_position) && static_cast<int32_t>(radix_position) - exponent > (1 << 14)) { + exponent = decimal128_limits::exponent_min; + } else { + exponent -= static_cast<int32_t>(radix_position); + } + + /* Attempt to normalize the exponent */ + while (exponent > decimal128_limits::exponent_max) { + /* Shift exponent to significand and decrease */ + last_digit++; + + if (last_digit - first_digit > decimal128_limits::max_digits) { + /* The exponent is too great to shift into the significand. */ + if (significant_digits == 0) { + /* Value is zero, we are allowed to clamp the exponent. */ + exponent = decimal128_limits::exponent_max; + break; + } + + /* Overflow is not permitted, error. */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + exponent--; + } + + while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) { + /* Shift last digit */ + if (last_digit == 0) { + /* underflow is not allowed, but zero clamping is */ + if (significant_digits == 0) { + exponent = decimal128_limits::exponent_min; + break; + } + + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + if (ndigits_stored < ndigits) { + if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && + significant_digits != 0) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + ndigits--; /* adjust to match digits not stored */ + } else { + if (digits[last_digit] != 0) { + /* Inexact rounding is not allowed. */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + + last_digit--; /* adjust to round */ + } + + if (exponent < decimal128_limits::exponent_max) { + exponent++; + } else { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + } + + /* Round */ + /* We've normalized the exponent, but might still need to round. */ + if (last_digit - first_digit + 1 < significant_digits) { + uint8_t round_digit; + + /* There are non-zero digits after last_digit that need rounding. */ + /* We round to nearest, ties to even */ + round_digit = + first[first_nonzero + last_digit + includes_sign + saw_radix + 1] - + '0'; + + if (round_digit != 0) { + /* Inexact (non-zero) rounding is not allowed */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + } + + /* Encode significand */ + significand_high = 0, /* The high 17 digits of the significand */ + significand_low = 0; /* The low 17 digits of the significand */ + + if (significant_digits == 0) { /* read a zero */ + significand_high = 0; + significand_low = 0; + } else if (last_digit - first_digit < 17) { + size_t d_idx = first_digit; + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + significand_high = 0; + } + } else { + size_t d_idx = first_digit; + significand_high = digits[d_idx++]; + + for (; d_idx <= last_digit - 17; d_idx++) { + significand_high *= 10; + significand_high += digits[d_idx]; + } + + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + } + } + + detail::mul_64x64 (significand_high, 100000000000000000ull, &significand); + significand.low += significand_low; + + if (significand.low < significand_low) { + significand.high += 1; + } + + + biased_exponent = static_cast<uint16_t>(exponent + static_cast<int32_t>(decimal128_limits::exponent_bias)); + + /* Encode combination, exponent, and significand. */ + if ((significand.high >> 49) & 1) { + /* Encode '11' into bits 1 to 3 */ + dec.high |= (0x3ull << 61); + dec.high |= (biased_exponent & 0x3fffull) << 47; + dec.high |= significand.high & 0x7fffffffffffull; + } else { + dec.high |= (biased_exponent & 0x3fffull) << 49; + dec.high |= significand.high & 0x1ffffffffffffull; + } + + dec.low = significand.low; + + /* Encode sign */ + if (is_negative) { + dec.high |= 0x8000000000000000ull; + } + + return decimal128_from_chars_result{str_read,std::errc()}; + } + +} // namespace bson +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp.bak b/include/jsoncons_ext/bson/bson_decimal128.hpp.bak new file mode 100644 index 0000000..8e27ee1 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_decimal128.hpp.bak @@ -0,0 +1,816 @@ +#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP +#define JSONCONS_BSON_BSON_DECIMAL128_HPP + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <system_error> +#include <jsoncons/config/jsoncons_config.hpp> + +namespace jsoncons { namespace bson { + + struct decimal128_to_chars_result + { + char* ptr; + std::errc ec; + }; + + struct decimal128_from_chars_result + { + const char* ptr; + std::errc ec; + }; + +/** + * BSON_DECIMAL128_STRING: + * + * The length of a decimal128 string (with null terminator). + * + * 1 for the sign + * 35 for digits and radix + * 2 for exponent indicator and sign + * 4 for exponent digits + */ +#define BSON_DECIMAL128_STRING 43 +#define BSON_DECIMAL128_INF "Infinity" +#define BSON_DECIMAL128_NAN "NaN" + + struct TP1 + { + uint64_t low; + uint64_t high; + + constexpr TP1() : low(0), high(0) {} + constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {} + }; + struct TP2 + { + uint64_t high; + uint64_t low; + + constexpr TP2() : high(0), low(0) {} + constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {} + }; + + typedef typename std::conditional< + jsoncons::endian::native == jsoncons::endian::little, + TP1, + TP2 + >::type decimal128_t; + + inline + bool operator==(const decimal128_t& lhs, const decimal128_t& rhs) + { + return lhs.high == rhs.high && lhs.low == rhs.low; + } + + inline + bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs) + { + return !(lhs == rhs); + } + + struct decimal128_limits + { + // The length of a decimal128 string (without null terminator). + // + // 1 for the sign + // 35 for digits and radix + // 2 for exponent indicator and sign + // 4 for exponent digits + static constexpr int recommended_buffer_size = 42; + static constexpr decimal128_t nan = decimal128_t(0x7c00000000000000ull, 0); + static constexpr decimal128_t infinity = decimal128_t(0x7800000000000000ull, 0); + static constexpr decimal128_t neg_infinity = decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0); + static constexpr int exponent_max = 6111; + static constexpr int exponent_min = -6176; + static constexpr int exponent_bias = 6176; + static constexpr int max_digits = 34; + }; + + /** + * bson_uint128_t: + * + * This struct represents a 128 bit integer. + */ + typedef struct { + uint32_t parts[4]; /* 32-bit words stored high to low. */ + } bson_uint128_t; + + typedef struct { + uint64_t high, low; + } bson_uint128_6464_t; + + namespace detail { + + /** + *------------------------------------------------------------------------------ + * + * bson_uint128_divide1B -- + * + * This function divides a #bson_uint128_t by 1000000000 (1 billion) and + * computes the quotient and remainder. + * + * The remainder will contain 9 decimal digits for conversion to string. + * + * @value The #bson_uint128_t operand. + * @quotient A pointer to store the #bson_uint128_t quotient. + * @rem A pointer to store the #uint64_t remainder. + * + * Returns: + * The quotient at @quotient and the remainder at @rem. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void bson_uint128_divide1B (bson_uint128_t value, /* IN */ + bson_uint128_t *quotient, /* OUT */ + uint32_t *rem) /* OUT */ + { + const uint32_t DIVISOR = 1000 * 1000 * 1000; + uint64_t _rem = 0; + int i = 0; + + if (!value.parts[0] && !value.parts[1] && !value.parts[2] && + !value.parts[3]) { + *quotient = value; + *rem = 0; + return; + } + + for (i = 0; i <= 3; i++) { + _rem <<= 32; /* Adjust remainder to match value of next dividend */ + _rem += value.parts[i]; /* Add the divided to _rem */ + value.parts[i] = (uint32_t) (_rem / DIVISOR); + _rem %= DIVISOR; /* Store the remainder */ + } + + *quotient = value; + *rem = (uint32_t) _rem; + } + + /** + *------------------------------------------------------------------------- + * + * mul64x64 -- + * + * This function multiplies two &uint64_t into a &bson_uint128_6464_t. + * + * Returns: + * The product of @left and @right. + * + * Side Effects: + * None. + * + *------------------------------------------------------------------------- + */ + + inline + void mul_64x64 (uint64_t left, /* IN */ + uint64_t right, /* IN */ + bson_uint128_6464_t *product) /* OUT */ + { + uint64_t left_high, left_low, right_high, right_low, product_high, + product_mid, product_mid2, product_low; + bson_uint128_6464_t rt = {0}; + + if (!left && !right) { + *product = rt; + return; + } + + left_high = left >> 32; + left_low = (uint32_t) left; + right_high = right >> 32; + right_low = (uint32_t) right; + + product_high = left_high * right_high; + product_mid = left_high * right_low; + product_mid2 = left_low * right_high; + product_low = left_low * right_low; + + product_high += product_mid >> 32; + product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32); + + product_high = product_high + (product_mid >> 32); + product_low = (product_mid << 32) + (uint32_t) product_low; + + rt.high = product_high; + rt.low = product_low; + *product = rt; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_tolower -- + * + * This function converts the ASCII character @c to lowercase. It is locale + * insensitive (unlike the stdlib tolower). + * + * Returns: + * The lowercased character. + */ + + inline + char dec128_tolower (char c) + { + if (isupper (c)) { + c += 32; + } + + return c; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_istreq -- + * + * This function compares the null-terminated *ASCII* strings @a and @b + * for case-insensitive equality. + * + * Returns: + * true if the strings are equal, false otherwise. + */ + + inline + bool dec128_istreq (const char* a, /* IN */ + const char* b /* IN */) + { + while (*a != '\0' || *b != '\0') { + /* strings are different lengths. */ + if (*a == '\0' || *b == '\0') { + return false; + } + + if (dec128_tolower (*a) != dec128_tolower (*b)) { + return false; + } + + a++; + b++; + } + + return true; + } + + } // namespace detail + + + /** + *------------------------------------------------------------------------------ + * + * decimal128_to_chars -- + * + * This function converts a BID formatted decimal128 value to string, + * accepting a &decimal128_t as @dec. The string is stored at @str. + * + * @dec : The BID formatted decimal to convert. + * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING + *characters. + * + * Returns: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void decimal128_to_chars(char* first, char* last, const decimal128_t& dec) + { + uint32_t COMBINATION_MASK = 0x1f; /* Extract least significant 5 bits */ + uint32_t EXPONENT_MASK = 0x3fff; /* Extract least significant 14 bits */ + uint32_t COMBINATION_INFINITY = 30; /* Value of combination field for Inf */ + uint32_t COMBINATION_NAN = 31; /* Value of combination field for NaN */ + uint32_t EXPONENT_BIAS = 6176; /* decimal128 exponent bias */ + + char* str_out = first; /* output pointer in string */ + char significand_str[35]; /* decoded significand digits */ + + + /* Note: bits in this routine are referred to starting at 0, */ + /* from the sign bit, towards the coefficient. */ + uint32_t high; /* bits 0 - 31 */ + uint32_t midh; /* bits 32 - 63 */ + uint32_t midl; /* bits 64 - 95 */ + uint32_t low; /* bits 96 - 127 */ + uint32_t combination; /* bits 1 - 5 */ + uint32_t biased_exponent; /* decoded biased exponent (14 bits) */ + uint32_t significand_digits = 0; /* the number of significand digits */ + uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ + uint32_t *significand_read = significand; /* read pointer into significand */ + int32_t exponent; /* unbiased exponent */ + int32_t scientific_exponent; /* the exponent if scientific notation is + * used */ + bool is_zero = false; /* true if the number is zero */ + + uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ + bson_uint128_t + significand128; /* temporary storage for significand decoding */ + size_t i; /* indexing variables */ + int j, k; + + memset (significand_str, 0, sizeof (significand_str)); + + if ((int64_t) dec.high < 0) { /* negative */ + *(str_out++) = '-'; + } + + low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32), + midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32); + + /* Decode combination field and exponent */ + combination = (high >> 26) & COMBINATION_MASK; + + if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) { + /* Check for 'special' values */ + if (combination == COMBINATION_INFINITY) { /* Infinity */ + strcpy (str_out, BSON_DECIMAL128_INF); + return; + } else if (combination == COMBINATION_NAN) { /* NaN */ + /* first, not str_out, to erase the sign */ + strcpy (first, BSON_DECIMAL128_NAN); + /* we don't care about the NaN payload. */ + return; + } else { + biased_exponent = (high >> 15) & EXPONENT_MASK; + significand_msb = 0x8 + ((high >> 14) & 0x1); + } + } else { + significand_msb = (high >> 14) & 0x7; + biased_exponent = (high >> 17) & EXPONENT_MASK; + } + + exponent = biased_exponent - EXPONENT_BIAS; + /* Create string of significand digits */ + + /* Convert the 114-bit binary number represented by */ + /* (high, midh, midl, low) to at most 34 decimal */ + /* digits through modulo and division. */ + significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); + significand128.parts[1] = midh; + significand128.parts[2] = midl; + significand128.parts[3] = low; + + if (significand128.parts[0] == 0 && significand128.parts[1] == 0 && + significand128.parts[2] == 0 && significand128.parts[3] == 0) { + is_zero = true; + } else if (significand128.parts[0] >= (1 << 17)) { + /* The significand is non-canonical or zero. + * In order to preserve compatibility with the densely packed decimal + * format, the maximum value for the significand of decimal128 is + * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754 + * standard dictates that the significand is interpreted as zero. + */ + is_zero = true; + } else { + for (k = 3; k >= 0; k--) { + uint32_t least_digits = 0; + detail::bson_uint128_divide1B ( + significand128, &significand128, &least_digits); + + /* We now have the 9 least significant digits (in base 2). */ + /* Convert and output to string. */ + if (!least_digits) { + continue; + } + + for (j = 8; j >= 0; j--) { + significand[k * 9 + j] = least_digits % 10; + least_digits /= 10; + } + } + } + + /* Output format options: */ + /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ + /* Regular - ddd.ddd */ + + if (is_zero) { + significand_digits = 1; + *significand_read = 0; + } else { + significand_digits = 36; + while (!(*significand_read)) { + significand_digits--; + significand_read++; + } + } + + scientific_exponent = significand_digits - 1 + exponent; + + /* The scientific exponent checks are dictated by the string conversion + * specification and are somewhat arbitrary cutoffs. + * + * We must check exponent > 0, because if this is the case, the number + * has trailing zeros. However, we *cannot* output these trailing zeros, + * because doing so would change the precision of the value, and would + * change stored data if the string converted number is round tripped. + */ + if (scientific_exponent < -6 || exponent > 0) { + /* Scientific format */ + *(str_out++) = *(significand_read++) + '0'; + significand_digits--; + + if (significand_digits) { + *(str_out++) = '.'; + } + + for (i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = *(significand_read++) + '0'; + } + /* Exponent */ + *(str_out++) = 'E'; + snprintf (str_out, 6, "%+d", scientific_exponent); + } else { + /* Regular format with no decimal place */ + if (exponent >= 0) { + for (i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = *(significand_read++) + '0'; + } + *str_out = '\0'; + } else { + int32_t radix_position = significand_digits + exponent; + + if (radix_position > 0) { /* non-zero digits before radix */ + for (i = 0; + i < radix_position && (str_out < last); + i++) { + *(str_out++) = *(significand_read++) + '0'; + } + } else { /* leading zero before radix point */ + *(str_out++) = '0'; + } + + *(str_out++) = '.'; + while (radix_position++ < 0) { /* add leading zeros after radix */ + *(str_out++) = '0'; + } + + for (i = 0; + (i < significand_digits - (std::max) (radix_position - 1, 0)) && + (str_out < last); + i++) { + *(str_out++) = *(significand_read++) + '0'; + } + *str_out = '\0'; + } + } + } + + + + /** + *------------------------------------------------------------------------------ + * + * bson_decimal128_from_string_w_len -- + * + * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to + * decimal128. Out of range values are converted to +/-Infinity. Invalid + * strings are converted to NaN. @len is the length of the string, or -1 + * meaning the string is null-terminated. + * + * If more digits are provided than the available precision allows, + * round to the nearest expressable decimal128 with ties going to even will + * occur. + * + * Note: @string must be ASCII only! + * + * Returns: + * true on success, or false on failure. @dec will be NaN if @str was invalid + * The &decimal128_t converted from @string at @dec. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + bool decimal128_from_chars(const char* first, const char* last, decimal128_t& dec) + { + int len = last - first; + + bson_uint128_6464_t significand = {0}; + + const char* str_read = first; /* Read pointer for consuming str. */ + + /* Parsing state tracking */ + bool is_negative = false; + bool saw_radix = false; + bool includes_sign = false; /* True if the input first contains a sign. */ + bool found_nonzero = false; + + size_t significant_digits = 0; /* Total number of significant digits + * (no leading or trailing zero) */ + size_t ndigits_read = 0; /* Total number of significand digits read */ + size_t ndigits = 0; /* Total number of digits (no leading zeros) */ + size_t radix_position = 0; /* The number of the digits after radix */ + size_t first_nonzero = 0; /* The index of the first non-zero in *str* */ + + uint16_t digits[decimal128_limits::max_digits] = {0}; + uint16_t ndigits_stored = 0; /* The number of digits in digits */ + uint16_t *digits_insert = digits; /* Insertion pointer for digits */ + size_t first_digit = 0; /* The index of the first non-zero digit */ + size_t last_digit = 0; /* The index of the last digit */ + + int32_t exponent = 0; + uint64_t significand_high = 0; /* The high 17 digits of the significand */ + uint64_t significand_low = 0; /* The low 17 digits of the significand */ + uint16_t biased_exponent = 0; /* The biased exponent */ + + dec.high = 0; + dec.low = 0; + + if (*str_read == '+' || *str_read == '-') { + is_negative = *(str_read++) == '-'; + includes_sign = true; + } + + /* Check for Infinity or NaN */ + if (!isdigit (*str_read) && *str_read != '.') { + if (detail::dec128_istreq (str_read, "inf") || + detail::dec128_istreq (str_read, "infinity")) { + dec = is_negative ? decimal128_limits::neg_infinity : decimal128_limits::infinity; + return true; + } else if (detail::dec128_istreq (str_read, "nan")) { + dec = decimal128_limits::nan; + return true; + } + + dec = decimal128_limits::nan; + return false; + } + + /* Read digits */ + while (((isdigit (*str_read) || *str_read == '.')) && + (len == -1 || str_read < first + len)) { + if (*str_read == '.') { + if (saw_radix) { + dec = decimal128_limits::nan; + return false; + } + + saw_radix = true; + str_read++; + continue; + } + + if (ndigits_stored < 34) { + if (*str_read != '0' || found_nonzero) { + if (!found_nonzero) { + first_nonzero = ndigits_read; + } + + found_nonzero = true; + *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */ + ndigits_stored++; + } + } + + if (found_nonzero) { + ndigits++; + } + + if (saw_radix) { + radix_position++; + } + + ndigits_read++; + str_read++; + } + + if (saw_radix && !ndigits_read) { + dec = decimal128_limits::nan; + return false; + } + + /* Read exponent if exists */ + if (*str_read == 'e' || *str_read == 'E') { + int nread = 0; + #ifdef _MSC_VER + #define SSCANF sscanf_s + #else + #define SSCANF sscanf + #endif + int read_exponent = SSCANF (++str_read, "%d%n", &exponent, &nread); + str_read += nread; + + if (!read_exponent || nread == 0) { + dec = decimal128_limits::nan; + return false; + } + + #undef SSCANF + } + + if ((len == -1 || str_read < first + len) && *str_read) { + dec = decimal128_limits::nan; + return false; + } + + /* Done reading input. */ + /* Find first non-zero digit in digits */ + first_digit = 0; + + if (!ndigits_stored) { /* value is zero */ + first_digit = 0; + last_digit = 0; + digits[0] = 0; + ndigits = 1; + ndigits_stored = 1; + significant_digits = 0; + } else { + last_digit = ndigits_stored - 1; + significant_digits = ndigits; + /* Mark trailing zeros as non-significant */ + while (first[first_nonzero + significant_digits - 1 + includes_sign + + saw_radix] == '0') { + significant_digits--; + } + } + + + /* Normalization of exponent */ + /* Correct exponent based on radix position, and shift significand as needed + */ + /* to represent user input */ + + /* Overflow prevention */ + if (exponent <= radix_position && radix_position - exponent > (1 << 14)) { + exponent = decimal128_limits::exponent_min; + } else { + exponent -= radix_position; + } + + /* Attempt to normalize the exponent */ + while (exponent > decimal128_limits::exponent_max) { + /* Shift exponent to significand and decrease */ + last_digit++; + + if (last_digit - first_digit > decimal128_limits::max_digits) { + /* The exponent is too great to shift into the significand. */ + if (significant_digits == 0) { + /* Value is zero, we are allowed to clamp the exponent. */ + exponent = decimal128_limits::exponent_max; + break; + } + + /* Overflow is not permitted, error. */ + dec = decimal128_limits::nan; + return false; + } + + exponent--; + } + + while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) { + /* Shift last digit */ + if (last_digit == 0) { + /* underflow is not allowed, but zero clamping is */ + if (significant_digits == 0) { + exponent = decimal128_limits::exponent_min; + break; + } + + dec = decimal128_limits::nan; + return false; + } + + if (ndigits_stored < ndigits) { + if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && + significant_digits != 0) { + dec = decimal128_limits::nan; + return false; + } + + ndigits--; /* adjust to match digits not stored */ + } else { + if (digits[last_digit] != 0) { + /* Inexact rounding is not allowed. */ + dec = decimal128_limits::nan; + return false; + } + + + last_digit--; /* adjust to round */ + } + + if (exponent < decimal128_limits::exponent_max) { + exponent++; + } else { + dec = decimal128_limits::nan; + return false; + } + } + + /* Round */ + /* We've normalized the exponent, but might still need to round. */ + if (last_digit - first_digit + 1 < significant_digits) { + uint8_t round_digit; + + /* There are non-zero digits after last_digit that need rounding. */ + /* We round to nearest, ties to even */ + round_digit = + first[first_nonzero + last_digit + includes_sign + saw_radix + 1] - + '0'; + + if (round_digit != 0) { + /* Inexact (non-zero) rounding is not allowed */ + dec = decimal128_limits::nan; + return false; + } + } + + /* Encode significand */ + significand_high = 0, /* The high 17 digits of the significand */ + significand_low = 0; /* The low 17 digits of the significand */ + + if (significant_digits == 0) { /* read a zero */ + significand_high = 0; + significand_low = 0; + } else if (last_digit - first_digit < 17) { + size_t d_idx = first_digit; + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + significand_high = 0; + } + } else { + size_t d_idx = first_digit; + significand_high = digits[d_idx++]; + + for (; d_idx <= last_digit - 17; d_idx++) { + significand_high *= 10; + significand_high += digits[d_idx]; + } + + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + } + } + + detail::mul_64x64 (significand_high, 100000000000000000ull, &significand); + significand.low += significand_low; + + if (significand.low < significand_low) { + significand.high += 1; + } + + + biased_exponent = (exponent + (int16_t) decimal128_limits::exponent_bias); + + /* Encode combination, exponent, and significand. */ + if ((significand.high >> 49) & 1) { + /* Encode '11' into bits 1 to 3 */ + dec.high |= (0x3ull << 61); + dec.high |= (biased_exponent & 0x3fffull) << 47; + dec.high |= significand.high & 0x7fffffffffffull; + } else { + dec.high |= (biased_exponent & 0x3fffull) << 49; + dec.high |= significand.high & 0x1ffffffffffffull; + } + + dec.low = significand.low; + + /* Encode sign */ + if (is_negative) { + dec.high |= 0x8000000000000000ull; + } + + return true; + } + +} // namespace bson +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/bson_encoder.hpp b/include/jsoncons_ext/bson/bson_encoder.hpp new file mode 100644 index 0000000..4569f06 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_encoder.hpp @@ -0,0 +1,585 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_ENCODER_HPP +#define JSONCONS_BSON_BSON_ENCODER_HPP + +#include <string> +#include <vector> +#include <limits> // std::numeric_limits +#include <memory> +#include <utility> // std::move +#include <jsoncons/json_exception.hpp> +#include <jsoncons/json_visitor.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons/sink.hpp> +#include <jsoncons/detail/parse_number.hpp> +#include <jsoncons_ext/bson/bson_type.hpp> +#include <jsoncons_ext/bson/bson_error.hpp> +#include <jsoncons_ext/bson/bson_options.hpp> +#include <jsoncons_ext/bson/bson_decimal128.hpp> +#include <jsoncons_ext/bson/bson_oid.hpp> + +namespace jsoncons { namespace bson { + +template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>> +class basic_bson_encoder final : public basic_json_visitor<char> +{ + enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 }; + static constexpr int64_t nanos_in_milli = 1000000; + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; +public: + using allocator_type = Allocator; + using char_type = char; + using typename basic_json_visitor<char>::string_view_type; + using sink_type = Sink; + +private: + struct stack_item + { + jsoncons::bson::bson_container_type type_; + std::size_t offset_; + std::size_t name_offset_; + std::size_t index_; + + stack_item(jsoncons::bson::bson_container_type type, std::size_t offset) noexcept + : type_(type), offset_(offset), name_offset_(0), index_(0) + { + } + + std::size_t offset() const + { + return offset_; + } + + std::size_t member_offset() const + { + return name_offset_; + } + + void member_offset(std::size_t offset) + { + name_offset_ = offset; + } + + std::size_t next_index() + { + return index_++; + } + + bool is_object() const + { + return type_ == jsoncons::bson::bson_container_type::document; + } + + + }; + + sink_type sink_; + const bson_encode_options options_; + allocator_type alloc_; + + std::vector<stack_item> stack_; + std::vector<uint8_t> buffer_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_bson_encoder(const basic_bson_encoder&) = delete; + basic_bson_encoder& operator=(const basic_bson_encoder&) = delete; +public: + explicit basic_bson_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_bson_encoder(std::forward<Sink>(sink), + bson_encode_options(), + alloc) + { + } + + explicit basic_bson_encoder(Sink&& sink, + const bson_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward<Sink>(sink)), + options_(options), + alloc_(alloc), + nesting_depth_(0) + { + } + + ~basic_bson_encoder() noexcept + { + sink_.flush(); + } + + void reset() + { + stack_.clear(); + buffer_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + +private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + return false; + } + if (buffer_.size() > 0) + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::document_type); + } + + stack_.emplace_back(jsoncons::bson::bson_container_type::document, buffer_.size()); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + buffer_.push_back(0x00); + + std::size_t length = buffer_.size() - stack_.back().offset(); + binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+stack_.back().offset()); + + stack_.pop_back(); + if (stack_.empty()) + { + for (auto c : buffer_) + { + sink_.push_back(c); + } + } + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + return false; + } + if (buffer_.size() > 0) + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::array_type); + } + stack_.emplace_back(jsoncons::bson::bson_container_type::array, buffer_.size()); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + buffer_.push_back(0x00); + + std::size_t length = buffer_.size() - stack_.back().offset(); + binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+stack_.back().offset()); + + stack_.pop_back(); + if (stack_.empty()) + { + for (auto c : buffer_) + { + sink_.push_back(c); + } + } + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + stack_.back().member_offset(buffer_.size()); + buffer_.push_back(0x00); // reserve space for code + for (auto c : name) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + return true; + } + + bool visit_null(semantic_tag tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + switch (tag) + { + case semantic_tag::undefined: + before_value(jsoncons::bson::bson_type::undefined_type); + break; + default: + before_value(jsoncons::bson::bson_type::null_type); + break; + } + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::bool_type); + if (val) + { + buffer_.push_back(0x01); + } + else + { + buffer_.push_back(0x00); + } + + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::float128: + { + before_value(jsoncons::bson::bson_type::decimal128_type); + decimal128_t dec; + auto rc = decimal128_from_chars(sv.data(), sv.data()+sv.size(), dec); + if (rc.ec != std::errc()) + { + ec = bson_errc::invalid_decimal128_string; + return false; + } + binary::native_to_little(dec.low,std::back_inserter(buffer_)); + binary::native_to_little(dec.high,std::back_inserter(buffer_)); + break; + } + case semantic_tag::id: + { + before_value(jsoncons::bson::bson_type::object_id_type); + oid_t oid(sv); + for (auto b : oid) + { + buffer_.push_back(b); + } + break; + } + case semantic_tag::regex: + { + before_value(jsoncons::bson::bson_type::regex_type); + std::size_t first = sv.find_first_of('/'); + std::size_t last = sv.find_last_of('/'); + if (first == string_view::npos || last == string_view::npos || first == last) + { + ec = bson_errc::invalid_regex_string; + return false; + } + string_view regex = sv.substr(first+1,last-1); + for (auto c : regex) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + string_view options = sv.substr(last+1); + for (auto c : options) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + break; + } + default: + switch (tag) + { + case semantic_tag::code: + before_value(jsoncons::bson::bson_type::javascript_type); + break; + default: + before_value(jsoncons::bson::bson_type::string_type); + break; + } + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + ec = bson_errc::invalid_utf8_text_string; + return false; + } + for (auto c : sv) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + std::size_t length = buffer_.size() - string_offset; + binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+offset); + break; + } + + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::binary_type); + + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + + buffer_.push_back(0x80); // default subtype + + for (auto c : b) + { + buffer_.push_back(c); + } + std::size_t length = buffer_.size() - string_offset - 1; + binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+offset); + + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::binary_type); + + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + + buffer_.push_back(static_cast<uint8_t>(ext_tag)); // default subtype + + for (auto c : b) + { + buffer_.push_back(c); + } + std::size_t length = buffer_.size() - string_offset - 1; + binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+offset); + + return true; + } + + bool visit_int64(int64_t val, + semantic_tag tag, + const ser_context&, + std::error_code& ec) override + { + static constexpr int64_t min_value_div_1000 = (std::numeric_limits<int64_t>::min)() / 1000; + static constexpr int64_t max_value_div_1000 = (std::numeric_limits<int64_t>::max)() / 1000; + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::epoch_second: + if (val < min_value_div_1000) + { + ec = bson_errc::datetime_too_small; + return false; + } + if (val > max_value_div_1000) + { + ec = bson_errc::datetime_too_large; + return false; + } + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(val*millis_in_second,std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_milli: + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(val,std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_nano: + before_value(jsoncons::bson::bson_type::datetime_type); + if (val != 0) + { + val /= nanos_in_milli; + } + binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_)); + return true; + default: + { + if (val >= (std::numeric_limits<int32_t>::lowest)() && val <= (std::numeric_limits<int32_t>::max)()) + { + before_value(jsoncons::bson::bson_type::int32_type); + binary::native_to_little(static_cast<uint32_t>(val),std::back_inserter(buffer_)); + } + else + { + before_value(jsoncons::bson::bson_type::int64_type); + binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_)); + } + return true; + } + } + } + + bool visit_uint64(uint64_t val, + semantic_tag tag, + const ser_context&, + std::error_code& ec) override + { + static constexpr uint64_t max_value_div_1000 = (std::numeric_limits<uint64_t>::max)() / 1000; + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::epoch_second: + if (val > max_value_div_1000) + { + ec = bson_errc::datetime_too_large; + return false; + } + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(static_cast<int64_t>(val*millis_in_second),std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_milli: + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_nano: + before_value(jsoncons::bson::bson_type::datetime_type); + if (val != 0) + { + val /= nanos_in_second; + } + binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_)); + return true; + default: + { + bool more; + if (val <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)())) + { + before_value(jsoncons::bson::bson_type::int32_type); + binary::native_to_little(static_cast<uint32_t>(val),std::back_inserter(buffer_)); + more = true; + } + else if (val <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)())) + { + before_value(jsoncons::bson::bson_type::int64_type); + binary::native_to_little(static_cast<uint64_t>(val),std::back_inserter(buffer_)); + more = true; + } + else + { + ec = bson_errc::number_too_large; + more = false; + } + return more; + } + } + } + + bool visit_double(double val, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::double_type); + binary::native_to_little(val,std::back_inserter(buffer_)); + return true; + } + + void before_value(uint8_t code) + { + JSONCONS_ASSERT(!stack_.empty()); + if (stack_.back().is_object()) + { + buffer_[stack_.back().member_offset()] = code; + } + else + { + buffer_.push_back(code); + std::string name = std::to_string(stack_.back().next_index()); + buffer_.insert(buffer_.end(), name.begin(), name.end()); + buffer_.push_back(0x00); + } + } +}; + +using bson_stream_encoder = basic_bson_encoder<jsoncons::binary_stream_sink>; +using bson_bytes_encoder = basic_bson_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>; + +#if !defined(JSONCONS_NO_DEPRECATED) +template<class Sink=jsoncons::binary_stream_sink> +using basic_bson_serializer = basic_bson_encoder<Sink>; + +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_encoder; +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_encoder") typedef bson_bytes_encoder bson_buffer_serializer; + +#endif + +}} +#endif diff --git a/include/jsoncons_ext/bson/bson_error.hpp b/include/jsoncons_ext/bson/bson_error.hpp new file mode 100644 index 0000000..85cf24d --- /dev/null +++ b/include/jsoncons_ext/bson/bson_error.hpp @@ -0,0 +1,103 @@ +/// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_ERROR_HPP +#define JSONCONS_BSON_BSON_ERROR_HPP + +#include <system_error> +#include <jsoncons/config/jsoncons_config.hpp> + +namespace jsoncons { namespace bson { + +enum class bson_errc +{ + success = 0, + unexpected_eof = 1, + source_error, + invalid_utf8_text_string, + max_nesting_depth_exceeded, + string_length_is_non_positive, + length_is_negative, + number_too_large, + invalid_decimal128_string, + datetime_too_small, + datetime_too_large, + expected_bson_document, + invalid_regex_string, + size_mismatch, + unknown_type +}; + +class bson_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/bson"; + } + std::string message(int ev) const override + { + switch (static_cast<bson_errc>(ev)) + { + case bson_errc::unexpected_eof: + return "Unexpected end of file"; + case bson_errc::source_error: + return "Source error"; + case bson_errc::invalid_utf8_text_string: + return "Illegal UTF-8 encoding in text string"; + case bson_errc::max_nesting_depth_exceeded: + return "Data item nesting exceeds limit in options"; + case bson_errc::string_length_is_non_positive: + return "Request for the length of a string returned a non-positive result"; + case bson_errc::length_is_negative: + return "Request for the length of a binary returned a negative result"; + case bson_errc::unknown_type: + return "An unknown type was found in the stream"; + case bson_errc::number_too_large: + return "Number too large"; + case bson_errc::invalid_decimal128_string: + return "Invalid decimal128 string"; + case bson_errc::datetime_too_large: + return "datetime too large"; + case bson_errc::datetime_too_small: + return "datetime too small"; + case bson_errc::expected_bson_document: + return "Expected BSON document"; + case bson_errc::invalid_regex_string: + return "Invalid regex string"; + case bson_errc::size_mismatch: + return "Document or array size doesn't match bytes read"; + default: + return "Unknown BSON parser error"; + } + } +}; + +inline +const std::error_category& bson_error_category() +{ + static bson_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(bson_errc result) +{ + return std::error_code(static_cast<int>(result),bson_error_category()); +} + + +}} + +namespace std { + template<> + struct is_error_code_enum<jsoncons::bson::bson_errc> : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/bson/bson_oid.hpp b/include/jsoncons_ext/bson/bson_oid.hpp new file mode 100644 index 0000000..065d8da --- /dev/null +++ b/include/jsoncons_ext/bson/bson_oid.hpp @@ -0,0 +1,245 @@ +#ifndef JSONCONS_BSON_BSON_OID_HPP +#define JSONCONS_BSON_BSON_OID_HPP + +/* + * Implements class oid_t and non member function bson_oid_to_string + * + * Based on the libjson functions bson_oid_to_string + * and bson_oid_init_from_string_unsafe , available at + * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-oid.h + * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-oid.c + * +*/ + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdlib> +#include <cstring> +#include <ctype.h> +#include <system_error> +#include <algorithm> +#include <string> +#include <type_traits> +#include <array> +#include <jsoncons/config/jsoncons_config.hpp> + +namespace jsoncons { namespace bson { + + class oid_t + { + std::array<uint8_t,12> bytes_; + public: + using iterator = std::array<uint8_t,12>::iterator; + using const_iterator = std::array<uint8_t,12>::const_iterator; + + oid_t(const std::array<uint8_t,12>& bytes) + : bytes_(bytes) + { + } + oid_t(uint8_t data[12]) + { + std::memcpy(bytes_.data(),data,12); + } + + oid_t(const string_view& str) + { + for (std::size_t i = 0; i < bytes_.size(); i++) + { + bytes_[i] = ((parse_hex_char (str[2 * i]) << 4) | + (parse_hex_char (str[2 * i + 1]))); + } + } + + const uint8_t* data() const + { + return bytes_.data(); + } + + std::size_t size() const + { + return bytes_.size(); + } + + iterator begin() + { + return bytes_.begin(); + } + + iterator end() + { + return bytes_.end(); + } + + const_iterator begin() const + { + return bytes_.begin(); + } + + const_iterator end() const + { + return bytes_.end(); + } + + private: + + static uint8_t parse_hex_char (char hex) + { + switch (hex) { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + case 'a': + case 'A': + return 0xa; + case 'b': + case 'B': + return 0xb; + case 'c': + case 'C': + return 0xc; + case 'd': + case 'D': + return 0xd; + case 'e': + case 'E': + return 0xe; + case 'f': + case 'F': + return 0xf; + default: + return 0; + } + } + }; + + namespace detail { + + inline + const uint16_t* get_hex_char_pairs(std::true_type) // big endian + { + static const uint16_t hex_char_pairs[] = { + 12336, 12337, 12338, 12339, 12340, 12341, 12342, 12343, 12344, 12345, 12385, + 12386, 12387, 12388, 12389, 12390, 12592, 12593, 12594, 12595, 12596, 12597, + 12598, 12599, 12600, 12601, 12641, 12642, 12643, 12644, 12645, 12646, 12848, + 12849, 12850, 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12897, 12898, + 12899, 12900, 12901, 12902, 13104, 13105, 13106, 13107, 13108, 13109, 13110, + 13111, 13112, 13113, 13153, 13154, 13155, 13156, 13157, 13158, 13360, 13361, + 13362, 13363, 13364, 13365, 13366, 13367, 13368, 13369, 13409, 13410, 13411, + 13412, 13413, 13414, 13616, 13617, 13618, 13619, 13620, 13621, 13622, 13623, + 13624, 13625, 13665, 13666, 13667, 13668, 13669, 13670, 13872, 13873, 13874, + 13875, 13876, 13877, 13878, 13879, 13880, 13881, 13921, 13922, 13923, 13924, + 13925, 13926, 14128, 14129, 14130, 14131, 14132, 14133, 14134, 14135, 14136, + 14137, 14177, 14178, 14179, 14180, 14181, 14182, 14384, 14385, 14386, 14387, + 14388, 14389, 14390, 14391, 14392, 14393, 14433, 14434, 14435, 14436, 14437, + 14438, 14640, 14641, 14642, 14643, 14644, 14645, 14646, 14647, 14648, 14649, + 14689, 14690, 14691, 14692, 14693, 14694, 24880, 24881, 24882, 24883, 24884, + 24885, 24886, 24887, 24888, 24889, 24929, 24930, 24931, 24932, 24933, 24934, + 25136, 25137, 25138, 25139, 25140, 25141, 25142, 25143, 25144, 25145, 25185, + 25186, 25187, 25188, 25189, 25190, 25392, 25393, 25394, 25395, 25396, 25397, + 25398, 25399, 25400, 25401, 25441, 25442, 25443, 25444, 25445, 25446, 25648, + 25649, 25650, 25651, 25652, 25653, 25654, 25655, 25656, 25657, 25697, 25698, + 25699, 25700, 25701, 25702, 25904, 25905, 25906, 25907, 25908, 25909, 25910, + 25911, 25912, 25913, 25953, 25954, 25955, 25956, 25957, 25958, 26160, 26161, + 26162, 26163, 26164, 26165, 26166, 26167, 26168, 26169, 26209, 26210, 26211, + 26212, 26213, 26214}; + + return hex_char_pairs; + } + + inline + const uint16_t* get_hex_char_pairs(std::false_type) // little endian + { + static const uint16_t hex_char_pairs[] = { + 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, + 25136, 25392, 25648, 25904, 26160, 12337, 12593, 12849, 13105, 13361, 13617, + 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, 12338, + 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, + 25394, 25650, 25906, 26162, 12339, 12595, 12851, 13107, 13363, 13619, 13875, + 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, 12340, 12596, + 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, + 25652, 25908, 26164, 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, + 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, 12342, 12598, 12854, + 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, + 25910, 26166, 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, + 14647, 24887, 25143, 25399, 25655, 25911, 26167, 12344, 12600, 12856, 13112, + 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, + 26168, 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, + 24889, 25145, 25401, 25657, 25913, 26169, 12385, 12641, 12897, 13153, 13409, + 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, + 12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, + 25186, 25442, 25698, 25954, 26210, 12387, 12643, 12899, 13155, 13411, 13667, + 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, 12388, + 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, + 25444, 25700, 25956, 26212, 12389, 12645, 12901, 13157, 13413, 13669, 13925, + 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, 12390, 12646, + 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, + 25702, 25958, 26214}; + + return hex_char_pairs; + } + + inline + void init_hex_char_pairs(const oid_t& oid, uint16_t* data) + { + const uint8_t* bytes = oid.data(); + const uint16_t* gHexCharPairs = get_hex_char_pairs(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>()); + + data[0] = gHexCharPairs[bytes[0]]; + data[1] = gHexCharPairs[bytes[1]]; + data[2] = gHexCharPairs[bytes[2]]; + data[3] = gHexCharPairs[bytes[3]]; + data[4] = gHexCharPairs[bytes[4]]; + data[5] = gHexCharPairs[bytes[5]]; + data[6] = gHexCharPairs[bytes[6]]; + data[7] = gHexCharPairs[bytes[7]]; + data[8] = gHexCharPairs[bytes[8]]; + data[9] = gHexCharPairs[bytes[9]]; + data[10] = gHexCharPairs[bytes[10]]; + data[11] = gHexCharPairs[bytes[11]]; + } + + } // namsepace detail + + template <typename StringT> + inline + void to_string(const oid_t& oid, StringT& s) + { + s.resize(24); + detail::init_hex_char_pairs(oid, reinterpret_cast<uint16_t*>(&s[0])); + } + +} // namespace bson +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/bson_options.hpp b/include/jsoncons_ext/bson/bson_options.hpp new file mode 100644 index 0000000..0e4620e --- /dev/null +++ b/include/jsoncons_ext/bson/bson_options.hpp @@ -0,0 +1,75 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_OPTIONS_HPP +#define JSONCONS_BSON_BSON_OPTIONS_HPP + +#include <string> +#include <limits> // std::numeric_limits +#include <cwchar> +#include <jsoncons/json_exception.hpp> +#include <jsoncons_ext/bson/bson_type.hpp> + +namespace jsoncons { namespace bson { + +class bson_options; + +class bson_options_common +{ + friend class bson_options; + + int max_nesting_depth_; +protected: + virtual ~bson_options_common() = default; + + bson_options_common() + : max_nesting_depth_(1024) + { + } + + bson_options_common(const bson_options_common&) = default; + bson_options_common& operator=(const bson_options_common&) = default; + bson_options_common(bson_options_common&&) = default; + bson_options_common& operator=(bson_options_common&&) = default; +public: + int max_nesting_depth() const + { + return max_nesting_depth_; + } +}; + +class bson_decode_options : public virtual bson_options_common +{ + friend class bson_options; +public: + bson_decode_options() + { + } +}; + +class bson_encode_options : public virtual bson_options_common +{ + friend class bson_options; +public: + bson_encode_options() + { + } +}; + +class bson_options final : public bson_decode_options, public bson_encode_options +{ +public: + using bson_options_common::max_nesting_depth; + + bson_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } +}; + +}} +#endif diff --git a/include/jsoncons_ext/bson/bson_parser.hpp b/include/jsoncons_ext/bson/bson_parser.hpp new file mode 100644 index 0000000..2dc6e75 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_parser.hpp @@ -0,0 +1,645 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_PARSER_HPP +#define JSONCONS_BSON_BSON_PARSER_HPP + +#include <string> +#include <vector> +#include <memory> +#include <utility> // std::move +#include <jsoncons/json.hpp> +#include <jsoncons/source.hpp> +#include <jsoncons/json_visitor.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons_ext/bson/bson_type.hpp> +#include <jsoncons_ext/bson/bson_decimal128.hpp> +#include <jsoncons_ext/bson/bson_error.hpp> +#include <jsoncons_ext/bson/bson_options.hpp> +#include <jsoncons_ext/bson/bson_oid.hpp> + +namespace jsoncons { namespace bson { + +enum class parse_mode {root,accept,document,array,value}; + +struct parse_state +{ + parse_mode mode; + std::size_t length; + std::size_t pos; + uint8_t type; + std::size_t index; + + parse_state(parse_mode mode_, std::size_t length_, std::size_t pos_, uint8_t type_ = 0) noexcept + : mode(mode_), length(length_), pos(pos_), type(type_), index(0) + { + } + + parse_state(const parse_state&) = default; + parse_state(parse_state&&) = default; + parse_state& operator=(const parse_state&) = default; + parse_state& operator=(parse_state&&) = default; +}; + +template <class Source,class Allocator=std::allocator<char>> +class basic_bson_parser : public ser_context +{ + using char_type = char; + using char_traits_type = std::char_traits<char>; + using temp_allocator_type = Allocator; + using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<char_type>; + using byte_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<uint8_t>; + using parse_state_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_state>; + + Source source_; + bson_decode_options options_; + bool more_; + bool done_; + std::vector<uint8_t,byte_allocator_type> bytes_buffer_; + std::basic_string<char,std::char_traits<char>,char_allocator_type> text_buffer_; + std::vector<parse_state,parse_state_allocator_type> state_stack_; +public: + template <class Sourceable> + basic_bson_parser(Sourceable&& source, + const bson_decode_options& options = bson_decode_options(), + const Allocator alloc = Allocator()) + : source_(std::forward<Sourceable>(source)), + options_(options), + more_(true), + done_(false), + text_buffer_(alloc), + state_stack_(alloc) + { + state_stack_.emplace_back(parse_mode::root,0,0); + } + + void restart() + { + more_ = true; + } + + void reset() + { + more_ = true; + done_ = false; + bytes_buffer_.clear(); + text_buffer_.clear(); + state_stack_.clear(); + state_stack_.emplace_back(parse_mode::root,0,0); + } + + template <class Sourceable> + void reset(Sourceable&& source) + { + source_ = std::forward<Sourceable>(source); + reset(); + } + + bool done() const + { + return done_; + } + + bool stopped() const + { + return !more_; + } + + std::size_t line() const override + { + return 0; + } + + std::size_t column() const override + { + return source_.position(); + } + + void array_expected(json_visitor& visitor, std::error_code& ec) + { + if (state_stack_.size() == 2 && state_stack_.back().mode == parse_mode::document) + { + state_stack_.back().mode = parse_mode::array; + more_ = visitor.begin_array(semantic_tag::none, *this, ec); + } + } + + void parse(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(source_.is_error())) + { + ec = bson_errc::source_error; + more_ = false; + return; + } + + while (!done_ && more_) + { + switch (state_stack_.back().mode) + { + case parse_mode::root: + state_stack_.back().mode = parse_mode::accept; + begin_document(visitor, ec); + break; + case parse_mode::document: + { + uint8_t type; + std::size_t n = source_.read(&type, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + if (type != 0x00) + { + read_e_name(visitor,jsoncons::bson::bson_container_type::document,ec); + state_stack_.back().mode = parse_mode::value; + state_stack_.back().type = type; + } + else + { + end_document(visitor,ec); + } + break; + } + case parse_mode::array: + { + uint8_t type; + std::size_t n = source_.read(&type, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + if (type != 0x00) + { + read_e_name(visitor,jsoncons::bson::bson_container_type::array,ec); + read_value(visitor, type, ec); + } + else + { + end_array(visitor,ec); + } + break; + } + case parse_mode::value: + state_stack_.back().mode = parse_mode::document; + read_value(visitor,state_stack_.back().type,ec); + break; + case parse_mode::accept: + { + JSONCONS_ASSERT(state_stack_.size() == 1); + state_stack_.clear(); + more_ = false; + done_ = true; + visitor.flush(); + break; + } + } + } + } + +private: + + void begin_document(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(static_cast<int>(state_stack_.size()) > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + + uint8_t buf[sizeof(int32_t)]; + size_t n = source_.read(buf, sizeof(int32_t)); + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + auto length = binary::little_to_native<int32_t>(buf, sizeof(buf)); + + more_ = visitor.begin_object(semantic_tag::none, *this, ec); + state_stack_.emplace_back(parse_mode::document,length,n); + } + + void end_document(json_visitor& visitor, std::error_code& ec) + { + JSONCONS_ASSERT(state_stack_.size() >= 2); + + more_ = visitor.end_object(*this,ec); + if (JSONCONS_UNLIKELY(state_stack_.back().pos != state_stack_.back().length)) + { + ec = bson_errc::size_mismatch; + more_ = false; + return; + } + std::size_t pos = state_stack_.back().pos; + state_stack_.pop_back(); + state_stack_.back().pos += pos; + } + + void begin_array(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(static_cast<int>(state_stack_.size()) > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto length = binary::little_to_native<int32_t>(buf, sizeof(buf)); + + more_ = visitor.begin_array(semantic_tag::none, *this, ec); + if (ec) + { + return; + } + state_stack_.emplace_back(parse_mode::array, length, n); + } + + void end_array(json_visitor& visitor, std::error_code& ec) + { + JSONCONS_ASSERT(state_stack_.size() >= 2); + + more_ = visitor.end_array(*this, ec); + if (JSONCONS_UNLIKELY(state_stack_.back().pos != state_stack_.back().length)) + { + ec = bson_errc::size_mismatch; + more_ = false; + return; + } + std::size_t pos = state_stack_.back().pos; + state_stack_.pop_back(); + state_stack_.back().pos += pos; + } + + void read_e_name(json_visitor& visitor, jsoncons::bson::bson_container_type type, std::error_code& ec) + { + text_buffer_.clear(); + read_cstring(ec); + if (ec) + { + return; + } + if (type == jsoncons::bson::bson_container_type::document) + { + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc())) + { + ec = bson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.key(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), *this, ec); + } + } + + void read_value(json_visitor& visitor, uint8_t type, std::error_code& ec) + { + switch (type) + { + case jsoncons::bson::bson_type::double_type: + { + uint8_t buf[sizeof(double)]; + std::size_t n = source_.read(buf, sizeof(double)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(double))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + double res = binary::little_to_native<double>(buf, sizeof(buf)); + more_ = visitor.double_value(res, semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::symbol_type: + case jsoncons::bson::bson_type::min_key_type: + case jsoncons::bson::bson_type::max_key_type: + case jsoncons::bson::bson_type::string_type: + { + text_buffer_.clear(); + read_string(ec); + if (ec) + { + return; + } + auto result = unicode_traits::validate(text_buffer_.data(), text_buffer_.size()); + if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc())) + { + ec = bson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::javascript_type: + { + text_buffer_.clear(); + read_string(ec); + if (ec) + { + return; + } + auto result = unicode_traits::validate(text_buffer_.data(), text_buffer_.size()); + if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc())) + { + ec = bson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::code, *this, ec); + break; + } + case jsoncons::bson::bson_type::regex_type: + { + text_buffer_.clear(); + text_buffer_.push_back('/'); + read_cstring(ec); + if (ec) + { + return; + } + text_buffer_.push_back('/'); + read_cstring(ec); + if (ec) + { + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::regex, *this, ec); + break; + } + case jsoncons::bson::bson_type::document_type: + { + begin_document(visitor,ec); + break; + } + + case jsoncons::bson::bson_type::array_type: + { + begin_array(visitor,ec); + break; + } + case jsoncons::bson::bson_type::undefined_type: + { + more_ = visitor.null_value(semantic_tag::undefined, *this, ec); + break; + } + case jsoncons::bson::bson_type::null_type: + { + more_ = visitor.null_value(semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::bool_type: + { + uint8_t c; + std::size_t n = source_.read(&c, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + more_ = visitor.bool_value(c != 0, semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::int32_type: + { + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native<int32_t>(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::bson::bson_type::timestamp_type: + { + uint8_t buf[sizeof(uint64_t)]; + std::size_t n = source_.read(buf, sizeof(uint64_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(uint64_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native<uint64_t>(buf, sizeof(buf)); + more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::bson::bson_type::int64_type: + { + uint8_t buf[sizeof(int64_t)]; + std::size_t n = source_.read(buf, sizeof(int64_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int64_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native<int64_t>(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::bson::bson_type::datetime_type: + { + uint8_t buf[sizeof(int64_t)]; + std::size_t n = source_.read(buf, sizeof(int64_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int64_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native<int64_t>(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::epoch_milli, *this, ec); + break; + } + case jsoncons::bson::bson_type::binary_type: + { + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + const auto len = binary::little_to_native<int32_t>(buf, sizeof(buf)); + if (JSONCONS_UNLIKELY(len < 0)) + { + ec = bson_errc::length_is_negative; + more_ = false; + return; + } + uint8_t subtype; + n = source_.read(&subtype, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + bytes_buffer_.clear(); + n = source_reader<Source>::read(source_, bytes_buffer_, len); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != static_cast<std::size_t>(len))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + more_ = visitor.byte_string_value(bytes_buffer_, + subtype, + *this, + ec); + break; + } + case jsoncons::bson::bson_type::decimal128_type: + { + uint8_t buf[sizeof(uint64_t)*2]; + std::size_t n = source_.read(buf, sizeof(buf)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(buf))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + decimal128_t dec; + dec.low = binary::little_to_native<uint64_t>(buf, sizeof(uint64_t)); + dec.high = binary::little_to_native<uint64_t>(buf+sizeof(uint64_t), sizeof(uint64_t)); + + text_buffer_.clear(); + text_buffer_.resize(bson::decimal128_limits::buf_size); + auto r = bson::decimal128_to_chars(&text_buffer_[0], &text_buffer_[0]+text_buffer_.size(), dec); + more_ = visitor.string_value(string_view(text_buffer_.data(),static_cast<std::size_t>(r.ptr-text_buffer_.data())), semantic_tag::float128, *this, ec); + break; + } + case jsoncons::bson::bson_type::object_id_type: + { + uint8_t buf[12]; + std::size_t n = source_.read(buf, sizeof(buf)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(buf))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + oid_t oid(buf); + to_string(oid, text_buffer_); + + more_ = visitor.string_value(text_buffer_, semantic_tag::id, *this, ec); + break; + } + default: + { + ec = bson_errc::unknown_type; + more_ = false; + return; + } + } + } + + void read_cstring(std::error_code& ec) + { + uint8_t c = 0xff; + while (true) + { + std::size_t n = source_.read(&c, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + if (c == 0) + { + break; + } + text_buffer_.push_back(c); + } + } + + void read_string(std::error_code& ec) + { + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto len = binary::little_to_native<int32_t>(buf, sizeof(buf)); + if (JSONCONS_UNLIKELY(len < 1)) + { + ec = bson_errc::string_length_is_non_positive; + more_ = false; + return; + } + + std::size_t size = static_cast<std::size_t>(len) - static_cast<std::size_t>(1); + n = source_reader<Source>::read(source_, text_buffer_, size); + state_stack_.back().pos += n; + + if (JSONCONS_UNLIKELY(n != size)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + uint8_t c; + n = source_.read(&c, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + } +}; + +}} + +#endif diff --git a/include/jsoncons_ext/bson/bson_reader.hpp b/include/jsoncons_ext/bson/bson_reader.hpp new file mode 100644 index 0000000..0079cc9 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_reader.hpp @@ -0,0 +1,92 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_READER_HPP +#define JSONCONS_BSON_BSON_READER_HPP + +#include <string> +#include <vector> +#include <memory> +#include <utility> // std::move +#include <jsoncons/json.hpp> +#include <jsoncons/source.hpp> +#include <jsoncons/json_visitor.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons_ext/bson/bson_type.hpp> +#include <jsoncons_ext/bson/bson_error.hpp> +#include <jsoncons_ext/bson/bson_parser.hpp> + +namespace jsoncons { namespace bson { + +template <class Source,class Allocator=std::allocator<char>> +class basic_bson_reader +{ + basic_bson_parser<Source,Allocator> parser_; + json_visitor& visitor_; +public: + template <class Sourceable> + basic_bson_reader(Sourceable&& source, + json_visitor& visitor, + const Allocator alloc) + : basic_bson_reader(std::forward<Sourceable>(source), + visitor, + bson_decode_options(), + alloc) + { + } + + template <class Sourceable> + basic_bson_reader(Sourceable&& source, + json_visitor& visitor, + const bson_decode_options& options = bson_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward<Sourceable>(source), options, alloc), + visitor_(visitor) + { + } + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line(),column())); + } + } + + void read(std::error_code& ec) + { + parser_.reset(); + parser_.parse(visitor_, ec); + if (ec) + { + return; + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } +}; + +using bson_stream_reader = basic_bson_reader<jsoncons::binary_stream_source>; +using bson_bytes_reader = basic_bson_reader<jsoncons::bytes_source>; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_reader") typedef bson_stream_reader bson_reader; +JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_reader") typedef bson_bytes_reader bson_buffer_reader; +#endif + +}} + +#endif diff --git a/include/jsoncons_ext/bson/bson_type.hpp b/include/jsoncons_ext/bson/bson_type.hpp new file mode 100644 index 0000000..cf12344 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_type.hpp @@ -0,0 +1,44 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_TYPE_HPP +#define JSONCONS_BSON_BSON_TYPE_HPP + +#include <string> +#include <memory> +#include <jsoncons/config/jsoncons_config.hpp> + +namespace jsoncons { namespace bson { + + namespace bson_type + { + const uint8_t double_type = 0x01; + const uint8_t string_type = 0x02; // UTF-8 string + const uint8_t document_type = 0x03; + const uint8_t array_type = 0x04; + const uint8_t binary_type = 0x05; + const uint8_t undefined_type = 0x06; // map to null + const uint8_t object_id_type = 0x07; + const uint8_t bool_type = 0x08; + const uint8_t datetime_type = 0x09; + const uint8_t null_type = 0x0a; + const uint8_t regex_type = 0x0b; + const uint8_t javascript_type = 0x0d; + const uint8_t symbol_type = 0x0e; // deprecated, mapped to string + const uint8_t javascript_with_scope_type = 0x0f; // unsupported + const uint8_t int32_type = 0x10; + const uint8_t timestamp_type = 0x11; // MongoDB internal Timestamp, uint64 + const uint8_t int64_type = 0x12; + const uint8_t decimal128_type = 0x13; + const uint8_t min_key_type = 0xff; + const uint8_t max_key_type = 0x7f; + } + + enum class bson_container_type {document, array}; + +}} + +#endif diff --git a/include/jsoncons_ext/bson/decode_bson.hpp b/include/jsoncons_ext/bson/decode_bson.hpp new file mode 100644 index 0000000..ad352f6 --- /dev/null +++ b/include/jsoncons_ext/bson/decode_bson.hpp @@ -0,0 +1,201 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_DECODE_BSON_HPP +#define JSONCONS_BSON_DECODE_BSON_HPP + +#include <string> +#include <vector> +#include <memory> +#include <type_traits> // std::enable_if +#include <istream> // std::basic_istream +#include <jsoncons/json.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons_ext/bson/bson_reader.hpp> +#include <jsoncons_ext/bson/bson_cursor.hpp> + +namespace jsoncons { +namespace bson { + + template<class T, class Source> + typename std::enable_if<type_traits::is_basic_json<T>::value && + type_traits::is_byte_sequence<Source>::value,T>::type + decode_bson(const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + jsoncons::json_decoder<T> decoder; + auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder); + basic_bson_reader<jsoncons::bytes_source> reader(v, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template<class T, class Source> + typename std::enable_if<!type_traits::is_basic_json<T>::value && + type_traits::is_byte_sequence<Source>::value,T>::type + decode_bson(const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor<bytes_source> cursor(v, options); + json_decoder<basic_json<char,sorted_policy>> decoder{}; + + std::error_code ec; + T val = decode_traits<T,char>::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template<class T> + typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type + decode_bson(std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + jsoncons::json_decoder<T> decoder; + auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder); + bson_stream_reader reader(is, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template<class T> + typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type + decode_bson(std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor<binary_stream_source> cursor(is, options); + json_decoder<basic_json<char,sorted_policy>> decoder{}; + + std::error_code ec; + T val = decode_traits<T,char>::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template<class T, class InputIt> + typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type + decode_bson(InputIt first, InputIt last, + const bson_decode_options& options = bson_decode_options()) + { + jsoncons::json_decoder<T> decoder; + auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder); + basic_bson_reader<binary_iterator_source<InputIt>> reader(binary_iterator_source<InputIt>(first, last), adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template<class T, class InputIt> + typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type + decode_bson(InputIt first, InputIt last, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor<binary_iterator_source<InputIt>> cursor(binary_iterator_source<InputIt>(first, last), options); + json_decoder<basic_json<char,sorted_policy>> decoder{}; + + std::error_code ec; + T val = decode_traits<T,char>::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + // With leading allocator parameter + + template<class T, class Source, class TempAllocator> + typename std::enable_if<type_traits::is_basic_json<T>::value && + type_traits::is_byte_sequence<Source>::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + json_decoder<T,TempAllocator> decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder); + basic_bson_reader<jsoncons::bytes_source,TempAllocator> reader(v, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template<class T, class Source, class TempAllocator> + typename std::enable_if<!type_traits::is_basic_json<T>::value && + type_traits::is_byte_sequence<Source>::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor<bytes_source,TempAllocator> cursor(v, options, temp_alloc); + json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits<T,char>::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template<class T,class TempAllocator> + typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + json_decoder<T,TempAllocator> decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder); + basic_bson_reader<jsoncons::binary_stream_source,TempAllocator> reader(is, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template<class T,class TempAllocator> + typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor<binary_stream_source,TempAllocator> cursor(is, options, temp_alloc); + json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits<T,char>::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // bson +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/encode_bson.hpp b/include/jsoncons_ext/bson/encode_bson.hpp new file mode 100644 index 0000000..55f8cf5 --- /dev/null +++ b/include/jsoncons_ext/bson/encode_bson.hpp @@ -0,0 +1,144 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_ENCODE_BSON_HPP +#define JSONCONS_BSON_ENCODE_BSON_HPP + +#include <string> +#include <vector> +#include <memory> +#include <type_traits> // std::enable_if +#include <istream> // std::basic_istream +#include <jsoncons/json.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons_ext/bson/bson_encoder.hpp> +#include <jsoncons_ext/bson/bson_reader.hpp> + +namespace jsoncons { +namespace bson { + + template<class T, class Container> + typename std::enable_if<type_traits::is_basic_json<T>::value && + type_traits::is_back_insertable_byte_container<Container>::value,void>::type + encode_bson(const T& j, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + basic_bson_encoder<jsoncons::bytes_sink<Container>> encoder(v, options); + auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder); + j.dump(adaptor); + } + + template<class T, class Container> + typename std::enable_if<!type_traits::is_basic_json<T>::value && + type_traits::is_back_insertable_byte_container<Container>::value,void>::type + encode_bson(const T& val, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + basic_bson_encoder<jsoncons::bytes_sink<Container>> encoder(v, options); + std::error_code ec; + encode_traits<T,char>::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template<class T> + typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type + encode_bson(const T& j, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + bson_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder); + j.dump(adaptor); + } + + template<class T> + typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type + encode_bson(const T& val, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + bson_stream_encoder encoder(os, options); + std::error_code ec; + encode_traits<T,char>::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // with temp_allocator_rag + + template<class T, class Container, class TempAllocator> + typename std::enable_if<type_traits::is_basic_json<T>::value && + type_traits::is_back_insertable_byte_container<Container>::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + basic_bson_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder); + j.dump(adaptor); + } + + template<class T, class Container, class TempAllocator> + typename std::enable_if<!type_traits::is_basic_json<T>::value && + type_traits::is_back_insertable_byte_container<Container>::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + basic_bson_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc); + std::error_code ec; + encode_traits<T,char>::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template<class T,class TempAllocator> + typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + basic_bson_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder); + j.dump(adaptor); + } + + template<class T,class TempAllocator> + typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + basic_bson_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc); + std::error_code ec; + encode_traits<T,char>::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + +} // bson +} // jsoncons + +#endif |