use crate::lib::std::{
fmt,
io::{self, Seek as _, SeekFrom, Write},
};
pub type GenResult<W> = Result<WriteContext<W>, GenError>;
#[derive(Debug)]
pub enum GenError {
BufferTooSmall(usize),
BufferTooBig(usize),
InvalidOffset,
IoError(io::Error),
CustomError(u32),
NotYetImplemented,
}
impl fmt::Display for GenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(feature = "std")]
impl std::error::Error for GenError {}
impl From<io::Error> for GenError {
fn from(err: io::Error) -> Self {
GenError::IoError(err)
}
}
pub trait SerializeFn<W>: Fn(WriteContext<W>) -> GenResult<W> {}
impl<W, F: Fn(WriteContext<W>) -> GenResult<W>> SerializeFn<W> for F {}
pub struct WriteContext<W> {
pub write: W,
pub position: u64,
}
impl<W: Write> From<W> for WriteContext<W> {
fn from(write: W) -> Self {
Self { write, position: 0 }
}
}
impl<W: Write> WriteContext<W> {
pub fn into_inner(self) -> (W, u64) {
(self.write, self.position)
}
}
impl<W: Write> Write for WriteContext<W> {
fn write(&mut self, data: &[u8]) -> crate::lib::std::io::Result<usize> {
let amt = self.write.write(data)?;
self.position += amt as u64;
Ok(amt)
}
#[cfg(feature = "std")]
fn flush(&mut self) -> io::Result<()> {
self.write.flush()
}
}
impl<W: Seek> io::Seek for WriteContext<W> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let old_pos = self.write.stream_position()?;
let new_pos = self.write.seek(pos)?;
if new_pos >= old_pos {
self.position += new_pos - old_pos;
} else {
self.position -= old_pos - new_pos;
}
Ok(new_pos)
}
}
pub fn gen<W: Write, F: SerializeFn<W>>(f: F, w: W) -> Result<(W, u64), GenError> {
f(WriteContext::from(w)).map(|ctx| ctx.into_inner())
}
pub fn gen_simple<W: Write, F: SerializeFn<W>>(f: F, w: W) -> Result<W, GenError> {
f(WriteContext::from(w)).map(|ctx| ctx.into_inner().0)
}
pub trait Skip: Write {
fn skip(s: WriteContext<Self>, sz: usize) -> GenResult<Self>
where
Self: Sized;
}
pub trait BackToTheBuffer: Write {
fn reserve_write_use<
Tmp,
Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
>(
s: WriteContext<Self>,
reserved: usize,
gen: &Gen,
before: &Before,
) -> Result<WriteContext<Self>, GenError>
where
Self: Sized;
}
pub trait Seek: Write + io::Seek {}
impl Seek for io::Cursor<&mut [u8]> {}
impl<W: Seek> BackToTheBuffer for W {
fn reserve_write_use<
Tmp,
Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
>(
mut s: WriteContext<Self>,
reserved: usize,
gen: &Gen,
before: &Before,
) -> Result<WriteContext<Self>, GenError> {
let start = s.stream_position()?;
let begin = s.seek(SeekFrom::Current(reserved as i64))?;
let (mut buf, tmp) = gen(s)?;
let end = buf.stream_position()?;
buf.seek(SeekFrom::Start(start))?;
let mut buf = before(buf, tmp)?;
let pos = buf.stream_position()?;
if pos != begin {
return Err(GenError::BufferTooBig((begin - pos) as usize));
}
buf.seek(SeekFrom::Start(end))?;
Ok(buf)
}
}
impl Skip for &mut [u8] {
fn skip(s: WriteContext<Self>, len: usize) -> Result<WriteContext<Self>, GenError> {
if s.write.len() < len {
Err(GenError::BufferTooSmall(len - s.write.len()))
} else {
Ok(WriteContext {
write: &mut s.write[len..],
position: s.position + len as u64,
})
}
}
}
impl Skip for io::Cursor<&mut [u8]> {
fn skip(mut s: WriteContext<Self>, len: usize) -> GenResult<Self> {
let remaining = s
.write
.get_ref()
.len()
.saturating_sub(s.write.position() as usize);
if remaining < len {
Err(GenError::BufferTooSmall(len - remaining))
} else {
let cursor_position = s.write.position();
s.write.set_position(cursor_position + len as u64);
s.position += len as u64;
Ok(s)
}
}
}
impl BackToTheBuffer for &mut [u8] {
fn reserve_write_use<
Tmp,
Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
>(
s: WriteContext<Self>,
reserved: usize,
gen: &Gen,
before: &Before,
) -> Result<WriteContext<Self>, GenError> {
let WriteContext {
write: slice,
position: original_position,
} = s;
let (res, buf) = slice.split_at_mut(reserved);
let (new_context, tmp) = gen(WriteContext {
write: buf,
position: original_position + reserved as u64,
})?;
let res = before(
WriteContext {
write: res,
position: original_position,
},
tmp,
)?;
if !res.write.is_empty() {
return Err(GenError::BufferTooBig(res.write.len()));
}
Ok(new_context)
}
}
#[cfg(feature = "std")]
impl BackToTheBuffer for Vec<u8> {
fn reserve_write_use<
Tmp,
Gen: Fn(WriteContext<Self>) -> Result<(WriteContext<Self>, Tmp), GenError>,
Before: Fn(WriteContext<Self>, Tmp) -> GenResult<Self>,
>(
s: WriteContext<Self>,
reserved: usize,
gen: &Gen,
before: &Before,
) -> Result<WriteContext<Self>, GenError> {
let WriteContext {
write: mut vec,
position: original_position,
} = s;
let start_len = vec.len();
vec.extend(std::iter::repeat(0).take(reserved));
let (mut new_context, tmp) = gen(WriteContext {
write: vec,
position: original_position + reserved as u64,
})?;
let tmp_context = before(
WriteContext {
write: Vec::new(),
position: original_position,
},
tmp,
)?;
let tmp_written = tmp_context.write.len();
if tmp_written != reserved {
return Err(GenError::BufferTooBig(reserved - tmp_written));
}
new_context.write[start_len..(start_len + reserved)]
.copy_from_slice(&tmp_context.write[..]);
Ok(new_context)
}
}