Skip to content

Commit

Permalink
Split stream traits (#33)
Browse files Browse the repository at this point in the history
* Split stream traits, bump major version.

* Add test spec for slice stream.

* Rename trait.

* Implement SeekStream for BinaryReader/BinaryWriter.

* Update test spec.

Should improve the coverage.

* Test write_char() and read_char().

* Use borrow trait in writer functions.

* Assert on writer len().
  • Loading branch information
tmpfs committed Jun 7, 2022
1 parent fbc9890 commit 3aad6b8
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 71 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "binary_rw"
version = "2.0.3"
version = "3.0.0"
authors = ["Mathias Danielsen <mathiasda98@hotmail.com>"]
edition = "2021"

Expand Down
131 changes: 74 additions & 57 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
//! Otherwise string length is encoded using `usize` which
//! may vary across platforms.
#![deny(missing_docs)]
use std::io::{Read, Write};
use std::{
borrow::Borrow,
io::{Read, Write},
};

mod error;
mod filestream;
mod memorystream;
mod stream;

pub use error::BinaryError;
pub use filestream::{FileStream, OpenType};
pub use memorystream::MemoryStream;
pub use stream::file::{FileStream, OpenType};
pub use stream::memory::MemoryStream;
pub use stream::slice::SliceStream;

/// Result type for binary errors.
pub type Result<T> = std::result::Result<T, BinaryError>;
Expand Down Expand Up @@ -54,8 +57,8 @@ impl Default for Endian {
}
}

