hff_core/utilities/
ksv.rs

1use super::StringVec;
2use crate::{
3    byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt},
4    Ecc, Endian, Error, Result, BE, LE, NE,
5};
6use std::{
7    collections::BTreeMap,
8    io::{copy, Read, Write},
9    ops::{Deref, DerefMut},
10};
11
12/// A key + string vector container.  Basically
13/// just a key+value system except keys are strings
14/// and the values are all string vectors.
15#[derive(Debug, Clone, Eq, PartialEq)]
16pub struct Ksv {
17    /// The key+value mapping.
18    value_map: BTreeMap<String, StringVec>,
19}
20
21impl Ksv {
22    /// Ecc identifier type.
23    const ID: Ecc = Ecc::new("STR_VEC");
24
25    /// Create a new empty Ksv.
26    pub fn new() -> Self {
27        Self {
28            value_map: BTreeMap::new(),
29        }
30    }
31
32    /// Convert the Ksv to bytes.
33    pub fn to_bytes<E: ByteOrder>(self) -> Result<Vec<u8>> {
34        let mut bytes = vec![];
35        let writer: &mut dyn Write = &mut bytes;
36
37        Self::ID.write::<E>(writer)?;
38        writer.write_u64::<E>(self.value_map.len() as u64)?;
39
40        for (k, v) in self.value_map {
41            // Write the key.
42            let kbytes = k.as_bytes();
43            writer.write_u64::<E>(kbytes.len() as u64)?;
44            writer.write_all(kbytes)?;
45
46            // Write the string vector.
47            let vbytes = v.to_bytes::<E>()?;
48            writer.write_u64::<E>(vbytes.len() as u64)?;
49            copy(&mut vbytes.as_slice(), writer)?;
50        }
51
52        Ok(bytes)
53    }
54
55    /// Create a Ksv from the given bytes.
56    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self> {
57        // Detect the endian via the initial ID.
58        let reader: &mut dyn Read = &mut bytes;
59        // Ecc's are written as u64, so we read it back in
60        // native endian and see which endian it was actually
61        // in.  (NOTE: Symetric ID's would not work for this
62        // so don't try it if you had an id like "ssssssss"
63        // since there is no way to detect endianess.)
64        let id = Ecc::from(reader.read_u64::<NE>()?);
65        match id.endian(Self::ID) {
66            Some(endian) => match endian {
67                Endian::Big => Self::from_bytes_endian::<BE>(reader),
68                Endian::Little => Self::from_bytes_endian::<LE>(reader),
69            },
70            None => Err(Error::Invalid("Not a string vector.".into())),
71        }
72    }
73
74    /// Helper to read in proper endian.
75    fn from_bytes_endian<E: ByteOrder>(reader: &mut dyn Read) -> Result<Self> {
76        let count = reader.read_u64::<E>()?;
77        let mut result = Self::new();
78        for _ in 0..count {
79            // Read the key.
80            let len = reader.read_u64::<E>()?;
81            let mut s = vec![0; len as usize];
82            reader.read_exact(&mut s)?;
83
84            // Read the string vector value.
85            let len = reader.read_u64::<E>()?;
86            let mut v = vec![0; len as usize];
87            reader.read_exact(&mut v)?;
88            let v = StringVec::from_bytes(&v)?;
89
90            // And put in the result.
91            result.insert(std::str::from_utf8(&s)?.to_owned(), v);
92        }
93
94        Ok(result)
95    }
96}
97
98impl<I, S> From<I> for Ksv
99where
100    I: Iterator<Item = (S, StringVec)>,
101    S: AsRef<str>,
102{
103    fn from(value: I) -> Self {
104        let mut result = Self::new();
105        for (k, v) in value {
106            result.insert(k.as_ref().to_owned(), v);
107        }
108        result
109    }
110}
111
112impl Deref for Ksv {
113    type Target = BTreeMap<String, StringVec>;
114
115    fn deref(&self) -> &Self::Target {
116        &self.value_map
117    }
118}
119
120impl DerefMut for Ksv {
121    fn deref_mut(&mut self) -> &mut Self::Target {
122        &mut self.value_map
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn simple() {
132        let test_data: &[(&'static str, StringVec)] = &[
133            ("1", ["this", "is"].iter().into()),
134            ("2", ["test", "data"].iter().into()),
135            ("3", ["for", "Ksv", "containers."].iter().into()),
136        ];
137        let test: Ksv = test_data.to_owned().into_iter().into();
138        let bytes = test.to_bytes::<LE>().unwrap();
139        let result = Ksv::from_bytes(&bytes).unwrap();
140
141        assert_eq!(Ksv::from(test_data.to_owned().into_iter()), result);
142    }
143}