Skip to content

Commit 537c39f

Browse files
perf: optimize string handling and reduce memory allocations
- Use Cow<str> for escape_chars/unescape_chars to avoid allocations when no escaping is needed - Cache default schema parsing with OnceLock to avoid repeated parsing - Replace merge_schema with merge_schema_owned to avoid unnecessary clones when merging JSON data - Optimize resolve_pointer to avoid string allocations using byte slices - Use Entry API for map operations and get_mut chains for schema updates
1 parent d17d76d commit 537c39f

File tree

7 files changed

+169
-98
lines changed

7 files changed

+169
-98
lines changed

src/bif/parse_bif_data.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl<'a> Bif<'a> {
2222
}
2323

2424
if self.flags.contains("|inline|") {
25-
let data: Value = match serde_json::from_str(&self.code) {
25+
let mut data: Value = match serde_json::from_str(&self.code) {
2626
Ok(value) => value,
2727
Err(_) => {
2828
return Err(self.bif_error(BIF_ERROR_NOT_VALID_JSON));
@@ -32,10 +32,11 @@ impl<'a> Bif<'a> {
3232
let indir = &self.inherit.create_block_schema(self.shared);
3333

3434
// Merge new locale data in curren local data.
35-
merge_schema(
36-
&mut self.shared.schema["__indir"][indir]["data"],
37-
&data["data"],
38-
);
35+
let merge_data = data
36+
.as_object_mut()
37+
.and_then(|obj| obj.remove("data"))
38+
.unwrap_or(Value::Null);
39+
merge_schema_owned(&mut self.shared.schema["__indir"][indir]["data"], merge_data);
3940

4041
self.out = UNPRINTABLE.to_string();
4142

@@ -81,7 +82,7 @@ impl<'a> Bif<'a> {
8182
self.inherit.data_files.push(canonical_path);
8283
let file_raw = fs::read_to_string(&self.file_path).unwrap_or("".to_string());
8384

84-
let data: Value = match serde_json::from_str(&file_raw) {
85+
let mut data: Value = match serde_json::from_str(&file_raw) {
8586
Ok(value) => value,
8687
Err(_) => {
8788
return Err(self.bif_error(BIF_ERROR_NOT_VALID_JSON));
@@ -91,10 +92,11 @@ impl<'a> Bif<'a> {
9192
let indir = &self.inherit.create_block_schema(self.shared);
9293

9394
// Merge new locale data in curren local data.
94-
merge_schema(
95-
&mut self.shared.schema["__indir"][indir]["data"],
96-
&data["data"],
97-
);
95+
let merge_data = data
96+
.as_object_mut()
97+
.and_then(|obj| obj.remove("data"))
98+
.unwrap_or(Value::Null);
99+
merge_schema_owned(&mut self.shared.schema["__indir"][indir]["data"], merge_data);
98100

99101
self.out = UNPRINTABLE.to_string();
100102

src/bif/parse_bif_debug.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl<'a> Bif<'a> {
7070
if self.mod_filter {
7171
// unescape_chars for prevent double encoding
7272
let tmp = unescape_chars(&self.out, true);
73-
self.out = escape_chars(&tmp, true);
73+
self.out = escape_chars(&tmp, true).into_owned();
7474
}
7575

7676
Ok(())

src/bif/parse_bif_locale.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl<'a> Bif<'a> {
4040
let indir = &self.inherit.create_block_schema(self.shared);
4141

4242
// Merge new locale data in curren locale.
43-
merge_schema(&mut self.shared.schema["__indir"][indir]["locale"], &locale);
43+
merge_schema_owned(&mut self.shared.schema["__indir"][indir]["locale"], locale);
4444

4545
self.out = EMPTY_STRING;
4646

@@ -100,7 +100,7 @@ impl<'a> Bif<'a> {
100100
let indir = &self.inherit.create_block_schema(self.shared);
101101

102102
// Merge new locale data in curren locale.
103-
merge_schema(&mut self.shared.schema["__indir"][indir]["locale"], &locale);
103+
merge_schema_owned(&mut self.shared.schema["__indir"][indir]["locale"], locale);
104104

105105
self.out = UNPRINTABLE.to_string();
106106

src/bif/parse_bif_var.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<'a> Bif<'a> {
4545
if (self.mod_filter || self.shared.filter_all) && !self.mod_negate {
4646
if !var_name.starts_with("CONTEXT->") {
4747
// unescape_chars for prevent double encoding
48-
self.out = escape_chars(&unescape_chars(&self.out, true), true);
48+
self.out = escape_chars(&unescape_chars(&self.out, true), true).into_owned();
4949
}
5050
} else {
5151
if self.shared.filter_bifs {
@@ -56,7 +56,7 @@ impl<'a> Bif<'a> {
5656
}
5757

5858
if self.mod_negate && !self.shared.filter_bifs && var_name.starts_with("CONTEXT->") {
59-
self.out = unescape_chars(&self.out, true);
59+
self.out = unescape_chars(&self.out, true).into_owned();
6060
}
6161

6262
Ok(())

src/block_parser.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,15 @@ impl BlockInherit {
6969
// that needs to be inherited to obtain the reference to the storage.
7070
pub(crate) fn create_block_schema(&mut self, shared: &mut Shared) -> String {
7171
let prev_id = self.indir.clone();
72-
let block_id;
7372

7473
// If this function is called before creating the first block.
7574
// It may be necessary to initialize values.
7675
// The first block is not 0, is 1.
77-
if self.block_count < 1 {
78-
block_id = "block_1".to_string();
76+
let block_id = if self.block_count < 1 {
77+
"block_1".to_string()
7978
} else {
80-
block_id = "block_".to_string() + self.block_count.to_string().as_str();
81-
}
79+
format!("block_{}", self.block_count)
80+
};
8281

8382
// It can be called several times from the same level, in which case
8483
// it does not need to be cloned again.
@@ -102,7 +101,7 @@ pub(crate) struct BlockParser<'a> {
102101
impl Drop for BlockParser<'_> {
103102
fn drop(&mut self) {
104103
// release memory
105-
let block_id = "block_".to_string() + self.inherit.block_count.to_string().as_str();
104+
let block_id = format!("block_{}", self.inherit.block_count);
106105

107106
// The first main block cannot be deleted
108107
if block_id != "block_1" {

src/template.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fs;
22
use std::path::Path;
3+
use std::sync::OnceLock;
34
use std::time::{Duration, Instant};
45
use serde_json::{json, Value};
56
use regex::Regex;
@@ -22,6 +23,16 @@ pub struct Template<'a> {
2223
out: String,
2324
}
2425

26+
fn default_schema_template() -> Result<Value, String> {
27+
static DEFAULT_SCHEMA: OnceLock<Result<Value, String>> = OnceLock::new();
28+
DEFAULT_SCHEMA
29+
.get_or_init(|| {
30+
serde_json::from_str(DEFAULT)
31+
.map_err(|_| "const DEFAULT is not a valid JSON string".to_string())
32+
})
33+
.clone()
34+
}
35+
2536
/// A struct representing a template that can be rendered.
2637
///
2738
/// This struct is used to handle the rendering of templates.
@@ -30,10 +41,7 @@ impl<'a> Template<'a> {
3041
///
3142
/// It allows you to set up a template and schema with different types.
3243
pub fn new() -> Result<Self, String> {
33-
let default_schema: Value = match serde_json::from_str(DEFAULT) {
34-
Ok(value) => value,
35-
Err(_) => return Err("const DEFAULT is not a valid JSON string".to_string()),
36-
};
44+
let default_schema = default_schema_template()?;
3745
let shared = Shared::new(default_schema.clone());
3846

3947
Ok(Template {
@@ -66,13 +74,7 @@ impl<'a> Template<'a> {
6674
return Err(e.to_string());
6775
}
6876
};
69-
let mut default_schema: Value = match serde_json::from_str(DEFAULT) {
70-
Ok(value) => value,
71-
Err(_) => {
72-
eprintln!("Internal error in const DEFAULT {}, line: {}", file!(), line!());
73-
return Err("const DEFAULT is not a valid JSON string".to_string());
74-
}
75-
};
77+
let mut default_schema = default_schema_template()?;
7678

7779
update_schema_owned(&mut default_schema, schema);
7880
let shared = Shared::new(default_schema.clone());
@@ -131,20 +133,20 @@ impl<'a> Template<'a> {
131133
/// - The file cannot be read.
132134
/// - The file's content is not a valid JSON string.
133135
pub fn merge_schema_path(&mut self, schema_path: &str) -> Result<(), String> {
134-
let schema_str: String = match fs::read_to_string(schema_path) {
135-
Ok(s) => s,
136+
let schema_bytes = match fs::read(schema_path) {
137+
Ok(bytes) => bytes,
136138
Err(e) => {
137139
eprintln!("Cannot be read: {}", schema_path);
138140
return Err(e.to_string());
139141
}
140142
};
141-
let schema_value: Value = match serde_json::from_str(&schema_str) {
143+
let schema_value: Value = match serde_json::from_slice(&schema_bytes) {
142144
Ok(value) => value,
143145
Err(_) => {
144146
return Err("Is not a valid JSON file".to_string());
145147
}
146148
};
147-
update_schema(&mut self.schema, &schema_value);
149+
update_schema_owned(&mut self.schema, schema_value);
148150

149151
Ok(())
150152
}
@@ -166,7 +168,7 @@ impl<'a> Template<'a> {
166168
return Err("Is not a valid JSON string".to_string());
167169
}
168170
};
169-
update_schema(&mut self.schema, &schema_value);
171+
update_schema_owned(&mut self.schema, schema_value);
170172

171173
Ok(())
172174
}

0 commit comments

Comments
 (0)