Compare commits

...

5 Commits

Author SHA1 Message Date
Daniel Lemire bfd8856913 less terrible maybe 2026-02-05 20:06:38 -05:00
Daniel Lemire 39bee282da implementing the backslash-free string optimization 2026-01-22 16:30:05 -05:00
Daniel Lemire d9956d7b3b Merge branch 'master' into optimization-get_string 2026-01-22 10:44:04 -05:00
Daniel Lemire 8e7ddb3155 Merge branch 'master' into optimization-get_string 2026-01-22 10:43:29 -05:00
Carlos Sousa e0ef3e7cec optimize get_string 2024-07-11 01:30:27 -03:00
2 changed files with 41 additions and 6 deletions
+5 -2
View File
@@ -198,14 +198,17 @@ public:
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns An UTF-8 string. The string is stored in the parser when escaping was needed
* and will be invalidated the next
* time it parses a document or when it is destroyed. If no escaping was needed,
* the string_view points directly into the original JSON buffer.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
* The data is stored into the provided std::string instance.
*
* The string is guaranteed to be valid UTF-8.
*
@@ -510,6 +510,38 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
// Optimization strategy:
// We expect that most strings do not have escape characters, and most are short.
// So we can quickly check for backslashes and if there are none, we can just return a string_view
// into the original JSON buffer. There is no need to copy or unescape.
// Fast path: check for backslash in the string
// It may seem that this function is odd in that it scans the string even if a
// backslash is found early. However, we expect that in most strings there will be no backslash,
// so we optimize for that case. The compiler knows to expect a full scan and it can optimize for it.
auto has_backslash_fast = [](std::string_view s) noexcept {
for(const char c : s) {
if(c == '\\') {
return true;
}
}
return false;
};
std::string_view string_with_quotes(reinterpret_cast<const char*>(peek_start()), peek_start_length());
if(string_with_quotes.front() != '"') {
return incorrect_type_error("Not a string");
}
if(!has_backslash_fast(string_with_quotes)) {
// Find the ending quote
size_t len = string_with_quotes.size();
while(string_with_quotes[len - 1] != '"') {
len--;
}
// At this point len is 2 or more
return std::string_view(string_with_quotes.data() + 1, len - 2);
}
// Slow path: we have a backslash, so we need to unescape
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
@@ -517,8 +549,7 @@ simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(strin
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
SIMDJSON_TRY(get_string(allow_replacement).get(content));
receiver = content;
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
@@ -656,6 +687,8 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
// We could optimize for the no-escape case here as well, but root strings
// are less common so we do the simple thing for now.
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
@@ -663,8 +696,7 @@ simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
SIMDJSON_TRY(get_root_string(check_trailing, allow_replacement).get(content));
receiver = content;
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;