musli_zerocopy/buf/mod.rs
1//! Types associated within reading and binding to a [`Buf`].
2//!
3//! Buffers are slices of bytes without any inherent alignment. They allow use
4//! to safely convert offset-like types to references for types which implements
5//! [`ZeroCopy`].
6//!
7//! # Extension traits
8//!
9//! This module provides a couple of extension traits which can be used to alter
10//! how types interact with buffers.
11//!
12//! The following is a more compact [`Ref<[u8]>`] which only occupies 4 bytes,
13//! but restricts the offset and length.
14//!
15//! [`Ref<u8>`]: crate::pointer::Ref
16//!
17//! ```
18//! use musli_zerocopy::{Error, Ref, ZeroCopy};
19//! use musli_zerocopy::buf::{OwnedBuf, Buf, Load, LoadMut, Visit};
20//!
21//! #[derive(Clone, Copy, ZeroCopy)]
22//! #[repr(C, packed)]
23//! struct CompactSlice {
24//! // 3 bytes of offset.
25//! offset: [u8; 3],
26//! // one byte of length.
27//! len: u8,
28//! }
29//!
30//! impl CompactSlice {
31//! /// Construct a new compact slice, panic if offset and length
32//! /// overflow 3 and 1 byte integers respectively.
33//! pub fn new(offset: usize, len: usize) -> Self {
34//! assert!(offset <= 0xffffffusize && len <= 0xff);
35//! let [a, b, c, ..] = offset.to_le_bytes();
36//!
37//! Self {
38//! offset: [a, b, c],
39//! len: len as u8,
40//! }
41//! }
42//!
43//! /// Get the length of the compact slice.
44//! pub fn len(&self) -> usize {
45//! self.len as usize
46//! }
47//!
48//! fn to_slice(&self) -> Ref<[u8]> {
49//! let [a, b, c] = self.offset;
50//! let offset = u32::from_le_bytes([a, b, c, 0]);
51//! Ref::with_metadata(offset as usize, self.len as usize)
52//! }
53//! }
54//!
55//! impl Load for CompactSlice {
56//! type Target = [u8];
57//!
58//! fn load<'buf>(&self, buf: &'buf Buf) -> Result<&'buf Self::Target, Error> {
59//! buf.load(self.to_slice())
60//! }
61//! }
62//!
63//! impl LoadMut for CompactSlice {
64//! fn load_mut<'buf>(&self, buf: &'buf mut Buf) -> Result<&'buf mut Self::Target, Error> {
65//! buf.load_mut(self.to_slice())
66//! }
67//! }
68//!
69//! let mut buf1 = OwnedBuf::new();
70//! let slice = buf1.store_slice(&[1u8, 2, 3, 4])?;
71//! let slice_ref: Ref<Ref<[u8]>> = buf1.store(&slice)?;
72//! assert_eq!(buf1.len(), 12);
73//!
74//! let slice = buf1.load(slice_ref)?;
75//! assert_eq!(slice.len(), 4);
76//! assert_eq!(buf1.load(*slice)?, &[1, 2, 3, 4]);
77//!
78//! let mut buf2 = OwnedBuf::new();
79//! let slice = buf2.store_slice(&[1u8, 2, 3, 4])?;
80//! let slice = CompactSlice::new(slice.offset(), slice.len());
81//! let slice_ref: Ref<CompactSlice> = buf2.store(&slice)?;
82//! assert_eq!(buf2.len(), 8);
83//!
84//! let slice = buf2.load(slice_ref)?;
85//! assert_eq!(slice.len(), 4);
86//! assert_eq!(buf2.load(*slice)?, &[1, 2, 3, 4]);
87//! # Ok::<_, musli_zerocopy::Error>(())
88//! ```
89
90#[cfg(test)]
91mod tests;
92
93pub use self::buf::Buf;
94mod buf;
95
96pub use self::bind::Bindable;
97mod bind;
98
99pub use self::load::{Load, LoadMut};
100mod load;
101
102pub use self::visit::Visit;
103pub(crate) mod visit;
104
105pub use self::validator::Validator;
106mod validator;
107
108pub use self::padder::Padder;
109mod padder;
110
111pub use self::store_buf::StoreBuf;
112mod store_buf;
113
114#[cfg(feature = "alloc")]
115pub use self::owned_buf::OwnedBuf;
116#[cfg(feature = "alloc")]
117mod owned_buf;
118
119pub use self::slice_mut::SliceMut;
120mod slice_mut;
121
122#[cfg(feature = "alloc")]
123use core::alloc::Layout;
124use core::fmt;
125use core::mem::{MaybeUninit, size_of};
126use core::ptr::NonNull;
127
128#[cfg(feature = "alloc")]
129use alloc::borrow::Cow;
130
131use crate::traits::ZeroCopy;
132
133/// The type used to calculate default alignment for [`OwnedBuf`].
134#[repr(transparent)]
135pub struct DefaultAlignment(usize);
136
137/// Error raised when allocation or space requirements in a fixed buffer fails.
138#[derive(Debug)]
139#[cfg_attr(test, derive(PartialEq))]
140pub struct AllocError {
141 kind: AllocErrorKind,
142}
143
144impl AllocError {
145 /// Construct a new allocation error due to insufficient capacity.
146 #[inline]
147 pub(crate) fn capacity(current: usize, needed: usize) -> Self {
148 Self {
149 kind: AllocErrorKind::Capacity { current, needed },
150 }
151 }
152
153 /// Construct a new allocation error due to an alignment requirement
154 /// not being met.
155 #[inline]
156 pub(crate) fn misaligned<T>(addr: *const T, align: usize) -> Self {
157 Self {
158 kind: AllocErrorKind::Alignment {
159 offset: addr.addr() % align,
160 align,
161 },
162 }
163 }
164
165 /// Construct a new allocation error due to an underlying allocation
166 /// failure.
167 #[cfg(feature = "alloc")]
168 #[inline]
169 #[allow(clippy::self_named_constructors)]
170 pub(crate) fn alloc_error(layout: Layout) -> Self {
171 Self {
172 kind: AllocErrorKind::AllocError { layout },
173 }
174 }
175}
176
177#[derive(Debug)]
178#[cfg_attr(test, derive(PartialEq))]
179enum AllocErrorKind {
180 Capacity {
181 current: usize,
182 needed: usize,
183 },
184 Alignment {
185 offset: usize,
186 align: usize,
187 },
188 #[cfg(feature = "alloc")]
189 AllocError {
190 layout: Layout,
191 },
192}
193
194impl fmt::Display for AllocError {
195 #[inline]
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 match self.kind {
198 AllocErrorKind::Capacity { current, needed } => write!(
199 f,
200 "Container has the capacity {current}, but {needed} bytes are needed",
201 ),
202 AllocErrorKind::Alignment { offset, align } => {
203 write!(f, "Container is not aligned to {align} by {offset} bytes",)
204 }
205 #[cfg(feature = "alloc")]
206 AllocErrorKind::AllocError { layout } => {
207 write!(
208 f,
209 "Failed to allocate {} bytes with alignment {}",
210 layout.size(),
211 layout.align()
212 )
213 }
214 }
215 }
216}
217
218impl core::error::Error for AllocError {}
219
220/// Return the max capacity of this vector. This depends on the requested
221/// alignment.
222///
223/// This follows how it's defined by `max_size_for_align` in [`Layout`].
224///
225/// [`Layout`]: core::alloc::Layout
226#[inline]
227pub fn max_capacity_for_align(align: usize) -> usize {
228 isize::MAX as usize - (align - 1)
229}
230
231/// Construct a buffer with an alignment matching that of `T` which is either
232/// wrapped in a [`Buf`] if it is already correctly aligned, or inside of an
233/// allocated [`OwnedBuf`].
234///
235/// # Examples
236///
237/// ```no_run
238/// use std::fs::read;
239///
240/// use musli_zerocopy::{Ref, ZeroCopy};
241/// use musli_zerocopy::buf;
242///
243/// #[derive(ZeroCopy)]
244/// #[repr(C)]
245/// struct Person {
246/// name: Ref<str>,
247/// age: u32,
248/// }
249///
250/// let bytes = read("person.bin")?;
251/// let buf = buf::aligned_buf::<u128>(&bytes)?;
252///
253/// let s = buf.load(Ref::<Person>::zero())?;
254/// # Ok::<_, anyhow::Error>(())
255/// ```
256#[cfg(feature = "alloc")]
257pub fn aligned_buf<T>(bytes: &[u8]) -> Result<Cow<'_, Buf>, AllocError> {
258 Buf::new(bytes).to_aligned::<T>()
259}
260
261/// Construct a buffer with a specific alignment which is either wrapped in a
262/// [`Buf`] if it is already correctly aligned, or inside of an allocated
263/// [`OwnedBuf`].
264///
265/// # Panics
266///
267/// Panics if `align` is not a power of two.
268///
269/// # Examples
270///
271/// ```no_run
272/// use std::fs::read;
273///
274/// use musli_zerocopy::{Ref, ZeroCopy};
275/// use musli_zerocopy::buf;
276///
277/// #[derive(ZeroCopy)]
278/// #[repr(C)]
279/// struct Person {
280/// name: Ref<str>,
281/// age: u32,
282/// }
283///
284/// let bytes = read("person.bin")?;
285/// let buf = buf::aligned_buf_with(&bytes, 16)?;
286///
287/// let s = buf.load(Ref::<Person>::zero())?;
288/// # Ok::<_, anyhow::Error>(())
289/// ```
290#[cfg(feature = "alloc")]
291pub fn aligned_buf_with(bytes: &[u8], align: usize) -> Result<Cow<'_, Buf>, AllocError> {
292 Buf::new(bytes).to_aligned_with(align)
293}
294
295/// # Safety
296///
297/// Must be called with an alignment that is a power of two.
298#[inline]
299pub(crate) fn is_aligned_with<T>(ptr: *const T, align: usize) -> bool {
300 ptr.addr() & (align - 1) == 0
301}
302
303/// Calculate padding with the assumption that alignment is a power of two.
304#[inline(always)]
305pub(crate) fn padding_to(len: usize, align: usize) -> usize {
306 let mask = align - 1;
307 (align - (len & mask)) & mask
308}
309
310/// Store the raw bytes associated with `*const T` into the buffer and advance
311/// its position by `size_of::<T>()`.
312///
313/// This does not require `T` to be aligned.
314///
315/// # Safety
316///
317/// The caller must ensure that any store call only includes data up-to the size
318/// of `Self`.
319///
320/// Also see the [type level safety documentation][#safety]
321#[inline]
322pub(crate) unsafe fn store_unaligned<T>(data: NonNull<MaybeUninit<u8>>, value: *const T)
323where
324 T: ZeroCopy,
325{
326 unsafe {
327 data.as_ptr()
328 .copy_from_nonoverlapping(value.cast(), size_of::<T>());
329
330 if T::PADDED {
331 let mut padder = Padder::new(data);
332 T::pad(&mut padder);
333 padder.remaining();
334 }
335 }
336}