Skip to content

Commit b28cafc

Browse files
committed
Remove backslash unescaping from JSON pointer impl
Also speed up non-escaped key lookup
1 parent 65f999b commit b28cafc

File tree

4 files changed

+47
-46
lines changed

4 files changed

+47
-46
lines changed

include/simdjson/document.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ class document::key_value_pair {
11101110
/** The result of a JSON navigation that may fail. */
11111111
class document::element_result : public simdjson_result<document::element> {
11121112
public:
1113+
really_inline element_result() noexcept;
11131114
really_inline element_result(element value) noexcept;
11141115
really_inline element_result(error_code error) noexcept;
11151116

@@ -1146,6 +1147,7 @@ class document::element_result : public simdjson_result<document::element> {
11461147
/** The result of a JSON conversion that may fail. */
11471148
class document::array_result : public simdjson_result<document::array> {
11481149
public:
1150+
really_inline array_result() noexcept;
11491151
really_inline array_result(array value) noexcept;
11501152
really_inline array_result(error_code error) noexcept;
11511153

@@ -1163,6 +1165,7 @@ class document::array_result : public simdjson_result<document::array> {
11631165
/** The result of a JSON conversion that may fail. */
11641166
class document::object_result : public simdjson_result<document::object> {
11651167
public:
1168+
really_inline object_result() noexcept;
11661169
really_inline object_result(object value) noexcept;
11671170
really_inline object_result(error_code error) noexcept;
11681171

include/simdjson/error.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ struct simdjson_result : public std::pair<T, error_code> {
118118

119119
#endif // SIMDJSON_EXCEPTIONS
120120

121+
/**
122+
* Create a new empty result with error = UNINITIALIZED.
123+
*/
124+
simdjson_result() noexcept : simdjson_result(UNINITIALIZED) {}
125+
121126
/**
122127
* Create a new error result.
123128
*/
@@ -180,6 +185,11 @@ struct simdjson_move_result : std::pair<T, error_code> {
180185

181186
#endif
182187

188+
/**
189+
* Create a new empty result with error = UNINITIALIZED.
190+
*/
191+
simdjson_move_result() noexcept : simdjson_move_result(UNINITIALIZED) {}
192+
183193
/**
184194
* Create a new error result.
185195
*/

include/simdjson/inline/document.h

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace simdjson {
1616
//
1717
// element_result inline implementation
1818
//
19+
really_inline document::element_result::element_result() noexcept : simdjson_result<element>() {}
1920
really_inline document::element_result::element_result(element value) noexcept : simdjson_result<element>(value) {}
2021
really_inline document::element_result::element_result(error_code error) noexcept : simdjson_result<element>(error) {}
2122
inline simdjson_result<bool> document::element_result::is_null() const noexcept {
@@ -111,6 +112,7 @@ inline document::element_result::operator document::object() const noexcept(fals
111112
//
112113
// array_result inline implementation
113114
//
115+
really_inline document::array_result::array_result() noexcept : simdjson_result<array>() {}
114116
really_inline document::array_result::array_result(array value) noexcept : simdjson_result<array>(value) {}
115117
really_inline document::array_result::array_result(error_code error) noexcept : simdjson_result<array>(error) {}
116118

@@ -146,6 +148,7 @@ inline document::element_result document::array_result::at(size_t index) const n
146148
//
147149
// object_result inline implementation
148150
//
151+
really_inline document::object_result::object_result() noexcept : simdjson_result<object>() {}
149152
really_inline document::object_result::object_result(object value) noexcept : simdjson_result<object>(value) {}
150153
really_inline document::object_result::object_result(error_code error) noexcept : simdjson_result<object>(error) {}
151154

@@ -805,53 +808,38 @@ inline document::element_result document::object::operator[](const char *json_po
805808
return (*this)[std::string_view(json_pointer)];
806809
}
807810
inline document::element_result document::object::at(std::string_view json_pointer) const noexcept {
808-
// Unescape the key
809-
std::string unescaped;
810-
unescaped.reserve(json_pointer.length());
811-
size_t i;
812-
for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
813-
switch (json_pointer[i]) {
814-
// Handle ~ escaping: ~0 = ~, ~1 = /
815-
case '~': {
816-
i++;
817-
// ~ at end of string is invalid
818-
if (i >= json_pointer.length()) { RETURN_ERROR(INVALID_JSON_POINTER, "~ at end of string in JSON pointer"); }
819-
switch (json_pointer[i]) {
820-
case '0':
821-
unescaped.push_back('~');
822-
break;
823-
case '1':
824-
unescaped.push_back('/');
825-
break;
826-
default:
827-
RETURN_ERROR(INVALID_JSON_POINTER, "Unexpected ~ escape character in JSON pointer");
828-
}
829-
break;
830-
}
831-
// TODO backslash doesn't appear to be a thing in JSON pointer
832-
case '\\': {
833-
i++;
834-
// backslash at end of string is invalid
835-
if (i >= json_pointer.length()) { RETURN_ERROR(INVALID_JSON_POINTER, "~ at end of string in JSON pointer"); }
836-
// Check for invalid escape characters
837-
if (json_pointer[i] != '\\' && json_pointer[i] != '"' && json_pointer[i] > 0x1F) {
838-
RETURN_ERROR(INVALID_JSON_POINTER, "Invalid backslash escape in JSON pointer");
839-
}
840-
unescaped.push_back(json_pointer[i]);
841-
break;
842-
}
843-
default:
844-
unescaped.push_back(json_pointer[i]);
845-
break;
846-
}
847-
}
811+
size_t slash = json_pointer.find('/');
812+
std::string_view key = json_pointer.substr(0, slash);
848813

849814
// Grab the child with the given key
850-
auto child = at_key(unescaped);
815+
document::element_result child;
816+
817+
// If there is an escape character in the key, unescape it and then get the child.
818+
size_t escape = key.find('~');
819+
if (escape != std::string_view::npos) {
820+
// Unescape the key
821+
std::string unescaped(key);
822+
do {
823+
switch (unescaped[escape+1]) {
824+
case '0':
825+
unescaped.replace(escape, 2, "~");
826+
break;
827+
case '1':
828+
unescaped.replace(escape, 2, "/");
829+
break;
830+
default:
831+
RETURN_ERROR(INVALID_JSON_POINTER, "Unexpected ~ escape character in JSON pointer");
832+
}
833+
escape = unescaped.find('~', escape+1);
834+
} while (escape != std::string::npos);
835+
child = at_key(unescaped);
836+
} else {
837+
child = at_key(key);
838+
}
851839

852840
// If there is a /, we have to recurse and look up more of the path
853-
if (i < json_pointer.length()) {
854-
child = child.at(json_pointer.substr(i+1));
841+
if (slash != std::string_view::npos) {
842+
child = child.at(json_pointer.substr(slash+1));
855843
}
856844

857845
return child;

tests/pointercheck.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ bool json_pointer_failure_test(const char *json_pointer, error_code expected_fai
6060

6161
int main() {
6262
if (
63-
json_pointer_success_test(R"(/~1~001abc/1/\\\" 0/0)", "value0") &&
64-
json_pointer_success_test(R"(/~1~001abc/1/\\\" 0/1)", "value1") &&
65-
json_pointer_failure_test(R"(/~1~001abc/1/\\\" 0/2)", INDEX_OUT_OF_BOUNDS) && // index actually out of bounds
63+
json_pointer_success_test("/~1~001abc/1/\\\" 0/0", "value0") &&
64+
json_pointer_success_test("/~1~001abc/1/\\\" 0/1", "value1") &&
65+
json_pointer_failure_test("/~1~001abc/1/\\\" 0/2", INDEX_OUT_OF_BOUNDS) && // index actually out of bounds
6666
json_pointer_success_test("/arr") && // get array
6767
json_pointer_failure_test("/arr/0", INDEX_OUT_OF_BOUNDS) && // array index 0 out of bounds on empty array
6868
json_pointer_success_test("/~1~001abc") && // get object

0 commit comments

Comments
 (0)