use keyhog_core::SensitiveString;
pub(crate) const MIN_PRINTABLE_STRING_LEN: usize = 8;
pub(crate) fn extract_printable_strings(bytes: &[u8], min_len: usize) -> Vec<SensitiveString> {
let mut strings = Vec::new();
let mut current_string = String::with_capacity(64);
for &b in bytes {
if b.is_ascii_graphic() || b == b' ' || b == b'\t' {
current_string.push(b as char);
} else {
if current_string.len() >= min_len {
strings.push(SensitiveString::from(current_string.as_str()));
}
current_string.clear();
}
}
if current_string.len() >= min_len {
strings.push(SensitiveString::from(current_string.as_str()));
}
let mut wide = extract_utf16_runs(bytes, min_len, true);
wide.extend(extract_utf16_runs(bytes, min_len, false));
wide.sort_unstable_by(|a, b| a.start.cmp(&b.start).then_with(|| b.end.cmp(&a.end)));
let mut covered_end = 0;
wide.retain(|run| {
if run.start < covered_end {
false
} else {
covered_end = run.end;
true
}
});
strings.extend(wide.into_iter().map(|run| run.value));
strings.sort_by(|a, b| a.as_ref().cmp(b.as_ref()));
strings.dedup_by(|a, b| a.as_ref() == b.as_ref());
strings
}
pub(crate) fn join_sensitive_strings(parts: &[SensitiveString], sep: &str) -> SensitiveString {
let mut joined = String::new();
for (index, part) in parts.iter().enumerate() {
if index > 0 {
joined.push_str(sep);
}
joined.push_str(part.as_ref());
}
SensitiveString::from(joined)
}
struct Utf16Run {
value: SensitiveString,
start: usize,
end: usize,
}
fn extract_utf16_runs(bytes: &[u8], min_len: usize, little: bool) -> Vec<Utf16Run> {
let mut runs = Vec::new();
let mut current = String::with_capacity(64);
let mut run_start = 0;
let mut i = 0;
while i + 1 < bytes.len() {
let (a, b) = (bytes[i], bytes[i + 1]);
let (lo, hi) = if little { (a, b) } else { (b, a) };
if hi == 0 && (lo.is_ascii_graphic() || lo == b' ' || lo == b'\t') {
if current.is_empty() {
run_start = i;
}
current.push(lo as char);
i += 2;
} else {
if current.len() >= min_len {
runs.push(Utf16Run {
value: SensitiveString::from(current.as_str()),
start: run_start,
end: i,
});
}
current.clear();
i += 1;
}
}
if current.len() >= min_len {
runs.push(Utf16Run {
value: SensitiveString::from(current),
start: run_start,
end: i,
});
}
runs
}