Squashed 'third_party/allwpilib_2019/' content from commit bd05dfa1c

Change-Id: I2b1c2250cdb9b055133780c33593292098c375b7
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: bd05dfa1c7cca74c4fac451e7b9d6a37e7b53447
diff --git a/wpiutil/src/main/native/cpp/json.cpp b/wpiutil/src/main/native/cpp/json.cpp
new file mode 100644
index 0000000..9becfdd
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json.cpp
@@ -0,0 +1,1493 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved.          */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project.                                                               */
+/*----------------------------------------------------------------------------*/
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++
+|  |  |__   |  |  | | | |  version 3.1.2
+|_____|_____|_____|_|___|  https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby  granted, free of charge, to any  person obtaining a copy
+of this software and associated  documentation files (the "Software"), to deal
+in the Software  without restriction, including without  limitation the rights
+to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
+copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
+IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
+FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
+AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
+LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+namespace detail {
+
+exception::exception(int id_, const Twine& what_arg)
+    : id(id_), m(what_arg.str()) {}
+
+parse_error parse_error::create(int id_, std::size_t byte_, const Twine& what_arg)
+{
+    return parse_error(id_, byte_, "[json.exception.parse_error." + Twine(id_) + "] parse error" +
+                    (byte_ != 0 ? (" at " + Twine(byte_)) : Twine("")) +
+                    ": " + what_arg);
+}
+
+invalid_iterator invalid_iterator::create(int id_, const Twine& what_arg)
+{
+    return invalid_iterator(id_, "[json.exception.invalid_iterator." + Twine(id_) + "] " + what_arg);
+}
+
+type_error type_error::create(int id_, const Twine& what_arg)
+{
+    return type_error(id_, "[json.exception.type_error." + Twine(id_) + "] " + what_arg);
+}
+
+out_of_range out_of_range::create(int id_, const Twine& what_arg)
+{
+    return out_of_range(id_, "[json.exception.out_of_range." + Twine(id_) + "] " + what_arg);
+}
+
+other_error other_error::create(int id_, const Twine& what_arg)
+{
+    return other_error(id_, "[json.exception.other_error." + Twine(id_) + "] " + what_arg);
+}
+
+}  // namespace detail
+
+json::json_value::json_value(value_t t)
+{
+    switch (t)
+    {
+        case value_t::object:
+        {
+            object = create<object_t>();
+            break;
+        }
+
+        case value_t::array:
+        {
+            array = create<array_t>();
+            break;
+        }
+
+        case value_t::string:
+        {
+            string = create<std::string>("");
+            break;
+        }
+
+        case value_t::boolean:
+        {
+            boolean = false;
+            break;
+        }
+
+        case value_t::number_integer:
+        {
+            number_integer = 0;
+            break;
+        }
+
+        case value_t::number_unsigned:
+        {
+            number_unsigned = 0u;
+            break;
+        }
+
+        case value_t::number_float:
+        {
+            number_float = 0.0;
+            break;
+        }
+
+        case value_t::null:
+        {
+            object = nullptr;  // silence warning, see #821
+            break;
+        }
+
+        default:
+        {
+            object = nullptr;  // silence warning, see #821
+            if (JSON_UNLIKELY(t == value_t::null))
+            {
+                JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.2")); // LCOV_EXCL_LINE
+            }
+            break;
+        }
+    }
+}
+
+void json::json_value::destroy(value_t t) noexcept
+{
+    switch (t)
+    {
+        case value_t::object:
+        {
+            std::allocator<object_t> alloc;
+            alloc.destroy(object);
+            alloc.deallocate(object, 1);
+            break;
+        }
+
+        case value_t::array:
+        {
+            std::allocator<array_t> alloc;
+            alloc.destroy(array);
+            alloc.deallocate(array, 1);
+            break;
+        }
+
+        case value_t::string:
+        {
+            std::allocator<std::string> alloc;
+            alloc.destroy(string);
+            alloc.deallocate(string, 1);
+            break;
+        }
+
+        default:
+        {
+            break;
+        }
+    }
+}
+
+json::json(initializer_list_t init,
+           bool type_deduction,
+           value_t manual_type)
+{
+    // check if each element is an array with two elements whose first
+    // element is a string
+    bool is_an_object = std::all_of(init.begin(), init.end(),
+                                    [](const detail::json_ref<json>& element_ref)
+    {
+        return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
+    });
+
+    // adjust type if type deduction is not wanted
+    if (not type_deduction)
+    {
+        // if array is wanted, do not create an object though possible
+        if (manual_type == value_t::array)
+        {
+            is_an_object = false;
+        }
+
+        // if object is wanted but impossible, throw an exception
+        if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
+        {
+            JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
+        }
+    }
+
+    if (is_an_object)
+    {
+        // the initializer list is a list of pairs -> create object
+        m_type = value_t::object;
+        m_value = value_t::object;
+
+        std::for_each(init.begin(), init.end(), [this](const detail::json_ref<json>& element_ref)
+        {
+            auto element = element_ref.moved_or_copied();
+            m_value.object->try_emplace(
+                *((*element.m_value.array)[0].m_value.string),
+                std::move((*element.m_value.array)[1]));
+        });
+    }
+    else
+    {
+        // the initializer list describes an array -> create array
+        m_type = value_t::array;
+        m_value.array = create<array_t>(init.begin(), init.end());
+    }
+
+    assert_invariant();
+}
+
+json::json(size_type cnt, const json& val)
+    : m_type(value_t::array)
+{
+    m_value.array = create<array_t>(cnt, val);
+    assert_invariant();
+}
+
+json::json(const json& other)
+    : m_type(other.m_type)
+{
+    // check of passed value is valid
+    other.assert_invariant();
+
+    switch (m_type)
+    {
+        case value_t::object:
+        {
+            m_value = *other.m_value.object;
+            break;
+        }
+
+        case value_t::array:
+        {
+            m_value = *other.m_value.array;
+            break;
+        }
+
+        case value_t::string:
+        {
+            m_value = *other.m_value.string;
+            break;
+        }
+
+        case value_t::boolean:
+        {
+            m_value = other.m_value.boolean;
+            break;
+        }
+
+        case value_t::number_integer:
+        {
+            m_value = other.m_value.number_integer;
+            break;
+        }
+
+        case value_t::number_unsigned:
+        {
+            m_value = other.m_value.number_unsigned;
+            break;
+        }
+
+        case value_t::number_float:
+        {
+            m_value = other.m_value.number_float;
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    assert_invariant();
+}
+
+json json::meta()
+{
+    json result;
+
+    result["copyright"] = "(C) 2013-2017 Niels Lohmann, (C) 2017-2018 FIRST";
+    result["name"] = "WPI version of JSON for Modern C++";
+    result["url"] = "https://github.com/wpilibsuite/allwpilib";
+    result["version"]["string"] =
+        std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
+        std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
+        std::to_string(NLOHMANN_JSON_VERSION_PATCH);
+    result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+    result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+    result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
+
+#ifdef _WIN32
+    result["platform"] = "win32";
+#elif defined __linux__
+    result["platform"] = "linux";
+#elif defined __APPLE__
+    result["platform"] = "apple";
+#elif defined __unix__
+    result["platform"] = "unix";
+#else
+    result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+    result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+    result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+    result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
+#elif defined(__HP_cc) || defined(__HP_aCC)
+    result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+    result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+    result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+    result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+    result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+    result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#ifdef __cplusplus
+    result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+    result["compiler"]["c++"] = "unknown";
+#endif
+    return result;
+}
+
+json::reference json::at(size_type idx)
+{
+    // at only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        JSON_TRY
+        {
+            return m_value.array->at(idx);
+        }
+        JSON_CATCH (std::out_of_range&)
+        {
+            // create better exception explanation
+            JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+        }
+    }
+    else
+    {
+        JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+    }
+}
+
+json::const_reference json::at(size_type idx) const
+{
+    // at only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        JSON_TRY
+        {
+            return m_value.array->at(idx);
+        }
+        JSON_CATCH (std::out_of_range&)
+        {
+            // create better exception explanation
+            JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+        }
+    }
+    else
+    {
+        JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+    }
+}
+
+json::reference json::at(StringRef key)
+{
+    // at only works for objects
+    if (JSON_LIKELY(is_object()))
+    {
+        auto it = m_value.object->find(key);
+        if (it == m_value.object->end())
+        {
+            // create better exception explanation
+            JSON_THROW(out_of_range::create(403, "key '" + Twine(key) + "' not found"));
+        }
+        return it->second;
+    }
+    else
+    {
+        JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+    }
+}
+
+json::const_reference json::at(StringRef key) const
+{
+    // at only works for objects
+    if (JSON_LIKELY(is_object()))
+    {
+        auto it = m_value.object->find(key);
+        if (it == m_value.object->end())
+        {
+            // create better exception explanation
+            JSON_THROW(out_of_range::create(403, "key '" + Twine(key) + "' not found"));
+        }
+        return it->second;
+    }
+    else
+    {
+        JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+    }
+}
+
+json::reference json::operator[](size_type idx)
+{
+    // implicitly convert null value to an empty array
+    if (is_null())
+    {
+        m_type = value_t::array;
+        m_value.array = create<array_t>();
+        assert_invariant();
+    }
+
+    // operator[] only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        // fill up array with null values if given idx is outside range
+        if (idx >= m_value.array->size())
+        {
+            m_value.array->insert(m_value.array->end(),
+                                  idx - m_value.array->size() + 1,
+                                  json());
+        }
+
+        return m_value.array->operator[](idx);
+    }
+
+    JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::const_reference json::operator[](size_type idx) const
+{
+    // const operator[] only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        return m_value.array->operator[](idx);
+    }
+
+    JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::reference json::operator[](StringRef key)
+{
+    // implicitly convert null value to an empty object
+    if (is_null())
+    {
+        m_type = value_t::object;
+        m_value.object = create<object_t>();
+        assert_invariant();
+    }
+
+    // operator[] only works for objects
+    if (JSON_LIKELY(is_object()))
+    {
+        return m_value.object->operator[](key);
+    }
+
+    JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::const_reference json::operator[](StringRef key) const
+{
+    // const operator[] only works for objects
+    if (JSON_LIKELY(is_object()))
+    {
+        assert(m_value.object->find(key) != m_value.object->end());
+        return m_value.object->find(key)->second;
+    }
+
+    JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::size_type json::erase(StringRef key)
+{
+    // this erase only works for objects
+    if (JSON_LIKELY(is_object()))
+    {
+        return m_value.object->erase(key);
+    }
+
+    JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+}
+
+void json::erase(const size_type idx)
+{
+    // this erase only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        if (JSON_UNLIKELY(idx >= size()))
+        {
+            JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+        }
+
+        m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
+    }
+    else
+    {
+        JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+    }
+}
+
+json::iterator json::find(StringRef key)
+{
+    auto result = end();
+
+    if (is_object())
+    {
+        result.m_it.object_iterator = m_value.object->find(key);
+    }
+
+    return result;
+}
+
+json::const_iterator json::find(StringRef key) const
+{
+    auto result = cend();
+
+    if (is_object())
+    {
+        result.m_it.object_iterator = m_value.object->find(key);
+    }
+
+    return result;
+}
+
+json::size_type json::count(StringRef key) const
+{
+    // return 0 for all nonobject types
+    return is_object() ? m_value.object->count(key) : 0;
+}
+
+bool json::empty() const noexcept
+{
+    switch (m_type)
+    {
+        case value_t::null:
+        {
+            // null values are empty
+            return true;
+        }
+
+        case value_t::array:
+        {
+            // delegate call to array_t::empty()
+            return m_value.array->empty();
+        }
+
+        case value_t::object:
+        {
+            // delegate call to object_t::empty()
+            return m_value.object->empty();
+        }
+
+        default:
+        {
+            // all other types are nonempty
+            return false;
+        }
+    }
+}
+
+json::size_type json::size() const noexcept
+{
+    switch (m_type)
+    {
+        case value_t::null:
+        {
+            // null values are empty
+            return 0;
+        }
+
+        case value_t::array:
+        {
+            // delegate call to array_t::size()
+            return m_value.array->size();
+        }
+
+        case value_t::object:
+        {
+            // delegate call to object_t::size()
+            return m_value.object->size();
+        }
+
+        default:
+        {
+            // all other types have size 1
+            return 1;
+        }
+    }
+}
+
+json::size_type json::max_size() const noexcept
+{
+    switch (m_type)
+    {
+        case value_t::array:
+        {
+            // delegate call to array_t::max_size()
+            return m_value.array->max_size();
+        }
+
+        case value_t::object:
+        {
+            // delegate call to std::allocator<json>::max_size()
+            return std::allocator<json>().max_size();
+        }
+
+        default:
+        {
+            // all other types have max_size() == size()
+            return size();
+        }
+    }
+}
+
+void json::clear() noexcept
+{
+    switch (m_type)
+    {
+        case value_t::number_integer:
+        {
+            m_value.number_integer = 0;
+            break;
+        }
+
+        case value_t::number_unsigned:
+        {
+            m_value.number_unsigned = 0;
+            break;
+        }
+
+        case value_t::number_float:
+        {
+            m_value.number_float = 0.0;
+            break;
+        }
+
+        case value_t::boolean:
+        {
+            m_value.boolean = false;
+            break;
+        }
+
+        case value_t::string:
+        {
+            m_value.string->clear();
+            break;
+        }
+
+        case value_t::array:
+        {
+            m_value.array->clear();
+            break;
+        }
+
+        case value_t::object:
+        {
+            m_value.object->clear();
+            break;
+        }
+
+        default:
+            break;
+    }
+}
+
+void json::push_back(json&& val)
+{
+    // push_back only works for null objects or arrays
+    if (JSON_UNLIKELY(not(is_null() or is_array())))
+    {
+        JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+    }
+
+    // transform null object into an array
+    if (is_null())
+    {
+        m_type = value_t::array;
+        m_value = value_t::array;
+        assert_invariant();
+    }
+
+    // add element to array (move semantics)
+    m_value.array->push_back(std::move(val));
+    // invalidate object
+    val.m_type = value_t::null;
+}
+
+void json::push_back(const json& val)
+{
+    // push_back only works for null objects or arrays
+    if (JSON_UNLIKELY(not(is_null() or is_array())))
+    {
+        JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+    }
+
+    // transform null object into an array
+    if (is_null())
+    {
+        m_type = value_t::array;
+        m_value = value_t::array;
+        assert_invariant();
+    }
+
+    // add element to array
+    m_value.array->push_back(val);
+}
+
+void json::push_back(initializer_list_t init)
+{
+    if (is_object() and init.size() == 2 and (*init.begin())->is_string())
+    {
+        std::string key = init.begin()->moved_or_copied();
+        push_back(std::pair<StringRef, json>(key, (init.begin() + 1)->moved_or_copied()));
+    }
+    else
+    {
+        push_back(json(init));
+    }
+}
+
+json::iterator json::insert(const_iterator pos, const json& val)
+{
+    // insert only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        // check if iterator pos fits to this JSON value
+        if (JSON_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
+        return result;
+    }
+
+    JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+}
+
+json::iterator json::insert(const_iterator pos, size_type cnt, const json& val)
+{
+    // insert only works for arrays
+    if (JSON_LIKELY(is_array()))
+    {
+        // check if iterator pos fits to this JSON value
+        if (JSON_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+        return result;
+    }
+
+    JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+}
+
+json::iterator json::insert(const_iterator pos, const_iterator first, const_iterator last)
+{
+    // insert only works for arrays
+    if (JSON_UNLIKELY(not is_array()))
+    {
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+    }
+
+    // check if iterator pos fits to this JSON value
+    if (JSON_UNLIKELY(pos.m_object != this))
+    {
+        JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+    }
+
+    // check if range iterators belong to the same JSON object
+    if (JSON_UNLIKELY(first.m_object != last.m_object))
+    {
+        JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+    }
+
+    if (JSON_UNLIKELY(first.m_object == this))
+    {
+        JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
+    }
+
+    // insert to array and return iterator
+    iterator result(this);
+    result.m_it.array_iterator = m_value.array->insert(
+                                     pos.m_it.array_iterator,
+                                     first.m_it.array_iterator,
+                                     last.m_it.array_iterator);
+    return result;
+}
+
+json::iterator json::insert(const_iterator pos, initializer_list_t ilist)
+{
+    // insert only works for arrays
+    if (JSON_UNLIKELY(not is_array()))
+    {
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+    }
+
+    // check if iterator pos fits to this JSON value
+    if (JSON_UNLIKELY(pos.m_object != this))
+    {
+        JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+    }
+
+    // insert to array and return iterator
+    iterator result(this);
+    result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end());
+    return result;
+}
+
+void json::insert(const_iterator first, const_iterator last)
+{
+    // insert only works for objects
+    if (JSON_UNLIKELY(not is_object()))
+    {
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+    }
+
+    // check if range iterators belong to the same JSON object
+    if (JSON_UNLIKELY(first.m_object != last.m_object))
+    {
+        JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+    }
+
+    // passed iterators must belong to objects
+    if (JSON_UNLIKELY(not first.m_object->is_object()))
+    {
+        JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+    }
+
+    for (auto it = first.m_it.object_iterator, end = last.m_it.object_iterator; it != end; ++it)
+    {
+        m_value.object->insert(std::make_pair(it->first(), it->second));
+    }
+}
+
+void json::update(const_reference j)
+{
+    // implicitly convert null value to an empty object
+    if (is_null())
+    {
+        m_type = value_t::object;
+        m_value.object = create<object_t>();
+        assert_invariant();
+    }
+
+    if (JSON_UNLIKELY(not is_object()))
+    {
+        JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(type_name())));
+    }
+    if (JSON_UNLIKELY(not j.is_object()))
+    {
+        JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(j.type_name())));
+    }
+
+    for (auto it = j.cbegin(); it != j.cend(); ++it)
+    {
+        m_value.object->operator[](it.key()) = it.value();
+    }
+}
+
+void json::update(const_iterator first, const_iterator last)
+{
+    // implicitly convert null value to an empty object
+    if (is_null())
+    {
+        m_type = value_t::object;
+        m_value.object = create<object_t>();
+        assert_invariant();
+    }
+
+    if (JSON_UNLIKELY(not is_object()))
+    {
+        JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(type_name())));
+    }
+
+    // check if range iterators belong to the same JSON object
+    if (JSON_UNLIKELY(first.m_object != last.m_object))
+    {
+        JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+    }
+
+    // passed iterators must belong to objects
+    if (JSON_UNLIKELY(not first.m_object->is_object()
+                      or not last.m_object->is_object()))
+    {
+        JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+    }
+
+    for (auto it = first; it != last; ++it)
+    {
+        m_value.object->operator[](it.key()) = it.value();
+    }
+}
+
+bool operator==(json::const_reference lhs, json::const_reference rhs) noexcept
+{
+    const auto lhs_type = lhs.type();
+    const auto rhs_type = rhs.type();
+
+    if (lhs_type == rhs_type)
+    {
+        switch (lhs_type)
+        {
+            case json::value_t::array:
+                return (*lhs.m_value.array == *rhs.m_value.array);
+
+            case json::value_t::object:
+                return (*lhs.m_value.object == *rhs.m_value.object);
+
+            case json::value_t::null:
+                return true;
+
+            case json::value_t::string:
+                return (*lhs.m_value.string == *rhs.m_value.string);
+
+            case json::value_t::boolean:
+                return (lhs.m_value.boolean == rhs.m_value.boolean);
+
+            case json::value_t::number_integer:
+                return (lhs.m_value.number_integer == rhs.m_value.number_integer);
+
+            case json::value_t::number_unsigned:
+                return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
+
+            case json::value_t::number_float:
+                return (lhs.m_value.number_float == rhs.m_value.number_float);
+
+            default:
+                return false;
+        }
+    }
+    else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
+    {
+        return (static_cast<double>(lhs.m_value.number_integer) == rhs.m_value.number_float);
+    }
+    else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
+    {
+        return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_integer));
+    }
+    else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
+    {
+        return (static_cast<double>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
+    }
+    else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
+    {
+        return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_unsigned));
+    }
+    else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
+    {
+        return (static_cast<int64_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
+    }
+    else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
+    {
+        return (lhs.m_value.number_integer == static_cast<int64_t>(rhs.m_value.number_unsigned));
+    }
+
+    return false;
+}
+
+bool operator<(json::const_reference lhs, json::const_reference rhs) noexcept
+{
+    const auto lhs_type = lhs.type();
+    const auto rhs_type = rhs.type();
+
+    if (lhs_type == rhs_type)
+    {
+        switch (lhs_type)
+        {
+            case json::value_t::array:
+                return (*lhs.m_value.array) < (*rhs.m_value.array);
+
+            case json::value_t::object:
+                return *lhs.m_value.object < *rhs.m_value.object;
+
+            case json::value_t::null:
+                return false;
+
+            case json::value_t::string:
+                return *lhs.m_value.string < *rhs.m_value.string;
+
+            case json::value_t::boolean:
+                return lhs.m_value.boolean < rhs.m_value.boolean;
+
+            case json::value_t::number_integer:
+                return lhs.m_value.number_integer < rhs.m_value.number_integer;
+
+            case json::value_t::number_unsigned:
+                return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
+
+            case json::value_t::number_float:
+                return lhs.m_value.number_float < rhs.m_value.number_float;
+
+            default:
+                return false;
+        }
+    }
+    else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
+    {
+        return static_cast<double>(lhs.m_value.number_integer) < rhs.m_value.number_float;
+    }
+    else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
+    {
+        return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_integer);
+    }
+    else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
+    {
+        return static_cast<double>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
+    }
+    else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
+    {
+        return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_unsigned);
+    }
+    else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
+    {
+        return lhs.m_value.number_integer < static_cast<int64_t>(rhs.m_value.number_unsigned);
+    }
+    else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
+    {
+        return static_cast<int64_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
+    }
+
+    // We only reach this line if we cannot compare values. In that case,
+    // we compare types. Note we have to call the operator explicitly,
+    // because MSVC has problems otherwise.
+    return operator<(lhs_type, rhs_type);
+}
+
+const char* json::type_name() const noexcept
+{
+    {
+        switch (m_type)
+        {
+            case value_t::null:
+                return "null";
+            case value_t::object:
+                return "object";
+            case value_t::array:
+                return "array";
+            case value_t::string:
+                return "string";
+            case value_t::boolean:
+                return "boolean";
+            case value_t::discarded:
+                return "discarded";
+            default:
+                return "number";
+        }
+    }
+}
+
+json json::patch(const json& json_patch) const
+{
+    // make a working copy to apply the patch to
+    json result = *this;
+
+    // the valid JSON Patch operations
+    enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+    const auto get_op = [](const std::string & op)
+    {
+        if (op == "add")
+        {
+            return patch_operations::add;
+        }
+        if (op == "remove")
+        {
+            return patch_operations::remove;
+        }
+        if (op == "replace")
+        {
+            return patch_operations::replace;
+        }
+        if (op == "move")
+        {
+            return patch_operations::move;
+        }
+        if (op == "copy")
+        {
+            return patch_operations::copy;
+        }
+        if (op == "test")
+        {
+            return patch_operations::test;
+        }
+
+        return patch_operations::invalid;
+    };
+
+    // wrapper for "add" operation; add value at ptr
+    const auto operation_add = [&result](json_pointer & ptr, json val)
+    {
+        // adding to the root of the target document means replacing it
+        if (ptr.is_root())
+        {
+            result = val;
+        }
+        else
+        {
+            // make sure the top element of the pointer exists
+            json_pointer top_pointer = ptr.top();
+            if (top_pointer != ptr)
+            {
+                result.at(top_pointer);
+            }
+
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.pop_back();
+            json& parent = result[ptr];
+
+            switch (parent.m_type)
+            {
+                case value_t::null:
+                case value_t::object:
+                {
+                    // use operator[] to add value
+                    parent[last_path] = val;
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    if (last_path == "-")
+                    {
+                        // special case: append to back
+                        parent.push_back(val);
+                    }
+                    else
+                    {
+                        const auto idx = json_pointer::array_index(last_path);
+                        if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
+                        {
+                            // avoid undefined behavior
+                            JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+                        }
+                        else
+                        {
+                            // default case: insert add offset
+                            parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+                        }
+                    }
+                    break;
+                }
+
+                default:
+                {
+                    // if there exists a parent it cannot be primitive
+                    assert(false);  // LCOV_EXCL_LINE
+                }
+            }
+        }
+    };
+
+    // wrapper for "remove" operation; remove value at ptr
+    const auto operation_remove = [&result](json_pointer & ptr)
+    {
+        // get reference to parent of JSON pointer ptr
+        const auto last_path = ptr.pop_back();
+        json& parent = result.at(ptr);
+
+        // remove child
+        if (parent.is_object())
+        {
+            // perform range check
+            auto it = parent.find(last_path);
+            if (JSON_LIKELY(it != parent.end()))
+            {
+                parent.erase(it);
+            }
+            else
+            {
+                JSON_THROW(out_of_range::create(403, "key '" + Twine(last_path) + "' not found"));
+            }
+        }
+        else if (parent.is_array())
+        {
+            // note erase performs range check
+            parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
+        }
+    };
+
+    // type check: top level value must be an array
+    if (JSON_UNLIKELY(not json_patch.is_array()))
+    {
+        JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
+    }
+
+    // iterate and apply the operations
+    for (const auto& val : json_patch)
+    {
+        // wrapper to get a value for an operation
+        const auto get_value = [&val](const std::string & op,
+                                      const std::string & member,
+                                      bool string_type) -> json &
+        {
+            // find value
+            auto it = val.m_value.object->find(member);
+
+            // context-sensitive error message
+            const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
+
+            // check if desired value is present
+            if (JSON_UNLIKELY(it == val.m_value.object->end()))
+            {
+                JSON_THROW(parse_error::create(105, 0, Twine(error_msg) + " must have member '" + Twine(member) + "'"));
+            }
+
+            // check if result is of type string
+            if (JSON_UNLIKELY(string_type and not it->second.is_string()))
+            {
+                JSON_THROW(parse_error::create(105, 0, Twine(error_msg) + " must have string member '" + Twine(member) + "'"));
+            }
+
+            // no error: return value
+            return it->second;
+        };
+
+        // type check: every element of the array must be an object
+        if (JSON_UNLIKELY(not val.is_object()))
+        {
+            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
+        }
+
+        // collect mandatory members
+        const std::string op = get_value("op", "op", true);
+        const std::string path = get_value(op, "path", true);
+        json_pointer ptr(path);
+
+        switch (get_op(op))
+        {
+            case patch_operations::add:
+            {
+                operation_add(ptr, get_value("add", "value", false));
+                break;
+            }
+
+            case patch_operations::remove:
+            {
+                operation_remove(ptr);
+                break;
+            }
+
+            case patch_operations::replace:
+            {
+                // the "path" location must exist - use at()
+                result.at(ptr) = get_value("replace", "value", false);
+                break;
+            }
+
+            case patch_operations::move:
+            {
+                const std::string from_path = get_value("move", "from", true);
+                json_pointer from_ptr(from_path);
+
+                // the "from" location must exist - use at()
+                json v = result.at(from_ptr);
+
+                // The move operation is functionally identical to a
+                // "remove" operation on the "from" location, followed
+                // immediately by an "add" operation at the target
+                // location with the value that was just removed.
+                operation_remove(from_ptr);
+                operation_add(ptr, v);
+                break;
+            }
+
+            case patch_operations::copy:
+            {
+                const std::string from_path = get_value("copy", "from", true);
+                const json_pointer from_ptr(from_path);
+
+                // the "from" location must exist - use at()
+                json v = result.at(from_ptr);
+
+                // The copy is functionally identical to an "add"
+                // operation at the target location using the value
+                // specified in the "from" member.
+                operation_add(ptr, v);
+                break;
+            }
+
+            case patch_operations::test:
+            {
+                bool success = false;
+                JSON_TRY
+                {
+                    // check if "value" matches the one at "path"
+                    // the "path" location must exist - use at()
+                    success = (result.at(ptr) == get_value("test", "value", false));
+                }
+                JSON_CATCH (out_of_range&)
+                {
+                    // ignore out of range errors: success remains false
+                }
+
+                // throw an exception if test fails
+                if (JSON_UNLIKELY(not success))
+                {
+                    JSON_THROW(other_error::create(501, "unsuccessful: " + Twine(val.dump())));
+                }
+
+                break;
+            }
+
+            case patch_operations::invalid:
+            {
+                // op must be "add", "remove", "replace", "move", "copy", or
+                // "test"
+                JSON_THROW(parse_error::create(105, 0, "operation value '" + Twine(op) + "' is invalid"));
+            }
+        }
+    }
+
+    return result;
+}
+
+json json::diff(const json& source, const json& target,
+                       const std::string& path)
+{
+    // the patch
+    json result(value_t::array);
+
+    // if the values are the same, return empty patch
+    if (source == target)
+    {
+        return result;
+    }
+
+    if (source.type() != target.type())
+    {
+        // different types: replace value
+        result.push_back(
+        {
+            {"op", "replace"}, {"path", path}, {"value", target}
+        });
+    }
+    else
+    {
+        switch (source.type())
+        {
+            case value_t::array:
+            {
+                // first pass: traverse common elements
+                std::size_t i = 0;
+                while (i < source.size() and i < target.size())
+                {
+                    // recursive call to compare array values at index i
+                    auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
+                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    ++i;
+                }
+
+                // i now reached the end of at least one array
+                // in a second pass, traverse the remaining elements
+
+                // remove my remaining elements
+                const auto end_index = static_cast<difference_type>(result.size());
+                while (i < source.size())
+                {
+                    // add operations in reverse order to avoid invalid
+                    // indices
+                    result.insert(result.begin() + end_index, object(
+                    {
+                        {"op", "remove"},
+                        {"path", path + "/" + std::to_string(i)}
+                    }));
+                    ++i;
+                }
+
+                // add other remaining elements
+                while (i < target.size())
+                {
+                    result.push_back(
+                    {
+                        {"op", "add"},
+                        {"path", path + "/" + std::to_string(i)},
+                        {"value", target[i]}
+                    });
+                    ++i;
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // first pass: traverse this object's elements
+                for (auto it = source.cbegin(); it != source.cend(); ++it)
+                {
+                    // escape the key name to be used in a JSON patch
+                    const auto key = json_pointer::escape(it.key());
+
+                    if (target.find(it.key()) != target.end())
+                    {
+                        // recursive call to compare object values at key it
+                        auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
+                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    }
+                    else
+                    {
+                        // found a key that is not in o -> remove it
+                        result.push_back(object(
+                        {
+                            {"op", "remove"}, {"path", path + "/" + key}
+                        }));
+                    }
+                }
+
+                // second pass: traverse other object's elements
+                for (auto it = target.cbegin(); it != target.cend(); ++it)
+                {
+                    if (source.find(it.key()) == source.end())
+                    {
+                        // found a key that is not in this -> add it
+                        const auto key = json_pointer::escape(it.key());
+                        result.push_back(
+                        {
+                            {"op", "add"}, {"path", path + "/" + key},
+                            {"value", it.value()}
+                        });
+                    }
+                }
+
+                break;
+            }
+
+            default:
+            {
+                // both primitive type: replace value
+                result.push_back(
+                {
+                    {"op", "replace"}, {"path", path}, {"value", target}
+                });
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+void json::merge_patch(const json& patch)
+{
+    if (patch.is_object())
+    {
+        if (not is_object())
+        {
+            *this = object();
+        }
+        for (auto it = patch.begin(); it != patch.end(); ++it)
+        {
+            if (it.value().is_null())
+            {
+                erase(it.key());
+            }
+            else
+            {
+                operator[](it.key()).merge_patch(it.value());
+            }
+        }
+    }
+    else
+    {
+        *this = patch;
+    }
+}
+
+}  // namespace wpi