1use std::io::{self, Read, Write};
13
14const MAGIC: &[u8; 8] = b"FTKEYS\0\0";
15const VERSION: u32 = 1;
16
17#[derive(Debug, thiserror::Error)]
19#[error("{what} exceeds the integer the key table layout stores")]
20struct Overflow {
21 what: &'static str,
22 #[source]
23 source: std::num::TryFromIntError,
24}
25
26#[derive(Debug, thiserror::Error)]
28pub enum KeyTableError {
29 #[error("key table I/O failed")]
31 Io(#[from] io::Error),
32 #[error("not a key table")]
34 Magic,
35 #[error("key table version {found}, expected {expected}")]
37 Version {
38 found: u32,
40 expected: u32,
42 },
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Default)]
47pub struct KeyTable {
48 pairs: Vec<(u64, u32)>,
49}
50
51impl KeyTable {
52 #[must_use]
54 pub fn new(mut pairs: Vec<(u64, u32)>) -> Self {
55 pairs.sort_unstable();
56 pairs.dedup();
57 Self { pairs }
58 }
59
60 #[must_use]
62 pub fn get(&self, key: u64) -> Option<u32> {
63 self.pairs
64 .binary_search_by_key(&key, |(k, _)| *k)
65 .ok()
66 .and_then(|i| self.pairs.get(i))
67 .map(|(_, o)| *o)
68 }
69
70 #[must_use]
72 pub fn len(&self) -> usize {
73 self.pairs.len()
74 }
75
76 #[must_use]
78 pub fn is_empty(&self) -> bool {
79 self.pairs.is_empty()
80 }
81
82 pub fn write_to(&self, out: &mut impl Write) -> Result<(), KeyTableError> {
88 out.write_all(MAGIC)?;
89 out.write_all(&VERSION.to_le_bytes())?;
90 let len = u64::try_from(self.pairs.len()).map_err(|source| {
91 io::Error::other(Overflow {
92 what: "the key count",
93 source,
94 })
95 })?;
96 out.write_all(&len.to_le_bytes())?;
97 for (key, ordinal) in &self.pairs {
98 out.write_all(&key.to_le_bytes())?;
99 out.write_all(&ordinal.to_le_bytes())?;
100 }
101 Ok(())
102 }
103
104 pub fn read_from(input: &mut impl Read) -> Result<Self, KeyTableError> {
110 let mut magic = [0_u8; 8];
111 input.read_exact(&mut magic)?;
112 if &magic != MAGIC {
113 return Err(KeyTableError::Magic);
114 }
115 let mut word = [0_u8; 4];
116 input.read_exact(&mut word)?;
117 let version = u32::from_le_bytes(word);
118 if version != VERSION {
119 return Err(KeyTableError::Version {
120 found: version,
121 expected: VERSION,
122 });
123 }
124 let mut long = [0_u8; 8];
125 input.read_exact(&mut long)?;
126 let len = usize::try_from(u64::from_le_bytes(long)).map_err(|source| {
127 io::Error::other(Overflow {
128 what: "the key count",
129 source,
130 })
131 })?;
132 let mut pairs = Vec::with_capacity(len);
133 for _ in 0..len {
134 input.read_exact(&mut long)?;
135 input.read_exact(&mut word)?;
136 pairs.push((u64::from_le_bytes(long), u32::from_le_bytes(word)));
137 }
138 Ok(Self { pairs })
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::{KeyTable, KeyTableError};
145
146 #[test]
147 fn the_table_answers_by_key_and_round_trips() {
148 let keys = KeyTable::new(vec![(829, 0), (12_251_526, 1), (2_798_745, 1), (829, 0)]);
149 assert_eq!(keys.len(), 3);
150 assert_eq!(keys.get(2_798_745), Some(1));
151 assert_eq!(keys.get(1), None);
152 let mut bytes = Vec::new();
153 keys.write_to(&mut bytes).expect("writes");
154 assert_eq!(
155 KeyTable::read_from(&mut bytes.as_slice()).expect("reads"),
156 keys
157 );
158 assert!(matches!(
159 KeyTable::read_from(&mut b"XXXXXXXX\0\0\0\0".as_slice()),
160 Err(KeyTableError::Magic)
161 ));
162 }
163}