diff --git a/3rdparty/picojson/picojson.h b/3rdparty/picojson/picojson.h index 24bb17072eda..542b527ca7d9 100644 --- a/3rdparty/picojson/picojson.h +++ b/3rdparty/picojson/picojson.h @@ -26,12 +26,21 @@ * POSSIBILITY OF SUCH DAMAGE. */ #pragma once + #ifndef PICOJSON_USE_INT64 #define PICOJSON_USE_INT64 #define __STDC_FORMAT_MACROS 1 #endif +// If PICOJSON_USE_ORDERED_OBJECT is set, picojson uses object_with_ordered_keys, which maintains +// the insertion order of keys, i.e. the order of keys in the json string. +// This macro is set by default. +#ifndef PICOJSON_USE_ORDERED_OBJECT +#define PICOJSON_USE_ORDERED_OBJECT 1 +#endif + #include +#include #include #include #include @@ -137,10 +146,17 @@ enum { INDENT_WIDTH = 2 }; struct null {}; +class object_with_ordered_keys; + class value { public: typedef std::vector array; +#ifdef PICOJSON_USE_ORDERED_OBJECT + typedef object_with_ordered_keys object; +#else typedef std::unordered_map object; +#endif + union _storage { bool boolean_; double number_; @@ -220,6 +236,92 @@ class value { void clear(); }; +// The ordered version of hashmap. It has the same interface as std::unordered_map, but provides +// ordered_keys() to return the keys in the order they were inserted. +class object_with_ordered_keys : private std::unordered_map { + public: + using typename std::unordered_map::value_type; + using typename std::unordered_map::iterator; + using typename std::unordered_map::const_iterator; + + object_with_ordered_keys() = default; + object_with_ordered_keys(const object_with_ordered_keys&) = default; + object_with_ordered_keys(object_with_ordered_keys&&) = default; + object_with_ordered_keys(std::initializer_list init) + : std::unordered_map(init) { + for (const auto& pair : init) { + ordered_keys_.push_back(pair.first); + } + } + object_with_ordered_keys& operator=(const object_with_ordered_keys&) = default; + object_with_ordered_keys& operator=(object_with_ordered_keys&&) = default; + + using std::unordered_map::begin; + using std::unordered_map::end; + using std::unordered_map::cbegin; + using std::unordered_map::cend; + using std::unordered_map::empty; + using std::unordered_map::size; + using std::unordered_map::at; + using std::unordered_map::count; + using std::unordered_map::find; + + value& operator[](const std::string& key) { + if (count(key) == 0) { + ordered_keys_.push_back(key); + } + return std::unordered_map::operator[](key); + } + + void clear() { + std::unordered_map::clear(); + ordered_keys_.clear(); + } + + std::pair insert(const value_type& kv) { + if (!count(kv.first)) { + ordered_keys_.push_back(kv.first); + } + return std::unordered_map::insert(kv); + } + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + iterator erase(const_iterator it) { + ordered_keys_.erase(std::find(ordered_keys_.begin(), ordered_keys_.end(), it->first)); + return std::unordered_map::erase(it); + } + + iterator erase(iterator it) { + ordered_keys_.erase(std::find(ordered_keys_.begin(), ordered_keys_.end(), it->first)); + return std::unordered_map::erase(it); + } + + size_t erase(const std::string& key) { + if (std::unordered_map::erase(key)) { + ordered_keys_.erase(std::find(ordered_keys_.begin(), ordered_keys_.end(), key)); + return 1; + } else { + return 0; + } + } + + const std::vector& ordered_keys() const { return ordered_keys_; } + + friend bool operator==(const object_with_ordered_keys& lhs, const object_with_ordered_keys& rhs); + + private: + std::vector ordered_keys_; +}; + +inline bool operator==(const object_with_ordered_keys& lhs, const object_with_ordered_keys& rhs) { + return static_cast&>(lhs) == + static_cast&>(rhs); +} + typedef value::array array; typedef value::object object; diff --git a/3rdparty/picojson/test_picojson.cpp b/3rdparty/picojson/test_picojson.cpp new file mode 100644 index 000000000000..b648702b4bbb --- /dev/null +++ b/3rdparty/picojson/test_picojson.cpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include + +#include "picojson.h" + +using picojson::object_with_ordered_keys; + +void test_constructor() { + object_with_ordered_keys obj; + obj["foo"] = picojson::value(true); + assert((obj.ordered_keys() == std::vector{"foo"})); + + object_with_ordered_keys obj1{{"foo", picojson::value(true)}, {"bar", picojson::value(false)}}; + assert((obj1.ordered_keys() == std::vector{"foo", "bar"})); + + object_with_ordered_keys obj2(obj1); + assert((obj2.ordered_keys() == std::vector{"foo", "bar"})); + + object_with_ordered_keys obj3(std::move(obj2)); + assert((obj3.ordered_keys() == std::vector{"foo", "bar"})); + + obj = obj3; + assert((obj.ordered_keys() == std::vector{"foo", "bar"})); +} + +void test_modifier() { + object_with_ordered_keys obj{{"foo", picojson::value(true)}, {"bar", picojson::value(false)}}; + obj.insert({"abc", picojson::value(false)}); + assert((obj.ordered_keys() == std::vector{"foo", "bar", "abc"})); + obj.emplace("def", picojson::value(true)); + assert((obj.ordered_keys() == std::vector{"foo", "bar", "abc", "def"})); + obj.insert({"abc", picojson::value(true)}); + assert((obj.ordered_keys() == std::vector{"foo", "bar", "abc", "def"})); + auto it = obj.find("abc"); + it = obj.erase(it); + assert((obj.ordered_keys() == std::vector{"foo", "bar", "def"})); + obj.erase("foo"); + assert((obj.ordered_keys() == std::vector{"bar", "def"})); + obj.clear(); + assert((obj.ordered_keys() == std::vector{})); +} + +int main() { + test_constructor(); + test_modifier(); + return 0; +}