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
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
**Learning:** In tight parsing loops in Rust (e.g., `normalize_keywords` and `remove_numeric_separators`), using `.chars().collect::<Vec<_>>()` introduces severe performance overhead due to O(N) memory allocation per operation. Similarly, repeatedly allocating new `String` instances for word extraction and `.to_ascii_lowercase()` within loops causes significant heap traffic.

**Action:** When performing string transformations or extractions in high-frequency functions, prefer slicing directly with `&str`, checking bytes directly (e.g. `value.as_bytes()`) where possible (such as when scanning for single-byte ASCII characters), and using non-allocating comparison methods like `eq_ignore_ascii_case()` instead of allocating a lowercase string. Always avoid `.chars().collect::<Vec<_>>()` when a streaming iterator (`chars()` or `as_bytes()`) suffices.
## 2024-08-07 - [Rust String Decoding Overhead]
**Learning:** Using `.char_indices()` to search for ASCII characters (like `|` or whitespace) introduces unnecessary UTF-8 decoding overhead for every character in the string, which slows down tight parsing loops.
**Action:** Use `.match_indices()` for exact char/string matches, and `.as_bytes().iter().position(...)` for searching by byte predicates (e.g. `b.is_ascii_whitespace()`), bypassing UTF-8 decoding for ASCII boundaries.
27 changes: 15 additions & 12 deletions compiler/rockql-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,16 @@ fn split_segments(source: &str) -> Vec<Segment> {
for (line_index, line) in source.lines().enumerate() {
let mut start = 0;

for (byte_index, character) in line.char_indices() {
if character == '|' {
push_segment(
&mut segments,
&line[start..byte_index],
line_index + 1,
start,
);
start = byte_index + character.len_utf8();
}
// ⚡ Bolt Optimization: Use `match_indices` instead of `char_indices`
// to avoid decoding overhead for every character in the string.
for (byte_index, _) in line.match_indices('|') {
push_segment(
&mut segments,
&line[start..byte_index],
line_index + 1,
start,
);
start = byte_index + 1; // '|' is exactly 1 byte
}

push_segment(&mut segments, &line[start..], line_index + 1, start);
Expand All @@ -102,9 +102,12 @@ fn push_segment(segments: &mut Vec<Segment>, raw: &str, line: usize, byte_start:
}

fn parse_transform(text: &str, span: Span) -> Result<Transform, Diagnostic> {
// ⚡ Bolt Optimization: Use byte iteration to find the first ASCII whitespace,
// avoiding UTF-8 decoding overhead in `.char_indices()`.
let keyword_end = text
.char_indices()
.find_map(|(index, character)| character.is_whitespace().then_some(index))
.as_bytes()
.iter()
.position(|&b| b.is_ascii_whitespace())
.unwrap_or(text.len());

let keyword = &text[..keyword_end];
Expand Down
Loading