From 8e681d1e570b2274a799521aad83292ed719e172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Sun, 9 Jul 2017 16:38:24 +0200 Subject: [PATCH] move lexer/parser outside of basic_json --- src/json.hpp | 19012 +++++++++++++++++++++++++------------------------ 1 file changed, 9525 insertions(+), 9487 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index fd63fe132b..8459c1a621 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1529,11843 +1529,11881 @@ struct input_adapter_factory } }; -} // namespace detail - - -/// namespace to hold default `to_json` / `from_json` functions -namespace -{ -constexpr const auto& to_json = detail::static_const::value; -constexpr const auto& from_json = detail::static_const::value; -} - +////////////////////// +// lexer and parser // +////////////////////// /*! -@brief default JSONSerializer template argument +@brief lexical analysis -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) -for serialization. +This class organizes the lexical analysis during JSON deserialization. */ -template -struct adl_serializer +template +class lexer { - /*! - @brief convert a JSON value to any value type + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for + ///actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual + ///value + value_float, ///< an floating point number -- use get_number_float() for + ///actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for + ///diagnostics) + }; - @param[in] j JSON value to read from - @param[in,out] val value to write to - */ - template - static void from_json(BasicJsonType&& j, ValueType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), val))) - { - ::nlohmann::from_json(std::forward(j), val); + /// return name of values of type token_type (only used for errors) + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + default: + { + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE + } + } } - /*! - @brief convert any value type to a JSON value + explicit lexer(detail::input_adapter_t adapter) + : ia(adapter), decimal_point_char(get_decimal_point()) {} - This function is usually called by the constructors of the @ref basic_json - class. + // delete because of pointer members + lexer(const lexer&) = delete; + lexer& operator=(lexer&) = delete; - @param[in,out] j JSON value to write to - @param[in] val value to read from - */ - template - static void to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + static char get_decimal_point() noexcept { - ::nlohmann::to_json(j, std::forward(val)); + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0]; } -}; - -/*! -@brief JSON Pointer - -A JSON pointer defines a string syntax for identifying a specific value -within a JSON document. It can be used with functions `at` and -`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - -@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - -@since version 2.0.0 -*/ -class json_pointer -{ - /// allow basic_json to access private members - NLOHMANN_BASIC_JSON_TPL_DECLARATION - friend class basic_json; - - public: - /*! - @brief create JSON pointer - - Create a JSON pointer according to the syntax described in - [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - - @param[in] s string representing the JSON pointer; if omitted, the - empty string is assumed which references the whole JSON - value - - @throw parse_error.107 if the given JSON pointer @a s is nonempty and - does not begin with a slash (`/`); see example below - - @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s - is not followed by `0` (representing `~`) or `1` (representing `/`); - see example below - - @liveexample{The example shows the construction several valid JSON - pointers as well as the exceptional behavior.,json_pointer} - @since version 2.0.0 - */ - explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} + ///////////////////// + // scan functions + ///////////////////// /*! - @brief return a string representation of the JSON pointer - - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode - - @return a string representation of the JSON pointer - - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} - - @since version 2.0.0 - */ - std::string to_string() const noexcept - { - return std::accumulate(reference_tokens.begin(), reference_tokens.end(), - std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + escape(b); - }); - } + @brief get codepoint from 4 hex characters following `\u` - /// @copydoc to_string() - operator std::string() const + @return codepoint or -1 in case of an error (e.g. EOF or non-hex + character) + */ + int get_codepoint() { - return to_string(); - } + // this function only makes sense after reading `\u` + assert(current == 'u'); + int codepoint = 0; - private: - /*! - @brief remove and return last reference pointer - @throw out_of_range.405 if JSON pointer has no parent - */ - std::string pop_back() - { - if (is_root()) + // byte 1: \uXxxx + switch (get()) { - JSON_THROW( - detail::out_of_range::create(405, "JSON pointer has no parent")); + case '0': + break; + case '1': + codepoint += 0x1000; + break; + case '2': + codepoint += 0x2000; + break; + case '3': + codepoint += 0x3000; + break; + case '4': + codepoint += 0x4000; + break; + case '5': + codepoint += 0x5000; + break; + case '6': + codepoint += 0x6000; + break; + case '7': + codepoint += 0x7000; + break; + case '8': + codepoint += 0x8000; + break; + case '9': + codepoint += 0x9000; + break; + case 'A': + case 'a': + codepoint += 0xa000; + break; + case 'B': + case 'b': + codepoint += 0xb000; + break; + case 'C': + case 'c': + codepoint += 0xc000; + break; + case 'D': + case 'd': + codepoint += 0xd000; + break; + case 'E': + case 'e': + codepoint += 0xe000; + break; + case 'F': + case 'f': + codepoint += 0xf000; + break; + default: + return -1; } - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; - } + // byte 2: \uxXxx + switch (get()) + { + case '0': + break; + case '1': + codepoint += 0x0100; + break; + case '2': + codepoint += 0x0200; + break; + case '3': + codepoint += 0x0300; + break; + case '4': + codepoint += 0x0400; + break; + case '5': + codepoint += 0x0500; + break; + case '6': + codepoint += 0x0600; + break; + case '7': + codepoint += 0x0700; + break; + case '8': + codepoint += 0x0800; + break; + case '9': + codepoint += 0x0900; + break; + case 'A': + case 'a': + codepoint += 0x0a00; + break; + case 'B': + case 'b': + codepoint += 0x0b00; + break; + case 'C': + case 'c': + codepoint += 0x0c00; + break; + case 'D': + case 'd': + codepoint += 0x0d00; + break; + case 'E': + case 'e': + codepoint += 0x0e00; + break; + case 'F': + case 'f': + codepoint += 0x0f00; + break; + default: + return -1; + } - /// return whether pointer points to the root document - bool is_root() const - { - return reference_tokens.empty(); - } + // byte 3: \uxxXx + switch (get()) + { + case '0': + break; + case '1': + codepoint += 0x0010; + break; + case '2': + codepoint += 0x0020; + break; + case '3': + codepoint += 0x0030; + break; + case '4': + codepoint += 0x0040; + break; + case '5': + codepoint += 0x0050; + break; + case '6': + codepoint += 0x0060; + break; + case '7': + codepoint += 0x0070; + break; + case '8': + codepoint += 0x0080; + break; + case '9': + codepoint += 0x0090; + break; + case 'A': + case 'a': + codepoint += 0x00a0; + break; + case 'B': + case 'b': + codepoint += 0x00b0; + break; + case 'C': + case 'c': + codepoint += 0x00c0; + break; + case 'D': + case 'd': + codepoint += 0x00d0; + break; + case 'E': + case 'e': + codepoint += 0x00e0; + break; + case 'F': + case 'f': + codepoint += 0x00f0; + break; + default: + return -1; + } - json_pointer top() const - { - if (is_root()) + // byte 4: \uxxxX + switch (get()) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + case '0': + break; + case '1': + codepoint += 0x0001; + break; + case '2': + codepoint += 0x0002; + break; + case '3': + codepoint += 0x0003; + break; + case '4': + codepoint += 0x0004; + break; + case '5': + codepoint += 0x0005; + break; + case '6': + codepoint += 0x0006; + break; + case '7': + codepoint += 0x0007; + break; + case '8': + codepoint += 0x0008; + break; + case '9': + codepoint += 0x0009; + break; + case 'A': + case 'a': + codepoint += 0x000a; + break; + case 'B': + case 'b': + codepoint += 0x000b; + break; + case 'C': + case 'c': + codepoint += 0x000c; + break; + case 'D': + case 'd': + codepoint += 0x000d; + break; + case 'E': + case 'e': + codepoint += 0x000e; + break; + case 'F': + case 'f': + codepoint += 0x000f; + break; + default: + return -1; } - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; + return codepoint; } - /*! - @brief create and return a reference to the pointed to value + @brief scan a string literal - @complexity Linear in the number of reference tokens. + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer yytext. Then the + function returns successfully, yytext is null-terminated and yylen + contains the number of bytes in the string. - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const; + @return token_type::value_string if string could be successfully + scanned, token_type::parse_error otherwise - /*! - @brief return a reference to the pointed to value + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset yytext (ignore opening quote) + reset(); - @note This version does not throw if a value is not present, but tries - to create nested values instead. For instance, calling this function - with pointer `"/this/that"` on a null value is equivalent to calling - `operator[]("this").operator[]("that")` on that value, effectively - changing the null value to an object. + // we entered the function by reading an open quote + assert(current == '\"'); - @param[in] ptr a JSON value + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } - @return reference to the JSON value pointed to by the JSON pointer + // closing quote + case '\"': + { + // terminate yytext + add('\0'); + --yylen; + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + int codepoint; + int codepoint1 = get_codepoint(); - @complexity Linear in the length of the JSON pointer. + if (JSON_UNLIKELY(codepoint1 == -1)) + { + error_message = + "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const; - - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const; + // check if code point is a high surrogate + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_LIKELY(get() == '\\' and get() == 'u')) + { + const int codepoint2 = get_codepoint(); - /*! - @brief return a const reference to the pointed to value + if (JSON_UNLIKELY(codepoint2 == -1)) + { + error_message = + "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - @param[in] ptr a JSON value + // check if codepoint2 is a low surrogate + if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + { + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must " + "be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must " + "be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must " + "follow U+D800..U+DBFF"; + return token_type::parse_error; + } - @return const reference to the JSON value pointed to by the JSON - pointer + // only work with first code point + codepoint = codepoint1; + } - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; + // result of the above calculation yields a proper codepoint + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; + // translate code point to bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(codepoint); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(0xC0 | (codepoint >> 6)); + add(0x80 | (codepoint & 0x3F)); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(0xE0 | (codepoint >> 12)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(0xF0 | (codepoint >> 18)); + add(0x80 | ((codepoint >> 12) & 0x3F)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); + } - /*! - @brief split the string input to reference tokens + break; + } - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector split(const std::string& reference_string) - { - std::vector result; + break; + } - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } + // invalid control characters + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + { + error_message = "invalid string: control character must be escaped"; + return token_type::parse_error; + } - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') - { - JSON_THROW(detail::parse_error::create( - 107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); - } + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5d: + case 0x5e: + case 0x5f: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + { + add(current); + break; + } - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc8: + case 0xc9: + case 0xca: + case 0xcb: + case 0xcc: + case 0xcd: + case 0xce: + case 0xcf: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xd3: + case 0xd4: + case 0xd5: + case 0xd6: + case 0xd7: + case 0xd8: + case 0xd9: + case 0xda: + case 0xdb: + case 0xdc: + case 0xdd: + case 0xde: + case 0xdf: + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } - // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) - { - assert(reference_token[pos] == '~'); + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } - // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xe0: { - JSON_THROW(detail::parse_error::create( - 108, 0, "escape character '~' must be followed with '0' or '1'")); + add(current); + get(); + if (JSON_LIKELY(0xa0 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } + } + + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; } - } - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + case 0xe8: + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xee: + case 0xef: + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } + } - return result; - } + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } - /*! - @brief replace all occurrences of a substring by another string + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xed: + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0x9f)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } + } - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xf0: + { + add(current); + get(); + if (JSON_LIKELY(0x90 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } + } + } - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, - const std::string& t) - { - assert(not f.empty()); + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } - for (size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ) - ; + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xf1: + case 0xf2: + case 0xf3: + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } + } + } + + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xf4: + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0x8f)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + get(); + if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + { + add(current); + continue; + } + } + } + + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } } - /// escape tilde and slash - static std::string escape(std::string s) + static void strtof(float& f, const char* str, char** endptr) noexcept { - // escape "~"" to "~0" and "/" to "~1" - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; + f = std::strtof(str, endptr); } - /// unescape tilde and slash - static void unescape(std::string& s) + static void strtof(double& f, const char* str, char** endptr) noexcept { - // first transform any occurrence of the sequence '~1' to '/' - replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' - replace_substring(s, "~0", "~"); + f = std::strtod(str, endptr); + } + + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); } /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to + @brief scan a number literal - @note Empty objects or arrays are flattened to `null`. - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - static void flatten(const std::string& reference_string, - const NLOHMANN_BASIC_JSON_TPL& value, - NLOHMANN_BASIC_JSON_TPL& result); + This function scans a string according to Sect. 6 of RFC 7159. - /*! - @param[in] value flattened JSON + The function is realized with a deterministic finite state machine + derived from the grammar described in RFC 7159. Starting in state + "init", the input is read and used to determined the next state. Only + state "done" accepts the number. State "error" is a trap state to model + errors. In the table below, "anything" means any character but the ones + listed before. - @return unflattened JSON + state | 0 | 1-9 | e E | + | - | . | + anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | + [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | + [error] + zero | done | done | exponent | done | done | decimal1 | + done + any1 | any1 | any1 | exponent | done | done | decimal1 | + done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | + [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | + done + exponent | any2 | any2 | [error] | sign | sign | [error] | + [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | + [error] + any2 | any2 | any2 | done | done | done | done | + done - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - static NLOHMANN_BASIC_JSON_TPL - unflatten(const NLOHMANN_BASIC_JSON_TPL& value); + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. - friend bool operator==(json_pointer const& lhs, - json_pointer const& rhs) noexcept; + During scanning, the read bytes are stored in yytext. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. - friend bool operator!=(json_pointer const& lhs, - json_pointer const& rhs) noexcept; + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise - /// the reference tokens - std::vector reference_tokens; -}; + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() + { + // reset yytext to store the number's bytes + reset(); -/*! -@brief a class to store JSON values + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; -@tparam ObjectType type for JSON objects (`std::map` by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (`std::string` by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (`bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c -`uint64_t` by default; will be used in @ref number_unsigned_t) -@tparam NumberFloatType type for JSON floating-point numbers (`double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (`std::allocator` by -default) -@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` -and `from_json()` (@ref adl_serializer by default) + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null - value. - - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): - JSON values have - [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the - class has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](http://en.cppreference.com/w/cpp/concept/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. + case '0': + { + add(current); + goto scan_number_zero; + } -@invariant The member variables @a m_value and @a m_type have the following -relationship: -- If `m_type == value_t::object`, then `m_value.object != nullptr`. -- If `m_type == value_t::array`, then `m_value.array != nullptr`. -- If `m_type == value_t::string`, then `m_value.string != nullptr`. -The invariants are checked by member function assert_invariant(). + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } -@internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 -@endinternal + default: + { + // all other characters are rejected outside scan_number() + assert(false); // LCOV_EXCL_LINE + } + } -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } -@since version 1.0.0 + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } -@nosubgrouping -*/ -NLOHMANN_BASIC_JSON_TPL_DECLARATION -class basic_json -{ - private: - template friend struct detail::external_constructor; - /// workaround type for MSVC - using basic_json_t = NLOHMANN_BASIC_JSON_TPL; - friend ::nlohmann::json_pointer; + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } - public: - using value_t = detail::value_t; - // forward declarations - template class iter_impl; - template class json_reverse_iterator; - using json_pointer = ::nlohmann::json_pointer; - template - using json_serializer = JSONSerializer; +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } - //////////////// - // exceptions // - //////////////// + default: + { + goto scan_number_done; + } + } - /// @name exceptions - /// Classes to implement user-defined exceptions. - /// @{ +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - /// @copydoc detail::exception - using exception = detail::exception; - /// @copydoc detail::parse_error - using parse_error = detail::parse_error; - /// @copydoc detail::invalid_iterator - using invalid_iterator = detail::invalid_iterator; - /// @copydoc detail::type_error - using type_error = detail::type_error; - /// @copydoc detail::out_of_range - using out_of_range = detail::out_of_range; - /// @copydoc detail::other_error - using other_error = detail::other_error; + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } - /// @} + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + default: + { + goto scan_number_done; + } + } - ///////////////////// - // container types // - ///////////////////// +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } - /// @name container types - /// The canonic container types to use @ref basic_json like any other STL - /// container. - /// @{ + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } - /// the type of elements in a basic_json container - using value_type = basic_json; +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } - /// the type of an element reference - using reference = value_type&; - /// the type of an element const reference - using const_reference = const value_type&; + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; + default: + { + goto scan_number_done; + } + } - /// the allocator type - using allocator_type = AllocatorType; +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } - /// the type of an element pointer - using pointer = typename std::allocator_traits::pointer; - /// the type of an element const pointer - using const_pointer = typename std::allocator_traits::const_pointer; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } - /// an iterator for a basic_json container - using iterator = iter_impl; - /// a const iterator for a basic_json container - using const_iterator = iter_impl; - /// a reverse iterator for a basic_json container - using reverse_iterator = json_reverse_iterator; - /// a const reverse iterator for a basic_json container - using const_reverse_iterator = json_reverse_iterator; + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } - /// @} +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + goto scan_number_done; + } + } + +scan_number_done: + // unget the character after the number (we only read it to know + // that we are done scanning a number) + --chars_read; + next_unget = true; + + // terminate token + add('\0'); + --yylen; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + char* endptr = nullptr; + errno = 0; + const auto x = std::strtoull(yytext.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == yytext.data() + yylen); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + char* endptr = nullptr; + errno = 0; + const auto x = std::strtoll(yytext.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == yytext.data() + yylen); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + // this code is reached if we parse a floating-point number or if + // an integer conversion above failed + strtof(value_float, yytext.data(), nullptr); + return token_type::value_float; + } /*! - @brief returns the allocator associated with the container + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success */ - static allocator_type get_allocator() + token_type scan_literal(const char* literal_text, const size_t length, + token_type return_type) { - return allocator_type(); + assert(current == literal_text[0]); + for (size_t i = 1; i < length; ++i) + { + if (JSON_UNLIKELY(get() != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; } - /*! - @brief returns version information on the library + ///////////////////// + // input management + ///////////////////// - This function returns a JSON object with information about the library, - including the version number and information on the platform and compiler. + /// reset yytext + void reset() noexcept + { + yylen = 0; + start_pos = chars_read - 1; + } - @return JSON object holding version information - key | description - ----------- | --------------- - `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). - `copyright` | The copyright line for the library as string. - `name` | The name of the library as string. - `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. - `url` | The URL of the project as string. - `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + /// get a character from the input + int get() + { + ++chars_read; + return next_unget ? (next_unget = false, current) + : (current = ia->get_character()); + } - @liveexample{The following code shows an example output of the `meta()` - function.,meta} + /// add a character to yytext + void add(int c) + { + // resize yytext if necessary; this condition is deemed unlikely, + // because we start with a 1024-byte buffer + if (JSON_UNLIKELY((yylen + 1 > yytext.capacity()))) + { + yytext.resize(2 * yytext.capacity(), '\0'); + } + assert(yylen < yytext.size()); + yytext[yylen++] = static_cast(c); + } - @complexity Constant. + public: + ///////////////////// + // value getters + ///////////////////// - @since 2.1.0 - */ - static basic_json meta() + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept { - basic_json result; + return value_integer; + } - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; - result["name"] = "JSON for Modern C++"; - result["url"] = "https://github.com/nlohmann/json"; - result["version"] = - { - {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1} - }; + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } -#ifdef _WIN32 - result["platform"] = "win32"; -#elif defined __linux__ - result["platform"] = "linux"; -#elif defined __APPLE__ - result["platform"] = "apple"; -#elif defined __unix__ - result["platform"] = "unix"; -#else - result["platform"] = "unknown"; -#endif - -#if defined(__clang__) - result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; -#elif defined(__ICC) || defined(__INTEL_COMPILER) - result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; -#elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; -#elif defined(__HP_cc) || defined(__HP_aCC) - result["compiler"] = "hp" -#elif defined(__IBMCPP__) - result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; -#elif defined(_MSC_VER) - result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; -#elif defined(__PGI) - result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; -#elif defined(__SUNPRO_CC) - result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; -#else - result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; -#endif - -#ifdef __cplusplus - result["compiler"]["c++"] = std::to_string(__cplusplus); -#else - result["compiler"]["c++"] = "unknown"; -#endif - return result; + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; } + /// return string value + const std::string get_string() + { + // yytext cannot be returned as char*, because it may contain a + // null byte (parsed as "\u0000") + return std::string(yytext.data(), yylen); + } - /////////////////////////// - // JSON value data types // - /////////////////////////// + ///////////////////// + // diagnostics + ///////////////////// - /// @name JSON value data types - /// The data types to store a JSON value. These types are derived from - /// the template arguments passed to class @ref basic_json. - /// @{ + /// return position of last read token + constexpr size_t get_position() const noexcept + { + return chars_read; + } - /*! - @brief a type for an object + /// return the last read token (for errors only) + std::string get_token_string() const + { + // get the raw byte sequence of the last token + std::string s = ia->read(start_pos, chars_read - start_pos); - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. + // escape control characters + std::string result; + for (auto c : s) + { + if (c == '\0' or c == std::char_traits::eof()) + { + // ignore EOF + continue; + } + else if ('\x00' <= c and c <= '\x1f') + { + // escape control characters + std::stringstream ss; + ss << "(c) << ">"; + result += ss.str(); + } + else + { + // add character as is + result.append(1, c); + } + } - To store objects in C++, a type is defined by the template parameters - described below. + return result; + } - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). - The comparison function `std::less` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) + /// return syntax error message + constexpr const char* get_error_message() const noexcept + { + return error_message; + } - #### Default type + ///////////////////// + // actual scanner + ///////////////////// - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default - value for @a object_t is: + token_type scan() + { + // read next character and ignore whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or + current == '\r'); + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less, // key_compare - std::allocator> // allocator_type - > - @endcode + private: + /// input adapter + detail::input_adapter_t ia = nullptr; - #### Behavior + /// the current character + int current = std::char_traits::eof(); - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: + /// whether get() should return the last character again + bool next_unget = false; - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on - the name-value mappings. - - When the names within an object are not unique, later stored name/value - pairs overwrite previously stored name/value pairs, leaving the used - names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will - be treated as equal and both stored as `{"key": 1}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. - For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored - and serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. + /// the number of characters read + size_t chars_read = 0; + /// the start position of the current token + size_t start_pos = 0; - #### Limits + /// buffer for variable-length tokens (numbers, strings) + std::vector yytext = std::vector(1024, '\0'); + /// current index in yytext + size_t yylen = 0; - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. + /// a description of occurred lexer errors + const char* error_message = ""; - In this class, the object's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON object. + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; - #### Storage + /// the decimal point + const char decimal_point_char = '.'; +}; - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be - dereferenced. +/*! +@brief syntax analysis - @sa @ref array_t -- type for an array value +This class implements a recursive decent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; - @since version 1.0.0 + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; - @note The order name/value pairs are added to the object is *not* - preserved by the library. Therefore, iterating an object may return - name/value pairs in a different order than they were originally stored. In - fact, keys will be traversed in alphabetical order as `std::map` with - `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. - */ - using object_t = ObjectType, - AllocatorType>>; + public: + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; - /*! - @brief a type for an array + using parser_callback_t = + std::function; - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. + /// a parser reading from an input adapter + explicit parser(detail::input_adapter_t adapter, + const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(adapter) {} - To store objects in C++, a type is defined by the template parameters - explained below. + /*! + @brief public parser interface - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + @param[in] strict whether to expect the last token to be EOF + @return parsed JSON value - #### Default type + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + BasicJsonType parse(const bool strict = true) + { + // read first token + get_token(); - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: + BasicJsonType result = parse_internal(true); + result.assert_invariant(); - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator // allocator_type - > - @endcode + if (strict) + { + get_token(); + expect(token_type::end_of_input); + } - #### Limits + // return parser result and replace it with null in case the + // top-level value was discarded by the callback function + return result.is_discarded() ? BasicJsonType() : std::move(result); + } - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. + /*! + @brief public accept interface - In this class, the array's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON array. + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + // read first token + get_token(); - #### Storage + if (not accept_internal()) + { + return false; + } - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. + if (strict and get_token() != token_type::end_of_input) + { + return false; + } - @sa @ref object_t -- type for an object value + return true; + } - @since version 1.0.0 + private: + /*! + @brief the actual parser + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails */ - using array_t = ArrayType>; + BasicJsonType parse_internal(bool keep) + { + auto result = BasicJsonType(value_t::discarded); - /*! - @brief a type for a string + switch (last_token) + { + case token_type::begin_object: + { + if (keep and (not callback or + ((keep = callback(depth++, parse_event_t::object_start, + result)) != 0))) + { + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; + } - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. + // read next token + get_token(); - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into - byte-sized characters during deserialization. + // closing } -> we are done + if (last_token == token_type::end_object) + { + if (keep and callback and + not callback(--depth, parse_event_t::object_end, result)) + { + result = BasicJsonType(value_t::discarded); + } + return result; + } - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. + // parse values + while (true) + { + // store key + expect(token_type::value_string); + const auto key = m_lexer.get_string(); - #### Default type + bool keep_tag = false; + if (keep) + { + if (callback) + { + BasicJsonType k(key); + keep_tag = callback(depth, parse_event_t::key, k); + } + else + { + keep_tag = true; + } + } - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: + // parse separator (:) + get_token(); + expect(token_type::name_separator); - @code {.cpp} - std::string - @endcode + // parse and add value + get_token(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) + { + result[key] = std::move(value); + } - #### Encoding + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } - Strings are stored in UTF-8 encoding. Therefore, functions like - `std::string::size()` or `std::string::length()` return the number of - bytes in the string rather than the number of characters or glyphs. + // closing } + expect(token_type::end_object); + break; + } - #### String comparison - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. + if (keep and callback and + not callback(--depth, parse_event_t::object_end, result)) + { + result = BasicJsonType(value_t::discarded); + } - This implementation is interoperable as it does compare strings code unit - by code unit. + return result; + } - #### Storage + case token_type::begin_array: + { + if (keep and (not callback or + ((keep = callback(depth++, parse_event_t::array_start, + result)) != 0))) + { + // explicitly set result to object to cope with [] + result.m_type = value_t::array; + result.m_value = value_t::array; + } - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. + // read next token + get_token(); - @since version 1.0.0 - */ - using string_t = StringType; + // closing ] -> we are done + if (last_token == token_type::end_array) + { + if (callback and + not callback(--depth, parse_event_t::array_end, result)) + { + result = BasicJsonType(value_t::discarded); + } + return result; + } - /*! - @brief a type for a boolean + // parse values + while (true) + { + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(std::move(value)); + } - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. + // closing ] + expect(token_type::end_array); + break; + } - #### Default type + if (keep and callback and + not callback(--depth, parse_event_t::array_end, result)) + { + result = BasicJsonType(value_t::discarded); + } - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: + return result; + } - @code {.cpp} - bool - @endcode + case token_type::literal_null: + { + result.m_type = value_t::null; + break; + } - #### Storage + case token_type::value_string: + { + result = BasicJsonType(m_lexer.get_string()); + break; + } - Boolean values are stored directly inside a @ref basic_json type. + case token_type::literal_true: + { + result.m_type = value_t::boolean; + result.m_value = true; + break; + } - @since version 1.0.0 - */ - using boolean_t = BooleanType; + case token_type::literal_false: + { + result.m_type = value_t::boolean; + result.m_value = false; + break; + } - /*! - @brief a type for a number (integer) + case token_type::value_unsigned: + { + result.m_type = value_t::number_unsigned; + result.m_value = m_lexer.get_number_unsigned(); + break; + } - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. + case token_type::value_integer: + { + result.m_type = value_t::number_integer; + result.m_value = m_lexer.get_number_integer(); + break; + } - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. + case token_type::value_float: + { + result.m_type = value_t::number_float; + result.m_value = m_lexer.get_number_float(); - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. + // throw in case of infinity or NAN + if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) + { + JSON_THROW(out_of_range::create(406, "number overflow parsing '" + + m_lexer.get_token_string() + + "'")); + } - #### Default type + break; + } - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + expect(token_type::uninitialized); + break; // LCOV_EXCL_LINE + } - @code {.cpp} - int64_t - @endcode + default: + { + // the last token was unexpected; we expected a value + expect(token_type::literal_or_value); + break; // LCOV_EXCL_LINE + } + } - #### Default behavior + if (keep and callback and + not callback(depth, parse_event_t::value, result)) + { + result = BasicJsonType(value_t::discarded); + } + return result; + } - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. + /*! + @brief the acutal acceptor - #### Limits + @invariant 1. The last token is not yet processed. Therefore, the + caller of this function must make sure a token has + been read. + 2. When this function returns, the last token is processed. + That is, the last read character was already considered. - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. + This invariant makes sure that no token needs to be "unput". + */ + bool accept_internal() + { + switch (last_token) + { + case token_type::begin_object: + { + // read next token + get_token(); - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a - constructor. During deserialization, too large or small integer numbers - will be automatically be stored as @ref number_unsigned_t or @ref - number_float_t. + // closing } -> we are done + if (last_token == token_type::end_object) + { + return true; + } - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. + // parse values + while (true) + { + // parse key + if (last_token != token_type::value_string) + { + return false; + } - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. + // parse separator (:) + get_token(); + if (last_token != token_type::name_separator) + { + return false; + } - #### Storage + // parse value + get_token(); + if (not accept_internal()) + { + return false; + } - Integer number values are stored directly inside a @ref basic_json type. + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } - @sa @ref number_float_t -- type for number values (floating-point) + // closing } + if (last_token != token_type::end_object) + { + return false; + } - @sa @ref number_unsigned_t -- type for number values (unsigned integer) + return true; + } + } - @since version 1.0.0 - */ - using number_integer_t = NumberIntegerType; + case token_type::begin_array: + { + // read next token + get_token(); - /*! - @brief a type for a number (unsigned) + // closing ] -> we are done + if (last_token == token_type::end_array) + { + return true; + } - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. + // parse values + while (true) + { + // parse value + if (not accept_internal()) + { + return false; + } - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } - To store unsigned integer numbers in C++, a type is defined by the - template parameter @a NumberUnsignedType which chooses the type to use. + // closing ] + if (last_token != token_type::end_array) + { + return false; + } - #### Default type + return true; + } + } - With the default values for @a NumberUnsignedType (`uint64_t`), the - default value for @a number_unsigned_t is: + case token_type::literal_false: + case token_type::literal_null: + case token_type::literal_true: + case token_type::value_float: + case token_type::value_integer: + case token_type::value_string: + case token_type::value_unsigned: + { + return true; + } - @code {.cpp} - uint64_t - @endcode + default: + { + // the last token was unexpected + return false; + } + } + } - #### Default behavior + /// get next token from lexer + token_type get_token() + { + return (last_token = m_lexer.scan()); + } - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. + /*! + @throw parse_error.101 if expected token did not occur + */ + void expect(token_type t) + { + if (JSON_UNLIKELY(t != last_token)) + { + errored = true; + expected = t; + throw_exception(); + } + } - #### Limits + [[noreturn]] void throw_exception() const + { + std::string error_msg = "syntax error - "; + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += + "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. + if (expected != token_type::uninitialized) + { + error_msg += + "; expected " + std::string(lexer_t::token_type_name(expected)); + } - When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer - number that can be stored is `0`. Integer numbers that are out of range - will yield over/underflow when used in a constructor. During - deserialization, too large or small integer numbers will be automatically - be stored as @ref number_integer_t or @ref number_float_t. + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); + } - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. + private: + /// current level of recursion + int depth = 0; + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether a syntax error occurred + bool errored = false; + /// possible reason for the syntax error + token_type expected = token_type::uninitialized; +}; +} // namespace detail - As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], - this class's integer type is interoperable. +/// namespace to hold default `to_json` / `from_json` functions +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +constexpr const auto& from_json = detail::static_const::value; +} - #### Storage - Integer number values are stored directly inside a @ref basic_json type. +/*! +@brief default JSONSerializer template argument - @sa @ref number_float_t -- type for number values (floating-point) - @sa @ref number_integer_t -- type for number values (integer) +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type - @since version 2.0.0 + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to */ - using number_unsigned_t = NumberUnsignedType; + template + static void from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + { + ::nlohmann::from_json(std::forward(j), val); + } /*! - @brief a type for a number (floating-point) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. + @brief convert any value type to a JSON value - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. + This function is usually called by the constructors of the @ref basic_json + class. - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static void to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; - #### Default type +/*! +@brief JSON Pointer - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - @code {.cpp} - double - @endcode +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - #### Default behavior +@since version 2.0.0 +*/ +class json_pointer +{ + /// allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, - the value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. + public: + /*! + @brief create JSON pointer - #### Limits + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations - > that expect no more precision or range than these provide, in the sense - > that implementations will approximate JSON numbers within the expected - > precision. + @param[in] s string representing the JSON pointer; if omitted, the + empty string is assumed which references the whole JSON + value - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. + @throw parse_error.107 if the given JSON pointer @a s is nonempty and + does not begin with a slash (`/`); see example below - #### Storage + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s + is not followed by `0` (representing `~`) or `1` (representing `/`); + see example below - Floating-point number values are stored directly inside a @ref basic_json - type. + @liveexample{The example shows the construction several valid JSON + pointers as well as the exceptional behavior.,json_pointer} - @sa @ref number_integer_t -- type for number values (integer) + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} - @sa @ref number_unsigned_t -- type for number values (unsigned integer) + /*! + @brief return a string representation of the JSON pointer - @since version 1.0.0 - */ - using number_float_t = NumberFloatType; + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode - /// @} + @return a string representation of the JSON pointer - private: + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} - /// helper for exception-safe object creation - template - static T* create(Args&& ... args) + @since version 2.0.0 + */ + std::string to_string() const noexcept { - AllocatorType alloc; - auto deleter = [&](T * object) + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) { - alloc.deallocate(object, 1); - }; - std::unique_ptr object(alloc.allocate(1), deleter); - alloc.construct(object.get(), std::forward(args)...); - assert(object != nullptr); - return object.release(); + return a + "/" + escape(b); + }); } - //////////////////////// - // JSON value storage // - //////////////////////// + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + private: /*! - @brief a JSON value - - The actual storage for a JSON value of the @ref basic_json class. This - union combines the different storage types for the JSON value types - defined in @ref value_t. - - JSON type | value_t type | used type - --------- | --------------- | ------------------------ - object | object | pointer to @ref object_t - array | array | pointer to @ref array_t - string | string | pointer to @ref string_t - boolean | boolean | @ref boolean_t - number | number_integer | @ref number_integer_t - number | number_unsigned | @ref number_unsigned_t - number | number_float | @ref number_float_t - null | null | *no value is stored* + @brief remove and return last reference pointer + @throw out_of_range.405 if JSON pointer has no parent + */ + std::string pop_back() + { + if (is_root()) + { + JSON_THROW( + detail::out_of_range::create(405, "JSON pointer has no parent")); + } - @note Variable-length types (objects, arrays, and strings) are stored as - pointers. The size of the union should not exceed 64 bits if the default - value types are used. + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } - @since version 1.0.0 - */ - union json_value + /// return whether pointer points to the root document + bool is_root() const { - /// object (stored with pointer to save storage) - object_t* object; - /// array (stored with pointer to save storage) - array_t* array; - /// string (stored with pointer to save storage) - string_t* string; - /// boolean - boolean_t boolean; - /// number (integer) - number_integer_t number_integer; - /// number (unsigned integer) - number_unsigned_t number_unsigned; - /// number (floating-point) - number_float_t number_float; + return reference_tokens.empty(); + } - /// default constructor (for null values) - json_value() = default; - /// constructor for booleans - json_value(boolean_t v) noexcept : boolean(v) {} - /// constructor for numbers (integer) - json_value(number_integer_t v) noexcept : number_integer(v) {} - /// constructor for numbers (unsigned) - json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} - /// constructor for numbers (floating-point) - json_value(number_float_t v) noexcept : number_float(v) {} - /// constructor for empty values of a given type - json_value(value_t t) + json_pointer top() const + { + if (is_root()) { - switch (t) - { - case value_t::object: - { - object = create(); - break; - } + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } - case value_t::array: - { - array = create(); - break; - } + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } - case value_t::string: - { - string = create(""); - break; - } - case value_t::boolean: - { - boolean = boolean_t(false); - break; - } + /*! + @brief create and return a reference to the pointed to value - case value_t::number_integer: - { - number_integer = number_integer_t(0); - break; - } + @complexity Linear in the number of reference tokens. - case value_t::number_unsigned: - { - number_unsigned = number_unsigned_t(0); - break; - } + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const; - case value_t::number_float: - { - number_float = number_float_t(0.0); - break; - } + /*! + @brief return a reference to the pointed to value - case value_t::null: - { - break; - } + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. - default: - { - if (JSON_UNLIKELY(t == value_t::null)) - { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE - } - break; - } - } - } + @param[in] ptr a JSON value - /// constructor for strings - json_value(const string_t& value) - { - string = create(value); - } + @return reference to the JSON value pointed to by the JSON pointer - /// constructor for objects - json_value(const object_t& value) - { - object = create(value); - } + @complexity Linear in the length of the JSON pointer. - /// constructor for arrays - json_value(const array_t& value) - { - array = create(value); - } - }; + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const; /*! - @brief checks the class invariants - - This function asserts the class invariants. It needs to be called at the - end of every constructor to make sure that created objects respect the - invariant. Furthermore, it has to be called each time the type of a JSON - value is changed, because the invariant expresses a relationship between - @a m_type and @a m_value. - */ - void assert_invariant() const - { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); - } - - public: - ////////////////////////// - // JSON parser callback // - ////////////////////////// + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const; /*! - @brief JSON callback events + @brief return a const reference to the pointed to value - This enumeration lists the parser events that can trigger calling a - callback function of type @ref parser_callback_t during parsing. + @param[in] ptr a JSON value - @image html callback_events.png "Example when certain parse events are triggered" + @return const reference to the JSON value pointed to by the JSON + pointer - @since version 1.0.0 - */ - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; /*! - @brief per-element parser callback type - - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const CharT, const parser_callback_t), - it is called on certain events (passed as @ref parse_event_t via parameter - @a event) with a set recursion depth @a depth and context JSON value - @a parsed. The return value of the callback function is a boolean - indicating whether the element that emitted the callback shall be kept or - not. - - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded - parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded - parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array - parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + /*! + @brief split the string input to reference tokens - @image html callback_events.png "Example when certain parse events are triggered" + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced - with `null`. This case happens if the top-level element is skipped. + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } - @param[in] depth the depth of the recursion during parsing + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + JSON_THROW(detail::parse_error::create( + 107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + JSON_THROW(detail::parse_error::create( + 108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } - @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const CharT, const parser_callback_t) for examples + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } - @since version 1.0.0 - */ - using parser_callback_t = std::function; + return result; + } + /*! + @brief replace all occurrences of a substring by another string - ////////////////// - // constructors // - ////////////////// - - /// @name constructors and destructors - /// Constructors of class @ref basic_json, copy/move constructor, copy - /// assignment, static functions creating objects, and the destructor. - /// @{ - - /*! - @brief create an empty value with a given type - - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** - @param[in] v the type of the value to create + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); - @complexity Constant. + for (size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ) + ; + } - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } - @since version 1.0.0 - */ - basic_json(const value_t v) - : m_type(v), m_value(v) + /// unescape tilde and slash + static void unescape(std::string& s) { - assert_invariant(); + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); } /*! - @brief create a null object + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to - Create a `null` JSON value. It either takes a null pointer as parameter - (explicitly creating `null`) or no parameter (implicitly creating `null`). - The passed null pointer itself is not read -- it is only used to choose - the right constructor. + @note Empty objects or arrays are flattened to `null`. + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + static void flatten(const std::string& reference_string, + const NLOHMANN_BASIC_JSON_TPL& value, + NLOHMANN_BASIC_JSON_TPL& result); - @complexity Constant. + /*! + @param[in] value flattened JSON - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. + @return unflattened JSON - @liveexample{The following code shows the constructor with and without a - null pointer parameter.,basic_json__nullptr_t} + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + static NLOHMANN_BASIC_JSON_TPL + unflatten(const NLOHMANN_BASIC_JSON_TPL& value); - @since version 1.0.0 - */ - basic_json(std::nullptr_t = nullptr) noexcept - : basic_json(value_t::null) - { - assert_invariant(); - } + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept; - /*! - @brief create a JSON value + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept; - This is a "catch all" constructor for all compatible JSON types; that is, - types for which a `to_json()` method exsits. The constructor forwards the - parameter @a val to that method (to `json_serializer::to_json` method - with `U = uncvref_t`, to be exact). + /// the reference tokens + std::vector reference_tokens; +}; - Template type @a CompatibleType includes, but is not limited to, the - following types: - - **arrays**: @ref array_t and all kinds of compatible containers such as - `std::vector`, `std::deque`, `std::list`, `std::forward_list`, - `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and - `unordered_multiset` with a `value_type` from which a @ref basic_json - value can be constructed. - - **objects**: @ref object_t and all kinds of compatible associative - containers such as `std::map`, `std::unordered_map`, `std::multimap`, - and `std::unordered_multimap` with a `key_type` compatible to - @ref string_t and a `value_type` from which a @ref basic_json value can - be constructed. - - **strings**: @ref string_t, string literals, and all compatible string - containers can be used. - - **numbers**: @ref number_integer_t, @ref number_unsigned_t, - @ref number_float_t, and all convertible number types such as `int`, - `size_t`, `int64_t`, `float` or `double` can be used. - - **boolean**: @ref boolean_t / `bool` can be used. +/*! +@brief a class to store JSON values - See the examples below. +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) - @tparam CompatibleType a type such that: - - @a CompatibleType is not derived from `std::istream`, - - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move - constructors), - - @a CompatibleType is not a @ref basic_json nested type (e.g., - @ref json_pointer, @ref iterator, etc ...) - - @ref @ref json_serializer has a - `to_json(basic_json_t&, CompatibleType&&)` method +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): + JSON values have + [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](http://en.cppreference.com/w/cpp/concept/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. - @tparam U = `uncvref_t` +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). - @param[in] val the value to be forwarded +@internal +@note ObjectType trick from http://stackoverflow.com/a/9860911 +@endinternal - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) - @throw what `json_serializer::to_json()` throws +@since version 1.0.0 - @liveexample{The following code shows the constructor with several - compatible types.,basic_json__CompatibleType} +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + friend ::nlohmann::detail::parser; + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; - @since version 2.1.0 - */ - template, - detail::enable_if_t::value and - not std::is_same::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json::value, - int> = 0> - basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer::to_json( - std::declval(), std::forward(val)))) - { - JSONSerializer::to_json(*this, std::forward(val)); - assert_invariant(); - } + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer; + using parser = ::nlohmann::detail::parser; - /*! - @brief create a container (array or object) from an initializer list + public: + using value_t = detail::value_t; + // forward declarations + template class iter_impl; + template class json_reverse_iterator; + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: - - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are - treated as keys and the second elements are as values. - 3. In all other cases, an array is created. - - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: - - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them - as an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. - - With the rules described above, the following JSON values cannot be - expressed by an initializer list: - - the empty array (`[]`): use @ref array(std::initializer_list) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(std::initializer_list) with the same initializer list - in this case + //////////////// + // exceptions // + //////////////// - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ - @param[in] init initializer list with JSON values + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(std::initializer_list) and - @ref object(std::initializer_list). + /// @} - @param[in] manual_type internal parameter; when @a type_deduction is set - to `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect - @throw type_error.301 if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string. In this case, the constructor could not - create an object. If @a type_deduction would have be `true`, an array - would have been created. See @ref object(std::initializer_list) - for an example. + ///////////////////// + // container types // + ///////////////////// - @complexity Linear in the size of the initializer list @a init. + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ - @liveexample{The example below shows how JSON values are created from - initializer lists.,basic_json__list_init_t} + /// the type of elements in a basic_json container + using value_type = basic_json; - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; - @since version 1.0.0 - */ - basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) - { - // check if each element is an array with two elements whose first - // element is a string - bool is_an_object = std::all_of(init.begin(), init.end(), - [](const basic_json & element) - { - return element.is_array() and element.size() == 2 and element[0].is_string(); - }); + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; - // adjust type if type deduction is not wanted - if (not type_deduction) - { - // if array is wanted, do not create an object though possible - if (manual_type == value_t::array) - { - is_an_object = false; - } + /// the allocator type + using allocator_type = AllocatorType; - // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_an_object) - { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); - } - } + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; - if (is_an_object) - { - // the initializer list is a list of pairs -> create object - m_type = value_t::object; - m_value = value_t::object; + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; - std::for_each(init.begin(), init.end(), [this](const basic_json & element) - { - m_value.object->emplace(*(element[0].m_value.string), element[1]); - }); - } - else - { - // the initializer list describes an array -> create array - m_type = value_t::array; - m_value.array = create(init); - } + /// @} - assert_invariant(); - } /*! - @brief explicitly create an array from an initializer list - - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. - - @note This function is only needed to express two edge cases that cannot - be realized with the initializer list constructor (@ref - basic_json(std::initializer_list, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object - - @param[in] init initializer list with JSON values to create an array from - (optional) - - @return JSON array value - - @complexity Linear in the size of @a init. - - @liveexample{The following code shows an example for the `array` - function.,array} - - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list - - @since version 1.0.0 + @brief returns the allocator associated with the container */ - static basic_json array(std::initializer_list init = - std::initializer_list()) + static allocator_type get_allocator() { - return basic_json(init, false, value_t::array); + return allocator_type(); } /*! - @brief explicitly create an object from an initializer list - - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. - - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(std::initializer_list), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(std::initializer_list, bool, value_t). - - @param[in] init initializer list to create an object from (optional) - - @return JSON object value + @brief returns version information on the library - @throw type_error.301 if @a init is not a list of pairs whose first - elements are strings. In this case, no object can be created. When such a - value is passed to @ref basic_json(std::initializer_list, bool, value_t), - an array would have been created from the passed initializer list @a init. - See example below. + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. - @complexity Linear in the size of @a init. + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). - @liveexample{The following code shows an example for the `object` - function.,object} + @liveexample{The following code shows an example output of the `meta()` + function.,meta} - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list + @complexity Constant. - @since version 1.0.0 + @since 2.1.0 */ - static basic_json object(std::initializer_list init = - std::initializer_list()) + static basic_json meta() { - return basic_json(init, false, value_t::object); - } - - /*! - @brief construct an array with count copies of given value - - Constructs a JSON array value by creating @a cnt copies of a passed value. - In case @a cnt is `0`, an empty array is created. As postcondition, - `std::distance(begin(),end()) == cnt` holds. + basic_json result; - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy + result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"] = + { + {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1} + }; - @complexity Linear in @a cnt. +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} +#if defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif - @since version 1.0.0 - */ - basic_json(size_type cnt, const basic_json& val) - : m_type(value_t::array) - { - m_value.array = create(cnt, val); - assert_invariant(); +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; } - /*! - @brief construct a JSON container given an iterator range - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of primitive types (number, boolean, or string), @a first must - be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, invalid_iterator.204 is thrown. - - In case of structured types (array, object), the constructor behaves as - similar versions for `std::vector`. - - In case of a null type, invalid_iterator.206 is thrown. + /////////////////////////// + // JSON value data types // + /////////////////////////// - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) + /*! + @brief a type for an object - @pre Iterators @a first and @a last must be initialized. **This - precondition is enforced with an assertion.** + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. - @pre Range `[first, last)` is valid. Usually, this precondition cannot be - checked efficiently. Only certain edge cases are detected; see the - description of the exceptions below. + To store objects in C++, a type is defined by the template parameters + described below. - @throw invalid_iterator.201 if iterators @a first and @a last are not - compatible (i.e., do not belong to the same JSON value). In this case, - the range `[first, last)` is undefined. - @throw invalid_iterator.204 if iterators @a first and @a last belong to a - primitive type (number, boolean, or string), but @a first does not point - to the first element any more. In this case, the range `[first, last)` is - undefined. See example code below. - @throw invalid_iterator.206 if iterators @a first and @a last belong to a - null value. In this case, the range `[first, last)` is undefined. + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) - @complexity Linear in distance between @a first and @a last. + #### Default type - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: - @since version 1.0.0 - */ - template::value or - std::is_same::value, int>::type = 0> - basic_json(InputIT first, InputIT last) - { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode - // make sure iterator fits the current value - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); - } + #### Behavior - // copy type from first iterator - m_type = first.m_object->m_type; + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: - // check if iterator range is complete for primitive values - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } - break; - } + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, later stored name/value + pairs overwrite previously stored name/value pairs, leaving the used + names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will + be treated as equal and both stored as `{"key": 1}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. - default: - { - break; - } - } + #### Limits - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. - case value_t::number_unsigned: - { - m_value.number_unsigned = first.m_object->m_value.number_unsigned; - break; - } + In this class, the object's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. - case value_t::number_float: - { - m_value.number_float = first.m_object->m_value.number_float; - break; - } + #### Storage - case value_t::boolean: - { - m_value.boolean = first.m_object->m_value.boolean; - break; - } + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. - case value_t::string: - { - m_value = *first.m_object->m_value.string; - break; - } + @sa @ref array_t -- type for an array value - case value_t::object: - { - m_value.object = create(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } + @since version 1.0.0 - case value_t::array: - { - m_value.array = create(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType, + AllocatorType>>; - default: - { - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - first.m_object->type_name())); - } - } + /*! + @brief a type for an array - assert_invariant(); - } + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + To store objects in C++, a type is defined by the template parameters + explained below. - /////////////////////////////////////// - // other constructors and destructor // - /////////////////////////////////////// + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - /*! - @brief copy constructor + #### Default type - Creates a copy of a given JSON value. + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: - @param[in] other the JSON value to copy + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode - @complexity Linear in the size of @a other. + #### Limits - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} + In this class, the array's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value @since version 1.0.0 */ - basic_json(const basic_json& other) - : m_type(other.m_type) - { - // check of passed value is valid - other.assert_invariant(); - - switch (m_type) - { - case value_t::object: - { - m_value = *other.m_value.object; - break; - } + using array_t = ArrayType>; - case value_t::array: - { - m_value = *other.m_value.array; - break; - } + /*! + @brief a type for a string - case value_t::string: - { - m_value = *other.m_value.string; - break; - } + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. - case value_t::boolean: - { - m_value = other.m_value.boolean; - break; - } + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. - case value_t::number_integer: - { - m_value = other.m_value.number_integer; - break; - } + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. - case value_t::number_unsigned: - { - m_value = other.m_value.number_unsigned; - break; - } + #### Default type - case value_t::number_float: - { - m_value = other.m_value.number_float; - break; - } + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: - default: - { - break; - } - } + @code {.cpp} + std::string + @endcode - assert_invariant(); - } + #### Encoding - /*! - @brief move constructor + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. + #### String comparison - @param[in,out] other value to move to this object + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. - @post @a other is a JSON null value + This implementation is interoperable as it does compare strings code unit + by code unit. - @complexity Constant. + #### Storage - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. @since version 1.0.0 */ - basic_json(basic_json&& other) noexcept - : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) - { - // check that passed value is valid - other.assert_invariant(); + using string_t = StringType; - // invalidate payload - other.m_type = value_t::null; - other.m_value = {}; + /*! + @brief a type for a boolean - assert_invariant(); - } + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. - /*! - @brief copy assignment + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, - and the swap() member function. + #### Default type - @param[in] other value to copy from + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: - @complexity Linear. + @code {.cpp} + bool + @endcode - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. + #### Storage - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} + Boolean values are stored directly inside a @ref basic_json type. @since version 1.0.0 */ - reference& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - // check that passed value is valid - other.assert_invariant(); - - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - - assert_invariant(); - return *this; - } + using boolean_t = BooleanType; /*! - @brief destructor + @brief a type for a number (integer) - Destroys the JSON value and frees all allocated memory. + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - @complexity Linear. + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. - @since version 1.0.0 - */ - ~basic_json() - { - assert_invariant(); + #### Default type - switch (m_type) - { - case value_t::object: - { - AllocatorType alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: - case value_t::array: - { - AllocatorType alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } + @code {.cpp} + int64_t + @endcode - case value_t::string: - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } + #### Default behavior - default: - { - // all other types need no specific destructor - break; - } - } - } + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. - /// @} - - public: - /////////////////////// - // object inspection // - /////////////////////// + #### Limits - /// @name object inspection - /// Functions to inspect the type of a JSON value. - /// @{ + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. - /*! - @brief serialization + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. - Serialization function for JSON values. The function tries to mimic - Python's `json.dumps()` function, and currently supports its @a indent - parameter. + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. - @param[in] indent If indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of - `0` will only insert newlines. `-1` (the default) selects the most compact - representation. - @param[in] indent_char The character to use for indentation if @a indent is - greater than `0`. The default is ` ` (space). + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. - @return string containing the serialization of the JSON value + #### Storage - @complexity Linear. + Integer number values are stored directly inside a @ref basic_json type. - @liveexample{The following example shows the effect of different @a indent - parameters to the result of the serialization.,dump} + @sa @ref number_float_t -- type for number values (floating-point) - @see https://docs.python.org/2/library/json.html#json.dump + @sa @ref number_unsigned_t -- type for number values (unsigned integer) - @since version 1.0.0; indentation character added in version 3.0.0 + @since version 1.0.0 */ - string_t dump(const int indent = -1, const char indent_char = ' ') const - { - string_t result; - serializer s(output_adapter::create(result), indent_char); + using number_integer_t = NumberIntegerType; - if (indent >= 0) - { - s.dump(*this, true, static_cast(indent)); - } - else - { - s.dump(*this, false, 0); - } + /*! + @brief a type for a number (unsigned) - return result; - } + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - /*! - @brief return the type of the JSON value (explicit) + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. - Return the type of the JSON value as a value from the @ref value_t - enumeration. + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. - @return the type of the JSON value + #### Default type - @complexity Constant. + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @code {.cpp} + uint64_t + @endcode - @liveexample{The following code exemplifies `type()` for all JSON - types.,type} + #### Default behavior - @since version 1.0.0 - */ - constexpr value_t type() const noexcept - { - return m_type; - } + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. - /*! - @brief return whether type is primitive + #### Limits - This function returns true iff the JSON type is primitive (string, number, - boolean, or null). + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. - @complexity Constant. + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. - @liveexample{The following code exemplifies `is_primitive()` for all JSON - types.,is_primitive} + #### Storage - @sa @ref is_structured() -- returns whether JSON value is structured - @sa @ref is_null() -- returns whether JSON value is `null` - @sa @ref is_string() -- returns whether JSON value is a string - @sa @ref is_boolean() -- returns whether JSON value is a boolean - @sa @ref is_number() -- returns whether JSON value is a number + Integer number values are stored directly inside a @ref basic_json type. - @since version 1.0.0 + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 */ - constexpr bool is_primitive() const noexcept - { - return is_null() or is_string() or is_boolean() or is_number(); - } + using number_unsigned_t = NumberUnsignedType; /*! - @brief return whether type is structured + @brief a type for a number (floating-point) - This function returns true iff the JSON type is structured (array or - object). + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - @return `true` if type is structured (array or object), `false` otherwise. + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. - @complexity Constant. + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + #### Default type - @liveexample{The following code exemplifies `is_structured()` for all JSON - types.,is_structured} + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: - @sa @ref is_primitive() -- returns whether value is primitive - @sa @ref is_array() -- returns whether value is an array - @sa @ref is_object() -- returns whether value is an object + @code {.cpp} + double + @endcode - @since version 1.0.0 - */ - constexpr bool is_structured() const noexcept - { - return is_array() or is_object(); - } + #### Default behavior - /*! - @brief return whether value is null + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. - This function returns true iff the JSON value is null. + #### Limits - @return `true` if type is null, `false` otherwise. + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. - @complexity Constant. + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + #### Storage - @liveexample{The following code exemplifies `is_null()` for all JSON - types.,is_null} + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) @since version 1.0.0 */ - constexpr bool is_null() const noexcept + using number_float_t = NumberFloatType; + + /// @} + + private: + + /// helper for exception-safe object creation + template + static T* create(Args&& ... args) { - return m_type == value_t::null; + AllocatorType alloc; + auto deleter = [&](T * object) + { + alloc.deallocate(object, 1); + }; + std::unique_ptr object(alloc.allocate(1), deleter); + alloc.construct(object.get(), std::forward(args)...); + assert(object != nullptr); + return object.release(); } - /*! - @brief return whether value is a boolean + //////////////////////// + // JSON value storage // + //////////////////////// - This function returns true iff the JSON value is a boolean. + /*! + @brief a JSON value - @return `true` if type is boolean, `false` otherwise. - - @complexity Constant. + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* - @liveexample{The following code exemplifies `is_boolean()` for all JSON - types.,is_boolean} + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. @since version 1.0.0 */ - constexpr bool is_boolean() const noexcept + union json_value { - return m_type == value_t::boolean; - } - - /*! - @brief return whether value is a number - - This function returns true iff the JSON value is a number. This includes - both integer and floating-point values. + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; - @return `true` if type is number (regardless whether integer, unsigned - integer or floating-type), `false` otherwise. + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } - @complexity Constant. + case value_t::array: + { + array = create(); + break; + } - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + case value_t::string: + { + string = create(""); + break; + } - @liveexample{The following code exemplifies `is_number()` for all JSON - types.,is_number} + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } - @since version 1.0.0 - */ - constexpr bool is_number() const noexcept - { - return is_number_integer() or is_number_float(); - } + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } - /*! - @brief return whether value is an integer number + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } - This function returns true iff the JSON value is an integer or unsigned - integer number. This excludes floating-point values. + case value_t::null: + { + break; + } - @return `true` if type is an integer or unsigned integer number, `false` - otherwise. + default: + { + if (JSON_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + } + break; + } + } + } - @complexity Constant. + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } - @liveexample{The following code exemplifies `is_number_integer()` for all - JSON types.,is_number_integer} + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + }; - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number + /*! + @brief checks the class invariants - @since version 1.0.0 + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. */ - constexpr bool is_number_integer() const noexcept + void assert_invariant() const { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); } - /*! - @brief return whether value is an unsigned integer number - - This function returns true iff the JSON value is an unsigned integer - number. This excludes floating-point and (signed) integer values. + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// - @return `true` if type is an unsigned integer number, `false` otherwise. + using parse_event_t = typename parser::parse_event_t; - @complexity Constant. + /*! + @brief per-element parser callback type - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse(std::istream&, const + parser_callback_t) or @ref parse(const CharT, const parser_callback_t), + it is called on certain events (passed as @ref parse_event_t via parameter + @a event) with a set recursion depth @a depth and context JSON value + @a parsed. The return value of the callback function is a boolean + indicating whether the element that emitted the callback shall be kept or + not. - @liveexample{The following code exemplifies `is_number_unsigned()` for all - JSON types.,is_number_unsigned} + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_float() -- check if value is a floating-point number + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value - @since version 2.0.0 - */ - constexpr bool is_number_unsigned() const noexcept - { - return m_type == value_t::number_unsigned; - } + @image html callback_events.png "Example when certain parse events are triggered" - /*! - @brief return whether value is a floating-point number + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: - This function returns true iff the JSON value is a floating-point number. - This excludes integer and unsigned integer values. + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. - @return `true` if type is a floating-point number, `false` otherwise. + @param[in] depth the depth of the recursion during parsing - @complexity Constant. + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events - @liveexample{The following code exemplifies `is_number_float()` for all - JSON types.,is_number_float} + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number + @sa @ref parse(std::istream&, parser_callback_t) or + @ref parse(const CharT, const parser_callback_t) for examples @since version 1.0.0 */ - constexpr bool is_number_float() const noexcept - { - return m_type == value_t::number_float; - } + using parser_callback_t = typename parser::parser_callback_t; - /*! - @brief return whether value is an object - This function returns true iff the JSON value is an object. + ////////////////// + // constructors // + ////////////////// - @return `true` if type is object, `false` otherwise. + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ - @complexity Constant. + /*! + @brief create an empty value with a given type - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: - @liveexample{The following code exemplifies `is_object()` for all JSON - types.,is_object} + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @param[in] v the type of the value to create + + @complexity Constant. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} @since version 1.0.0 */ - constexpr bool is_object() const noexcept + basic_json(const value_t v) + : m_type(v), m_value(v) { - return m_type == value_t::object; + assert_invariant(); } /*! - @brief return whether value is an array - - This function returns true iff the JSON value is an array. + @brief create a null object - @return `true` if type is array, `false` otherwise. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. @complexity Constant. - @exceptionsafety No-throw guarantee: this member function never throws + @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code exemplifies `is_array()` for all JSON - types.,is_array} + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - constexpr bool is_array() const noexcept + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) { - return m_type == value_t::array; + assert_invariant(); } /*! - @brief return whether value is a string + @brief create a JSON value - This function returns true iff the JSON value is a string. + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exsits. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). - @return `true` if type is string, `false` otherwise. + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and + `unordered_multiset` with a `value_type` from which a @ref basic_json + value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. - @complexity Constant. + See the examples below. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method - @liveexample{The following code exemplifies `is_string()` for all JSON - types.,is_string} + @tparam U = `uncvref_t` - @since version 1.0.0 + @param[in] val the value to be forwarded + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @throw what `json_serializer::to_json()` throws + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 */ - constexpr bool is_string() const noexcept + template, + detail::enable_if_t::value and + not std::is_same::value and + not detail::is_basic_json_nested_type< + basic_json_t, U>::value and + detail::has_to_json::value, + int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer::to_json( + std::declval(), std::forward(val)))) { - return m_type == value_t::string; + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); } /*! - @brief return whether value is discarded + @brief create a container (array or object) from an initializer list - This function returns true iff the JSON value was discarded during parsing - with a callback function (see @ref parser_callback_t). + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. - @return `true` if type is discarded, `false` otherwise. + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: - @complexity Constant. + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has now way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + With the rules described above, the following JSON values cannot be + expressed by an initializer list: - @liveexample{The following code exemplifies `is_discarded()` for all JSON - types.,is_discarded} + - the empty array (`[]`): use @ref array(std::initializer_list) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(std::initializer_list) with the same initializer list + in this case - @since version 1.0.0 - */ - constexpr bool is_discarded() const noexcept - { - return m_type == value_t::discarded; - } + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. - /*! - @brief return the type of the JSON value (implicit) + @param[in] init initializer list with JSON values - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(std::initializer_list) and + @ref object(std::initializer_list). - @return the type of the JSON value + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect - @complexity Constant. + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(std::initializer_list) + for an example. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @complexity Linear in the size of the initializer list @a init. - @liveexample{The following code exemplifies the @ref value_t operator for - all JSON types.,operator__value_t} + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list @since version 1.0.0 */ - constexpr operator value_t() const noexcept + basic_json(std::initializer_list init, + bool type_deduction = true, + value_t manual_type = value_t::array) { - return m_type; - } - - /// @} - - private: - ////////////////// - // value access // - ////////////////// + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json & element) + { + return element.is_array() and element.size() == 2 and element[0].is_string(); + }); - /// get a boolean (explicit) - boolean_t get_impl(boolean_t* /*unused*/) const - { - if (is_boolean()) + // adjust type if type deduction is not wanted + if (not type_deduction) { - return m_value.boolean; + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (manual_type == value_t::object and not is_an_object) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); - } + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; - /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t* /*unused*/) noexcept - { - return is_object() ? m_value.object : nullptr; - } + std::for_each(init.begin(), init.end(), [this](const basic_json & element) + { + m_value.object->emplace(*(element[0].m_value.string), element[1]); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init); + } - /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept - { - return is_object() ? m_value.object : nullptr; + assert_invariant(); } - /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t* /*unused*/) noexcept - { - return is_array() ? m_value.array : nullptr; - } + /*! + @brief explicitly create an array from an initializer list - /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept - { - return is_array() ? m_value.array : nullptr; - } + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. - /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t* /*unused*/) noexcept - { - return is_string() ? m_value.string : nullptr; - } + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(std::initializer_list, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object - /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept - { - return is_string() ? m_value.string : nullptr; - } + @param[in] init initializer list with JSON values to create an array from + (optional) - /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } + @return JSON array value - /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } + @complexity Linear in the size of @a init. - /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } + @liveexample{The following code shows an example for the `array` + function.,array} - /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list - /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + @since version 1.0.0 + */ + static basic_json array(std::initializer_list init = + std::initializer_list()) { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + return basic_json(init, false, value_t::array); } - /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } + /*! + @brief explicitly create an object from an initializer list - /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. - /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(std::initializer_list), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(std::initializer_list, bool, value_t). - /*! - @brief helper function to implement get_ref() + @param[in] init initializer list to create an object from (optional) - This function helps to implement get_ref() without code duplication for - const and non-const overloads + @return JSON object value - @tparam ThisType will be deduced as `basic_json` or `const basic_json` + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(std::initializer_list, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. - @throw type_error.303 if ReferenceType does not match underlying value - type of the current JSON - */ - template - static ReferenceType get_ref_impl(ThisType& obj) - { - // helper type - using PointerType = typename std::add_pointer::type; + @complexity Linear in the size of @a init. - // delegate the call to get_ptr<>() - auto ptr = obj.template get_ptr(); + @liveexample{The following code shows an example for the `object` + function.,object} - if (ptr != nullptr) - { - return *ptr; - } + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); + @since version 1.0.0 + */ + static basic_json object(std::initializer_list init = + std::initializer_list()) + { + return basic_json(init, false, value_t::object); } - public: - /// @name value access - /// Direct access to the stored value of a JSON value. - /// @{ - /*! - @brief get special-case overload + @brief construct an array with count copies of given value - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. As postcondition, + `std::distance(begin(),end()) == cnt` holds. - @tparam BasicJsonType == @ref basic_json + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy - @return a copy of *this + @complexity Linear in @a cnt. - @complexity Constant. + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} - @since version 2.1.0 + @since version 1.0.0 */ - template < - typename BasicJsonType, - detail::enable_if_t::type, - basic_json_t>::value, - int> = 0 > - basic_json get() const + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) { - return *this; + m_value.array = create(cnt, val); + assert_invariant(); } /*! - @brief get a value (explicit) + @brief construct a JSON container given an iterator range - Explicit type conversion between the JSON value and a compatible value - which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of primitive types (number, boolean, or string), @a first must + be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector`. + - In case of a null type, invalid_iterator.206 is thrown. - The function is equivalent to executing - @code {.cpp} - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - @endcode + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and - - @ref json_serializer does not have a `from_json()` method of - the form `ValueType from_json(const basic_json&)` + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** - @return copy of the JSON value, converted to @a ValueType + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. - @throw what @ref json_serializer `from_json()` method throws + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get__ValueType_const} + @complexity Linear in distance between @a first and @a last. - @since version 2.1.0 + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t < - not std::is_same::value and - detail::has_from_json::value and - not detail::has_non_default_from_json::value, - int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) + template::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get(), which is why we - // still need the uncvref - static_assert(not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert(std::is_default_constructible::value, - "types must be DefaultConstructible when used with get()"); + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - } + // make sure iterator fits the current value + if (first.m_object != last.m_object) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } - /*! - @brief get a value (explicit); special case + // copy type from first iterator + m_type = first.m_object->m_type; - Explicit type conversion between the JSON value and a compatible value - which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + break; + } - The function is equivalent to executing - @code {.cpp} - return JSONSerializer::from_json(*this); - @endcode + default: + { + break; + } + } - This overloads is chosen if: - - @a ValueType is not @ref basic_json and - - @ref json_serializer has a `from_json()` method of the form - `ValueType from_json(const basic_json&)` + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } - @note If @ref json_serializer has both overloads of - `from_json()`, this one is chosen. + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } - @return copy of the JSON value, converted to @a ValueType + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } - @throw what @ref json_serializer `from_json()` method throws + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } - @since version 2.1.0 - */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t::value and - detail::has_non_default_from_json::value, int> = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) - { - static_assert(not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer::from_json(*this); + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + { + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + first.m_object->type_name())); + } + } + + assert_invariant(); } - /*! - @brief get a pointer value (explicit) - Explicit pointer access to the internally stored JSON value. No copies are - made. + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// - @warning The pointer becomes invalid if the underlying JSON object - changes. + /*! + @brief copy constructor - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. + Creates a copy of a given JSON value. - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + @param[in] other the JSON value to copy - @complexity Constant. + @complexity Linear in the size of @a other. - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get__PointerType} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. - @sa @ref get_ptr() for explicit pointer-member access + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} @since version 1.0.0 */ - template::value, int>::type = 0> - PointerType get() noexcept + basic_json(const basic_json& other) + : m_type(other.m_type) { - // delegate the call to get_ptr - return get_ptr(); - } + // check of passed value is valid + other.assert_invariant(); - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value, int>::type = 0> - constexpr const PointerType get() const noexcept - { - // delegate the call to get_ptr - return get_ptr(); + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + default: + { + break; + } + } + + assert_invariant(); } /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. + @brief move constructor - @warning Writing data to the pointee of the result yields an undefined - state. + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + @param[in,out] other value to move to this object - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + @post @a other is a JSON null value @complexity Constant. - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} @since version 1.0.0 */ - template::value, int>::type = 0> - PointerType get_ptr() noexcept + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); - - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } + // check that passed value is valid + other.assert_invariant(); - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - template::value and - std::is_const::type>::value, int>::type = 0> - constexpr const PointerType get_ptr() const noexcept - { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + assert_invariant(); } /*! - @brief get a reference value (implicit) - - Implicit reference access to the internally stored JSON value. No copies - are made. - - @warning Writing data to the referee of the result yields an undefined - state. + @brief copy assignment - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. Enforced by static assertion. + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the swap() member function. - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - type_error.303 otherwise + @param[in] other value to copy from - @throw type_error.303 in case passed type @a ReferenceType is incompatible - with the stored JSON value; see example below + @complexity Linear. - @complexity Constant. + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. - @liveexample{The example shows several calls to `get_ref()`.,get_ref} + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} - @since version 1.1.0 + @since version 1.0.0 */ - template::value, int>::type = 0> - ReferenceType get_ref() + reference& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } + // check that passed value is valid + other.assert_invariant(); - /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template::value and - std::is_const::type>::value, int>::type = 0> - ReferenceType get_ref() const - { - // delegate call to get_ref_impl - return get_ref_impl(*this); + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; } /*! - @brief get a value (implicit) - - Implicit type conversion between the JSON value and a compatible value. - The call is realized by calling @ref get() const. - - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. - - @return copy of the JSON value, converted to type @a ValueType + @brief destructor - @throw type_error.302 in case passed type @a ValueType is incompatible - to the JSON value type (e.g., the JSON value is of type boolean, but a - string is requested); see example below + Destroys the JSON value and frees all allocated memory. - @complexity Linear in the size of the JSON value. + @complexity Linear. - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,operator__ValueType} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. @since version 1.0.0 */ - template < typename ValueType, typename std::enable_if < - not std::is_pointer::value and - not std::is_same::value -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 - and not std::is_same>::value -#endif -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSC_VER) && _MSC_VER >1900 && defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - and not std::is_same::value -#endif - , int >::type = 0 > - operator ValueType() const + ~basic_json() { - // delegate the call to get<>() const - return get(); + assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + AllocatorType alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + break; + } + + default: + { + // all other types need no specific destructor + break; + } + } } /// @} + public: + /////////////////////// + // object inspection // + /////////////////////// - //////////////////// - // element access // - //////////////////// - - /// @name element access - /// Access to the JSON value. + /// @name object inspection + /// Functions to inspect the type of a JSON value. /// @{ /*! - @brief access specified array element with bounds checking - - Returns a reference to the element at specified location @a idx, with - bounds checking. + @brief serialization - @param[in] idx index of the element to access + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + parameter. - @return reference to the element at index @a idx + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. + @return string containing the serialization of the JSON value - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @complexity Linear. - @complexity Constant. + @liveexample{The following example shows the effect of different @a indent + parameters to the result of the serialization.,dump} - @since version 1.0.0 + @see https://docs.python.org/2/library/json.html#json.dump - @liveexample{The example below shows how array elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__size_type} + @since version 1.0.0; indentation character added in version 3.0.0 */ - reference at(size_type idx) + string_t dump(const int indent = -1, const char indent_char = ' ') const { - // at only works for arrays - if (is_array()) + string_t result; + serializer s(output_adapter::create(result), indent_char); + + if (indent >= 0) { - JSON_TRY - { - return m_value.array->at(idx); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } + s.dump(*this, true, static_cast(indent)); } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + s.dump(*this, false, 0); } + + return result; } /*! - @brief access specified array element with bounds checking - - Returns a const reference to the element at specified location @a idx, - with bounds checking. + @brief return the type of the JSON value (explicit) - @param[in] idx index of the element to access + Return the type of the JSON value as a value from the @ref value_t + enumeration. - @return const reference to the element at index @a idx + @return the type of the JSON value - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Constant. + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__size_type_const} */ - const_reference at(size_type idx) const + constexpr value_t type() const noexcept { - // at only works for arrays - if (is_array()) - { - JSON_TRY - { - return m_value.array->at(idx); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return m_type; } /*! - @brief access specified object element with bounds checking - - Returns a reference to the element at with specified key @a key, with - bounds checking. + @brief return whether type is primitive - @param[in] key key of the element to access + This function returns true iff the JSON type is primitive (string, number, + boolean, or null). - @return reference to the element at key @a key + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Logarithmic in the size of the container. + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__object_t_key_type} */ - reference at(const typename object_t::key_type& key) + constexpr bool is_primitive() const noexcept { - // at only works for objects - if (is_object()) - { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return is_null() or is_string() or is_boolean() or is_number(); } /*! - @brief access specified object element with bounds checking - - Returns a const reference to the element at with specified key @a key, - with bounds checking. + @brief return whether type is structured - @param[in] key key of the element to access + This function returns true iff the JSON type is structured (array or + object). - @return const reference to the element at key @a key + @return `true` if type is structured (array or object), `false` otherwise. - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Logarithmic in the size of the container. + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__object_t_key_type_const} */ - const_reference at(const typename object_t::key_type& key) const + constexpr bool is_structured() const noexcept { - // at only works for objects - if (is_object()) - { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return is_array() or is_object(); } /*! - @brief access specified array element - - Returns a reference to the element at specified location @a idx. - - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. + @brief return whether value is null - @param[in] idx index of the element to access + This function returns true iff the JSON value is null. - @return reference to the element at index @a idx + @return `true` if type is null, `false` otherwise. - @throw type_error.305 if the JSON value is not an array or null; in that - cases, using the [] operator with an index makes no sense. + @complexity Constant. - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @liveexample{The example below shows how array elements can be read and - written using `[]` operator. Note the addition of `null` - values.,operatorarray__size_type} + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} @since version 1.0.0 */ - reference operator[](size_type idx) + constexpr bool is_null() const noexcept { - // implicitly convert null value to an empty array - if (is_null()) - { - m_type = value_t::array; - m_value.array = create(); - assert_invariant(); - } - - // operator[] only works for arrays - if (is_array()) - { - // fill up array with null values if given idx is outside range - if (idx >= m_value.array->size()) - { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); - } - - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::null; } /*! - @brief access specified array element - - Returns a const reference to the element at specified location @a idx. - - @param[in] idx index of the element to access + @brief return whether value is a boolean - @return const reference to the element at index @a idx + This function returns true iff the JSON value is a boolean. - @throw type_error.305 if the JSON value is not an array; in that cases, - using the [] operator with an index makes no sense. + @return `true` if type is boolean, `false` otherwise. @complexity Constant. - @liveexample{The example below shows how array elements can be read using - the `[]` operator.,operatorarray__size_type_const} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} @since version 1.0.0 */ - const_reference operator[](size_type idx) const + constexpr bool is_boolean() const noexcept { - // const operator[] only works for arrays - if (is_array()) - { - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::boolean; } /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. + @brief return whether value is a number - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. + This function returns true iff the JSON value is a number. This includes + both integer and floating-point values. - @param[in] key key of the element to access + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. - @return reference to the element at key @a key + @complexity Constant. - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ - reference operator[](const typename object_t::key_type& key) + constexpr bool is_number() const noexcept { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - assert_invariant(); - } - - // operator[] only works for objects - if (is_object()) - { - return m_value.object->operator[](key); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return is_number_integer() or is_number_float(); } /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access + @brief return whether value is an integer number - @return const reference to the element at key @a key + This function returns true iff the JSON value is an integer or unsigned + integer number. This excludes floating-point values. - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @complexity Constant. - @complexity Logarithmic in the size of the container. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ - const_reference operator[](const typename object_t::key_type& key) const + constexpr bool is_number_integer() const noexcept { - // const operator[] only works for objects - if (is_object()) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; } /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. + @brief return whether value is an unsigned integer number - @param[in] key key of the element to access + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. - @return reference to the element at key @a key + @return `true` if type is an unsigned integer number, `false` otherwise. - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + @complexity Constant. - @complexity Logarithmic in the size of the container. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number - @since version 1.1.0 + @since version 2.0.0 */ - template - reference operator[](T* key) + constexpr bool is_number_unsigned() const noexcept { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // at only works for objects - if (is_object()) - { - return m_value.object->operator[](key); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::number_unsigned; } /*! - @brief read-only access specified object element + @brief return whether value is a floating-point number - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. + This function returns true iff the JSON value is a floating-point number. + This excludes integer and unsigned integer values. - @warning If the element with key @a key does not exist, the behavior is - undefined. + @return `true` if type is a floating-point number, `false` otherwise. - @param[in] key key of the element to access + @complexity Constant. - @return const reference to the element at key @a key + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number - @complexity Logarithmic in the size of the container. + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} + /*! + @brief return whether value is an object - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + This function returns true iff the JSON value is an object. - @since version 1.1.0 + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 */ - template - const_reference operator[](T* key) const + constexpr bool is_object() const noexcept { - // at only works for objects - if (is_object()) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::object; } /*! - @brief access specified object element with default value + @brief return whether value is an array - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. + This function returns true iff the JSON value is an array. - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(out_of_range) { - return default_value; - } - @endcode + @return `true` if type is array, `false` otherwise. - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. + @complexity Constant. - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } - @return copy of the element at key @a key or @a default_value if @a key - is not found + /*! + @brief return whether value is a string - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + This function returns true iff the JSON value is a string. - @complexity Logarithmic in the size of the container. + @return `true` if type is string, `false` otherwise. - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} + @complexity Constant. - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} @since version 1.0.0 */ - template::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType default_value) const + constexpr bool is_string() const noexcept { - // at only works for objects - if (is_object()) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return *it; - } - - return default_value; - } - else - { - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); - } + return m_type == value_t::string; } /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const - */ - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } + @brief return whether value is discarded - /*! - @brief access specified object element via JSON Pointer with default value + This function returns true iff the JSON value was discarded during parsing + with a callback function (see @ref parser_callback_t). - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. - The function is basically equivalent to executing - @code {.cpp} - try { - return at(ptr); - } catch(out_of_range) { - return default_value; - } - @endcode + @return `true` if type is discarded, `false` otherwise. - @note Unlike @ref at(const json_pointer&), this function does not throw - if the given key @a key was not found. + @complexity Constant. - @param[in] ptr a JSON pointer to the element to access - @param[in] default_value the value to return if @a ptr found no value + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} - @return copy of the element at key @a key or @a default_value if @a key - is not found + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + /*! + @brief return the type of the JSON value (implicit) - @complexity Logarithmic in the size of the container. + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value_ptr} + @return the type of the JSON value - @sa @ref operator[](const json_pointer&) for unchecked access by reference + @complexity Constant. - @since version 2.0.2 - */ - template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, ValueType default_value) const - { - // at only works for objects - if (is_object()) - { - // if pointer resolves a value, return it or use default value - JSON_TRY - { - return ptr.get_checked(this); - } - JSON_CATCH (out_of_range&) - { - return default_value; - } - } + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); - } + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const json_pointer&, ValueType) const + @since version 1.0.0 */ - string_t value(const json_pointer& ptr, const char* default_value) const + constexpr operator value_t() const noexcept { - return value(ptr, string_t(default_value)); + return m_type; } - /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In case of number, string, or boolean values, a - reference to the value is returned. + /// @} - @complexity Constant. + private: + ////////////////// + // value access // + ////////////////// - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (is_boolean()) + { + return m_value.boolean; + } - @throw invalid_iterator.214 when called on `null` value + JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); + } - @liveexample{The following code shows an example for `front()`.,front} + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } - @sa @ref back() -- access the last element + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } - @since version 1.0.0 - */ - reference front() + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept { - return *begin(); + return is_array() ? m_value.array : nullptr; } - /*! - @copydoc basic_json::front() - */ - const_reference front() const + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { - return *cbegin(); + return is_array() ? m_value.array : nullptr; } - /*! - @brief access the last element + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to - @code {.cpp} - auto tmp = c.end(); - --tmp; - return *tmp; - @endcode + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } - @return In case of a structured type (array or object), a reference to the - last element is returned. In case of number, string, or boolean values, a - reference to the value is returned. + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } - @complexity Constant. + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } - @throw invalid_iterator.214 when called on a `null` value. See example - below. + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } - @liveexample{The following code shows an example for `back()`.,back} + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } - @sa @ref front() -- access the first element + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } - @since version 1.0.0 - */ - reference back() + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { - auto tmp = end(); - --tmp; - return *tmp; + return is_number_float() ? &m_value.number_float : nullptr; } - /*! - @copydoc basic_json::back() - */ - const_reference back() const + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { - auto tmp = cend(); - --tmp; - return *tmp; + return is_number_float() ? &m_value.number_float : nullptr; } /*! - @brief remove element given an iterator + @brief helper function to implement get_ref() - Removes the element specified by iterator @a pos. The iterator @a pos must - be valid and dereferenceable. Thus the `end()` iterator (which is valid, - but is not dereferenceable) cannot be used as a value for @a pos. + This function helps to implement get_ref() without code duplication for + const and non-const overloads - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. + @tparam ThisType will be deduced as `basic_json` or `const basic_json` - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a - pos refers to the last element, the `end()` iterator is returned. + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // helper type + using PointerType = typename std::add_pointer::type; - @tparam IteratorType an @ref iterator or @ref const_iterator + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr(); - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. + if (ptr != nullptr) + { + return *ptr; + } - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.202 if called on an iterator which does not belong - to the current JSON value; example: `"iterator does not fit current - value"` - @throw invalid_iterator.205 if called on a primitive type with invalid - iterator (i.e., any iterator which is not `begin()`); example: `"iterator - out of range"` + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); + } - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between @a pos and the end of the container - - strings: linear in the length of the string - - other types: constant + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType} + /*! + @brief get special-case overload - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method - @since version 1.0.0 + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 */ - template::value or - std::is_same::value, int>::type - = 0> - IteratorType erase(IteratorType pos) + template < + typename BasicJsonType, + detail::enable_if_t::type, + basic_json_t>::value, + int> = 0 > + basic_json get() const { - // make sure iterator fits the current value - if (this != pos.m_object) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } + return *this; + } - IteratorType result = end(); + /*! + @brief get a value (explicit) - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not pos.m_it.primitive_iterator.is_begin()) - { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); - } + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. - if (is_string()) - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; - } + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode - m_type = value_t::null; - assert_invariant(); - break; - } + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` - case value_t::object: - { - result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); - break; - } + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - case value_t::array: - { - result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); - break; - } + @return copy of the JSON value, converted to @a ValueType - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } - } + @throw what @ref json_serializer `from_json()` method throws - return result; - } + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} - /*! - @brief remove elements given an iterator range + @since version 2.1.0 + */ + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t < + not std::is_same::value and + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); - Removes the element specified by the range `[first; last)`. The iterator - @a first does not need to be dereferenceable if `first == last`: erasing - an empty range is a no-op. + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. + /*! + @brief get a value (explicit); special case - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the `end()` iterator is returned. + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. - @tparam IteratorType an @ref iterator or @ref const_iterator + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.203 if called on iterators which does not belong - to the current JSON value; example: `"iterators do not fit current value"` - @throw invalid_iterator.204 if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType_IteratorType} + @return copy of the JSON value, converted to @a ValueType - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + @throw what @ref json_serializer `from_json()` method throws - @since version 1.0.0 + @since version 2.1.0 */ - template::value or - std::is_same::value, int>::type - = 0> - IteratorType erase(IteratorType first, IteratorType last) + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t::value and + detail::has_non_default_from_json::value, int> = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) { - // make sure iterator fits the current value - if (this != first.m_object or this != last.m_object) - { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); - } + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); + } - IteratorType result = end(); + /*! + @brief get a pointer value (explicit) - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } - - if (is_string()) - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; - } - - m_type = value_t::null; - assert_invariant(); - break; - } - - case value_t::object: - { - result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } - - case value_t::array: - { - result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } - - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } - } - - return result; - } - - /*! - @brief remove element from a JSON object given a key - - Removes elements from a JSON object with the key value @a key. - - @param[in] key value of the elements to remove + Explicit pointer access to the internally stored JSON value. No copies are + made. - @return Number of elements removed. If @a ObjectType is the default - `std::map` type, the return value will always be `0` (@a key was not - found) or `1` (@a key was found). + @warning The pointer becomes invalid if the underlying JSON object + changes. - @post References and iterators to the erased elements are invalidated. - Other references and iterators are not affected. + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - @complexity `log(size()) + count(key)` + @complexity Constant. - @liveexample{The example shows the effect of `erase()`.,erase__key_type} + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + @sa @ref get_ptr() for explicit pointer-member access @since version 1.0.0 */ - size_type erase(const typename object_t::key_type& key) + template::value, int>::type = 0> + PointerType get() noexcept { - // this erase only works for objects - if (is_object()) - { - return m_value.object->erase(key); - } + // delegate the call to get_ptr + return get_ptr(); + } - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int>::type = 0> + constexpr const PointerType get() const noexcept + { + // delegate the call to get_ptr + return get_ptr(); } /*! - @brief remove element from a JSON array given an index + @brief get a pointer value (implicit) - Removes element from a JSON array at the index @a idx. + Implicit pointer access to the internally stored JSON value. No copies are + made. - @param[in] idx index of the element to remove + @warning Writing data to the pointee of the result yields an undefined + state. - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 - is out of range"` + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. - @complexity Linear in distance between @a idx and the end of the container. + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - @liveexample{The example shows the effect of `erase()`.,erase__size_type} + @complexity Constant. - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} @since version 1.0.0 */ - void erase(const size_type idx) + template::value, int>::type = 0> + PointerType get_ptr() noexcept { - // this erase only works for arrays - if (is_array()) - { - if (idx >= size()) - { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); - m_value.array->erase(m_value.array->begin() + static_cast(idx)); - } - else - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); } - /// @} - - - //////////// - // lookup // - //////////// + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); - /// @name lookup - /// @{ + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } /*! - @brief find an element in a JSON object + @brief get a reference value (implicit) - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is - returned. + Implicit reference access to the internally stored JSON value. No copies + are made. - @note This method always returns @ref end() when executed on a JSON type - that is not an object. + @warning Writing data to the referee of the result yields an undefined + state. - @param[in] key key value of the element to search for + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. - @return Iterator to an element with key equivalent to @a key. If no such - element is found or the JSON value is not an object, past-the-end (see - @ref end()) iterator is returned. + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise - @complexity Logarithmic in the size of the JSON object. + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below - @liveexample{The example shows how `find()` is used.,find__key_type} + @complexity Constant. - @since version 1.0.0 + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 */ - iterator find(typename object_t::key_type key) + template::value, int>::type = 0> + ReferenceType get_ref() { - auto result = end(); - - if (is_object()) - { - result.m_it.object_iterator = m_value.object->find(key); - } - - return result; + // delegate call to get_ref_impl + return get_ref_impl(*this); } /*! - @brief find an element in a JSON object - @copydoc find(typename object_t::key_type) + @brief get a reference value (implicit) + @copydoc get_ref() */ - const_iterator find(typename object_t::key_type key) const + template::value and + std::is_const::type>::value, int>::type = 0> + ReferenceType get_ref() const { - auto result = cend(); - - if (is_object()) - { - result.m_it.object_iterator = m_value.object->find(key); - } - - return result; + // delegate call to get_ref_impl + return get_ref_impl(*this); } /*! - @brief returns the number of occurrences of a key in a JSON object + @brief get a value (implicit) - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. - @note This method always returns `0` when executed on a JSON type that is - not an object. + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. - @param[in] key key value of the element to count + @return copy of the JSON value, converted to type @a ValueType - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below - @complexity Logarithmic in the size of the JSON object. + @complexity Linear in the size of the JSON value. - @liveexample{The example shows how `count()` is used.,count} + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} @since version 1.0.0 */ - size_type count(typename object_t::key_type key) const + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value +#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 + and not std::is_same>::value +#endif +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSC_VER) && _MSC_VER >1900 && defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + and not std::is_same::value +#endif + , int >::type = 0 > + operator ValueType() const { - // return 0 for all nonobject types - return is_object() ? m_value.object->count(key) : 0; + // delegate the call to get<>() const + return get(); } /// @} - /////////////// - // iterators // - /////////////// + //////////////////// + // element access // + //////////////////// - /// @name iterators + /// @name element access + /// Access to the JSON value. /// @{ /*! - @brief returns an iterator to the first element - - Returns an iterator to the first element. + @brief access specified array element with bounds checking - @image html range-begin-end.svg "Illustration from cppreference.com" + Returns a reference to the element at specified location @a idx, with + bounds checking. - @return iterator to the first element + @param[in] idx index of the element to access - @complexity Constant. + @return reference to the element at index @a idx - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. - @liveexample{The following code shows an example for `begin()`.,begin} + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @sa @ref cbegin() -- returns a const iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end + @complexity Constant. @since version 1.0.0 - */ - iterator begin() noexcept - { - iterator result(this); - result.set_begin(); - return result; - } - /*! - @copydoc basic_json::cbegin() + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} */ - const_iterator begin() const noexcept + reference at(size_type idx) { - return cbegin(); + // at only works for arrays + if (is_array()) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } /*! - @brief returns a const iterator to the first element - - Returns a const iterator to the first element. + @brief access specified array element with bounds checking - @image html range-begin-end.svg "Illustration from cppreference.com" + Returns a const reference to the element at specified location @a idx, + with bounds checking. - @return const iterator to the first element + @param[in] idx index of the element to access - @complexity Constant. + @return const reference to the element at index @a idx - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).begin()`. + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. - @liveexample{The following code shows an example for `cbegin()`.,cbegin} + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end + @complexity Constant. @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} */ - const_iterator cbegin() const noexcept + const_reference at(size_type idx) const { - const_iterator result(this); - result.set_begin(); - return result; + // at only works for arrays + if (is_array()) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } /*! - @brief returns an iterator to one past the last element + @brief access specified object element with bounds checking - Returns an iterator to one past the last element. + Returns a reference to the element at with specified key @a key, with + bounds checking. - @image html range-begin-end.svg "Illustration from cppreference.com" + @param[in] key key of the element to access - @return iterator one past the last element + @return reference to the element at key @a key - @complexity Constant. + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @liveexample{The following code shows an example for `end()`.,end} + @complexity Logarithmic in the size of the container. - @sa @ref cend() -- returns a const iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value @since version 1.0.0 - */ - iterator end() noexcept - { - iterator result(this); - result.set_end(); - return result; - } - /*! - @copydoc basic_json::cend() + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} */ - const_iterator end() const noexcept + reference at(const typename object_t::key_type& key) { - return cend(); + // at only works for objects + if (is_object()) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } /*! - @brief returns a const iterator to one past the last element + @brief access specified object element with bounds checking - Returns a const iterator to one past the last element. + Returns a const reference to the element at with specified key @a key, + with bounds checking. - @image html range-begin-end.svg "Illustration from cppreference.com" + @param[in] key key of the element to access - @return const iterator one past the last element + @return const reference to the element at key @a key - @complexity Constant. + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).end()`. + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @liveexample{The following code shows an example for `cend()`.,cend} + @complexity Logarithmic in the size of the container. - @sa @ref end() -- returns an iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} */ - const_iterator cend() const noexcept + const_reference at(const typename object_t::key_type& key) const { - const_iterator result(this); - result.set_end(); - return result; + // at only works for objects + if (is_object()) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } /*! - @brief returns an iterator to the reverse-beginning + @brief access specified array element - Returns an iterator to the reverse-beginning; that is, the last element. + Returns a reference to the element at specified location @a idx. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. - @complexity Constant. + @param[in] idx index of the element to access - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. + @return reference to the element at index @a idx - @liveexample{The following code shows an example for `rbegin()`.,rbegin} + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} @since version 1.0.0 */ - reverse_iterator rbegin() noexcept + reference operator[](size_type idx) { - return reverse_iterator(end()); - } + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } - /*! - @copydoc basic_json::crbegin() - */ - const_reverse_iterator rbegin() const noexcept - { - return crbegin(); + // operator[] only works for arrays + if (is_array()) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns an iterator to the reverse-end + @brief access specified array element - Returns an iterator to the reverse-end; that is, one before the first - element. + Returns a const reference to the element at specified location @a idx. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @param[in] idx index of the element to access - @complexity Constant. + @return const reference to the element at index @a idx - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. + @throw type_error.305 if the JSON value is not an array; in that cases, + using the [] operator with an index makes no sense. - @liveexample{The following code shows an example for `rend()`.,rend} + @complexity Constant. - @sa @ref crend() -- returns a const reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} @since version 1.0.0 */ - reverse_iterator rend() noexcept + const_reference operator[](size_type idx) const { - return reverse_iterator(begin()); - } + // const operator[] only works for arrays + if (is_array()) + { + return m_value.array->operator[](idx); + } - /*! - @copydoc basic_json::crend() - */ - const_reverse_iterator rend() const noexcept - { - return crend(); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns a const reverse iterator to the last element + @brief access specified object element - Returns a const iterator to the reverse-beginning; that is, the last - element. + Returns a reference to the element at with specified key @a key. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. - @complexity Constant. + @param[in] key key of the element to access - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rbegin()`. + @return reference to the element at key @a key - @liveexample{The following code shows an example for `crbegin()`.,crbegin} + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value @since version 1.0.0 */ - const_reverse_iterator crbegin() const noexcept + reference operator[](const typename object_t::key_type& key) { - return const_reverse_iterator(cend()); - } + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } - /*! - @brief returns a const reverse iterator to one before the first + // operator[] only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + } - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + /*! + @brief read-only access specified object element - @complexity Constant. + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rend()`. + @warning If the element with key @a key does not exist, the behavior is + undefined. - @liveexample{The following code shows an example for `crend()`.,crend} + @param[in] key key of the element to access - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @return const reference to the element at key @a key - @since version 1.0.0 - */ - const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator(cbegin()); - } + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** - private: - // forward declaration - template class iteration_proxy; + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. - public: - /*! - @brief wrapper to access iterator member functions in range-based for + @complexity Logarithmic in the size of the container. - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} - @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value - @note The name of this function is not yet final and may change in the - future. + @since version 1.0.0 */ - static iteration_proxy iterator_wrapper(reference cont) + const_reference operator[](const typename object_t::key_type& key) const { - return iteration_proxy(cont); + // const operator[] only works for objects + if (is_object()) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @copydoc iterator_wrapper(reference) - */ - static iteration_proxy iterator_wrapper(const_reference cont) - { - return iteration_proxy(cont); - } + @brief access specified object element - /// @} + Returns a reference to the element at with specified key @a key. + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. - ////////////// - // capacity // - ////////////// - - /// @name capacity - /// @{ - - /*! - @brief checks whether the container is empty - - Checks if a JSON value has no elements. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `true` - boolean | `false` - string | `false` - number | `false` - object | result of function `object_t::empty()` - array | result of function `array_t::empty()` + @param[in] key key of the element to access - @note This function does not return whether a string stored as JSON value - is empty - it returns whether the JSON container itself is empty which is - false in the case of a string. + @return reference to the element at key @a key - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `empty()` functions have constant - complexity. + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. + @complexity Logarithmic in the size of the container. - @liveexample{The following code uses `empty()` to check if a JSON - object contains any elements.,empty} + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} - @sa @ref size() -- returns the number of elements + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value - @since version 1.0.0 + @since version 1.1.0 */ - bool empty() const noexcept + template + reference operator[](T* key) { - switch (m_type) + // implicitly convert null to object + if (is_null()) { - case value_t::null: - { - // null values are empty - return true; - } - - case value_t::array: - { - // delegate call to array_t::empty() - return m_value.array->empty(); - } - - case value_t::object: - { - // delegate call to object_t::empty() - return m_value.object->empty(); - } + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } - default: - { - // all other types are nonempty - return false; - } + // at only works for objects + if (is_object()) + { + return m_value.object->operator[](key); } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns the number of elements + @brief read-only access specified object element - Returns the number of elements in a JSON value. + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` - boolean | `1` - string | `1` - number | `1` - object | result of function object_t::size() - array | result of function array_t::size() + @warning If the element with key @a key does not exist, the behavior is + undefined. - @note This function does not return the length of a string stored as JSON - value - it returns the number of elements in the JSON value which is 1 in - the case of a string. + @param[in] key key of the element to access - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their size() functions have constant - complexity. + @return const reference to the element at key @a key - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** - @liveexample{The following code calls `size()` on the different value - types.,size} + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. - @sa @ref empty() -- checks whether the container is empty - @sa @ref max_size() -- returns the maximal number of elements + @complexity Logarithmic in the size of the container. - @since version 1.0.0 + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 */ - size_type size() const noexcept + template + const_reference operator[](T* key) const { - switch (m_type) + // at only works for objects + if (is_object()) { - case value_t::null: - { - // null values are empty - return 0; - } + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } - case value_t::array: - { - // delegate call to array_t::size() - return m_value.array->size(); - } + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + } - case value_t::object: - { - // delegate call to object_t::size() - return m_value.object->size(); - } + /*! + @brief access specified object element with default value - default: - { - // all other types have size 1 - return 1; - } - } + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; } + @endcode - /*! - @brief returns the maximum possible number of elements + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` (same as `size()`) - boolean | `1` (same as `size()`) - string | `1` (same as `size()`) - number | `1` (same as `size()`) - object | result of function `object_t::max_size()` - array | result of function `array_t::max_size()` + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `max_size()` functions have constant - complexity. + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. + @return copy of the element at key @a key or @a default_value if @a key + is not found - @liveexample{The following code calls `max_size()` on the different value - types. Note the output is implementation specific.,max_size} + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. - @sa @ref size() -- returns the number of elements + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference @since version 1.0.0 */ - size_type max_size() const noexcept + template::value, int>::type = 0> + ValueType value(const typename object_t::key_type& key, ValueType default_value) const { - switch (m_type) + // at only works for objects + if (is_object()) { - case value_t::array: - { - // delegate call to array_t::max_size() - return m_value.array->max_size(); - } - - case value_t::object: + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) { - // delegate call to object_t::max_size() - return m_value.object->max_size(); + return *it; } - default: - { - // all other types have max_size() == size() - return size(); - } + return default_value; } - } - - /// @} - - - /////////////// - // modifiers // - /////////////// + else + { + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); + } + } - /// @name modifiers - /// @{ + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } /*! - @brief clears the contents + @brief access specified object element via JSON Pointer with default value - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called: + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode - @complexity Linear in the size of the JSON value. + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. - @liveexample{The example below shows the effect of `clear()` to different - JSON types.,clear} + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value - @since version 1.0.0 - */ - void clear() noexcept - { - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = 0; - break; - } + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. - case value_t::number_unsigned: - { - m_value.number_unsigned = 0; - break; - } + @return copy of the element at key @a key or @a default_value if @a key + is not found - case value_t::number_float: - { - m_value.number_float = 0.0; - break; - } + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. - case value_t::boolean: - { - m_value.boolean = false; - break; - } + @complexity Logarithmic in the size of the container. - case value_t::string: - { - m_value.string->clear(); - break; - } + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} - case value_t::array: - { - m_value.array->clear(); - break; - } + @sa @ref operator[](const json_pointer&) for unchecked access by reference - case value_t::object: + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if pointer resolves a value, return it or use default value + JSON_TRY { - m_value.object->clear(); - break; + return ptr.get_checked(this); } - - default: + JSON_CATCH (out_of_range&) { - break; + return default_value; } } + + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } /*! - @brief add an object to an array + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. + /*! + @brief access the first element - @param[in] val the value to add to the JSON array + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - @throw type_error.308 when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, or boolean values, a + reference to the value is returned. - @complexity Amortized constant. + @complexity Constant. - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON array. Note how the `null` value was silently - converted to a JSON array.,push_back} + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. - @since version 1.0.0 - */ - void push_back(basic_json&& val) - { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); - } + @throw invalid_iterator.214 when called on `null` value - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } + @liveexample{The following code shows an example for `front()`.,front} - // add element to array (move semantics) - m_value.array->push_back(std::move(val)); - // invalidate object - val.m_type = value_t::null; - } + @sa @ref back() -- access the last element - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) + @since version 1.0.0 */ - reference operator+=(basic_json&& val) + reference front() { - push_back(std::move(val)); - return *this; + return *begin(); } /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) + @copydoc basic_json::front() */ - void push_back(const basic_json& val) + const_reference front() const { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } - - // add element to array - m_value.array->push_back(val); + return *cbegin(); } /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(const basic_json& val) - { - push_back(val); - return *this; - } + @brief access the last element - /*! - @brief add an object to an object + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting - @a val. + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, or boolean values, a + reference to the value is returned. - @param[in] val the value to add to the JSON object + @complexity Constant. - @throw type_error.308 when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. - @complexity Logarithmic in the size of the container, O(log(`size()`)). + @throw invalid_iterator.214 when called on a `null` value. See example + below. - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON object. Note how the `null` value was silently - converted to a JSON object.,push_back__object_t__value} + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element @since version 1.0.0 */ - void push_back(const typename object_t::value_type& val) + reference back() { - // push_back only works for null objects or objects - if (not(is_null() or is_object())) - { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); - } - - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // add element to array - m_value.object->insert(val); + auto tmp = end(); + --tmp; + return *tmp; } /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) + @copydoc basic_json::back() */ - reference operator+=(const typename object_t::value_type& val) + const_reference back() const { - push_back(val); - return *this; + auto tmp = cend(); + --tmp; + return *tmp; } /*! - @brief add an object to an object + @brief remove element given an iterator - This function allows to use `push_back` with an initializer list. In case + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. - 1. the current value is an object, - 2. the initializer list @a init contains only two elements, and - 3. the first element of @a init is a string, + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. - @a init is converted into an object element and added using - @ref push_back(const typename object_t::value_type&). Otherwise, @a init - is converted to a JSON value and added using @ref push_back(basic_json&&). + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. - @param[in] init an initializer list + @tparam IteratorType an @ref iterator or @ref const_iterator - @complexity Linear in the size of the initializer list @a init. + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. - @note This function is required to resolve an ambiguous overload error, - because pairs like `{"key", "value"}` can be both interpreted as - `object_t::value_type` or `std::initializer_list`, see - https://github.com/nlohmann/json/issues/235 for more information. + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` - @liveexample{The example shows how initializer lists are treated as - objects when possible.,push_back__initializer_list} + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 */ - void push_back(std::initializer_list init) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { - if (is_object() and init.size() == 2 and init.begin()->is_string()) - { - const string_t key = *init.begin(); - push_back(typename object_t::value_type(key, *(init.begin() + 1))); - } - else + // make sure iterator fits the current value + if (this != pos.m_object) { - push_back(basic_json(init)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } - } - - /*! - @brief add an object to an object - @copydoc push_back(std::initializer_list) - */ - reference operator+=(std::initializer_list init) - { - push_back(init); - return *this; - } - - /*! - @brief add an object to an array - Creates a JSON value from the passed parameters @a args to the end of the - JSON value. If the function is called on a JSON null value, an empty array - is created before appending the value created from @a args. + IteratorType result = end(); - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not pos.m_it.primitive_iterator.is_begin()) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } - @throw type_error.311 when called on a type other than JSON array or - null; example: `"cannot use emplace_back() with number"` + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } - @complexity Amortized constant. + m_type = value_t::null; + assert_invariant(); + break; + } - @liveexample{The example shows how `push_back()` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,emplace_back} + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } - @since version 2.0.8 - */ - template - void emplace_back(Args&& ... args) - { - // emplace_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); - } + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); + default: + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + } } - // add element to array (perfect forwarding) - m_value.array->emplace_back(std::forward(args)...); + return result; } /*! - @brief add an object to an object if key does not exist + @brief remove elements given an iterator range - Inserts a new element into a JSON object constructed in-place with the - given @a args if there is no element with the key in the container. If the - function is called on a JSON null value, an empty object is created before - appending the value created from @a args. + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. - @return a pair consisting of an iterator to the inserted element, or the - already-existing element if no insertion happened, and a bool - denoting whether the insertion took place. + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. - @throw type_error.311 when called on a type other than JSON object or - null; example: `"cannot use emplace() with number"` + @tparam IteratorType an @ref iterator or @ref const_iterator - @complexity Logarithmic in the size of the container, O(log(`size()`)). + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. - @liveexample{The example shows how `emplace()` can be used to add elements - to a JSON object. Note how the `null` value was silently converted to a - JSON object. Further note how no value is added if there was already one - value stored with the same key.,emplace} + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` - @since version 2.0.8 + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 */ - template - std::pair emplace(Args&& ... args) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { - // emplace only works for null objects or arrays - if (not(is_null() or is_object())) - { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); - } - - // transform null object into an object - if (is_null()) + // make sure iterator fits the current value + if (this != first.m_object or this != last.m_object) { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } - // add element to array (perfect forwarding) - auto res = m_value.object->emplace(std::forward(args)...); - // create result iterator and set iterator to the result of emplace - auto it = begin(); - it.m_it.object_iterator = res.first; + IteratorType result = end(); - // return pair of iterator and boolean - return {it, res.second}; - } - - /*! - @brief inserts element - - Inserts element @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } - @throw type_error.309 if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } - @complexity Constant plus linear in the distance between @a pos and end of - the container. + m_type = value_t::null; + assert_invariant(); + break; + } - @liveexample{The example shows how `insert()` is used.,insert} + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const basic_json& val) - { - // insert only works for arrays - if (is_array()) - { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) + case value_t::array: { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; } - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); - return result; + default: + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + } } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + return result; } /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) - */ - iterator insert(const_iterator pos, basic_json&& val) - { - return insert(pos, val); - } + @brief remove element from a JSON object given a key - /*! - @brief inserts elements + Removes elements from a JSON object with the key value @a key. - Inserts @a cnt copies of @a val before iterator @a pos. + @param[in] key value of the elements to remove - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` - @liveexample{The example shows how `insert()` is used.,insert__count} + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index @since version 1.0.0 */ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + size_type erase(const typename object_t::key_type& key) { - // insert only works for arrays - if (is_array()) + // this erase only works for objects + if (is_object()) { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); - return result; + return m_value.object->erase(key); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } /*! - @brief inserts elements + @brief remove element from a JSON array given an index - Inserts elements from range `[first, last)` before iterator @a pos. + Removes element from a JSON array at the index @a idx. - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert + @param[in] idx index of the element to remove - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - @throw invalid_iterator.211 if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` - @return iterator pointing to the first element inserted, or @a pos if - `first==last` + @complexity Linear in distance between @a idx and the end of the container. - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. + @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @liveexample{The example shows how `insert()` is used.,insert__range} + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key @since version 1.0.0 */ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) + void erase(const size_type idx) { - // insert only works for arrays - if (not is_array()) + // this erase only works for arrays + if (is_array()) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } + if (idx >= size()) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + m_value.array->erase(m_value.array->begin() + static_cast(idx)); } - - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) + else { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } + } - if (first.m_object == this or last.m_object == this) - { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); - } + /// @} - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); - return result; - } + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ /*! - @brief inserts elements + @brief find an element in a JSON object - Inserts elements from initializer list @a ilist before iterator @a pos. + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from + @note This method always returns @ref end() when executed on a JSON type + that is not an object. - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @param[in] key key value of the element to search for - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. - @complexity Linear in `ilist.size()` plus linear in the distance between - @a pos and end of the container. + @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how `insert()` is used.,insert__ilist} + @liveexample{The example shows how `find()` is used.,find__key_type} @since version 1.0.0 */ - iterator insert(const_iterator pos, std::initializer_list ilist) + iterator find(typename object_t::key_type key) { - // insert only works for arrays - if (not is_array()) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } + auto result = end(); - // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (is_object()) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + result.m_it.object_iterator = m_value.object->find(key); } - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } /*! - @brief inserts elements - - Inserts elements from range `[first, last)`. - - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.309 if called on JSON values other than objects; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - - @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number - of elements to insert. - - @liveexample{The example shows how `insert()` is used.,insert__range_object} - - @since version 3.0.0 + @brief find an element in a JSON object + @copydoc find(typename object_t::key_type) */ - void insert(const_iterator first, const_iterator last) + const_iterator find(typename object_t::key_type key) const { - // insert only works for objects - if (not is_object()) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } - - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } + auto result = cend(); - // passed iterators must belong to objects - if (not first.m_object->is_object() or not first.m_object->is_object()) + if (is_object()) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + result.m_it.object_iterator = m_value.object->find(key); } - m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + return result; } /*! - @brief exchanges the values + @brief returns the number of occurrences of a key in a JSON object - Exchanges the contents of the JSON value with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). - @param[in,out] other JSON value to exchange the contents with + @note This method always returns `0` when executed on a JSON type that is + not an object. - @complexity Constant. + @param[in] key key value of the element to count - @liveexample{The example below shows how JSON values can be swapped with - `swap()`.,swap__reference} + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} @since version 1.0.0 */ - void swap(reference other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) + size_type count(typename object_t::key_type key) const { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); - assert_invariant(); + // return 0 for all nonobject types + return is_object() ? m_value.object->count(key) : 0; } + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + /*! - @brief exchanges the values + @brief returns an iterator to the first element - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + Returns an iterator to the first element. - @param[in,out] other array to exchange the contents with + @image html range-begin-end.svg "Illustration from cppreference.com" - @throw type_error.310 when JSON value is not an array; example: `"cannot - use swap() with string"` + @return iterator to the first element @complexity Constant. - @liveexample{The example below shows how arrays can be swapped with - `swap()`.,swap__array_t} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - void swap(array_t& other) + iterator begin() noexcept { - // swap only works for arrays - if (is_array()) - { - std::swap(*(m_value.array), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); - } + iterator result(this); + result.set_begin(); + return result; } /*! - @brief exchanges the values + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } - Exchanges the contents of a JSON object with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + /*! + @brief returns a const iterator to the first element - @param[in,out] other object to exchange the contents with + Returns a const iterator to the first element. - @throw type_error.310 when JSON value is not an object; example: - `"cannot use swap() with string"` + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element @complexity Constant. - @liveexample{The example below shows how objects can be swapped with - `swap()`.,swap__object_t} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - void swap(object_t& other) + const_iterator cbegin() const noexcept { - // swap only works for objects - if (is_object()) - { - std::swap(*(m_value.object), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); - } + const_iterator result(this); + result.set_begin(); + return result; } /*! - @brief exchanges the values + @brief returns an iterator to one past the last element - Exchanges the contents of a JSON string with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + Returns an iterator to one past the last element. - @param[in,out] other string to exchange the contents with + @image html range-begin-end.svg "Illustration from cppreference.com" - @throw type_error.310 when JSON value is not a string; example: `"cannot - use swap() with boolean"` + @return iterator one past the last element @complexity Constant. - @liveexample{The example below shows how strings can be swapped with - `swap()`.,swap__string_t} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - void swap(string_t& other) + iterator end() noexcept { - // swap only works for strings - if (is_string()) - { - std::swap(*(m_value.string), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); - } + iterator result(this); + result.set_end(); + return result; } - /// @} + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } - public: - ////////////////////////////////////////// - // lexicographical comparison operators // - ////////////////////////////////////////// + /*! + @brief returns a const iterator to one past the last element - /// @name lexicographical comparison operators - /// @{ + Returns a const iterator to one past the last element. - /*! - @brief comparison: equal + @image html range-begin-end.svg "Illustration from cppreference.com" - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same according to their respective - `operator==`. - - Integer and floating-point numbers are automatically converted before - comparison. Note than two NaN values are always treated as unequal. - - Two JSON null values are equal. + @return const iterator one past the last element - @note NaN values never compare equal to themselves or to other NaN values. + @complexity Constant. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. - @complexity Linear. + @liveexample{The following code shows an example for `cend()`.,cend} - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - friend bool operator==(const_reference lhs, const_reference rhs) noexcept + const_iterator cend() const noexcept { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - return *lhs.m_value.array == *rhs.m_value.array; - } - case value_t::object: - { - return *lhs.m_value.object == *rhs.m_value.object; - } - case value_t::null: - { - return true; - } - case value_t::string: - { - return *lhs.m_value.string == *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean == rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer == rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float == rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); - } - - return false; + const_iterator result(this); + result.set_end(); + return result; } /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 */ - template::value, int>::type = 0> - friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + reverse_iterator rbegin() noexcept { - return (lhs == basic_json(rhs)); + return reverse_iterator(end()); } /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) + @copydoc basic_json::crbegin() */ - template::value, int>::type = 0> - friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + const_reverse_iterator rbegin() const noexcept { - return (basic_json(lhs) == rhs); + return crbegin(); } /*! - @brief comparison: not equal + @brief returns an iterator to the reverse-end - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + Returns an iterator to the reverse-end; that is, one before the first + element. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal + @image html range-rbegin-rend.svg "Illustration from cppreference.com" - @complexity Linear. + @complexity Constant. - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + reverse_iterator rend() noexcept { - return not (lhs == rhs); + return reverse_iterator(begin()); } /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) + @copydoc basic_json::crend() */ - template::value, int>::type = 0> - friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + const_reverse_iterator rend() const noexcept { - return (lhs != basic_json(rhs)); + return crend(); } /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 */ - template::value, int>::type = 0> - friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + const_reverse_iterator crbegin() const noexcept { - return (basic_json(lhs) != rhs); + return const_reverse_iterator(cend()); } /*! - @brief comparison: less than + @brief returns a const reverse iterator to one before the first - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs + @image html range-rbegin-rend.svg "Illustration from cppreference.com" - @complexity Linear. + @complexity Constant. - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - friend bool operator<(const_reference lhs, const_reference rhs) noexcept + const_reverse_iterator crend() const noexcept { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); + return const_reverse_iterator(cbegin()); + } - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - return (*lhs.m_value.array) < (*rhs.m_value.array); - } - case value_t::object: - { - return *lhs.m_value.object < *rhs.m_value.object; - } - case value_t::null: - { - return false; - } - case value_t::string: - { - return *lhs.m_value.string < *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean < rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer < rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float < rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; - } - - // We only reach this line if we cannot compare values. In that case, - // we compare types. Note we have to call the operator explicitly, - // because MSVC has problems otherwise. - return operator<(lhs_type, rhs_type); - } + private: + // forward declaration + template class iteration_proxy; + public: /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @note The name of this function is not yet final and may change in the + future. */ - template::value, int>::type = 0> - friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + static iteration_proxy iterator_wrapper(reference cont) { - return (lhs < basic_json(rhs)); + return iteration_proxy(cont); } /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) + @copydoc iterator_wrapper(reference) */ - template::value, int>::type = 0> - friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept + static iteration_proxy iterator_wrapper(const_reference cont) { - return (basic_json(lhs) < rhs); + return iteration_proxy(cont); } - /*! - @brief comparison: less than or equal - - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs + /// @} - @complexity Linear. - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} + ////////////// + // capacity // + ////////////// - @since version 1.0.0 - */ - friend bool operator<=(const_reference lhs, const_reference rhs) noexcept - { - return not (rhs < lhs); - } + /// @name capacity + /// @{ /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs <= basic_json(rhs)); - } + @brief checks whether the container is empty - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) <= rhs); - } + Checks if a JSON value has no elements. - /*! - @brief comparison: greater than + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. - @complexity Linear. + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @sa @ref size() -- returns the number of elements @since version 1.0.0 */ - friend bool operator>(const_reference lhs, const_reference rhs) noexcept + bool empty() const noexcept { - return not (lhs <= rhs); - } + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs > basic_json(rhs)); - } + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) > rhs); + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } } /*! - @brief comparison: greater than or equal + @brief returns the number of elements - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. + Returns the number of elements in a JSON value. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + object | result of function object_t::size() + array | result of function array_t::size() - @complexity Linear. + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements @since version 1.0.0 */ - friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + size_type size() const noexcept { - return not (lhs < rhs); - } + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs >= basic_json(rhs)); - } + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) >= rhs); - } - - /// @} - - private: - ///////////////////// - // output adapters // - ///////////////////// - - /// abstract output adapter interface - template - class output_adapter - { - public: - virtual void write_character(CharType c) = 0; - virtual void write_characters(const CharType* s, size_t length) = 0; - virtual ~output_adapter() {} + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } - static std::shared_ptr> create(std::vector& vec) - { - return std::make_shared>(vec); + default: + { + // all other types have size 1 + return 1; + } } + } - static std::shared_ptr> create(std::ostream& s) - { - return std::make_shared>(s); - } + /*! + @brief returns the maximum possible number of elements - static std::shared_ptr> create(std::string& s) - { - return std::make_shared>(s); - } - }; + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. - /// a type to simplify interfaces - template - using output_adapter_t = std::shared_ptr>; + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` - /// output adapter for byte vectors - template - class output_vector_adapter : public output_adapter - { - public: - output_vector_adapter(std::vector& vec) - : v(vec) - {} + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. - void write_character(CharType c) override - { - v.push_back(c); - } + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. - void write_characters(const CharType* s, size_t length) override - { - std::copy(s, s + length, std::back_inserter(v)); - } + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} - private: - std::vector& v; - }; + @sa @ref size() -- returns the number of elements - /// output adapter for output streams - template - class output_stream_adapter : public output_adapter + @since version 1.0.0 + */ + size_type max_size() const noexcept { - public: - output_stream_adapter(std::basic_ostream& s) - : stream(s) - {} - - void write_character(CharType c) override - { - stream.put(c); - } - - void write_characters(const CharType* s, size_t length) override + switch (m_type) { - stream.write(s, static_cast(length)); - } - - private: - std::basic_ostream& stream; - }; - - /// output adapter for basic_string - template - class output_string_adapter : public output_adapter - { - public: - output_string_adapter(std::string& s) - : str(s) - {} + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } - void write_character(CharType c) override - { - str.push_back(c); - } + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } - void write_characters(const CharType* s, size_t length) override - { - str.append(s, length); + default: + { + // all other types have max_size() == size() + return size(); + } } + } - private: - std::basic_string& str; - }; + /// @} - /////////////////// - // serialization // - /////////////////// + /////////////// + // modifiers // + /////////////// - /// @name serialization + /// @name modifiers /// @{ - private: /*! - @brief wrapper around the serialization functions - */ - class serializer - { - public: - /*! - @param[in] s output stream to serialize to - @param[in] ichar indentation character to use - */ - serializer(output_adapter_t s, const char ichar) - : o(s), loc(std::localeconv()), - thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), - decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]), - indent_char(ichar), indent_string(512, indent_char) - {} + @brief clears the contents - // delete because of pointer members - serializer(const serializer&) = delete; - serializer& operator=(const serializer&) = delete; + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called: - /*! - @brief internal implementation of the serialization function + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` - This function is called by the public member function dump and - organizes the serialization internally. The indentation level is - propagated as additional parameter. In case of arrays and objects, the - function is called recursively. + @complexity Linear in the size of the JSON value. - - strings and object keys are escaped using `escape_string()` - - integer numbers are converted implicitly via `operator<<` - - floating-point numbers are converted to a string using `"%g"` format + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} - @param[in] val value to serialize - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) - */ - void dump(const basic_json& val, - const bool pretty_print, - const unsigned int indent_step, - const unsigned int current_indent = 0) + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) { - switch (val.m_type) + case value_t::number_integer: { - case value_t::object: - { - if (val.m_value.object->empty()) - { - o->write_characters("{}", 2); - return; - } + m_value.number_integer = 0; + break; + } - if (pretty_print) - { - o->write_characters("{\n", 2); + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (JSON_UNLIKELY(indent_string.size() < new_indent)) - { - indent_string.resize(indent_string.size() * 2, ' '); - } + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) - { - o->write_characters(indent_string.c_str(), new_indent); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\": ", 3); - dump(i->second, true, indent_step, new_indent); - o->write_characters(",\n", 2); - } - - // last element - assert(i != val.m_value.object->cend()); - o->write_characters(indent_string.c_str(), new_indent); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\": ", 3); - dump(i->second, true, indent_step, new_indent); + case value_t::boolean: + { + m_value.boolean = false; + break; + } - o->write_character('\n'); - o->write_characters(indent_string.c_str(), current_indent); - o->write_character('}'); - } - else - { - o->write_character('{'); + case value_t::string: + { + m_value.string->clear(); + break; + } - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) - { - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\":", 2); - dump(i->second, false, indent_step, current_indent); - o->write_character(','); - } + case value_t::array: + { + m_value.array->clear(); + break; + } - // last element - assert(i != val.m_value.object->cend()); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\":", 2); - dump(i->second, false, indent_step, current_indent); + case value_t::object: + { + m_value.object->clear(); + break; + } - o->write_character('}'); - } + default: + { + break; + } + } + } - return; - } + /*! + @brief add an object to an array - case value_t::array: - { - if (val.m_value.array->empty()) - { - o->write_characters("[]", 2); - return; - } + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. - if (pretty_print) - { - o->write_characters("[\n", 2); + @param[in] val the value to add to the JSON array - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) - { - indent_string.resize(new_indent, ' '); - } + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) - { - o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, indent_step, new_indent); - o->write_characters(",\n", 2); - } + @complexity Amortized constant. - // last element - assert(not val.m_value.array->empty()); - o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, indent_step, new_indent); + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} - o->write_character('\n'); - o->write_characters(indent_string.c_str(), current_indent); - o->write_character(']'); - } - else - { - o->write_character('['); + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + } - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) - { - dump(*i, false, indent_step, current_indent); - o->write_character(','); - } + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } - // last element - assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, indent_step, current_indent); + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // invalidate object + val.m_type = value_t::null; + } - o->write_character(']'); - } + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } - return; - } + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + } - case value_t::string: - { - o->write_character('\"'); - dump_escaped(*val.m_value.string); - o->write_character('\"'); - return; - } + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } - case value_t::boolean: - { - if (val.m_value.boolean) - { - o->write_characters("true", 4); - } - else - { - o->write_characters("false", 5); - } - return; - } + // add element to array + m_value.array->push_back(val); + } - case value_t::number_integer: - { - dump_integer(val.m_value.number_integer); - return; - } + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } - case value_t::number_unsigned: - { - dump_integer(val.m_value.number_unsigned); - return; - } + /*! + @brief add an object to an object - case value_t::number_float: - { - dump_float(val.m_value.number_float); - return; - } + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. - case value_t::discarded: - { - o->write_characters("", 11); - return; - } + @param[in] val the value to add to the JSON object - case value_t::null: - { - o->write_characters("null", 4); - return; - } - } - } + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` - private: - /*! - @brief calculates the extra space to escape a JSON string + @complexity Logarithmic in the size of the container, O(log(`size()`)). - @param[in] s the string to escape - @return the number of characters required to escape string @a s + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} - @complexity Linear in the length of string @a s. - */ - static std::size_t extra_space(const string_t& s) noexcept - { - return std::accumulate(s.begin(), s.end(), size_t{}, - [](size_t res, typename string_t::value_type c) - { - switch (c) - { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - { - // from c (1 byte) to \x (2 bytes) - return res + 1; - } - - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // from c (1 byte) to \uxxxx (6 bytes) - return res + 5; - } + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (not(is_null() or is_object())) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + } - default: - { - return res; - } - } - }); + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); } - /*! - @brief dump escaped string + // add element to array + m_value.object->insert(val); + } - Escape a string by replacing certain special characters by a sequence - of an escape character (backslash) and another character and other - control characters by a sequence of "\u" followed by a four-digit hex - representation. The escaped string is written to output stream @a o. + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } - @param[in] s the string to escape + /*! + @brief add an object to an object - @complexity Linear in the length of string @a s. - */ - void dump_escaped(const string_t& s) const - { - const auto space = extra_space(s); - if (space == 0) - { - o->write_characters(s.c_str(), s.size()); - return; - } + This function allows to use `push_back` with an initializer list. In case - // create a result string of necessary size - string_t result(s.size() + space, '\\'); - std::size_t pos = 0; + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, - for (const auto& c : s) - { - switch (c) - { - // quotation mark (0x22) - case '"': - { - result[pos + 1] = '"'; - pos += 2; - break; - } + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). - // reverse solidus (0x5c) - case '\\': - { - // nothing to change - pos += 2; - break; - } + @param[in] init an initializer list - // backspace (0x08) - case '\b': - { - result[pos + 1] = 'b'; - pos += 2; - break; - } + @complexity Linear in the size of the initializer list @a init. - // formfeed (0x0c) - case '\f': - { - result[pos + 1] = 'f'; - pos += 2; - break; - } + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. - // newline (0x0a) - case '\n': - { - result[pos + 1] = 'n'; - pos += 2; - break; - } + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(std::initializer_list init) + { + if (is_object() and init.size() == 2 and init.begin()->is_string()) + { + const string_t key = *init.begin(); + push_back(typename object_t::value_type(key, *(init.begin() + 1))); + } + else + { + push_back(basic_json(init)); + } + } - // carriage return (0x0d) - case '\r': - { - result[pos + 1] = 'r'; - pos += 2; - break; - } + /*! + @brief add an object to an object + @copydoc push_back(std::initializer_list) + */ + reference operator+=(std::initializer_list init) + { + push_back(init); + return *this; + } - // horizontal tab (0x09) - case '\t': - { - result[pos + 1] = 't'; - pos += 2; - break; - } + /*! + @brief add an object to an array - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // convert a number 0..15 to its hex representation - // (0..f) - static const char hexify[16] = - { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. - // print character c as \uxxxx - for (const char m : - { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] - }) - { - result[++pos] = m; - } + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object - ++pos; - break; - } + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` - default: - { - // all other characters are added as-is - result[pos++] = c; - break; - } - } - } + @complexity Amortized constant. - assert(pos == s.size() + space); - o->write_characters(result.c_str(), result.size()); + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); } - /*! - @brief dump an integer + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } - Dump a given integer to output stream @a o. Works internally with - @a number_buffer. + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } - @param[in] x integer number (signed or unsigned) to dump - @tparam NumberType either @a number_integer_t or @a number_unsigned_t - */ - template::value or - std::is_same::value, int> = 0> - void dump_integer(NumberType x) - { - // special case for "0" - if (x == 0) - { - o->write_character('0'); - return; - } + /*! + @brief add an object to an object if key does not exist - const bool is_negative = x < 0; - size_t i = 0; + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. - // spare 1 byte for '\0' - while (x != 0 and i < number_buffer.size() - 1) - { - const auto digit = std::labs(static_cast(x % 10)); - number_buffer[i++] = static_cast('0' + digit); - x /= 10; - } + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object - // make sure the number has been processed completely - assert(x == 0); + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. - if (is_negative) - { - // make sure there is capacity for the '-' - assert(i < number_buffer.size() - 2); - number_buffer[i++] = '-'; - } + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` - std::reverse(number_buffer.begin(), number_buffer.begin() + i); - o->write_characters(number_buffer.data(), i); - } + @complexity Logarithmic in the size of the container, O(log(`size()`)). - /*! - @brief dump a floating-point number + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} - Dump a given floating-point number to output stream @a o. Works - internally with @a number_buffer. + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); + } - @param[in] x floating-point number to dump - */ - void dump_float(number_float_t x) + // transform null object into an object + if (is_null()) { - // NaN / inf - if (not std::isfinite(x) or std::isnan(x)) - { - o->write_characters("null", 4); - return; - } + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } - // special case for 0.0 and -0.0 - if (x == 0) - { - if (std::signbit(x)) - { - o->write_characters("-0.0", 4); - } - else - { - o->write_characters("0.0", 3); - } - return; - } + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; - // get number of digits for a text -> float -> text round-trip - static constexpr auto d = std::numeric_limits::digits10; + // return pair of iterator and boolean + return {it, res.second}; + } - // the actual conversion - std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), - "%.*g", d, x); + /*! + @brief inserts element - // negative value indicates an error - assert(len > 0); - // check if buffer was large enough - assert(static_cast(len) < number_buffer.size()); + Inserts element @a val before iterator @a pos. - // erase thousands separator - if (thousands_sep != '\0') - { - const auto end = std::remove(number_buffer.begin(), - number_buffer.begin() + len, - thousands_sep); - std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); - len = (end - number_buffer.begin()); - } + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. - // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') - { - for (auto& c : number_buffer) - { - if (c == decimal_point) - { - c = '.'; - break; - } - } - } + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` - o->write_characters(number_buffer.data(), static_cast(len)); + @complexity Constant plus linear in the distance between @a pos and end of + the container. - // determine if need to append ".0" - const bool value_is_int_like = std::none_of(number_buffer.begin(), - number_buffer.begin() + len + 1, - [](char c) - { - return c == '.' or c == 'e'; - }); + @liveexample{The example shows how `insert()` is used.,insert} - if (value_is_int_like) + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) { - o->write_characters(".0", 2); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } - } - private: - /// the output of the serializer - output_adapter_t o = nullptr; + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); + return result; + } - /// a (hopefully) large enough character buffer - std::array number_buffer{{}}; + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } - /// the locale - const std::lconv* loc = nullptr; - /// the locale's thousand separator character - const char thousands_sep = '\0'; - /// the locale's decimal point character - const char decimal_point = '\0'; + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } - /// the indentation character - const char indent_char; + /*! + @brief inserts elements - /// the indentation string - string_t indent_string; - }; + Inserts @a cnt copies of @a val before iterator @a pos. - public: - /*! - @brief serialize to stream + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` - - The indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. - - The indentation characrer can be controlled with the member variable - `fill` of the output stream @a o. For instance, the manipulator - `std::setfill('\\t')` sets indentation to use a tab character rather than - the default space character. + @liveexample{The example shows how `insert()` is used.,insert__count} - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @complexity Linear. - - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} - - @since version 1.0.0; indentaction character added in version 3.0.0 + @since version 1.0.0 */ - friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } - // reset width to 0 for subsequent calls to this stream - o.width(0); + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + return result; + } - // do the actual serialization - serializer s(output_adapter::create(o), o.fill()); - s.dump(j, pretty_print, static_cast(indentation)); - return o; + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } /*! - @brief serialize to stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::ostream& operator<<(std::ostream&, const basic_json&) - instead; that is, replace calls like `j >> o;` with `o << j;`. - */ - JSON_DEPRECATED - friend std::ostream& operator>>(const basic_json& j, std::ostream& o) - { - return o << j; - } - - /// @} - - - ///////////////////// - // deserialization // - ///////////////////// - - /// @name deserialization - /// @{ + @brief inserts elements - /*! - @brief deserialize from an array + Inserts elements from range `[first, last)` before iterator @a pos. - This function reads from an array of 1-byte values. + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` - @param[in] array array to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @return iterator pointing to the first element inserted, or @a pos if + `first==last` - @return result of the deserialization + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @liveexample{The example shows how `insert()` is used.,insert__range} - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (not is_array()) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } - @note A UTF-8 byte order mark is silently ignored. + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} + // check if range iterators belong to the same JSON object + if (first.m_object != last.m_object) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } - @since version 2.0.3 - */ - template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) - { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); - } + if (first.m_object == this or last.m_object == this) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + } - template - static bool accept(T (&array)[N]) - { - // delegate the call to the iterator-range accept overload - return accept(std::begin(array), std::end(array)); + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert( + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); + return result; } /*! - @brief deserialize from string literal - - @tparam CharT character/literal type with size of 1 byte - @param[in] s string literal to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @brief inserts elements - @return result of the deserialization + Inserts elements from initializer list @a ilist before iterator @a pos. - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` - @note A UTF-8 byte order mark is silently ignored. - @note String containers like `std::string` or @ref string_t can be parsed - with @ref parse(const ContiguousContainer&, const parser_callback_t) + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream + @liveexample{The example shows how `insert()` is used.,insert__ilist} - @since version 1.0.0 (originally for @ref string_t) + @since version 1.0.0 */ - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static basic_json parse(const CharT s, - const parser_callback_t cb = nullptr) + iterator insert(const_iterator pos, std::initializer_list ilist) { - return parser(detail::input_adapter_factory::create(s), cb).parse(true); - } + // insert only works for arrays + if (not is_array()) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static bool accept(const CharT s) - { - return parser(detail::input_adapter_factory::create(s)).accept(true); + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); + return result; } /*! - @brief deserialize from stream - - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization + @brief inserts elements - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + Inserts elements from range `[first, last)`. - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert - @note A UTF-8 byte order mark is silently ignored. + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. - @sa @ref parse(const CharT, const parser_callback_t) for a version - that reads from a string + @liveexample{The example shows how `insert()` is used.,insert__range_object} - @since version 1.0.0 + @since version 3.0.0 */ - static basic_json parse(std::istream& i, - const parser_callback_t cb = nullptr) + void insert(const_iterator first, const_iterator last) { - return parser(detail::input_adapter_factory::create(i), cb).parse(true); - } + // insert only works for objects + if (not is_object()) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } - static bool accept(std::istream& i) - { - return parser(detail::input_adapter_factory::create(i)).accept(true); - } + // check if range iterators belong to the same JSON object + if (first.m_object != last.m_object) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } - /*! - @copydoc parse(std::istream&, const parser_callback_t) - */ - static basic_json parse(std::istream&& i, - const parser_callback_t cb = nullptr) - { - return parser(detail::input_adapter_factory::create(i), cb).parse(true); - } + // passed iterators must belong to objects + if (not first.m_object->is_object() or not first.m_object->is_object()) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } - static bool accept(std::istream&& i) - { - return parser(detail::input_adapter_factory::create(i)).accept(true); + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); } /*! - @brief deserialize from an iterator range with contiguous storage - - This function reads from an iterator range of a container with contiguous - storage of 1-byte values. Compatible container types include - `std::vector`, `std::string`, `std::array`, `std::valarray`, and - `std::initializer_list`. Furthermore, C-style arrays can be used with - `std::begin()`/`std::end()`. User-defined containers can be used as long - as they implement random-access iterators and a contiguous storage. - - @pre The iterator range is contiguous. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** - @pre Each element in the range has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with noncompliant iterators and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam IteratorType iterator of container with contiguous storage - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization + @brief exchanges the values - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @param[in,out] other JSON value to exchange the contents with - @note A UTF-8 byte order mark is silently ignored. + @complexity Constant. - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} - @since version 2.0.3 + @since version 1.0.0 */ - template::iterator_category>::value, int>::type = 0> - static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr) - { - return parser(detail::input_adapter_factory::create(first, last), cb).parse(true); - } - - template::iterator_category>::value, int>::type = 0> - static bool accept(IteratorType first, IteratorType last) + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) { - return parser(detail::input_adapter_factory::create(first, last)).accept(true); + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); } /*! - @brief deserialize from a container with contiguous storage + @brief exchanges the values - This function reads from a container with contiguous storage of 1-byte - values. Compatible container types include `std::vector`, `std::string`, - `std::array`, and `std::initializer_list`. User-defined containers can be - used as long as they implement random-access iterators and a contiguous - storage. + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** + @param[in,out] other array to exchange the contents with - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` - @tparam ContiguousContainer container type with contiguous storage - @param[in] c container to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @complexity Constant. - @return result of the deserialization + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (is_array()) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + } + } - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + /*! + @brief exchanges the values - @note A UTF-8 byte order mark is silently ignored. + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + @param[in,out] other object to exchange the contents with - @since version 2.0.3 - */ - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static basic_json parse(const ContiguousContainer& c, - const parser_callback_t cb = nullptr) - { - // delegate the call to the iterator-range parse overload - return parse(std::begin(c), std::end(c), cb); - } + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static bool accept(const ContiguousContainer& c) - { - // delegate the call to the iterator-range accept overload - return accept(std::begin(c), std::end(c)); - } + @complexity Constant. - /*! - @brief deserialize from stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::istream& operator>>(std::istream&, basic_json&) - instead; that is, replace calls like `j << i;` with `i >> j;`. + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 */ - JSON_DEPRECATED - friend std::istream& operator<<(basic_json& j, std::istream& i) + void swap(object_t& other) { - j = parser(detail::input_adapter_factory::create(i)).parse(false); - return i; + // swap only works for objects + if (is_object()) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + } } /*! - @brief deserialize from stream - - Deserializes an input stream to a JSON value. - - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to + @brief exchanges the values - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. + @param[in,out] other string to exchange the contents with - @note A UTF-8 byte order mark is silently ignored. + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} + @complexity Constant. - @sa parse(std::istream&, const parser_callback_t) for a variant with a - parser callback function to filter values while parsing + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} @since version 1.0.0 */ - friend std::istream& operator>>(std::istream& i, basic_json& j) + void swap(string_t& other) { - j = parser(detail::input_adapter_factory::create(i)).parse(false); - return i; + // swap only works for strings + if (is_string()) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + } } /// @} - /////////////////////////// - // convenience functions // - /////////////////////////// + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ /*! - @brief return the type as string + @brief comparison: equal - Returns the type name as string to be used in error messages - usually to - indicate that a function was called on a wrong JSON type. + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note than two NaN values are always treated as unequal. + - Two JSON null values are equal. - @return basically a string representation of a the @a m_type member + @note NaN values never compare equal to themselves or to other NaN values. - @complexity Constant. + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal - @liveexample{The following code exemplifies `type_name()` for all JSON - types.,type_name} + @complexity Linear. - @since version 1.0.0, public since 2.1.0 + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 */ - std::string type_name() const + friend bool operator==(const_reference lhs, const_reference rhs) noexcept { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) { - switch (m_type) + switch (lhs_type) { - case value_t::null: - return "null"; - case value_t::object: - return "object"; case value_t::array: - return "array"; + { + return *lhs.m_value.array == *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object == *rhs.m_value.object; + } + case value_t::null: + { + return true; + } case value_t::string: - return "string"; + { + return *lhs.m_value.string == *rhs.m_value.string; + } case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; + { + return lhs.m_value.boolean == rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer == rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float == rhs.m_value.number_float; + } default: - return "number"; + { + return false; + } } } - } - - - private: - ////////////////////// - // member variables // - ////////////////////// - - /// the type of the current element - value_t m_type = value_t::null; - - /// the value of the current element - json_value m_value = {}; - - - private: - /////////////// - // iterators // - /////////////// - - /*! - @brief an iterator for primitive JSON types - - This class models an iterator for primitive JSON types (boolean, number, - string). It's only purpose is to allow the iterator/const_iterator classes - to "iterate" over primitive values. Internally, the iterator is modeled by - a `difference_type` variable. Value begin_value (`0`) models the begin, - end_value (`1`) models past the end. - */ - class primitive_iterator_t - { - public: - - difference_type get_value() const noexcept + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { - return m_it; + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; } - /// set iterator to a defined beginning - void set_begin() noexcept + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { - m_it = begin_value; + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); } - - /// set iterator to a defined past the end - void set_end() noexcept + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { - m_it = end_value; + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; } - - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { - return (m_it == begin_value); + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); } - - /// return whether the iterator is at end - constexpr bool is_end() const noexcept + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { - return (m_it == end_value); + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; } - - friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { - return lhs.m_it == rhs.m_it; + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); } - friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return !(lhs == rhs); - } + return false; + } - friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it < rhs.m_it; - } + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs == basic_json(rhs)); + } - friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it <= rhs.m_it; - } + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) == rhs); + } - friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it > rhs.m_it; - } + /*! + @brief comparison: not equal - friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it >= rhs.m_it; - } + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - primitive_iterator_t operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal - friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it - rhs.m_it; - } + @complexity Linear. - friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) - { - return os << it.m_it; - } + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} - primitive_iterator_t& operator++() - { - ++m_it; - return *this; - } + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs == rhs); + } - primitive_iterator_t operator++(int) - { - auto result = *this; - m_it++; - return result; - } + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs != basic_json(rhs)); + } - primitive_iterator_t& operator--() - { - --m_it; - return *this; - } + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) != rhs); + } - primitive_iterator_t operator--(int) + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) { - auto result = *this; - m_it--; - return result; + switch (lhs_type) + { + case value_t::array: + { + return (*lhs.m_value.array) < (*rhs.m_value.array); + } + case value_t::object: + { + return *lhs.m_value.object < *rhs.m_value.object; + } + case value_t::null: + { + return false; + } + case value_t::string: + { + return *lhs.m_value.string < *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean < rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer < rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float < rhs.m_value.number_float; + } + default: + { + return false; + } + } } - - primitive_iterator_t& operator+=(difference_type n) + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { - m_it += n; - return *this; + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; } - - primitive_iterator_t& operator-=(difference_type n) + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { - m_it -= n; - return *this; + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } - private: - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; - - /// iterator as signed integer type - difference_type m_it = std::numeric_limits::denorm_min(); - }; + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } /*! - @brief an iterator value + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs < basic_json(rhs)); + } - @note This structure could easily be a union, but MSVC currently does not - allow unions members with complex constructors, see - https://github.com/nlohmann/json/pull/105. + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) */ - struct internal_iterator + template::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept { - /// iterator for JSON objects - typename object_t::iterator object_iterator; - /// iterator for JSON arrays - typename array_t::iterator array_iterator; - /// generic iterator for all other types - primitive_iterator_t primitive_iterator; + return (basic_json(lhs) < rhs); + } - /// create an uninitialized internal_iterator - internal_iterator() noexcept - : object_iterator(), array_iterator(), primitive_iterator() - {} - }; + /*! + @brief comparison: less than or equal - /// proxy class for the iterator_wrapper functions - template - class iteration_proxy - { - private: - /// helper class for iteration - class iteration_proxy_internal - { - private: - /// the iterator - IteratorType anchor; - /// an index for arrays (used to create key names) - size_t array_index = 0; + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. - public: - explicit iteration_proxy_internal(IteratorType it) noexcept - : anchor(it) - {} + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() - { - return *this; - } + @complexity Linear. - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() - { - ++anchor; - ++array_index; + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} - return *this; - } + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return not (rhs < lhs); + } - /// inequality operator (needed for range-based for) - bool operator!= (const iteration_proxy_internal& o) const - { - return anchor != o.anchor; - } + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs <= basic_json(rhs)); + } - /// return key of the iterator - typename basic_json::string_t key() const - { - assert(anchor.m_object != nullptr); + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) <= rhs); + } - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } + /*! + @brief comparison: greater than - // use key from the object - case value_t::object: - { - return anchor.key(); - } + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. - // use an empty key for all primitive types - default: - { - return ""; - } - } - } + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } - }; + @complexity Linear. - /// the container to iterate - typename IteratorType::reference container; + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} - public: - /// construct iteration proxy from a container - explicit iteration_proxy(typename IteratorType::reference cont) - : container(cont) - {} + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs <= rhs); + } - /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() noexcept - { - return iteration_proxy_internal(container.begin()); - } + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs > basic_json(rhs)); + } - /// return iterator end (needed for range-based for) - iteration_proxy_internal end() noexcept - { - return iteration_proxy_internal(container.end()); - } - }; + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) > rhs); + } - public: /*! - @brief a template for a random access iterator for the @ref basic_json class + @brief comparison: greater than or equal - This class implements a both iterators (iterator and const_iterator) for the - @ref basic_json class. + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. - @note An iterator is called *initialized* when a pointer to a JSON value - has been set (e.g., by a constructor or a copy assignment). If the - iterator is default-constructed, it is *uninitialized* and most - methods are undefined. **The library uses assertions to detect calls - on uninitialized iterators.** + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. + @complexity Linear. - @since version 1.0.0, simplified in version 2.0.9 + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 */ - template - class iter_impl : public std::iterator + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { - /// allow basic_json to access private members - friend class basic_json; + return not (lhs < rhs); + } - // make sure U is basic_json or const basic_json - static_assert(std::is_same::value - or std::is_same::value, - "iter_impl only accepts (const) basic_json"); + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs >= basic_json(rhs)); + } - public: - /// the type of the values when the iterator is dereferenced - using value_type = typename basic_json::value_type; - /// a type to represent differences between iterators - using difference_type = typename basic_json::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = typename std::conditional::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; - /// defines a reference to the type iterated over (value_type) - using reference = typename std::conditional::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; - /// the category of the iterator - using iterator_category = std::bidirectional_iterator_tag; + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) >= rhs); + } - /// default constructor - iter_impl() = default; + /// @} - /*! - @brief constructor for a given JSON instance - @param[in] object pointer to a JSON object for this iterator - @pre object != nullptr - @post The iterator is initialized; i.e. `m_object != nullptr`. - */ - explicit iter_impl(pointer object) noexcept - : m_object(object) - { - assert(m_object != nullptr); + private: + ///////////////////// + // output adapters // + ///////////////////// - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } + /// abstract output adapter interface + template + class output_adapter + { + public: + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, size_t length) = 0; + virtual ~output_adapter() {} - case basic_json::value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } + static std::shared_ptr> create(std::vector& vec) + { + return std::make_shared>(vec); + } - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } + static std::shared_ptr> create(std::ostream& s) + { + return std::make_shared>(s); } - /*! - @note The conventional copy constructor and copy assignment are - implicitly defined. - Combined with the following converting constructor and assignment, - they support: copy from iterator to iterator, - copy from const iterator to const iterator, - and conversion from iterator to const iterator. - However conversion from const iterator to iterator is not defined. - */ + static std::shared_ptr> create(std::string& s) + { + return std::make_shared>(s); + } + }; - /*! - @brief converting constructor - @param[in] other non-const iterator to copy from - @note It is not checked whether @a other is initialized. - */ - iter_impl(const iter_impl& other) noexcept - : m_object(other.m_object), m_it(other.m_it) + /// a type to simplify interfaces + template + using output_adapter_t = std::shared_ptr>; + + /// output adapter for byte vectors + template + class output_vector_adapter : public output_adapter + { + public: + output_vector_adapter(std::vector& vec) + : v(vec) {} - /*! - @brief converting assignment - @param[in,out] other non-const iterator to copy from - @return const/non-const iterator - @note It is not checked whether @a other is initialized. - */ - iter_impl& operator=(const iter_impl& other) noexcept + void write_character(CharType c) override { - m_object = other.m_object; - m_it = other.m_it; - return *this; + v.push_back(c); } - private: - /*! - @brief set the iterator to the first value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_begin() noexcept + void write_characters(const CharType* s, size_t length) override { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } + std::copy(s, s + length, std::back_inserter(v)); + } - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } + private: + std::vector& v; + }; - case basic_json::value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } + /// output adapter for output streams + template + class output_stream_adapter : public output_adapter + { + public: + output_stream_adapter(std::basic_ostream& s) + : stream(s) + {} - default: - { - m_it.primitive_iterator.set_begin(); - break; - } - } + void write_character(CharType c) override + { + stream.put(c); } - /*! - @brief set the iterator past the last value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_end() noexcept + void write_characters(const CharType* s, size_t length) override { - assert(m_object != nullptr); + stream.write(s, static_cast(length)); + } - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } + private: + std::basic_ostream& stream; + }; - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } + /// output adapter for basic_string + template + class output_string_adapter : public output_adapter + { + public: + output_string_adapter(std::string& s) + : str(s) + {} - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } + void write_character(CharType c) override + { + str.push_back(c); + } + + void write_characters(const CharType* s, size_t length) override + { + str.append(s, length); } + private: + std::basic_string& str; + }; + + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + + private: + /*! + @brief wrapper around the serialization functions + */ + class serializer + { public: /*! - @brief return a reference to the value pointed to by the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use */ - reference operator*() const - { - assert(m_object != nullptr); + serializer(output_adapter_t s, const char ichar) + : o(s), loc(std::localeconv()), + thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), + decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]), + indent_char(ichar), indent_string(512, indent_char) + {} - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } + /*! + @brief internal implementation of the serialization function - case basic_json::value_t::null: - { - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + This function is called by the public member function dump and + organizes the serialization internally. The indentation level is + propagated as additional parameter. In case of arrays and objects, the + function is called recursively. - default: + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const basic_json& val, + const bool pretty_print, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: { - if (m_it.primitive_iterator.is_begin()) + if (val.m_value.object->empty()) { - return *m_object; + o->write_characters("{}", 2); + return; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } - } - } + if (pretty_print) + { + o->write_characters("{\n", 2); - /*! - @brief dereference the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - pointer operator->() const - { - assert(m_object != nullptr); + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\": ", 3); + dump(i->second, true, indent_step, new_indent); + o->write_characters(",\n", 2); + } - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } + // last element + assert(i != val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\": ", 3); + dump(i->second, true, indent_step, new_indent); - default: - { - if (m_it.primitive_iterator.is_begin()) - { - return m_object; + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); } + else + { + o->write_character('{'); - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } - } - } - - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int) - { - auto result = *this; - ++(*this); - return result; - } + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\":", 2); + dump(i->second, false, indent_step, current_indent); + o->write_character(','); + } - /*! - @brief pre-increment (++it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator++() - { - assert(m_object != nullptr); + // last element + assert(i != val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\":", 2); + dump(i->second, false, indent_step, current_indent); - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, 1); - break; - } + o->write_character('}'); + } - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, 1); - break; + return; } - default: + case value_t::array: { - ++m_it.primitive_iterator; - break; - } - } + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } - return *this; - } + if (pretty_print) + { + o->write_characters("[\n", 2); - /*! - @brief post-decrement (it--) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator--(int) - { - auto result = *this; - --(*this); - return result; - } + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (indent_string.size() < new_indent) + { + indent_string.resize(new_indent, ' '); + } - /*! - @brief pre-decrement (--it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator--() - { - assert(m_object != nullptr); + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, indent_step, new_indent); + o->write_characters(",\n", 2); + } - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; + // last element + assert(not val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, indent_step, current_indent); + o->write_character(','); + } + + // last element + assert(not val.m_value.array->empty()); + dump(val.m_value.array->back(), false, indent_step, current_indent); + + o->write_character(']'); + } + + return; } - case basic_json::value_t::array: + case value_t::string: { - std::advance(m_it.array_iterator, -1); - break; + o->write_character('\"'); + dump_escaped(*val.m_value.string); + o->write_character('\"'); + return; } - default: + case value_t::boolean: { - --m_it.primitive_iterator; - break; + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; } - } - return *this; - } - - /*! - @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator==(const iter_impl& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); - } + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } - assert(m_object != nullptr); + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } - switch (m_object->m_type) - { - case basic_json::value_t::object: + case value_t::number_float: { - return (m_it.object_iterator == other.m_it.object_iterator); + dump_float(val.m_value.number_float); + return; } - case basic_json::value_t::array: + case value_t::discarded: { - return (m_it.array_iterator == other.m_it.array_iterator); + o->write_characters("", 11); + return; } - default: + case value_t::null: { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); + o->write_characters("null", 4); + return; } } } + private: /*! - @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator!=(const iter_impl& other) const - { - return not operator==(other); - } + @brief calculates the extra space to escape a JSON string - /*! - @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @param[in] s the string to escape + @return the number of characters required to escape string @a s + + @complexity Linear in the length of string @a s. */ - bool operator<(const iter_impl& other) const + static std::size_t extra_space(const string_t& s) noexcept { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) { - case basic_json::value_t::object: + switch (c) { - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); - } + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + { + // from c (1 byte) to \x (2 bytes) + return res + 1; + } - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x0b: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + { + // from c (1 byte) to \uxxxx (6 bytes) + return res + 5; + } - default: - { - return (m_it.primitive_iterator < other.m_it.primitive_iterator); + default: + { + return res; + } } - } + }); } /*! - @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<=(const iter_impl& other) const - { - return not other.operator < (*this); - } + @brief dump escaped string - /*! - @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>(const iter_impl& other) const - { - return not operator<=(other); - } + Escape a string by replacing certain special characters by a sequence + of an escape character (backslash) and another character and other + control characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. - /*! - @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>=(const iter_impl& other) const - { - return not operator<(other); - } + @param[in] s the string to escape - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @complexity Linear in the length of string @a s. */ - iter_impl& operator+=(difference_type i) + void dump_escaped(const string_t& s) const { - assert(m_object != nullptr); - - switch (m_object->m_type) + const auto space = extra_space(s); + if (space == 0) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - } + o->write_characters(s.c_str(), s.size()); + return; + } - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } + // create a result string of necessary size + string_t result(s.size() + space, '\\'); + std::size_t pos = 0; - default: + for (const auto& c : s) + { + switch (c) { - m_it.primitive_iterator += i; - break; - } - } + // quotation mark (0x22) + case '"': + { + result[pos + 1] = '"'; + pos += 2; + break; + } - return *this; - } + // reverse solidus (0x5c) + case '\\': + { + // nothing to change + pos += 2; + break; + } - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator-=(difference_type i) - { - return operator+=(-i); - } + // backspace (0x08) + case '\b': + { + result[pos + 1] = 'b'; + pos += 2; + break; + } - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } + // formfeed (0x0c) + case '\f': + { + result[pos + 1] = 'f'; + pos += 2; + break; + } - /*! - @brief addition of distance and iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - friend iter_impl operator+(difference_type i, const iter_impl& it) - { - auto result = it; - result += i; - return result; - } + // newline (0x0a) + case '\n': + { + result[pos + 1] = 'n'; + pos += 2; + break; + } - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } + // carriage return (0x0d) + case '\r': + { + result[pos + 1] = 'r'; + pos += 2; + break; + } - /*! - @brief return difference - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - difference_type operator-(const iter_impl& other) const - { - assert(m_object != nullptr); + // horizontal tab (0x09) + case '\t': + { + result[pos + 1] = 't'; + pos += 2; + break; + } - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - } + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x0b: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + { + // convert a number 0..15 to its hex representation + // (0..f) + static const char hexify[16] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } + // print character c as \uxxxx + for (const char m : + { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] + }) + { + result[++pos] = m; + } - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; + ++pos; + break; + } + + default: + { + // all other characters are added as-is + result[pos++] = c; + break; + } } } + + assert(pos == s.size() + space); + o->write_characters(result.c_str(), result.size()); } /*! - @brief access to successor - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ - reference operator[](difference_type n) const + template::value or + std::is_same::value, int> = 0> + void dump_integer(NumberType x) { - assert(m_object != nullptr); - - switch (m_object->m_type) + // special case for "0" + if (x == 0) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); - } - - case basic_json::value_t::array: - { - return *std::next(m_it.array_iterator, n); - } - - case basic_json::value_t::null: - { - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + o->write_character('0'); + return; + } - default: - { - if (m_it.primitive_iterator.get_value() == -n) - { - return *m_object; - } + const bool is_negative = x < 0; + size_t i = 0; - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + // spare 1 byte for '\0' + while (x != 0 and i < number_buffer.size() - 1) + { + const auto digit = std::labs(static_cast(x % 10)); + number_buffer[i++] = static_cast('0' + digit); + x /= 10; } - } - /*! - @brief return the key of an object iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - typename object_t::key_type key() const - { - assert(m_object != nullptr); + // make sure the number has been processed completely + assert(x == 0); - if (m_object->is_object()) + if (is_negative) { - return m_it.object_iterator->first; + // make sure there is capacity for the '-' + assert(i < number_buffer.size() - 2); + number_buffer[i++] = '-'; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + std::reverse(number_buffer.begin(), number_buffer.begin() + i); + o->write_characters(number_buffer.data(), i); } /*! - @brief return the value of an iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works + internally with @a number_buffer. + + @param[in] x floating-point number to dump */ - reference value() const + void dump_float(number_float_t x) { - return operator*(); - } + // NaN / inf + if (not std::isfinite(x) or std::isnan(x)) + { + o->write_characters("null", 4); + return; + } - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - struct internal_iterator m_it = internal_iterator(); - }; + // special case for 0.0 and -0.0 + if (x == 0) + { + if (std::signbit(x)) + { + o->write_characters("-0.0", 4); + } + else + { + o->write_characters("0.0", 3); + } + return; + } - /*! - @brief a template for a reverse iterator class + // get number of digits for a text -> float -> text round-trip + static constexpr auto d = std::numeric_limits::digits10; - @tparam Base the base iterator type to reverse. Valid types are @ref - iterator (to create @ref reverse_iterator) and @ref const_iterator (to - create @ref const_reverse_iterator). + // the actual conversion + std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), + "%.*g", d, x); - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element (only if @a Base is - @ref iterator). + // negative value indicates an error + assert(len > 0); + // check if buffer was large enough + assert(static_cast(len) < number_buffer.size()); - @since version 1.0.0 - */ - template - class json_reverse_iterator : public std::reverse_iterator - { - public: - /// shortcut to the reverse iterator adaptor - using base_iterator = std::reverse_iterator; - /// the reference type for the pointed-to element - using reference = typename Base::reference; + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, + thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + assert((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } - /// create reverse iterator from iterator - json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) - {} + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') + { + for (auto& c : number_buffer) + { + if (c == decimal_point) + { + c = '.'; + break; + } + } + } - /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) noexcept - : base_iterator(it) - {} + o->write_characters(number_buffer.data(), static_cast(len)); - /// post-increment (it++) - json_reverse_iterator operator++(int) - { - return static_cast(base_iterator::operator++(1)); - } + // determine if need to append ".0" + const bool value_is_int_like = std::none_of(number_buffer.begin(), + number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' or c == 'e'; + }); - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - return static_cast(base_iterator::operator++()); + if (value_is_int_like) + { + o->write_characters(".0", 2); + } } - /// post-decrement (it--) - json_reverse_iterator operator--(int) - { - return static_cast(base_iterator::operator--(1)); - } + private: + /// the output of the serializer + output_adapter_t o = nullptr; - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - return static_cast(base_iterator::operator--()); - } + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - return static_cast(base_iterator::operator+=(i)); - } + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - return static_cast(base_iterator::operator+(i)); - } + /// the indentation character + const char indent_char; - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - return static_cast(base_iterator::operator-(i)); - } + /// the indentation string + string_t indent_string; + }; - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return base_iterator(*this) - base_iterator(other); - } + public: + /*! + @brief serialize to stream - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. - /// return the key of an object iterator - typename object_t::key_type key() const - { - auto it = --this->base(); - return it.key(); - } + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } - }; + - The indentation characrer can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize - private: - ////////////////////////////////////////// - // binary serialization/deserialization // - ////////////////////////////////////////// + @return the stream @a o - /// @name binary serialization/deserialization support - /// @{ + @complexity Linear. - /*! - @brief deserialization of CBOR and MessagePack values + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0; indentaction character added in version 3.0.0 */ - class binary_reader + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { - public: - /*! - @brief create a binary reader + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); - @param[in] adapter input adapter to read from - */ - explicit binary_reader(detail::input_adapter_t adapter) - : ia(adapter), is_little_endian(little_endianess()) - { - assert(ia); - } + // reset width to 0 for subsequent calls to this stream + o.width(0); - /*! - @brief create a JSON value from CBOR input + // do the actual serialization + serializer s(output_adapter::create(o), o.fill()); + s.dump(j, pretty_print, static_cast(indentation)); + return o; + } - @param[in] get_char whether a new character should be retrieved from - the input (true, default) or whether the last - read character should be considered instead + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in a + future version of the library. Please use + @ref std::ostream& operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + */ + JSON_DEPRECATED + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } - @return JSON value created from CBOR input + /// @} - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_cbor(const bool get_char = true) - { - switch (get_char ? get() : current) - { - // EOF - case std::char_traits::eof(): - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } - // Integer 0x00..0x17 (0..23) - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - { - return static_cast(current); - } + ///////////////////// + // deserialization // + ///////////////////// - case 0x18: // Unsigned integer (one-byte uint8_t follows) - { - return get_number(); - } + /// @name deserialization + /// @{ - case 0x19: // Unsigned integer (two-byte uint16_t follows) - { - return get_number(); - } + /*! + @brief deserialize from an array - case 0x1a: // Unsigned integer (four-byte uint32_t follows) - { - return get_number(); - } + This function reads from an array of 1-byte values. - case 0x1b: // Unsigned integer (eight-byte uint64_t follows) - { - return get_number(); - } + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** - // Negative integer -1-0x00..-1-0x17 (-1..-24) - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - { - return static_cast(0x20 - 1 - current); - } + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case 0x38: // Negative integer (one-byte uint8_t follows) - { - // must be uint8_t ! - return static_cast(-1) - get_number(); - } + @return result of the deserialization - case 0x39: // Negative integer -1-n (two-byte uint16_t follows) - { - return static_cast(-1) - get_number(); - } + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) - { - return static_cast(-1) - get_number(); - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) - { - return static_cast(-1) - static_cast(get_number()); - } + @note A UTF-8 byte order mark is silently ignored. - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - case 0x7f: // UTF-8 string (indefinite length) - { - return get_cbor_string(); - } + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} - // array (0x00..0x17 data items follow) - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } - case 0x98: // array (one-byte uint8_t for n follows) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } + template + static bool accept(T (&array)[N]) + { + // delegate the call to the iterator-range accept overload + return accept(std::begin(array), std::end(array)); + } - case 0x99: // array (two-byte uint16_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } + /*! + @brief deserialize from string literal - case 0x9a: // array (four-byte uint32_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case 0x9b: // array (eight-byte uint64_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } + @return result of the deserialization - case 0x9f: // array (indefinite length) - { - basic_json result = value_t::array; - while (get() != 0xff) - { - result.push_back(parse_cbor(false)); - } - return result; - } + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - // map (0x00..0x17 pairs of data items follow) - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - case 0xb8: // map (one-byte uint8_t for n follows) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) - case 0xb9: // map (two-byte uint16_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} - case 0xba: // map (four-byte uint32_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream - case 0xbb: // map (eight-byte uint64_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } + @since version 1.0.0 (originally for @ref string_t) + */ + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static basic_json parse(const CharT s, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(s), cb).parse(true); + } - case 0xbf: // map (indefinite length) - { - basic_json result = value_t::object; - while (get() != 0xff) - { - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static bool accept(const CharT s) + { + return parser(detail::input_adapter_factory::create(s)).accept(true); + } - case 0xf4: // false - { - return false; - } + /*! + @brief deserialize from stream - case 0xf5: // true - { - return true; - } + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case 0xf6: // null - { - return value_t::null; - } + @return result of the deserialization - case 0xf9: // Half-Precision Float (two-byte IEEE 754) - { - const int byte1 = get(); - check_eof(); - const int byte2 = get(); - check_eof(); + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - // code from RFC 7049, Appendix D, Figure 3: - // As half-precision floating-point numbers were only added - // to IEEE 754 in 2008, today's programming platforms often - // still only have limited support for them. It is very - // easy to include at least decoding support for them even - // without such support. An example of a small decoder for - // half-precision floating-point numbers in the C language - // is shown in Fig. 3. - const int half = (byte1 << 8) + byte2; - const int exp = (half >> 10) & 0x1f; - const int mant = half & 0x3ff; - double val; - if (exp == 0) - { - val = std::ldexp(mant, -24); - } - else if (exp != 31) - { - val = std::ldexp(mant + 1024, exp - 25); - } - else - { - val = (mant == 0) - ? std::numeric_limits::infinity() - : std::numeric_limits::quiet_NaN(); - } - return (half & 0x8000) != 0 ? -val : val; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - case 0xfa: // Single-Precision Float (four-byte IEEE 754) - { - return get_number(); - } + @note A UTF-8 byte order mark is silently ignored. - case 0xfb: // Double-Precision Float (eight-byte IEEE 754) - { - return get_number(); - } + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} - default: // anything else (0xFF is handled inside the other types) - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); - } - } - } + @sa @ref parse(const CharT, const parser_callback_t) for a version + that reads from a string - /*! - @brief create a JSON value from MessagePack input + @since version 1.0.0 + */ + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(i), cb).parse(true); + } - @return JSON value created from MessagePack input + static bool accept(std::istream& i) + { + return parser(detail::input_adapter_factory::create(i)).accept(true); + } - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_msgpack() - { - switch (get()) - { - // EOF - case std::char_traits::eof(): - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } + /*! + @copydoc parse(std::istream&, const parser_callback_t) + */ + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(i), cb).parse(true); + } - // positive fixint - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5a: - case 0x5b: - case 0x5c: - case 0x5d: - case 0x5e: - case 0x5f: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7a: - case 0x7b: - case 0x7c: - case 0x7d: - case 0x7e: - case 0x7f: - { - return static_cast(current); - } + static bool accept(std::istream&& i) + { + return parser(detail::input_adapter_factory::create(i)).accept(true); + } - // fixmap - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } + /*! + @brief deserialize from an iterator range with contiguous storage - // fixarray - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - return get_msgpack_string(); - } + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** - case 0xc0: // nil - { - return value_t::null; - } + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. - case 0xc2: // false - { - return false; - } + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case 0xc3: // true - { - return true; - } + @return result of the deserialization - case 0xca: // float 32 - { - return get_number(); - } + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - case 0xcb: // float 64 - { - return get_number(); - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - case 0xcc: // uint 8 - { - return get_number(); - } + @note A UTF-8 byte order mark is silently ignored. - case 0xcd: // uint 16 - { - return get_number(); - } + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} - case 0xce: // uint 32 - { - return get_number(); - } + @since version 2.0.3 + */ + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(first, last), cb).parse(true); + } - case 0xcf: // uint 64 - { - return get_number(); - } + template::iterator_category>::value, int>::type = 0> + static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter_factory::create(first, last)).accept(true); + } - case 0xd0: // int 8 - { - return get_number(); - } + /*! + @brief deserialize from a container with contiguous storage - case 0xd1: // int 16 - { - return get_number(); - } + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. - case 0xd2: // int 32 - { - return get_number(); - } + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** - case 0xd3: // int 64 - { - return get_number(); - } + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. - case 0xd9: // str 8 - case 0xda: // str 16 - case 0xdb: // str 32 - { - return get_msgpack_string(); - } + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case 0xdc: // array 16 - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } + @return result of the deserialization - case 0xdd: // array 32 - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - case 0xde: // map 16 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - case 0xdf: // map 32 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } + @note A UTF-8 byte order mark is silently ignored. - // positive fixint - case 0xe0: - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe4: - case 0xe5: - case 0xe6: - case 0xe7: - case 0xe8: - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xed: - case 0xee: - case 0xef: - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - case 0xff: - { - return static_cast(current); - } + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - default: // anything else - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + ss.str())); - } - } - } + @since version 2.0.3 + */ + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } - /*! - @brief determine system byte order + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static bool accept(const ContiguousContainer& c) + { + // delegate the call to the iterator-range accept overload + return accept(std::begin(c), std::end(c)); + } - @return true iff system's byte order is little endian + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in a + future version of the library. Please use + @ref std::istream& operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + */ + JSON_DEPRECATED + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + j = parser(detail::input_adapter_factory::create(i)).parse(false); + return i; + } - @note from http://stackoverflow.com/a/1001328/266378 - */ - static bool little_endianess() noexcept - { - int num = 1; - return (*reinterpret_cast(&num) == 1); - } + /*! + @brief deserialize from stream - private: - /*! - @brief get next character from the input + Deserializes an input stream to a JSON value. - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns - `std::char_traits::eof()` in that case. + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to - @return character read from the input - */ - int get() - { - ++chars_read; - return (current = ia->get_character()); - } + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - /* - @brief read a number from the input + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. - @tparam NumberType the type of the number + @note A UTF-8 byte order mark is silently ignored. - @return number of type @a NumberType + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing - @throw parse_error.110 if input has less than `sizeof(NumberType)` - bytes - */ - template - NumberType get_number() - { - // step 1: read input into array with system's byte order - std::array vec; - for (size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - check_eof(); + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + j = parser(detail::input_adapter_factory::create(i)).parse(false); + return i; + } - // reverse byte order prior to conversion if necessary - if (is_little_endian) - { - vec[sizeof(NumberType) - i - 1] = static_cast(current); - } - else - { - vec[i] = static_cast(current); // LCOV_EXCL_LINE - } - } + /// @} - // step 2: convert array into number of type T and return - NumberType result; - std::memcpy(&result, vec.data(), sizeof(NumberType)); - return result; - } + /////////////////////////// + // convenience functions // + /////////////////////////// - /*! - @brief create a string by reading characters from the input + /*! + @brief return the type as string - @param[in] len number of bytes to read + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref check_eof() detects the end of - the input before we run out of string memory. + @return basically a string representation of a the @a m_type member - @return string created by reading @a len bytes + @complexity Constant. - @throw parse_error.110 if input has less than @a len bytes - */ - std::string get_string(const size_t len) + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @since version 1.0.0, public since 2.1.0 + */ + std::string type_name() const + { { - std::string result; - for (size_t i = 0; i < len; ++i) + switch (m_type) { - get(); - check_eof(); - result.append(1, static_cast(current)); + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; } - return result; } + } - /*! - @brief reads a CBOR string - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - Additionally, CBOR's strings with indefinite lengths are supported. + private: + ////////////////////// + // member variables // + ////////////////////// - @return string + /// the type of the current element + value_t m_type = value_t::null; - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpected byte is read - */ - std::string get_cbor_string() - { - check_eof(); + /// the value of the current element + json_value m_value = {}; - switch (current) - { - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - { - const auto len = static_cast(current & 0x1f); - return get_string(len); - } - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + private: + /////////////// + // iterators // + /////////////// - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + /*! + @brief an iterator for primitive JSON types - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + This class models an iterator for primitive JSON types (boolean, number, + string). It's only purpose is to allow the iterator/const_iterator classes + to "iterate" over primitive values. Internally, the iterator is modeled by + a `difference_type` variable. Value begin_value (`0`) models the begin, + end_value (`1`) models past the end. + */ + class primitive_iterator_t + { + public: - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + difference_type get_value() const noexcept + { + return m_it; + } + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } - case 0x7f: // UTF-8 string (indefinite length) - { - std::string result; - while (get() != 0xff) - { - check_eof(); - result.append(1, static_cast(current)); - } - return result; - } + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } - default: - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); - } - } + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return (m_it == begin_value); } - /*! - @brief reads a MessagePack string + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return (m_it == end_value); + } - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } - @return string + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return !(lhs == rhs); + } - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpected byte is read - */ - std::string get_msgpack_string() + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - check_eof(); + return lhs.m_it < rhs.m_it; + } - switch (current) - { - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - const auto len = static_cast(current & 0x1f); - return get_string(len); - } + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it <= rhs.m_it; + } - case 0xd9: // str 8 - { - const auto len = static_cast(get_number()); - return get_string(len); - } + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it > rhs.m_it; + } - case 0xda: // str 16 - { - const auto len = static_cast(get_number()); - return get_string(len); - } + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it >= rhs.m_it; + } - case 0xdb: // str 32 - { - const auto len = static_cast(get_number()); - return get_string(len); - } + primitive_iterator_t operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } - default: - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + ss.str())); - } - } + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; } - /*! - @brief check if input ended - @throw parse_error.110 if input ended - */ - void check_eof() const + friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) { - if (JSON_UNLIKELY(current == std::char_traits::eof())) - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } + return os << it.m_it; } - private: - /// input adapter - detail::input_adapter_t ia = nullptr; + primitive_iterator_t& operator++() + { + ++m_it; + return *this; + } - /// the current character - int current = std::char_traits::eof(); + primitive_iterator_t operator++(int) + { + auto result = *this; + m_it++; + return result; + } - /// the number of characters read - size_t chars_read = 0; + primitive_iterator_t& operator--() + { + --m_it; + return *this; + } - /// whether we can assume little endianess - const bool is_little_endian = true; + primitive_iterator_t operator--(int) + { + auto result = *this; + m_it--; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) + { + m_it -= n; + return *this; + } + + private: + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = std::numeric_limits::denorm_min(); }; /*! - @brief serialization to CBOR and MessagePack values + @brief an iterator value + + @note This structure could easily be a union, but MSVC currently does not + allow unions members with complex constructors, see + https://github.com/nlohmann/json/pull/105. */ - class binary_writer + struct internal_iterator { - public: - /*! - @brief create a binary writer + /// iterator for JSON objects + typename object_t::iterator object_iterator; + /// iterator for JSON arrays + typename array_t::iterator array_iterator; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator; - @param[in] adapter output adapter to write to - */ - explicit binary_writer(output_adapter_t adapter) - : is_little_endian(binary_reader::little_endianess()), oa(adapter) - { - assert(oa); - } + /// create an uninitialized internal_iterator + internal_iterator() noexcept + : object_iterator(), array_iterator(), primitive_iterator() + {} + }; - /*! - @brief[in] j JSON value to serialize - */ - void write_cbor(const basic_json& j) + /// proxy class for the iterator_wrapper functions + template + class iteration_proxy + { + private: + /// helper class for iteration + class iteration_proxy_internal { - switch (j.type()) + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + size_t array_index = 0; + + public: + explicit iteration_proxy_internal(IteratorType it) noexcept + : anchor(it) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() { - case value_t::null: - { - oa->write_character(0xf6); - break; - } + return *this; + } - case value_t::boolean: - { - oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); - break; - } + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; - case value_t::number_integer: + return *this; + } + + /// inequality operator (needed for range-based for) + bool operator!= (const iteration_proxy_internal& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + typename basic_json::string_t key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) { - if (j.m_value.number_integer >= 0) + // use integer array index as key + case value_t::array: { - // CBOR does not differentiate between positive signed - // integers and unsigned integers. Therefore, we used the - // code from the value_t::number_unsigned case here. - if (j.m_value.number_integer <= 0x17) - { - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_integer)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_integer)); - } + return std::to_string(array_index); } - else - { - // The conversions below encode the sign in the first - // byte, and the value is converted to a positive number. - const auto positive_number = -1 - j.m_value.number_integer; - if (j.m_value.number_integer >= -24) - { - write_number(static_cast(0x20 + positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x38); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x39); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x3a); - write_number(static_cast(positive_number)); - } - else - { - oa->write_character(0x3b); - write_number(static_cast(positive_number)); - } - } - break; - } - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned <= 0x17) - { - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + // use key from the object + case value_t::object: { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_unsigned)); + return anchor.key(); } - else + + // use an empty key for all primitive types + default: { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_unsigned)); + return ""; } - break; } + } - case value_t::number_float: + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } + }; + + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) + : container(cont) + {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_internal begin() noexcept + { + return iteration_proxy_internal(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_internal end() noexcept + { + return iteration_proxy_internal(container.end()); + } + }; + + public: + /*! + @brief a template for a random access iterator for the @ref basic_json class + + This class implements a both iterators (iterator and const_iterator) for the + @ref basic_json class. + + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + + @since version 1.0.0, simplified in version 2.0.9 + */ + template + class iter_impl : public std::iterator + { + /// allow basic_json to access private members + friend class basic_json; + + // make sure U is basic_json or const basic_json + static_assert(std::is_same::value + or std::is_same::value, + "iter_impl only accepts (const) basic_json"); + + public: + /// the type of the values when the iterator is dereferenced + using value_type = typename basic_json::value_type; + /// a type to represent differences between iterators + using difference_type = typename basic_json::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename basic_json::const_pointer, + typename basic_json::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = typename std::conditional::value, + typename basic_json::const_reference, + typename basic_json::reference>::type; + /// the category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept + : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - // Double-Precision Float - oa->write_character(0xfb); - write_number(j.m_value.number_float); + m_it.object_iterator = typename object_t::iterator(); break; } - case value_t::string: + case basic_json::value_t::array: { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 0x17) - { - write_number(static_cast(0x60 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x78); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x79); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x7a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x7b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP - - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); + m_it.array_iterator = typename array_t::iterator(); break; } - case value_t::array: + default: { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 0x17) - { - write_number(static_cast(0x80 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x98); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x99); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x9a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x9b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP - - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_cbor(el); - } + m_it.primitive_iterator = primitive_iterator_t(); break; } + } + } - case value_t::object: - { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 0x17) - { - write_number(static_cast(0xa0 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0xb8); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0xb9); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0xba); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0xbb); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + /*! + @note The conventional copy constructor and copy assignment are + implicitly defined. + Combined with the following converting constructor and assignment, + they support: copy from iterator to iterator, + copy from const iterator to const iterator, + and conversion from iterator to const iterator. + However conversion from const iterator to iterator is not defined. + */ - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_cbor(el.first); - write_cbor(el.second); - } + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in,out] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case basic_json::value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); break; } default: { + m_it.primitive_iterator.set_begin(); break; } } } /*! - @brief[in] j JSON value to serialize + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - void write_msgpack(const basic_json& j) + void set_end() noexcept { - switch (j.type()) + assert(m_object != nullptr); + + switch (m_object->m_type) { - case value_t::null: + case basic_json::value_t::object: { - // nil - oa->write_character(0xc0); + m_it.object_iterator = m_object->m_value.object->end(); break; } - case value_t::boolean: + case basic_json::value_t::array: { - // true and false - oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); + m_it.array_iterator = m_object->m_value.array->end(); break; } - case value_t::number_integer: + default: { - if (j.m_value.number_integer >= 0) - { - // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we - // used the code from the value_t::number_unsigned case - // here. - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - if (j.m_value.number_integer >= -32) - { - // negative fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 8 - oa->write_character(0xd0); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 16 - oa->write_character(0xd1); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 32 - oa->write_character(0xd2); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 64 - oa->write_character(0xd3); - write_number(static_cast(j.m_value.number_integer)); - } - } + m_it.primitive_iterator.set_end(); break; } + } + } - case value_t::number_unsigned: + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - break; + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; } - case value_t::number_float: + case basic_json::value_t::array: { - // float 64 - oa->write_character(0xcb); - write_number(j.m_value.number_float); - break; + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; } - case value_t::string: + case basic_json::value_t::null: { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 31) - { - // fixstr - write_number(static_cast(0xa0 | N)); - } - else if (N <= 255) - { - // str 8 - oa->write_character(0xd9); - write_number(static_cast(N)); - } - else if (N <= 65535) - { - // str 16 - oa->write_character(0xda); - write_number(static_cast(N)); - } - else if (N <= 4294967295) + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + + default: + { + if (m_it.primitive_iterator.is_begin()) { - // str 32 - oa->write_character(0xdb); - write_number(static_cast(N)); + return *m_object; } - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } + } + } - case value_t::array: + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 15) - { - // fixarray - write_number(static_cast(0x90 | N)); - } - else if (N <= 0xffff) - { - // array 16 - oa->write_character(0xdc); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - // array 32 - oa->write_character(0xdd); - write_number(static_cast(N)); - } + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_msgpack(el); - } - break; + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; } - case value_t::object: + default: { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 15) - { - // fixmap - write_number(static_cast(0x80 | (N & 0xf))); - } - else if (N <= 65535) - { - // map 16 - oa->write_character(0xde); - write_number(static_cast(N)); - } - else if (N <= 4294967295) + if (m_it.primitive_iterator.is_begin()) { - // map 32 - oa->write_character(0xdf); - write_number(static_cast(N)); + return m_object; } - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_msgpack(el.first); - write_msgpack(el.second); - } + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, 1); break; } default: { + ++m_it.primitive_iterator; break; } } - } - private: - /* - @brief write a number to output input + return *this; + } - @param[in] n number of type @a NumberType - @tparam NumberType the type of the number + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator--(int) + { + auto result = *this; + --(*this); + return result; + } - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - template - void write_number(NumberType n) + iter_impl& operator--() { - // step 1: write number to array of length NumberType - std::array vec; - std::memcpy(vec.data(), &n, sizeof(NumberType)); + assert(m_object != nullptr); - // step 2: write array to output (with possible reordering) - if (is_little_endian) + switch (m_object->m_type) { - // reverse byte order prior to conversion if necessary - std::reverse(vec.begin(), vec.end()); + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } } - oa->write_characters(vec.data(), sizeof(NumberType)); + return *this; } - private: - /// whether we can assume little endianess - const bool is_little_endian = true; - - /// the output - output_adapter_t oa = nullptr; - }; + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } - public: - /*! - @brief create a CBOR serialization of a given JSON value + assert(m_object != nullptr); - Serializes a given JSON value @a j to a byte vector using the CBOR (Concise - Binary Object Representation) serialization format. CBOR is a binary - serialization format which aims to be more compact than JSON itself, yet - more efficient to parse. + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + return (m_it.object_iterator == other.m_it.object_iterator); + } - The library uses the following mapping from JSON values types to - CBOR types according to the CBOR specification (RFC 7049): + case basic_json::value_t::array: + { + return (m_it.array_iterator == other.m_it.array_iterator); + } - JSON value type | value/range | CBOR type | first byte - --------------- | ------------------------------------------ | ---------------------------------- | --------------- - null | `null` | Null | 0xf6 - boolean | `true` | True | 0xf5 - boolean | `false` | False | 0xf4 - number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3b - number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3a - number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 - number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 - number_integer | -24..-1 | Negative integer | 0x20..0x37 - number_integer | 0..23 | Integer | 0x00..0x17 - number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 - number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 - number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1a - number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1b - number_unsigned | 0..23 | Integer | 0x00..0x17 - number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 - number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 - number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1a - number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1b - number_float | *any value* | Double-Precision Float | 0xfb - string | *length*: 0..23 | UTF-8 string | 0x60..0x77 - string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 - string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 - string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7a - string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7b - array | *size*: 0..23 | array | 0x80..0x97 - array | *size*: 23..255 | array (1 byte follow) | 0x98 - array | *size*: 256..65535 | array (2 bytes follow) | 0x99 - array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9a - array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9b - object | *size*: 0..23 | map | 0xa0..0xb7 - object | *size*: 23..255 | map (1 byte follow) | 0xb8 - object | *size*: 256..65535 | map (2 bytes follow) | 0xb9 - object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xba - object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xbb + default: + { + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + } - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a CBOR value. + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } - @note The following CBOR types are not used in the conversion: - - byte strings (0x40..0x5f) - - UTF-8 strings terminated by "break" (0x7f) - - arrays terminated by "break" (0x9f) - - maps terminated by "break" (0xbf) - - date/time (0xc0..0xc1) - - bignum (0xc2..0xc3) - - decimal fraction (0xc4) - - bigfloat (0xc5) - - tagged items (0xc6..0xd4, 0xd8..0xdb) - - expected conversions (0xd5..0xd7) - - simple values (0xe0..0xf3, 0xf8) - - undefined (0xf7) - - half and single-precision floats (0xf9-0xfa) - - break (0xff) + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + assert(m_object != nullptr); - @complexity Linear in the size of the JSON value @a j. + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + } - @liveexample{The example shows the serialization of a JSON value to a byte - vector in CBOR format.,to_cbor} + case basic_json::value_t::array: + { + return (m_it.array_iterator < other.m_it.array_iterator); + } - @sa http://cbor.io - @sa @ref from_cbor(const std::vector&, const size_t) for the - analogous deserialization - @sa @ref to_msgpack(const basic_json& for the related MessagePack format + default: + { + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + } - @since version 2.0.9 - */ - static std::vector to_cbor(const basic_json& j) - { - std::vector result; - binary_writer bw(output_adapter::create(result)); - bw.write_cbor(j); - return result; - } + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } - /*! - @brief create a MessagePack serialization of a given JSON value + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } - Serializes a given JSON value @a j to a byte vector using the MessagePack - serialization format. MessagePack is a binary serialization format which - aims to be more compact than JSON itself, yet more efficient to parse. + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } - The library uses the following mapping from JSON values types to - MessagePack types according to the MessagePack specification: + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); - JSON value type | value/range | MessagePack type | first byte - --------------- | --------------------------------- | ---------------- | ---------- - null | `null` | nil | 0xc0 - boolean | `true` | true | 0xc3 - boolean | `false` | false | 0xc2 - number_integer | -9223372036854775808..-2147483649 | int64 | 0xd3 - number_integer | -2147483648..-32769 | int32 | 0xd2 - number_integer | -32768..-129 | int16 | 0xd1 - number_integer | -128..-33 | int8 | 0xd0 - number_integer | -32..-1 | negative fixint | 0xe0..0xff - number_integer | 0..127 | positive fixint | 0x00..0x7f - number_integer | 128..255 | uint 8 | 0xcc - number_integer | 256..65535 | uint 16 | 0xcd - number_integer | 65536..4294967295 | uint 32 | 0xce - number_integer | 4294967296..18446744073709551615 | uint 64 | 0xcf - number_unsigned | 0..127 | positive fixint | 0x00..0x7f - number_unsigned | 128..255 | uint 8 | 0xcc - number_unsigned | 256..65535 | uint 16 | 0xcd - number_unsigned | 65536..4294967295 | uint 32 | 0xce - number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xcf - number_float | *any value* | float 64 | 0xcb - string | *length*: 0..31 | fixstr | 0xa0..0xbf - string | *length*: 32..255 | str 8 | 0xd9 - string | *length*: 256..65535 | str 16 | 0xda - string | *length*: 65536..4294967295 | str 32 | 0xdb - array | *size*: 0..15 | fixarray | 0x90..0x9f - array | *size*: 16..65535 | array 16 | 0xdc - array | *size*: 65536..4294967295 | array 32 | 0xdd - object | *size*: 0..15 | fix map | 0x80..0x8f - object | *size*: 16..65535 | map 16 | 0xde - object | *size*: 65536..4294967295 | map 32 | 0xdf + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + } - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a MessagePack value. + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } - @note The following values can **not** be converted to a MessagePack value: - - strings with more than 4294967295 bytes - - arrays with more than 4294967295 elements - - objects with more than 4294967295 elements + default: + { + m_it.primitive_iterator += i; + break; + } + } - @note The following MessagePack types are not used in the conversion: - - bin 8 - bin 32 (0xc4..0xc6) - - ext 8 - ext 32 (0xc7..0xc9) - - float 32 (0xca) - - fixext 1 - fixext 16 (0xd4..0xd8) + return *this; + } - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } - @complexity Linear in the size of the JSON value @a j. + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } - @liveexample{The example shows the serialization of a JSON value to a byte - vector in MessagePack format.,to_msgpack} + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } - @sa http://msgpack.org - @sa @ref from_msgpack(const std::vector&, const size_t) for the - analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); - @since version 2.0.9 - */ - static std::vector to_msgpack(const basic_json& j) - { - std::vector result; - binary_writer bw(output_adapter::create(result)); - bw.write_msgpack(j); - return result; - } + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + } - /*! - @brief create a JSON value from a byte vector in CBOR format + case basic_json::value_t::array: + { + return m_it.array_iterator - other.m_it.array_iterator; + } - Deserializes a given byte vector @a v to a JSON value using the CBOR - (Concise Binary Object Representation) serialization format. + default: + { + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + } - The library maps CBOR types to JSON value types as follows: + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); - CBOR type | JSON value type | first byte - ---------------------- | --------------- | ---------- - Integer | number_unsigned | 0x00..0x17 - Unsigned integer | number_unsigned | 0x18 - Unsigned integer | number_unsigned | 0x19 - Unsigned integer | number_unsigned | 0x1a - Unsigned integer | number_unsigned | 0x1b - Negative integer | number_integer | 0x20..0x37 - Negative integer | number_integer | 0x38 - Negative integer | number_integer | 0x39 - Negative integer | number_integer | 0x3a - Negative integer | number_integer | 0x3b - Negative integer | number_integer | 0x40..0x57 - UTF-8 string | string | 0x60..0x77 - UTF-8 string | string | 0x78 - UTF-8 string | string | 0x79 - UTF-8 string | string | 0x7a - UTF-8 string | string | 0x7b - UTF-8 string | string | 0x7f - array | array | 0x80..0x97 - array | array | 0x98 - array | array | 0x99 - array | array | 0x9a - array | array | 0x9b - array | array | 0x9f - map | object | 0xa0..0xb7 - map | object | 0xb8 - map | object | 0xb9 - map | object | 0xba - map | object | 0xbb - map | object | 0xbf - False | `false` | 0xf4 - True | `true` | 0xf5 - Nill | `null` | 0xf6 - Half-Precision Float | number_float | 0xf9 - Single-Precision Float | number_float | 0xfa - Double-Precision Float | number_float | 0xfb + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + } - @warning The mapping is **incomplete** in the sense that not all CBOR - types can be converted to a JSON value. The following CBOR types - are not supported and will yield parse errors (parse_error.112): - - byte strings (0x40..0x5f) - - date/time (0xc0..0xc1) - - bignum (0xc2..0xc3) - - decimal fraction (0xc4) - - bigfloat (0xc5) - - tagged items (0xc6..0xd4, 0xd8..0xdb) - - expected conversions (0xd5..0xd7) - - simple values (0xe0..0xf3, 0xf8) - - undefined (0xf7) + case basic_json::value_t::array: + { + return *std::next(m_it.array_iterator, n); + } - @warning CBOR allows map keys of any type, whereas JSON only allows - strings as keys in object values. Therefore, CBOR maps with keys - other than UTF-8 strings are rejected (parse_error.113). + case basic_json::value_t::null: + { + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } - @note Any CBOR output created @ref to_cbor can be successfully parsed by - @ref from_cbor. + default: + { + if (m_it.primitive_iterator.get_value() == -n) + { + return *m_object; + } - @param[in] v a byte vector in CBOR format - @param[in] start_index the index to start reading from @a v (0 by default) - @return deserialized JSON value + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from CBOR were - used in the given vector @a v or if the input is not valid CBOR - @throw parse_error.113 if a string was expected as map key, but not found + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + typename object_t::key_type key() const + { + assert(m_object != nullptr); - @complexity Linear in the size of the byte vector @a v. + if (m_object->is_object()) + { + return m_it.object_iterator->first; + } - @liveexample{The example shows the deserialization of a byte vector in CBOR - format to a JSON value.,from_cbor} + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } - @sa http://cbor.io - @sa @ref to_cbor(const basic_json&) for the analogous serialization - @sa @ref from_msgpack(const std::vector&, const size_t) for the - related MessagePack format + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } - @since version 2.0.9, parameter @a start_index since 2.1.1 + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + struct internal_iterator m_it = internal_iterator(); + }; + + /*! + @brief a template for a reverse iterator class + + @tparam Base the base iterator type to reverse. Valid types are @ref + iterator (to create @ref reverse_iterator) and @ref const_iterator (to + create @ref const_reverse_iterator). + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + + @since version 1.0.0 */ - static basic_json from_cbor(const std::vector& v, - const size_t start_index = 0) + template + class json_reverse_iterator : public std::reverse_iterator { - binary_reader br(detail::input_adapter_factory::create(v.begin() + static_cast(start_index), v.end())); - return br.parse_cbor(); - } + public: + /// shortcut to the reverse iterator adaptor + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + /// create reverse iterator from iterator + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) + {} - /*! - @brief create a JSON value from a byte vector in MessagePack format + /// create reverse iterator from base class + json_reverse_iterator(const base_iterator& it) noexcept + : base_iterator(it) + {} - Deserializes a given byte vector @a v to a JSON value using the MessagePack - serialization format. + /// post-increment (it++) + json_reverse_iterator operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } - The library maps MessagePack types to JSON value types as follows: + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } - MessagePack type | JSON value type | first byte - ---------------- | --------------- | ---------- - positive fixint | number_unsigned | 0x00..0x7f - fixmap | object | 0x80..0x8f - fixarray | array | 0x90..0x9f - fixstr | string | 0xa0..0xbf - nil | `null` | 0xc0 - false | `false` | 0xc2 - true | `true` | 0xc3 - float 32 | number_float | 0xca - float 64 | number_float | 0xcb - uint 8 | number_unsigned | 0xcc - uint 16 | number_unsigned | 0xcd - uint 32 | number_unsigned | 0xce - uint 64 | number_unsigned | 0xcf - int 8 | number_integer | 0xd0 - int 16 | number_integer | 0xd1 - int 32 | number_integer | 0xd2 - int 64 | number_integer | 0xd3 - str 8 | string | 0xd9 - str 16 | string | 0xda - str 32 | string | 0xdb - array 16 | array | 0xdc - array 32 | array | 0xdd - map 16 | object | 0xde - map 32 | object | 0xdf - negative fixint | number_integer | 0xe0-0xff + /// post-decrement (it--) + json_reverse_iterator operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } - @warning The mapping is **incomplete** in the sense that not all - MessagePack types can be converted to a JSON value. The following - MessagePack types are not supported and will yield parse errors: - - bin 8 - bin 32 (0xc4..0xc6) - - ext 8 - ext 32 (0xc7..0xc9) - - fixext 1 - fixext 16 (0xd4..0xd8) + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } - @param[in] v a byte vector in MessagePack format - @param[in] start_index the index to start reading from @a v (0 by default) - @return deserialized JSON value + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from MessagePack were - used in the given vector @a v or if the input is not valid MessagePack - @throw parse_error.113 if a string was expected as map key, but not found + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } - @complexity Linear in the size of the byte vector @a v. + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + typename object_t::key_type key() const + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } + }; + + + private: + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + /*! + @brief deserialization of CBOR and MessagePack values + */ + class binary_reader + { + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(detail::input_adapter_t adapter) + : ia(adapter), is_little_endian(little_endianess()) + { + assert(ia); + } + + /*! + @brief create a JSON value from CBOR input + + @param[in] get_char whether a new character should be retrieved from + the input (true, default) or whether the last + read character should be considered instead + + @return JSON value created from CBOR input + + @throw parse_error.110 if input ended unexpectedly + @throw parse_error.112 if unsupported byte was read + */ + basic_json parse_cbor(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + { + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + } + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + return static_cast(current); + } + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + return get_number(); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + return get_number(); + } + + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + return get_number(); + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + return get_number(); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + { + return static_cast(0x20 - 1 - current); + } + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + // must be uint8_t ! + return static_cast(-1) - get_number(); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + { + return static_cast(-1) - static_cast(get_number()); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7f: // UTF-8 string (indefinite length) + { + return get_cbor_string(); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + basic_json result = value_t::array; + const auto len = static_cast(current & 0x1f); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x98: // array (one-byte uint8_t for n follows) + { + basic_json result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x9a: // array (four-byte uint32_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x9b: // array (eight-byte uint64_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x9f: // array (indefinite length) + { + basic_json result = value_t::array; + while (get() != 0xff) + { + result.push_back(parse_cbor(false)); + } + return result; + } + + // map (0x00..0x17 pairs of data items follow) + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + { + basic_json result = value_t::object; + const auto len = static_cast(current & 0x1f); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xb8: // map (one-byte uint8_t for n follows) + { + basic_json result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xb9: // map (two-byte uint16_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xba: // map (four-byte uint32_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xbb: // map (eight-byte uint64_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xbf: // map (indefinite length) + { + basic_json result = value_t::object; + while (get() != 0xff) + { + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xf4: // false + { + return false; + } + + case 0xf5: // true + { + return true; + } + + case 0xf6: // null + { + return value_t::null; + } + + case 0xf9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1 = get(); + check_eof(); + const int byte2 = get(); + check_eof(); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const int half = (byte1 << 8) + byte2; + const int exp = (half >> 10) & 0x1f; + const int mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + } + return (half & 0x8000) != 0 ? -val : val; + } + + case 0xfa: // Single-Precision Float (four-byte IEEE 754) + { + return get_number(); + } + + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + return get_number(); + } + + default: // anything else (0xFF is handled inside the other types) + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); + } + } + } + + /*! + @brief create a JSON value from MessagePack input + + @return JSON value created from MessagePack input + + @throw parse_error.110 if input ended unexpectedly + @throw parse_error.112 if unsupported byte was read + */ + basic_json parse_msgpack() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + { + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + } + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + { + return static_cast(current); + } + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + { + basic_json result = value_t::object; + const auto len = static_cast(current & 0x0f); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + { + basic_json result = value_t::array; + const auto len = static_cast(current & 0x0f); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } + + // fixstr + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + { + return get_msgpack_string(); + } - @liveexample{The example shows the deserialization of a byte vector in - MessagePack format to a JSON value.,from_msgpack} + case 0xc0: // nil + { + return value_t::null; + } - @sa http://msgpack.org - @sa @ref to_msgpack(const basic_json&) for the analogous serialization - @sa @ref from_cbor(const std::vector&, const size_t) for the - related CBOR format + case 0xc2: // false + { + return false; + } - @since version 2.0.9, parameter @a start_index since 2.1.1 - */ - static basic_json from_msgpack(const std::vector& v, - const size_t start_index = 0) - { - binary_reader br(detail::input_adapter_factory::create(v.begin() + static_cast(start_index), v.end())); - return br.parse_msgpack(); - } + case 0xc3: // true + { + return true; + } - /// @} + case 0xca: // float 32 + { + return get_number(); + } - ////////////////////// - // lexer and parser // - ////////////////////// + case 0xcb: // float 64 + { + return get_number(); + } - private: - /*! - @brief lexical analysis + case 0xcc: // uint 8 + { + return get_number(); + } - This class organizes the lexical analysis during JSON deserialization. - */ - class lexer - { - public: - /// token types for the parser - enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value - value_integer, ///< a signed integer -- use get_number_integer() for actual value - value_float, ///< an floating point number -- use get_number_float() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input, ///< indicating the end of the input buffer - literal_or_value ///< a literal or the begin of a value (only for diagnostics) - }; + case 0xcd: // uint 16 + { + return get_number(); + } - /// return name of values of type token_type (only used for errors) - static const char* token_type_name(const token_type t) noexcept - { - switch (t) - { - case token_type::uninitialized: - return ""; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return ""; - case token_type::end_of_input: - return "end of input"; - case token_type::literal_or_value: - return "'[', '{', or a literal"; - default: + case 0xce: // uint 32 { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE + return get_number(); } - } - } - explicit lexer(detail::input_adapter_t adapter) - : ia(adapter), decimal_point_char(get_decimal_point()) - {} + case 0xcf: // uint 64 + { + return get_number(); + } - // delete because of pointer members - lexer(const lexer&) = delete; - lexer& operator=(lexer&) = delete; + case 0xd0: // int 8 + { + return get_number(); + } - private: - ///////////////////// - // locales - ///////////////////// + case 0xd1: // int 16 + { + return get_number(); + } - /// return the locale-dependent decimal point - static char get_decimal_point() noexcept - { - const auto loc = localeconv(); - assert(loc != nullptr); - return (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0]; - } + case 0xd2: // int 32 + { + return get_number(); + } - ///////////////////// - // scan functions - ///////////////////// + case 0xd3: // int 64 + { + return get_number(); + } - /*! - @brief get codepoint from 4 hex characters following `\u` + case 0xd9: // str 8 + case 0xda: // str 16 + case 0xdb: // str 32 + { + return get_msgpack_string(); + } - @return codepoint or -1 in case of an error (e.g. EOF or non-hex - character) - */ - int get_codepoint() - { - // this function only makes sense after reading `\u` - assert(current == 'u'); - int codepoint = 0; + case 0xdc: // array 16 + { + basic_json result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } - // byte 1: \uXxxx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x1000; - break; - case '2': - codepoint += 0x2000; - break; - case '3': - codepoint += 0x3000; - break; - case '4': - codepoint += 0x4000; - break; - case '5': - codepoint += 0x5000; - break; - case '6': - codepoint += 0x6000; - break; - case '7': - codepoint += 0x7000; - break; - case '8': - codepoint += 0x8000; - break; - case '9': - codepoint += 0x9000; - break; - case 'A': - case 'a': - codepoint += 0xa000; - break; - case 'B': - case 'b': - codepoint += 0xb000; - break; - case 'C': - case 'c': - codepoint += 0xc000; - break; - case 'D': - case 'd': - codepoint += 0xd000; - break; - case 'E': - case 'e': - codepoint += 0xe000; - break; - case 'F': - case 'f': - codepoint += 0xf000; - break; - default: - return -1; - } + case 0xdd: // array 32 + { + basic_json result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } - // byte 2: \uxXxx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0100; - break; - case '2': - codepoint += 0x0200; - break; - case '3': - codepoint += 0x0300; - break; - case '4': - codepoint += 0x0400; - break; - case '5': - codepoint += 0x0500; - break; - case '6': - codepoint += 0x0600; - break; - case '7': - codepoint += 0x0700; - break; - case '8': - codepoint += 0x0800; - break; - case '9': - codepoint += 0x0900; - break; - case 'A': - case 'a': - codepoint += 0x0a00; - break; - case 'B': - case 'b': - codepoint += 0x0b00; - break; - case 'C': - case 'c': - codepoint += 0x0c00; - break; - case 'D': - case 'd': - codepoint += 0x0d00; - break; - case 'E': - case 'e': - codepoint += 0x0e00; - break; - case 'F': - case 'f': - codepoint += 0x0f00; - break; - default: - return -1; - } + case 0xde: // map 16 + { + basic_json result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } - // byte 3: \uxxXx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0010; - break; - case '2': - codepoint += 0x0020; - break; - case '3': - codepoint += 0x0030; - break; - case '4': - codepoint += 0x0040; - break; - case '5': - codepoint += 0x0050; - break; - case '6': - codepoint += 0x0060; - break; - case '7': - codepoint += 0x0070; - break; - case '8': - codepoint += 0x0080; - break; - case '9': - codepoint += 0x0090; - break; - case 'A': - case 'a': - codepoint += 0x00a0; - break; - case 'B': - case 'b': - codepoint += 0x00b0; - break; - case 'C': - case 'c': - codepoint += 0x00c0; - break; - case 'D': - case 'd': - codepoint += 0x00d0; - break; - case 'E': - case 'e': - codepoint += 0x00e0; - break; - case 'F': - case 'f': - codepoint += 0x00f0; - break; - default: - return -1; - } + case 0xdf: // map 32 + { + basic_json result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } - // byte 4: \uxxxX - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0001; - break; - case '2': - codepoint += 0x0002; - break; - case '3': - codepoint += 0x0003; - break; - case '4': - codepoint += 0x0004; - break; - case '5': - codepoint += 0x0005; - break; - case '6': - codepoint += 0x0006; - break; - case '7': - codepoint += 0x0007; - break; - case '8': - codepoint += 0x0008; - break; - case '9': - codepoint += 0x0009; - break; - case 'A': - case 'a': - codepoint += 0x000a; - break; - case 'B': - case 'b': - codepoint += 0x000b; - break; - case 'C': - case 'c': - codepoint += 0x000c; - break; - case 'D': - case 'd': - codepoint += 0x000d; - break; - case 'E': - case 'e': - codepoint += 0x000e; - break; - case 'F': - case 'f': - codepoint += 0x000f; - break; - default: - return -1; - } + // positive fixint + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + case 0xe8: + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xed: + case 0xee: + case 0xef: + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + case 0xfd: + case 0xfe: + case 0xff: + { + return static_cast(current); + } - return codepoint; + default: // anything else + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + ss.str())); + } + } } /*! - @brief scan a string literal - - This function scans a string according to Sect. 7 of RFC 7159. While - scanning, bytes are escaped and copied into buffer yytext. Then the - function returns successfully, yytext is null-terminated and yylen - contains the number of bytes in the string. + @brief determine system byte order - @return token_type::value_string if string could be successfully - scanned, token_type::parse_error otherwise + @return true iff system's byte order is little endian - @note In case of errors, variable error_message contains a textual - description. + @note from http://stackoverflow.com/a/1001328/266378 */ - token_type scan_string() + static bool little_endianess() noexcept { - // reset yytext (ignore opening quote) - reset(); + int num = 1; + return (*reinterpret_cast(&num) == 1); + } - // we entered the function by reading an open quote - assert(current == '\"'); + private: + /*! + @brief get next character from the input - while (true) - { - // get next character - switch (get()) - { - // end of file while parsing string - case std::char_traits::eof(): - { - error_message = "invalid string: missing closing quote"; - return token_type::parse_error; - } + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns + `std::char_traits::eof()` in that case. - // closing quote - case '\"': - { - // terminate yytext - add('\0'); - --yylen; - return token_type::value_string; - } + @return character read from the input + */ + int get() + { + ++chars_read; + return (current = ia->get_character()); + } - // escapes - case '\\': - { - switch (get()) - { - // quotation mark - case '\"': - add('\"'); - break; - // reverse solidus - case '\\': - add('\\'); - break; - // solidus - case '/': - add('/'); - break; - // backspace - case 'b': - add('\b'); - break; - // form feed - case 'f': - add('\f'); - break; - // line feed - case 'n': - add('\n'); - break; - // carriage return - case 'r': - add('\r'); - break; - // tab - case 't': - add('\t'); - break; - - // unicode escapes - case 'u': - { - int codepoint; - int codepoint1 = get_codepoint(); + /* + @brief read a number from the input - if (JSON_UNLIKELY(codepoint1 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } + @tparam NumberType the type of the number - // check if code point is a high surrogate - if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) - { - // expect next \uxxxx entry - if (JSON_LIKELY(get() == '\\' and get() == 'u')) - { - const int codepoint2 = get_codepoint(); - - if (JSON_UNLIKELY(codepoint2 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } - - // check if codepoint2 is a low surrogate - if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) - { - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - } - else - { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) - { - error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; - return token_type::parse_error; - } + @return number of type @a NumberType - // only work with first code point - codepoint = codepoint1; - } + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. - // result of the above calculation yields a proper codepoint - assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + @throw parse_error.110 if input has less than `sizeof(NumberType)` + bytes + */ + template + NumberType get_number() + { + // step 1: read input into array with system's byte order + std::array vec; + for (size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + check_eof(); - // translate code point to bytes - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - add(codepoint); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - add(0xC0 | (codepoint >> 6)); - add(0x80 | (codepoint & 0x3F)); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(0xE0 | (codepoint >> 12)); - add(0x80 | ((codepoint >> 6) & 0x3F)); - add(0x80 | (codepoint & 0x3F)); - } - else - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(0xF0 | (codepoint >> 18)); - add(0x80 | ((codepoint >> 12) & 0x3F)); - add(0x80 | ((codepoint >> 6) & 0x3F)); - add(0x80 | (codepoint & 0x3F)); - } + // reverse byte order prior to conversion if necessary + if (is_little_endian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } - break; - } + // step 2: convert array into number of type T and return + NumberType result; + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return result; + } - // other characters after escape - default: - error_message = "invalid string: forbidden character after backslash"; - return token_type::parse_error; - } + /*! + @brief create a string by reading characters from the input - break; - } + @param[in] len number of bytes to read - // invalid control characters - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - error_message = "invalid string: control character must be escaped"; - return token_type::parse_error; - } - - // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) - case 0x20: - case 0x21: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5a: - case 0x5b: - case 0x5d: - case 0x5e: - case 0x5f: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7a: - case 0x7b: - case 0x7c: - case 0x7d: - case 0x7e: - case 0x7f: - { - add(current); - break; - } + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref check_eof() detects the end of + the input before we run out of string memory. - // U+0080..U+07FF: bytes C2..DF 80..BF - case 0xc2: - case 0xc3: - case 0xc4: - case 0xc5: - case 0xc6: - case 0xc7: - case 0xc8: - case 0xc9: - case 0xca: - case 0xcb: - case 0xcc: - case 0xcd: - case 0xce: - case 0xcf: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xd3: - case 0xd4: - case 0xd5: - case 0xd6: - case 0xd7: - case 0xd8: - case 0xd9: - case 0xda: - case 0xdb: - case 0xdc: - case 0xdd: - case 0xde: - case 0xdf: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } + @return string created by reading @a len bytes - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + @throw parse_error.110 if input has less than @a len bytes + */ + std::string get_string(const size_t len) + { + std::string result; + for (size_t i = 0; i < len; ++i) + { + get(); + check_eof(); + result.append(1, static_cast(current)); + } + return result; + } - // U+0800..U+0FFF: bytes E0 A0..BF 80..BF - case 0xe0: - { - add(current); - get(); - if (JSON_LIKELY(0xa0 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + /*! + @brief reads a CBOR string - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } - - // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF - // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe4: - case 0xe5: - case 0xe6: - case 0xe7: - case 0xe8: - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xee: - case 0xef: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + @return string - // U+D000..U+D7FF: bytes ED 80..9F 80..BF - case 0xed: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0x9f)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + std::string get_cbor_string() + { + check_eof(); - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + const auto len = static_cast(current & 0x1f); + return get_string(len); + } - // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF - case 0xf0: - { - add(current); - get(); - if (JSON_LIKELY(0x90 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } - } + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + const auto len = static_cast(get_number()); + return get_string(len); + } - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } - // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF - case 0xf1: - case 0xf2: - case 0xf3: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } - } + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } - // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF - case 0xf4: + case 0x7f: // UTF-8 string (indefinite length) + { + std::string result; + while (get() != 0xff) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0x8f)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } - } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + check_eof(); + result.append(1, static_cast(current)); } + return result; + } - // remaining bytes (80..C1 and F5..FF) are ill-formed - default: - { - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + default: + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); } } } - static void strtof(float& f, const char* str, char** endptr) noexcept - { - f = std::strtof(str, endptr); - } + /*! + @brief reads a MessagePack string - static void strtof(double& f, const char* str, char** endptr) noexcept - { - f = std::strtod(str, endptr); - } + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. - static void strtof(long double& f, const char* str, char** endptr) noexcept - { - f = std::strtold(str, endptr); - } + @return string - /*! - @brief scan a number literal - - This function scans a string according to Sect. 6 of RFC 7159. - - The function is realized with a deterministic finite state machine - derived from the grammar described in RFC 7159. Starting in state - "init", the input is read and used to determined the next state. Only - state "done" accepts the number. State "error" is a trap state to model - errors. In the table below, "anything" means any character but the ones - listed before. - - state | 0 | 1-9 | e E | + | - | . | anything - ---------|----------|----------|----------|---------|---------|----------|----------- - init | zero | any1 | [error] | [error] | minus | [error] | [error] - minus | zero | any1 | [error] | [error] | [error] | [error] | [error] - zero | done | done | exponent | done | done | decimal1 | done - any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] - decimal2 | decimal2 | decimal2 | exponent | done | done | done | done - exponent | any2 | any2 | [error] | sign | sign | [error] | [error] - sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] - any2 | any2 | any2 | done | done | done | done | done - - The state machine is realized with one label per state (prefixed with - "scan_number_") and `goto` statements between them. The state machine - contains cycles, but any cycle can be left when EOF is read. Therefore, - the function is guaranteed to terminate. - - During scanning, the read bytes are stored in yytext. This string is - then converted to a signed integer, an unsigned integer, or a - floating-point number. - - @return token_type::value_unsigned, token_type::value_integer, or - token_type::value_float if number could be successfully scanned, - token_type::parse_error otherwise - - @note The scanner is independent of the current locale. Internally, the - locale's decimal point is used instead of `.` to work with the - locale-dependent converters. + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read */ - token_type scan_number() + std::string get_msgpack_string() { - // reset yytext to store the number's bytes - reset(); - - // the type of the parsed number; initially set to unsigned; will be - // changed if minus sign, decimal point or exponent is read - token_type number_type = token_type::value_unsigned; + check_eof(); - // state (init): we just found out we need to scan a number switch (current) { - case '-': + // fixstr + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: { - add(current); - goto scan_number_minus; + const auto len = static_cast(current & 0x1f); + return get_string(len); } - case '0': + case 0xd9: // str 8 { - add(current); - goto scan_number_zero; + const auto len = static_cast(get_number()); + return get_string(len); } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case 0xda: // str 16 { - add(current); - goto scan_number_any1; + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0xdb: // str 32 + { + const auto len = static_cast(get_number()); + return get_string(len); } default: { - // all other characters are rejected outside scan_number() - assert(false); // LCOV_EXCL_LINE + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + ss.str())); } } + } -scan_number_minus: - // state: we just parsed a leading minus sign - number_type = token_type::value_integer; - switch (get()) + /*! + @brief check if input ended + @throw parse_error.110 if input ended + */ + void check_eof() const + { + if (JSON_UNLIKELY(current == std::char_traits::eof())) { - case '0': - { - add(current); - goto scan_number_zero; - } + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + } + } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } + private: + /// input adapter + detail::input_adapter_t ia = nullptr; - default: - { - error_message = "invalid number; expected digit after '-'"; - return token_type::parse_error; - } - } + /// the current character + int current = std::char_traits::eof(); -scan_number_zero: - // state: we just parse a zero (maybe with a leading minus sign) - switch (get()) + /// the number of characters read + size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = true; + }; + + /*! + @brief serialization to CBOR and MessagePack values + */ + class binary_writer + { + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) + : is_little_endian(binary_reader::little_endianess()), oa(adapter) + { + assert(oa); + } + + /*! + @brief[in] j JSON value to serialize + */ + void write_cbor(const basic_json& j) + { + switch (j.type()) { - case '.': + case value_t::null: { - add(decimal_point_char); - goto scan_number_decimal1; + oa->write_character(0xf6); + break; } - case 'e': - case 'E': + case value_t::boolean: { - add(current); - goto scan_number_exponent; + oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); + break; } - default: + case value_t::number_integer: { - goto scan_number_done; + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(0x18); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(0x19); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(0x1a); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(0x1b); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x38); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x39); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x3a); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(0x3b); + write_number(static_cast(positive_number)); + } + } + break; } - } -scan_number_any1: - // state: we just parsed a number 0-9 (maybe with a leading minus sign) - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case value_t::number_unsigned: { - add(current); - goto scan_number_any1; + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(0x18); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(0x19); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(0x1a); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(0x1b); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; } - case '.': + case value_t::number_float: { - add(decimal_point_char); - goto scan_number_decimal1; + // Double-Precision Float + oa->write_character(0xfb); + write_number(j.m_value.number_float); + break; } - case 'e': - case 'E': + case value_t::string: { - add(current); - goto scan_number_exponent; - } + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0x78); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0x79); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0x7a); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0x7b); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP - default: - { - goto scan_number_done; + // step 2: write the string + oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; } - } -scan_number_decimal1: - // state: we just parsed a decimal point - number_type = token_type::value_float; - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case value_t::array: { - add(current); - goto scan_number_decimal2; - } + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0x98); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0x99); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0x9a); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0x9b); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP - default: - { - error_message = "invalid number; expected digit after '.'"; - return token_type::parse_error; + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; } - } -scan_number_decimal2: - // we just parsed at least one number after a decimal point - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case value_t::object: { - add(current); - goto scan_number_decimal2; - } + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xa0 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0xb8); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0xb9); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0xba); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0xbb); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; } default: { - goto scan_number_done; + break; } } + } -scan_number_exponent: - // we just parsed an exponent - number_type = token_type::value_float; - switch (get()) + /*! + @brief[in] j JSON value to serialize + */ + void write_msgpack(const basic_json& j) + { + switch (j.type()) { - case '+': - case '-': + case value_t::null: { - add(current); - goto scan_number_sign; + // nil + oa->write_character(0xc0); + break; } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case value_t::boolean: { - add(current); - goto scan_number_any2; + // true and false + oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); + break; } - default: + case value_t::number_integer: { - error_message = "invalid number; expected '+', '-', or digit after exponent"; - return token_type::parse_error; + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we + // used the code from the value_t::number_unsigned case + // here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(0xcc); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(0xcd); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(0xce); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(0xcf); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(0xd0); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(0xd1); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(0xd2); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(0xd3); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; } - } -scan_number_sign: - // we just parsed an exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case value_t::number_unsigned: { - add(current); - goto scan_number_any2; + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(0xcc); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(0xcd); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(0xce); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(0xcf); + write_number(static_cast(j.m_value.number_integer)); + } + break; } - default: + case value_t::number_float: { - error_message = "invalid number; expected digit after exponent sign"; - return token_type::parse_error; + // float 64 + oa->write_character(0xcb); + write_number(j.m_value.number_float); + break; } - } -scan_number_any2: - // we just parsed a number after the exponent or exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case value_t::string: { - add(current); - goto scan_number_any2; - } + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xa0 | N)); + } + else if (N <= 255) + { + // str 8 + oa->write_character(0xd9); + write_number(static_cast(N)); + } + else if (N <= 65535) + { + // str 16 + oa->write_character(0xda); + write_number(static_cast(N)); + } + else if (N <= 4294967295) + { + // str 32 + oa->write_character(0xdb); + write_number(static_cast(N)); + } - default: - { - goto scan_number_done; + // step 2: write the string + oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; } - } - -scan_number_done: - // unget the character after the number (we only read it to know - // that we are done scanning a number) - --chars_read; - next_unget = true; - - // terminate token - add('\0'); - --yylen; - // try to parse integers first and fall back to floats - if (number_type == token_type::value_unsigned) - { - char* endptr = nullptr; - errno = 0; - const auto x = std::strtoull(yytext.data(), &endptr, 10); - - // we checked the number format before - assert(endptr == yytext.data() + yylen); - - if (errno == 0) + case value_t::array: { - value_unsigned = static_cast(x); - if (value_unsigned == x) + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) { - return token_type::value_unsigned; + // fixarray + write_number(static_cast(0x90 | N)); } - } - } - else if (number_type == token_type::value_integer) - { - char* endptr = nullptr; - errno = 0; - const auto x = std::strtoll(yytext.data(), &endptr, 10); - - // we checked the number format before - assert(endptr == yytext.data() + yylen); - - if (errno == 0) - { - value_integer = static_cast(x); - if (value_integer == x) + else if (N <= 0xffff) { - return token_type::value_integer; + // array 16 + oa->write_character(0xdc); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + // array 32 + oa->write_character(0xdd); + write_number(static_cast(N)); } - } - } - - // this code is reached if we parse a floating-point number or if - // an integer conversion above failed - strtof(value_float, yytext.data(), nullptr); - return token_type::value_float; - } - /*! - @param[in] literal_text the literal text to expect - @param[in] length the length of the passed literal text - @param[in] return_type the token type to return on success - */ - token_type scan_literal(const char* literal_text, const size_t length, - token_type return_type) - { - assert(current == literal_text[0]); - for (size_t i = 1; i < length; ++i) - { - if (JSON_UNLIKELY(get() != literal_text[i])) - { - error_message = "invalid literal"; - return token_type::parse_error; + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; } - } - return return_type; - } - - ///////////////////// - // input management - ///////////////////// - - /// reset yytext - void reset() noexcept - { - yylen = 0; - start_pos = chars_read - 1; - } - - /// get a character from the input - int get() - { - ++chars_read; - return next_unget - ? (next_unget = false, current) - : (current = ia->get_character()); - } - /// add a character to yytext - void add(int c) - { - // resize yytext if necessary; this condition is deemed unlikely, - // because we start with a 1024-byte buffer - if (JSON_UNLIKELY((yylen + 1 > yytext.capacity()))) - { - yytext.resize(2 * yytext.capacity(), '\0'); - } - assert(yylen < yytext.size()); - yytext[yylen++] = static_cast(c); - } - - public: - ///////////////////// - // value getters - ///////////////////// - - /// return integer value - constexpr number_integer_t get_number_integer() const noexcept - { - return value_integer; - } - - /// return unsigned integer value - constexpr number_unsigned_t get_number_unsigned() const noexcept - { - return value_unsigned; - } - - /// return floating-point value - constexpr number_float_t get_number_float() const noexcept - { - return value_float; - } - - /// return string value - const std::string get_string() - { - // yytext cannot be returned as char*, because it may contain a - // null byte (parsed as "\u0000") - return std::string(yytext.data(), yylen); - } - - ///////////////////// - // diagnostics - ///////////////////// - - /// return position of last read token - constexpr size_t get_position() const noexcept - { - return chars_read; - } - - /// return the last read token (for errors only) - std::string get_token_string() const - { - // get the raw byte sequence of the last token - std::string s = ia->read(start_pos, chars_read - start_pos); - - // escape control characters - std::string result; - for (auto c : s) - { - if (c == '\0' or c == std::char_traits::eof()) - { - // ignore EOF - continue; - } - else if ('\x00' <= c and c <= '\x1f') - { - // escape control characters - std::stringstream ss; - ss << "(c) << ">"; - result += ss.str(); - } - else + case value_t::object: { - // add character as is - result.append(1, c); - } - } - - return result; - } - - /// return syntax error message - constexpr const char* get_error_message() const noexcept - { - return error_message; - } - - ///////////////////// - // actual scanner - ///////////////////// - - token_type scan() - { - // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); - - switch (current) - { - // structural characters - case '[': - return token_type::begin_array; - case ']': - return token_type::end_array; - case '{': - return token_type::begin_object; - case '}': - return token_type::end_object; - case ':': - return token_type::name_separator; - case ',': - return token_type::value_separator; - - // literals - case 't': - return scan_literal("true", 4, token_type::literal_true); - case 'f': - return scan_literal("false", 5, token_type::literal_false); - case 'n': - return scan_literal("null", 4, token_type::literal_null); - - // string - case '\"': - return scan_string(); - - // number - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return scan_number(); - - // end of input (the null byte is needed when parsing from - // string literals) - case '\0': - case std::char_traits::eof(): - return token_type::end_of_input; - - // error - default: - error_message = "invalid literal"; - return token_type::parse_error; - } - } - - private: - /// input adapter - detail::input_adapter_t ia = nullptr; - - /// the current character - int current = std::char_traits::eof(); - - /// whether get() should return the last character again - bool next_unget = false; - - /// the number of characters read - size_t chars_read = 0; - /// the start position of the current token - size_t start_pos = 0; - - /// buffer for variable-length tokens (numbers, strings) - std::vector yytext = std::vector(1024, '\0'); - /// current index in yytext - size_t yylen = 0; - - /// a description of occurred lexer errors - const char* error_message = ""; - - // number values - number_integer_t value_integer = 0; - number_unsigned_t value_unsigned = 0; - number_float_t value_float = 0; - - /// the decimal point - const char decimal_point_char = '.'; - }; - - /*! - @brief syntax analysis - - This class implements a recursive decent parser. - */ - class parser - { - public: - /// a parser reading from an input adapter - explicit parser(detail::input_adapter_t adapter, - const parser_callback_t cb = nullptr) - : callback(cb), m_lexer(adapter) - {} - - /*! - @brief public parser interface - - @param[in] strict whether to expect the last token to be EOF - @return parsed JSON value - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ - basic_json parse(const bool strict = true) - { - // read first token - get_token(); + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xf))); + } + else if (N <= 65535) + { + // map 16 + oa->write_character(0xde); + write_number(static_cast(N)); + } + else if (N <= 4294967295) + { + // map 32 + oa->write_character(0xdf); + write_number(static_cast(N)); + } - basic_json result = parse_internal(true); - result.assert_invariant(); + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } - if (strict) - { - get_token(); - expect(lexer::token_type::end_of_input); + default: + { + break; + } } - - // return parser result and replace it with null in case the - // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : std::move(result); } - /*! - @brief public accept interface + private: + /* + @brief write a number to output input + + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number - @param[in] strict whether to expect the last token to be EOF - @return whether the input is a proper JSON text + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. */ - bool accept(const bool strict = true) + template + void write_number(NumberType n) { - // read first token - get_token(); - - if (not accept_internal()) - { - return false; - } + // step 1: write number to array of length NumberType + std::array vec; + std::memcpy(vec.data(), &n, sizeof(NumberType)); - if (strict and get_token() != lexer::token_type::end_of_input) + // step 2: write array to output (with possible reordering) + if (is_little_endian) { - return false; + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); } - return true; + oa->write_characters(vec.data(), sizeof(NumberType)); } private: - /*! - @brief the actual parser - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ - basic_json parse_internal(bool keep) - { - auto result = basic_json(value_t::discarded); - - switch (last_token) - { - case lexer::token_type::begin_object: - { - if (keep and (not callback - or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) - { - // explicitly set result to object to cope with {} - result.m_type = value_t::object; - result.m_value = value_t::object; - } - - // read next token - get_token(); - - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // parse values - while (true) - { - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); + /// whether we can assume little endianess + const bool is_little_endian = true; - bool keep_tag = false; - if (keep) - { - if (callback) - { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); - } - else - { - keep_tag = true; - } - } + /// the output + output_adapter_t oa = nullptr; + }; - // parse separator (:) - get_token(); - expect(lexer::token_type::name_separator); + public: + /*! + @brief create a CBOR serialization of a given JSON value - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) - { - result[key] = std::move(value); - } + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): - // closing } - expect(lexer::token_type::end_object); - break; - } + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | ---------------------------------- | --------------- + null | `null` | Null | 0xf6 + boolean | `true` | True | 0xf5 + boolean | `false` | False | 0xf4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3b + number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3a + number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 + number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 + number_integer | -24..-1 | Negative integer | 0x20..0x37 + number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1a + number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1b + number_unsigned | 0..23 | Integer | 0x00..0x17 + number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1a + number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1b + number_float | *any value* | Double-Precision Float | 0xfb + string | *length*: 0..23 | UTF-8 string | 0x60..0x77 + string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 + string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 + string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7a + string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7b + array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9a + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9b + object | *size*: 0..23 | map | 0xa0..0xb7 + object | *size*: 23..255 | map (1 byte follow) | 0xb8 + object | *size*: 256..65535 | map (2 bytes follow) | 0xb9 + object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xba + object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xbb - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. - return result; - } + @note The following CBOR types are not used in the conversion: + - byte strings (0x40..0x5f) + - UTF-8 strings terminated by "break" (0x7f) + - arrays terminated by "break" (0x9f) + - maps terminated by "break" (0xbf) + - date/time (0xc0..0xc1) + - bignum (0xc2..0xc3) + - decimal fraction (0xc4) + - bigfloat (0xc5) + - tagged items (0xc6..0xd4, 0xd8..0xdb) + - expected conversions (0xd5..0xd7) + - simple values (0xe0..0xf3, 0xf8) + - undefined (0xf7) + - half and single-precision floats (0xf9-0xfa) + - break (0xff) - case lexer::token_type::begin_array: - { - if (keep and (not callback - or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) - { - // explicitly set result to object to cope with [] - result.m_type = value_t::array; - result.m_value = value_t::array; - } + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector - // read next token - get_token(); + @complexity Linear in the size of the JSON value @a j. - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { - if (callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} - // parse values - while (true) - { - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) - { - result.push_back(std::move(value)); - } + @sa http://cbor.io + @sa @ref from_cbor(const std::vector&, const size_t) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + @since version 2.0.9 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + binary_writer bw(output_adapter::create(result)); + bw.write_cbor(j); + return result; + } - // closing ] - expect(lexer::token_type::end_array); - break; - } + /*! + @brief create a MessagePack serialization of a given JSON value - if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. - return result; - } + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: - case lexer::token_type::literal_null: - { - result.m_type = value_t::null; - break; - } + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xc0 + boolean | `true` | true | 0xc3 + boolean | `false` | false | 0xc2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xd3 + number_integer | -2147483648..-32769 | int32 | 0xd2 + number_integer | -32768..-129 | int16 | 0xd1 + number_integer | -128..-33 | int8 | 0xd0 + number_integer | -32..-1 | negative fixint | 0xe0..0xff + number_integer | 0..127 | positive fixint | 0x00..0x7f + number_integer | 128..255 | uint 8 | 0xcc + number_integer | 256..65535 | uint 16 | 0xcd + number_integer | 65536..4294967295 | uint 32 | 0xce + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xcf + number_unsigned | 0..127 | positive fixint | 0x00..0x7f + number_unsigned | 128..255 | uint 8 | 0xcc + number_unsigned | 256..65535 | uint 16 | 0xcd + number_unsigned | 65536..4294967295 | uint 32 | 0xce + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xcf + number_float | *any value* | float 64 | 0xcb + string | *length*: 0..31 | fixstr | 0xa0..0xbf + string | *length*: 32..255 | str 8 | 0xd9 + string | *length*: 256..65535 | str 16 | 0xda + string | *length*: 65536..4294967295 | str 32 | 0xdb + array | *size*: 0..15 | fixarray | 0x90..0x9f + array | *size*: 16..65535 | array 16 | 0xdc + array | *size*: 65536..4294967295 | array 32 | 0xdd + object | *size*: 0..15 | fix map | 0x80..0x8f + object | *size*: 16..65535 | map 16 | 0xde + object | *size*: 65536..4294967295 | map 32 | 0xdf - case lexer::token_type::value_string: - { - result = basic_json(m_lexer.get_string()); - break; - } + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. - case lexer::token_type::literal_true: - { - result.m_type = value_t::boolean; - result.m_value = true; - break; - } + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements - case lexer::token_type::literal_false: - { - result.m_type = value_t::boolean; - result.m_value = false; - break; - } + @note The following MessagePack types are not used in the conversion: + - bin 8 - bin 32 (0xc4..0xc6) + - ext 8 - ext 32 (0xc7..0xc9) + - float 32 (0xca) + - fixext 1 - fixext 16 (0xd4..0xd8) - case lexer::token_type::value_unsigned: - { - result.m_type = value_t::number_unsigned; - result.m_value = m_lexer.get_number_unsigned(); - break; - } + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. - case lexer::token_type::value_integer: - { - result.m_type = value_t::number_integer; - result.m_value = m_lexer.get_number_integer(); - break; - } + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector - case lexer::token_type::value_float: - { - result.m_type = value_t::number_float; - result.m_value = m_lexer.get_number_float(); + @complexity Linear in the size of the JSON value @a j. - // throw in case of infinity or NAN - if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) - { - JSON_THROW(out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); - } + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} - break; - } + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector&, const size_t) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format - case lexer::token_type::parse_error: - { - // using "uninitialized" to avoid "expected" message - expect(lexer::token_type::uninitialized); - break; // LCOV_EXCL_LINE - } + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + binary_writer bw(output_adapter::create(result)); + bw.write_msgpack(j); + return result; + } - default: - { - // the last token was unexpected; we expected a value - expect(lexer::token_type::literal_or_value); - break; // LCOV_EXCL_LINE - } - } + /*! + @brief create a JSON value from a byte vector in CBOR format - if (keep and callback and not callback(depth, parse_event_t::value, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } + Deserializes a given byte vector @a v to a JSON value using the CBOR + (Concise Binary Object Representation) serialization format. - /*! - @brief the acutal acceptor + The library maps CBOR types to JSON value types as follows: - @invariant 1. The last token is not yet processed. Therefore, the - caller of this function must make sure a token has - been read. - 2. When this function returns, the last token is processed. - That is, the last read character was already considered. + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1a + Unsigned integer | number_unsigned | 0x1b + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3a + Negative integer | number_integer | 0x3b + Negative integer | number_integer | 0x40..0x57 + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7a + UTF-8 string | string | 0x7b + UTF-8 string | string | 0x7f + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9a + array | array | 0x9b + array | array | 0x9f + map | object | 0xa0..0xb7 + map | object | 0xb8 + map | object | 0xb9 + map | object | 0xba + map | object | 0xbb + map | object | 0xbf + False | `false` | 0xf4 + True | `true` | 0xf5 + Nill | `null` | 0xf6 + Half-Precision Float | number_float | 0xf9 + Single-Precision Float | number_float | 0xfa + Double-Precision Float | number_float | 0xfb - This invariant makes sure that no token needs to be "unput". - */ - bool accept_internal() - { - switch (last_token) - { - case lexer::token_type::begin_object: - { - // read next token - get_token(); + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - byte strings (0x40..0x5f) + - date/time (0xc0..0xc1) + - bignum (0xc2..0xc3) + - decimal fraction (0xc4) + - bigfloat (0xc5) + - tagged items (0xc6..0xd4, 0xd8..0xdb) + - expected conversions (0xd5..0xd7) + - simple values (0xe0..0xf3, 0xf8) + - undefined (0xf7) - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - return true; - } + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). - // parse values - while (true) - { - // parse key - if (last_token != lexer::token_type::value_string) - { - return false; - } + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. - // parse separator (:) - get_token(); - if (last_token != lexer::token_type::name_separator) - { - return false; - } + @param[in] v a byte vector in CBOR format + @param[in] start_index the index to start reading from @a v (0 by default) + @return deserialized JSON value - // parse value - get_token(); - if (not accept_internal()) - { - return false; - } + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from CBOR were + used in the given vector @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + @complexity Linear in the size of the byte vector @a v. - // closing } - if (last_token != lexer::token_type::end_object) - { - return false; - } + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} - return true; - } - } + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(const std::vector&, const size_t) for the + related MessagePack format - case lexer::token_type::begin_array: - { - // read next token - get_token(); + @since version 2.0.9, parameter @a start_index since 2.1.1 + */ + static basic_json from_cbor(const std::vector& v, + const size_t start_index = 0) + { + binary_reader br(detail::input_adapter_factory::create(v.begin() + static_cast(start_index), v.end())); + return br.parse_cbor(); + } - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { - return true; - } - // parse values - while (true) - { - // parse value - if (not accept_internal()) - { - return false; - } + /*! + @brief create a JSON value from a byte vector in MessagePack format - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + Deserializes a given byte vector @a v to a JSON value using the MessagePack + serialization format. - // closing ] - if (last_token != lexer::token_type::end_array) - { - return false; - } + The library maps MessagePack types to JSON value types as follows: - return true; - } - } + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7f + fixmap | object | 0x80..0x8f + fixarray | array | 0x90..0x9f + fixstr | string | 0xa0..0xbf + nil | `null` | 0xc0 + false | `false` | 0xc2 + true | `true` | 0xc3 + float 32 | number_float | 0xca + float 64 | number_float | 0xcb + uint 8 | number_unsigned | 0xcc + uint 16 | number_unsigned | 0xcd + uint 32 | number_unsigned | 0xce + uint 64 | number_unsigned | 0xcf + int 8 | number_integer | 0xd0 + int 16 | number_integer | 0xd1 + int 32 | number_integer | 0xd2 + int 64 | number_integer | 0xd3 + str 8 | string | 0xd9 + str 16 | string | 0xda + str 32 | string | 0xdb + array 16 | array | 0xdc + array 32 | array | 0xdd + map 16 | object | 0xde + map 32 | object | 0xdf + negative fixint | number_integer | 0xe0-0xff - case lexer::token_type::literal_false: - case lexer::token_type::literal_null: - case lexer::token_type::literal_true: - case lexer::token_type::value_float: - case lexer::token_type::value_integer: - case lexer::token_type::value_string: - case lexer::token_type::value_unsigned: - { - return true; - } + @warning The mapping is **incomplete** in the sense that not all + MessagePack types can be converted to a JSON value. The following + MessagePack types are not supported and will yield parse errors: + - bin 8 - bin 32 (0xc4..0xc6) + - ext 8 - ext 32 (0xc7..0xc9) + - fixext 1 - fixext 16 (0xd4..0xd8) - default: - { - // the last token was unexpected - return false; - } - } - } + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. - /// get next token from lexer - typename lexer::token_type get_token() - { - return (last_token = m_lexer.scan()); - } + @param[in] v a byte vector in MessagePack format + @param[in] start_index the index to start reading from @a v (0 by default) + @return deserialized JSON value - /*! - @throw parse_error.101 if expected token did not occur - */ - void expect(typename lexer::token_type t) - { - if (JSON_UNLIKELY(t != last_token)) - { - errored = true; - expected = t; - throw_exception(); - } - } + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found - [[noreturn]] void throw_exception() const - { - std::string error_msg = "syntax error - "; - if (last_token == lexer::token_type::parse_error) - { - error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + m_lexer.get_token_string() + "'"; - } - else - { - error_msg += "unexpected " + std::string(lexer::token_type_name(last_token)); - } + @complexity Linear in the size of the byte vector @a v. - if (expected != lexer::token_type::uninitialized) - { - error_msg += "; expected " + std::string(lexer::token_type_name(expected)); - } + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} - JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); - } + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(const std::vector&, const size_t) for the + related CBOR format - private: - /// current level of recursion - int depth = 0; - /// callback function - const parser_callback_t callback = nullptr; - /// the type of the last read token - typename lexer::token_type last_token = lexer::token_type::uninitialized; - /// the lexer - lexer m_lexer; - /// whether a syntax error occurred - bool errored = false; - /// possible reason for the syntax error - typename lexer::token_type expected = lexer::token_type::uninitialized; - }; + @since version 2.0.9, parameter @a start_index since 2.1.1 + */ + static basic_json from_msgpack(const std::vector& v, + const size_t start_index = 0) + { + binary_reader br(detail::input_adapter_factory::create(v.begin() + static_cast(start_index), v.end())); + return br.parse_msgpack(); + } + + /// @} - public: ////////////////////////// // JSON Pointer support // //////////////////////////