Skip to main content

concept_store/
keys.rs

1//! A sorted `u64` key to `u32` ordinal table beside an artifact.
2//!
3//! A code system whose concepts have identifiers beyond their codes (the atom
4//! identifiers of `RxNorm`, the entity identifiers of ICD-11) keeps them here,
5//! each next to its concept ordinal. No spec governs the layout: our own
6//! design, little-endian, a magic and version, a count, then sorted
7//! `(key, ordinal)` pairs.
8
9use std::io::{self, Read, Write};
10
11const MAGIC: &[u8; 8] = b"FTKEYS\0\0";
12const VERSION: u32 = 1;
13
14/// A failure while reading or writing the table.
15#[derive(Debug, thiserror::Error)]
16pub enum KeyTableError {
17    /// An I/O failure.
18    #[error("key table I/O failed")]
19    Io(#[from] io::Error),
20    /// The bytes do not start with the table magic.
21    #[error("not a key table")]
22    Magic,
23    /// The layout version is not the one this build reads.
24    #[error("key table version {found}, expected {expected}")]
25    Version {
26        /// The version found.
27        found: u32,
28        /// The version this build reads.
29        expected: u32,
30    },
31}
32
33/// The sorted table.
34#[derive(Debug, Clone, PartialEq, Eq, Default)]
35pub struct KeyTable {
36    pairs: Vec<(u64, u32)>,
37}
38
39impl KeyTable {
40    /// Builds the table from `(key, ordinal)` pairs, in any order.
41    #[must_use]
42    pub fn new(mut pairs: Vec<(u64, u32)>) -> Self {
43        pairs.sort_unstable();
44        pairs.dedup();
45        Self { pairs }
46    }
47
48    /// The ordinal stored under `key`.
49    #[must_use]
50    pub fn get(&self, key: u64) -> Option<u32> {
51        self.pairs
52            .binary_search_by_key(&key, |(k, _)| *k)
53            .ok()
54            .and_then(|i| self.pairs.get(i))
55            .map(|(_, o)| *o)
56    }
57
58    /// The entry count.
59    #[must_use]
60    pub fn len(&self) -> usize {
61        self.pairs.len()
62    }
63
64    /// Whether the table is empty.
65    #[must_use]
66    pub fn is_empty(&self) -> bool {
67        self.pairs.is_empty()
68    }
69
70    /// Writes the layout.
71    ///
72    /// # Errors
73    ///
74    /// Returns [`KeyTableError::Io`] when writing fails.
75    pub fn write_to(&self, out: &mut impl Write) -> Result<(), KeyTableError> {
76        out.write_all(MAGIC)?;
77        out.write_all(&VERSION.to_le_bytes())?;
78        let len = u64::try_from(self.pairs.len()).map_err(|_| io::Error::other("too many keys"))?;
79        out.write_all(&len.to_le_bytes())?;
80        for (key, ordinal) in &self.pairs {
81            out.write_all(&key.to_le_bytes())?;
82            out.write_all(&ordinal.to_le_bytes())?;
83        }
84        Ok(())
85    }
86
87    /// Reads the layout.
88    ///
89    /// # Errors
90    ///
91    /// Returns [`KeyTableError`] for a truncated or foreign table.
92    pub fn read_from(input: &mut impl Read) -> Result<Self, KeyTableError> {
93        let mut magic = [0_u8; 8];
94        input.read_exact(&mut magic)?;
95        if &magic != MAGIC {
96            return Err(KeyTableError::Magic);
97        }
98        let mut word = [0_u8; 4];
99        input.read_exact(&mut word)?;
100        let version = u32::from_le_bytes(word);
101        if version != VERSION {
102            return Err(KeyTableError::Version {
103                found: version,
104                expected: VERSION,
105            });
106        }
107        let mut long = [0_u8; 8];
108        input.read_exact(&mut long)?;
109        let len = usize::try_from(u64::from_le_bytes(long))
110            .map_err(|_| io::Error::other("key table too large"))?;
111        let mut pairs = Vec::with_capacity(len);
112        for _ in 0..len {
113            input.read_exact(&mut long)?;
114            input.read_exact(&mut word)?;
115            pairs.push((u64::from_le_bytes(long), u32::from_le_bytes(word)));
116        }
117        Ok(Self { pairs })
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::{KeyTable, KeyTableError};
124
125    #[test]
126    fn the_table_answers_by_key_and_round_trips() {
127        let keys = KeyTable::new(vec![(829, 0), (12_251_526, 1), (2_798_745, 1), (829, 0)]);
128        assert_eq!(keys.len(), 3);
129        assert_eq!(keys.get(2_798_745), Some(1));
130        assert_eq!(keys.get(1), None);
131        let mut bytes = Vec::new();
132        keys.write_to(&mut bytes).expect("writes");
133        assert_eq!(
134            KeyTable::read_from(&mut bytes.as_slice()).expect("reads"),
135            keys
136        );
137        assert!(matches!(
138            KeyTable::read_from(&mut b"XXXXXXXX\0\0\0\0".as_slice()),
139            Err(KeyTableError::Magic)
140        ));
141    }
142}