type_layout/lib.rs
1/*!
2[](https://github.com/LPGhatguy/type-layout/actions)
3[](https://crates.io/crates/type-layout)
4[](https://docs.rs/type-layout)
5
6type-layout is a type layout debugging aid, providing a `#[derive]`able trait
7that reports:
8- The type's name, size, and minimum alignment
9- Each field's name, type, offset, and size
10- Padding due to alignment requirements
11
12**type-layout currently only functions on structs with named fields.** This is a
13temporary limitation.
14
15## Examples
16
17The layout of types is only defined if they're `#[repr(C)]`. This crate works on
18non-`#[repr(C)]` types, but their layout is unpredictable.
19
20```rust
21use type_layout::TypeLayout;
22
23#[derive(TypeLayout)]
24#[repr(C)]
25struct Foo {
26 a: u8,
27 b: u32,
28}
29
30println!("{}", Foo::type_layout());
31// prints:
32// Foo (size 8, alignment 4)
33// | Offset | Name | Size |
34// | ------ | --------- | ---- |
35// | 0 | a | 1 |
36// | 1 | [padding] | 3 |
37// | 4 | b | 4 |
38```
39
40Over-aligned types have trailing padding, which can be a source of bugs in some
41FFI scenarios:
42
43```rust
44use type_layout::TypeLayout;
45
46#[derive(TypeLayout)]
47#[repr(C, align(128))]
48struct OverAligned {
49 value: u8,
50}
51
52println!("{}", OverAligned::type_layout());
53// prints:
54// OverAligned (size 128, alignment 128)
55// | Offset | Name | Size |
56// | ------ | --------- | ---- |
57// | 0 | value | 1 |
58// | 1 | [padding] | 127 |
59```
60
61## Minimum Supported Rust Version (MSRV)
62
63type-layout supports Rust 1.34.1 and newer. Until type-layout reaches 1.0,
64changes to the MSRV will require major version bumps. After 1.0, MSRV changes
65will only require minor version bumps, but will need significant justification.
66*/
67
68use std::borrow::Cow;
69use std::fmt::{self, Display};
70use std::str;
71
72pub use type_layout_derive::TypeLayout;
73
74#[doc(hidden)]
75pub use memoffset;
76
77pub trait TypeLayout {
78 fn type_layout() -> TypeLayoutInfo;
79}
80
81#[derive(Debug)]
82#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
83pub struct TypeLayoutInfo {
84 pub name: Cow<'static, str>,
85 pub size: usize,
86 pub alignment: usize,
87 pub fields: Vec<Field>,
88}
89
90#[derive(Debug)]
91#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
92pub enum Field {
93 Field {
94 name: Cow<'static, str>,
95 ty: Cow<'static, str>,
96 size: usize,
97 },
98 Padding {
99 size: usize,
100 },
101}
102
103impl fmt::Display for TypeLayoutInfo {
104 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
105 writeln!(
106 formatter,
107 "{} (size {}, alignment {})",
108 self.name, self.size, self.alignment
109 )?;
110
111 let longest_name = self
112 .fields
113 .iter()
114 .map(|field| match field {
115 Field::Field { name, .. } => name.len(),
116 Field::Padding { .. } => "[padding]".len(),
117 })
118 .max()
119 .unwrap_or(1);
120
121 let widths = RowWidths {
122 offset: "Offset".len(),
123 name: longest_name,
124 size: "Size".len(),
125 };
126
127 write_row(
128 formatter,
129 widths,
130 Row {
131 offset: "Offset",
132 name: "Name",
133 size: "Size",
134 },
135 )?;
136
137 write_row(
138 formatter,
139 widths,
140 Row {
141 offset: "------",
142 name: str::repeat("-", longest_name),
143 size: "----",
144 },
145 )?;
146
147 let mut offset = 0;
148
149 for field in &self.fields {
150 match field {
151 Field::Field { name, size, .. } => {
152 write_row(formatter, widths, Row { offset, name, size })?;
153
154 offset += size;
155 }
156 Field::Padding { size } => {
157 write_row(
158 formatter,
159 widths,
160 Row {
161 offset,
162 name: "[padding]",
163 size,
164 },
165 )?;
166
167 offset += size;
168 }
169 }
170 }
171
172 Ok(())
173 }
174}
175
176#[derive(Clone, Copy)]
177struct RowWidths {
178 offset: usize,
179 name: usize,
180 size: usize,
181}
182
183struct Row<O, N, S> {
184 offset: O,
185 name: N,
186 size: S,
187}
188
189fn write_row<O: Display, N: Display, S: Display>(
190 formatter: &mut fmt::Formatter,
191 widths: RowWidths,
192 row: Row<O, N, S>,
193) -> fmt::Result {
194 writeln!(
195 formatter,
196 "| {:<offset_width$} | {:<name_width$} | {:<size_width$} |",
197 row.offset,
198 row.name,
199 row.size,
200 offset_width = widths.offset,
201 name_width = widths.name,
202 size_width = widths.size
203 )
204}