Skip to content

Commit 111b1c2

Browse files
committed
src: use simdjson for json parser
1 parent e665231 commit 111b1c2

File tree

3 files changed

+117
-115
lines changed

3 files changed

+117
-115
lines changed

src/json_parser.cc

Lines changed: 111 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,163 @@
11
#include "json_parser.h"
2-
#include "node_errors.h"
3-
#include "node_v8_platform-inl.h"
4-
#include "util-inl.h"
2+
#include <cstdio>
3+
#include "debug_utils.h"
54

65
namespace node {
7-
using v8::Array;
8-
using v8::Context;
9-
using v8::Isolate;
10-
using v8::Local;
11-
using v8::Object;
12-
using v8::String;
13-
using v8::Value;
146

157
JSONParser::JSONParser() {}
168

179
bool JSONParser::Parse(const std::string& content) {
1810
DCHECK(!parsed_);
1911

20-
Isolate* isolate = isolate_.get();
21-
v8::Locker locker(isolate);
22-
v8::Isolate::Scope isolate_scope(isolate);
23-
v8::HandleScope handle_scope(isolate);
24-
25-
Local<Context> context = Context::New(isolate);
26-
Context::Scope context_scope(context);
27-
28-
// It's not a real script, so don't print the source line.
29-
errors::PrinterTryCatch bootstrapCatch(
30-
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
31-
Local<Value> json_string_value;
32-
Local<Value> result_value;
33-
if (!ToV8Value(context, content).ToLocal(&json_string_value) ||
34-
!json_string_value->IsString() ||
35-
!v8::JSON::Parse(context, json_string_value.As<String>())
36-
.ToLocal(&result_value) ||
37-
!result_value->IsObject()) {
12+
json_content_ = content;
13+
size_t json_length = json_content_.size();
14+
json_content_.append(simdjson::SIMDJSON_PADDING, ' ');
15+
16+
simdjson::padded_string_view json_view(
17+
json_content_.data(), json_length, json_content_.size());
18+
19+
simdjson::ondemand::document document;
20+
simdjson::error_code error = parser_.iterate(json_view).get(document);
21+
22+
if (error != simdjson::SUCCESS) {
23+
error_message_ = simdjson::error_message(error);
24+
std::fprintf(stderr, "%s\n", error_message_.c_str());
3825
return false;
3926
}
4027

41-
context_.Reset(isolate, context);
42-
content_.Reset(isolate, result_value.As<Object>());
43-
parsed_ = true;
28+
simdjson::ondemand::object obj;
29+
error = document.get_object().get(obj);
30+
if (error != simdjson::SUCCESS) {
31+
error_message_ = simdjson::error_message(error);
32+
std::fprintf(stderr, "%s\n", error_message_.c_str());
33+
return false;
34+
}
4435

36+
parsed_ = true;
4537
return true;
4638
}
4739

4840
std::optional<std::string> JSONParser::GetTopLevelStringField(
4941
std::string_view field) {
50-
Isolate* isolate = isolate_.get();
51-
v8::Locker locker(isolate);
52-
v8::Isolate::Scope isolate_scope(isolate);
53-
v8::HandleScope handle_scope(isolate);
54-
55-
Local<Context> context = context_.Get(isolate);
56-
Context::Scope context_scope(context);
57-
58-
Local<Object> content_object = content_.Get(isolate);
59-
60-
Local<Value> value;
61-
// It's not a real script, so don't print the source line.
62-
errors::PrinterTryCatch bootstrapCatch(
63-
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
64-
Local<Value> field_local;
65-
if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) {
42+
if (!parsed_) {
6643
return {};
6744
}
68-
if (!content_object->Get(context, field_local).ToLocal(&value) ||
69-
!value->IsString()) {
45+
46+
simdjson::padded_string_view json_view(
47+
json_content_.data(),
48+
json_content_.size() - simdjson::SIMDJSON_PADDING,
49+
json_content_.size());
50+
51+
simdjson::ondemand::document document;
52+
simdjson::error_code error = parser_.iterate(json_view).get(document);
53+
if (error != simdjson::SUCCESS) {
7054
return {};
7155
}
72-
Utf8Value utf8_value(isolate, value);
73-
return utf8_value.ToString();
56+
57+
simdjson::ondemand::object obj;
58+
error = document.get_object().get(obj);
59+
if (error != simdjson::SUCCESS) {
60+
return {};
61+
}
62+
63+
std::string_view result;
64+
error = obj[field].get_string().get(result);
65+
if (error != simdjson::SUCCESS) {
66+
return {};
67+
}
68+
69+
return std::string(result);
7470
}
7571

7672
std::optional<bool> JSONParser::GetTopLevelBoolField(std::string_view field) {
77-
Isolate* isolate = isolate_.get();
78-
v8::Locker locker(isolate);
79-
v8::Isolate::Scope isolate_scope(isolate);
80-
v8::HandleScope handle_scope(isolate);
81-
82-
Local<Context> context = context_.Get(isolate);
83-
Context::Scope context_scope(context);
84-
85-
Local<Object> content_object = content_.Get(isolate);
86-
Local<Value> value;
87-
bool has_field;
88-
// It's not a real script, so don't print the source line.
89-
errors::PrinterTryCatch bootstrapCatch(
90-
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
91-
Local<Value> field_local;
92-
if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) {
73+
if (!parsed_) {
9374
return {};
9475
}
95-
if (!content_object->Has(context, field_local).To(&has_field)) {
76+
77+
simdjson::padded_string_view json_view(
78+
json_content_.data(),
79+
json_content_.size() - simdjson::SIMDJSON_PADDING,
80+
json_content_.size());
81+
82+
simdjson::ondemand::document document;
83+
simdjson::error_code error = parser_.iterate(json_view).get(document);
84+
if (error != simdjson::SUCCESS) {
9685
return {};
9786
}
98-
if (!has_field) {
87+
88+
simdjson::ondemand::object obj;
89+
error = document.get_object().get(obj);
90+
if (error != simdjson::SUCCESS) {
91+
return {};
92+
}
93+
94+
simdjson::ondemand::value val;
95+
error = obj[field].get(val);
96+
if (error != simdjson::SUCCESS) {
9997
return false;
10098
}
101-
if (!content_object->Get(context, field_local).ToLocal(&value) ||
102-
!value->IsBoolean()) {
99+
100+
bool result;
101+
error = val.get_bool().get(result);
102+
if (error != simdjson::SUCCESS) {
103103
return {};
104104
}
105-
return value->BooleanValue(isolate);
105+
106+
return result;
106107
}
107108

108109
std::optional<JSONParser::StringDict> JSONParser::GetTopLevelStringDict(
109110
std::string_view field) {
110-
Isolate* isolate = isolate_.get();
111-
v8::Locker locker(isolate);
112-
v8::Isolate::Scope isolate_scope(isolate);
113-
v8::HandleScope handle_scope(isolate);
114-
Local<Context> context = context_.Get(isolate);
115-
Local<Object> content_object = content_.Get(isolate);
116-
Local<Value> value;
117-
bool has_field;
118-
// It's not a real script, so don't print the source line.
119-
errors::PrinterTryCatch bootstrapCatch(
120-
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
121-
Local<Value> field_local;
122-
if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) {
111+
if (!parsed_) {
123112
return std::nullopt;
124113
}
125-
if (!content_object->Has(context, field_local).To(&has_field)) {
114+
115+
simdjson::padded_string_view json_view(
116+
json_content_.data(),
117+
json_content_.size() - simdjson::SIMDJSON_PADDING,
118+
json_content_.size());
119+
120+
simdjson::ondemand::document document;
121+
simdjson::error_code error = parser_.iterate(json_view).get(document);
122+
if (error != simdjson::SUCCESS) {
126123
return std::nullopt;
127124
}
128-
if (!has_field) {
129-
return StringDict();
130-
}
131-
if (!content_object->Get(context, field_local).ToLocal(&value) ||
132-
!value->IsObject()) {
125+
126+
simdjson::ondemand::object obj;
127+
error = document.get_object().get(obj);
128+
if (error != simdjson::SUCCESS) {
133129
return std::nullopt;
134130
}
135-
Local<Object> dict = value.As<Object>();
136-
Local<Array> keys;
137-
if (!dict->GetOwnPropertyNames(context).ToLocal(&keys)) {
131+
132+
simdjson::ondemand::value val;
133+
error = obj[field].get(val);
134+
if (error != simdjson::SUCCESS) {
135+
return StringDict();
136+
}
137+
138+
simdjson::ondemand::object dict;
139+
error = val.get_object().get(dict);
140+
if (error != simdjson::SUCCESS) {
138141
return std::nullopt;
139142
}
140-
std::unordered_map<std::string, std::string> result;
141-
uint32_t length = keys->Length();
142-
for (uint32_t i = 0; i < length; ++i) {
143-
Local<Value> key;
144-
Local<Value> value;
145-
if (!keys->Get(context, i).ToLocal(&key) || !key->IsString())
143+
144+
StringDict result;
145+
for (auto field_value : dict) {
146+
std::string_view key_view;
147+
error = field_value.unescaped_key().get(key_view);
148+
if (error != simdjson::SUCCESS) {
146149
return StringDict();
147-
if (!dict->Get(context, key).ToLocal(&value) || !value->IsString())
150+
}
151+
152+
std::string_view value_view;
153+
error = field_value.value().get_string().get(value_view);
154+
if (error != simdjson::SUCCESS) {
148155
return StringDict();
156+
}
149157

150-
Utf8Value key_utf8(isolate, key);
151-
Utf8Value value_utf8(isolate, value);
152-
result.emplace(*key_utf8, *value_utf8);
158+
result.emplace(std::string(key_view), std::string(value_view));
153159
}
160+
154161
return result;
155162
}
156163

src/json_parser.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
#include <optional>
88
#include <string>
99
#include <unordered_map>
10-
#include "util.h"
11-
#include "v8.h"
10+
#include "simdjson.h"
1211

1312
namespace node {
1413
// This is intended to be used to get some top-level fields out of a JSON
@@ -23,14 +22,12 @@ class JSONParser {
2322
std::optional<std::string> GetTopLevelStringField(std::string_view field);
2423
std::optional<bool> GetTopLevelBoolField(std::string_view field);
2524
std::optional<StringDict> GetTopLevelStringDict(std::string_view field);
25+
std::string GetErrorMessage() const { return error_message_; }
2626

2727
private:
28-
// We might want a lighter-weight JSON parser for this use case. But for now
29-
// using V8 is good enough.
30-
RAIIIsolateWithoutEntering isolate_;
31-
32-
v8::Global<v8::Context> context_;
33-
v8::Global<v8::Object> content_;
28+
simdjson::ondemand::parser parser_;
29+
std::string json_content_;
30+
std::string error_message_;
3431
bool parsed_ = false;
3532
};
3633
} // namespace node

test/parallel/test-single-executable-blob-config-errors.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ const assert = require('assert');
5050
['--experimental-sea-config', config], {
5151
cwd: tmpdir.path,
5252
});
53-
const stderr = child.stderr.toString();
54-
assert.strictEqual(child.status, 1);
55-
assert.match(stderr, /SyntaxError: Expected ':' after property name/);
53+
const stderr = child.stderr.toString(); assert.match(stderr, /INCOMPLETE_ARRAY_OR_OBJECT/);
5654
assert(
5755
stderr.includes(
5856
`Cannot parse JSON from ${config}`

0 commit comments

Comments
 (0)