Skip to content

Commit 195acc3

Browse files
committed
Add find_field / find_field_unordered to object
1 parent b842658 commit 195acc3

File tree

10 files changed

+732
-132
lines changed

10 files changed

+732
-132
lines changed

include/simdjson/generic/ondemand/document-inl.h

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,46 @@ simdjson_really_inline document document::start(json_iterator &&iter) noexcept {
1212
return document(std::forward<json_iterator>(iter));
1313
}
1414

15-
simdjson_really_inline value document::as_value() noexcept {
16-
return as_value_iterator();
15+
simdjson_really_inline value_iterator document::resume_value_iterator() noexcept {
16+
return value_iterator(&iter, 1, iter.root_checkpoint());
1717
}
18-
simdjson_really_inline value_iterator document::as_value_iterator() noexcept {
18+
simdjson_really_inline value_iterator document::get_root_value_iterator() noexcept {
1919
iter.assert_at_root();
20-
return value_iterator(&iter, 1, iter.root_checkpoint());
20+
return resume_value_iterator();
2121
}
22-
simdjson_really_inline value_iterator document::as_non_root_value_iterator() noexcept {
23-
return value_iterator(&iter, 1, iter.root_checkpoint());
22+
simdjson_really_inline value document::resume_value() noexcept {
23+
return resume_value_iterator();
24+
}
25+
simdjson_really_inline value document::get_root_value() noexcept {
26+
return get_root_value_iterator();
2427
}
2528

2629
simdjson_really_inline simdjson_result<array> document::get_array() & noexcept {
27-
return as_value().get_array();
30+
return get_root_value().get_array();
2831
}
2932
simdjson_really_inline simdjson_result<object> document::get_object() & noexcept {
30-
return as_value().get_object();
33+
return get_root_value().get_object();
3134
}
3235
simdjson_really_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
33-
return as_value_iterator().require_root_uint64();
36+
return get_root_value_iterator().require_root_uint64();
3437
}
3538
simdjson_really_inline simdjson_result<int64_t> document::get_int64() noexcept {
36-
return as_value_iterator().require_root_int64();
39+
return get_root_value_iterator().require_root_int64();
3740
}
3841
simdjson_really_inline simdjson_result<double> document::get_double() noexcept {
39-
return as_value_iterator().require_root_double();
42+
return get_root_value_iterator().require_root_double();
4043
}
4144
simdjson_really_inline simdjson_result<std::string_view> document::get_string() & noexcept {
42-
return as_value().get_string();
45+
return get_root_value().get_string();
4346
}
4447
simdjson_really_inline simdjson_result<raw_json_string> document::get_raw_json_string() & noexcept {
45-
return as_value().get_raw_json_string();
48+
return get_root_value().get_raw_json_string();
4649
}
4750
simdjson_really_inline simdjson_result<bool> document::get_bool() noexcept {
48-
return as_value_iterator().require_root_bool();
51+
return get_root_value_iterator().require_root_bool();
4952
}
5053
simdjson_really_inline bool document::is_null() noexcept {
51-
return as_value_iterator().is_root_null();
54+
return get_root_value_iterator().is_root_null();
5255
}
5356

5457
template<> simdjson_really_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
@@ -89,21 +92,24 @@ simdjson_really_inline simdjson_result<array_iterator> document::begin() & noexc
8992
simdjson_really_inline simdjson_result<array_iterator> document::end() & noexcept {
9093
return {};
9194
}
95+
96+
simdjson_really_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
97+
return resume_value().find_field(key);
98+
}
99+
simdjson_really_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
100+
return resume_value().find_field(key);
101+
}
102+
simdjson_really_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
103+
return resume_value().find_field_unordered(key);
104+
}
105+
simdjson_really_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
106+
return resume_value().find_field_unordered(key);
107+
}
92108
simdjson_really_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
93-
if (iter.at_root()) {
94-
return get_object()[key];
95-
} else {
96-
// If we're not at the root, this is not the first key we've grabbed
97-
return object::resume(as_non_root_value_iterator())[key];
98-
}
109+
return resume_value()[key];
99110
}
100111
simdjson_really_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
101-
if (iter.at_root()) {
102-
return get_object()[key];
103-
} else {
104-
// If we're not at the root, this is not the first key we've grabbed
105-
return object::resume(as_non_root_value_iterator())[key];
106-
}
112+
return resume_value()[key];
107113
}
108114

