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