1use std::{cmp::Ordering, num::NonZeroU64};
2
3use bao_tree::{ChunkNum, ChunkRanges};
4use range_collections::range_set::RangeSetRange;
5use serde::{Deserialize, Deserializer, Serialize};
6use smallvec::SmallVec;
7
8use crate::store::util::{
9 observer::{Combine, CombineInPlace},
10 RangeSetExt,
11};
12
13pub(crate) fn is_validated(size: NonZeroU64, ranges: &ChunkRanges) -> bool {
14 let size = size.get();
15 let last_chunk = ChunkNum::chunks(size) - 1;
17 ranges.contains(&last_chunk)
18}
19
20pub fn is_complete(size: NonZeroU64, ranges: &ChunkRanges) -> bool {
21 let complete = ChunkRanges::from(..ChunkNum::chunks(size.get()));
22 complete.is_subset(ranges)
24}
25
26#[derive(Debug, PartialEq, Eq, Clone, Default)]
28pub struct Bitfield {
29 pub(crate) size: u64,
31 pub ranges: ChunkRanges,
33}
34
35#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
36pub struct BitfieldState {
37 pub complete: bool,
38 pub validated_size: Option<u64>,
39}
40
41impl Serialize for Bitfield {
42 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
43 let mut numbers = SmallVec::<[_; 4]>::new();
44 numbers.push(self.size);
45 numbers.extend(self.ranges.boundaries().iter().map(|x| x.0));
46 numbers.serialize(serializer)
47 }
48}
49
50impl<'de> Deserialize<'de> for Bitfield {
51 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
52 let mut numbers: SmallVec<[u64; 4]> = SmallVec::deserialize(deserializer)?;
53
54 if numbers.is_empty() {
56 return Err(serde::de::Error::custom("Bitfield needs at least size"));
57 }
58
59 let size = numbers.remove(0);
61 let mut ranges = SmallVec::<[_; 2]>::new();
62 for num in numbers {
63 ranges.push(ChunkNum(num));
64 }
65 let Some(ranges) = ChunkRanges::new(ranges) else {
66 return Err(serde::de::Error::custom("Boundaries are not sorted"));
67 };
68 Ok(Bitfield::new(ranges, size))
69 }
70}
71
72impl Bitfield {
73 #[cfg(feature = "fs-store")]
74 pub(crate) fn new_unchecked(ranges: ChunkRanges, size: u64) -> Self {
75 Self { ranges, size }
76 }
77
78 pub fn new(mut ranges: ChunkRanges, size: u64) -> Self {
79 if let Some(size) = NonZeroU64::new(size) {
81 let end = ChunkNum::chunks(size.get());
82 if ChunkRanges::from(..end).is_subset(&ranges) {
83 ranges = ChunkRanges::all();
85 } else if ranges.contains(&(end - 1)) {
86 ranges |= ChunkRanges::from(end..);
88 }
89 }
90 Self { ranges, size }
91 }
92
93 pub fn size(&self) -> u64 {
94 self.size
95 }
96
97 pub fn empty() -> Self {
99 Self {
100 ranges: ChunkRanges::empty(),
101 size: 0,
102 }
103 }
104
105 pub fn complete(size: u64) -> Self {
107 Self {
108 ranges: ChunkRanges::all(),
109 size,
110 }
111 }
112
113 pub fn is_validated(&self) -> bool {
115 if let Some(size) = NonZeroU64::new(self.size) {
116 is_validated(size, &self.ranges)
117 } else {
118 self.ranges.is_all()
119 }
120 }
121
122 pub fn is_complete(&self) -> bool {
124 if let Some(size) = NonZeroU64::new(self.size) {
125 is_complete(size, &self.ranges)
126 } else {
127 self.ranges.is_all()
128 }
129 }
130
131 pub fn validated_size(&self) -> Option<u64> {
133 if self.is_validated() {
134 Some(self.size)
135 } else {
136 None
137 }
138 }
139
140 pub fn total_bytes(&self) -> u64 {
142 let mut total = 0;
143 for range in self.ranges.iter() {
144 let (start, end) = match range {
145 RangeSetRange::Range(range) => {
146 (range.start.to_bytes(), range.end.to_bytes().min(self.size))
147 }
148 RangeSetRange::RangeFrom(range) => (range.start.to_bytes(), self.size),
149 };
150 total += end - start;
151 }
152 total
153 }
154
155 pub fn state(&self) -> BitfieldState {
156 BitfieldState {
157 complete: self.is_complete(),
158 validated_size: self.validated_size(),
159 }
160 }
161
162 pub fn update(&mut self, update: &Bitfield) -> UpdateResult {
167 let s0 = self.state();
168 let bitfield1 = self.clone().combine(update.clone());
170 if bitfield1 != *self {
171 let s1 = bitfield1.state();
172 *self = bitfield1;
173 if s0 != s1 {
174 UpdateResult::MajorChange(s0, s1)
175 } else {
176 UpdateResult::MinorChange(s1)
177 }
178 } else {
179 UpdateResult::NoChange(s0)
180 }
181 }
182
183 pub fn is_empty(&self) -> bool {
184 self.ranges.is_empty() && self.size == 0
185 }
186
187 pub fn diff(&self, that: &Bitfield) -> Self {
189 let size = choose_size(self, that);
190 let ranges_diff = &self.ranges ^ &that.ranges;
191 Self {
192 ranges: ranges_diff,
193 size,
194 }
195 }
196}
197
198pub fn choose_size(a: &Bitfield, b: &Bitfield) -> u64 {
199 match (a.ranges.upper_bound(), b.ranges.upper_bound()) {
200 (Some(ac), Some(bc)) => match ac.cmp(&bc) {
201 Ordering::Less => b.size,
202 Ordering::Greater => a.size,
203 Ordering::Equal => a.size.max(b.size),
204 },
205 (Some(_), None) => b.size,
206 (None, Some(_)) => a.size,
207 (None, None) => a.size.max(b.size),
208 }
209}
210
211impl Combine for Bitfield {
212 fn combine(self, that: Self) -> Self {
213 let size = choose_size(&self, &that);
215 let ranges = self.ranges | that.ranges;
216 Self::new(ranges, size)
217 }
218}
219
220impl CombineInPlace for Bitfield {
221 fn combine_with(&mut self, other: Self) -> Self {
222 let new = &other.ranges - &self.ranges;
223 if new.is_empty() {
224 return Bitfield::empty();
225 }
226 self.ranges.union_with(&new);
227 self.size = choose_size(self, &other);
228 Bitfield {
229 ranges: new,
230 size: self.size,
231 }
232 }
233
234 fn is_neutral(&self) -> bool {
235 self.ranges.is_empty() && self.size == 0
236 }
237}
238
239#[allow(clippy::enum_variant_names)]
240#[derive(Debug, Clone, Copy)]
241pub enum UpdateResult {
242 NoChange(BitfieldState),
243 MinorChange(BitfieldState),
244 MajorChange(BitfieldState, BitfieldState),
245}
246
247impl UpdateResult {
248 pub fn new_state(&self) -> &BitfieldState {
249 match self {
250 UpdateResult::NoChange(new) => new,
251 UpdateResult::MinorChange(new) => new,
252 UpdateResult::MajorChange(_, new) => new,
253 }
254 }
255
256 pub fn was_validated(&self) -> bool {
258 match self {
259 UpdateResult::NoChange(_) => false,
260 UpdateResult::MinorChange(_) => false,
261 UpdateResult::MajorChange(old, new) => {
262 new.validated_size.is_some() && old.validated_size.is_none()
263 }
264 }
265 }
266
267 pub fn changed(&self) -> bool {
268 match self {
269 UpdateResult::NoChange(_) => false,
270 UpdateResult::MinorChange(_) => true,
271 UpdateResult::MajorChange(_, _) => true,
272 }
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use bao_tree::{ChunkNum, ChunkRanges};
279 use proptest::prelude::{prop, Strategy};
280 use smallvec::SmallVec;
281 use test_strategy::proptest;
282
283 use super::Bitfield;
284 use crate::store::util::observer::{Combine, CombineInPlace};
285
286 fn gen_chunk_ranges(max: ChunkNum, k: usize) -> impl Strategy<Value = ChunkRanges> {
287 prop::collection::btree_set(0..=max.0, 0..=k).prop_map(|vec| {
288 let bounds = vec.into_iter().map(ChunkNum).collect::<SmallVec<[_; 2]>>();
289 ChunkRanges::new(bounds).unwrap()
290 })
291 }
292
293 fn gen_bitfields(size: u64, k: usize) -> impl Strategy<Value = Bitfield> {
294 (0..size).prop_flat_map(move |size| {
295 let chunks = ChunkNum::full_chunks(size);
296 gen_chunk_ranges(chunks, k).prop_map(move |ranges| Bitfield::new(ranges, size))
297 })
298 }
299
300 fn gen_non_empty_bitfields(size: u64, k: usize) -> impl Strategy<Value = Bitfield> {
301 gen_bitfields(size, k).prop_filter("non-empty", |x| !x.is_neutral())
302 }
303
304 #[proptest]
305 fn test_combine_empty(#[strategy(gen_non_empty_bitfields(32768, 4))] a: Bitfield) {
306 assert_eq!(a.clone().combine(Bitfield::empty()), a);
307 assert_eq!(Bitfield::empty().combine(a.clone()), a);
308 }
309
310 #[proptest]
311 fn test_combine_order(
312 #[strategy(gen_non_empty_bitfields(32768, 4))] a: Bitfield,
313 #[strategy(gen_non_empty_bitfields(32768, 4))] b: Bitfield,
314 ) {
315 let ab = a.clone().combine(b.clone());
316 let ba = b.combine(a);
317 assert_eq!(ab, ba);
318 }
319
320 #[proptest]
321 fn test_complete_normalized(#[strategy(gen_non_empty_bitfields(32768, 4))] a: Bitfield) {
322 if a.is_complete() {
323 assert_eq!(a.ranges, ChunkRanges::all());
324 }
325 }
326}