1use frame_support::storage::StorageMap;
29use sp_std::marker::PhantomData;
30
31pub trait SparseBitmap<BitMap>
33where
34 BitMap: StorageMap<u64, u128, Query = u128>,
35{
36 fn get(index: u64) -> bool;
38 fn set(index: u64);
40}
41
42pub struct SparseBitmapImpl<BitMap>(PhantomData<BitMap>);
44
45impl<BitMap> SparseBitmapImpl<BitMap>
46where
47 BitMap: StorageMap<u64, u128, Query = u128>,
48{
49 fn compute_bucket_and_mask(index: u64) -> (u64, u128) {
52 (index >> 7, 1u128 << (index & 127))
53 }
54}
55
56impl<BitMap> SparseBitmap<BitMap> for SparseBitmapImpl<BitMap>
57where
58 BitMap: StorageMap<u64, u128, Query = u128>,
59{
60 fn get(index: u64) -> bool {
64 let (bucket, mask) = Self::compute_bucket_and_mask(index);
66
67 let bucket_value = BitMap::get(bucket);
69 bucket_value & mask != 0
70 }
71
72 fn set(index: u64) {
76 let (bucket, mask) = Self::compute_bucket_and_mask(index);
78
79 BitMap::mutate(bucket, |value| {
81 *value |= mask; });
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use frame_support::{
90 storage::{generator::StorageMap as StorageMapHelper, storage_prefix},
91 Twox64Concat,
92 };
93 use sp_io::TestExternalities;
94 pub struct MockStorageMap;
95
96 impl StorageMapHelper<u64, u128> for MockStorageMap {
97 type Query = u128;
98 type Hasher = Twox64Concat;
99 fn pallet_prefix() -> &'static [u8] {
100 b"MyModule"
101 }
102
103 fn storage_prefix() -> &'static [u8] {
104 b"MyStorageMap"
105 }
106
107 fn prefix_hash() -> [u8; 32] {
108 storage_prefix(Self::pallet_prefix(), Self::storage_prefix())
109 }
110
111 fn from_optional_value_to_query(v: Option<u128>) -> Self::Query {
112 v.unwrap_or_default()
113 }
114
115 fn from_query_to_optional_value(v: Self::Query) -> Option<u128> {
116 Some(v)
117 }
118 }
119
120 type TestSparseBitmap = SparseBitmapImpl<MockStorageMap>;
121
122 #[test]
123 fn test_sparse_bitmap_set_and_get() {
124 TestExternalities::default().execute_with(|| {
125 let index = 300u64;
126 let (bucket, mask) = TestSparseBitmap::compute_bucket_and_mask(index);
127
128 assert_eq!(MockStorageMap::get(bucket), 0);
130 assert!(!TestSparseBitmap::get(index));
131
132 TestSparseBitmap::set(index);
134
135 assert_eq!(MockStorageMap::get(bucket), mask);
137 assert!(TestSparseBitmap::get(index));
138 });
139 }
140
141 #[test]
142 fn test_sparse_bitmap_multiple_sets() {
143 TestExternalities::default().execute_with(|| {
144 let index1 = 300u64;
145 let index2 = 305u64; let (bucket, _) = TestSparseBitmap::compute_bucket_and_mask(index1);
147
148 let (_, mask1) = TestSparseBitmap::compute_bucket_and_mask(index1);
149 let (_, mask2) = TestSparseBitmap::compute_bucket_and_mask(index2);
150
151 assert_eq!(MockStorageMap::get(bucket), 0);
153 assert!(!TestSparseBitmap::get(index1));
154 assert!(!TestSparseBitmap::get(index2));
155
156 TestSparseBitmap::set(index1);
158
159 assert_eq!(MockStorageMap::get(bucket), mask1);
161 assert!(TestSparseBitmap::get(index1));
162 assert!(!TestSparseBitmap::get(index2));
163
164 TestSparseBitmap::set(index2);
166
167 assert_eq!(MockStorageMap::get(bucket), mask1 | mask2); assert!(TestSparseBitmap::get(index1));
170 assert!(TestSparseBitmap::get(index2));
171 })
172 }
173
174 #[test]
175 fn test_sparse_bitmap_different_buckets() {
176 TestExternalities::default().execute_with(|| {
177 let index1 = 300u64; let index2 = 300u64 + (1 << 7); let (bucket1, _) = TestSparseBitmap::compute_bucket_and_mask(index1);
181 let (bucket2, _) = TestSparseBitmap::compute_bucket_and_mask(index2);
182
183 let (_, mask1) = TestSparseBitmap::compute_bucket_and_mask(index1);
184 let (_, mask2) = TestSparseBitmap::compute_bucket_and_mask(index2);
185
186 assert_eq!(MockStorageMap::get(bucket1), 0);
188 assert_eq!(MockStorageMap::get(bucket2), 0);
189
190 TestSparseBitmap::set(index1);
192 TestSparseBitmap::set(index2);
193
194 assert_eq!(MockStorageMap::get(bucket1), mask1); assert_eq!(MockStorageMap::get(bucket2), mask2); assert!(TestSparseBitmap::get(index1));
199 assert!(TestSparseBitmap::get(index2));
200 })
201 }
202
203 #[test]
204 fn test_sparse_bitmap_wide_range() {
205 TestExternalities::default().execute_with(|| {
206 let test_indices = [
208 0u64, 1u64, 127u64, 128u64, 255u64, 1000u64, 123456u64, (1u64 << 32) - 1, 1u64 << 32, (1u64 << 32) + 1, (1u64 << 40) - 1, (1u64 << 40), (1u64 << 40) + 1, u64::MAX / 2, u64::MAX - 128, u64::MAX - 1, u64::MAX, ];
226
227 for &index in &test_indices {
229 assert!(!TestSparseBitmap::get(index), "Index {} should initially be unset", index);
231
232 TestSparseBitmap::set(index);
234
235 assert!(
237 TestSparseBitmap::get(index),
238 "Index {} should be set after setting",
239 index
240 );
241
242 let (bucket, mask) = TestSparseBitmap::compute_bucket_and_mask(index);
244
245 let value = MockStorageMap::get(bucket);
247 assert!(value & mask != 0, "Storage for index {} should have bit set", index);
248 }
249
250 for &index in &test_indices {
252 assert!(TestSparseBitmap::get(index), "Index {} should still be set", index);
253 }
254 })
255 }
256
257 #[test]
258 fn test_sparse_bitmap_bucket_boundaries() {
259 TestExternalities::default().execute_with(|| {
260 let boundary_pairs = [
262 (127u64, 128u64), (255u64, 256u64), (1023u64, 1024u64), ];
266
267 for (i1, i2) in boundary_pairs {
268 let (b1, m1) = TestSparseBitmap::compute_bucket_and_mask(i1);
270 let (b2, m2) = TestSparseBitmap::compute_bucket_and_mask(i2);
271
272 assert_ne!(b1, b2, "Indices {} and {} should be in different buckets", i1, i2);
274
275 TestSparseBitmap::set(i1);
277 TestSparseBitmap::set(i2);
278
279 assert!(TestSparseBitmap::get(i1), "Boundary index {} should be set", i1);
281 assert!(TestSparseBitmap::get(i2), "Boundary index {} should be set", i2);
282
283 let stored_b1_value = MockStorageMap::get(b1);
285 let stored_b2_value = MockStorageMap::get(b2);
286
287 assert_ne!(stored_b1_value, 0, "Storage for bucket {} should not be 0", b1);
289 assert_ne!(stored_b2_value, 0, "Storage for bucket {} should not be 0", b2);
290 assert!(
291 stored_b1_value & m1 != 0,
292 "Bit for index {} should be set in bucket {}",
293 i1,
294 b1
295 );
296 assert!(
297 stored_b2_value & m2 != 0,
298 "Bit for index {} should be set in bucket {}",
299 i2,
300 b2
301 );
302 }
303 })
304 }
305
306 #[test]
307 fn test_sparse_bitmap_large_buckets() {
308 TestExternalities::default().execute_with(|| {
309 let large_indices = [u64::MAX - 1, u64::MAX];
311
312 for &index in &large_indices {
313 let (bucket, mask) = TestSparseBitmap::compute_bucket_and_mask(index);
314
315 assert_eq!(
317 bucket,
318 u64::from(index) >> 7,
319 "Bucket calculation incorrect for {}",
320 index
321 );
322
323 TestSparseBitmap::set(index);
325 assert!(TestSparseBitmap::get(index), "Large index {} should be set", index);
326
327 let stored_value = MockStorageMap::get(bucket);
329 assert_ne!(stored_value, 0, "Storage for bucket {} should not be 0", bucket);
330 assert!(
331 stored_value & mask != 0,
332 "Bit for index {} should be set in bucket {}",
333 index,
334 bucket
335 );
336 }
337 })
338 }
339}