Skip to main content

windows_namespace_request_sys/
buffer.rs

1// Copyright (c) Mike Grier.
2
3//! An owned byte buffer with a guaranteed alignment.
4//!
5//! Two unrelated parts of this crate need one, for the same underlying reason:
6//! a Win32 structure that a buffer merely *contains* still has to be aligned as
7//! though the buffer were that structure. A self-relative security descriptor
8//! requires DWORD alignment, and the directory-information classes require
9//! 8-byte alignment. A `Box<[u8]>` guarantees neither -- its alignment is 1 --
10//! so the requirement is met explicitly here rather than assumed twice.
11
12use std::alloc::{Layout, alloc_zeroed, dealloc, handle_alloc_error};
13use std::fmt;
14use std::ptr::NonNull;
15use std::slice;
16
17/// An owned, zero-initialised byte buffer aligned to a stated boundary.
18///
19/// The alignment is a property of the buffer, not of the first thing written
20/// into it: it holds for the buffer's whole life and is preserved by
21/// [`Clone`].
22///
23/// # Example
24///
25/// ```
26/// use windows_namespace_request_sys::AlignedBuffer;
27///
28/// // A self-relative security descriptor needs DWORD alignment; the directory
29/// // information classes need 8. A `Box<[u8]>` guarantees neither -- its
30/// // alignment is 1.
31/// let mut buffer = AlignedBuffer::zeroed(20, 8);
32///
33/// assert_eq!(buffer.len(), 20);
34/// assert_eq!(buffer.align(), 8);
35/// assert_eq!(buffer.as_ptr() as usize % 8, 0);
36/// assert!(buffer.as_slice().iter().all(|byte| *byte == 0));
37///
38/// // The length need not be a multiple of the alignment: the constraint is on
39/// // the buffer's address, not its size.
40/// buffer.as_mut_slice()[19] = 0xAB;
41/// assert_eq!(buffer.as_slice()[19], 0xAB);
42/// ```
43///
44/// # Example: a clone keeps the guarantee
45///
46/// ```
47/// use windows_namespace_request_sys::AlignedBuffer;
48///
49/// let original = AlignedBuffer::from_bytes(&[1, 2, 3], 4);
50/// let clone = original.clone();
51///
52/// assert_eq!(clone, original);
53/// assert_eq!(clone.as_ptr() as usize % 4, 0);
54/// assert_ne!(clone.as_ptr(), original.as_ptr(), "a copy, not a second view");
55///
56/// // Alignment is part of what the buffer promises, so it is part of equality.
57/// assert_ne!(AlignedBuffer::from_bytes(&[1, 2, 3], 8), original);
58/// ```
59pub struct AlignedBuffer {
60    /// Always non-null and aligned to `layout.align()`. When `layout.size()` is
61    /// zero this is a dangling-but-aligned pointer that is never dereferenced
62    /// and never freed.
63    pointer: NonNull<u8>,
64    layout: Layout,
65}
66
67impl AlignedBuffer {
68    /// Allocates `len` zeroed bytes aligned to `align`.
69    ///
70    /// # Panics
71    ///
72    /// Panics if `align` is not a power of two, or if `len` rounded up to
73    /// `align` overflows `isize` -- both of which are programming errors rather
74    /// than conditions a caller can encounter with valid input.
75    #[must_use]
76    pub fn zeroed(len: usize, align: usize) -> Self {
77        let layout = Layout::from_size_align(len, align)
78            .expect("an alignment that is a power of two, and a size that does not overflow");
79
80        if len == 0 {
81            // A zero-sized allocation is undefined, so use the alignment itself
82            // as the address: non-null, correctly aligned, never dereferenced,
83            // and never freed.
84            let pointer = NonNull::new(align as *mut u8).expect("a non-zero alignment");
85            return Self { pointer, layout };
86        }
87
88        // SAFETY: layout has a non-zero size, which is alloc_zeroed's only
89        // requirement beyond a valid layout.
90        let raw = unsafe { alloc_zeroed(layout) };
91        let Some(pointer) = NonNull::new(raw) else {
92            handle_alloc_error(layout);
93        };
94
95        Self { pointer, layout }
96    }
97
98    /// Allocates a buffer aligned to `align` holding a copy of `bytes`.
99    ///
100    /// # Panics
101    ///
102    /// As [`zeroed`](Self::zeroed).
103    #[must_use]
104    pub fn from_bytes(bytes: &[u8], align: usize) -> Self {
105        let mut buffer = Self::zeroed(bytes.len(), align);
106        buffer.as_mut_slice().copy_from_slice(bytes);
107        buffer
108    }
109
110    /// The buffer's length in bytes.
111    #[must_use]
112    pub fn len(&self) -> usize {
113        self.layout.size()
114    }
115
116    /// Whether the buffer holds no bytes.
117    #[must_use]
118    pub fn is_empty(&self) -> bool {
119        self.len() == 0
120    }
121
122    /// The alignment the buffer's address is guaranteed to satisfy.
123    #[must_use]
124    pub fn align(&self) -> usize {
125        self.layout.align()
126    }
127
128    /// The buffer's contents.
129    #[must_use]
130    pub fn as_slice(&self) -> &[u8] {
131        // SAFETY: pointer is valid for layout.size() initialised bytes, and the
132        // borrow ties the slice to this buffer.
133        unsafe { slice::from_raw_parts(self.pointer.as_ptr(), self.layout.size()) }
134    }
135
136    /// The buffer's contents, mutably.
137    #[must_use]
138    pub fn as_mut_slice(&mut self) -> &mut [u8] {
139        // SAFETY: as as_slice, and the exclusive borrow rules out aliasing.
140        unsafe { slice::from_raw_parts_mut(self.pointer.as_ptr(), self.layout.size()) }
141    }
142
143    /// The buffer's address, for handing to a Win32 call.
144    #[must_use]
145    pub fn as_ptr(&self) -> *const u8 {
146        self.pointer.as_ptr()
147    }
148
149    /// The buffer's address, for a Win32 call that writes into it.
150    #[must_use]
151    pub fn as_mut_ptr(&mut self) -> *mut u8 {
152        self.pointer.as_ptr()
153    }
154}
155
156impl Drop for AlignedBuffer {
157    fn drop(&mut self) {
158        if self.layout.size() == 0 {
159            return;
160        }
161
162        // SAFETY: pointer came from alloc_zeroed with exactly this layout, and
163        // Drop runs once.
164        unsafe { dealloc(self.pointer.as_ptr(), self.layout) };
165    }
166}
167
168impl Clone for AlignedBuffer {
169    fn clone(&self) -> Self {
170        Self::from_bytes(self.as_slice(), self.align())
171    }
172}
173
174impl fmt::Debug for AlignedBuffer {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.debug_struct("AlignedBuffer")
177            .field("len", &self.len())
178            .field("align", &self.align())
179            .finish_non_exhaustive()
180    }
181}
182
183impl PartialEq for AlignedBuffer {
184    /// Compares contents and alignment, not addresses.
185    fn eq(&self, other: &Self) -> bool {
186        self.align() == other.align() && self.as_slice() == other.as_slice()
187    }
188}
189
190impl Eq for AlignedBuffer {}
191
192// SAFETY: the buffer owns its allocation exclusively and holds plain bytes with
193// no interior mutability, so moving it between threads and sharing a shared
194// reference are both sound. The raw pointer is what blocks the automatic
195// derivation, and it is an owning pointer rather than a borrow.
196unsafe impl Send for AlignedBuffer {}
197// SAFETY: as above.
198unsafe impl Sync for AlignedBuffer {}
199
200#[cfg(test)]
201mod tests;