1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/json
7  
// Official repository: https://github.com/boostorg/json
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_JSON_IMPL_OBJECT_IPP
10  
#ifndef BOOST_JSON_IMPL_OBJECT_IPP
11  
#define BOOST_JSON_IMPL_OBJECT_IPP
11  
#define BOOST_JSON_IMPL_OBJECT_IPP
12  

12  

13  
#include <boost/core/detail/static_assert.hpp>
13  
#include <boost/core/detail/static_assert.hpp>
14  
#include <boost/container_hash/hash.hpp>
14  
#include <boost/container_hash/hash.hpp>
15  
#include <boost/json/object.hpp>
15  
#include <boost/json/object.hpp>
16  
#include <boost/json/detail/digest.hpp>
16  
#include <boost/json/detail/digest.hpp>
17  
#include <boost/json/detail/except.hpp>
17  
#include <boost/json/detail/except.hpp>
18  
#include <algorithm>
18  
#include <algorithm>
19  
#include <cmath>
19  
#include <cmath>
20  
#include <cstdlib>
20  
#include <cstdlib>
21  
#include <cstring>
21  
#include <cstring>
22  
#include <new>
22  
#include <new>
23  
#include <stdexcept>
23  
#include <stdexcept>
24  
#include <type_traits>
24  
#include <type_traits>
25  

25  

