zeppelin_embed/meta/
alive.rs1use super::bitmap::DocBitmap;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub struct AliveError {
8 document: u32,
9 row_count: u32,
10}
11
12impl AliveError {
13 #[must_use]
15 pub const fn document(self) -> u32 {
16 self.document
17 }
18
19 #[must_use]
21 pub const fn row_count(self) -> u32 {
22 self.row_count
23 }
24}
25
26impl std::fmt::Display for AliveError {
27 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(
29 formatter,
30 "document {} is outside segment row count {}",
31 self.document, self.row_count
32 )
33 }
34}
35
36impl std::error::Error for AliveError {}
37
38#[derive(Clone, Debug, Eq, PartialEq)]
40pub struct AliveSet {
41 row_count: u32,
42 alive: DocBitmap,
43 tombstones: DocBitmap,
44}
45
46impl AliveSet {
47 pub(crate) fn compact_for_cache(&mut self) {
48 self.alive.compact_for_cache();
49 self.tombstones.compact_for_cache();
50 }
51
52 pub(crate) fn resident_bytes(&self) -> Option<usize> {
53 self.alive
54 .resident_bytes()?
55 .checked_add(self.tombstones.resident_bytes()?)
56 }
57
58 #[must_use]
60 pub fn new(row_count: u32) -> Self {
61 let state = Self {
62 row_count,
63 alive: DocBitmap::full(row_count),
64 tombstones: DocBitmap::new(),
65 };
66 state.debug_assert_consistent();
67 state
68 }
69
70 #[must_use]
72 pub const fn row_count(&self) -> u32 {
73 self.row_count
74 }
75
76 pub fn tombstone(&mut self, document: u32) -> Result<bool, AliveError> {
80 if document >= self.row_count {
81 return Err(AliveError {
82 document,
83 row_count: self.row_count,
84 });
85 }
86 let changed = self.alive.remove(document);
87 if changed {
88 self.tombstones.insert(document);
89 }
90 self.debug_assert_consistent();
91 Ok(changed)
92 }
93
94 #[must_use]
96 pub fn is_alive(&self, document: u32) -> bool {
97 self.alive.contains(document)
98 }
99
100 #[must_use]
102 pub fn alive_bitmap(&self) -> &DocBitmap {
103 &self.alive
104 }
105
106 #[must_use]
108 pub fn tombstone_bitmap(&self) -> &DocBitmap {
109 &self.tombstones
110 }
111
112 #[must_use]
114 pub fn live_count(&self) -> u64 {
115 self.alive.cardinality()
116 }
117
118 #[must_use]
120 pub fn tombstone_count(&self) -> u64 {
121 self.tombstones.cardinality()
122 }
123
124 pub fn iter_alive(&self) -> impl Iterator<Item = u32> + '_ {
126 self.alive.iter()
127 }
128
129 pub(crate) const fn scan_mask(&self) -> &roaring::RoaringBitmap {
130 self.alive.as_roaring()
131 }
132
133 pub fn debug_assert_consistent(&self) {
135 debug_assert_eq!(
136 self.alive.cardinality() + self.tombstones.cardinality(),
137 u64::from(self.row_count)
138 );
139 debug_assert!(self.alive.iter().all(|id| !self.tombstones.contains(id)));
140 debug_assert!(self.tombstones.iter().all(|id| id < self.row_count));
141 }
142}
143
144#[allow(clippy::expect_used)]
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn tombstone_then_alive_iteration_skips() {
151 let mut alive = AliveSet::new(4);
152 alive.tombstone(1).expect("row exists");
153 assert_eq!(alive.iter_alive().collect::<Vec<_>>(), vec![0, 2, 3]);
154 }
155
156 #[test]
157 fn alive_counters_remain_consistent() {
158 let mut alive = AliveSet::new(4);
159 alive.tombstone(1).expect("row exists");
160 alive.tombstone(1).expect("idempotent tombstone");
161 alive.debug_assert_consistent();
162 assert_eq!(alive.tombstone_count(), 1);
163 assert_eq!(alive.live_count(), 3);
164 assert_eq!(
165 alive.tombstone_count(),
166 alive.tombstone_bitmap().cardinality()
167 );
168 assert_eq!(alive.live_count(), alive.alive_bitmap().cardinality());
169 }
170}