1 // Copyright 2019 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_CONTAINERS_BUFFER_ITERATOR_H_ 6 #define BASE_CONTAINERS_BUFFER_ITERATOR_H_ 7 8 #include <string.h> 9 10 #include <type_traits> 11 12 #include "base/bit_cast.h" 13 #include "base/containers/span.h" 14 #include "base/numerics/checked_math.h" 15 #include "third_party/abseil-cpp/absl/types/optional.h" 16 17 namespace base { 18 19 // BufferIterator is a bounds-checked container utility to access variable- 20 // length, heterogeneous structures contained within a buffer. If the data are 21 // homogeneous, use base::span<> instead. 22 // 23 // After being created with a weakly-owned buffer, BufferIterator returns 24 // pointers to structured data within the buffer. After each method call that 25 // returns data in the buffer, the iterator position is advanced by the byte 26 // size of the object (or span of objects) returned. If there are not enough 27 // bytes remaining in the buffer to return the requested object(s), a nullptr 28 // or empty span is returned. 29 // 30 // This class is similar to base::Pickle, which should be preferred for 31 // serializing to disk. Pickle versions its header and does not support writing 32 // structures, which are problematic for serialization due to struct padding and 33 // version shear concerns. 34 // 35 // Example usage: 36 // 37 // std::vector<uint8_t> buffer(4096); 38 // if (!ReadSomeData(&buffer, buffer.size())) { 39 // LOG(ERROR) << "Failed to read data."; 40 // return false; 41 // } 42 // 43 // BufferIterator<uint8_t> iterator(buffer); 44 // uint32_t* num_items = iterator.Object<uint32_t>(); 45 // if (!num_items) { 46 // LOG(ERROR) << "No num_items field." 47 // return false; 48 // } 49 // 50 // base::span<const item_struct> items = 51 // iterator.Span<item_struct>(*num_items); 52 // if (items.size() != *num_items) { 53 // LOG(ERROR) << "Not enough items."; 54 // return false; 55 // } 56 // 57 // // ... validate the objects in |items|. 58 template <typename B> 59 class BufferIterator { 60 public: 61 static_assert(std::is_same<std::remove_const_t<B>, char>::value || 62 std::is_same<std::remove_const_t<B>, unsigned char>::value, 63 "Underlying buffer type must be char-type."); 64 BufferIterator()65 BufferIterator() {} BufferIterator(B * data,size_t size)66 BufferIterator(B* data, size_t size) 67 : BufferIterator(make_span(data, size)) {} BufferIterator(span<B> buffer)68 explicit BufferIterator(span<B> buffer) 69 : buffer_(buffer), remaining_(buffer) {} ~BufferIterator()70 ~BufferIterator() {} 71 72 // Returns a pointer to a mutable structure T in the buffer at the current 73 // position. On success, the iterator position is advanced by sizeof(T). If 74 // there are not sizeof(T) bytes remaining in the buffer, returns nullptr. 75 template <typename T, 76 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> MutableObject()77 T* MutableObject() { 78 size_t size = sizeof(T); 79 if (size > remaining_.size()) 80 return nullptr; 81 T* t = reinterpret_cast<T*>(remaining_.data()); 82 remaining_ = remaining_.subspan(size); 83 return t; 84 } 85 86 // Returns a const pointer to an object of type T in the buffer at the current 87 // position. 88 template <typename T, 89 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> Object()90 const T* Object() { 91 return MutableObject<const T>(); 92 } 93 94 // Copies out an object. As compared to using Object, this avoids potential 95 // unaligned access which may be undefined behavior. 96 template <typename T, 97 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> CopyObject()98 absl::optional<T> CopyObject() { 99 absl::optional<T> t; 100 if (remaining_.size() >= sizeof(T)) { 101 memcpy(&t.emplace(), remaining_.data(), sizeof(T)); 102 remaining_ = remaining_.subspan(sizeof(T)); 103 } 104 return t; 105 } 106 107 // Returns a span of |count| T objects in the buffer at the current position. 108 // On success, the iterator position is advanced by |sizeof(T) * count|. If 109 // there are not enough bytes remaining in the buffer to fulfill the request, 110 // returns an empty span. 111 template <typename T, 112 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> MutableSpan(size_t count)113 span<T> MutableSpan(size_t count) { 114 size_t size; 115 if (!CheckMul(sizeof(T), count).AssignIfValid(&size)) 116 return span<T>(); 117 if (size > remaining_.size()) 118 return span<T>(); 119 auto result = span<T>(reinterpret_cast<T*>(remaining_.data()), count); 120 remaining_ = remaining_.subspan(size); 121 return result; 122 } 123 124 // Returns a span to |count| const objects of type T in the buffer at the 125 // current position. 126 template <typename T, 127 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> Span(size_t count)128 span<const T> Span(size_t count) { 129 return MutableSpan<const T>(count); 130 } 131 132 // Resets the iterator position to the absolute offset |to|. Seek(size_t to)133 void Seek(size_t to) { remaining_ = buffer_.subspan(to); } 134 135 // Limits the remaining data to the specified size. 136 // Seeking to an absolute offset reverses this. TruncateTo(size_t size)137 void TruncateTo(size_t size) { remaining_ = remaining_.first(size); } 138 139 // Returns the total size of the underlying buffer. total_size()140 size_t total_size() const { return buffer_.size(); } 141 142 // Returns the current position in the buffer. position()143 size_t position() const { 144 DCHECK(buffer_.data() <= remaining_.data()); 145 DCHECK(remaining_.data() <= buffer_.data() + buffer_.size()); 146 return static_cast<size_t>(remaining_.data() - buffer_.data()); 147 } 148 149 private: 150 // The original buffer that the iterator was constructed with. 151 const span<B> buffer_; 152 // A subspan of |buffer_| containing the remaining bytes to iterate over. 153 span<B> remaining_; 154 // Copy and assign allowed. 155 }; 156 157 } // namespace base 158 159 #endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_ 160