mod boxbuf;
pub(crate) mod bytes;
pub(crate) mod chunk;
mod slice;
mod vec;
pub(crate) use boxbuf::*;
pub(crate) use bytes::*;
pub(crate) use chunk::*;
#[cfg(test)]
macro_rules! unsafe_slice {
($s:expr,$r:expr) => {
&$s[$r]
};
}
#[cfg(test)]
macro_rules! unsafe_slice_mut {
($s:expr,$r:expr) => {
&mut $s[$r]
};
}
#[cfg(not(test))]
macro_rules! unsafe_slice {
($s:expr,$r:expr) => {{
let slice = &*$s;
let index = $r;
unsafe { slice.get_unchecked(index) }
}};
}
#[cfg(not(test))]
macro_rules! unsafe_slice_mut {
($s:expr,$r:expr) => {{
let slice = &mut *$s;
let index = $r;
unsafe { slice.get_unchecked_mut(index) }
}};
}
pub(crate) use unsafe_slice;
pub(crate) use unsafe_slice_mut;
pub(crate) mod writer {
use core::num::NonZeroUsize;
use super::Chunk;
#[derive(Debug, Clone, Copy)]
pub(crate) struct DidntWrite;
pub(crate) trait Writer {
fn write(&mut self, bytes: &[u8]) -> Result<NonZeroUsize, DidntWrite>;
fn write_exact(&mut self, bytes: &[u8]) -> Result<(), DidntWrite>;
fn remaining(&self) -> usize;
fn write_u8(&mut self, byte: u8) -> Result<(), DidntWrite> {
self.write_exact(core::slice::from_ref(&byte))
}
fn write_chunk(&mut self, slice: &Chunk) -> Result<(), DidntWrite> {
self.write_exact(slice.as_slice())
}
#[allow(unused)]
fn can_write(&self) -> bool {
self.remaining() != 0
}
unsafe fn with_slot<F>(&mut self, len: usize, write: F) -> Result<NonZeroUsize, DidntWrite>
where
F: FnOnce(&mut [u8]) -> usize;
}
pub(crate) trait BacktrackableWriter: Writer {
type Mark;
fn mark(&mut self) -> Self::Mark;
fn rewind(&mut self, mark: Self::Mark) -> bool;
}
pub(crate) trait HasWriter {
type Writer: Writer;
fn writer(self) -> Self::Writer;
}
}
pub(crate) mod reader {
use core::num::NonZeroUsize;
use super::Chunk;
#[derive(Debug, Clone, Copy)]
pub(crate) struct DidntRead;
pub(crate) trait Reader {
fn read(&mut self, into: &mut [u8]) -> Result<NonZeroUsize, DidntRead>;
fn read_exact(&mut self, into: &mut [u8]) -> Result<(), DidntRead>;
fn remaining(&self) -> usize;
fn read_chunks<F: FnMut(Chunk)>(&mut self, len: usize, for_each_slice: F) -> Result<(), DidntRead>;
fn read_chunk(&mut self, len: usize) -> Result<Chunk, DidntRead>;
fn read_u8(&mut self) -> Result<u8, DidntRead> {
let mut byte = 0;
let read = self.read(core::slice::from_mut(&mut byte))?;
if read.get() == 1 { Ok(byte) } else { Err(DidntRead) }
}
fn can_read(&self) -> bool {
self.remaining() != 0
}
}
pub(crate) trait AdvanceableReader: Reader {
fn skip(&mut self, offset: usize) -> Result<(), DidntRead>;
fn backtrack(&mut self, offset: usize) -> Result<(), DidntRead>;
fn advance(&mut self, offset: isize) -> Result<(), DidntRead> {
if offset > 0 {
self.skip(offset as usize)
} else {
self.backtrack((-offset) as usize)
}
}
}
pub(crate) trait SeekableReader: Reader {
type Mark;
fn mark(&mut self) -> Self::Mark;
fn seek(&mut self, mark: Self::Mark) -> bool;
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct DidntSiphon;
pub(crate) trait SiphonableReader: Reader {
#[allow(unused)]
fn siphon<W>(&mut self, writer: &mut W) -> Result<NonZeroUsize, DidntSiphon>
where
W: super::writer::Writer;
}
pub(crate) trait HasReader {
type Reader: Reader;
fn reader(self) -> Self::Reader;
}
}
#[cfg(test)]
mod tests {
use super::{
BoxBuf, Bytes, Chunk,
reader::{HasReader, Reader, SiphonableReader},
writer::{HasWriter, Writer},
};
const BYTES: usize = 18;
const WBS0: u8 = 0;
const WBS1: u8 = 1;
const WBS2: [u8; 4] = [2, 3, 4, 5];
const WBS3: [u8; 4] = [6, 7, 8, 9];
const WBS4: [u8; 4] = [10, 11, 12, 13];
const WBS5: [u8; 4] = [14, 15, 16, 17];
macro_rules! run_write {
($buffer:expr) => {{
println!(">>> Write");
let mut writer = $buffer.writer();
assert!(writer.can_write());
writer.write_u8(WBS0).unwrap();
writer.write_u8(WBS1).unwrap();
let w = writer.write(&WBS2).unwrap();
assert_eq!(4, w.get());
writer.write_exact(&WBS3).unwrap();
writer.write_exact(&WBS4).unwrap();
unsafe {
writer.with_slot(4, |mut buffer| {
let w = buffer.write(&WBS5).unwrap();
assert_eq!(4, w.get());
w.get()
})
}
.unwrap();
}};
}
macro_rules! run_read {
($buffer:expr) => {
println!(">>> Read");
let mut reader = $buffer.reader();
let b = reader.read_u8().unwrap();
assert_eq!(WBS0, b);
assert_eq!(BYTES - 1, reader.remaining());
let b = reader.read_u8().unwrap();
assert_eq!(WBS1, b);
assert_eq!(BYTES - 2, reader.remaining());
let mut rbs: [u8; 4] = [0, 0, 0, 0];
let r = reader.read(&mut rbs).unwrap();
assert_eq!(4, r.get());
assert_eq!(BYTES - 6, reader.remaining());
assert_eq!(WBS2, rbs);
reader.read_exact(&mut rbs).unwrap();
assert_eq!(BYTES - 10, reader.remaining());
assert_eq!(WBS3, rbs);
reader.read_exact(&mut rbs).unwrap();
assert_eq!(BYTES - 14, reader.remaining());
assert_eq!(WBS4, rbs);
reader.read_exact(&mut rbs).unwrap();
assert_eq!(BYTES - 18, reader.remaining());
assert_eq!(WBS5, rbs);
assert!(reader.read(&mut rbs).is_err());
assert!(reader.read_u8().is_err());
assert!(reader.read_exact(&mut rbs).is_err());
};
}
macro_rules! run_empty {
($buffer:expr) => {
let mut s = [0u8; 64];
println!(">>> Read empty");
let mut reader = $buffer.reader();
assert!(reader.read_u8().is_err());
assert!(reader.read(&mut s).is_err());
assert!(reader.read_exact(&mut s).is_err());
};
}
macro_rules! run_bound {
($buffer:expr, $capacity:expr) => {
println!(">>> Write bound");
let mut writer = $buffer.writer();
for i in 0..$capacity {
writer.write_u8(i as u8).unwrap();
}
assert!(writer.write_u8(0).is_err());
println!(">>> Read bound");
let mut reader = $buffer.reader();
for i in 0..$capacity {
let j = reader.read_u8().unwrap();
assert_eq!(i as u8, j);
}
assert!(reader.read_u8().is_err());
};
}
macro_rules! run_siphon {
($from:expr, $fcap:expr, $into:expr, $icap:expr) => {
println!(">>> Write siphon");
{
let mut writer = $from.writer();
for i in 0..$fcap {
writer.write_u8(i as u8).unwrap();
}
}
println!(">>> Read siphon");
let mut reader = $from.reader();
let mut read = 0;
while read < $fcap {
$into.clear();
let written = {
let mut writer = $into.writer();
reader.siphon(&mut writer).unwrap()
};
let mut reader = $into.reader();
for i in read..read + written.get() {
let j = reader.read_u8().unwrap();
assert_eq!(i as u8, j);
}
assert!(reader.read_u8().is_err());
read += written.get();
}
assert_eq!(read, $fcap);
};
}
#[test]
fn buffer_slice() {
println!("Buffer Slice");
let mut sbuf = [0u8; BYTES];
run_write!(sbuf.as_mut());
run_read!(sbuf.as_mut());
}
#[test]
fn buffer_vec() {
println!("Buffer Vec");
let mut vbuf = vec![];
run_empty!(vbuf);
run_write!(&mut vbuf);
run_read!(&vbuf);
}
#[test]
fn buffer_bbuf() {
println!("Buffer BBuf");
let capacity = 1 + u8::MAX as usize;
let mut bbuf = BoxBuf::with_capacity(capacity);
run_empty!(bbuf);
run_write!(bbuf);
run_read!(bbuf);
bbuf.clear();
run_bound!(bbuf, capacity);
}
#[test]
fn buffer_bytes() {
println!("Buffer Bytes");
let mut bytes = Bytes::new();
run_empty!(bytes);
run_write!(bytes);
run_read!(bytes);
let bytes = Bytes::from(vec![]);
run_empty!(bytes);
}
#[test]
fn buffer_chunk() {
println!("Buffer Chunk");
let mut vbuf = vec![];
run_write!(&mut vbuf);
let mut chunk = Chunk::from(vbuf);
run_read!(chunk);
let mut chunk = Chunk::from(vec![]);
run_empty!(chunk);
}
#[test]
fn buffer_siphon() {
let capacity = 1 + u8::MAX as usize;
println!("Buffer Siphon BBuf({capacity}) -> BBuf({capacity})");
let mut bbuf1 = BoxBuf::with_capacity(capacity);
let mut bbuf2 = BoxBuf::with_capacity(capacity);
run_siphon!(bbuf1, capacity, bbuf2, capacity);
println!("Buffer Siphon Bytes({capacity}) -> Bytes({capacity})");
let mut bytes1 = Bytes::new();
let mut bytes2 = Bytes::new();
run_siphon!(bytes1, capacity, bytes2, capacity);
println!("Buffer Siphon Bytes({capacity}) -> BBuf({capacity})");
let mut bytes1 = Bytes::new();
let mut bbuf1 = BoxBuf::with_capacity(capacity);
run_siphon!(bytes1, capacity, bbuf1, capacity);
let capacity2 = 1 + capacity / 2;
println!("Buffer Siphon BBuf({capacity}) -> BBuf({capacity2})");
let mut bbuf1 = BoxBuf::with_capacity(capacity);
let mut bbuf2 = BoxBuf::with_capacity(capacity2);
run_siphon!(bbuf1, capacity, bbuf2, capacity2);
println!("Buffer Siphon Bytes({capacity}) -> BBuf({capacity2})");
let mut bytes1 = Bytes::new();
let mut bbuf1 = BoxBuf::with_capacity(capacity2);
run_siphon!(bytes1, capacity, bbuf1, capacity2);
}
}