simdjson  3.11.0
Ridiculously Fast JSON
object-inl.h
1 #ifndef SIMDJSON_OBJECT_INL_H
2 #define SIMDJSON_OBJECT_INL_H
3 
4 #include "simdjson/dom/base.h"
5 #include "simdjson/dom/object.h"
6 #include "simdjson/dom/document.h"
7 
8 #include "simdjson/dom/element-inl.h"
9 #include "simdjson/error-inl.h"
10 #include "simdjson/jsonpathutil.h"
11 
12 #include <cstring>
13 
14 namespace simdjson {
15 
16 //
17 // simdjson_result<dom::object> inline implementation
18 //
19 simdjson_inline simdjson_result<dom::object>::simdjson_result() noexcept
20  : internal::simdjson_result_base<dom::object>() {}
21 simdjson_inline simdjson_result<dom::object>::simdjson_result(dom::object value) noexcept
22  : internal::simdjson_result_base<dom::object>(std::forward<dom::object>(value)) {}
23 simdjson_inline simdjson_result<dom::object>::simdjson_result(error_code error) noexcept
24  : internal::simdjson_result_base<dom::object>(error) {}
25 
26 inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](std::string_view key) const noexcept {
27  if (error()) { return error(); }
28  return first[key];
29 }
30 inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](const char *key) const noexcept {
31  if (error()) { return error(); }
32  return first[key];
33 }
34 inline simdjson_result<dom::element> simdjson_result<dom::object>::at_pointer(std::string_view json_pointer) const noexcept {
35  if (error()) { return error(); }
36  return first.at_pointer(json_pointer);
37 }
38 inline simdjson_result<dom::element> simdjson_result<dom::object>::at_path(std::string_view json_path) const noexcept {
39  auto json_pointer = json_path_to_pointer_conversion(json_path);
40  if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
41  return at_pointer(json_pointer);
42 }
43 inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
44  if (error()) { return error(); }
45  return first.at_key(key);
46 }
47 inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
48  if (error()) { return error(); }
49  return first.at_key_case_insensitive(key);
50 }
51 
52 #if SIMDJSON_EXCEPTIONS
53 
54 inline dom::object::iterator simdjson_result<dom::object>::begin() const noexcept(false) {
55  if (error()) { throw simdjson_error(error()); }
56  return first.begin();
57 }
58 inline dom::object::iterator simdjson_result<dom::object>::end() const noexcept(false) {
59  if (error()) { throw simdjson_error(error()); }
60  return first.end();
61 }
62 inline size_t simdjson_result<dom::object>::size() const noexcept(false) {
63  if (error()) { throw simdjson_error(error()); }
64  return first.size();
65 }
66 
67 #endif // SIMDJSON_EXCEPTIONS
68 
69 namespace dom {
70 
71 //
72 // object inline implementation
73 //
74 simdjson_inline object::object() noexcept : tape{} {}
75 simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
76 inline object::iterator object::begin() const noexcept {
77  SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
78  return internal::tape_ref(tape.doc, tape.json_index + 1);
79 }
80 inline object::iterator object::end() const noexcept {
81  SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
82  return internal::tape_ref(tape.doc, tape.after_element() - 1);
83 }
84 inline size_t object::size() const noexcept {
85  SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
86  return tape.scope_count();
87 }
88 
89 inline simdjson_result<element> object::operator[](std::string_view key) const noexcept {
90  return at_key(key);
91 }
92 inline simdjson_result<element> object::operator[](const char *key) const noexcept {
93  return at_key(key);
94 }
95 inline simdjson_result<element> object::at_pointer(std::string_view json_pointer) const noexcept {
96  SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
97  if(json_pointer.empty()) { // an empty string means that we return the current node
98  return element(this->tape); // copy the current node
99  } else if(json_pointer[0] != '/') { // otherwise there is an error
100  return INVALID_JSON_POINTER;
101  }
102  json_pointer = json_pointer.substr(1);
103  size_t slash = json_pointer.find('/');
104  std::string_view key = json_pointer.substr(0, slash);
105  // Grab the child with the given key
107 
108  // If there is an escape character in the key, unescape it and then get the child.
109  size_t escape = key.find('~');
110  if (escape != std::string_view::npos) {
111  // Unescape the key
112  std::string unescaped(key);
113  do {
114  switch (unescaped[escape+1]) {
115  case '0':
116  unescaped.replace(escape, 2, "~");
117  break;
118  case '1':
119  unescaped.replace(escape, 2, "/");
120  break;
121  default:
122  return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
123  }
124  escape = unescaped.find('~', escape+1);
125  } while (escape != std::string::npos);
126  child = at_key(unescaped);
127  } else {
128  child = at_key(key);
129  }
130  if(child.error()) {
131  return child; // we do not continue if there was an error
132  }
133  // If there is a /, we have to recurse and look up more of the path
134  if (slash != std::string_view::npos) {
135  child = child.at_pointer(json_pointer.substr(slash));
136  }
137  return child;
138 }
139 
140 inline simdjson_result<element> object::at_path(std::string_view json_path) const noexcept {
141  auto json_pointer = json_path_to_pointer_conversion(json_path);
142  if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
143  return at_pointer(json_pointer);
144 }
145 
146 inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
147  iterator end_field = end();
148  for (iterator field = begin(); field != end_field; ++field) {
149  if (field.key_equals(key)) {
150  return field.value();
151  }
152  }
153  return NO_SUCH_FIELD;
154 }
155 // In case you wonder why we need this, please see
156 // https://github.com/simdjson/simdjson/issues/323
157 // People do seek keys in a case-insensitive manner.
158 inline simdjson_result<element> object::at_key_case_insensitive(std::string_view key) const noexcept {
159  iterator end_field = end();
160  for (iterator field = begin(); field != end_field; ++field) {
161  if (field.key_equals_case_insensitive(key)) {
162  return field.value();
163  }
164  }
165  return NO_SUCH_FIELD;
166 }
167 
168 inline object::operator element() const noexcept {
169  return element(tape);
170 }
171 
172 //
173 // object::iterator inline implementation
174 //
175 simdjson_inline object::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
176 inline const key_value_pair object::iterator::operator*() const noexcept {
177  return key_value_pair(key(), value());
178 }
179 inline bool object::iterator::operator!=(const object::iterator& other) const noexcept {
180  return tape.json_index != other.tape.json_index;
181 }
182 inline bool object::iterator::operator==(const object::iterator& other) const noexcept {
183  return tape.json_index == other.tape.json_index;
184 }
185 inline bool object::iterator::operator<(const object::iterator& other) const noexcept {
186  return tape.json_index < other.tape.json_index;
187 }
188 inline bool object::iterator::operator<=(const object::iterator& other) const noexcept {
189  return tape.json_index <= other.tape.json_index;
190 }
191 inline bool object::iterator::operator>=(const object::iterator& other) const noexcept {
192  return tape.json_index >= other.tape.json_index;
193 }
194 inline bool object::iterator::operator>(const object::iterator& other) const noexcept {
195  return tape.json_index > other.tape.json_index;
196 }
198  tape.json_index++;
199  tape.json_index = tape.after_element();
200  return *this;
201 }
203  object::iterator out = *this;
204  ++*this;
205  return out;
206 }
207 inline std::string_view object::iterator::key() const noexcept {
208  return tape.get_string_view();
209 }
210 inline uint32_t object::iterator::key_length() const noexcept {
211  return tape.get_string_length();
212 }
213 inline const char* object::iterator::key_c_str() const noexcept {
214  return reinterpret_cast<const char *>(&tape.doc->string_buf[size_t(tape.tape_value()) + sizeof(uint32_t)]);
215 }
216 inline element object::iterator::value() const noexcept {
217  return element(internal::tape_ref(tape.doc, tape.json_index + 1));
218 }
219 
233 inline bool object::iterator::key_equals(std::string_view o) const noexcept {
234  // We use the fact that the key length can be computed quickly
235  // without access to the string buffer.
236  const uint32_t len = key_length();
237  if(o.size() == len) {
238  // We avoid construction of a temporary string_view instance.
239  return (memcmp(o.data(), key_c_str(), len) == 0);
240  }
241  return false;
242 }
243 
244 inline bool object::iterator::key_equals_case_insensitive(std::string_view o) const noexcept {
245  // We use the fact that the key length can be computed quickly
246  // without access to the string buffer.
247  const uint32_t len = key_length();
248  if(o.size() == len) {
249  // See For case-insensitive string comparisons, avoid char-by-char functions
250  // https://lemire.me/blog/2020/04/30/for-case-insensitive-string-comparisons-avoid-char-by-char-functions/
251  // Note that it might be worth rolling our own strncasecmp function, with vectorization.
252  return (simdjson_strncasecmp(o.data(), key_c_str(), len) == 0);
253  }
254  return false;
255 }
256 //
257 // key_value_pair inline implementation
258 //
259 inline key_value_pair::key_value_pair(std::string_view _key, element _value) noexcept :
260  key(_key), value(_value) {}
261 
262 } // namespace dom
263 
264 } // namespace simdjson
265 
266 #if defined(__cpp_lib_ranges)
267 static_assert(std::ranges::view<simdjson::dom::object>);
268 static_assert(std::ranges::sized_range<simdjson::dom::object>);
269 #if SIMDJSON_EXCEPTIONS
270 static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::object>>);
271 static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::object>>);
272 #endif // SIMDJSON_EXCEPTIONS
273 #endif // defined(__cpp_lib_ranges)
274 
275 #endif // SIMDJSON_OBJECT_INL_H
A JSON element.
Definition: element.h:31
Key/value pair in an object.
Definition: object.h:238
bool operator!=(const iterator &other) const noexcept
Check if these values come from the same place in the JSON.
Definition: object-inl.h:179
element value() const noexcept
Get the value of this key/value pair.
Definition: object-inl.h:216
bool key_equals(std::string_view o) const noexcept
Returns true if the key in this key/value pair is equal to the provided string_view.
Definition: object-inl.h:233
const char * key_c_str() const noexcept
Get the key of this key/value pair.
Definition: object-inl.h:213
uint32_t key_length() const noexcept
Get the length (in bytes) of the key in this key/value pair.
Definition: object-inl.h:210
reference operator*() const noexcept
Get the actual key/value pair.
Definition: object-inl.h:176
bool key_equals_case_insensitive(std::string_view o) const noexcept
Returns true if the key in this key/value pair is equal to the provided string_view in a case-insensi...
Definition: object-inl.h:244
std::string_view key() const noexcept
Get the key of this key/value pair.
Definition: object-inl.h:207
iterator & operator++() noexcept
Get the next key/value pair.
Definition: object-inl.h:197
simdjson_result< element > at_key(std::string_view key) const noexcept
Get the value associated with the given key.
Definition: object-inl.h:146
iterator end() const noexcept
One past the last key/value pair.
Definition: object-inl.h:80
size_t size() const noexcept
Get the size of the object (number of keys).
Definition: object-inl.h:84
simdjson_result< element > at_path(std::string_view json_path) const noexcept
Get the value associated with the given JSONPath expression.
Definition: object-inl.h:140
simdjson_result< element > at_key_case_insensitive(std::string_view key) const noexcept
Get the value associated with the given key in a case-insensitive manner.
Definition: object-inl.h:158
simdjson_result< element > operator[](std::string_view key) const noexcept
Get the value associated with the given key.
Definition: object-inl.h:89
simdjson_result< element > at_pointer(std::string_view json_pointer) const noexcept
Get the value associated with the given JSON pointer.
Definition: object-inl.h:95
simdjson_inline object() noexcept
Create a new, invalid object.
Definition: object-inl.h:74
iterator begin() const noexcept
Return the first key/value pair.
Definition: object-inl.h:76
The top level simdjson namespace, containing everything the library provides.
Definition: base.h:8
error_code
All possible errors returned by simdjson.
Definition: error.h:19
@ NO_SUCH_FIELD
JSON field not found in object.
Definition: error.h:40
@ INVALID_JSON_POINTER
Invalid JSON pointer syntax.
Definition: error.h:42
std::string json_path_to_pointer_conversion(std::string_view json_path)
Converts JSONPath to JSON Pointer.
Definition: jsonpathutil.h:13
The result of a simdjson operation that could fail.
Definition: error.h:215
simdjson_inline error_code error() const noexcept
The error.
Definition: error-inl.h:131