Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
## 2026-09-22 - String allocation optimization in std.web DSL rendering
**Learning:** Generating deep HTML structures in `std.web` heavily penalized performance because `dsl_to_html` allocated and returned a new `String` for every child DSL node. This causes `O(N)` heap allocations and redundant copying in the render tree. By passing a mutable `&mut String` buffer recursively downwards, we avoid all intermediate string heap allocations and significantly improve serialization speed.
**Action:** Always prefer using a recursive builder pattern passing a single mutable `&mut String` buffer to `write!` or `push_str` when rendering nested tree structures (like HTML, JSON, or ASTs) instead of returning newly allocated strings at each layer.

## 2024-05-24 - Avoid Repeated Array Searches in Rust
**Learning:** Multiple `.find()` calls on the same array to extract different properties result in repeated O(N) iterations. Combining them into a single `for` loop that captures all required values significantly improves performance, particularly in hot paths like DSL rendering. However, to preserve the exact semantic behavior of `.find()` (which returns the *first* matching element), you must only assign to variables if they are currently `None` (`if my_prop.is_none()`).
**Action:** When extracting multiple attributes from a collection (like DSL block properties), use a single pass loop and early exit instead of chained `.find()` methods. Always ensure original match semantics are preserved.
167 changes: 99 additions & 68 deletions stdlib/src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,29 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
if is_dragon {
match dsl.kind.as_str() {
"logo" => {
let text = dsl
.properties
.iter()
.find(|p| p.name == "text")
let mut text_prop = None;
let mut color_prop = None;
let mut size_prop = None;
for p in &dsl.properties {
match p.name.as_str() {
"text" if text_prop.is_none() => text_prop = Some(p),
"color" if color_prop.is_none() => color_prop = Some(p),
"size" if size_prop.is_none() => size_prop = Some(p),
_ => {}
}
if text_prop.is_some() && color_prop.is_some() && size_prop.is_some() {
break;
}
}
let text = text_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "Logo".to_string());
let color = dsl
.properties
.iter()
.find(|p| p.name == "color")
let color = color_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "#00d4ff".to_string());
let size = dsl
.properties
.iter()
.find(|p| p.name == "size")
let size = size_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(48);
Expand Down Expand Up @@ -82,24 +87,29 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
// RENDER OLD GEOMETRIC LOGO
match dsl.kind.as_str() {
"logo" => {
let text = dsl
.properties
.iter()
.find(|p| p.name == "text")
let mut text_prop = None;
let mut color_prop = None;
let mut size_prop = None;
for p in &dsl.properties {
match p.name.as_str() {
"text" if text_prop.is_none() => text_prop = Some(p),
"color" if color_prop.is_none() => color_prop = Some(p),
"size" if size_prop.is_none() => size_prop = Some(p),
_ => {}
}
if text_prop.is_some() && color_prop.is_some() && size_prop.is_some() {
break;
}
}
let text = text_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "Logo".to_string());
let color = dsl
.properties
.iter()
.find(|p| p.name == "color")
let color = color_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "#00d4ff".to_string());
let size = dsl
.properties
.iter()
.find(|p| p.name == "size")
let size = size_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(48);
Expand All @@ -110,31 +120,35 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"rings" => {
let count = dsl
.properties
.iter()
.find(|p| p.name == "count")
let mut count_prop = None;
let mut color_prop = None;
let mut size_prop = None;
let mut thickness_prop = None;
for p in &dsl.properties {
match p.name.as_str() {
"count" if count_prop.is_none() => count_prop = Some(p),
"color" if color_prop.is_none() => color_prop = Some(p),
"size" if size_prop.is_none() => size_prop = Some(p),
"thickness" if thickness_prop.is_none() => thickness_prop = Some(p),
_ => {}
}
if count_prop.is_some() && color_prop.is_some() && size_prop.is_some() && thickness_prop.is_some() {
break;
}
}
let count = count_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(3);
let color = dsl
.properties
.iter()
.find(|p| p.name == "color")
let color = color_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "#00d4ff".to_string());
let size = dsl
.properties
.iter()
.find(|p| p.name == "size")
let size = size_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(40);
let thickness = dsl
.properties
.iter()
.find(|p| p.name == "thickness")
let thickness = thickness_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(3);
Expand All @@ -149,17 +163,23 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
}
}
"emblem" => {
let color = dsl
.properties
.iter()
.find(|p| p.name == "color")
let mut color_prop = None;
let mut size_prop = None;
for p in &dsl.properties {
match p.name.as_str() {
"color" if color_prop.is_none() => color_prop = Some(p),
"size" if size_prop.is_none() => size_prop = Some(p),
_ => {}
}
if color_prop.is_some() && size_prop.is_some() {
break;
}
}
let color = color_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "#0088cc".to_string());
let size = dsl
.properties
.iter()
.find(|p| p.name == "size")
let size = size_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(120);
Expand All @@ -177,24 +197,29 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"letter" => {
let ch = dsl
.properties
.iter()
.find(|p| p.name == "char")
let mut char_prop = None;
let mut color_prop = None;
let mut size_prop = None;
for p in &dsl.properties {
match p.name.as_str() {
"char" if char_prop.is_none() => char_prop = Some(p),
"color" if color_prop.is_none() => color_prop = Some(p),
"size" if size_prop.is_none() => size_prop = Some(p),
_ => {}
}
if char_prop.is_some() && color_prop.is_some() && size_prop.is_some() {
break;
}
}
let ch = char_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "T".to_string());
let color = dsl
.properties
.iter()
.find(|p| p.name == "color")
let color = color_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "#0a0e27".to_string());
let size = dsl
.properties
.iter()
.find(|p| p.name == "size")
let size = size_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(32);
Expand All @@ -205,17 +230,23 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"core" => {
let color = dsl
.properties
.iter()
.find(|p| p.name == "color")
let mut color_prop = None;
let mut size_prop = None;
for p in &dsl.properties {
match p.name.as_str() {
"color" if color_prop.is_none() => color_prop = Some(p),
"size" if size_prop.is_none() => size_prop = Some(p),
_ => {}
}
if color_prop.is_some() && size_prop.is_some() {
break;
}
}
let color = color_prop
.and_then(|p| p.value.as_ref())
.map(|v| v.to_string())
.unwrap_or_else(|| "#66e0ff".to_string());
let size = dsl
.properties
.iter()
.find(|p| p.name == "size")
let size = size_prop
.and_then(|p| p.value.as_ref())
.and_then(|v| v.try_into_int().ok())
.unwrap_or(40);
Expand Down
Loading