use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
ffi::CString,
rc::Rc,
string::String,
sync::Arc,
vec::Vec,
};
use core::{
ffi::CStr,
mem::ManuallyDrop,
num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
},
};
#[cfg(feature = "std")]
use std::{
ffi::{OsStr, OsString},
io::{IoSlice, IoSliceMut},
path::{Path, PathBuf},
};
mod mapped;
pub use mapped::*;
pub trait AsBytes {
fn as_bytes(&self) -> &[u8];
}
pub unsafe trait NoPrefixesBytes: AsBytes {}
pub unsafe trait OrderedBytes: AsBytes + Ord {}
macro_rules! as_bytes_for_integer_like_types {
($($type:ty),*) => {
$(
impl AsBytes for $type {
fn as_bytes(&self) -> &[u8] {
<$type as zerocopy::IntoBytes>::as_bytes(self)
}
}
unsafe impl NoPrefixesBytes for $type {}
impl AsBytes for [$type] {
fn as_bytes(&self) -> &[u8] {
<[$type] as zerocopy::IntoBytes>::as_bytes(self)
}
}
impl AsBytes for Vec<$type> {
fn as_bytes(&self) -> &[u8] {
<[$type] as zerocopy::IntoBytes>::as_bytes(self.as_slice())
}
}
)*
};
}
as_bytes_for_integer_like_types!(
u8,
i8,
u16,
i16,
u32,
i32,
u64,
i64,
u128,
i128,
usize,
isize,
char,
bool,
NonZeroU8,
NonZeroI8,
NonZeroU16,
NonZeroI16,
NonZeroU32,
NonZeroI32,
NonZeroU64,
NonZeroI64,
NonZeroU128,
NonZeroI128,
NonZeroUsize,
NonZeroIsize
);
unsafe impl OrderedBytes for u8 {}
macro_rules! as_bytes_for_integer_arrays {
($($type:ty),*) => {
$(
impl<const N: usize> AsBytes for [$type; N] {
fn as_bytes(&self) -> &[u8] {
<[$type; N] as zerocopy::IntoBytes>::as_bytes(self)
}
}
unsafe impl<const N: usize> NoPrefixesBytes for [$type; N] {}
)*
};
}
as_bytes_for_integer_arrays!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
unsafe impl<const N: usize> OrderedBytes for [u8; N] {}
unsafe impl OrderedBytes for [u8] {}
unsafe impl OrderedBytes for Vec<u8> {}
impl AsBytes for str {
fn as_bytes(&self) -> &[u8] {
str::as_bytes(self)
}
}
unsafe impl OrderedBytes for str {}
impl AsBytes for String {
fn as_bytes(&self) -> &[u8] {
str::as_bytes(self)
}
}
unsafe impl OrderedBytes for String {}
impl AsBytes for CStr {
fn as_bytes(&self) -> &[u8] {
self.to_bytes_with_nul()
}
}
unsafe impl NoPrefixesBytes for CStr {}
unsafe impl OrderedBytes for CStr {}
impl AsBytes for CString {
fn as_bytes(&self) -> &[u8] {
self.to_bytes_with_nul()
}
}
unsafe impl NoPrefixesBytes for CString {}
unsafe impl OrderedBytes for CString {}
#[cfg(unix)]
#[cfg(feature = "std")]
impl AsBytes for OsStr {
fn as_bytes(&self) -> &[u8] {
use std::os::unix::prelude::OsStrExt;
<OsStr as OsStrExt>::as_bytes(self)
}
}
#[cfg(unix)]
#[cfg(feature = "std")]
impl AsBytes for OsString {
fn as_bytes(&self) -> &[u8] {
use std::os::unix::prelude::OsStrExt;
<OsStr as OsStrExt>::as_bytes(self)
}
}
#[cfg(feature = "std")]
#[cfg(target_os = "wasi")]
impl AsBytes for OsStr {
fn as_bytes(&self) -> &[u8] {
use std::os::wasi::prelude::OsStrExt;
<OsStr as OsStrExt>::as_bytes(self)
}
}
#[cfg(feature = "std")]
#[cfg(target_os = "wasi")]
impl AsBytes for OsString {
fn as_bytes(&self) -> &[u8] {
use std::os::wasi::prelude::OsStrExt;
<OsStr as OsStrExt>::as_bytes(self)
}
}
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(feature = "std")]
unsafe impl OrderedBytes for OsStr {}
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(feature = "std")]
unsafe impl OrderedBytes for OsString {}
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(feature = "std")]
impl AsBytes for Path {
fn as_bytes(&self) -> &[u8] {
<OsStr as AsBytes>::as_bytes(self.as_os_str())
}
}
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(feature = "std")]
unsafe impl OrderedBytes for Path {}
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(feature = "std")]
impl AsBytes for PathBuf {
fn as_bytes(&self) -> &[u8] {
<OsStr as AsBytes>::as_bytes(self.as_os_str())
}
}
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(feature = "std")]
unsafe impl OrderedBytes for PathBuf {}
impl<B> AsBytes for Cow<'_, B>
where
B: ToOwned + AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<B as AsBytes>::as_bytes(self.as_ref())
}
}
unsafe impl<'a, B> OrderedBytes for Cow<'a, B>
where
B: OrderedBytes + 'a + ToOwned + ?Sized,
Cow<'a, B>: AsBytes,
{
}
unsafe impl<'a, B> NoPrefixesBytes for Cow<'a, B>
where
B: NoPrefixesBytes + ToOwned + ?Sized,
Cow<'a, B>: AsBytes,
{
}
impl<T> AsBytes for &T
where
T: AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<T as AsBytes>::as_bytes(self)
}
}
unsafe impl<T> OrderedBytes for &T where T: OrderedBytes + ?Sized {}
unsafe impl<T> NoPrefixesBytes for &T where T: NoPrefixesBytes + ?Sized {}
impl<T> AsBytes for &mut T
where
T: AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<T as AsBytes>::as_bytes(self)
}
}
unsafe impl<T> OrderedBytes for &mut T where T: OrderedBytes + ?Sized {}
unsafe impl<T> NoPrefixesBytes for &mut T where T: NoPrefixesBytes + ?Sized {}
impl<T> AsBytes for Rc<T>
where
T: AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<T as AsBytes>::as_bytes(self)
}
}
unsafe impl<T> OrderedBytes for Rc<T> where T: OrderedBytes + ?Sized {}
unsafe impl<T> NoPrefixesBytes for Rc<T> where T: NoPrefixesBytes + ?Sized {}
impl<T> AsBytes for Arc<T>
where
T: AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<T as AsBytes>::as_bytes(self)
}
}
unsafe impl<T> OrderedBytes for Arc<T> where T: OrderedBytes + ?Sized {}
unsafe impl<T> NoPrefixesBytes for Arc<T> where T: NoPrefixesBytes + ?Sized {}
impl<T> AsBytes for Box<T>
where
T: AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<T as AsBytes>::as_bytes(self)
}
}
unsafe impl<T> OrderedBytes for Box<T> where T: OrderedBytes + ?Sized {}
unsafe impl<T> NoPrefixesBytes for Box<T> where T: NoPrefixesBytes + ?Sized {}
impl<T> AsBytes for ManuallyDrop<T>
where
T: AsBytes + ?Sized,
{
fn as_bytes(&self) -> &[u8] {
<T as AsBytes>::as_bytes(self)
}
}
unsafe impl<T> OrderedBytes for ManuallyDrop<T> where T: OrderedBytes + ?Sized {}
unsafe impl<T> NoPrefixesBytes for ManuallyDrop<T> where T: NoPrefixesBytes + ?Sized {}
#[cfg(feature = "std")]
impl AsBytes for IoSlice<'_> {
fn as_bytes(&self) -> &[u8] {
self
}
}
#[cfg(feature = "std")]
impl AsBytes for IoSliceMut<'_> {
fn as_bytes(&self) -> &[u8] {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn various_numeric_types_as_bytes() {
assert_eq!(u8::MAX.as_bytes(), &[u8::MAX]);
assert_eq!(i8::MAX.as_bytes(), &[i8::MAX as u8]);
assert_eq!(65535u16.as_bytes(), 65535u16.to_ne_bytes());
assert_eq!(32767i16.as_bytes(), 32767i16.to_ne_bytes());
assert_eq!(2387u32.as_bytes(), 2387u32.to_ne_bytes());
assert_eq!(2387i32.as_bytes(), 2387i32.to_ne_bytes());
assert_eq!(
[26343u16, 0, u16::MAX].as_bytes(),
&[
26343u16.to_ne_bytes()[0],
26343u16.to_ne_bytes()[1],
0,
0,
255,
255
]
);
assert_eq!(
Box::<[u16]>::from([26343u16, 0, u16::MAX]).as_bytes(),
&[
26343u16.to_ne_bytes()[0],
26343u16.to_ne_bytes()[1],
0,
0,
255,
255
]
);
assert_eq!(
Vec::<u16>::from([26343u16, 0, u16::MAX]).as_bytes(),
&[
26343u16.to_ne_bytes()[0],
26343u16.to_ne_bytes()[1],
0,
0,
255,
255
]
);
assert_eq!(
NonZeroU32::try_from(u32::MAX).unwrap().as_bytes(),
&[255, 255, 255, 255]
);
assert_eq!('Z'.as_bytes(), 90u32.to_ne_bytes());
assert_eq!(false.as_bytes(), &[0]);
}
#[test]
fn various_string_types_as_bytes() {
assert_eq!(<str as AsBytes>::as_bytes("hello world"), b"hello world");
assert_eq!(
<String as AsBytes>::as_bytes(&"hello world".into()),
b"hello world"
);
assert_eq!(
<CStr as AsBytes>::as_bytes(c"hello world"),
b"hello world\0"
);
assert_eq!(
<CString as AsBytes>::as_bytes(&c"hello world".into()),
b"hello world\0"
);
assert_eq!(
<CString as AsBytes>::as_bytes(&c"hello world".into()),
b"hello world\0"
);
#[cfg(feature = "std")]
#[cfg(any(unix, target_os = "wasi"))]
{
assert_eq!(
<OsStr as AsBytes>::as_bytes(OsStr::new("hello world")),
b"hello world"
);
assert_eq!(
<OsString as AsBytes>::as_bytes(&OsStr::new("hello world").into()),
b"hello world"
);
}
#[cfg(feature = "std")]
#[cfg(any(unix, target_os = "wasi"))]
{
assert_eq!(
<Path as AsBytes>::as_bytes(Path::new("hello/world")),
b"hello/world"
);
assert_eq!(
<PathBuf as AsBytes>::as_bytes(&Path::new("hello/world").into()),
b"hello/world"
);
}
}
#[test]
fn various_wrapper_types_as_bytes() {
assert_eq!(
<&[u8] as AsBytes>::as_bytes(&&b"hello world"[..]),
b"hello world"
);
assert_eq!(
<Box<&[u8]> as AsBytes>::as_bytes(&Box::new(b"hello world")),
b"hello world"
);
assert_eq!(
<Box<[u8]> as AsBytes>::as_bytes(&b"hello world".to_vec().into_boxed_slice()),
b"hello world"
);
assert_eq!(
<Arc<&[u8]> as AsBytes>::as_bytes(&Arc::new(b"hello world")),
b"hello world"
);
assert_eq!(
<Rc<&[u8]> as AsBytes>::as_bytes(&Rc::new(b"hello world")),
b"hello world"
);
assert_eq!(
<Cow<[u8]> as AsBytes>::as_bytes(&Cow::Borrowed(b"hello world")),
b"hello world"
);
assert_eq!(
<ManuallyDrop<&[u8]> as AsBytes>::as_bytes(&ManuallyDrop::new(b"hello world")),
b"hello world"
);
#[cfg(feature = "std")]
{
assert_eq!(
<IoSlice as AsBytes>::as_bytes(&IoSlice::new(b"hello world")),
b"hello world"
);
let mut buffer = [104u8, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100];
assert_eq!(
<IoSliceMut as AsBytes>::as_bytes(&IoSliceMut::new(&mut buffer)),
b"hello world"
)
}
}
}