diff options
author | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
---|---|---|
committer | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
commit | 1d055261b4144dbf86b2658437015b15d4dd9bff (patch) | |
tree | 6049b19d1bf953a650383de1a5e438b8b82679f6 /include/jsoncons/json_object.hpp | |
download | csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2 csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip |
initial
Diffstat (limited to 'include/jsoncons/json_object.hpp')
-rw-r--r-- | include/jsoncons/json_object.hpp | 1772 |
1 files changed, 1772 insertions, 0 deletions
diff --git a/include/jsoncons/json_object.hpp b/include/jsoncons/json_object.hpp new file mode 100644 index 0000000..76e2cd0 --- /dev/null +++ b/include/jsoncons/json_object.hpp @@ -0,0 +1,1772 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_OBJECT_HPP +#define JSONCONS_JSON_OBJECT_HPP + +#include <string> +#include <vector> +#include <tuple> +#include <exception> +#include <cstring> +#include <algorithm> // std::sort, std::stable_sort, std::lower_bound, std::unique +#include <utility> +#include <initializer_list> +#include <iterator> // std::iterator_traits +#include <memory> // std::allocator +#include <utility> // std::move +#include <cassert> // assert +#include <type_traits> // std::enable_if +#include <jsoncons/json_exception.hpp> +#include <jsoncons/allocator_holder.hpp> +#include <jsoncons/json_array.hpp> + +namespace jsoncons { + + struct sorted_unique_range_tag + { + explicit sorted_unique_range_tag() = default; + }; + + // key_value + + template <class KeyT, class ValueT> + class key_value + { + public: + using key_type = KeyT; + using value_type = ValueT; + using allocator_type = typename ValueT::allocator_type; + using string_view_type = typename value_type::string_view_type; + private: + + key_type key_; + value_type value_; + public: + + key_value() noexcept + { + } + + key_value(const key_type& name, const value_type& val) + : key_(name), value_(val) + { + } + + key_value(const string_view_type& name) + : key_(name) + { + } + + template <typename... Args> + key_value(const key_type& name, Args&& ... args) + : key_(name), value_(std::forward<Args>(args)...) + { + } + + template <typename... Args> + key_value(key_type&& name, Args&& ... args) noexcept + : key_(std::forward<key_type>(name)), value_(std::forward<Args>(args)...) + { + } + + key_value(const key_value& member) + : key_(member.key_), value_(member.value_) + { + } + + key_value(key_value&& member) noexcept + : key_(std::move(member.key_)), value_(std::move(member.value_)) + { + } + template <typename... U1, typename... U2> + JSONCONS_CONSTEXPR key_value(std::piecewise_construct_t /*unused*/, std::tuple<U1...> a, + std::tuple<U2...> + b) noexcept(noexcept(key_value(std::declval<std::tuple<U1...>&>(), + std::declval<std::tuple<U2...>&>(), + jsoncons::type_traits::index_sequence_for<U1...>(), + jsoncons::type_traits::index_sequence_for<U2...>()))) + : key_value(a, b, jsoncons::type_traits::index_sequence_for<U1...>(), + jsoncons::type_traits::index_sequence_for<U2...>()) { + } + + template <typename... U1, size_t... I1, typename... U2, size_t... I2> + key_value(std::tuple<U1...>& a, std::tuple<U2...>& b, jsoncons::type_traits::index_sequence<I1...> /*unused*/, jsoncons::type_traits::index_sequence<I2...> /*unused*/) noexcept( + noexcept(KeyT(std::forward<U1>(std::get<I1>( + std::declval<std::tuple< + U1...>&>()))...)) && noexcept(ValueT(std:: + forward<U2>(std::get<I2>( + std::declval<std::tuple<U2...>&>()))...))) + : key_(std::forward<U1>(std::get<I1>(a))...) + , value_(std::forward<U2>(std::get<I2>(b))...) { + // make visual studio compiler happy about warning about unused a & b. + // Visual studio's key_value implementation disables warning 4100. + (void)a; + (void)b; + } + + const key_type& key() const + { + return key_; + } + + value_type& value() + { + return value_; + } + + const value_type& value() const + { + return value_; + } + + template <class T> + void value(T&& value) + { + value_ = std::forward<T>(value); + } + + void swap(key_value& member) noexcept + { + key_.swap(member.key_); + value_.swap(member.value_); + } + + key_value& operator=(const key_value& member) + { + if (this != & member) + { + key_ = member.key_; + value_ = member.value_; + } + return *this; + } + + key_value& operator=(key_value&& member) noexcept + { + if (this != &member) + { + key_.swap(member.key_); + value_.swap(member.value_); + } + return *this; + } + + void shrink_to_fit() + { + key_.shrink_to_fit(); + value_.shrink_to_fit(); + } + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use key()") + const key_type& name() const + { + return key_; + } + #endif + + friend bool operator==(const key_value& lhs, const key_value& rhs) noexcept + { + return lhs.key_ == rhs.key_ && lhs.value_ == rhs.value_; + } + + friend bool operator!=(const key_value& lhs, const key_value& rhs) noexcept + { + return !(lhs == rhs); + } + + friend bool operator<(const key_value& lhs, const key_value& rhs) noexcept + { + if (lhs.key_ < rhs.key_) + { + return true; + } + if (lhs.key_ == rhs.key_ && lhs.value_ < rhs.value_) + { + return true; + } + return false; + } + + friend bool operator<=(const key_value& lhs, const key_value& rhs) noexcept + { + return !(rhs < lhs); + } + + friend bool operator>(const key_value& lhs, const key_value& rhs) noexcept + { + return !(lhs <= rhs); + } + + friend bool operator>=(const key_value& lhs, const key_value& rhs) noexcept + { + return !(lhs < rhs); + } + + friend void swap(key_value& a, key_value& b) noexcept( + noexcept(std::declval<key_value&>().swap(std::declval<key_value&>()))) + { + a.swap(b); + } + }; + + template <class KeyT, class ValueT> + struct get_key_value + { + using key_value_type = key_value<KeyT,ValueT>; + + template <class T1,class T2> + key_value_type operator()(const std::pair<T1,T2>& p) + { + return key_value_type(p.first,p.second); + } + template <class T1,class T2> + key_value_type operator()(std::pair<T1,T2>&& p) + { + return key_value_type(std::forward<T1>(p.first),std::forward<T2>(p.second)); + } + template <class T1,class T2> + const key_value_type& operator()(const key_value<T1,T2>& p) + { + return p; + } + template <class T1,class T2> + key_value_type operator()(key_value<T1,T2>&& p) + { + return std::move(p); + } + }; + + struct sort_key_order + { + explicit sort_key_order() = default; + }; + + struct preserve_key_order + { + explicit preserve_key_order() = default; + }; + + + // Sort keys + template <class KeyT,class Json,template<typename,typename> class SequenceContainer = std::vector> + class sorted_json_object : public allocator_holder<typename Json::allocator_type> + { + public: + using allocator_type = typename Json::allocator_type; + using key_type = KeyT; + using key_value_type = key_value<KeyT,Json>; + using char_type = typename Json::char_type; + using string_view_type = typename Json::string_view_type; + private: + struct Comp + { + bool operator() (const key_value_type& kv, string_view_type k) const { return kv.key() < k; } + bool operator() (string_view_type k, const key_value_type& kv) const { return k < kv.key(); } + }; + + using key_value_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<key_value_type>; + using key_value_container_type = SequenceContainer<key_value_type,key_value_allocator_type>; + + key_value_container_type members_; + public: + using iterator = typename key_value_container_type::iterator; + using const_iterator = typename key_value_container_type::const_iterator; + + using allocator_holder<allocator_type>::get_allocator; + + sorted_json_object() + { + } + + explicit sorted_json_object(const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(key_value_allocator_type(alloc)) + { + } + + sorted_json_object(const sorted_json_object& val) + : allocator_holder<allocator_type>(val.get_allocator()), + members_(val.members_) + { + } + + sorted_json_object(sorted_json_object&& val) + : allocator_holder<allocator_type>(val.get_allocator()), + members_(std::move(val.members_)) + { + } + + sorted_json_object& operator=(const sorted_json_object& val) + { + allocator_holder<allocator_type>::operator=(val.get_allocator()); + members_ = val.members_; + return *this; + } + + sorted_json_object& operator=(sorted_json_object&& val) + { + val.swap(*this); + return *this; + } + + sorted_json_object(const sorted_json_object& val, const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(val.members_,key_value_allocator_type(alloc)) + { + } + + sorted_json_object(sorted_json_object&& val,const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), members_(std::move(val.members_),key_value_allocator_type(alloc)) + { + } + + template<class InputIt> + sorted_json_object(InputIt first, InputIt last) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value<KeyT,Json>()(*s)); + } + std::stable_sort(members_.begin(),members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); + auto it = std::unique(members_.begin(), members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));}); + members_.erase(it, members_.end()); + } + + template<class InputIt> + sorted_json_object(InputIt first, InputIt last, + const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(key_value_allocator_type(alloc)) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value<KeyT,Json>()(*s)); + } + std::stable_sort(members_.begin(),members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); + auto it = std::unique(members_.begin(), members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));}); + members_.erase(it, members_.end()); + } + + sorted_json_object(const std::initializer_list<std::pair<std::basic_string<char_type>,Json>>& init, + const allocator_type& alloc = allocator_type()) + : allocator_holder<allocator_type>(alloc), + members_(key_value_allocator_type(alloc)) + { + members_.reserve(init.size()); + for (auto& item : init) + { + insert_or_assign(item.first, item.second); + } + } + + ~sorted_json_object() noexcept + { + flatten_and_destroy(); + } + + bool empty() const + { + return members_.empty(); + } + + void swap(sorted_json_object& val) noexcept + { + members_.swap(val.members_); + } + + iterator begin() + { + return members_.begin(); + } + + iterator end() + { + return members_.end(); + } + + const_iterator begin() const + { + return members_.begin(); + } + + const_iterator end() const + { + return members_.end(); + } + + std::size_t size() const {return members_.size();} + + std::size_t capacity() const {return members_.capacity();} + + void clear() {members_.clear();} + + void shrink_to_fit() + { + for (std::size_t i = 0; i < members_.size(); ++i) + { + members_[i].shrink_to_fit(); + } + members_.shrink_to_fit(); + } + + void reserve(std::size_t n) {members_.reserve(n);} + + Json& at(std::size_t i) + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript")); + } + return members_[i].value(); + } + + const Json& at(std::size_t i) const + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript")); + } + return members_[i].value(); + } + + iterator find(const string_view_type& name) noexcept + { + auto p = std::equal_range(members_.begin(),members_.end(), name, + Comp()); + return p.first == p.second ? members_.end() : p.first; + } + + const_iterator find(const string_view_type& name) const noexcept + { + auto p = std::equal_range(members_.begin(),members_.end(), name, + Comp()); + return p.first == p.second ? members_.end() : p.first; + } + + iterator erase(const_iterator pos) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = members_.begin() + (pos - members_.begin()); + return members_.erase(it); + #else + return members_.erase(pos); + #endif + } + + iterator erase(const_iterator first, const_iterator last) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it1 = members_.begin() + (first - members_.begin()); + iterator it2 = members_.begin() + (last - members_.begin()); + return members_.erase(it1,it2); + #else + return members_.erase(first,last); + #endif + } + + void erase(const string_view_type& name) + { + auto it = find(name); + if (it != members_.end()) + { + members_.erase(it); + } + } + + template<class InputIt, class Convert> + void insert(InputIt first, InputIt last, Convert convert) + { + std::size_t count = std::distance(first,last); + members_.reserve(members_.size() + count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + std::stable_sort(members_.begin(),members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); + auto it = std::unique(members_.begin(), members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));}); + members_.erase(it, members_.end()); + } + + template<class InputIt, class Convert> + void insert(sorted_unique_range_tag, InputIt first, InputIt last, Convert convert) + { + if (first != last) + { + std::size_t count = std::distance(first,last); + members_.reserve(members_.size() + count); + + auto it = find(convert(*first).key()); + if (it != members_.end()) + { + for (auto s = first; s != last; ++s) + { + it = members_.emplace(it, convert(*s)); + } + } + else + { + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + } + } + } + + // insert_or_assign + + template <class T, class A=allocator_type> + typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward<T>(value)); + inserted = true; + it = members_.begin() + members_.size() - 1; + } + else if (it->key() == name) + { + it->value(Json(std::forward<T>(value))); + inserted = false; // assigned + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward<T>(value)); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template <class T, class A=allocator_type> + typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward<T>(value),get_allocator()); + inserted = true; + it = members_.begin() + members_.size() - 1; + } + else if (it->key() == name) + { + it->value(Json(std::forward<T>(value), get_allocator())); + inserted = false; // assigned + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward<T>(value),get_allocator()); + inserted = true; + } + return std::make_pair(it,inserted); + } + + // try_emplace + + template <class A=allocator_type, class... Args> + typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + try_emplace(const string_view_type& name, Args&&... args) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward<Args>(args)...); + it = members_.begin() + members_.size() - 1; + inserted = true; + } + else if (it->key() == name) + { + inserted = false; + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward<Args>(args)...); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template <class A=allocator_type, class... Args> + typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + try_emplace(const string_view_type& name, Args&&... args) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward<Args>(args)...); + it = members_.begin() + members_.size() - 1; + inserted = true; + } + else if (it->key() == name) + { + inserted = false; + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward<Args>(args)...); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template <class A=allocator_type, class ... Args> + typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type + try_emplace(iterator hint, const string_view_type& name, Args&&... args) + { + iterator it = hint; + + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + Comp()); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward<Args>(args)...); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward<Args>(args)...); + } + + return it; + } + + template <class A=allocator_type, class ... Args> + typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type + try_emplace(iterator hint, const string_view_type& name, Args&&... args) + { + iterator it = hint; + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + Comp()); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward<Args>(args)...); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward<Args>(args)...); + } + return it; + } + + // insert_or_assign + + template <class T, class A=allocator_type> + typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& name, T&& value) + { + iterator it; + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + [](const key_value_type& a, const string_view_type& k) -> bool {return string_view_type(a.key()).compare(k) < 0;}); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + [](const key_value_type& a, const string_view_type& k) -> bool {return string_view_type(a.key()).compare(k) < 0;}); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward<T>(value)); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + it->value(Json(std::forward<T>(value))); + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward<T>(value)); + } + return it; + } + + template <class T, class A=allocator_type> + typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& name, T&& value) + { + iterator it; + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + Comp()); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward<T>(value),get_allocator()); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + it->value(Json(std::forward<T>(value),get_allocator())); + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward<T>(value),get_allocator()); + } + return it; + } + + // merge + + void merge(const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + try_emplace(it->key(),it->value()); + } + } + + void merge(sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + if (pos == members_.end() ) + { + members_.emplace_back(*it); + } + else if ((*it).key() != pos->key()) + { + members_.emplace(pos,*it); + } + } + } + + void merge(iterator hint, const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = try_emplace(hint, (*it).key(),(*it).value()); + } + } + + void merge(iterator hint, sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + iterator pos; + if (hint != members_.end() && hint->key() <= (*it).key()) + { + pos = std::lower_bound(hint,members_.end(), (*it).key(), + Comp()); + } + else + { + pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + } + if (pos == members_.end() ) + { + members_.emplace_back(*it); + hint = members_.begin() + (members_.size() - 1); + } + else if ((*it).key() != pos->key()) + { + hint = members_.emplace(pos,*it); + } + } + } + + // merge_or_update + + void merge_or_update(const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + insert_or_assign((*it).key(),(*it).value()); + } + } + + void merge_or_update(sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + if (pos == members_.end() ) + { + members_.emplace_back(*it); + } + else + { + pos->value((*it).value()); + } + } + } + + void merge_or_update(iterator hint, const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = insert_or_assign(hint, (*it).key(),(*it).value()); + } + } + + void merge_or_update(iterator hint, sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + iterator pos; + if (hint != members_.end() && hint->key() <= (*it).key()) + { + pos = std::lower_bound(hint,members_.end(), (*it).key(), + Comp()); + } + else + { + pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + } + if (pos == members_.end() ) + { + members_.emplace_back(*it); + hint = members_.begin() + (members_.size() - 1); + } + else + { + pos->value((*it).value()); + hint = pos; + } + } + } + + bool operator==(const sorted_json_object& rhs) const + { + return members_ == rhs.members_; + } + + bool operator<(const sorted_json_object& rhs) const + { + return members_ < rhs.members_; + } + private: + + void flatten_and_destroy() noexcept + { + if (!members_.empty()) + { + json_array<Json> temp(get_allocator()); + + for (auto& kv : members_) + { + switch (kv.value().storage_kind()) + { + case json_storage_kind::array_value: + case json_storage_kind::object_value: + if (!kv.value().empty()) + { + temp.emplace_back(std::move(kv.value())); + } + break; + default: + break; + } + } + } + } + }; + + // Preserve order + template <class KeyT,class Json,template<typename,typename> class SequenceContainer = std::vector> + class order_preserving_json_object : public allocator_holder<typename Json::allocator_type> + { + public: + using allocator_type = typename Json::allocator_type; + using char_type = typename Json::char_type; + using key_type = KeyT; + //using mapped_type = Json; + using string_view_type = typename Json::string_view_type; + using key_value_type = key_value<KeyT,Json>; + //using implementation_policy = typename Json::implementation_policy; + private: + + using key_value_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<key_value_type>; + using key_value_container_type = SequenceContainer<key_value_type,key_value_allocator_type>; + typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<std::size_t> index_allocator_type; + //using index_container_type = typename implementation_policy::template sequence_container_type<std::size_t,index_allocator_type>; + using index_container_type = SequenceContainer<std::size_t,index_allocator_type>; + + key_value_container_type members_; + index_container_type index_; + + struct Comp + { + const key_value_container_type& members_; + + Comp(const key_value_container_type& members_) + : members_(members_) + { + } + + bool operator() (std::size_t i, string_view_type k) const { return members_.at(i).key() < k; } + bool operator() (string_view_type k, std::size_t i) const { return k < members_.at(i).key(); } + }; + public: + using iterator = typename key_value_container_type::iterator; + using const_iterator = typename key_value_container_type::const_iterator; + + using allocator_holder<allocator_type>::get_allocator; + + order_preserving_json_object() + { + } + order_preserving_json_object(const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(key_value_allocator_type(alloc)), + index_(index_allocator_type(alloc)) + { + } + + order_preserving_json_object(const order_preserving_json_object& val) + : allocator_holder<allocator_type>(val.get_allocator()), + members_(val.members_), + index_(val.index_) + { + } + + order_preserving_json_object(order_preserving_json_object&& val) + : allocator_holder<allocator_type>(val.get_allocator()), + members_(std::move(val.members_)), + index_(std::move(val.index_)) + { + } + + order_preserving_json_object(const order_preserving_json_object& val, const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(val.members_,key_value_allocator_type(alloc)), + index_(val.index_,index_allocator_type(alloc)) + { + } + + order_preserving_json_object(order_preserving_json_object&& val,const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(std::move(val.members_),key_value_allocator_type(alloc)), + index_(std::move(val.index_),index_allocator_type(alloc)) + { + } + + template<class InputIt> + order_preserving_json_object(InputIt first, InputIt last) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value<KeyT,Json>()(*s)); + } + + build_index(); + auto last_unique = std::unique(index_.begin(), index_.end(), + [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); }); + + if (last_unique != index_.end()) + { + index_.erase(last_unique, index_.end()); + std::sort(index_.begin(), index_.end()); + + auto result = index_.rbegin(); + if (*result != members_.size()) + { + members_.erase(members_.begin() + (*result + 1), members_.end()); + } + for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result) + { + if (*result - *it > 1) + { + members_.erase(members_.begin() + (*it + 1), members_.begin() + *result); + } + } + } + build_index(); + } + + template<class InputIt> + order_preserving_json_object(InputIt first, InputIt last, + const allocator_type& alloc) + : allocator_holder<allocator_type>(alloc), + members_(key_value_allocator_type(alloc)), + index_(index_allocator_type(alloc)) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value<KeyT,Json>()(*s)); + } + + build_index(); + auto last_unique = std::unique(index_.begin(), index_.end(), + [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); }); + + if (last_unique != index_.end()) + { + index_.erase(last_unique, index_.end()); + std::sort(index_.begin(), index_.end()); + + auto result = index_.rbegin(); + if (*result != members_.size()) + { + members_.erase(members_.begin() + (*result + 1), members_.end()); + } + for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result) + { + if (*result - *it > 1) + { + members_.erase(members_.begin() + (*it + 1), members_.begin() + *result); + } + } + } + build_index(); + } + + order_preserving_json_object(std::initializer_list<std::pair<std::basic_string<char_type>,Json>> init, + const allocator_type& alloc = allocator_type()) + : allocator_holder<allocator_type>(alloc), + members_(key_value_allocator_type(alloc)), + index_(index_allocator_type(alloc)) + { + members_.reserve(init.size()); + for (auto& item : init) + { + insert_or_assign(item.first, item.second); + } + } + + ~order_preserving_json_object() noexcept + { + flatten_and_destroy(); + } + + order_preserving_json_object& operator=(order_preserving_json_object&& val) + { + val.swap(*this); + return *this; + } + + order_preserving_json_object& operator=(const order_preserving_json_object& val) + { + allocator_holder<allocator_type>::operator=(val.get_allocator()); + members_ = val.members_; + index_ = val.index_; + return *this; + } + + void swap(order_preserving_json_object& val) noexcept + { + members_.swap(val.members_); + } + + bool empty() const + { + return members_.empty(); + } + + iterator begin() + { + return members_.begin(); + } + + iterator end() + { + return members_.end(); + } + + const_iterator begin() const + { + return members_.begin(); + } + + const_iterator end() const + { + return members_.end(); + } + + std::size_t size() const {return members_.size();} + + std::size_t capacity() const {return members_.capacity();} + + void clear() + { + members_.clear(); + index_.clear(); + } + + void shrink_to_fit() + { + for (std::size_t i = 0; i < members_.size(); ++i) + { + members_[i].shrink_to_fit(); + } + members_.shrink_to_fit(); + index_.shrink_to_fit(); + } + + void reserve(std::size_t n) {members_.reserve(n);} + + Json& at(std::size_t i) + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript")); + } + return members_[i].value(); + } + + const Json& at(std::size_t i) const + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript")); + } + return members_[i].value(); + } + + iterator find(const string_view_type& name) noexcept + { + auto p = std::equal_range(index_.begin(),index_.end(), name, + Comp(members_)); + return p.first == p.second ? members_.end() : members_.begin() + *p.first; + } + + const_iterator find(const string_view_type& name) const noexcept + { + auto p = std::equal_range(index_.begin(),index_.end(), name, + Comp(members_)); + return p.first == p.second ? members_.end() : members_.begin() + *p.first; + } + + iterator erase(const_iterator pos) + { + if (pos != members_.end()) + { + std::size_t pos1 = pos - members_.begin(); + std::size_t pos2 = pos1 + 1; + + erase_index_entries(pos1, pos2); + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = members_.begin() + (pos - members_.begin()); + return members_.erase(it); + #else + return members_.erase(pos); + #endif + } + else + { + return members_.end(); + } + } + + iterator erase(const_iterator first, const_iterator last) + { + std::size_t pos1 = first == members_.end() ? members_.size() : first - members_.begin(); + std::size_t pos2 = last == members_.end() ? members_.size() : last - members_.begin(); + + if (pos1 < members_.size() && pos2 <= members_.size()) + { + erase_index_entries(pos1,pos2); + + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it1 = members_.begin() + (first - members_.begin()); + iterator it2 = members_.begin() + (last - members_.begin()); + return members_.erase(it1,it2); + #else + return members_.erase(first,last); + #endif + } + else + { + return members_.end(); + } + } + + void erase(const string_view_type& name) + { + auto pos = find(name); + if (pos != members_.end()) + { + std::size_t pos1 = pos - members_.begin(); + std::size_t pos2 = pos1 + 1; + + erase_index_entries(pos1, pos2); + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = members_.begin() + (pos - members_.begin()); + members_.erase(it); + #else + members_.erase(pos); + #endif + } + } + + template<class InputIt, class Convert> + void insert(InputIt first, InputIt last, Convert convert) + { + std::size_t count = std::distance(first,last); + members_.reserve(members_.size() + count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + + build_index(); + auto last_unique = std::unique(index_.begin(), index_.end(), + [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); }); + + if (last_unique != index_.end()) + { + index_.erase(last_unique, index_.end()); + std::sort(index_.begin(), index_.end()); + + auto result = index_.rbegin(); + if (*result != members_.size()) + { + members_.erase(members_.begin() + (*result + 1), members_.end()); + } + for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result) + { + if (*result - *it > 1) + { + members_.erase(members_.begin() + (*it + 1), members_.begin() + *result); + } + } + } + build_index(); + } + + template<class InputIt, class Convert> + void insert(sorted_unique_range_tag, InputIt first, InputIt last, Convert convert) + { + std::size_t count = std::distance(first,last); + + members_.reserve(members_.size() + count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + + build_index(); + } + + template <class T, class A=allocator_type> + typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + insert_or_assign(const string_view_type& name, T&& value) + { + auto result = insert_index_entry(name,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(name.begin(), name.end()), std::forward<T>(value)); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward<T>(value))); + return std::make_pair(it,false); + } + } + + template <class T, class A=allocator_type> + typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + insert_or_assign(const string_view_type& name, T&& value) + { + auto result = insert_index_entry(name,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(name.begin(),name.end(),get_allocator()), + std::forward<T>(value),get_allocator()); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward<T>(value),get_allocator())); + return std::make_pair(it,false); + } + } + + template <class A=allocator_type, class T> + typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& key, T&& value) + { + if (hint == members_.end()) + { + auto result = insert_or_assign(key, std::forward<T>(value)); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key,pos); + + if (result.second) + { + auto it = members_.emplace(hint, key_type(key.begin(), key.end()), std::forward<T>(value)); + return it; + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward<T>(value))); + return it; + } + } + } + + template <class A=allocator_type, class T> + typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& key, T&& value) + { + if (hint == members_.end()) + { + auto result = insert_or_assign(key, std::forward<T>(value)); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key,pos); + + if (result.second) + { + auto it = members_.emplace(hint, + key_type(key.begin(),key.end(),get_allocator()), + std::forward<T>(value),get_allocator()); + return it; + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward<T>(value),get_allocator())); + return it; + } + } + } + + // merge + + void merge(const order_preserving_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + try_emplace((*it).key(),(*it).value()); + } + } + + void merge(order_preserving_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = find((*it).key()); + if (pos == members_.end() ) + { + try_emplace((*it).key(),std::move((*it).value())); + } + } + } + + void merge(iterator hint, const order_preserving_json_object& source) + { + std::size_t pos = hint - members_.begin(); + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = try_emplace(hint, (*it).key(),(*it).value()); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + void merge(iterator hint, order_preserving_json_object&& source) + { + std::size_t pos = hint - members_.begin(); + + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + hint = try_emplace(hint, (*it).key(), std::move((*it).value())); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + // merge_or_update + + void merge_or_update(const order_preserving_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + insert_or_assign((*it).key(),(*it).value()); + } + } + + void merge_or_update(order_preserving_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = find((*it).key()); + if (pos == members_.end() ) + { + insert_or_assign((*it).key(),std::move((*it).value())); + } + else + { + pos->value(std::move((*it).value())); + } + } + } + + void merge_or_update(iterator hint, const order_preserving_json_object& source) + { + std::size_t pos = hint - members_.begin(); + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = insert_or_assign(hint, (*it).key(),(*it).value()); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + void merge_or_update(iterator hint, order_preserving_json_object&& source) + { + std::size_t pos = hint - members_.begin(); + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + hint = insert_or_assign(hint, (*it).key(), std::move((*it).value())); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + // try_emplace + + template <class A=allocator_type, class... Args> + typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + try_emplace(const string_view_type& name, Args&&... args) + { + auto result = insert_index_entry(name,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(name.begin(), name.end()), std::forward<Args>(args)...); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + return std::make_pair(it,false); + } + } + + template <class A=allocator_type, class... Args> + typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type + try_emplace(const string_view_type& key, Args&&... args) + { + auto result = insert_index_entry(key,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(key.begin(),key.end(), get_allocator()), + std::forward<Args>(args)...); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + return std::make_pair(it,false); + } + } + + template <class A=allocator_type, class ... Args> + typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type + try_emplace(iterator hint, const string_view_type& key, Args&&... args) + { + if (hint == members_.end()) + { + auto result = try_emplace(key, std::forward<Args>(args)...); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key, pos); + + if (result.second) + { + auto it = members_.emplace(hint, key_type(key.begin(), key.end()), std::forward<Args>(args)...); + return it; + } + else + { + auto it = members_.begin() + result.first; + return it; + } + } + } + + template <class A=allocator_type, class ... Args> + typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type + try_emplace(iterator hint, const string_view_type& key, Args&&... args) + { + if (hint == members_.end()) + { + auto result = try_emplace(key, std::forward<Args>(args)...); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key, pos); + + if (result.second) + { + auto it = members_.emplace(hint, + key_type(key.begin(),key.end(), get_allocator()), + std::forward<Args>(args)...); + return it; + } + else + { + auto it = members_.begin() + result.first; + return it; + } + } + } + + bool operator==(const order_preserving_json_object& rhs) const + { + return members_ == rhs.members_; + } + + bool operator<(const order_preserving_json_object& rhs) const + { + return members_ < rhs.members_; + } + private: + + void flatten_and_destroy() noexcept + { + if (!members_.empty()) + { + json_array<Json> temp(get_allocator()); + + for (auto&& kv : members_) + { + if (kv.value().size() > 0) + { + temp.emplace_back(std::move(kv.value())); + } + } + } + } + + std::pair<std::size_t,bool> insert_index_entry(const string_view_type& key, std::size_t pos) + { + JSONCONS_ASSERT(pos <= index_.size()); + + auto it = std::lower_bound(index_.begin(),index_.end(), key, + Comp(members_)); + + if (it == index_.end()) + { + std::size_t count = index_.size() - pos; + for (std::size_t i = 0; count > 0 && i < index_.size(); ++i) + { + if (index_[i] >= pos) + { + ++index_[i]; + --count; + } + } + index_.push_back(pos); + return std::make_pair(pos,true); + } + else if (members_.at(*it).key() != key) + { + std::size_t count = index_.size() - pos; + for (std::size_t i = 0; count > 0 && i < index_.size(); ++i) + { + if (index_[i] >= pos) + { + ++index_[i]; + --count; + } + } + auto it2 = index_.insert(it, pos); + return std::make_pair(*it2,true); + } + else + { + return std::make_pair(*it,false); + } + } + + void erase_index_entries(std::size_t pos1, std::size_t pos2) + { + JSONCONS_ASSERT(pos1 <= pos2); + JSONCONS_ASSERT(pos2 <= index_.size()); + + const size_t offset = pos2 - pos1; + const size_t n = index_.size() - offset; + for (std::size_t i = 0; i < index_.size(); ++i) + { + if (index_[i] >= pos1 && index_[i] < pos2) + { + index_.erase(index_.begin()+i); + --i; + } + } + for (std::size_t i = 0; i < index_.size(); ++i) + { + if (index_[i] >= pos2) + { + index_[i] -= offset; + } + } + JSONCONS_ASSERT(index_.size() == n); + } + + void build_index() + { + index_.clear(); + index_.reserve(members_.size()); + for (std::size_t i = 0; i < members_.size(); ++i) + { + index_.push_back(i); + } + std::stable_sort(index_.begin(),index_.end(), + [&](std::size_t a, std::size_t b) -> bool {return members_.at(a).key().compare(members_.at(b).key()) < 0;}); + } + }; + +} // namespace jsoncons + +#endif |