// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_CONTAINERS_BUFFER_ITERATOR_H_ #define BASE_CONTAINERS_BUFFER_ITERATOR_H_ #include #include "base/bit_cast.h" #include "base/containers/span.h" #include "base/numerics/checked_math.h" namespace base { // BufferIterator is a bounds-checked container utility to access variable- // length, heterogeneous structures contained within a buffer. If the data are // homogeneous, use base::span<> instead. // // After being created with a weakly-owned buffer, BufferIterator returns // pointers to structured data within the buffer. After each method call that // returns data in the buffer, the iterator position is advanced by the byte // size of the object (or span of objects) returned. If there are not enough // bytes remaining in the buffer to return the requested object(s), a nullptr // or empty span is returned. // // This class is similar to base::Pickle, which should be preferred for // serializing to disk. Pickle versions its header and does not support writing // structures, which are problematic for serialization due to struct padding and // version shear concerns. // // Example usage: // // std::vector buffer(4096); // if (!ReadSomeData(&buffer, buffer.size())) { // LOG(ERROR) << "Failed to read data."; // return false; // } // // BufferIterator iterator(buffer); // uint32_t* num_items = iterator.Object(); // if (!num_items) { // LOG(ERROR) << "No num_items field." // return false; // } // // base::span items = // iterator.Span(*num_items); // if (items.size() != *num_items) { // LOG(ERROR) << "Not enough items."; // return false; // } // // // ... validate the objects in |items|. template class BufferIterator { public: static_assert(std::is_same, char>::value || std::is_same, unsigned char>::value, "Underlying buffer type must be char-type."); BufferIterator() {} BufferIterator(B* data, size_t size) : BufferIterator(make_span(data, size)) {} explicit BufferIterator(span buffer) : buffer_(buffer), remaining_(buffer) {} ~BufferIterator() {} // Returns a pointer to a mutable structure T in the buffer at the current // position. On success, the iterator position is advanced by sizeof(T). If // there are not sizeof(T) bytes remaining in the buffer, returns nullptr. template ::value>> T* MutableObject() { size_t size = sizeof(T); size_t next_position; if (!CheckAdd(position(), size).AssignIfValid(&next_position)) return nullptr; if (next_position > total_size()) return nullptr; T* t = bit_cast(remaining_.data()); remaining_ = remaining_.subspan(size); return t; } // Returns a const pointer to an object of type T in the buffer at the current // position. template ::value>> const T* Object() { return MutableObject(); } // Returns a span of |count| T objects in the buffer at the current position. // On success, the iterator position is advanced by |sizeof(T) * count|. If // there are not enough bytes remaining in the buffer to fulfill the request, // returns an empty span. template ::value>> span MutableSpan(size_t count) { size_t size; if (!CheckMul(sizeof(T), count).AssignIfValid(&size)) return span(); size_t next_position; if (!CheckAdd(position(), size).AssignIfValid(&next_position)) return span(); if (next_position > total_size()) return span(); auto result = span(bit_cast(remaining_.data()), count); remaining_ = remaining_.subspan(size); return result; } // Returns a span to |count| const objects of type T in the buffer at the // current position. template ::value>> span Span(size_t count) { return MutableSpan(count); } // Resets the iterator position to the absolute offset |to|. void Seek(size_t to) { remaining_ = buffer_.subspan(to); } // Returns the total size of the underlying buffer. size_t total_size() { return buffer_.size(); } // Returns the current position in the buffer. size_t position() { return buffer_.size_bytes() - remaining_.size_bytes(); } private: // The original buffer that the iterator was constructed with. const span buffer_; // A subspan of |buffer_| containing the remaining bytes to iterate over. span remaining_; // Copy and assign allowed. }; } // namespace base #endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_