Skip to main content

nodedb_types/surrogate_bitmap/
codec.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! zerompk `ToMessagePack` / `FromMessagePack` implementations for
4//! [`SurrogateBitmap`].
5//!
6//! Wire encoding: a single msgpack `bin` value whose bytes are the
7//! portable roaring-bitmap serialization produced by
8//! `RoaringBitmap::serialize_into`. This is the most compact format
9//! that survives cross-platform round-trips and can be decoded without
10//! allocating an intermediate `Vec<u8>` on the read side.
11//!
12//! The zerompk `bin` type maps to msgpack's binary ext (`0xc4`/`0xc5`/`0xc6`
13//! family). Note: zerompk's derive macro does **not** use this family for
14//! plain `Vec<u8>` fields — by default it routes through the generic
15//! `impl<T: ToMessagePack> ToMessagePack for Vec<T>` which writes a
16//! MessagePack array (fixarray / array16 / array32) with each `u8` as a
17//! `uint8` marker. The derive macro only emits `write_binary` /
18//! `read_binary` when the field carries the `#[as_bytes]` attribute.
19//! Manually implementing the binary encoding here keeps `SurrogateBitmap`
20//! interchangeable with the hand-rolled binary `Option<Vec<u8>>` fields
21//! in `TextFields` (which call `writer.write_binary` directly to dodge
22//! the array fallback).
23
24use std::io::Cursor;
25
26use roaring::RoaringBitmap;
27use zerompk::{FromMessagePack, ToMessagePack};
28
29use super::bitmap::SurrogateBitmap;
30
31impl ToMessagePack for SurrogateBitmap {
32    fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
33        // Serialise the roaring bitmap to a byte vec.
34        let mut buf = Vec::with_capacity(self.0.serialized_size());
35        self.0
36            .serialize_into(&mut buf)
37            .map_err(zerompk::Error::IoError)?;
38        // Write as msgpack `bin`.
39        writer.write_binary(&buf)
40    }
41}
42
43impl<'a> FromMessagePack<'a> for SurrogateBitmap {
44    fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
45        let cow = reader.read_binary()?;
46        let bytes: &[u8] = &cow;
47        let inner =
48            RoaringBitmap::deserialize_from(Cursor::new(bytes)).map_err(zerompk::Error::IoError)?;
49        Ok(SurrogateBitmap(inner))
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use crate::surrogate::Surrogate;
56    use crate::surrogate_bitmap::SurrogateBitmap;
57
58    fn roundtrip(b: &SurrogateBitmap) -> SurrogateBitmap {
59        let bytes = zerompk::to_msgpack_vec(b).expect("serialize");
60        zerompk::from_msgpack(&bytes).expect("deserialize")
61    }
62
63    #[test]
64    fn empty_bitmap_codec_roundtrip() {
65        let b = SurrogateBitmap::new();
66        let b2 = roundtrip(&b);
67        assert_eq!(b, b2);
68    }
69
70    #[test]
71    fn small_bitmap_codec_roundtrip() {
72        let b = SurrogateBitmap::from_iter([1, 2, 3, 100, 200].map(Surrogate));
73        let b2 = roundtrip(&b);
74        assert_eq!(b, b2);
75    }
76
77    #[test]
78    fn large_bitmap_codec_roundtrip() {
79        let b = SurrogateBitmap::from_iter((1u32..=10_000).map(Surrogate));
80        let b2 = roundtrip(&b);
81        assert_eq!(b, b2);
82    }
83}