use rudb_common::{Error, Result};
const SEGMENT: usize = 256 * 1024;
const MIN_MATCH: usize = 4;
const HASH_BITS: u32 = 16;
const MAX_TRIES: usize = 32;
pub(crate) struct Tokens<'a> {
pub literals: Vec<&'a [u8]>,
pub lengths: Vec<i64>,
pub offsets: Vec<i64>,
}
#[derive(Default)]
struct Raw {
runs: Vec<(usize, usize)>,
lengths: Vec<i64>,
offsets: Vec<i64>,
}
pub(crate) fn tokens_of(input: &[u8]) -> Tokens<'_> {
let mut raw = Raw::default();
let mut head = vec![u32::MAX; 1 << HASH_BITS];
let span = SEGMENT.min(input.len()).max(1);
let mut prev = vec![u32::MAX; span];
let mut start = 0;
while start < input.len() {
let end = (start + SEGMENT).min(input.len());
head.fill(u32::MAX);
matches_in(input, start, end, &mut head, &mut prev, &mut raw);
start = end;
}
Tokens {
literals: raw.runs.iter().map(|(from, to)| &input[*from..*to]).collect(),
lengths: raw.lengths,
offsets: raw.offsets,
}
}
fn matches_in(
input: &[u8],
start: usize,
end: usize,
head: &mut [u32],
prev: &mut [u32],
raw: &mut Raw,
) {
let mut literal_start = start;
let mut at = start;
while at < end {
if at + MIN_MATCH > end {
break;
}
let key = hash(&input[at..at + MIN_MATCH]);
let found = longest(input, at, end, head[key], prev, start);
insert(input, at, end, head, prev, start);
match found {
Some((length, offset)) => {
push(raw, (literal_start, at), length, offset);
for step in 1..length {
insert(input, at + step, end, head, prev, start);
}
at += length;
literal_start = at;
}
None => at += 1,
}
}
if literal_start < end {
push(raw, (literal_start, end), 0, 0);
}
}
fn longest(
input: &[u8],
at: usize,
end: usize,
mut candidate: u32,
prev: &[u32],
start: usize,
) -> Option<(usize, usize)> {
let mut best: Option<(usize, usize)> = None;
let mut tries = 0;
while candidate != u32::MAX && tries < MAX_TRIES {
let position = start + candidate as usize;
if position >= at {
break;
}
let length = shared(&input[position..end], &input[at..end]);
if length >= MIN_MATCH && best.is_none_or(|(had, _)| length > had) {
best = Some((length, at - position));
}
candidate = prev[candidate as usize];
tries += 1;
}
best
}
fn insert(input: &[u8], at: usize, end: usize, head: &mut [u32], prev: &mut [u32], start: usize) {
if at + MIN_MATCH > end {
return;
}
let key = hash(&input[at..at + MIN_MATCH]);
let slot = at - start;
prev[slot] = head[key];
head[key] = slot as u32;
}
fn push(raw: &mut Raw, run: (usize, usize), length: usize, offset: usize) {
raw.runs.push(run);
raw.lengths.push(length as i64);
raw.offsets.push(offset as i64);
}
pub(crate) fn rebuild(
literals: &[Vec<u8>],
lengths: &[i64],
offsets: &[i64],
total: usize,
) -> Result<Vec<u8>> {
if literals.len() != lengths.len() || lengths.len() != offsets.len() {
return Err(Error::internal(format!(
"a matched chunk has {} literal runs, {} lengths and {} offsets",
literals.len(),
lengths.len(),
offsets.len()
)));
}
let mut out = Vec::with_capacity(total);
for (index, run) in literals.iter().enumerate() {
out.extend_from_slice(run);
let length = usize::try_from(lengths[index])
.map_err(|_| Error::internal("a negative copy length"))?;
if length == 0 {
continue;
}
let offset = usize::try_from(offsets[index])
.map_err(|_| Error::internal("a negative copy offset"))?;
if offset == 0 || offset > out.len() {
return Err(Error::internal(format!(
"a copy reaches {offset} bytes back into {} bytes of output",
out.len()
)));
}
let from = out.len() - offset;
for step in 0..length {
let byte = out[from + step];
out.push(byte);
}
}
Ok(out)
}
fn hash(bytes: &[u8]) -> usize {
let word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
(word.wrapping_mul(2_654_435_761) >> (32 - HASH_BITS)) as usize
}
fn shared(a: &[u8], b: &[u8]) -> usize {
let cap = a.len().min(b.len());
let mut n = 0;
while n < cap && a[n] == b[n] {
n += 1;
}
n
}
#[cfg(test)]
mod tests {
use super::*;
fn round_trip(input: &[u8]) {
let tokens = tokens_of(input);
let owned: Vec<Vec<u8>> = tokens.literals.iter().map(|run| run.to_vec()).collect();
let back = rebuild(&owned, &tokens.lengths, &tokens.offsets, input.len()).unwrap();
assert_eq!(back, input, "{} tokens", tokens.lengths.len());
}
#[test]
fn nothing_round_trips() {
round_trip(b"");
}
#[test]
fn something_with_no_repeats_round_trips() {
round_trip(b"abcdefghijklmnop");
}
#[test]
fn a_repeat_becomes_a_copy() {
let input = b"the same sentence twice, the same sentence twice";
let tokens = tokens_of(input);
assert!(tokens.lengths.iter().any(|length| *length >= MIN_MATCH as i64), "no copy emitted");
round_trip(input);
}
#[test]
fn a_run_of_one_byte_is_one_overlapping_copy() {
let input = vec![b'x'; 4096];
round_trip(&input);
let tokens = tokens_of(&input);
assert!(tokens.lengths.len() < 8, "{} tokens for one repeated byte", tokens.lengths.len());
}
#[test]
fn something_longer_than_a_segment_round_trips() {
let mut input = Vec::new();
while input.len() < SEGMENT * 2 + 1234 {
input.extend_from_slice(b"http://example.com/some/path?query=value&more=stuff ");
}
round_trip(&input);
}
#[test]
fn urls_compress() {
let mut input = Vec::new();
for n in 0..4000 {
input.extend_from_slice(format!("http://example.com/page/{n}?ref=search\n").as_bytes());
}
let tokens = tokens_of(&input);
let literal_bytes: usize = tokens.literals.iter().map(|run| run.len()).sum();
assert!(literal_bytes * 4 < input.len(), "{literal_bytes} literal of {}", input.len());
round_trip(&input);
}
#[test]
fn a_copy_that_reaches_too_far_is_refused() {
let literals = vec![b"ab".to_vec()];
assert!(rebuild(&literals, &[4], &[99], 6).is_err());
}
#[test]
fn streams_of_different_lengths_are_refused() {
let literals = vec![b"ab".to_vec()];
assert!(rebuild(&literals, &[0, 0], &[0, 0], 2).is_err());
}
}