use std::cmp;
use super::U24;
use storage::portion::DataPortion;
use storage::Address;
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
pub struct FreePortion(u64);
#[allow(clippy::len_without_is_empty)]
impl FreePortion {
pub fn new(offset: Address, len: U24) -> Self {
FreePortion((u64::from(len) << 40) | offset.as_u64())
}
pub fn start(self) -> Address {
Address::from_u64(self.0 & Address::MAX).unwrap()
}
pub fn end(self) -> Address {
self.start() + Address::from(self.len())
}
pub fn len(self) -> U24 {
(self.0 >> 40) as U24
}
pub fn checked_extend(&mut self, size: U24) -> bool {
let new_len = u64::from(self.len()) + u64::from(size);
if new_len <= 0xFF_FFFF {
*self = FreePortion::new(self.start(), new_len as U24);
true
} else {
false
}
}
pub fn allocate(&mut self, size: u16) -> DataPortion {
assert!(U24::from(size) <= self.len());
let allocated = DataPortion {
start: self.start(),
len: size,
};
*self = Self::new(
self.start() + Address::from(u32::from(size)),
self.len() - U24::from(size),
);
allocated
}
}
impl From<DataPortion> for FreePortion {
fn from(f: DataPortion) -> Self {
FreePortion::new(f.start, U24::from(f.len))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SizeBasedFreePortion(pub FreePortion);
impl PartialOrd for SizeBasedFreePortion {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SizeBasedFreePortion {
fn cmp(&self, other: &Self) -> cmp::Ordering {
match self.0.len().cmp(&other.0.len()) {
cmp::Ordering::Equal => self.0.start().cmp(&other.0.start()),
not_equal => not_equal,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EndBasedFreePortion(pub FreePortion);
impl PartialOrd for EndBasedFreePortion {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for EndBasedFreePortion {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.end().cmp(&other.0.end())
}
}
#[cfg(test)]
mod tests {
use super::*;
use storage::Address;
#[test]
fn it_works() {
let mut p = FreePortion::new(Address::from(100), 50);
assert_eq!(p.start(), Address::from(100));
assert_eq!(p.end(), Address::from(150));
assert_eq!(p.len(), 50);
assert!(!p.checked_extend(0xFF_FFFF));
assert!(p.checked_extend(100));
assert_eq!(p.start(), Address::from(100));
assert_eq!(p.len(), 150);
let allocated = p.allocate(30);
assert_eq!(allocated.start, Address::from(100));
assert_eq!(allocated.len, 30);
assert_eq!(p.start(), Address::from(130));
assert_eq!(p.len(), 120);
let allocated = p.allocate(120);
assert_eq!(allocated.start, Address::from(130));
assert_eq!(allocated.len, 120);
assert_eq!(p.start(), Address::from(250));
assert_eq!(p.len(), 00);
}
#[test]
#[should_panic]
fn underflow() {
let mut p = FreePortion::new(Address::from(100), 50);
p.allocate(51);
}
}