xref: /aosp_15_r20/external/cronet/base/containers/span_reader.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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