1use std::collections::HashMap;
19use std::fmt::Debug;
20use std::sync::Arc;
21
22use arrow_array::{ArrayRef, RecordBatch};
23use arrow_buffer::{Buffer, MutableBuffer};
24use arrow_data::UnsafeFlag;
25use arrow_schema::{ArrowError, SchemaRef};
26
27use crate::convert::MessageBuffer;
28use crate::reader::{MAX_PREALLOC_BYTES, RecordBatchDecoder, read_dictionary_impl};
29use crate::{CONTINUATION_MARKER, MessageHeader};
30
31#[derive(Debug, Default)]
35pub struct StreamDecoder {
36 schema: Option<SchemaRef>,
38 dictionaries: HashMap<i64, ArrayRef>,
40 state: DecoderState,
42 buf: MutableBuffer,
44 require_alignment: bool,
46 skip_validation: UnsafeFlag,
51}
52
53#[derive(Debug)]
54enum DecoderState {
55 Header {
57 buf: [u8; 4],
59 read: u8,
61 continuation: bool,
63 },
64 Message {
66 size: u32,
68 },
69 Body {
71 message: MessageBuffer,
73 },
74 Finished,
76}
77
78impl Default for DecoderState {
79 fn default() -> Self {
80 Self::Header {
81 buf: [0; 4],
82 read: 0,
83 continuation: false,
84 }
85 }
86}
87
88impl StreamDecoder {
89 pub fn new() -> Self {
91 Self::default()
92 }
93
94 pub fn with_require_alignment(mut self, require_alignment: bool) -> Self {
107 self.require_alignment = require_alignment;
108 self
109 }
110
111 pub fn schema(&self) -> Option<SchemaRef> {
113 self.schema.as_ref().map(|schema| schema.clone())
114 }
115
116 pub unsafe fn with_skip_validation(mut self, skip_validation: bool) -> Self {
126 unsafe { self.skip_validation.set(skip_validation) };
127 self
128 }
129
130 pub fn decode(&mut self, buffer: &mut Buffer) -> Result<Option<RecordBatch>, ArrowError> {
160 while !buffer.is_empty() {
161 match &mut self.state {
162 DecoderState::Header {
163 buf,
164 read,
165 continuation,
166 } => {
167 let offset_buf = &mut buf[*read as usize..];
168 let to_read = buffer.len().min(offset_buf.len());
169 offset_buf[..to_read].copy_from_slice(&buffer[..to_read]);
170 *read += to_read as u8;
171 buffer.advance(to_read);
172 if *read == 4 {
173 if !*continuation && buf == &CONTINUATION_MARKER {
174 *continuation = true;
175 *read = 0;
176 continue;
177 }
178 let size = u32::from_le_bytes(*buf);
179
180 if size == 0 {
181 self.state = DecoderState::Finished;
182 continue;
183 }
184 self.state = DecoderState::Message { size };
185 }
186 }
187 DecoderState::Message { size } => {
188 let len = *size as usize;
189 if self.buf.is_empty() && buffer.len() > len {
190 let message = MessageBuffer::try_new(buffer.slice_with_length(0, len))?;
191 self.state = DecoderState::Body { message };
192 buffer.advance(len);
193 continue;
194 }
195
196 reserve_declared(&mut self.buf, len)?;
197 let to_read = buffer.len().min(len - self.buf.len());
198 self.buf
199 .try_extend_from_slice(&buffer[..to_read])
200 .map_err(|e| ArrowError::MemoryError(e.to_string()))?;
201 buffer.advance(to_read);
202 if self.buf.len() == len {
203 let message = MessageBuffer::try_new(std::mem::take(&mut self.buf).into())?;
204 self.state = DecoderState::Body { message };
205 }
206 }
207 DecoderState::Body { message } => {
208 let message = message.as_ref();
209 let body_length = message.bodyLength() as usize;
210
211 let body = if self.buf.is_empty() && buffer.len() >= body_length {
212 let body = buffer.slice_with_length(0, body_length);
213 buffer.advance(body_length);
214 body
215 } else {
216 reserve_declared(&mut self.buf, body_length)?;
217 let to_read = buffer.len().min(body_length - self.buf.len());
218 self.buf
219 .try_extend_from_slice(&buffer[..to_read])
220 .map_err(|e| ArrowError::MemoryError(e.to_string()))?;
221 buffer.advance(to_read);
222
223 if self.buf.len() != body_length {
224 continue;
225 }
226 std::mem::take(&mut self.buf).into()
227 };
228
229 let version = message.version();
230 match message.header_type() {
231 MessageHeader::Schema => {
232 if self.schema.is_some() {
233 return Err(ArrowError::IpcError(
234 "Not expecting a schema when messages are read".to_string(),
235 ));
236 }
237
238 let ipc_schema = message.header_as_schema().ok_or_else(|| {
239 ArrowError::ParseError(
240 "Unable to read IPC message as schema".to_string(),
241 )
242 })?;
243 let schema = crate::convert::try_fb_to_schema(ipc_schema)?;
244 self.state = DecoderState::default();
245 self.schema = Some(Arc::new(schema));
246 }
247 MessageHeader::RecordBatch => {
248 let batch = message.header_as_record_batch().unwrap();
249 let schema = self.schema.clone().ok_or_else(|| {
250 ArrowError::IpcError("Missing schema".to_string())
251 })?;
252 let batch = RecordBatchDecoder::try_new(
253 &body,
254 batch,
255 schema,
256 &self.dictionaries,
257 &version,
258 )?
259 .with_require_alignment(self.require_alignment)
260 .read_record_batch()?;
261 self.state = DecoderState::default();
262 return Ok(Some(batch));
263 }
264 MessageHeader::DictionaryBatch => {
265 let dictionary = message.header_as_dictionary_batch().unwrap();
266 let schema = self.schema.as_deref().ok_or_else(|| {
267 ArrowError::IpcError("Missing schema".to_string())
268 })?;
269 read_dictionary_impl(
270 &body,
271 dictionary,
272 schema,
273 &mut self.dictionaries,
274 &version,
275 self.require_alignment,
276 self.skip_validation.clone(),
277 )?;
278 self.state = DecoderState::default();
279 }
280 MessageHeader::NONE => {
281 self.state = DecoderState::default();
282 }
283 t => {
284 return Err(ArrowError::IpcError(format!(
285 "Message type unsupported by StreamDecoder: {t:?}"
286 )));
287 }
288 }
289 }
290 DecoderState::Finished => {
291 return Err(ArrowError::IpcError("Unexpected EOS".to_string()));
292 }
293 }
294 }
295 Ok(None)
296 }
297
298 pub fn finish(&mut self) -> Result<(), ArrowError> {
302 match self.state {
303 DecoderState::Finished
304 | DecoderState::Header {
305 read: 0,
306 continuation: false,
307 ..
308 } => Ok(()),
309 _ => Err(ArrowError::IpcError("Unexpected End of Stream".to_string())),
310 }
311 }
312}
313
314fn reserve_declared(buf: &mut MutableBuffer, len: usize) -> Result<(), ArrowError> {
318 if buf.is_empty() {
319 buf.try_reserve(len.min(MAX_PREALLOC_BYTES))
320 .map_err(|e| ArrowError::MemoryError(e.to_string()))?;
321 }
322 Ok(())
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::writer::{IpcWriteOptions, StreamWriter};
329 use arrow_array::{
330 DictionaryArray, Int32Array, Int64Array, RecordBatch, RunArray, StringArray,
331 types::Int32Type,
332 };
333 use arrow_schema::{DataType, Field, Schema};
334
335 #[test]
338 fn test_eos() {
339 let schema = Arc::new(Schema::new(vec![
340 Field::new("int32", DataType::Int32, false),
341 Field::new("int64", DataType::Int64, false),
342 ]));
343
344 let input = RecordBatch::try_new(
345 schema.clone(),
346 vec![
347 Arc::new(Int32Array::from(vec![1, 2, 3])) as _,
348 Arc::new(Int64Array::from(vec![1, 2, 3])) as _,
349 ],
350 )
351 .unwrap();
352
353 let mut buf = Vec::with_capacity(1024);
354 let mut s = StreamWriter::try_new(&mut buf, &schema).unwrap();
355 s.write(&input).unwrap();
356 s.finish().unwrap();
357 drop(s);
358
359 let buffer = Buffer::from_vec(buf);
360
361 let mut b = buffer.slice_with_length(0, buffer.len() - 1);
362 let mut decoder = StreamDecoder::new();
363 let output = decoder.decode(&mut b).unwrap().unwrap();
364 assert_eq!(output, input);
365 assert_eq!(b.len(), 7); assert!(decoder.decode(&mut b).unwrap().is_none());
367
368 let err = decoder.finish().unwrap_err().to_string();
369 assert_eq!(err, "Ipc error: Unexpected End of Stream");
370 }
371
372 #[test]
373 fn test_schema() {
374 let schema = Arc::new(Schema::new(vec![
375 Field::new("int32", DataType::Int32, false),
376 Field::new("int64", DataType::Int64, false),
377 ]));
378
379 let mut buf = Vec::with_capacity(1024);
380 let mut s = StreamWriter::try_new(&mut buf, &schema).unwrap();
381 s.finish().unwrap();
382 drop(s);
383
384 let buffer = Buffer::from_vec(buf);
385
386 let mut b = buffer.slice_with_length(0, buffer.len() - 1);
387 let mut decoder = StreamDecoder::new();
388 let output = decoder.decode(&mut b).unwrap();
389 assert!(output.is_none());
390 let decoded_schema = decoder.schema().unwrap();
391 assert_eq!(schema, decoded_schema);
392
393 let err = decoder.finish().unwrap_err().to_string();
394 assert_eq!(err, "Ipc error: Unexpected End of Stream");
395 }
396
397 #[test]
398 fn test_read_ree_dict_record_batches_from_buffer() {
399 let schema = Schema::new(vec![Field::new(
400 "test1",
401 DataType::RunEndEncoded(
402 Arc::new(Field::new(
403 Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
404 DataType::Int32,
405 false,
406 )),
407 #[expect(deprecated)]
408 Arc::new(Field::new_dict(
409 Field::REE_VALUES_FIELD_DEFAULT_NAME,
410 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
411 true,
412 0,
413 false,
414 )),
415 ),
416 true,
417 )]);
418 let batch = RecordBatch::try_new(
419 schema.clone().into(),
420 vec![Arc::new(
421 RunArray::try_new(
422 &Int32Array::from(vec![1, 2, 3]),
423 &vec![Some("a"), None, Some("a")]
424 .into_iter()
425 .collect::<DictionaryArray<Int32Type>>(),
426 )
427 .expect("Failed to create RunArray"),
428 )],
429 )
430 .expect("Failed to create RecordBatch");
431
432 let mut buffer = vec![];
433 {
434 let mut writer = StreamWriter::try_new_with_options(
435 &mut buffer,
436 &schema,
437 IpcWriteOptions::default(),
438 )
439 .expect("Failed to create StreamWriter");
440 writer.write(&batch).expect("Failed to write RecordBatch");
441 writer.finish().expect("Failed to finish StreamWriter");
442 }
443
444 let mut decoder = StreamDecoder::new();
445 let buf = &mut Buffer::from(buffer.as_slice());
446 while let Some(batch) = decoder
447 .decode(buf)
448 .map_err(|e| {
449 ArrowError::ExternalError(format!("Failed to decode record batch: {e}").into())
450 })
451 .expect("Failed to decode record batch")
452 {
453 assert_eq!(batch, batch);
454 }
455
456 decoder.finish().expect("Failed to finish decoder");
457 }
458
459 #[test]
462 fn test_split_body_is_buffered_at_its_declared_size() {
463 let schema = Arc::new(Schema::new(vec![Field::new("s", DataType::Utf8, false)]));
464 let input = RecordBatch::try_new(
465 schema.clone(),
466 vec![Arc::new(StringArray::from_iter_values(
467 (0..7000).map(|i| format!("value{i:05}")),
468 )) as _],
469 )
470 .unwrap();
471
472 let mut stream = Vec::new();
473 let mut writer = StreamWriter::try_new(&mut stream, &schema).unwrap();
474 writer.write(&input).unwrap();
475 writer.finish().unwrap();
476 drop(writer);
477
478 let mut decoder = StreamDecoder::new();
479 let mut output = None;
480 for byte in stream.chunks(1) {
481 if let Some(batch) = decoder.decode(&mut Buffer::from(byte)).unwrap() {
482 output = Some(batch);
483 }
484 }
485 decoder.finish().unwrap();
486 let output = output.unwrap();
487 assert_eq!(output, input);
488
489 let held = output.column(0).to_data().buffers()[0].capacity();
490 assert!(
491 held <= stream.len(),
492 "decoded batch holds {held} bytes, more than the {} byte stream it came from",
493 stream.len()
494 );
495 }
496
497 #[test]
500 fn test_implausible_body_length_reserves_a_bounded_buffer() {
501 for body_length in [i64::MAX, 1 << 50] {
502 let mut fbb = flatbuffers::FlatBufferBuilder::new();
503 let mut message = crate::MessageBuilder::new(&mut fbb);
504 message.add_version(crate::MetadataVersion::V5);
505 message.add_header_type(MessageHeader::NONE);
506 message.add_bodyLength(body_length);
507 let root = message.finish();
508 fbb.finish(root, None);
509 let metadata = fbb.finished_data();
510
511 let mut stream = Vec::new();
512 stream.extend_from_slice(&CONTINUATION_MARKER);
513 stream.extend_from_slice(&(metadata.len() as i32).to_le_bytes());
514 stream.extend_from_slice(metadata);
515 stream.extend_from_slice(&[0xAB; 16]);
516
517 let mut decoder = StreamDecoder::new();
518 let decoded = decoder
519 .decode(&mut Buffer::from(stream.as_slice()))
520 .unwrap();
521 assert!(decoded.is_none(), "body_length {body_length}");
522 assert!(decoder.finish().is_err(), "body_length {body_length}");
523 }
524 }
525}