#[deny(missing_docs,
missing_debug_implementations, missing_copy_implementations,
trivial_casts, trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces, unused_qualifications)]
extern crate byteorder;
extern crate rustc_serialize;
use byteorder::{LittleEndian, ReadBytesExt};
use std::error;
use std::fmt;
use std::fs::File;
use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom};
use std::path::Path;
#[derive(Debug)]
pub enum Error {
InvalidOffset { shot_number: u16, offset: u16 },
InvalidShotNumber(u16),
Io(io::Error),
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::InvalidOffset { .. } => "invalid shot offset",
Error::InvalidShotNumber(_) => "invalid shot number",
Error::Io(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::Io(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::InvalidOffset { shot_number, offset } => {
write!(f, "invalid offset {} for shot {}", offset, shot_number)
}
Error::InvalidShotNumber(number) => write!(f, "invalid shot number: {}", number),
Error::Io(ref err) => write!(f, "IO error: {}", err),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Reader<R: Read> {
reader: R,
}
impl Reader<BufReader<File>> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Reader<BufReader<File>>> {
let file = BufReader::new(File::open(path)?);
Ok(Reader { reader: file })
}
}
impl<R: Read> Reader<R> {
pub fn read_one(&mut self) -> Result<Option<Shot>> {
let number = match self.reader.read_u16::<LittleEndian>() {
Ok(number) => number,
Err(err) => {
match err.kind() {
ErrorKind::UnexpectedEof => return Ok(None),
_ => return Err(err.into()),
}
}
};
let offset = self.reader.read_u16::<LittleEndian>()?;
let mut bytes_remaining = offset * 2;
let outgoing = Segment::from_read(&mut self.reader)?;
bytes_remaining -= outgoing.len();
let mut segments = Vec::new();
while bytes_remaining > 0 {
let segment = Segment::from_read(&mut self.reader)?;
if segment.len() > bytes_remaining {
return Err(Error::InvalidOffset {
shot_number: number,
offset: offset,
});
}
bytes_remaining -= segment.len();
segments.push(segment);
}
Ok(Some(Shot {
number: number,
outgoing: outgoing.data,
segments: segments,
}))
}
}
impl<R: Read + Seek> Reader<R> {
pub fn seek(&mut self, number: u16) -> Result<()> {
if number == 0 {
return Err(Error::InvalidShotNumber(number));
}
self.reader.seek(SeekFrom::Start(2))?;
let mut position: u64 = 2;
let mut current = 1;
while current < number {
let offset = self.reader.read_u16::<LittleEndian>()? * 2 + 2;
position += 2;
let new_position = self.reader.seek(SeekFrom::Current(offset as i64))?;
if new_position != position + offset as u64 {
return Err(Error::InvalidShotNumber(number));
}
position += offset as u64;
current += 1;
}
self.reader.seek(SeekFrom::Current(-2))?;
Ok(())
}
}
impl<R: Read> Iterator for Reader<R> {
type Item = Result<Shot>;
fn next(&mut self) -> Option<Result<Shot>> {
match self.read_one() {
Ok(option) => {
match option {
Some(shot) => Some(Ok(shot)),
None => None,
}
}
Err(err) => Some(Err(err)),
}
}
}
#[derive(Debug, PartialEq, RustcEncodable)]
pub struct Shot {
pub number: u16,
pub outgoing: Vec<u16>,
pub segments: Vec<Segment>,
}
#[derive(Debug, PartialEq, RustcEncodable)]
pub struct Segment {
pub data: Vec<u16>,
pub time_interval: u16,
}
impl Segment {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Segment> {
File::open(path)
.map_err(|err| err.into())
.and_then(|mut file| Segment::from_read(&mut file))
}
pub fn from_read<R: Read>(read: &mut R) -> Result<Segment> {
let nsamples = read.read_u16::<LittleEndian>()?;
let data = (0..nsamples).map(|_| read.read_u16::<LittleEndian>().map_err(|err| err.into()))
.collect::<Result<Vec<u16>>>()?;
let time_interval = read.read_u16::<LittleEndian>()?;
let _ = read.read_u16::<LittleEndian>()?;
Ok(Segment {
data: data,
time_interval: time_interval,
})
}
pub fn len(&self) -> u16 {
self.data.len() as u16 * 2 + 6
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reader_from_path() {
assert!(Reader::from_path("data/one-shot.df2").is_ok());
}
#[test]
fn reader_read_one() {
let mut reader = Reader::from_path("data/one-shot.df2").unwrap();
let shot = reader.read_one().unwrap().unwrap();
assert_eq!(1, shot.number);
}
#[test]
fn reader_iterator() {
let shots: Vec<_> = Reader::from_path("data/one-shot.df2").unwrap().collect();
assert_eq!(1, shots.len());
}
#[test]
fn reader_seek_identical() {
let shot_by_iter =
Reader::from_path("data/two-shots.df2").unwrap().skip(1).next().unwrap().unwrap();
let mut reader = Reader::from_path("data/two-shots.df2").unwrap();
reader.seek(2).unwrap();
let shot_by_seek = reader.read_one().unwrap().unwrap();
assert_eq!(shot_by_iter, shot_by_seek);
}
#[test]
fn reader_seek_skip_two() {
let mut reader = Reader::from_path("data/four-shots.df2").unwrap();
reader.seek(3).unwrap();
assert_eq!(2, reader.collect::<Result<Vec<Shot>>>().unwrap().len());
}
#[test]
fn segment_from_path() {
assert!(Segment::from_path("data/one-segment.bin").is_ok());
}
#[test]
fn segment_len() {
let segment = Segment::from_path("data/one-segment.bin").unwrap();
assert_eq!(110, segment.len());
}
}