use std::ffi::OsStr;
use std::mem;
use std::os::unix::ffi::OsStrExt;
pub struct ArgumentIterator<'a> {
data: &'a [u8],
}
impl<'a> ArgumentIterator<'a> {
pub fn new(data: &'a [u8]) -> ArgumentIterator<'a> {
ArgumentIterator { data }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn fetch_all(&mut self) -> &'a [u8] {
let bytes = self.data;
self.data = &[];
bytes
}
pub fn fetch_bytes(&mut self, amt: usize) -> Option<&'a [u8]> {
if amt > self.data.len() {
return None;
}
let bytes = &self.data[..amt];
self.data = &self.data[amt..];
Some(bytes)
}
pub unsafe fn fetch<T>(&mut self) -> Option<&'a T> {
let len = mem::size_of::<T>();
let bytes = self.fetch_bytes(len)?;
(bytes.as_ptr() as *const T).as_ref()
}
pub unsafe fn fetch_str(&mut self) -> Option<&'a OsStr> {
let len = self.data.iter().position(|&c| c == 0)?;
let bytes = self.fetch_bytes(len)?;
let _zero = self.fetch_bytes(1)?;
Some(OsStr::from_bytes(&bytes))
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_DATA: [u8; 10] = [0x66, 0x6f, 0x6f, 0x00, 0x62, 0x61, 0x72, 0x00, 0x62, 0x61];
#[repr(C)]
struct TestArgument {
p1: u8,
p2: u8,
p3: u16,
}
#[test]
fn all_data() {
let mut it = ArgumentIterator::new(&TEST_DATA);
unsafe { it.fetch_str().unwrap() };
let arg = it.fetch_all();
assert_eq!(arg, [0x62, 0x61, 0x72, 0x00, 0x62, 0x61]);
}
#[test]
fn bytes_data() {
let mut it = ArgumentIterator::new(&TEST_DATA);
let arg = it.fetch_bytes(5).unwrap();
assert_eq!(arg, [0x66, 0x6f, 0x6f, 0x00, 0x62]);
let arg = it.fetch_bytes(2).unwrap();
assert_eq!(arg, [0x61, 0x72]);
assert_eq!(it.len(), 3);
}
#[test]
fn generic_argument() {
let mut it = ArgumentIterator::new(&TEST_DATA);
let arg: &TestArgument = unsafe { it.fetch().unwrap() };
assert_eq!(arg.p1, 0x66);
assert_eq!(arg.p2, 0x6f);
assert_eq!(arg.p3, 0x006f);
let arg: &TestArgument = unsafe { it.fetch().unwrap() };
assert_eq!(arg.p1, 0x62);
assert_eq!(arg.p2, 0x61);
assert_eq!(arg.p3, 0x0072);
assert_eq!(it.len(), 2);
}
#[test]
fn string_argument() {
let mut it = ArgumentIterator::new(&TEST_DATA);
let arg = unsafe { it.fetch_str().unwrap() };
assert_eq!(arg, "foo");
let arg = unsafe { it.fetch_str().unwrap() };
assert_eq!(arg, "bar");
assert_eq!(it.len(), 2);
}
#[test]
fn mixed_arguments() {
let mut it = ArgumentIterator::new(&TEST_DATA);
let arg: &TestArgument = unsafe { it.fetch().unwrap() };
assert_eq!(arg.p1, 0x66);
assert_eq!(arg.p2, 0x6f);
assert_eq!(arg.p3, 0x006f);
let arg = unsafe { it.fetch_str().unwrap() };
assert_eq!(arg, "bar");
let arg = it.fetch_all();
assert_eq!(arg, [0x62, 0x61]);
}
#[test]
fn out_of_data() {
let mut it = ArgumentIterator::new(&TEST_DATA);
let _arg = it.fetch_bytes(8).unwrap();
let arg: Option<&TestArgument> = unsafe { it.fetch() };
assert!(arg.is_none());
assert_eq!(it.len(), 2);
let arg = unsafe { it.fetch_str() };
assert!(arg.is_none());
assert_eq!(it.len(), 2);
}
}