Skip to content

Commit cdab6fe

Browse files
committed
src: implement initial simdjson parser
1 parent 8e41b8d commit cdab6fe

File tree

8 files changed

+393
-0
lines changed

8 files changed

+393
-0
lines changed

lib/internal/bootstrap/realm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ const schemelessBlockList = new SafeSet([
129129
'quic',
130130
'test',
131131
'test/reporters',
132+
'json',
132133
]);
133134
// Modules that will only be enabled at run time.
134135
const experimentalModuleList = new SafeSet(['sqlite', 'quic']);

lib/json.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
module.exports = internalBinding('json_parser');

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
'src/node_http_parser.cc',
128128
'src/node_http2.cc',
129129
'src/node_i18n.cc',
130+
'src/node_json_parser.cc',
130131
'src/node_locks.cc',
131132
'src/node_main_instance.cc',
132133
'src/node_messaging.cc',
@@ -261,6 +262,7 @@
261262
'src/node_http2_state.h',
262263
'src/node_i18n.h',
263264
'src/node_internals.h',
265+
'src/node_json_parser.h',
264266
'src/node_locks.h',
265267
'src/node_main_instance.h',
266268
'src/node_mem.h',

src/node_binding.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
V(http2) \
5858
V(http_parser) \
5959
V(inspector) \
60+
V(json_parser) \
6061
V(internal_only_v8) \
6162
V(js_stream) \
6263
V(js_udp_wrap) \

