#![no_std]
use core::mem;
use core::slice::from_raw_parts;
use core::str::{from_utf8, from_utf8_unchecked};
pub fn read<'a, T: Pod>(input: &'a [u8]) -> &'a T {
assert!(mem::size_of::<T>() <= input.len());
unsafe {
read_unsafe(input)
}
}
pub fn read_array<'a, T: Pod>(input: &'a [u8]) -> &'a [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);
unsafe {
read_array_unsafe(input)
}
}
pub fn read_str<'a>(input: &'a [u8]) -> &'a str {
from_utf8(read_str_bytes(input)).expect("Non-utf8 string")
}
pub fn read_strs_to_null<'a>(input: &'a [u8]) -> StrReaderIterator<'a> {
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 i8 {}
unsafe impl Pod for i16 {}
unsafe impl Pod for i32 {}
unsafe impl Pod for i64 {}
pub unsafe fn read_unsafe<'a, T: Sized>(input: &'a [u8]) -> &'a T {
mem::transmute(input as *const [u8] as *const u8 as *const T)
}
pub unsafe fn read_array_unsafe<'a, T: Sized>(input: &'a [u8]) -> &'a [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<'a>(input: &'a [u8]) -> &'a 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.len() == 0 || 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<'a>(input: &'a [u8]) -> &'a [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(Copy, Clone, PartialEq, Eq)]
struct Zero;
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(packed)]
struct Foo {
a: u8,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(packed)]
struct Bar {
a: u32,
b: u64,
c: i8,
}
unsafe impl Pod for Zero {}
unsafe impl Pod for Foo {}
unsafe impl Pod for Bar {}
#[test]
fn test_read() {
let a = &[];
assert!(read::<Zero>(a) == &Zero);
let a = &[42];
assert!(read::<Foo>(a) == &Foo { a: 42 });
let a = &[42, 0, 0, 0, 0x03, 0xff, 0x62, 0xa2, 0x5b, 0x42, 0x00, 0xf0, -2i8 as u8];
assert!(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]
fn test_read_array() {
let a = &[42];
assert!(read_array::<Foo>(a) == &[Foo { a: 42 }]);
let a = &[42, 43, 44, 45];
assert!(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!(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!(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);
}
#[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();
}
}