Skip to content

Commit 31ca9cc

Browse files
committed
src: use simdjson
1 parent e665231 commit 31ca9cc

File tree

3 files changed

+119
-114
lines changed

3 files changed

+119
-114
lines changed

src/json_parser.cc

Lines changed: 113 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,165 @@
11
#include "json_parser.h"
2-
#include "node_errors.h"
3-
#include "node_v8_platform-inl.h"
4-
#include "util-inl.h"
2+
#include "debug_utils.h"
3+
#include <cstdio>
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+
// Print error to stderr to match V8 behavior
25+
std::fprintf(stderr, "JSON Parse error: %s\n", error_message_.c_str());
3826
return false;
3927
}
4028

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

38+
parsed_ = true;
4539
return true;
4640
}
4741

4842
std::optional<std::string> JSONParser::GetTopLevelStringField(
4943
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)) {
44+
if (!parsed_) {
6645
return {};
6746
}
68-
if (!content_object->Get(context, field_local).ToLocal(&value) ||
69-
!value->IsString()) {
47+
48+
simdjson::padded_string_view json_view(
49+
json_content_.data(),
50+
json_content_.size() - simdjson::SIMDJSON_PADDING,
51+
json_content_.size());
52+
53+
simdjson::ondemand::document document;
54+
simdjson::error_code error = parser_.iterate(json_view).get(document);
55+
if (error != simdjson::SUCCESS) {
7056
return {};
7157
}
72-
Utf8Value utf8_value(isolate, value);
73-
return utf8_value.ToString();
58+
59+
simdjson::ondemand::object obj;
60+
error = document.get_object().get(obj);
61+
if (error != simdjson::SUCCESS) {
62+
return {};
63+
}
64+
65+
std::string_view result;
66+
error = obj[field].get_string().get(result);
67+
if (error != simdjson::SUCCESS) {
68+
return {};
69+
}
70+
71+
return std::string(result);
7472
}
7573

7674
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)) {
75+
if (!parsed_) {
9376
return {};
9477
}
95-
if (!content_object->Has(context, field_local).To(&has_field)) {
78+
79+
simdjson::padded_string_view json_view(
80+
json_content_.data(),
81+
json_content_.size() - simdjson::SIMDJSON_PADDING,
82+
json_content_.size());
83+
84+
simdjson::ondemand::document document;
85+
simdjson::error_code error = parser_.iterate(json_view).get(document);
86+
if (error != simdjson::SUCCESS) {
9687
return {};
9788
}
98-
if (!has_field) {
89+
90+
simdjson::ondemand::object obj;
91+
error = document.get_object().get(obj);
92+
if (error != simdjson::SUCCESS) {
93+
return {};
94+
}
95+
96+
simdjson::ondemand::value val;
97+
error = obj[field].get(val);
98+
if (error != simdjson::SUCCESS) {
9999
return false;
100100
}
101-
if (!content_object->Get(context, field_local).ToLocal(&value) ||
102-
!value->IsBoolean()) {
101+
102+
bool result;
103+
error = val.get_bool().get(result);
104+
if (error != simdjson::SUCCESS) {
103105
return {};
104106
}
105-
return value->BooleanValue(isolate);
107+
108+
return result;
106109
}
107110

108111
std::optional<JSONParser::StringDict> JSONParser::GetTopLevelStringDict(
109112
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)) {
113+
if (!parsed_) {
123114
return std::nullopt;
124115
}
125-
if (!content_object->Has(context, field_local).To(&has_field)) {
116+
117+
simdjson::padded_string_view json_view(
118+
json_content_.data(),
119+
json_content_.size() - simdjson::SIMDJSON_PADDING,
120+
json_content_.size());
121+
122+
simdjson::ondemand::document document;
123+
simdjson::error_code error = parser_.iterate(json_view).get(document);
124+
if (error != simdjson::SUCCESS) {
126125
return std::nullopt;
127126
}
128-
if (!has_field) {
129-
return StringDict();
130-
}
131-
if (!content_object->Get(context, field_local).ToLocal(&value) ||
132-
!value->IsObject()) {
127+
128+
simdjson::ondemand::object obj;
129+
error = document.get_object().get(obj);
130+
if (error != simdjson::SUCCESS) {
133131
return std::nullopt;
134132
}
135-
Local<Object> dict = value.As<Object>();
136-
Local<Array> keys;
137-
if (!dict->GetOwnPropertyNames(context).ToLocal(&keys)) {
133+
134+
simdjson::ondemand::value val;
135+
error = obj[field].get(val);
136+
if (error != simdjson::SUCCESS) {
137+
return StringDict();
138+
}
139+
140+
simdjson::ondemand::object dict;
141+
error = val.get_object().get(dict);
142+
if (error != simdjson::SUCCESS) {
138143
return std::nullopt;
139144
}
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())
145+
146+
StringDict result;
147+
for (auto field_value : dict) {
148+
std::string_view key_view;
149+
error = field_value.unescaped_key().get(key_view);
150+
if (error != simdjson::SUCCESS) {
146151
return StringDict();
147-
if (!dict->Get(context, key).ToLocal(&value) || !value->IsString())
152+
}
153+
154+
std::string_view value_view;
155+
error = field_value.value().get_string().get(value_view);
156+
if (error != simdjson::SUCCESS) {
148157
return StringDict();
158+
}
149159

150-
Utf8Value key_utf8(isolate, key);
151-
Utf8Value value_utf8(isolate, value);
152-
result.emplace(*key_utf8, *value_utf8);
160+
result.emplace(std::string(key_view), std::string(value_view));
153161
}
162+
154163
return result;
155164
}
156165

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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ const assert = require('assert');
5151
cwd: tmpdir.path,
5252
});
5353
const stderr = child.stderr.toString();
54-
assert.strictEqual(child.status, 1);
55-
assert.match(stderr, /SyntaxError: Expected ':' after property name/);
54+
assert.match(stderr, /JSON Parse error: INCOMPLETE_ARRAY_OR_OBJECT/);
5655
assert(
5756
stderr.includes(
5857
`Cannot parse JSON from ${config}`

0 commit comments

Comments
 (0)