Skip to main content

swamp_abi_memtypes/
lib.rs

1use std::fmt::{Display, Formatter};
2use std::ops::{Add, Div, Sub};
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub enum MemoryAlignment {
6    // Do not change the order.
7    U8,
8    U16,
9    U32,
10    U64,
11}
12
13impl MemoryAlignment {
14    #[must_use]
15    const fn rank(&self) -> usize {
16        match self {
17            Self::U8 => 1,
18            Self::U16 => 2,
19            Self::U32 => 3,
20            Self::U64 => 4,
21        }
22    }
23    #[must_use]
24    pub const fn greater_than(&self, other: Self) -> bool {
25        self.rank() > other.rank()
26    }
27}
28
29impl From<MemoryAlignment> for usize {
30    fn from(val: MemoryAlignment) -> Self {
31        match val {
32            MemoryAlignment::U8 => 1,
33            MemoryAlignment::U16 => 2,
34            MemoryAlignment::U32 => 4,
35            MemoryAlignment::U64 => 8,
36        }
37    }
38}
39
40impl From<MemoryAlignment> for u8 {
41    fn from(val: MemoryAlignment) -> Self {
42        match val {
43            MemoryAlignment::U8 => 1,
44            MemoryAlignment::U16 => 2,
45            MemoryAlignment::U32 => 4,
46            MemoryAlignment::U64 => 8,
47        }
48    }
49}
50
51impl TryInto<MemoryAlignment> for usize {
52    type Error = ();
53
54    fn try_into(self) -> Result<MemoryAlignment, Self::Error> {
55        let converted = match self {
56            1 => MemoryAlignment::U8,
57            2 => MemoryAlignment::U16,
58            4 => MemoryAlignment::U32,
59            8 => MemoryAlignment::U64,
60
61            _ => return Err(()),
62        };
63        Ok(converted)
64    }
65}
66
67
68#[derive(Debug, Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
69pub struct MemorySize(pub u32);
70
71impl Display for MemorySize {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        let description = human_memsize::human_size(self.0.into());
74        write!(f, "{description}")
75    }
76}
77
78
79
80#[derive(Debug, Copy, Eq, PartialEq, Hash, Clone, Ord, PartialOrd)]
81pub struct MemoryOffset(pub u32);
82
83impl MemoryOffset {
84    #[must_use]
85    pub const fn to_size(&self) -> MemorySize {
86        MemorySize(self.0)
87    }
88}
89
90impl Display for MemoryOffset {
91    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
92        write!(f, "+{:X}", self.0)
93    }
94}
95
96impl MemoryOffset {
97    pub fn space(&mut self, memory_size: MemorySize, alignment: MemoryAlignment) -> Self {
98        let start = align(self.0 as usize, alignment.into()) as u32;
99        self.0 = start + memory_size.0;
100        Self(start)
101    }
102}
103
104impl Add<MemorySize> for MemoryOffset {
105    type Output = Self;
106
107    fn add(self, rhs: MemorySize) -> Self {
108        Self(self.0 + rhs.0)
109    }
110}
111
112impl From<MemoryAlignment> for MemoryOffset {
113    fn from(val: MemoryAlignment) -> Self {
114        let octets: usize = val.into();
115        Self(octets as u32)
116    }
117}
118
119impl From<MemorySize> for usize {
120    fn from(val: MemorySize) -> Self {
121        val.0 as Self
122    }
123}
124
125
126#[must_use]
127pub fn align_to(addr: MemoryOffset, alignment: MemoryAlignment) -> MemoryOffset {
128    MemoryOffset(align(addr.0 as usize, alignment.into()) as u32)
129}
130
131/// # Arguments
132/// * `offset` - The offset after the last field (end of layout).
133/// * `base_offset` - The starting offset of the struct/tuple/union.
134/// * `max_alignment` - The maximum alignment required by any field.
135///
136/// # Returns
137/// The total size, rounded up to `max_alignment`.
138/// # Notes
139/// The total size of a struct is always rounded up to a multiple of its alignment.
140/// It might be strange in that it "wastes" memory for the potential parent struct
141/// to place items of lower memory alignment. (reuse tail padding).
142/// It simplifies things as well with code generation and similar, that a struct
143/// is always the same size and doesn't have to rely on where the struct is contained.
144/// It also ensures that arrays of the struct are correctly aligned according to the ABI,
145/// and matches the behavior of C, C++, and Rust.
146/// Note: The tail padding at the end of a struct is not reused for subsequent fields
147/// in a parent struct-this is required for safe and predictable layout
148#[must_use]
149pub fn adjust_size_to_alignment(
150    unaligned_size: MemorySize,
151    max_alignment: MemoryAlignment,
152) -> MemorySize {
153    align_to(MemoryOffset(unaligned_size.0), max_alignment).to_size()
154}
155
156
157#[derive(Copy, Clone)]
158pub struct CountU32(pub u32);
159
160
161
162impl Div<Self> for MemorySize {
163    type Output = CountU32;
164
165    fn div(self, rhs: Self) -> Self::Output {
166        assert!(rhs.0 > 0, "Division by zero in MemorySize");
167        assert!(
168            self.0 > 0,
169            "Numerator must be positive in MemorySize division"
170        );
171        assert_eq!(
172            self.0 % rhs.0,
173            0,
174            "MemorySize division must be exact and positive"
175        );
176
177        CountU32(self.0 / rhs.0)
178    }
179}
180
181impl Add<Self> for MemoryOffset {
182    type Output = Self;
183
184    fn add(self, rhs: Self) -> Self {
185        Self(self.0 + rhs.0)
186    }
187}
188
189impl Sub<Self> for MemoryOffset {
190    type Output = Self;
191
192    fn sub(self, rhs: Self) -> Self {
193        assert!(rhs.0 <= self.0);
194        Self(self.0 - rhs.0)
195    }
196}
197
198impl MemoryOffset {
199    #[must_use]
200    pub const fn as_size(&self) -> MemorySize {
201        MemorySize(self.0)
202    }
203}
204
205pub const SAFE_ALIGNMENT: usize = 8;
206
207#[must_use]
208pub fn align(addr: usize, alignment: usize) -> usize {
209    debug_assert!(
210        alignment.is_power_of_two(),
211        "alignment must be a power of two"
212    );
213    (addr + alignment - 1) & !(alignment - 1)
214}
215
216
217impl MemoryOffset {
218    #[must_use]
219    pub fn add(&self, size: MemorySize, alignment: MemoryAlignment) -> Self {
220        let new_start = align(self.0 as usize, alignment.into()) as u32;
221        Self(new_start + size.0)
222    }
223}