#[cfg(test)]
static SCAN_BYTES_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
#[cfg(test)]
pub(crate) fn reset_scan_bytes_counter() {
SCAN_BYTES_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst);
}
#[cfg(test)]
pub(crate) fn scan_bytes_counter() -> usize {
SCAN_BYTES_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
}
pub(crate) fn earliest_stop_match(haystack: &str, stops: &[String]) -> Option<usize> {
stops
.iter()
.filter_map(|s| {
#[cfg(test)]
SCAN_BYTES_COUNTER.fetch_add(haystack.len(), std::sync::atomic::Ordering::SeqCst);
haystack.find(s.as_str())
})
.min()
}
pub(crate) fn earliest_stop_match_from(
haystack: &str,
stops: &[String],
start: usize,
) -> Option<usize> {
let start = start.min(haystack.len());
stops
.iter()
.filter_map(|s| {
#[cfg(test)]
SCAN_BYTES_COUNTER
.fetch_add(haystack.len() - start, std::sync::atomic::Ordering::SeqCst);
haystack[start..].find(s.as_str()).map(|p| p + start)
})
.min()
}
pub(crate) fn floor_char_boundary(s: &str, idx: usize) -> usize {
let mut idx = idx.min(s.len());
while idx > 0 && !s.is_char_boundary(idx) {
idx -= 1;
}
idx
}
pub(crate) fn stop_scan_search_start(haystack: &str, prev_len: usize, max_stop: usize) -> usize {
floor_char_boundary(
haystack,
prev_len.saturating_sub(max_stop.saturating_sub(1)),
)
}
pub(crate) struct StopStringMatcher {
full: String,
emitted: usize,
max_stop: usize,
stops: Vec<String>,
stopped: bool,
}
impl StopStringMatcher {
pub(crate) fn new(stops: &[String]) -> Self {
let max_stop = stops.iter().map(String::len).max().unwrap_or(1);
Self {
full: String::new(),
emitted: 0,
max_stop,
stops: stops.to_vec(),
stopped: false,
}
}
pub(crate) fn push(&mut self, delta: &str, sink: &mut impl FnMut(&str)) -> bool {
let prev_len = self.full.len();
if !delta.is_empty() {
self.full.push_str(delta);
}
let search_start = stop_scan_search_start(&self.full, prev_len, self.max_stop);
if let Some(hit) = earliest_stop_match_from(&self.full, &self.stops, search_start) {
let slice = &self.full[self.emitted..hit];
if !slice.is_empty() {
sink(slice);
}
self.full.truncate(hit);
self.emitted = self.full.len();
self.stopped = true;
return true;
}
let mut safe = self
.full
.len()
.saturating_sub(self.max_stop.saturating_sub(1));
safe = safe.max(self.emitted);
while safe > self.emitted && !self.full.is_char_boundary(safe) {
safe -= 1;
}
if safe > self.emitted {
sink(&self.full[self.emitted..safe]);
self.emitted = safe;
}
false
}
pub(crate) fn finish(&mut self, tail: &str, sink: &mut impl FnMut(&str)) {
if self.stopped {
return;
}
if !tail.is_empty() {
self.full.push_str(tail);
}
if let Some(hit) = earliest_stop_match(&self.full, &self.stops) {
let slice = &self.full[self.emitted..hit];
if !slice.is_empty() {
sink(slice);
}
self.full.truncate(hit);
self.emitted = self.full.len();
self.stopped = true;
return;
}
if self.emitted < self.full.len() {
sink(&self.full[self.emitted..]);
self.emitted = self.full.len();
}
}
pub(crate) fn stopped(&self) -> bool {
self.stopped
}
#[cfg(any(test, feature = "metal-gpu"))]
pub(crate) fn into_text(self) -> String {
self.full
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn earliest_stop_match_single_present() {
assert_eq!(
earliest_stop_match("hello world", &["world".to_string()]),
Some(6)
);
}
#[test]
fn earliest_stop_match_no_match() {
assert_eq!(
earliest_stop_match("hello world", &["foo".to_string()]),
None
);
}
#[test]
fn earliest_stop_match_multiple_returns_earliest() {
assert_eq!(
earliest_stop_match("hello world", &["world".to_string(), "lo".to_string()]),
Some(3)
);
}
#[test]
fn earliest_stop_match_at_index_zero() {
assert_eq!(
earliest_stop_match("stopword rest", &["stop".to_string()]),
Some(0)
);
}
#[test]
fn earliest_stop_match_multibyte_utf8() {
assert_eq!(
earliest_stop_match("世界hello", &["界".to_string()]),
Some(3)
);
}
#[test]
fn earliest_stop_match_empty_stops() {
assert_eq!(earliest_stop_match("hello", &[]), None);
}
#[test]
fn stop_streamer_stop_split_across_deltas_no_double_emit() {
let stops = vec!["World".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut all_emitted: Vec<String> = Vec::new();
let stopped1 = streamer.push("hel", &mut |s| all_emitted.push(s.to_string()));
assert!(!stopped1);
let stopped2 = streamer.push("lo W", &mut |s| all_emitted.push(s.to_string()));
assert!(!stopped2);
let stopped3 = streamer.push("orld!", &mut |s| all_emitted.push(s.to_string()));
assert!(stopped3, "stop should be detected on third delta");
let concatenated = all_emitted.join("");
assert_eq!(
concatenated, "hello ",
"emitted concat must equal pre-stop text exactly once (BUG 1 regression)"
);
assert_eq!(streamer.into_text(), "hello ");
}
#[test]
fn stop_streamer_stop_at_first_delta() {
let stops = vec!["Stop".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped = streamer.push("Stop now", &mut |s| emitted.push(s.to_string()));
assert!(stopped);
assert_eq!(emitted.join(""), "");
assert_eq!(streamer.into_text(), "");
}
#[test]
fn stop_streamer_no_stop_natural_end() {
let stops = vec!["zzz".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
streamer.push("abc", &mut |s| emitted.push(s.to_string()));
streamer.push("def", &mut |s| emitted.push(s.to_string()));
streamer.finish("", &mut |s| emitted.push(s.to_string()));
assert_eq!(emitted.join(""), "abcdef");
assert_eq!(streamer.into_text(), "abcdef");
}
#[test]
fn stop_streamer_multibyte_no_panic() {
let stops = vec!["STOP".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
streamer.push("世", &mut |s| emitted.push(s.to_string()));
streamer.push("界x", &mut |s| emitted.push(s.to_string()));
streamer.finish("", &mut |s| emitted.push(s.to_string()));
let concat = emitted.join("");
assert_eq!(concat, streamer.into_text());
}
#[test]
fn stop_streamer_stop_at_delta_boundary() {
let stops = vec!["STOP".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped1 = streamer.push("abc", &mut |s| emitted.push(s.to_string()));
assert!(!stopped1);
let stopped2 = streamer.push("STOP", &mut |s| emitted.push(s.to_string()));
assert!(stopped2);
assert_eq!(emitted.join(""), "abc");
assert_eq!(streamer.into_text(), "abc");
}
#[test]
fn stop_streamer_hold_back_emits_safe_prefix_then_finish() {
let stops = vec!["xyz".to_string()]; let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped = streamer.push("abcde", &mut |s| emitted.push(s.to_string()));
assert!(!stopped);
assert_eq!(emitted.join(""), "abc");
streamer.finish("", &mut |s| emitted.push(s.to_string()));
assert_eq!(emitted.join(""), "abcde");
assert_eq!(streamer.into_text(), "abcde");
}
#[test]
fn stop_streamer_finish_noop_after_stop() {
let stops = vec!["END".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped = streamer.push("helloENDextra", &mut |s| emitted.push(s.to_string()));
assert!(stopped);
streamer.finish("should_not_appear", &mut |s| emitted.push(s.to_string()));
assert_eq!(emitted.join(""), "hello");
assert_eq!(streamer.into_text(), "hello");
}
#[test]
fn stop_streamer_blocking_style_sink_into_buffer() {
let stops = vec!["STOP".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut buf = String::new();
let stopped = streamer.push("hello STOP world", &mut |s| buf.push_str(s));
assert!(stopped);
assert_eq!(buf, "hello ");
assert_eq!(streamer.into_text(), "hello ");
}
#[test]
fn stop_streamer_empty_stop_matches_on_empty_delta() {
let stops = vec![String::new()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped = streamer.push("", &mut |s| emitted.push(s.to_string()));
assert!(
stopped,
"an empty stop string must match at byte 0 even when the delta is empty \
(empty-delta early-return regression)"
);
assert_eq!(emitted.join(""), "");
assert_eq!(streamer.into_text(), "");
}
#[test]
fn stop_streamer_empty_stop_matches_after_leading_empty_deltas() {
let stops = vec![String::new()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped = streamer.push("", &mut |s| emitted.push(s.to_string()));
assert!(
stopped,
"empty stop must match on the first push regardless of prior empty pushes"
);
}
#[test]
fn stop_streamer_empty_delta_no_false_positive_for_nonempty_stop() {
let stops = vec!["STOP".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped1 = streamer.push("", &mut |s| emitted.push(s.to_string()));
assert!(
!stopped1,
"a non-empty stop string must not match an empty delta"
);
let stopped2 = streamer.push("hello STOP world", &mut |s| emitted.push(s.to_string()));
assert!(stopped2);
assert_eq!(emitted.join(""), "hello ");
assert_eq!(streamer.into_text(), "hello ");
}
#[test]
fn stop_streamer_empty_delta_mid_run_is_noop() {
let stops = vec!["World".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped1 = streamer.push("hello ", &mut |s| emitted.push(s.to_string()));
assert!(!stopped1);
let stopped_mid = streamer.push("", &mut |s| emitted.push(s.to_string()));
assert!(!stopped_mid);
let stopped2 = streamer.push("World!", &mut |s| emitted.push(s.to_string()));
assert!(stopped2);
assert_eq!(emitted.join(""), "hello ");
assert_eq!(streamer.into_text(), "hello ");
}
fn byte_level_token(byte_encoder: &[char], bytes: &[u8]) -> String {
bytes.iter().map(|&b| byte_encoder[b as usize]).collect()
}
#[test]
fn token_level_split_cjk_codepoint_decodes_without_mojibake() {
use crate::model::qwen35::detokenize::{IncrementalDetokenizer, bytes_to_unicode};
use crate::tokenizer::bpe::BpeTokenizer;
use std::collections::HashMap;
let byte_encoder = bytes_to_unicode();
let mut vocab: HashMap<String, u32> = HashMap::new();
vocab.insert(byte_level_token(&byte_encoder, &[0xE5, 0xA5]), 0);
vocab.insert(byte_level_token(&byte_encoder, &[0xBD]), 1);
let tokenizer = BpeTokenizer::from_vocab_and_merges(vocab, Vec::new())
.expect("byte-level split-CJK vocab builds");
let mut detok = IncrementalDetokenizer::new();
let delta0 = detok.push(&tokenizer, 0);
assert_eq!(
delta0, "",
"first 2 of 3 bytes of a CJK codepoint must buffer, not emit a replacement char"
);
let delta1 = detok.push(&tokenizer, 1);
assert_eq!(
delta1, "好",
"the completing byte must reveal the exact codepoint, not mojibake"
);
}
#[test]
fn stop_streamer_matches_split_cjk_stop_string_no_dropped_tail() {
use crate::model::qwen35::detokenize::{IncrementalDetokenizer, bytes_to_unicode};
use crate::tokenizer::bpe::BpeTokenizer;
use std::collections::HashMap;
let byte_encoder = bytes_to_unicode();
let mut vocab: HashMap<String, u32> = HashMap::new();
vocab.insert(byte_level_token(&byte_encoder, b"hi"), 2);
vocab.insert(byte_level_token(&byte_encoder, &[0xE5, 0xA5]), 0);
vocab.insert(byte_level_token(&byte_encoder, &[0xBD]), 1);
let tokenizer = BpeTokenizer::from_vocab_and_merges(vocab, Vec::new())
.expect("byte-level split-CJK vocab builds");
let stops = vec!["好".to_string()];
let mut detok = IncrementalDetokenizer::new();
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
for id in [2u32, 0, 1] {
let delta = detok.push(&tokenizer, id);
let stopped = streamer.push(&delta, &mut |s| emitted.push(s.to_string()));
if stopped {
break;
}
}
assert_eq!(
emitted.join(""),
"hi",
"must emit exactly the pre-stop ASCII prefix, no dropped tail and no \
CJK bytes leaking through before the codepoint completed"
);
assert_eq!(streamer.into_text(), "hi");
}
#[test]
fn search_start_helper_stays_within_max_stop_bound_of_prev_len() {
let max_stop = 6usize; let mut full = String::new();
for i in 0..5_000usize {
let prev_len = full.len();
full.push('a');
let search_start = stop_scan_search_start(&full, prev_len, max_stop);
let window = full.len() - search_start;
assert!(
window <= max_stop,
"iteration {i}: scan window {window} exceeds max_stop {max_stop} \
(search_start={search_start}, full.len()={})",
full.len()
);
}
}
#[test]
fn push_scan_bytes_stay_linear_via_production_path() {
reset_scan_bytes_counter();
let stops = vec!["ZZZZZZZZZZ_NEVER_MATCHES".to_string()]; let mut streamer = StopStringMatcher::new(&stops);
let delta = "aaaaaaaaaa"; let n = 5_000usize;
for _ in 0..n {
streamer.push(delta, &mut |_s| {});
}
let scanned = scan_bytes_counter();
let per_push_max = 24 + delta.len();
let bound = n * per_push_max * 2;
assert!(
scanned <= bound,
"push() scanned {scanned} bytes over {n} calls (bound {bound}); \
production `push()` must route through the bounded \
`earliest_stop_match_from`, not a full O(full.len()) rescan"
);
}
#[test]
#[ignore = "wall-clock timing bench, not a deterministic CI gate — run with --ignored"]
fn push_scaling_is_linear_not_quadratic_bench() {
use std::time::Instant;
fn run(tokens: usize) -> std::time::Duration {
let stops = vec!["ZZZZZZZZZZ_NEVER_MATCHES".to_string()]; let mut streamer = StopStringMatcher::new(&stops);
let delta = "aaaaaaaaaa"; let start = Instant::now();
for _ in 0..tokens {
streamer.push(delta, &mut |_s| {});
}
start.elapsed()
}
run(1_000);
let n = 20_000usize;
let t_n = run(n);
let t_4n = run(4 * n);
let ratio = t_4n.as_secs_f64() / t_n.as_secs_f64().max(1e-9);
assert!(
ratio < 8.0,
"4x input took {ratio:.2}x as long as 1x input (t_n={t_n:?}, t_4n={t_4n:?}); \
expected close to linear (~4x), not quadratic (~16x)"
);
}
#[test]
fn stop_longer_than_any_single_push() {
let stop = "a-very-long-stop-string-marker";
let stops = vec![stop.to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let prefix = "prefix text before ";
let mut stopped_at = None;
for (i, ch) in prefix.chars().chain(stop.chars()).enumerate() {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
let stopped = streamer.push(s, &mut |s| emitted.push(s.to_string()));
if stopped {
stopped_at = Some(i);
break;
}
}
assert!(stopped_at.is_some(), "stop string must eventually be found");
assert_eq!(emitted.join(""), prefix);
assert_eq!(streamer.into_text(), prefix);
}
#[test]
fn stop_split_across_three_pushes() {
let stops = vec!["ABCDEF".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let s1 = streamer.push("xy AB", &mut |s| emitted.push(s.to_string()));
assert!(!s1);
let s2 = streamer.push("CD", &mut |s| emitted.push(s.to_string()));
assert!(!s2);
let s3 = streamer.push("EF trailing", &mut |s| emitted.push(s.to_string()));
assert!(s3, "stop must be detected once the third push completes it");
assert_eq!(emitted.join(""), "xy ");
assert_eq!(streamer.into_text(), "xy ");
}
#[test]
fn multibyte_utf8_at_truncation_boundary_no_panic() {
let stops = vec!["STOP".to_string()]; let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
for delta in ["ab", "😀", "cd", "😀", "ef"] {
let stopped = streamer.push(delta, &mut |s| emitted.push(s.to_string()));
assert!(!stopped);
}
streamer.finish("", &mut |s| emitted.push(s.to_string()));
let concat = emitted.join("");
assert_eq!(concat, "ab😀cd😀ef");
assert_eq!(streamer.into_text(), "ab😀cd😀ef");
}
#[test]
fn stop_at_very_first_token() {
let stops = vec!["GO".to_string()];
let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped = streamer.push("GO!", &mut |s| emitted.push(s.to_string()));
assert!(stopped);
assert_eq!(emitted.join(""), "");
assert_eq!(streamer.into_text(), "");
}
#[test]
fn overlapping_stop_candidates_match_spans_push_boundary() {
let stops = vec!["abcab".to_string(), "cab".to_string()]; let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
let stopped1 = streamer.push("xxxab", &mut |s| emitted.push(s.to_string()));
assert!(!stopped1);
let stopped2 = streamer.push("cab", &mut |s| emitted.push(s.to_string()));
assert!(
stopped2,
"the boundary-spanning \"abcab\" match must be found"
);
assert_eq!(
emitted.join(""),
"xxx",
"text before the EARLIEST match (byte 3, \"abcab\") must be emitted exactly \
once, not the text before the later \"cab\" match at byte 5"
);
assert_eq!(streamer.into_text(), "xxx");
}
#[test]
fn repeated_near_miss_prefix_does_not_lose_live_partial_match() {
let stops = vec!["AAAAB".to_string()]; let mut streamer = StopStringMatcher::new(&stops);
let mut emitted: Vec<String> = Vec::new();
for i in 0..20 {
let stopped = streamer.push("A", &mut |s| emitted.push(s.to_string()));
assert!(!stopped, "no 'B' pushed yet; iteration {i} must not match");
}
let stopped = streamer.push("B", &mut |s| emitted.push(s.to_string()));
assert!(
stopped,
"the last 4 'A's plus this 'B' must complete \"AAAAB\" even though \
the 'A's arrived one at a time, 16 pushes before this one"
);
assert_eq!(emitted.join(""), "A".repeat(16));
assert_eq!(streamer.into_text(), "A".repeat(16));
}
}