1use core::{fmt, num::NonZeroU16};
2
3use smallvec::SmallVec;
4
5use super::{Alignable, Type};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct StructType {
11 pub(crate) repr: TypeRepr,
13 pub(crate) size: u32,
15 pub(crate) fields: SmallVec<[StructField; 2]>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct StructField {
27 pub index: u8,
29 pub align: u16,
31 pub offset: u32,
33 pub ty: Type,
35}
36
37#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40pub enum TypeRepr {
41 #[default]
43 Default,
44 Align(NonZeroU16),
51 Packed(NonZeroU16),
64 Transparent,
67 BigEndian,
83}
84
85impl TypeRepr {
86 #[inline]
88 pub fn packed(align: u16) -> Self {
89 Self::Packed(
90 NonZeroU16::new(align).expect("invalid alignment: expected value in range 1..=65535"),
91 )
92 }
93
94 #[inline]
96 pub fn align(align: u16) -> Self {
97 Self::Align(
98 NonZeroU16::new(align).expect("invalid alignment: expected value in range 1..=65535"),
99 )
100 }
101
102 pub fn is_transparent(&self) -> bool {
104 matches!(self, Self::Transparent)
105 }
106
107 pub fn is_packed(&self) -> bool {
109 matches!(self, Self::Packed(_))
110 }
111
112 pub fn min_alignment(&self) -> Option<usize> {
114 match self {
115 Self::Packed(align) | Self::Align(align) => Some(align.get() as usize),
116 _ => None,
117 }
118 }
119}
120
121impl StructType {
122 #[inline]
125 pub fn new<I: IntoIterator<Item = Type>>(fields: I) -> Self {
126 Self::new_with_repr(TypeRepr::Default, fields)
127 }
128
129 pub fn new_with_repr<I: IntoIterator<Item = Type>>(repr: TypeRepr, fields: I) -> Self {
133 let tys = fields.into_iter().collect::<SmallVec<[_; 2]>>();
134 let mut fields = SmallVec::<[_; 2]>::with_capacity(tys.len());
135 let size = match repr {
136 TypeRepr::Transparent => {
137 let mut offset = 0u32;
138 for (index, ty) in tys.into_iter().enumerate() {
139 let index: u8 =
140 index.try_into().expect("invalid struct: expected no more than 255 fields");
141 let field_size: u32 = ty
142 .size_in_bytes()
143 .try_into()
144 .expect("invalid type: size is larger than 2^32 bytes");
145 if field_size == 0 {
146 fields.push(StructField {
147 index,
148 align: 1,
149 offset,
150 ty,
151 });
152 } else {
153 let align = ty.min_alignment().try_into().expect(
154 "invalid struct field alignment: expected power of two between 1 and \
155 2^16",
156 );
157 assert_eq!(
158 offset, 0,
159 "invalid transparent representation for struct: repr(transparent) is \
160 only valid for structs with a single non-zero sized field"
161 );
162 fields.push(StructField {
163 index,
164 align,
165 offset,
166 ty,
167 });
168 offset += field_size;
169 }
170 }
171 offset
172 }
173 repr => {
174 let mut offset = 0u32;
175 let default_align: u16 =
176 tys.iter().map(|t| t.min_alignment()).max().unwrap_or(1).try_into().expect(
177 "invalid struct field alignment: expected power of two between 1 and 2^16",
178 );
179 let align = match repr {
180 TypeRepr::Align(align) => core::cmp::max(align.get(), default_align),
181 TypeRepr::Packed(align) => core::cmp::min(align.get(), default_align),
182 TypeRepr::Transparent | TypeRepr::Default | TypeRepr::BigEndian => {
183 default_align
184 }
185 };
186
187 for (index, ty) in tys.into_iter().enumerate() {
188 let index: u8 =
189 index.try_into().expect("invalid struct: expected no more than 255 fields");
190 let field_size: u32 = ty
191 .size_in_bytes()
192 .try_into()
193 .expect("invalid type: size is larger than 2^32 bytes");
194 let default_align: u16 = ty.min_alignment().try_into().expect(
195 "invalid struct field alignment: expected power of two between 1 and 2^16",
196 );
197 let align: u16 = match repr {
198 TypeRepr::Packed(align) => core::cmp::min(align.get(), default_align),
199 _ => default_align,
200 };
201 offset += offset.align_offset(align as u32);
202 fields.push(StructField {
203 index,
204 align,
205 offset,
206 ty,
207 });
208 offset += field_size;
209 }
210 offset.align_up(align as u32)
211 }
212 };
213
214 Self { repr, size, fields }
215 }
216
217 #[inline]
219 pub const fn repr(&self) -> TypeRepr {
220 self.repr
221 }
222
223 pub fn min_alignment(&self) -> usize {
225 self.repr
226 .min_alignment()
227 .unwrap_or_else(|| self.fields.iter().map(|f| f.align as usize).max().unwrap_or(1))
228 }
229
230 #[inline]
232 pub fn size(&self) -> usize {
233 self.size as usize
234 }
235
236 pub fn get(&self, index: usize) -> &StructField {
238 &self.fields[index]
239 }
240
241 pub fn fields(&self) -> &[StructField] {
243 self.fields.as_slice()
244 }
245
246 pub fn is_empty(&self) -> bool {
248 self.fields.is_empty()
249 }
250
251 pub fn len(&self) -> usize {
253 self.fields.len()
254 }
255}
256
257impl TryFrom<Type> for StructType {
258 type Error = Type;
259
260 fn try_from(ty: Type) -> Result<Self, Self::Error> {
261 match ty {
262 Type::Struct(ty) => Ok(alloc::sync::Arc::unwrap_or_clone(ty)),
263 other => Err(other),
264 }
265 }
266}
267
268impl fmt::Display for StructType {
269 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270 use miden_formatting::prettier::PrettyPrint;
271 self.pretty_print(f)
272 }
273}
274
275impl miden_formatting::prettier::PrettyPrint for StructType {
276 fn render(&self) -> miden_formatting::prettier::Document {
277 use miden_formatting::prettier::*;
278
279 let header = match self.repr.render() {
280 Document::Empty => const_text("struct "),
281 repr => const_text("struct ") + const_text("#[repr(") + repr + const_text(")] "),
282 };
283
284 let singleline = self.fields.iter().enumerate().fold(Document::Empty, |acc, (i, field)| {
285 if i > 0 {
286 acc + const_text(", ") + field.render()
287 } else {
288 field.render()
289 }
290 });
291 let multiline = indent(
292 4,
293 self.fields.iter().enumerate().fold(Document::Empty, |acc, (i, field)| {
294 if i > 0 {
295 acc + nl() + field.render()
296 } else {
297 nl() + field.render()
298 }
299 }),
300 );
301 let body = const_text("{") + (singleline | multiline) + const_text("}");
302
303 header + body
304 }
305}
306
307impl fmt::Display for StructField {
308 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
309 fmt::Display::fmt(&self.ty, f)
310 }
311}
312
313impl miden_formatting::prettier::PrettyPrint for StructField {
314 fn render(&self) -> miden_formatting::prettier::Document {
315 self.ty.render()
316 }
317}
318
319impl fmt::Display for TypeRepr {
320 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
321 use miden_formatting::prettier::PrettyPrint;
322 self.pretty_print(f)
323 }
324}
325
326impl miden_formatting::prettier::PrettyPrint for TypeRepr {
327 fn render(&self) -> miden_formatting::prettier::Document {
328 use alloc::format;
329
330 use miden_formatting::prettier::*;
331 match self {
332 Self::Default => Document::Empty,
333 Self::Transparent => const_text("transparent"),
334 Self::Align(align) => text(format!("align({align})")),
335 Self::Packed(align) => text(format!("packed({align})")),
336 Self::BigEndian => const_text("big-endian"),
337 }
338 }
339}