@@ -126,16 +126,30 @@ class ValueDescWriter {
126126 {static_cast <uint32_t >(parsed_writer->size ()) & 0x7fffffffU , quoted_});
127127 }
128128
129- void Finish (std::shared_ptr<Buffer>* out_values) {
130- ARROW_CHECK_OK (values_buffer_->Resize (values_size_ * sizeof (*values_)));
131- *out_values = values_buffer_;
129+ Result<std::shared_ptr<Buffer>> Finish () {
130+ RETURN_NOT_OK (values_buffer_->Resize (values_size_ * sizeof (*values_)));
131+ return std::move (values_buffer_);
132+ }
133+
134+ const Status& status () const { return status_; }
135+
136+ // Convenience error-checking factory. The arguments are forwarded to the
137+ // Derived class constructor.
138+ template <typename ... Args>
139+ static Result<Derived> Make (Args&&... args) {
140+ auto self = Derived (std::forward<Args>(args)...);
141+ RETURN_NOT_OK (self.status ());
142+ return self;
132143 }
133144
134145 protected:
135146 ValueDescWriter (MemoryPool* pool, int64_t values_capacity)
136- : values_size_(0 ), values_capacity_(values_capacity) {
137- values_buffer_ = *AllocateResizableBuffer (values_capacity_ * sizeof (*values_), pool);
138- values_ = reinterpret_cast <ParsedValueDesc*>(values_buffer_->mutable_data ());
147+ : values_size_(0 ), values_capacity_(values_capacity), status_(Status::OK ()) {
148+ status_ &= AllocateResizableBuffer (values_capacity_ * sizeof (*values_), pool)
149+ .Value (&values_buffer_);
150+ if (status_.ok ()) {
151+ values_ = reinterpret_cast <ParsedValueDesc*>(values_buffer_->mutable_data ());
152+ }
139153 }
140154
141155 std::shared_ptr<ResizableBuffer> values_buffer_;
@@ -145,6 +159,7 @@ class ValueDescWriter {
145159 bool quoted_;
146160 // Checkpointing, for when an incomplete line is encountered at end of block
147161 int64_t saved_values_size_;
162+ Status status_;
148163};
149164
150165// A helper class handling a growable buffer for values offsets. This class is
@@ -157,11 +172,21 @@ class ResizableValueDescWriter : public ValueDescWriter<ResizableValueDescWriter
157172
158173 void PushValue (ParsedValueDesc v) {
159174 if (ARROW_PREDICT_FALSE (values_size_ == values_capacity_)) {
160- values_capacity_ = values_capacity_ * 2 ;
161- ARROW_CHECK_OK (values_buffer_->Resize (values_capacity_ * sizeof (*values_)));
162- values_ = reinterpret_cast <ParsedValueDesc*>(values_buffer_->mutable_data ());
175+ int64_t new_capacity = values_capacity_ * 2 ;
176+ auto resize_status = values_buffer_->Resize (new_capacity * sizeof (*values_));
177+ if (resize_status.ok ()) {
178+ values_ = reinterpret_cast <ParsedValueDesc*>(values_buffer_->mutable_data ());
179+ values_capacity_ = new_capacity;
180+ }
181+ status_ &= std::move (resize_status);
182+ }
183+ // The `values_` pointer may have become invalid if the `Resize` call above failed.
184+ // Note that ResizableValueDescWriter is less performance-critical than
185+ // PresizedValueDescWriter, as it should only be called on the first line(s)
186+ // of CSV data.
187+ if (ARROW_PREDICT_TRUE (status_.ok ())) {
188+ values_[values_size_++] = v;
163189 }
164- values_[values_size_++] = v;
165190 }
166191};
167192
@@ -171,12 +196,26 @@ class ResizableValueDescWriter : public ValueDescWriter<ResizableValueDescWriter
171196// faster CSV parsing code.
172197class PresizedValueDescWriter : public ValueDescWriter <PresizedValueDescWriter> {
173198 public:
199+ // The number of offsets being written will be `1 + num_rows * num_cols`,
200+ // however we allow for one extraneous write in case of excessive columns,
201+ // hence `2 + num_rows * num_cols` (see explanation in PushValue below).
174202 PresizedValueDescWriter (MemoryPool* pool, int32_t num_rows, int32_t num_cols)
175- : ValueDescWriter(pool, /* values_capacity=*/ 1 + num_rows * num_cols) {}
203+ : ValueDescWriter(pool, /* values_capacity=*/ 2 + num_rows * num_cols) {}
176204
177205 void PushValue (ParsedValueDesc v) {
178206 DCHECK_LT (values_size_, values_capacity_);
179- values_[values_size_++] = v;
207+ values_[values_size_] = v;
208+ // We must take care not to write past the buffer's end if the line being
209+ // parsed has more than `num_cols` columns. The obvious solution of setting
210+ // an error status hurts too much on benchmarks, which is why we instead
211+ // cap `values_size_` to stay inside the buffer.
212+ //
213+ // Not setting an error immediately is not a problem since the `num_cols`
214+ // mismatch is detected later in ParseLine.
215+ //
216+ // Note that we want `values_size_` to reflect the number of written values
217+ // in the nominal case, which is why we choose a slightly larger `values_capacity_`.
218+ values_size_ += (values_size_ != values_capacity_ - 1 );
180219 }
181220};
182221
@@ -464,6 +503,7 @@ class BlockParserImpl {
464503 RETURN_NOT_OK ((ParseLine<SpecializedOptions, true >(values_writer, parsed_writer,
465504 data, data_end, is_final,
466505 &line_end, bulk_filter)));
506+ RETURN_NOT_OK (values_writer->status ());
467507 if (line_end == data) {
468508 // Cannot parse any further
469509 *finished_parsing = true ;
@@ -477,6 +517,7 @@ class BlockParserImpl {
477517 RETURN_NOT_OK ((ParseLine<SpecializedOptions, false >(values_writer, parsed_writer,
478518 data, data_end, is_final,
479519 &line_end, bulk_filter)));
520+ RETURN_NOT_OK (values_writer->status ());
480521 if (line_end == data) {
481522 // Cannot parse any further
482523 *finished_parsing = true ;
@@ -496,8 +537,7 @@ class BlockParserImpl {
496537 }
497538
498539 // Append new buffers and update size
499- std::shared_ptr<Buffer> values_buffer;
500- values_writer->Finish (&values_buffer);
540+ ARROW_ASSIGN_OR_RAISE (auto values_buffer, values_writer->Finish ());
501541 if (values_buffer->size () > 0 ) {
502542 values_size_ +=
503543 static_cast <int32_t >(values_buffer->size () / sizeof (ParsedValueDesc) - 1 );
@@ -535,7 +575,7 @@ class BlockParserImpl {
535575 // Can't presize values when the number of columns is not known, first parse
536576 // a single line
537577 const int32_t rows_in_chunk = 1 ;
538- ResizableValueDescWriter values_writer (pool_);
578+ ARROW_ASSIGN_OR_RAISE ( auto values_writer, ResizableValueDescWriter::Make (pool_) );
539579 values_writer.Start (parsed_writer);
540580
541581 RETURN_NOT_OK (ParseChunk<SpecializedOptions>(
@@ -560,7 +600,9 @@ class BlockParserImpl {
560600 rows_in_chunk = std::min (kTargetChunkSize , max_num_rows_ - batch_.num_rows_ );
561601 }
562602
563- PresizedValueDescWriter values_writer (pool_, rows_in_chunk, batch_.num_cols_ );
603+ ARROW_ASSIGN_OR_RAISE (
604+ auto values_writer,
605+ PresizedValueDescWriter::Make (pool_, rows_in_chunk, batch_.num_cols_ ));
564606 values_writer.Start (parsed_writer);
565607
566608 RETURN_NOT_OK (ParseChunk<SpecializedOptions>(
0 commit comments