// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.11.3 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once #include // equal_to, less #include // initializer_list #include // input_iterator_tag, iterator_traits #include // allocator #include // for out_of_range #include // enable_if, is_convertible #include // pair #include // vector #include #include NLOHMANN_JSON_NAMESPACE_BEGIN /// ordered_map: a minimal map-like container that preserves insertion order /// for use within nlohmann::basic_json template , class Allocator = std::allocator>> struct ordered_map : std::vector, Allocator> { using key_type = Key; using mapped_type = T; using Container = std::vector, Allocator>; using iterator = typename Container::iterator; using const_iterator = typename Container::const_iterator; using size_type = typename Container::size_type; using value_type = typename Container::value_type; #ifdef JSON_HAS_CPP_14 using key_compare = std::equal_to<>; #else using key_compare = std::equal_to; #endif // Explicit constructors instead of `using Container::Container` // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) ordered_map() noexcept(noexcept(Container())) : Container{} {} explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {} template ordered_map(It first, It last, const Allocator& alloc = Allocator()) : Container{first, last, alloc} {} ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) : Container{init, alloc} {} std::pair emplace(const key_type& key, T&& t) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return {it, false}; } } Container::emplace_back(key, std::forward(t)); return {std::prev(this->end()), true}; } template::value, int> = 0> std::pair emplace(KeyType && key, T && t) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return {it, false}; } } Container::emplace_back(std::forward(key), std::forward(t)); return {std::prev(this->end()), true}; } T& operator[](const key_type& key) { return emplace(key, T{}).first->second; } template::value, int> = 0> T & operator[](KeyType && key) { return emplace(std::forward(key), T{}).first->second; } const T& operator[](const key_type& key) const { return at(key); } template::value, int> = 0> const T & operator[](KeyType && key) const { return at(std::forward(key)); } T& at(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it->second; } } JSON_THROW(std::out_of_range("key not found")); } template::value, int> = 0> T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it->second; } } JSON_THROW(std::out_of_range("key not found")); } const T& at(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it->second; } } JSON_THROW(std::out_of_range("key not found")); } template::value, int> = 0> const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it->second; } } JSON_THROW(std::out_of_range("key not found")); } size_type erase(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { // Since we cannot move const Keys, re-construct them in place for (auto next = it; ++next != this->end(); ++it) { it->~value_type(); // Destroy but keep allocation new (&*it) value_type{std::move(*next)}; } Container::pop_back(); return 1; } } return 0; } template::value, int> = 0> size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { // Since we cannot move const Keys, re-construct them in place for (auto next = it; ++next != this->end(); ++it) { it->~value_type(); // Destroy but keep allocation new (&*it) value_type{std::move(*next)}; } Container::pop_back(); return 1; } } return 0; } iterator erase(iterator pos) { return erase(pos, std::next(pos)); } iterator erase(iterator first, iterator last) { if (first == last) { return first; } const auto elements_affected = std::distance(first, last); const auto offset = std::distance(Container::begin(), first); // This is the start situation. We need to delete elements_affected // elements (3 in this example: e, f, g), and need to return an // iterator past the last deleted element (h in this example). // Note that offset is the distance from the start of the vector // to first. We will need this later. // [ a, b, c, d, e, f, g, h, i, j ] // ^ ^ // first last // Since we cannot move const Keys, we re-construct them in place. // We start at first and re-construct (viz. copy) the elements from // the back of the vector. Example for first iteration: // ,--------. // v | destroy e and re-construct with h // [ a, b, c, d, e, f, g, h, i, j ] // ^ ^ // it it + elements_affected for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) { it->~value_type(); // destroy but keep allocation new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it } // [ a, b, c, d, h, i, j, h, i, j ] // ^ ^ // first last // remove the unneeded elements at the end of the vector Container::resize(this->size() - static_cast(elements_affected)); // [ a, b, c, d, h, i, j ] // ^ ^ // first last // first is now pointing past the last deleted element, but we cannot // use this iterator, because it may have been invalidated by the // resize call. Instead, we can return begin() + offset. return Container::begin() + offset; } size_type count(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return 1; } } return 0; } template::value, int> = 0> size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return 1; } } return 0; } iterator find(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it; } } return Container::end(); } template::value, int> = 0> iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it; } } return Container::end(); } const_iterator find(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, key)) { return it; } } return Container::end(); } std::pair insert( value_type&& value ) { return emplace(value.first, std::move(value.second)); } std::pair insert( const value_type& value ) { for (auto it = this->begin(); it != this->end(); ++it) { if (m_compare(it->first, value.first)) { return {it, false}; } } Container::push_back(value); return {--this->end(), true}; } template using require_input_iter = typename std::enable_if::iterator_category, std::input_iterator_tag>::value>::type; template> void insert(InputIt first, InputIt last) { for (auto it = first; it != last; ++it) { insert(*it); } } private: JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare(); }; NLOHMANN_JSON_NAMESPACE_END