impl/value.ipp

99.6% Lines (449/451) 100.0% Functions (74/74)
impl/value.ipp
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
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)
6 //
7 // Official repository: https://github.com/boostorg/json
8 //
9
10 #ifndef BOOST_JSON_IMPL_VALUE_IPP
11 #define BOOST_JSON_IMPL_VALUE_IPP
12
13 #include <boost/container_hash/hash.hpp>
14 #include <boost/json/value.hpp>
15 #include <boost/json/parser.hpp>
16 #include <cstring>
17 #include <istream>
18 #include <limits>
19 #include <new>
20 #include <utility>
21
22 namespace boost {
23 namespace json {
24
25 namespace
26 {
27
28 int parse_depth_xalloc = std::ios::xalloc();
29 int parse_flags_xalloc = std::ios::xalloc();
30
31 struct value_hasher
32 {
33 std::size_t& seed;
34
35 template< class T >
36 248 void operator()( T&& t ) const noexcept
37 {
38 248 boost::hash_combine( seed, t );
39 248 }
40 };
41
42 enum class stream_parse_flags
43 {
44 allow_comments = 1 << 0,
45 allow_trailing_commas = 1 << 1,
46 allow_invalid_utf8 = 1 << 2,
47 };
48
49 long
50 3 to_bitmask( parse_options const& opts )
51 {
52 using E = stream_parse_flags;
53 return
54 3 (opts.allow_comments ?
55 3 static_cast<long>(E::allow_comments) : 0) |
56 3 (opts.allow_trailing_commas ?
57 static_cast<long>(E::allow_trailing_commas) : 0) |
58 3 (opts.allow_invalid_utf8 ?
59 3 static_cast<long>(E::allow_invalid_utf8) : 0);
60 }
61
62 parse_options
63 9 get_parse_options( std::istream& is )
64 {
65 9 long const flags = is.iword(parse_flags_xalloc);
66
67 using E = stream_parse_flags;
68 9 parse_options opts;
69 9 opts.allow_comments =
70 9 flags & static_cast<long>(E::allow_comments) ? true : false;
71 9 opts.allow_trailing_commas =
72 9 flags & static_cast<long>(E::allow_trailing_commas) ? true : false;
73 9 opts.allow_invalid_utf8 =
74 9 flags & static_cast<long>(E::allow_invalid_utf8) ? true : false;
75 9 return opts;
76 }
77
78 } // namespace
79
80 2178636 value::
81 ~value() noexcept
82 {
83 2178636 switch(kind())
84 {
85 2112969 case json::kind::null:
86 case json::kind::bool_:
87 case json::kind::int64:
88 case json::kind::uint64:
89 case json::kind::double_:
90 2112969 sca_.~scalar();
91 2112969 break;
92
93 27376 case json::kind::string:
94 27376 str_.~string();
95 27376 break;
96
97 3087 case json::kind::array:
98 3087 arr_.~array();
99 3087 break;
100
101 35204 case json::kind::object:
102 35204 obj_.~object();
103 35204 break;
104 }
105 2178636 }
106
107 9490 value::
108 value(
109 value const& other,
110 9490 storage_ptr sp)
111 {
112 9490 switch(other.kind())
113 {
114 2034 case json::kind::null:
115 6102 ::new(&sca_) scalar(
116 2034 std::move(sp));
117 2034 break;
118
119 121 case json::kind::bool_:
120 363 ::new(&sca_) scalar(
121 121 other.sca_.b,
122 121 std::move(sp));
123 121 break;
124
125 7006 case json::kind::int64:
126 21018 ::new(&sca_) scalar(
127 7006 other.sca_.i,
128 7006 std::move(sp));
129 7006 break;
130
131 35 case json::kind::uint64:
132 105 ::new(&sca_) scalar(
133 35 other.sca_.u,
134 35 std::move(sp));
135 35 break;
136
137 12 case json::kind::double_:
138 36 ::new(&sca_) scalar(
139 12 other.sca_.d,
140 12 std::move(sp));
141 12 break;
142
143 130 case json::kind::string:
144 17 ::new(&str_) string(
145 130 other.str_,
146 164 std::move(sp));
147 113 break;
148
149 122 case json::kind::array:
150 26 ::new(&arr_) array(
151 122 other.arr_,
152 174 std::move(sp));
153 96 break;
154
155 30 case json::kind::object:
156 10 ::new(&obj_) object(
157 30 other.obj_,
158 50 std::move(sp));
159 20 break;
160 }
161 9437 }
162
163 3784 value::
164 3784 value(value&& other) noexcept
165 {
166 3784 relocate(this, other);
167 3784 ::new(&other.sca_) scalar(sp_);
168 3784 }
169
170 11452 value::
171 value(
172 value&& other,
173 11452 storage_ptr sp)
174 {
175 11452 switch(other.kind())
176 {
177 77 case json::kind::null:
178 229 ::new(&sca_) scalar(
179 77 std::move(sp));
180 77 break;
181
182 190 case json::kind::bool_:
183 570 ::new(&sca_) scalar(
184 190 other.sca_.b, std::move(sp));
185 190 break;
186
187 10452 case json::kind::int64:
188 31356 ::new(&sca_) scalar(
189 10452 other.sca_.i, std::move(sp));
190 10452 break;
191
192 75 case json::kind::uint64:
193 225 ::new(&sca_) scalar(
194 75 other.sca_.u, std::move(sp));
195 75 break;
196
197 34 case json::kind::double_:
198 102 ::new(&sca_) scalar(
199 34 other.sca_.d, std::move(sp));
200 34 break;
201
202 336 case json::kind::string:
203 4 ::new(&str_) string(
204 336 std::move(other.str_),
205 680 std::move(sp));
206 332 break;
207
208 224 case json::kind::array:
209 5 ::new(&arr_) array(
210 224 std::move(other.arr_),
211 458 std::move(sp));
212 219 break;
213
214 64 case json::kind::object:
215 13 ::new(&obj_) object(
216 64 std::move(other.obj_),
217 154 std::move(sp));
218 51 break;
219 }
220 11430 }
221
222 //----------------------------------------------------------
223 //
224 // Conversion
225 //
226 //----------------------------------------------------------
227
228 336 value::
229 value(
230 std::initializer_list<value_ref> init,
231 336 storage_ptr sp)
232 {
233 336 if(value_ref::maybe_object(init))
234 {
235 ::new(&obj_) object(
236 value_ref::make_object(
237 103 init, std::move(sp)));
238 }
239 else
240 {
241 233 if( init.size() == 1 )
242 {
243 13 ::new(&sca_) scalar();
244 13 value temp = init.begin()->make_value( std::move(sp) );
245 13 swap(temp);
246 13 }
247 else
248 {
249 ::new(&arr_) array(
250 value_ref::make_array(
251 220 init, std::move(sp)));
252 }
253 }
254 336 }
255
256 //----------------------------------------------------------
257 //
258 // Assignment
259 //
260 //----------------------------------------------------------
261
262 value&
263 38 value::
264 operator=(value const& other)
265 {
266 76 value(other,
267 32 storage()).swap(*this);
268 32 return *this;
269 }
270
271 value&
272 82 value::
273 operator=(value&& other)
274 {
275 164 value(std::move(other),
276 63 storage()).swap(*this);
277 63 return *this;
278 }
279
280 value&
281 13 value::
282 operator=(
283 std::initializer_list<value_ref> init)
284 {
285 26 value(init,
286 13 storage()).swap(*this);
287 13 return *this;
288 }
289
290 value&
291 2 value::
292 operator=(string_view s)
293 {
294 2 value(s, storage()).swap(*this);
295 2 return *this;
296 }
297
298 value&
299 28 value::
300 operator=(char const* s)
301 {
302 28 value(s, storage()).swap(*this);
303 28 return *this;
304 }
305
306 value&
307 12 value::
308 operator=(string const& str)
309 {
310 12 value(str, storage()).swap(*this);
311 12 return *this;
312 }
313
314 value&
315 7 value::
316 operator=(string&& str)
317 {
318 14 value(std::move(str),
319 7 storage()).swap(*this);
320 7 return *this;
321 }
322
323 value&
324 4 value::
325 operator=(array const& arr)
326 {
327 4 value(arr, storage()).swap(*this);
328 4 return *this;
329 }
330
331 value&
332 21 value::
333 operator=(array&& arr)
334 {
335 42 value(std::move(arr),
336 21 storage()).swap(*this);
337 21 return *this;
338 }
339
340 value&
341 4 value::
342 operator=(object const& obj)
343 {
344 4 value(obj, storage()).swap(*this);
345 4 return *this;
346 }
347
348 value&
349 54 value::
350 operator=(object&& obj)
351 {
352 108 value(std::move(obj),
353 54 storage()).swap(*this);
354 54 return *this;
355 }
356
357 //----------------------------------------------------------
358 //
359 // Accessors
360 //
361 //----------------------------------------------------------
362
363 system::result<array&>
364 16 value::try_as_array() noexcept
365 {
366 16 if( is_array() )
367 9 return arr_;
368
369 7 system::error_code ec;
370 7 BOOST_JSON_FAIL(ec, error::not_array);
371 7 return ec;
372 }
373
374 system::result<array const&>
375 186 value::try_as_array() const noexcept
376 {
377 186 if( is_array() )
378 158 return arr_;
379
380 28 system::error_code ec;
381 28 BOOST_JSON_FAIL(ec, error::not_array);
382 28 return ec;
383 }
384
385 system::result<object&>
386 9 value::try_as_object() noexcept
387 {
388 9 if( is_object() )
389 2 return obj_;
390
391 7 system::error_code ec;
392 7 BOOST_JSON_FAIL(ec, error::not_object);
393 7 return ec;
394 }
395
396 system::result<object const&>
397 282 value::try_as_object() const noexcept
398 {
399 282 if( is_object() )
400 242 return obj_;
401
402 40 system::error_code ec;
403 40 BOOST_JSON_FAIL(ec, error::not_object);
404 40 return ec;
405 }
406
407 system::result<string&>
408 9 value::try_as_string() noexcept
409 {
410 9 if( is_string() )
411 2 return str_;
412
413 7 system::error_code ec;
414 7 BOOST_JSON_FAIL(ec, error::not_string);
415 7 return ec;
416 }
417
418 system::result<string const&>
419 121 value::try_as_string() const noexcept
420 {
421 121 if( is_string() )
422 92 return str_;
423
424 29 system::error_code ec;
425 29 BOOST_JSON_FAIL(ec, error::not_string);
426 29 return ec;
427 }
428
429 system::result<std::int64_t&>
430 52 value::try_as_int64() noexcept
431 {
432 52 if( is_int64() )
433 38 return sca_.i;
434
435 14 system::error_code ec;
436 14 BOOST_JSON_FAIL(ec, error::not_int64);
437 14 return ec;
438 }
439
440 system::result<std::int64_t>
441 33 value::try_as_int64() const noexcept
442 {
443 33 if( is_int64() )
444 19 return sca_.i;
445
446 14 system::error_code ec;
447 14 BOOST_JSON_FAIL(ec, error::not_int64);
448 14 return ec;
449 }
450
451 system::result<std::uint64_t&>
452 16 value::try_as_uint64() noexcept
453 {
454 16 if( is_uint64() )
455 2 return sca_.u;
456
457 14 system::error_code ec;
458 14 BOOST_JSON_FAIL(ec, error::not_uint64);
459 14 return ec;
460 }
461
462 system::result<std::uint64_t>
463 16 value::try_as_uint64() const noexcept
464 {
465 16 if( is_uint64() )
466 2 return sca_.u;
467
468 14 system::error_code ec;
469 14 BOOST_JSON_FAIL(ec, error::not_uint64);
470 14 return ec;
471 }
472
473 system::result<double&>
474 2000657 value::try_as_double() noexcept
475 {
476 2000657 if( is_double() )
477 2000643 return sca_.d;
478
479 14 system::error_code ec;
480 14 BOOST_JSON_FAIL(ec, error::not_double);
481 14 return ec;
482 }
483
484 system::result<double>
485 580 value::try_as_double() const noexcept
486 {
487 580 if( is_double() )
488 566 return sca_.d;
489
490 14 system::error_code ec;
491 14 BOOST_JSON_FAIL(ec, error::not_double);
492 14 return ec;
493 }
494
495 system::result<bool&>
496 19 value::try_as_bool() noexcept
497 {
498 19 if( is_bool() )
499 4 return sca_.b;
500
501 15 system::error_code ec;
502 15 BOOST_JSON_FAIL(ec, error::not_bool);
503 15 return ec;
504 }
505
506 system::result<bool>
507 30 value::try_as_bool() const noexcept
508 {
509 30 if( is_bool() )
510 16 return sca_.b;
511
512 14 system::error_code ec;
513 14 BOOST_JSON_FAIL(ec, error::not_bool);
514 14 return ec;
515 }
516
517 system::result<std::nullptr_t>
518 2 value::try_as_null() const noexcept
519 {
520 2 if( is_null() )
521 1 return nullptr;
522
523 1 system::error_code ec;
524 1 BOOST_JSON_FAIL(ec, error::not_null);
525 1 return ec;
526 }
527
528 boost::system::result<value&>
529 1 value::try_at(string_view key) noexcept
530 {
531 2 return try_as_object() & [key](object& jo) { return jo.try_at(key); };
532 }
533
534 boost::system::result<value const&>
535 3 value::try_at(string_view key) const noexcept
536 {
537 6 return try_as_object()
538 6 & [key](object const& jo) { return jo.try_at(key); };
539 }
540
541 boost::system::result<value&>
542 8 value::try_at(std::size_t pos) noexcept
543 {
544 16 return try_as_array() & [pos](array& ja) { return ja.try_at(pos); };
545 }
546
547 boost::system::result<value const&>
548 2 value::try_at(std::size_t pos) const noexcept
549 {
550 4 return try_as_array() & [pos](array const& ja) { return ja.try_at(pos); };
551 }
552
553 object const&
554 197 value::as_object(source_location const& loc) const&
555 {
556 197 return try_as_object().value(loc);
557 }
558
559 array const&
560 176 value::as_array(source_location const& loc) const&
561 {
562 176 return try_as_array().value(loc);
563 }
564
565 string const&
566 113 value::as_string(source_location const& loc) const&
567 {
568 113 return try_as_string().value(loc);
569 }
570
571 std::int64_t&
572 44 value::as_int64(source_location const& loc)
573 {
574 44 return try_as_int64().value(loc);
575 }
576
577 std::int64_t
578 26 value::as_int64(source_location const& loc) const
579 {
580 26 return try_as_int64().value(loc);
581 }
582
583 std::uint64_t&
584 8 value::as_uint64(source_location const& loc)
585 {
586 8 return try_as_uint64().value(loc);
587 }
588
589 std::uint64_t
590 8 value::as_uint64(source_location const& loc) const
591 {
592 8 return try_as_uint64().value(loc);
593 }
594
595 double&
596 2000649 value::as_double(source_location const& loc)
597 {
598 2000649 return try_as_double().value(loc);
599 }
600
601 double
602 572 value::as_double(source_location const& loc) const
603 {
604 572 return try_as_double().value(loc);
605 }
606
607 bool&
608 10 value::as_bool(source_location const& loc)
609 {
610 10 return try_as_bool().value(loc);
611 }
612
613 bool
614 22 value::as_bool(source_location const& loc) const
615 {
616 22 return try_as_bool().value(loc);
617 }
618
619 //----------------------------------------------------------
620 //
621 // Modifiers
622 //
623 //----------------------------------------------------------
624
625 string&
626 98 value::
627 emplace_string() noexcept
628 {
629 98 return *::new(&str_) string(destroy());
630 }
631
632 array&
633 249 value::
634 emplace_array() noexcept
635 {
636 249 return *::new(&arr_) array(destroy());
637 }
638
639 object&
640 55 value::
641 emplace_object() noexcept
642 {
643 55 return *::new(&obj_) object(destroy());
644 }
645
646 void
647 259 value::
648 swap(value& other)
649 {
650 259 if(*storage() == *other.storage())
651 {
652 // fast path
653 union U
654 {
655 value tmp;
656 258 U(){}
657 258 ~U(){}
658 };
659 258 U u;
660 258 relocate(&u.tmp, *this);
661 258 relocate(this, other);
662 258 relocate(&other, u.tmp);
663 258 return;
664 258 }
665
666 // copy
667 value temp1(
668 1 std::move(*this),
669 2 other.storage());
670 value temp2(
671 1 std::move(other),
672 2 this->storage());
673 1 other.~value();
674 1 ::new(&other) value(pilfer(temp1));
675 1 this->~value();
676 1 ::new(this) value(pilfer(temp2));
677 1 }
678
679 std::istream&
680 10 operator>>(
681 std::istream& is,
682 value& jv)
683 {
684 using Traits = std::istream::traits_type;
685
686 // sentry prepares the stream for reading and finalizes it in destructor
687 10 std::istream::sentry sentry(is);
688 10 if( !sentry )
689 1 return is;
690
691 9 parse_options opts = get_parse_options( is );
692 9 if( auto depth = static_cast<std::size_t>( is.iword(parse_depth_xalloc) ) )
693 3 opts.max_depth = depth;
694
695 unsigned char parser_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2];
696 9 stream_parser p( {}, opts, parser_buf );
697 9 p.reset( jv.storage() );
698
699 char read_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2];
700 9 std::streambuf& buf = *is.rdbuf();
701 9 std::ios::iostate err = std::ios::goodbit;
702 #ifndef BOOST_NO_EXCEPTIONS
703 try
704 #endif
705 {
706 while( true )
707 {
708 15 system::error_code ec;
709
710 // we peek the buffer; this either makes sure that there's no
711 // more input, or makes sure there's something in the internal
712 // buffer (so in_avail will return a positive number)
713 15 std::istream::int_type c = is.rdbuf()->sgetc();
714 // if we indeed reached EOF, we check if we parsed a full JSON
715 // document; if not, we error out
716 13 if( Traits::eq_int_type(c, Traits::eof()) )
717 {
718 3 err |= std::ios::eofbit;
719 3 p.finish(ec);
720 3 if( ec.failed() )
721 4 break;
722 }
723
724 // regardless of reaching EOF, we might have parsed a full JSON
725 // document; if so, we successfully finish
726 12 if( p.done() )
727 {
728 3 jv = p.release();
729 3 return is;
730 }
731
732 // at this point we definitely have more input, specifically in
733 // buf's internal buffer; we also definitely haven't parsed a whole
734 // document
735 9 std::streamsize available = buf.in_avail();
736 // if this assert fails, the streambuf is buggy
737 9 BOOST_ASSERT( available > 0 );
738
739 18 available = ( std::min )(
740 9 static_cast<std::size_t>(available), sizeof(read_buf) );
741 // we read from the internal buffer of buf into our buffer
742 9 available = buf.sgetn( read_buf, available );
743
744 9 std::size_t consumed = p.write_some(
745 read_buf, static_cast<std::size_t>(available), ec );
746 // if the parser hasn't consumed the entire input we've took from
747 // buf, we put the remaining data back; this should succeed,
748 // because we only read data from buf's internal buffer
749 21 while( consumed++ < static_cast<std::size_t>(available) )
750 {
751 12 std::istream::int_type const status = buf.sungetc();
752 12 BOOST_ASSERT( status != Traits::eof() );
753 (void)status;
754 }
755
756 9 if( ec.failed() )
757 3 break;
758 6 }
759 }
760 #ifndef BOOST_NO_EXCEPTIONS
761 2 catch(...)
762 {
763 try
764 {
765 2 is.setstate(std::ios::badbit);
766 }
767 // we ignore the exception, because we need to throw the original
768 // exception instead
769 1 catch( std::ios::failure const& ) { }
770
771 2 if( is.exceptions() & std::ios::badbit )
772 1 throw;
773 2 }
774 #endif
775
776 5 is.setstate(err | std::ios::failbit);
777 5 return is;
778 9 }
779
780 std::istream&
781 3 operator>>(
782 std::istream& is,
783 parse_options const& opts)
784 {
785 3 is.iword(parse_flags_xalloc) = to_bitmask(opts);
786 3 is.iword(parse_depth_xalloc) = static_cast<long>(opts.max_depth);
787 3 return is;
788 }
789
790 //----------------------------------------------------------
791 //
792 // private
793 //
794 //----------------------------------------------------------
795
796 storage_ptr
797 420 value::
798 destroy() noexcept
799 {
800 420 switch(kind())
801 {
802 401 case json::kind::null:
803 case json::kind::bool_:
804 case json::kind::int64:
805 case json::kind::uint64:
806 case json::kind::double_:
807 401 break;
808
809 14 case json::kind::string:
810 {
811 14 auto sp = str_.storage();
812 14 str_.~string();
813 14 return sp;
814 14 }
815
816 2 case json::kind::array:
817 {
818 2 auto sp = arr_.storage();
819 2 arr_.~array();
820 2 return sp;
821 2 }
822
823 3 case json::kind::object:
824 {
825 3 auto sp = obj_.storage();
826 3 obj_.~object();
827 3 return sp;
828 3 }
829
830 }
831 401 return std::move(sp_);
832 }
833
834 bool
835 4172 value::
836 equal(value const& other) const noexcept
837 {
838 4172 switch(kind())
839 {
840 21 default: // unreachable()?
841 case json::kind::null:
842 21 return other.kind() == json::kind::null;
843
844 17 case json::kind::bool_:
845 return
846 27 other.kind() == json::kind::bool_ &&
847 27 get_bool() == other.get_bool();
848
849 3943 case json::kind::int64:
850 3943 switch(other.kind())
851 {
852 3916 case json::kind::int64:
853 3916 return get_int64() == other.get_int64();
854 26 case json::kind::uint64:
855 26 if(get_int64() < 0)
856 1 return false;
857 25 return static_cast<std::uint64_t>(
858 25 get_int64()) == other.get_uint64();
859 1 default:
860 1 return false;
861 }
862
863 7 case json::kind::uint64:
864 7 switch(other.kind())
865 {
866 2 case json::kind::uint64:
867 2 return get_uint64() == other.get_uint64();
868 3 case json::kind::int64:
869 3 if(other.get_int64() < 0)
870 2 return false;
871 1 return static_cast<std::uint64_t>(
872 1 other.get_int64()) == get_uint64();
873 2 default:
874 2 return false;
875 }
876
877 55 case json::kind::double_:
878 return
879 108 other.kind() == json::kind::double_ &&
880 108 get_double() == other.get_double();
881
882 47 case json::kind::string:
883 return
884 91 other.kind() == json::kind::string &&
885 91 get_string() == other.get_string();
886
887 62 case json::kind::array:
888 return
889 122 other.kind() == json::kind::array &&
890 122 get_array() == other.get_array();
891
892 20 case json::kind::object:
893 return
894 37 other.kind() == json::kind::object &&
895 37 get_object() == other.get_object();
896 }
897 }
898
899 //----------------------------------------------------------
900 //
901 // key_value_pair
902 //
903 //----------------------------------------------------------
904
905 // empty keys point here
906 BOOST_JSON_REQUIRE_CONST_INIT
907 char const
908 key_value_pair::empty_[1] = { 0 };
909
910 38150 key_value_pair::
911 key_value_pair(
912 pilfered<json::value> key,
913 38150 pilfered<json::value> value) noexcept
914 38150 : value_(value)
915 {
916 std::size_t len;
917 38150 key_ = access::release_key(key.get(), len);
918 38150 len_ = static_cast<std::uint32_t>(len);
919 38150 }
920
921 6858 key_value_pair::
922 key_value_pair(
923 key_value_pair const& other,
924 6858 storage_ptr sp)
925 6862 : value_(other.value_, std::move(sp))
926 {
927 auto p = reinterpret_cast<
928 6854 char*>(value_.storage()->
929 6854 allocate(other.len_ + 1,
930 alignof(char)));
931 6596 std::memcpy(
932 6596 p, other.key_, other.len_);
933 6596 len_ = other.len_;
934 6596 p[len_] = 0;
935 6596 key_ = p;
936 6854 }
937
938 //----------------------------------------------------------
939
940 namespace detail
941 {
942
943 std::size_t
944 248 hash_value_impl( value const& jv ) noexcept
945 {
946 248 std::size_t seed = 0;
947
948 248 kind const k = jv.kind();
949 248 boost::hash_combine( seed, k != kind::int64 ? k : kind::uint64 );
950
951 248 visit( value_hasher{seed}, jv );
952 248 return seed;
953 }
954
955 } // namespace detail
956 } // namespace json
957 } // namespace boost
958
959 //----------------------------------------------------------
960 //
961 // std::hash specialization
962 //
963 //----------------------------------------------------------
964
965 std::size_t
966 62 std::hash<::boost::json::value>::operator()(
967 ::boost::json::value const& jv) const noexcept
968 {
969 62 return ::boost::hash< ::boost::json::value >()( jv );
970 }
971
972 //----------------------------------------------------------
973
974 #endif
975