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/numerics/checked_math.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::same_as<std::remove_const_t<B>, char> || 62 std::same_as<std::remove_const_t<B>, unsigned char>, 63 "Underlying buffer type must be char-type."); 64 // Constructs an empty BufferIterator that will always return null pointers. BufferIterator()65 BufferIterator() {} 66 67 // Constructs a BufferIterator over the `buffer` span, that will return 68 // pointers into the span. BufferIterator(span<B> buffer)69 explicit BufferIterator(span<B> buffer) 70 : buffer_(buffer), remaining_(buffer) {} 71 72 // TODO(crbug.com/40284755): Move all callers to use spans and remove this. BufferIterator(B * data,size_t size)73 UNSAFE_BUFFER_USAGE BufferIterator(B* data, size_t size) 74 : BufferIterator( 75 // TODO(crbug.com/40284755): Remove this constructor entirely, 76 // callers should provide a span. There's no way to know that the 77 // size is correct here. 78 UNSAFE_BUFFERS(span(data, size))) {} 79 80 // Copies out an object. As compared to using `Object`, this avoids potential 81 // unaligned access which may be undefined behavior. 82 template <typename T, 83 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> CopyObject()84 std::optional<T> CopyObject() { 85 std::optional<T> t; 86 if (remaining_.size() >= sizeof(T)) { 87 auto [source, remain] = remaining_.template split_at<sizeof(T)>(); 88 byte_span_from_ref(t.emplace()).copy_from(as_bytes(source)); 89 remaining_ = remain; 90 } 91 return t; 92 } 93 94 // Returns a const pointer to an object of type T in the buffer at the current 95 // position. 96 // 97 // # Safety 98 // Note that the buffer's current position must be aligned for the type T 99 // or using the pointer will cause Undefined Behaviour. Generally prefer 100 // `CopyObject` as it avoids this problem entirely. 101 // TODO(danakj): We should probably CHECK this instead of allowing UB into 102 // production. 103 template <typename T, 104 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> Object()105 const T* Object() { 106 return MutableObject<const T>(); 107 } 108 109 // Returns a pointer to a mutable structure T in the buffer at the current 110 // position. On success, the iterator position is advanced by sizeof(T). If 111 // there are not sizeof(T) bytes remaining in the buffer, returns nullptr. 112 // 113 // # Safety 114 // Note that the buffer's current position must be aligned for the type T or 115 // using the pointer will cause Undefined Behaviour. Generally prefer 116 // `CopyObject` as it avoids this problem entirely. 117 // TODO(danakj): We should probably CHECK this instead of allowing UB into 118 // production. 119 template <typename T, 120 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> MutableObject()121 T* MutableObject() { 122 T* t = nullptr; 123 if (remaining_.size() >= sizeof(T)) { 124 auto [source, remain] = remaining_.template split_at<sizeof(T)>(); 125 // TODO(danakj): This is UB without creating a lifetime for the object in 126 // the compiler, which we can not do before C++23: 127 // https://en.cppreference.com/w/cpp/memory/start_lifetime_as 128 t = reinterpret_cast<T*>(source.data()); 129 remaining_ = remain; 130 } 131 return t; 132 } 133 134 // Returns a span of |count| T objects in the buffer at the current position. 135 // On success, the iterator position is advanced by |sizeof(T) * count|. If 136 // there are not enough bytes remaining in the buffer to fulfill the request, 137 // returns an empty span. 138 // 139 // # Safety 140 // Note that the buffer's current position must be aligned for the type T or 141 // using the span will cause Undefined Behaviour. 142 // TODO(danakj): We should probably CHECK this instead of allowing UB into 143 // production. 144 template <typename T, 145 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> MutableSpan(size_t count)146 span<T> MutableSpan(size_t count) { 147 size_t byte_size; 148 if (!CheckMul(sizeof(T), count).AssignIfValid(&byte_size)) { 149 return span<T>(); 150 } 151 if (byte_size > remaining_.size()) { 152 return span<T>(); 153 } 154 auto [lhs, rhs] = remaining_.split_at(byte_size); 155 remaining_ = rhs; 156 // SAFETY: The byte size of `span<T>` with size `count` is `count * 157 // sizeof(T)` which is exactly `byte_size`, the byte size of `lhs`. 158 // 159 // TODO(danakj): This is UB without creating a lifetime for the object in 160 // the compiler, which we can not do before C++23: 161 // https://en.cppreference.com/w/cpp/memory/start_lifetime_as 162 return UNSAFE_BUFFERS(span<T>(reinterpret_cast<T*>(lhs.data()), count)); 163 } 164 165 // An overload for when the size is known at compile time. The result will be 166 // a fixed-size span. 167 template <typename T, 168 size_t N, 169 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> 170 requires(N <= std::numeric_limits<size_t>::max() / sizeof(T)) MutableSpan()171 std::optional<span<T, N>> MutableSpan() { 172 constexpr size_t byte_size = 173 N * sizeof(T); // Overflow is checked by `requires`. 174 if (byte_size > remaining_.size()) { 175 return std::nullopt; 176 } 177 auto [lhs, rhs] = remaining_.split_at(byte_size); 178 remaining_ = rhs; 179 // SAFETY: The byte size of `span<T>` with size `count` is `count * 180 // sizeof(T)` which is exactly `byte_size`, the byte size of `lhs`. 181 // 182 // TODO(danakj): This is UB without creating a lifetime for the object in 183 // the compiler, which we can not do before C++23: 184 // https://en.cppreference.com/w/cpp/memory/start_lifetime_as 185 return UNSAFE_BUFFERS(span<T, N>(reinterpret_cast<T*>(lhs.data()), N)); 186 } 187 188 // Returns a span to |count| const objects of type T in the buffer at the 189 // current position. 190 // 191 // # Safety 192 // Note that the buffer's current position must be aligned for the type T or 193 // using the span will cause Undefined Behaviour. 194 // TODO(danakj): We should probably CHECK this instead of allowing UB into 195 // production. 196 template <typename T, 197 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> Span(size_t count)198 span<const T> Span(size_t count) { 199 return MutableSpan<const T>(count); 200 } 201 202 // An overload for when the size is known at compile time. The result will be 203 // a fixed-size span. 204 template <typename T, 205 size_t N, 206 typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> 207 requires(N <= std::numeric_limits<size_t>::max() / sizeof(T)) Span()208 std::optional<span<const T, N>> Span() { 209 return MutableSpan<const T, N>(); 210 } 211 212 // Resets the iterator position to the absolute offset |to|. Seek(size_t to)213 void Seek(size_t to) { remaining_ = buffer_.subspan(to); } 214 215 // Limits the remaining data to the specified size. 216 // Seeking to an absolute offset reverses this. TruncateTo(size_t size)217 void TruncateTo(size_t size) { remaining_ = remaining_.first(size); } 218 219 // Returns the total size of the underlying buffer. total_size()220 size_t total_size() const { return buffer_.size(); } 221 222 // Returns the current position in the buffer. position()223 size_t position() const { 224 // SAFETY: `remaining_` is a subspan always constructed from `buffer_` (or 225 // from itself) so its `data()` pointer is always inside `buffer_`. This 226 // means the subtraction is well-defined and the result is always 227 // non-negative. 228 return static_cast<size_t>( 229 UNSAFE_BUFFERS(remaining_.data() - buffer_.data())); 230 } 231 232 private: 233 // The original buffer that the iterator was constructed with. 234 const span<B> buffer_; 235 // A subspan of `buffer_` containing the remaining bytes to iterate over. 236 span<B> remaining_; 237 // Copy and assign allowed. 238 }; 239 240 } // namespace base 241 242 #endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_ 243