Skip to main content

reth_db_api/models/
integer_list.rs

1//! Implements [`Compress`] and [`Decompress`] for [`IntegerList`]
2
3use crate::table::{Compress, Decompress};
4use bytes::BufMut;
5use core::{fmt, ops::RangeBounds};
6use derive_more::Deref;
7use reth_codecs::DecompressError;
8use roaring::RoaringTreemap;
9use serde::ser::SerializeSeq;
10
11/// A data structure that uses Roaring Bitmaps to efficiently store a list of integers.
12///
13/// This structure provides excellent compression while allowing direct access to individual
14/// elements without the need for full decompression.
15///
16/// Key features:
17/// - Efficient compression: the underlying Roaring Bitmaps significantly reduce memory usage.
18/// - Direct access: elements can be accessed or queried without needing to decode the entire list.
19/// - [`RoaringTreemap`] backing: internally backed by [`RoaringTreemap`], which supports 64-bit
20///   integers.
21#[derive(Clone, PartialEq, Eq, Default, Deref)]
22pub struct IntegerList(pub RoaringTreemap);
23
24impl fmt::Debug for IntegerList {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.write_str("IntegerList")?;
27        f.debug_list().entries(self.0.iter()).finish()
28    }
29}
30
31impl IntegerList {
32    /// Creates a new empty [`IntegerList`].
33    pub fn empty() -> Self {
34        Self(RoaringTreemap::new())
35    }
36
37    /// Creates an [`IntegerList`] from a list of integers.
38    ///
39    /// Returns an error if the list is not pre-sorted.
40    pub fn new(list: impl IntoIterator<Item = u64>) -> Result<Self, IntegerListError> {
41        RoaringTreemap::from_sorted_iter(list)
42            .map(Self)
43            .map_err(|_| IntegerListError::UnsortedInput)
44    }
45
46    /// Creates an [`IntegerList`] from a pre-sorted list of integers.
47    ///
48    /// # Panics
49    ///
50    /// Panics if the list is not pre-sorted.
51    #[inline]
52    #[track_caller]
53    pub fn new_pre_sorted(list: impl IntoIterator<Item = u64>) -> Self {
54        Self::new(list).expect("IntegerList must be pre-sorted and non-empty")
55    }
56
57    /// Appends a list of integers to the current list.
58    pub fn append(&mut self, list: impl IntoIterator<Item = u64>) -> Result<u64, IntegerListError> {
59        self.0.append(list).map_err(|_| IntegerListError::UnsortedInput)
60    }
61
62    /// Pushes a new integer to the list.
63    pub fn push(&mut self, value: u64) -> Result<(), IntegerListError> {
64        self.0.try_push(value).map_err(|_| IntegerListError::UnsortedInput)
65    }
66
67    /// Clears the list.
68    pub fn clear(&mut self) {
69        self.0.clear();
70    }
71
72    /// Removes the integers in the given range, returning how many were removed.
73    pub fn remove_range<R: RangeBounds<u64>>(&mut self, range: R) -> u64 {
74        self.0.remove_range(range)
75    }
76
77    /// Serializes an [`IntegerList`] into a sequence of bytes.
78    pub fn to_bytes(&self) -> Vec<u8> {
79        let mut vec = Vec::with_capacity(self.0.serialized_size());
80        self.0.serialize_into(&mut vec).expect("not able to encode IntegerList");
81        vec
82    }
83
84    /// Serializes an [`IntegerList`] into a sequence of bytes.
85    pub fn to_mut_bytes<B: bytes::BufMut>(&self, buf: &mut B) {
86        self.0.serialize_into(buf.writer()).unwrap();
87    }
88
89    /// Deserializes a sequence of bytes into a proper [`IntegerList`].
90    pub fn from_bytes(data: &[u8]) -> Result<Self, IntegerListError> {
91        RoaringTreemap::deserialize_from(data)
92            .map(Self)
93            .map_err(|_| IntegerListError::FailedToDeserialize)
94    }
95}
96
97impl serde::Serialize for IntegerList {
98    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99    where
100        S: serde::Serializer,
101    {
102        let mut seq = serializer.serialize_seq(Some(self.len() as usize))?;
103        for e in &self.0 {
104            seq.serialize_element(&e)?;
105        }
106        seq.end()
107    }
108}
109
110struct IntegerListVisitor;
111
112impl<'de> serde::de::Visitor<'de> for IntegerListVisitor {
113    type Value = IntegerList;
114
115    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        f.write_str("a usize array")
117    }
118
119    fn visit_seq<E>(self, mut seq: E) -> Result<Self::Value, E::Error>
120    where
121        E: serde::de::SeqAccess<'de>,
122    {
123        let mut list = IntegerList::empty();
124        while let Some(item) = seq.next_element()? {
125            list.push(item).map_err(serde::de::Error::custom)?;
126        }
127        Ok(list)
128    }
129}
130
131impl<'de> serde::Deserialize<'de> for IntegerList {
132    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133    where
134        D: serde::Deserializer<'de>,
135    {
136        deserializer.deserialize_byte_buf(IntegerListVisitor)
137    }
138}
139
140#[cfg(any(test, feature = "arbitrary"))]
141use arbitrary::{Arbitrary, Unstructured};
142
143#[cfg(any(test, feature = "arbitrary"))]
144impl<'a> Arbitrary<'a> for IntegerList {
145    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {
146        let mut nums: Vec<u64> = Vec::arbitrary(u)?;
147        nums.sort_unstable();
148        Self::new(nums).map_err(|_| arbitrary::Error::IncorrectFormat)
149    }
150}
151
152/// Primitives error type.
153#[derive(Debug, derive_more::Display, derive_more::Error)]
154pub enum IntegerListError {
155    /// The provided input is unsorted.
156    #[display("the provided input is unsorted")]
157    UnsortedInput,
158    /// Failed to deserialize data into type.
159    #[display("failed to deserialize data into type")]
160    FailedToDeserialize,
161}
162
163impl Compress for IntegerList {
164    type Compressed = Vec<u8>;
165
166    fn compress(self) -> Self::Compressed {
167        self.to_bytes()
168    }
169
170    fn compress_to_buf<B: bytes::BufMut + AsMut<[u8]>>(&self, buf: &mut B) {
171        self.to_mut_bytes(buf)
172    }
173}
174
175impl Decompress for IntegerList {
176    fn decompress(value: &[u8]) -> Result<Self, DecompressError> {
177        Self::from_bytes(value).map_err(DecompressError::new)
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184
185    #[test]
186    fn empty_list() {
187        assert_eq!(IntegerList::empty().len(), 0);
188        assert_eq!(IntegerList::new_pre_sorted(std::iter::empty()).len(), 0);
189    }
190
191    #[test]
192    fn test_integer_list() {
193        let original_list = [1, 2, 3];
194        let ef_list = IntegerList::new(original_list).unwrap();
195        assert_eq!(ef_list.iter().collect::<Vec<_>>(), original_list);
196    }
197
198    #[test]
199    fn test_integer_list_serialization() {
200        let original_list = [1, 2, 3];
201        let ef_list = IntegerList::new(original_list).unwrap();
202
203        let blist = ef_list.to_bytes();
204        assert_eq!(IntegerList::from_bytes(&blist).unwrap(), ef_list)
205    }
206}