subms_hyperloglog/
codec.rs1use crate::{HllError, HyperLogLog, MAX_PRECISION, MIN_PRECISION};
26
27pub const MAGIC: [u8; 4] = *b"SHLL";
29pub const FORMAT_VERSION: u8 = 1;
31
32pub(crate) const ENC_DENSE: u8 = 0;
33pub(crate) const ENC_SPARSE: u8 = 1;
34pub(crate) const HEADER_LEN: usize = 8;
35
36pub(crate) fn write_header(out: &mut Vec<u8>, encoding: u8, p: u32) {
37 out.extend_from_slice(&MAGIC);
38 out.push(FORMAT_VERSION);
39 out.push(encoding);
40 out.push(p as u8);
41 out.push(0);
42}
43
44pub(crate) fn read_header(bytes: &[u8]) -> Result<(u8, u32), HllError> {
46 if bytes.len() < HEADER_LEN {
47 return Err(HllError::Truncated {
48 expected: HEADER_LEN,
49 actual: bytes.len(),
50 });
51 }
52 if bytes[..4] != MAGIC {
53 return Err(HllError::BadMagic);
54 }
55 if bytes[4] != FORMAT_VERSION {
56 return Err(HllError::UnsupportedVersion(bytes[4]));
57 }
58 let p = u32::from(bytes[6]);
59 if !(MIN_PRECISION..=MAX_PRECISION).contains(&p) {
60 return Err(HllError::InvalidPrecision(p));
61 }
62 Ok((bytes[5], p))
63}
64
65pub(crate) fn read_u32(bytes: &[u8], at: usize) -> u32 {
66 u32::from_be_bytes([bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]])
67}
68
69impl HyperLogLog {
70 pub fn to_bytes(&self) -> Vec<u8> {
74 let mut out = Vec::with_capacity(HEADER_LEN + self.registers.len());
75 write_header(&mut out, ENC_DENSE, self.precision());
76 out.extend_from_slice(&self.registers);
77 out
78 }
79
80 pub fn from_bytes(bytes: &[u8]) -> Result<Self, HllError> {
84 let (encoding, p) = read_header(bytes)?;
85 if encoding != ENC_DENSE {
86 return Err(HllError::UnsupportedEncoding(encoding));
87 }
88 let m = 1usize << p;
89 let expected = HEADER_LEN + m;
90 if bytes.len() < expected {
91 return Err(HllError::Truncated {
92 expected,
93 actual: bytes.len(),
94 });
95 }
96 let mut hll = HyperLogLog::new(p);
97 hll.registers
98 .copy_from_slice(&bytes[HEADER_LEN..HEADER_LEN + m]);
99 Ok(hll)
100 }
101}
102
103#[cfg(test)]
104#[path = "codec_tests.rs"]
105mod tests;