@@ -134,15 +134,15 @@ size_t ByteBuffer::EstimateOffsetsReserveCountFromSample(tcb::span<const uint8_t
134134 ++sampled_elements;
135135 }
136136
137+ // If no elements were sampled or the cursor didn't move, it means the buffer is empty. So return 0.
137138 if (sampled_elements == 0 || cursor == 0 )
138139 return 0 ;
139140
140141 // If sampling consumed the full buffer (<= sample window), we already know the exact count.
141142 if (cursor == bytes.size ())
142143 return sampled_elements;
143144
144- // Estimate total element count from sample density:
145- // (sampled_elements / sampled_bytes) * total_bytes.
145+ // Estimate total element count from sample density: (sampled_elements / sampled_bytes) * total_bytes.
146146 // - sampled_elements / sampled_bytes gives "elements per byte" in the sampled prefix,
147147 // - then multiplying by total_bytes extrapolates a full-buffer estimate.
148148 const long double estimated =
@@ -151,11 +151,13 @@ size_t ByteBuffer::EstimateOffsetsReserveCountFromSample(tcb::span<const uint8_t
151151 const size_t estimated_count = static_cast <size_t >(std::ceil (estimated));
152152 const size_t estimated_with_headroom =
153153 static_cast <size_t >(std::ceil (static_cast <long double >(estimated_count) * 1 .1L ));
154+
155+ // If the count of sampled elements is more than the estimated, conservatively return actual count.
154156 return std::max (estimated_with_headroom, sampled_elements);
155157}
156158
157159// -----------------------------------------------------------------------------
158- // Span reader methods
160+ // Element span reader methods
159161// -----------------------------------------------------------------------------
160162
161163size_t ByteBuffer::CalculateOffsetOfElement (size_t position) const {
@@ -189,6 +191,99 @@ tcb::span<const uint8_t> ByteBuffer::GetElement(size_t position) const {
189191 return elements_span_.subspan (offset + kSizePrefixBytes , element_size);
190192}
191193
194+ // -----------------------------------------------------------------------------
195+ // Elemment span iterator
196+ //
197+ // Allows an alternative read of elements_span_ without need for lazy initialization of offsets_,
198+ // so saving execution time when the traversal of the buffer is strictly sequential.
199+ // This is the most common behavior when reading elements in single threaded mode.
200+ // -----------------------------------------------------------------------------
201+
202+ ByteBuffer::ConstIterator::ConstIterator (const ByteBuffer* buffer, size_t cursor_offset)
203+ : buffer_(buffer), cursor_offset_(cursor_offset) {}
204+
205+ void ByteBuffer::ConstIterator::ValidateFixedSizeElementAtCursor () const {
206+ if (buffer_->element_size_ <= 0 ) {
207+ throw InvalidInputException (" Invalid fixed-size buffer: element_size must be greater than zero" );
208+ }
209+ if ((buffer_->elements_span_ .size () - cursor_offset_) < buffer_->element_size_ ) {
210+ throw InvalidInputException (" Malformed fixed-size buffer: truncated element payload" );
211+ }
212+ }
213+
214+ size_t ByteBuffer::ConstIterator::ReadAndValidateVariableElementSizeAtCursor () const {
215+ if ((buffer_->elements_span_ .size () - cursor_offset_) < kSizePrefixBytes ) {
216+ throw InvalidInputException (" Malformed variable-size buffer: truncated length prefix" );
217+ }
218+ const size_t current_element_size = ReadSizeAt (buffer_->elements_span_ , cursor_offset_);
219+ const size_t payload_offset = cursor_offset_ + kSizePrefixBytes ;
220+ if ((buffer_->elements_span_ .size () - payload_offset) < current_element_size) {
221+ throw InvalidInputException (" Malformed variable-size buffer: truncated element payload" );
222+ }
223+ return current_element_size;
224+ }
225+
226+ ByteBuffer::ConstIterator::value_type ByteBuffer::ConstIterator::operator *() const {
227+ if (buffer_ == nullptr || cursor_offset_ >= buffer_->elements_span_ .size ()) {
228+ throw InvalidInputException (" Cannot dereference ByteBuffer iterator at end position" );
229+ }
230+ if (buffer_->has_fixed_sized_elements_ ) {
231+ ValidateFixedSizeElementAtCursor ();
232+ return buffer_->elements_span_ .subspan (cursor_offset_, buffer_->element_size_ );
233+ }
234+
235+ const size_t current_element_size = ReadAndValidateVariableElementSizeAtCursor ();
236+ const size_t payload_offset = cursor_offset_ + kSizePrefixBytes ;
237+ return buffer_->elements_span_ .subspan (payload_offset, current_element_size);
238+ }
239+
240+ ByteBuffer::ConstIterator& ByteBuffer::ConstIterator::operator ++() {
241+ if (buffer_ == nullptr || cursor_offset_ >= buffer_->elements_span_ .size ()) {
242+ return *this ;
243+ }
244+ if (buffer_->has_fixed_sized_elements_ ) {
245+ ValidateFixedSizeElementAtCursor ();
246+ cursor_offset_ += buffer_->element_size_ ;
247+ return *this ;
248+ }
249+
250+ const size_t current_element_size = ReadAndValidateVariableElementSizeAtCursor ();
251+ cursor_offset_ += (kSizePrefixBytes + current_element_size);
252+ return *this ;
253+ }
254+
255+ bool ByteBuffer::ConstIterator::operator ==(const ConstIterator& other) const {
256+ return buffer_ == other.buffer_ && cursor_offset_ == other.cursor_offset_ ;
257+ }
258+
259+ bool ByteBuffer::ConstIterator::operator !=(const ConstIterator& other) const {
260+ return !(*this == other);
261+ }
262+
263+ void ByteBuffer::ValidateIteratorReadPreconditions () const {
264+ if (is_write_buffer_initialized_) {
265+ throw InvalidInputException (" Iterator is only available for read buffers" );
266+ }
267+ if (has_fixed_sized_elements_) {
268+ if (element_size_ <= 0 ) {
269+ throw InvalidInputException (" Invalid fixed-size buffer: element_size must be greater than zero" );
270+ }
271+ if ((elements_span_.size () % element_size_) != 0 ) {
272+ throw InvalidInputException (" Malformed fixed-size buffer: buffer does not align with element_size" );
273+ }
274+ }
275+ }
276+
277+ ByteBuffer::ConstIterator ByteBuffer::begin () const {
278+ ValidateIteratorReadPreconditions ();
279+ return ConstIterator (this , 0u );
280+ }
281+
282+ ByteBuffer::ConstIterator ByteBuffer::end () const {
283+ ValidateIteratorReadPreconditions ();
284+ return ConstIterator (this , elements_span_.size ());
285+ }
286+
192287// -----------------------------------------------------------------------------
193288// Constructors and initializers for write buffer
194289// -----------------------------------------------------------------------------
0 commit comments