1 // Copyright 2024 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_SPAN_READER_H_ 6 #define BASE_CONTAINERS_SPAN_READER_H_ 7 8 #include <concepts> 9 #include <optional> 10 11 #include "base/containers/span.h" 12 #include "base/memory/stack_allocated.h" 13 #include "base/numerics/byte_conversions.h" 14 #include "base/numerics/safe_conversions.h" 15 16 namespace base { 17 18 // A Reader to consume elements from the front of a span dynamically. 19 // 20 // SpanReader is used to split off prefix spans from a larger span, reporting 21 // errors if there's not enough room left (instead of crashing, as would happen 22 // with span directly). 23 template <class T> 24 class SpanReader { 25 STACK_ALLOCATED(); 26 27 public: 28 // Construct SpanReader from a span. SpanReader(span<T> buf)29 explicit SpanReader(span<T> buf) : buf_(buf), original_size_(buf_.size()) {} 30 31 // Returns a span over the next `n` objects, if there are enough objects left. 32 // Otherwise, it returns nullopt and does nothing. Read(StrictNumeric<size_t> n)33 std::optional<span<T>> Read(StrictNumeric<size_t> n) { 34 if (n > remaining()) { 35 return std::nullopt; 36 } 37 auto [lhs, rhs] = buf_.split_at(n); 38 buf_ = rhs; 39 return lhs; 40 } 41 42 // Returns a fixed-size span over the next `N` objects, if there are enough 43 // objects left. Otherwise, it returns nullopt and does nothing. 44 template <size_t N> Read()45 std::optional<span<T, N>> Read() { 46 if (N > remaining()) { 47 return std::nullopt; 48 } 49 auto [lhs, rhs] = buf_.template split_at<N>(); 50 buf_ = rhs; 51 return lhs; 52 } 53 54 // Returns true and writes a span over the next `n` objects into `out`, if 55 // there are enough objects left. Otherwise, it returns false and does 56 // nothing. ReadInto(StrictNumeric<size_t> n,span<T> & out)57 bool ReadInto(StrictNumeric<size_t> n, span<T>& out) { 58 if (n > remaining()) { 59 return false; 60 } 61 auto [lhs, rhs] = buf_.split_at(n); 62 out = lhs; 63 buf_ = rhs; 64 return true; 65 } 66 67 // Returns true and copies objects into `out`, if there are enough objects 68 // left to fill `out`. Otherwise, it returns false and does nothing. ReadCopy(span<std::remove_const_t<T>> out)69 bool ReadCopy(span<std::remove_const_t<T>> out) { 70 if (out.size() > remaining()) { 71 return false; 72 } 73 auto [lhs, rhs] = buf_.split_at(out.size()); 74 out.copy_from(lhs); 75 buf_ = rhs; 76 return true; 77 } 78 79 // Returns true and skips over the next `n` objects, if there are enough 80 // objects left. Otherwise, it returns false and does nothing. Skip(StrictNumeric<size_t> n)81 std::optional<span<T>> Skip(StrictNumeric<size_t> n) { 82 if (n > remaining()) { 83 return std::nullopt; 84 } 85 auto [lhs, rhs] = buf_.split_at(n); 86 buf_ = rhs; 87 return lhs; 88 } 89 90 // For a SpanReader over bytes, we can read integer values directly from those 91 // bytes as a memcpy. Returns true if there was room remaining and the bytes 92 // were read. 93 // 94 // These treat the bytes from the buffer as being in big endian order. ReadU8BigEndian(uint8_t & value)95 bool ReadU8BigEndian(uint8_t& value) 96 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 97 { 98 return ReadAnd<1>([&](auto buf) { value = U8FromBigEndian(buf); }); 99 } ReadU16BigEndian(uint16_t & value)100 bool ReadU16BigEndian(uint16_t& value) 101 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 102 { 103 return ReadAnd<2>([&](auto buf) { value = U16FromBigEndian(buf); }); 104 } ReadU32BigEndian(uint32_t & value)105 bool ReadU32BigEndian(uint32_t& value) 106 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 107 { 108 return ReadAnd<4>([&](auto buf) { value = U32FromBigEndian(buf); }); 109 } ReadU64BigEndian(uint64_t & value)110 bool ReadU64BigEndian(uint64_t& value) 111 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 112 { 113 return ReadAnd<8>([&](auto buf) { value = U64FromBigEndian(buf); }); 114 } 115 116 // For a SpanReader over bytes, we can read integer values directly from those 117 // bytes as a memcpy. Returns true if there was room remaining and the bytes 118 // were read. 119 // 120 // These treat the bytes from the buffer as being in little endian order. ReadU8LittleEndian(uint8_t & value)121 bool ReadU8LittleEndian(uint8_t& value) 122 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 123 { 124 return ReadAnd<1>([&](auto buf) { value = U8FromLittleEndian(buf); }); 125 } ReadU16LittleEndian(uint16_t & value)126 bool ReadU16LittleEndian(uint16_t& value) 127 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 128 { 129 return ReadAnd<2>([&](auto buf) { value = U16FromLittleEndian(buf); }); 130 } ReadU32LittleEndian(uint32_t & value)131 bool ReadU32LittleEndian(uint32_t& value) 132 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 133 { 134 return ReadAnd<4>([&](auto buf) { value = U32FromLittleEndian(buf); }); 135 } ReadU64LittleEndian(uint64_t & value)136 bool ReadU64LittleEndian(uint64_t& value) 137 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 138 { 139 return ReadAnd<8>([&](auto buf) { value = U64FromLittleEndian(buf); }); 140 } 141 142 // For a SpanReader over bytes, we can read integer values directly from those 143 // bytes as a memcpy. Returns true if there was room remaining and the bytes 144 // were read. 145 // 146 // These treat the bytes from the buffer as being in native endian order. Note 147 // that this is almost never what you want to do. Native ordering only makes 148 // sense for byte buffers that are only meant to stay in memory and never be 149 // written to the disk or network. ReadU8NativeEndian(uint8_t & value)150 bool ReadU8NativeEndian(uint8_t& value) 151 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 152 { 153 return ReadAnd<1>([&](auto buf) { value = U8FromNativeEndian(buf); }); 154 } ReadU16NativeEndian(uint16_t & value)155 bool ReadU16NativeEndian(uint16_t& value) 156 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 157 { 158 return ReadAnd<2>([&](auto buf) { value = U16FromNativeEndian(buf); }); 159 } ReadU32NativeEndian(uint32_t & value)160 bool ReadU32NativeEndian(uint32_t& value) 161 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 162 { 163 return ReadAnd<4>([&](auto buf) { value = U32FromNativeEndian(buf); }); 164 } ReadU64NativeEndian(uint64_t & value)165 bool ReadU64NativeEndian(uint64_t& value) 166 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 167 { 168 return ReadAnd<8>([&](auto buf) { value = U64FromNativeEndian(buf); }); 169 } 170 171 // For a SpanReader over bytes, we can read integer values directly from those 172 // bytes as a memcpy. Returns true if there was room remaining and the bytes 173 // were read. 174 // 175 // These treat the bytes from the buffer as being in big endian order. ReadI8BigEndian(int8_t & value)176 bool ReadI8BigEndian(int8_t& value) 177 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 178 { 179 return ReadAnd<1>([&](auto buf) { value = I8FromBigEndian(buf); }); 180 } ReadI16BigEndian(int16_t & value)181 bool ReadI16BigEndian(int16_t& value) 182 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 183 { 184 return ReadAnd<2>([&](auto buf) { value = I16FromBigEndian(buf); }); 185 } ReadI32BigEndian(int32_t & value)186 bool ReadI32BigEndian(int32_t& value) 187 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 188 { 189 return ReadAnd<4>([&](auto buf) { value = I32FromBigEndian(buf); }); 190 } ReadI64BigEndian(int64_t & value)191 bool ReadI64BigEndian(int64_t& value) 192 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 193 { 194 return ReadAnd<8>([&](auto buf) { value = I64FromBigEndian(buf); }); 195 } 196 197 // For a SpanReader over bytes, we can read integer values directly from those 198 // bytes as a memcpy. Returns true if there was room remaining and the bytes 199 // were read. 200 // 201 // These treat the bytes from the buffer as being in little endian order. ReadI8LittleEndian(int8_t & value)202 bool ReadI8LittleEndian(int8_t& value) 203 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 204 { 205 return ReadAnd<1>([&](auto buf) { value = I8FromLittleEndian(buf); }); 206 } ReadI16LittleEndian(int16_t & value)207 bool ReadI16LittleEndian(int16_t& value) 208 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 209 { 210 return ReadAnd<2>([&](auto buf) { value = I16FromLittleEndian(buf); }); 211 } ReadI32LittleEndian(int32_t & value)212 bool ReadI32LittleEndian(int32_t& value) 213 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 214 { 215 return ReadAnd<4>([&](auto buf) { value = I32FromLittleEndian(buf); }); 216 } ReadI64LittleEndian(int64_t & value)217 bool ReadI64LittleEndian(int64_t& value) 218 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 219 { 220 return ReadAnd<8>([&](auto buf) { value = I64FromLittleEndian(buf); }); 221 } 222 223 // For a SpanReader over bytes, we can read integer values directly from those 224 // bytes as a memcpy. Returns true if there was room remaining and the bytes 225 // were read. 226 // 227 // These treat the bytes from the buffer as being in native endian order. Note 228 // that this is almost never what you want to do. Native ordering only makes 229 // sense for byte buffers that are only meant to stay in memory and never be 230 // written to the disk or network. ReadI8NativeEndian(int8_t & value)231 bool ReadI8NativeEndian(int8_t& value) 232 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 233 { 234 return ReadAnd<1>([&](auto buf) { value = I8FromNativeEndian(buf); }); 235 } ReadI16NativeEndian(int16_t & value)236 bool ReadI16NativeEndian(int16_t& value) 237 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 238 { 239 return ReadAnd<2>([&](auto buf) { value = I16FromNativeEndian(buf); }); 240 } ReadI32NativeEndian(int32_t & value)241 bool ReadI32NativeEndian(int32_t& value) 242 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 243 { 244 return ReadAnd<4>([&](auto buf) { value = I32FromNativeEndian(buf); }); 245 } ReadI64NativeEndian(int64_t & value)246 bool ReadI64NativeEndian(int64_t& value) 247 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 248 { 249 return ReadAnd<8>([&](auto buf) { value = I64FromNativeEndian(buf); }); 250 } 251 252 // For a SpanReader over bytes, reads one byte and returns it as a `char`, 253 // which may be signed or unsigned depending on the platform. Returns true if 254 // there was room remaining and the byte was read. ReadChar(char & value)255 bool ReadChar(char& value) 256 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 257 { 258 return ReadAnd<1>([&](auto buf) { value = static_cast<char>(buf[0u]); }); 259 } 260 261 // Returns the number of objects remaining to be read from the original span. remaining()262 size_t remaining() const { return buf_.size(); } 263 // Returns the objects that have not yet been read, as a span. remaining_span()264 span<T> remaining_span() const { return buf_; } 265 266 // Returns the number of objects read (or skipped) in the original span. num_read()267 size_t num_read() const { return original_size_ - buf_.size(); } 268 269 private: 270 template <size_t N, class F> requires(std::invocable<F,span<T,N>>)271 requires(std::invocable<F, span<T, N>>) 272 bool ReadAnd(F f) { 273 auto buf = Read<N>(); 274 if (buf.has_value()) { 275 f(*buf); 276 } 277 return buf.has_value(); 278 } 279 280 span<T> buf_; 281 size_t original_size_; 282 }; 283 284 template <typename ElementType, size_t Extent, typename InternalPtrType> 285 SpanReader(span<ElementType, Extent, InternalPtrType>) 286 -> SpanReader<ElementType>; 287 288 } // namespace base 289 290 #endif // BASE_CONTAINERS_SPAN_READER_H_ 291