109115
} // namespace ondemand
@@ -136,6 +142,14 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_
136142
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::end() & noexcept {
137143
return {};
138144
}
145+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
146+
if (error()) { return error(); }
147+
return first.find_field_unordered(key);
148+
}
149+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::find_field_unordered(const char *key) & noexcept {
150+
if (error()) { return error(); }
151+
return first.find_field_unordered(key);
152+
}
139153
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator[](std::string_view key) & noexcept {
140154
if (error()) { return error(); }
141155
return first[key];
@@ -144,6 +158,14 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
144158
if (error()) { return error(); }
145159
return first[key];
146160
}
161+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::find_field(std::string_view key) & noexcept {
162+
if (error()) { return error(); }
163+
return first.find_field(key);
164+
}
165+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::find_field(const char *key) & noexcept {
166+
if (error()) { return error(); }
167+
return first.find_field(key);
168+
}
147169
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_array() & noexcept {
148170
if (error()) { return error(); }
149171
return first.get_array();

include/simdjson/generic/ondemand/document.h

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,30 +206,64 @@ class document {
206206
simdjson_really_inline simdjson_result<array_iterator> end() & noexcept;
207207

208208
/**
209-
* Look up a field by name on an object.
209+
* Look up a field by name on an object (order-sensitive).
210210
*
211-
* Important notes:
211+
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
212+
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
212213
*
213-
* * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
214-
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
215-
* * **Once Only:** You may only look up a single field on a document. To look up multiple fields,
216-
* use `.get_object()` or cast to `object`.
214+
* ```c++
215+
* simdjson::builtin::ondemand::parser parser;
216+
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
217+
* double z = obj.find_field("z");
218+
* double y = obj.find_field("y");
219+
* double x = obj.find_field("x");
220+
* ```
221+
*
222+
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
223+
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
224+
*
225+
* @param key The key to look up.
226+
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
227+
*/
228+
simdjson_really_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
229+
/** @overload simdjson_really_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
230+
simdjson_really_inline simdjson_result<value> find_field(const char *key) & noexcept;
231+
232+
/**
233+
* Look up a field by name on an object, without regard to key order.
234+
*
235+
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
236+
* and often appears negligible. It starts out normally, starting out at the last field; but if
237+
* the field is not found, it scans from the beginning of the object to see if it missed it. That
238+
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
239+
* in question is large. The fact that the extra code is there also bumps the executable size.
240+
*
241+
* It is the default, however, because it would be highly surprising (and hard to debug) if the
242+
* default behavior failed to look up a field just because it was in the wrong order--and many
243+
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
244+
*
245+
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
246+
* field wasn't there when they aren't).
217247
*
218248
* @param key The key to look up.
219-
* @returns The value of the field, NO_SUCH_FIELD if the field is not in the object, or
220-
* INCORRECT_TYPE if the JSON value is not an array.
249+
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
221250
*/
251+
simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
252+
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
253+
simdjson_really_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
254+
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
222255
simdjson_really_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
223-
/** @overload simdjson_really_inline simdjson_result<value> operator[](std::string_view key) & noexcept; */
256+
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
224257
simdjson_really_inline simdjson_result<value> operator[](const char *key) & noexcept;
225258

226259
protected:
227260
simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept;
228261
simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept;
229262

230-
simdjson_really_inline value as_value() noexcept;
231-
simdjson_really_inline value_iterator as_value_iterator() noexcept;
232-
simdjson_really_inline value_iterator as_non_root_value_iterator() noexcept;
263+
simdjson_really_inline value_iterator resume_value_iterator() noexcept;
264+
simdjson_really_inline value_iterator get_root_value_iterator() noexcept;
265+
simdjson_really_inline value resume_value() noexcept;
266+
simdjson_really_inline value get_root_value() noexcept;
233267
static simdjson_really_inline document start(ondemand::json_iterator &&iter) noexcept;
234268

235269
//
@@ -290,8 +324,12 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> : public SIM
290324

291325
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> begin() & noexcept;
292326
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> end() & noexcept;
327+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) & noexcept;
328+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(const char *key) & noexcept;
293329
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
294330
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) & noexcept;
331+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
332+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
295333
};
296334

