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;