use std::io::Read;
use serde::{Deserialize, Serialize};
use crate::error::MuninError;
const MAX_VARINT_BYTES: usize = 5;
pub fn read_7bit_int(reader: &mut impl Read) -> Result<i32, MuninError> {
let mut result: u32 = 0;
let mut shift: u32 = 0;
let mut buf = [0u8; 1];
for _ in 0..MAX_VARINT_BYTES {
reader.read_exact(&mut buf)?;
let byte = buf[0];
result |= ((byte & 0x7F) as u32) << shift;
if byte & 0x80 == 0 {
return Ok(result as i32);
}
shift += 7;
}
Err(MuninError::OverlongVarInt)
}
pub const MAX_BINLOG_FIELD_LEN: usize = 256 * 1024 * 1024;
pub const MAX_BINLOG_ELEMENT_COUNT: usize = 1 << 20;
pub fn read_7bit_length(reader: &mut impl Read, what: &'static str) -> Result<usize, MuninError> {
let n = read_7bit_int(reader)?;
if n < 0 {
return Err(MuninError::InvalidFormat(format!("negative {what}: {n}")));
}
let n = n as usize;
if n > MAX_BINLOG_FIELD_LEN {
return Err(MuninError::InvalidFormat(format!(
"{what} too large: {n} (max {MAX_BINLOG_FIELD_LEN})"
)));
}
Ok(n)
}
pub fn read_7bit_count(reader: &mut impl Read, what: &'static str) -> Result<usize, MuninError> {
let n = read_7bit_int(reader)?;
if n < 0 {
return Err(MuninError::InvalidFormat(format!("negative {what}: {n}")));
}
let n = n as usize;
if n > MAX_BINLOG_ELEMENT_COUNT {
return Err(MuninError::InvalidFormat(format!(
"{what} too large: {n} (max {MAX_BINLOG_ELEMENT_COUNT})"
)));
}
Ok(n)
}
pub fn read_dotnet_string(reader: &mut impl Read) -> Result<String, MuninError> {
let len = read_7bit_length(reader, "string length")?;
if len == 0 {
return Ok(String::new());
}
let mut buf = vec![0u8; len];
reader.read_exact(&mut buf)?;
String::from_utf8(buf).map_err(|_| MuninError::InvalidUtf8)
}
pub fn read_bool(reader: &mut impl Read) -> Result<bool, MuninError> {
let mut buf = [0u8; 1];
reader.read_exact(&mut buf)?;
Ok(buf[0] != 0)
}
pub fn read_i32_le(reader: &mut impl Read) -> Result<i32, MuninError> {
let mut buf = [0u8; 4];
reader.read_exact(&mut buf)?;
Ok(i32::from_le_bytes(buf))
}
pub fn read_i64_le(reader: &mut impl Read) -> Result<i64, MuninError> {
let mut buf = [0u8; 8];
reader.read_exact(&mut buf)?;
Ok(i64::from_le_bytes(buf))
}
pub fn read_guid(reader: &mut impl Read) -> Result<[u8; 16], MuninError> {
let mut buf = [0u8; 16];
reader.read_exact(&mut buf)?;
Ok(buf)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct BinlogDateTime {
pub ticks: i64,
pub kind: i32,
}
pub fn read_datetime(reader: &mut impl Read) -> Result<BinlogDateTime, MuninError> {
let ticks = read_i64_le(reader)?;
let kind = read_7bit_int(reader)?;
Ok(BinlogDateTime { ticks, kind })
}
pub fn read_timespan_ticks(reader: &mut impl Read) -> Result<i64, MuninError> {
read_i64_le(reader)
}
pub fn skip_bytes(reader: &mut impl Read, n: usize) -> Result<(), MuninError> {
let mut remaining = n;
let mut buf = [0u8; 4096];
while remaining > 0 {
let to_read = remaining.min(buf.len());
reader.read_exact(&mut buf[..to_read])?;
remaining -= to_read;
}
Ok(())
}
#[cfg(test)]
mod tests;