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
122use core::mem::size_of;
123use core::ptr::NonNull;
124
125#[cfg(feature = "alloc")]
126use alloc::borrow::Cow;
127
128use crate::traits::ZeroCopy;
129
130/// The type used to calculate default alignment for [`OwnedBuf`].
131#[repr(transparent)]
132pub struct DefaultAlignment(usize);
133
134/// Return the max capacity of this vector. This depends on the requested
135/// alignment.
136///
137/// This follows how it's defined by `max_size_for_align` in [`Layout`].
138///
139/// [`Layout`]: core::alloc::Layout
140#[inline]
141pub fn max_capacity_for_align(align: usize) -> usize {
142 isize::MAX as usize - (align - 1)
143}
144
145/// Construct a buffer with an alignment matching that of `T` which is either
146/// wrapped in a [`Buf`] if it is already correctly aligned, or inside of an
147/// allocated [`OwnedBuf`].
148///
149/// # Examples
150///
151/// ```no_run
152/// use std::fs::read;
153///
154/// use musli_zerocopy::{Ref, ZeroCopy};
155/// use musli_zerocopy::buf;
156///
157/// #[derive(ZeroCopy)]
158/// #[repr(C)]
159/// struct Person {
160/// name: Ref<str>,
161/// age: u32,
162/// }
163///
164/// let bytes = read("person.bin")?;
165/// let buf = buf::aligned_buf::<u128>(&bytes);
166///
167/// let s = buf.load(Ref::<Person>::zero())?;
168/// # Ok::<_, anyhow::Error>(())
169/// ```
170#[cfg(feature = "alloc")]
171pub fn aligned_buf<T>(bytes: &[u8]) -> Cow<'_, Buf> {
172 Buf::new(bytes).to_aligned::<T>()
173}
174
175/// Construct a buffer with a specific alignment which is either wrapped in a
176/// [`Buf`] if it is already correctly aligned, or inside of an allocated
177/// [`OwnedBuf`].
178///
179/// # Panics
180///
181/// Panics if `align` is not a power of two or if the size of the buffer is
182/// larger than [`max_capacity_for_align(align)`].
183///
184/// # Examples
185///
186/// ```no_run
187/// use std::fs::read;
188///
189/// use musli_zerocopy::{Ref, ZeroCopy};
190/// use musli_zerocopy::buf;
191///
192/// #[derive(ZeroCopy)]
193/// #[repr(C)]
194/// struct Person {
195/// name: Ref<str>,
196/// age: u32,
197/// }
198///
199/// let bytes = read("person.bin")?;
200/// let buf = buf::aligned_buf_with(&bytes, 16);
201///
202/// let s = buf.load(Ref::<Person>::zero())?;
203/// # Ok::<_, anyhow::Error>(())
204/// ```
205#[cfg(feature = "alloc")]
206pub fn aligned_buf_with(bytes: &[u8], align: usize) -> Cow<'_, Buf> {
207 Buf::new(bytes).to_aligned_with(align)
208}
209
210/// # Safety
211///
212/// Must be called with an alignment that is a power of two.
213#[inline]
214pub(crate) fn is_aligned_with(ptr: *const u8, align: usize) -> bool {
215 (ptr as usize) & (align - 1) == 0
216}
217
218/// Calculate padding with the assumption that alignment is a power of two.
219#[inline(always)]
220pub(crate) fn padding_to(len: usize, align: usize) -> usize {
221 let mask = align - 1;
222 (align - (len & mask)) & mask
223}
224
225/// Store the raw bytes associated with `*const T` into the buffer and advance
226/// its position by `size_of::<T>()`.
227///
228/// This does not require `T` to be aligned.
229///
230/// # Safety
231///
232/// The caller must ensure that any store call only includes data up-to the size
233/// of `Self`.
234///
235/// Also see the [type level safety documentation][#safety]
236#[inline]
237pub(crate) unsafe fn store_unaligned<T>(data: NonNull<u8>, value: *const T)
238where
239 T: ZeroCopy,
240{
241 data.as_ptr()
242 .copy_from_nonoverlapping(value.cast(), size_of::<T>());
243
244 if T::PADDED {
245 let mut padder = Padder::new(data);
246 T::pad(&mut padder);
247 padder.remaining();
248 }
249}