mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fbacb0058 | |||
| 509066f06a | |||
| 070f0b26a3 | |||
| 1a195623a5 | |||
| 5f7a56e7f1 | |||
| 5e60f0482d |
Vendored
+11
-1
@@ -86,6 +86,16 @@
|
|||||||
"vector": "cpp",
|
"vector": "cpp",
|
||||||
"*.ipp": "cpp",
|
"*.ipp": "cpp",
|
||||||
"__functional_base_03": "cpp",
|
"__functional_base_03": "cpp",
|
||||||
"filesystem": "cpp"
|
"filesystem": "cpp",
|
||||||
|
"*.inc": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"variant": "cpp",
|
||||||
|
"__bits": "cpp",
|
||||||
|
"csignal": "cpp",
|
||||||
|
"future": "cpp",
|
||||||
|
"queue": "cpp",
|
||||||
|
"shared_mutex": "cpp",
|
||||||
|
"ranges": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
|||||||
project(
|
project(
|
||||||
simdjson
|
simdjson
|
||||||
# The version number is modified by tools/release.py
|
# The version number is modified by tools/release.py
|
||||||
VERSION 2.0.4
|
VERSION 2.1.0
|
||||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||||
HOMEPAGE_URL "https://simdjson.org/"
|
HOMEPAGE_URL "https://simdjson.org/"
|
||||||
LANGUAGES CXX C
|
LANGUAGES CXX C
|
||||||
@@ -20,8 +20,8 @@ string(
|
|||||||
# ---- Options, variables ----
|
# ---- Options, variables ----
|
||||||
|
|
||||||
# These version numbers are modified by tools/release.py
|
# These version numbers are modified by tools/release.py
|
||||||
set(SIMDJSON_LIB_VERSION "11.0.0" CACHE STRING "simdjson library version")
|
set(SIMDJSON_LIB_VERSION "12.0.0" CACHE STRING "simdjson library version")
|
||||||
set(SIMDJSON_LIB_SOVERSION "11" CACHE STRING "simdjson library soversion")
|
set(SIMDJSON_LIB_SOVERSION "12" CACHE STRING "simdjson library soversion")
|
||||||
|
|
||||||
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
|
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = "2.0.4"
|
PROJECT_NUMBER = "2.1.0"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
|||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
#include "json2msgpack/simdjson_ondemand.h"
|
||||||
|
#include "json2msgpack/rapidjson.h"
|
||||||
|
#include "json2msgpack/yyjson.h"
|
||||||
|
#include "json2msgpack/sajson.h"
|
||||||
|
#include "json2msgpack/nlohmann_json.h"
|
||||||
|
|
||||||
#include "amazon_cellphones/simdjson_dom.h"
|
#include "amazon_cellphones/simdjson_dom.h"
|
||||||
#include "amazon_cellphones/simdjson_ondemand.h"
|
#include "amazon_cellphones/simdjson_ondemand.h"
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ BENCHMARK_TEMPLATE(distinct_user_id, rapidjson)->UseManualTime();
|
|||||||
|
|
||||||
struct rapidjson_insitu : rapidjson_base {
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson_insitu)->UseManualTime();
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ namespace distinct_user_id {
|
|||||||
struct sajson {
|
struct sajson {
|
||||||
size_t ast_buffer_size{0};
|
size_t ast_buffer_size{0};
|
||||||
size_t *ast_buffer{nullptr};
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
||||||
auto val = obj.get_value_of_key({key.data(), key.length()});
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ BENCHMARK_TEMPLATE(find_tweet, rapidjson)->UseManualTime();
|
|||||||
|
|
||||||
struct rapidjson_insitu : rapidjson_base {
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), find_id, result);
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), find_id, result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(find_tweet, rapidjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(find_tweet, rapidjson_insitu)->UseManualTime();
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ struct sajson {
|
|||||||
|
|
||||||
size_t ast_buffer_size{0};
|
size_t ast_buffer_size{0};
|
||||||
size_t *ast_buffer{nullptr};
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
||||||
auto val = obj.get_value_of_key({key.data(), key.length()});
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "json_benchmark/file_runner.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace json_benchmark;
|
||||||
|
|
||||||
|
template <typename I> struct runner : public file_runner<I> {
|
||||||
|
std::string_view result;
|
||||||
|
std::unique_ptr<char[]> buffer;
|
||||||
|
|
||||||
|
bool setup(benchmark::State &state) {
|
||||||
|
bool isok = this->load_json(state, TWITTER_JSON);
|
||||||
|
if (isok) {
|
||||||
|
// Let us allocate a sizeable buffer.
|
||||||
|
buffer = std::unique_ptr<char[]>(new char[this->json.size() * 4 + 1024]);
|
||||||
|
}
|
||||||
|
return isok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool before_run(benchmark::State &state) {
|
||||||
|
if (!file_runner<I>::before_run(state)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Clear the buffer.
|
||||||
|
::memset(buffer.get(), 0, this->json.size() * 4 + 1024);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(benchmark::State &) {
|
||||||
|
return this->implementation.run(this->json, buffer.get(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
bool diff(benchmark::State &state, runner<R> &reference) {
|
||||||
|
return diff_results(state, result.size(), reference.result.size(), diff_flags::NONE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct simdjson_ondemand;
|
||||||
|
|
||||||
|
template <typename I>
|
||||||
|
simdjson_really_inline static void json2msgpack(benchmark::State &state) {
|
||||||
|
run_json_benchmark<runner<I>, runner<simdjson_ondemand>>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
struct nlohmann_json2msgpack {
|
||||||
|
inline std::string_view to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_string(const std::string& str);
|
||||||
|
inline void recursive_processor(basic_json<> element);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view nlohmann_json2msgpack::to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
auto val = nlohmann::json::parse(json.data(), json.data() + json.size());
|
||||||
|
recursive_processor(val);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_string(const std::string & str) {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(str.size()));
|
||||||
|
::memcpy(buff, str.data(), str.size());
|
||||||
|
buff += str.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::recursive_processor(json element) {
|
||||||
|
switch (element.type()) {
|
||||||
|
case nlohmann::detail::value_t::array: {
|
||||||
|
uint32_t counter = 0;
|
||||||
|
write_byte(0xdd);
|
||||||
|
std::vector<json> array = element.get<std::vector<json>>();
|
||||||
|
write_uint32(uint32_t(array.size()));
|
||||||
|
for (auto child : array) {
|
||||||
|
recursive_processor(child);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case nlohmann::detail::value_t::object: {
|
||||||
|
write_byte(0xdf);
|
||||||
|
std::map<std::string,json> object = element.get<std::map<std::string,json>>();
|
||||||
|
write_uint32(uint32_t(object.size()));
|
||||||
|
for (auto field : object) {
|
||||||
|
write_string(field.first);
|
||||||
|
recursive_processor(field.second);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case nlohmann::detail::value_t::number_integer:
|
||||||
|
case nlohmann::detail::value_t::number_unsigned:
|
||||||
|
case nlohmann::detail::value_t::number_float:
|
||||||
|
write_double(double(element));
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::string:
|
||||||
|
write_string(std::string(element));
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::boolean:
|
||||||
|
write_byte(0xc2 + bool(element));
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::null:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::discarded:
|
||||||
|
case nlohmann::detail::value_t::binary:
|
||||||
|
default:
|
||||||
|
printf("unexpected\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nlohmann_json {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
nlohmann_json2msgpack parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result = parser.to_msgpack(json, reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, nlohmann_json)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
struct rapidjson2msgpack {
|
||||||
|
inline std::string_view to_msgpack(char *json, uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_uint32_at(const uint32_t w, uint8_t *p) noexcept;
|
||||||
|
void write_string(const char * s, size_t length) noexcept;
|
||||||
|
inline void recursive_processor(Value &v);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
std::string_view rapidjson2msgpack<parseflag>::to_msgpack(char *json, uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
Document doc{};
|
||||||
|
if(parseflag & kParseInsituFlag) {
|
||||||
|
doc.ParseInsitu<parseflag>(json);
|
||||||
|
} else {
|
||||||
|
doc.Parse<parseflag>(json);
|
||||||
|
}
|
||||||
|
recursive_processor(doc);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_string(const char * c, size_t len) noexcept {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(len));
|
||||||
|
::memcpy(buff, c, len);
|
||||||
|
buff += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
|
||||||
|
::memcpy(p, &w, sizeof(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::recursive_processor(Value &v) {
|
||||||
|
switch (v.GetType()) {
|
||||||
|
case kArrayType:
|
||||||
|
write_byte(0xdd);
|
||||||
|
write_uint32(v.Size());
|
||||||
|
for (Value::ValueIterator i = v.Begin(); i != v.End(); ++i) {
|
||||||
|
recursive_processor(*i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kObjectType:
|
||||||
|
write_byte(0xdf);
|
||||||
|
write_uint32(uint32_t(v.MemberEnd()-v.MemberBegin()));
|
||||||
|
for (Value::MemberIterator m = v.MemberBegin(); m != v.MemberEnd();
|
||||||
|
++m) {
|
||||||
|
write_string(m->name.GetString(), m->name.GetStringLength());
|
||||||
|
recursive_processor(m->value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kStringType:
|
||||||
|
write_string(v.GetString(), v.GetStringLength());
|
||||||
|
break;
|
||||||
|
case kNumberType:
|
||||||
|
write_double(v.GetDouble());
|
||||||
|
break;
|
||||||
|
case kFalseType:
|
||||||
|
write_byte(0xc2);
|
||||||
|
break;
|
||||||
|
case kTrueType:
|
||||||
|
write_byte(0xc3);
|
||||||
|
break;
|
||||||
|
case kNullType:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
struct rapidjson_base {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
rapidjson2msgpack<parseflag> parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result =
|
||||||
|
parser.to_msgpack(json.data(), reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
using rapidjson_lossless = rapidjson_base<kParseValidateEncodingFlag|kParseFullPrecisionFlag>;
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, rapidjson_lossless)->UseManualTime();
|
||||||
|
|
||||||
|
|
||||||
|
using rapidjson = rapidjson_base<kParseValidateEncodingFlag>;
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, rapidjson)->UseManualTime();
|
||||||
|
|
||||||
|
using rapidjson_insitu = rapidjson_base<kParseValidateEncodingFlag|kParseInsituFlag>;
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, rapidjson_insitu)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_SAJSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace sajson;
|
||||||
|
|
||||||
|
|
||||||
|
struct sajson2msgpack {
|
||||||
|
inline std::string_view to_msgpack(char *json, size_t size, uint8_t *buf);
|
||||||
|
virtual ~sajson2msgpack() { free(ast_buffer); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_string(const char * s, size_t length) noexcept;
|
||||||
|
inline void recursive_processor(const sajson::value &v);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
size_t ast_buffer_size{0};
|
||||||
|
size_t *ast_buffer{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::string_view sajson2msgpack::to_msgpack(char *json, size_t size, uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
|
||||||
|
if (!ast_buffer) {
|
||||||
|
ast_buffer_size = size;
|
||||||
|
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
|
||||||
|
}
|
||||||
|
auto doc = parse(
|
||||||
|
bounded_allocation(ast_buffer, ast_buffer_size),
|
||||||
|
mutable_string_view(size, json)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto root = doc.get_root();
|
||||||
|
recursive_processor(root);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_string(const char * c, size_t len) noexcept {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(len));
|
||||||
|
::memcpy(buff, c, len);
|
||||||
|
buff += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::recursive_processor(const sajson::value &node) {
|
||||||
|
using namespace sajson;
|
||||||
|
switch (node.get_type()) {
|
||||||
|
case TYPE_NULL:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
case TYPE_FALSE:
|
||||||
|
write_byte(0xc2);
|
||||||
|
break;
|
||||||
|
case TYPE_TRUE:
|
||||||
|
write_byte(0xc3);
|
||||||
|
break;
|
||||||
|
case TYPE_ARRAY: {
|
||||||
|
auto length = node.get_length();
|
||||||
|
write_byte(0xdf);
|
||||||
|
write_uint32(uint32_t(length));
|
||||||
|
for (size_t i = 0; i < length; ++i) {
|
||||||
|
recursive_processor(node.get_array_element(i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE_OBJECT: {
|
||||||
|
auto length = node.get_length();
|
||||||
|
write_byte(0xdd);
|
||||||
|
write_uint32(uint32_t(length));
|
||||||
|
for (auto i = 0u; i < length; ++i) {
|
||||||
|
auto s = node.get_object_key(i);
|
||||||
|
write_string(s.data(), s.length());
|
||||||
|
recursive_processor(node.get_object_value(i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE_STRING:
|
||||||
|
write_string(node.as_cstring(), node.get_string_length());
|
||||||
|
break;
|
||||||
|
case TYPE_DOUBLE:
|
||||||
|
case TYPE_INTEGER:
|
||||||
|
write_double(node.get_number_value());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false && "unknown node type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct sajson {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
sajson2msgpack parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result =
|
||||||
|
parser.to_msgpack(json.data(), json.size(), reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, sajson)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAJSON
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
#pragma once
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The simdjson2msgpack struct is used to quickly convert
|
||||||
|
* JSON strings to msgpack views. You must provide a pointer to
|
||||||
|
* a large memory region where the msgpack gets written. The
|
||||||
|
* buffer should be large enough to store the msgpack output (which
|
||||||
|
* can never be 3x larger than the input JSON) with an additional
|
||||||
|
* simdjson::SIMDJSON_PADDING bytes.
|
||||||
|
*
|
||||||
|
* Recommended usage:
|
||||||
|
*
|
||||||
|
* simdjson2msgpack parser{};
|
||||||
|
* simdjson::padded_string json = "[1,2]"_padded; // some JSON
|
||||||
|
* uint8_t * buffer = new uint8_t[3*json.size() + simdjson::SIMDJSON_PADDING]; // large buffer
|
||||||
|
*
|
||||||
|
* std::string_view msgpack = parser.to_msgpack(json, buffer);
|
||||||
|
*
|
||||||
|
* The result (msgpack) is a string view to a msgpack serialization of the input JSON,
|
||||||
|
* it points inside the buffer you provided.
|
||||||
|
*
|
||||||
|
* You may reuse the simdjson2msgpack instance though you should use
|
||||||
|
* one per thread.
|
||||||
|
*/
|
||||||
|
struct simdjson2msgpack {
|
||||||
|
/**
|
||||||
|
* @brief Converts the provided JSON into msgpack.
|
||||||
|
*
|
||||||
|
* @param json JSON input
|
||||||
|
* @param buf temporary buffer (must be large enough, with 32 bytes of
|
||||||
|
* padding)
|
||||||
|
* @return std::string_view msgpack output, writting to the temporary buffer
|
||||||
|
*/
|
||||||
|
inline std::string_view to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
simdjson_really_inline void write_double(const double d) noexcept;
|
||||||
|
simdjson_really_inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
simdjson_really_inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
simdjson_really_inline uint8_t *skip_uint32() noexcept;
|
||||||
|
simdjson_really_inline void write_uint32_at(const uint32_t w,
|
||||||
|
uint8_t *p) noexcept;
|
||||||
|
simdjson_really_inline void
|
||||||
|
write_raw_string(simdjson::ondemand::raw_json_string rjs);
|
||||||
|
inline void recursive_processor(simdjson::ondemand::value element);
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view
|
||||||
|
simdjson2msgpack::to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
if (doc.is_scalar()) {
|
||||||
|
// we have a special case where the JSON document is a single document...
|
||||||
|
switch (doc.type()) {
|
||||||
|
case simdjson::ondemand::json_type::number:
|
||||||
|
write_double(doc.get_double());
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::string:
|
||||||
|
write_raw_string(doc.get_raw_json_string());
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::boolean:
|
||||||
|
write_byte(0xc2 + doc.get_bool());
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::null:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::array:
|
||||||
|
case simdjson::ondemand::json_type::object:
|
||||||
|
default:
|
||||||
|
// impossible
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
simdjson::ondemand::value val = doc;
|
||||||
|
recursive_processor(val);
|
||||||
|
}
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjson2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjson2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjson2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *simdjson2msgpack::skip_uint32() noexcept {
|
||||||
|
uint8_t *ret = buff;
|
||||||
|
buff += sizeof(uint32_t);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjson2msgpack::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
|
||||||
|
::memcpy(p, &w, sizeof(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjson2msgpack::write_raw_string(
|
||||||
|
simdjson::ondemand::raw_json_string in) {
|
||||||
|
write_byte(0xdb);
|
||||||
|
uint8_t *location = skip_uint32();
|
||||||
|
std::string_view v = parser.unescape(in, buff);
|
||||||
|
write_uint32_at(uint32_t(v.size()), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjson2msgpack::recursive_processor(simdjson::ondemand::value element) {
|
||||||
|
switch (element.type()) {
|
||||||
|
case simdjson::ondemand::json_type::array: {
|
||||||
|
uint32_t counter = 0;
|
||||||
|
write_byte(0xdd);
|
||||||
|
uint8_t *location = skip_uint32();
|
||||||
|
for (auto child : element.get_array()) {
|
||||||
|
counter++;
|
||||||
|
recursive_processor(child.value());
|
||||||
|
}
|
||||||
|
write_uint32_at(counter, location);
|
||||||
|
} break;
|
||||||
|
case simdjson::ondemand::json_type::object: {
|
||||||
|
uint32_t counter = 0;
|
||||||
|
write_byte(0xdf);
|
||||||
|
uint8_t *location = skip_uint32();
|
||||||
|
for (auto field : element.get_object()) {
|
||||||
|
counter++;
|
||||||
|
write_raw_string(field.key());
|
||||||
|
recursive_processor(field.value());
|
||||||
|
}
|
||||||
|
write_uint32_at(counter, location);
|
||||||
|
} break;
|
||||||
|
case simdjson::ondemand::json_type::number:
|
||||||
|
write_double(element.get_double());
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::string:
|
||||||
|
write_raw_string(element.get_raw_json_string());
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::boolean:
|
||||||
|
write_byte(0xc2 + element.get_bool());
|
||||||
|
break;
|
||||||
|
case simdjson::ondemand::json_type::null:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SIMDJSON_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct simdjson_ondemand {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
simdjson2msgpack parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result = parser.to_msgpack(json, reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, simdjson_ondemand)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
struct yyjson2msgpack {
|
||||||
|
inline std::string_view to_msgpack(yyjson_doc *doc, uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_string(const char *s, size_t length) noexcept;
|
||||||
|
inline void recursive_processor(yyjson_val *obj);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view yyjson2msgpack::to_msgpack(yyjson_doc *doc, uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||||
|
recursive_processor(root);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void yyjson2msgpack::write_string(const char *c, size_t len) noexcept {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(len));
|
||||||
|
::memcpy(buff, c, len);
|
||||||
|
buff += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void yyjson2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void yyjson2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void yyjson2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void yyjson2msgpack::recursive_processor(yyjson_val *obj) {
|
||||||
|
size_t idx, max;
|
||||||
|
yyjson_val *val;
|
||||||
|
yyjson_val *key;
|
||||||
|
switch (yyjson_get_type(obj)) {
|
||||||
|
case YYJSON_TYPE_STR:
|
||||||
|
write_string(yyjson_get_str(obj), yyjson_get_len(obj));
|
||||||
|
break;
|
||||||
|
case YYJSON_TYPE_ARR:
|
||||||
|
write_byte(0xdf);
|
||||||
|
write_uint32(uint32_t(yyjson_arr_size(obj)));
|
||||||
|
yyjson_arr_foreach(obj, idx, max, val) { recursive_processor(val); }
|
||||||
|
break;
|
||||||
|
case YYJSON_TYPE_OBJ:
|
||||||
|
write_byte(0xdd);
|
||||||
|
write_uint32(uint32_t(yyjson_obj_size(obj)));
|
||||||
|
yyjson_obj_foreach(obj, idx, max, key, val) {
|
||||||
|
write_string(yyjson_get_str(key), yyjson_get_len(key));
|
||||||
|
recursive_processor(val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case YYJSON_TYPE_BOOL:
|
||||||
|
write_byte(0xc2 + yyjson_get_bool(obj));
|
||||||
|
break;
|
||||||
|
case YYJSON_TYPE_NULL:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
case YYJSON_TYPE_NUM:
|
||||||
|
switch (yyjson_get_subtype(obj)) {
|
||||||
|
case YYJSON_SUBTYPE_UINT:
|
||||||
|
write_double(double(yyjson_get_uint(obj)));
|
||||||
|
break;
|
||||||
|
case YYJSON_SUBTYPE_SINT:
|
||||||
|
write_double(double(yyjson_get_sint(obj)));
|
||||||
|
break;
|
||||||
|
case YYJSON_SUBTYPE_REAL:
|
||||||
|
write_double(yyjson_get_real(obj));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SIMDJSON_UNREACHABLE();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SIMDJSON_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct yyjson : yyjson2msgpack {
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||||
|
result = to_msgpack(doc, reinterpret_cast<uint8_t*>(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, yyjson)->UseManualTime();
|
||||||
|
|
||||||
|
struct yyjson_insitu : yyjson2msgpack {
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
yyjson_doc *doc =
|
||||||
|
yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||||
|
result = to_msgpack(doc, reinterpret_cast<uint8_t*>(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, yyjson_insitu)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||||
@@ -42,7 +42,7 @@ struct nlohmann_json_sax {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool number_unsigned(number_unsigned_t val) override { // Need this event because coordinate value can be equal to 1
|
bool number_unsigned(number_unsigned_t val) override { // Need this event because coordinate value can be equal to 1
|
||||||
buffer[k] = val;
|
buffer[k] = double(val);
|
||||||
if (k == 2) {
|
if (k == 2) {
|
||||||
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
|
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
|
||||||
k = 0;
|
k = 0;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ BENCHMARK_TEMPLATE(kostya, rapidjson_lossless)->UseManualTime();
|
|||||||
|
|
||||||
struct rapidjson_insitu : rapidjson_base {
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||||
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(kostya, rapidjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(kostya, rapidjson_insitu)->UseManualTime();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ struct sajson {
|
|||||||
|
|
||||||
size_t ast_buffer_size{0};
|
size_t ast_buffer_size{0};
|
||||||
size_t *ast_buffer{nullptr};
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
simdjson_really_inline double get_double(const ::sajson::value &obj, std::string_view key) {
|
simdjson_really_inline double get_double(const ::sajson::value &obj, std::string_view key) {
|
||||||
using namespace sajson;
|
using namespace sajson;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ struct nlohmann_json_sax {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool number_unsigned(number_unsigned_t val) override {
|
bool number_unsigned(number_unsigned_t val) override {
|
||||||
buffer[k] = val;
|
buffer[k] = double(val);
|
||||||
if (k == 2) {
|
if (k == 2) {
|
||||||
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
|
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
|
||||||
k = 0;
|
k = 0;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ BENCHMARK_TEMPLATE(large_random, rapidjson_lossless)->UseManualTime();
|
|||||||
|
|
||||||
struct rapidjson_insitu : rapidjson_base {
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||||
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(large_random, rapidjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(large_random, rapidjson_insitu)->UseManualTime();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ struct sajson {
|
|||||||
|
|
||||||
size_t ast_buffer_size{0};
|
size_t ast_buffer_size{0};
|
||||||
size_t *ast_buffer{nullptr};
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
simdjson_really_inline double get_double(const ::sajson::value &obj, std::string_view key) {
|
simdjson_really_inline double get_double(const ::sajson::value &obj, std::string_view key) {
|
||||||
using namespace sajson;
|
using namespace sajson;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ BENCHMARK_TEMPLATE(partial_tweets, rapidjson)->UseManualTime();
|
|||||||
|
|
||||||
struct rapidjson_insitu : rapidjson_base {
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||||
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(partial_tweets, rapidjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(partial_tweets, rapidjson_insitu)->UseManualTime();
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ struct sajson {
|
|||||||
|
|
||||||
size_t ast_buffer_size{0};
|
size_t ast_buffer_size{0};
|
||||||
size_t *ast_buffer{nullptr};
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
||||||
auto val = obj.get_value_of_key({key.data(), key.length()});
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ struct nlohmann_json_sax {
|
|||||||
}
|
}
|
||||||
bool number_unsigned(number_unsigned_t val) override {
|
bool number_unsigned(number_unsigned_t val) override {
|
||||||
if (values & key_rt && !(values & found_rt)) { // retweet_count
|
if (values & key_rt && !(values & found_rt)) { // retweet_count
|
||||||
rt = val;
|
rt = int(val);
|
||||||
values &= ~(key_rt);
|
values &= ~(key_rt);
|
||||||
values |= (found_rt);
|
values |= (found_rt);
|
||||||
if (rt <= max_rt && rt >= result.retweet_count) { // Check if current tweet has more retweet than previous top tweet
|
if (rt <= max_rt && rt >= result.retweet_count) { // Check if current tweet has more retweet than previous top tweet
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ BENCHMARK_TEMPLATE(top_tweet, rapidjson)->UseManualTime();
|
|||||||
|
|
||||||
struct rapidjson_insitu : rapidjson_base {
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
|
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
|
||||||
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), max_retweet_count, result);
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), max_retweet_count, result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(top_tweet, rapidjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(top_tweet, rapidjson_insitu)->UseManualTime();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ struct sajson {
|
|||||||
|
|
||||||
size_t ast_buffer_size{0};
|
size_t ast_buffer_size{0};
|
||||||
size_t *ast_buffer{nullptr};
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
bool run(simdjson::padded_string &json, int32_t max_retweet_count, top_tweet_result<StringType> &result) {
|
bool run(simdjson::padded_string &json, int32_t max_retweet_count, top_tweet_result<StringType> &result) {
|
||||||
if (!ast_buffer) {
|
if (!ast_buffer) {
|
||||||
|
|||||||
+66
-25
@@ -3,30 +3,35 @@ The Basics
|
|||||||
|
|
||||||
An overview of what you need to know to use simdjson, with examples.
|
An overview of what you need to know to use simdjson, with examples.
|
||||||
|
|
||||||
* [Requirements](#requirements)
|
- [The Basics](#the-basics)
|
||||||
* [Including simdjson](#including-simdjson)
|
- [Requirements](#requirements)
|
||||||
* [Using simdjson with package managers](#using-simdjson-with-package-managers)
|
- [Including simdjson](#including-simdjson)
|
||||||
* [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
|
- [Using simdjson with package managers](#using-simdjson-with-package-managers)
|
||||||
* [Versions](#versions)
|
- [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
|
||||||
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
|
- [Versions](#versions)
|
||||||
* [Documents are Iterators](#documents-are-iterators)
|
- [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
|
||||||
* [C++11 Support and string_view](#c11-support-and-string_view)
|
- [Documents are Iterators](#documents-are-iterators)
|
||||||
* [Using the Parsed JSON](#using-the-parsed-json)
|
- [Parser, Document and JSON Scope](#parser-document-and-json-scope)
|
||||||
* [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
- [C++11 Support and string_view](#c11-support-and-string_view)
|
||||||
* [UTF-8 validation (alone)](#utf-8-validation-alone)
|
- [Using the Parsed JSON](#using-the-parsed-json)
|
||||||
* [JSON Pointer](#json-pointer)
|
- [Using the Parsed JSON: Additional examples](#using-the-parsed-json-additional-examples)
|
||||||
* [Error Handling](#error-handling)
|
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
||||||
* [Error Handling Example without Exceptions](#error-handling-examples-without-exceptions)
|
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||||
* [Disabling Exceptions](#disabling-exceptions)
|
- [JSON Pointer](#json-pointer)
|
||||||
* [Exceptions](#exceptions)
|
- [Error Handling](#error-handling)
|
||||||
* [Current location in document](#current-location-in-document)
|
- [Error Handling Examples without Exceptions](#error-handling-examples-without-exceptions)
|
||||||
* [Rewinding](#rewinding)
|
- [Disabling Exceptions](#disabling-exceptions)
|
||||||
* [Direct Access to the Raw String](#direct-access-to-the-raw-string)
|
- [Exceptions](#exceptions)
|
||||||
* [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
- [Current location in document](#current-location-in-document)
|
||||||
* [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings)
|
- [Rewinding](#rewinding)
|
||||||
* [Dynamic Number Types](#dynamic-number-types)
|
- [Direct Access to the Raw String](#direct-access-to-the-raw-string)
|
||||||
* [Thread Safety](#thread-safety)
|
- [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
||||||
* [Standard Compliance](#standard-compliance)
|
- [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings)
|
||||||
|
- [Dynamic Number Types](#dynamic-number-types)
|
||||||
|
- [Raw Strings](#raw-strings)
|
||||||
|
- [Thread Safety](#thread-safety)
|
||||||
|
- [Standard Compliance](#standard-compliance)
|
||||||
|
- [Backwards Compatibility](#backwards-compatibility)
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
@@ -467,7 +472,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
```
|
```
|
||||||
* **Tree Walking and JSON Element Types:** Sometimes you don't necessarily have a document
|
* **Tree Walking and JSON Element Types:** Sometimes you don't necessarily have a document
|
||||||
with a known type, and are trying to generically inspect or walk over JSON elements. To do that, you can use iterators and the `type()` method. You can also represent arbitrary JSON values with
|
with a known type, and are trying to generically inspect or walk over JSON elements. To do that, you can use iterators and the `type()` method. You can also represent arbitrary JSON values with
|
||||||
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`.
|
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`. You may also access [raw strings](#raw-strings).
|
||||||
For example, the following is a quick and dirty recursive function that verbosely prints the JSON document as JSON. This example also illustrates lifecycle requirements: the `document` instance holds the iterator. The document must remain in scope while you are accessing instances of `value`, `object` and `array`.
|
For example, the following is a quick and dirty recursive function that verbosely prints the JSON document as JSON. This example also illustrates lifecycle requirements: the `document` instance holds the iterator. The document must remain in scope while you are accessing instances of `value`, `object` and `array`.
|
||||||
```c++
|
```c++
|
||||||
void recursive_print_json(ondemand::value element) {
|
void recursive_print_json(ondemand::value element) {
|
||||||
@@ -1445,6 +1450,42 @@ It will output:
|
|||||||
9999999999999999999 negative: 0 is_integer: 1 large 64-bit integer: 9999999999999999999 large 64-bit integer: 9999999999999999999
|
9999999999999999999 negative: 0 is_integer: 1 large 64-bit integer: 9999999999999999999 large 64-bit integer: 9999999999999999999
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Raw Strings
|
||||||
|
-----------
|
||||||
|
|
||||||
|
It is sometimes useful to have access to a raw (unescaped) string: we make available a
|
||||||
|
minimalist `raw_json_string` data type which contains a pointer inside the string in the
|
||||||
|
original document, right after the quote. It is accessible via `get_raw_json_string()` on a
|
||||||
|
string instance and returned by the `key()` method on an object's field instance. It is always
|
||||||
|
optional: replacing `get_raw_json_string()` with `get_string()` and `key()` by
|
||||||
|
`unescaped_key()` returns an `string_view` instance of the unescaped string.
|
||||||
|
|
||||||
|
You can quickly compare a `raw_json_string` instance with a target string. You may also
|
||||||
|
unescape the `raw_json_string` on your own string buffer: `parser.unescape(mystr, ptr)`
|
||||||
|
advances the provided pointer `ptr` and returns a string_view instance on the newly serialized
|
||||||
|
string upon success, otherwise it returns an error. When unescaping to your own string buffer,
|
||||||
|
you should ensure that you have sufficient memory space: the total size of the strings plus
|
||||||
|
`simdjson::SIMDJSON_PADDING` bytes. The following example illustrates how we can unescape
|
||||||
|
JSON string to a user-provided buffer:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
auto json = R"( {"name": "Jack The Ripper \u0033"} )"_padded;
|
||||||
|
// We create a buffer large enough to store all strings we need:
|
||||||
|
std::unique_ptr<uint8_t[]> buffer(new uint8_t[json.size() + simdjson::SIMDJSON_PADDING]);
|
||||||
|
uint8_t * ptr = buffer.get();
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
// We store our strings as 'string_view' instances in a vector:
|
||||||
|
std::vector<std::string_view> mystrings;
|
||||||
|
for (auto key_value : doc.get_object()) {
|
||||||
|
std::string_view keysv = parser.unescape(key_value.key(), ptr);// writes 'name'
|
||||||
|
mystrings.push_back(keysv);
|
||||||
|
std::string_view valuesv = parser.unescape(key_value.value().get_raw_json_string(), ptr);
|
||||||
|
// writes 'Jack The Ripper 3', escaping the \u0033
|
||||||
|
mystrings.push_back(valuesv);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Thread Safety
|
Thread Safety
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ different version of the JSON parser for different CPU architectures, often with
|
|||||||
algorithms to take better advantage of a given CPU!
|
algorithms to take better advantage of a given CPU!
|
||||||
|
|
||||||
The current implementations are:
|
The current implementations are:
|
||||||
|
* icelake: AVX-512F, AVX-512VBMI, etc.
|
||||||
* haswell: AVX2 (2013 Intel Haswell or later)
|
* haswell: AVX2 (2013 Intel Haswell or later)
|
||||||
* westmere: SSE4.2 (2010 Westmere or later).
|
* westmere: SSE4.2 (2010 Westmere or later).
|
||||||
* arm64: 64-bit ARMv8-A NEON
|
* arm64: 64-bit ARMv8-A NEON
|
||||||
@@ -28,7 +29,7 @@ compiles *all* the implementations into the executable. On Intel, it will includ
|
|||||||
(haswell, westmere and fallback), on ARM it will include 2 (arm64 and fallback), and on PPC it will include 2 (ppc64 and fallback).
|
(haswell, westmere and fallback), on ARM it will include 2 (arm64 and fallback), and on PPC it will include 2 (ppc64 and fallback).
|
||||||
|
|
||||||
If you know more about where you're going to run and want to save the space, you can disable any of
|
If you know more about where you're going to run and want to save the space, you can disable any of
|
||||||
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is HASWELL,
|
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
|
||||||
WESTMERE, ARM64, PPC64 and FALLBACK).
|
WESTMERE, ARM64, PPC64 and FALLBACK).
|
||||||
|
|
||||||
The simdjson library automatically sets header flags for each implementation as it compiles; there
|
The simdjson library automatically sets header flags for each implementation as it compiles; there
|
||||||
|
|||||||
@@ -143,13 +143,6 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
|
|||||||
*/
|
*/
|
||||||
simdjson_really_inline error_code get(T &value) && noexcept;
|
simdjson_really_inline error_code get(T &value) && noexcept;
|
||||||
|
|
||||||
/**
|
|
||||||
* Move the value to the provided variable.
|
|
||||||
*
|
|
||||||
* @param value The variable to assign the value to. May not be set if there is an error.
|
|
||||||
*/
|
|
||||||
simdjson_really_inline const T &value(error_code &error) const & noexcept;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error.
|
* The error.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public:
|
|||||||
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
|
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
|
||||||
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
|
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
|
||||||
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
|
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
|
||||||
|
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final;
|
||||||
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
|
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
|
||||||
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
|
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ simdjson_really_inline simdjson_result<std::string_view> array::raw_json() noexc
|
|||||||
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
|
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
|
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
|
||||||
simdjson_really_inline simdjson_result<size_t> array::count_elements() & noexcept {
|
simdjson_really_inline simdjson_result<size_t> array::count_elements() & noexcept {
|
||||||
size_t count{0};
|
size_t count{0};
|
||||||
@@ -101,6 +102,7 @@ simdjson_really_inline simdjson_result<size_t> array::count_elements() & noexcep
|
|||||||
iter.reset_array();
|
iter.reset_array();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
simdjson_really_inline simdjson_result<bool> array::is_empty() & noexcept {
|
simdjson_really_inline simdjson_result<bool> array::is_empty() & noexcept {
|
||||||
bool is_not_empty;
|
bool is_not_empty;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ simdjson_really_inline simdjson_result<field> field::start(const value_iterator
|
|||||||
|
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key() noexcept {
|
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key() noexcept {
|
||||||
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
|
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
|
||||||
simdjson_result<std::string_view> answer = first.unescape(second.iter.string_buf_loc());
|
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter());
|
||||||
first.consume();
|
first.consume();
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,6 +312,10 @@ simdjson_really_inline token_position json_iterator::position() const noexcept {
|
|||||||
return token.position();
|
return token.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_really_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in) noexcept {
|
||||||
|
return parser->unescape(in, _string_buf_loc);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_really_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
|
simdjson_really_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
|
||||||
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
|
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
|
||||||
SIMDJSON_ASSUME(_depth == child_depth - 1);
|
SIMDJSON_ASSUME(_depth == child_depth - 1);
|
||||||
|
|||||||
@@ -233,7 +233,14 @@ public:
|
|||||||
template<int N> simdjson_warn_unused simdjson_really_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t (&tmpbuf)[N]) noexcept;
|
template<int N> simdjson_warn_unused simdjson_really_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t (&tmpbuf)[N]) noexcept;
|
||||||
|
|
||||||
simdjson_really_inline token_position position() const noexcept;
|
simdjson_really_inline token_position position() const noexcept;
|
||||||
|
/**
|
||||||
|
* Write the raw_json_string to the string buffer and return a string_view.
|
||||||
|
* Each raw_json_string should be unescaped once, or else the string buffer might
|
||||||
|
* overflow.
|
||||||
|
*/
|
||||||
|
simdjson_really_inline simdjson_result<std::string_view> unescape(raw_json_string in) noexcept;
|
||||||
simdjson_really_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
|
simdjson_really_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
|
||||||
|
|
||||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
simdjson_really_inline token_position start_position(depth_t depth) const noexcept;
|
simdjson_really_inline token_position start_position(depth_t depth) const noexcept;
|
||||||
simdjson_really_inline void set_start_position(depth_t depth, token_position position) noexcept;
|
simdjson_really_inline void set_start_position(depth_t depth, token_position position) noexcept;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ simdjson_really_inline bool object_iterator::operator!=(const object_iterator &)
|
|||||||
return iter.is_open();
|
return iter.is_open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
|
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
|
||||||
simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
|
simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
|
||||||
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
|
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
|
||||||
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
|
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
|
||||||
@@ -38,6 +40,7 @@ simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
|
|||||||
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
|
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
//
|
//
|
||||||
// ### Live States
|
// ### Live States
|
||||||
|
|||||||
@@ -117,6 +117,14 @@ simdjson_really_inline void parser::set_max_capacity(size_t max_capacity) noexce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst) const noexcept {
|
||||||
|
uint8_t *end = implementation->parse_string(in.buf, dst);
|
||||||
|
if (!end) { return STRING_ERROR; }
|
||||||
|
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
|
||||||
|
dst = end;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ondemand
|
} // namespace ondemand
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|||||||
@@ -252,6 +252,27 @@ public:
|
|||||||
bool threaded{true};
|
bool threaded{true};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
|
||||||
|
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
|
||||||
|
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
|
||||||
|
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
|
||||||
|
*
|
||||||
|
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
|
||||||
|
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
|
||||||
|
* instead of get_raw_json_string()).
|
||||||
|
*
|
||||||
|
* ## IMPORTANT: string_view lifetime
|
||||||
|
*
|
||||||
|
* The string_view is only valid as long as the bytes in dst.
|
||||||
|
*
|
||||||
|
* @param raw_json_string input
|
||||||
|
* @param dst A pointer to a buffer at least large enough to write this string as well as
|
||||||
|
* an additional SIMDJSON_PADDING bytes.
|
||||||
|
* @return A string_view pointing at the unescaped string in dst
|
||||||
|
* @error STRING_ERROR if escapes are incorrect.
|
||||||
|
*/
|
||||||
|
simdjson_really_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst) const noexcept;
|
||||||
private:
|
private:
|
||||||
/** @private [for benchmarking access] The implementation to use */
|
/** @private [for benchmarking access] The implementation to use */
|
||||||
std::unique_ptr<internal::dom_parser_implementation> implementation{};
|
std::unique_ptr<internal::dom_parser_implementation> implementation{};
|
||||||
|
|||||||
@@ -6,13 +6,7 @@ namespace ondemand {
|
|||||||
simdjson_really_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
|
simdjson_really_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
|
||||||
|
|
||||||
simdjson_really_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast<const char *>(buf); }
|
simdjson_really_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast<const char *>(buf); }
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(uint8_t *&dst) const noexcept {
|
|
||||||
uint8_t *end = stringparsing::parse_string(buf, dst);
|
|
||||||
if (!end) { return STRING_ERROR; }
|
|
||||||
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
|
|
||||||
dst = end;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_really_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
|
simdjson_really_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
|
||||||
size_t pos{0};
|
size_t pos{0};
|
||||||
@@ -150,7 +144,7 @@ simdjson_unused simdjson_really_inline bool operator!=(std::string_view c, const
|
|||||||
|
|
||||||
|
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter) const noexcept {
|
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter) const noexcept {
|
||||||
return unescape(iter.string_buf_loc());
|
return iter.unescape(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -183,10 +177,6 @@ simdjson_really_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IM
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.raw();
|
return first.raw();
|
||||||
}
|
}
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string>::unescape(uint8_t *&dst) const noexcept {
|
|
||||||
if (error()) { return error(); }
|
|
||||||
return first.unescape(dst);
|
|
||||||
}
|
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string>::unescape(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept {
|
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string>::unescape(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.unescape(iter);
|
return first.unescape(iter);
|
||||||
|
|||||||
@@ -17,12 +17,19 @@ class json_iterator;
|
|||||||
*
|
*
|
||||||
* This class is deliberately simplistic and has little functionality. You can
|
* This class is deliberately simplistic and has little functionality. You can
|
||||||
* compare a raw_json_string instance with an unescaped C string, but
|
* compare a raw_json_string instance with an unescaped C string, but
|
||||||
* that is pretty much all you can do.
|
* that is nearly all you can do.
|
||||||
|
*
|
||||||
|
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
|
||||||
|
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
|
||||||
|
* instance. Doing so requires you to have a sufficiently large buffer.
|
||||||
|
*
|
||||||
|
* The raw_json_string instances originate typically from field instance which in turn represent
|
||||||
|
* key-value pairs from object instances. From a field instance, you get the raw_json_string
|
||||||
|
* instance by calling key(). You can, if you want a more usable string_view instance, call
|
||||||
|
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
|
||||||
|
* any other string value, with the value.get_raw_json_string() method. Again, you can get
|
||||||
|
* a more usable string_view instance by calling get_string().
|
||||||
*
|
*
|
||||||
* They originate typically from field instance which in turn represent key-value pairs from
|
|
||||||
* object instances. From a field instance, you get the raw_json_string instance by calling key().
|
|
||||||
* You can, if you want a more usable string_view instance, call the unescaped_key() method
|
|
||||||
* on the field instance.
|
|
||||||
*/
|
*/
|
||||||
class raw_json_string {
|
class raw_json_string {
|
||||||
public:
|
public:
|
||||||
@@ -130,20 +137,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
simdjson_really_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
|
simdjson_really_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
|
|
||||||
*
|
|
||||||
* ## IMPORTANT: string_view lifetime
|
|
||||||
*
|
|
||||||
* The string_view is only valid as long as the bytes in dst.
|
|
||||||
*
|
|
||||||
* @param dst A pointer to a buffer at least large enough to write this string as well as a \0.
|
|
||||||
* dst will be updated to the next unused location (just after the \0 written out at
|
|
||||||
* the end of this string).
|
|
||||||
* @return A string_view pointing at the unescaped string in dst
|
|
||||||
* @error STRING_ERROR if escapes are incorrect.
|
|
||||||
*/
|
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(uint8_t *&dst) const noexcept;
|
|
||||||
/**
|
/**
|
||||||
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
|
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
|
||||||
*
|
*
|
||||||
@@ -158,6 +151,7 @@ private:
|
|||||||
const uint8_t * buf{};
|
const uint8_t * buf{};
|
||||||
friend class object;
|
friend class object;
|
||||||
friend class field;
|
friend class field;
|
||||||
|
friend class parser;
|
||||||
friend struct simdjson_result<raw_json_string>;
|
friend struct simdjson_result<raw_json_string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -188,7 +182,6 @@ public:
|
|||||||
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
|
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
|
||||||
|
|
||||||
simdjson_really_inline simdjson_result<const char *> raw() const noexcept;
|
simdjson_really_inline simdjson_result<const char *> raw() const noexcept;
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(uint8_t *&dst) const noexcept;
|
|
||||||
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept;
|
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
|
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
|
||||||
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
|
||||||
/**
|
/**
|
||||||
* When find_field_unordered_raw is called, we can either be pointing at the
|
* When find_field_unordered_raw is called, we can either be pointing at the
|
||||||
@@ -367,6 +369,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||||||
// never reach this point.
|
// never reach this point.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
|
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
|
||||||
assert_at_next();
|
assert_at_next();
|
||||||
@@ -470,7 +473,7 @@ simdjson_really_inline bool value_iterator::parse_null(const uint8_t *json) cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> value_iterator::get_string() noexcept {
|
simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> value_iterator::get_string() noexcept {
|
||||||
return get_raw_json_string().unescape(_json_iter->string_buf_loc());
|
return get_raw_json_string().unescape(json_iter());
|
||||||
}
|
}
|
||||||
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
|
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
|
||||||
auto json = peek_scalar("string");
|
auto json = peek_scalar("string");
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
// This file contains the common code every implementation uses
|
|
||||||
// It is intended to be included multiple times and compiled multiple times
|
|
||||||
|
|
||||||
namespace simdjson {
|
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
|
||||||
namespace {
|
|
||||||
/// @private
|
|
||||||
namespace stringparsing {
|
|
||||||
|
|
||||||
// begin copypasta
|
|
||||||
// These chars yield themselves: " \ /
|
|
||||||
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
|
||||||
// u not handled in this table as it's complex
|
|
||||||
static const uint8_t escape_map[256] = {
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
|
||||||
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
|
||||||
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
|
||||||
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle a unicode codepoint
|
|
||||||
// write appropriate values into dest
|
|
||||||
// src will advance 6 bytes or 12 bytes
|
|
||||||
// dest will advance a variable amount (return via pointer)
|
|
||||||
// return true if the unicode codepoint was valid
|
|
||||||
// We work in little-endian then swap at write time
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
|
||||||
uint8_t **dst_ptr) {
|
|
||||||
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
|
||||||
// conversion isn't valid; we defer the check for this to inside the
|
|
||||||
// multilingual plane check
|
|
||||||
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
|
||||||
*src_ptr += 6;
|
|
||||||
// check for low surrogate for characters outside the Basic
|
|
||||||
// Multilingual Plane.
|
|
||||||
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
|
||||||
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
|
||||||
|
|
||||||
// if the first code point is invalid we will get here, as we will go past
|
|
||||||
// the check for being outside the Basic Multilingual plane. If we don't
|
|
||||||
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
|
||||||
// this check catches both the case of the first code point being invalid
|
|
||||||
// or the second code point being invalid.
|
|
||||||
if ((code_point | code_point_2) >> 16) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
code_point =
|
|
||||||
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
|
||||||
*src_ptr += 6;
|
|
||||||
}
|
|
||||||
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
|
||||||
*dst_ptr += offset;
|
|
||||||
return offset > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unescape a string from src to dst, stopping at a final unescaped quote. E.g., if src points at 'joe"', then
|
|
||||||
* dst needs to have four free bytes.
|
|
||||||
*/
|
|
||||||
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
|
||||||
while (1) {
|
|
||||||
// Copy the next n bytes, and find the backslash and quote in them.
|
|
||||||
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
|
||||||
// If the next thing is the end quote, copy and return
|
|
||||||
if (bs_quote.has_quote_first()) {
|
|
||||||
// we encountered quotes first. Move dst to point to quotes and exit
|
|
||||||
return dst + bs_quote.quote_index();
|
|
||||||
}
|
|
||||||
if (bs_quote.has_backslash()) {
|
|
||||||
/* find out where the backspace is */
|
|
||||||
auto bs_dist = bs_quote.backslash_index();
|
|
||||||
uint8_t escape_char = src[bs_dist + 1];
|
|
||||||
/* we encountered backslash first. Handle backslash */
|
|
||||||
if (escape_char == 'u') {
|
|
||||||
/* move src/dst up to the start; they will be further adjusted
|
|
||||||
within the unicode codepoint handling code. */
|
|
||||||
src += bs_dist;
|
|
||||||
dst += bs_dist;
|
|
||||||
if (!handle_unicode_codepoint(&src, &dst)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
|
||||||
* write bs_dist+1 characters to output
|
|
||||||
* note this may reach beyond the part of the buffer we've actually
|
|
||||||
* seen. I think this is ok */
|
|
||||||
uint8_t escape_result = escape_map[escape_char];
|
|
||||||
if (escape_result == 0u) {
|
|
||||||
return nullptr; /* bogus escape value is an error */
|
|
||||||
}
|
|
||||||
dst[bs_dist] = escape_result;
|
|
||||||
src += bs_dist + 2;
|
|
||||||
dst += bs_dist + 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* they are the same. Since they can't co-occur, it means we
|
|
||||||
* encountered neither. */
|
|
||||||
src += backslash_and_quote::BYTES_PROCESSED;
|
|
||||||
dst += backslash_and_quote::BYTES_PROCESSED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* can't be reached */
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_unused simdjson_warn_unused simdjson_really_inline error_code parse_string_to_buffer(const uint8_t *src, uint8_t *¤t_string_buf_loc, std::string_view &s) {
|
|
||||||
if (*(src++) != '"') { return STRING_ERROR; }
|
|
||||||
auto end = stringparsing::parse_string(src, current_string_buf_loc);
|
|
||||||
if (!end) { return STRING_ERROR; }
|
|
||||||
s = std::string_view(reinterpret_cast<const char *>(current_string_buf_loc), end-current_string_buf_loc);
|
|
||||||
current_string_buf_loc = end;
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace stringparsing
|
|
||||||
} // unnamed namespace
|
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
|
||||||
} // namespace simdjson
|
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ namespace simdjson {
|
|||||||
* @return true if the string is valid UTF-8.
|
* @return true if the string is valid UTF-8.
|
||||||
*/
|
*/
|
||||||
simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) noexcept;
|
simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) noexcept;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the UTF-8 string.
|
* Validate the UTF-8 string.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -102,6 +102,22 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
|
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*
|
||||||
|
* Overridden by each implementation.
|
||||||
|
*
|
||||||
|
* @param str pointer to the beginning of a valid UTF-8 JSON string, must end with an unescaped quote.
|
||||||
|
* @param dst pointer to a destination buffer, it must point a region in memory of sufficient size.
|
||||||
|
* @return end of the of the written region (exclusive) or nullptr in case of error.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused virtual uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the capacity of this parser.
|
* Change the capacity of this parser.
|
||||||
*
|
*
|
||||||
@@ -163,6 +179,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth) noexcept;
|
simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth) noexcept;
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* The maximum document length this parser supports.
|
* The maximum document length this parser supports.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||||
|
|
||||||
/** The version of simdjson being used (major.minor.revision) */
|
/** The version of simdjson being used (major.minor.revision) */
|
||||||
#define SIMDJSON_VERSION 2.0.4
|
#define SIMDJSON_VERSION 2.1.0
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
enum {
|
enum {
|
||||||
@@ -15,11 +15,11 @@ enum {
|
|||||||
/**
|
/**
|
||||||
* The minor version (major.MINOR.revision) of simdjson being used.
|
* The minor version (major.MINOR.revision) of simdjson being used.
|
||||||
*/
|
*/
|
||||||
SIMDJSON_VERSION_MINOR = 0,
|
SIMDJSON_VERSION_MINOR = 1,
|
||||||
/**
|
/**
|
||||||
* The revision (major.minor.REVISION) of simdjson being used.
|
* The revision (major.minor.REVISION) of simdjson being used.
|
||||||
*/
|
*/
|
||||||
SIMDJSON_VERSION_REVISION = 4
|
SIMDJSON_VERSION_REVISION = 0
|
||||||
};
|
};
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
|
|||||||
+834
-2
@@ -1,4 +1,4 @@
|
|||||||
/* auto-generated on 2022-06-15 15:21:33 -0400. Do not edit! */
|
/* auto-generated on 2022-06-30 10:29:50 -0400. Do not edit! */
|
||||||
/* begin file src/simdjson.cpp */
|
/* begin file src/simdjson.cpp */
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
|
|
||||||
@@ -2793,7 +2793,6 @@ simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, s
|
|||||||
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
|
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
|
||||||
return get_active_implementation()->validate_utf8(buf, len);
|
return get_active_implementation()->validate_utf8(buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
const implementation * builtin_implementation() {
|
const implementation * builtin_implementation() {
|
||||||
static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
|
static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
|
||||||
assert(builtin_impl);
|
assert(builtin_impl);
|
||||||
@@ -4134,6 +4133,141 @@ bool generic_validate_utf8(const char * input, size_t length) {
|
|||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
|
||||||
|
/* begin file src/generic/stage2/stringparsing.h */
|
||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace arm64 {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace arm64
|
||||||
|
} // namespace simdjson
|
||||||
|
/* end file src/generic/stage2/stringparsing.h */
|
||||||
/* begin file src/generic/stage2/tape_builder.h */
|
/* begin file src/generic/stage2/tape_builder.h */
|
||||||
/* begin file src/generic/stage2/json_iterator.h */
|
/* begin file src/generic/stage2/json_iterator.h */
|
||||||
/* begin file src/generic/stage2/logger.h */
|
/* begin file src/generic/stage2/logger.h */
|
||||||
@@ -4973,6 +5107,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return arm64::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
@@ -5462,6 +5600,141 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
/* begin file src/generic/stage2/stringparsing.h */
|
||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace fallback {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace fallback
|
||||||
|
} // namespace simdjson
|
||||||
|
/* end file src/generic/stage2/stringparsing.h */
|
||||||
/* begin file src/generic/stage2/tape_builder.h */
|
/* begin file src/generic/stage2/tape_builder.h */
|
||||||
/* begin file src/generic/stage2/json_iterator.h */
|
/* begin file src/generic/stage2/json_iterator.h */
|
||||||
/* begin file src/generic/stage2/logger.h */
|
/* begin file src/generic/stage2/logger.h */
|
||||||
@@ -6272,6 +6545,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return fallback::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
@@ -7668,6 +7945,141 @@ bool generic_validate_utf8(const char * input, size_t length) {
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
/* begin file src/generic/stage2/stringparsing.h */
|
||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace icelake {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace icelake
|
||||||
|
} // namespace simdjson
|
||||||
|
/* end file src/generic/stage2/stringparsing.h */
|
||||||
/* begin file src/generic/stage2/tape_builder.h */
|
/* begin file src/generic/stage2/tape_builder.h */
|
||||||
/* begin file src/generic/stage2/json_iterator.h */
|
/* begin file src/generic/stage2/json_iterator.h */
|
||||||
/* begin file src/generic/stage2/logger.h */
|
/* begin file src/generic/stage2/logger.h */
|
||||||
@@ -8505,6 +8917,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return icelake::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
@@ -9856,6 +10272,141 @@ bool generic_validate_utf8(const char * input, size_t length) {
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
/* begin file src/generic/stage2/stringparsing.h */
|
||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace haswell {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace haswell
|
||||||
|
} // namespace simdjson
|
||||||
|
/* end file src/generic/stage2/stringparsing.h */
|
||||||
/* begin file src/generic/stage2/tape_builder.h */
|
/* begin file src/generic/stage2/tape_builder.h */
|
||||||
/* begin file src/generic/stage2/json_iterator.h */
|
/* begin file src/generic/stage2/json_iterator.h */
|
||||||
/* begin file src/generic/stage2/logger.h */
|
/* begin file src/generic/stage2/logger.h */
|
||||||
@@ -10693,6 +11244,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return haswell::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
@@ -12007,7 +12562,141 @@ bool generic_validate_utf8(const char * input, size_t length) {
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
/* begin file src/generic/stage2/stringparsing.h */
|
||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace ppc64 {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace ppc64
|
||||||
|
} // namespace simdjson
|
||||||
|
/* end file src/generic/stage2/stringparsing.h */
|
||||||
/* begin file src/generic/stage2/tape_builder.h */
|
/* begin file src/generic/stage2/tape_builder.h */
|
||||||
/* begin file src/generic/stage2/json_iterator.h */
|
/* begin file src/generic/stage2/json_iterator.h */
|
||||||
/* begin file src/generic/stage2/logger.h */
|
/* begin file src/generic/stage2/logger.h */
|
||||||
@@ -12847,6 +13536,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return ppc64::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
@@ -14194,6 +14887,141 @@ bool generic_validate_utf8(const char * input, size_t length) {
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
/* begin file src/generic/stage2/stringparsing.h */
|
||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace westmere {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace westmere
|
||||||
|
} // namespace simdjson
|
||||||
|
/* end file src/generic/stage2/stringparsing.h */
|
||||||
/* begin file src/generic/stage2/tape_builder.h */
|
/* begin file src/generic/stage2/tape_builder.h */
|
||||||
/* begin file src/generic/stage2/json_iterator.h */
|
/* begin file src/generic/stage2/json_iterator.h */
|
||||||
/* begin file src/generic/stage2/logger.h */
|
/* begin file src/generic/stage2/logger.h */
|
||||||
@@ -15032,6 +15860,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return westmere::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
+92
-875
File diff suppressed because it is too large
Load Diff
@@ -109,6 +109,7 @@ simdjson_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t>
|
|||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "generic/stage2/stringparsing.h"
|
||||||
#include "generic/stage2/tape_builder.h"
|
#include "generic/stage2/tape_builder.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -151,6 +152,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return arm64::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
@@ -346,6 +346,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
#include "generic/stage2/stringparsing.h"
|
||||||
#include "generic/stage2/tape_builder.h"
|
#include "generic/stage2/tape_builder.h"
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
@@ -359,6 +360,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return fallback::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
// This file contains the common code every implementation uses
|
||||||
|
// It is intended to be included multiple times and compiled multiple times
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
|
namespace {
|
||||||
|
/// @private
|
||||||
|
namespace stringparsing {
|
||||||
|
|
||||||
|
// begin copypasta
|
||||||
|
// These chars yield themselves: " \ /
|
||||||
|
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
|
||||||
|
// u not handled in this table as it's complex
|
||||||
|
static const uint8_t escape_map[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
|
||||||
|
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
|
||||||
|
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle a unicode codepoint
|
||||||
|
// write appropriate values into dest
|
||||||
|
// src will advance 6 bytes or 12 bytes
|
||||||
|
// dest will advance a variable amount (return via pointer)
|
||||||
|
// return true if the unicode codepoint was valid
|
||||||
|
// We work in little-endian then swap at write time
|
||||||
|
simdjson_warn_unused
|
||||||
|
simdjson_really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
|
||||||
|
uint8_t **dst_ptr) {
|
||||||
|
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
|
||||||
|
// conversion isn't valid; we defer the check for this to inside the
|
||||||
|
// multilingual plane check
|
||||||
|
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
*src_ptr += 6;
|
||||||
|
// check for low surrogate for characters outside the Basic
|
||||||
|
// Multilingual Plane.
|
||||||
|
if (code_point >= 0xd800 && code_point < 0xdc00) {
|
||||||
|
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
|
||||||
|
|
||||||
|
// if the first code point is invalid we will get here, as we will go past
|
||||||
|
// the check for being outside the Basic Multilingual plane. If we don't
|
||||||
|
// find a \u immediately afterwards we fail out anyhow, but if we do,
|
||||||
|
// this check catches both the case of the first code point being invalid
|
||||||
|
// or the second code point being invalid.
|
||||||
|
if ((code_point | code_point_2) >> 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point =
|
||||||
|
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
|
||||||
|
*src_ptr += 6;
|
||||||
|
}
|
||||||
|
size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr);
|
||||||
|
*dst_ptr += offset;
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
|
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
||||||
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
|
* SIMDJSON_PADDING bytes.
|
||||||
|
*/
|
||||||
|
simdjson_warn_unused simdjson_really_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) {
|
||||||
|
while (1) {
|
||||||
|
// Copy the next n bytes, and find the backslash and quote in them.
|
||||||
|
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||||
|
// If the next thing is the end quote, copy and return
|
||||||
|
if (bs_quote.has_quote_first()) {
|
||||||
|
// we encountered quotes first. Move dst to point to quotes and exit
|
||||||
|
return dst + bs_quote.quote_index();
|
||||||
|
}
|
||||||
|
if (bs_quote.has_backslash()) {
|
||||||
|
/* find out where the backspace is */
|
||||||
|
auto bs_dist = bs_quote.backslash_index();
|
||||||
|
uint8_t escape_char = src[bs_dist + 1];
|
||||||
|
/* we encountered backslash first. Handle backslash */
|
||||||
|
if (escape_char == 'u') {
|
||||||
|
/* move src/dst up to the start; they will be further adjusted
|
||||||
|
within the unicode codepoint handling code. */
|
||||||
|
src += bs_dist;
|
||||||
|
dst += bs_dist;
|
||||||
|
if (!handle_unicode_codepoint(&src, &dst)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and
|
||||||
|
* write bs_dist+1 characters to output
|
||||||
|
* note this may reach beyond the part of the buffer we've actually
|
||||||
|
* seen. I think this is ok */
|
||||||
|
uint8_t escape_result = escape_map[escape_char];
|
||||||
|
if (escape_result == 0u) {
|
||||||
|
return nullptr; /* bogus escape value is an error */
|
||||||
|
}
|
||||||
|
dst[bs_dist] = escape_result;
|
||||||
|
src += bs_dist + 2;
|
||||||
|
dst += bs_dist + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* they are the same. Since they can't co-occur, it means we
|
||||||
|
* encountered neither. */
|
||||||
|
src += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* can't be reached */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stringparsing
|
||||||
|
} // unnamed namespace
|
||||||
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
|
} // namespace simdjson
|
||||||
@@ -112,6 +112,7 @@ simdjson_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t>
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
#include "generic/stage2/stringparsing.h"
|
||||||
#include "generic/stage2/tape_builder.h"
|
#include "generic/stage2/tape_builder.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -152,6 +153,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return haswell::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
#include "generic/stage2/stringparsing.h"
|
||||||
#include "generic/stage2/tape_builder.h"
|
#include "generic/stage2/tape_builder.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -198,6 +199,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return icelake::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, s
|
|||||||
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
|
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
|
||||||
return get_active_implementation()->validate_utf8(buf, len);
|
return get_active_implementation()->validate_utf8(buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
const implementation * builtin_implementation() {
|
const implementation * builtin_implementation() {
|
||||||
static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
|
static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
|
||||||
assert(builtin_impl);
|
assert(builtin_impl);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ simdjson_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t>
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
#include "generic/stage2/stringparsing.h"
|
||||||
#include "generic/stage2/tape_builder.h"
|
#include "generic/stage2/tape_builder.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -122,6 +122,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return ppc64::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ simdjson_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t>
|
|||||||
//
|
//
|
||||||
// Stage 2
|
// Stage 2
|
||||||
//
|
//
|
||||||
|
#include "generic/stage2/stringparsing.h"
|
||||||
#include "generic/stage2/tape_builder.h"
|
#include "generic/stage2/tape_builder.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -151,6 +152,10 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
|||||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept {
|
||||||
|
return westmere::stringparsing::parse_string(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ using error_code=simdjson::error_code;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void recursive_print_json(ondemand::value element) {
|
void recursive_print_json(ondemand::value element) {
|
||||||
@@ -792,6 +791,29 @@ bool simple_error_example() {
|
|||||||
|
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
bool raw_string() {
|
||||||
|
TEST_START();
|
||||||
|
auto json = R"( {"name": "Jack The Ripper \u0033"} )"_padded;
|
||||||
|
// We create a buffer large enough to store all strings we need:
|
||||||
|
std::unique_ptr<uint8_t[]> buffer(new uint8_t[json.size() + simdjson::SIMDJSON_PADDING]);
|
||||||
|
uint8_t * ptr = buffer.get();
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
// We store our strings as 'string_view' instances in a vector:
|
||||||
|
std::vector<std::string_view> mystrings;
|
||||||
|
for (auto key_value : doc.get_object()) {
|
||||||
|
std::string_view keysv = parser.unescape(key_value.key(), ptr);// writes 'name'
|
||||||
|
mystrings.push_back(keysv);
|
||||||
|
std::string_view valuesv = parser.unescape(key_value.value().get_raw_json_string(), ptr);
|
||||||
|
// writes 'Jack The Ripper 3', escaping the \u0033
|
||||||
|
mystrings.push_back(valuesv);
|
||||||
|
}
|
||||||
|
ASSERT_EQUAL(mystrings[0],"name");
|
||||||
|
ASSERT_EQUAL(mystrings[1],"Jack The Ripper 3");
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool simple_error_example_except() {
|
bool simple_error_example_except() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
@@ -800,30 +822,34 @@ bool simple_error_example() {
|
|||||||
ondemand::document doc = parser.iterate(json);
|
ondemand::document doc = parser.iterate(json);
|
||||||
double x = doc["bad number"].get_double();
|
double x = doc["bad number"].get_double();
|
||||||
std::cout << "Got " << x << std::endl;
|
std::cout << "Got " << x << std::endl;
|
||||||
return true;
|
TEST_SUCCEED();
|
||||||
} catch(simdjson_error& e) {
|
} catch(simdjson_error& e) {
|
||||||
// e.error() == NUMBER_ERROR
|
// e.error() == NUMBER_ERROR
|
||||||
std::cout << e.error() << std::endl;
|
std::cout << e.error() << std::endl;
|
||||||
return false;
|
TEST_FAIL("I did not expect an exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t current_location_tape_error_with_except() {
|
int64_t current_location_tape_error_with_except() {
|
||||||
|
TEST_START();
|
||||||
auto broken_json = R"( {"double": 13.06, false, "integer": -343} )"_padded;
|
auto broken_json = R"( {"double": 13.06, false, "integer": -343} )"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document doc = parser.iterate(broken_json);
|
ondemand::document doc;
|
||||||
try {
|
try {
|
||||||
|
doc = parser.iterate(broken_json);
|
||||||
return int64_t(doc["integer"]);
|
return int64_t(doc["integer"]);
|
||||||
} catch(simdjson_error& err) {
|
} catch(simdjson_error& err) {
|
||||||
std::cerr << err.error() << std::endl;
|
std::cout << err.error() << std::endl;
|
||||||
std::cerr << doc.current_location() << std::endl;
|
std::cout << doc.current_location() << std::endl;
|
||||||
return -1;
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
TEST_FAIL("I expected an exception!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int load_example() {
|
int load_example() {
|
||||||
|
TEST_START();
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
simdjson::ondemand::document tweets;
|
simdjson::ondemand::document tweets;
|
||||||
padded_string json;
|
padded_string json;
|
||||||
@@ -872,6 +898,7 @@ int example_1() {
|
|||||||
}
|
}
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
int load_example_except() {
|
int load_example_except() {
|
||||||
|
TEST_START();
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
padded_string json = padded_string::load("twitter.json");
|
padded_string json = padded_string::load("twitter.json");
|
||||||
simdjson::ondemand::document tweets = parser.iterate(json);
|
simdjson::ondemand::document tweets = parser.iterate(json);
|
||||||
@@ -958,15 +985,12 @@ bool current_location_no_error() {
|
|||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
bool run() {
|
||||||
#if SIMDJSON_EXCEPTIONS
|
return true
|
||||||
basics_treewalk();
|
|
||||||
basics_treewalk_breakline();
|
|
||||||
#endif
|
|
||||||
if (
|
|
||||||
true
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
// && basics_1() // Fails because twitter.json isn't in current directory. Compile test only.
|
// && basics_1() // Fails because twitter.json isn't in current directory. Compile test only.
|
||||||
|
&& basics_treewalk()
|
||||||
|
&& basics_treewalk_breakline()
|
||||||
&& json_value_with_array_count()
|
&& json_value_with_array_count()
|
||||||
&& json_array_with_array_count()
|
&& json_array_with_array_count()
|
||||||
&& json_array_count_complex()
|
&& json_array_count_complex()
|
||||||
@@ -999,12 +1023,13 @@ int main() {
|
|||||||
&& current_location_out_of_bounds()
|
&& current_location_out_of_bounds()
|
||||||
&& current_location_no_error()
|
&& current_location_no_error()
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
&& raw_string()
|
||||||
&& number_tests()
|
&& number_tests()
|
||||||
&& current_location_tape_error_with_except()
|
&& current_location_tape_error_with_except()
|
||||||
#endif
|
#endif
|
||||||
) {
|
;
|
||||||
return 0;
|
}
|
||||||
} else {
|
|
||||||
return 1;
|
int main(int argc, char *argv[]) {
|
||||||
}
|
return test_main(argc, argv, run);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user