xref: /aosp_15_r20/external/cronet/base/containers/span_writer.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_WRITER_H_
6 #define BASE_CONTAINERS_SPAN_WRITER_H_
7 
8 #include <optional>
9 
10 #include "base/containers/span.h"
11 #include "base/memory/raw_span.h"
12 #include "base/numerics/byte_conversions.h"
13 
14 namespace base {
15 
16 // A Writer to write into and consume elements from the front of a span
17 // dynamically.
18 //
19 // SpanWriter 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 SpanWriter {
24   static_assert(!std::is_const_v<T>,
25                 "SpanWriter needs mutable access to its buffer");
26 
27  public:
28   // Construct SpanWriter that writes to `buf`.
SpanWriter(span<T> buf)29   explicit SpanWriter(span<T> buf) : buf_(buf), original_size_(buf_.size()) {}
30 
31   // Returns true and writes the span `data` into the front of the inner span,
32   // if there is enough room left. Otherwise, it returns false and does
33   // nothing.
Write(span<const T> data)34   bool Write(span<const T> data) {
35     if (data.size() > remaining()) {
36       return false;
37     }
38     auto [lhs, rhs] = buf_.split_at(data.size());
39     lhs.copy_from(data);
40     buf_ = rhs;
41     return true;
42   }
43 
44   // Skips over the next `n` objects, and returns a span that points to the
45   // skipped objects, if there are enough objects left. Otherwise, it returns
46   // nullopt and does nothing.
Skip(StrictNumeric<size_t> n)47   std::optional<span<T>> Skip(StrictNumeric<size_t> n) {
48     if (n > remaining()) {
49       return std::nullopt;
50     }
51     auto [lhs, rhs] = buf_.split_at(n);
52     buf_ = rhs;
53     return lhs;
54   }
55   // Skips over the next `N` objects, and returns a fixed-size span that points
56   // to the skipped objects, if there are enough objects left. Otherwise, it
57   // returns nullopt and does nothing.
58   template <size_t N>
Skip()59   std::optional<span<T, N>> Skip() {
60     if (N > remaining()) {
61       return std::nullopt;
62     }
63     auto [lhs, rhs] = buf_.template split_at<N>();
64     buf_ = rhs;
65     return lhs;
66   }
67 
68   // For a SpanWriter over bytes, we can write integer values directly to those
69   // bytes as a memcpy. Returns true if there was room remaining and the bytes
70   // were written.
71   //
72   // These copy the bytes into the buffer in big endian order.
WriteU8BigEndian(uint8_t value)73   bool WriteU8BigEndian(uint8_t value)
74     requires(std::same_as<T, uint8_t>)
75   {
76     return Write(U8ToBigEndian(value));
77   }
WriteU16BigEndian(uint16_t value)78   bool WriteU16BigEndian(uint16_t value)
79     requires(std::same_as<T, uint8_t>)
80   {
81     return Write(U16ToBigEndian(value));
82   }
WriteU32BigEndian(uint32_t value)83   bool WriteU32BigEndian(uint32_t value)
84     requires(std::same_as<T, uint8_t>)
85   {
86     return Write(U32ToBigEndian(value));
87   }
WriteU64BigEndian(uint64_t value)88   bool WriteU64BigEndian(uint64_t value)
89     requires(std::same_as<T, uint8_t>)
90   {
91     return Write(U64ToBigEndian(value));
92   }
93 
94   // For a SpanWriter over bytes, we can write integer values directly to those
95   // bytes as a memcpy. Returns true if there was room remaining and the bytes
96   // were written.
97   //
98   // These copy the bytes into the buffer in little endian order.
WriteU8LittleEndian(uint8_t value)99   bool WriteU8LittleEndian(uint8_t value)
100     requires(std::same_as<T, uint8_t>)
101   {
102     return Write(U8ToLittleEndian(value));
103   }
WriteU16LittleEndian(uint16_t value)104   bool WriteU16LittleEndian(uint16_t value)
105     requires(std::same_as<T, uint8_t>)
106   {
107     return Write(U16ToLittleEndian(value));
108   }
WriteU32LittleEndian(uint32_t value)109   bool WriteU32LittleEndian(uint32_t value)
110     requires(std::same_as<T, uint8_t>)
111   {
112     return Write(U32ToLittleEndian(value));
113   }
WriteU64LittleEndian(uint64_t value)114   bool WriteU64LittleEndian(uint64_t value)
115     requires(std::same_as<T, uint8_t>)
116   {
117     return Write(U64ToLittleEndian(value));
118   }
119 
120   // For a SpanWriter over bytes, we can write integer values directly to those
121   // bytes as a memcpy. Returns true if there was room remaining and the bytes
122   // were written.
123   //
124   // These copy the bytes into the buffer in native endian order. Note that this
125   // is almost never what you want to do. Native ordering only makes sense for
126   // byte buffers that are only meant to stay in memory and never be written to
127   // the disk or network.
WriteU8NativeEndian(uint8_t value)128   bool WriteU8NativeEndian(uint8_t value)
129     requires(std::same_as<T, uint8_t>)
130   {
131     return Write(U8ToNativeEndian(value));
132   }
WriteU16NativeEndian(uint16_t value)133   bool WriteU16NativeEndian(uint16_t value)
134     requires(std::same_as<T, uint8_t>)
135   {
136     return Write(U16ToNativeEndian(value));
137   }
WriteU32NativeEndian(uint32_t value)138   bool WriteU32NativeEndian(uint32_t value)
139     requires(std::same_as<T, uint8_t>)
140   {
141     return Write(U32ToNativeEndian(value));
142   }
WriteU64NativeEndian(uint64_t value)143   bool WriteU64NativeEndian(uint64_t value)
144     requires(std::same_as<T, uint8_t>)
145   {
146     return Write(U64ToNativeEndian(value));
147   }
148 
149   // Returns the number of objects remaining to be written to the original span.
remaining()150   size_t remaining() const { return buf_.size(); }
151   // Returns the objects that have not yet been written to, as a span.
remaining_span()152   span<T> remaining_span() const { return buf_; }
153 
154   // Returns the number of objects written (or skipped) in the original span.
num_written()155   size_t num_written() const { return original_size_ - buf_.size(); }
156 
157  private:
158   raw_span<T> buf_;
159   size_t original_size_;
160 };
161 
162 template <class T, size_t N>
163 SpanWriter(span<T, N>) -> SpanWriter<T>;
164 
165 }  // namespace base
166 
167 #endif  // BASE_CONTAINERS_SPAN_WRITER_H_
168