// Copyright 2019 The Chromium Authors // 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 #include "base/bit_cast.h" #include "base/containers/span.h" #include "base/numerics/checked_math.h" #include "third_party/abseil-cpp/absl/types/optional.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 >> T* MutableObject() { size_t size = sizeof(T); if (size > remaining_.size()) return nullptr; T* t = reinterpret_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 >> const T* Object() { return MutableObject(); } // Copies out an object. As compared to using Object, this avoids potential // unaligned access which may be undefined behavior. template >> absl::optional CopyObject() { absl::optional t; if (remaining_.size() >= sizeof(T)) { memcpy(&t.emplace(), remaining_.data(), sizeof(T)); remaining_ = remaining_.subspan(sizeof(T)); } return t; } // 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 >> span MutableSpan(size_t count) { size_t size; if (!CheckMul(sizeof(T), count).AssignIfValid(&size)) return span(); if (size > remaining_.size()) return span(); auto result = span(reinterpret_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 >> 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); } // Limits the remaining data to the specified size. // Seeking to an absolute offset reverses this. void TruncateTo(size_t size) { remaining_ = remaining_.first(size); } // Returns the total size of the underlying buffer. size_t total_size() const { return buffer_.size(); } // Returns the current position in the buffer. size_t position() const { DCHECK(buffer_.data() <= remaining_.data()); DCHECK(remaining_.data() <= buffer_.data() + buffer_.size()); return static_cast(remaining_.data() - buffer_.data()); } 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_