use std::fmt::{Display, Formatter};
use std::ops::{Add, Div, Sub};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MemoryAlignment {
U8,
U16,
U32,
U64,
}
impl MemoryAlignment {
#[must_use]
const fn rank(&self) -> usize {
match self {
Self::U8 => 1,
Self::U16 => 2,
Self::U32 => 3,
Self::U64 => 4,
}
}
#[must_use]
pub const fn greater_than(&self, other: Self) -> bool {
self.rank() > other.rank()
}
}
impl From<MemoryAlignment> for usize {
fn from(val: MemoryAlignment) -> Self {
match val {
MemoryAlignment::U8 => 1,
MemoryAlignment::U16 => 2,
MemoryAlignment::U32 => 4,
MemoryAlignment::U64 => 8,
}
}
}
impl From<MemoryAlignment> for u8 {
fn from(val: MemoryAlignment) -> Self {
match val {
MemoryAlignment::U8 => 1,
MemoryAlignment::U16 => 2,
MemoryAlignment::U32 => 4,
MemoryAlignment::U64 => 8,
}
}
}
impl TryInto<MemoryAlignment> for usize {
type Error = ();
fn try_into(self) -> Result<MemoryAlignment, Self::Error> {
let converted = match self {
1 => MemoryAlignment::U8,
2 => MemoryAlignment::U16,
4 => MemoryAlignment::U32,
8 => MemoryAlignment::U64,
_ => return Err(()),
};
Ok(converted)
}
}
#[derive(Debug, Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
pub struct MemorySize(pub u32);
impl Display for MemorySize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let description = human_memsize::human_size(self.0.into());
write!(f, "{description}")
}
}
#[derive(Debug, Copy, Eq, PartialEq, Hash, Clone, Ord, PartialOrd)]
pub struct MemoryOffset(pub u32);
impl MemoryOffset {
#[must_use]
pub const fn to_size(&self) -> MemorySize {
MemorySize(self.0)
}
}
impl Display for MemoryOffset {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "+{:X}", self.0)
}
}
impl MemoryOffset {
pub fn space(&mut self, memory_size: MemorySize, alignment: MemoryAlignment) -> Self {
let start = align(self.0 as usize, alignment.into()) as u32;
self.0 = start + memory_size.0;
Self(start)
}
}
impl Add<MemorySize> for MemoryOffset {
type Output = Self;
fn add(self, rhs: MemorySize) -> Self {
Self(self.0 + rhs.0)
}
}
impl From<MemoryAlignment> for MemoryOffset {
fn from(val: MemoryAlignment) -> Self {
let octets: usize = val.into();
Self(octets as u32)
}
}
impl From<MemorySize> for usize {
fn from(val: MemorySize) -> Self {
val.0 as Self
}
}
#[must_use]
pub fn align_to(addr: MemoryOffset, alignment: MemoryAlignment) -> MemoryOffset {
MemoryOffset(align(addr.0 as usize, alignment.into()) as u32)
}
#[must_use]
pub fn adjust_size_to_alignment(
unaligned_size: MemorySize,
max_alignment: MemoryAlignment,
) -> MemorySize {
align_to(MemoryOffset(unaligned_size.0), max_alignment).to_size()
}
#[derive(Copy, Clone)]
pub struct CountU32(pub u32);
impl Div<Self> for MemorySize {
type Output = CountU32;
fn div(self, rhs: Self) -> Self::Output {
assert!(rhs.0 > 0, "Division by zero in MemorySize");
assert!(
self.0 > 0,
"Numerator must be positive in MemorySize division"
);
assert_eq!(
self.0 % rhs.0,
0,
"MemorySize division must be exact and positive"
);
CountU32(self.0 / rhs.0)
}
}
impl Add<Self> for MemoryOffset {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0)
}
}
impl Sub<Self> for MemoryOffset {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
assert!(rhs.0 <= self.0);
Self(self.0 - rhs.0)
}
}
impl MemoryOffset {
#[must_use]
pub const fn as_size(&self) -> MemorySize {
MemorySize(self.0)
}
}
pub const SAFE_ALIGNMENT: usize = 8;
#[must_use]
pub fn align(addr: usize, alignment: usize) -> usize {
debug_assert!(
alignment.is_power_of_two(),
"alignment must be a power of two"
);
(addr + alignment - 1) & !(alignment - 1)
}
impl MemoryOffset {
#[must_use]
pub fn add(&self, size: MemorySize, alignment: MemoryAlignment) -> Self {
let new_start = align(self.0 as usize, alignment.into()) as u32;
Self(new_start + size.0)
}
}