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 <concepts> 11 #include <optional> 12 13 #include "base/compiler_specific.h" 14 #include "base/containers/span.h" 15 #include "base/memory/raw_span.h" 16 #include "base/numerics/checked_math.h" 17 18 namespace base { 19 20 // BufferIterator is a bounds-checked container utility to access variable- 21 // length, heterogeneous structures contained within a buffer. If the data are 22 // homogeneous, use base::span<> instead. 23 // 24 // After being created with a weakly-owned buffer, BufferIterator returns 25 // pointers to structured data within the buffer. After each method call that 26 // returns data in the buffer, the iterator position is advanced by the byte 27 // size of the object (or span of objects) returned. If there are not enough 28 // bytes remaining in the buffer to return the requested object(s), a nullptr 29 // or empty span is returned. 30 // 31 // This class is similar to base::Pickle, which should be preferred for 32 // serializing to disk. Pickle versions its header and does not support writing 33 // structures, which are problematic for serialization due to struct padding and 34 // version shear concerns. 35 // 36 // Example usage: 37 // 38 // std::vector<uint8_t> buffer(4096); 39 // if (!ReadSomeData(&buffer, buffer.size())) { 40 // LOG(ERROR) << "Failed to read data."; 41 // return false; 42 // } 43 // 44 // BufferIterator<uint8_t> iterator(buffer); 45 // uint32_t* num_items = iterator.Object<uint32_t>(); 46 // if (!num_items) { 47 // LOG(ERROR) << "No num_items field." 48 // return false; 49 // } 50 // 51 // base::span<const item_struct> items = 52 // iterator.Span<item_struct>(*num_items); 53 // if (items.size() != *num_items) { 54 // LOG(ERROR) << "Not enough items."; 55 // return false; 56 // } 57 // 58 // // ... validate the objects in |items|. 59 template <typename B> 60 class BufferIterator { 61 public: 62 static_assert(std::same_as<std::remove_const_t<B>, char> || 63 std::same_as<std::remove_const_t<B>, unsigned char>, 64 "Underlying buffer type must be char-type."); 65 // Constructs an empty BufferIterator that will always return null pointers. 66 BufferIterator() = default; 67 68 // Constructs a BufferIterator over the `buffer` span, that will return 69 // pointers into the span. BufferIterator(span<B> buffer)70 explicit BufferIterator(span<B> buffer) 71 : buffer_(buffer), remaining_(buffer) {} 72 73 // TODO(crbug.com/40284755): Move all callers to use spans and remove this. BufferIterator(B * data,size_t size)74 UNSAFE_BUFFER_USAGE BufferIterator(B* data, size_t size) 75 : BufferIterator( 76 // TODO(crbug.com/40284755): Remove this constructor entirely, 77 // callers should provide a span. There's no way to know that the 78 // size is correct here. 79 UNSAFE_BUFFERS(span(data, size))) {} 80 81 // Copies out an object. As compared to using `Object`, this avoids potential 82 // unaligned access which may be undefined behavior. 83 template <typename T, 84 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> CopyObject()85 std::optional<T> CopyObject() { 86 std::optional<T> t; 87 if (remaining_.size() >= sizeof(T)) { 88 auto [source, remain] = remaining_.template split_at<sizeof(T)>(); 89 byte_span_from_ref(t.emplace()).copy_from(as_bytes(source)); 90 remaining_ = remain; 91 } 92 return t; 93 } 94 95 // Returns a const pointer to an object of type T in the buffer at the current 96 // position. 97 // 98 // # Safety 99 // Note that the buffer's current position must be aligned for the type T 100 // or using the pointer will cause Undefined Behaviour. Generally prefer 101 // `CopyObject` as it avoids this problem entirely. 102 // TODO(danakj): We should probably CHECK this instead of allowing UB into 103 // production. 104 template <typename T, 105 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> Object()106 const T* Object() { 107 return MutableObject<const T>(); 108 } 109 110 // Returns a pointer to a mutable structure T in the buffer at the current 111 // position. On success, the iterator position is advanced by sizeof(T). If 112 // there are not sizeof(T) bytes remaining in the buffer, returns nullptr. 113 // 114 // # Safety 115 // Note that the buffer's current position must be aligned for the type T or 116 // using the pointer will cause Undefined Behaviour. Generally prefer 117 // `CopyObject` as it avoids this problem entirely. 118 // TODO(danakj): We should probably CHECK this instead of allowing UB into 119 // production. 120 template <typename T, 121 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> MutableObject()122 T* MutableObject() { 123 T* t = nullptr; 124 if (remaining_.size() >= sizeof(T)) { 125 auto [source, remain] = remaining_.template split_at<sizeof(T)>(); 126 // TODO(danakj): This is UB without creating a lifetime for the object in 127 // the compiler, which we can not do before C++23: 128 // https://en.cppreference.com/w/cpp/memory/start_lifetime_as 129 t = reinterpret_cast<T*>(source.data()); 130 remaining_ = remain; 131 } 132 return t; 133 } 134 135 // Returns a span of |count| T objects in the buffer at the current position. 136 // On success, the iterator position is advanced by |sizeof(T) * count|. If 137 // there are not enough bytes remaining in the buffer to fulfill the request, 138 // returns an empty span. 139 // 140 // # Safety 141 // Note that the buffer's current position must be aligned for the type T or 142 // using the span will cause Undefined Behaviour. 143 // TODO(danakj): We should probably CHECK this instead of allowing UB into 144 // production. 145 template <typename T, 146 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> MutableSpan(size_t count)147 span<T> MutableSpan(size_t count) { 148 size_t byte_size; 149 if (!CheckMul(sizeof(T), count).AssignIfValid(&byte_size)) { 150 return span<T>(); 151 } 152 if (byte_size > remaining_.size()) { 153 return span<T>(); 154 } 155 auto [lhs, rhs] = remaining_.split_at(byte_size); 156 remaining_ = rhs; 157 // SAFETY: The byte size of `span<T>` with size `count` is `count * 158 // sizeof(T)` which is exactly `byte_size`, the byte size of `lhs`. 159 // 160 // TODO(danakj): This is UB without creating a lifetime for the object in 161 // the compiler, which we can not do before C++23: 162 // https://en.cppreference.com/w/cpp/memory/start_lifetime_as 163 return UNSAFE_BUFFERS(span<T>(reinterpret_cast<T*>(lhs.data()), count)); 164 } 165 166 // An overload for when the size is known at compile time. The result will be 167 // a fixed-size span. 168 template <typename T, 169 size_t N, 170 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> 171 requires(N <= std::numeric_limits<size_t>::max() / sizeof(T)) MutableSpan()172 std::optional<span<T, N>> MutableSpan() { 173 constexpr size_t byte_size = 174 N * sizeof(T); // Overflow is checked by `requires`. 175 if (byte_size > remaining_.size()) { 176 return std::nullopt; 177 } 178 auto [lhs, rhs] = remaining_.split_at(byte_size); 179 remaining_ = rhs; 180 // SAFETY: The byte size of `span<T>` with size `count` is `count * 181 // sizeof(T)` which is exactly `byte_size`, the byte size of `lhs`. 182 // 183 // TODO(danakj): This is UB without creating a lifetime for the object in 184 // the compiler, which we can not do before C++23: 185 // https://en.cppreference.com/w/cpp/memory/start_lifetime_as 186 return UNSAFE_BUFFERS(span<T, N>(reinterpret_cast<T*>(lhs.data()), N)); 187 } 188 189 // Returns a span to |count| const objects of type T in the buffer at the 190 // current position. 191 // 192 // # Safety 193 // Note that the buffer's current position must be aligned for the type T or 194 // using the span will cause Undefined Behaviour. 195 // TODO(danakj): We should probably CHECK this instead of allowing UB into 196 // production. 197 template <typename T, 198 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> Span(size_t count)199 span<const T> Span(size_t count) { 200 return MutableSpan<const T>(count); 201 } 202 203 // An overload for when the size is known at compile time. The result will be 204 // a fixed-size span. 205 template <typename T, 206 size_t N, 207 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> 208 requires(N <= std::numeric_limits<size_t>::max() / sizeof(T)) Span()209 std::optional<span<const T, N>> Span() { 210 return MutableSpan<const T, N>(); 211 } 212 213 // Resets the iterator position to the absolute offset |to|. Seek(size_t to)214 void Seek(size_t to) { remaining_ = buffer_.subspan(to); } 215 216 // Limits the remaining data to the specified size. 217 // Seeking to an absolute offset reverses this. TruncateTo(size_t size)218 void TruncateTo(size_t size) { remaining_ = remaining_.first(size); } 219 220 // Returns the total size of the underlying buffer. total_size()221 size_t total_size() const { return buffer_.size(); } 222 223 // Returns the current position in the buffer. position()224 size_t position() const { 225 // SAFETY: `remaining_` is a subspan always constructed from `buffer_` (or 226 // from itself) so its `data()` pointer is always inside `buffer_`. This 227 // means the subtraction is well-defined and the result is always 228 // non-negative. 229 return static_cast<size_t>( 230 UNSAFE_BUFFERS(remaining_.data() - buffer_.data())); 231 } 232 233 private: 234 // The original buffer that the iterator was constructed with. 235 const raw_span<B> buffer_; 236 // A subspan of `buffer_` containing the remaining bytes to iterate over. 237 raw_span<B> remaining_; 238 // Copy and assign allowed. 239 }; 240 241 } // namespace base 242 243 #endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_ 244