src/node_external_reference.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class ExternalReferenceRegistry {
8080
V(heap_utils) \
8181
V(http_parser) \
8282
V(internal_only_v8) \
83+
V(json_parser) \
8384
V(locks) \
8485
V(messaging) \
8586
V(mksnapshot) \

src/node_json_parser.cc

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
#include "node_json_parser.h"
2+
#include "node_errors.h"
3+
#include "node_external_reference.h"
4+
#include "simdjson.h"
5+
#include "env-inl.h"
6+
7+
namespace node {
8+
namespace json_parser {
9+
10+
using std::string;
11+
12+
using v8::Array;
13+
using v8::Boolean;
14+
using v8::Context;
15+
using v8::FunctionCallbackInfo;
16+
using v8::Isolate;
17+
using v8::Local;
18+
using v8::MaybeLocal;
19+
using v8::Null;
20+
using v8::Number;
21+
using v8::Object;
22+
using v8::Primitive;
23+
using v8::String;
24+
using v8::Undefined;
25+
using v8::Value;
26+
27+
template <typename T>
28+
inline MaybeLocal<Value> ToV8Number(Isolate* isolate,
29+
simdjson::dom::element element) {
30+
T value;
31+
simdjson::error_code error = std::move(element.get<T>()).get(value);
32+
33+
if (error) {
34+
THROW_ERR_MODULE_NOT_INSTANTIATED(isolate);
35+
return MaybeLocal<Value>();
36+
}
37+
38+
return MaybeLocal<Value>(Number::New(isolate, static_cast<T>(value)));
39+
}
40+
41+
string ObjectToString(Isolate* isolate, Local<Value> value) {
42+
Utf8Value utf8_value(isolate, value);
43+
return string(*utf8_value);
44+
}
45+
46+
MaybeLocal<Value> ConvertSimdjsonElement(Isolate* isolate,
47+
simdjson::dom::element element) {
48+
simdjson::dom::element_type type = element.type();
49+
50+
switch (type) {
51+
case simdjson::dom::element_type::INT64: {
52+
return ToV8Number<int64_t>(isolate, element);
53+
}
54+
case simdjson::dom::element_type::UINT64: {
55+
return ToV8Number<uint64_t>(isolate, element);
56+
}
57+
case simdjson::dom::element_type::DOUBLE: {
58+
return ToV8Number<double>(isolate, element);
59+
}
60+
case simdjson::dom::element_type::BOOL: {
61+
bool value;
62+
simdjson::error_code error = element.get_bool().get(value);
63+
64+
if (error) {
65+
THROW_ERR_MODULE_NOT_INSTANTIATED(isolate);
66+
return MaybeLocal<Value>();
67+
}
68+
69+
return MaybeLocal<Value>(Boolean::New(isolate, value));
70+
}
71+
case simdjson::dom::element_type::NULL_VALUE: {
72+
Local<Primitive> null = Null(isolate);
73+
74+
return MaybeLocal<Value>(null);
75+
}
76+
case simdjson::dom::element_type::STRING: {
77+
string value;
78+
simdjson::error_code error = element.get_string().get(value);
79+
80+
if (error) {
81+
THROW_ERR_MODULE_NOT_INSTANTIATED(isolate);
82+
return MaybeLocal<Value>();
83+
}
84+
85+
return String::NewFromUtf8(isolate, value.c_str());
86+
}
87+
case simdjson::dom::element_type::ARRAY: {
88+
simdjson::dom::array array;
89+
simdjson::error_code error = element.get_array().get(array);
90+
91+
if (error) {
92+
THROW_ERR_MODULE_NOT_INSTANTIATED(isolate);
93+
return MaybeLocal<Value>();
94+
}
95+
96+
Local<Array> v8_array =
97+
v8::Array::New(isolate, array.size());
98+
99+
Local<Context> context = isolate->GetCurrentContext();
100+
101+
uint32_t index = 0;
102+
for (simdjson::dom::element child : array) {
103+
Local<Value> converted;
104+
105+
if (!ConvertSimdjsonElement(isolate, child).ToLocal(&converted))
106+
return MaybeLocal<Value>();
107+
108+
if (v8_array->Set(context, index, converted).IsNothing())
109+
return MaybeLocal<Value>();
110+
index++;
111+
}
112+
113+
return MaybeLocal<Value>(v8_array);
114+
}
115+
case simdjson::dom::element_type::OBJECT: {
116+
simdjson::dom::object object;
117+
simdjson::error_code error = element.get_object().get(object);
118+
119+
if (error) {
120+
THROW_ERR_MODULE_NOT_INSTANTIATED(isolate);
121+
return MaybeLocal<Value>();
122+
}
123+
124+
Local<Object> v8_object = Object::New(isolate);
125+
Local<Context> context = isolate->GetCurrentContext();
126+
127+
for (const simdjson::dom::key_value_pair& kv : object) {
128+
const std::string_view key = kv.key;
129+
130+
Local<String> v8_key;
131+
if (!String::NewFromUtf8(isolate,
132+
key.data(),
133+
v8::NewStringType::kNormal,
134+
static_cast<int>(key.size()))
135+
.ToLocal(&v8_key)) {
136+
return MaybeLocal<Value>();
137+
}
138+
139+
Local<Value> converted;
140+
if (!ConvertSimdjsonElement(isolate, kv.value).ToLocal(&converted))
141+
return MaybeLocal<Value>();
142+
143+
if (v8_object->Set(context, v8_key, converted).IsNothing())
144+
return MaybeLocal<Value>();
145+
}
146+
147+
return MaybeLocal<Value>(v8_object);
148+
}
149+
default:
150+
THROW_ERR_MODULE_NOT_INSTANTIATED(isolate);
151+
return MaybeLocal<Value>();
152+
}
153+
}
154+
155+
void Parse(const FunctionCallbackInfo<Value>& args) {
156+
if (args.Length() < 1 || !args[0]->IsString()) {
157+
THROW_ERR_INVALID_ARG_TYPE(args.GetIsolate(),
158+
"The \"text\" argument must be a string.");
159+
return;
160+
}
161+
162+
Local<String> json_str = args[0].As<String>();
163+
164+
// TODO(araujogui): Remove memory copy
165+
string key = ObjectToString(args.GetIsolate(), json_str);
166+
simdjson::padded_string padded_string(key);
167+
168+
simdjson::dom::parser parser;
169+
simdjson::dom::element doc;
170+
171+
simdjson::error_code error = parser.parse(padded_string).get(doc);
172+
173+
if (error) {
174+
// TODO(araujogui): create a ERR_INVALID_JSON macro
175+
THROW_ERR_SOURCE_PHASE_NOT_DEFINED(
176+
args.GetIsolate(), "The \"json\" argument must be a string.");
177+
return;
178+
}
179+
180+
Local<Value> result;
181+
182+
if (!ConvertSimdjsonElement(args.GetIsolate(), doc).ToLocal(&result)) return;
183+
184+
args.GetReturnValue().Set(result);
185+
}
186+
187+
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
188+
registry->Register(Parse);
189+
}
190+
191+
void Initialize(Local<Object> target,
192+
Local<Value> unused,
193+
Local<Context> context,
194+
void* priv) {
195+
SetMethodNoSideEffect(context, target, "parse", Parse);
196+
}
197+
198+
} // namespace json_parser
199+
} // namespace node
200+
201+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(json_parser, node::json_parser::Initialize)
202+
NODE_BINDING_EXTERNAL_REFERENCE(json_parser,
203+
node::json_parser::RegisterExternalReferences)

src/node_json_parser.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef SRC_NODE_JSON_PARSER_H_
2+
#define SRC_NODE_JSON_PARSER_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "node_external_reference.h"
7+
8+
namespace node {
9+
namespace json_parser {
10+
11+
void Initialize(v8::Local<v8::Object> target,
12+
v8::Local<v8::Value> unused,
13+
v8::Local<v8::Context> context,
14+
void* priv);
15+
16+
void RegisterExternalReferences(ExternalReferenceRegistry* registry);
17+
18+
} // namespace json_parser
19+
} // namespace node
20+
21+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
22+
23+
#endif // SRC_NODE_JSON_PARSER_H_

0 commit comments

Comments
 (0)