1#ifndef SOURCEMETA_CORE_JSON_OBJECT_H_
2#define SOURCEMETA_CORE_JSON_OBJECT_H_
8#include <initializer_list>
15namespace sourcemeta::core {
19template <
typename Key,
typename Value,
typename Hash>
class JSONObject {
21 JSONObject() =
default;
24 using mapped_type = Value;
25 using hash_type = Hash::HashType;
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>;
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));
63 const hash_type key_hash)
const ->
bool {
68 return this->hash == key_hash &&
69 (HASHER.is_perfect(key_hash) ? this->first.size() == key.size()
70 : this->first == key);
74 using underlying_type = std::vector<Entry>;
75 using value_type = underlying_type::value_type;
76 using size_type = underlying_type::size_type;
77 using difference_type = underlying_type::difference_type;
78 using allocator_type = underlying_type::allocator_type;
79 using reference = underlying_type::reference;
80 using const_reference = underlying_type::const_reference;
81 using pointer = underlying_type::pointer;
82 using const_pointer = underlying_type::const_pointer;
83 using const_iterator = underlying_type::const_iterator;
97 if (this->data_.size() != other.data_.size()) {
98 return this->data_.size() < other.data_.size();
101 const Key *decisive_key{
nullptr};
102 bool decision{
false};
103 for (
const auto &entry : this->data_) {
104 const auto match{other.find(entry.first)};
105 const bool differs{match == other.cend() ||
106 !(entry.second == match->second)};
107 if (differs && (decisive_key ==
nullptr || entry.first < *decisive_key)) {
108 decisive_key = &entry.first;
109 decision = match == other.cend() || entry.second < match->second;
113 for (
const auto &entry : other.data_) {
114 if (this->
find(entry.first) == this->cend() &&
115 (decisive_key ==
nullptr || entry.first < *decisive_key)) {
116 decisive_key = &entry.first;
124 auto operator<=(
const JSONObject<Key, Value, Hash> &other)
const noexcept
126 return !(other < *
this);
128 auto operator>(
const JSONObject<Key, Value, Hash> &other)
const noexcept
130 return other < *
this;
132 auto operator>=(
const JSONObject<Key, Value, Hash> &other)
const noexcept
134 return !(*
this < other);
137 auto operator==(
const JSONObject<Key, Value, Hash> &other)
const noexcept
139 if (this->
size() != other.size()) {
143 for (
const auto &entry : this->data_) {
144 const auto *result{other.try_at(entry.first, entry.hash)};
145 if ((result ==
nullptr) || *result != entry.second) {
153 auto operator!=(
const JSONObject<Key, Value, Hash> &other)
const noexcept
156 [[nodiscard]]
auto begin() const noexcept -> const_iterator {
157 return this->data_.begin();
160 [[nodiscard]]
auto end() const noexcept -> const_iterator {
161 return this->data_.end();
164 [[nodiscard]]
auto cbegin() const noexcept -> const_iterator {
165 return this->data_.cbegin();
168 [[nodiscard]]
auto cend() const noexcept -> const_iterator {
169 return this->data_.cend();
180 [[nodiscard]]
static constexpr auto hash(
const Key &key)
noexcept
186 template <
typename T>
187 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
188 [[nodiscard]]
static constexpr auto hash(T key)
noexcept -> hash_type {
189 return HASHER(key.data(), key.size());
193 [[nodiscard]]
static constexpr auto hash(
const char *raw_data,
194 const std::size_t raw_size)
noexcept
196 return HASHER(raw_data, raw_size);
200 [[nodiscard]]
auto find(
const Key &key)
const -> const_iterator {
201 const auto key_hash{this->
hash(key)};
204 if (this->HASHER.is_perfect(key_hash)) {
205 for (size_type index = 0; index < this->
size(); index++) {
206 if (this->data_[index].
hash == key_hash &&
207 this->data_[index].first.size() == key.size()) {
208 auto iterator{this->cbegin()};
209 std::advance(iterator, index);
214 for (size_type index = 0; index < this->
size(); index++) {
215 if (this->data_[index].
hash == key_hash &&
216 this->data_[index].first == key) {
217 auto iterator{this->cbegin()};
218 std::advance(iterator, index);
228 template <
typename T>
229 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
230 [[nodiscard]]
auto find(T key)
const -> const_iterator {
231 const auto key_hash{this->
hash(key)};
234 if (this->HASHER.is_perfect(key_hash)) {
235 for (size_type index = 0; index < this->
size(); index++) {
236 if (this->data_[index].
hash == key_hash &&
237 this->data_[index].first.size() == key.size()) {
238 auto iterator{this->cbegin()};
239 std::advance(iterator, index);
244 for (size_type index = 0; index < this->
size(); index++) {
245 if (this->data_[index].
hash == key_hash &&
246 this->data_[index].first == key) {
247 auto iterator{this->cbegin()};
248 std::advance(iterator, index);
258 [[nodiscard]]
auto defines(
const Key &key,
const hash_type
hash)
const
263 if (this->HASHER.is_perfect(
hash)) {
264 for (
const auto &entry : *
this) {
265 if (entry.hash ==
hash && entry.first.size() == key.size()) {
270 for (
const auto &entry : *
this) {
271 if (entry.hash ==
hash && entry.first == key) {
281 template <
typename T>
282 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
283 [[nodiscard]]
auto defines(T key,
const hash_type
hash)
const ->
bool {
287 if (this->HASHER.is_perfect(
hash)) {
288 for (
const auto &entry : *
this) {
289 if (entry.hash ==
hash && entry.first.size() == key.size()) {
294 for (
const auto &entry : *
this) {
295 if (entry.hash ==
hash && entry.first == key) {
305 [[nodiscard]]
auto size() const -> std::
size_t {
return this->data_.size(); }
308 [[nodiscard]]
auto empty() const ->
bool {
return this->data_.empty(); }
311 auto reserve(
const size_type capacity) ->
void {
312 this->data_.reserve(capacity);
316 [[nodiscard]]
auto at(
const size_type index)
const ->
const Entry & {
317 return this->data_.at(index);
321 [[nodiscard]]
auto at(
const Key &key,
const hash_type key_hash)
const
322 ->
const mapped_type & {
323 assert(this->
hash(key) == key_hash);
326 if (this->HASHER.is_perfect(key_hash)) {
327 for (
const auto &entry : *
this) {
328 if (entry.hash == key_hash && entry.first.size() == key.size()) {
333 for (
const auto &entry : *
this) {
334 if (entry.hash == key_hash && entry.first == key) {
344 template <
typename T>
345 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
346 [[nodiscard]]
auto at(T key,
const hash_type key_hash)
const
347 ->
const mapped_type & {
348 assert(this->
hash(key) == key_hash);
351 if (this->HASHER.is_perfect(key_hash)) {
352 for (
const auto &entry : *
this) {
353 if (entry.hash == key_hash && entry.first.size() == key.size()) {
358 for (
const auto &entry : *
this) {
359 if (entry.hash == key_hash && entry.first == key) {
369 auto at(
const Key &key,
const hash_type key_hash) -> mapped_type & {
370 assert(this->
hash(key) == key_hash);
373 if (this->HASHER.is_perfect(key_hash)) {
374 for (
auto &entry : this->data_) {
375 if (entry.hash == key_hash && entry.first.size() == key.size()) {
380 for (
auto &entry : this->data_) {
381 if (entry.hash == key_hash && entry.first == key) {
391 template <
typename T>
392 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
393 auto at(T key,
const hash_type key_hash) -> mapped_type & {
394 assert(this->
hash(key) == key_hash);
397 if (this->HASHER.is_perfect(key_hash)) {
398 for (
auto &entry : this->data_) {
399 if (entry.hash == key_hash && entry.first.size() == key.size()) {
404 for (
auto &entry : this->data_) {
405 if (entry.hash == key_hash && entry.first == key) {
415 [[nodiscard]]
auto try_at(
const Key &key,
const hash_type key_hash)
417 assert(this->
hash(key) == key_hash);
420 if (this->HASHER.is_perfect(key_hash)) {
421 for (
auto &entry : this->data_) {
422 if (entry.hash == key_hash && entry.first.size() == key.size()) {
423 return &entry.second;
427 for (
auto &entry : this->data_) {
428 if (entry.hash == key_hash && entry.first == key) {
429 return &entry.second;
438 template <
typename T>
439 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
440 [[nodiscard]]
auto try_at(T key,
const hash_type key_hash) -> mapped_type * {
441 assert(this->
hash(key) == key_hash);
444 if (this->HASHER.is_perfect(key_hash)) {
445 for (
auto &entry : this->data_) {
446 if (entry.hash == key_hash && entry.first.size() == key.size()) {
447 return &entry.second;
451 for (
auto &entry : this->data_) {
452 if (entry.hash == key_hash && entry.first == key) {
453 return &entry.second;
462 [[nodiscard]]
auto try_at(
const Key &key,
const hash_type key_hash)
const
463 ->
const mapped_type * {
464 assert(this->
hash(key) == key_hash);
467 if (this->HASHER.is_perfect(key_hash)) {
468 for (size_type index = 0; index < this->
size(); index++) {
469 if (this->data_[index].
hash == key_hash &&
470 this->data_[index].first.size() == key.size()) {
471 return &this->data_[index].second;
475 for (size_type index = 0; index < this->
size(); index++) {
476 if (this->data_[index].
hash == key_hash &&
477 this->data_[index].first == key) {
478 return &this->data_[index].second;
487 template <
typename T>
488 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
489 [[nodiscard]]
auto try_at(T key,
const hash_type key_hash)
const
490 ->
const mapped_type * {
491 assert(this->
hash(key) == key_hash);
494 if (this->HASHER.is_perfect(key_hash)) {
495 for (size_type index = 0; index < this->
size(); index++) {
496 if (this->data_[index].
hash == key_hash &&
497 this->data_[index].first.size() == key.size()) {
498 return &this->data_[index].second;
502 for (size_type index = 0; index < this->
size(); index++) {
503 if (this->data_[index].
hash == key_hash &&
504 this->data_[index].first == key) {
505 return &this->data_[index].second;
515 [[nodiscard]]
auto try_at(
const Key &key,
const hash_type key_hash,
516 size_type &start)
const ->
const mapped_type * {
517 assert(this->
hash(key) == key_hash);
518 const auto object_size{this->
size()};
519 assert(start <= object_size);
520 if (this->HASHER.is_perfect(key_hash)) {
521 for (size_type count = 0; count < object_size; count++) {
522 const auto index{(start + count) % object_size};
523 if (this->data_[index].
hash == key_hash &&
524 this->data_[index].first.size() == key.size()) {
526 return &this->data_[index].second;
530 for (size_type count = 0; count < object_size; count++) {
531 const auto index{(start + count) % object_size};
532 if (this->data_[index].
hash == key_hash &&
533 this->data_[index].first == key) {
535 return &this->data_[index].second;
545 template <
typename T>
546 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
547 [[nodiscard]]
auto try_at(T key,
const hash_type key_hash,
548 size_type &start)
const ->
const mapped_type * {
549 assert(this->
hash(key) == key_hash);
550 const auto object_size{this->
size()};
551 assert(start <= object_size);
552 if (this->HASHER.is_perfect(key_hash)) {
553 for (size_type count = 0; count < object_size; count++) {
554 const auto index{(start + count) % object_size};
555 if (this->data_[index].
hash == key_hash &&
556 this->data_[index].first.size() == key.size()) {
558 return &this->data_[index].second;
562 for (size_type count = 0; count < object_size; count++) {
563 const auto index{(start + count) % object_size};
564 if (this->data_[index].
hash == key_hash &&
565 this->data_[index].first == key) {
567 return &this->data_[index].second;
577 const Key &suffix) -> hash_type {
578 const auto key_hash{this->
hash(key)};
579 const auto suffix_hash{this->
hash(suffix)};
585 auto insertion_point{this->data_.end()};
587 if (this->HASHER.is_perfect(key_hash)) {
588 for (
auto iterator = this->data_.begin(); iterator != this->data_.end();
590 if (iterator->hash == key_hash &&
591 iterator->first.size() == key.size()) {
592 iterator->second = value;
595 if (insertion_point == this->data_.end() &&
596 iterator->hash == suffix_hash && iterator->first == suffix) {
597 insertion_point = iterator;
601 for (
auto iterator = this->data_.begin(); iterator != this->data_.end();
603 if (iterator->hash == key_hash && iterator->first == key) {
604 iterator->second = value;
607 if (insertion_point == this->data_.end() &&
608 iterator->hash == suffix_hash && iterator->first == suffix) {
609 insertion_point = iterator;
614 this->data_.insert(insertion_point, {key, value, key_hash});
619 auto emplace(Key &&key, mapped_type &&value) -> hash_type {
620 const auto key_hash{this->
hash(key)};
622 if (this->HASHER.is_perfect(key_hash)) {
623 for (
auto &entry : this->data_) {
624 if (entry.hash == key_hash && entry.first.size() == key.size()) {
625 entry.second = std::move(value);
630 for (
auto &entry : this->data_) {
631 if (entry.hash == key_hash && entry.first == key) {
632 entry.second = std::move(value);
638 this->data_.push_back({std::move(key), std::move(value), key_hash});
643 auto emplace(
const Key &key, mapped_type &&value) -> hash_type {
644 const auto key_hash{this->
hash(key)};
646 if (this->HASHER.is_perfect(key_hash)) {
647 for (
auto &entry : this->data_) {
648 if (entry.hash == key_hash && entry.first.size() == key.size()) {
649 entry.second = std::move(value);
654 for (
auto &entry : this->data_) {
655 if (entry.hash == key_hash && entry.first == key) {
656 entry.second = std::move(value);
662 this->data_.push_back({key, std::move(value), key_hash});
667 auto emplace(
const Key &key,
const mapped_type &value) -> hash_type {
668 const auto key_hash{this->
hash(key)};
670 if (this->HASHER.is_perfect(key_hash)) {
671 for (
auto &entry : this->data_) {
672 if (entry.hash == key_hash && entry.first.size() == key.size()) {
673 entry.second = value;
678 for (
auto &entry : this->data_) {
679 if (entry.hash == key_hash && entry.first == key) {
680 entry.second = value;
686 this->data_.push_back({key, value, key_hash});
692 const auto key_hash{this->
hash(key)};
693 this->data_.push_back({std::move(key), std::move(value), key_hash});
699 const auto key_hash{this->
hash(key)};
700 this->data_.push_back({key, std::move(value), key_hash});
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;
714 const hash_type key_hash) ->
void {
715 this->data_.push_back({key, std::move(value), key_hash});
719 [[nodiscard]]
auto back_key() const noexcept -> const Key & {
720 assert(!this->data_.empty());
721 return this->data_.back().first;
725 auto clear() noexcept ->
void { this->data_.clear(); }
728 auto rename(
const Key &key,
const hash_type key_hash, Key &&target,
729 const hash_type target_hash) ->
void {
730 this->
erase(target, target_hash);
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(target);
736 entry.hash = target_hash;
741 for (
auto &entry : this->data_) {
742 if (entry.hash == key_hash && entry.first == key) {
743 entry.first = std::move(target);
744 entry.hash = target_hash;
752 auto erase(
const Key &key,
const hash_type key_hash) -> size_type {
753 assert(this->
hash(key) == key_hash);
754 const auto current_size{this->
size()};
756 if (this->HASHER.is_perfect(key_hash)) {
757 for (
auto iterator = this->data_.begin(); iterator != this->data_.end();
759 if (iterator->hash == key_hash &&
760 iterator->first.size() == key.size()) {
761 this->data_.erase(iterator);
762 return current_size - 1;
766 for (
auto iterator = this->data_.begin(); iterator != this->data_.end();
768 if (iterator->hash == key_hash && iterator->first == key) {
769 this->data_.erase(iterator);
770 return current_size - 1;
779 template <
typename T>
780 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
781 auto erase(T key,
const hash_type key_hash) -> size_type {
782 assert(this->
hash(key) == key_hash);
783 const auto current_size{this->
size()};
785 if (this->HASHER.is_perfect(key_hash)) {
786 for (
auto iterator = this->data_.begin(); iterator != this->data_.end();
788 if (iterator->hash == key_hash &&
789 iterator->first.size() == key.size()) {
790 this->data_.erase(iterator);
791 return current_size - 1;
795 for (
auto iterator = this->data_.begin(); iterator != this->data_.end();
797 if (iterator->hash == key_hash && iterator->first == key) {
798 this->data_.erase(iterator);
799 return current_size - 1;
808 auto erase(
const Key &key) -> size_type {
809 return this->
erase(key, this->
hash(key));
813 template <
typename T>
814 requires std::same_as<std::remove_cvref_t<T>,
KeyView>
816 return this->
erase(key, this->
hash(key));
820 template <
typename Compare>
auto reorder(
const Compare &compare) ->
void {
821 std::sort(this->data_.begin(), this->data_.end(),
822 [&compare](
const auto &left,
const auto &right) ->
auto {
823 return compare(left.first, right.first);
834#pragma warning(disable : 4251)
836 static constexpr Hash HASHER{};
837 underlying_type data_;
static constexpr auto hash(const Key &key) noexcept -> hash_type
Compute a hash for a key.
Definition json_object.h:180
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:691
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:230
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:462
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:346
auto at(const size_type index) const -> const Entry &
Access an object entry by its underlying positional index.
Definition json_object.h:316
auto erase(T key, const hash_type key_hash) -> size_type
Erase an object property.
Definition json_object.h:781
auto reserve(const size_type capacity) -> void
Reserve capacity for a given number of entries.
Definition json_object.h:311
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:415
auto emplace(Key &&key, mapped_type &&value) -> hash_type
Emplace an object property.
Definition json_object.h:619
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:193
auto emplace(const Key &key, const mapped_type &value) -> hash_type
Emplace an object property.
Definition json_object.h:667
static constexpr auto hash(T key) noexcept -> hash_type
Compute a hash for a key.
Definition json_object.h:188
auto empty() const -> bool
Check if the object is empty.
Definition json_object.h:308
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:576
auto reorder(const Compare &compare) -> void
Reorder object properties by keys according to a comparator function.
Definition json_object.h:820
auto defines(const Key &key, const hash_type hash) const -> bool
Check if an entry with the given key exists.
Definition json_object.h:258
auto erase(const Key &key) -> size_type
Erase an object property.
Definition json_object.h:808
auto find(const Key &key) const -> const_iterator
Attempt to find an entry by key.
Definition json_object.h:200
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:321
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:815
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:698
auto at(const Key &key, const hash_type key_hash) -> mapped_type &
Access an object entry by its key name.
Definition json_object.h:369
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:489
auto try_at(T key, const hash_type key_hash, size_type &start) const -> const mapped_type *
Definition json_object.h:547
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:440
auto rename(const Key &key, const hash_type key_hash, Key &&target, const hash_type target_hash) -> void
Rename an object property in place.
Definition json_object.h:728
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:305
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:393
auto defines(T key, const hash_type hash) const -> bool
Check if an entry with the given key exists.
Definition json_object.h:283
auto try_at(const Key &key, const hash_type key_hash, size_type &start) const -> const mapped_type *
Definition json_object.h:515
auto emplace(const Key &key, mapped_type &&value) -> hash_type
Emplace an object property.
Definition json_object.h:643
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