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/numerics/byte_conversions.h" 13 #include "base/numerics/safe_conversions.h" 14 15 namespace base { 16 17 // A Reader to consume elements from the front of a span dynamically. 18 // 19 // SpanReader is used to split off prefix spans from a larger span, reporting 20 // errors if there's not enough room left (instead of crashing, as would happen 21 // with span directly). 22 template <class T> 23 class SpanReader { 24 public: 25 // Construct SpanReader from a span. SpanReader(span<T> buf)26 explicit SpanReader(span<T> buf) : buf_(buf), original_size_(buf_.size()) {} 27 28 // Returns a span over the next `n` objects, if there are enough objects left. 29 // Otherwise, it returns nullopt and does nothing. Read(base::StrictNumeric<size_t> n)30 std::optional<span<T>> Read(base::StrictNumeric<size_t> n) { 31 if (n > remaining()) { 32 return std::nullopt; 33 } 34 auto [lhs, rhs] = buf_.split_at(n); 35 buf_ = rhs; 36 return lhs; 37 } 38 39 // Returns a fixed-size span over the next `N` objects, if there are enough 40 // objects left. Otherwise, it returns nullopt and does nothing. 41 template <size_t N> Read()42 std::optional<span<T, N>> Read() { 43 if (N > remaining()) { 44 return std::nullopt; 45 } 46 auto [lhs, rhs] = buf_.template split_at<N>(); 47 buf_ = rhs; 48 return lhs; 49 } 50 51 // Returns true and writes a span over the next `n` objects into `out`, if 52 // there are enough objects left. Otherwise, it returns false and does 53 // nothing. ReadInto(base::StrictNumeric<size_t> n,span<T> & out)54 bool ReadInto(base::StrictNumeric<size_t> n, span<T>& out) { 55 if (n > remaining()) { 56 return false; 57 } 58 auto [lhs, rhs] = buf_.split_at(n); 59 out = lhs; 60 buf_ = rhs; 61 return true; 62 } 63 64 // Returns true and copies objects into `out`, if there are enough objects 65 // left to fill `out`. Otherwise, it returns false and does nothing. ReadCopy(span<std::remove_const_t<T>> out)66 bool ReadCopy(span<std::remove_const_t<T>> out) { 67 if (out.size() > remaining()) { 68 return false; 69 } 70 auto [lhs, rhs] = buf_.split_at(out.size()); 71 out.copy_from(lhs); 72 buf_ = rhs; 73 return true; 74 } 75 76 // Returns true and skips over the next `n` objects, if there are enough 77 // objects left. Otherwise, it returns false and does nothing. Skip(base::StrictNumeric<size_t> n)78 std::optional<base::span<T>> Skip(base::StrictNumeric<size_t> n) { 79 if (n > remaining()) { 80 return std::nullopt; 81 } 82 auto [lhs, rhs] = buf_.split_at(n); 83 buf_ = rhs; 84 return lhs; 85 } 86 87 // For a SpanReader over bytes, we can read integer values directly from those 88 // bytes as a memcpy. Returns true if there was room remaining and the bytes 89 // were read. 90 // 91 // These treat the bytes from the buffer as being in big endian order. ReadU8BigEndian(uint8_t & value)92 bool ReadU8BigEndian(uint8_t& value) 93 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 94 { 95 return ReadAnd<1>([&](auto buf) { value = U8FromBigEndian(buf); }); 96 } ReadU16BigEndian(uint16_t & value)97 bool ReadU16BigEndian(uint16_t& value) 98 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 99 { 100 return ReadAnd<2>([&](auto buf) { value = U16FromBigEndian(buf); }); 101 } ReadU32BigEndian(uint32_t & value)102 bool ReadU32BigEndian(uint32_t& value) 103 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 104 { 105 return ReadAnd<4>([&](auto buf) { value = U32FromBigEndian(buf); }); 106 } ReadU64BigEndian(uint64_t & value)107 bool ReadU64BigEndian(uint64_t& value) 108 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 109 { 110 return ReadAnd<8>([&](auto buf) { value = U64FromBigEndian(buf); }); 111 } 112 113 // For a SpanReader over bytes, we can read integer values directly from those 114 // bytes as a memcpy. Returns true if there was room remaining and the bytes 115 // were read. 116 // 117 // These treat the bytes from the buffer as being in little endian order. ReadU8LittleEndian(uint8_t & value)118 bool ReadU8LittleEndian(uint8_t& value) 119 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 120 { 121 return ReadAnd<1>([&](auto buf) { value = U8FromLittleEndian(buf); }); 122 } ReadU16LittleEndian(uint16_t & value)123 bool ReadU16LittleEndian(uint16_t& value) 124 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 125 { 126 return ReadAnd<2>([&](auto buf) { value = U16FromLittleEndian(buf); }); 127 } ReadU32LittleEndian(uint32_t & value)128 bool ReadU32LittleEndian(uint32_t& value) 129 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 130 { 131 return ReadAnd<4>([&](auto buf) { value = U32FromLittleEndian(buf); }); 132 } ReadU64LittleEndian(uint64_t & value)133 bool ReadU64LittleEndian(uint64_t& value) 134 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 135 { 136 return ReadAnd<8>([&](auto buf) { value = U64FromLittleEndian(buf); }); 137 } 138 139 // For a SpanReader over bytes, we can read integer values directly from those 140 // bytes as a memcpy. Returns true if there was room remaining and the bytes 141 // were read. 142 // 143 // These treat the bytes from the buffer as being in native endian order. Note 144 // that this is almost never what you want to do. Native ordering only makes 145 // sense for byte buffers that are only meant to stay in memory and never be 146 // written to the disk or network. ReadU8NativeEndian(uint8_t & value)147 bool ReadU8NativeEndian(uint8_t& value) 148 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 149 { 150 return ReadAnd<1>([&](auto buf) { value = U8FromNativeEndian(buf); }); 151 } ReadU16NativeEndian(uint16_t & value)152 bool ReadU16NativeEndian(uint16_t& value) 153 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 154 { 155 return ReadAnd<2>([&](auto buf) { value = U16FromNativeEndian(buf); }); 156 } ReadU32NativeEndian(uint32_t & value)157 bool ReadU32NativeEndian(uint32_t& value) 158 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 159 { 160 return ReadAnd<4>([&](auto buf) { value = U32FromNativeEndian(buf); }); 161 } ReadU64NativeEndian(uint64_t & value)162 bool ReadU64NativeEndian(uint64_t& value) 163 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 164 { 165 return ReadAnd<8>([&](auto buf) { value = U64FromNativeEndian(buf); }); 166 } 167 168 // For a SpanReader over bytes, reads one byte and returns it as a `char`, 169 // which may be signed or unsigned depending on the platform. Returns true if 170 // there was room remaining and the byte was read. ReadChar(char & value)171 bool ReadChar(char& value) 172 requires(std::same_as<std::remove_const_t<T>, uint8_t>) 173 { 174 return ReadAnd<1>([&](auto buf) { value = static_cast<char>(buf[0u]); }); 175 } 176 177 // Returns the number of objects remaining to be read from the original span. remaining()178 size_t remaining() const { return buf_.size(); } 179 // Returns the objects that have not yet been read, as a span. remaining_span()180 span<T> remaining_span() const { return buf_; } 181 182 // Returns the number of objects read (or skipped) in the original span. num_read()183 size_t num_read() const { return original_size_ - buf_.size(); } 184 185 private: 186 template <size_t N, class F> requires(std::invocable<F,span<T,N>>)187 requires(std::invocable<F, span<T, N>>) 188 bool ReadAnd(F f) { 189 auto buf = Read<N>(); 190 if (buf.has_value()) { 191 f(*buf); 192 } 193 return buf.has_value(); 194 } 195 196 span<T> buf_; 197 size_t original_size_; 198 }; 199 200 template <class T, size_t N> 201 SpanReader(span<T, N>) -> SpanReader<T>; 202 203 } // namespace base 204 205 #endif // BASE_CONTAINERS_SPAN_READER_H_ 206