]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/boost/json/impl/object.ipp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / json / impl / object.ipp
diff --git a/ceph/src/boost/boost/json/impl/object.ipp b/ceph/src/boost/boost/json/impl/object.ipp
new file mode 100644 (file)
index 0000000..3c885a1
--- /dev/null
@@ -0,0 +1,825 @@
+//
+// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/json
+//
+
+#ifndef BOOST_JSON_IMPL_OBJECT_IPP
+#define BOOST_JSON_IMPL_OBJECT_IPP
+
+#include <boost/json/object.hpp>
+#include <boost/json/detail/digest.hpp>
+#include <boost/json/detail/except.hpp>
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <new>
+#include <stdexcept>
+#include <type_traits>
+
+BOOST_JSON_NS_BEGIN
+
+//----------------------------------------------------------
+
+constexpr object::table::table() = default;
+
+// empty objects point here
+BOOST_JSON_REQUIRE_CONST_INIT
+object::table object::empty_;
+
+std::size_t
+object::table::
+digest(string_view key) const noexcept
+{
+    BOOST_ASSERT(salt != 0);
+    return detail::digest(
+        key.data(), key.size(), salt);
+}
+
+auto
+object::table::
+bucket(std::size_t hash) noexcept ->
+    index_t&
+{
+    return reinterpret_cast<
+        index_t*>(&(*this)[capacity])[
+            hash % capacity];
+}
+
+auto
+object::table::
+bucket(string_view key) noexcept ->
+    index_t&
+{
+    return bucket(digest(key));
+}
+
+void
+object::table::
+clear() noexcept
+{
+    BOOST_ASSERT(! is_small());
+    // initialize buckets
+    std::memset(
+        reinterpret_cast<index_t*>(
+            &(*this)[capacity]),
+        0xff, // null_index_
+        capacity * sizeof(index_t));
+}
+
+object::table*
+object::table::
+allocate(
+    std::size_t capacity,
+    std::uintptr_t salt,
+    storage_ptr const& sp)
+{
+    BOOST_STATIC_ASSERT(
+        alignof(key_value_pair) >=
+        alignof(index_t));
+    BOOST_ASSERT(capacity > 0);
+    BOOST_ASSERT(capacity <= max_size());
+    table* p;
+    if(capacity <= detail::small_object_size_)
+    {
+        p = reinterpret_cast<
+            table*>(sp->allocate(
+                sizeof(table) + capacity *
+                    sizeof(key_value_pair)));
+        p->capacity = static_cast<
+            std::uint32_t>(capacity);
+    }
+    else
+    {
+        p = reinterpret_cast<
+            table*>(sp->allocate(
+                sizeof(table) + capacity * (
+                    sizeof(key_value_pair) +
+                    sizeof(index_t))));
+        p->capacity = static_cast<
+            std::uint32_t>(capacity);
+        p->clear();
+    }
+    if(salt)
+    {
+        p->salt = salt;
+    }
+    else
+    {
+        // VFALCO This would be better if it
+        //        was random, but maybe this
+        //        is good enough.
+        p->salt = reinterpret_cast<
+            std::uintptr_t>(p);
+    }
+    return p;
+}
+
+//----------------------------------------------------------
+
+void
+object::
+revert_construct::
+destroy() noexcept
+{
+    obj_->destroy();
+}
+
+//----------------------------------------------------------
+
+void
+object::
+revert_insert::
+destroy() noexcept
+{
+    obj_->destroy(
+        &(*obj_->t_)[size_],
+        obj_->end());
+}
+
+//----------------------------------------------------------
+//
+// Construction
+//
+//----------------------------------------------------------
+
+object::
+object(detail::unchecked_object&& uo)
+    : sp_(uo.storage())
+{
+    if(uo.size() == 0)
+    {
+        t_ = &empty_;
+        return;
+    }
+    // should already be checked
+    BOOST_ASSERT(
+        uo.size() <= max_size());
+    t_ = table::allocate(
+        uo.size(), 0, sp_);
+
+    // insert all elements, keeping
+    // the last of any duplicate keys.
+    auto dest = begin();
+    auto src = uo.release();
+    auto const end = src + 2 * uo.size();
+    if(t_->is_small())
+    {
+        t_->size = 0;
+        while(src != end)
+        {
+            access::construct_key_value_pair(
+                dest, pilfer(src[0]), pilfer(src[1]));
+            src += 2;
+            auto result = find_impl(dest->key());
+            if(! result.first)
+            {
+                ++dest;
+                ++t_->size;
+                continue;
+            }
+            // handle duplicate
+            auto& v = *result.first;
+            // don't bother to check if
+            // storage deallocate is trivial
+            v.~key_value_pair();
+            // trivial relocate
+            std::memcpy(
+                static_cast<void*>(&v),
+                    dest, sizeof(v));
+        }
+        return;
+    }
+    while(src != end)
+    {
+        access::construct_key_value_pair(
+            dest, pilfer(src[0]), pilfer(src[1]));
+        src += 2;
+        auto& head = t_->bucket(dest->key());
+        auto i = head;
+        for(;;)
+        {
+            if(i == null_index_)
+            {
+                // end of bucket
+                access::next(
+                    *dest) = head;
+                head = static_cast<index_t>(
+                    dest - begin());
+                ++dest;
+                break;
+            }
+            auto& v = (*t_)[i];
+            if(v.key() != dest->key())
+            {
+                i = access::next(v);
+                continue;
+            }
+
+            // handle duplicate
+            access::next(*dest) =
+                access::next(v);
+            // don't bother to check if
+            // storage deallocate is trivial
+            v.~key_value_pair();
+            // trivial relocate
+            std::memcpy(
+                static_cast<void*>(&v),
+                    dest, sizeof(v));
+            break;
+        }
+    }
+    t_->size = static_cast<
+        index_t>(dest - begin());
+}
+
+object::
+~object()
+{
+    if(sp_.is_not_shared_and_deallocate_is_trivial())
+        return;
+    if(t_->capacity == 0)
+        return;
+    destroy();
+}
+
+object::
+object(
+    std::size_t min_capacity,
+    storage_ptr sp)
+    : sp_(std::move(sp))
+    , t_(&empty_)
+{
+    reserve(min_capacity);
+}
+
+object::
+object(object&& other) noexcept
+    : sp_(other.sp_)
+    , t_(detail::exchange(
+        other.t_, &empty_))
+{
+}
+
+object::
+object(
+    object&& other,
+    storage_ptr sp)
+    : sp_(std::move(sp))
+{
+    if(*sp_ == *other.sp_)
+    {
+        t_ = detail::exchange(
+            other.t_, &empty_);
+        return;
+    }
+
+    t_ = &empty_;
+    object(other, sp_).swap(*this);
+}
+
+object::
+object(
+    object const& other,
+    storage_ptr sp)
+    : sp_(std::move(sp))
+    , t_(&empty_)
+{
+    reserve(other.size());
+    revert_construct r(*this);
+    if(t_->is_small())
+    {
+        for(auto const& v : other)
+        {
+            ::new(end())
+                key_value_pair(v, sp_);
+            ++t_->size;
+        }
+        r.commit();
+        return;
+    }
+    for(auto const& v : other)
+    {
+        // skip duplicate checking
+        auto& head =
+            t_->bucket(v.key());
+        auto pv = ::new(end())
+            key_value_pair(v, sp_);
+        access::next(*pv) = head;
+        head = t_->size;
+        ++t_->size;
+    }
+    r.commit();
+}
+
+object::
+object(
+    std::initializer_list<std::pair<
+        string_view, value_ref>> init,
+    std::size_t min_capacity,
+    storage_ptr sp)
+    : sp_(std::move(sp))
+    , t_(&empty_)
+{
+    if( min_capacity < init.size())
+        min_capacity = init.size();
+    reserve(min_capacity);
+    revert_construct r(*this);
+    insert(init);
+    r.commit();
+}
+
+//----------------------------------------------------------
+//
+// Assignment
+//
+//----------------------------------------------------------
+
+object&
+object::
+operator=(object const& other)
+{
+    object tmp(other, sp_);
+    this->~object();
+    ::new(this) object(pilfer(tmp));
+    return *this;
+}
+
+object&
+object::
+operator=(object&& other)
+{
+    object tmp(std::move(other), sp_);
+    this->~object();
+    ::new(this) object(pilfer(tmp));
+    return *this;
+}
+
+object&
+object::
+operator=(
+    std::initializer_list<std::pair<
+        string_view, value_ref>> init)
+{
+    object tmp(init, sp_);
+    this->~object();
+    ::new(this) object(pilfer(tmp));
+    return *this;
+}
+
+//----------------------------------------------------------
+//
+// Modifiers
+//
+//----------------------------------------------------------
+
+void
+object::
+clear() noexcept
+{
+    if(empty())
+        return;
+    if(! sp_.is_not_shared_and_deallocate_is_trivial())
+        destroy(begin(), end());
+    if(! t_->is_small())
+        t_->clear();
+    t_->size = 0;
+}
+
+void
+object::
+insert(
+    std::initializer_list<std::pair<
+        string_view, value_ref>> init)
+{
+    auto const n0 = size();
+    if(init.size() > max_size() - n0)
+        detail::throw_length_error(
+            "object too large",
+            BOOST_CURRENT_LOCATION);
+    reserve(n0 + init.size());
+    revert_insert r(*this);
+    if(t_->is_small())
+    {
+        for(auto& iv : init)
+        {
+            auto result =
+                find_impl(iv.first);
+            if(result.first)
+            {
+                // ignore duplicate
+                continue;
+            }
+            ::new(end()) key_value_pair(
+                iv.first,
+                iv.second.make_value(sp_));
+            ++t_->size;
+        }
+        r.commit();
+        return;
+    }
+    for(auto& iv : init)
+    {
+        auto& head = t_->bucket(iv.first);
+        auto i = head;
+        for(;;)
+        {
+            if(i == null_index_)
+            {
+                // VFALCO value_ref should construct
+                // a key_value_pair using placement
+                auto& v = *::new(end())
+                    key_value_pair(
+                        iv.first,
+                        iv.second.make_value(sp_));
+                access::next(v) = head;
+                head = static_cast<index_t>(
+                    t_->size);
+                ++t_->size;
+                break;
+            }
+            auto& v = (*t_)[i];
+            if(v.key() == iv.first)
+            {
+                // ignore duplicate
+                break;
+            }
+            i = access::next(v);
+        }
+    }
+    r.commit();
+}
+
+auto
+object::
+erase(const_iterator pos) noexcept ->
+    iterator
+{
+    auto p = begin() + (pos - begin());
+    if(t_->is_small())
+    {
+        p->~value_type();
+        --t_->size;
+        auto const pb = end();
+        if(p != end())
+        {
+            // the casts silence warnings
+            std::memcpy(
+                static_cast<void*>(p),
+                static_cast<void const*>(pb),
+                sizeof(*p));
+        }
+        return p;
+    }
+    remove(t_->bucket(p->key()), *p);
+    p->~value_type();
+    --t_->size;
+    auto const pb = end();
+    if(p != end())
+    {
+        auto& head = t_->bucket(pb->key());
+        remove(head, *pb);
+        // the casts silence warnings
+        std::memcpy(
+            static_cast<void*>(p),
+            static_cast<void const*>(pb),
+            sizeof(*p));
+        access::next(*p) = head;
+        head = static_cast<
+            index_t>(p - begin());
+    }
+    return p;
+}
+
+auto
+object::
+erase(string_view key) noexcept ->
+    std::size_t
+{
+    auto it = find(key);
+    if(it == end())
+        return 0;
+    erase(it);
+    return 1;
+}
+
+void
+object::
+swap(object& other)
+{
+    if(*sp_ == *other.sp_)
+    {
+        t_ = detail::exchange(
+            other.t_, t_);
+        return;
+    }
+    object temp1(
+        std::move(*this),
+        other.storage());
+    object temp2(
+        std::move(other),
+        this->storage());
+    other.~object();
+    ::new(&other) object(pilfer(temp1));
+    this->~object();
+    ::new(this) object(pilfer(temp2));
+}
+
+//----------------------------------------------------------
+//
+// Lookup
+//
+//----------------------------------------------------------
+
+auto
+object::
+operator[](string_view key) ->
+    value&
+{
+    auto const result =
+        emplace(key, nullptr);
+    return result.first->value();
+}
+
+auto
+object::
+count(string_view key) const noexcept ->
+    std::size_t
+{
+    if(find(key) == end())
+        return 0;
+    return 1;
+}
+
+auto
+object::
+find(string_view key) noexcept ->
+    iterator
+{
+    if(empty())
+        return end();
+    auto const p =
+        find_impl(key).first;
+    if(p)
+        return p;
+    return end();
+}
+
+auto
+object::
+find(string_view key) const noexcept ->
+    const_iterator
+{
+    if(empty())
+        return end();
+    auto const p =
+        find_impl(key).first;
+    if(p)
+        return p;
+    return end();
+}
+
+bool
+object::
+contains(
+    string_view key) const noexcept
+{
+    if(empty())
+        return false;
+    return find_impl(
+        key).first != nullptr;
+}
+
+value const*
+object::
+if_contains(
+    string_view key) const noexcept
+{
+    auto const it = find(key);
+    if(it != end())
+        return &it->value();
+    return nullptr;
+}
+
+value*
+object::
+if_contains(
+    string_view key) noexcept
+{
+    auto const it = find(key);
+    if(it != end())
+        return &it->value();
+    return nullptr;
+}
+
+//----------------------------------------------------------
+//
+// (private)
+//
+//----------------------------------------------------------
+
+auto
+object::
+find_impl(
+    string_view key) const noexcept ->
+        std::pair<
+            key_value_pair*,
+            std::size_t>
+{
+    BOOST_ASSERT(t_->capacity > 0);
+    if(t_->is_small())
+    {
+        auto it = &(*t_)[0];
+        auto const last =
+            &(*t_)[t_->size];
+        for(;it != last; ++it)
+            if(key == it->key())
+                return { it, 0 };
+        return { nullptr, 0 };
+    }
+    std::pair<
+        key_value_pair*,
+        std::size_t> result;
+    result.second = t_->digest(key);
+    auto i = t_->bucket(
+        result.second);
+    while(i != null_index_)
+    {
+        auto& v = (*t_)[i];
+        if(v.key() == key)
+        {
+            result.first = &v;
+            return result;
+        }
+        i = access::next(v);
+    }
+    result.first = nullptr;
+    return result;
+}
+
+auto
+object::
+insert_impl(
+    pilfered<key_value_pair> p) ->
+        std::pair<iterator, bool>
+{
+    // caller is responsible
+    // for preventing aliasing.
+    reserve(size() + 1);
+    auto const result =
+        find_impl(p.get().key());
+    if(result.first)
+        return { result.first, false };
+    return { insert_impl(
+        p, result.second), true };
+}
+
+key_value_pair*
+object::
+insert_impl(
+    pilfered<key_value_pair> p,
+    std::size_t hash)
+{
+    BOOST_ASSERT(
+        capacity() > size());
+    if(t_->is_small())
+    {
+        auto const pv = ::new(end())
+            key_value_pair(p);
+        ++t_->size;
+        return pv;
+    }
+    auto& head =
+        t_->bucket(hash);
+    auto const pv = ::new(end())
+        key_value_pair(p);
+    access::next(*pv) = head;
+    head = t_->size;
+    ++t_->size;
+    return pv;
+}
+
+// rehash to at least `n` buckets
+void
+object::
+rehash(std::size_t new_capacity)
+{
+    BOOST_ASSERT(
+        new_capacity > t_->capacity);
+    auto t = table::allocate(
+        growth(new_capacity),
+            t_->salt, sp_);
+    if(! empty())
+        std::memcpy(
+            static_cast<
+                void*>(&(*t)[0]),
+            begin(),
+            size() * sizeof(
+                key_value_pair));
+    t->size = t_->size;
+    table::deallocate(t_, sp_);
+    t_ = t;
+
+    if(! t_->is_small())
+    {
+        // rebuild hash table,
+        // without dup checks
+        auto p = end();
+        index_t i = t_->size;
+        while(i-- > 0)
+        {
+            --p;
+            auto& head =
+                t_->bucket(p->key());
+            access::next(*p) = head;
+            head = i;
+        }
+    }
+}
+
+bool
+object::
+equal(object const& other) const noexcept
+{
+    if(size() != other.size())
+        return false;
+    auto const end_ = other.end();
+    for(auto e : *this)
+    {
+        auto it = other.find(e.key());
+        if(it == end_)
+            return false;
+        if(it->value() != e.value())
+            return false;
+    }
+    return true;
+}
+
+std::size_t
+object::
+growth(
+    std::size_t new_size) const
+{
+    if(new_size > max_size())
+        detail::throw_length_error(
+            "object too large",
+            BOOST_CURRENT_LOCATION);
+    std::size_t const old = capacity();
+    if(old > max_size() - old / 2)
+        return new_size;
+    std::size_t const g =
+        old + old / 2; // 1.5x
+    if(g < new_size)
+        return new_size;
+    return g;
+}
+
+void
+object::
+remove(
+    index_t& head,
+    key_value_pair& v) noexcept
+{
+    BOOST_ASSERT(! t_->is_small());
+    auto const i = static_cast<
+        index_t>(&v - begin());
+    if(head == i)
+    {
+        head = access::next(v);
+        return;
+    }
+    auto* pn =
+        &access::next((*t_)[head]);
+    while(*pn != i)
+        pn = &access::next((*t_)[*pn]);
+    *pn = access::next(v);
+}
+
+void
+object::
+destroy() noexcept
+{
+    BOOST_ASSERT(t_->capacity > 0);
+    BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
+    destroy(begin(), end());
+    table::deallocate(t_, sp_);
+}
+
+void
+object::
+destroy(
+    key_value_pair* first,
+    key_value_pair* last) noexcept
+{
+    BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
+    while(last != first)
+        (--last)->~key_value_pair();
+}
+
+BOOST_JSON_NS_END
+
+#endif