use num::{One, Zero};
use int::{round_down, round_up, BinaryUInteger};
#[derive(Debug)]
pub struct Ring<T> {
size: T,
start: T,
end: T,
empty: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RingRegion<T> {
start: T,
end: T,
}
impl<T: BinaryUInteger> Ring<T> {
pub fn new(size: T) -> Self {
assert!(size < T::max_value() >> 1);
Self {
size: size.clone(),
start: Zero::zero(),
end: Zero::zero(),
empty: true,
}
}
pub fn is_empty(&self) -> bool {
self.empty
}
pub fn is_full(&self) -> bool {
self.start == self.end
}
pub fn alloc_back(&mut self, size: T) -> Option<(RingRegion<T>, T)> {
self.alloc_back_aligned(size, One::one())
}
pub fn alloc_front(&mut self, size: T) -> Option<(RingRegion<T>, T)> {
self.alloc_front_aligned(size, One::one())
}
pub fn alloc_back_aligned(&mut self, size: T, align: T) -> Option<(RingRegion<T>, T)> {
assert_ne!(size, Zero::zero());
assert!(align.is_power_of_two());
if self.empty {
self.alloc_empty(size)
} else if size >= self.size {
None
} else {
let mut new_wrapped = self.end <= self.start;
let mut new_end = round_up(&self.end, &align);
if new_end.clone() + size.clone() > self.size && !new_wrapped {
new_end = Zero::zero();
new_wrapped = true;
}
if new_wrapped && new_end.clone() + size.clone() > self.start {
return None;
}
let offset = new_end.clone();
new_end += size;
if new_end == self.size {
new_end = Zero::zero();
}
let region = RingRegion {
start: self.end.clone(),
end: new_end.clone(),
};
self.end = new_end;
Some((region, offset))
}
}
pub fn alloc_front_aligned(&mut self, size: T, align: T) -> Option<(RingRegion<T>, T)> {
assert_ne!(size, Zero::zero());
assert!(align.is_power_of_two());
if self.empty {
self.alloc_empty(size)
} else if size >= self.size {
None
} else {
let enlarged_size = round_up(&size, &align);
let pad = enlarged_size.clone() - size.clone();
let mut new_wrapped = self.end <= self.start;
let mut new_start = round_down(&(self.start.clone() + pad.clone()), &align);
if new_start < enlarged_size.clone() && !new_wrapped {
new_start = round_down(&(self.size.clone() + pad.clone()), &align);
new_wrapped = true;
}
if new_wrapped && self.end.clone() + enlarged_size.clone() > new_start {
return None;
}
new_start -= enlarged_size;
let offset = new_start.clone();
let region = RingRegion {
start: new_start.clone(),
end: self.start.clone(),
};
self.start = new_start;
Some((region, offset))
}
}
fn alloc_empty(&mut self, size: T) -> Option<(RingRegion<T>, T)> {
debug_assert!(self.empty);
if size <= self.size {
self.start = Zero::zero();
self.end = if size == self.size {
Zero::zero()
} else {
size.clone()
};
self.empty = false;
Some((
RingRegion {
start: Zero::zero(),
end: size,
},
Zero::zero(),
))
} else {
None
}
}
pub fn dealloc_front_until(&mut self, r: RingRegion<T>) {
assert!(!self.empty, "empty");
self.start = r.start;
}
pub fn dealloc_back_until(&mut self, r: RingRegion<T>) {
assert!(!self.empty, "empty");
self.end = r.end;
}
pub fn dealloc_front(&mut self, r: RingRegion<T>) {
assert!(!self.empty, "empty");
assert!(self.start == r.start, "not front");
self.start = r.end;
if self.start == self.end {
self.empty = true;
}
}
pub fn dealloc_back(&mut self, r: RingRegion<T>) {
assert!(!self.empty, "empty");
assert!(self.end == r.end, "not back");
self.end = r.start;
if self.start == self.end {
self.empty = true;
}
}
}