const CHUNK_SEP: &str = "#c";
pub const MAX_CHUNKS: usize = 16;
pub fn chunk_key(rid: &str, idx: usize) -> String {
debug_assert!(idx >= 1, "chunk 0 lives under the plain rid");
format!("{rid}{CHUNK_SEP}{idx}")
}
pub fn parent_of(key: &str) -> &str {
parent_and_idx(key).0
}
pub fn parent_and_idx(key: &str) -> (&str, u32) {
if let Some(pos) = key.rfind(CHUNK_SEP) {
let suffix = &key[pos + CHUNK_SEP.len()..];
if !suffix.is_empty() && suffix.bytes().all(|b| b.is_ascii_digit()) {
if let Ok(idx) = suffix.parse::<u32>() {
return (&key[..pos], idx);
}
}
}
(key, 0)
}
pub fn collapse_to_parents(results: Vec<(String, f64)>) -> Vec<(String, f64)> {
collapse_to_parents_indexed(results)
.into_iter()
.map(|(rid, dist, _)| (rid, dist))
.collect()
}
pub fn collapse_to_parents_indexed(results: Vec<(String, f64)>) -> Vec<(String, f64, u32)> {
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
let mut out: Vec<(String, f64, u32)> = Vec::with_capacity(results.len());
for (key, dist) in results {
let (parent, idx) = parent_and_idx(&key);
if seen.insert(parent.to_string()) {
out.push((parent.to_string(), dist, idx));
}
}
out
}
pub fn chunk_ranges(text: &str, window: usize) -> Vec<(usize, usize)> {
let len = text.len();
if window == 0 || len <= window {
return Vec::new();
}
let overlap = (window / 5).max(1);
let stride = window - overlap;
let mut out = Vec::new();
let mut start = stride;
let mut covered_to = window;
while start < len && out.len() < MAX_CHUNKS {
let end = (start + window).min(len);
if end <= covered_to + overlap.min(len.saturating_sub(covered_to)) && end < len {
break;
}
if end == len && end.saturating_sub(covered_to) < overlap {
break;
}
let a = floor_char_boundary(text, start);
let b = floor_char_boundary(text, end).max(a);
if b > a {
out.push((a, b));
}
covered_to = end;
if end == len {
break;
}
start += stride;
}
out
}
fn floor_char_boundary(s: &str, i: usize) -> usize {
if i >= s.len() {
return s.len();
}
let mut j = i;
while j > 0 && !s.is_char_boundary(j) {
j -= 1;
}
j
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunk_key_roundtrips_through_parent_of() {
let rid = "019fcea7-f941-7d9d-bc96-9882a8704026";
for idx in [1, 2, 9, 16] {
assert_eq!(parent_of(&chunk_key(rid, idx)), rid);
}
}
#[test]
fn plain_rid_is_its_own_parent() {
let rid = "019fcea7-f941-7d9d-bc96-9882a8704026";
assert_eq!(parent_of(rid), rid);
}
#[test]
fn non_numeric_suffix_is_not_a_chunk_key() {
assert_eq!(parent_of("note#cool"), "note#cool");
assert_eq!(parent_of("x#c"), "x#c");
assert_eq!(parent_of("x#c1a"), "x#c1a");
}
#[test]
fn collapse_keeps_best_window_per_parent_in_order() {
let results = vec![
("a#c2".to_string(), 0.10),
("b".to_string(), 0.20),
("a".to_string(), 0.30),
("b#c1".to_string(), 0.40),
("c#c3".to_string(), 0.50),
];
let collapsed = collapse_to_parents(results);
assert_eq!(
collapsed,
vec![
("a".to_string(), 0.10),
("b".to_string(), 0.20),
("c".to_string(), 0.50),
]
);
}
#[test]
fn short_text_needs_no_chunks() {
assert!(chunk_ranges("hello", 100).is_empty());
let exactly = "x".repeat(100);
assert!(chunk_ranges(&exactly, 100).is_empty());
}
#[test]
fn ranges_cover_the_tail() {
let text = "y".repeat(1000);
let ranges = chunk_ranges(&text, 300);
assert!(!ranges.is_empty());
assert_eq!(ranges.last().unwrap().1, 1000);
for (a, b) in &ranges {
assert!(b > a && b - a <= 300);
}
let mut covered = 300; for (a, b) in &ranges {
assert!(*a <= covered, "gap before {a} (covered to {covered})");
covered = covered.max(*b);
}
assert_eq!(covered, 1000);
}
#[test]
fn tiny_tail_sliver_is_dropped() {
let text = "z".repeat(310);
assert!(chunk_ranges(&text, 300).is_empty());
}
#[test]
fn chunk_count_is_capped() {
let text = "w".repeat(1_000_000);
assert!(chunk_ranges(&text, 300).len() <= MAX_CHUNKS);
}
#[test]
fn ranges_respect_utf8_boundaries() {
let text = "é".repeat(500); for (a, b) in chunk_ranges(&text, 300) {
let _ = &text[a..b]; }
}
}