26  
namespace boost {
26  
namespace boost {
27  
namespace json {
27  
namespace json {
28  
namespace detail {
28  
namespace detail {
29  

29  

30  
template<class CharRange>
30  
template<class CharRange>
31  
std::pair<key_value_pair*, std::size_t>
31  
std::pair<key_value_pair*, std::size_t>
32  
find_in_object(
32  
find_in_object(
33  
    object const& obj,
33  
    object const& obj,
34  
    CharRange key) noexcept
34  
    CharRange key) noexcept
35  
{
35  
{
36  
    BOOST_ASSERT(obj.t_->capacity > 0);
36  
    BOOST_ASSERT(obj.t_->capacity > 0);
37  
    if(obj.t_->is_small())
37  
    if(obj.t_->is_small())
38  
    {
38  
    {
39  
        auto it = &(*obj.t_)[0];
39  
        auto it = &(*obj.t_)[0];
40  
        auto const last =
40  
        auto const last =
41  
            &(*obj.t_)[obj.t_->size];
41  
            &(*obj.t_)[obj.t_->size];
42  
        for(;it != last; ++it)
42  
        for(;it != last; ++it)
43  
            if( key == it->key() )
43  
            if( key == it->key() )
44  
                return { it, 0 };
44  
                return { it, 0 };
45  
        return { nullptr, 0 };
45  
        return { nullptr, 0 };
46  
    }
46  
    }
47  
    std::pair<
47  
    std::pair<
48  
        key_value_pair*,
48  
        key_value_pair*,
49  
        std::size_t> result;
49  
        std::size_t> result;
50  
    BOOST_ASSERT(obj.t_->salt != 0);
50  
    BOOST_ASSERT(obj.t_->salt != 0);
51  
    result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
51  
    result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
52  
    auto i = obj.t_->bucket(
52  
    auto i = obj.t_->bucket(
53  
        result.second);
53  
        result.second);
54  
    while(i != object::null_index_)
54  
    while(i != object::null_index_)
55  
    {
55  
    {
56  
        auto& v = (*obj.t_)[i];
56  
        auto& v = (*obj.t_)[i];
57  
        if( key == v.key() )
57  
        if( key == v.key() )
58  
        {
58  
        {
59  
            result.first = &v;
59  
            result.first = &v;
60  
            return result;
60  
            return result;
61  
        }
61  
        }
62  
        i = access::next(v);
62  
        i = access::next(v);
63  
    }
63  
    }
64  
    result.first = nullptr;
64  
    result.first = nullptr;
65  
    return result;
65  
    return result;
66  
}
66  
}
67  

67  

68  

68  

69  
template
69  
template
70  
std::pair<key_value_pair*, std::size_t>
70  
std::pair<key_value_pair*, std::size_t>
71  
find_in_object<string_view>(
71  
find_in_object<string_view>(
72  
    object const& obj,
72  
    object const& obj,
73  
    string_view key) noexcept;
73  
    string_view key) noexcept;
74  

74  

75  
} // namespace detail
75  
} // namespace detail
76  

76  

77  
//----------------------------------------------------------
77  
//----------------------------------------------------------
78  

78  

79  
constexpr object::table::table() = default;
79  
constexpr object::table::table() = default;
80  

80  

81  
// empty objects point here
81  
// empty objects point here
82  
BOOST_JSON_REQUIRE_CONST_INIT
82  
BOOST_JSON_REQUIRE_CONST_INIT
83  
object::table object::empty_;
83  
object::table object::empty_;
84  

84  

85  
std::size_t
85  
std::size_t
86  
object::table::
86  
object::table::
87  
digest(string_view key) const noexcept
87  
digest(string_view key) const noexcept
88  
{
88  
{
89  
    BOOST_ASSERT(salt != 0);
89  
    BOOST_ASSERT(salt != 0);
90  
    return detail::digest(
90  
    return detail::digest(
91  
        key.begin(), key.end(), salt);
91  
        key.begin(), key.end(), salt);
92  
}
92  
}
93  

93  

94  
auto
94  
auto
95  
object::table::
95  
object::table::
96  
bucket(std::size_t hash) noexcept ->
96  
bucket(std::size_t hash) noexcept ->
97  
    index_t&
97  
    index_t&
98  
{
98  
{
99  
    return reinterpret_cast<
99  
    return reinterpret_cast<
100  
        index_t*>(&(*this)[capacity])[
100  
        index_t*>(&(*this)[capacity])[
101  
            hash % capacity];
101  
            hash % capacity];
102  
}
102  
}
103  

103  

104  
auto
104  
auto
105  
object::table::
105  
object::table::
106  
bucket(string_view key) noexcept ->
106  
bucket(string_view key) noexcept ->
107  
    index_t&
107  
    index_t&
108  
{
108  
{
109  
    return bucket(digest(key));
109  
    return bucket(digest(key));
110  
}
110  
}
111  

111  

112  
void
112  
void
113  
object::table::
113  
object::table::
114  
clear() noexcept
114  
clear() noexcept
115  
{
115  
{
116  
    BOOST_ASSERT(! is_small());
116  
    BOOST_ASSERT(! is_small());
117  
    // initialize buckets
117  
    // initialize buckets
118  
    std::memset(
118  
    std::memset(
119  
        reinterpret_cast<index_t*>(
119  
        reinterpret_cast<index_t*>(
120  
            &(*this)[capacity]),
120  
            &(*this)[capacity]),
121  
        0xff, // null_index_
121  
        0xff, // null_index_
122  
        capacity * sizeof(index_t));
122  
        capacity * sizeof(index_t));
123  
}
123  
}
124  

124  

125  
object::table*
125  
object::table*
126  
object::table::
126  
object::table::
127  
allocate(
127  
allocate(
128  
    std::size_t capacity,
128  
    std::size_t capacity,
129  
    std::uintptr_t salt,
129  
    std::uintptr_t salt,
130  
    storage_ptr const& sp)
130  
    storage_ptr const& sp)
131  
{
131  
{
132  
    BOOST_CORE_STATIC_ASSERT(
132  
    BOOST_CORE_STATIC_ASSERT(
133  
        alignof(key_value_pair) >= alignof(index_t));
133  
        alignof(key_value_pair) >= alignof(index_t));
134  
    BOOST_ASSERT(capacity > 0);
134  
    BOOST_ASSERT(capacity > 0);
135  
    BOOST_ASSERT(capacity <= max_size());
135  
    BOOST_ASSERT(capacity <= max_size());
136  
    table* p;
136  
    table* p;
137  
    if(capacity <= detail::small_object_size_)
137  
    if(capacity <= detail::small_object_size_)
138  
    {
138  
    {
139  
        p = reinterpret_cast<
139  
        p = reinterpret_cast<
140  
            table*>(sp->allocate(
140  
            table*>(sp->allocate(
141  
                sizeof(table) + capacity *
141  
                sizeof(table) + capacity *
142  
                    sizeof(key_value_pair)));
142  
                    sizeof(key_value_pair)));
143  
        p->capacity = static_cast<
143  
        p->capacity = static_cast<
144  
            std::uint32_t>(capacity);
144  
            std::uint32_t>(capacity);
145  
    }
145  
    }
146  
    else
146  
    else
147  
    {
147  
    {
148  
        p = reinterpret_cast<
148  
        p = reinterpret_cast<
149  
            table*>(sp->allocate(
149  
            table*>(sp->allocate(
150  
                sizeof(table) + capacity * (
150  
                sizeof(table) + capacity * (
151  
                    sizeof(key_value_pair) +
151  
                    sizeof(key_value_pair) +
152  
                    sizeof(index_t))));
152  
                    sizeof(index_t))));
153  
        p->capacity = static_cast<
153  
        p->capacity = static_cast<
154  
            std::uint32_t>(capacity);
154  
            std::uint32_t>(capacity);
155  
        p->clear();
155  
        p->clear();
156  
    }
156  
    }
157  
    if(salt)
157  
    if(salt)
158  
    {
158  
    {
159  
        p->salt = salt;
159  
        p->salt = salt;
160  
    }
160  
    }
161  
    else
161  
    else
162  
    {
162  
    {
163  
        // VFALCO This would be better if it
163  
        // VFALCO This would be better if it
164  
        //        was random, but maybe this
164  
        //        was random, but maybe this
165  
        //        is good enough.
165  
        //        is good enough.
166  
        p->salt = reinterpret_cast<
166  
        p->salt = reinterpret_cast<
167  
            std::uintptr_t>(p);
167  
            std::uintptr_t>(p);
168  
    }
168  
    }
169  
    return p;
169  
    return p;
170  
}
170  
}
171  

171  

172  
//----------------------------------------------------------
172  
//----------------------------------------------------------
173  

173  

174  
void
174  
void
175  
object::
175  
object::
176  
revert_construct::
176  
revert_construct::
177  
destroy() noexcept
177  
destroy() noexcept
178  
{
178  
{
179  
    obj_->destroy();
179  
    obj_->destroy();
180  
}
180  
}
181  

181  

182  
//----------------------------------------------------------
182  
//----------------------------------------------------------
183  

183  

184  
void
184  
void
185  
object::
185  
object::
186  
revert_insert::
186  
revert_insert::
187  
destroy() noexcept
187  
destroy() noexcept
188  
{
188  
{
189  
    obj_->destroy(
189  
    obj_->destroy(
190  
        &(*obj_->t_)[size_],
190  
        &(*obj_->t_)[size_],
191  
        obj_->end());
191  
        obj_->end());
192  
}
192  
}
193  

193  

194  
//----------------------------------------------------------
194  
//----------------------------------------------------------
195  
//
195  
//
196  
// Construction
196  
// Construction
197  
//
197  
//
198  
//----------------------------------------------------------
198  
//----------------------------------------------------------
199  

199  

200  
object::
200  
object::
201  
object(detail::unchecked_object&& uo)
201  
object(detail::unchecked_object&& uo)
202  
    : sp_(uo.storage())
202  
    : sp_(uo.storage())
203  
{
203  
{
204  
    if(uo.size() == 0)
204  
    if(uo.size() == 0)
205  
    {
205  
    {
206  
        t_ = &empty_;
206  
        t_ = &empty_;
207  
        return;
207  
        return;
208  
    }
208  
    }
209  
    // should already be checked
209  
    // should already be checked
210  
    BOOST_ASSERT(
210  
    BOOST_ASSERT(
211  
        uo.size() <= max_size());
211  
        uo.size() <= max_size());
212  
    t_ = table::allocate(
212  
    t_ = table::allocate(
213  
        uo.size(), 0, sp_);
213  
        uo.size(), 0, sp_);
214  

214  

215  
    // insert all elements, keeping
215  
    // insert all elements, keeping
216  
    // the last of any duplicate keys.
216  
    // the last of any duplicate keys.
217  
    auto dest = begin();
217  
    auto dest = begin();
218  
    auto src = uo.release();
218  
    auto src = uo.release();
219  
    auto const end = src + 2 * uo.size();
219  
    auto const end = src + 2 * uo.size();
220  
    if(t_->is_small())
220  
    if(t_->is_small())
221  
    {
221  
    {
222  
        t_->size = 0;
222  
        t_->size = 0;
223  
        while(src != end)
223  
        while(src != end)
224  
        {
224  
        {
225  
            access::construct_key_value_pair(
225  
            access::construct_key_value_pair(
226  
                dest, pilfer(src[0]), pilfer(src[1]));
226  
                dest, pilfer(src[0]), pilfer(src[1]));
227  
            src += 2;
227  
            src += 2;
228  
            auto result = detail::find_in_object(*this, dest->key());
228  
            auto result = detail::find_in_object(*this, dest->key());
229  
            if(! result.first)
229  
            if(! result.first)
230  
            {
230  
            {
231  
                ++dest;
231  
                ++dest;
232  
                ++t_->size;
232  
                ++t_->size;
233  
                continue;
233  
                continue;
234  
            }
234  
            }
235  
            // handle duplicate
235  
            // handle duplicate
236  
            auto& v = *result.first;
236  
            auto& v = *result.first;
237  
            // don't bother to check if
237  
            // don't bother to check if
238  
            // storage deallocate is trivial
238  
            // storage deallocate is trivial
239  
            v.~key_value_pair();
239  
            v.~key_value_pair();
240  
            // trivial relocate
240  
            // trivial relocate
241  
            std::memcpy(
241  
            std::memcpy(
242  
                static_cast<void*>(&v),
242  
                static_cast<void*>(&v),
243  
                    dest, sizeof(v));
243  
                    dest, sizeof(v));
244  
        }
244  
        }
245  
        return;
245  
        return;
246  
    }
246  
    }
247  
    while(src != end)
247  
    while(src != end)
248  
    {
248  
    {
249  
        access::construct_key_value_pair(
249  
        access::construct_key_value_pair(
250  
            dest, pilfer(src[0]), pilfer(src[1]));
250  
            dest, pilfer(src[0]), pilfer(src[1]));
251  
        src += 2;
251  
        src += 2;
252  
        auto& head = t_->bucket(dest->key());
252  
        auto& head = t_->bucket(dest->key());
253  
        auto i = head;
253  
        auto i = head;
254  
        for(;;)
254  
        for(;;)
255  
        {
255  
        {
256  
            if(i == null_index_)
256  
            if(i == null_index_)
257  
            {
257  
            {
258  
                // end of bucket
258  
                // end of bucket
259  
                access::next(
259  
                access::next(
260  
                    *dest) = head;
260  
                    *dest) = head;
261  
                head = static_cast<index_t>(
261  
                head = static_cast<index_t>(
262  
                    dest - begin());
262  
                    dest - begin());
263  
                ++dest;
263  
                ++dest;
264  
                break;
264  
                break;
265  
            }
265  
            }
266  
            auto& v = (*t_)[i];
266  
            auto& v = (*t_)[i];
267  
            if(v.key() != dest->key())
267  
            if(v.key() != dest->key())
268  
            {
268  
            {
269  
                i = access::next(v);
269  
                i = access::next(v);
270  
                continue;
270  
                continue;
271  
            }
271  
            }
272  

272  

273  
            // handle duplicate
273  
            // handle duplicate
274  
            access::next(*dest) =
274  
            access::next(*dest) =
275  
                access::next(v);
275  
                access::next(v);
276  
            // don't bother to check if
276  
            // don't bother to check if
277  
            // storage deallocate is trivial
277  
            // storage deallocate is trivial
278  
            v.~key_value_pair();
278  
            v.~key_value_pair();
279  
            // trivial relocate
279  
            // trivial relocate
280  
            std::memcpy(
280  
            std::memcpy(
281  
                static_cast<void*>(&v),
281  
                static_cast<void*>(&v),
282  
                    dest, sizeof(v));
282  
                    dest, sizeof(v));
283  
            break;
283  
            break;
284  
        }
284  
        }
285  
    }
285  
    }
286  
    t_->size = static_cast<
286  
    t_->size = static_cast<
287  
        index_t>(dest - begin());
287  
        index_t>(dest - begin());
288  
}
288  
}
289  

289  

290  
object::
290  
object::
291  
~object() noexcept
291  
~object() noexcept
292  
{
292  
{
293  
    if(sp_.is_not_shared_and_deallocate_is_trivial())
293  
    if(sp_.is_not_shared_and_deallocate_is_trivial())
294  
        return;
294  
        return;
295  
    if(t_->capacity == 0)
295  
    if(t_->capacity == 0)
296  
        return;
296  
        return;
297  
    destroy();
297  
    destroy();
298  
}
298  
}
299  

299  

300  
object::
300  
object::
301  
object(
301  
object(
302  
    std::size_t min_capacity,
302  
    std::size_t min_capacity,
303  
    storage_ptr sp)
303  
    storage_ptr sp)
304  
    : sp_(std::move(sp))
304  
    : sp_(std::move(sp))
305  
    , t_(&empty_)
305  
    , t_(&empty_)
306  
{
306  
{
307  
    reserve(min_capacity);
307  
    reserve(min_capacity);
308  
}
308  
}
309  

309  

310  
object::
310  
object::
311  
object(object&& other) noexcept
311  
object(object&& other) noexcept
312  
    : sp_(other.sp_)
312  
    : sp_(other.sp_)
313  
    , t_(detail::exchange(
313  
    , t_(detail::exchange(
314  
        other.t_, &empty_))
314  
        other.t_, &empty_))
315  
{
315  
{
316  
}
316  
}
317  

317  

318  
object::
318  
object::
319  
object(
319  
object(
320  
    object&& other,
320  
    object&& other,
321  
    storage_ptr sp)
321  
    storage_ptr sp)
322  
    : sp_(std::move(sp))
322  
    : sp_(std::move(sp))
323  
{
323  
{
324  
    if(*sp_ == *other.sp_)
324  
    if(*sp_ == *other.sp_)
325  
    {
325  
    {
326  
        t_ = detail::exchange(
326  
        t_ = detail::exchange(
327  
            other.t_, &empty_);
327  
            other.t_, &empty_);
328  
        return;
328  
        return;
329  
    }
329  
    }
330  

330  

331  
    t_ = &empty_;
331  
    t_ = &empty_;
332  
    object(other, sp_).swap(*this);
332  
    object(other, sp_).swap(*this);
333  
}
333  
}
334  

334  

335  
object::
335  
object::
336  
object(
336  
object(
337  
    object const& other,
337  
    object const& other,
338  
    storage_ptr sp)
338  
    storage_ptr sp)
339  
    : sp_(std::move(sp))
339  
    : sp_(std::move(sp))
340  
    , t_(&empty_)
340  
    , t_(&empty_)
341  
{
341  
{
342  
    reserve(other.size());
342  
    reserve(other.size());
343  
    revert_construct r(*this);
343  
    revert_construct r(*this);
344  
    if(t_->is_small())
344  
    if(t_->is_small())
345  
    {
345  
    {
346  
        for(auto const& v : other)
346  
        for(auto const& v : other)
347  
        {
347  
        {
348  
            ::new(end())
348  
            ::new(end())
349  
                key_value_pair(v, sp_);
349  
                key_value_pair(v, sp_);
350  
            ++t_->size;
350  
            ++t_->size;
351  
        }
351  
        }
352  
        r.commit();
352  
        r.commit();
353  
        return;
353  
        return;
354  
    }
354  
    }
355  
    for(auto const& v : other)
355  
    for(auto const& v : other)
356  
    {
356  
    {
357  
        // skip duplicate checking
357  
        // skip duplicate checking
358  
        auto& head =
358  
        auto& head =
359  
            t_->bucket(v.key());
359  
            t_->bucket(v.key());
360  
        auto pv = ::new(end())
360  
        auto pv = ::new(end())
361  
            key_value_pair(v, sp_);
361  
            key_value_pair(v, sp_);
362  
        access::next(*pv) = head;
362  
        access::next(*pv) = head;
363  
        head = t_->size;
363  
        head = t_->size;
364  
        ++t_->size;
364  
        ++t_->size;
365  
    }
365  
    }
366  
    r.commit();
366  
    r.commit();
367  
}
367  
}
368  

368  

369  
object::
369  
object::
370  
object(
370  
object(
371  
    std::initializer_list<std::pair<
371  
    std::initializer_list<std::pair<
372  
        string_view, value_ref>> init,
372  
        string_view, value_ref>> init,
373  
    std::size_t min_capacity,
373  
    std::size_t min_capacity,
374  
    storage_ptr sp)
374  
    storage_ptr sp)
375  
    : sp_(std::move(sp))
375  
    : sp_(std::move(sp))
376  
    , t_(&empty_)
376  
    , t_(&empty_)
377  
{
377  
{
378  
    if( min_capacity < init.size())
378  
    if( min_capacity < init.size())
379  
        min_capacity = init.size();
379  
        min_capacity = init.size();
380  
    reserve(min_capacity);
380  
    reserve(min_capacity);
381  
    revert_construct r(*this);
381  
    revert_construct r(*this);
382  
    insert(init);
382  
    insert(init);
383  
    r.commit();
383  
    r.commit();
384  
}
384  
}
385  

385  

386  
//----------------------------------------------------------
386  
//----------------------------------------------------------
387  
//
387  
//
388  
// Assignment
388  
// Assignment
389  
//
389  
//
390  
//----------------------------------------------------------
390  
//----------------------------------------------------------
391  

391  

392  
object&
392  
object&
393  
object::
393  
object::
394  
operator=(object const& other)
394  
operator=(object const& other)
395  
{
395  
{
396  
    object tmp(other, sp_);
396  
    object tmp(other, sp_);
397  
    this->~object();
397  
    this->~object();
398  
    ::new(this) object(pilfer(tmp));
398  
    ::new(this) object(pilfer(tmp));
399  
    return *this;
399  
    return *this;
400  
}
400  
}
401  

401  

402  
object&
402  
object&
403  
object::
403  
object::
404  
operator=(object&& other)
404  
operator=(object&& other)
405  
{
405  
{
406  
    object tmp(std::move(other), sp_);
406  
    object tmp(std::move(other), sp_);
407  
    this->~object();
407  
    this->~object();
408  
    ::new(this) object(pilfer(tmp));
408  
    ::new(this) object(pilfer(tmp));
409  
    return *this;
409  
    return *this;
410  
}
410  
}
411  

411  

412  
object&
412  
object&
413  
object::
413  
object::
414  
operator=(
414  
operator=(
415  
    std::initializer_list<std::pair<
415  
    std::initializer_list<std::pair<
416  
        string_view, value_ref>> init)
416  
        string_view, value_ref>> init)
417  
{
417  
{
418  
    object tmp(init, sp_);
418  
    object tmp(init, sp_);
419  
    this->~object();
419  
    this->~object();
420  
    ::new(this) object(pilfer(tmp));
420  
    ::new(this) object(pilfer(tmp));
421  
    return *this;
421  
    return *this;
422  
}
422  
}
423  

423  

424  
//----------------------------------------------------------
424  
//----------------------------------------------------------
425  
//
425  
//
426  
// Lookup
426  
// Lookup
427  
//
427  
//
428  
//----------------------------------------------------------
428  
//----------------------------------------------------------
429  

429  

430  
system::result<value&>
430  
system::result<value&>
431  
object::
431  
object::
432  
try_at(string_view key) noexcept
432  
try_at(string_view key) noexcept
433  
{
433  
{
434  
    auto it = find(key);
434  
    auto it = find(key);
435  
    if( it != end() )
435  
    if( it != end() )
436  
        return it->value();
436  
        return it->value();
437  

437  

438  
    system::error_code ec;
438  
    system::error_code ec;
439  
    BOOST_JSON_FAIL(ec, error::out_of_range);
439  
    BOOST_JSON_FAIL(ec, error::out_of_range);
440  
    return ec;
440  
    return ec;
441  
}
441  
}
442  

442  

443  
system::result<value const&>
443  
system::result<value const&>
444  
object::
444  
object::
445  
try_at(string_view key) const noexcept
445  
try_at(string_view key) const noexcept
446  
{
446  
{
447  
    auto it = find(key);
447  
    auto it = find(key);
448  
    if( it != end() )
448  
    if( it != end() )
449  
        return it->value();
449  
        return it->value();
450  

450  

451  
    system::error_code ec;
451  
    system::error_code ec;
452  
    BOOST_JSON_FAIL(ec, error::out_of_range);
452  
    BOOST_JSON_FAIL(ec, error::out_of_range);
453  
    return ec;
453  
    return ec;
454  
}
454  
}
455  

455  

456  
value const&
456  
value const&
457  
object::
457  
object::
458  
at(string_view key, source_location const& loc) const&
458  
at(string_view key, source_location const& loc) const&
459  
{
459  
{
460  
    return try_at(key).value(loc);
460  
    return try_at(key).value(loc);
461  
}
461  
}
462  

462  

463  
//----------------------------------------------------------
463  
//----------------------------------------------------------
464  
//
464  
//
465  
// Modifiers
465  
// Modifiers
466  
//
466  
//
467  
//----------------------------------------------------------
467  
//----------------------------------------------------------
468  

468  

469  
void
469  
void
470  
object::
470  
object::
471  
clear() noexcept
471  
clear() noexcept
472  
{
472  
{
473  
    if(empty())
473  
    if(empty())
474  
        return;
474  
        return;
475  
    if(! sp_.is_not_shared_and_deallocate_is_trivial())
475  
    if(! sp_.is_not_shared_and_deallocate_is_trivial())
476  
        destroy(begin(), end());
476  
        destroy(begin(), end());
477  
    if(! t_->is_small())
477  
    if(! t_->is_small())
478  
        t_->clear();
478  
        t_->clear();
479  
    t_->size = 0;
479  
    t_->size = 0;
480  
}
480  
}
481  

481  

482  
void
482  
void
483  
object::
483  
object::
484  
insert(
484  
insert(
485  
    std::initializer_list<std::pair<
485  
    std::initializer_list<std::pair<
486  
        string_view, value_ref>> init)
486  
        string_view, value_ref>> init)
487  
{
487  
{
488  
    auto const n0 = size();
488  
    auto const n0 = size();
489  
    if(init.size() > max_size() - n0)
489  
    if(init.size() > max_size() - n0)
490  
    {
490  
    {
491  
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
491  
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
492  
        detail::throw_system_error( error::object_too_large, &loc );
492  
        detail::throw_system_error( error::object_too_large, &loc );
493  
    }
493  
    }
494  
    revert_insert r( *this, n0 + init.size() );
494  
    revert_insert r( *this, n0 + init.size() );
495  
    if(t_->is_small())
495  
    if(t_->is_small())
496  
    {
496  
    {
497  
        for(auto& iv : init)
497  
        for(auto& iv : init)
498  
        {
498  
        {
499  
            auto result =
499  
            auto result =
500  
                detail::find_in_object(*this, iv.first);
500  
                detail::find_in_object(*this, iv.first);
501  
            if(result.first)
501  
            if(result.first)
502  
            {
502  
            {
503  
                // ignore duplicate
503  
                // ignore duplicate
504  
                continue;
504  
                continue;
505  
            }
505  
            }
506  
            ::new(end()) key_value_pair(
506  
            ::new(end()) key_value_pair(
507  
                iv.first,
507  
                iv.first,
508  
                iv.second.make_value(sp_));
508  
                iv.second.make_value(sp_));
509  
            ++t_->size;
509  
            ++t_->size;
510  
        }
510  
        }
511  
        r.commit();
511  
        r.commit();
512  
        return;
512  
        return;
513  
    }
513  
    }
514  
    for(auto& iv : init)
514  
    for(auto& iv : init)
515  
    {
515  
    {
516  
        auto& head = t_->bucket(iv.first);
516  
        auto& head = t_->bucket(iv.first);
517  
        auto i = head;
517  
        auto i = head;
518  
        for(;;)
518  
        for(;;)
519  
        {
519  
        {
520  
            if(i == null_index_)
520  
            if(i == null_index_)
521  
            {
521  
            {
522  
                // VFALCO value_ref should construct
522  
                // VFALCO value_ref should construct
523  
                // a key_value_pair using placement
523  
                // a key_value_pair using placement
524  
                auto& v = *::new(end())
524  
                auto& v = *::new(end())
525  
                    key_value_pair(
525  
                    key_value_pair(
526  
                        iv.first,
526  
                        iv.first,
527  
                        iv.second.make_value(sp_));
527  
                        iv.second.make_value(sp_));
528  
                access::next(v) = head;
528  
                access::next(v) = head;
529  
                head = static_cast<index_t>(
529  
                head = static_cast<index_t>(
530  
                    t_->size);
530  
                    t_->size);
531  
                ++t_->size;
531  
                ++t_->size;
532  
                break;
532  
                break;
533  
            }
533  
            }
534  
            auto& v = (*t_)[i];
534  
            auto& v = (*t_)[i];
535  
            if(v.key() == iv.first)
535  
            if(v.key() == iv.first)
536  
            {
536  
            {
537  
                // ignore duplicate
537  
                // ignore duplicate
538  
                break;
538  
                break;
539  
            }
539  
            }
540  
            i = access::next(v);
540  
            i = access::next(v);
541  
        }
541  
        }
542  
    }
542  
    }
543  
    r.commit();
543  
    r.commit();
544  
}
544  
}
545  

545  

546  
auto
546  
auto
547  
object::
547  
object::
548  
erase(const_iterator pos) noexcept ->
548  
erase(const_iterator pos) noexcept ->
549  
    iterator
549  
    iterator
550  
{
550  
{
551  
    return do_erase(pos,
551  
    return do_erase(pos,
552  
        [this](iterator p) {
552  
        [this](iterator p) {
553  
            // the casts silence warnings
553  
            // the casts silence warnings
554  
            std::memcpy(
554  
            std::memcpy(
555  
                static_cast<void*>(p),
555  
                static_cast<void*>(p),
556  
                static_cast<void const*>(end()),
556  
                static_cast<void const*>(end()),
557  
                sizeof(*p));
557  
                sizeof(*p));
558  
        },
558  
        },
559  
        [this](iterator p) {
559  
        [this](iterator p) {
560  
            reindex_relocate(end(), p);
560  
            reindex_relocate(end(), p);
561  
        });
561  
        });
562  
}
562  
}
563  

563  

564  
auto
564  
auto
565  
object::
565  
object::
566  
erase(string_view key) noexcept ->
566  
erase(string_view key) noexcept ->
567  
    std::size_t
567  
    std::size_t
568  
{
568  
{
569  
    auto it = find(key);
569  
    auto it = find(key);
570  
    if(it == end())
570  
    if(it == end())
571  
        return 0;
571  
        return 0;
572  
    erase(it);
572  
    erase(it);
573  
    return 1;
573  
    return 1;
574  
}
574  
}
575  

575  

576  
auto
576  
auto
577  
object::
577  
object::
578  
stable_erase(const_iterator pos) noexcept ->
578  
stable_erase(const_iterator pos) noexcept ->
579  
    iterator
579  
    iterator
580  
{
580  
{
581  
    return do_erase(pos,
581  
    return do_erase(pos,
582  
        [this](iterator p) {
582  
        [this](iterator p) {
583  
            // the casts silence warnings
583  
            // the casts silence warnings
584  
            std::memmove(
584  
            std::memmove(
585  
                static_cast<void*>(p),
585  
                static_cast<void*>(p),
586  
                static_cast<void const*>(p + 1),
586  
                static_cast<void const*>(p + 1),
587  
                sizeof(*p) * (end() - p));
587  
                sizeof(*p) * (end() - p));
588  
        },
588  
        },
589  
        [this](iterator p) {
589  
        [this](iterator p) {
590  
            for (; p != end(); ++p)
590  
            for (; p != end(); ++p)
591  
            {
591  
            {
592  
                reindex_relocate(p + 1, p);
592  
                reindex_relocate(p + 1, p);
593  
            }
593  
            }
594  
        });
594  
        });
595  
}
595  
}
596  

596  

597  
auto
597  
auto
598  
object::
598  
object::
599  
stable_erase(string_view key) noexcept ->
599  
stable_erase(string_view key) noexcept ->
600  
    std::size_t
600  
    std::size_t
601  
{
601  
{
602  
    auto it = find(key);
602  
    auto it = find(key);
603  
    if(it == end())
603  
    if(it == end())
604  
        return 0;
604  
        return 0;
605  
    stable_erase(it);
605  
    stable_erase(it);
606  
    return 1;
606  
    return 1;
607  
}
607  
}
608  

608  

609  
void
609  
void
610  
object::
610  
object::
611  
swap(object& other)
611  
swap(object& other)
612  
{
612  
{
613  
    if(*sp_ == *other.sp_)
613  
    if(*sp_ == *other.sp_)
614  
    {
614  
    {
615  
        t_ = detail::exchange(
615  
        t_ = detail::exchange(
616  
            other.t_, t_);
616  
            other.t_, t_);
617  
        return;
617  
        return;
618  
    }
618  
    }
619  
    object temp1(
619  
    object temp1(
620  
        std::move(*this),
620  
        std::move(*this),
621  
        other.storage());
621  
        other.storage());
622  
    object temp2(
622  
    object temp2(
623  
        std::move(other),
623  
        std::move(other),
624  
        this->storage());
624  
        this->storage());
625  
    other.~object();
625  
    other.~object();
626  
    ::new(&other) object(pilfer(temp1));
626  
    ::new(&other) object(pilfer(temp1));
627  
    this->~object();
627  
    this->~object();
628  
    ::new(this) object(pilfer(temp2));
628  
    ::new(this) object(pilfer(temp2));
629  
}
629  
}
630  

630  

631  
//----------------------------------------------------------
631  
//----------------------------------------------------------
632  
//
632  
//
633  
// Lookup
633  
// Lookup
634  
//
634  
//
635  
//----------------------------------------------------------
635  
//----------------------------------------------------------
636  

636  

637  
auto
637  
auto
638  
object::
638  
object::
639  
operator[](string_view key) ->
639  
operator[](string_view key) ->
640  
    value&
640  
    value&
641  
{
641  
{
642  
    auto const result =
642  
    auto const result =
643  
        emplace(key, nullptr);
643  
        emplace(key, nullptr);
644  
    return result.first->value();
644  
    return result.first->value();
645  
}
645  
}
646  

646  

647  
auto
647  
auto
648  
object::
648  
object::
649  
count(string_view key) const noexcept ->
649  
count(string_view key) const noexcept ->
650  
    std::size_t
650  
    std::size_t
651  
{
651  
{
652  
    if(find(key) == end())
652  
    if(find(key) == end())
653  
        return 0;
653  
        return 0;
654  
    return 1;
654  
    return 1;
655  
}
655  
}
656  

656  

657  
auto
657  
auto
658  
object::
658  
object::
659  
find(string_view key) noexcept ->
659  
find(string_view key) noexcept ->
660  
    iterator
660  
    iterator
661  
{
661  
{
662  
    if(empty())
662  
    if(empty())
663  
        return end();
663  
        return end();
664  
    auto const p =
664  
    auto const p =
665  
        detail::find_in_object(*this, key).first;
665  
        detail::find_in_object(*this, key).first;
666  
    if(p)
666  
    if(p)
667  
        return p;
667  
        return p;
668  
    return end();
668  
    return end();
669  
}
669  
}
670  

670  

671  
auto
671  
auto
672  
object::
672  
object::
673  
find(string_view key) const noexcept ->
673  
find(string_view key) const noexcept ->
674  
    const_iterator
674  
    const_iterator
675  
{
675  
{
676  
    if(empty())
676  
    if(empty())
677  
        return end();
677  
        return end();
678  
    auto const p =
678  
    auto const p =
679  
        detail::find_in_object(*this, key).first;
679  
        detail::find_in_object(*this, key).first;
680  
    if(p)
680  
    if(p)
681  
        return p;
681  
        return p;
682  
    return end();
682  
    return end();
683  
}
683  
}
684  

684  

685  
bool
685  
bool
686  
object::
686  
object::
687  
contains(
687  
contains(
688  
    string_view key) const noexcept
688  
    string_view key) const noexcept
689  
{
689  
{
690  
    if(empty())
690  
    if(empty())
691  
        return false;
691  
        return false;
692  
    return detail::find_in_object(*this, key).first
692  
    return detail::find_in_object(*this, key).first
693  
        != nullptr;
693  
        != nullptr;
694  
}
694  
}
695  

695  

696  
value const*
696  
value const*
697  
object::
697  
object::
698  
if_contains(
698  
if_contains(
699  
    string_view key) const noexcept
699  
    string_view key) const noexcept
700  
{
700  
{
701  
    auto const it = find(key);
701  
    auto const it = find(key);
702  
    if(it != end())
702  
    if(it != end())
703  
        return &it->value();
703  
        return &it->value();
704  
    return nullptr;
704  
    return nullptr;
705  
}
705  
}
706  

706  

707  
value*
707  
value*
708  
object::
708  
object::
709  
if_contains(
709  
if_contains(
710  
    string_view key) noexcept
710  
    string_view key) noexcept
711  
{
711  
{
712  
    auto const it = find(key);
712  
    auto const it = find(key);
713  
    if(it != end())
713  
    if(it != end())
714  
        return &it->value();
714  
        return &it->value();
715  
    return nullptr;
715  
    return nullptr;
716  
}
716  
}
717  

717  

718  
//----------------------------------------------------------
718  
//----------------------------------------------------------
719  
//
719  
//
720  
// (private)
720  
// (private)
721  
//
721  
//
722  
//----------------------------------------------------------
722  
//----------------------------------------------------------
723  

723  

724  
key_value_pair*
724  
key_value_pair*
725  
object::
725  
object::
726  
insert_impl(
726  
insert_impl(
727  
    pilfered<key_value_pair> p,
727  
    pilfered<key_value_pair> p,
728  
    std::size_t hash)
728  
    std::size_t hash)
729  
{
729  
{
730  
    BOOST_ASSERT(
730  
    BOOST_ASSERT(
731  
        capacity() > size());
731  
        capacity() > size());
732  
    if(t_->is_small())
732  
    if(t_->is_small())
733  
    {
733  
    {
734  
        auto const pv = ::new(end())
734  
        auto const pv = ::new(end())
735  
            key_value_pair(p);
735  
            key_value_pair(p);
736  
        ++t_->size;
736  
        ++t_->size;
737  
        return pv;
737  
        return pv;
738  
    }
738  
    }
739  
    auto& head =
739  
    auto& head =
740  
        t_->bucket(hash);
740  
        t_->bucket(hash);
741  
    auto const pv = ::new(end())
741  
    auto const pv = ::new(end())
742  
        key_value_pair(p);
742  
        key_value_pair(p);
743  
    access::next(*pv) = head;
743  
    access::next(*pv) = head;
744  
    head = t_->size;
744  
    head = t_->size;
745  
    ++t_->size;
745  
    ++t_->size;
746  
    return pv;
746  
    return pv;
747  
}
747  
}
748  

748  

749  
// allocate new table, copy elements there, and rehash them
749  
// allocate new table, copy elements there, and rehash them
750  
object::table*
750  
object::table*
751  
object::
751  
object::
752  
reserve_impl(std::size_t new_capacity)
752  
reserve_impl(std::size_t new_capacity)
753  
{
753  
{
754  
    BOOST_ASSERT(
754  
    BOOST_ASSERT(
755  
        new_capacity > t_->capacity);
755  
        new_capacity > t_->capacity);
756  
    auto t = table::allocate(
756  
    auto t = table::allocate(
757  
        growth(new_capacity),
757  
        growth(new_capacity),
758  
            t_->salt, sp_);
758  
            t_->salt, sp_);
759  
    if(! empty())
759  
    if(! empty())
760  
        std::memcpy(
760  
        std::memcpy(
761  
            static_cast<
761  
            static_cast<
762  
                void*>(&(*t)[0]),
762  
                void*>(&(*t)[0]),
763  
            begin(),
763  
            begin(),
764  
            size() * sizeof(
764  
            size() * sizeof(
765  
                key_value_pair));
765  
                key_value_pair));
766  
    t->size = t_->size;
766  
    t->size = t_->size;
767  
    std::swap(t_, t);
767  
    std::swap(t_, t);
768  

768  

769  
    if(! t_->is_small())
769  
    if(! t_->is_small())
770  
    {
770  
    {
771  
        // rebuild hash table,
771  
        // rebuild hash table,
772  
        // without dup checks
772  
        // without dup checks
773  
        auto p = end();
773  
        auto p = end();
774  
        index_t i = t_->size;
774  
        index_t i = t_->size;
775  
        while(i-- > 0)
775  
        while(i-- > 0)
776  
        {
776  
        {
777  
            --p;
777  
            --p;
778  
            auto& head =
778  
            auto& head =
779  
                t_->bucket(p->key());
779  
                t_->bucket(p->key());
780  
            access::next(*p) = head;
780  
            access::next(*p) = head;
781  
            head = i;
781  
            head = i;
782  
        }
782  
        }
783  
    }
783  
    }
784  

784  

785  
    return t;
785  
    return t;
786  
}
786  
}
787  

787  

788  
bool
788  
bool
789  
object::
789  
object::
790  
equal(object const& other) const noexcept
790  
equal(object const& other) const noexcept
791  
{
791  
{
792  
    if(size() != other.size())
792  
    if(size() != other.size())
793  
        return false;
793  
        return false;
794  
    auto const end_ = other.end();
794  
    auto const end_ = other.end();
795  
    for(auto e : *this)
795  
    for(auto e : *this)
796  
    {
796  
    {
797  
        auto it = other.find(e.key());
797  
        auto it = other.find(e.key());
798  
        if(it == end_)
798  
        if(it == end_)
799  
            return false;
799  
            return false;
800  
        if(it->value() != e.value())
800  
        if(it->value() != e.value())
801  
            return false;
801  
            return false;
802  
    }
802  
    }
803  
    return true;
803  
    return true;
804  
}
804  
}
805  

805  

806  
std::size_t
806  
std::size_t
807  
object::
807  
object::
808  
growth(
808  
growth(
809  
    std::size_t new_size) const
809  
    std::size_t new_size) const
810  
{
810  
{
811  
    if(new_size > max_size())
811  
    if(new_size > max_size())
812  
    {
812  
    {
813  
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
813  
        BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
814  
        detail::throw_system_error( error::object_too_large, &loc );
814  
        detail::throw_system_error( error::object_too_large, &loc );
815  
    }
815  
    }
816  
    std::size_t const old = capacity();
816  
    std::size_t const old = capacity();
817  
    if(old > max_size() - old / 2)
817  
    if(old > max_size() - old / 2)
818  
        return new_size;
818  
        return new_size;
819  
    std::size_t const g =
819  
    std::size_t const g =
820  
        old + old / 2; // 1.5x
820  
        old + old / 2; // 1.5x
821  
    if(g < new_size)
821  
    if(g < new_size)
822  
        return new_size;
822  
        return new_size;
823  
    return g;
823  
    return g;
824  
}
824  
}
825  

825  

826  
void
826  
void
827  
object::
827  
object::
828  
remove(
828  
remove(
829  
    index_t& head,
829  
    index_t& head,
830  
    key_value_pair& v) noexcept
830  
    key_value_pair& v) noexcept
831  
{
831  
{
832  
    BOOST_ASSERT(! t_->is_small());
832  
    BOOST_ASSERT(! t_->is_small());
833  
    auto const i = static_cast<
833  
    auto const i = static_cast<
834  
        index_t>(&v - begin());
834  
        index_t>(&v - begin());
835  
    if(head == i)
835  
    if(head == i)
836  
    {
836  
    {
837  
        head = access::next(v);
837  
        head = access::next(v);
838  
        return;
838  
        return;
839  
    }
839  
    }
840  
    auto* pn =
840  
    auto* pn =
841  
        &access::next((*t_)[head]);
841  
        &access::next((*t_)[head]);
842  
    while(*pn != i)
842  
    while(*pn != i)
843  
        pn = &access::next((*t_)[*pn]);
843  
        pn = &access::next((*t_)[*pn]);
844  
    *pn = access::next(v);
844  
    *pn = access::next(v);
845  
}
845  
}
846  

846  

847  
void
847  
void
848  
object::
848  
object::
849  
destroy() noexcept
849  
destroy() noexcept
850  
{
850  
{
851  
    BOOST_ASSERT(t_->capacity > 0);
851  
    BOOST_ASSERT(t_->capacity > 0);
852  
    BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
852  
    BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
853  
    destroy(begin(), end());
853  
    destroy(begin(), end());
854  
    table::deallocate(t_, sp_);
854  
    table::deallocate(t_, sp_);
855  
}
855  
}
856  

856  

857  
void
857  
void
858  
object::
858  
object::
859  
destroy(
859  
destroy(
860  
    key_value_pair* first,
860  
    key_value_pair* first,
861  
    key_value_pair* last) noexcept
861  
    key_value_pair* last) noexcept
862  
{
862  
{
863  
    BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
863  
    BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
864  
    while(last != first)
864  
    while(last != first)
865  
        (--last)->~key_value_pair();
865  
        (--last)->~key_value_pair();
866  
}
866  
}
867  

867  

868  
template<class FS, class FB>
868  
template<class FS, class FB>
869  
auto
869  
auto
870  
object::
870  
object::
871  
do_erase(
871  
do_erase(
872  
    const_iterator pos,
872  
    const_iterator pos,
873  
    FS small_reloc,
873  
    FS small_reloc,
874  
    FB big_reloc) noexcept
874  
    FB big_reloc) noexcept
875  
    -> iterator
875  
    -> iterator
876  
{
876  
{
877  
    auto p = begin() + (pos - begin());
877  
    auto p = begin() + (pos - begin());
878  
    if(t_->is_small())
878  
    if(t_->is_small())
879  
    {
879  
    {
880  
        p->~value_type();
880  
        p->~value_type();
881  
        --t_->size;
881  
        --t_->size;
882  
        if(p != end())
882  
        if(p != end())
883  
        {
883  
        {
884  
            small_reloc(p);
884  
            small_reloc(p);
885  
        }
885  
        }
886  
        return p;
886  
        return p;
887  
    }
887  
    }
888  
    remove(t_->bucket(p->key()), *p);
888  
    remove(t_->bucket(p->key()), *p);
889  
    p->~value_type();
889  
    p->~value_type();
890  
    --t_->size;
890  
    --t_->size;
891  
    if(p != end())
891  
    if(p != end())
892  
    {
892  
    {
893  
        big_reloc(p);
893  
        big_reloc(p);
894  
    }
894  
    }
895  
    return p;
895  
    return p;
896  
}
896  
}
897  

897  

898  
void
898  
void
899  
object::
899  
object::
900  
reindex_relocate(
900  
reindex_relocate(
901  
    key_value_pair* src,
901  
    key_value_pair* src,
902  
    key_value_pair* dst) noexcept
902  
    key_value_pair* dst) noexcept
903  
{
903  
{
904  
    BOOST_ASSERT(! t_->is_small());
904  
    BOOST_ASSERT(! t_->is_small());
905  
    auto& head = t_->bucket(src->key());
905  
    auto& head = t_->bucket(src->key());
906  
    remove(head, *src);
906  
    remove(head, *src);
907  
    // the casts silence warnings
907  
    // the casts silence warnings
908  
    std::memcpy(
908  
    std::memcpy(
909  
        static_cast<void*>(dst),
909  
        static_cast<void*>(dst),
910  
        static_cast<void const*>(src),
910  
        static_cast<void const*>(src),
911  
        sizeof(*dst));
911  
        sizeof(*dst));
912  
    access::next(*dst) = head;
912  
    access::next(*dst) = head;
913  
    head = static_cast<
913  
    head = static_cast<
914  
        index_t>(dst - begin());
914  
        index_t>(dst - begin());
915  
}
915  
}
916  

916  

917  
} // namespace json
917  
} // namespace json
918  
} // namespace boost
918  
} // namespace boost
919  

919  

920  
//----------------------------------------------------------
920  
//----------------------------------------------------------
921  
//
921  
//
922  
// std::hash specialization
922  
// std::hash specialization
923  
//
923  
//
924  
//----------------------------------------------------------
924  
//----------------------------------------------------------
925  

925  

926  
std::size_t
926  
std::size_t
927  
std::hash<::boost::json::object>::operator()(
927  
std::hash<::boost::json::object>::operator()(
928  
    ::boost::json::object const& jo) const noexcept
928  
    ::boost::json::object const& jo) const noexcept
929  
{
929  
{
930  
    return ::boost::hash< ::boost::json::object >()( jo );
930  
    return ::boost::hash< ::boost::json::object >()( jo );
931  
}
931  
}
932  

932  

933  
//----------------------------------------------------------
933  
//----------------------------------------------------------
934  

934  

935  

935  

936  
#endif
936  
#endif