buf_trait/lib.rs
1//! The `Buf` trait.
2//!
3//! This crate provides a trait for abstracting over buffer-like types, such
4//! as `str` and `[u8]`. This is a much stronger property than, say,
5//! implementing [`AsRef<[u8]>`]. These are variable-length types that you might
6//! want to store as a raw byte buffer and then transmute to and from `&[u8]`.
7//!
8//! This crate provides all the functionality necessary for doing so safely,
9//! correctly, and in `const`.
10
11#![no_std]
12
13use core::alloc::Layout;
14use core::mem;
15use core::slice;
16use core::slice::SliceIndex;
17
18/// A trait for abstracting over `str`, `[u8]`, and other byte-string-like
19/// types.
20///
21/// See the [crate docs](self) for more information.
22///
23/// # Safety
24///
25/// This trait should only be implemented on types that are, essentially, a
26/// `repr(transpartent)` wrapper over a `[T]` for some Copy type `T`.
27///
28/// In particular, `B: Buf` the requires that the following must hold:
29///
30/// 1. Transmute `&B` to `&[T]`, where `T` is [`zerocopy::AsBytes`]. Transmute
31/// here is quite literal: `mem::transmute<&B, &[T]>` MUST be a valid way
32/// to convert between them.
33///
34/// 2. Transmute `&[T]` to `&B` if the contents of that `&[T]` originated from
35/// operation (1).
36///
37/// 3. Byte-copy `&B` to a `T`-aligned buffer, and then transmute
38/// the resulting `&[T]` to `&B` again.
39///
40/// 4. `x == y` implies that `x.as_bytes() == y.as_bytes()`.
41///
42/// 5. `B::from_bytes(&[])` and `B::from_bytes_mut(&mut [])` always produce
43/// valid values.
44///
45/// Notably, none of `CStr`, `OsStr`, or `Path` can implement `Buf` because
46/// their layout as slices is not part of their interface.
47///
48/// `T` may be zero-sized, but functions will panic in this case.
49pub unsafe trait Buf {
50 /// The element type of the underlying type. This is used for computing e.g.
51 /// alignment and stride.
52 type Element: zerocopy::AsBytes + Copy;
53
54 /// The length of this value, in elements.
55 fn elem_len(&self) -> usize {
56 mem::size_of_val(self) / mem::size_of::<Self::Element>()
57 }
58
59 /// The length of this value, in bytes.
60 fn byte_len(&self) -> usize {
61 mem::size_of_val(self)
62 }
63
64 /// Creates a new empty [`Buf`].
65 fn empty<'a, B: ?Sized + Buf>() -> &'a B {
66 empty()
67 }
68
69 /// Converts a reference to a [`Buf`] into its underlying bytes.
70 fn as_bytes(&self) -> &[u8] {
71 as_bytes(self)
72 }
73
74 /// Converts a byte slice to a reference to a [`Buf`].
75 ///
76 /// # Safety
77 ///
78 /// `bytes` must have been either constructed via transmuting from `&Self`,
79 /// or a bytewise copy of a `Self`.
80 unsafe fn from_bytes(bytes: &[u8]) -> &Self {
81 as_buf(bytes)
82 }
83
84 /// Converts a reference to a [`Buf`] into its underlying bytes.
85 fn as_bytes_mut(&mut self) -> &mut [u8] {
86 as_bytes_mut(self)
87 }
88
89 /// Converts a byte slice to a reference to a [`Buf`].
90 ///
91 /// # Safety
92 ///
93 /// `bytes` must have been either constructed via transmuting from `&Self`,
94 /// or a bytewise copy of a `Self`.
95 unsafe fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
96 as_buf_mut(bytes)
97 }
98
99 /// Performs a slicing operation on `self` with respect to byte indices.
100 ///
101 /// # Safety
102 ///
103 /// This function does not perform any checking beyonds bounds checking. For
104 /// example, if called on `str`, this function may slice through a multi-byte
105 /// Unicode scalar, producing a `&str` that violate's `str`'s validity
106 /// constraints (i.e., Undefined Behavior).
107 unsafe fn slice_along_bytes<Idx>(&self, index: Idx) -> Option<&Self>
108 where
109 Idx: SliceIndex<[u8], Output = [u8]>,
110 {
111 self.as_bytes().get(index).map(|b| Self::from_bytes(b))
112 }
113}
114
115unsafe impl<T: zerocopy::AsBytes + Copy> Buf for [T] {
116 type Element = T;
117}
118
119unsafe impl Buf for str {
120 type Element = u8;
121}
122
123/// Computes the layout of `buf`.
124///
125/// This function is `const`, unlike [`Layout::for_value()`].
126pub const fn layout_of<B: ?Sized + Buf>(buf: &B) -> Layout {
127 unsafe {
128 Layout::from_size_align_unchecked(
129 as_bytes(buf).len(),
130 mem::align_of::<B::Element>(),
131 )
132 }
133}
134
135/// Creates a new empty [`Buf`].
136///
137/// Unlike [`Buf::empty()`], this function is `const`.
138pub const fn empty<'a, B: ?Sized + Buf>() -> &'a B {
139 unsafe { as_buf(&[]) }
140}
141
142/// Converts a reference to a [`Buf`] into its underlying bytes.
143///
144/// Unlike [`Buf::as_bytes()`], this function is `const`.
145pub const fn as_bytes<B: ?Sized + Buf>(buf: &B) -> &[u8] {
146 assert!(
147 mem::size_of::<B::Element>() > 0,
148 "buf-trait: cannot use ZST as in type-erased context"
149 );
150
151 let ptr = &buf as *const &_ as *const &[B::Element];
152
153 unsafe {
154 let buf = *ptr;
155 // SAFETY: The safety rules of `Buf` make this valid.
156 let ptr = buf as *const _ as *const u8;
157 let len = buf.len() * mem::size_of::<B::Element>();
158 slice::from_raw_parts(ptr, len)
159 }
160}
161
162/// Converts a mutable reference to a [`Buf`] into its underlying bytes.
163pub fn as_bytes_mut<B: ?Sized + Buf>(mut buf: &mut B) -> &mut [u8] {
164 assert!(
165 mem::size_of::<B::Element>() > 0,
166 "buf-trait: cannot use ZST as in type-erased context"
167 );
168
169 let ptr = &mut buf as *mut &mut _ as *mut &mut [B::Element];
170
171 unsafe {
172 let buf = &mut *ptr;
173 // SAFETY: The safety rules of `Buf` make this valid.
174 let ptr = buf as *mut _ as *mut u8;
175 slice::from_raw_parts_mut(ptr, mem::size_of_val(&**buf))
176 }
177}
178
179/// Converts a byte slice to a reference to a [`Buf`].
180///
181/// Unlike [`Buf::from_bytes()`], this function is `const`.
182///
183/// # Safety
184///
185/// See [`Buf::from_bytes()`].
186pub const unsafe fn as_buf<B: ?Sized + Buf>(bytes: &[u8]) -> &B {
187 assert!(
188 mem::size_of::<B::Element>() > 0,
189 "buf-trait: cannot use ZST as in type-erased context"
190 );
191
192 let buf = slice::from_raw_parts(
193 bytes.as_ptr().cast::<B::Element>(),
194 bytes.len() / mem::size_of::<B::Element>(),
195 );
196
197 let ptr = &buf as *const &[_] as *const &B;
198 *ptr
199}
200
201/// Converts a mutable byte slice to a reference to a [`Buf`].
202///
203/// # Safety
204///
205/// See [`Buf::from_bytes()`].
206pub unsafe fn as_buf_mut<B: ?Sized + Buf>(bytes: &mut [u8]) -> &mut B {
207 assert!(
208 mem::size_of::<B::Element>() > 0,
209 "buf-trait: cannot use ZST as in type-erased context"
210 );
211
212 let mut buf = slice::from_raw_parts_mut(
213 bytes.as_mut_ptr().cast::<B::Element>(),
214 bytes.len() / mem::size_of::<B::Element>(),
215 );
216
217 let ptr = &mut buf as *mut &mut [_] as *mut &mut B;
218 *ptr
219}