use std::io::{self, Read};
use crate::Result;
pub trait ReadExt: Read {
fn read_u8(&mut self) -> Result<u8>;
fn read_u16_be(&mut self) -> Result<u16>;
fn read_u32_be(&mut self) -> Result<u32>;
fn read_i32_be(&mut self) -> Result<i32>;
fn read_i32_le(&mut self) -> Result<i32>;
#[allow(dead_code)] fn read_i16_be(&mut self) -> Result<i16>;
fn read_u64_be(&mut self) -> Result<u64>;
#[allow(dead_code)] fn read_u64_le(&mut self) -> Result<u64>;
#[allow(dead_code)] fn read_tag(&mut self) -> Result<[u8; 4]>;
fn read_exact_vec(&mut self, n: usize) -> Result<Vec<u8>>;
fn skip(&mut self, n: u64) -> Result<()>;
}
impl<R: Read> ReadExt for R {
fn read_u8(&mut self) -> Result<u8> {
let mut buf = [0u8; 1];
self.read_exact(&mut buf)?;
Ok(buf[0])
}
fn read_u16_be(&mut self) -> Result<u16> {
let mut buf = [0u8; 2];
self.read_exact(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}
fn read_u32_be(&mut self) -> Result<u32> {
let mut buf = [0u8; 4];
self.read_exact(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}
fn read_i32_be(&mut self) -> Result<i32> {
let mut buf = [0u8; 4];
self.read_exact(&mut buf)?;
Ok(i32::from_be_bytes(buf))
}
fn read_i32_le(&mut self) -> Result<i32> {
let mut buf = [0u8; 4];
self.read_exact(&mut buf)?;
Ok(i32::from_le_bytes(buf))
}
fn read_i16_be(&mut self) -> Result<i16> {
let mut buf = [0u8; 2];
self.read_exact(&mut buf)?;
Ok(i16::from_be_bytes(buf))
}
fn read_u64_be(&mut self) -> Result<u64> {
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
Ok(u64::from_be_bytes(buf))
}
fn read_u64_le(&mut self) -> Result<u64> {
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
fn read_tag(&mut self) -> Result<[u8; 4]> {
let mut buf = [0u8; 4];
self.read_exact(&mut buf)?;
Ok(buf)
}
fn read_exact_vec(&mut self, n: usize) -> Result<Vec<u8>> {
let mut buf = vec![0u8; n];
self.read_exact(&mut buf)?;
Ok(buf)
}
fn skip(&mut self, n: u64) -> Result<()> {
let consumed = io::copy(&mut self.by_ref().take(n), &mut io::sink())?;
if consumed < n {
return Err(
io::Error::new(io::ErrorKind::UnexpectedEof, "skip: unexpected EOF").into(),
);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::ReadExt;
use std::io::Cursor;
#[test]
fn read_u8_eof() {
assert!(Cursor::new([]).read_u8().is_err());
}
#[test]
fn read_u16_be_truncated() {
assert!(Cursor::new([0x12u8]).read_u16_be().is_err());
}
#[test]
fn read_u32_be_truncated() {
assert!(Cursor::new([0x01u8, 0x02, 0x03]).read_u32_be().is_err());
}
#[test]
fn read_u64_be_truncated() {
assert!(
Cursor::new([0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
.read_u64_be()
.is_err()
);
}
#[test]
fn read_tag_truncated() {
assert!(Cursor::new(b"SQP" as &[u8]).read_tag().is_err());
}
#[test]
fn read_exact_vec_truncated() {
assert!(Cursor::new(b"hi!!" as &[u8]).read_exact_vec(5).is_err());
}
#[test]
fn read_u32_be_endian() {
assert_eq!(
Cursor::new([0x01u8, 0x02, 0x03, 0x04])
.read_u32_be()
.unwrap(),
0x01020304
);
}
#[test]
fn read_i32_le_endian() {
assert_eq!(
Cursor::new([0x04u8, 0x03, 0x02, 0x01])
.read_i32_le()
.unwrap(),
0x01020304
);
}
#[test]
fn read_u64_le_endian() {
assert_eq!(
Cursor::new([0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])
.read_u64_le()
.unwrap(),
0x0807060504030201
);
}
#[test]
fn read_u64_be_endian() {
assert_eq!(
Cursor::new([0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])
.read_u64_be()
.unwrap(),
0x0102030405060708
);
}
#[test]
fn read_i16_be_endian() {
assert_eq!(Cursor::new([0x01u8, 0x02]).read_i16_be().unwrap(), 0x0102);
}
#[test]
fn read_i32_be_min() {
assert_eq!(
Cursor::new([0x80u8, 0x00, 0x00, 0x00])
.read_i32_be()
.unwrap(),
i32::MIN
);
}
#[test]
fn read_i32_le_min() {
assert_eq!(
Cursor::new([0x00u8, 0x00, 0x00, 0x80])
.read_i32_le()
.unwrap(),
i32::MIN
);
}
#[test]
fn read_i16_be_min() {
assert_eq!(Cursor::new([0x80u8, 0x00]).read_i16_be().unwrap(), i16::MIN);
}
#[test]
fn skip_zero() {
let mut cur = Cursor::new([1u8, 2]);
cur.skip(0).unwrap();
assert_eq!(cur.read_u8().unwrap(), 1);
}
#[test]
fn skip_advances_position() {
let mut cur = Cursor::new([1u8, 2, 3, 4, 5]);
cur.skip(3).unwrap();
assert_eq!(cur.read_u8().unwrap(), 4);
}
#[test]
fn skip_past_eof() {
let mut cur = Cursor::new([1u8, 2, 3, 4, 5]);
assert!(cur.skip(100).is_err());
}
#[test]
fn read_exact_vec_empty() {
assert_eq!(
Cursor::new(b"hello" as &[u8]).read_exact_vec(0).unwrap(),
b""
);
}
#[test]
fn read_i32_be_truncated() {
assert!(Cursor::new([0x01u8, 0x02]).read_i32_be().is_err());
}
#[test]
fn read_i32_le_truncated() {
assert!(Cursor::new([0x01u8, 0x02]).read_i32_le().is_err());
}
#[test]
fn read_i16_be_truncated() {
assert!(Cursor::new([0x01u8]).read_i16_be().is_err());
}
#[test]
fn read_u64_le_truncated() {
assert!(
Cursor::new([0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
.read_u64_le()
.is_err()
);
}
#[test]
fn read_tag_success() {
assert_eq!(Cursor::new(b"SQPK" as &[u8]).read_tag().unwrap(), *b"SQPK");
}
}