use std::io::{self, Read, Write};
const MAGIC: &[u8; 8] = b"FTKEYS\0\0";
const VERSION: u32 = 1;
#[derive(Debug, thiserror::Error)]
pub enum KeyTableError {
#[error("key table I/O failed")]
Io(#[from] io::Error),
#[error("not a key table")]
Magic,
#[error("key table version {found}, expected {expected}")]
Version {
found: u32,
expected: u32,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct KeyTable {
pairs: Vec<(u64, u32)>,
}
impl KeyTable {
#[must_use]
pub fn new(mut pairs: Vec<(u64, u32)>) -> Self {
pairs.sort_unstable();
pairs.dedup();
Self { pairs }
}
#[must_use]
pub fn get(&self, key: u64) -> Option<u32> {
self.pairs
.binary_search_by_key(&key, |(k, _)| *k)
.ok()
.and_then(|i| self.pairs.get(i))
.map(|(_, o)| *o)
}
#[must_use]
pub fn len(&self) -> usize {
self.pairs.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.pairs.is_empty()
}
pub fn write_to(&self, out: &mut impl Write) -> Result<(), KeyTableError> {
out.write_all(MAGIC)?;
out.write_all(&VERSION.to_le_bytes())?;
let len = u64::try_from(self.pairs.len()).map_err(|_| io::Error::other("too many keys"))?;
out.write_all(&len.to_le_bytes())?;
for (key, ordinal) in &self.pairs {
out.write_all(&key.to_le_bytes())?;
out.write_all(&ordinal.to_le_bytes())?;
}
Ok(())
}
pub fn read_from(input: &mut impl Read) -> Result<Self, KeyTableError> {
let mut magic = [0_u8; 8];
input.read_exact(&mut magic)?;
if &magic != MAGIC {
return Err(KeyTableError::Magic);
}
let mut word = [0_u8; 4];
input.read_exact(&mut word)?;
let version = u32::from_le_bytes(word);
if version != VERSION {
return Err(KeyTableError::Version {
found: version,
expected: VERSION,
});
}
let mut long = [0_u8; 8];
input.read_exact(&mut long)?;
let len = usize::try_from(u64::from_le_bytes(long))
.map_err(|_| io::Error::other("key table too large"))?;
let mut pairs = Vec::with_capacity(len);
for _ in 0..len {
input.read_exact(&mut long)?;
input.read_exact(&mut word)?;
pairs.push((u64::from_le_bytes(long), u32::from_le_bytes(word)));
}
Ok(Self { pairs })
}
}
#[cfg(test)]
mod tests {
use super::{KeyTable, KeyTableError};
#[test]
fn the_table_answers_by_key_and_round_trips() {
let keys = KeyTable::new(vec![(829, 0), (12_251_526, 1), (2_798_745, 1), (829, 0)]);
assert_eq!(keys.len(), 3);
assert_eq!(keys.get(2_798_745), Some(1));
assert_eq!(keys.get(1), None);
let mut bytes = Vec::new();
keys.write_to(&mut bytes).expect("writes");
assert_eq!(
KeyTable::read_from(&mut bytes.as_slice()).expect("reads"),
keys
);
assert!(matches!(
KeyTable::read_from(&mut b"XXXXXXXX\0\0\0\0".as_slice()),
Err(KeyTableError::Magic)
));
}
}