pub(super) const WINDOW_WORDS: usize = 250;
pub(super) const WINDOW_OVERLAP_WORDS: usize = 40;
pub(super) fn first_subword_labels(
word_ids: &[Option<u32>],
argmax_per_token: &[usize],
num_words: usize,
) -> Vec<usize> {
let mut labels = vec![0usize; num_words];
let mut seen = vec![false; num_words];
for (tok_idx, wid) in word_ids.iter().enumerate() {
let Some(w) = wid else { continue };
let w = *w as usize;
if w < num_words && !seen[w] {
seen[w] = true;
labels[w] = argmax_per_token.get(tok_idx).copied().unwrap_or(0);
}
}
labels
}
pub(super) fn word_spans(text: &str) -> Vec<(usize, usize)> {
let mut spans = Vec::new();
let mut start: Option<usize> = None;
for (i, c) in text.char_indices() {
if c.is_whitespace() {
if let Some(s) = start.take() {
spans.push((s, i));
}
} else if start.is_none() {
start = Some(i);
}
}
if let Some(s) = start {
spans.push((s, text.len()));
}
spans
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Window {
pub(super) start: usize,
pub(super) end: usize,
pub(super) keep_start: usize,
pub(super) keep_end: usize,
}
pub(super) fn plan_windows(num_words: usize) -> Vec<Window> {
if num_words == 0 {
return Vec::new();
}
if num_words <= WINDOW_WORDS {
return vec![Window {
start: 0,
end: num_words,
keep_start: 0,
keep_end: num_words,
}];
}
let stride = WINDOW_WORDS - WINDOW_OVERLAP_WORDS;
let half = WINDOW_OVERLAP_WORDS / 2;
let mut windows = Vec::new();
let mut start = 0usize;
loop {
let end = (start + WINDOW_WORDS).min(num_words);
let is_last = end == num_words;
windows.push(Window {
start,
end,
keep_start: if start == 0 { 0 } else { start + half },
keep_end: if is_last { num_words } else { end - half },
});
if is_last {
break;
}
start += stride;
}
windows
}
pub(super) fn splice_window_labels(
windows: &[Window],
per_window: &[Option<Vec<usize>>],
num_words: usize,
) -> Vec<Option<usize>> {
let mut merged = vec![None; num_words];
for (window, labels) in windows.iter().zip(per_window.iter()) {
let Some(labels) = labels else { continue };
let keep_end = window.keep_end.min(num_words);
let keep_start = window.keep_start.min(keep_end);
let Some(base) = keep_start.checked_sub(window.start) else {
continue;
};
for (offset, slot) in merged[keep_start..keep_end].iter_mut().enumerate() {
*slot = labels.get(base + offset).copied();
}
}
merged
}