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 =
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>;
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
69 return this->hash == key_hash &&
70 (hasher.is_perfect(key_hash) ? this->first.size() == key.size()
71 : this->first == key);
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;
98 if (this->data.size() != other.data.size()) {
99 return this->data.size() < other.data.size();
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;
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;
125 auto operator<=(
const JSONObject<Key, Value, Hash> &other)
const noexcept
127 return !(other < *
this);
129 auto operator>(
const JSONObject<Key, Value, Hash> &other)
const noexcept
131 return other < *
this;
133 auto operator>=(
const JSONObject<Key, Value, Hash> &other)
const noexcept
135 return !(*
this < other);
138 auto operator==(
const JSONObject<Key, Value, Hash> &other)
const noexcept
140 if (this->
size() != other.size()) {
144 for (
const auto &entry : this->data) {
145 const auto *result{other.try_at(entry.first, entry.hash)};
146 if (!result || *result != entry.second) {
154 auto operator!=(
const JSONObject<Key, Value, Hash> &other)
const noexcept
157 [[nodiscard]]
inline auto begin() const noexcept -> const_iterator {
158 return this->data.begin();
161 [[nodiscard]]
inline auto end() const noexcept -> const_iterator {
162 return this->data.end();
165 [[nodiscard]]
inline auto cbegin() const noexcept -> const_iterator {
166 return this->data.cbegin();
169 [[nodiscard]]
inline auto cend() const noexcept -> const_iterator {
170 return this->data.cend();
181 [[nodiscard]]
static constexpr auto hash(
const Key &key)
noexcept
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());
194 [[nodiscard]]
static constexpr auto hash(
const char *raw_data,
195 const std::size_t raw_size)
noexcept
197 return hasher(raw_data, raw_size);
201 [[nodiscard]]
inline auto find(
const Key &key)
const -> const_iterator {
202 const auto key_hash{this->
hash(key)};
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);
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);
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)};
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);
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);
259 [[nodiscard]]
inline auto defines(
const Key &key,
const hash_type
hash)
const
264 if (this->hasher.is_perfect(
hash)) {
265 for (
const auto &entry : *
this) {
266 if (entry.hash ==
hash && entry.first.size() == key.size()) {
271 for (
const auto &entry : *
this) {
272 if (entry.hash ==
hash && entry.first == key) {
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 {
288 if (this->hasher.is_perfect(
hash)) {
289 for (
const auto &entry : *
this) {
290 if (entry.hash ==
hash && entry.first.size() == key.size()) {
295 for (
const auto &entry : *
this) {
296 if (entry.hash ==
hash && entry.first == key) {
306 [[nodiscard]]
inline auto size() const -> std::
size_t {
307 return this->data.size();
311 [[nodiscard]]
inline auto empty() const ->
bool {
return this->data.empty(); }
314 inline auto reserve(
const size_type capacity) ->
void {
315 this->data.reserve(capacity);
319 [[nodiscard]]
inline auto at(
const size_type index)
const ->
const Entry & {
320 return this->data.at(index);
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);
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()) {
336 for (
const auto &entry : *
this) {
337 if (entry.hash == key_hash && entry.first == key) {
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);
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()) {
361 for (
const auto &entry : *
this) {
362 if (entry.hash == key_hash && entry.first == key) {
372 inline auto at(
const Key &key,
const hash_type key_hash) -> mapped_type & {
373 assert(this->
hash(key) == key_hash);
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()) {
383 for (
auto &entry : this->data) {
384 if (entry.hash == key_hash && entry.first == key) {
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);
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()) {
407 for (
auto &entry : this->data) {
408 if (entry.hash == key_hash && entry.first == key) {
418 [[nodiscard]]
inline auto try_at(
const Key &key,
const hash_type key_hash)
420 assert(this->
hash(key) == key_hash);
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;
430 for (
auto &entry : this->data) {
431 if (entry.hash == key_hash && entry.first == key) {
432 return &entry.second;
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)
445 assert(this->
hash(key) == key_hash);
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;
455 for (
auto &entry : this->data) {
456 if (entry.hash == key_hash && entry.first == key) {
457 return &entry.second;
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);
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;
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;
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);
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;
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;
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()) {
532 return &this->data[index].second;
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) {
541 return &this->data[index].second;
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()) {
565 return &this->data[index].second;
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) {
574 return &this->data[index].second;
584 const Key &suffix) -> hash_type {
585 const auto key_hash{this->
hash(key)};
586 const auto suffix_hash{this->
hash(suffix)};
588 if (this->hasher.is_perfect(key_hash)) {
589 for (
auto iterator = this->data.begin(); iterator != this->data.end();
591 if (iterator->hash == key_hash &&
592 iterator->first.size() == key.size()) {
593 iterator->second = value;
595 }
else if (iterator->hash == suffix_hash && iterator->first == suffix) {
596 this->data.insert(iterator, {key, value, key_hash});
601 for (
auto iterator = this->data.begin(); iterator != this->data.end();
603 if (iterator->hash == key_hash && iterator->first == key) {
604 iterator->second = value;
606 }
else if (iterator->hash == suffix_hash && iterator->first == suffix) {
607 this->data.insert(iterator, {key, value, key_hash});
613 this->data.push_back({key, value, key_hash});
618 inline auto emplace(Key &&key, mapped_type &&value) -> hash_type {
619 const auto key_hash{this->
hash(key)};
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);
629 for (
auto &entry : this->data) {
630 if (entry.hash == key_hash && entry.first == key) {
631 entry.second = std::move(value);
637 this->data.push_back({std::move(key), std::move(value), key_hash});
642 inline auto emplace(
const Key &key, mapped_type &&value) -> hash_type {
643 const auto key_hash{this->
hash(key)};
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);
653 for (
auto &entry : this->data) {
654 if (entry.hash == key_hash && entry.first == key) {
655 entry.second = std::move(value);
661 this->data.push_back({key, std::move(value), key_hash});
666 inline auto emplace(
const Key &key,
const mapped_type &value) -> hash_type {
667 const auto key_hash{this->
hash(key)};
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;
677 for (
auto &entry : this->data) {
678 if (entry.hash == key_hash && entry.first == key) {
679 entry.second = value;
685 this->data.push_back({key, value, key_hash});
691 const auto key_hash{this->
hash(key)};
692 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]]
inline auto back_key() const noexcept -> const Key & {
720 assert(!this->data.empty());
721 return this->data.back().first;
725 inline auto clear() noexcept ->
void { this->data.clear(); }
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);
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;
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;
752 auto erase(
const Key &key,
const hash_type key_hash) -> size_type {
753 const auto current_size{this->
size()};
755 if (this->hasher.is_perfect(key_hash)) {
756 for (
auto iterator = this->data.begin(); iterator != this->data.end();
758 if (iterator->hash == key_hash &&
759 iterator->first.size() == key.size()) {
760 this->data.erase(iterator);
761 return current_size - 1;
765 for (
auto iterator = this->data.begin(); iterator != this->data.end();
767 if (iterator->hash == key_hash && iterator->first == key) {
768 this->data.erase(iterator);
769 return current_size - 1;
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()};
783 if (this->hasher.is_perfect(key_hash)) {
784 for (
auto iterator = this->data.begin(); iterator != this->data.end();
786 if (iterator->hash == key_hash &&
787 iterator->first.size() == key.size()) {
788 this->data.erase(iterator);
789 return current_size - 1;
793 for (
auto iterator = this->data.begin(); iterator != this->data.end();
795 if (iterator->hash == key_hash && iterator->first == key) {
796 this->data.erase(iterator);
797 return current_size - 1;
806 inline auto erase(
const Key &key) -> size_type {
807 return this->
erase(key, this->
hash(key));
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));
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);
831#pragma warning(disable : 4251)
833 static constexpr Hash hasher{};
834 underlying_type data;
836#pragma warning(default : 4251)
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
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