Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
jsonpointer_pointer.h
1#ifndef SOURCEMETA_CORE_JSONPOINTER_POINTER_H_
2#define SOURCEMETA_CORE_JSONPOINTER_POINTER_H_
3
4#include <sourcemeta/core/jsonpointer_token.h>
5
6#include <algorithm> // std::move, std::equal
7#include <cassert> // assert
8#include <cstddef> // std::size_t
9#include <functional> // std::reference_wrapper
10#include <initializer_list> // std::initializer_list
11#include <iterator> // std::advance, std::back_inserter, std::next
12#include <optional> // std::optional
13#include <ranges> // std::ranges::subrange
14#include <type_traits> // std::is_same_v, std::decay_t
15#include <utility> // std::move
16#include <vector> // std::vector
17
18#include <sourcemeta/core/preprocessor.h>
19
20namespace sourcemeta::core {
21
25template <typename PropertyT, typename Hash> class GenericPointer {
26public:
30 using Value = typename Token::Value;
32 using Container = std::vector<Token>;
33
43 GenericPointer() noexcept : data{} {}
44
56 GenericPointer(std::initializer_list<Token> tokens)
57 : data{std::move(tokens)} {}
58
59 // Member types
60 using value_type = typename Container::value_type;
61 using allocator_type = typename Container::allocator_type;
62 using size_type = typename Container::size_type;
63 using difference_type = typename Container::difference_type;
64 using reference = typename Container::reference;
65 using const_reference = typename Container::const_reference;
66 using pointer = typename Container::pointer;
67 using const_pointer = typename Container::const_pointer;
68 using iterator = typename Container::iterator;
69 using const_iterator = typename Container::const_iterator;
70 using reverse_iterator = typename Container::reverse_iterator;
71 using const_reverse_iterator = typename Container::const_reverse_iterator;
72
74 auto begin() noexcept -> iterator { return this->data.begin(); }
76 auto end() noexcept -> iterator { return this->data.end(); }
78 [[nodiscard]] auto begin() const noexcept -> const_iterator {
79 return this->data.begin();
80 }
82 [[nodiscard]] auto end() const noexcept -> const_iterator {
83 return this->data.end();
84 }
86 [[nodiscard]] auto cbegin() const noexcept -> const_iterator {
87 return this->data.cbegin();
88 }
90 [[nodiscard]] auto cend() const noexcept -> const_iterator {
91 return this->data.cend();
92 }
94 auto rbegin() noexcept -> reverse_iterator { return this->data.rbegin(); }
96 auto rend() noexcept -> reverse_iterator { return this->data.rend(); }
98 [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator {
99 return this->data.rbegin();
100 }
102 [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator {
103 return this->data.rend();
104 }
106 [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator {
107 return this->data.crbegin();
108 }
110 [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator {
111 return this->data.crend();
112 }
113
125 [[nodiscard]] auto at(const size_type index) const -> const_reference {
126 assert(this->size() > index);
127 return this->data[index];
128 }
129
141 [[nodiscard]] SOURCEMETA_FORCEINLINE auto back() const -> const_reference {
142 assert(!this->empty());
143 return this->data.back();
144 }
145
156 [[nodiscard]] SOURCEMETA_FORCEINLINE auto size() const noexcept -> size_type {
157 return this->data.size();
158 }
159
172 [[nodiscard]] SOURCEMETA_FORCEINLINE auto empty() const noexcept -> bool {
173 return this->data.empty();
174 }
175
189 template <class... Args>
190 SOURCEMETA_FORCEINLINE auto emplace_back(Args &&...args) -> reference {
191 return this->data.emplace_back(std::forward<Args>(args)...);
192 }
193
202 auto reserve(const typename Container::size_type capacity) -> void {
203 this->data.reserve(capacity);
204 }
205
226 SOURCEMETA_FORCEINLINE auto
228 if (other.empty()) {
229 return;
230 } else if (other.size() == 1) {
231 this->emplace_back(other.back());
232 return;
233 }
234
235 this->reserve(this->data.size() + other.size());
236// TODO: Remove once GitHub Actions ship proper C++23 support
237#if __cpp_lib_containers_ranges >= 202202L
238 this->data.append_range(other.data);
239#else
240 std::copy(other.data.cbegin(), other.data.cend(),
241 std::back_inserter(this->data));
242#endif
243 }
244
265 SOURCEMETA_FORCEINLINE auto push_back(GenericPointer<PropertyT, Hash> &&other)
266 -> void {
267 if (other.empty()) {
268 return;
269 } else if (other.size() == 1) {
270 this->emplace_back(std::move(other.back()));
271 return;
272 }
273
274 this->reserve(this->data.size() + other.size());
275 std::move(other.data.begin(), other.data.end(),
276 std::back_inserter(this->data));
277 }
278
301 template <typename OtherT>
302 SOURCEMETA_FORCEINLINE auto
304 requires std::is_same_v<PropertyT, std::reference_wrapper<const OtherT>>
305 {
306 if (other.empty()) {
307 return;
308 } else if (other.size() == 1) {
309 const auto &token{other.back()};
310 if (token.is_property()) {
311 // We should make sure to re-use the existing hash
312 this->data.emplace_back(token.to_property(), token.property_hash());
313 } else {
314 this->data.emplace_back(token.to_index());
315 }
316 } else {
317 this->reserve(this->data.size() + other.size());
318 for (const auto &token : other) {
319 if (token.is_property()) {
320 // We should make sure to re-use the existing hash
321 this->data.emplace_back(token.to_property(), token.property_hash());
322 } else {
323 this->data.emplace_back(token.to_index());
324 }
325 }
326 }
327 }
328
347 SOURCEMETA_FORCEINLINE auto
348 push_back(const typename Token::Property &property) -> void {
349 this->data.emplace_back(property);
350 }
351
369 SOURCEMETA_FORCEINLINE auto push_back(typename Token::Property &&property)
370 -> void {
371 this->data.emplace_back(std::move(property));
372 }
373
392 SOURCEMETA_FORCEINLINE auto push_back(const typename Token::Index &index)
393 -> void {
394 this->data.emplace_back(index);
395 }
396
409 auto pop_back() -> void {
410 assert(!this->empty());
411 this->data.pop_back();
412 }
413
426 auto pop_back(const size_type count) -> void {
427 assert(this->size() >= count);
428 for (std::size_t index = 0; index < count; index++) {
429 this->data.pop_back();
430 }
431 }
432
448 [[nodiscard]] auto initial() const -> GenericPointer<PropertyT, Hash> {
449 assert(!this->empty());
451 result.pop_back();
452 return result;
453 }
454
471 [[nodiscard]] auto slice(const std::size_t index) const
473 assert(index <= this->size());
474 auto new_begin{this->data.cbegin()};
475 std::advance(new_begin, index);
477 result.reserve(this->size() - index);
478// TODO: Remove once GitHub Actions ship proper C++23 support
479#if __cpp_lib_containers_ranges >= 202202L
480 result.data.append_range(
481 std::ranges::subrange(new_begin, this->data.cend()));
482#else
483 std::copy(new_begin, this->data.cend(), std::back_inserter(result.data));
484#endif
485 return result;
486 }
487
505 [[nodiscard]] auto slice(const std::size_t start, const std::size_t end) const
507 assert(start <= end);
508 assert(end <= this->size());
509 auto new_begin{this->data.cbegin()};
510 std::advance(new_begin, start);
511 auto new_end{this->data.cbegin()};
512 std::advance(new_end, end);
514 result.reserve(end - start);
515// TODO: Remove once GitHub Actions ship proper C++23 support
516#if __cpp_lib_containers_ranges >= 202202L
517 result.data.append_range(std::ranges::subrange(new_begin, new_end));
518#else
519 std::copy(new_begin, new_end, std::back_inserter(result.data));
520#endif
521 return result;
522 }
523
536 [[nodiscard]] auto concat(const GenericPointer<PropertyT, Hash> &other) const
539 result.push_back(other);
540 return result;
541 }
542
553 [[nodiscard]] auto concat(const typename Token::Property &property) const
556 result.push_back(property);
557 return result;
558 }
559
570 [[nodiscard]] auto concat(const typename Token::Index &index) const
573 result.push_back(index);
574 return result;
575 }
576
588 [[nodiscard]] auto
589 starts_with(const GenericPointer<PropertyT, Hash> &other) const -> bool {
590 return other.data.size() <= this->data.size() &&
591 std::equal(other.data.cbegin(), other.data.cend(),
592 this->data.cbegin());
593 }
594
607 [[nodiscard]] auto starts_with(const GenericPointer<PropertyT, Hash> &other,
608 const Token &tail) const -> bool {
609 if (other.size() == this->size() + 1) {
610 assert(!other.empty());
611 return other.starts_with(*this) && other.back() == tail;
612 } else {
613 return this->starts_with(other);
614 }
615 }
616
630 template <typename StringT>
631 requires(!std::is_same_v<std::decay_t<StringT>, Token>)
632 [[nodiscard]] auto starts_with(const GenericPointer<PropertyT, Hash> &other,
633 const StringT &tail) const -> bool {
634 const auto prefix_size{other.size()};
635 return this->size() > prefix_size && this->starts_with(other) &&
636 this->data[prefix_size].is_property() &&
637 this->data[prefix_size].to_property() == tail;
638 }
639
653 template <typename StringLeftT, typename StringRightT>
654 requires(!std::is_same_v<std::decay_t<StringLeftT>, Token> &&
655 !std::is_same_v<std::decay_t<StringRightT>, Token>)
656 [[nodiscard]] auto starts_with(const GenericPointer<PropertyT, Hash> &other,
657 const StringLeftT &tail_left,
658 const StringRightT &tail_right) const -> bool {
659 const auto prefix_size{other.size()};
660 return this->size() > prefix_size + 1 &&
661 this->starts_with(other, tail_left) &&
662 this->data[prefix_size + 1].is_property() &&
663 this->data[prefix_size + 1].to_property() == tail_right;
664 }
665
678 [[nodiscard]] auto shares_prefix(const GenericPointer<PropertyT, Hash> &other,
679 const size_type prefix_size) const -> bool {
680 return this->data.size() >= prefix_size &&
681 other.data.size() >= prefix_size &&
682 std::equal(this->data.cbegin(),
683 std::next(this->data.cbegin(),
684 static_cast<difference_type>(prefix_size)),
685 other.data.cbegin());
686 }
687
700 [[nodiscard]] auto
702 -> bool {
703 return this->data.size() > other.data.size() &&
704 this->shares_prefix(other, other.data.size());
705 }
706
718 [[nodiscard]] auto
720 -> bool {
721 const auto prefix_size{other.size()};
722 if (prefix_size == 0) {
723 return true;
724 } else if (this->size() < prefix_size - 1) {
725 return false;
726 }
727
728 for (std::size_t index = 0; index < prefix_size - 1; index++) {
729 if (this->data[index] != other.data[index]) {
730 return false;
731 }
732 }
733
734 return true;
735 }
736
750 [[nodiscard]] auto
752 const GenericPointer<PropertyT, Hash> &replacement) const
754 typename Container::size_type index{0};
755 while (index < prefix.size()) {
756 if (index >= this->size() || prefix.data[index] != this->data[index]) {
757 return *this;
758 } else {
759 index++;
760 }
761 }
762
763 assert(index == prefix.size());
764 assert(this->starts_with(prefix));
765 auto new_begin{this->data.cbegin()};
766 std::advance(new_begin, index);
767 GenericPointer<PropertyT, Hash> result{replacement};
768// TODO: Remove once GitHub Actions ship proper C++23 support
769#if __cpp_lib_containers_ranges >= 202202L
770 result.data.append_range(
771 std::ranges::subrange(new_begin, this->data.cend()));
772#else
773 std::copy(new_begin, this->data.cend(), std::back_inserter(result.data));
774#endif
775 return result;
776 }
777
792 [[nodiscard]] auto
795 if (base.empty()) {
796 return *this;
797 }
798
799 typename Container::size_type index{0};
800 while (index < base.size()) {
801 if (index >= this->size() || base.data[index] != this->data[index]) {
802 return *this;
803 } else {
804 index++;
805 }
806 }
807
808 // Make a pointer from the remaining tokens
809 auto new_begin{this->data.cbegin()};
810 std::advance(new_begin, index);
812 const auto remaining{static_cast<typename Container::size_type>(
813 this->data.cend() - new_begin)};
814 result.data.reserve(remaining);
815// TODO: Remove once GitHub Actions ship proper C++23 support
816#if __cpp_lib_containers_ranges >= 202202L
817 result.data.append_range(
818 std::ranges::subrange(new_begin, this->data.cend()));
819#else
820 std::copy(new_begin, this->data.cend(), std::back_inserter(result.data));
821#endif
822 return result;
823 }
824
826 [[nodiscard]] auto
827 operator==(const GenericPointer<PropertyT, Hash> &other) const noexcept
828 -> bool {
829 return this->data == other.data;
830 }
831
833 [[nodiscard]] auto
834 operator==(const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
835 &other) const noexcept -> bool {
836 return this->data == other.get().data;
837 }
838
841 [[nodiscard]] auto
842 operator<(const GenericPointer<PropertyT, Hash> &other) const noexcept
843 -> bool {
844 return this->data < other.data;
845 }
846
848 [[nodiscard]] auto
849 operator<(const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
850 &other) const noexcept -> bool {
851 return this->data < other.get().data;
852 }
853
855 struct Hasher {
856 using is_transparent = void;
857
858 auto
859 operator()(const GenericPointer<PropertyT, Hash> &pointer) const noexcept
860 -> std::size_t {
861 const auto size{pointer.size()};
862 if (size == 0) {
863 return size;
864 }
865
866 const auto &first{pointer.at(0)};
867 const auto &middle{pointer.at(size / 2)};
868 const auto &last{pointer.at(size - 1)};
869
870 return size +
871 (first.is_property() ? property_hash(first.property_hash())
872 : first.to_index()) +
873 (middle.is_property() ? property_hash(middle.property_hash())
874 : middle.to_index()) +
875 (last.is_property() ? property_hash(last.property_hash())
876 : last.to_index());
877 }
878
879 auto operator()(
880 const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
881 &reference) const noexcept -> std::size_t {
882 return (*this)(reference.get());
883 }
884
885 private:
886 // Intentionally only fold hash.a for performance, as the first
887 // 16 bytes already provide sufficient entropy for bucketing
888 static auto property_hash(const typename Hash::hash_type &hash) noexcept
889 -> std::size_t {
890 return static_cast<std::size_t>(hash.a) ^
891 static_cast<std::size_t>(hash.a >> 64);
892 }
893 };
894
896 struct Comparator {
897 using is_transparent = void;
898
899 auto operator()(const GenericPointer<PropertyT, Hash> &left,
900 const GenericPointer<PropertyT, Hash> &right) const noexcept
901 -> bool {
902 return left == right;
903 }
904
905 auto operator()(
906 const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
907 &left,
908 const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
909 &right) const noexcept -> bool {
910 return left.get() == right.get();
911 }
912
913 auto operator()(
914 const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
915 &left,
916 const GenericPointer<PropertyT, Hash> &right) const noexcept -> bool {
917 return left.get() == right;
918 }
919
920 auto operator()(
922 const std::reference_wrapper<const GenericPointer<PropertyT, Hash>>
923 &right) const noexcept -> bool {
924 return left == right.get();
925 }
926 };
927
929 [[nodiscard]] auto to_json() const -> Value {
930 auto result{Value::make_array()};
931 for (const auto &token : this->data) {
932 result.push_back(token.to_json());
933 }
934
935 return result;
936 }
937
939 static auto from_json(const Value &value)
940 -> std::optional<GenericPointer<PropertyT, Hash>>
941 requires std::is_same_v<PropertyT, typename Value::String>
942 {
943 if (!value.is_array()) {
944 return std::nullopt;
945 }
946
948 for (const auto &element : value.as_array()) {
949 if (element.is_string()) {
950 result.emplace_back(element.to_string());
951 } else if (element.is_integer() && element.to_integer() >= 0) {
952 result.emplace_back(
953 static_cast<typename Token::Index>(element.to_integer()));
954 } else {
955 return std::nullopt;
956 }
957 }
958
959 return result;
960 }
961
962private:
963 Container data;
964};
965
966} // namespace sourcemeta::core
967
968#endif
typename Value::Array::size_type Index
The stored type of an array index token.
Definition jsonpointer_token.h:20
auto rebase(const GenericPointer< PropertyT, Hash > &prefix, const GenericPointer< PropertyT, Hash > &replacement) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:751
auto slice(const std::size_t start, const std::size_t end) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:505
SOURCEMETA_FORCEINLINE auto push_back(const typename Token::Property &property) -> void
Definition jsonpointer_pointer.h:348
auto shares_prefix(const GenericPointer< PropertyT, Hash > &other, const size_type prefix_size) const -> bool
Definition jsonpointer_pointer.h:678
static auto from_json(const Value &value) -> std::optional< GenericPointer< PropertyT, Hash > >
Deserialise a JSON Pointer from a JSON array of tokens.
Definition jsonpointer_pointer.h:939
auto slice(const std::size_t index) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:471
JSON Value
The JSON value type these tokens convert to.
Definition jsonpointer_token.h:16
auto concat(const GenericPointer< PropertyT, Hash > &other) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:536
SOURCEMETA_FORCEINLINE auto push_back(GenericPointer< PropertyT, Hash > &&other) -> void
Definition jsonpointer_pointer.h:265
auto resolve_from(const GenericPointer< PropertyT, Hash > &base) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:793
auto starts_with_initial(const GenericPointer< PropertyT, Hash > &other) const -> bool
Definition jsonpointer_pointer.h:719
auto starts_with(const GenericPointer< PropertyT, Hash > &other, const Token &tail) const -> bool
Definition jsonpointer_pointer.h:607
SOURCEMETA_FORCEINLINE auto emplace_back(Args &&...args) -> reference
Definition jsonpointer_pointer.h:190
auto pop_back() -> void
Definition jsonpointer_pointer.h:409
auto concat(const typename Token::Property &property) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:553
typename Token::Value Value
Definition jsonpointer_pointer.h:30
auto concat(const typename Token::Index &index) const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:570
SOURCEMETA_FORCEINLINE auto push_back(const GenericPointer< PropertyT, Hash > &other) -> void
Definition jsonpointer_pointer.h:227
auto at(const size_type index) const -> const_reference
Definition jsonpointer_pointer.h:125
SOURCEMETA_FORCEINLINE auto push_back(const typename Token::Index &index) -> void
Definition jsonpointer_pointer.h:392
SOURCEMETA_FORCEINLINE auto empty() const noexcept -> bool
Definition jsonpointer_pointer.h:172
auto starts_with(const GenericPointer< PropertyT, Hash > &other) const -> bool
Definition jsonpointer_pointer.h:589
auto reserve(const typename Container::size_type capacity) -> void
Definition jsonpointer_pointer.h:202
auto starts_with_strict(const GenericPointer< PropertyT, Hash > &other) const -> bool
Definition jsonpointer_pointer.h:701
GenericPointer() noexcept
Definition jsonpointer_pointer.h:43
SOURCEMETA_FORCEINLINE auto back() const -> const_reference
Definition jsonpointer_pointer.h:141
GenericToken< JSON::String, PropertyHashJSON< JSON::String > > Token
Definition jsonpointer_pointer.h:28
GenericPointer(std::initializer_list< Token > tokens)
Definition jsonpointer_pointer.h:56
auto to_json() const -> Value
Serialise a JSON Pointer as a JSON array of tokens.
Definition jsonpointer_pointer.h:929
auto pop_back(const size_type count) -> void
Definition jsonpointer_pointer.h:426
SOURCEMETA_FORCEINLINE auto push_back(typename Token::Property &&property) -> void
Definition jsonpointer_pointer.h:369
SOURCEMETA_FORCEINLINE auto push_back(const GenericPointer< OtherT, Hash > &other) -> void
Definition jsonpointer_pointer.h:303
SOURCEMETA_FORCEINLINE auto size() const noexcept -> size_type
Definition jsonpointer_pointer.h:156
std::vector< Token > Container
Definition jsonpointer_pointer.h:32
auto starts_with(const GenericPointer< PropertyT, Hash > &other, const StringT &tail) const -> bool
Definition jsonpointer_pointer.h:632
PropertyT Property
The stored type of an object property token.
Definition jsonpointer_token.h:18
auto initial() const -> GenericPointer< PropertyT, Hash >
Definition jsonpointer_pointer.h:448
auto starts_with(const GenericPointer< PropertyT, Hash > &other, const StringLeftT &tail_left, const StringRightT &tail_right) const -> bool
Definition jsonpointer_pointer.h:656
Definition jsonpointer_pointer.h:25
Definition jsonpointer_token.h:13
Comparator for use with containers.
Definition jsonpointer_pointer.h:896
Hash functor for use with containers.
Definition jsonpointer_pointer.h:855