Mercurial > crates > nonstick
comparison libpam-sys/libpam-sys-helpers/src/lib.rs @ 148:4b3a5095f68c
Move libpam-sys helpers into their own library.
- Renames libpam-sys-helpers to libpam-sys-consts.
- Moves libpam-sys-helpers::helpers into libpam-sys-helpers,
which moves them completely out of libpam-sys's dependency chain.
- Moves the aliases from libpam-sys into libpam-sys::aliases.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Mon, 07 Jul 2025 12:11:43 -0400 |
parents | libpam-sys/libpam-sys-helpers/src/memory.rs@ebb71a412b58 |
children | 14708d9061dc |
comparison
equal
deleted
inserted
replaced
147:4d7333337569 | 148:4b3a5095f68c |
---|---|
1 #![doc = include_str!("../README.md")] | 1 //! This package contains helpers to deal with memory management and |
2 //! | 2 //! annoying type stuff in `libpam-sys` (and LibPAM in general). |
3 //! ## Current implementation | 3 |
4 //! | 4 use std::error::Error; |
5 //! This documentation was built based on the | 5 use std::marker::{PhantomData, PhantomPinned}; |
6 #![doc = concat!("**", env!("LIBPAMSYS_IMPL"), "** implementation.")] | 6 use std::mem::ManuallyDrop; |
7 | 7 use std::ptr::NonNull; |
8 pub mod constants; | 8 use std::{any, fmt, mem, ptr, slice}; |
9 pub mod memory; | 9 // Type aliases: |
10 | 10 |
11 /// Information about the PAM implementation you're using right now. | 11 // Memory management |
12 /// | 12 |
13 /// This module contains constants and values that can be used at build-script, | 13 /// A pointer-to-pointer-to-message container for PAM's conversation callback. |
14 /// compile, and run time to determine what PAM implementation you're using. | 14 /// |
15 /// | 15 /// The PAM conversation callback requires a pointer to a pointer of |
16 /// ## Always available | 16 /// `pam_message`s. Linux-PAM handles this differently than all other |
17 /// | 17 /// PAM implementations (including the X/SSO PAM standard). |
18 /// [`PamImpl::CURRENT`] will tell you what version of PAM you're using. | 18 /// |
19 /// It can be imported in any Rust code, from build scripts to runtime. | 19 /// X/SSO appears to specify a pointer-to-pointer-to-array: |
20 /// | 20 /// |
21 /// ## Compile time | 21 /// ```text |
22 /// | 22 /// points to ┌────────────┐ ╔═ Message[] ═╗ |
23 /// Use [`enable_pam_impl_cfg`] in your `build.rs` to generate custom `#[cfg]`s | 23 /// messages ┄┄┄┄┄┄┄┄┄┄> │ *messages ┄┼┄┄┄┄┄> ║ style ║ |
24 /// for conditional compilation based on PAM implementation. | 24 /// └────────────┘ ║ data ┄┄┄┄┄┄┄╫┄┄> ... |
25 /// | 25 /// ╟─────────────╢ |
26 /// This will set the current `pam_impl` as well as registering all known | 26 /// ║ style ║ |
27 /// PAM implementations with `rustc-check-cfg` to get cfg-checking. | 27 /// ║ data ┄┄┄┄┄┄┄╫┄┄> ... |
28 /// | 28 /// ╟─────────────╢ |
29 /// The names that appear in the `cfg` variables are the same as the values | 29 /// ║ ... ║ |
30 /// in the [`PamImpl`] enum. | |
31 /// | |
32 /// ``` | 30 /// ``` |
33 /// #[cfg(pam_impl = "OpenPam")] | 31 /// |
34 /// fn openpam_specific_func(handle: *const libpam_sys::pam_handle) { | 32 /// whereas Linux-PAM uses an `**argv`-style pointer-to-array-of-pointers: |
35 /// let environ = libpam_sys::pam_getenvlist(handle); | 33 /// |
36 /// // ... | 34 /// ```text |
37 /// libpam_sys::openpam_free_envlist() | 35 /// points to ┌──────────────┐ ╔═ Message ═╗ |
38 /// } | 36 /// messages ┄┄┄┄┄┄┄┄┄┄> │ messages[0] ┄┼┄┄┄┄> ║ style ║ |
39 /// | 37 /// │ messages[1] ┄┼┄┄┄╮ ║ data ┄┄┄┄┄╫┄┄> ... |
40 /// // This will give you a warning since "UnknownImpl" is not in the cfg. | 38 /// │ ... │ ┆ ╚═══════════╝ |
41 /// #[cfg(not(pam_impl = "UnknownImpl"))] | 39 /// ┆ |
42 /// fn do_something() { | 40 /// ┆ ╔═ Message ═╗ |
43 /// // ... | 41 /// ╰┄┄> ║ style ║ |
44 /// } | 42 /// ║ data ┄┄┄┄┄╫┄┄> ... |
43 /// ╚═══════════╝ | |
45 /// ``` | 44 /// ``` |
46 /// | 45 /// |
47 /// The [`pam_impl_name!`] macro will expand to this same value, currently | 46 /// Because the `messages` remain owned by the application which calls into PAM, |
48 #[doc = concat!("`", env!("LIBPAMSYS_IMPL"), "`.")] | 47 /// we can solve this with One Simple Trick: make the intermediate list point |
49 pub mod pam_impl; | 48 /// into the same array: |
50 #[doc(inline)] | 49 /// |
51 pub use pam_impl::*; | 50 /// ```text |
51 /// points to ┌──────────────┐ ╔═ Message[] ═╗ | |
52 /// messages ┄┄┄┄┄┄┄┄┄┄> │ messages[0] ┄┼┄┄┄┄> ║ style ║ | |
53 /// │ messages[1] ┄┼┄┄╮ ║ data ┄┄┄┄┄┄┄╫┄┄> ... | |
54 /// │ ... │ ┆ ╟─────────────╢ | |
55 /// ╰┄> ║ style ║ | |
56 /// ║ data ┄┄┄┄┄┄┄╫┄┄> ... | |
57 /// ╟─────────────╢ | |
58 /// ║ ... ║ | |
59 /// | |
60 /// ``` | |
61 #[derive(Debug)] | |
62 pub struct PtrPtrVec<T> { | |
63 data: Vec<T>, | |
64 pointers: Vec<*const T>, | |
65 } | |
66 | |
67 // Since this is a wrapper around a Vec with no dangerous functionality*, | |
68 // this can be Send and Sync provided the original Vec is. | |
69 // | |
70 // * It will only become unsafe when the user dereferences a pointer or sends it | |
71 // to an unsafe function. | |
72 unsafe impl<T> Send for PtrPtrVec<T> where Vec<T>: Send {} | |
73 unsafe impl<T> Sync for PtrPtrVec<T> where Vec<T>: Sync {} | |
74 | |
75 impl<T> PtrPtrVec<T> { | |
76 /// Takes ownership of the given Vec and creates a vec of pointers to it. | |
77 pub fn new(data: Vec<T>) -> Self { | |
78 let start = data.as_ptr(); | |
79 // We do this slightly tricky little dance to satisfy Miri: | |
80 // | |
81 // A pointer extracted from a reference can only legally access | |
82 // that reference's memory. This means that if we say: | |
83 // pointers[0] = &data[0] as *const T; | |
84 // we can't traverse through pointers[0] to reach data[1], | |
85 // we can only use pointers[1]. | |
86 // | |
87 // However, if we use the start-of-vec pointer from the `data` vector, | |
88 // its "provenance"* is valid for the entire array (even if the address | |
89 // of the pointer is the same). This avoids some behavior which is | |
90 // technically undefined. While the CPU sees no difference between | |
91 // those two pointers, the compiler is allowed to make optimizations | |
92 // based on that provenance (even if, in this case, it isn't likely | |
93 // to do so). | |
94 // | |
95 // data.as_ptr() points here, and is valid for the whole Vec. | |
96 // ┃ | |
97 // ┠─────────────────╮ | |
98 // ┌─────┬─────┬─────┐ | |
99 // data │ [0] │ [1] │ [2] │ | |
100 // └─────┴─────┴─────┘ | |
101 // ┠─────╯ ┊ | |
102 // ┃ ┊ ┊ | |
103 // (&data[0] as *const T) points to the same place, but is valid | |
104 // only for that 0th element. | |
105 // ┊ ┊ | |
106 // ┠─────╯ | |
107 // ┃ | |
108 // (&data[1] as *const T) points here, and is only valid | |
109 // for that element. | |
110 // | |
111 // We only have to do this for pointers[0] because only that pointer | |
112 // is used for accessing elements other than data[0] (in XSSO). | |
113 // | |
114 // * "provenance" is kind of like if every pointer in your program | |
115 // remembered where it came from and, based on that, it had an implied | |
116 // memory range it was valid for, separate from its address. | |
117 // https://doc.rust-lang.org/std/ptr/#provenance | |
118 // (It took a long time for me to understand this.) | |
119 let mut pointers = Vec::with_capacity(data.len()); | |
120 // Ensure the 0th pointer has provenance from the entire vec | |
121 // (even though it's numerically identical to &data[0] as *const T). | |
122 pointers.push(start); | |
123 // The 1st and everything thereafter only need to have the provenance | |
124 // of their own memory. | |
125 pointers.extend(data[1..].iter().map(|r| r as *const T)); | |
126 Self { data, pointers } | |
127 } | |
128 | |
129 /// Gives you back your Vec. | |
130 pub fn into_inner(self) -> Vec<T> { | |
131 self.data | |
132 } | |
133 | |
134 /// Gets a pointer-to-pointer suitable for passing into the Conversation. | |
135 pub fn as_ptr<Dest>(&self) -> *const *const Dest { | |
136 Self::assert_size::<Dest>(); | |
137 self.pointers.as_ptr().cast::<*const Dest>() | |
138 } | |
139 | |
140 /// Iterates over a Linux-PAM–style pointer-to-array-of-pointers. | |
141 /// | |
142 /// # Safety | |
143 /// | |
144 /// `ptr_ptr` must be a valid pointer to an array of pointers, | |
145 /// there must be at least `count` valid pointers in the array, | |
146 /// and each pointer in that array must point to a valid `T`. | |
147 #[deprecated = "use [`Self::iter_over`] instead, unless you really need this specific version"] | |
148 #[allow(dead_code)] | |
149 pub unsafe fn iter_over_linux<'a, Src>( | |
150 ptr_ptr: *const *const Src, | |
151 count: usize, | |
152 ) -> impl Iterator<Item = &'a T> | |
153 where | |
154 T: 'a, | |
155 { | |
156 Self::assert_size::<Src>(); | |
157 slice::from_raw_parts(ptr_ptr.cast::<&T>(), count) | |
158 .iter() | |
159 .copied() | |
160 } | |
161 | |
162 /// Iterates over an X/SSO–style pointer-to-pointer-to-array. | |
163 /// | |
164 /// # Safety | |
165 /// | |
166 /// You must pass a valid pointer to a valid pointer to an array, | |
167 /// there must be at least `count` elements in the array, | |
168 /// and each value in that array must be a valid `T`. | |
169 #[deprecated = "use [`Self::iter_over`] instead, unless you really need this specific version"] | |
170 #[allow(dead_code)] | |
171 pub unsafe fn iter_over_xsso<'a, Src>( | |
172 ptr_ptr: *const *const Src, | |
173 count: usize, | |
174 ) -> impl Iterator<Item = &'a T> | |
175 where | |
176 T: 'a, | |
177 { | |
178 Self::assert_size::<Src>(); | |
179 slice::from_raw_parts(*ptr_ptr.cast(), count).iter() | |
180 } | |
181 | |
182 /// Iterates over a PAM message list appropriate to your system's impl. | |
183 /// | |
184 /// This selects the correct pointer/array structure to use for a message | |
185 /// that was given to you by your system. | |
186 /// | |
187 /// # Safety | |
188 /// | |
189 /// `ptr_ptr` must point to a valid message list, there must be at least | |
190 /// `count` messages in the list, and all messages must be a valid `Src`. | |
191 #[allow(deprecated)] | |
192 pub unsafe fn iter_over<'a, Src>( | |
193 ptr_ptr: *const *const Src, | |
194 count: usize, | |
195 ) -> impl Iterator<Item = &'a T> | |
196 where | |
197 T: 'a, | |
198 { | |
199 #[cfg(pam_impl = "LinuxPam")] | |
200 return Self::iter_over_linux(ptr_ptr, count); | |
201 #[cfg(not(pam_impl = "LinuxPam"))] | |
202 return Self::iter_over_xsso(ptr_ptr, count); | |
203 } | |
204 | |
205 fn assert_size<That>() { | |
206 assert_eq!( | |
207 mem::size_of::<T>(), | |
208 mem::size_of::<That>(), | |
209 "type {t} is not the size of {that}", | |
210 t = any::type_name::<T>(), | |
211 that = any::type_name::<That>(), | |
212 ); | |
213 } | |
214 } | |
215 | |
216 /// Error returned when attempting to allocate a buffer that is too big. | |
217 /// | |
218 /// This is specifically used in [`OwnedBinaryPayload`] when you try to allocate | |
219 /// a message larger than 2<sup>32</sup> bytes. | |
220 #[derive(Debug, PartialEq)] | |
221 pub struct TooBigError { | |
222 pub size: usize, | |
223 pub max: usize, | |
224 } | |
225 | |
226 impl Error for TooBigError {} | |
227 | |
228 impl fmt::Display for TooBigError { | |
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
230 write!( | |
231 f, | |
232 "can't allocate a message of {size} bytes (max {max})", | |
233 size = self.size, | |
234 max = self.max | |
235 ) | |
236 } | |
237 } | |
238 | |
239 /// A trait wrapping memory management. | |
240 /// | |
241 /// This is intended to allow you to bring your own allocator for | |
242 /// [`OwnedBinaryPayload`]s. | |
243 /// | |
244 /// For an implementation example, see the implementation of this trait | |
245 /// for [`Vec`]. | |
246 #[allow(clippy::wrong_self_convention)] | |
247 pub trait Buffer { | |
248 /// Allocates a buffer of `len` elements, filled with the default. | |
249 fn allocate(len: usize) -> Self; | |
250 | |
251 fn as_ptr(this: &Self) -> *const u8; | |
252 | |
253 /// Returns a slice view of `size` elements of the given memory. | |
254 /// | |
255 /// # Safety | |
256 /// | |
257 /// The caller must not request more elements than are allocated. | |
258 unsafe fn as_mut_slice(this: &mut Self, len: usize) -> &mut [u8]; | |
259 | |
260 /// Consumes this ownership and returns a pointer to the start of the arena. | |
261 fn into_ptr(this: Self) -> NonNull<u8>; | |
262 | |
263 /// "Adopts" the memory at the given pointer, taking it under management. | |
264 /// | |
265 /// Running the operation: | |
266 /// | |
267 /// ``` | |
268 /// # use libpam_sys_helpers::Buffer; | |
269 /// # fn test<T: Default, OwnerType: Buffer>(bytes: usize) { | |
270 /// let owner = OwnerType::allocate(bytes); | |
271 /// let ptr = OwnerType::into_ptr(owner); | |
272 /// let owner = unsafe { OwnerType::from_ptr(ptr, bytes) }; | |
273 /// # } | |
274 /// ``` | |
275 /// | |
276 /// must be a no-op. | |
277 /// | |
278 /// # Safety | |
279 /// | |
280 /// The pointer must be valid, and the caller must provide the exact size | |
281 /// of the given arena. | |
282 unsafe fn from_ptr(ptr: NonNull<u8>, bytes: usize) -> Self; | |
283 } | |
284 | |
285 impl Buffer for Vec<u8> { | |
286 fn allocate(bytes: usize) -> Self { | |
287 vec![0; bytes] | |
288 } | |
289 | |
290 fn as_ptr(this: &Self) -> *const u8 { | |
291 Vec::as_ptr(this) | |
292 } | |
293 | |
294 unsafe fn as_mut_slice(this: &mut Self, bytes: usize) -> &mut [u8] { | |
295 &mut this[..bytes] | |
296 } | |
297 | |
298 fn into_ptr(this: Self) -> NonNull<u8> { | |
299 let mut me = ManuallyDrop::new(this); | |
300 // SAFETY: a Vec is guaranteed to have a nonzero pointer. | |
301 unsafe { NonNull::new_unchecked(me.as_mut_ptr()) } | |
302 } | |
303 | |
304 unsafe fn from_ptr(ptr: NonNull<u8>, bytes: usize) -> Self { | |
305 Vec::from_raw_parts(ptr.as_ptr(), bytes, bytes) | |
306 } | |
307 } | |
308 | |
309 /// The structure of the "binary message" payload for the `PAM_BINARY_PROMPT` | |
310 /// extension from Linux-PAM. | |
311 pub struct BinaryPayload { | |
312 /// The total byte size of the message, including this header, | |
313 /// as u32 in network byte order (big endian). | |
314 pub total_bytes_u32be: [u8; 4], | |
315 /// A tag used to provide some kind of hint as to what the data is. | |
316 /// Its meaning is undefined. | |
317 pub data_type: u8, | |
318 /// Where the data itself would start, used as a marker to make this | |
319 /// not [`Unpin`] (since it is effectively an intrusive data structure | |
320 /// pointing to immediately after itself). | |
321 pub _marker: PhantomData<PhantomPinned>, | |
322 } | |
323 | |
324 impl BinaryPayload { | |
325 /// The most data it's possible to put into a [`BinaryPayload`]. | |
326 pub const MAX_SIZE: usize = (u32::MAX - 5) as usize; | |
327 | |
328 /// Fills in the provided buffer with the given data. | |
329 /// | |
330 /// This uses [`copy_from_slice`](slice::copy_from_slice) internally, | |
331 /// so `buf` must be exactly 5 bytes longer than `data`, or this function | |
332 /// will panic. | |
333 pub fn fill(buf: &mut [u8], data: &[u8], data_type: u8) { | |
334 let ptr: *mut Self = buf.as_mut_ptr().cast(); | |
335 // SAFETY: We're given a slice, which always has a nonzero pointer. | |
336 let me = unsafe { ptr.as_mut().unwrap_unchecked() }; | |
337 me.total_bytes_u32be = u32::to_be_bytes(buf.len() as u32); | |
338 me.data_type = data_type; | |
339 buf[5..].copy_from_slice(data) | |
340 } | |
341 | |
342 /// The total storage needed for the message, including header. | |
343 pub unsafe fn total_bytes(this: *const Self) -> usize { | |
344 let header = this.as_ref().unwrap_unchecked(); | |
345 u32::from_be_bytes(header.total_bytes_u32be) as usize | |
346 } | |
347 | |
348 /// Gets the total byte buffer of the BinaryMessage stored at the pointer. | |
349 /// | |
350 /// The returned data slice is borrowed from where the pointer points to. | |
351 /// | |
352 /// # Safety | |
353 /// | |
354 /// - The pointer must point to a valid `BinaryPayload`. | |
355 /// - The borrowed data must not outlive the pointer's validity. | |
356 pub unsafe fn buffer_of<'a>(ptr: *const Self) -> &'a [u8] { | |
357 slice::from_raw_parts(ptr.cast(), Self::total_bytes(ptr).max(5)) | |
358 } | |
359 | |
360 /// Gets the contents of the BinaryMessage stored at the given pointer. | |
361 /// | |
362 /// The returned data slice is borrowed from where the pointer points to. | |
363 /// This is a cheap operation and doesn't do *any* copying. | |
364 /// | |
365 /// We don't take a `&self` reference here because accessing beyond | |
366 /// the range of the `Self` data (i.e., beyond the 5 bytes of `self`) | |
367 /// is undefined behavior. Instead, you have to pass a raw pointer | |
368 /// directly to the data. | |
369 /// | |
370 /// # Safety | |
371 /// | |
372 /// - The pointer must point to a valid `BinaryPayload`. | |
373 /// - The borrowed data must not outlive the pointer's validity. | |
374 pub unsafe fn contents<'a>(ptr: *const Self) -> (&'a [u8], u8) { | |
375 let header: &Self = ptr.as_ref().unwrap_unchecked(); | |
376 (&Self::buffer_of(ptr)[5..], header.data_type) | |
377 } | |
378 | |
379 /// Zeroes out the data of this payload. | |
380 /// | |
381 /// # Safety | |
382 /// | |
383 /// - The pointer must point to a valid `BinaryPayload`. | |
384 /// - The binary payload must not be used in the future, | |
385 /// since its length metadata is gone and so its buffer is unknown. | |
386 pub unsafe fn zero(ptr: *mut Self) { | |
387 let size = Self::total_bytes(ptr); | |
388 let ptr: *mut u8 = ptr.cast(); | |
389 for x in 0..size { | |
390 ptr::write_volatile(ptr.byte_add(x), mem::zeroed()) | |
391 } | |
392 } | |
393 } | |
394 | |
395 /// A binary message owned by some storage. | |
396 /// | |
397 /// This is an owned, memory-managed version of [`BinaryPayload`]. | |
398 /// The `O` type manages the memory where the payload lives. | |
399 /// [`Vec<u8>`] is one such manager and can be used when ownership | |
400 /// of the data does not need to transit through PAM. | |
401 #[derive(Debug)] | |
402 pub struct OwnedBinaryPayload<Owner: Buffer>(Owner); | |
403 | |
404 impl<O: Buffer> OwnedBinaryPayload<O> { | |
405 /// Allocates a new OwnedBinaryPayload. | |
406 /// | |
407 /// This will return a [`TooBigError`] if you try to allocate too much | |
408 /// (more than [`BinaryPayload::MAX_SIZE`]). | |
409 pub fn new(data: &[u8], type_: u8) -> Result<Self, TooBigError> { | |
410 let total_len: u32 = (data.len() + 5).try_into().map_err(|_| TooBigError { | |
411 size: data.len(), | |
412 max: BinaryPayload::MAX_SIZE, | |
413 })?; | |
414 let total_len = total_len as usize; | |
415 let mut buf = O::allocate(total_len); | |
416 // SAFETY: We just allocated this exact size. | |
417 BinaryPayload::fill( | |
418 unsafe { Buffer::as_mut_slice(&mut buf, total_len) }, | |
419 data, | |
420 type_, | |
421 ); | |
422 Ok(Self(buf)) | |
423 } | |
424 | |
425 /// The contents of the buffer. | |
426 pub fn contents(&self) -> (&[u8], u8) { | |
427 unsafe { BinaryPayload::contents(self.as_ptr()) } | |
428 } | |
429 | |
430 /// The total bytes needed to store this, including the header. | |
431 pub fn total_bytes(&self) -> usize { | |
432 unsafe { BinaryPayload::buffer_of(Buffer::as_ptr(&self.0).cast()).len() } | |
433 } | |
434 | |
435 /// Unwraps this into the raw storage backing it. | |
436 pub fn into_inner(self) -> O { | |
437 self.0 | |
438 } | |
439 | |
440 /// Gets a const pointer to the start of the message's buffer. | |
441 pub fn as_ptr(&self) -> *const BinaryPayload { | |
442 Buffer::as_ptr(&self.0).cast() | |
443 } | |
444 | |
445 /// Consumes ownership of this message and converts it to a raw pointer | |
446 /// to the start of the message. | |
447 /// | |
448 /// To clean this up, you should eventually pass it into [`Self::from_ptr`] | |
449 /// with the same `O` ownership type. | |
450 pub fn into_ptr(self) -> NonNull<BinaryPayload> { | |
451 Buffer::into_ptr(self.0).cast() | |
452 } | |
453 | |
454 /// Takes ownership of the given pointer. | |
455 /// | |
456 /// # Safety | |
457 /// | |
458 /// You must provide a valid pointer, allocated by (or equivalent to one | |
459 /// allocated by) [`Self::new`]. For instance, passing a pointer allocated | |
460 /// by `malloc` to `OwnedBinaryPayload::<Vec<u8>>::from_ptr` is not allowed. | |
461 pub unsafe fn from_ptr(ptr: NonNull<BinaryPayload>) -> Self { | |
462 Self(O::from_ptr( | |
463 ptr.cast(), | |
464 BinaryPayload::total_bytes(ptr.as_ptr()), | |
465 )) | |
466 } | |
467 } | |
468 | |
469 #[cfg(test)] | |
470 mod tests { | |
471 use super::*; | |
472 use std::ptr; | |
473 | |
474 type VecPayload = OwnedBinaryPayload<Vec<u8>>; | |
475 | |
476 #[test] | |
477 fn test_binary_payload() { | |
478 let simple_message = &[0u8, 0, 0, 16, 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | |
479 let empty = &[0u8; 5]; | |
480 | |
481 assert_eq!((&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..], 0xff), unsafe { | |
482 BinaryPayload::contents(simple_message.as_ptr().cast()) | |
483 }); | |
484 assert_eq!((&[][..], 0x00), unsafe { | |
485 BinaryPayload::contents(empty.as_ptr().cast()) | |
486 }); | |
487 } | |
488 | |
489 #[test] | |
490 fn test_owned_binary_payload() { | |
491 let (data, typ) = ( | |
492 &[0, 1, 1, 8, 9, 9, 9, 8, 8, 1, 9, 9, 9, 1, 1, 9, 7, 2, 5, 3][..], | |
493 112, | |
494 ); | |
495 let payload = VecPayload::new(data, typ).unwrap(); | |
496 assert_eq!((data, typ), payload.contents()); | |
497 let ptr = payload.into_ptr(); | |
498 let payload = unsafe { VecPayload::from_ptr(ptr) }; | |
499 assert_eq!((data, typ), payload.contents()); | |
500 } | |
501 | |
502 #[test] | |
503 #[ignore] | |
504 fn test_owned_too_big() { | |
505 let data = vec![0xFFu8; 0x1_0000_0001]; | |
506 assert_eq!( | |
507 TooBigError { | |
508 max: 0xffff_fffa, | |
509 size: 0x1_0000_0001 | |
510 }, | |
511 VecPayload::new(&data, 5).unwrap_err() | |
512 ) | |
513 } | |
514 | |
515 #[cfg(debug_assertions)] | |
516 #[test] | |
517 #[should_panic] | |
518 fn test_new_wrong_size() { | |
519 let bad_vec = vec![0; 19]; | |
520 let msg = PtrPtrVec::new(bad_vec); | |
521 let _ = msg.as_ptr::<u64>(); | |
522 } | |
523 | |
524 #[allow(deprecated)] | |
525 #[test] | |
526 #[should_panic] | |
527 fn test_iter_xsso_wrong_size() { | |
528 unsafe { | |
529 let _ = PtrPtrVec::<u8>::iter_over_xsso::<f64>(ptr::null(), 1); | |
530 } | |
531 } | |
532 | |
533 #[allow(deprecated)] | |
534 #[test] | |
535 #[should_panic] | |
536 fn test_iter_linux_wrong_size() { | |
537 unsafe { | |
538 let _ = PtrPtrVec::<u128>::iter_over_linux::<()>(ptr::null(), 1); | |
539 } | |
540 } | |
541 | |
542 #[allow(deprecated)] | |
543 #[test] | |
544 fn test_right_size() { | |
545 let good_vec = vec![(1u64, 2u64), (3, 4), (5, 6)]; | |
546 let ptr = good_vec.as_ptr(); | |
547 let msg = PtrPtrVec::new(good_vec); | |
548 let msg_ref: *const *const (i64, i64) = msg.as_ptr(); | |
549 assert_eq!(unsafe { *msg_ref }, ptr.cast()); | |
550 | |
551 let linux_result: Vec<(i64, i64)> = unsafe { PtrPtrVec::iter_over_linux(msg_ref, 3) } | |
552 .cloned() | |
553 .collect(); | |
554 let xsso_result: Vec<(i64, i64)> = unsafe { PtrPtrVec::iter_over_xsso(msg_ref, 3) } | |
555 .cloned() | |
556 .collect(); | |
557 assert_eq!(vec![(1, 2), (3, 4), (5, 6)], linux_result); | |
558 assert_eq!(vec![(1, 2), (3, 4), (5, 6)], xsso_result); | |
559 drop(msg) | |
560 } | |
561 | |
562 #[allow(deprecated)] | |
563 #[test] | |
564 fn test_iter_ptr_ptr() { | |
565 // These boxes are larger than a single pointer because we want to | |
566 // make sure they're not accidentally allocated adjacently | |
567 // in such a way that it's compatible with X/SSO. | |
568 // | |
569 // a pointer to (&str, i32) can be treated as a pointer to (&str). | |
570 #[repr(C)] | |
571 struct pair(&'static str, i32); | |
572 let boxes = vec![ | |
573 Box::new(pair("a", 1)), | |
574 Box::new(pair("b", 2)), | |
575 Box::new(pair("c", 3)), | |
576 Box::new(pair("D", 4)), | |
577 ]; | |
578 let ptr: *const *const &str = boxes.as_ptr().cast(); | |
579 let got: Vec<&str> = unsafe { PtrPtrVec::iter_over_linux(ptr, 4) } | |
580 .cloned() | |
581 .collect(); | |
582 assert_eq!(vec!["a", "b", "c", "D"], got); | |
583 | |
584 // On the other hand, we explicitly want these to be adjacent. | |
585 let nums = [-1i8, 2, 3]; | |
586 let ptr = nums.as_ptr(); | |
587 let got: Vec<u8> = unsafe { PtrPtrVec::iter_over_xsso(&ptr, 3) } | |
588 .cloned() | |
589 .collect(); | |
590 assert_eq!(vec![255, 2, 3], got); | |
591 } | |
592 } |