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 |