/// Trait for underlying readable and writable stream.
pub trait Stream: Read + Write {
/// Trait for streams that can seek.
pub trait SeekStream {
/// Seek to a position.
fn seek(&mut self, to: usize) -> Result<usize>;
/// Get the current position.
Expand All @@ -64,26 +67,36 @@ pub trait Stream: Read + Write {
fn len(&self) -> Result<usize>;
}

/// Trait for a readable stream.
pub trait ReadStream: Read + SeekStream {}

/// Trait for a writable stream.
pub trait WriteStream: Write + SeekStream {}

/// Read from a stream.
pub struct BinaryReader<'a> {
stream: &'a mut dyn Stream,
stream: &'a mut dyn ReadStream,
endian: Endian,
}

impl<'a> BinaryReader<'a> {
/// Create a binary reader with the given endianness.
pub fn new(stream: &'a mut impl Stream, endian: Endian) -> Self {
Self { stream, endian }
impl<'a> SeekStream for BinaryReader<'a> {
fn seek(&mut self, to: usize) -> Result<usize> {
self.stream.seek(to)
}

/// Seek to a position.
pub fn seek(&mut self, to: usize) -> Result<usize> {
Ok(self.stream.seek(to)?)
fn tell(&mut self) -> Result<usize> {
self.stream.tell()
}

/// Get the current position.
pub fn tell(&mut self) -> Result<usize> {
Ok(self.stream.tell()?)
fn len(&self) -> Result<usize> {
self.stream.len()
}
}

impl<'a> BinaryReader<'a> {
/// Create a binary reader with the given endianness.
pub fn new(stream: &'a mut impl ReadStream, endian: Endian) -> Self {
Self { stream, endian }
}

/// Read a length-prefixed `String` from the stream.
Expand Down Expand Up @@ -225,24 +238,28 @@ impl<'a> BinaryReader<'a> {

/// Write to a stream.
pub struct BinaryWriter<'a> {
stream: &'a mut dyn Stream,
stream: &'a mut dyn WriteStream,
endian: Endian,
}

impl<'a> BinaryWriter<'a> {
/// Create a binary writer with the given endianness.
pub fn new(stream: &'a mut impl Stream, endian: Endian) -> Self {
Self { stream, endian }
impl<'a> SeekStream for BinaryWriter<'a> {
fn seek(&mut self, to: usize) -> Result<usize> {
self.stream.seek(to)
}

/// Seek to a position.
pub fn seek(&mut self, to: usize) -> Result<usize> {
Ok(self.stream.seek(to)?)
fn tell(&mut self) -> Result<usize> {
self.stream.tell()
}

/// Get the current position.
pub fn tell(&mut self) -> Result<usize> {
Ok(self.stream.tell()?)
fn len(&self) -> Result<usize> {
self.stream.len()
}
}

impl<'a> BinaryWriter<'a> {
/// Create a binary writer with the given endianness.
pub fn new(stream: &'a mut impl WriteStream, endian: Endian) -> Self {
Self { stream, endian }
}

/// Write a length-prefixed `String` to the stream.
Expand All @@ -261,74 +278,74 @@ impl<'a> BinaryWriter<'a> {
}

/// Write a character to the stream.
pub fn write_char(&mut self, v: char) -> Result<usize> {
self.write_u32(v as u32)
pub fn write_char<V: Borrow<char>>(&mut self, v: V) -> Result<usize> {
self.write_u32(*v.borrow() as u32)
}

/// Write a `bool` to the stream.
pub fn write_bool(&mut self, value: bool) -> Result<usize> {
let written = self.write_u8(if value { 1 } else { 0 })?;
pub fn write_bool<V: Borrow<bool>>(&mut self, value: V) -> Result<usize> {
let written = self.write_u8(if *value.borrow() { 1 } else { 0 })?;
Ok(written)
}

/// Write a `f32` to the stream.
pub fn write_f32(&mut self, value: f32) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_f32<V: Borrow<f32>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a `f64` to the stream.
pub fn write_f64(&mut self, value: f64) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_f64<V: Borrow<f64>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write an `isize` to the stream.
pub fn write_isize(&mut self, value: isize) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_isize<V: Borrow<isize>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a `usize` to the stream.
pub fn write_usize(&mut self, value: usize) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_usize<V: Borrow<usize>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a `u64` to the stream.
pub fn write_u64(&mut self, value: u64) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_u64<V: Borrow<u64>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write an `i64` to the stream.
pub fn write_i64(&mut self, value: i64) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_i64<V: Borrow<i64>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a `u32` to the stream.
pub fn write_u32(&mut self, value: u32) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_u32<V: Borrow<u32>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write an `i32` to the stream.
pub fn write_i32(&mut self, value: i32) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_i32<V: Borrow<i32>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a `u16` to the stream.
pub fn write_u16(&mut self, value: u16) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_u16<V: Borrow<u16>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write an `i16` to the stream.
pub fn write_i16(&mut self, value: i16) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_i16<V: Borrow<i16>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a `u8` to the stream.
pub fn write_u8(&mut self, value: u8) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_u8<V: Borrow<u8>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write an `i8` to the stream.
pub fn write_i8(&mut self, value: i8) -> Result<usize> {
encode!(self.endian, &value, self.stream);
pub fn write_i8<V: Borrow<i8>>(&mut self, value: V) -> Result<usize> {
encode!(self.endian, value.borrow(), self.stream);
}

/// Write a byte buffer to the stream.
Expand Down
14 changes: 7 additions & 7 deletions src/filestream.rs → src/stream/file.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Stream for operating on files.
use crate::{Stream, Result, BinaryError};
use crate::{BinaryError, ReadStream, Result, SeekStream, WriteStream};
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
use std::io::{Error, ErrorKind, Read, SeekFrom, Write};
Expand Down Expand Up @@ -29,17 +29,14 @@ impl FileStream {
pub fn new<P: AsRef<Path>>(path: P, open_type: OpenType) -> Result<FileStream> {
let file = match open_type {
OpenType::OpenAndCreate => fs::File::create(path)?,
OpenType::ReadWrite => OpenOptions::new()
.read(true)
.write(true)
.open(path)?,
OpenType::ReadWrite => OpenOptions::new().read(true).write(true).open(path)?,
OpenType::Open => fs::File::open(path)?,
};
Ok(FileStream { file })
Ok(Self { file })
}
}

impl Stream for FileStream {
impl SeekStream for FileStream {
fn seek(&mut self, to: usize) -> Result<usize> {
Ok(self.file.seek(SeekFrom::Start(to as u64))? as usize)
}
Expand Down Expand Up @@ -74,3 +71,6 @@ impl Write for FileStream {
self.file.flush()
}
}

impl ReadStream for FileStream {}
impl WriteStream for FileStream {}
13 changes: 8 additions & 5 deletions src/memorystream.rs → src/stream/memory.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Stream for operating on in-memory buffers.
use crate::{Stream, BinaryError, Result};
//! Stream that reads from and writes to an owned buffer.
use crate::{BinaryError, ReadStream, Result, SeekStream, WriteStream};
use std::io::{Error, ErrorKind, Read, Write};

/// Stream that wraps an in-memory buffer.
/// Stream that wraps an owned buffer.
pub struct MemoryStream {
buffer: Vec<u8>,
position: usize,
Expand All @@ -11,14 +11,14 @@ pub struct MemoryStream {
impl MemoryStream {
/// Create a memory stream.
pub fn new() -> Self {
MemoryStream {
Self {
buffer: Vec::new(),
position: 0,
}
}
}

impl Stream for MemoryStream {
impl SeekStream for MemoryStream {
fn seek(&mut self, to: usize) -> Result<usize> {
self.position = to;
Ok(self.position)
Expand Down Expand Up @@ -95,3 +95,6 @@ impl Into<Vec<u8>> for MemoryStream {
self.buffer
}
}

impl ReadStream for MemoryStream {}
impl WriteStream for MemoryStream {}
3 changes: 3 additions & 0 deletions src/stream/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub(crate) mod file;
pub(crate) mod memory;
pub(crate) mod slice;
57 changes: 57 additions & 0 deletions src/stream/slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! Stream that reads from a slice of bytes.
use crate::{BinaryError, ReadStream, Result, SeekStream};
use std::io::{Error, ErrorKind, Read};

/// Stream that wraps a slice of bytes.
pub struct SliceStream<'a> {
buffer: &'a [u8],
position: usize,
}

impl<'a> SliceStream<'a> {
/// Create a slice stream.
pub fn new(buffer: &'a [u8]) -> Self {
Self {
buffer,
position: 0,
}
}
}

impl SeekStream for SliceStream<'_> {
fn seek(&mut self, to: usize) -> Result<usize> {
self.position = to;
Ok(self.position)
}

fn tell(&mut self) -> Result<usize> {
Ok(self.position)
}

fn len(&self) -> Result<usize> {
Ok(self.buffer.len())
}
}

impl Read for SliceStream<'_> {
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
if self.position + buffer.len() > self.buffer.len() {
return Err(Error::new(
ErrorKind::UnexpectedEof,
BinaryError::ReadPastEof,
));
}

let mut idx = 0;
for i in self.position..self.position + buffer.len() {
buffer[idx] = self.buffer[i];
idx += 1;
}

self.position += buffer.len();

Ok(buffer.len())
}
}

impl ReadStream for SliceStream<'_> {}

0 comments on commit 3aad6b8

Please sign in to comment.