object.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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_HPP
  10. #define BOOST_JSON_IMPL_OBJECT_HPP
  11. #include <boost/core/detail/static_assert.hpp>
  12. #include <boost/json/value.hpp>
  13. #include <iterator>
  14. #include <cmath>
  15. #include <type_traits>
  16. #include <utility>
  17. namespace boost {
  18. namespace json {
  19. namespace detail {
  20. // Objects with size less than or equal
  21. // to this number will use a linear search
  22. // instead of the more expensive hash function.
  23. static
  24. constexpr
  25. std::size_t
  26. small_object_size_ = 18;
  27. BOOST_CORE_STATIC_ASSERT(
  28. small_object_size_ < BOOST_JSON_MAX_STRUCTURED_SIZE);
  29. } // detail
  30. //----------------------------------------------------------
  31. struct alignas(key_value_pair)
  32. object::table
  33. {
  34. std::uint32_t size = 0;
  35. std::uint32_t capacity = 0;
  36. std::uintptr_t salt = 0;
  37. #if defined(_MSC_VER) && BOOST_JSON_ARCH == 32
  38. // VFALCO If we make key_value_pair smaller,
  39. // then we might want to revisit this
  40. // padding.
  41. BOOST_CORE_STATIC_ASSERT( sizeof(key_value_pair) == 32 );
  42. char pad[4] = {}; // silence warnings
  43. #endif
  44. constexpr table();
  45. // returns true if we use a linear
  46. // search instead of the hash table.
  47. bool is_small() const noexcept
  48. {
  49. return capacity <=
  50. detail::small_object_size_;
  51. }
  52. key_value_pair&
  53. operator[](
  54. std::size_t pos) noexcept
  55. {
  56. return reinterpret_cast<
  57. key_value_pair*>(
  58. this + 1)[pos];
  59. }
  60. // VFALCO This is exported for tests
  61. BOOST_JSON_DECL
  62. std::size_t
  63. digest(string_view key) const noexcept;
  64. inline
  65. index_t&
  66. bucket(std::size_t hash) noexcept;
  67. inline
  68. index_t&
  69. bucket(string_view key) noexcept;
  70. inline
  71. void
  72. clear() noexcept;
  73. static
  74. inline
  75. table*
  76. allocate(
  77. std::size_t capacity,
  78. std::uintptr_t salt,
  79. storage_ptr const& sp);
  80. static
  81. void
  82. deallocate(
  83. table* p,
  84. storage_ptr const& sp) noexcept
  85. {
  86. if(p->capacity == 0)
  87. return;
  88. if(! p->is_small())
  89. sp->deallocate(p,
  90. sizeof(table) + p->capacity * (
  91. sizeof(key_value_pair) +
  92. sizeof(index_t)));
  93. else
  94. sp->deallocate(p,
  95. sizeof(table) + p->capacity *
  96. sizeof(key_value_pair));
  97. }
  98. };
  99. //----------------------------------------------------------
  100. class object::revert_construct
  101. {
  102. object* obj_;
  103. BOOST_JSON_DECL
  104. void
  105. destroy() noexcept;
  106. public:
  107. explicit
  108. revert_construct(
  109. object& obj) noexcept
  110. : obj_(&obj)
  111. {
  112. }
  113. ~revert_construct()
  114. {
  115. if(! obj_)
  116. return;
  117. destroy();
  118. }
  119. void
  120. commit() noexcept
  121. {
  122. obj_ = nullptr;
  123. }
  124. };
  125. //----------------------------------------------------------
  126. class object::revert_insert
  127. {
  128. object* obj_;
  129. table* t_ = nullptr;
  130. std::size_t size_;
  131. BOOST_JSON_DECL
  132. void
  133. destroy() noexcept;
  134. public:
  135. explicit
  136. revert_insert(
  137. object& obj,
  138. std::size_t capacity)
  139. : obj_(&obj)
  140. , size_(obj_->size())
  141. {
  142. if( capacity > obj_->capacity() )
  143. t_ = obj_->reserve_impl(capacity);
  144. }
  145. ~revert_insert()
  146. {
  147. if(! obj_)
  148. return;
  149. destroy();
  150. if( t_ )
  151. {
  152. table::deallocate( obj_->t_, obj_->sp_ );
  153. obj_->t_ = t_;
  154. }
  155. else
  156. {
  157. obj_->t_->size = static_cast<index_t>(size_);
  158. }
  159. }
  160. void
  161. commit() noexcept
  162. {
  163. BOOST_ASSERT(obj_);
  164. if( t_ )
  165. table::deallocate( t_, obj_->sp_ );
  166. obj_ = nullptr;
  167. }
  168. };
  169. //----------------------------------------------------------
  170. //
  171. // Iterators
  172. //
  173. //----------------------------------------------------------
  174. auto
  175. object::
  176. begin() noexcept ->
  177. iterator
  178. {
  179. return &(*t_)[0];
  180. }
  181. auto
  182. object::
  183. begin() const noexcept ->
  184. const_iterator
  185. {
  186. return &(*t_)[0];
  187. }
  188. auto
  189. object::
  190. cbegin() const noexcept ->
  191. const_iterator
  192. {
  193. return &(*t_)[0];
  194. }
  195. auto
  196. object::
  197. end() noexcept ->
  198. iterator
  199. {
  200. return &(*t_)[t_->size];
  201. }
  202. auto
  203. object::
  204. end() const noexcept ->
  205. const_iterator
  206. {
  207. return &(*t_)[t_->size];
  208. }
  209. auto
  210. object::
  211. cend() const noexcept ->
  212. const_iterator
  213. {
  214. return &(*t_)[t_->size];
  215. }
  216. auto
  217. object::
  218. rbegin() noexcept ->
  219. reverse_iterator
  220. {
  221. return reverse_iterator(end());
  222. }
  223. auto
  224. object::
  225. rbegin() const noexcept ->
  226. const_reverse_iterator
  227. {
  228. return const_reverse_iterator(end());
  229. }
  230. auto
  231. object::
  232. crbegin() const noexcept ->
  233. const_reverse_iterator
  234. {
  235. return const_reverse_iterator(end());
  236. }
  237. auto
  238. object::
  239. rend() noexcept ->
  240. reverse_iterator
  241. {
  242. return reverse_iterator(begin());
  243. }
  244. auto
  245. object::
  246. rend() const noexcept ->
  247. const_reverse_iterator
  248. {
  249. return const_reverse_iterator(begin());
  250. }
  251. auto
  252. object::
  253. crend() const noexcept ->
  254. const_reverse_iterator
  255. {
  256. return const_reverse_iterator(begin());
  257. }
  258. //----------------------------------------------------------
  259. //
  260. // Capacity
  261. //
  262. //----------------------------------------------------------
  263. bool
  264. object::
  265. empty() const noexcept
  266. {
  267. return t_->size == 0;
  268. }
  269. auto
  270. object::
  271. size() const noexcept ->
  272. std::size_t
  273. {
  274. return t_->size;
  275. }
  276. constexpr
  277. std::size_t
  278. object::
  279. max_size() noexcept
  280. {
  281. // max_size depends on the address model
  282. using min = std::integral_constant<std::size_t,
  283. (std::size_t(-1) - sizeof(table)) /
  284. (sizeof(key_value_pair) + sizeof(index_t))>;
  285. return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
  286. min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
  287. }
  288. auto
  289. object::
  290. capacity() const noexcept ->
  291. std::size_t
  292. {
  293. return t_->capacity;
  294. }
  295. void
  296. object::
  297. reserve(std::size_t new_capacity)
  298. {
  299. if( new_capacity <= capacity() )
  300. return;
  301. table* const old_table = reserve_impl(new_capacity);
  302. table::deallocate( old_table, sp_ );
  303. }
  304. //----------------------------------------------------------
  305. //
  306. // Lookup
  307. //
  308. //----------------------------------------------------------
  309. value&
  310. object::
  311. at(string_view key, source_location const& loc) &
  312. {
  313. auto const& self = *this;
  314. return const_cast< value& >( self.at(key, loc) );
  315. }
  316. value&&
  317. object::
  318. at(string_view key, source_location const& loc) &&
  319. {
  320. return std::move( at(key, loc) );
  321. }
  322. //----------------------------------------------------------
  323. template<class P, class>
  324. auto
  325. object::
  326. insert(P&& p) ->
  327. std::pair<iterator, bool>
  328. {
  329. key_value_pair v(
  330. std::forward<P>(p), sp_);
  331. return emplace_impl( v.key(), pilfer(v) );
  332. }
  333. template<class M>
  334. auto
  335. object::
  336. insert_or_assign(
  337. string_view key, M&& m) ->
  338. std::pair<iterator, bool>
  339. {
  340. std::pair<iterator, bool> result = emplace_impl(
  341. key, key, static_cast<M&&>(m) );
  342. if( !result.second )
  343. {
  344. value(static_cast<M>(m), sp_).swap(
  345. result.first->value());
  346. }
  347. return result;
  348. }
  349. template<class Arg>
  350. auto
  351. object::
  352. emplace(
  353. string_view key,
  354. Arg&& arg) ->
  355. std::pair<iterator, bool>
  356. {
  357. return emplace_impl( key, key, static_cast<Arg&&>(arg) );
  358. }
  359. //----------------------------------------------------------
  360. //
  361. // (private)
  362. //
  363. //----------------------------------------------------------
  364. template<class InputIt>
  365. void
  366. object::
  367. construct(
  368. InputIt first,
  369. InputIt last,
  370. std::size_t min_capacity,
  371. std::input_iterator_tag)
  372. {
  373. reserve(min_capacity);
  374. revert_construct r(*this);
  375. while(first != last)
  376. {
  377. insert(*first);
  378. ++first;
  379. }
  380. r.commit();
  381. }
  382. template<class InputIt>
  383. void
  384. object::
  385. construct(
  386. InputIt first,
  387. InputIt last,
  388. std::size_t min_capacity,
  389. std::forward_iterator_tag)
  390. {
  391. auto n = static_cast<
  392. std::size_t>(std::distance(
  393. first, last));
  394. if( n < min_capacity)
  395. n = min_capacity;
  396. reserve(n);
  397. revert_construct r(*this);
  398. while(first != last)
  399. {
  400. insert(*first);
  401. ++first;
  402. }
  403. r.commit();
  404. }
  405. template<class InputIt>
  406. void
  407. object::
  408. insert(
  409. InputIt first,
  410. InputIt last,
  411. std::input_iterator_tag)
  412. {
  413. // Since input iterators cannot be rewound,
  414. // we keep inserted elements on an exception.
  415. //
  416. while(first != last)
  417. {
  418. insert(*first);
  419. ++first;
  420. }
  421. }
  422. template<class InputIt>
  423. void
  424. object::
  425. insert(
  426. InputIt first,
  427. InputIt last,
  428. std::forward_iterator_tag)
  429. {
  430. auto const n =
  431. static_cast<std::size_t>(
  432. std::distance(first, last));
  433. auto const n0 = size();
  434. if(n > max_size() - n0)
  435. {
  436. BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
  437. detail::throw_system_error( error::object_too_large, &loc );
  438. }
  439. revert_insert r( *this, n0 + n );
  440. while(first != last)
  441. {
  442. insert(*first);
  443. ++first;
  444. }
  445. r.commit();
  446. }
  447. template< class... Args >
  448. std::pair<object::iterator, bool>
  449. object::
  450. emplace_impl( string_view key, Args&& ... args )
  451. {
  452. std::pair<iterator, std::size_t> search_result(nullptr, 0);
  453. if( !empty() )
  454. {
  455. search_result = detail::find_in_object(*this, key);
  456. if( search_result.first )
  457. return { search_result.first, false };
  458. }
  459. // we create the new value before reserving, in case it is a reference to
  460. // a subobject of the current object
  461. key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
  462. // the key might get deallocated too
  463. key = kv.key();
  464. std::size_t const old_capacity = capacity();
  465. reserve(size() + 1);
  466. if( (empty() && capacity() > detail::small_object_size_)
  467. || (capacity() != old_capacity) )
  468. search_result.second = detail::digest(
  469. key.begin(), key.end(), t_->salt);
  470. BOOST_ASSERT(
  471. t_->is_small() ||
  472. (search_result.second ==
  473. detail::digest(key.begin(), key.end(), t_->salt)) );
  474. return { insert_impl(pilfer(kv), search_result.second), true };
  475. }
  476. //----------------------------------------------------------
  477. namespace detail {
  478. unchecked_object::
  479. ~unchecked_object()
  480. {
  481. if(! data_)
  482. return;
  483. if(sp_.is_not_shared_and_deallocate_is_trivial())
  484. return;
  485. value* p = data_;
  486. while(size_--)
  487. {
  488. p[0].~value();
  489. p[1].~value();
  490. p += 2;
  491. }
  492. }
  493. } // detail
  494. } // namespace json
  495. } // namespace boost
  496. #endif