Skip to main content

dbsp/utils/
supports_roaring.rs

1//! Trait for key types that can be mapped into a roaring bitmap domain.
2
3use crate::dynamic::{BSet, DowncastTrait, DynData, LeanVec};
4use crate::time::UnitTimestamp;
5use std::collections::BTreeMap;
6use std::rc::Rc;
7use std::sync::Arc;
8use uuid::Uuid;
9
10/// Key types that can be mapped into a `u32` domain of a roaring bitmap
11/// while inside of a batch.
12///
13/// A roaring bitmap stores `u32` values. To use one as a batch filter,
14/// each key must be translated into a `u32` offset relative to the minimum key
15/// in the batch. Implementors define how (and whether) this translation is
16/// possible for a given key type.
17pub trait SupportsRoaring {
18    /// Returns `true` if this key type can be represented in a 32-bit roaring
19    /// bitmap.
20    #[inline]
21    fn supports_roaring32(&self) -> bool {
22        false
23    }
24
25    /// Computes the `u32` offset of `self` relative to `min`.
26    ///
27    /// Returns `Some(offset)` when `self >= min` and the difference fits in a
28    /// `u32`; returns `None` otherwise.
29    #[inline]
30    fn roaring_u32_offset(&self, _min: &Self) -> Option<u32>
31    where
32        Self: Sized,
33    {
34        None
35    }
36
37    /// Like [`roaring_u32_offset`](Self::roaring_u32_offset), but accepts `min`
38    /// as a type-erased [`DynData`] reference.
39    #[inline]
40    fn roaring_u32_offset_dyn(&self, _min: &DynData) -> Option<u32> {
41        None
42    }
43
44    /// Like [`roaring_u32_offset_dyn`](Self::roaring_u32_offset_dyn), but
45    /// panics when the offset cannot be computed.
46    #[inline]
47    fn roaring_u32_offset_dyn_checked(&self, min: &DynData) -> u32 {
48        self.roaring_u32_offset_dyn(min)
49            .expect("roaring-u32 filter was selected for a key outside the planned batch range")
50    }
51}
52
53#[macro_export]
54macro_rules! never_roaring_filter {
55    ($($ty:ty),* $(,)?) => {
56        $(
57            impl $crate::utils::SupportsRoaring for $ty {}
58        )*
59    };
60}
61
62// Types excluded from roaring: non-numeric, floating-point (no meaningful
63// integer offset), or platform-dependent width (usize/isize).
64never_roaring_filter!(
65    (),
66    bool,
67    char,
68    f32,
69    f64,
70    usize,
71    isize,
72    String,
73    UnitTimestamp,
74    Uuid
75);
76
77impl SupportsRoaring for u8 {
78    #[inline]
79    fn supports_roaring32(&self) -> bool {
80        true
81    }
82
83    #[inline]
84    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
85        self.checked_sub(*min).map(u32::from)
86    }
87
88    #[inline]
89    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
90        // Safety: the caller guarantees `min` wraps `u8`.
91        self.roaring_u32_offset(unsafe { min.downcast::<u8>() })
92    }
93}
94
95impl SupportsRoaring for i8 {
96    #[inline]
97    fn supports_roaring32(&self) -> bool {
98        true
99    }
100
101    #[inline]
102    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
103        (*self >= *min).then_some(u32::from(self.abs_diff(*min)))
104    }
105
106    #[inline]
107    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
108        // Safety: the caller guarantees `min` wraps `i8`.
109        self.roaring_u32_offset(unsafe { min.downcast::<i8>() })
110    }
111}
112
113impl SupportsRoaring for u16 {
114    #[inline]
115    fn supports_roaring32(&self) -> bool {
116        true
117    }
118
119    #[inline]
120    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
121        self.checked_sub(*min).map(u32::from)
122    }
123
124    #[inline]
125    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
126        // Safety: the caller guarantees `min` wraps `u16`.
127        self.roaring_u32_offset(unsafe { min.downcast::<u16>() })
128    }
129}
130
131impl SupportsRoaring for i16 {
132    #[inline]
133    fn supports_roaring32(&self) -> bool {
134        true
135    }
136
137    #[inline]
138    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
139        (*self >= *min).then_some(u32::from(self.abs_diff(*min)))
140    }
141
142    #[inline]
143    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
144        // Safety: the caller guarantees `min` wraps `i16`.
145        self.roaring_u32_offset(unsafe { min.downcast::<i16>() })
146    }
147}
148
149impl SupportsRoaring for u32 {
150    #[inline]
151    fn supports_roaring32(&self) -> bool {
152        true
153    }
154
155    #[inline]
156    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
157        self.checked_sub(*min)
158    }
159
160    #[inline]
161    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
162        // Safety: the caller guarantees `min` wraps `u32`.
163        self.roaring_u32_offset(unsafe { min.downcast::<u32>() })
164    }
165}
166
167impl SupportsRoaring for i32 {
168    #[inline]
169    fn supports_roaring32(&self) -> bool {
170        true
171    }
172
173    #[inline]
174    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
175        (*self >= *min).then_some(self.abs_diff(*min))
176    }
177
178    #[inline]
179    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
180        // Safety: the caller guarantees `min` wraps `i32`.
181        self.roaring_u32_offset(unsafe { min.downcast::<i32>() })
182    }
183}
184
185impl SupportsRoaring for u64 {
186    #[inline]
187    fn supports_roaring32(&self) -> bool {
188        true
189    }
190
191    #[inline]
192    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
193        self.checked_sub(*min)
194            .and_then(|diff| u32::try_from(diff).ok())
195    }
196
197    #[inline]
198    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
199        // Safety: the caller guarantees `min` wraps `u64`.
200        self.roaring_u32_offset(unsafe { min.downcast::<u64>() })
201    }
202}
203
204impl SupportsRoaring for i64 {
205    #[inline]
206    fn supports_roaring32(&self) -> bool {
207        true
208    }
209
210    #[inline]
211    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
212        (*self >= *min)
213            .then_some(self.abs_diff(*min))
214            .and_then(|diff| diff.try_into().ok())
215    }
216
217    #[inline]
218    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
219        // Safety: the caller guarantees `min` wraps `i64`.
220        self.roaring_u32_offset(unsafe { min.downcast::<i64>() })
221    }
222}
223
224impl SupportsRoaring for u128 {
225    #[inline]
226    fn supports_roaring32(&self) -> bool {
227        true
228    }
229
230    #[inline]
231    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
232        self.checked_sub(*min)
233            .and_then(|diff| u32::try_from(diff).ok())
234    }
235
236    #[inline]
237    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
238        // Safety: the caller guarantees `min` wraps `u128`.
239        self.roaring_u32_offset(unsafe { min.downcast::<u128>() })
240    }
241}
242
243impl SupportsRoaring for i128 {
244    #[inline]
245    fn supports_roaring32(&self) -> bool {
246        true
247    }
248
249    #[inline]
250    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
251        (*self >= *min)
252            .then_some(self.abs_diff(*min))
253            .and_then(|diff| diff.try_into().ok())
254    }
255
256    #[inline]
257    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
258        // Safety: the caller guarantees `min` wraps `i128`.
259        self.roaring_u32_offset(unsafe { min.downcast::<i128>() })
260    }
261}
262
263impl<T> SupportsRoaring for Option<T> {}
264
265#[macro_export]
266macro_rules! never_roaring_filter_1 {
267    ($($wrapper:ident),* $(,)?) => {
268        $(
269            impl<T> $crate::utils::SupportsRoaring for $wrapper<T> {}
270        )*
271    };
272}
273
274never_roaring_filter_1!(Vec, LeanVec, BSet);
275
276#[macro_export]
277macro_rules! delegate_supports_roaring {
278    ($($wrapper:ident),* $(,)?) => {
279        $(
280            impl<T: $crate::utils::SupportsRoaring> $crate::utils::SupportsRoaring for $wrapper<T> {
281                #[inline]
282                fn supports_roaring32(&self) -> bool {
283                    self.as_ref().supports_roaring32()
284                }
285
286                #[inline]
287                fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
288                    self.as_ref().roaring_u32_offset(min.as_ref())
289                }
290
291                #[inline]
292                fn roaring_u32_offset_dyn(&self, min: &$crate::dynamic::DynData) -> Option<u32> {
293                    self.as_ref().roaring_u32_offset_dyn(min)
294                }
295
296                #[inline]
297                fn roaring_u32_offset_dyn_checked(&self, min: &$crate::dynamic::DynData) -> u32 {
298                    self.as_ref().roaring_u32_offset_dyn_checked(min)
299                }
300            }
301        )*
302    };
303}
304
305delegate_supports_roaring!(Box, Rc, Arc);
306
307#[macro_export]
308macro_rules! never_roaring_filter_tuples {
309    ($($name:ident),+) => {
310        impl<$($name),+> SupportsRoaring for ($($name,)+) {}
311    };
312}
313
314never_roaring_filter_tuples!(A);
315never_roaring_filter_tuples!(A, B);
316never_roaring_filter_tuples!(A, B, C);
317never_roaring_filter_tuples!(A, B, C, D);
318never_roaring_filter_tuples!(A, B, C, D, E);
319never_roaring_filter_tuples!(A, B, C, D, E, F);
320
321impl<K, V> SupportsRoaring for BTreeMap<K, V> {}
322
323impl<T: SupportsRoaring + 'static> SupportsRoaring for crate::utils::Tup1<T> {
324    #[inline]
325    fn supports_roaring32(&self) -> bool {
326        self.0.supports_roaring32()
327    }
328
329    #[inline]
330    fn roaring_u32_offset(&self, min: &Self) -> Option<u32> {
331        self.0.roaring_u32_offset(&min.0)
332    }
333
334    #[inline]
335    fn roaring_u32_offset_dyn(&self, min: &DynData) -> Option<u32> {
336        // Safety: the caller guarantees `min` wraps `Tup1<T>`.
337        self.roaring_u32_offset(unsafe { min.downcast::<Self>() })
338    }
339
340    #[inline]
341    fn roaring_u32_offset_dyn_checked(&self, min: &DynData) -> u32 {
342        // Safety: the caller guarantees `min` wraps `Tup1<T>`.
343        self.roaring_u32_offset(unsafe { min.downcast::<Self>() })
344            .expect("roaring-u32 filter was selected for a key outside the planned batch range")
345    }
346}
347
348#[cfg(test)]
349mod test {
350    use super::SupportsRoaring;
351    use crate::{dynamic::DynData, utils::Tup1};
352
353    #[test]
354    fn u32_roaring_offset_boundaries() {
355        assert!(0u32.supports_roaring32());
356        assert_eq!(0u32.roaring_u32_offset(&0), Some(0));
357        assert_eq!(u32::MAX.roaring_u32_offset(&0), Some(u32::MAX));
358        assert_eq!(5u32.roaring_u32_offset(&7), None);
359
360        assert_eq!(7u32.roaring_u32_offset_dyn((&0u32) as &DynData), Some(7));
361    }
362
363    #[test]
364    fn i32_roaring_offset_boundaries() {
365        assert!(0i32.supports_roaring32());
366        assert_eq!(i32::MIN.roaring_u32_offset(&i32::MIN), Some(0));
367        assert_eq!(i32::MAX.roaring_u32_offset(&i32::MIN), Some(u32::MAX));
368        assert_eq!((-7i32).roaring_u32_offset(&-10), Some(3));
369        assert_eq!((-11i32).roaring_u32_offset(&-10), None);
370
371        assert_eq!(
372            (-7i32).roaring_u32_offset_dyn((&-10i32) as &DynData),
373            Some(3)
374        );
375    }
376
377    #[test]
378    fn u64_roaring_offset_boundaries() {
379        let min = (u64::from(u32::MAX) << 8) + 11;
380
381        assert!(min.supports_roaring32());
382        assert_eq!(min.roaring_u32_offset(&min), Some(0));
383        assert_eq!(
384            min.wrapping_add(u64::from(u32::MAX))
385                .roaring_u32_offset(&min),
386            Some(u32::MAX)
387        );
388        assert_eq!(
389            min.wrapping_add(u64::from(u32::MAX) + 1)
390                .roaring_u32_offset(&min),
391            None
392        );
393        assert_eq!(11u64.roaring_u32_offset(&(u64::MAX - 1)), None);
394
395        assert_eq!(11u64.roaring_u32_offset_dyn((&9u64) as &DynData), Some(2));
396    }
397
398    #[test]
399    fn i64_roaring_offset_boundaries() {
400        let min = -5i64;
401        let max_fitting = min + i64::from(u32::MAX);
402
403        assert!(min.supports_roaring32());
404        assert_eq!(min.roaring_u32_offset(&min), Some(0));
405        assert_eq!(max_fitting.roaring_u32_offset(&min), Some(u32::MAX));
406        assert_eq!((max_fitting + 1).roaring_u32_offset(&min), None);
407        assert_eq!((min - 1).roaring_u32_offset(&min), None);
408
409        assert_eq!(
410            (-2i64).roaring_u32_offset_dyn((&-5i64) as &DynData),
411            Some(3)
412        );
413    }
414
415    #[test]
416    fn tup1_delegates_roaring_support() {
417        assert!(Tup1(-7i32).supports_roaring32());
418        assert_eq!(
419            Tup1(-7i32).roaring_u32_offset_dyn((&Tup1(-10i32)) as &DynData),
420            Some(3)
421        );
422    }
423
424    #[test]
425    fn u8_roaring_offset_boundaries() {
426        assert!(0u8.supports_roaring32());
427        assert_eq!(0u8.roaring_u32_offset(&0), Some(0));
428        assert_eq!(u8::MAX.roaring_u32_offset(&0), Some(255));
429        assert_eq!(5u8.roaring_u32_offset(&7), None);
430
431        assert_eq!(7u8.roaring_u32_offset_dyn((&0u8) as &DynData), Some(7));
432    }
433
434    #[test]
435    fn i8_roaring_offset_boundaries() {
436        assert!(0i8.supports_roaring32());
437        assert_eq!(i8::MIN.roaring_u32_offset(&i8::MIN), Some(0));
438        assert_eq!(i8::MAX.roaring_u32_offset(&i8::MIN), Some(255));
439        assert_eq!((-7i8).roaring_u32_offset(&-10), Some(3));
440        assert_eq!((-11i8).roaring_u32_offset(&-10), None);
441
442        assert_eq!((-7i8).roaring_u32_offset_dyn((&-10i8) as &DynData), Some(3));
443    }
444
445    #[test]
446    fn u16_roaring_offset_boundaries() {
447        assert!(0u16.supports_roaring32());
448        assert_eq!(0u16.roaring_u32_offset(&0), Some(0));
449        assert_eq!(u16::MAX.roaring_u32_offset(&0), Some(65535));
450        assert_eq!(5u16.roaring_u32_offset(&7), None);
451
452        assert_eq!(7u16.roaring_u32_offset_dyn((&0u16) as &DynData), Some(7));
453    }
454
455    #[test]
456    fn i16_roaring_offset_boundaries() {
457        assert!(0i16.supports_roaring32());
458        assert_eq!(i16::MIN.roaring_u32_offset(&i16::MIN), Some(0));
459        assert_eq!(i16::MAX.roaring_u32_offset(&i16::MIN), Some(65535));
460        assert_eq!((-7i16).roaring_u32_offset(&-10), Some(3));
461        assert_eq!((-11i16).roaring_u32_offset(&-10), None);
462
463        assert_eq!(
464            (-7i16).roaring_u32_offset_dyn((&-10i16) as &DynData),
465            Some(3)
466        );
467    }
468
469    #[test]
470    fn u128_roaring_offset_boundaries() {
471        let min = u128::from(u32::MAX) << 64;
472
473        assert!(min.supports_roaring32());
474        assert_eq!(min.roaring_u32_offset(&min), Some(0));
475        assert_eq!(
476            (min + u128::from(u32::MAX)).roaring_u32_offset(&min),
477            Some(u32::MAX)
478        );
479        assert_eq!(
480            (min + u128::from(u32::MAX) + 1).roaring_u32_offset(&min),
481            None
482        );
483        assert_eq!(11u128.roaring_u32_offset(&(u128::MAX - 1)), None);
484
485        assert_eq!(11u128.roaring_u32_offset_dyn((&9u128) as &DynData), Some(2));
486    }
487
488    #[test]
489    fn i128_roaring_offset_boundaries() {
490        let min = -5i128;
491        let max_fitting = min + i128::from(u32::MAX);
492
493        assert!(min.supports_roaring32());
494        assert_eq!(min.roaring_u32_offset(&min), Some(0));
495        assert_eq!(max_fitting.roaring_u32_offset(&min), Some(u32::MAX));
496        assert_eq!((max_fitting + 1).roaring_u32_offset(&min), None);
497        assert_eq!((min - 1).roaring_u32_offset(&min), None);
498
499        assert_eq!(
500            (-2i128).roaring_u32_offset_dyn((&-5i128) as &DynData),
501            Some(3)
502        );
503    }
504
505    #[test]
506    fn unsupported_roaring_keys() {
507        assert!(!"feldera".to_string().supports_roaring32());
508        assert_eq!(
509            "feldera"
510                .to_string()
511                .roaring_u32_offset_dyn((&String::new()) as &DynData),
512            None
513        );
514    }
515}