From addebefa2b8a8a1fd4bbebefc4f92c477e12e2cf Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2026 05:15:35 +0000 Subject: [PATCH] Refactor `push_str(&format!(...))` to use `write!` in canvas module This commit optimizes string concatenation in the `canvas.rs` module by using `std::fmt::Write` and the `write!` macro instead of `push_str(&format!(...))`. This eliminates temporary `String` allocations inside rendering loops, reducing memory overhead and improving performance for complex canvas operations. Explanatory comments were added, and the learning was appended to the Bolt journal. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- .jules/bolt.md | 3 ++ fix_journal.py | 4 ++ stdlib/src/canvas.rs | 88 ++++++++++++++++++++++++++++---------------- 3 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 fix_journal.py diff --git a/.jules/bolt.md b/.jules/bolt.md index 670131a6..3311e44d 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -11,3 +11,6 @@ ## 2024-06-25 - Suboptimal Line Search in LSP **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. **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. +## 2024-05-24 - Avoid `push_str(&format!(...))` in loops +**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. +**Action:** Use `std::fmt::Write` and the `write!` macro directly onto the target string buffer instead. diff --git a/fix_journal.py b/fix_journal.py new file mode 100644 index 00000000..acc1d4d3 --- /dev/null +++ b/fix_journal.py @@ -0,0 +1,4 @@ +with open('.jules/bolt.md', 'r') as f: + content = f.read() + +# 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. diff --git a/stdlib/src/canvas.rs b/stdlib/src/canvas.rs index 502f5ac3..6df917b5 100644 --- a/stdlib/src/canvas.rs +++ b/stdlib/src/canvas.rs @@ -1,6 +1,11 @@ use crate::{StdFunction, StdlibModule, StdlibRegistry}; use std::cell::RefCell; use std::collections::HashMap; +// Performance Note: +// This module uses `std::fmt::Write` and the `write!` macro instead of `push_str(&format!(...))` +// to append content to SVG and buffer strings. This prevents the allocation of temporary `String` +// objects during concatenation, significantly reducing memory overhead when rendering complex canvases. +use std::fmt::Write; use std::rc::Rc; use techscript_runtime::{ context::{Capability, RuntimeContext}, @@ -38,10 +43,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { .and_then(|p| p.value.as_ref()) .and_then(|v| v.try_into_int().ok()) .unwrap_or(48); - svg.push_str(&format!( + let _ = write!( + svg, r#"{}"#, size, color, text - )); + ); } "rings" => { // BLUE FLAME / WING (Bottom-Left Swirl) @@ -98,10 +104,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { .and_then(|p| p.value.as_ref()) .and_then(|v| v.try_into_int().ok()) .unwrap_or(48); - svg.push_str(&format!( + let _ = write!( + svg, r#"{}"#, size, color, text - )); + ); } "rings" => { let count = dsl @@ -135,10 +142,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { for i in 0..count { let r = 80 + i as i64 * (size / 2); let opacity = 0.4 - (i as f32 * 0.08); - svg.push_str(&format!( + let _ = write!( + svg, r#""#, r, color, thickness, opacity - )); + ); } } "emblem" => { @@ -158,10 +166,16 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { .unwrap_or(120); let x = 250 - size / 2; let y = 180 - size / 2; - svg.push_str(&format!( + let _ = write!( + svg, r#""#, - x, y, size, size, size / 4, color - )); + x, + y, + size, + size, + size / 4, + color + ); } "letter" => { let ch = dsl @@ -185,10 +199,11 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { .and_then(|p| p.value.as_ref()) .and_then(|v| v.try_into_int().ok()) .unwrap_or(32); - svg.push_str(&format!( + let _ = write!( + svg, r#"{}"#, size, color, ch - )); + ); } "core" => { let color = dsl @@ -205,11 +220,12 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { .and_then(|p| p.value.as_ref()) .and_then(|v| v.try_into_int().ok()) .unwrap_or(40); - svg.push_str(&format!( + let _ = write!( + svg, r#""#, size / 2, color - )); + ); } "circuits" => { let color = dsl @@ -219,7 +235,8 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { .and_then(|p| p.value.as_ref()) .map(|v| v.to_string()) .unwrap_or_else(|| "#00d4ff".to_string()); - svg.push_str(&format!( + let _ = write!( + svg, r#" @@ -227,7 +244,7 @@ fn dsl_to_svg(val: &RuntimeValue, is_dragon: bool) -> String { "#, color, color, color, color, color, color - )); + ); } _ => {} } @@ -292,10 +309,11 @@ impl Callable for CanvasFn { let w = parse_width(&buf); let h = parse_height(&buf); let fill = args[0].to_string(); - buf.push_str(&format!( + let _ = write!( + buf, r#""#, w, h, fill - )); + ); Ok(RuntimeValue::Null) } CanvasOp::Rect => { @@ -304,10 +322,11 @@ impl Callable for CanvasFn { let w = args[2].try_into_int().unwrap_or(100); let h = args[3].try_into_int().unwrap_or(100); let fill = args[4].to_string(); - buf.push_str(&format!( + let _ = write!( + buf, r#""#, x, y, w, h, fill - )); + ); Ok(RuntimeValue::Null) } CanvasOp::Circle => { @@ -315,10 +334,11 @@ impl Callable for CanvasFn { let cy = args[1].try_into_int().unwrap_or(50); let r = args[2].try_into_int().unwrap_or(40); let fill = args[3].to_string(); - buf.push_str(&format!( + let _ = write!( + buf, r#""#, cx, cy, r, fill - )); + ); Ok(RuntimeValue::Null) } CanvasOp::Text => { @@ -335,16 +355,17 @@ impl Callable for CanvasFn { } else { "black".to_string() }; - buf.push_str(&format!(r#"{}"#, x, y, size, color, txt)); + let _ = write!( + buf, + r#"{}"#, + x, y, size, color, txt + ); Ok(RuntimeValue::Null) } CanvasOp::Polygon => { let points = args[0].to_string(); let fill = args[1].to_string(); - buf.push_str(&format!( - r#""#, - points, fill - )); + let _ = write!(buf, r#""#, points, fill); Ok(RuntimeValue::Null) } CanvasOp::Line => { @@ -353,10 +374,11 @@ impl Callable for CanvasFn { let x2 = args[2].try_into_int().unwrap_or(100); let y2 = args[3].try_into_int().unwrap_or(100); let stroke = args[4].to_string(); - buf.push_str(&format!( + let _ = write!( + buf, r#""#, x1, y1, x2, y2, stroke - )); + ); Ok(RuntimeValue::Null) } CanvasOp::Close => { @@ -405,7 +427,11 @@ impl Callable for CanvasFn { } else { "1".to_string() }; - buf.push_str(&format!(r#""#, cx, cy, r, color, width, opacity)); + let _ = write!( + buf, + r#""#, + cx, cy, r, color, width, opacity + ); Ok(RuntimeValue::Null) } CanvasOp::Reset => { @@ -633,7 +659,7 @@ impl StdlibRegistry { let bg_color = if is_dragon { "#030408" } else { "#0a0e27" }; let mut svg = String::new(); - svg.push_str(&format!(r##" + let _ = write!(svg, r##" @@ -656,7 +682,7 @@ impl StdlibRegistry { -"##, bg_color)); +"##, bg_color); for block in &blocks { svg.push_str(&dsl_to_svg(block, is_dragon)); }