object.ipp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  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. #ifndef BOOST_JSON_IMPL_OBJECT_IPP
  10. #define BOOST_JSON_IMPL_OBJECT_IPP
  11. #include <boost/container_hash/hash.hpp>
  12. #include <boost/json/object.hpp>
  13. #include <boost/json/detail/digest.hpp>
  14. #include <boost/json/detail/except.hpp>
  15. #include <algorithm>
  16. #include <cmath>
  17. #include <cstdlib>
  18. #include <cstring>
  19. #include <new>
  20. #include <stdexcept>
  21. #include <type_traits>
  22. namespace boost {
  23. namespace json {
  24. namespace detail {
  25. template<class CharRange>
  26. std::pair<key_value_pair*, std::size_t>
  27. find_in_object(
  28. object const& obj,
  29. CharRange key) noexcept
  30. {
  31. BOOST_ASSERT(obj.t_->capacity > 0);
  32. if(obj.t_->is_small())
  33. {
  34. auto it = &(*obj.t_)[0];
  35. auto const last =
  36. &(*obj.t_)[obj.t_->size];
  37. for(;it != last; ++it)
  38. if( key == it->key() )
  39. return { it, 0 };
  40. return { nullptr, 0 };
  41. }
  42. std::pair<
  43. key_value_pair*,
  44. std::size_t> result;
  45. BOOST_ASSERT(obj.t_->salt != 0);
  46. result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
  47. auto i = obj.t_->bucket(
  48. result.second);
  49. while(i != object::null_index_)
  50. {
  51. auto& v = (*obj.t_)[i];
  52. if( key == v.key() )
  53. {
  54. result.first = &v;
  55. return result;
  56. }
  57. i = access::next(v);
  58. }
  59. result.first = nullptr;
  60. return result;
  61. }
  62. template
  63. std::pair<key_value_pair*, std::size_t>
  64. find_in_object<string_view>(
  65. object const& obj,
  66. string_view key) noexcept;
  67. } // namespace detail
  68. //----------------------------------------------------------
  69. constexpr object::table::table() = default;
  70. // empty objects point here
  71. BOOST_JSON_REQUIRE_CONST_INIT
  72. object::table object::empty_;
  73. std::size_t
  74. object::table::
  75. digest(string_view key) const noexcept
  76. {
  77. BOOST_ASSERT(salt != 0);
  78. return detail::digest(
  79. key.begin(), key.end(), salt);
  80. }
  81. auto
  82. object::table::
  83. bucket(std::size_t hash) noexcept ->
  84. index_t&
  85. {
  86. return reinterpret_cast<
  87. index_t*>(&(*this)[capacity])[
  88. hash % capacity];
  89. }
  90. auto
  91. object::table::
  92. bucket(string_view key) noexcept ->
  93. index_t&
  94. {
  95. return bucket(digest(key));
  96. }
  97. void
  98. object::table::
  99. clear() noexcept
  100. {
  101. BOOST_ASSERT(! is_small());
  102. // initialize buckets
  103. std::memset(
  104. reinterpret_cast<index_t*>(
  105. &(*this)[capacity]),
  106. 0xff, // null_index_
  107. capacity * sizeof(index_t));
  108. }
  109. object::table*
  110. object::table::
  111. allocate(
  112. std::size_t capacity,
  113. std::uintptr_t salt,
  114. storage_ptr const& sp)
  115. {
  116. BOOST_STATIC_ASSERT(
  117. alignof(key_value_pair) >=
  118. alignof(index_t));
  119. BOOST_ASSERT(capacity > 0);
  120. BOOST_ASSERT(capacity <= max_size());
  121. table* p;
  122. if(capacity <= detail::small_object_size_)
  123. {
  124. p = reinterpret_cast<
  125. table*>(sp->allocate(
  126. sizeof(table) + capacity *
  127. sizeof(key_value_pair)));
  128. p->capacity = static_cast<
  129. std::uint32_t>(capacity);
  130. }
  131. else
  132. {
  133. p = reinterpret_cast<
  134. table*>(sp->allocate(
  135. sizeof(table) + capacity * (
  136. sizeof(key_value_pair) +
  137. sizeof(index_t))));
  138. p->capacity = static_cast<
  139. std::uint32_t>(capacity);
  140. p->clear();
  141. }
  142. if(salt)
  143. {
  144. p->salt = salt;
  145. }
  146. else
  147. {
  148. // VFALCO This would be better if it
  149. // was random, but maybe this
  150. // is good enough.
  151. p->salt = reinterpret_cast<
  152. std::uintptr_t>(p);
  153. }
  154. return p;
  155. }
  156. //----------------------------------------------------------
  157. void
  158. object::
  159. revert_construct::
  160. destroy() noexcept
  161. {
  162. obj_->destroy();
  163. }
  164. //----------------------------------------------------------
  165. void
  166. object::
  167. revert_insert::
  168. destroy() noexcept
  169. {
  170. obj_->destroy(
  171. &(*obj_->t_)[size_],
  172. obj_->end());
  173. }
  174. //----------------------------------------------------------
  175. //
  176. // Construction
  177. //
  178. //----------------------------------------------------------
  179. object::
  180. object(detail::unchecked_object&& uo)
  181. : sp_(uo.storage())
  182. {
  183. if(uo.size() == 0)
  184. {
  185. t_ = &empty_;
  186. return;
  187. }
  188. // should already be checked
  189. BOOST_ASSERT(
  190. uo.size() <= max_size());
  191. t_ = table::allocate(
  192. uo.size(), 0, sp_);
  193. // insert all elements, keeping
  194. // the last of any duplicate keys.
  195. auto dest = begin();
  196. auto src = uo.release();
  197. auto const end = src + 2 * uo.size();
  198. if(t_->is_small())
  199. {
  200. t_->size = 0;
  201. while(src != end)
  202. {
  203. access::construct_key_value_pair(
  204. dest, pilfer(src[0]), pilfer(src[1]));
  205. src += 2;
  206. auto result = detail::find_in_object(*this, dest->key());
  207. if(! result.first)
  208. {
  209. ++dest;
  210. ++t_->size;
  211. continue;
  212. }
  213. // handle duplicate
  214. auto& v = *result.first;
  215. // don't bother to check if
  216. // storage deallocate is trivial
  217. v.~key_value_pair();
  218. // trivial relocate
  219. std::memcpy(
  220. static_cast<void*>(&v),
  221. dest, sizeof(v));
  222. }
  223. return;
  224. }
  225. while(src != end)
  226. {
  227. access::construct_key_value_pair(
  228. dest, pilfer(src[0]), pilfer(src[1]));
  229. src += 2;
  230. auto& head = t_->bucket(dest->key());
  231. auto i = head;
  232. for(;;)
  233. {
  234. if(i == null_index_)
  235. {
  236. // end of bucket
  237. access::next(
  238. *dest) = head;
  239. head = static_cast<index_t>(
  240. dest - begin());
  241. ++dest;
  242. break;
  243. }
  244. auto& v = (*t_)[i];
  245. if(v.key() != dest->key())
  246. {
  247. i = access::next(v);
  248. continue;
  249. }
  250. // handle duplicate
  251. access::next(*dest) =
  252. access::next(v);
  253. // don't bother to check if
  254. // storage deallocate is trivial
  255. v.~key_value_pair();
  256. // trivial relocate
  257. std::memcpy(
  258. static_cast<void*>(&v),
  259. dest, sizeof(v));
  260. break;
  261. }
  262. }
  263. t_->size = static_cast<
  264. index_t>(dest - begin());
  265. }
  266. object::
  267. ~object() noexcept
  268. {
  269. if(sp_.is_not_shared_and_deallocate_is_trivial())
  270. return;
  271. if(t_->capacity == 0)
  272. return;
  273. destroy();
  274. }
  275. object::
  276. object(
  277. std::size_t min_capacity,
  278. storage_ptr sp)
  279. : sp_(std::move(sp))
  280. , t_(&empty_)
  281. {
  282. reserve(min_capacity);
  283. }
  284. object::
  285. object(object&& other) noexcept
  286. : sp_(other.sp_)
  287. , t_(detail::exchange(
  288. other.t_, &empty_))
  289. {
  290. }
  291. object::
  292. object(
  293. object&& other,
  294. storage_ptr sp)
  295. : sp_(std::move(sp))
  296. {
  297. if(*sp_ == *other.sp_)
  298. {
  299. t_ = detail::exchange(
  300. other.t_, &empty_);
  301. return;
  302. }
  303. t_ = &empty_;
  304. object(other, sp_).swap(*this);
  305. }
  306. object::
  307. object(
  308. object const& other,
  309. storage_ptr sp)
  310. : sp_(std::move(sp))
  311. , t_(&empty_)
  312. {
  313. reserve(other.size());
  314. revert_construct r(*this);
  315. if(t_->is_small())
  316. {
  317. for(auto const& v : other)
  318. {
  319. ::new(end())
  320. key_value_pair(v, sp_);
  321. ++t_->size;
  322. }
  323. r.commit();
  324. return;
  325. }
  326. for(auto const& v : other)
  327. {
  328. // skip duplicate checking
  329. auto& head =
  330. t_->bucket(v.key());
  331. auto pv = ::new(end())
  332. key_value_pair(v, sp_);
  333. access::next(*pv) = head;
  334. head = t_->size;
  335. ++t_->size;
  336. }
  337. r.commit();
  338. }
  339. object::
  340. object(
  341. std::initializer_list<std::pair<
  342. string_view, value_ref>> init,
  343. std::size_t min_capacity,
  344. storage_ptr sp)
  345. : sp_(std::move(sp))
  346. , t_(&empty_)
  347. {
  348. if( min_capacity < init.size())
  349. min_capacity = init.size();
  350. reserve(min_capacity);
  351. revert_construct r(*this);
  352. insert(init);
  353. r.commit();
  354. }
  355. //----------------------------------------------------------
  356. //
  357. // Assignment
  358. //
  359. //----------------------------------------------------------
  360. object&
  361. object::
  362. operator=(object const& other)
  363. {
  364. object tmp(other, sp_);
  365. this->~object();
  366. ::new(this) object(pilfer(tmp));
  367. return *this;
  368. }
  369. object&
  370. object::
  371. operator=(object&& other)
  372. {
  373. object tmp(std::move(other), sp_);
  374. this->~object();
  375. ::new(this) object(pilfer(tmp));
  376. return *this;
  377. }
  378. object&
  379. object::
  380. operator=(
  381. std::initializer_list<std::pair<
  382. string_view, value_ref>> init)
  383. {
  384. object tmp(init, sp_);
  385. this->~object();
  386. ::new(this) object(pilfer(tmp));
  387. return *this;
  388. }
  389. //----------------------------------------------------------
  390. //
  391. // Lookup
  392. //
  393. //----------------------------------------------------------
  394. system::result<value&>
  395. object::
  396. try_at(string_view key) noexcept
  397. {
  398. auto it = find(key);
  399. if( it != end() )
  400. return it->value();
  401. system::error_code ec;
  402. BOOST_JSON_FAIL(ec, error::out_of_range);
  403. return ec;
  404. }
  405. system::result<value const&>
  406. object::
  407. try_at(string_view key) const noexcept
  408. {
  409. auto it = find(key);
  410. if( it != end() )
  411. return it->value();
  412. system::error_code ec;
  413. BOOST_JSON_FAIL(ec, error::out_of_range);
  414. return ec;
  415. }
  416. value const&
  417. object::
  418. at(string_view key, source_location const& loc) const&
  419. {
  420. return try_at(key).value(loc);
  421. }
  422. //----------------------------------------------------------
  423. //
  424. // Modifiers
  425. //
  426. //----------------------------------------------------------
  427. void
  428. object::
  429. clear() noexcept
  430. {
  431. if(empty())
  432. return;
  433. if(! sp_.is_not_shared_and_deallocate_is_trivial())
  434. destroy(begin(), end());
  435. if(! t_->is_small())
  436. t_->clear();
  437. t_->size = 0;
  438. }
  439. void
  440. object::
  441. insert(
  442. std::initializer_list<std::pair<
  443. string_view, value_ref>> init)
  444. {
  445. auto const n0 = size();
  446. if(init.size() > max_size() - n0)
  447. {
  448. BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
  449. detail::throw_system_error( error::object_too_large, &loc );
  450. }
  451. revert_insert r( *this, n0 + init.size() );
  452. if(t_->is_small())
  453. {
  454. for(auto& iv : init)
  455. {
  456. auto result =
  457. detail::find_in_object(*this, iv.first);
  458. if(result.first)
  459. {
  460. // ignore duplicate
  461. continue;
  462. }
  463. ::new(end()) key_value_pair(
  464. iv.first,
  465. iv.second.make_value(sp_));
  466. ++t_->size;
  467. }
  468. r.commit();
  469. return;
  470. }
  471. for(auto& iv : init)
  472. {
  473. auto& head = t_->bucket(iv.first);
  474. auto i = head;
  475. for(;;)
  476. {
  477. if(i == null_index_)
  478. {
  479. // VFALCO value_ref should construct
  480. // a key_value_pair using placement
  481. auto& v = *::new(end())
  482. key_value_pair(
  483. iv.first,
  484. iv.second.make_value(sp_));
  485. access::next(v) = head;
  486. head = static_cast<index_t>(
  487. t_->size);
  488. ++t_->size;
  489. break;
  490. }
  491. auto& v = (*t_)[i];
  492. if(v.key() == iv.first)
  493. {
  494. // ignore duplicate
  495. break;
  496. }
  497. i = access::next(v);
  498. }
  499. }
  500. r.commit();
  501. }
  502. auto
  503. object::
  504. erase(const_iterator pos) noexcept ->
  505. iterator
  506. {
  507. return do_erase(pos,
  508. [this](iterator p) {
  509. // the casts silence warnings
  510. std::memcpy(
  511. static_cast<void*>(p),
  512. static_cast<void const*>(end()),
  513. sizeof(*p));
  514. },
  515. [this](iterator p) {
  516. reindex_relocate(end(), p);
  517. });
  518. }
  519. auto
  520. object::
  521. erase(string_view key) noexcept ->
  522. std::size_t
  523. {
  524. auto it = find(key);
  525. if(it == end())
  526. return 0;
  527. erase(it);
  528. return 1;
  529. }
  530. auto
  531. object::
  532. stable_erase(const_iterator pos) noexcept ->
  533. iterator
  534. {
  535. return do_erase(pos,
  536. [this](iterator p) {
  537. // the casts silence warnings
  538. std::memmove(
  539. static_cast<void*>(p),
  540. static_cast<void const*>(p + 1),
  541. sizeof(*p) * (end() - p));
  542. },
  543. [this](iterator p) {
  544. for (; p != end(); ++p)
  545. {
  546. reindex_relocate(p + 1, p);
  547. }
  548. });
  549. }
  550. auto
  551. object::
  552. stable_erase(string_view key) noexcept ->
  553. std::size_t
  554. {
  555. auto it = find(key);
  556. if(it == end())
  557. return 0;
  558. stable_erase(it);
  559. return 1;
  560. }
  561. void
  562. object::
  563. swap(object& other)
  564. {
  565. if(*sp_ == *other.sp_)
  566. {
  567. t_ = detail::exchange(
  568. other.t_, t_);
  569. return;
  570. }
  571. object temp1(
  572. std::move(*this),
  573. other.storage());
  574. object temp2(
  575. std::move(other),
  576. this->storage());
  577. other.~object();
  578. ::new(&other) object(pilfer(temp1));
  579. this->~object();
  580. ::new(this) object(pilfer(temp2));
  581. }
  582. //----------------------------------------------------------
  583. //
  584. // Lookup
  585. //
  586. //----------------------------------------------------------
  587. auto
  588. object::
  589. operator[](string_view key) ->
  590. value&
  591. {
  592. auto const result =
  593. emplace(key, nullptr);
  594. return result.first->value();
  595. }
  596. auto
  597. object::
  598. count(string_view key) const noexcept ->
  599. std::size_t
  600. {
  601. if(find(key) == end())
  602. return 0;
  603. return 1;
  604. }
  605. auto
  606. object::
  607. find(string_view key) noexcept ->
  608. iterator
  609. {
  610. if(empty())
  611. return end();
  612. auto const p =
  613. detail::find_in_object(*this, key).first;
  614. if(p)
  615. return p;
  616. return end();
  617. }
  618. auto
  619. object::
  620. find(string_view key) const noexcept ->
  621. const_iterator
  622. {
  623. if(empty())
  624. return end();
  625. auto const p =
  626. detail::find_in_object(*this, key).first;
  627. if(p)
  628. return p;
  629. return end();
  630. }
  631. bool
  632. object::
  633. contains(
  634. string_view key) const noexcept
  635. {
  636. if(empty())
  637. return false;
  638. return detail::find_in_object(*this, key).first
  639. != nullptr;
  640. }
  641. value const*
  642. object::
  643. if_contains(
  644. string_view key) const noexcept
  645. {
  646. auto const it = find(key);
  647. if(it != end())
  648. return &it->value();
  649. return nullptr;
  650. }
  651. value*
  652. object::
  653. if_contains(
  654. string_view key) noexcept
  655. {
  656. auto const it = find(key);
  657. if(it != end())
  658. return &it->value();
  659. return nullptr;
  660. }
  661. //----------------------------------------------------------
  662. //
  663. // (private)
  664. //
  665. //----------------------------------------------------------
  666. key_value_pair*
  667. object::
  668. insert_impl(
  669. pilfered<key_value_pair> p,
  670. std::size_t hash)
  671. {
  672. BOOST_ASSERT(
  673. capacity() > size());
  674. if(t_->is_small())
  675. {
  676. auto const pv = ::new(end())
  677. key_value_pair(p);
  678. ++t_->size;
  679. return pv;
  680. }
  681. auto& head =
  682. t_->bucket(hash);
  683. auto const pv = ::new(end())
  684. key_value_pair(p);
  685. access::next(*pv) = head;
  686. head = t_->size;
  687. ++t_->size;
  688. return pv;
  689. }
  690. // allocate new table, copy elements there, and rehash them
  691. object::table*
  692. object::
  693. reserve_impl(std::size_t new_capacity)
  694. {
  695. BOOST_ASSERT(
  696. new_capacity > t_->capacity);
  697. auto t = table::allocate(
  698. growth(new_capacity),
  699. t_->salt, sp_);
  700. if(! empty())
  701. std::memcpy(
  702. static_cast<
  703. void*>(&(*t)[0]),
  704. begin(),
  705. size() * sizeof(
  706. key_value_pair));
  707. t->size = t_->size;
  708. std::swap(t_, t);
  709. if(! t_->is_small())
  710. {
  711. // rebuild hash table,
  712. // without dup checks
  713. auto p = end();
  714. index_t i = t_->size;
  715. while(i-- > 0)
  716. {
  717. --p;
  718. auto& head =
  719. t_->bucket(p->key());
  720. access::next(*p) = head;
  721. head = i;
  722. }
  723. }
  724. return t;
  725. }
  726. bool
  727. object::
  728. equal(object const& other) const noexcept
  729. {
  730. if(size() != other.size())
  731. return false;
  732. auto const end_ = other.end();
  733. for(auto e : *this)
  734. {
  735. auto it = other.find(e.key());
  736. if(it == end_)
  737. return false;
  738. if(it->value() != e.value())
  739. return false;
  740. }
  741. return true;
  742. }
  743. std::size_t
  744. object::
  745. growth(
  746. std::size_t new_size) const
  747. {
  748. if(new_size > max_size())
  749. {
  750. BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
  751. detail::throw_system_error( error::object_too_large, &loc );
  752. }
  753. std::size_t const old = capacity();
  754. if(old > max_size() - old / 2)
  755. return new_size;
  756. std::size_t const g =
  757. old + old / 2; // 1.5x
  758. if(g < new_size)
  759. return new_size;
  760. return g;
  761. }
  762. void
  763. object::
  764. remove(
  765. index_t& head,
  766. key_value_pair& v) noexcept
  767. {
  768. BOOST_ASSERT(! t_->is_small());
  769. auto const i = static_cast<
  770. index_t>(&v - begin());
  771. if(head == i)
  772. {
  773. head = access::next(v);
  774. return;
  775. }
  776. auto* pn =
  777. &access::next((*t_)[head]);
  778. while(*pn != i)
  779. pn = &access::next((*t_)[*pn]);
  780. *pn = access::next(v);
  781. }
  782. void
  783. object::
  784. destroy() noexcept
  785. {
  786. BOOST_ASSERT(t_->capacity > 0);
  787. BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
  788. destroy(begin(), end());
  789. table::deallocate(t_, sp_);
  790. }
  791. void
  792. object::
  793. destroy(
  794. key_value_pair* first,
  795. key_value_pair* last) noexcept
  796. {
  797. BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
  798. while(last != first)
  799. (--last)->~key_value_pair();
  800. }
  801. template<class FS, class FB>
  802. auto
  803. object::
  804. do_erase(
  805. const_iterator pos,
  806. FS small_reloc,
  807. FB big_reloc) noexcept
  808. -> iterator
  809. {
  810. auto p = begin() + (pos - begin());
  811. if(t_->is_small())
  812. {
  813. p->~value_type();
  814. --t_->size;
  815. if(p != end())
  816. {
  817. small_reloc(p);
  818. }
  819. return p;
  820. }
  821. remove(t_->bucket(p->key()), *p);
  822. p->~value_type();
  823. --t_->size;
  824. if(p != end())
  825. {
  826. big_reloc(p);
  827. }
  828. return p;
  829. }
  830. void
  831. object::
  832. reindex_relocate(
  833. key_value_pair* src,
  834. key_value_pair* dst) noexcept
  835. {
  836. BOOST_ASSERT(! t_->is_small());
  837. auto& head = t_->bucket(src->key());
  838. remove(head, *src);
  839. // the casts silence warnings
  840. std::memcpy(
  841. static_cast<void*>(dst),
  842. static_cast<void const*>(src),
  843. sizeof(*dst));
  844. access::next(*dst) = head;
  845. head = static_cast<
  846. index_t>(dst - begin());
  847. }
  848. } // namespace json
  849. } // namespace boost
  850. //----------------------------------------------------------
  851. //
  852. // std::hash specialization
  853. //
  854. //----------------------------------------------------------
  855. std::size_t
  856. std::hash<::boost::json::object>::operator()(
  857. ::boost::json::object const& jo) const noexcept
  858. {
  859. return ::boost::hash< ::boost::json::object >()( jo );
  860. }
  861. //----------------------------------------------------------
  862. #endif