• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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