blob: 20b2eaeca6460503b0b6732ba029325d069ac7fb [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{error::Error, format::ExtentInfo, properties::ExtentProperties, utils::RangeOps},
std::convert::TryFrom,
std::io::Write,
std::ops::Range,
zerocopy::AsBytes,
};
#[derive(Debug, Clone)]
pub struct Extent {
/// storage_range is the offsets this extent maps to. This is the range the
/// user has provided to us.
pub storage_range: Range<u64>,
/// Properties of this extent.
pub properties: ExtentProperties,
// When user adds an extent, extent may or may not be accompanied with data.
// Data is sent if user has already read data from the disk or when user has
// modified the data.
data: Option<Box<[u8]>>,
}
impl PartialEq for Extent {
fn eq(&self, other: &Self) -> bool {
// We do not care about where this extent maps into image file. So we consider
// only storage_range and properties and ignore image_range.
self.storage_range == other.storage_range && self.properties == other.properties
}
}
impl TryFrom<ExtentInfo> for Extent {
type Error = Error;
fn try_from(extent_c: ExtentInfo) -> Result<Self, Self::Error> {
extent_c.check()?;
Ok(Extent {
storage_range: extent_c.start..extent_c.end,
properties: ExtentProperties {
extent_kind: extent_c.extent.to_kind()?,
data_kind: extent_c.data.to_kind()?,
},
data: None,
})
}
}
impl Extent {
pub fn new(
storage_range: Range<u64>,
properties: ExtentProperties,
data: Option<Box<[u8]>>,
) -> Result<Self, Error> {
if data.is_some() {
// We allow contructing extent with invalid range. But invalid range should not
// accompany with valid data.
if !storage_range.is_valid() {
return Err(Error::InvalidRange);
}
// If range is valid and data is not None, then data length should match range length.
let data_length = data.as_ref().unwrap().len() as u64;
if data_length != storage_range.length() {
return Err(Error::InvalidDataLength);
}
}
Ok(Extent { storage_range, properties, data })
}
/// Returns storage range start offset for the extent.
pub fn start(&self) -> u64 {
self.storage_range.start
}
/// Returns storage range end offset for the extent.
pub fn end(&self) -> u64 {
self.storage_range.end
}
// Sets storage_range start.
// Test only helper routines that reduces boiler plate code.
#[cfg(test)]
pub(crate) fn set_start(&mut self, start: u64) {
self.storage_range.start = start;
}
// Sets storage_range end.
// This function is available only for tests. It reduces boilerplate.
#[cfg(test)]
pub(crate) fn set_end(&mut self, end: u64) {
self.storage_range.end = end;
}
/// Returns storage range of this extent.
pub fn storage_range(&self) -> &Range<u64> {
&self.storage_range
}
/// Returns properties of this extent.
pub fn properties(&self) -> &ExtentProperties {
&self.properties
}
pub fn serialized_size(&self) -> u64 {
let c_extent = ExtentInfo::from(self.clone());
c_extent.as_bytes().len() as u64
}
pub fn write(&self, out_stream: &mut dyn Write) -> Result<u64, Error> {
let c_extent = ExtentInfo::from(self.clone());
out_stream.write_all(c_extent.as_bytes()).unwrap();
Ok(c_extent.as_bytes().len() as u64)
}
/// Two extents are is_adjacent if one ends where another starts.
/// Extent properties don't matter.
pub fn is_adjacent(&self, other: &Self) -> bool {
self.storage_range.is_adjacent(&other.storage_range)
}
/// Two extents are considered mergeable if they
/// * either overlap or are is_adjacent, and
/// * they have same set of properties
fn is_mergeable(&self, other: &Self) -> bool {
(self.storage_range.overlaps(&other.storage_range)
|| self.storage_range.is_adjacent(&other.storage_range))
&& self.properties() == other.properties()
&& self.data == other.data
}
/// Merge data of two mergable extents.
/// TODO(auradkar).
fn merge_data(&self, other: &Self) -> Option<Box<[u8]>> {
debug_assert!(self.is_mergeable(&other));
match &self.data {
Some(_data) => {
todo!("Adding data along with extent is not yet supported");
}
None => None,
}
}
/// Merge two mergeable extents.
fn merge(&self, other: &Self) -> Self {
assert!(self.is_mergeable(&other));
let storage_range = std::cmp::min(self.storage_range.start, other.storage_range.start)
..std::cmp::max(self.storage_range.end, other.storage_range.end);
Extent::new(storage_range, self.properties().clone(), self.merge_data(&other)).unwrap()
}
/// Two extents overlap if their storage ranges overlap. Extent
/// properties don't matter.
pub fn overlaps(&self, other: &Self) -> bool {
self.storage_range().overlaps(&other.storage_range())
}
/// Returns `true` if self has higher priority that the other.
///
/// Asserts that the two extents overlap.
fn overrides(&self, other: &Self) -> bool {
assert!(self.storage_range().overlaps(other.storage_range()));
self.properties.overrides(&other.properties)
}
/// Splits or merges two extents based on the storage_range and properties of
/// the extents.
///
/// Function returns remaining part of self whose storage_range.start is greater
/// than or equal to other.storage_range.end. Any other extents that should be
/// kept from either `self` or `other` are appended to `result`.
/// If self is entirely consumed, it returns None.
///
/// Function asserts that the two extents overlap.
///
/// | self | other | return | result |
/// |---------------|---------------|---------------|--------------------------------|
/// | // Merge with same range and same properties. | | | |
/// | (1..5, &low) | (1..5, &low) | (1..5, &low) | [] |
/// | // Merge with same range and different properties. | | | |
/// | (1..5, &low) | (1..5, &high) | None | [(1..5, &high)] |
/// | (1..5, &high) | (1..5, &low) | (1..5, &high) | [] |
/// | // Merge is_adjacent. | | | |
/// | (1..5, &low) | (5..8, &low) | (1..8, &low) | [] |
/// | (5..8, &low) | (1..5, &low) | (1..8, &low) | [] |
/// | // Non-mergeable is_adjacent and mixed properties. | | | |
/// | (1..5, &low) | (5..8, &high) | (1..5, &low) | [(5..8, &high)] |
/// | (1..5, &high) | (5..8, &low) | (1..5, &high) | [(5..8, &low)] |
/// | (5..8, &low) | (1..5, &high) | (5..8, &low) | [(1..5, &high)] |
/// | (5..8, &high) | (1..5, &low) | (5..8, &high) | [(1..5, &low)] |
/// | // Merge with overlapping ranges. Different combination of properties. ||| |
/// | (1..5, &low) | (4..8, &low) | (1..8, &low) | [] |
/// | (4..8, &low) | (1..5, &low) | (1..8, &low) | [] |
/// | (1..5, &low) | (4..8, &high) | None, | [(1..4, &low), (4..8, &high)] |
/// | (1..5, &high) | (4..8, &low) | (1..5, &high) | [(5..8, &low)] |
/// | (4..8, &low) | (1..5, &high) | (5..8, &low) | [(1..5, &high)] |
/// | (4..8, &high) | (1..5, &low) | (4..8, &high) | [((1..4, &low)] |
/// | // One has the other. | | | |
/// | (1..8, &low) | (4..6, &low) | (1..8, &low) | [] |
/// | (4..6, &low) | (1..8, &low) | (1..8, &low) | [] |
/// | (1..8, &high) | (4..6, &low) | (1..8, &high) | [] |
/// | (4..6, &low) | (1..8, &high) | None | [(1..8, &high)] |
/// | (1..8, &low) | (4..6, &high) | (6..8, &low) | [(1..4, &low), (4..6, &high)], |
/// | (4..6, &high) | (1..8, &low) | (4..6, &high) | [(1..4, &low), (6..8, &low)] |
///
pub fn split_or_merge(&self, other: &Self, result: &mut Vec<Self>) -> Option<Self> {
assert!(self.overlaps(other) || self.is_adjacent(other));
if self.is_mergeable(&other) {
return Some(self.merge(other));
}
// These are adjacent that cannot be merged. Just retain both of them.
if self.is_adjacent(&other) {
result.push(other.clone());
return Some(self.clone());
}
// If properties are not the same then self can split the other based on
// whether properties of self are stronger than other's.
//
// If self has stronger properties, then it overrides other for the ranges that overlaps.
if self.overrides(other) {
// self swallows all of other.
if self.storage_range().contains_range(&other.storage_range()) {
return Some(self.clone());
}
// Other starts before self. So a slice from other's start to self's start is
// retained.
if self.start() > other.start() {
result.push(
Extent::new(other.start()..self.start(), *other.properties(), None).unwrap(),
);
}
// Other ends after self. So a slice from self's end to other's start is
// retained.
if self.end() < other.end() {
result
.push(Extent::new(self.end()..other.end(), *other.properties(), None).unwrap());
}
// The whole of self is retained.
return Some(self.clone());
} else {
// other swallows all of self.
if other.storage_range().contains_range(self.storage_range())
|| self.storage_range() == other.storage_range()
{
result.push(other.clone());
return None;
}
// self start before the other. Some part of self at the beginning is retained.
if self.start() < other.start() {
result.push(
Extent::new(self.start()..other.start(), *self.properties(), None).unwrap(),
);
}
result.push(other.clone());
// self ends after the other ends. Some part of self towards the self's end is retained.
if self.end() > other.end() {
return Some(
Extent::new(other.end()..self.end(), *self.properties(), None).unwrap(),
);
}
// All relevant pieces of self are broken down. None remains.
None
}
}
}
#[cfg(test)]
mod test {
use {
crate::{
extent::Extent,
properties::{DataKind, ExtentKind, ExtentProperties},
utils::RangeOps,
utils::*,
},
std::ops::Range,
};
fn get_same_properties() -> (ExtentProperties, ExtentProperties) {
let p = ExtentProperties { extent_kind: ExtentKind::Data, data_kind: DataKind::Unmodified };
(p, p)
}
fn get_different_properties() -> (ExtentProperties, ExtentProperties) {
(
ExtentProperties { extent_kind: ExtentKind::Data, data_kind: DataKind::Unmodified },
ExtentProperties { extent_kind: ExtentKind::Pii, data_kind: DataKind::Unmodified },
)
}
// First property's priority is lower.
fn get_lower_priority_properties() -> (ExtentProperties, ExtentProperties) {
let (a, b) = (
ExtentProperties { extent_kind: ExtentKind::Data, data_kind: DataKind::Unmodified },
ExtentProperties { extent_kind: ExtentKind::Pii, data_kind: DataKind::Unmodified },
);
assert!(a < b);
(a, b)
}
fn get_extents(
ranges: (Range<u64>, Range<u64>),
properties: (ExtentProperties, ExtentProperties),
) -> (Extent, Extent) {
(
Extent::new(ranges.0, properties.0, None).unwrap(),
Extent::new(ranges.1, properties.1, None).unwrap(),
)
}
#[test]
fn test_extent_info_partial_eq() {
let (mut extent1, extent2) = get_extents((2..50, 30..80), get_different_properties());
assert_eq!(extent1, extent1);
// Different properties should make info unequal.
extent1.storage_range = extent2.storage_range.clone();
assert_ne!(extent1, extent2);
// Different storage_range should make info unequal.
extent1.storage_range.start = extent1.storage_range.start + 2;
extent1.properties = extent2.properties;
assert_ne!(extent1, extent2);
}
#[test]
fn test_extent_is_mergeable() {
let (extent1, extent2) = get_extents(get_overlapping_ranges(), get_same_properties());
assert!(extent1.is_mergeable(&extent1));
assert!(extent1.is_mergeable(&extent2));
assert!(extent2.is_mergeable(&extent1));
let (extent3, extent4) = get_extents(get_adjacent_ranges(), get_same_properties());
assert!(extent3.is_mergeable(&extent4));
assert!(extent4.is_mergeable(&extent3));
let (extent5, extent6) = get_extents(get_containing_ranges(), get_same_properties());
assert!(extent5.is_mergeable(&extent6));
assert!(extent6.is_mergeable(&extent5));
let (extent7, extent8) = get_extents(get_non_overlapping_ranges(), get_same_properties());
assert!(!extent7.is_mergeable(&extent8));
assert!(!extent8.is_mergeable(&extent7));
}
#[test]
fn test_extent_non_is_mergeable() {
let (extent1, extent2) = get_extents(get_overlapping_ranges(), get_different_properties());
assert!(!extent1.is_mergeable(&extent2));
assert!(!extent2.is_mergeable(&extent1));
let (extent3, extent4) = get_extents(get_adjacent_ranges(), get_different_properties());
assert!(!extent3.is_mergeable(&extent4));
assert!(!extent4.is_mergeable(&extent3));
let (extent5, extent6) = get_extents(get_containing_ranges(), get_different_properties());
assert!(!extent5.is_mergeable(&extent6));
assert!(!extent6.is_mergeable(&extent5));
let (extent7, extent8) =
get_extents(get_non_overlapping_ranges(), get_different_properties());
assert!(!extent7.is_mergeable(&extent8));
assert!(!extent8.is_mergeable(&extent7));
}
fn split_merge_verify(
case_number: u32,
extent1: &Extent,
extent2: &Extent,
return_extent: Option<Extent>,
expected: &Vec<Extent>,
) {
let mut found = vec![];
let ret = extent1.split_or_merge(extent2, &mut found);
match return_extent {
Some(x) => {
assert!(
ret.is_some(),
"Case Number:{}\nExpected: {:?}\nFound: None",
case_number,
x,
);
assert_eq!(x, ret.unwrap(), "Case number: {}", case_number);
}
None => assert!(
ret.is_none(),
"Case number: {}\nExpected: None\nFound: {:?}",
case_number,
ret,
),
}
assert_eq!(
expected.len(),
found.len(),
"Case Number:{}\nExpected: {:?}\nFound: {:?}",
case_number,
expected,
found
);
for (i, e) in expected.iter().enumerate() {
assert_eq!(
e.clone(),
found[i],
"Case number:{} Expected: {:?}\nFound: {:?}",
case_number,
expected,
found
);
}
}
struct MergeSplitCase {
extent1: Extent,
extent2: Extent,
expected_result: Option<Extent>,
result: Vec<Extent>,
}
fn merge_split_case(
ext1: (Range<u64>, ExtentProperties),
ext2: (Range<u64>, ExtentProperties),
return_ext: Option<(Range<u64>, ExtentProperties)>,
result: Vec<(Range<u64>, ExtentProperties)>,
) -> MergeSplitCase {
let mut res = vec![];
for e in result {
res.push(Extent::new(e.0, e.1, None).unwrap());
}
MergeSplitCase {
extent1: Extent::new(ext1.0, ext1.1, None).unwrap(),
extent2: Extent::new(ext2.0, ext2.1, None).unwrap(),
expected_result: match return_ext {
Some(x) => Some(Extent::new(x.0, x.1, None).unwrap()),
None => None,
},
result: res,
}
}
fn verify_merge_split_case(case_number: usize, case: &MergeSplitCase) {
assert!(case.extent1.storage_range().is_valid());
assert!(case.extent2.storage_range().is_valid());
if case.expected_result.is_some() {
assert!(case.expected_result.clone().unwrap().storage_range().is_valid());
}
for e in &case.result {
assert!(e.storage_range().is_valid());
}
split_merge_verify(
case_number as u32,
&case.extent1,
&case.extent2,
case.expected_result.clone(),
&case.result,
);
}
#[test]
fn test_extent_split_merge() {
use merge_split_case as case;
let (low, high) = get_lower_priority_properties();
let test_cases: Vec<MergeSplitCase> = vec![
// Merge with same range and same properties.
case((1..5, low), (1..5, low), Some((1..5, low)), vec![]),
// Merge with same range and different properties.
case((1..5, low), (1..5, high), None, vec![(1..5, high)]),
case((1..5, high), (1..5, low), Some((1..5, high)), vec![]),
// Merge is_adjacent.
case((1..5, low), (5..8, low), Some((1..8, low)), vec![]),
case((5..8, low), (1..5, low), Some((1..8, low)), vec![]),
// Non-mergeable is_adjacent and mixed properties.
case((1..5, low), (5..8, high), Some((1..5, low)), vec![(5..8, high)]),
case((1..5, high), (5..8, low), Some((1..5, high)), vec![(5..8, low)]),
case((5..8, low), (1..5, high), Some((5..8, low)), vec![(1..5, high)]),
case((5..8, high), (1..5, low), Some((5..8, high)), vec![(1..5, low)]),
// Merge with overlapping ranges. Different combination of properties.
case((1..5, low), (4..8, low), Some((1..8, low)), vec![]),
case((4..8, low), (1..5, low), Some((1..8, low)), vec![]),
case((1..5, low), (4..8, high), None, vec![(1..4, low), (4..8, high)]),
case((1..5, high), (4..8, low), Some((1..5, high)), vec![(5..8, low)]),
case((4..8, low), (1..5, high), Some((5..8, low)), vec![(1..5, high)]),
case((4..8, high), (1..5, low), Some((4..8, high)), vec![((1..4, low))]),
// One has the other.
case((1..8, low), (4..6, low), Some((1..8, low)), vec![]),
case((4..6, low), (1..8, low), Some((1..8, low)), vec![]),
case((1..8, high), (4..6, low), Some((1..8, high)), vec![]),
case((4..6, low), (1..8, high), None, vec![(1..8, high)]),
case((1..8, low), (4..6, high), Some((6..8, low)), vec![(1..4, low), (4..6, high)]),
case((4..6, high), (1..8, low), Some((4..6, high)), vec![(1..4, low), (6..8, low)]),
];
for (case_number, case) in test_cases.iter().enumerate() {
verify_merge_split_case(case_number, case);
}
}
}