dataview/
lib.rs

1/*!
2The [`Pod` trait](Pod) marks types whose values can be safely transmuted between byte arrays of the same size.
3
4The [`DataView` type](DataView) defines read and write data APIs to an underlying byte buffer.
5
6# Examples
7
8```
9#[derive(dataview::Pod)]
10#[repr(C)]
11struct MyType {
12	field: i32,
13}
14
15// Construct a zero initialized instance
16let mut inst: MyType = dataview::zeroed();
17assert_eq!(inst.field, 0);
18
19// Use DataView to access the instance
20let view = dataview::DataView::from_mut(&mut inst);
21view.write(2, &255_u8);
22
23// Create a byte view over the instance
24assert_eq!(dataview::bytes(&inst), &[0, 0, 255, 0]);
25```
26*/
27
28#![no_std]
29
30use core::{mem, slice};
31use core::marker::PhantomData;
32
33mod data_view;
34pub use self::data_view::DataView;
35
36#[cfg(feature = "derive_pod")]
37#[doc(inline)]
38pub use ::derive_pod::Pod;
39
40#[cfg(feature = "derive_pod")]
41#[doc(hidden)]
42pub use ::derive_pod::FieldOffsets;
43
44mod derive_pod;
45mod field_offsets;
46mod offset_of;
47
48/// Types whose values can be safely transmuted between byte arrays of the same size.
49///
50/// # Safety
51///
52/// It must be safe to transmute between any byte array (with length equal to the size of the type) and `Self`.
53///
54/// This is true for these primitive types: `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128`, `f32`, `f64`.
55/// The raw pointer types are not pod under strict provenance rules but can be through the 'int2ptr' feature.
56/// Primitives such as `str` and `bool` are not pod because not every valid byte pattern is a valid instance of these types.
57/// References or types with lifetimes are _never_ pod.
58///
59/// Arrays and slices of pod types are also pod themselves.
60///
61/// Note that it is legal for pod types to be a [ZST](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts).
62///
63/// When `Pod` is implemented for a user defined type it must meet the following requirements:
64///
65/// * Must be annotated with [`#[repr(C)]`](https://doc.rust-lang.org/nomicon/other-reprs.html#reprc)
66///   or [`#[repr(transparent)]`](https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent).
67/// * Must have every field's type implement `Pod` itself.
68/// * Must not have any padding between its fields, define dummy fields to cover the padding.
69///
70/// # Derive macro
71///
72/// To help with safely implementing this trait for user defined types, a [derive macro](derive@Pod) is provided to implement the `Pod` trait if the requirements are satisfied.
73pub unsafe trait Pod: 'static {}
74
75/// Returns a zero-initialized instance of the type.
76///
77/// ```
78/// let v: i32 = dataview::zeroed();
79/// assert_eq!(v, 0);
80/// ```
81#[inline]
82pub fn zeroed<T: Pod>() -> T {
83	unsafe { mem::MaybeUninit::zeroed().assume_init() }
84}
85
86/// Returns the object's memory as a byte slice.
87///
88/// ```
89/// let v = 0xcdcdcdcd_u32;
90/// assert_eq!(dataview::bytes(&v), &[0xcd, 0xcd, 0xcd, 0xcd]);
91/// ```
92#[inline]
93pub fn bytes<T: ?Sized + Pod>(src: &T) -> &[u8] {
94	unsafe { slice::from_raw_parts(src as *const _ as *const u8, mem::size_of_val(src)) }
95}
96
97/// Returns the object's memory as a mutable byte slice.
98#[inline]
99pub fn bytes_mut<T: ?Sized + Pod>(src: &mut T) -> &mut [u8] {
100	unsafe { slice::from_raw_parts_mut(src as *mut _ as *mut u8, mem::size_of_val(src)) }
101}
102
103/// Helper trait to provide methods directly on the pod types.
104///
105/// Do not use this trait in any signatures, use [`Pod`] directly instead.
106/// There's a blanket impl that provides these methods for all pod types.
107pub trait PodMethods {
108	/// Returns a zero-initialized instance of the type.
109	fn zeroed() -> Self where Self: Sized;
110	/// Returns the object's memory as a byte slice.
111	fn as_bytes(&self) -> &[u8];
112	/// Returns the object's memory as a mutable byte slice.
113	fn as_bytes_mut(&mut self) -> &mut [u8];
114	/// Returns a data view into the object's memory.
115	fn as_data_view(&self) -> &DataView;
116	/// Returns a mutable data view into the object's memory.
117	fn as_data_view_mut(&mut self) -> &mut DataView;
118}
119
120impl<T: ?Sized + Pod> PodMethods for T {
121	#[inline]
122	fn zeroed() -> T where T: Sized {
123		zeroed()
124	}
125	#[inline]
126	fn as_bytes(&self) -> &[u8] {
127		bytes(self)
128	}
129	#[inline]
130	fn as_bytes_mut(&mut self) -> &mut [u8] {
131		bytes_mut(self)
132	}
133	#[inline]
134	fn as_data_view(&self) -> &DataView {
135		DataView::from(self)
136	}
137	#[inline]
138	fn as_data_view_mut(&mut self) -> &mut DataView {
139		DataView::from_mut(self)
140	}
141}
142
143unsafe impl Pod for () {}
144
145unsafe impl Pod for i8 {}
146unsafe impl Pod for i16 {}
147unsafe impl Pod for i32 {}
148unsafe impl Pod for i64 {}
149unsafe impl Pod for i128 {}
150unsafe impl Pod for isize {}
151
152unsafe impl Pod for u8 {}
153unsafe impl Pod for u16 {}
154unsafe impl Pod for u32 {}
155unsafe impl Pod for u64 {}
156unsafe impl Pod for u128 {}
157unsafe impl Pod for usize {}
158
159unsafe impl Pod for f32 {}
160unsafe impl Pod for f64 {}
161
162#[cfg(feature = "int2ptr")]
163unsafe impl<T: 'static> Pod for *const T {}
164#[cfg(feature = "int2ptr")]
165unsafe impl<T: 'static> Pod for *mut T {}
166
167unsafe impl<T: 'static> Pod for PhantomData<T> {}
168
169unsafe impl<T: Pod> Pod for [T] {}
170unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
171
172// Strict provenance approved way of checking raw pointer alignment without exposing the pointer
173const fn is_aligned<T>(ptr: *const T) -> bool {
174	let addr: usize = unsafe { mem::transmute(ptr) };
175	addr % mem::align_of::<T>() == 0
176}
177
178#[cfg(test)]
179mod tests;
180
181#[cfg(doc)]
182#[doc = include_str!("../readme.md")]
183fn readme() {}
184
185/// Reveals the evaluated value of a constant expression.
186///
187/// The result is a compiletime error: `expected an array with a fixed size of 0 elements, found one with N elements` where `N` is the value of the constant expression.
188///
189/// ```compile_fail
190/// struct Foo {
191/// 	field1: i8,
192/// 	field2: u16,
193/// }
194///
195/// dataview::reveal_const!(std::mem::size_of::<Foo>());
196/// ```
197#[doc(hidden)]
198#[macro_export]
199macro_rules! reveal_const {
200	($e:expr) => {
201		const _: [(); 0] = [(); $e];
202	};
203}