#![no_std]
use core::mem;
use core::slice::from_raw_parts;
use core::str::{from_utf8, from_utf8_unchecked};
pub fn read<T: Pod>(input: &[u8]) -> &T {
assert!(mem::size_of::<T>() <= input.len());
let addr = input.as_ptr() as usize;
assert!((addr & (mem::align_of::<T>() - 1)) == 0);
unsafe { read_unsafe(input) }
}
pub fn read_array<T: Pod>(input: &[u8]) -> &[T] {
let t_size = mem::size_of::<T>();
assert!(t_size > 0, "Can't read arrays of zero-sized types");
assert!(input.len() % t_size == 0);
let addr = input.as_ptr() as usize;
assert!(addr & (mem::align_of::<T>() - 1) == 0);
unsafe { read_array_unsafe(input) }
}
pub fn read_str(input: &[u8]) -> &str {
from_utf8(read_str_bytes(input)).expect("Invalid UTF-8 string")
}
pub fn read_strs_to_null(input: &[u8]) -> StrReaderIterator {
StrReaderIterator { data: input }
}
pub unsafe trait Pod: Sized {}
unsafe impl Pod for u8 {}
unsafe impl Pod for u16 {}
unsafe impl Pod for u32 {}
unsafe impl Pod for u64 {}
unsafe impl Pod for u128 {}
unsafe impl Pod for i8 {}
unsafe impl Pod for i16 {}
unsafe impl Pod for i32 {}
unsafe impl Pod for i64 {}
unsafe impl Pod for i128 {}
pub unsafe fn read_unsafe<T: Sized>(input: &[u8]) -> &T {
&*(input.as_ptr() as *const T)
}
pub unsafe fn read_array_unsafe<T: Sized>(input: &[u8]) -> &[T] {
let ptr = input.as_ptr() as *const T;
from_raw_parts(ptr, input.len() / mem::size_of::<T>())
}
pub unsafe fn read_str_unsafe(input: &[u8]) -> &str {
from_utf8_unchecked(read_str_bytes(input))
}
#[derive(Clone, Debug)]
pub struct StrReaderIterator<'a> {
data: &'a [u8],
}
impl<'a> Iterator for StrReaderIterator<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
if self.data.is_empty() || self.data[0] == 0 {
return None;
}
let result = read_str(self.data);
self.data = &self.data[result.len() + 1..];
Some(result)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.data.len() / 2))
}
}
fn read_str_bytes(input: &[u8]) -> &[u8] {
for (i, byte) in input.iter().enumerate() {
if *byte == 0 {
return &input[..i];
}
}
panic!("No null byte in input");
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Zero;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(packed)]
struct Foo {
a: u8,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(packed)]
struct Bar {
a: u32,
b: u64,
c: i8,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(C)]
struct Baz {
a: u32,
}
unsafe impl Pod for Zero {}
unsafe impl Pod for Foo {}
unsafe impl Pod for Bar {}
unsafe impl Pod for Baz {}
#[rustfmt::skip]
#[test]
fn test_read() {
let a = &[];
assert_eq!(read::<Zero>(a), &Zero);
let a = &[42];
assert_eq!(read::<Foo>(a), &Foo { a: 42 });
let a = &[42, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0, -2i8 as u8];
assert_eq!(
read::<Bar>(a),
&Bar { a: 42, b: 0xf000425b_a262ff03, c: -2 }
);
}
#[test]
#[should_panic]
fn test_too_small() {
let a = &[];
read::<Foo>(a);
}
#[test]
#[should_panic]
fn test_too_small2() {
let a = &[42, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0];
read::<Bar>(a);
}
#[test]
#[should_panic]
fn test_unaligned() {
let a = &[0, 42, 0, 0, 0];
read::<Baz>(&a[1..]);
}
#[rustfmt::skip]
#[test]
fn test_read_array() {
let a = &[42];
assert_eq!(read_array::<Foo>(a), &[Foo { a: 42 }]);
let a = &[42, 43, 44, 45];
assert_eq!(
read_array::<Foo>(a),
&[Foo { a: 42 }, Foo { a: 43 }, Foo { a: 44 }, Foo { a: 45 }]
);
let a = &[
42, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0, -2i8 as u8,
];
assert_eq!(read_array::<Bar>(a), &[Bar { a: 42, b: 0xf000425b_a262ff03, c: -2 }]);
let a = &[
42, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0, -2i8 as u8,
43, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0, -2i8 as u8,
44, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0, -2i8 as u8,
];
assert_eq!(
read_array::<Bar>(a),
&[
Bar { a: 42, b: 0xf000425b_a262ff03, c: -2 },
Bar { a: 43, b: 0xf000425b_a262ff03, c: -2 },
Bar { a: 44, b: 0xf000425b_a262ff03, c: -2 },
]
);
}
#[test]
#[should_panic]
fn test_array_zero_sized() {
let a = &[0];
read_array::<Zero>(a);
}
#[ignore]
#[test]
#[should_panic]
fn test_array_unaligned() {
let a = &[0, 42, 0, 0, 0, 37, 0, 0, 0];
read_array::<Baz>(&a[1..]);
}
#[test]
fn test_good_strs() {
let a = &[0];
assert_eq!(read_str(a), "");
let a = &[0x61, 0];
assert_eq!(read_str(a), "a");
let a = &[0x61, 0x41, 0x7a, 0, 0x61];
assert_eq!(read_str(a), "aAz");
}
#[test]
#[should_panic]
fn test_no_null() {
let a = &[];
read_str(a);
}
#[test]
#[should_panic]
fn test_no_null2() {
let a = &[0x61, 0x41, 0x7a];
read_str(a);
}
#[test]
#[should_panic]
fn test_not_unicode() {
panic!();
}
#[test]
fn test_good_strs_to_null() {
let a = &[0];
assert_eq!(read_strs_to_null(a).count(), 0);
let a = &[0, 0];
assert_eq!(read_strs_to_null(a).count(), 0);
let a = &[0, 1];
assert_eq!(read_strs_to_null(a).count(), 0);
let a = &[0x61, 0];
let mut iter = read_strs_to_null(a);
assert_eq!(iter.next(), Some("a"));
assert_eq!(iter.next(), None);
let a = &[0x61, 0, 0x61, 0x41, 0x7a, 0, 0x61, 0];
let mut iter = read_strs_to_null(a);
assert_eq!(iter.next(), Some("a"));
assert_eq!(iter.next(), Some("aAz"));
assert_eq!(iter.next(), Some("a"));
assert_eq!(iter.next(), None);
let a = &[0x61, 0, 0x61, 0x41, 0x7a, 0, 0x61, 0, 0, 0x61];
let mut iter = read_strs_to_null(a);
assert_eq!(iter.next(), Some("a"));
assert_eq!(iter.next(), Some("aAz"));
assert_eq!(iter.next(), Some("a"));
assert_eq!(iter.next(), None);
}
#[test]
#[should_panic]
fn test_no_null_to_null() {
let a = &[0x61];
let mut iter = read_strs_to_null(a);
iter.next();
}
}