1#![cfg_attr(not(any(test, feature = "std")), no_std)]
4#![deny(unsafe_op_in_unsafe_fn)]
6
7use core::mem::MaybeUninit;
8
9pub unsafe trait Pod: Copy + 'static {}
36
37pub fn to_u8_slice<T>(slice: &[T]) -> &[MaybeUninit<u8>]
41where
42 T: Pod,
43{
44 unsafe {
46 core::slice::from_raw_parts(
47 slice.as_ptr() as *const MaybeUninit<u8>,
48 slice.len() * core::mem::size_of::<MaybeUninit<T>>(),
49 )
50 }
51}
52
53pub fn as_u8_slice<T>(x: &T) -> &[MaybeUninit<u8>]
57where
58 T: Pod,
59{
60 to_u8_slice(core::slice::from_ref(x))
61}
62
63pub unsafe fn to_u8_slice_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<u8>]
72where
73 T: Pod,
74{
75 unsafe {
77 core::slice::from_raw_parts_mut(
78 slice.as_mut_ptr() as *mut MaybeUninit<u8>,
79 slice.len() * core::mem::size_of::<MaybeUninit<T>>(),
80 )
81 }
82}
83
84pub unsafe fn as_u8_slice_mut<T>(x: &mut T) -> &mut [MaybeUninit<u8>]
92where
93 T: Pod,
94{
95 unsafe { to_u8_slice_mut(core::slice::from_mut(x)) }
96}
97
98pub fn zeroed<T>() -> T
100where
101 T: Pod,
102{
103 unsafe { core::mem::zeroed() }
105}
106
107struct PodTransmute<const N: usize, T> {
109 _t: core::marker::PhantomData<T>,
110}
111
112impl<const N: usize, T: Pod> PodTransmute<N, T> {
113 const CHECK: () = assert!(N == core::mem::size_of::<T>());
114 #[inline(always)]
115 fn transmute_array(x: &[u8; N]) -> T {
116 #[allow(clippy::let_unit_value)]
118 let _ = Self::CHECK;
119
120 assert_eq!(N, core::mem::size_of::<T>());
123
124 unsafe { core::mem::transmute_copy(x) }
129 }
130}
131
132pub fn from_array<const N: usize, T: Pod>(x: &[u8; N]) -> T {
134 PodTransmute::transmute_array(x)
135}
136
137#[cfg(feature = "std")]
138pub trait ReadExt {
139 fn read_pod<T: Pod>(&mut self) -> std::io::Result<Option<T>>;
144}
145
146#[cfg(feature = "std")]
147impl<R> ReadExt for R
148where
149 R: std::io::Read,
150{
151 fn read_pod<T: Pod>(&mut self) -> std::io::Result<Option<T>> {
152 let mut dst = std::mem::MaybeUninit::<T>::uninit();
153 let dst_slice = unsafe { as_u8_slice_mut(&mut dst) };
155 dst_slice.fill(MaybeUninit::new(0u8));
156 let dst_slice = unsafe { dst_slice.assume_init_mut() };
158
159 let mut total_read = 0;
164 while total_read != dst_slice.len() {
165 let nread = self.read(&mut dst_slice[total_read..])?;
166 if nread == 0 {
167 return if total_read != 0 {
168 Err(std::io::Error::new(
169 std::io::ErrorKind::UnexpectedEof,
170 "EOF in middle of value",
171 ))
172 } else {
173 Ok(None)
174 };
175 }
176 total_read += nread;
177 }
178 Ok(Some(unsafe { dst.assume_init() }))
181 }
182}
183
184#[cfg(feature = "std")]
185pub trait WriteExt {
186 fn write_pod<T: Pod>(&mut self, val: &T) -> std::io::Result<()>;
188}
189
190#[cfg(feature = "std")]
191impl<W> WriteExt for W
192where
193 W: std::os::fd::AsRawFd,
197{
198 fn write_pod<T: Pod>(&mut self, val: &T) -> std::io::Result<()> {
199 let bytes = as_u8_slice(val);
200 let mut total_written = 0;
201 while total_written != bytes.len() {
202 use linux_syscall::Result64 as _;
216 let nwritten = unsafe {
217 linux_syscall::syscall!(
218 linux_syscall::SYS_write,
219 self.as_raw_fd(),
220 bytes.as_ptr().add(total_written),
221 bytes.len() - total_written,
222 )
223 }
224 .try_u64()
225 .map_err(|x| std::io::Error::from_raw_os_error(x.get().into()))?;
226 total_written += usize::try_from(nwritten).unwrap();
227 }
228 Ok(())
229 }
230}
231
232unsafe impl Pod for u8 {}
234unsafe impl Pod for u16 {}
235unsafe impl Pod for u32 {}
236unsafe impl Pod for u64 {}
237unsafe impl Pod for i8 {}
238unsafe impl Pod for i16 {}
239unsafe impl Pod for i32 {}
240unsafe impl Pod for i64 {}
241unsafe impl Pod for isize {}
242unsafe impl Pod for usize {}
243
244unsafe impl<T> Pod for core::mem::MaybeUninit<T> where T: Pod {}
251unsafe impl<T, const N: usize> Pod for [T; N] where T: Pod {}
252
253unsafe impl Pod for libc::Dl_info {}
255unsafe impl Pod for libc::Elf32_Chdr {}
256unsafe impl Pod for libc::Elf32_Ehdr {}
257unsafe impl Pod for libc::Elf32_Phdr {}
258unsafe impl Pod for libc::Elf32_Shdr {}
259unsafe impl Pod for libc::Elf32_Sym {}
260unsafe impl Pod for libc::Elf64_Chdr {}
261unsafe impl Pod for libc::Elf64_Ehdr {}
262unsafe impl Pod for libc::Elf64_Phdr {}
263unsafe impl Pod for libc::Elf64_Shdr {}
264unsafe impl Pod for libc::Elf64_Sym {}
265unsafe impl Pod for libc::__c_anonymous_sockaddr_can_j1939 {}
266unsafe impl Pod for libc::__c_anonymous_sockaddr_can_tp {}
267unsafe impl Pod for libc::__exit_status {}
268unsafe impl Pod for libc::__timeval {}
269unsafe impl Pod for libc::_libc_fpstate {}
270unsafe impl Pod for libc::_libc_fpxreg {}
271unsafe impl Pod for libc::_libc_xmmreg {}
272unsafe impl Pod for libc::addrinfo {}
273unsafe impl Pod for libc::aiocb {}
275unsafe impl Pod for libc::arpd_request {}
276unsafe impl Pod for libc::arphdr {}
277unsafe impl Pod for libc::arpreq {}
278unsafe impl Pod for libc::arpreq_old {}
279unsafe impl Pod for libc::can_filter {}
280unsafe impl Pod for libc::can_frame {}
281unsafe impl Pod for libc::canfd_frame {}
282unsafe impl Pod for libc::cmsghdr {}
283unsafe impl Pod for libc::cpu_set_t {}
284unsafe impl Pod for libc::dirent {}
285unsafe impl Pod for libc::dirent64 {}
286unsafe impl Pod for libc::dl_phdr_info {}
287unsafe impl Pod for libc::dqblk {}
288unsafe impl Pod for libc::epoll_event {}
289unsafe impl Pod for libc::fanotify_event_metadata {}
290unsafe impl Pod for libc::fanotify_response {}
291unsafe impl Pod for libc::fd_set {}
292unsafe impl Pod for libc::ff_condition_effect {}
293unsafe impl Pod for libc::ff_constant_effect {}
294unsafe impl Pod for libc::ff_effect {}
295unsafe impl Pod for libc::ff_envelope {}
296unsafe impl Pod for libc::ff_periodic_effect {}
297unsafe impl Pod for libc::ff_ramp_effect {}
298unsafe impl Pod for libc::ff_replay {}
299unsafe impl Pod for libc::ff_rumble_effect {}
300unsafe impl Pod for libc::ff_trigger {}
301unsafe impl Pod for libc::flock {}
302unsafe impl Pod for libc::flock64 {}
303unsafe impl Pod for libc::fsid_t {}
304unsafe impl Pod for libc::genlmsghdr {}
305unsafe impl Pod for libc::glob64_t {}
306unsafe impl Pod for libc::glob_t {}
307unsafe impl Pod for libc::group {}
308unsafe impl Pod for libc::hostent {}
309unsafe impl Pod for libc::if_nameindex {}
310unsafe impl Pod for libc::ifaddrs {}
311unsafe impl Pod for libc::in6_addr {}
312unsafe impl Pod for libc::in6_pktinfo {}
313unsafe impl Pod for libc::in6_rtmsg {}
314unsafe impl Pod for libc::in_addr {}
315unsafe impl Pod for libc::in_pktinfo {}
316unsafe impl Pod for libc::inotify_event {}
317unsafe impl Pod for libc::input_absinfo {}
318unsafe impl Pod for libc::input_event {}
319unsafe impl Pod for libc::input_id {}
320unsafe impl Pod for libc::input_keymap_entry {}
321unsafe impl Pod for libc::input_mask {}
322unsafe impl Pod for libc::iovec {}
323unsafe impl Pod for libc::ip_mreq {}
324unsafe impl Pod for libc::ip_mreq_source {}
325unsafe impl Pod for libc::ip_mreqn {}
326unsafe impl Pod for libc::ipc_perm {}
327unsafe impl Pod for libc::ipv6_mreq {}
328unsafe impl Pod for libc::itimerspec {}
329unsafe impl Pod for libc::itimerval {}
330unsafe impl Pod for libc::lconv {}
331unsafe impl Pod for libc::linger {}
332unsafe impl Pod for libc::mallinfo {}
333unsafe impl Pod for libc::max_align_t {}
334unsafe impl Pod for libc::mcontext_t {}
335unsafe impl Pod for libc::mmsghdr {}
336unsafe impl Pod for libc::mntent {}
337unsafe impl Pod for libc::mq_attr {}
338unsafe impl Pod for libc::msghdr {}
339unsafe impl Pod for libc::msginfo {}
340unsafe impl Pod for libc::msqid_ds {}
341unsafe impl Pod for libc::nl_mmap_hdr {}
342unsafe impl Pod for libc::nl_mmap_req {}
343unsafe impl Pod for libc::nl_pktinfo {}
344unsafe impl Pod for libc::nlattr {}
345unsafe impl Pod for libc::nlmsgerr {}
346unsafe impl Pod for libc::nlmsghdr {}
347unsafe impl Pod for libc::ntptimeval {}
348unsafe impl Pod for libc::packet_mreq {}
349unsafe impl Pod for libc::passwd {}
350unsafe impl Pod for libc::pollfd {}
351unsafe impl Pod for libc::posix_spawn_file_actions_t {}
352unsafe impl Pod for libc::posix_spawnattr_t {}
353unsafe impl Pod for libc::protoent {}
354unsafe impl Pod for libc::pthread_attr_t {}
355unsafe impl Pod for libc::pthread_cond_t {}
356unsafe impl Pod for libc::pthread_condattr_t {}
357unsafe impl Pod for libc::pthread_mutex_t {}
358unsafe impl Pod for libc::pthread_mutexattr_t {}
359unsafe impl Pod for libc::pthread_rwlock_t {}
360unsafe impl Pod for libc::pthread_rwlockattr_t {}
361unsafe impl Pod for libc::regex_t {}
362unsafe impl Pod for libc::regmatch_t {}
363unsafe impl Pod for libc::rlimit {}
364unsafe impl Pod for libc::rlimit64 {}
365unsafe impl Pod for libc::rtentry {}
366unsafe impl Pod for libc::rusage {}
367unsafe impl Pod for libc::sched_param {}
368unsafe impl Pod for libc::sem_t {}
369unsafe impl Pod for libc::sembuf {}
370unsafe impl Pod for libc::servent {}
371unsafe impl Pod for libc::shmid_ds {}
372unsafe impl Pod for libc::sigaction {}
373unsafe impl Pod for libc::sigevent {}
374unsafe impl Pod for libc::siginfo_t {}
375unsafe impl Pod for libc::signalfd_siginfo {}
376unsafe impl Pod for libc::sigset_t {}
377unsafe impl Pod for libc::sigval {}
378unsafe impl Pod for libc::sock_extended_err {}
379unsafe impl Pod for libc::sockaddr {}
380unsafe impl Pod for libc::sockaddr_alg {}
381unsafe impl Pod for libc::sockaddr_can {}
382unsafe impl Pod for libc::sockaddr_in {}
383unsafe impl Pod for libc::sockaddr_in6 {}
384unsafe impl Pod for libc::sockaddr_ll {}
385unsafe impl Pod for libc::sockaddr_nl {}
386unsafe impl Pod for libc::sockaddr_storage {}
387unsafe impl Pod for libc::sockaddr_un {}
388unsafe impl Pod for libc::sockaddr_vm {}
389unsafe impl Pod for libc::spwd {}
390unsafe impl Pod for libc::stack_t {}
391unsafe impl Pod for libc::stat {}
392unsafe impl Pod for libc::stat64 {}
393unsafe impl Pod for libc::statfs {}
394unsafe impl Pod for libc::statfs64 {}
395unsafe impl Pod for libc::statvfs {}
396unsafe impl Pod for libc::statvfs64 {}
397unsafe impl Pod for libc::statx {}
398unsafe impl Pod for libc::statx_timestamp {}
399unsafe impl Pod for libc::sysinfo {}
400unsafe impl Pod for libc::termios {}
401unsafe impl Pod for libc::termios2 {}
402unsafe impl Pod for libc::timespec {}
403unsafe impl Pod for libc::timeval {}
404unsafe impl Pod for libc::timex {}
405unsafe impl Pod for libc::tm {}
406unsafe impl Pod for libc::tms {}
407unsafe impl Pod for libc::ucontext_t {}
408unsafe impl Pod for libc::ucred {}
409unsafe impl Pod for libc::uinput_abs_setup {}
410unsafe impl Pod for libc::uinput_ff_erase {}
411unsafe impl Pod for libc::uinput_ff_upload {}
412unsafe impl Pod for libc::uinput_setup {}
413unsafe impl Pod for libc::uinput_user_dev {}
414unsafe impl Pod for libc::user {}
415unsafe impl Pod for libc::user_fpregs_struct {}
416unsafe impl Pod for libc::user_regs_struct {}
417unsafe impl Pod for libc::utimbuf {}
418unsafe impl Pod for libc::utmpx {}
419unsafe impl Pod for libc::utsname {}
420unsafe impl Pod for libc::winsize {}
421unsafe impl Pod for libc::clone_args {}
422
423#[cfg(test)]
424mod test {
425 use super::*;
426 use std::io::Write;
427
428 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
430 #[repr(C)]
431 struct TypeWithPadding {
432 begin: u8,
433 middle: u64,
434 end: u8,
435 }
436 unsafe impl Pod for TypeWithPadding {}
437
438 struct NosyReader<R>(R);
441 impl<R> std::io::Read for NosyReader<R>
442 where
443 R: std::io::Read,
444 {
445 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
446 println!("overwriting {buf:?}");
449 self.0.read(buf)
450 }
451 }
452
453 struct SlowReader<R>(R);
455 impl<R> std::io::Read for SlowReader<R>
456 where
457 R: std::io::Read,
458 {
459 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
460 let buf = match buf.get_mut(0..1) {
462 Some(x) => x,
463 None => buf,
464 };
465 self.0.read(buf)
466 }
467 }
468
469 #[cfg(feature = "std")]
470 #[test]
471 fn test_read_pod() {
472 let (reader, mut writer) = std::io::pipe().unwrap();
473 let bytes = [0u8; std::mem::size_of::<TypeWithPadding>()];
474 writer.write_all(&bytes).unwrap();
475
476 let read_value =
477 super::ReadExt::read_pod::<TypeWithPadding>(&mut NosyReader(SlowReader(reader)))
478 .unwrap();
479 assert_eq!(
480 read_value,
481 Some(TypeWithPadding {
482 begin: 0,
483 middle: 0,
484 end: 0
485 })
486 );
487 }
488
489 #[cfg(all(feature = "std", not(miri)))]
491 #[test]
492 fn test_write_pod() {
493 let (reader, mut writer) = std::io::pipe().unwrap();
494 let val = TypeWithPadding {
495 begin: 1u8,
496 middle: 0x1122334455667788u64,
497 end: 2u8,
498 };
499 writer.write_pod(&val).unwrap();
500 let read_value =
501 super::ReadExt::read_pod::<TypeWithPadding>(&mut NosyReader(SlowReader(reader)))
502 .unwrap();
503 assert_eq!(read_value, Some(val));
504 }
505}