use std::collections::hash_map::DefaultHasher;
use std::convert::TryInto;
use std::hash::{Hash, Hasher};
use std::io::BufRead;
use std::mem;
use bstr::io::BufReadExt;
use memchr::memchr;
pub fn shard<R, F>(r: R, npartitions: usize, bufsize: usize, mut f: F)
where
R: BufRead,
F: FnMut(usize, Vec<u8>),
{
let mut used_space = 0;
let mut bufs = vec![Vec::new(); npartitions];
let npartitions: u64 = npartitions.try_into().unwrap();
r.for_byte_line_with_terminator(|line| {
let key = hash_key(line, npartitions);
used_space += line.len();
bufs[key].extend_from_slice(line);
if used_space >= bufsize {
for (i, buf) in bufs.iter_mut().enumerate() {
if buf.len() > 0 {
f(i, mem::take(buf));
}
}
used_space = 0;
}
Ok(true)
})
.expect("successful byte line read");
for (i, buf) in bufs.into_iter().enumerate() {
if buf.len() > 0 {
f(i, buf)
}
}
}
fn hash_key(bytes: &[u8], npartitions: u64) -> usize {
let end = memchr(b' ', bytes).unwrap_or(bytes.len());
let mut hasher = DefaultHasher::default();
bytes[..end].hash(&mut hasher);
(hasher.finish() % npartitions) as usize
}