spacetimedb_table/table_index/
unique_direct_fixed_cap_index.rs1use super::unique_direct_index::{UniqueDirectIndexPointIter, NONE_PTR};
2use crate::indexes::RowPointer;
3use crate::MemoryUsage;
4use core::mem;
5use core::ops::{Bound, RangeBounds};
6use core::slice::Iter;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct UniqueDirectFixedCapIndex {
15 array: Box<[RowPointer]>,
17 len: usize,
19}
20
21impl MemoryUsage for UniqueDirectFixedCapIndex {
22 fn heap_usage(&self) -> usize {
23 let Self { array, len } = self;
24 array.heap_usage() + len.heap_usage()
25 }
26}
27
28impl UniqueDirectFixedCapIndex {
29 pub fn new(cap: usize) -> Self {
31 Self {
32 len: 0,
33 array: vec![NONE_PTR; cap].into(),
34 }
35 }
36
37 pub fn clone_structure(&self) -> Self {
39 Self::new(self.array.len())
40 }
41
42 pub fn insert(&mut self, key: usize, val: RowPointer) -> Result<(), RowPointer> {
49 let slot = &mut self.array[key];
51 let in_slot = *slot;
52 if in_slot == NONE_PTR {
53 *slot = val.with_reserved_bit(true);
55 self.len += 1;
56 Ok(())
57 } else {
58 Err(in_slot.with_reserved_bit(false))
59 }
60 }
61
62 pub fn delete(&mut self, key: usize) -> bool {
66 let Some(slot) = self.array.get_mut(key) else {
67 return false;
68 };
69 let old_val = mem::replace(slot, NONE_PTR);
70 let deleted = old_val != NONE_PTR;
71 self.len -= deleted as usize;
72 deleted
73 }
74
75 pub fn seek_point(&self, key: usize) -> UniqueDirectIndexPointIter {
77 let point = self.array.get(key).copied().filter(|slot| *slot != NONE_PTR);
78 UniqueDirectIndexPointIter::new(point)
79 }
80
81 pub fn seek_range(&self, range: &impl RangeBounds<usize>) -> UniqueDirectFixedCapIndexRangeIter {
83 let end = match range.end_bound() {
85 Bound::Included(&e) => e + 1,
86 Bound::Excluded(&e) => e,
87 Bound::Unbounded => self.array.len(),
88 };
89 let start = match range.start_bound() {
90 Bound::Included(&s) => s,
91 Bound::Excluded(&s) => s + 1,
92 Bound::Unbounded => 0,
93 };
94
95 let start = start.min(end);
97
98 UniqueDirectFixedCapIndexRangeIter::new(self.array.get(start..end).unwrap_or_default())
100 }
101
102 pub fn num_keys(&self) -> usize {
104 self.len
105 }
106
107 pub fn len(&self) -> usize {
109 self.len
110 }
111
112 #[allow(unused)] pub fn is_empty(&self) -> bool {
115 self.len() == 0
116 }
117
118 pub fn clear(&mut self) {
120 self.array.fill(NONE_PTR);
121 self.len = 0;
122 }
123
124 pub(crate) fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {
129 for (slot_s, slot_o) in self.array.iter().zip(other.array.iter()) {
130 let ptr_s = slot_s.with_reserved_bit(false);
131 if *slot_s != NONE_PTR && *slot_o != NONE_PTR && !ignore(&ptr_s) {
132 return Err(ptr_s);
134 }
135 }
136 Ok(())
137 }
138}
139
140#[derive(Debug)]
142pub struct UniqueDirectFixedCapIndexRangeIter<'a> {
143 iter: Iter<'a, RowPointer>,
144}
145
146impl<'a> UniqueDirectFixedCapIndexRangeIter<'a> {
147 fn new(slice: &'a [RowPointer]) -> Self {
148 let iter = slice.iter();
149 Self { iter }
150 }
151}
152
153impl Iterator for UniqueDirectFixedCapIndexRangeIter<'_> {
154 type Item = RowPointer;
155 fn next(&mut self) -> Option<Self::Item> {
156 self.iter
157 .find(|slot| **slot != NONE_PTR)
159 .map(|ptr| ptr.with_reserved_bit(false))
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use super::*;
166 use crate::table_index::unique_direct_index::test::gen_row_pointers;
167 use core::ops::Range;
168 use proptest::prelude::*;
169
170 fn range(start: u8, end: u8) -> Range<usize> {
171 let min = start.min(end);
172 let max = start.max(end);
173 min as usize..max as usize
174 }
175
176 fn setup(start: u8, end: u8) -> (UniqueDirectFixedCapIndex, Range<usize>, Vec<RowPointer>) {
177 let range = range(start, end);
178 let (keys, ptrs): (Vec<_>, Vec<_>) = range.clone().zip(gen_row_pointers()).unzip();
179
180 let mut index = UniqueDirectFixedCapIndex::new(u8::MAX as usize + 1);
181 for (key, ptr) in keys.iter().zip(&ptrs) {
182 index.insert(*key, *ptr).unwrap();
183 }
184 assert_eq!(index.len(), range.end - range.start);
185 (index, range, ptrs)
186 }
187
188 proptest! {
189 #[test]
190 fn seek_range_gives_back_inserted(start: u8, end: u8) {
191 let (index, range, ptrs) = setup(start, end);
192 let ptrs_found = index.seek_range(&range).collect::<Vec<_>>();
193 assert_eq!(ptrs, ptrs_found);
194 }
195
196 #[test]
197 fn inserting_again_errors(start: u8, end: u8) {
198 let (mut index, keys, ptrs) = setup(start, end);
199 for (key, ptr) in keys.zip(&ptrs) {
200 assert_eq!(index.insert(key, *ptr).unwrap_err(), *ptr)
201 }
202 }
203
204 #[test]
205 fn deleting_allows_reinsertion(start: u8, end: u8, key: u8) {
206 let (mut index, range, _) = setup(start, end);
207
208 if range.start == range.end {
209 return Err(TestCaseError::Reject("empty range".into()));
210 }
211
212 let key = (key as usize).clamp(range.start, range.end.saturating_sub(1));
213
214 let ptr = index.seek_point(key).next().unwrap();
215 assert!(index.delete(key));
216 assert!(!index.delete(key));
217 assert_eq!(index.len(), range.end - range.start - 1);
218
219 index.insert(key, ptr).unwrap();
220 assert_eq!(index.len(), range.end - range.start);
221 }
222 }
223}