Skip to content

Commit 3ec17dd

Browse files
committed
perf(lib): add heuristics for fast allocation
1 parent 9e248ce commit 3ec17dd

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

src/pipeline/parser.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ pub fn parse_template(template: &str) -> Result<(Vec<StringOp>, bool), String> {
6767
.next()
6868
.unwrap();
6969

70-
// Estimate capacity based on template length and complexity
70+
// Heuristic: reserve enough space for `|`-separated operations but avoid gross
71+
// over-allocation for medium templates. Count of `|` is cheap (single pass
72+
// over the input) and gives an upper bound on the number of operations.
73+
let pipe_count = template.as_bytes().iter().filter(|&&b| b == b'|').count();
74+
7175
let estimated_capacity = if template.len() < 50 {
7276
4 // Simple templates typically have 1-4 operations
7377
} else if template.len() < 150 {
@@ -76,7 +80,7 @@ pub fn parse_template(template: &str) -> Result<(Vec<StringOp>, bool), String> {
7680
16 // Complex templates might have 8+ operations
7781
};
7882

79-
let mut ops = Vec::with_capacity(estimated_capacity);
83+
let mut ops = Vec::with_capacity(std::cmp::min(estimated_capacity, pipe_count + 1));
8084
let mut debug = false;
8185

8286
for pair in pairs.into_inner() {

src/pipeline/template.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ impl MultiTemplate {
208208
/// let template = Template::parse("{split:,:..|sort|join: - }").unwrap();
209209
/// ```
210210
pub fn parse(template: &str) -> Result<Self, String> {
211+
// Fast-path: if the input is a *single* template block (no outer-level
212+
// literal text) we can skip the multi-template scanner and directly
213+
// parse the operation list.
214+
if let Some(single) = Self::try_single_block(template)? {
215+
return Ok(single);
216+
}
217+
211218
let (sections, _) = parser::parse_multi_template(template)?;
212219
Ok(Self::new(template.to_string(), sections, false))
213220
}
@@ -252,6 +259,14 @@ impl MultiTemplate {
252259
/// let template = Template::parse_with_debug("{upper}", Some(true)).unwrap();
253260
/// ```
254261
pub fn parse_with_debug(template: &str, debug: Option<bool>) -> Result<Self, String> {
262+
// Re-use the single-block shortcut when applicable.
263+
if let Some(mut single) = Self::try_single_block(template)? {
264+
if let Some(dbg_override) = debug {
265+
single.debug = dbg_override;
266+
}
267+
return Ok(single);
268+
}
269+
255270
let (sections, inner_dbg) = parser::parse_multi_template(template)?;
256271
Ok(Self::new(
257272
template.to_string(),
@@ -588,6 +603,45 @@ impl MultiTemplate {
588603
.collect::<Vec<_>>()
589604
.join(" | ")
590605
}
606+
607+
/* -------- helper: detect plain single-block templates ------------- */
608+
609+
/// Detects and parses templates that consist of exactly one `{ ... }` block
610+
/// with no surrounding literal text. Returns `Ok(Some(MultiTemplate))` when
611+
/// the fast path can be applied, `Ok(None)` otherwise.
612+
fn try_single_block(template: &str) -> Result<Option<Self>, String> {
613+
// Must start with '{' and end with '}' to be a candidate.
614+
if !(template.starts_with('{') && template.ends_with('}')) {
615+
return Ok(None);
616+
}
617+
618+
// Verify that the outer-most braces close at the very end and that the
619+
// brace nesting never returns to zero before the last char.
620+
let mut depth = 0u32;
621+
for ch in template[1..template.len() - 1].chars() {
622+
match ch {
623+
'{' => depth += 1,
624+
'}' => {
625+
if depth == 0 {
626+
// Closed the top-level early → literal content exists.
627+
return Ok(None);
628+
}
629+
depth -= 1;
630+
}
631+
_ => {}
632+
}
633+
}
634+
635+
if depth != 0 {
636+
// Unbalanced braces – fall back to full parser for proper error.
637+
return Ok(None);
638+
}
639+
640+
// Safe to treat as single template block.
641+
let (ops, dbg_flag) = parser::parse_template(template)?;
642+
let sections = vec![TemplateSection::Template(ops)];
643+
Ok(Some(Self::new(template.to_string(), sections, dbg_flag)))
644+
}
591645
}
592646

593647
/* ---------- trait impls -------------------------------------------------- */

0 commit comments

Comments
 (0)