From a44fc4f4530ae559e52e77da36172f6b2cd7cbb7 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 6 Oct 2019 12:21:19 -0700 Subject: [PATCH] string work --- doc/source.dox | 1 + include/boost/json/array.hpp | 2 +- include/boost/json/impl/array.hpp | 48 +- include/boost/json/impl/array.ipp | 23 +- include/boost/json/impl/string.hpp | 126 +++++ include/boost/json/impl/string.ipp | 749 +++++++++++++++++++++++++++++ include/boost/json/impl/value.ipp | 66 +-- include/boost/json/string.hpp | 678 +++++++++++++++++++++++++- include/boost/json/value.hpp | 20 +- test/array.cpp | 6 +- test/object.cpp | 12 - test/string.cpp | 626 +++++++++++++++++++++++- test/test_storage.hpp | 2 +- 13 files changed, 2210 insertions(+), 149 deletions(-) create mode 100644 include/boost/json/impl/string.hpp create mode 100644 include/boost/json/impl/string.ipp diff --git a/doc/source.dox b/doc/source.dox index f8eb164a..6242cf96 100644 --- a/doc/source.dox +++ b/doc/source.dox @@ -52,6 +52,7 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = \ GENERATING_DOCUMENTATION \ + BOOST_JSON_DECL \ "BOOST_JSON_INLINE_VARIABLE(v, t)= " EXPAND_AS_DEFINED = diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp index 2bec697f..f77bf22a 100644 --- a/include/boost/json/array.hpp +++ b/include/boost/json/array.hpp @@ -80,7 +80,7 @@ class array table* tab_ = nullptr; storage_ptr sp_; - struct undo; + struct undo_create; struct undo_insert; public: diff --git a/include/boost/json/impl/array.hpp b/include/boost/json/impl/array.hpp index 9f017022..f233be66 100644 --- a/include/boost/json/impl/array.hpp +++ b/include/boost/json/impl/array.hpp @@ -87,6 +87,26 @@ struct array::table //------------------------------------------------------------------------------ +struct array::undo_create +{ + table* tab; + storage_ptr const& sp; + + undo_create( + size_type n, + storage_ptr const& sp_) + : tab(table::create(n, sp_)) + , sp(sp_) + { + } + + ~undo_create() + { + if(tab) + table::destroy(tab, sp); + } +}; + struct array::undo_insert { value* it; @@ -117,7 +137,8 @@ array( InputIt first, InputIt last) : array( first, last, - default_storage()) + default_storage(), + iter_cat{}) { static_assert( std::is_constructible array:: array( InputIt first, InputIt last, - storage_ptr store) + storage_ptr sp) : array( first, last, - std::move(store), + std::move(sp), iter_cat{}) { static_assert( @@ -185,26 +206,31 @@ template array:: array( InputIt first, InputIt last, - storage_ptr store, + storage_ptr sp, std::input_iterator_tag) - : sp_(std::move(store)) + : sp_(std::move(sp)) { - while(first != last) - emplace_impl(end(), *first++); } template array:: array( InputIt first, InputIt last, - storage_ptr store, + storage_ptr sp, std::forward_iterator_tag) - : sp_(std::move(store)) + : sp_(std::move(sp)) { - reserve(std::distance(first, last)); + undo_create u( + std::distance(first, last), sp_); while(first != last) - emplace_impl(end(), *first++); + { + ::new(u.tab->end()) value( + *first++, sp_); + ++u.tab->d.size; + } + std::swap(tab_, u.tab); } + template auto array:: diff --git a/include/boost/json/impl/array.ipp b/include/boost/json/impl/array.ipp index e5a17021..fdb76b2d 100644 --- a/include/boost/json/impl/array.ipp +++ b/include/boost/json/impl/array.ipp @@ -89,20 +89,6 @@ relocate( //------------------------------------------------------------------------------ -struct array::undo -{ - table* tab; - storage_ptr const& sp; - - ~undo() - { - if(tab) - table::destroy(tab, sp); - } -}; - -//------------------------------------------------------------------------------ - array:: undo_insert:: undo_insert( @@ -197,8 +183,7 @@ array( if(count == 0) return; - undo u{table::create( - count, sp_), sp_}; + undo_create u(count, sp_); while(count--) { ::new(u.tab->end()) value(v, sp_); @@ -825,8 +810,7 @@ copy(array const& other) return; } - undo u{table::create( - other.size(), sp_), sp_}; + undo_create u(other.size(), sp_); for(auto const& v : other) { ::new(u.tab->end()) value(v, sp_); @@ -873,8 +857,7 @@ assign( return; } - undo u{table::create( - init.size(), sp_), sp_}; + undo_create u(init.size(), sp_); for(auto const& v : init) { ::new(u.tab->end()) value(v, sp_); diff --git a/include/boost/json/impl/string.hpp b/include/boost/json/impl/string.hpp new file mode 100644 index 00000000..521e9e93 --- /dev/null +++ b/include/boost/json/impl/string.hpp @@ -0,0 +1,126 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/vinniefalco/json +// + +#ifndef BOOST_JSON_IMPL_STRING_HPP +#define BOOST_JSON_IMPL_STRING_HPP + +namespace boost { +namespace json { + +//------------------------------------------------------------------------------ + +struct string::impl +{ + size_type size; + size_type capacity; + + static + impl* + construct( + size_type capacity, + storage_ptr const& sp) + { + auto s = ::new(sp->allocate( + sizeof(impl) + capacity + 1, + sizeof(impl))) impl; + s->capacity = capacity; + return s; + } + + static + impl* + construct( + size_type size, + size_type capacity, + storage_ptr const& sp) + { + auto s = construct(capacity, sp); + s->term(size); + return s; + } + + static + void + destroy( + impl* s, + storage_ptr const& sp) noexcept + { + sp->deallocate(s, + sizeof(impl) + s->capacity + 1, + sizeof(impl)); + } + + char* + data() noexcept + { + return reinterpret_cast< + char*>(this+1); + } + + char* + end() noexcept + { + return data() + size; + } + + void + term() noexcept + { + data()[size] = 0; + } + + void + term(size_type n) noexcept + { + size = n; + data()[size] = 0; + } +}; + +//------------------------------------------------------------------------------ + +template +string:: +string( + InputIt first, + InputIt last, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(first, last); +} + +template +string& +string:: +assign( + InputIt first, + InputIt last) +{ + maybe_raw_resize( + first, last, iter_cat{}); + char* p = s_->data(); + while(first != last) + *p++ = *first++; + *p = 0; + return *this; +} + +template +string& +string:: +append(InputIt, InputIt) +{ + return *this; +} + +} // json +} // boost + +#endif diff --git a/include/boost/json/impl/string.ipp b/include/boost/json/impl/string.ipp new file mode 100644 index 00000000..ff538153 --- /dev/null +++ b/include/boost/json/impl/string.ipp @@ -0,0 +1,749 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/vinniefalco/json +// + +#ifndef BOOST_JSON_IMPL_STRING_IPP +#define BOOST_JSON_IMPL_STRING_IPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace json { + +string:: +~string() +{ + if(s_) + impl::destroy(s_, sp_); +} + +string:: +string() noexcept + : sp_(default_storage()) +{ +} + +string:: +string(storage_ptr sp) noexcept + : sp_(std::move(sp)) +{ +} + +string:: +string( + size_type count, + char ch, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(count, ch); +} + +string:: +string( + string const& other, + size_type pos, + size_type count, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(other, pos, count); +} + +string:: +string( + char const* s, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(s); +} + +string:: +string( + char const* s, + size_type count, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(s, count); +} + +string:: +string(string const& other) + : sp_(default_storage()) +{ + assign(other); +} + +string:: +string( + string const& other, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(other); +} + +string:: +string(pilfered p) noexcept + : s_(boost::exchange( + p.get().s_, nullptr)) + , sp_(std::move(p.get().sp_)) +{ +} + +string:: +string(string&& other) noexcept + : s_(boost::exchange( + other.s_, nullptr)) + , sp_(other.sp_) +{ +} + +string:: +string( + string&& other, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(std::move(other)); +} + +string:: +string( + std::initializer_list init, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(init); +} + +string:: +string( + string_view sv, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(sv); +} + +string:: +string( + string_view sv, + size_type pos, + size_type n, + storage_ptr sp) + : sp_(std::move(sp)) +{ + assign(sv.substr(pos, n)); +} + +//------------------------------------------------------------------------------ + +string& +string:: +operator=(string const& other) +{ + assign(other); + return *this; +} + +string& +string:: +operator=(string&& other) +{ + assign(std::move(other)); + return *this; +} + +string& +string:: +operator=(char const* s) +{ + assign(s); + return *this; +} + +string& +string:: +operator=(char ch) +{ + raw_resize(1); + traits_type::assign( + s_->data(), 1, ch); + return *this; +} + +string& +string:: +operator=( + std::initializer_list init) +{ + assign(init); + return *this; +} + +string& +string:: +operator=(string_view sv) +{ + assign(sv); + return *this; +} + +//------------------------------------------------------------------------------ + +string& +string:: +assign( + size_type count, + char ch) +{ + raw_resize(count); + traits_type::assign( + s_->data(), count, ch); + return *this; +} + +string& +string:: +assign( + string const& other) +{ + raw_resize(other.size()); + traits_type::copy(data(), + other.data(), other.size()); + return *this; +} + +string& +string:: +assign( + string const& other, + size_type pos, + size_type count) +{ + return assign( + other.substr(pos, count)); +} + +string& +string:: +assign(string&& other) +{ + if(*sp_ != *other.sp_) + return assign(other); + + auto s = s_; + s_ = other.s_; + other.s_ = nullptr; + if(s) + impl::destroy(s, sp_); + return *this; +} + +string& +string:: +assign( + char const* s, + size_type count) +{ + raw_resize(count); + traits_type::copy( + data(), s, count); + return *this; +} + +string& +string:: +assign( + char const* s) +{ + return assign(s, + traits_type::length(s)); +} + +string& +string:: +assign(std::initializer_list init) +{ + raw_resize(init.size()); + std::copy( + init.begin(), + init.end(), + s_->data()); + return *this; +} + +string& +string:: +assign(string_view sv) +{ + return assign( + sv.data(), sv.size()); +} + +string& +string:: +assign( + string_view sv, + size_type pos, + size_type count) +{ + return assign( + sv.substr(pos, count)); +} + +//------------------------------------------------------------------------------ +// +// Element Access +// +//------------------------------------------------------------------------------ + +char& +string:: +at(size_type pos) +{ + if(pos >= size()) + BOOST_THROW_EXCEPTION( + std::out_of_range( + "pos >= size()")); + return s_->data()[pos]; +} + +char const& +string:: +at(size_type pos) const +{ + if(pos >= size()) + BOOST_THROW_EXCEPTION( + std::out_of_range( + "pos >= size()")); + return s_->data()[pos]; +} + +char& +string:: +operator[](size_type pos) +{ + return s_->data()[pos]; +} + +const char& +string:: +operator[](size_type pos) const +{ + return s_->data()[pos]; +} + +char& +string:: +front() +{ + return s_->data()[0]; +} + +char const& +string:: +front() const +{ + return s_->data()[0]; +} + +char& +string:: +back() +{ + return s_->data()[s_->size - 1]; +} + +char const& +string:: +back() const +{ + return s_->data()[s_->size - 1]; +} + +char* +string:: +data() noexcept +{ + if(! s_) + return const_cast< + char*>(""); + return s_->data(); +} + +char const* +string:: +data() const noexcept +{ + if(! s_) + return ""; + return s_->data(); +} + +char const* +string:: +c_str() const noexcept +{ + return data(); +} + +string:: +operator string_view() const noexcept +{ + return {data(), size()}; +} + +//------------------------------------------------------------------------------ +// +// Iterators +// +//------------------------------------------------------------------------------ + +auto +string:: +begin() noexcept -> + iterator +{ + if(! s_) + return nullptr; + return s_->data(); +} + +auto +string:: +begin() const noexcept -> + const_iterator +{ + if(! s_) + return nullptr; + return s_->data(); +} + +auto +string:: +end() noexcept -> + iterator +{ + if(! s_) + return nullptr; + return s_->end(); +} + +auto +string:: +end() const noexcept -> + const_iterator +{ + if(! s_) + return nullptr; + return s_->end(); +} + +auto +string:: +rbegin() noexcept -> + reverse_iterator +{ + if(! s_) + return reverse_iterator(nullptr); + return reverse_iterator(s_->end()); +} + +auto +string:: +rbegin() const noexcept -> + const_reverse_iterator +{ + if(! s_) + return reverse_iterator(nullptr); + return const_reverse_iterator(s_->end()); +} + +auto +string:: +rend() noexcept -> + reverse_iterator +{ + if(! s_) + return reverse_iterator(nullptr); + return reverse_iterator(s_->data()); +} + +auto +string:: +rend() const noexcept -> + const_reverse_iterator +{ + if(! s_) + return reverse_iterator(nullptr); + return const_reverse_iterator(s_->data()); +} + +//------------------------------------------------------------------------------ +// +// Capacity +// +//------------------------------------------------------------------------------ + +bool +string:: +empty() const noexcept +{ + if(! s_) + return true; + return s_->size == 0; +} + +auto +string:: +size() const noexcept -> + size_type +{ + if(! s_) + return 0; + return s_->size; +} + +void +string:: +reserve(size_type new_capacity) +{ + if(new_capacity <= capacity()) + return; + auto s = impl::construct( + size(), new_capacity, sp_); + traits_type::copy( + s->data(), data(), size()); + impl::destroy( + boost::exchange(s_, s), sp_); +} + +auto +string:: +capacity() const noexcept -> + size_type +{ + if(! s_) + return 0; + return s_->capacity; +} + +//------------------------------------------------------------------------------ +// +// Operations +// +//------------------------------------------------------------------------------ + +void +string:: +clear() noexcept +{ + if(s_) + s_->term(0); +} + +//------------------------------------------------------------------------------ + +void +string:: +push_back(char ch) +{ + s_->data()[s_->size++] = ch; + s_->data()[s_->size] = 0; +} + +void +string:: +pop_back() +{ + s_->data()[--s_->size] = 0; +} + +//------------------------------------------------------------------------------ + +string& +string:: +append(size_type count, char ch) +{ + traits_type::assign( + raw_insert(size(), count), + count, ch); + return *this; +} + +string& +string:: +append(string const& str) +{ + return append( + str.data(), str.size()); +} + +string& +string:: +append( + string const& str, + size_type pos, + size_type count) +{ + return append(str.substr(pos, count)); +} + +string& +string:: +append( + char const* s, + size_type count) +{ + traits_type::copy( + raw_insert(size(), count), + s, count); + return *this; +} + +string& +string:: +append(char const* s) +{ + return append(s, + traits_type::length(s)); +} + +string& +string:: +append(std::initializer_list init) +{ + // TODO + boost::ignore_unused(init); + return *this; +} + +string& +string:: +append(string_view sv) +{ + return append( + sv.data(), sv.size()); +} + +//------------------------------------------------------------------------------ + +string_view +string:: +substr( + size_type pos , + size_type count) const +{ + if(pos > size()) + BOOST_THROW_EXCEPTION( + std::out_of_range("pos > size()")); + if(! s_) + return {}; + return { + s_->data() + pos, + std::min( + s_->size - pos, + count)}; +} + +//------------------------------------------------------------------------------ + +void +string:: +raw_resize(size_type n) +{ + if(n <= capacity()) + { + if(s_) + s_->term(n); + return; + } + auto s = boost::exchange(s_, + impl::construct(n, n, sp_)); + if(s) + impl::destroy(s, sp_); +} + +// insert `n` uninitialized chars at `pos`, +// reallocating as needed. does nothing if n==0. +// returns the insertion point +char* +string:: +raw_insert( + size_type pos, + size_type n) +{ + if( n > max_size() || + size() > max_size() - n) + BOOST_THROW_EXCEPTION( + std::out_of_range( + "size() + n > max_size()")); + if(pos > size()) + BOOST_THROW_EXCEPTION( + std::out_of_range( + "pos > size()")); + auto const new_size = size() + n; + + if(new_size <= capacity()) + { + if(s_) + { + traits_type::move( + s_->data() + pos + n, + s_->data() + pos, + s_->size + 1 - pos); + s_->size = new_size; + } + } + else + { + auto s = impl::construct( + new_size, sp_); + if(s_) + { + traits_type::copy( + s->data(), + s_->data(), + pos); + traits_type::copy( + s->data() + pos + n, + s_->data() + pos, + s_->size + 1 - pos); + s->size = new_size; + impl::destroy(s_, sp_); + } + else + { + s->term(n); + } + s_ = s; + } + return s_->data() + pos; +} + +std::ostream& +operator<<(std::ostream& os, string const& s) +{ + os.write(s.data(), s.size()); + return os; +} + +} // json +} // boost + +#endif diff --git a/include/boost/json/impl/value.ipp b/include/boost/json/impl/value.ipp index 7a1087bf..0ea71e91 100644 --- a/include/boost/json/impl/value.ipp +++ b/include/boost/json/impl/value.ipp @@ -143,8 +143,7 @@ value( case json::kind::string: ::new(&str_) string( - string::allocator_type( - std::move(sp))); + std::move(sp)); break; case json::kind::number: @@ -188,10 +187,8 @@ value( break; case json::kind::string: - // workaround for some stdlibs - construct_string( - other.str_, - std::move(sp)); + ::new(&str_) string( + other.str_, std::move(sp)); break; case json::kind::number: @@ -318,8 +315,7 @@ value( break; case json::kind::string: - // workaround for some stdlibs - construct_string( + ::new(&str_) string( std::move(other.str_), std::move(sp)); break; @@ -424,11 +420,11 @@ value:: value( string str, storage_ptr sp) -{ - // workaround for some stdlibs - construct_string( + : str_( std::move(str), - std::move(sp)); + std::move(sp)) + , kind_(json::kind::string) +{ } value:: @@ -591,7 +587,7 @@ maybe_object( // //------------------------------------------------------------------------------ -storage_ptr +storage_ptr const& value:: get_storage() const noexcept { @@ -604,7 +600,7 @@ get_storage() const noexcept return arr_.get_storage(); case json::kind::string: - return str_.get_allocator().get_storage(); + return str_.get_storage(); default: break; @@ -1004,48 +1000,6 @@ pop_back() //------------------------------------------------------------------------------ -// private - -template -auto -value:: -construct_string( - S&& str, storage_ptr sp) -> - typename std::enable_if< - ! std::is_constructible::value>::type - -{ - // workaround for missing std::string - // ctors in some stdlib versions - auto s = string(typename - string::allocator_type( - std::move(sp))); - s = std::move(str); - ::new(&str_) string( - std::move(s)); - kind_ = json::kind::string; -} - -template -auto -value:: -construct_string( - S&& str, storage_ptr sp) -> - typename std::enable_if< - std::is_constructible::value>::type -{ - ::new(&str_) string(std::move(str), - typename string::allocator_type( - std::move(sp))); - kind_ = json::kind::string; -} - -//------------------------------------------------------------------------------ - // friends std::ostream& diff --git a/include/boost/json/string.hpp b/include/boost/json/string.hpp index 5ba59a23..7e89c07d 100644 --- a/include/boost/json/string.hpp +++ b/include/boost/json/string.hpp @@ -11,23 +11,685 @@ #define BOOST_JSON_STRING_HPP #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include namespace boost { namespace json { -class value; - /** The native type of string values */ -using string = - std::basic_string< - char, - std::char_traits, - allocator>; +class string +{ + struct impl; + + impl* s_ = nullptr; + storage_ptr sp_; + +public: + using traits_type = std::char_traits; + using value_type = char; + using size_type = unsigned long; + using difference_type = long; + using pointer = char*; + using reference = char&; + using iterator = char*; + using const_pointer = char const*; + using const_reference = const char&; + using const_iterator = char const*; + using reverse_iterator = + std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + /// A special index + static constexpr size_type npos = + (std::numeric_limits::max)(); + + BOOST_JSON_DECL + ~string(); + + BOOST_JSON_DECL + string() noexcept; + + BOOST_JSON_DECL + explicit + string(storage_ptr sp) noexcept; + + BOOST_JSON_DECL + string( + size_type count, + char ch, + storage_ptr sp = + default_storage()); + + BOOST_JSON_DECL + string( + string const& other, + size_type pos, + size_type count = npos, + storage_ptr sp = + default_storage()); + + BOOST_JSON_DECL + string( + char const* s, + storage_ptr sp = + default_storage()); + + BOOST_JSON_DECL + string( + char const* s, + size_type count, + storage_ptr sp = + default_storage()); + + template::value_type, + char>::value>::type + #endif + > + string( + InputIt first, + InputIt last, + storage_ptr sp = + default_storage()); + + BOOST_JSON_DECL + string(string const& other); + + BOOST_JSON_DECL + string( + string const& other, + storage_ptr sp); + + BOOST_JSON_DECL + string(pilfered other) noexcept; + + BOOST_JSON_DECL + string(string&& other) noexcept; + + BOOST_JSON_DECL + string( + string&& other, + storage_ptr sp); + + BOOST_JSON_DECL + string( + std::initializer_list init, + storage_ptr sp = default_storage()); + + BOOST_JSON_DECL + string( + string_view sv, + storage_ptr sp = + default_storage()); + + BOOST_JSON_DECL + string( + string_view sv, + size_type pos, + size_type n, + storage_ptr sp = + default_storage()); + + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + string& + operator=(string const& other); + + BOOST_JSON_DECL + string& + operator=(string&& other); + + BOOST_JSON_DECL + string& + operator=(char const* s); + + BOOST_JSON_DECL + string& + operator=(char ch); + + BOOST_JSON_DECL + string& + operator=(std::initializer_list init); + + BOOST_JSON_DECL + string& + operator=(string_view sv); + + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + string& + assign( + size_type count, + char ch); + + BOOST_JSON_DECL + string& + assign( + string const& other); + + BOOST_JSON_DECL + string& + assign( + string const& other, + size_type pos, + size_type count); + + BOOST_JSON_DECL + string& + assign(string&& other); + + BOOST_JSON_DECL + string& + assign( + char const* s, + size_type count); + + BOOST_JSON_DECL + string& + assign( + char const* s); + + template::value_type, + char>::value>::type + #endif + > + string& + assign( + InputIt first, + InputIt last); + + BOOST_JSON_DECL + string& + assign(std::initializer_list init); + + BOOST_JSON_DECL + string& + assign(string_view sv); + + BOOST_JSON_DECL + string& + assign( + string_view sv, + size_type pos, + size_type count = npos); + + //-------------------------------------------------------------------------- + + storage_ptr const& + get_storage() const noexcept + { + return sp_; + } + + //-------------------------------------------------------------------------- + // + // Element Access + // + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + char& + at(size_type pos); + + BOOST_JSON_DECL + char const& + at(size_type pos) const; + + BOOST_JSON_DECL + char& + operator[](size_type pos); + + BOOST_JSON_DECL + const char& + operator[](size_type pos) const; + + BOOST_JSON_DECL + char& + front(); + + BOOST_JSON_DECL + char const& + front() const; + + BOOST_JSON_DECL + char& + back(); + + BOOST_JSON_DECL + char const& + back() const; + + BOOST_JSON_DECL + char* + data() noexcept; + + BOOST_JSON_DECL + char const* + data() const noexcept; + + BOOST_JSON_DECL + char const* + c_str() const noexcept; + + BOOST_JSON_DECL + operator string_view() const noexcept; + + //-------------------------------------------------------------------------- + // + // Iterators + // + //-------------------------------------------------------------------------- + + /** Return an iterator to the first character + + If the container is empty, the returned iterator + will be equal to @ref end(). + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + iterator + begin() noexcept; + + /** Return an iterator to the first character + + If the container is empty, the returned iterator + will be equal to @ref end(). + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_iterator + begin() const noexcept; + + /** Return an iterator to the first character + + If the container is empty, the returned iterator + will be equal to @ref end(). + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_iterator + cbegin() const noexcept + { + return begin(); + } + + /** Return an iterator to the character following the last character + + The character acts as a placeholder; attempting to + access it results in undefined behavior. + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + iterator + end() noexcept; + + /** Return an iterator to the character following the last character + + The character acts as a placeholder; attempting to + access it results in undefined behavior. + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_iterator + end() const noexcept; + + /** Return an iterator to the character following the last character + + The character acts as a placeholder; attempting to + access it results in undefined behavior. + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_iterator + cend() const noexcept + { + return end(); + } + + /** Return a reverse iterator to the first character of the reversed container + + The pointed-to character corresponds to the last character + of the non-reversed container. If the container is empty, + the returned iterator is equal to @ref rend() + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + reverse_iterator + rbegin() noexcept; + + /** Return a reverse iterator to the first character of the reversed container + + The pointed-to character corresponds to the last character + of the non-reversed container. If the container is empty, + the returned iterator is equal to @ref rend() + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_reverse_iterator + rbegin() const noexcept; + + /** Return a reverse iterator to the first character of the reversed container + + The pointed-to character corresponds to the last character + of the non-reversed container. If the container is empty, + the returned iterator is equal to @ref rend() + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_reverse_iterator + crbegin() const noexcept + { + return rbegin(); + } + + /** Return a reverse iterator to the character following the last character of the reversed container + + The pointed-to character corresponds to the character + preceding the first character of the non-reversed container. + This character acts as a placeholder, attempting to access + it results in undefined behavior. + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + reverse_iterator + rend() noexcept; + + /** Return a reverse iterator to the character following the last character of the reversed container + + The pointed-to character corresponds to the character + preceding the first character of the non-reversed container. + This character acts as a placeholder, attempting to access + it results in undefined behavior. + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_reverse_iterator + rend() const noexcept; + + /** Return a reverse iterator to the character following the last character of the reversed container + + The pointed-to character corresponds to the character + preceding the first character of the non-reversed container. + This character acts as a placeholder, attempting to access + it results in undefined behavior. + + @par Complexity + + Constant. + */ + BOOST_JSON_DECL + const_reverse_iterator + crend() const noexcept + { + return rend(); + } + + //-------------------------------------------------------------------------- + // + // Capacity + // + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + bool + empty() const noexcept; + + BOOST_JSON_DECL + size_type + size() const noexcept; + + size_type + length() const noexcept + { + return size(); + } + + size_type + max_size() const noexcept + { + return npos - 1; + } + + BOOST_JSON_DECL + void + reserve(size_type new_capacity); + + BOOST_JSON_DECL + size_type + capacity() const noexcept; + + //-------------------------------------------------------------------------- + // + // Operations + // + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + void + clear() noexcept; + + //-------------------------------------------------------------------------- + + // insert + + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + string& + erase( + size_type index = 0, + size_type count = npos); + + BOOST_JSON_DECL + iterator + erase(const_iterator pos); + + BOOST_JSON_DECL + iterator + erase( + const_iterator first, + const_iterator last); + + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + void + push_back(char ch); + + BOOST_JSON_DECL + void + pop_back(); + + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + string& + append(size_type count, char ch); + + BOOST_JSON_DECL + string& + append(string const& str ); + + BOOST_JSON_DECL + string& + append( + string const& str, + size_type pos, + size_type count = npos); + + BOOST_JSON_DECL + string& + append( + char const* s, + size_type count); + + BOOST_JSON_DECL + string& + append(char const* s); + + template::value_type, + char>::value>::type + #endif + > + string& + append(InputIt first, InputIt last); + + BOOST_JSON_DECL + string& + append(std::initializer_list init); + + BOOST_JSON_DECL + string& + append(string_view sv); + + //-------------------------------------------------------------------------- + // + // Observers + // + //-------------------------------------------------------------------------- + + BOOST_JSON_DECL + string_view + substr( + size_type pos = 0, + size_type count = npos) const; + +private: + template + using iter_cat = typename + std::iterator_traits::iterator_category; + + BOOST_JSON_DECL + void + raw_resize( + size_type size); + + BOOST_JSON_DECL + char* + raw_insert( + size_type pos, + size_type n); + + template + void + maybe_raw_resize( + InputIt, InputIt, + std::input_iterator_tag) + { + } + + template + void + maybe_raw_resize( + InputIt first, + InputIt last, + std::forward_iterator_tag) + { + raw_resize(std::distance( + first, last)); + } +}; + +inline +bool +operator==(string const& lhs, string const& rhs) +{ + return std::lexicographical_compare( + lhs.begin(), lhs.end(), + rhs.begin(), rhs.end()) + == 0; +} + +inline +bool +operator==(char const* lhs, string const& rhs) +{ + return std::lexicographical_compare( + lhs, lhs + std::char_traits::length(lhs), + rhs.begin(), rhs.end()) + == 0; +} + +inline +bool +operator==(string const& lhs, char const* rhs) +{ + return std::lexicographical_compare( + lhs.begin(), lhs.end(), + rhs, rhs + std::char_traits::length(rhs)) + == 0; +} + +BOOST_JSON_DECL +std::ostream& +operator<<(std::ostream& os, string const& s); } // json } // boost +#include +#if BOOST_JSON_HEADER_ONLY +#include +#endif + #endif diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp index ff0b625f..d44901e1 100644 --- a/include/boost/json/value.hpp +++ b/include/boost/json/value.hpp @@ -767,7 +767,7 @@ public: //-------------------------------------------------------------------------- BOOST_JSON_DECL - storage_ptr + storage_ptr const& get_storage() const noexcept; object& @@ -1079,24 +1079,6 @@ private: construct( json::kind, storage_ptr) noexcept; - template - auto - construct_string( - S&& str, storage_ptr store) -> - typename std::enable_if< - ! std::is_constructible::value>::type; - - template - auto - construct_string( - S&& str, storage_ptr store) -> - typename std::enable_if< - std::is_constructible::value>::type; - BOOST_JSON_DECL friend std::ostream& diff --git a/test/array.cpp b/test/array.cpp index 71fb55e7..12f6d6d9 100644 --- a/test/array.cpp +++ b/test/array.cpp @@ -305,7 +305,7 @@ public: } void - testElementAccess() + testAccess() { // at(pos) { @@ -657,7 +657,7 @@ public: }); // insert(const_iterator, size_type, value_type const&) - fail_loop([this](storage_ptr const& sp) + fail_loop([](storage_ptr const& sp) { value v({1,2,3}); array a({1, "hello"}, sp); @@ -1009,7 +1009,7 @@ public: run() override { testSpecial(); - testElementAccess(); + testAccess(); testIterators(); testCapacity(); testModifiers(); diff --git a/test/object.cpp b/test/object.cpp index b89fcd1b..44f3fcc3 100644 --- a/test/object.cpp +++ b/test/object.cpp @@ -19,18 +19,6 @@ namespace boost { namespace json { -#define BEAST_THROWS( EXPR, EXCEP ) \ - try { \ - EXPR; \ - BEAST_FAIL(); \ - } \ - catch(EXCEP const&) { \ - BEAST_PASS(); \ - } \ - catch(...) { \ - BEAST_FAIL(); \ - } - class object_test : public beast::unit_test::suite { public: diff --git a/test/string.cpp b/test/string.cpp index 06c7c617..37c29503 100644 --- a/test/string.cpp +++ b/test/string.cpp @@ -22,35 +22,625 @@ class string_test : public beast::unit_test::suite { public: void - testExceptions() + testCtors() { - string s1 = "Hello, world!"; - auto sp = make_storage(); - string s2; - while(sp->fail < 200) + // string() { - try + scoped_fail_storage fs; + string s; + } + + // string(storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s(sp); + BEAST_EXPECT(s.empty()); + BEAST_EXPECT( + *s.get_storage() == *sp); + }); + + // string(size_type, char, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s(3, '*', sp); + BEAST_EXPECT(s.size() == 3); + BEAST_EXPECT(s[0] == '*'); + }); + + // string(string const&, size_type, size_type, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s1 = "abcde"; + string s2(s1, 1, 3, sp); + BEAST_EXPECT(s2 == "bcd"); + }); + + // string(char const*, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s("abc", sp); + BEAST_EXPECT(s == "abc"); + }); + + // string(char const*, size_type, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s("abcde", 3, sp); + BEAST_EXPECT(s == "abc"); + }); + + // string(InputIt, InputIt, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + auto init = { 'a', 'b', 'c' }; + string s(init.begin(), init.end(), sp); + BEAST_EXPECT(s == "abc"); + }); + + // string(string) + fail_loop([] + { + string s1 = "abc"; + string s2(s1); + BEAST_EXPECT(s2 == "abc"); + }); + + // string(string, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s1 = "abc"; + string s2(s1, sp); + BEAST_EXPECT(s2 == "abc"); + }); + + // string(pilfered) + { + string s1 = "abc"; + string s2(pilfer(s1)); + BEAST_EXPECT(s2 == "abc"); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT( + s1.get_storage() == nullptr); + } + + // string(string&&) + { + string s1 = "abc"; + scoped_fail_storage fs; + string s2(std::move(s1)); + BEAST_EXPECT(s2 == "abc"); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT( + *s1.get_storage() == + *s2.get_storage()); + } + + // string(string&&, storage_ptr) + { + // same storage + fail_loop([](storage_ptr const& sp) { - auto s = string( - string::allocator_type{sp}); - //obj.emplace("a", 2); - //obj = obj0; - //obj1 = obj; - break; - } - catch(test_failure const&) + string s1("abc", sp); + string s2(std::move(s1), sp); + BEAST_EXPECT(s2 == "abc"); + BEAST_EXPECT(s1.empty()); + BEAST_EXPECT( + *s1.get_storage() == + *s2.get_storage()); + }); + + // different storage + fail_loop([](storage_ptr const& sp) { + string s1 = "abc"; + string s2(std::move(s1), sp); + BEAST_EXPECT(s2 == "abc"); + BEAST_EXPECT(s1 == "abc"); + BEAST_EXPECT( + *s1.get_storage() != + *s2.get_storage()); + }); + } + + // string(initializer_list, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s({'a', 'b', 'c'}, sp); + BEAST_EXPECT(s == "abc"); + }); + + // string(string_view, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s(string_view("abc"), sp); + BEAST_EXPECT(s == "abc"); + }); + + // string(string_view, size_type, size_type, storage_ptr) + fail_loop([](storage_ptr const& sp) + { + string s(string_view("abcde"), 1, 3, sp); + BEAST_EXPECT(s == "bcd"); + }); + } + + void + testAssignment() + { + // operator=(string) + fail_loop([](storage_ptr const& sp) + { + string s1("abc"); + string s2(sp); + s2 = s1; + BEAST_EXPECT(s2 == "abc"); + }); + + // operator=(string&&) + { + // same storage + fail_loop([](storage_ptr const& sp) + { + string s1("abc", sp); + string s2(sp); + s2 = std::move(s1); + BEAST_EXPECT(s2 == "abc"); + BEAST_EXPECT(s1.empty()); + }); + + // different storage + fail_loop([](storage_ptr const& sp) + { + string s1("abc"); + string s2(sp); + s2 = std::move(s1); + BEAST_EXPECT(s2 == "abc"); + BEAST_EXPECT(s1 == "abc"); + }); + } + + // operator=(char const*) + fail_loop([](storage_ptr const& sp) + { + string s(sp); + s = "abc"; + BEAST_EXPECT(s == "abc"); + }); + + // operator=(char) + fail_loop([](storage_ptr const& sp) + { + string s(sp); + s = '*'; + BEAST_EXPECT(s == "*"); + }); + + // operator=(std::initializer_list) + fail_loop([](storage_ptr const& sp) + { + string s(sp); + s = {'a', 'b', 'c'}; + BEAST_EXPECT(s == "abc"); + }); + + // operator=(string_view); + fail_loop([](storage_ptr const& sp) + { + string s(sp); + s = string_view("abc", 3); + BEAST_EXPECT(s == "abc"); + }); + } + + void + testAssign() + { + // assign(size_type, char) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.assign(3, '*'); + BEAST_EXPECT(s == "***"); + }); + + // assign(string) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + string s2("abc"); + s.assign(s2); + BEAST_EXPECT(s == "abc"); + }); + + // assign(string, size_type, size_type) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + string s2("abcde"); + s.assign(s2, 1, 3); + BEAST_EXPECT(s == "bcd"); + }); + + // assign(string&&) + { + // same storage + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + string s2("abc", sp); + s.assign(std::move(s2)); + BEAST_EXPECT(s == "abc"); + }); + + // different storage + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + string s2("abc"); + s.assign(std::move(s2)); + BEAST_EXPECT(s == "abc"); + }); + } + + // assign(char const*, size_type) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + }); + + // assign(char const* s) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.assign("abc"); + BEAST_EXPECT(s == "abc"); + }); + + // assign(InputIt, InputIt) + fail_loop([](storage_ptr const& sp) + { + std::initializer_list< + char> init = { 'a', 'b', 'c' }; + string s("123", sp); + s.assign(init.begin(), init.end()); + BEAST_EXPECT(s == "abc"); + }); + + // assign(std::initializer_list) + fail_loop([](storage_ptr const& sp) + { + std::initializer_list< + char> init = { 'a', 'b', 'c' }; + string s("123", sp); + s.assign(init); + BEAST_EXPECT(s == "abc"); + }); + + // assign(string_view) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.assign(string_view("abc", 3)); + BEAST_EXPECT(s == "abc"); + }); + + // assign(string_view, size_type, size_type); + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.assign(string_view("abcde"), 1, 3); + BEAST_EXPECT(s == "bcd"); + }); + } + + void + testAccess() + { + string s("abc"); + auto const& cs = s; + + // at(size_type) + { + BEAST_EXPECT(s.at(1) == 'b'); + s.at(1) = '*'; + BEAST_EXPECT(s.at(1) == '*'); + s.at(1) = 'b'; + BEAST_THROWS(s.at(3), + std::out_of_range); + } + + // at(size_type) const + { + BEAST_EXPECT(cs.at(1) == 'b'); + BEAST_THROWS(cs.at(3), + std::out_of_range); + } + + // operator[](size_type) + { + BEAST_EXPECT(s[1] == 'b'); + s[1] = '*'; + BEAST_EXPECT(s[1] == '*'); + s[1] = 'b'; + } + + // operator[](size_type) const + { + BEAST_EXPECT(cs[1] == 'b'); + } + + // front() + { + s.front() = '*'; + BEAST_EXPECT(s.front() == '*'); + s.front() = 'a'; + } + + // front() const + { + BEAST_EXPECT(cs.front() == 'a'); + } + + // back() + { + s.back() = '*'; + BEAST_EXPECT(s.back() == '*'); + s.back() = 'c'; + } + + // back() const + { + BEAST_EXPECT(cs.back() == 'c'); + } + + // data() + { + BEAST_EXPECT(string_view( + s.data()) == "abc"); + } + // data() const + { + BEAST_EXPECT(string_view( + cs.data()) == "abc"); + } + + // c_str() + { + BEAST_EXPECT(string_view( + s.c_str()) == "abc"); + } + + // operator string_view() + { + BEAST_EXPECT( + string_view(s) == "abc"); + } + } + + void + testIterators() + { + string s = "abc"; + auto const& ac(s); + + { + auto it = s.begin(); + BEAST_EXPECT(*it == 'a'); ++it; + BEAST_EXPECT(*it == 'b'); it++; + BEAST_EXPECT(*it == 'c'); ++it; + BEAST_EXPECT(it == s.end()); + } + { + auto it = s.cbegin(); + BEAST_EXPECT(*it == 'a'); ++it; + BEAST_EXPECT(*it == 'b'); it++; + BEAST_EXPECT(*it == 'c'); ++it; + BEAST_EXPECT(it == s.cend()); + } + { + auto it = ac.begin(); + BEAST_EXPECT(*it == 'a'); ++it; + BEAST_EXPECT(*it == 'b'); it++; + BEAST_EXPECT(*it == 'c'); ++it; + BEAST_EXPECT(it == ac.end()); + } + { + auto it = s.end(); + --it; BEAST_EXPECT(*it == 'c'); + it--; BEAST_EXPECT(*it == 'b'); + --it; BEAST_EXPECT(*it == 'a'); + BEAST_EXPECT(it == s.begin()); + } + { + auto it = s.cend(); + --it; BEAST_EXPECT(*it == 'c'); + it--; BEAST_EXPECT(*it == 'b'); + --it; BEAST_EXPECT(*it == 'a'); + BEAST_EXPECT(it == s.cbegin()); + } + { + auto it = ac.end(); + --it; BEAST_EXPECT(*it == 'c'); + it--; BEAST_EXPECT(*it == 'b'); + --it; BEAST_EXPECT(*it == 'a'); + BEAST_EXPECT(it == ac.begin()); + } + + { + auto it = s.rbegin(); + BEAST_EXPECT(*it == 'c'); ++it; + BEAST_EXPECT(*it == 'b'); it++; + BEAST_EXPECT(*it == 'a'); ++it; + BEAST_EXPECT(it == s.rend()); + } + { + auto it = s.crbegin(); + BEAST_EXPECT(*it == 'c'); ++it; + BEAST_EXPECT(*it == 'b'); it++; + BEAST_EXPECT(*it == 'a'); ++it; + BEAST_EXPECT(it == s.crend()); + } + { + auto it = ac.rbegin(); + BEAST_EXPECT(*it == 'c'); ++it; + BEAST_EXPECT(*it == 'b'); it++; + BEAST_EXPECT(*it == 'a'); ++it; + BEAST_EXPECT(it == ac.rend()); + } + { + auto it = s.rend(); + --it; BEAST_EXPECT(*it == 'a'); + it--; BEAST_EXPECT(*it == 'b'); + --it; BEAST_EXPECT(*it == 'c'); + BEAST_EXPECT(it == s.rbegin()); + } + { + auto it = s.crend(); + --it; BEAST_EXPECT(*it == 'a'); + it--; BEAST_EXPECT(*it == 'b'); + --it; BEAST_EXPECT(*it == 'c'); + BEAST_EXPECT(it == s.crbegin()); + } + { + auto it = ac.rend(); + --it; BEAST_EXPECT(*it == 'a'); + it--; BEAST_EXPECT(*it == 'b'); + --it; BEAST_EXPECT(*it == 'c'); + BEAST_EXPECT(it == ac.rbegin()); + } + + { + string s2; + string const& cs2(s2); + BEAST_EXPECT(std::distance( + s2.begin(), s2.end()) == 0); + BEAST_EXPECT(std::distance( + cs2.begin(), cs2.end()) == 0); + BEAST_EXPECT(std::distance( + s2.rbegin(), s2.rend()) == 0); + BEAST_EXPECT(std::distance( + cs2.rbegin(), cs2.rend()) == 0); + } + } + + void + testAppend() + { + // append(size_type, char) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append(3, '*'); + BEAST_EXPECT(s == "123***"); + }); + + // append(string) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append(string("abc")); + BEAST_EXPECT(s == "123abc"); + }); + + // append(string, size_type, size_type) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append(string("abcde"), 1, 3); + BEAST_EXPECT(s == "123bcd"); + }); + + // append(char const*, size_type) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append("abc", 3); + BEAST_EXPECT(s == "123abc"); + }); + + // append(char const*) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append("abc"); + BEAST_EXPECT(s == "123abc"); + }); + + // append(InputIt, InputIt) +#if 0 + fail_loop([](storage_ptr const& sp) + { + std::initializer_list< + char> init = { 'a', 'b', 'c' }; + string s("123", sp); + s.append(init.begin(), init.end()); + BEAST_EXPECT(s == "123abc"); + }); + + // append(init_list) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append({'a', 'b', 'c'}); + BEAST_EXPECT(s == "123abc"); + }); +#endif + + // append(string_view) + fail_loop([](storage_ptr const& sp) + { + string s("123", sp); + s.append(string_view("abc", 3)); + BEAST_EXPECT(s == "123abc"); + }); + + } + + void + testCapacity() + { + // empty() + { + { + string s; + BEAST_EXPECT(s.empty()); } + { + string s = "abc"; + BEAST_EXPECT(! s.empty()); + } + } + + // size() + // length() + // max_size() + { + string s = "abc"; + BEAST_EXPECT(s.size() == 3); + BEAST_EXPECT(s.length() == 3); + BEAST_EXPECT(s.max_size() < string::npos); + } + + // reserve(size_type) + { + } + + // capacity() + { } } void run() override { - #if !defined(BOOST_MSVC) || _ITERATOR_DEBUG_LEVEL == 0 - testExceptions(); - #endif - pass(); + testCtors(); + testAssignment(); + testAssign(); + testAccess(); + testIterators(); + testCapacity(); + + testAppend(); } }; diff --git a/test/test_storage.hpp b/test/test_storage.hpp index b3284a5c..f8e4be05 100644 --- a/test/test_storage.hpp +++ b/test/test_storage.hpp @@ -234,7 +234,7 @@ equal_storage( return equal_storage(v.as_array(), sp); case json::kind::string: - return *v.as_string().get_allocator().get_storage() == *sp; + return *v.as_string().get_storage() == *sp; case json::kind::number: case json::kind::boolean: