use crate::types::{ArchiveError, BlockType, Offset, Oid};
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Seek;
use std::num::ParseIntError;
use std::string::String;
#[derive(Debug, PartialEq)]
pub struct ReadConfig {
pub int_size: usize,
pub offset_size: usize,
}
impl Default for ReadConfig {
fn default() -> Self {
Self::new()
}
}
impl ReadConfig {
pub fn new() -> ReadConfig {
ReadConfig {
int_size: 0,
offset_size: 0,
}
}
pub fn read_byte(&self, f: &mut (impl Read + ?Sized)) -> io::Result<u8> {
let mut buffer: [u8; 1] = [0];
f.read_exact(&mut buffer)?;
Ok(buffer[0])
}
pub fn read_int(&self, f: &mut (impl Read + ?Sized)) -> io::Result<i64> {
read_int(f, self.int_size)
}
pub fn read_string(&self, f: &mut (impl Read + ?Sized)) -> io::Result<String> {
let length = self.read_int(f)?;
if length == -1 {
return Ok(String::new());
}
if length < 0 {
return Err(io::Error::new(
io::ErrorKind::Other,
"invalid string length",
));
}
let mut buffer = vec![0; length as usize];
f.read_exact(buffer.as_mut_slice())?;
let s = String::from_utf8(buffer)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
Ok(s)
}
pub fn read_int_bool(&self, f: &mut (impl Read + ?Sized)) -> io::Result<bool> {
self.read_int(f).map(|v| v != 0)
}
pub fn read_string_bool(&self, f: &mut (impl Read + ?Sized)) -> io::Result<bool> {
self.read_string(f).map(|v| v == "true")
}
pub fn read_oid(&self, f: &mut (impl Read + ?Sized)) -> io::Result<Oid> {
let v = self.read_string(f)?;
Oid::from_str_radix(v.as_str(), 10)
.map_err(|e: ParseIntError| io::Error::new(io::ErrorKind::Other, e.to_string()))
}
pub fn read_offset(&self, f: &mut (impl Read + ?Sized)) -> io::Result<Offset> {
if self.offset_size == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "offset size unknown"));
}
let mut buffer = vec![0; self.offset_size + 1];
f.read_exact(buffer.as_mut_slice())?;
match buffer[0] {
0 => Ok(Offset::Unknown),
1 => Ok(Offset::PosNotSet),
2 => {
let mut offset: u64 = 0;
for i in 0..self.offset_size {
offset |= (buffer[i + 1] as u64) << (i * 8);
}
Ok(Offset::PosSet(offset))
}
3 => Ok(Offset::NoData),
_ => Err(io::Error::new(io::ErrorKind::Other, "invalid offset type")),
}
}
pub fn read_data(&self, f: &mut File, o: Offset) -> Result<Box<dyn io::Read>, ArchiveError> {
match o {
Offset::NoData => Ok(Box::new(DataReader::empty(f.try_clone()?))),
Offset::PosNotSet => Err(ArchiveError::NoDataPresent),
Offset::Unknown => Err(ArchiveError::NoDataPresent),
Offset::PosSet(offset) => {
f.seek(io::SeekFrom::Start(offset))?;
let block_type: BlockType = self
.read_byte(f)?
.try_into()
.or(Err(ArchiveError::InvalidData("invalid block type".into())))?;
let _id = self.read_int(f)?;
match block_type {
BlockType::Blob => Err(ArchiveError::BlobNotSupported),
BlockType::Data => Ok(Box::new(DataReader::new(f.try_clone()?, self.int_size))),
}
}
}
}
}
fn read_int(f: &mut (impl Read + ?Sized), int_size: usize) -> io::Result<i64> {
if int_size == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "integer size unknown"));
}
let mut buffer = vec![0; int_size + 1];
f.read_exact(buffer.as_mut_slice())?;
let is_negative = buffer[0] != 0;
let mut result: i64 = 0;
for i in 0..int_size {
result += (buffer[i + 1] as i64) << (i * 8);
}
Ok(if is_negative { -result } else { result })
}
#[derive(Debug)]
pub struct DataReader<T: Read> {
int_size: usize,
inner: std::io::Take<T>,
eof: bool,
}
impl<T: Read> DataReader<T> {
pub fn new(fd: T, int_size: usize) -> DataReader<T> {
DataReader {
int_size,
inner: fd.take(0),
eof: false,
}
}
pub fn empty(fd: T) -> DataReader<T> {
DataReader {
int_size: 0,
inner: fd.take(0),
eof: true,
}
}
}
impl<T: Read> Read for DataReader<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.eof {
return Ok(0);
}
if self.inner.limit() == 0 {
self.inner.set_limit((self.int_size + 1) as u64);
let l = read_int(&mut self.inner, self.int_size)?;
if l == 0 {
self.eof = true;
return Ok(0);
}
self.inner.set_limit(l as u64);
}
self.inner.read(buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_byte() -> Result<(), io::Error> {
let cfg = ReadConfig::new();
let mut input: &[u8] = b"\x42";
assert_eq!(cfg.read_byte(&mut input)?, 0x42);
input = b"";
assert!(cfg.read_byte(&mut input).is_err());
Ok(())
}
#[test]
fn read_int() -> Result<(), io::Error> {
let mut cfg = ReadConfig::new();
let mut input: &[u8] = b"\x01\x02\x03\x04";
assert!(cfg.read_int(&mut input).is_err());
cfg.int_size = 2;
input = b"\x00\x01\x02";
assert_eq!(cfg.read_int(&mut input)?, 0x0201);
input = b"\x01\x01\x02";
assert_eq!(cfg.read_int(&mut input)?, -0x0201);
input = b"\x00";
assert!(cfg.read_int(&mut input).is_err());
Ok(())
}
#[test]
fn read_string() -> Result<(), io::Error> {
let mut cfg: ReadConfig = ReadConfig::new();
let mut input: &[u8] = b"\x01\x02\x03\x04";
assert!(cfg.read_string(&mut input).is_err());
cfg.int_size = 2;
input = b"\x01\x01\x00";
assert_eq!(cfg.read_string(&mut input)?, "");
input = b"\x01\x02\x00";
assert!(cfg.read_string(&mut input).is_err());
input = b"\x00\x0d\x00hello, world!";
assert_eq!(cfg.read_string(&mut input)?, "hello, world!");
input = b"\x00";
assert!(cfg.read_string(&mut input).is_err());
Ok(())
}
#[test]
fn read_int_bool() -> Result<(), io::Error> {
let mut cfg: ReadConfig = ReadConfig::new();
let mut input: &[u8] = b"\x01\x01\x00";
assert!(cfg.read_int_bool(&mut input).is_err());
cfg.int_size = 2;
input = b"\x01\x01\x00";
assert_eq!(cfg.read_int_bool(&mut input)?, true);
input = b"\x01\x02\x00";
assert_eq!(cfg.read_int_bool(&mut input)?, true);
input = b"\x00\x00\x00";
assert_eq!(cfg.read_int_bool(&mut input)?, false);
input = b"\x00";
assert!(cfg.read_int_bool(&mut input).is_err());
Ok(())
}
#[test]
fn read_string_bool() -> Result<(), io::Error> {
let mut cfg: ReadConfig = ReadConfig::new();
let mut input: &[u8] = b"\x00\x04\x00true";
assert!(cfg.read_string_bool(&mut input).is_err());
cfg.int_size = 2;
input = b"\x00\x04\x00true";
assert_eq!(cfg.read_string_bool(&mut input)?, true);
input = b"\x00\x05\x00false";
assert_eq!(cfg.read_string_bool(&mut input)?, false);
input = b"\x00\x04\x00oops";
assert_eq!(cfg.read_string_bool(&mut input)?, false);
input = b"\x00";
assert!(cfg.read_string_bool(&mut input).is_err());
Ok(())
}
#[test]
fn read_oid() -> Result<(), io::Error> {
let mut cfg: ReadConfig = ReadConfig::new();
let mut input: &[u8] = b"\x01\x02\x03\x04";
assert!(cfg.read_oid(&mut input).is_err());
cfg.int_size = 2;
input = b"\x00\x04\x001234";
assert_eq!(cfg.read_oid(&mut input)?, 1234);
input = b"\x00\x05\x00-1234";
assert!(cfg.read_oid(&mut input).is_err());
input = b"\x00\x05\x00x1234";
assert!(cfg.read_oid(&mut input).is_err());
input = b"\x00";
assert!(cfg.read_oid(&mut input).is_err());
Ok(())
}
#[test]
fn read_offset() -> Result<(), io::Error> {
let mut cfg: ReadConfig = ReadConfig::new();
let mut input: &[u8] = b"\x01\x02\x03\x04";
assert!(cfg.read_offset(&mut input).is_err());
cfg.offset_size = 2;
input = b"\x00\x01\x02";
assert_eq!(cfg.read_offset(&mut input)?, Offset::Unknown);
input = b"\x01\x01\x02";
assert_eq!(cfg.read_offset(&mut input)?, Offset::PosNotSet);
input = b"\x02\x01\x02";
assert_eq!(cfg.read_offset(&mut input)?, Offset::PosSet(513));
input = b"\x03\x01\x02";
assert_eq!(cfg.read_offset(&mut input)?, Offset::NoData);
input = b"\x00";
assert!(cfg.read_offset(&mut input).is_err());
Ok(())
}
}