Skip to content

Commit c1b0660

Browse files
authored
Merge pull request #123 from Tcode-Motion/bolt/canvas-write-optimization-5041535596994256397
⚡ Bolt: Use write! macro to prevent temporary allocations in canvas
2 parents e98a550 + addebef commit c1b0660

3 files changed

Lines changed: 64 additions & 31 deletions

File tree

‎.jules/bolt.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@
1111
## 2024-06-25 - Suboptimal Line Search in LSP
1212
**Learning:** Using `chars().nth()` with a byte offset (such as one returned by `.find()`) inside a loop over a string creates an O(N) penalty and may result in an incorrect character lookup if multi-byte unicode characters are present.
1313
**Action:** Use string slicing with the byte index to create a subset string slice, and call `.chars().next_back()` or `.chars().next()` on it for an O(1) and UTF-8 safe boundary lookup.
14+
## 2024-05-24 - Avoid `push_str(&format!(...))` in loops
15+
**Learning:** Using `push_str(&format!(...))` inside loops creates unnecessary temporary String allocations because `format!` allocates a new `String` which is then appended and immediately dropped.
16+
**Action:** Use `std::fmt::Write` and the `write!` macro directly onto the target string buffer instead.

‎fix_journal.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
with open('.jules/bolt.md', 'r') as f:
2+
content = f.read()
3+
4+
# I will append the learning at the end if it doesn't exist, but since it currently only contains my previous write, I need to look at git history to restore it.

