use crate::errors::AvroError;
use crate::reader::vlq::VLQDecoder;
#[derive(Debug, Default)]
pub struct Block {
pub count: usize,
pub data: Vec<u8>,
pub sync: [u8; 16],
}
#[derive(Debug)]
pub struct BlockDecoder {
state: BlockDecoderState,
in_progress: Block,
vlq_decoder: VLQDecoder,
bytes_remaining: usize,
}
#[derive(Debug)]
pub(crate) enum BlockDecoderState {
Count,
Size,
Data,
Sync,
Finished,
}
impl Default for BlockDecoder {
fn default() -> Self {
Self {
state: BlockDecoderState::Count,
in_progress: Default::default(),
vlq_decoder: Default::default(),
bytes_remaining: 0,
}
}
}
impl BlockDecoder {
pub fn decode(&mut self, mut buf: &[u8]) -> Result<usize, AvroError> {
let max_read = buf.len();
while !buf.is_empty() {
match self.state {
BlockDecoderState::Count => {
if let Some(c) = self.vlq_decoder.long(&mut buf)? {
self.in_progress.count = c.try_into().map_err(|_| {
AvroError::ParseError(format!(
"Block count cannot be negative, got {c}"
))
})?;
self.state = BlockDecoderState::Size;
}
}
BlockDecoderState::Size => {
if let Some(c) = self.vlq_decoder.long(&mut buf)? {
self.bytes_remaining = c.try_into().map_err(|_| {
AvroError::ParseError(format!("Block size cannot be negative, got {c}"))
})?;
self.in_progress
.data
.reserve(self.bytes_remaining.min(buf.len()));
self.state = BlockDecoderState::Data;
}
}
BlockDecoderState::Data => {
let to_read = self.bytes_remaining.min(buf.len());
self.in_progress.data.extend_from_slice(&buf[..to_read]);
buf = &buf[to_read..];
self.bytes_remaining -= to_read;
if self.bytes_remaining == 0 {
self.bytes_remaining = 16;
self.state = BlockDecoderState::Sync;
}
}
BlockDecoderState::Sync => {
let to_decode = buf.len().min(self.bytes_remaining);
let write = &mut self.in_progress.sync[16 - to_decode..];
write[..to_decode].copy_from_slice(&buf[..to_decode]);
self.bytes_remaining -= to_decode;
buf = &buf[to_decode..];
if self.bytes_remaining == 0 {
self.state = BlockDecoderState::Finished;
}
}
BlockDecoderState::Finished => return Ok(max_read - buf.len()),
}
}
Ok(max_read)
}
pub fn flush(&mut self) -> Option<Block> {
match self.state {
BlockDecoderState::Finished => {
self.state = BlockDecoderState::Count;
Some(std::mem::take(&mut self.in_progress))
}
_ => None,
}
}
}
#[cfg(feature = "async")]
impl BlockDecoder {
pub(crate) fn state(&self) -> &BlockDecoderState {
&self.state
}
pub(crate) fn bytes_remaining(&self) -> usize {
self.bytes_remaining
}
}
#[cfg(test)]
mod tests {
use super::*;
fn encode_long(value: i64, out: &mut Vec<u8>) {
let mut n = ((value << 1) ^ (value >> 63)) as u64;
while n >= 0x80 {
out.push((n as u8) | 0x80);
n >>= 7;
}
out.push(n as u8);
}
#[test]
fn test_oversized_block_size_bounds_reserve() {
let mut buf = Vec::new();
encode_long(1, &mut buf); encode_long(i64::MAX, &mut buf); buf.extend_from_slice(&[0u8; 8]);
let mut decoder = BlockDecoder::default();
let read = decoder.decode(&buf).unwrap();
assert_eq!(read, buf.len(), "all available input should be consumed");
assert!(
decoder.in_progress.data.capacity() <= buf.len(),
"capacity {} must stay bounded by available input {}, not the advertised i64::MAX",
decoder.in_progress.data.capacity(),
buf.len(),
);
}
#[test]
fn test_negative_block_size_errors() {
let mut buf = Vec::new();
encode_long(1, &mut buf); encode_long(-1, &mut buf);
let mut decoder = BlockDecoder::default();
let err = decoder.decode(&buf).unwrap_err();
assert!(
err.to_string().contains("Block size cannot be negative"),
"unexpected error: {err}",
);
}
#[test]
fn test_well_formed_block_round_trips() {
let payload = [1u8, 2, 3, 4];
let sync = [7u8; 16];
let mut buf = Vec::new();
encode_long(2, &mut buf); encode_long(payload.len() as i64, &mut buf); buf.extend_from_slice(&payload);
buf.extend_from_slice(&sync);
let mut decoder = BlockDecoder::default();
assert_eq!(decoder.decode(&buf).unwrap(), buf.len());
let block = decoder.flush().expect("a complete block");
assert_eq!(block.count, 2);
assert_eq!(block.data, payload);
assert_eq!(block.sync, sync);
}
}