@@ -285,17 +285,21 @@ std::string FormatMessageType(MessageType type) {
285285
286286namespace {
287287
288- Status ReadFieldsSubset (int64_t offset, int32_t metadata_length,
289- io::RandomAccessFile* file,
288+ Status ReadFieldsSubset (int64_t offset, io::RandomAccessFile* file,
290289 const FieldsLoaderFunction& fields_loader,
291290 const std::shared_ptr<Buffer>& metadata, int64_t required_size,
292291 std::shared_ptr<Buffer>& body) {
292+ DCHECK_GE (static_cast <size_t >(metadata->size ()), sizeof (int32_t ));
293+ const auto continuation = util::SafeLoadAs<int32_t >(metadata->data ());
294+ // Either 8 bytes (32-bit continuation indicator + 32-bit little-endian length prefix)
295+ // or 4 bytes for legacy IPC without continuation indicator
296+ const auto continuation_size = (continuation == internal::kIpcContinuationToken )
297+ ? 2 * sizeof (int32_t )
298+ : sizeof (int32_t );
299+
293300 const flatbuf::Message* message = nullptr ;
294- uint8_t continuation_metadata_size = sizeof (int32_t ) + sizeof (int32_t );
295- // skip 8 bytes (32-bit continuation indicator + 32-bit little-endian length prefix)
296- RETURN_NOT_OK (internal::VerifyMessage (metadata->data () + continuation_metadata_size,
297- metadata->size () - continuation_metadata_size,
298- &message));
301+ RETURN_NOT_OK (internal::VerifyMessage (metadata->data () + continuation_size,
302+ metadata->size () - continuation_size, &message));
299303 auto batch = message->header_as_RecordBatch ();
300304 if (batch == nullptr ) {
301305 return Status::IOError (
@@ -305,8 +309,8 @@ Status ReadFieldsSubset(int64_t offset, int32_t metadata_length,
305309 RETURN_NOT_OK (fields_loader (batch, &io_recorded_random_access_file));
306310 const auto & read_ranges = io_recorded_random_access_file.GetReadRanges ();
307311 for (const auto & range : read_ranges) {
308- auto read_result = file->ReadAt (offset + metadata_length + range.offset , range. length ,
309- body->mutable_data () + range.offset );
312+ auto read_result = file->ReadAt (offset + metadata-> size () + range.offset ,
313+ range. length , body->mutable_data () + range.offset );
310314 if (!read_result.ok ()) {
311315 return Status::IOError (" Failed to read message body, error " ,
312316 read_result.status ().ToString ());
@@ -315,6 +319,84 @@ Status ReadFieldsSubset(int64_t offset, int32_t metadata_length,
315319 return Status::OK ();
316320}
317321
322+ struct ReadMessageState {
323+ std::unique_ptr<Message> result;
324+ std::shared_ptr<MessageDecoderListener> listener;
325+ std::shared_ptr<MessageDecoder> decoder;
326+
327+ ReadMessageState ()
328+ : listener(std::make_shared<AssignMessageDecoderListener>(&result)),
329+ decoder (std::make_shared<MessageDecoder>(listener)) {}
330+
331+ // ReadMessageState points into itself, so it shouldn't be moved
332+ ReadMessageState (ReadMessageState&&) = delete;
333+ ReadMessageState& operator =(ReadMessageState&&) = delete ;
334+ };
335+
336+ // A common continuation callback for ReadMessage and ReadMessageAsync overloads
337+ static Result<std::unique_ptr<Message>> ReadMessageContinued (
338+ int64_t offset, int32_t metadata_length, std::optional<int64_t > body_length,
339+ std::shared_ptr<Buffer> metadata, io::RandomAccessFile* file,
340+ const FieldsLoaderFunction& fields_loader, ReadMessageState* state) {
341+ MessageDecoder* decoder = state->decoder .get ();
342+ if (body_length.has_value ()) {
343+ // If body length was known, the given buffer should contain exactly the metadata
344+ // followed by the body.
345+ DCHECK_EQ (metadata->size (), metadata_length + *body_length);
346+ }
347+ ARROW_RETURN_NOT_OK (decoder->Consume (SliceBuffer (metadata, 0 , metadata_length)));
348+ if (decoder->buffered_size () > 0 ) {
349+ return Status::Invalid (" Message metadata too long by " , decoder->buffered_size (),
350+ " bytes" );
351+ }
352+ switch (decoder->state ()) {
353+ case MessageDecoder::State::INITIAL :
354+ return std::move (state->result );
355+ case MessageDecoder::State::METADATA_LENGTH :
356+ return Status::Invalid (" metadata length is missing. File offset: " , offset,
357+ " , metadata length: " , metadata_length);
358+ case MessageDecoder::State::METADATA :
359+ return Status::Invalid (" flatbuffer size " , decoder->next_required_size (),
360+ " invalid. File offset: " , offset,
361+ " , metadata length: " , metadata_length);
362+ case MessageDecoder::State::BODY : {
363+ std::shared_ptr<Buffer> body;
364+ if (fields_loader) {
365+ // Selective field loading: allocate a body buffer and read only the
366+ // requested field ranges into it.
367+ ARROW_ASSIGN_OR_RAISE (
368+ body, AllocateBuffer (decoder->next_required_size (), default_memory_pool ()));
369+ RETURN_NOT_OK (ReadFieldsSubset (offset, file, fields_loader,
370+ SliceBuffer (metadata, 0 , metadata_length),
371+ decoder->next_required_size (), body));
372+ } else if (body_length.has_value ()) {
373+ // Body was already read as part of the combined IO; just slice it out.
374+ if (*body_length != decoder->next_required_size ()) {
375+ // The streaming decoder got out of sync with the actual advertised
376+ // metadata and body size, which signals an invalid IPC file.
377+ return Status::IOError (" Invalid IPC file: advertised body size is " ,
378+ *body_length, " , but message decoder expects to read " ,
379+ decoder->next_required_size (), " bytes instead" );
380+ }
381+ body = SliceBuffer (metadata, metadata_length,
382+ std::min (*body_length, metadata->size () - metadata_length));
383+ } else {
384+ // Body length was unknown; do a separate IO to read the body.
385+ ARROW_ASSIGN_OR_RAISE (
386+ body, file->ReadAt (offset + metadata_length, decoder->next_required_size (),
387+ /* allow_short_read=*/ false ));
388+ }
389+
390+ RETURN_NOT_OK (decoder->Consume (body));
391+ return std::move (state->result );
392+ }
393+ case MessageDecoder::State::EOS :
394+ return Status::Invalid (" Unexpected empty message in IPC file format" );
395+ default :
396+ return Status::Invalid (" Unexpected state: " , state->decoder ->state ());
397+ }
398+ }
399+
318400} // namespace
319401
320402Result<std::unique_ptr<Message>> ReadMessage (std::shared_ptr<Buffer> metadata,
@@ -330,6 +412,10 @@ Result<std::unique_ptr<Message>> ReadMessage(std::shared_ptr<Buffer> metadata,
330412 }
331413
332414 ARROW_RETURN_NOT_OK (decoder.Consume (metadata));
415+ if (decoder.buffered_size () > 0 ) {
416+ return Status::Invalid (" Message metadata too long by " , decoder.buffered_size (),
417+ " bytes" );
418+ }
333419
334420 switch (decoder.state ()) {
335421 case MessageDecoder::State::INITIAL :
@@ -368,69 +454,20 @@ Result<std::unique_ptr<Message>> ReadMessage(std::shared_ptr<Buffer> metadata,
368454static Result<std::unique_ptr<Message>> ReadMessageInternal (
369455 int64_t offset, int32_t metadata_length, std::optional<int64_t > body_length,
370456 io::RandomAccessFile* file, const FieldsLoaderFunction& fields_loader) {
371- std::unique_ptr<Message> result;
372- auto listener = std::make_shared<AssignMessageDecoderListener>(&result);
373- MessageDecoder decoder (listener);
457+ ReadMessageState state;
374458
375- if (metadata_length < decoder. next_required_size ()) {
459+ if (metadata_length < state. decoder -> next_required_size ()) {
376460 return Status::Invalid (" metadata_length should be at least " ,
377- decoder. next_required_size ());
461+ state. decoder -> next_required_size ());
378462 }
379-
380463 // When body_length is known, read metadata + body in one IO call.
381464 // Otherwise, read only metadata first.
382465 ARROW_ASSIGN_OR_RAISE (std::shared_ptr<Buffer> metadata,
383466 file->ReadAt (offset, metadata_length + body_length.value_or (0 ),
384467 /* allow_short_read=*/ false ));
385468
386- ARROW_RETURN_NOT_OK (decoder.Consume (SliceBuffer (metadata, 0 , metadata_length)));
387-
388- switch (decoder.state ()) {
389- case MessageDecoder::State::INITIAL :
390- return result;
391- case MessageDecoder::State::METADATA_LENGTH :
392- return Status::Invalid (" metadata length is missing. File offset: " , offset,
393- " , metadata length: " , metadata_length);
394- case MessageDecoder::State::METADATA :
395- return Status::Invalid (" flatbuffer size " , decoder.next_required_size (),
396- " invalid. File offset: " , offset,
397- " , metadata length: " , metadata_length);
398- case MessageDecoder::State::BODY : {
399- std::shared_ptr<Buffer> body;
400- if (fields_loader) {
401- // Selective field loading: allocate a body buffer and read only the
402- // requested field ranges into it.
403- ARROW_ASSIGN_OR_RAISE (
404- body, AllocateBuffer (decoder.next_required_size (), default_memory_pool ()));
405- RETURN_NOT_OK (ReadFieldsSubset (offset, metadata_length, file, fields_loader,
406- SliceBuffer (metadata, 0 , metadata_length),
407- decoder.next_required_size (), body));
408- } else if (body_length.has_value ()) {
409- // Body was already read as part of the combined IO; just slice it out.
410- if (*body_length != decoder.next_required_size ()) {
411- // The streaming decoder got out of sync with the actual advertised
412- // metadata and body size, which signals an invalid IPC file.
413- return Status::IOError (" Invalid IPC file: advertised body size is " ,
414- *body_length, " , but message decoder expects to read " ,
415- decoder.next_required_size (), " bytes instead" );
416- }
417- body = SliceBuffer (metadata, metadata_length,
418- std::min (*body_length, metadata->size () - metadata_length));
419- } else {
420- // Body length was unknown; do a separate IO to read the body.
421- ARROW_ASSIGN_OR_RAISE (
422- body, file->ReadAt (offset + metadata_length, decoder.next_required_size (),
423- /* allow_short_read=*/ false ));
424- }
425-
426- RETURN_NOT_OK (decoder.Consume (body));
427- return result;
428- }
429- case MessageDecoder::State::EOS :
430- return Status::Invalid (" Unexpected empty message in IPC file format" );
431- default :
432- return Status::Invalid (" Unexpected state: " , decoder.state ());
433- }
469+ return ReadMessageContinued (offset, metadata_length, body_length, metadata, file,
470+ fields_loader, &state);
434471}
435472
436473Result<std::unique_ptr<Message>> ReadMessage (int64_t offset, int32_t metadata_length,
@@ -452,14 +489,9 @@ Future<std::shared_ptr<Message>> ReadMessageAsync(int64_t offset, int32_t metada
452489 int64_t body_length,
453490 io::RandomAccessFile* file,
454491 const io::IOContext& context) {
455- struct State {
456- std::unique_ptr<Message> result;
457- std::shared_ptr<MessageDecoderListener> listener;
458- std::shared_ptr<MessageDecoder> decoder;
459- };
460- auto state = std::make_shared<State>();
461- state->listener = std::make_shared<AssignMessageDecoderListener>(&state->result );
462- state->decoder = std::make_shared<MessageDecoder>(state->listener );
492+ // Make a std::shared_ptr so as to have a stable ReadMessageState pointer,
493+ // since state->listener will point back to state->result.
494+ auto state = std::make_shared<ReadMessageState>();
463495
464496 if (metadata_length < state->decoder ->next_required_size ()) {
465497 return Status::Invalid (" metadata_length should be at least " ,
@@ -469,35 +501,8 @@ Future<std::shared_ptr<Message>> ReadMessageAsync(int64_t offset, int32_t metada
469501 ->ReadAsync (context, offset, metadata_length + body_length,
470502 /* allow_short_read=*/ false )
471503 .Then ([=](std::shared_ptr<Buffer> metadata) -> Result<std::shared_ptr<Message>> {
472- DCHECK_EQ (metadata->size (), metadata_length + body_length);
473- ARROW_RETURN_NOT_OK (
474- state->decoder ->Consume (SliceBuffer (metadata, 0 , metadata_length)));
475- switch (state->decoder ->state ()) {
476- case MessageDecoder::State::INITIAL :
477- return std::move (state->result );
478- case MessageDecoder::State::METADATA_LENGTH :
479- return Status::Invalid (" metadata length is missing. File offset: " , offset,
480- " , metadata length: " , metadata_length);
481- case MessageDecoder::State::METADATA :
482- return Status::Invalid (" flatbuffer size " ,
483- state->decoder ->next_required_size (),
484- " invalid. File offset: " , offset,
485- " , metadata length: " , metadata_length);
486- case MessageDecoder::State::BODY : {
487- auto body = SliceBuffer (metadata, metadata_length, body_length);
488- if (body->size () < state->decoder ->next_required_size ()) {
489- return Status::IOError (" Expected to be able to read " ,
490- state->decoder ->next_required_size (),
491- " bytes for message body, got " , body->size ());
492- }
493- RETURN_NOT_OK (state->decoder ->Consume (body));
494- return std::move (state->result );
495- }
496- case MessageDecoder::State::EOS :
497- return Status::Invalid (" Unexpected empty message in IPC file format" );
498- default :
499- return Status::Invalid (" Unexpected state: " , state->decoder ->state ());
500- }
504+ return ReadMessageContinued (offset, metadata_length, body_length, metadata, file,
505+ /* fields_loader=*/ {}, state.get ());
501506 });
502507}
503508
@@ -755,6 +760,8 @@ class MessageDecoder::MessageDecoderImpl {
755760
756761 int64_t next_required_size () const { return next_required_size_ - buffered_size_; }
757762
763+ int64_t buffered_size () const { return buffered_size_; }
764+
758765 MessageDecoder::State state () const { return state_; }
759766
760767 private:
@@ -1020,6 +1027,8 @@ Status MessageDecoder::Consume(std::shared_ptr<Buffer> buffer) {
10201027
10211028int64_t MessageDecoder::next_required_size () const { return impl_->next_required_size (); }
10221029
1030+ int64_t MessageDecoder::buffered_size () const { return impl_->buffered_size (); }
1031+
10231032MessageDecoder::State MessageDecoder::state () const { return impl_->state (); }
10241033
10251034// ----------------------------------------------------------------------
0 commit comments