fxfs/object_store/data_object_handle/
allocated_ranges.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fuchsia_sync::{Mutex, MutexGuard};
6use std::ops::Range;
7
8/// Whether this particular logical file range is in overwrite or CoW mode. Overwrite mode ranges
9/// have overwrite extents already allocated, and should use multi_overwrite. CoW mode ranges
10/// should use multi_write, and might or might not have extents in the region already.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum RangeType {
13    Cow(Range<u64>),
14    Overwrite(Range<u64>),
15}
16
17/// AllocatedRanges tracks the logical ranges of a file which are pre-allocated using allocate, in
18/// other words, the ranges of the file with overwrite extents. It's used by PagedObjectHandle to
19/// split writes to CoW ranges and writes to overwrite ranges into separate batches so they can
20/// have different transaction options.
21///
22/// It has a mutex on the list of ranges to make sure checking for overlaps and adding new ranges
23/// don't collide. When getting an iterator of overlapping ranges, the lock is held until the
24/// iterator is dropped.
25#[derive(Debug)]
26pub struct AllocatedRanges {
27    ranges: Mutex<Vec<Range<u64>>>,
28}
29
30/// An iterator over the types of ranges within a particular query range. The range types can be
31/// CoW or overwrite. The lock inside AllocatedRanges is held until this is dropped so be careful
32/// with it across await points.
33pub struct RangeOverlapIter<'a> {
34    query_range: Range<u64>,
35    index: usize,
36    ranges: MutexGuard<'a, Vec<Range<u64>>>,
37}
38
39impl<'a> Iterator for RangeOverlapIter<'a> {
40    type Item = RangeType;
41
42    fn next(&mut self) -> Option<Self::Item> {
43        if self.query_range.start == self.query_range.end {
44            return None;
45        }
46
47        if self.index == self.ranges.len() || self.query_range.start < self.ranges[self.index].start
48        {
49            let range = self.query_range.start
50                ..std::cmp::min(
51                    self.query_range.end,
52                    self.ranges.get(self.index).map(|r| r.start).unwrap_or(self.query_range.end),
53                );
54            self.query_range.start = range.end;
55            return Some(RangeType::Cow(range));
56        }
57
58        let range = self.query_range.start
59            ..std::cmp::min(self.query_range.end, self.ranges[self.index].end);
60        self.query_range.start = range.end;
61        self.index += 1;
62
63        return Some(RangeType::Overwrite(range));
64    }
65}
66
67impl AllocatedRanges {
68    pub fn new(ranges_to_apply: &[Range<u64>]) -> Self {
69        let mut ranges = Vec::new();
70        for range_to_apply in ranges_to_apply {
71            Self::apply_range_to(&mut ranges, range_to_apply.clone());
72        }
73        Self { ranges: Mutex::new(ranges) }
74    }
75
76    pub fn clear(&self) {
77        self.ranges.lock().clear();
78    }
79
80    pub fn is_empty(&self) -> bool {
81        self.ranges.lock().is_empty()
82    }
83
84    /// Find the overlapping overwrite ranges in the given range for this file, so writes can be
85    /// split between them appropriately. Ranges with RangeType::Overwrite should be written to
86    /// with multi_overwrite and RangeType::Cow should use multi_write.
87    ///
88    /// Note: The returned iterator holds a lock on the ranges until it's dropped, so use it
89    /// accordingly.
90    pub fn overlap<'a>(&'a self, query_range: Range<u64>) -> RangeOverlapIter<'a> {
91        let ranges = self.ranges.lock();
92        let index = match ranges.binary_search_by_key(&query_range.start, |r| r.end) {
93            // If the start of the query range is exactly at the end of a range, there is zero
94            // overlap with that range, so start with the next one.
95            Ok(pos) => pos + 1,
96            Err(pos) => pos,
97        };
98        RangeOverlapIter { query_range, index, ranges }
99    }
100
101    /// Apply range takes a single, valid file range and inserts it into the list of ranges it's
102    /// storing. This list of ranges, so it's easy to insert and search, is kept sorted and merged,
103    /// so that the list has no overlapping ranges.
104    pub fn apply_range(&self, new_range: Range<u64>) {
105        Self::apply_range_to(self.ranges.lock().as_mut(), new_range)
106    }
107
108    pub fn apply_range_to(ranges: &mut Vec<Range<u64>>, new_range: Range<u64>) {
109        let merge_start = match ranges.binary_search_by_key(&new_range.start, |r| r.end) {
110            // Ok means the returned index has a range that ends where this new one starts, which
111            // is handled fine by the logic below.
112            Ok(pos) => pos,
113            Err(pos) => pos,
114        };
115        if merge_start == ranges.len() {
116            // The new ranges starts beyond the end of all the current ranges.
117            ranges.push(new_range);
118            return;
119        }
120
121        if ranges[merge_start].start <= new_range.start {
122            // If the new range start is past (or at) the start but before the end, this is the
123            // first range that needs to get merged.
124            ranges[merge_start].end = std::cmp::max(ranges[merge_start].end, new_range.end);
125        } else {
126            // The new range starts before this one. Insert it at this spot, and merge from here.
127            ranges.insert(merge_start, new_range);
128        }
129
130        let mut merge_index = merge_start + 1;
131        while merge_index < ranges.len() && ranges[merge_index].start <= ranges[merge_start].end {
132            ranges[merge_start].end =
133                std::cmp::max(ranges[merge_start].end, ranges[merge_index].end);
134            merge_index += 1;
135        }
136        ranges.drain(merge_start + 1..merge_index);
137    }
138
139    /// For when a file is truncated. Drop any ranges past the cutoff point. If a range covers the
140    /// cutoff point, it is modified to end at the cutoff.
141    ///
142    /// Additionally, this returns true if there were previously tracked ranges but they were all
143    /// completely removed by this truncate call. In this case, metadata for a file will need to be
144    /// updated since there are no longer any overwrite ranges.
145    pub fn truncate(&self, cutoff: u64) -> bool {
146        let mut ranges = self.ranges.lock();
147        if ranges.is_empty() {
148            // Nothing to do, return early. Since there were no ranges, we didn't _remove_ all the
149            // ranges which is the specific case we want to flag on return.
150            return false;
151        }
152        let mut index = match ranges.binary_search_by_key(&cutoff, |r| r.end) {
153            // If the cutoff is exactly at the end of a range, that range doesn't change, so start
154            // with the next one.
155            Ok(pos) => pos + 1,
156            Err(pos) => pos,
157        };
158        // If the index points at the end of the list, the cutoff is after all the ranges.
159        if index == ranges.len() {
160            return false;
161        }
162        // Handle the cutoff being partway through a range.
163        if ranges[index].start < cutoff {
164            ranges[index].end = cutoff;
165            index += 1;
166        }
167
168        ranges.truncate(index);
169        // If at this point our index is zero, then we completely dropped all the ranges, and there
170        // were some ranges, because we would have returned early if it was empty to begin with.
171        index == 0
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::{AllocatedRanges, RangeType};
178    use std::ops::Range;
179
180    #[fuchsia::test]
181    fn test_allocated_ranges() {
182        struct Case {
183            applied_ranges: Vec<Range<u64>>,
184            expected_ranges: Vec<Range<u64>>,
185        }
186        let cases = [
187            Case { applied_ranges: vec![0..1], expected_ranges: vec![0..1] },
188            Case { applied_ranges: vec![0..1, 2..3], expected_ranges: vec![0..1, 2..3] },
189            Case {
190                applied_ranges: vec![0..1, 2..3, 4..5],
191                expected_ranges: vec![0..1, 2..3, 4..5],
192            },
193            Case {
194                applied_ranges: vec![4..5, 2..3, 0..1],
195                expected_ranges: vec![0..1, 2..3, 4..5],
196            },
197            Case {
198                applied_ranges: vec![0..1, 4..5, 2..3],
199                expected_ranges: vec![0..1, 2..3, 4..5],
200            },
201            Case { applied_ranges: vec![0..10, 20..30], expected_ranges: vec![0..10, 20..30] },
202            Case { applied_ranges: vec![0..5, 0..5], expected_ranges: vec![0..5] },
203            Case { applied_ranges: vec![0..5, 0..1], expected_ranges: vec![0..5] },
204            Case { applied_ranges: vec![0..5, 0..10], expected_ranges: vec![0..10] },
205            Case { applied_ranges: vec![3..4, 2..3], expected_ranges: vec![2..4] },
206            Case { applied_ranges: vec![2..3, 3..4], expected_ranges: vec![2..4] },
207            Case { applied_ranges: vec![2..3, 3..4, 4..5, 1..2], expected_ranges: vec![1..5] },
208            Case { applied_ranges: vec![1..10, 2..4, 8..9, 2..9], expected_ranges: vec![1..10] },
209            Case { applied_ranges: vec![2..3, 3..4, 1..2, 0..10], expected_ranges: vec![0..10] },
210            Case {
211                applied_ranges: vec![1..2, 3..4, 5..6, 7..8],
212                expected_ranges: vec![1..2, 3..4, 5..6, 7..8],
213            },
214            Case {
215                applied_ranges: vec![1..2, 3..4, 5..6, 7..8, 0..10],
216                expected_ranges: vec![0..10],
217            },
218            Case { applied_ranges: vec![4..8, 6..10], expected_ranges: vec![4..10] },
219            Case { applied_ranges: vec![4..8, 2..6], expected_ranges: vec![2..8] },
220            Case {
221                applied_ranges: vec![2..5, 7..11, 13..18, 20..30, 40..45, 10..25],
222                expected_ranges: vec![2..5, 7..30, 40..45],
223            },
224        ];
225
226        for case in cases {
227            let ranges = AllocatedRanges::new(&case.applied_ranges);
228            assert_eq!(*ranges.ranges.lock(), case.expected_ranges);
229        }
230    }
231
232    #[fuchsia::test]
233    fn test_allocated_ranges_overlap() {
234        let ranges = AllocatedRanges::new(&[]);
235        // With no overwrite ranges recorded, all overlap calls should return the same range
236        // wrapped with Cow.
237        assert_eq!(ranges.overlap(0..1).collect::<Vec<_>>(), vec![RangeType::Cow(0..1)]);
238        assert_eq!(ranges.overlap(10..20).collect::<Vec<_>>(), vec![RangeType::Cow(10..20)]);
239
240        ranges.apply_range(10..20);
241        assert_eq!(ranges.overlap(30..35).collect::<Vec<_>>(), vec![RangeType::Cow(30..35)]);
242        assert_eq!(ranges.overlap(20..30).collect::<Vec<_>>(), vec![RangeType::Cow(20..30)]);
243        assert_eq!(ranges.overlap(0..5).collect::<Vec<_>>(), vec![RangeType::Cow(0..5)]);
244        assert_eq!(ranges.overlap(0..10).collect::<Vec<_>>(), vec![RangeType::Cow(0..10)]);
245
246        assert_eq!(ranges.overlap(12..13).collect::<Vec<_>>(), vec![RangeType::Overwrite(12..13)]);
247        assert_eq!(ranges.overlap(10..20).collect::<Vec<_>>(), vec![RangeType::Overwrite(10..20)]);
248
249        assert_eq!(
250            ranges.overlap(5..15).collect::<Vec<_>>(),
251            vec![RangeType::Cow(5..10), RangeType::Overwrite(10..15)]
252        );
253        assert_eq!(
254            ranges.overlap(5..20).collect::<Vec<_>>(),
255            vec![RangeType::Cow(5..10), RangeType::Overwrite(10..20)]
256        );
257        assert_eq!(
258            ranges.overlap(5..25).collect::<Vec<_>>(),
259            vec![RangeType::Cow(5..10), RangeType::Overwrite(10..20), RangeType::Cow(20..25)]
260        );
261
262        assert_eq!(ranges.overlap(10..15).collect::<Vec<_>>(), vec![RangeType::Overwrite(10..15)]);
263        assert_eq!(ranges.overlap(10..20).collect::<Vec<_>>(), vec![RangeType::Overwrite(10..20)]);
264        assert_eq!(
265            ranges.overlap(10..25).collect::<Vec<_>>(),
266            vec![RangeType::Overwrite(10..20), RangeType::Cow(20..25)]
267        );
268
269        assert_eq!(ranges.overlap(15..20).collect::<Vec<_>>(), vec![RangeType::Overwrite(15..20)]);
270        assert_eq!(
271            ranges.overlap(15..25).collect::<Vec<_>>(),
272            vec![RangeType::Overwrite(15..20), RangeType::Cow(20..25)]
273        );
274
275        assert_eq!(ranges.overlap(20..25).collect::<Vec<_>>(), vec![RangeType::Cow(20..25)]);
276
277        ranges.apply_range(30..40);
278        ranges.apply_range(50..60);
279
280        assert_eq!(
281            ranges.overlap(15..35).collect::<Vec<_>>(),
282            vec![
283                RangeType::Overwrite(15..20),
284                RangeType::Cow(20..30),
285                RangeType::Overwrite(30..35)
286            ]
287        );
288        assert_eq!(
289            ranges.overlap(25..45).collect::<Vec<_>>(),
290            vec![RangeType::Cow(25..30), RangeType::Overwrite(30..40), RangeType::Cow(40..45)]
291        );
292        assert_eq!(
293            ranges.overlap(0..70).collect::<Vec<_>>(),
294            vec![
295                RangeType::Cow(0..10),
296                RangeType::Overwrite(10..20),
297                RangeType::Cow(20..30),
298                RangeType::Overwrite(30..40),
299                RangeType::Cow(40..50),
300                RangeType::Overwrite(50..60),
301                RangeType::Cow(60..70)
302            ]
303        );
304
305        ranges.apply_range(0..100);
306        assert_eq!(ranges.overlap(0..100).collect::<Vec<_>>(), vec![RangeType::Overwrite(0..100)]);
307    }
308
309    #[fuchsia::test]
310    fn test_trim_ranges() {
311        struct Case {
312            applied: Vec<Range<u64>>,
313            cutoff: u64,
314            expected: Vec<Range<u64>>,
315            dropped_all: bool,
316        }
317
318        let cases = [
319            Case { applied: vec![], cutoff: 10, expected: vec![], dropped_all: false },
320            Case { applied: vec![0..20], cutoff: 0, expected: vec![], dropped_all: true },
321            Case { applied: vec![0..20], cutoff: 10, expected: vec![0..10], dropped_all: false },
322            Case { applied: vec![0..20], cutoff: 20, expected: vec![0..20], dropped_all: false },
323            Case { applied: vec![0..20], cutoff: 30, expected: vec![0..20], dropped_all: false },
324            Case { applied: vec![0..20, 30..50], cutoff: 0, expected: vec![], dropped_all: true },
325            Case {
326                applied: vec![0..20, 30..50],
327                cutoff: 10,
328                expected: vec![0..10],
329                dropped_all: false,
330            },
331            Case {
332                applied: vec![0..20, 30..50],
333                cutoff: 30,
334                expected: vec![0..20],
335                dropped_all: false,
336            },
337            Case {
338                applied: vec![0..20, 30..50],
339                cutoff: 40,
340                expected: vec![0..20, 30..40],
341                dropped_all: false,
342            },
343            Case {
344                applied: vec![30..50, 60..80, 90..100],
345                cutoff: 29,
346                expected: vec![],
347                dropped_all: true,
348            },
349            Case {
350                applied: vec![30..50, 60..80, 90..100],
351                cutoff: 30,
352                expected: vec![],
353                dropped_all: true,
354            },
355            Case {
356                applied: vec![30..50, 60..80, 90..100],
357                cutoff: 31,
358                expected: vec![30..31],
359                dropped_all: false,
360            },
361            Case {
362                applied: vec![30..50, 60..80, 90..100],
363                cutoff: 70,
364                expected: vec![30..50, 60..70],
365                dropped_all: false,
366            },
367            Case {
368                applied: vec![30..50, 60..80, 90..100],
369                cutoff: 110,
370                expected: vec![30..50, 60..80, 90..100],
371                dropped_all: false,
372            },
373        ];
374
375        for (i, case) in cases.into_iter().enumerate() {
376            let ranges = AllocatedRanges::new(&case.applied);
377            assert_eq!(ranges.truncate(case.cutoff), case.dropped_all, "failed case # {}", i);
378            assert_eq!(*ranges.ranges.lock(), case.expected, "failed case # {}", i);
379        }
380    }
381}