1#ifndef SIMDJSON_INLINE_OBJECT_H
2#define SIMDJSON_INLINE_OBJECT_H
3
4#include "simdjson/dom/element.h"
5#include "simdjson/dom/object.h"
6#include "simdjson/portability.h"
7#include <cstring>
8#include <string>
9
10namespace simdjson {
11
12//
13// simdjson_result<dom::object> inline implementation
14//
15simdjson_inline simdjson_result<dom::object>::simdjson_result() noexcept
16 : internal::simdjson_result_base<dom::object>() {}
17simdjson_inline simdjson_result<dom::object>::simdjson_result(dom::object value) noexcept
18 : internal::simdjson_result_base<dom::object>(std::forward<dom::object>(t&: value)) {}
19simdjson_inline simdjson_result<dom::object>::simdjson_result(error_code error) noexcept
20 : internal::simdjson_result_base<dom::object>(error) {}
21
22inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](std::string_view key) const noexcept {
23 if (error()) { return error(); }
24 return first[key];
25}
26inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](const char *key) const noexcept {
27 if (error()) { return error(); }
28 return first[key];
29}
30inline simdjson_result<dom::element> simdjson_result<dom::object>::at_pointer(std::string_view json_pointer) const noexcept {
31 if (error()) { return error(); }
32 return first.at_pointer(json_pointer);
33}
34inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
35 if (error()) { return error(); }
36 return first.at_key(key);
37}
38inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
39 if (error()) { return error(); }
40 return first.at_key_case_insensitive(key);
41}
42
43#if SIMDJSON_EXCEPTIONS
44
45inline dom::object::iterator simdjson_result<dom::object>::begin() const noexcept(false) {
46 if (error()) { throw simdjson_error(error()); }
47 return first.begin();
48}
49inline dom::object::iterator simdjson_result<dom::object>::end() const noexcept(false) {
50 if (error()) { throw simdjson_error(error()); }
51 return first.end();
52}
53inline size_t simdjson_result<dom::object>::size() const noexcept(false) {
54 if (error()) { throw simdjson_error(error()); }
55 return first.size();
56}
57
58#endif // SIMDJSON_EXCEPTIONS
59
60namespace dom {
61
62//
63// object inline implementation
64//
65simdjson_inline object::object() noexcept : tape{} {}
66simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
67inline object::iterator object::begin() const noexcept {
68 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
69 return internal::tape_ref(tape.doc, tape.json_index + 1);
70}
71inline object::iterator object::end() const noexcept {
72 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
73 return internal::tape_ref(tape.doc, tape.after_element() - 1);
74}
75inline size_t object::size() const noexcept {
76 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
77 return tape.scope_count();
78}
79
80inline simdjson_result<element> object::operator[](std::string_view key) const noexcept {
81 return at_key(key);
82}
83inline simdjson_result<element> object::operator[](const char *key) const noexcept {
84 return at_key(key);
85}
86inline simdjson_result<element> object::at_pointer(std::string_view json_pointer) const noexcept {
87 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
88 if(json_pointer.empty()) { // an empty string means that we return the current node
89 return element(this->tape); // copy the current node
90 } else if(json_pointer[0] != '/') { // otherwise there is an error
91 return INVALID_JSON_POINTER;
92 }
93 json_pointer = json_pointer.substr(pos: 1);
94 size_t slash = json_pointer.find(c: '/');
95 std::string_view key = json_pointer.substr(pos: 0, n: slash);
96 // Grab the child with the given key
97 simdjson_result<element> child;
98
99 // If there is an escape character in the key, unescape it and then get the child.
100 size_t escape = key.find(c: '~');
101 if (escape != std::string_view::npos) {
102 // Unescape the key
103 std::string unescaped(key);
104 do {
105 switch (unescaped[escape+1]) {
106 case '0':
107 unescaped.replace(pos: escape, n1: 2, s: "~");
108 break;
109 case '1':
110 unescaped.replace(pos: escape, n1: 2, s: "/");
111 break;
112 default:
113 return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
114 }
115 escape = unescaped.find(c: '~', pos: escape+1);
116 } while (escape != std::string::npos);
117 child = at_key(key: unescaped);
118 } else {
119 child = at_key(key);
120 }
121 if(child.error()) {
122 return child; // we do not continue if there was an error
123 }
124 // If there is a /, we have to recurse and look up more of the path
125 if (slash != std::string_view::npos) {
126 child = child.at_pointer(json_pointer: json_pointer.substr(pos: slash));
127 }
128 return child;
129}
130
131inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
132 iterator end_field = end();
133 for (iterator field = begin(); field != end_field; ++field) {
134 if (field.key_equals(o: key)) {
135 return field.value();
136 }
137 }
138 return NO_SUCH_FIELD;
139}
140// In case you wonder why we need this, please see
141// https://github.com/simdjson/simdjson/issues/323
142// People do seek keys in a case-insensitive manner.
143inline simdjson_result<element> object::at_key_case_insensitive(std::string_view key) const noexcept {
144 iterator end_field = end();
145 for (iterator field = begin(); field != end_field; ++field) {
146 if (field.key_equals_case_insensitive(o: key)) {
147 return field.value();
148 }
149 }
150 return NO_SUCH_FIELD;
151}
152
153//
154// object::iterator inline implementation
155//
156simdjson_inline object::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
157inline const key_value_pair object::iterator::operator*() const noexcept {
158 return key_value_pair(key(), value());
159}
160inline bool object::iterator::operator!=(const object::iterator& other) const noexcept {
161 return tape.json_index != other.tape.json_index;
162}
163inline bool object::iterator::operator==(const object::iterator& other) const noexcept {
164 return tape.json_index == other.tape.json_index;
165}
166inline bool object::iterator::operator<(const object::iterator& other) const noexcept {
167 return tape.json_index < other.tape.json_index;
168}
169inline bool object::iterator::operator<=(const object::iterator& other) const noexcept {
170 return tape.json_index <= other.tape.json_index;
171}
172inline bool object::iterator::operator>=(const object::iterator& other) const noexcept {
173 return tape.json_index >= other.tape.json_index;
174}
175inline bool object::iterator::operator>(const object::iterator& other) const noexcept {
176 return tape.json_index > other.tape.json_index;
177}
178inline object::iterator& object::iterator::operator++() noexcept {
179 tape.json_index++;
180 tape.json_index = tape.after_element();
181 return *this;
182}
183inline object::iterator object::iterator::operator++(int) noexcept {
184 object::iterator out = *this;
185 ++*this;
186 return out;
187}
188inline std::string_view object::iterator::key() const noexcept {
189 return tape.get_string_view();
190}
191inline uint32_t object::iterator::key_length() const noexcept {
192 return tape.get_string_length();
193}
194inline const char* object::iterator::key_c_str() const noexcept {
195 return reinterpret_cast<const char *>(&tape.doc->string_buf[size_t(tape.tape_value()) + sizeof(uint32_t)]);
196}
197inline element object::iterator::value() const noexcept {
198 return element(internal::tape_ref(tape.doc, tape.json_index + 1));
199}
200
201/**
202 * Design notes:
203 * Instead of constructing a string_view and then comparing it with a
204 * user-provided strings, it is probably more performant to have dedicated
205 * functions taking as a parameter the string we want to compare against
206 * and return true when they are equal. That avoids the creation of a temporary
207 * std::string_view. Though it is possible for the compiler to avoid entirely
208 * any overhead due to string_view, relying too much on compiler magic is
209 * problematic: compiler magic sometimes fail, and then what do you do?
210 * Also, enticing users to rely on high-performance function is probably better
211 * on the long run.
212 */
213
214inline bool object::iterator::key_equals(std::string_view o) const noexcept {
215 // We use the fact that the key length can be computed quickly
216 // without access to the string buffer.
217 const uint32_t len = key_length();
218 if(o.size() == len) {
219 // We avoid construction of a temporary string_view instance.
220 return (memcmp(s1: o.data(), s2: key_c_str(), n: len) == 0);
221 }
222 return false;
223}
224
225inline bool object::iterator::key_equals_case_insensitive(std::string_view o) const noexcept {
226 // We use the fact that the key length can be computed quickly
227 // without access to the string buffer.
228 const uint32_t len = key_length();
229 if(o.size() == len) {
230 // See For case-insensitive string comparisons, avoid char-by-char functions
231 // https://lemire.me/blog/2020/04/30/for-case-insensitive-string-comparisons-avoid-char-by-char-functions/
232 // Note that it might be worth rolling our own strncasecmp function, with vectorization.
233 return (simdjson_strncasecmp(s1: o.data(), s2: key_c_str(), n: len) == 0);
234 }
235 return false;
236}
237//
238// key_value_pair inline implementation
239//
240inline key_value_pair::key_value_pair(std::string_view _key, element _value) noexcept :
241 key(_key), value(_value) {}
242
243} // namespace dom
244
245} // namespace simdjson
246
247#if defined(__cpp_lib_ranges)
248static_assert(std::ranges::view<simdjson::dom::object>);
249static_assert(std::ranges::sized_range<simdjson::dom::object>);
250#if SIMDJSON_EXCEPTIONS
251static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::object>>);
252static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::object>>);
253#endif // SIMDJSON_EXCEPTIONS
254#endif // defined(__cpp_lib_ranges)
255
256#endif // SIMDJSON_INLINE_OBJECT_H
257