blob: 964b3113aa8cb3c9964acad6d0d4d8951131d7e7 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io::Read;
use std::mem::size_of;
#[derive(Debug)]
pub enum Error {
ReadStruct,
}
pub type Result<T> = std::result::Result<T, Error>;
/// Reads a struct from an input buffer.
///
/// # Arguments
///
/// * `f` - The input to read from. Often this is a file.
/// * `out` - The struct to fill with data read from `f`.
///
/// # Safety
///
/// This is unsafe because the struct is initialized to unverified data read from the input.
/// `read_struct` should only be called to fill plain old data structs. It is not endian safe.
pub unsafe fn read_struct<T: Copy, F: Read>(f: &mut F, out: &mut T) -> Result<()> {
let out_slice = std::slice::from_raw_parts_mut(out as *mut T as *mut u8, size_of::<T>());
f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?;
Ok(())
}
/// Reads an array of structs from an input buffer. Returns a Vec of structs initialized with data
/// from the specified input.
///
/// # Arguments
///
/// * `f` - The input to read from. Often this is a file.
/// * `len` - The number of structs to fill with data read from `f`.
///
/// # Safety
///
/// This is unsafe because the structs are initialized to unverified data read from the input.
/// `read_struct_slice` should only be called for plain old data structs. It is not endian safe.
pub unsafe fn read_struct_slice<T: Copy, F: Read>(f: &mut F, len: usize) -> Result<Vec<T>> {
let mut out: Vec<T> = Vec::with_capacity(len);
out.set_len(len);
let out_slice =
std::slice::from_raw_parts_mut(out.as_ptr() as *mut T as *mut u8, size_of::<T>() * len);
f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?;
Ok(out)
}
#[cfg(test)]
mod test {
use super::*;
use std::io::Cursor;
use std::mem;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
struct TestRead {
a: u64,
b: u8,
c: u8,
d: u8,
e: u8,
}
#[test]
fn struct_basic_read() {
let orig = TestRead {
a: 0x7766554433221100,
b: 0x88,
c: 0x99,
d: 0xaa,
e: 0xbb,
};
let source = unsafe {
// Don't worry it's a test
std::slice::from_raw_parts(
&orig as *const _ as *const u8,
std::mem::size_of::<TestRead>(),
)
};
assert_eq!(mem::size_of::<TestRead>(), mem::size_of_val(&source));
let mut tr: TestRead = Default::default();
unsafe {
read_struct(&mut Cursor::new(source), &mut tr).unwrap();
}
assert_eq!(orig, tr);
}
#[test]
fn struct_read_past_end() {
let orig = TestRead {
a: 0x7766554433221100,
b: 0x88,
c: 0x99,
d: 0xaa,
e: 0xbb,
};
let source = unsafe {
// Don't worry it's a test
std::slice::from_raw_parts(
&orig as *const _ as *const u8,
std::mem::size_of::<TestRead>() - 1,
)
};
let mut tr: TestRead = Default::default();
unsafe {
assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err());
}
}
#[test]
fn struct_slice_read() {
let orig = vec![
TestRead {
a: 0x7766554433221100,
b: 0x88,
c: 0x99,
d: 0xaa,
e: 0xbb,
},
TestRead {
a: 0x7867564534231201,
b: 0x02,
c: 0x13,
d: 0x24,
e: 0x35,
},
TestRead {
a: 0x7a69584736251403,
b: 0x04,
c: 0x15,
d: 0x26,
e: 0x37,
},
];
let source = unsafe {
// Don't worry it's a test
std::slice::from_raw_parts(
orig.as_ptr() as *const u8,
std::mem::size_of::<TestRead>() * 3,
)
};
let tr: Vec<TestRead> = unsafe { read_struct_slice(&mut Cursor::new(source), 3).unwrap() };
assert_eq!(orig, tr);
}
}