diff options
Diffstat (limited to 'boost/property_tree')
-rw-r--r-- | boost/property_tree/detail/json_parser/narrow_encoding.hpp | 159 | ||||
-rw-r--r-- | boost/property_tree/detail/json_parser/parser.hpp | 524 | ||||
-rw-r--r-- | boost/property_tree/detail/json_parser/read.hpp | 55 | ||||
-rw-r--r-- | boost/property_tree/detail/json_parser/standard_callbacks.hpp | 152 | ||||
-rw-r--r-- | boost/property_tree/detail/json_parser/wide_encoding.hpp | 171 | ||||
-rw-r--r-- | boost/property_tree/detail/json_parser_read.hpp | 332 | ||||
-rw-r--r-- | boost/property_tree/detail/rapidxml.hpp | 2 | ||||
-rw-r--r-- | boost/property_tree/json_parser.hpp | 9 | ||||
-rw-r--r-- | boost/property_tree/ptree_serialization.hpp | 51 |
9 files changed, 1106 insertions, 349 deletions
diff --git a/boost/property_tree/detail/json_parser/narrow_encoding.hpp b/boost/property_tree/detail/json_parser/narrow_encoding.hpp new file mode 100644 index 0000000000..13218743d1 --- /dev/null +++ b/boost/property_tree/detail/json_parser/narrow_encoding.hpp @@ -0,0 +1,159 @@ +#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_NARROW_ENCODING_HPP +#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_NARROW_ENCODING_HPP + +#include <boost/range/iterator_range_core.hpp> + +#include <cassert> +#include <utility> + +namespace boost { namespace property_tree { + namespace json_parser { namespace detail +{ + + struct external_ascii_superset_encoding + { + typedef char external_char; + + bool is_nl(char c) const { return c == '\n'; } + bool is_ws(char c) const { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } + + bool is_minus(char c) const { return c == '-'; } + bool is_plusminus(char c) const { return c == '+' || c == '-'; } + bool is_dot(char c) const { return c == '.'; } + bool is_eE(char c) const { return c == 'e' || c == 'E'; } + bool is_0(char c) const { return c == '0'; } + bool is_digit(char c) const { return c >= '0' && c <= '9'; } + bool is_digit0(char c) const { return c >= '1' && c <= '9'; } + + bool is_quote(char c) const { return c == '"'; } + bool is_backslash(char c) const { return c == '\\'; } + bool is_slash(char c) const { return c == '/'; } + + bool is_comma(char c) const { return c == ','; } + bool is_open_bracket(char c) const { return c == '['; } + bool is_close_bracket(char c) const { return c == ']'; } + bool is_colon(char c) const { return c == ':'; } + bool is_open_brace(char c) const { return c == '{'; } + bool is_close_brace(char c) const { return c == '}'; } + + bool is_a(char c) const { return c == 'a'; } + bool is_b(char c) const { return c == 'b'; } + bool is_e(char c) const { return c == 'e'; } + bool is_f(char c) const { return c == 'f'; } + bool is_l(char c) const { return c == 'l'; } + bool is_n(char c) const { return c == 'n'; } + bool is_r(char c) const { return c == 'r'; } + bool is_s(char c) const { return c == 's'; } + bool is_t(char c) const { return c == 't'; } + bool is_u(char c) const { return c == 'u'; } + + int decode_hexdigit(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; + } + }; + + struct utf8_utf8_encoding : external_ascii_superset_encoding + { + typedef char internal_char; + + template <typename Iterator> + boost::iterator_range<Iterator> + to_internal(Iterator first, Iterator last) const { + return boost::make_iterator_range(first, last); + } + + char to_internal_trivial(char c) const { + assert(c <= 0x7f); + return c; + } + + template <typename Iterator, typename Sentinel, + typename EncodingErrorFn> + void skip_codepoint(Iterator& cur, Sentinel end, + EncodingErrorFn error_fn) const { + transcode_codepoint(cur, end, DoNothing(), error_fn); + } + + template <typename Iterator, typename Sentinel, typename TranscodedFn, + typename EncodingErrorFn> + void transcode_codepoint(Iterator& cur, Sentinel end, + TranscodedFn transcoded_fn, EncodingErrorFn error_fn) const { + unsigned char c = *cur; + ++cur; + if (c <= 0x7f) { + // Solo byte, filter out disallowed codepoints. + if (c < 0x20) { + error_fn(); + } + transcoded_fn(c); + return; + } + int trailing = trail_table(c); + if (trailing == -1) { + // Standalone trailing byte or overly long sequence. + error_fn(); + } + transcoded_fn(c); + for (int i = 0; i < trailing; ++i) { + if (cur == end || !is_trail(*cur)) { + error_fn(); + } + transcoded_fn(*cur); + ++cur; + } + } + + template <typename TranscodedFn> + void feed_codepoint(unsigned codepoint, + TranscodedFn transcoded_fn) const { + if (codepoint <= 0x7f) { + transcoded_fn(static_cast<char>(codepoint)); + } else if (codepoint <= 0x7ff) { + transcoded_fn(static_cast<char>(0xc0 | (codepoint >> 6))); + transcoded_fn(trail(codepoint)); + } else if (codepoint <= 0xffff) { + transcoded_fn(static_cast<char>(0xe0 | (codepoint >> 12))); + transcoded_fn(trail(codepoint >> 6)); + transcoded_fn(trail(codepoint)); + } else if (codepoint <= 0x10ffff) { + transcoded_fn(static_cast<char>(0xf0 | (codepoint >> 18))); + transcoded_fn(trail(codepoint >> 12)); + transcoded_fn(trail(codepoint >> 6)); + transcoded_fn(trail(codepoint)); + } + } + + private: + struct DoNothing { + void operator ()(char) const {} + }; + + bool is_trail(unsigned char c) const { + return (c & 0xc0) == 0x80; + } + + int trail_table(unsigned char c) const { + static const signed char table[] = { + /* not a lead byte */ + /* 0x10???sss */ -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x110??sss */ 1, 1, 1, 1, /* 1 trailing byte */ + /* 0x1110?sss */ 2, 2, /* 2 trailing bytes */ + /* 0x11110sss */ 3, /* 3 trailing bytes */ + /* 0x11111sss */ -1 /* 4 or 5 trailing bytes, disallowed */ + }; + return table[(c & 0x7f) >> 3]; + } + + char trail(unsigned unmasked) const { + return static_cast<char>(0x80 | (unmasked & 0x3f)); + } + }; + +}}}} + +#endif diff --git a/boost/property_tree/detail/json_parser/parser.hpp b/boost/property_tree/detail/json_parser/parser.hpp new file mode 100644 index 0000000000..c44b99291d --- /dev/null +++ b/boost/property_tree/detail/json_parser/parser.hpp @@ -0,0 +1,524 @@ +#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_PARSER_HPP +#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_PARSER_HPP + +#include <boost/property_tree/detail/json_parser_error.hpp> + +#include <boost/bind.hpp> +#include <boost/format.hpp> + +#include <iterator> +#include <sstream> +#include <string> + +namespace boost { namespace property_tree { + namespace json_parser { namespace detail +{ + + template <typename Encoding, typename Iterator, typename Sentinel> + class source + { + public: + typedef typename std::iterator_traits<Iterator>::value_type + code_unit; + typedef bool (Encoding::*encoding_predicate)(code_unit c) const; + + explicit source(Encoding& encoding) : encoding(encoding) {} + + template <typename Range> + void set_input(const std::string& filename, const Range& r) + { + this->filename = filename; + cur = r.begin(); + end = r.end(); + line = 1; + offset = 0; + } + + bool done() const { return cur == end; } + + void parse_error(const char* msg) { + BOOST_PROPERTY_TREE_THROW( + json_parser_error(msg, filename, line)); + } + + void next() { + if (encoding.is_nl(*cur)) { + ++line; + offset = 0; + } else { + ++offset; + } + ++cur; + } + + template <typename Action> + bool have(encoding_predicate p, Action& a) { + bool found = cur != end && (encoding.*p)(*cur); + if (found) { + a(*cur); + next(); + } + return found; + } + + bool have(encoding_predicate p) { + DoNothing n; + return have(p, n); + } + + template <typename Action> + void expect(encoding_predicate p, const char* msg, Action& a) { + if (!have(p, a)) { + parse_error(msg); + } + } + + void expect(encoding_predicate p, const char* msg) { + DoNothing n; + expect(p, msg, n); + } + + code_unit need_cur(const char* msg) { + if (cur == end) { + parse_error(msg); + } + return *cur; + } + + Iterator& raw_cur() { return cur; } + Sentinel raw_end() { return end; } + + private: + struct DoNothing { + void operator ()(code_unit) const {} + }; + + Encoding& encoding; + Iterator cur; + Sentinel end; + std::string filename; + int line; + int offset; + }; + + template <typename Callbacks, typename Encoding, typename Iterator, + typename = typename std::iterator_traits<Iterator> + ::iterator_category> + class number_callback_adapter + { + public: + number_callback_adapter(Callbacks& callbacks, Encoding& encoding, + Iterator& cur) + : callbacks(callbacks), encoding(encoding), first(cur), cur(cur) + {} + + void operator ()(typename Encoding::external_char) {} + + void finish() const { + callbacks.on_number(encoding.to_internal(first, cur)); + } + + private: + number_callback_adapter(const number_callback_adapter&); + + Callbacks& callbacks; + Encoding& encoding; + Iterator first; + Iterator& cur; + }; + + template <typename Callbacks, typename Encoding, typename Iterator> + class number_callback_adapter<Callbacks, Encoding, Iterator, + std::input_iterator_tag> + { + public: + number_callback_adapter(Callbacks& callbacks, Encoding& encoding, + Iterator&) + : callbacks(callbacks), encoding(encoding), first(true) + {} + + void operator ()(typename Encoding::external_char c) { + if (first) { + callbacks.on_begin_number(); + first = false; + } + callbacks.on_digit(encoding.to_internal_trivial(c)); + } + + void finish() const { + callbacks.on_end_number(); + } + private: + number_callback_adapter(const number_callback_adapter&); + + Callbacks& callbacks; + Encoding& encoding; + bool first; + }; + + template <typename Callbacks, typename Encoding, typename Iterator, + typename = typename std::iterator_traits<Iterator> + ::iterator_category> + class string_callback_adapter + { + public: + string_callback_adapter(Callbacks& callbacks, Encoding& encoding, + Iterator& cur) + : callbacks(callbacks), encoding(encoding), cur(cur), + run_begin(cur) + {} + + void start_run() { + run_begin = cur; + } + + void finish_run() { + callbacks.on_code_units(encoding.to_internal(run_begin, cur)); + } + + template <typename Sentinel, typename EncodingErrorFn> + void process_codepoint(Sentinel end, EncodingErrorFn error_fn) { + encoding.skip_codepoint(cur, end, error_fn); + } + + private: + string_callback_adapter(const string_callback_adapter&); + + Callbacks& callbacks; + Encoding& encoding; + Iterator& cur; + Iterator run_begin; + }; + + template <typename Callbacks, typename Encoding, typename Iterator> + class string_callback_adapter<Callbacks, Encoding, Iterator, + std::input_iterator_tag> + { + public: + string_callback_adapter(Callbacks& callbacks, Encoding& encoding, + Iterator& cur) + : callbacks(callbacks), encoding(encoding), cur(cur) + {} + + void start_run() {} + + void finish_run() {} + + template <typename Sentinel, typename EncodingErrorFn> + void process_codepoint(Sentinel end, EncodingErrorFn error_fn) { + encoding.transcode_codepoint(cur, end, + boost::bind(&Callbacks::on_code_unit, + boost::ref(callbacks), _1), + error_fn); + } + + private: + string_callback_adapter(const string_callback_adapter&); + + Callbacks& callbacks; + Encoding& encoding; + Iterator& cur; + }; + + template <typename Callbacks, typename Encoding, typename Iterator, + typename Sentinel> + class parser + { + typedef detail::number_callback_adapter<Callbacks, Encoding, Iterator> + number_adapter; + typedef detail::string_callback_adapter<Callbacks, Encoding, Iterator> + string_adapter; + typedef detail::source<Encoding, Iterator, Sentinel> source; + typedef typename source::code_unit code_unit; + + public: + parser(Callbacks& callbacks, Encoding& encoding) + : callbacks(callbacks), encoding(encoding), src(encoding) + {} + + template <typename Range> + void set_input(const std::string& filename, const Range& r) { + src.set_input(filename, r); + } + + void finish() { + skip_ws(); + if (!src.done()) { + parse_error("garbage after data"); + } + } + + void parse_value() { + if (parse_object()) return; + if (parse_array()) return; + if (parse_string()) return; + if (parse_boolean()) return; + if (parse_null()) return; + if (parse_number()) return; + parse_error("expected value"); + } + + bool parse_null() { + skip_ws(); + if (!have(&Encoding::is_n)) { + return false; + } + expect(&Encoding::is_u, "expected 'null'"); + expect(&Encoding::is_l, "expected 'null'"); + expect(&Encoding::is_l, "expected 'null'"); + callbacks.on_null(); + return true; + } + + bool parse_boolean() { + skip_ws(); + if (have(&Encoding::is_t)) { + expect(&Encoding::is_r, "expected 'true'"); + expect(&Encoding::is_u, "expected 'true'"); + expect(&Encoding::is_e, "expected 'true'"); + callbacks.on_boolean(true); + return true; + } + if (have(&Encoding::is_f)) { + expect(&Encoding::is_a, "expected 'false'"); + expect(&Encoding::is_l, "expected 'false'"); + expect(&Encoding::is_s, "expected 'false'"); + expect(&Encoding::is_e, "expected 'false'"); + callbacks.on_boolean(false); + return true; + } + return false; + } + + bool parse_number() { + skip_ws(); + + number_adapter adapter(callbacks, encoding, src.raw_cur()); + bool started = false; + if (have(&Encoding::is_minus, adapter)) { + started = true; + } + if (!have(&Encoding::is_0, adapter) && !parse_int_part(adapter)) { + if (started) { + parse_error("expected digits after -"); + } + return false; + } + parse_frac_part(adapter); + parse_exp_part(adapter); + adapter.finish(); + return true; + } + + bool parse_string() { + skip_ws(); + + if (!have(&Encoding::is_quote)) { + return false; + } + + callbacks.on_begin_string(); + string_adapter adapter(callbacks, encoding, src.raw_cur()); + while (!encoding.is_quote(need_cur("unterminated string"))) { + if (encoding.is_backslash(*src.raw_cur())) { + adapter.finish_run(); + next(); + parse_escape(); + adapter.start_run(); + } else { + adapter.process_codepoint(src.raw_end(), + boost::bind(&parser::parse_error, + this, "invalid code sequence")); + } + } + adapter.finish_run(); + callbacks.on_end_string(); + next(); + return true; + } + + bool parse_array() { + skip_ws(); + + if (!have(&Encoding::is_open_bracket)) { + return false; + } + + callbacks.on_begin_array(); + skip_ws(); + if (have(&Encoding::is_close_bracket)) { + callbacks.on_end_array(); + return true; + } + do { + parse_value(); + skip_ws(); + } while (have(&Encoding::is_comma)); + expect(&Encoding::is_close_bracket, "expected ']' or ','"); + callbacks.on_end_array(); + return true; + } + + bool parse_object() { + skip_ws(); + + if (!have(&Encoding::is_open_brace)) { + return false; + } + + callbacks.on_begin_object(); + skip_ws(); + if (have(&Encoding::is_close_brace)) { + callbacks.on_end_object(); + return true; + } + do { + if (!parse_string()) { + parse_error("expected key string"); + } + skip_ws(); + expect(&Encoding::is_colon, "expected ':'"); + parse_value(); + skip_ws(); + } while (have(&Encoding::is_comma)); + expect(&Encoding::is_close_brace, "expected '}' or ','"); + callbacks.on_end_object(); + return true; + } + + private: + typedef typename source::encoding_predicate encoding_predicate; + + void parse_error(const char* msg) { src.parse_error(msg); } + void next() { src.next(); } + template <typename Action> + bool have(encoding_predicate p, Action& a) { return src.have(p, a); } + bool have(encoding_predicate p) { return src.have(p); } + template <typename Action> + void expect(encoding_predicate p, const char* msg, Action& a) { + src.expect(p, msg, a); + } + void expect(encoding_predicate p, const char* msg) { + src.expect(p, msg); + } + code_unit need_cur(const char* msg) { return src.need_cur(msg); } + + void skip_ws() { + while (have(&Encoding::is_ws)) { + } + } + + bool parse_int_part(number_adapter& action) { + if (!have(&Encoding::is_digit0, action)) { + return false; + } + parse_digits(action); + return true; + } + + void parse_frac_part(number_adapter& action) { + if (!have(&Encoding::is_dot, action)) { + return; + } + expect(&Encoding::is_digit, "need at least one digit after '.'", + action); + parse_digits(action); + } + + void parse_exp_part(number_adapter& action) { + if (!have(&Encoding::is_eE, action)) { + return; + } + have(&Encoding::is_plusminus, action); + expect(&Encoding::is_digit, "need at least one digit in exponent", + action); + parse_digits(action); + } + + void parse_digits(number_adapter& action) { + while (have(&Encoding::is_digit, action)) { + } + } + + void parse_escape() { + if (have(&Encoding::is_quote)) { + feed(0x22); + } else if (have(&Encoding::is_backslash)) { + feed(0x5c); + } else if (have(&Encoding::is_slash)) { + feed(0x2f); + } else if (have(&Encoding::is_b)) { + feed(0x08); // backspace + } else if (have(&Encoding::is_f)) { + feed(0x0c); // formfeed + } else if (have(&Encoding::is_n)) { + feed(0x0a); // line feed + } else if (have(&Encoding::is_r)) { + feed(0x0d); // carriage return + } else if (have(&Encoding::is_t)) { + feed(0x09); // horizontal tab + } else if (have(&Encoding::is_u)) { + parse_codepoint_ref(); + } else { + parse_error("invalid escape sequence"); + } + } + + unsigned parse_hex_quad() { + unsigned codepoint = 0; + for (int i = 0; i < 4; ++i) { + int value = encoding.decode_hexdigit( + need_cur("invalid escape sequence")); + if (value < 0) { + parse_error("invalid escape sequence"); + } + codepoint *= 16; + codepoint += value; + next(); + } + return codepoint; + } + + static bool is_surrogate_high(unsigned codepoint) { + return (codepoint & 0xfc00) == 0xd800; + } + static bool is_surrogate_low(unsigned codepoint) { + return (codepoint & 0xfc00) == 0xdc00; + } + static unsigned combine_surrogates(unsigned high, unsigned low) { + return 0x010000 + (((high & 0x3ff) << 10) | (low & 0x3ff)); + } + + void parse_codepoint_ref() { + unsigned codepoint = parse_hex_quad(); + if (is_surrogate_low(codepoint)) { + parse_error("invalid codepoint, stray low surrogate"); + } + if (is_surrogate_high(codepoint)) { + expect(&Encoding::is_backslash, + "invalid codepoint, stray high surrogate"); + expect(&Encoding::is_u, + "expected codepoint reference after high surrogate"); + int low = parse_hex_quad(); + if (!is_surrogate_low(low)) { + parse_error("expected low surrogate after high surrogate"); + } + codepoint = combine_surrogates(codepoint, low); + } + feed(codepoint); + } + + void feed(unsigned codepoint) { + encoding.feed_codepoint(codepoint, + boost::bind(&Callbacks::on_code_unit, + boost::ref(callbacks), _1)); + } + + Callbacks& callbacks; + Encoding& encoding; + source src; + }; + +}}}} + +#endif diff --git a/boost/property_tree/detail/json_parser/read.hpp b/boost/property_tree/detail/json_parser/read.hpp new file mode 100644 index 0000000000..61c0162781 --- /dev/null +++ b/boost/property_tree/detail/json_parser/read.hpp @@ -0,0 +1,55 @@ +// ---------------------------------------------------------------------------- +// Copyright (C) 2015 Sebastian Redl +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see www.boost.org +// ---------------------------------------------------------------------------- +#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP +#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP + +#include <boost/property_tree/detail/json_parser/parser.hpp> +#include <boost/property_tree/detail/json_parser/narrow_encoding.hpp> +#include <boost/property_tree/detail/json_parser/wide_encoding.hpp> +#include <boost/property_tree/detail/json_parser/standard_callbacks.hpp> + +#include <boost/range/iterator_range_core.hpp> + +#include <istream> +#include <iterator> +#include <string> + +namespace boost { namespace property_tree { + namespace json_parser { namespace detail +{ + + template <typename Ch> struct encoding; + template <> struct encoding<char> : utf8_utf8_encoding {}; + template <> struct encoding<wchar_t> : wide_wide_encoding {}; + + template <typename Ptree> + void read_json_internal( + std::basic_istream<typename Ptree::key_type::value_type> &stream, + Ptree &pt, const std::string &filename) + { + typedef typename Ptree::key_type::value_type char_type; + typedef standard_callbacks<Ptree> callbacks_type; + typedef detail::encoding<char_type> encoding_type; + typedef std::istreambuf_iterator<char_type> iterator; + callbacks_type callbacks; + encoding_type encoding; + detail::parser<callbacks_type, encoding_type, iterator, iterator> + parser(callbacks, encoding); + parser.set_input(filename, + boost::make_iterator_range(iterator(stream), iterator())); + parser.parse_value(); + parser.finish(); + + pt.swap(callbacks.output()); + } + +}}}} + +#endif diff --git a/boost/property_tree/detail/json_parser/standard_callbacks.hpp b/boost/property_tree/detail/json_parser/standard_callbacks.hpp new file mode 100644 index 0000000000..56c378ec9c --- /dev/null +++ b/boost/property_tree/detail/json_parser/standard_callbacks.hpp @@ -0,0 +1,152 @@ +#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_STANDARD_CALLBACKS_HPP +#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_STANDARD_CALLBACKS_HPP + +#include <boost/property_tree/ptree.hpp> +#include <vector> + +namespace boost { namespace property_tree { + namespace json_parser { namespace detail +{ + + namespace constants + { + template <typename Ch> const Ch* null_value(); + template <> inline const char* null_value() { return "null"; } + template <> inline const wchar_t* null_value() { return L"null"; } + + template <typename Ch> const Ch* true_value(); + template <> inline const char* true_value() { return "true"; } + template <> inline const wchar_t* true_value() { return L"true"; } + + template <typename Ch> const Ch* false_value(); + template <> inline const char* false_value() { return "false"; } + template <> inline const wchar_t* false_value() { return L"false"; } + } + + template <typename Ptree> + class standard_callbacks { + public: + typedef typename Ptree::data_type string; + typedef typename string::value_type char_type; + + void on_null() { + new_value() = constants::null_value<char_type>(); + } + + void on_boolean(bool b) { + new_value() = b ? constants::true_value<char_type>() + : constants::false_value<char_type>(); + } + + template <typename Range> + void on_number(Range code_units) { + new_value().assign(code_units.begin(), code_units.end()); + } + void on_begin_number() { + new_value(); + } + void on_digit(char_type d) { + current_value() += d; + } + void on_end_number() {} + + void on_begin_string() { + new_value(); + } + template <typename Range> + void on_code_units(Range code_units) { + current_value().append(code_units.begin(), code_units.end()); + } + void on_code_unit(char_type c) { + current_value() += c; + } + void on_end_string() {} + + void on_begin_array() { + new_tree(); + stack.back().k = array; + } + void on_end_array() { + if (stack.back().k == leaf) stack.pop_back(); + stack.pop_back(); + } + + void on_begin_object() { + new_tree(); + stack.back().k = object; + } + void on_end_object() { + if (stack.back().k == leaf) stack.pop_back(); + stack.pop_back(); + } + + Ptree& output() { return root; } + + protected: + bool is_key() const { + return stack.back().k == key; + } + string& current_value() { + layer& l = stack.back(); + switch (l.k) { + case key: return key_buffer; + default: return l.t->data(); + } + } + + private: + Ptree root; + string key_buffer; + enum kind { array, object, key, leaf }; + struct layer { kind k; Ptree* t; }; + std::vector<layer> stack; + + Ptree& new_tree() { + if (stack.empty()) { + layer l = {leaf, &root}; + stack.push_back(l); + return root; + } + layer& l = stack.back(); + switch (l.k) { + case array: { + l.t->push_back(std::make_pair(string(), Ptree())); + layer nl = {leaf, &l.t->back().second}; + stack.push_back(nl); + return *stack.back().t; + } + case object: + assert(false); // must start with string, i.e. call new_value + case key: { + l.t->push_back(std::make_pair(key_buffer, Ptree())); + l.k = object; + layer nl = {leaf, &l.t->back().second}; + stack.push_back(nl); + return *stack.back().t; + } + case leaf: + stack.pop_back(); + return new_tree(); + } + assert(false); + } + string& new_value() { + if (stack.empty()) return new_tree().data(); + layer& l = stack.back(); + switch (l.k) { + case leaf: + stack.pop_back(); + return new_value(); + case object: + l.k = key; + key_buffer.clear(); + return key_buffer; + default: + return new_tree().data(); + } + } + }; + +}}}} + +#endif diff --git a/boost/property_tree/detail/json_parser/wide_encoding.hpp b/boost/property_tree/detail/json_parser/wide_encoding.hpp new file mode 100644 index 0000000000..0dec1c03b0 --- /dev/null +++ b/boost/property_tree/detail/json_parser/wide_encoding.hpp @@ -0,0 +1,171 @@ +#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_WIDE_ENCODING_HPP +#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_WIDE_ENCODING_HPP + +#include <boost/range/iterator_range_core.hpp> + +#include <cassert> +#include <utility> + +namespace boost { namespace property_tree { + namespace json_parser { namespace detail +{ + + struct external_wide_encoding + { + typedef wchar_t external_char; + + bool is_nl(wchar_t c) const { return c == L'\n'; } + bool is_ws(wchar_t c) const { + return c == L' ' || c == L'\t' || c == L'\n' || c == L'\r'; + } + + bool is_minus(wchar_t c) const { return c == L'-'; } + bool is_plusminus(wchar_t c) const { return c == L'+' || c == L'-'; } + bool is_dot(wchar_t c) const { return c == L'.'; } + bool is_eE(wchar_t c) const { return c == L'e' || c == L'E'; } + bool is_0(wchar_t c) const { return c == L'0'; } + bool is_digit(wchar_t c) const { return c >= L'0' && c <= L'9'; } + bool is_digit0(wchar_t c) const { return c >= L'1' && c <= L'9'; } + + bool is_quote(wchar_t c) const { return c == L'"'; } + bool is_backslash(wchar_t c) const { return c == L'\\'; } + bool is_slash(wchar_t c) const { return c == L'/'; } + + bool is_comma(wchar_t c) const { return c == L','; } + bool is_open_bracket(wchar_t c) const { return c == L'['; } + bool is_close_bracket(wchar_t c) const { return c == L']'; } + bool is_colon(wchar_t c) const { return c == L':'; } + bool is_open_brace(wchar_t c) const { return c == L'{'; } + bool is_close_brace(wchar_t c) const { return c == L'}'; } + + bool is_a(wchar_t c) const { return c == L'a'; } + bool is_b(wchar_t c) const { return c == L'b'; } + bool is_e(wchar_t c) const { return c == L'e'; } + bool is_f(wchar_t c) const { return c == L'f'; } + bool is_l(wchar_t c) const { return c == L'l'; } + bool is_n(wchar_t c) const { return c == L'n'; } + bool is_r(wchar_t c) const { return c == L'r'; } + bool is_s(wchar_t c) const { return c == L's'; } + bool is_t(wchar_t c) const { return c == L't'; } + bool is_u(wchar_t c) const { return c == L'u'; } + + int decode_hexdigit(wchar_t c) { + if (c >= L'0' && c <= L'9') return c - L'0'; + if (c >= L'A' && c <= L'F') return c - L'A' + 10; + if (c >= L'a' && c <= L'f') return c - L'a' + 10; + return -1; + } + }; + + template <bool B> struct is_utf16 {}; + + class wide_wide_encoding : public external_wide_encoding + { + typedef is_utf16<sizeof(wchar_t) == 2> test_utf16; + public: + typedef wchar_t internal_char; + + template <typename Iterator> + boost::iterator_range<Iterator> + to_internal(Iterator first, Iterator last) const { + return boost::make_iterator_range(first, last); + } + + wchar_t to_internal_trivial(wchar_t c) const { + assert(!is_surrogate_high(c) && !is_surrogate_low(c)); + return c; + } + + template <typename Iterator, typename Sentinel, + typename EncodingErrorFn> + void skip_codepoint(Iterator& cur, Sentinel end, + EncodingErrorFn error_fn) const { + transcode_codepoint(cur, end, DoNothing(), error_fn); + } + + template <typename Iterator, typename Sentinel, typename TranscodedFn, + typename EncodingErrorFn> + void transcode_codepoint(Iterator& cur, Sentinel end, + TranscodedFn transcoded_fn, EncodingErrorFn error_fn) const { + return transcode_codepoint(cur, end, transcoded_fn, error_fn, + test_utf16()); + } + + template <typename TranscodedFn> + void feed_codepoint(unsigned codepoint, + TranscodedFn transcoded_fn) const { + feed_codepoint(codepoint, transcoded_fn, test_utf16()); + } + + private: + struct DoNothing { + void operator ()(wchar_t) const {} + }; + + template <typename Iterator, typename Sentinel, typename TranscodedFn, + typename EncodingErrorFn> + void transcode_codepoint(Iterator& cur, Sentinel end, + TranscodedFn transcoded_fn, + EncodingErrorFn error_fn, + is_utf16<false>) const { + wchar_t c = *cur; + if (c < 0x20) { + error_fn(); + } + transcoded_fn(c); + ++cur; + } + template <typename Iterator, typename Sentinel, typename TranscodedFn, + typename EncodingErrorFn> + void transcode_codepoint(Iterator& cur, Sentinel end, + TranscodedFn transcoded_fn, + EncodingErrorFn error_fn, + is_utf16<true>) const { + wchar_t c = *cur; + if (c < 0x20) { + error_fn(); + } + if (is_surrogate_low(c)) { + error_fn(); + } + transcoded_fn(c); + ++cur; + if (is_surrogate_high(c)) { + c = *cur; + if (!is_surrogate_low(c)) { + error_fn(); + } + transcoded_fn(c); + ++cur; + } + } + + template <typename TranscodedFn> + void feed_codepoint(unsigned codepoint, TranscodedFn transcoded_fn, + is_utf16<false>) const { + transcoded_fn(static_cast<wchar_t>(codepoint)); + } + template <typename TranscodedFn> + void feed_codepoint(unsigned codepoint, TranscodedFn transcoded_fn, + is_utf16<true>) const { + if (codepoint < 0x10000) { + transcoded_fn(static_cast<wchar_t>(codepoint)); + } else { + codepoint -= 0x10000; + transcoded_fn(static_cast<wchar_t>((codepoint >> 10) | 0xd800)); + transcoded_fn(static_cast<wchar_t>( + (codepoint & 0x3ff) | 0xdc00)); + } + } + + static bool is_surrogate_high(unsigned codepoint) { + return (codepoint & 0xfc00) == 0xd800; + } + static bool is_surrogate_low(unsigned codepoint) { + return (codepoint & 0xfc00) == 0xdc00; + } + }; + +}}}} + +#endif diff --git a/boost/property_tree/detail/json_parser_read.hpp b/boost/property_tree/detail/json_parser_read.hpp deleted file mode 100644 index 798552f6f1..0000000000 --- a/boost/property_tree/detail/json_parser_read.hpp +++ /dev/null @@ -1,332 +0,0 @@ -// ---------------------------------------------------------------------------- -// Copyright (C) 2002-2006 Marcin Kalicinski -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// For more information, see www.boost.org -// ---------------------------------------------------------------------------- -#ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED -#define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED - -//#define BOOST_SPIRIT_DEBUG - -#include <boost/property_tree/ptree.hpp> -#include <boost/property_tree/detail/ptree_utils.hpp> -#include <boost/property_tree/detail/json_parser_error.hpp> -#include <boost/spirit/include/classic.hpp> -#include <boost/limits.hpp> -#include <string> -#include <locale> -#include <istream> -#include <vector> -#include <algorithm> - -namespace boost { namespace property_tree { namespace json_parser -{ - - /////////////////////////////////////////////////////////////////////// - // Json parser context - - template<class Ptree> - struct context - { - typedef typename Ptree::key_type Str; - typedef typename Str::value_type Ch; - typedef typename std::vector<Ch>::iterator It; - - Str string; - Str name; - Ptree root; - std::vector<Ptree *> stack; - - struct a_object_s - { - context &c; - a_object_s(context &c): c(c) { } - void operator()(Ch) const - { - if (c.stack.empty()) - c.stack.push_back(&c.root); - else - { - Ptree *parent = c.stack.back(); - Ptree *child = &parent->push_back(std::make_pair(c.name, Ptree()))->second; - c.stack.push_back(child); - c.name.clear(); - } - } - }; - - struct a_object_e - { - context &c; - a_object_e(context &c): c(c) { } - void operator()(Ch) const - { - BOOST_ASSERT(c.stack.size() >= 1); - c.stack.pop_back(); - } - }; - - struct a_name - { - context &c; - a_name(context &c): c(c) { } - void operator()(It, It) const - { - c.name.swap(c.string); - c.string.clear(); - } - }; - - struct a_string_val - { - context &c; - a_string_val(context &c): c(c) { } - void operator()(It, It) const - { - BOOST_ASSERT(c.stack.size() >= 1); - c.stack.back()->push_back(std::make_pair(c.name, Ptree(c.string))); - c.name.clear(); - c.string.clear(); - } - }; - - struct a_literal_val - { - context &c; - a_literal_val(context &c): c(c) { } - void operator()(It b, It e) const - { - BOOST_ASSERT(c.stack.size() >= 1); - c.stack.back()->push_back(std::make_pair(c.name, - Ptree(Str(b, e)))); - c.name.clear(); - c.string.clear(); - } - }; - - struct a_char - { - context &c; - a_char(context &c): c(c) { } - void operator()(It b, It) const - { - c.string += *b; - } - }; - - struct a_escape - { - context &c; - a_escape(context &c): c(c) { } - void operator()(Ch ch) const - { - switch (ch) - { - case Ch('\"'): c.string += Ch('\"'); break; - case Ch('\\'): c.string += Ch('\\'); break; - case Ch('/'): c.string += Ch('/'); break; - case Ch('b'): c.string += Ch('\b'); break; - case Ch('f'): c.string += Ch('\f'); break; - case Ch('n'): c.string += Ch('\n'); break; - case Ch('r'): c.string += Ch('\r'); break; - case Ch('t'): c.string += Ch('\t'); break; - default: BOOST_ASSERT(0); - } - } - }; - - struct a_unicode - { - context &c; - a_unicode(context &c): c(c) { } - void operator()(unsigned long u) const - { - u = (std::min)(u, static_cast<unsigned long>((std::numeric_limits<Ch>::max)())); - c.string += Ch(u); - } - }; - - }; - - /////////////////////////////////////////////////////////////////////// - // Json grammar - - template<class Ptree> - struct json_grammar : - public boost::spirit::classic::grammar<json_grammar<Ptree> > - { - - typedef context<Ptree> Context; - typedef typename Ptree::key_type Str; - typedef typename Str::value_type Ch; - - mutable Context c; - - template<class Scanner> - struct definition - { - - boost::spirit::classic::rule<Scanner> - root, object, member, array, item, value, string, number; - boost::spirit::classic::rule< - typename boost::spirit::classic::lexeme_scanner<Scanner>::type> - character, escape; - - definition(const json_grammar &self) - { - - using namespace boost::spirit::classic; - // There's a boost::assertion too, so another explicit using - // here: - using boost::spirit::classic::assertion; - - // Assertions - assertion<std::string> expect_root("expected object or array"); - assertion<std::string> expect_eoi("expected end of input"); - assertion<std::string> expect_objclose("expected ',' or '}'"); - assertion<std::string> expect_arrclose("expected ',' or ']'"); - assertion<std::string> expect_name("expected object name"); - assertion<std::string> expect_colon("expected ':'"); - assertion<std::string> expect_value("expected value"); - assertion<std::string> expect_escape("invalid escape sequence"); - - // JSON grammar rules - root - = expect_root(object | array) - >> expect_eoi(end_p) - ; - - object - = ch_p('{')[typename Context::a_object_s(self.c)] - >> (ch_p('}')[typename Context::a_object_e(self.c)] - | (list_p(member, ch_p(',')) - >> expect_objclose(ch_p('}')[typename Context::a_object_e(self.c)]) - ) - ) - ; - - member - = expect_name(string[typename Context::a_name(self.c)]) - >> expect_colon(ch_p(':')) - >> expect_value(value) - ; - - array - = ch_p('[')[typename Context::a_object_s(self.c)] - >> (ch_p(']')[typename Context::a_object_e(self.c)] - | (list_p(item, ch_p(',')) - >> expect_arrclose(ch_p(']')[typename Context::a_object_e(self.c)]) - ) - ) - ; - - item - = expect_value(value) - ; - - value - = string[typename Context::a_string_val(self.c)] - | (number | str_p("true") | "false" | "null")[typename Context::a_literal_val(self.c)] - | object - | array - ; - - number - = !ch_p("-") >> - (ch_p("0") | (range_p(Ch('1'), Ch('9')) >> *digit_p)) >> - !(ch_p(".") >> +digit_p) >> - !(chset_p(detail::widen<Str>("eE").c_str()) >> - !chset_p(detail::widen<Str>("-+").c_str()) >> - +digit_p) - ; - - string - = +(lexeme_d[confix_p('\"', *character, '\"')]) - ; - - character - = (anychar_p - "\\" - "\"") - [typename Context::a_char(self.c)] - | ch_p("\\") >> expect_escape(escape) - ; - - escape - = chset_p(detail::widen<Str>("\"\\/bfnrt").c_str()) - [typename Context::a_escape(self.c)] - | 'u' >> uint_parser<unsigned long, 16, 4, 4>() - [typename Context::a_unicode(self.c)] - ; - - // Debug - BOOST_SPIRIT_DEBUG_RULE(root); - BOOST_SPIRIT_DEBUG_RULE(object); - BOOST_SPIRIT_DEBUG_RULE(member); - BOOST_SPIRIT_DEBUG_RULE(array); - BOOST_SPIRIT_DEBUG_RULE(item); - BOOST_SPIRIT_DEBUG_RULE(value); - BOOST_SPIRIT_DEBUG_RULE(string); - BOOST_SPIRIT_DEBUG_RULE(number); - BOOST_SPIRIT_DEBUG_RULE(escape); - BOOST_SPIRIT_DEBUG_RULE(character); - - } - - const boost::spirit::classic::rule<Scanner> &start() const - { - return root; - } - - }; - - }; - - template<class It, class Ch> - unsigned long count_lines(It begin, It end) - { - return static_cast<unsigned long>(std::count(begin, end, Ch('\n')) + 1); - } - - template<class Ptree> - void read_json_internal(std::basic_istream<typename Ptree::key_type::value_type> &stream, - Ptree &pt, - const std::string &filename) - { - - using namespace boost::spirit::classic; - typedef typename Ptree::key_type::value_type Ch; - typedef typename std::vector<Ch>::iterator It; - - // Load data into vector - std::vector<Ch> v(std::istreambuf_iterator<Ch>(stream.rdbuf()), - std::istreambuf_iterator<Ch>()); - if (!stream.good()) - BOOST_PROPERTY_TREE_THROW(json_parser_error("read error", filename, 0)); - - // Prepare grammar - json_grammar<Ptree> g; - - // Parse - try - { - parse_info<It> pi = parse(v.begin(), v.end(), g, - space_p | comment_p("//") | comment_p("/*", "*/")); - if (!pi.hit || !pi.full) - BOOST_PROPERTY_TREE_THROW((parser_error<std::string, It>(v.begin(), "syntax error"))); - } - catch (parser_error<std::string, It> &e) - { - BOOST_PROPERTY_TREE_THROW(json_parser_error(e.descriptor, filename, count_lines<It, Ch>(v.begin(), e.where))); - } - - // Swap grammar context root and pt - pt.swap(g.c.root); - - } - -} } } - -#endif diff --git a/boost/property_tree/detail/rapidxml.hpp b/boost/property_tree/detail/rapidxml.hpp index d3615c1193..9e3d76af9d 100644 --- a/boost/property_tree/detail/rapidxml.hpp +++ b/boost/property_tree/detail/rapidxml.hpp @@ -65,7 +65,7 @@ namespace boost { namespace property_tree { namespace detail {namespace rapidxml //! Gets pointer to character data where error happened. //! Ch should be the same as char type of xml_document that produced the error. - //! \return Pointer to location within the parsed string where error occured. + //! \return Pointer to location within the parsed string where error occurred. template<class Ch> Ch *where() const { diff --git a/boost/property_tree/json_parser.hpp b/boost/property_tree/json_parser.hpp index d3bc32edb1..d8a3e1025f 100644 --- a/boost/property_tree/json_parser.hpp +++ b/boost/property_tree/json_parser.hpp @@ -1,5 +1,6 @@ // ---------------------------------------------------------------------------- // Copyright (C) 2002-2006 Marcin Kalicinski +// Copyright (C) 2015 Sebastian Redl // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -11,9 +12,9 @@ #define BOOST_PROPERTY_TREE_JSON_PARSER_HPP_INCLUDED #include <boost/property_tree/ptree.hpp> -#include <boost/property_tree/detail/json_parser_read.hpp> -#include <boost/property_tree/detail/json_parser_write.hpp> #include <boost/property_tree/detail/json_parser_error.hpp> +#include <boost/property_tree/detail/json_parser/read.hpp> +#include <boost/property_tree/detail/json_parser_write.hpp> #include <fstream> #include <string> @@ -42,7 +43,7 @@ namespace boost { namespace property_tree { namespace json_parser > &stream, Ptree &pt) { - read_json_internal(stream, pt, std::string()); + detail::read_json_internal(stream, pt, std::string()); } /** @@ -71,7 +72,7 @@ namespace boost { namespace property_tree { namespace json_parser BOOST_PROPERTY_TREE_THROW(json_parser_error( "cannot open file", filename, 0)); stream.imbue(loc); - read_json_internal(stream, pt, filename); + detail::read_json_internal(stream, pt, filename); } /** diff --git a/boost/property_tree/ptree_serialization.hpp b/boost/property_tree/ptree_serialization.hpp index 4dc916c0d0..a8181ca3b7 100644 --- a/boost/property_tree/ptree_serialization.hpp +++ b/boost/property_tree/ptree_serialization.hpp @@ -14,7 +14,7 @@ #include <boost/serialization/nvp.hpp> #include <boost/serialization/collections_save_imp.hpp> -#include <boost/serialization/collections_load_imp.hpp> +#include <boost/serialization/detail/stack_constructor.hpp> #include <boost/serialization/split_free.hpp> #include <boost/serialization/utility.hpp> @@ -48,6 +48,41 @@ namespace boost { namespace property_tree ar << make_nvp("data", t.data()); } + namespace detail + { + template <class Archive, class K, class D, class C> + inline void load_children(Archive &ar, + basic_ptree<K, D, C> &t) + { + namespace bsl = boost::serialization; + namespace bsa = boost::archive; + + typedef basic_ptree<K, D, C> tree; + typedef typename tree::value_type value_type; + + bsl::collection_size_type count; + ar >> BOOST_SERIALIZATION_NVP(count); + bsl::item_version_type item_version(0); + const bsa::library_version_type library_version( + ar.get_library_version() + ); + if(bsa::library_version_type(3) < library_version){ + ar >> BOOST_SERIALIZATION_NVP(item_version); + } + // Can't use the serialization helper, it expects resize() to exist + // for default-constructible elements. + // This is a copy/paste of the fallback version. + t.clear(); + while(count-- > 0){ + bsl::detail::stack_construct<Archive, value_type> + u(ar, item_version); + ar >> bsl::make_nvp("item", u.reference()); + t.push_back(u.reference()); + ar.reset_object_address(& t.back() , & u.reference()); + } + } + } + /** * De-serialize the property tree to the given archive. * @note In addition to de-serializing from regular archives, this supports @@ -66,18 +101,10 @@ namespace boost { namespace property_tree basic_ptree<K, D, C> &t, const unsigned int file_version) { - using namespace boost::serialization; - // Load children - stl::load_collection<Archive, - basic_ptree<K, D, C>, - stl::archive_input_seq<Archive, - basic_ptree<K, D, C> >, - stl::no_reserve_imp< - basic_ptree<K, D, C> > - >(ar, t); + namespace bsl = boost::serialization; - // Load data (must be after load_collection, as it calls clear()) - ar >> make_nvp("data", t.data()); + detail::load_children(ar, t); + ar >> bsl::make_nvp("data", t.data()); } /** |