pub(super) use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
pub(super) use libc::O_DIRECT;
#[derive(Eq, PartialEq, Debug)]
pub(super) struct IntersectedPages(pub std::ops::Range<u64>);
impl Ord for IntersectedPages {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0
.start
.cmp(&other.0.start)
.then_with(|| other.0.end.cmp(&self.0.end))
}
}
impl PartialOrd for IntersectedPages {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
pub(super) fn coalesce_byte_ranges_with_page_size(
file_size: u64,
byte_ranges: &[zarrs_storage::byte_range::ByteRange],
page_size: u64,
) -> impl Iterator<Item = IntersectedPages> {
use itertools::Itertools;
let intersected_pages: std::collections::BTreeSet<IntersectedPages> = byte_ranges
.iter()
.map(|range| {
let start_page = range.start(file_size) / page_size;
let end_page = range.end(file_size).div_ceil(page_size);
IntersectedPages(start_page..end_page)
})
.collect();
intersected_pages.into_iter().coalesce(|a, b| {
if a.0.end >= b.0.start {
Ok(IntersectedPages(a.0.start..b.0.end.max(a.0.end)))
} else {
Err((a, b))
}
})
}
#[cfg(test)]
mod tests {
use zarrs_storage::byte_range::ByteRange;
use super::*;
#[test]
fn test_coalesce_byte_ranges_with_page_size() {
let ps = 4;
let file_size = 64;
let byte_ranges = vec![
ByteRange::FromStart(5, Some(2)), ByteRange::FromStart(0, Some(1)), ByteRange::FromStart(30, Some(4)), ByteRange::Suffix(4), ByteRange::FromStart(8, Some(4)), ByteRange::FromStart(8, Some(8)), ByteRange::Suffix(7), ];
let pages: Vec<_> =
coalesce_byte_ranges_with_page_size(file_size, &byte_ranges, ps).collect();
let expected = vec![
IntersectedPages(0..4),
IntersectedPages(7..9),
IntersectedPages(14..16),
];
assert_eq!(pages, expected);
}
}