ripsync_core/util/
aligned_buf.rs1#![allow(unsafe_code)]
12
13use std::alloc::{self, Layout};
14use std::ops::{Deref, DerefMut};
15use std::ptr::NonNull;
16
17pub struct AlignedBuf {
22 ptr: NonNull<u8>,
23 layout: Layout,
24 len: usize,
25}
26
27unsafe impl Send for AlignedBuf {}
30
31impl AlignedBuf {
32 #[must_use]
40 pub fn new(size: usize) -> Self {
41 assert!(size > 0, "buffer size must be non-zero");
42
43 let page_size = page_size();
44 let size = size.next_multiple_of(page_size);
47
48 #[cfg(unix)]
49 let (ptr, layout) = alloc_unix(size, page_size);
50
51 #[cfg(windows)]
52 let (ptr, layout) = alloc_windows(size, page_size);
53
54 #[cfg(not(any(unix, windows)))]
55 let (ptr, layout) = alloc_fallback(size, page_size);
56
57 Self { ptr, layout, len: size }
58 }
59
60 #[must_use]
62 pub fn len(&self) -> usize {
63 self.len
64 }
65
66 #[must_use]
68 pub fn is_empty(&self) -> bool {
69 self.len == 0
70 }
71}
72
73impl Deref for AlignedBuf {
74 type Target = [u8];
75
76 fn deref(&self) -> &[u8] {
77 unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
81 }
82}
83
84impl DerefMut for AlignedBuf {
85 fn deref_mut(&mut self) -> &mut [u8] {
86 unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
90 }
91}
92
93impl Drop for AlignedBuf {
94 fn drop(&mut self) {
95 #[cfg(unix)]
96 {
97 unsafe {
100 alloc::dealloc(self.ptr.as_ptr(), self.layout);
101 }
102 }
103
104 #[cfg(windows)]
105 {
106 unsafe {
110 _aligned_free(self.ptr.as_ptr().cast::<std::ffi::c_void>());
111 }
112 }
113
114 #[cfg(not(any(unix, windows)))]
115 {
116 unsafe {
119 alloc::dealloc(self.ptr.as_ptr(), self.layout);
120 }
121 }
122 }
123}
124
125#[must_use]
127fn page_size() -> usize {
128 #[cfg(unix)]
129 {
130 rustix::param::page_size()
131 }
132
133 #[cfg(windows)]
134 {
135 use windows_sys::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
136
137 let mut sysinfo: SYSTEM_INFO = unsafe { std::mem::zeroed() };
140 unsafe {
141 GetSystemInfo(&mut sysinfo);
142 }
143 sysinfo.dwPageSize as usize
144 }
145
146 #[cfg(not(any(unix, windows)))]
147 {
148 4096 }
150}
151
152#[cfg(unix)]
153fn alloc_unix(size: usize, page_size: usize) -> (NonNull<u8>, Layout) {
154 let layout =
155 Layout::from_size_align(size, page_size).expect("invalid layout for page-aligned buffer");
156
157 let ptr = unsafe { alloc::alloc(layout) };
160 let ptr = NonNull::new(ptr).expect("page-aligned allocation failed");
161
162 (ptr, layout)
163}
164
165#[cfg(windows)]
166fn alloc_windows(size: usize, page_size: usize) -> (NonNull<u8>, Layout) {
167 let ptr = unsafe { _aligned_malloc(size, page_size) };
171 if ptr.is_null() {
172 panic!("page-aligned allocation failed");
173 }
174
175 let ptr = unsafe { NonNull::new_unchecked(ptr.cast::<u8>()) };
178
179 let layout = Layout::from_size_align(1, 1).unwrap();
181
182 (ptr, layout)
183}
184
185#[cfg(not(any(unix, windows)))]
186fn alloc_fallback(size: usize, page_size: usize) -> (NonNull<u8>, Layout) {
187 let layout =
188 Layout::from_size_align(size, page_size).expect("invalid layout for page-aligned buffer");
189
190 let ptr = unsafe { alloc::alloc(layout) };
193 let ptr = NonNull::new(ptr).expect("page-aligned allocation failed");
194
195 (ptr, layout)
196}
197
198#[cfg(windows)]
199unsafe extern "C" {
200 fn _aligned_malloc(size: usize, alignment: usize) -> *mut std::ffi::c_void;
201 fn _aligned_free(ptr: *mut std::ffi::c_void);
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn allocates_page_aligned_buffer() {
210 let buf = AlignedBuf::new(1024);
211 assert!(buf.len() >= 1024);
212 assert!(!buf.is_empty());
213
214 let ptr = buf.ptr.as_ptr() as usize;
215 let ps = page_size();
216 assert_eq!(ptr % ps, 0, "buffer not page-aligned");
217 }
218
219 #[test]
220 fn buffer_is_writable_via_deref_mut() {
221 let mut buf = AlignedBuf::new(1024);
222 buf[0] = 42;
223 buf[999] = 24;
224 assert_eq!(buf[0], 42);
225 assert_eq!(buf[999], 24);
226 }
227
228 #[test]
229 fn deref_returns_full_slice() {
230 let buf = AlignedBuf::new(512);
231 assert_eq!(buf.len(), buf.len());
232 let slice: &[u8] = &buf;
234 assert_eq!(slice.len(), buf.len());
235 }
236
237 #[test]
238 fn respects_minimum_page_size() {
239 let buf = AlignedBuf::new(1);
240 let ps = page_size();
241 assert!(buf.len() >= ps);
242 }
243
244 #[test]
245 fn size_is_page_multiple() {
246 let buf = AlignedBuf::new(100_000);
247 let ps = page_size();
248 assert_eq!(buf.len() % ps, 0);
249 }
250}