Skip to content

Commit a3d19bc

Browse files
committed
[json] Merge Position into Message to save allocations
NAPI's object creation is incredibly slow compared to Node's internal creation, so we want to avoid it as much as possible. One simple (albeit slightly inconsistent) way to do that is to move the properties from a separate `Position` object into the `Message` object itself. That way Node just has to make one object and iterate its properties. See nodejs/node#45905 for future updates about possibly making this faster.
1 parent b3cb30d commit a3d19bc

File tree

2 files changed

+21
-57
lines changed

2 files changed

+21
-57
lines changed
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
/* auto-generated by NAPI-RS */
22
/* eslint-disable */
3-
export interface Message {
3+
export declare class Message {
44
key: string
55
value: string
6-
position: Position
6+
line: number
7+
col: number
78
}
89

910
export declare function parseJson(text: string): Message[]
1011

1112
export declare function parseJsonFile(filePath: string): Message[]
1213

13-
export interface Position {
14-
line: number
15-
col: number
16-
}
17-

crates/intl_flat_json_parser/src/napi.rs

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,43 @@
1-
use crate::{JsonMessage, JsonPosition};
1+
use crate::JsonMessage;
22
use napi::bindgen_prelude::Array;
3-
use napi::{Env, Property};
3+
use napi::Env;
44
use napi_derive::napi;
55

66
// Use the mimalloc allocator explicitly when building the node addon.
77
extern crate intl_allocator;
88

9-
#[napi(object)]
10-
pub struct Position {
9+
#[napi]
10+
pub struct Message {
11+
pub key: String,
12+
pub value: String,
13+
// The Position properties are created directly inline on the message
14+
// because NAPI object construction is pretty slow (like 10x slower than
15+
// the engine creating an object directly), so moving these to direct
16+
// properties of the class makes the whole process ~30-35% faster.
17+
//
18+
// See https://github.com/nodejs/node/issues/45905 for future updates.
1119
pub line: u32,
1220
pub col: u32,
1321
}
1422

15-
impl From<JsonPosition> for Position {
16-
fn from(pos: JsonPosition) -> Self {
17-
Self {
18-
line: pos.line,
19-
col: pos.col,
20-
}
21-
}
22-
}
23-
2423
fn collect_messages(env: Env, iterator: impl Iterator<Item = JsonMessage>) -> napi::Result<Array> {
2524
// This is an arbitrary size hint that should be suitable for a lot of use
2625
// cases. While it may inadvertently allocate extra memory for some,
2726
// avoiding repeated re-allocations that we're pretty confident will happen
2827
// ends up saving a lot more time in the end.
2928
let mut result = env.create_array(1024)?;
30-
// NAPI does not have an API for creating multiple instances of the same
31-
// object structure, but we can get as close as possible by pre-defining
32-
// object properties and cloning them to avoid allocating extra space for
33-
// the same key many times over.
34-
// See https://github.com/nodejs/node/issues/45905 for future updates.
35-
let line_prop = Property::new("line")?;
36-
let col_prop = Property::new("col")?;
37-
let key_prop = Property::new("key")?;
38-
let value_prop = Property::new("value")?;
39-
let position_prop = Property::new("position")?;
4029
for message in iterator {
41-
let key = env.create_string(&message.key)?;
42-
let value = env.create_string(&message.value)?;
43-
let mut position = env.create_object()?;
44-
position.define_properties(&[
45-
line_prop
46-
.clone()
47-
.with_value(&env.create_uint32(message.position.line)?),
48-
col_prop
49-
.clone()
50-
.with_value(&env.create_uint32(message.position.col)?),
51-
])?;
52-
53-
let mut converted = env.create_object()?;
54-
converted.define_properties(&[
55-
key_prop.clone().with_value(&key),
56-
value_prop.clone().with_value(&value),
57-
position_prop.clone().with_value(&position),
58-
])?;
59-
60-
result.insert(converted)?;
30+
result.insert(Message {
31+
key: message.key.to_string(),
32+
value: message.value.to_string(),
33+
line: message.position.line,
34+
col: message.position.col,
35+
})?;
6136
}
6237

6338
Ok(result)
6439
}
6540

66-
#[napi(object)]
67-
pub struct Message {
68-
pub key: String,
69-
pub value: String,
70-
pub position: Position,
71-
}
72-
7341
#[napi(ts_return_type = "Message[]")]
7442
pub fn parse_json(env: Env, text: String) -> napi::Result<Array> {
7543
let messages = crate::parse_flat_translation_json(&text);

0 commit comments

Comments
 (0)