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