‎stdlib/src/canvas.rs‎

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use crate::{StdFunction, StdlibModule, StdlibRegistry};
22
use std::cell::RefCell;
33
use std::collections::HashMap;
4+
// Performance Note:
5+
// This module uses `std::fmt::Write` and the `write!` macro instead of `push_str(&format!(...))`
6+
// to append content to SVG and buffer strings. This prevents the allocation of temporary `String`
7+
// objects during concatenation, significantly reducing memory overhead when rendering complex canvases.
8+
use std::fmt::Write;
49
use std::rc::Rc;
510
use techscript_runtime::{
611
context::{Capability, RuntimeContext},
@@ -38,10 +43,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
3843
.and_then(|p| p.value.as_ref())
3944
.and_then(|v| v.try_into_int().ok())
4045
.unwrap_or(48);
41-
svg.push_str(&format!(
46+
let _ = write!(
47+
svg,
4248
r#"<text x="250" y="440" text-anchor="middle" dominant-baseline="central" font-size="{}" font-weight="800" fill="{}" font-family="system-ui, -apple-system, sans-serif" letter-spacing="3">{}</text>"#,
4349
size, color, text
44-
));
50+
);
4551
}
4652
"rings" => {
4753
// BLUE FLAME / WING (Bottom-Left Swirl)
@@ -98,10 +104,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
98104
.and_then(|p| p.value.as_ref())
99105
.and_then(|v| v.try_into_int().ok())
100106
.unwrap_or(48);
101-
svg.push_str(&format!(
107+
let _ = write!(
108+
svg,
102109
r#"<text x="250" y="380" text-anchor="middle" dominant-baseline="central" font-size="{}" font-weight="800" fill="{}" font-family="system-ui, -apple-system, sans-serif" letter-spacing="3">{}</text>"#,
103110
size, color, text
104-
));
111+
);
105112
}
106113
"rings" => {
107114
let count = dsl
@@ -135,10 +142,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
135142
for i in 0..count {
136143
let r = 80 + i as i64 * (size / 2);
137144
let opacity = 0.4 - (i as f32 * 0.08);
138-
svg.push_str(&format!(
145+
let _ = write!(
146+
svg,
139147
r#"<circle cx="250" cy="180" r="{}" fill="none" stroke="{}" stroke-width="{}" opacity="{}"/>"#,
140148
r, color, thickness, opacity
141-
));
149+
);
142150
}
143151
}
144152
"emblem" => {
@@ -158,10 +166,16 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
158166
.unwrap_or(120);
159167
let x = 250 - size / 2;
160168
let y = 180 - size / 2;
161-
svg.push_str(&format!(
169+
let _ = write!(
170+
svg,
162171
r#"<rect x="{}" y="{}" width="{}" height="{}" rx="{}" fill="{}" transform="rotate(45 250 180)" filter="url(#glow)"/>"#,
163-
x, y, size, size, size / 4, color
164-
));
172+
x,
173+
y,
174+
size,
175+
size,
176+
size / 4,
177+
color
178+
);
165179
}
166180
"letter" => {
167181
let ch = dsl
@@ -185,10 +199,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
185199
.and_then(|p| p.value.as_ref())
186200
.and_then(|v| v.try_into_int().ok())
187201
.unwrap_or(32);
188-
svg.push_str(&format!(
202+
let _ = write!(
203+
svg,
189204
r#"<text x="250" y="180" text-anchor="middle" dominant-baseline="central" font-size="{}" font-weight="900" fill="{}" font-family="system-ui, -apple-system, sans-serif">{}</text>"#,
190205
size, color, ch
191-
));
206+
);
192207
}
193208
"core" => {
194209
let color = dsl
@@ -205,11 +220,12 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
205220
.and_then(|p| p.value.as_ref())
206221
.and_then(|v| v.try_into_int().ok())
207222
.unwrap_or(40);
208-
svg.push_str(&format!(
223+
let _ = write!(
224+
svg,
209225
r#"<circle cx="250" cy="180" r="{}" fill="{}" opacity="0.8"/>"#,
210226
size / 2,
211227
color
212-
));
228+
);
213229
}
214230
"circuits" => {
215231
let color = dsl
@@ -219,15 +235,16 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String {
219235
.and_then(|p| p.value.as_ref())
220236
.map(|v| v.to_string())
221237
.unwrap_or_else(|| "#00d4ff".to_string());
222-
svg.push_str(&format!(
238+
let _ = write!(
239+
svg,
223240
r#"<line x1="50" y1="180" x2="450" y2="180" stroke="{}" stroke-width="1.5" stroke-dasharray="5 5" opacity="0.6"/>
224241
<line x1="250" y1="30" x2="250" y2="330" stroke="{}" stroke-width="1.5" stroke-dasharray="5 5" opacity="0.6"/>
225242
<circle cx="50" cy="180" r="4" fill="{}"/>
226243
<circle cx="450" cy="180" r="4" fill="{}"/>
227244
<circle cx="250" cy="30" r="4" fill="{}"/>
228245
<circle cx="250" cy="330" r="4" fill="{}"/>"#,
229246
color, color, color, color, color, color
230-
));
247+
);
231248
}
232249
_ => {}
233250
}
@@ -292,10 +309,11 @@ impl Callable for CanvasFn {
292309
let w = parse_width(&buf);
293310
let h = parse_height(&buf);
294311
let fill = args[0].to_string();
295-
buf.push_str(&format!(
312+
let _ = write!(
313+
buf,
296314
r#"<rect x="0" y="0" width="{}" height="{}" fill="{}"/>"#,
297315
w, h, fill
298-
));
316+
);
299317
Ok(RuntimeValue::Null)
300318
}
301319
CanvasOp::Rect => {
@@ -304,21 +322,23 @@ impl Callable for CanvasFn {
304322
let w = args[2].try_into_int().unwrap_or(100);
305323
let h = args[3].try_into_int().unwrap_or(100);
306324
let fill = args[4].to_string();
307-
buf.push_str(&format!(
325+
let _ = write!(
326+
buf,
308327
r#"<rect x="{}" y="{}" width="{}" height="{}" fill="{}"/>"#,
309328
x, y, w, h, fill
310-
));
329+
);
311330
Ok(RuntimeValue::Null)
312331
}
313332
CanvasOp::Circle => {
314333
let cx = args[0].try_into_int().unwrap_or(50);
315334
let cy = args[1].try_into_int().unwrap_or(50);
316335
let r = args[2].try_into_int().unwrap_or(40);
317336
let fill = args[3].to_string();
318-
buf.push_str(&format!(
337+
let _ = write!(
338+
buf,
319339
r#"<circle cx="{}" cy="{}" r="{}" fill="{}"/>"#,
320340
cx, cy, r, fill
321-
));
341+
);
322342
Ok(RuntimeValue::Null)
323343
}
324344
CanvasOp::Text => {
@@ -335,16 +355,17 @@ impl Callable for CanvasFn {
335355
} else {
336356
"black".to_string()
337357
};
338-
buf.push_str(&format!(r#"<text x="{}" y="{}" font-size="{}" font-family="Arial,sans-serif" fill="{}">{}</text>"#, x, y, size, color, txt));
358+
let _ = write!(
359+
buf,
360+
r#"<text x="{}" y="{}" font-size="{}" font-family="Arial,sans-serif" fill="{}">{}</text>"#,
361+
x, y, size, color, txt
362+
);
339363
Ok(RuntimeValue::Null)
340364
}
341365
CanvasOp::Polygon => {
342366
let points = args[0].to_string();
343367
let fill = args[1].to_string();
344-
buf.push_str(&format!(
345-
r#"<polygon points="{}" fill="{}"/>"#,
346-
points, fill
347-
));
368+
let _ = write!(buf, r#"<polygon points="{}" fill="{}"/>"#, points, fill);
348369
Ok(RuntimeValue::Null)
349370
}
350371
CanvasOp::Line => {
@@ -353,10 +374,11 @@ impl Callable for CanvasFn {
353374
let x2 = args[2].try_into_int().unwrap_or(100);
354375
let y2 = args[3].try_into_int().unwrap_or(100);
355376
let stroke = args[4].to_string();
356-
buf.push_str(&format!(
377+
let _ = write!(
378+
buf,
357379
r#"<line x1="{}" y1="{}" x2="{}" y2="{}" stroke="{}" stroke-width="2"/>"#,
358380
x1, y1, x2, y2, stroke
359-
));
381+
);
360382
Ok(RuntimeValue::Null)
361383
}
362384
CanvasOp::Close => {
@@ -405,7 +427,11 @@ impl Callable for CanvasFn {
405427
} else {
406428
"1".to_string()
407429
};
408-
buf.push_str(&format!(r#"<circle cx="{}" cy="{}" r="{}" fill="none" stroke="{}" stroke-width="{}" opacity="{}"/>"#, cx, cy, r, color, width, opacity));
430+
let _ = write!(
431+
buf,
432+
r#"<circle cx="{}" cy="{}" r="{}" fill="none" stroke="{}" stroke-width="{}" opacity="{}"/>"#,
433+
cx, cy, r, color, width, opacity
434+
);
409435
Ok(RuntimeValue::Null)
410436
}
411437
CanvasOp::Reset => {
@@ -633,7 +659,7 @@ impl StdlibRegistry {
633659
let bg_color = if is_dragon { "#030408" } else { "#0a0e27" };
634660

635661
let mut svg = String::new();
636-
svg.push_str(&format!(r##"<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500">
662+
let _ = write!(svg, r##"<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500">
637663
<defs>
638664
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
639665
<feGaussianBlur stdDeviation="6" result="blur" />
@@ -656,7 +682,7 @@ impl StdlibRegistry {
656682
</linearGradient>
657683
</defs>
658684
<rect width="100%" height="100%" fill="{}"/>
659-
"##, bg_color));
685+
"##, bg_color);
660686
for block in &blocks {
661687
svg.push_str(&dsl_to_svg(block, is_dragon));
662688
}

0 commit comments

Comments
 (0)