1use std::collections::BTreeMap;
11use std::io::{self, Read, Write};
12
13use roaring::RoaringBitmap;
14
15use crate::ordinal::{Ordinal, to_usize};
16
17const MAGIC: &[u8; 8] = b"FTRSETS\0";
18const VERSION: u32 = 1;
19
20#[derive(Debug, thiserror::Error)]
22pub enum MembersError {
23 #[error("memberships I/O failed")]
25 Io(#[from] io::Error),
26 #[error("not a memberships artifact")]
28 Magic,
29 #[error("memberships layout version {found}, expected {expected}")]
31 Version {
32 found: u32,
34 expected: u32,
36 },
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Default)]
41pub struct Memberships {
42 sets: BTreeMap<u64, RoaringBitmap>,
43}
44
45impl Memberships {
46 #[must_use]
48 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn insert(&mut self, refset: u64, concept: Ordinal) {
54 self.sets.entry(refset).or_default().insert(concept.index());
55 }
56
57 #[must_use]
59 pub fn size_in_bytes(&self) -> usize {
60 self.sets.values().fold(
61 crate::footprint::entries::<u64, RoaringBitmap>(self.sets.len()),
62 |total, set| total.saturating_add(crate::footprint::bitmap(set)),
63 )
64 }
65
66 #[must_use]
68 pub fn members(&self, refset: u64) -> Option<&RoaringBitmap> {
69 self.sets.get(&refset)
70 }
71
72 pub fn refsets(&self) -> impl Iterator<Item = u64> + '_ {
74 self.sets.keys().copied()
75 }
76
77 #[must_use]
79 pub fn len(&self) -> usize {
80 self.sets.len()
81 }
82
83 #[must_use]
85 pub fn is_empty(&self) -> bool {
86 self.sets.is_empty()
87 }
88
89 #[must_use]
91 pub fn total(&self) -> u64 {
92 self.sets.values().map(RoaringBitmap::len).sum()
93 }
94
95 pub fn write_to(&self, out: &mut impl Write) -> Result<(), MembersError> {
101 out.write_all(MAGIC)?;
102 out.write_all(&VERSION.to_le_bytes())?;
103 let count = crate::persist::u32_len(self.sets.len(), "the refset count")?;
104 out.write_all(&count.to_le_bytes())?;
105 for (refset, set) in &self.sets {
106 out.write_all(&refset.to_le_bytes())?;
107 let size = crate::persist::u32_len(set.serialized_size(), "a set size")?;
108 out.write_all(&size.to_le_bytes())?;
109 set.serialize_into(&mut *out)?;
110 }
111 Ok(())
112 }
113
114 pub fn read_from(input: &mut impl Read) -> Result<Self, MembersError> {
120 let mut magic = [0_u8; 8];
121 input.read_exact(&mut magic)?;
122 if &magic != MAGIC {
123 return Err(MembersError::Magic);
124 }
125 let version = read_u32(input)?;
126 if version != VERSION {
127 return Err(MembersError::Version {
128 found: version,
129 expected: VERSION,
130 });
131 }
132 let count = read_u32(input)?;
133 let mut sets = BTreeMap::new();
134 for _ in 0..count {
135 let mut long = [0_u8; 8];
136 input.read_exact(&mut long)?;
137 let size = read_u32(input)?;
138 let mut bytes = vec![0_u8; to_usize(size)];
139 input.read_exact(&mut bytes)?;
140 sets.insert(
141 u64::from_le_bytes(long),
142 RoaringBitmap::deserialize_from(bytes.as_slice())?,
143 );
144 }
145 Ok(Self { sets })
146 }
147}
148
149fn read_u32(input: &mut impl Read) -> io::Result<u32> {
150 let mut buffer = [0_u8; 4];
151 input.read_exact(&mut buffer)?;
152 Ok(u32::from_le_bytes(buffer))
153}
154
155#[cfg(test)]
156mod tests {
157 use roaring::RoaringBitmap;
158
159 use super::{MembersError, Memberships};
160 use crate::ordinal::Ordinal;
161
162 #[test]
163 fn memberships_round_trip_and_reject_foreign_bytes() {
164 let mut members = Memberships::new();
165 members.insert(31_000_147_101, Ordinal::new(2));
166 members.insert(31_000_147_101, Ordinal::new(3));
167 members.insert(900_000_000_000_497_000, Ordinal::new(3));
168 assert_eq!(members.len(), 2);
169 assert_eq!(members.total(), 3);
170 assert_eq!(
171 members.members(31_000_147_101).map(RoaringBitmap::len),
172 Some(2)
173 );
174 assert!(members.members(1).is_none());
175 assert_eq!(members.refsets().next(), Some(31_000_147_101));
176 let mut bytes = Vec::new();
177 members.write_to(&mut bytes).expect("writes");
178 assert_eq!(
179 Memberships::read_from(&mut bytes.as_slice()).expect("reads"),
180 members
181 );
182 assert!(matches!(
183 Memberships::read_from(&mut b"XXXXXXXX\0\0\0\0".as_slice()),
184 Err(MembersError::Magic)
185 ));
186 }
187}