Files
OpenSK/libraries/opensk/src/api/upgrade_storage/helper.rs
kaczmarczyck 752db8cc90 Fixes new clippy lints on the latest nightly (#603)
* Fixes new clippy lints on the latest nightly

We didn't see these before because of our old Rust toolchain.

* fixes nit
2023-03-09 12:08:34 +01:00

382 lines
13 KiB
Rust

// Copyright 2019-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// For compiling with std outside of tests.
#![cfg_attr(feature = "std", allow(dead_code))]
use alloc::vec::Vec;
use core::iter::Iterator;
use persistent_store::{StorageError, StorageResult};
/// Reads a slice from a list of slices.
///
/// The returned slice contains the interval `[start, start+length)`.
///
/// # Errors
///
/// Returns [`StorageError::OutOfBounds`] if the range is not within exactly one slice.
pub fn find_slice<'a>(
slices: &'a [&'a [u8]],
mut start: usize,
length: usize,
) -> StorageResult<&'a [u8]> {
for slice in slices {
if start >= slice.len() {
start -= slice.len();
continue;
}
if start + length > slice.len() {
break;
}
return Ok(&slice[start..][..length]);
}
Err(StorageError::OutOfBounds)
}
/// Checks whether the address is aligned with the block size.
///
/// Requires `block_size` to be a power of two.
pub fn is_aligned(block_size: usize, address: usize) -> bool {
debug_assert!(block_size.is_power_of_two());
address & (block_size - 1) == 0
}
/// A range implementation using start and length.
///
/// The range is treated as the interval `[start, start + length)`.
/// All objects with length of 0, regardless of the start value, are considered empty.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ModRange {
start: usize,
length: usize,
}
impl ModRange {
/// Returns a new range of given start and length.
pub fn new(start: usize, length: usize) -> ModRange {
ModRange { start, length }
}
/// Create a new empty range.
pub fn new_empty() -> ModRange {
ModRange::new(0, 0)
}
/// Returns the start of the range.
pub fn start(&self) -> usize {
self.start
}
/// Returns the length of the range.
pub fn length(&self) -> usize {
self.length
}
/// Returns whether this range contains any addresses.
pub fn is_empty(&self) -> bool {
self.length == 0
}
/// Returns the disjoint union with the other range, if consecutive.
///
/// Appending empty ranges is not possible.
/// Appending to the empty range returns the other range.
///
/// Returns true if successful.
pub fn append(&mut self, other: &ModRange) -> bool {
if self.is_empty() {
self.start = other.start;
self.length = other.length;
return true;
}
if other.is_empty() {
return false;
}
if self.start >= other.start {
return false;
}
if self.length != other.start - self.start {
return false;
}
if let Some(new_length) = self.length.checked_add(other.length) {
self.length = new_length;
true
} else {
false
}
}
/// Helper function to check whether a range starts within another.
fn starts_inside(&self, range: &ModRange) -> bool {
!range.is_empty() && self.start >= range.start && self.start - range.start < range.length
}
/// Returns whether the given range has intersects.
///
/// Mathematically, we calculate whether: `self ∩ range ≠ ∅`.
pub fn intersects_range(&self, range: &ModRange) -> bool {
self.starts_inside(range) || range.starts_inside(self)
}
/// Returns whether the given range is fully contained.
///
/// Mathematically, we calculate whether: `self ∩ range = range`.
pub fn contains_range(&self, range: &ModRange) -> bool {
range.is_empty()
|| (self.start <= range.start
&& range.length <= self.length
&& range.start - self.start <= self.length - range.length)
}
/// Returns an iterator for all contained numbers that are divisible by the modulus.
pub fn aligned_iter(&self, modulus: usize) -> impl Iterator<Item = usize> {
(self.start..=usize::MAX)
.take(self.length)
// Skip the minimum number of elements to align.
.skip((modulus - self.start % modulus) % modulus)
// Only return aligned elements.
.step_by(modulus)
}
}
#[derive(Default)]
pub struct Partition {
ranges: Vec<ModRange>,
}
impl Partition {
/// Total length of all ranges.
pub fn length(&self) -> usize {
self.ranges.iter().map(|r| r.length()).sum()
}
/// Appends the given range.
///
/// Ranges should be appending with ascending start addresses.
pub fn append(&mut self, range: ModRange) -> bool {
if let Some(last_range) = self.ranges.last_mut() {
if range.start() <= last_range.start()
|| range.start() - last_range.start() < last_range.length()
{
return false;
}
if !last_range.append(&range) {
self.ranges.push(range);
}
} else {
self.ranges.push(range);
}
true
}
/// Returns the start address that corresponds to the given offset.
///
/// If the offset bigger than the accumulated length or the requested slice doesn't fit a
/// connected component, return `None`.
pub fn find_address(&self, mut offset: usize, length: usize) -> Option<usize> {
for range in &self.ranges {
if offset < range.length() {
return if range.length() - offset >= length {
Some(range.start() + offset)
} else {
None
};
}
offset -= range.length()
}
None
}
pub fn ranges_from(&self, start_address: usize) -> Vec<ModRange> {
let mut result = Vec::new();
for range in &self.ranges {
match start_address.checked_sub(range.start()) {
None | Some(0) => result.push(range.clone()),
Some(offset) => {
if range.length() > offset {
result.push(ModRange::new(start_address, range.length() - offset));
}
}
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_slice_ok() {
assert_eq!(
find_slice(&[&[1, 2, 3, 4]], 0, 4).ok(),
Some(&[1u8, 2, 3, 4] as &[u8])
);
assert_eq!(
find_slice(&[&[1, 2, 3, 4], &[5, 6]], 1, 2).ok(),
Some(&[2u8, 3] as &[u8])
);
assert_eq!(
find_slice(&[&[1, 2, 3, 4], &[5, 6]], 4, 2).ok(),
Some(&[5u8, 6] as &[u8])
);
assert_eq!(
find_slice(&[&[1, 2, 3, 4], &[5, 6]], 4, 0).ok(),
Some(&[] as &[u8])
);
assert!(find_slice(&[], 0, 1).is_err());
assert!(find_slice(&[&[1, 2, 3, 4], &[5, 6]], 6, 0).is_err());
assert!(find_slice(&[&[1, 2, 3, 4], &[5, 6]], 3, 2).is_err());
}
#[test]
fn alignment() {
for exponent in 0..8 {
let block_size = 1 << exponent;
for i in 0..10 {
assert!(is_aligned(block_size, block_size * i));
}
for i in 1..block_size {
assert!(!is_aligned(block_size, block_size + i));
}
}
}
#[test]
fn mod_range_parameters() {
let range = ModRange::new(200, 100);
assert_eq!(range.start(), 200);
assert_eq!(range.length(), 100);
assert_eq!(ModRange::new_empty().length(), 0);
}
#[test]
fn mod_range_is_empty() {
assert!(!ModRange::new(200, 100).is_empty());
assert!(ModRange::new(200, 0).is_empty());
assert!(ModRange::new_empty().is_empty());
assert!(!ModRange::new(usize::MAX, 2).is_empty());
}
#[test]
fn mod_range_append() {
let mut range = ModRange::new(200, 100);
assert!(range.append(&ModRange::new(300, 400)));
assert!(range.start() == 200);
assert!(range.length() == 500);
assert!(!range.append(&ModRange::new(499, 400)));
assert!(!range.append(&ModRange::new(501, 400)));
assert!(!range.append(&ModRange::new(300, 400)));
let mut range = ModRange::new_empty();
assert!(range.append(&ModRange::new(200, 100)));
assert!(range.start() == 200);
assert!(range.length() == 100);
}
#[test]
fn mod_range_contains_range() {
let range = ModRange::new(200, 100);
assert!(!range.contains_range(&ModRange::new(199, 100)));
assert!(!range.contains_range(&ModRange::new(201, 100)));
assert!(!range.contains_range(&ModRange::new(199, 99)));
assert!(!range.contains_range(&ModRange::new(202, 99)));
assert!(!range.contains_range(&ModRange::new(200, 101)));
assert!(range.contains_range(&ModRange::new(200, 100)));
assert!(range.contains_range(&ModRange::new(200, 99)));
assert!(range.contains_range(&ModRange::new(201, 99)));
assert!(ModRange::new_empty().contains_range(&ModRange::new_empty()));
assert!(ModRange::new(usize::MAX, 1).contains_range(&ModRange::new(usize::MAX, 1)));
assert!(ModRange::new(usize::MAX, 2).contains_range(&ModRange::new(usize::MAX, 2)));
}
#[test]
fn mod_range_intersects_range() {
let range = ModRange::new(200, 100);
assert!(range.intersects_range(&ModRange::new(200, 1)));
assert!(range.intersects_range(&ModRange::new(299, 1)));
assert!(!range.intersects_range(&ModRange::new(199, 1)));
assert!(!range.intersects_range(&ModRange::new(300, 1)));
assert!(!ModRange::new_empty().intersects_range(&ModRange::new_empty()));
assert!(!ModRange::new_empty().intersects_range(&ModRange::new(200, 100)));
assert!(!ModRange::new(200, 100).intersects_range(&ModRange::new_empty()));
assert!(ModRange::new(usize::MAX, 1).intersects_range(&ModRange::new(usize::MAX, 1)));
assert!(ModRange::new(usize::MAX, 2).intersects_range(&ModRange::new(usize::MAX, 2)));
}
#[test]
fn mod_range_aligned_iter() {
let mut iter = ModRange::new(200, 100).aligned_iter(100);
assert_eq!(iter.next(), Some(200));
assert_eq!(iter.next(), None);
let mut iter = ModRange::new(200, 101).aligned_iter(100);
assert_eq!(iter.next(), Some(200));
assert_eq!(iter.next(), Some(300));
assert_eq!(iter.next(), None);
let mut iter = ModRange::new(199, 100).aligned_iter(100);
assert_eq!(iter.next(), Some(200));
assert_eq!(iter.next(), None);
let mut iter = ModRange::new(201, 99).aligned_iter(100);
assert_eq!(iter.next(), None);
let mut iter = ModRange::new(usize::MAX - 16, 20).aligned_iter(16);
assert_eq!(iter.next(), Some(0xffff_ffff_ffff_fff0));
assert_eq!(iter.next(), None);
}
#[test]
fn partition_append() {
let mut partition = Partition::default();
partition.append(ModRange::new(0x4000, 0x1000));
partition.append(ModRange::new(0x20000, 0x20000));
partition.append(ModRange::new(0x40000, 0x20000));
assert_eq!(partition.find_address(0, 1), Some(0x4000));
assert_eq!(partition.length(), 0x41000);
}
#[test]
fn partition_find_address() {
let mut partition = Partition::default();
partition.append(ModRange::new(0x4000, 0x1000));
partition.append(ModRange::new(0x20000, 0x20000));
partition.append(ModRange::new(0x40000, 0x20000));
assert_eq!(partition.find_address(0, 0x1000), Some(0x4000));
assert_eq!(partition.find_address(0x1000, 0x1000), Some(0x20000));
assert_eq!(partition.find_address(0x20000, 0x1000), Some(0x3F000));
assert_eq!(partition.find_address(0x21000, 0x1000), Some(0x40000));
assert_eq!(partition.find_address(0x40000, 0x1000), Some(0x5F000));
assert_eq!(partition.find_address(0x41000, 0x1000), None);
assert_eq!(partition.find_address(0x40000, 0x2000), None);
}
#[test]
fn partition_ranges_from() {
let mut partition = Partition::default();
partition.append(ModRange::new(0x4000, 0x1000));
partition.append(ModRange::new(0x20000, 0x20000));
partition.append(ModRange::new(0x40000, 0x20000));
let all_ranges = partition.ranges_from(0);
let from_start_ranges = partition.ranges_from(0x4000);
assert_eq!(&all_ranges, &from_start_ranges);
assert_eq!(all_ranges.len(), 2);
assert_eq!(all_ranges[0], ModRange::new(0x4000, 0x1000));
assert_eq!(all_ranges[1], ModRange::new(0x20000, 0x40000));
let second_range = partition.ranges_from(0x20000);
let same_second_range = partition.ranges_from(0x1F000);
assert_eq!(&second_range, &same_second_range);
assert_eq!(&second_range, &all_ranges[1..]);
let partial_range = partition.ranges_from(0x30000);
assert_eq!(partial_range[0], ModRange::new(0x30000, 0x30000));
}
}