diff --git a/bench/bench.cpp b/bench/bench.cpp index 8e785bdf..3c1bc36b 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -294,7 +294,7 @@ public: out.reserve(512); while(repeat--) { - sr.reset(jv); + sr.reset(&jv); out.clear(); for(;;) { @@ -362,7 +362,7 @@ public: out.reserve(512); while(repeat--) { - sr.reset(jv); + sr.reset(&jv); out.clear(); for(;;) { diff --git a/doc/qbk/02_8_serializing.qbk b/doc/qbk/02_8_serializing.qbk index 563ba774..7472b762 100644 --- a/doc/qbk/02_8_serializing.qbk +++ b/doc/qbk/02_8_serializing.qbk @@ -25,7 +25,7 @@ serialization: [@https://en.cppreference.com/w/cpp/io/basic_ostream `std::ostream`]. ] ][ - [__to_string__] + [__serialize__] [ Returns a __string__ representing a serialized __value__. ] @@ -43,7 +43,7 @@ written to standard output streams using the stream operator: [snippet_serializing_1] -The __to_string__ function converts a __value__ into a __string__: +The __serialize__ function converts a __value__ into a __string__: [snippet_serializing_2] diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk index 23d984c9..e01d51a1 100644 --- a/doc/qbk/main.qbk +++ b/doc/qbk/main.qbk @@ -67,13 +67,13 @@ [def __polymorphic_allocator__ [link json.ref.boost__json__polymorphic_allocator `polymorphic_allocator`]] [def __memory_resource__ [link json.ref.boost__json__memory_resource `memory_resource`]] [def __monotonic_resource__ [link json.ref.boost__json__monotonic_resource `monotonic_resource`]] +[def __serialize__ [link json.ref.boost__json__serialize `serialize`]] [def __serializer__ [link json.ref.boost__json__serializer `serializer`]] [def __static_resource__ [link json.ref.boost__json__static_resource `static_resource`]] [def __storage_ptr__ [link json.ref.boost__json__storage_ptr `storage_ptr`]] [def __string__ [link json.ref.boost__json__string `string`]] [def __string_view__ [link json.ref.boost__json__string_view `string_view`]] [def __system_error__ [link json.ref.boost__json__system_error `system_error`]] -[def __to_string__ [link json.ref.boost__json__to_string `to_string`]] [def __value__ [link json.ref.boost__json__value `value`]] [def __value_from__ [link json.ref.boost__json__value_from `value_from`]] [def __value_stack_ [link json.ref.boost__json__value_stack `value_stack`]] @@ -98,7 +98,7 @@ [import ../../example/pretty.cpp] [import ../../example/validate.cpp] -[import ../../include/boost/json/impl/to_string.ipp] +[import ../../include/boost/json/impl/serialize.ipp] [import ../../test/memory_resource.cpp] [import ../../test/snippets.cpp] diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index 27751286..dcfb312c 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -43,8 +43,8 @@ make_counted_resource number_cast parse + serialize swap - to_string value_from value_to diff --git a/example/pretty.cpp b/example/pretty.cpp index 01501e6a..47e5c5f3 100644 --- a/example/pretty.cpp +++ b/example/pretty.cpp @@ -62,7 +62,7 @@ pretty_print( std::ostream& os, json::value const& jv, std::string* indent = nul auto it = obj.begin(); for(;;) { - os << *indent << json::to_string(it->key()) << " : "; + os << *indent << json::serialize(it->key()) << " : "; pretty_print(os, it->value(), indent); if(++it == obj.end()) break; @@ -100,7 +100,7 @@ pretty_print( std::ostream& os, json::value const& jv, std::string* indent = nul case json::kind::string: { - os << json::to_string(jv.get_string()); + os << json::serialize(jv.get_string()); break; } diff --git a/fuzzing/fuzz_parser.cpp b/fuzzing/fuzz_parser.cpp index 0228ba36..d367668c 100644 --- a/fuzzing/fuzz_parser.cpp +++ b/fuzzing/fuzz_parser.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include using namespace boost::json; bool @@ -33,7 +33,7 @@ fuzz_parser(parse_options popt,string_view sv) { value jv = p.release( ec ); if(! ec ) - return to_string(jv).size()==42; + return serialize(jv).size()==42; } return false; } diff --git a/include/boost/json.hpp b/include/boost/json.hpp index f4b800f8..42b3a692 100644 --- a/include/boost/json.hpp +++ b/include/boost/json.hpp @@ -20,12 +20,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/include/boost/json/impl/serialize.ipp b/include/boost/json/impl/serialize.ipp new file mode 100644 index 00000000..8c187f90 --- /dev/null +++ b/include/boost/json/impl/serialize.ipp @@ -0,0 +1,185 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// 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) +// +// Official repository: https://github.com/cppalliance/json +// + +#ifndef BOOST_JSON_IMPL_SERIALIZE_IPP +#define BOOST_JSON_IMPL_SERIALIZE_IPP + +#include +#include +#include + +BOOST_JSON_NS_BEGIN + +static +void +serialize_impl( + string& s, + serializer& sr) +{ + // serialize to a small buffer + // to avoid most reallocations + char buf[16384]; + string_view sv; + sv = sr.read(buf); + if(sr.done()) + { + // fast path + s.append(sv); + return; + } + s.reserve(sv.size() * 2); + s.append(sv); + do + { + sv = sr.read( + s.data() + s.size(), + s.capacity() - s.size()); + s.grow(sv.size()); + } + while(! sr.done()); +} + +string +serialize( + value const& jv, + storage_ptr sp) +{ + string s(std::move(sp)); + serializer sr; + sr.reset(&jv); + serialize_impl(s, sr); + return s; +} + +string +serialize( + array const& arr, + storage_ptr sp) +{ + string s(std::move(sp)); + serializer sr; + sr.reset(&arr); + serialize_impl(s, sr); + return s; +} + +string +serialize( + object const& obj, + storage_ptr sp) +{ + string s(std::move(sp)); + serializer sr; + sr.reset(&obj); + serialize_impl(s, sr); + return s; +} + +string +serialize( + string const& str, + storage_ptr sp) +{ + string s(std::move(sp)); + serializer sr; + sr.reset(&str); + serialize_impl(s, sr); + return s; +} + +string +serialize( + string_view sv, + storage_ptr sp) +{ + string s(std::move(sp)); + serializer sr; + sr.reset(sv); + serialize_impl(s, sr); + return s; +} + +//---------------------------------------------------------- + +//[example_operator_lt__lt_ +// Serialize a value into an output stream + +std::ostream& +operator<<( std::ostream& os, value const& jv ) +{ + // Create a serializer + serializer sr; + + // Set the serializer up for our value + sr.reset( &jv ); + + // Loop until all output is produced. + while( ! sr.done() ) + { + // Use a local buffer to avoid allocation. + char buf[ 4000 ]; + + // Fill our buffer with serialized characters and write it to the output stream. + os << sr.read( buf ); + } + + return os; +} +//] + +static +void +to_ostream( + std::ostream& os, + serializer& sr) +{ + char buf[16384]; + while(! sr.done()) + { + auto s = sr.read(buf); + os.write(s.data(), s.size()); + } +} + +std::ostream& +operator<<( + std::ostream& os, + array const& arr) +{ + serializer sr; + sr.reset(&arr); + to_ostream(os, sr); + return os; +} + +std::ostream& +operator<<( + std::ostream& os, + object const& obj) +{ + serializer sr; + sr.reset(&obj); + to_ostream(os, sr); + return os; +} + +std::ostream& +operator<<( + std::ostream& os, + string const& str) +{ + serializer sr; + sr.reset(&str); + to_ostream(os, sr); + return os; +} + +BOOST_JSON_NS_END + +#endif diff --git a/include/boost/json/impl/serializer.ipp b/include/boost/json/impl/serializer.ipp index aa3da8b2..e6eb2380 100644 --- a/include/boost/json/impl/serializer.ipp +++ b/include/boost/json/impl/serializer.ipp @@ -49,9 +49,9 @@ serializer:: suspend( state st, array::const_iterator it, - value const* jv) + array const* pa) { - st_.push(jv); + st_.push(pa); st_.push(it); st_.push(st); return false; @@ -62,9 +62,9 @@ serializer:: suspend( state st, object::const_iterator it, - value const* jv) + object const* po) { - st_.push(jv); + st_.push(po); st_.push(it); st_.push(st); return false; @@ -454,23 +454,23 @@ bool serializer:: write_array(stream& ss0) { - value const* jv; + array const* pa; local_stream ss(ss0); array::const_iterator it; array::const_iterator end; if(StackEmpty || st_.empty()) { - jv = jv_; - it = jv->get_array().begin(); - end = jv->get_array().end(); + pa = pa_; + it = pa->begin(); + end = pa->end(); } else { state st; st_.pop(st); st_.pop(it); - st_.pop(jv); - end = jv->get_array().end(); + st_.pop(pa); + end = pa->end(); switch(st) { default: @@ -486,7 +486,7 @@ do_arr1: ss.append('['); else return suspend( - state::arr1, it, jv); + state::arr1, it, pa); if(it == end) goto do_arr4; for(;;) @@ -495,7 +495,7 @@ do_arr2: jv_ = &*it; if(! write_value(ss)) return suspend( - state::arr2, it, jv); + state::arr2, it, pa); if(BOOST_JSON_UNLIKELY( ++it == end)) break; @@ -504,14 +504,14 @@ do_arr3: ss.append(','); else return suspend( - state::arr3, it, jv); + state::arr3, it, pa); } do_arr4: if(BOOST_JSON_LIKELY(ss)) ss.append(']'); else return suspend( - state::arr4, it, jv); + state::arr4, it, pa); return true; } @@ -520,23 +520,23 @@ bool serializer:: write_object(stream& ss0) { - value const* jv; + object const* po; local_stream ss(ss0); object::const_iterator it; object::const_iterator end; if(StackEmpty || st_.empty()) { - jv = jv_; - it = jv->get_object().begin(); - end = jv->get_object().end(); + po = po_; + it = po->begin(); + end = po->end(); } else { state st; st_.pop(st); st_.pop(it); - st_.pop(jv); - end = jv->get_object().end(); + st_.pop(po); + end = po->end(); switch(st) { default: @@ -554,7 +554,7 @@ do_obj1: ss.append('{'); else return suspend( - state::obj1, it, jv); + state::obj1, it, po); if(BOOST_JSON_UNLIKELY( it == end)) goto do_obj6; @@ -567,19 +567,19 @@ do_obj2: if(BOOST_JSON_UNLIKELY( ! write_string(ss))) return suspend( - state::obj2, it, jv); + state::obj2, it, po); do_obj3: if(BOOST_JSON_LIKELY(ss)) ss.append(':'); else return suspend( - state::obj3, it, jv); + state::obj3, it, po); do_obj4: jv_ = &it->value(); if(BOOST_JSON_UNLIKELY( ! write_value(ss))) return suspend( - state::obj4, it, jv); + state::obj4, it, po); ++it; if(BOOST_JSON_UNLIKELY(it == end)) break; @@ -588,7 +588,7 @@ do_obj5: ss.append(','); else return suspend( - state::obj5, it, jv); + state::obj5, it, po); } do_obj6: if(BOOST_JSON_LIKELY(ss)) @@ -597,7 +597,7 @@ do_obj6: return true; } return suspend( - state::obj6, it, jv); + state::obj6, it, po); } template @@ -612,9 +612,11 @@ write_value(stream& ss) { default: case kind::object: + po_ = jv.is_object(); return write_object(ss); case kind::array: + pa_ = jv.is_array(); return write_array(ss); case kind::string: @@ -717,9 +719,9 @@ read_some( stream ss(dest, size); if(st_.empty()) - write_value(ss); + (this->*fn0_)(ss); else - write_value(ss); + (this->*fn1_)(ss); if(st_.empty()) { done_ = true; @@ -739,17 +741,59 @@ serializer() noexcept sizeof(serializer::buf_) >= 7); } +void serializer:: -serializer(value const& jv) noexcept +reset(value const* p) noexcept { - reset(jv); + pv_ = p; + fn0_ = &serializer::write_value; + fn1_ = &serializer::write_value; + + jv_ = p; + st_.clear(); + done_ = false; } void serializer:: -reset(value const& jv) noexcept +reset(array const* p) noexcept { - jv_ = &jv; + pa_ = p; + fn0_ = &serializer::write_array; + fn1_ = &serializer::write_array; + st_.clear(); + done_ = false; +} + +void +serializer:: +reset(object const* p) noexcept +{ + po_ = p; + fn0_ = &serializer::write_object; + fn1_ = &serializer::write_object; + st_.clear(); + done_ = false; +} + +void +serializer:: +reset(string const* p) noexcept +{ + cs0_ = { p->data(), p->size() }; + fn0_ = &serializer::write_string; + fn1_ = &serializer::write_string; + st_.clear(); + done_ = false; +} + +void +serializer:: +reset(string_view sv) noexcept +{ + cs0_ = { sv.data(), sv.size() }; + fn0_ = &serializer::write_string; + fn1_ = &serializer::write_string; st_.clear(); done_ = false; } diff --git a/include/boost/json/impl/string.ipp b/include/boost/json/impl/string.ipp index 68d9cc93..c8339755 100644 --- a/include/boost/json/impl/string.ipp +++ b/include/boost/json/impl/string.ipp @@ -310,14 +310,6 @@ reserve_impl(size_type new_cap) } } -//---------------------------------------------------------- - -std::ostream& -operator<<(std::ostream& os, string const& s) -{ - return os << static_cast(s); -} - BOOST_JSON_NS_END #endif diff --git a/include/boost/json/impl/to_string.ipp b/include/boost/json/impl/to_string.ipp deleted file mode 100644 index 112a653b..00000000 --- a/include/boost/json/impl/to_string.ipp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// -// 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) -// -// Official repository: https://github.com/cppalliance/json -// - -#ifndef BOOST_JSON_IMPL_TO_STRING_IPP -#define BOOST_JSON_IMPL_TO_STRING_IPP - -#include -#include -#include - -BOOST_JSON_NS_BEGIN - -string -to_string( - json::value const& jv) -{ - string s; - serializer sr(jv); - while(! sr.done()) - { - if(s.size() >= s.capacity()) - s.reserve(s.capacity() + 1); - s.grow(static_cast< - string::size_type>( - sr.read(s.data() + s.size(), - s.capacity() - s.size()).size())); - } - return s; -} - -//[example_operator_lt__lt_ -// Serialize a value into an output stream - -std::ostream& -operator<<( std::ostream& os, value const& jv ) -{ - // Create a serializer that is set to output our value. - serializer sr( jv ); - - // Loop until all output is produced. - while( ! sr.done() ) - { - // Use a local buffer. - char buf[4000]; - - // Try to fill up the local buffer. - auto const bytes_written = sr.read( buf ).size(); - - // Write the valid portion of the buffer to the output stream. - os.write( buf, bytes_written ); - } - - return os; -} -//] - -BOOST_JSON_NS_END - -#endif diff --git a/include/boost/json/serialize.hpp b/include/boost/json/serialize.hpp new file mode 100644 index 00000000..0ac1a746 --- /dev/null +++ b/include/boost/json/serialize.hpp @@ -0,0 +1,118 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// 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) +// +// Official repository: https://github.com/cppalliance/json +// + +#ifndef BOOST_JSON_SERIALIZE_HPP +#define BOOST_JSON_SERIALIZE_HPP + +#include +#include +#include + +BOOST_JSON_NS_BEGIN + +/** Return a string representing a serialized element. + + This function serializes `t` as JSON and returns + it as a @ref string using the specified memory + resource. + + @par Complexity + Constant or linear in the size of `t`. + + @par Exception Safety + Strong guarantee. + Calls to `memory_resource::allocate` may throw. + + @return The serialized string + + @param t The value to serialize + + @param sp The memory resource to use for the + returned string. If omitted, the default resource + is used. +*/ +/** @{ */ +BOOST_JSON_DECL +string +serialize( + value const& t, + storage_ptr sp = {}); + +BOOST_JSON_DECL +string +serialize( + array const& t, + storage_ptr sp = {}); + +BOOST_JSON_DECL +string +serialize( + object const& t, + storage_ptr sp = {}); + +BOOST_JSON_DECL +string +serialize( + string const& t, + storage_ptr sp = {}); + +BOOST_JSON_DECL +string +serialize( + string_view t, + storage_ptr sp = {}); +/** @} */ + +/** Serialize an element to an output stream. + + This function serializes the specified element + as JSON into the output stream. + + @return `os`. + + @par Complexity + Constant or linear in the size of `t`. + + @par Exception Safety + Strong guarantee. + Calls to `memory_resource::allocate` may throw. + + @param os The output stream to serialize to. + + @param t The value to serialize +*/ +/** @{ */ +BOOST_JSON_DECL +std::ostream& +operator<<( + std::ostream& os, + value const& t); + +BOOST_JSON_DECL +std::ostream& +operator<<( + std::ostream& os, + array const& t); + +BOOST_JSON_DECL +std::ostream& +operator<<( + std::ostream& os, + object const& t); + +BOOST_JSON_DECL +std::ostream& +operator<<( + std::ostream& os, + string const& t); +/** @} */ + +BOOST_JSON_NS_END + +#endif diff --git a/include/boost/json/serializer.hpp b/include/boost/json/serializer.hpp index 99693451..3f8f5ed2 100644 --- a/include/boost/json/serializer.hpp +++ b/include/boost/json/serializer.hpp @@ -20,14 +20,13 @@ BOOST_JSON_NS_BEGIN /** A serializer for JSON. - This class traverses a @ref value and emits - a JSON text by filling a series of one or more - caller-provided buffers. To use it, declare - a serializer and construct it with a reference - to the value to serialize. Alternatively, call - @ref reset to set the value to serialize. - Then call @ref read over and over while - @ref done returns `false`. + This class traverses an instance of a library + type and emits serialized JSON text by filling + in one or more caller-provided buffers. To use, + declare a variable and call @ref reset with + a pointer to the variable you want to serialize. + Then call @ref read over and over until + @ref done returns `true`. @par Example @@ -39,10 +38,11 @@ BOOST_JSON_NS_BEGIN void print( std::ostream& os, value const& jv) { - serializer sr( jv ); + serializer sr; + sr.reset( &jv ); while( ! sr.done() ) { - char buf[4000]; + char buf[ 4000 ]; os << sr.read( buf ); } } @@ -63,6 +63,18 @@ class serializer using local_const_stream = detail::local_const_stream; + using fn_t = bool (serializer::*)(stream&); + +#ifndef BOOST_JSON_DOCS + union + { + value const* pv_; + array const* pa_; + object const* po_; + }; +#endif + fn_t fn0_ = &serializer::write_null; + fn_t fn1_ = &serializer::write_null; value const* jv_ = nullptr; detail::stack st_; const_stream cs0_; @@ -70,18 +82,18 @@ class serializer bool done_ = false; inline bool suspend(state st); - inline bool suspend(state st, - array::const_iterator it, value const* jv); - inline bool suspend(state st, - object::const_iterator it, value const* jv); - template bool write_null(stream& ss); - template bool write_true(stream& ss); - template bool write_false(stream& ss); - template bool write_string(stream& ss); - template bool write_number(stream& ss); - template bool write_array(stream& ss); - template bool write_object(stream& ss); - template bool write_value(stream& ss); + inline bool suspend( + state st, array::const_iterator it, array const* pa); + inline bool suspend( + state st, object::const_iterator it, object const* po); + template bool write_null (stream& ss); + template bool write_true (stream& ss); + template bool write_false (stream& ss); + template bool write_string (stream& ss); + template bool write_number (stream& ss); + template bool write_array (stream& ss); + template bool write_object (stream& ss); + template bool write_value (stream& ss); inline string_view read_some(char* dest, std::size_t size); public: @@ -101,29 +113,6 @@ public: BOOST_JSON_DECL serializer() noexcept; - /** Constructor - - This constructs the serializer and prepares - it to serialize the @ref value `jv` as if - @ref reset was called. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - - @param jv The value to serialize. Ownership - is not transferred. The caller is responsible - for ensuring that the lifetime of the value - extends until @ref done returns `true`, - @ref reset is called with a new value, or - the serializer is destroyed. - */ - explicit - BOOST_JSON_DECL - serializer(value const& jv) noexcept; - /** Returns `true` if the serialization is complete This function returns `true` when all of the @@ -142,23 +131,52 @@ public: return done_; } - /** Reset the serializer for a new JSON value + /** Reset the serializer for a new element This function prepares the serializer to emit - a new serialized JSON based on the specified - value. Any internally allocated memory is + a new serialized JSON representing `*p`. + Any internally allocated memory is preserved and re-used for the new output. - @param jv The value to serialize. Ownership - is not transferred. The caller is responsible - for ensuring that the lifetime of the value - extends until @ref done returns `true`, - @ref reset is called with a new value, or - the serializer is destroyed. + @param p A pointer to the element to serialize. + Ownership is not transferred; The caller is + responsible for ensuring that the lifetime of + `*p` extends until it is no longer needed. + */ + /** @{ */ + BOOST_JSON_DECL + void + reset(value const* p) noexcept; + + BOOST_JSON_DECL + void + reset(array const* p) noexcept; + + BOOST_JSON_DECL + void + reset(object const* p) noexcept; + + BOOST_JSON_DECL + void + reset(string const* p) noexcept; + /** @} */ + + /** Reset the serializer for a new string + + This function prepares the serializer to emit + a new serialized JSON representing the string. + Any internally allocated memory is + preserved and re-used for the new output. + + @param sv The characters representing the string. + Ownership is not transferred; The caller is + responsible for ensuring that the lifetime of + the characters reference by `sv` extends + until it is no longer needed. */ BOOST_JSON_DECL void - reset(value const& jv) noexcept; + reset(string_view sv) noexcept; /** Read the next buffer of serialized JSON diff --git a/include/boost/json/src.hpp b/include/boost/json/src.hpp index 26b7ae28..ea2f39c9 100644 --- a/include/boost/json/src.hpp +++ b/include/boost/json/src.hpp @@ -29,10 +29,10 @@ in a translation unit of the program. #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/include/boost/json/string.hpp b/include/boost/json/string.hpp index 66949fca..ed45aea8 100644 --- a/include/boost/json/string.hpp +++ b/include/boost/json/string.hpp @@ -2769,14 +2769,6 @@ swap(string& lhs, string& rhs) //---------------------------------------------------------- -/** Serialize a @ref string to an output stream - - Behaves as a formatted output function. -*/ -BOOST_JSON_DECL -std::ostream& -operator<<(std::ostream& os, string const& s); - /** Return true if lhs equals rhs. A lexicographical comparison is used. diff --git a/include/boost/json/to_string.hpp b/include/boost/json/to_string.hpp deleted file mode 100644 index 58f31d70..00000000 --- a/include/boost/json/to_string.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// -// 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) -// -// Official repository: https://github.com/cppalliance/json -// - -#ifndef BOOST_JSON_TO_STRING_HPP -#define BOOST_JSON_TO_STRING_HPP - -#include -#include -#include - -BOOST_JSON_NS_BEGIN - -/** Return a string representing a serialized @ref value. - - This function serializes the specified value - and returns it as a @ref string using the - default memory resource. - - @par Exception Safety - - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @return The serialized string - - @param jv The value to serialize -*/ -BOOST_JSON_DECL -string -to_string( - value const& jv); - -/** Serialize a @ref value to an output stream. - - This function serializes the specified value - into the output stream. - - @par Exception Safety - - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @return `os`. - - @param os The output stream to serialize to. - - @param jv The value to serialize. -*/ -BOOST_JSON_DECL -std::ostream& -operator<<( - std::ostream& os, - value const& jv); - -BOOST_JSON_NS_END - -#endif diff --git a/include/boost/json/value_stack.hpp b/include/boost/json/value_stack.hpp index 7b4d182d..6c3ffbc4 100644 --- a/include/boost/json/value_stack.hpp +++ b/include/boost/json/value_stack.hpp @@ -102,7 +102,7 @@ BOOST_JSON_NS_BEGIN // Pop the object from the stack and take ownership. value jv = st.release(); - assert( to_string(jv) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); + assert( serialize(jv) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); // At this point we could re-use the stack by calling reset @@ -274,7 +274,7 @@ public: // Pop the object from the stack and take ownership. value jv = st.release(); - assert( to_string(jv) == "[1,2,3]" ); + assert( serialize(jv) == "[1,2,3]" ); // At this point, reset must be called again to use the stack @@ -325,7 +325,7 @@ public: // Pop the object from the stack and take ownership. value jv = st.release(); - assert( to_string(jv) == "{\"x\",true}" ); + assert( serialize(jv) == "{\"x\",true}" ); // At this point, reset must be called again to use the stack diff --git a/test/Jamfile b/test/Jamfile index fdc0ea87..04e5db33 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -37,6 +37,7 @@ local SOURCES = parse.cpp parser.cpp pilfer.cpp + serialize.cpp serializer.cpp snippets.cpp static_resource.cpp @@ -44,7 +45,6 @@ local SOURCES = string.cpp string_view.cpp system_error.cpp - to_string.cpp value.cpp value_from.cpp value_stack.cpp diff --git a/test/monotonic_resource.cpp b/test/monotonic_resource.cpp index 9a042db7..a808483f 100644 --- a/test/monotonic_resource.cpp +++ b/test/monotonic_resource.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/test/parser.cpp b/test/parser.cpp index 120cce39..638fd88d 100644 --- a/test/parser.cpp +++ b/test/parser.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -80,7 +80,7 @@ public: { auto const s2 = //to_string_test(jv1); // use this if serializer is broken - to_string(jv1); + serialize(jv1); auto jv2 = from_string_test(s2, {}, po); BOOST_TEST(equal(jv1, jv2)); @@ -860,7 +860,7 @@ R"xx({ p.finish(ec); if(BOOST_TEST(! ec)) { - BOOST_TEST(to_string(p.release(ec)) == out); + BOOST_TEST(serialize(p.release(ec)) == out); BOOST_TEST(! ec); } } @@ -1038,7 +1038,7 @@ R"xx({ auto const jv = parse("[]"); auto const t = T{jv}; - BOOST_TEST(to_string(t.jv) == "[]"); + BOOST_TEST(serialize(t.jv) == "[]"); } //------------------------------------------------------ diff --git a/test/serialize.cpp b/test/serialize.cpp new file mode 100644 index 00000000..e68a47f7 --- /dev/null +++ b/test/serialize.cpp @@ -0,0 +1,90 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// 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) +// +// Official repository: https://github.com/cppalliance/json +// + +// Test that header file is self-contained. +#include + +#include +#include +#include + +#include "test_suite.hpp" + +BOOST_JSON_NS_BEGIN + +class serialize_test +{ +public: + template + static + std::string + print(T const& t) + { + std::stringstream ss; + ss << t; + return ss.str(); + } + + void + testSerialize() + { + { + value const jv = { 1, 2, 3 }; + BOOST_TEST(serialize(jv) == "[1,2,3]"); + BOOST_TEST(print(jv) == "[1,2,3]"); + unsigned char buf[100]; + static_resource mr(buf); + BOOST_TEST(serialize( + jv, &mr).storage().get() == &mr); + } + { + array const arr = { 1, 2 ,3 }; + BOOST_TEST(serialize(arr) == "[1,2,3]"); + BOOST_TEST(print(arr) == "[1,2,3]"); + unsigned char buf[100]; + static_resource mr(buf); + BOOST_TEST(serialize( + arr, &mr).storage().get() == &mr); + } + { + object const obj = { {"k1",1}, {"k2",2} }; + BOOST_TEST(serialize(obj) == "{\"k1\":1,\"k2\":2}"); + BOOST_TEST(print(obj) == "{\"k1\":1,\"k2\":2}"); + unsigned char buf[100]; + static_resource mr(buf); + BOOST_TEST(serialize( + obj, &mr).storage().get() == &mr); + } + { + string const str = "123"; + BOOST_TEST(serialize(str) == "\"123\""); + BOOST_TEST(print(str) == "\"123\""); + unsigned char buf[100]; + static_resource mr(buf); + BOOST_TEST(serialize( + str, &mr).storage().get() == &mr); + } + } + + void + testOstream() + { + } + + void + run() + { + testSerialize(); + testOstream(); + } +}; + +TEST_SUITE(serialize_test, "boost.json.serialize"); + +BOOST_JSON_NS_END diff --git a/test/serializer.cpp b/test/serializer.cpp index 7b2566f1..064885ae 100644 --- a/test/serializer.cpp +++ b/test/serializer.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include "parse-vectors.hpp" @@ -23,6 +23,23 @@ BOOST_JSON_NS_BEGIN class serializer_test { public: + //------------------------------------------------------ + + // From the javadoc + + void print( std::ostream& os, value const& jv) + { + serializer sr; + sr.reset( &jv ); + while( ! sr.done() ) + { + char buf[ 4000 ]; + os << sr.read( buf ); + } + } + + //------------------------------------------------------ + ::test_suite::log_type log; void @@ -33,7 +50,7 @@ public: { { error_code ec; - auto const s1 = to_string(jv); + auto const s1 = serialize(jv); auto const jv2 = parse(s1, ec); if(! BOOST_TEST(equal(jv, jv2))) { @@ -53,13 +70,14 @@ public: // large buffer { error_code ec; - serializer sr(jv); + serializer sr; + sr.reset(&jv); string js; js.reserve(4096); js.grow(sr.read( js.data(), js.capacity()).size()); - auto const s1 = to_string(jv); + auto const s1 = serialize(jv); auto const jv2 = parse(s1, ec); BOOST_TEST(equal(jv, jv2)); } @@ -73,12 +91,12 @@ public: { grind_one(s0, jv, name); - auto const s1 = - to_string(jv); + auto const s1 = serialize(jv); for(std::size_t i = 1; i < s1.size(); ++i) { - serializer sr(jv); + serializer sr; + sr.reset(&jv); string s2; s2.reserve(s1.size()); s2.grow(sr.read( @@ -339,8 +357,6 @@ public: void testMembers() { - value jv = 1; - // serializer() { serializer sr; @@ -349,17 +365,11 @@ public: BOOST_TEST(sr.read(buf) == "null"); } - // serializer(value) - { - serializer sr(jv); - - char buf[32]; - BOOST_TEST(sr.read(buf) == "1"); - } - // done() { - serializer sr(jv); + value jv = 1; + serializer sr; + sr.reset(&jv); BOOST_TEST(! sr.done()); char buf[32]; BOOST_TEST(sr.read(buf) == "1"); @@ -368,7 +378,9 @@ public: // read() { - serializer sr(jv); + value jv = 1; + serializer sr; + sr.reset(&jv); char buf[1024]; auto const s = sr.read(buf); BOOST_TEST(sr.done()); @@ -381,6 +393,42 @@ public: char buf[100]; BOOST_TEST(sr.read(buf, 50) == "null"); } + + // reset(value) + { + char buf[100]; + serializer sr; + value jv = { 1, 2, 3 }; + sr.reset(&jv); + BOOST_TEST(sr.read(buf) == "[1,2,3]"); + } + + // reset(array) + { + char buf[100]; + serializer sr; + array arr = { 1, 2, 3 }; + sr.reset(&arr); + BOOST_TEST(sr.read(buf) == "[1,2,3]"); + } + + // reset(object) + { + char buf[100]; + serializer sr; + object obj = { {"k1",1}, {"k2",2} }; + sr.reset(&obj); + BOOST_TEST(sr.read(buf) == "{\"k1\":1,\"k2\":2}"); + } + + // reset(string) + { + char buf[100]; + serializer sr; + string str = "123"; + sr.reset(&str); + BOOST_TEST(sr.read(buf) == "\"123\""); + } } void @@ -486,11 +534,11 @@ public: { // no decimal or exponent parsed as integer BOOST_TEST(parse("-0").as_int64() == 0); - BOOST_TEST(to_string(parse("-0")) == "0"); + BOOST_TEST(serialize(parse("-0")) == "0"); BOOST_TEST(parse("-0.0").as_double() == -0); - BOOST_TEST(to_string(parse("0.0")) == "0E0"); + BOOST_TEST(serialize(parse("0.0")) == "0E0"); BOOST_TEST(parse("0.0").as_double() == 0); - BOOST_TEST(to_string(parse("-0.0")) == "-0E0"); + BOOST_TEST(serialize(parse("-0.0")) == "-0E0"); } void diff --git a/test/snippets.cpp b/test/snippets.cpp index 5d30583b..1da7fc66 100644 --- a/test/snippets.cpp +++ b/test/snippets.cpp @@ -257,7 +257,7 @@ usingInitLists() assert( jv.as_array().size() == 4 ); - assert( to_string(jv) == "[true,2,\"hello\",null]" ); + assert( serialize(jv) == "[true,2,\"hello\",null]" ); //] } @@ -271,7 +271,7 @@ usingInitLists() assert( jv.as_array().back().is_array() ); - assert( to_string(jv) == "[true,2,\"hello\",[\"bye\",null,false]]" ); + assert( serialize(jv) == "[true,2,\"hello\",[\"bye\",null,false]]" ); //] } @@ -294,7 +294,7 @@ usingInitLists() assert( jv1.as_object().size() == 2 ); - assert( to_string(jv1) == R"({"hello":42,"world":43})" ); + assert( serialize(jv1) == R"({"hello":42,"world":43})" ); // All of the following are arrays @@ -320,7 +320,7 @@ usingInitLists() assert( ja[0].is_array() && ja[1].is_array()); - assert ( to_string(jv) == R"([["hello",42],["world",43]])" ); + assert ( serialize(jv) == R"([["hello",42],["world",43]])" ); //] @@ -334,11 +334,11 @@ usingInitLists() assert( jv.is_object() ); - assert( to_string(jv) == "{\"mercury\":36,\"venus\":67,\"earth\":93}" ); + assert( serialize(jv) == "{\"mercury\":36,\"venus\":67,\"earth\":93}" ); array ja = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } }; - assert( to_string(ja) == "[[\"mercury\",36],[\"venus\",67],[\"earth\",93]]" ); + assert( serialize(ja) == "[[\"mercury\",36],[\"venus\",67],[\"earth\",93]]" ); //] @@ -368,7 +368,7 @@ usingInitLists() assert( ! jo2.empty() && jo1.empty() ); - assert( to_string(jv) == R"({"clients":{"john":100,"dave":500,"joe":300}})" ); + assert( serialize(jv) == R"({"clients":{"john":100,"dave":500,"joe":300}})" ); //] @@ -939,7 +939,7 @@ usingSerializing() value jv = { 1, 2, 3, 4, 5 }; - string s = to_string( jv ); + string s = serialize( jv ); //] } @@ -1070,7 +1070,7 @@ usingExchange() value jv = value_from( pos ); - assert( to_string( jv ) == "{\"x\":4,\"y\":1,\"z\":4}" ); + assert( serialize( jv ) == "{\"x\":4,\"y\":1,\"z\":4}" ); //] } diff --git a/test/static_resource.cpp b/test/static_resource.cpp index 8905e3f5..f6b6e370 100644 --- a/test/static_resource.cpp +++ b/test/static_resource.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include "test_suite.hpp" @@ -46,7 +46,7 @@ public: unsigned char buf[1000]; static_resource mr( &buf[0], sizeof(buf)); - BOOST_TEST(to_string(parse( + BOOST_TEST(serialize(parse( "[1,2,3]", &mr)) == "[1,2,3]"); } @@ -55,7 +55,7 @@ public: unsigned char buf[10]; static_resource mr(buf); BOOST_TEST_THROWS( - to_string(parse("[1,2,3]", &mr)), + serialize(parse("[1,2,3]", &mr)), std::bad_alloc); } @@ -64,7 +64,7 @@ public: unsigned char buf[1000]; static_resource mr( buf, 500); - BOOST_TEST(to_string(parse( + BOOST_TEST(serialize(parse( "[1,2,3]", &mr)) == "[1,2,3]"); } diff --git a/test/string.cpp b/test/string.cpp index 832edd50..4cb51f1d 100644 --- a/test/string.cpp +++ b/test/string.cpp @@ -2697,11 +2697,6 @@ public: BOOST_TEST( operator> (s1, s2)); BOOST_TEST( operator> (v1, s2)); BOOST_TEST( operator> (c1, s2)); - - std::stringstream ss; - string s = "Hello, world"; - ss << s; - BOOST_TEST(ss.str() == s); } void diff --git a/test/test.hpp b/test/test.hpp index f0dbd4b0..71be135a 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -883,7 +883,7 @@ to_string_test( dest.append(jv.get_string()); dest.push_back('\"'); #else - dest.append(to_string(jv)); + dest.append(serialize(jv)); #endif break; diff --git a/test/to_string.cpp b/test/to_string.cpp deleted file mode 100644 index 8db1442f..00000000 --- a/test/to_string.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// -// 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) -// -// Official repository: https://github.com/cppalliance/json -// - -// Test that header file is self-contained. -#include - -#include - -#include "test_suite.hpp" - -BOOST_JSON_NS_BEGIN - -class to_string_test -{ -public: - void - testToString() - { - BOOST_TEST( - to_string(parse("[1,2,3]")) == "[1,2,3]"); - } - - void - testOstream() - { - } - - void - run() - { - testToString(); - testOstream(); - } -}; - -TEST_SUITE(to_string_test, "boost.json.to_string"); - -BOOST_JSON_NS_END diff --git a/test/value_from.cpp b/test/value_from.cpp index e356c4f1..91912823 100644 --- a/test/value_from.cpp +++ b/test/value_from.cpp @@ -12,7 +12,7 @@ #include #include // prevent intellisense bugs -#include +#include #include "test_suite.hpp" @@ -88,14 +88,14 @@ check( auto const jv = value_from(t, ::boost::json::storage_ptr{}); auto const js = - ::boost::json::to_string(jv); + ::boost::json::serialize(jv); BOOST_TEST(js == s); } { auto const jv = ::boost::json::value_from(t); auto const js = - ::boost::json::to_string(jv); + ::boost::json::serialize(jv); BOOST_TEST(js == s); } } @@ -106,9 +106,9 @@ void testValueCtor() { BOOST_TEST( - ::boost::json::to_string( + ::boost::json::serialize( ::boost::json::value_from(T{})) == - ::boost::json::to_string( + ::boost::json::serialize( ::boost::json::value(T{}))); } @@ -161,21 +161,21 @@ public: value b{1, 2, 3, 4}; value c = value_from(a); BOOST_TEST(c.is_array()); - BOOST_TEST(to_string(c) == to_string(b)); + BOOST_TEST(serialize(c) == serialize(b)); } { std::tuple a{1, "2", 42, true}; value b{1, "2", 42, true}; value c = value_from(a); BOOST_TEST(c.is_array()); - BOOST_TEST(to_string(c) == to_string(b)); + BOOST_TEST(serialize(c) == serialize(b)); } { std::pair a{1, string("2")}; value b{1, "2"}; value c = value_from(a); BOOST_TEST(c.is_array()); - BOOST_TEST(to_string(c) == to_string(b)); + BOOST_TEST(serialize(c) == serialize(b)); } { // ensures that this isn't parsed as a key value pair @@ -183,14 +183,14 @@ public: value b{"2", 1}; value c = value_from(a); BOOST_TEST(c.is_array()); - BOOST_TEST(to_string(c) == to_string(b)); + BOOST_TEST(serialize(c) == serialize(b)); } { key_value_pair a{"2", 1}; value b{"2", 1}; value c = value_from(a); BOOST_TEST(c.is_array()); - BOOST_TEST(to_string(c) == to_string(b)); + BOOST_TEST(serialize(c) == serialize(b)); } } diff --git a/test/value_ref.cpp b/test/value_ref.cpp index 0d1d9daa..3cbab9e7 100644 --- a/test/value_ref.cpp +++ b/test/value_ref.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include "test_suite.hpp" @@ -209,7 +209,7 @@ public: { auto const jv = value_ref(init).make_value({}); - auto const js = to_string(jv); + auto const js = serialize(jv); BOOST_TEST(js == s); } @@ -309,7 +309,7 @@ public: string_view s) { auto const jv = value(object(init)); - auto const js = to_string(jv); + auto const js = serialize(jv); BOOST_TEST(js == s); } diff --git a/test/value_stack.cpp b/test/value_stack.cpp index cf773028..ee5ef91a 100644 --- a/test/value_stack.cpp +++ b/test/value_stack.cpp @@ -10,8 +10,8 @@ // Test that header file is self-contained. #include +#include #include -#include #include "test_suite.hpp" @@ -59,7 +59,7 @@ public: // Pop the object from the stack and take ownership. value jv = st.release(); - assert( to_string(jv) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); + assert( serialize(jv) == "{\"a\":1,\"b\":null,\"c\":\"hello\"}" ); // At this point we could re-use the stack by calling reset } @@ -83,7 +83,7 @@ public: // Pop the object from the stack and take ownership. value jv = st.release(); - assert( to_string(jv) == "[1,2,3]" ); + assert( serialize(jv) == "[1,2,3]" ); // At this point, reset must be called again to use the stack } @@ -106,7 +106,7 @@ public: // Pop the object from the stack and take ownership. value jv = st.release(); - assert( to_string(jv) == "{\"x\":true}" ); + assert( serialize(jv) == "{\"x\":true}" ); // At this point, reset must be called again to use the stack }