diff --git a/Cargo.lock b/Cargo.lock index 941419c..4023d2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.2.1" @@ -628,6 +634,7 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" name = "sdk" version = "0.1.0" dependencies = [ + "byteorder", "color-eyre", "csv-async", "futures", diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index 82d2552..dda9084 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +byteorder = "1.4.3" color-eyre = "0.6.2" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } futures = "0.3.25" diff --git a/lib/sdk/src/binary.rs b/lib/sdk/src/binary.rs index 925f61f..53f93eb 100644 --- a/lib/sdk/src/binary.rs +++ b/lib/sdk/src/binary.rs @@ -152,3 +152,156 @@ where Ok(0) } } + +pub mod sync { + use std::io::{self, Read, Seek, SeekFrom, Write}; + + use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + use color_eyre::eyre::WrapErr; + use color_eyre::{Help, Result, SectionExt}; + + macro_rules! make_read { + ($func:ident, $read:ident, $type:ty) => { + fn $read(&mut self) -> io::Result<$type> { + ReadBytesExt::$func::(&mut self) + } + + fn $func(&mut self) -> Result<$type> { + let res = ReadExt::$read(&mut self) + .wrap_err(concat!("failed to read ", stringify!($type))); + + if res.is_ok() { + return res; + } + + let pos = self.stream_position(); + if pos.is_ok() { + res.with_section(|| { + format!("{pos:#X} ({pos})", pos = pos.unwrap()).header("Position: ") + }) + } else { + res + } + } + }; + } + + macro_rules! make_write { + ($func:ident, $write:ident, $type:ty) => { + fn $write(&mut self, val: $type) -> io::Result<()> { + WriteBytesExt::$func::(&mut self, val) + } + + fn $func(&mut self, val: $type) -> Result<()> { + let res = WriteExt::$write(&mut self, val) + .wrap_err(concat!("failed to write ", stringify!($type))); + + if res.is_ok() { + return res; + } + + let pos = self.stream_position(); + if pos.is_ok() { + res.with_section(|| { + format!("{pos:#X} ({pos})", pos = pos.unwrap()).header("Position: ") + }) + } else { + res + } + } + }; + } + + macro_rules! make_skip { + ($func:ident, $read:ident, $type:ty) => { + fn $func(&mut self, cmp: $type) -> Result<()> { + let val = ReadExt::$read(&mut self)?; + + if val != cmp { + let pos = self.stream_position().unwrap_or(u64::MAX); + tracing::debug!( + pos, + expected = cmp, + actual = val, + "Unexpected value for skipped {}", + stringify!($type) + ); + } + + Ok(()) + } + }; + } + + pub trait ReadExt: ReadBytesExt + Seek { + fn read_u8(&mut self) -> io::Result { + ReadBytesExt::read_u8(&mut self) + } + + make_read!(read_u32, read_u32_le, u32); + make_read!(read_u64, read_u64_le, u64); + + make_skip!(skip_u8, read_u8, u8); + make_skip!(skip_u32, read_u32, u32); + + fn skip_padding(&mut self) -> io::Result<()> { + let pos = self.stream_position()?; + let padding_size = 16 - (pos % 16); + + if padding_size < 16 && padding_size > 0 { + tracing::trace!(pos, padding_size, "Skipping padding"); + self.seek(SeekFrom::Current(padding_size as i64))?; + } else { + tracing::trace!(pos, padding_size, "No padding to skip"); + } + + Ok(()) + } + } + + pub trait WriteExt: WriteBytesExt + Seek { + make_write!(write_u32, write_u32_le, u32); + make_write!(write_u64, write_u64_le, u64); + + fn write_padding(&mut self) -> io::Result { + let pos = self.stream_position()?; + let size = 16 - (pos % 16) as usize; + + tracing::trace!(padding_size = size, "Writing padding"); + + if size > 0 && size < 16 { + let buf = vec![0; size]; + self.write_all(&buf)?; + Ok(size) + } else { + Ok(0) + } + } + } + + impl ReadExt for R {} + impl WriteExt for W {} + + pub(crate) fn _read_up_to(r: &mut R, buf: &mut Vec) -> Result + where + R: Read + Seek, + { + let pos = r.stream_position()?; + + let err = { + match r.read_exact(buf) { + Ok(_) => return Ok(buf.len()), + Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => { + r.seek(SeekFrom::Start(pos))?; + match r.read_to_end(buf) { + Ok(read) => return Ok(read), + Err(err) => err, + } + } + Err(err) => err, + } + }; + + Err(err).with_section(|| format!("{pos:#X} ({pos})", pos = pos).header("Position: ")) + } +}