Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
json_object.h
1#ifndef SOURCEMETA_CORE_JSON_OBJECT_H_
2#define SOURCEMETA_CORE_JSON_OBJECT_H_
3
4#include <algorithm> // std::sort
5#include <cassert> // assert
6#include <concepts> // std::same_as
7#include <cstddef> // std::size_t
8#include <initializer_list> // std::initializer_list
9#include <iterator> // std::advance
10#include <string_view> // std::basic_string_view
11#include <type_traits> // std::remove_cvref_t
12#include <utility> // std::pair, std::move, std::unreachable
13#include <vector> // std::vector
14
15namespace sourcemeta::core {
16
19template <typename Key, typename Value, typename Hash> class JSONObject {
20public:
21 JSONObject() = default;
22
23 using key_type = Key;
24 using mapped_type = Value;
25 using hash_type = typename Hash::hash_type;
26 using pair_value_type = std::pair<key_type, mapped_type>;
28 using KeyView = std::basic_string_view<typename Key::value_type,
29 typename Key::traits_type>;
30
32 JSONObject(std::initializer_list<pair_value_type> entries) : data{} {
33 this->data.reserve(entries.size());
34 for (auto &&entry : entries) {
35 this->emplace(std::move(entry.first), std::move(entry.second));
36 }
37 }
38
40 struct Entry {
42 key_type first;
44 mapped_type second;
46 hash_type hash;
47
62 [[nodiscard]] inline auto key_equals(const KeyView key,
63 const hash_type key_hash) const
64 -> bool {
65 assert(JSONObject::hash(key) == key_hash);
66 // A perfect hash captures the key bytes but not its length, so two keys
67 // that differ only by trailing NUL bytes hash equal. Comparing sizes
68 // disambiguates them without the cost of a full string comparison
69 return this->hash == key_hash &&
70 (hasher.is_perfect(key_hash) ? this->first.size() == key.size()
71 : this->first == key);
72 }
73 };
74
75 using underlying_type = std::vector<Entry>;
76 using value_type = typename underlying_type::value_type;
77 using size_type = typename underlying_type::size_type;
78 using difference_type = typename underlying_type::difference_type;
79 using allocator_type = typename underlying_type::allocator_type;
80 using reference = typename underlying_type::reference;
81 using const_reference = typename underlying_type::const_reference;
82 using pointer = typename underlying_type::pointer;
83 using const_pointer = typename underlying_type::const_pointer;
84 using const_iterator = typename underlying_type::const_iterator;
85
86 // Operators
87 // We cannot default given that this class references
88 // a JSON "value" as an incomplete type
89
90 auto operator<(const JSONObject<Key, Value, Hash> &other) const noexcept
91 -> bool {
92 // Objects have no inherent order, but a deterministic strict weak ordering
93 // independent of insertion order is needed so that collections of objects
94 // can be sorted. Smaller objects come first, and objects of equal size are
95 // ordered as their entries would compare in key order. That outcome is
96 // decided entirely by the smallest key at which the two objects differ,
97 // which is found by scanning the entries in place to avoid allocating
98 if (this->data.size() != other.data.size()) {
99 return this->data.size() < other.data.size();
100 }
101
102 const Key *decisive_key{nullptr};
103 bool decision{false};
104 for (const auto &entry : this->data) {
105 const auto match{other.find(entry.first)};
106 const bool differs{match == other.cend() ||
107 !(entry.second == match->second)};
108 if (differs && (decisive_key == nullptr || entry.first < *decisive_key)) {
109 decisive_key = &entry.first;
110 decision = match == other.cend() || entry.second < match->second;
111 }
112 }
113
114 for (const auto &entry : other.data) {
115 if (this->find(entry.first) == this->cend() &&
116 (decisive_key == nullptr || entry.first < *decisive_key)) {
117 decisive_key = &entry.first;
118 decision = false;
119 }
120 }
121
122 return decision;
123 }
124
125 auto operator<=(const JSONObject<Key, Value, Hash> &other) const noexcept
126 -> bool {
127 return !(other < *this);
128 }
129 auto operator>(const JSONObject<Key, Value, Hash> &other) const noexcept
130 -> bool {
131 return other < *this;
132 }
133 auto operator>=(const JSONObject<Key, Value, Hash> &other) const noexcept
134 -> bool {
135 return !(*this < other);
136 }
137
138 auto operator==(const JSONObject<Key, Value, Hash> &other) const noexcept
139 -> bool {
140 if (this->size() != other.size()) {
141 return false;
142 }
143
144 for (const auto &entry : this->data) {
145 const auto *result{other.try_at(entry.first, entry.hash)};
146 if (!result || *result != entry.second) {
147 return false;
148 }
149 }
150
151 return true;
152 }
153
154 auto operator!=(const JSONObject<Key, Value, Hash> &other) const noexcept
155 -> bool = default;
156
157 [[nodiscard]] inline auto begin() const noexcept -> const_iterator {
158 return this->data.begin();
159 }
161 [[nodiscard]] inline auto end() const noexcept -> const_iterator {
162 return this->data.end();
163 }
165 [[nodiscard]] inline auto cbegin() const noexcept -> const_iterator {
166 return this->data.cbegin();
167 }
169 [[nodiscard]] inline auto cend() const noexcept -> const_iterator {
170 return this->data.cend();
171 }
172
173 // GCC does not optimise well across implicit type conversions such as
174 // std::string to std::string_view, so we provide separate overloads with
175 // duplicated logic instead of unifying on a single parameter type. The
176 // `KeyView`-accepting overloads are constrained to actual `std::string_view`
177 // arguments only, so callers passing a string literal continue to bind to
178 // the `Key`-accepting overload as before.
179
181 [[nodiscard]] static constexpr auto hash(const Key &key) noexcept
182 -> hash_type {
183 return hasher(key);
184 }
185
187 template <typename T>
188 requires std::same_as<std::remove_cvref_t<T>, KeyView>
189 [[nodiscard]] static constexpr auto hash(T key) noexcept -> hash_type {
190 return hasher(key.data(), key.size());
191 }
192
194 [[nodiscard]] static constexpr auto hash(const char *raw_data,
195 const std::size_t raw_size) noexcept
196 -> hash_type {
197 return hasher(raw_data, raw_size);
198 }
199
201 [[nodiscard]] inline auto find(const Key &key) const -> const_iterator {
202 const auto key_hash{this->hash(key)};
203
204 // Move the perfect hash condition out of the loop for extra performance
205 if (this->hasher.is_perfect(key_hash)) {
206 for (size_type index = 0; index < this->size(); index++) {
207 if (this->data[index].hash == key_hash &&
208 this->data[index].first.size() == key.size()) {
209 auto iterator{this->cbegin()};
210 std::advance(iterator, index);
211 return iterator;
212 }
213 }
214 } else {
215 for (size_type index = 0; index < this->size(); index++) {
216 if (this->data[index].hash == key_hash &&
217 this->data[index].first == key) {
218 auto iterator{this->cbegin()};
219 std::advance(iterator, index);
220 return iterator;
221 }
222 }
223 }
224
225 return this->cend();
226 }
227
229 template <typename T>
230 requires std::same_as<std::remove_cvref_t<T>, KeyView>
231 [[nodiscard]] inline auto find(T key) const -> const_iterator {
232 const auto key_hash{this->hash(key)};
233
234 // Move the perfect hash condition out of the loop for extra performance
235 if (this->hasher.is_perfect(key_hash)) {
236 for (size_type index = 0; index < this->size(); index++) {
237 if (this->data[index].hash == key_hash &&
238 this->data[index].first.size() == key.size()) {
239 auto iterator{this->cbegin()};
240 std::advance(iterator, index);
241 return iterator;
242 }
243 }
244 } else {
245 for (size_type index = 0; index < this->size(); index++) {
246 if (this->data[index].hash == key_hash &&
247 this->data[index].first == key) {
248 auto iterator{this->cbegin()};
249 std::advance(iterator, index);
250 return iterator;
251 }
252 }
253 }
254
255 return this->cend();
256 }
257
259 [[nodiscard]] inline auto defines(const Key &key, const hash_type hash) const
260 -> bool {
261 assert(this->hash(key) == hash);
262
263 // Move the perfect hash condition out of the loop for extra performance
264 if (this->hasher.is_perfect(hash)) {
265 for (const auto &entry : *this) {
266 if (entry.hash == hash && entry.first.size() == key.size()) {
267 return true;
268 }
269 }
270 } else {
271 for (const auto &entry : *this) {
272 if (entry.hash == hash && entry.first == key) {
273 return true;
274 }
275 }
276 }
277
278 return false;
279 }
280
282 template <typename T>
283 requires std::same_as<std::remove_cvref_t<T>, KeyView>
284 [[nodiscard]] inline auto defines(T key, const hash_type hash) const -> bool {
285 assert(this->hash(key) == hash);
286
287 // Move the perfect hash condition out of the loop for extra performance
288 if (this->hasher.is_perfect(hash)) {
289 for (const auto &entry : *this) {
290 if (entry.hash == hash && entry.first.size() == key.size()) {
291 return true;
292 }
293 }
294 } else {
295 for (const auto &entry : *this) {
296 if (entry.hash == hash && entry.first == key) {
297 return true;
298 }
299 }
300 }
301
302 return false;
303 }
304
306 [[nodiscard]] inline auto size() const -> std::size_t {
307 return this->data.size();
308 }
309
311 [[nodiscard]] inline auto empty() const -> bool { return this->data.empty(); }
312
314 inline auto reserve(const size_type capacity) -> void {
315 this->data.reserve(capacity);
316 }
317
319 [[nodiscard]] inline auto at(const size_type index) const -> const Entry & {
320 return this->data.at(index);
321 }
322
324 [[nodiscard]] inline auto at(const Key &key, const hash_type key_hash) const
325 -> const mapped_type & {
326 assert(this->hash(key) == key_hash);
327
328 // Move the perfect hash condition out of the loop for extra performance
329 if (this->hasher.is_perfect(key_hash)) {
330 for (const auto &entry : *this) {
331 if (entry.hash == key_hash && entry.first.size() == key.size()) {
332 return entry.second;
333 }
334 }
335 } else {
336 for (const auto &entry : *this) {
337 if (entry.hash == key_hash && entry.first == key) {
338 return entry.second;
339 }
340 }
341 }
342
343 std::unreachable();
344 }
345
347 template <typename T>
348 requires std::same_as<std::remove_cvref_t<T>, KeyView>
349 [[nodiscard]] inline auto at(T key, const hash_type key_hash) const
350 -> const mapped_type & {
351 assert(this->hash(key) == key_hash);
352
353 // Move the perfect hash condition out of the loop for extra performance
354 if (this->hasher.is_perfect(key_hash)) {
355 for (const auto &entry : *this) {
356 if (entry.hash == key_hash && entry.first.size() == key.size()) {
357 return entry.second;
358 }
359 }
360 } else {
361 for (const auto &entry : *this) {
362 if (entry.hash == key_hash && entry.first == key) {
363 return entry.second;
364 }
365 }
366 }
367
368 std::unreachable();
369 }
370
372 inline auto at(const Key &key, const hash_type key_hash) -> mapped_type & {
373 assert(this->hash(key) == key_hash);
374
375 // Move the perfect hash condition out of the loop for extra performance
376 if (this->hasher.is_perfect(key_hash)) {
377 for (auto &entry : this->data) {
378 if (entry.hash == key_hash && entry.first.size() == key.size()) {
379 return entry.second;
380 }
381 }
382 } else {
383 for (auto &entry : this->data) {
384 if (entry.hash == key_hash && entry.first == key) {
385 return entry.second;
386 }
387 }
388 }
389
390 std::unreachable();
391 }
392
394 template <typename T>
395 requires std::same_as<std::remove_cvref_t<T>, KeyView>
396 inline auto at(T key, const hash_type key_hash) -> mapped_type & {
397 assert(this->hash(key) == key_hash);
398
399 // Move the perfect hash condition out of the loop for extra performance
400 if (this->hasher.is_perfect(key_hash)) {
401 for (auto &entry : this->data) {
402 if (entry.hash == key_hash && entry.first.size() == key.size()) {
403 return entry.second;
404 }
405 }
406 } else {
407 for (auto &entry : this->data) {
408 if (entry.hash == key_hash && entry.first == key) {
409 return entry.second;
410 }
411 }
412 }
413
414 std::unreachable();
415 }
416
418 [[nodiscard]] inline auto try_at(const Key &key, const hash_type key_hash)
419 -> mapped_type * {
420 assert(this->hash(key) == key_hash);
421
422 // Move the perfect hash condition out of the loop for extra performance
423 if (this->hasher.is_perfect(key_hash)) {
424 for (auto &entry : this->data) {
425 if (entry.hash == key_hash && entry.first.size() == key.size()) {
426 return &entry.second;
427 }
428 }
429 } else {
430 for (auto &entry : this->data) {
431 if (entry.hash == key_hash && entry.first == key) {
432 return &entry.second;
433 }
434 }
435 }
436
437 return nullptr;
438 }
439
441 template <typename T>
442 requires std::same_as<std::remove_cvref_t<T>, KeyView>
443 [[nodiscard]] inline auto try_at(T key, const hash_type key_hash)
444 -> mapped_type * {
445 assert(this->hash(key) == key_hash);
446
447 // Move the perfect hash condition out of the loop for extra performance
448 if (this->hasher.is_perfect(key_hash)) {
449 for (auto &entry : this->data) {
450 if (entry.hash == key_hash && entry.first.size() == key.size()) {
451 return &entry.second;
452 }
453 }
454 } else {
455 for (auto &entry : this->data) {
456 if (entry.hash == key_hash && entry.first == key) {
457 return &entry.second;
458 }
459 }
460 }
461
462 return nullptr;
463 }
464
466 [[nodiscard]] inline auto try_at(const Key &key,
467 const hash_type key_hash) const
468 -> const mapped_type * {
469 assert(this->hash(key) == key_hash);
470
471 // Move the perfect hash condition out of the loop for extra performance
472 if (this->hasher.is_perfect(key_hash)) {
473 for (size_type index = 0; index < this->size(); index++) {
474 if (this->data[index].hash == key_hash &&
475 this->data[index].first.size() == key.size()) {
476 return &this->data[index].second;
477 }
478 }
479 } else {
480 for (size_type index = 0; index < this->size(); index++) {
481 if (this->data[index].hash == key_hash &&
482 this->data[index].first == key) {
483 return &this->data[index].second;
484 }
485 }
486 }
487
488 return nullptr;
489 }
490
492 template <typename T>
493 requires std::same_as<std::remove_cvref_t<T>, KeyView>
494 [[nodiscard]] inline auto try_at(T key, const hash_type key_hash) const
495 -> const mapped_type * {
496 assert(this->hash(key) == key_hash);
497
498 // Move the perfect hash condition out of the loop for extra performance
499 if (this->hasher.is_perfect(key_hash)) {
500 for (size_type index = 0; index < this->size(); index++) {
501 if (this->data[index].hash == key_hash &&
502 this->data[index].first.size() == key.size()) {
503 return &this->data[index].second;
504 }
505 }
506 } else {
507 for (size_type index = 0; index < this->size(); index++) {
508 if (this->data[index].hash == key_hash &&
509 this->data[index].first == key) {
510 return &this->data[index].second;
511 }
512 }
513 }
514
515 return nullptr;
516 }
517
520 [[nodiscard]] inline auto try_at(const Key &key, const hash_type key_hash,
521 size_type &start) const
522 -> const mapped_type * {
523 assert(this->hash(key) == key_hash);
524 const auto object_size{this->size()};
525 assert(start <= object_size);
526 if (this->hasher.is_perfect(key_hash)) {
527 for (size_type count = 0; count < object_size; count++) {
528 const auto index{(start + count) % object_size};
529 if (this->data[index].hash == key_hash &&
530 this->data[index].first.size() == key.size()) {
531 start = index + 1;
532 return &this->data[index].second;
533 }
534 }
535 } else {
536 for (size_type count = 0; count < object_size; count++) {
537 const auto index{(start + count) % object_size};
538 if (this->data[index].hash == key_hash &&
539 this->data[index].first == key) {
540 start = index + 1;
541 return &this->data[index].second;
542 }
543 }
544 }
545
546 return nullptr;
547 }
548
551 template <typename T>
552 requires std::same_as<std::remove_cvref_t<T>, KeyView>
553 [[nodiscard]] inline auto try_at(T key, const hash_type key_hash,
554 size_type &start) const
555 -> const mapped_type * {
556 assert(this->hash(key) == key_hash);
557 const auto object_size{this->size()};
558 assert(start <= object_size);
559 if (this->hasher.is_perfect(key_hash)) {
560 for (size_type count = 0; count < object_size; count++) {
561 const auto index{(start + count) % object_size};
562 if (this->data[index].hash == key_hash &&
563 this->data[index].first.size() == key.size()) {
564 start = index + 1;
565 return &this->data[index].second;
566 }
567 }
568 } else {
569 for (size_type count = 0; count < object_size; count++) {
570 const auto index{(start + count) % object_size};
571 if (this->data[index].hash == key_hash &&
572 this->data[index].first == key) {
573 start = index + 1;
574 return &this->data[index].second;
575 }
576 }
577 }
578
579 return nullptr;
580 }
581
583 auto try_emplace_before(const Key &key, const mapped_type &value,
584 const Key &suffix) -> hash_type {
585 const auto key_hash{this->hash(key)};
586 const auto suffix_hash{this->hash(suffix)};
587
588 if (this->hasher.is_perfect(key_hash)) {
589 for (auto iterator = this->data.begin(); iterator != this->data.end();
590 ++iterator) {
591 if (iterator->hash == key_hash &&
592 iterator->first.size() == key.size()) {
593 iterator->second = value;
594 return key_hash;
595 } else if (iterator->hash == suffix_hash && iterator->first == suffix) {
596 this->data.insert(iterator, {key, value, key_hash});
597 return key_hash;
598 }
599 }
600 } else {
601 for (auto iterator = this->data.begin(); iterator != this->data.end();
602 ++iterator) {
603 if (iterator->hash == key_hash && iterator->first == key) {
604 iterator->second = value;
605 return key_hash;
606 } else if (iterator->hash == suffix_hash && iterator->first == suffix) {
607 this->data.insert(iterator, {key, value, key_hash});
608 return key_hash;
609 }
610 }
611 }
612
613 this->data.push_back({key, value, key_hash});
614 return key_hash;
615 }
616
618 inline auto emplace(Key &&key, mapped_type &&value) -> hash_type {
619 const auto key_hash{this->hash(key)};
620
621 if (this->hasher.is_perfect(key_hash)) {
622 for (auto &entry : this->data) {
623 if (entry.hash == key_hash && entry.first.size() == key.size()) {
624 entry.second = std::move(value);
625 return key_hash;
626 }
627 }
628 } else {
629 for (auto &entry : this->data) {
630 if (entry.hash == key_hash && entry.first == key) {
631 entry.second = std::move(value);
632 return key_hash;
633 }
634 }
635 }
636
637 this->data.push_back({std::move(key), std::move(value), key_hash});
638 return key_hash;
639 }
640
642 inline auto emplace(const Key &key, mapped_type &&value) -> hash_type {
643 const auto key_hash{this->hash(key)};
644
645 if (this->hasher.is_perfect(key_hash)) {
646 for (auto &entry : this->data) {
647 if (entry.hash == key_hash && entry.first.size() == key.size()) {
648 entry.second = std::move(value);
649 return key_hash;
650 }
651 }
652 } else {
653 for (auto &entry : this->data) {
654 if (entry.hash == key_hash && entry.first == key) {
655 entry.second = std::move(value);
656 return key_hash;
657 }
658 }
659 }
660
661 this->data.push_back({key, std::move(value), key_hash});
662 return key_hash;
663 }
664
666 inline auto emplace(const Key &key, const mapped_type &value) -> hash_type {
667 const auto key_hash{this->hash(key)};
668
669 if (this->hasher.is_perfect(key_hash)) {
670 for (auto &entry : this->data) {
671 if (entry.hash == key_hash && entry.first.size() == key.size()) {
672 entry.second = value;
673 return key_hash;
674 }
675 }
676 } else {
677 for (auto &entry : this->data) {
678 if (entry.hash == key_hash && entry.first == key) {
679 entry.second = value;
680 return key_hash;
681 }
682 }
683 }
684
685 this->data.push_back({key, value, key_hash});
686 return key_hash;
687 }
688
690 inline auto emplace_assume_new(Key &&key, mapped_type &&value) -> hash_type {
691 const auto key_hash{this->hash(key)};
692 this->data.push_back({std::move(key), std::move(value), key_hash});
693 return key_hash;
694 }
695
697 inline auto emplace_assume_new(const Key &key, mapped_type &&value)
698 -> hash_type {
699 const auto key_hash{this->hash(key)};
700 this->data.push_back({key, std::move(value), key_hash});
701 return key_hash;
702 }
703
706 inline auto emplace_assume_new(Key &&key, mapped_type &&value,
707 const hash_type key_hash) -> mapped_type & {
708 this->data.push_back({std::move(key), std::move(value), key_hash});
709 return this->data.back().second;
710 }
711
713 inline auto emplace_assume_new(const Key &key, mapped_type &&value,
714 const hash_type key_hash) -> void {
715 this->data.push_back({key, std::move(value), key_hash});
716 }
717
719 [[nodiscard]] inline auto back_key() const noexcept -> const Key & {
720 assert(!this->data.empty());
721 return this->data.back().first;
722 }
723
725 inline auto clear() noexcept -> void { this->data.clear(); }
726
728 auto rename(const Key &key, const hash_type key_hash, Key &&to,
729 const hash_type to_hash) -> void {
730 this->erase(to, to_hash);
731
732 if (this->hasher.is_perfect(key_hash)) {
733 for (auto &entry : this->data) {
734 if (entry.hash == key_hash && entry.first.size() == key.size()) {
735 entry.first = std::move(to);
736 entry.hash = to_hash;
737 break;
738 }
739 }
740 } else {
741 for (auto &entry : this->data) {
742 if (entry.hash == key_hash && entry.first == key) {
743 entry.first = std::move(to);
744 entry.hash = to_hash;
745 break;
746 }
747 }
748 }
749 }
750
752 auto erase(const Key &key, const hash_type key_hash) -> size_type {
753 const auto current_size{this->size()};
754
755 if (this->hasher.is_perfect(key_hash)) {
756 for (auto iterator = this->data.begin(); iterator != this->data.end();
757 ++iterator) {
758 if (iterator->hash == key_hash &&
759 iterator->first.size() == key.size()) {
760 this->data.erase(iterator);
761 return current_size - 1;
762 }
763 }
764 } else {
765 for (auto iterator = this->data.begin(); iterator != this->data.end();
766 ++iterator) {
767 if (iterator->hash == key_hash && iterator->first == key) {
768 this->data.erase(iterator);
769 return current_size - 1;
770 }
771 }
772 }
773
774 return current_size;
775 }
776
778 template <typename T>
779 requires std::same_as<std::remove_cvref_t<T>, KeyView>
780 auto erase(T key, const hash_type key_hash) -> size_type {
781 const auto current_size{this->size()};
782
783 if (this->hasher.is_perfect(key_hash)) {
784 for (auto iterator = this->data.begin(); iterator != this->data.end();
785 ++iterator) {
786 if (iterator->hash == key_hash &&
787 iterator->first.size() == key.size()) {
788 this->data.erase(iterator);
789 return current_size - 1;
790 }
791 }
792 } else {
793 for (auto iterator = this->data.begin(); iterator != this->data.end();
794 ++iterator) {
795 if (iterator->hash == key_hash && iterator->first == key) {
796 this->data.erase(iterator);
797 return current_size - 1;
798 }
799 }
800 }
801
802 return current_size;
803 }
804
806 inline auto erase(const Key &key) -> size_type {
807 return this->erase(key, this->hash(key));
808 }
809
811 template <typename T>
812 requires std::same_as<std::remove_cvref_t<T>, KeyView>
813 inline auto erase(T key) -> size_type {
814 return this->erase(key, this->hash(key));
815 }
816
818 template <typename Compare> auto reorder(const Compare &compare) -> void {
819 std::sort(this->data.begin(), this->data.end(),
820 [&compare](const auto &left, const auto &right) -> auto {
821 return compare(left.first, right.first);
822 });
823 }
824
825private:
826 friend Value;
827// Exporting symbols that depends on the standard C++ library is considered
828// safe.
829// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN
830#if defined(_MSC_VER)
831#pragma warning(disable : 4251)
832#endif
833 static constexpr Hash hasher{};
834 underlying_type data;
835#if defined(_MSC_VER)
836#pragma warning(default : 4251)
837#endif
838};
839
840} // namespace sourcemeta::core
841
842#endif
static constexpr auto hash(const Key &key) noexcept -> hash_type
Compute a hash for a key.
Definition json_object.h:181
auto emplace_assume_new(Key &&key, mapped_type &&value) -> hash_type
Emplace an object property assuming the key does not already exist.
Definition json_object.h:690
auto clear() noexcept -> void
Remove every property in the object.
Definition json_object.h:725
auto find(T key) const -> const_iterator
Attempt to find an entry by key.
Definition json_object.h:231
auto try_at(const Key &key, const hash_type key_hash) const -> const mapped_type *
Try to access an object entry by its underlying positional index.
Definition json_object.h:466
auto at(T key, const hash_type key_hash) const -> const mapped_type &
Access an object entry by its key name.
Definition json_object.h:349
auto rename(const Key &key, const hash_type key_hash, Key &&to, const hash_type to_hash) -> void
Rename an object property in place.
Definition json_object.h:728
auto at(const size_type index) const -> const Entry &
Access an object entry by its underlying positional index.
Definition json_object.h:319
auto erase(T key, const hash_type key_hash) -> size_type
Erase an object property.
Definition json_object.h:780
auto reserve(const size_type capacity) -> void
Reserve capacity for a given number of entries.
Definition json_object.h:314
auto try_at(const Key &key, const hash_type key_hash) -> mapped_type *
Try to access an object entry by its key name.
Definition json_object.h:418
auto emplace(Key &&key, mapped_type &&value) -> hash_type
Emplace an object property.
Definition json_object.h:618
static constexpr auto hash(const char *raw_data, const std::size_t raw_size) noexcept -> hash_type
Compute a hash from raw data.
Definition json_object.h:194
auto emplace(const Key &key, const mapped_type &value) -> hash_type
Emplace an object property.
Definition json_object.h:666
static constexpr auto hash(T key) noexcept -> hash_type
Compute a hash for a key.
Definition json_object.h:189
auto empty() const -> bool
Check if the object is empty.
Definition json_object.h:311
auto try_emplace_before(const Key &key, const mapped_type &value, const Key &suffix) -> hash_type
Try to emplace a property before another property.
Definition json_object.h:583
auto reorder(const Compare &compare) -> void
Reorder object properties by keys according to a comparator function.
Definition json_object.h:818
auto defines(const Key &key, const hash_type hash) const -> bool
Check if an entry with the given key exists.
Definition json_object.h:259
auto erase(const Key &key) -> size_type
Erase an object property.
Definition json_object.h:806
auto find(const Key &key) const -> const_iterator
Attempt to find an entry by key.
Definition json_object.h:201
auto at(const Key &key, const hash_type key_hash) const -> const mapped_type &
Access an object entry by its key name.
Definition json_object.h:324
auto emplace_assume_new(Key &&key, mapped_type &&value, const hash_type key_hash) -> mapped_type &
Definition json_object.h:706
JSONObject(std::initializer_list< pair_value_type > entries)
Construct an object from a list of key and value pairs.
Definition json_object.h:32
auto emplace_assume_new(const Key &key, mapped_type &&value, const hash_type key_hash) -> void
Emplace an object property with a pre-computed hash.
Definition json_object.h:713
auto erase(T key) -> size_type
Erase an object property.
Definition json_object.h:813
auto back_key() const noexcept -> const Key &
Get the key of the last-inserted property.
Definition json_object.h:719
auto emplace_assume_new(const Key &key, mapped_type &&value) -> hash_type
Emplace an object property assuming the key does not already exist.
Definition json_object.h:697
auto at(const Key &key, const hash_type key_hash) -> mapped_type &
Access an object entry by its key name.
Definition json_object.h:372
auto try_at(T key, const hash_type key_hash) const -> const mapped_type *
Try to access an object entry by its underlying positional index.
Definition json_object.h:494
auto try_at(T key, const hash_type key_hash, size_type &start) const -> const mapped_type *
Definition json_object.h:553
auto try_at(T key, const hash_type key_hash) -> mapped_type *
Try to access an object entry by its key name.
Definition json_object.h:443
std::basic_string_view< typename Key::value_type, typename Key::traits_type > KeyView
The string view type used to look up object keys.
Definition json_object.h:28
auto size() const -> std::size_t
Check the size of the object.
Definition json_object.h:306
auto erase(const Key &key, const hash_type key_hash) -> size_type
Erase an object property.
Definition json_object.h:752
auto at(T key, const hash_type key_hash) -> mapped_type &
Access an object entry by its key name.
Definition json_object.h:396
auto defines(T key, const hash_type hash) const -> bool
Check if an entry with the given key exists.
Definition json_object.h:284
auto try_at(const Key &key, const hash_type key_hash, size_type &start) const -> const mapped_type *
Definition json_object.h:520
auto emplace(const Key &key, mapped_type &&value) -> hash_type
Emplace an object property.
Definition json_object.h:642
Definition json_object.h:19
A single object property entry.
Definition json_object.h:40
mapped_type second
The property value.
Definition json_object.h:44
auto key_equals(const KeyView key, const hash_type key_hash) const -> bool
Definition json_object.h:62
hash_type hash
The precomputed hash of the property key.
Definition json_object.h:46
key_type first
The property key.
Definition json_object.h:42