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