297335
} // namespace simdjson

include/simdjson/generic/ondemand/json_iterator-inl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,13 @@ simdjson_really_inline uint32_t json_iterator::peek_length(int32_t delta) const
132132
}
133133

134134
simdjson_really_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
135+
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
135136
SIMDJSON_ASSUME(_depth == parent_depth + 1);
136137
_depth = parent_depth;
137138
}
138139

139140
simdjson_really_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
141+
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
140142
SIMDJSON_ASSUME(_depth == child_depth - 1);
141143
_depth = child_depth;
142144
}

include/simdjson/generic/ondemand/object-inl.h

Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,31 @@ namespace simdjson {
22
namespace SIMDJSON_IMPLEMENTATION {
33
namespace ondemand {
44

5-
//
6-
// ### Live States
7-
//
8-
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
9-
// always SUCCESS:
10-
//
11-
// - Start: This is the state when the object is first found and the iterator is just past the {.
12-
// In this state, at_start == true.
13-
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
14-
// iterate over, the iterator is at the , or } before the next value. In this state,
15-
// depth == iter.depth, at_start == false, and error == SUCCESS.
16-
// - Unfinished Business: When we hand an array/object to the user which they do not fully
17-
// iterate over, we need to finish that iteration by skipping child values until we reach the
18-
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
19-
//
20-
// ## Error States
21-
//
22-
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
23-
// and at_start is always false. We decrement after yielding the error, moving to the Finished
24-
// state.
25-
//
26-
// - Chained Error: When the object iterator is part of an error chain--for example, in
27-
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
28-
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
29-
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
30-
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
31-
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
32-
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
33-
//
34-
// Errors that occur while reading a field to give to the user (such as when the key is not a
35-
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
36-
// moving to the Finished state without transitioning through an Error state at all.
37-
//
38-
// ## Terminal State
39-
//
40-
// The terminal state has iter.depth < depth. at_start is always false.
41-
//
42-
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
43-
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
44-
//
45-
5+
simdjson_really_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
6+
bool has_value;
7+
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
8+
if (!has_value) { return NO_SUCH_FIELD; }
9+
return value(iter.child());
10+
}
11+
simdjson_really_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
12+
bool has_value;
13+
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
14+
if (!has_value) { return NO_SUCH_FIELD; }
15+
return value(iter.child());
16+
}
4617
simdjson_really_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
18+
return find_field_unordered(key);
19+
}
20+
simdjson_really_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
21+
return find_field_unordered(key);
22+
}
23+
simdjson_really_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
4724
bool has_value;
4825
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
4926
if (!has_value) { return NO_SUCH_FIELD; }
5027
return value(iter.child());
5128
}
52-
53-
simdjson_really_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
29+
simdjson_really_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
5430
bool has_value;
5531
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
5632
if (!has_value) { return NO_SUCH_FIELD; }
@@ -108,6 +84,14 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object
10884
if (error()) { return error(); }
10985
return first.end();
11086
}
87+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
88+
if (error()) { return error(); }
89+
return first.find_field_unordered(key);
90+
}
91+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
92+
if (error()) { return error(); }
93+
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field_unordered(key);
94+
}
11195
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::operator[](std::string_view key) & noexcept {
11296
if (error()) { return error(); }
11397
return first[key];
@@ -116,5 +100,13 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
116100
if (error()) { return error(); }
117101
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first)[key];
118102
}
103+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(std::string_view key) & noexcept {
104+
if (error()) { return error(); }
105+
return first.find_field(key);
106+
}
107+
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(std::string_view key) && noexcept {
108+
if (error()) { return error(); }
109+
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key);
110+
}
119111

120112
} // namespace simdjson

0 commit comments

Comments
 (0)