#[cfg(test)]
use core::fmt::{self, Debug, Formatter};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum State {
Free,
Used,
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Entry(u32);
impl Entry {
pub const fn free(size: usize) -> Self {
assert!(size <= 0x7FFF_FFFF);
#[allow(clippy::cast_possible_truncation)] Self((size << 1) as _)
}
pub const fn used(size: usize) -> Self {
assert!(size <= 0x7FFF_FFFF);
#[allow(clippy::cast_possible_truncation)] Self((size << 1 | 1) as _)
}
pub const fn state(self) -> State {
if self.0 & 1 == 0 {
State::Free
} else {
State::Used
}
}
pub const fn size(self) -> usize {
let size = self.0 >> 1;
size as _
}
pub const fn as_raw(self) -> [u8; 4] {
self.0.to_ne_bytes()
}
}
#[cfg(test)]
impl Debug for Entry {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Entry")
.field("state", &self.state())
.field("size", &self.size())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::{Entry, State};
#[test]
fn equality() {
assert_eq!(Entry::used(4), Entry::used(4));
assert_ne!(Entry::used(4), Entry::used(5));
assert_eq!(Entry::free(4), Entry::free(4));
assert_ne!(Entry::free(4), Entry::free(5));
assert_ne!(Entry::used(4), Entry::free(4));
assert_ne!(Entry::used(4), Entry::free(5));
assert_eq!(Entry::used(4).clone(), Entry::used(4));
assert_ne!(Entry::used(4).clone(), Entry::used(5));
assert_eq!(Entry::free(4).clone(), Entry::free(4));
assert_ne!(Entry::free(4).clone(), Entry::free(5));
assert_ne!(Entry::used(4).clone(), Entry::free(4));
assert_ne!(Entry::used(4).clone(), Entry::free(5));
}
#[test]
fn entry_bitpacking_state() {
assert_eq!(Entry::free(5).state(), State::Free);
assert_eq!(Entry::used(5).state(), State::Used);
assert_eq!(Entry(0b00_0).state(), State::Free);
assert_eq!(Entry(0b00_1).state(), State::Used);
assert_eq!(Entry(0b10_0).state(), State::Free);
assert_eq!(Entry(0b10_1).state(), State::Used);
assert_eq!(Entry(0b11_1).state(), State::Used);
assert_eq!(Entry(0b11_0).state(), State::Free);
assert_eq!(Entry::free(5).state().clone(), State::Free);
assert_eq!(Entry::used(5).state().clone(), State::Used);
assert_eq!(Entry(0b00_0).state().clone(), State::Free);
assert_eq!(Entry(0b00_1).state().clone(), State::Used);
assert_eq!(Entry(0b10_0).state().clone(), State::Free);
assert_eq!(Entry(0b10_1).state().clone(), State::Used);
assert_eq!(Entry(0b11_1).state().clone(), State::Used);
assert_eq!(Entry(0b11_0).state().clone(), State::Free);
}
#[test]
fn entry_bitpacking_size() {
assert_eq!(Entry(0b1_1).size(), 1);
assert_eq!(Entry(0b1_0).size(), 1);
assert_eq!(Entry(123 << 1).size(), 123);
assert_eq!(Entry(123 << 1 | 1).size(), 123);
}
#[test]
fn alignment() {
use core::mem;
assert_eq!(mem::align_of::<Entry>(), mem::align_of::<u32>());
}
#[test]
fn large_entries() {
Entry::free((1 << 31) - 4);
Entry::used((1 << 31) - 4);
}
#[test]
#[should_panic]
fn huge_free_block() {
Entry::free(1 << 31); }
#[test]
#[should_panic]
fn huge_used_block() {
Entry::used(1 << 31); }
#[test]
fn debug_representation() {
assert_eq!(
format!("{:?}", Entry::used(123)),
"Entry { state: Used, size: 123 }"
);
assert_eq!(
format!("{:?}", Entry::free(456)),
"Entry { state: Free, size: 456 }"
);
}
}