uni_query/query/df_graph/
bitmap.rs1use bitvec::vec::BitVec;
2use fxhash::FxHashSet;
3use uni_common::core::id::{Eid, Vid};
4
5const DENSITY_THRESHOLD: f64 = 0.125; fn build_filter(ids: Vec<u64>, max_hint: usize) -> (Option<BitVec>, Option<FxHashSet<u64>>) {
15 if ids.is_empty() {
16 return (None, Some(FxHashSet::default()));
17 }
18
19 let range = max_hint.max(ids.iter().copied().max().unwrap_or(0) as usize + 1);
20 let density = ids.len() as f64 / range.max(1) as f64;
21
22 if density > DENSITY_THRESHOLD {
23 let mut bv = BitVec::repeat(false, range);
24 for &id in &ids {
25 let idx = id as usize;
26 if idx < bv.len() {
27 bv.set(idx, true);
28 }
29 }
30 (Some(bv), None)
31 } else {
32 (None, Some(ids.into_iter().collect()))
33 }
34}
35
36fn bitvec_contains(bv: &BitVec, raw: u64) -> bool {
38 let idx = raw as usize;
39 idx < bv.len() && bv[idx]
40}
41
42macro_rules! define_id_filter {
44 (
45 $(#[$meta:meta])*
46 $filter_name:ident, $id_type:ty, $from_fn:ident
47 ) => {
48 $(#[$meta])*
49 #[derive(Debug)]
50 pub enum $filter_name {
51 AllAllowed,
53 DenseBitVec(BitVec),
55 Sparse(FxHashSet<u64>),
57 }
58
59 impl $filter_name {
60 pub fn contains(&self, id: $id_type) -> bool {
62 match self {
63 Self::AllAllowed => true,
64 Self::DenseBitVec(bv) => bitvec_contains(bv, id.as_u64()),
65 Self::Sparse(set) => set.contains(&id.as_u64()),
66 }
67 }
68
69 pub fn $from_fn(ids: Vec<u64>, max_hint: usize) -> Self {
74 let (bv, set) = build_filter(ids, max_hint);
75 if let Some(bv) = bv {
76 Self::DenseBitVec(bv)
77 } else {
78 Self::Sparse(set.unwrap_or_default())
79 }
80 }
81 }
82 };
83}
84
85define_id_filter!(
86 EidFilter, Eid, from_eids
88);
89
90define_id_filter!(
91 VidFilter, Vid, from_vids
93);
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_eid_all_allowed() {
101 let f = EidFilter::AllAllowed;
102 assert!(f.contains(Eid::new(0)));
103 assert!(f.contains(Eid::new(999)));
104 assert!(f.contains(Eid::new(u64::MAX - 1)));
105 }
106
107 #[test]
108 fn test_eid_dense_bitvec_contains() {
109 let f = EidFilter::from_eids(vec![1, 3, 5, 7], 8);
110 assert!(matches!(f, EidFilter::DenseBitVec(_)));
112 assert!(f.contains(Eid::new(1)));
113 assert!(f.contains(Eid::new(3)));
114 assert!(f.contains(Eid::new(5)));
115 assert!(f.contains(Eid::new(7)));
116 assert!(!f.contains(Eid::new(0)));
117 assert!(!f.contains(Eid::new(2)));
118 assert!(!f.contains(Eid::new(4)));
119 assert!(!f.contains(Eid::new(6)));
120 }
121
122 #[test]
123 fn test_eid_dense_bitvec_empty() {
124 let f = EidFilter::from_eids(vec![], 100);
125 assert!(matches!(f, EidFilter::Sparse(_)));
127 assert!(!f.contains(Eid::new(0)));
128 assert!(!f.contains(Eid::new(50)));
129 }
130
131 #[test]
132 fn test_eid_hashset_contains() {
133 let f = EidFilter::from_eids(vec![100, 500, 999], 1000);
135 assert!(matches!(f, EidFilter::Sparse(_)));
136 assert!(f.contains(Eid::new(100)));
137 assert!(f.contains(Eid::new(500)));
138 assert!(f.contains(Eid::new(999)));
139 assert!(!f.contains(Eid::new(0)));
140 assert!(!f.contains(Eid::new(101)));
141 assert!(!f.contains(Eid::new(998)));
142 }
143
144 #[test]
145 fn test_eid_from_eids_chooses_dense() {
146 let eids: Vec<u64> = (0..20).collect();
148 let f = EidFilter::from_eids(eids, 100);
149 assert!(matches!(f, EidFilter::DenseBitVec(_)));
150 }
151
152 #[test]
153 fn test_eid_from_eids_chooses_hashset() {
154 let f = EidFilter::from_eids(vec![10, 100, 1000, 5000, 9999], 10000);
156 assert!(matches!(f, EidFilter::Sparse(_)));
157 }
158
159 #[test]
160 fn test_eid_out_of_range_dense() {
161 let f = EidFilter::from_eids(vec![1, 2, 3, 4, 5, 6, 7, 8], 10);
163 assert!(matches!(f, EidFilter::DenseBitVec(_)));
164 assert!(!f.contains(Eid::new(100)));
165 }
166
167 #[test]
168 fn test_vid_filter_basic() {
169 let f = VidFilter::AllAllowed;
171 assert!(f.contains(Vid::new(0)));
172 assert!(f.contains(Vid::new(999)));
173
174 let f = VidFilter::from_vids(vec![1, 2, 3, 4], 8);
176 assert!(matches!(f, VidFilter::DenseBitVec(_)));
177 assert!(f.contains(Vid::new(1)));
178 assert!(f.contains(Vid::new(4)));
179 assert!(!f.contains(Vid::new(0)));
180 assert!(!f.contains(Vid::new(5)));
181
182 let f = VidFilter::from_vids(vec![100, 9999], 10000);
184 assert!(matches!(f, VidFilter::Sparse(_)));
185 assert!(f.contains(Vid::new(100)));
186 assert!(f.contains(Vid::new(9999)));
187 assert!(!f.contains(Vid::new(0)));
188 }
189}