arrow_avro/reader/
block.rs1use crate::errors::AvroError;
21use crate::reader::vlq::VLQDecoder;
22
23#[derive(Debug, Default)]
27pub struct Block {
28 pub count: usize,
30 pub data: Vec<u8>,
32 pub sync: [u8; 16],
34}
35
36#[derive(Debug)]
38pub struct BlockDecoder {
39 state: BlockDecoderState,
40 in_progress: Block,
41 vlq_decoder: VLQDecoder,
42 bytes_remaining: usize,
43}
44
45#[derive(Debug)]
46pub(crate) enum BlockDecoderState {
47 Count,
48 Size,
49 Data,
50 Sync,
51 Finished,
52}
53
54impl Default for BlockDecoder {
55 fn default() -> Self {
56 Self {
57 state: BlockDecoderState::Count,
58 in_progress: Default::default(),
59 vlq_decoder: Default::default(),
60 bytes_remaining: 0,
61 }
62 }
63}
64
65impl BlockDecoder {
66 pub fn decode(&mut self, mut buf: &[u8]) -> Result<usize, AvroError> {
79 let max_read = buf.len();
80 while !buf.is_empty() {
81 match self.state {
82 BlockDecoderState::Count => {
83 if let Some(c) = self.vlq_decoder.long(&mut buf)? {
84 self.in_progress.count = c.try_into().map_err(|_| {
85 AvroError::ParseError(format!(
86 "Block count cannot be negative, got {c}"
87 ))
88 })?;
89
90 self.state = BlockDecoderState::Size;
91 }
92 }
93 BlockDecoderState::Size => {
94 if let Some(c) = self.vlq_decoder.long(&mut buf)? {
95 self.bytes_remaining = c.try_into().map_err(|_| {
96 AvroError::ParseError(format!("Block size cannot be negative, got {c}"))
97 })?;
98
99 self.in_progress
104 .data
105 .reserve(self.bytes_remaining.min(buf.len()));
106 self.state = BlockDecoderState::Data;
107 }
108 }
109 BlockDecoderState::Data => {
110 let to_read = self.bytes_remaining.min(buf.len());
111 self.in_progress.data.extend_from_slice(&buf[..to_read]);
112 buf = &buf[to_read..];
113 self.bytes_remaining -= to_read;
114 if self.bytes_remaining == 0 {
115 self.bytes_remaining = 16;
116 self.state = BlockDecoderState::Sync;
117 }
118 }
119 BlockDecoderState::Sync => {
120 let to_decode = buf.len().min(self.bytes_remaining);
121 let offset = 16 - self.bytes_remaining;
123 self.in_progress.sync[offset..offset + to_decode]
124 .copy_from_slice(&buf[..to_decode]);
125 self.bytes_remaining -= to_decode;
126 buf = &buf[to_decode..];
127 if self.bytes_remaining == 0 {
128 self.state = BlockDecoderState::Finished;
129 }
130 }
131 BlockDecoderState::Finished => return Ok(max_read - buf.len()),
132 }
133 }
134 Ok(max_read)
135 }
136
137 pub fn flush(&mut self) -> Option<Block> {
139 match self.state {
140 BlockDecoderState::Finished => {
141 self.state = BlockDecoderState::Count;
142 Some(std::mem::take(&mut self.in_progress))
143 }
144 _ => None,
145 }
146 }
147}
148
149#[cfg(feature = "async")]
150impl BlockDecoder {
151 pub(crate) fn state(&self) -> &BlockDecoderState {
152 &self.state
153 }
154
155 pub(crate) fn bytes_remaining(&self) -> usize {
156 self.bytes_remaining
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 fn encode_long(value: i64, out: &mut Vec<u8>) {
166 let mut n = ((value << 1) ^ (value >> 63)) as u64;
167 while n >= 0x80 {
168 out.push((n as u8) | 0x80);
169 n >>= 7;
170 }
171 out.push(n as u8);
172 }
173
174 #[test]
175 fn test_oversized_block_size_bounds_reserve() {
176 let mut buf = Vec::new();
179 encode_long(1, &mut buf); encode_long(i64::MAX, &mut buf); buf.extend_from_slice(&[0u8; 8]); let mut decoder = BlockDecoder::default();
184 let read = decoder.decode(&buf).unwrap();
185
186 assert_eq!(read, buf.len(), "all available input should be consumed");
187 assert!(
188 decoder.in_progress.data.capacity() <= buf.len(),
189 "capacity {} must stay bounded by available input {}, not the advertised i64::MAX",
190 decoder.in_progress.data.capacity(),
191 buf.len(),
192 );
193 }
194
195 #[test]
196 fn test_negative_block_size_errors() {
197 let mut buf = Vec::new();
198 encode_long(1, &mut buf); encode_long(-1, &mut buf); let mut decoder = BlockDecoder::default();
202 let err = decoder.decode(&buf).unwrap_err();
203 assert!(
204 err.to_string().contains("Block size cannot be negative"),
205 "unexpected error: {err}",
206 );
207 }
208
209 #[test]
210 fn test_well_formed_block_round_trips() {
211 let payload = [1u8, 2, 3, 4];
213 let sync = [7u8; 16];
214 let mut buf = Vec::new();
215 encode_long(2, &mut buf); encode_long(payload.len() as i64, &mut buf); buf.extend_from_slice(&payload);
218 buf.extend_from_slice(&sync);
219
220 let mut decoder = BlockDecoder::default();
221 assert_eq!(decoder.decode(&buf).unwrap(), buf.len());
222 let block = decoder.flush().expect("a complete block");
223 assert_eq!(block.count, 2);
224 assert_eq!(block.data, payload);
225 assert_eq!(block.sync, sync);
226 }
227
228 #[test]
229 fn test_sync_marker_split_across_decode_calls() {
230 let sync: [u8; 16] = core::array::from_fn(|i| i as u8);
232 let mut block_bytes = vec![0x02, 0x02, 0xAA];
233 block_bytes.extend_from_slice(&sync);
234
235 for chunk_size in 1..block_bytes.len() {
236 let mut decoder = BlockDecoder::default();
237 for chunk in block_bytes.chunks(chunk_size) {
238 decoder.decode(chunk).unwrap();
239 }
240 let block = decoder.flush().expect("complete block");
241 assert_eq!(block.count, 1);
242 assert_eq!(block.data, vec![0xAA]);
243 assert_eq!(block.sync, sync, "chunk_size {chunk_size}");
244 }
245 }
246}