blob: 2f684b7a73ca5fa39db730ff0b3afddad99b621a [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CONTAINERS_SPAN_WRITER_H_
#define BASE_CONTAINERS_SPAN_WRITER_H_
#include <optional>
#include "base/containers/span.h"
#include "base/memory/raw_span.h"
#include "base/numerics/byte_conversions.h"
namespace base {
// A Writer to write into and consume elements from the front of a span
// dynamically.
//
// SpanWriter is used to split off prefix spans from a larger span, reporting
// errors if there's not enough room left (instead of crashing, as would happen
// with span directly).
template <typename T>
class SpanWriter {
static_assert(!std::is_const_v<T>,
"SpanWriter needs mutable access to its buffer");
public:
// Construct SpanWriter that writes to `buf`.
constexpr explicit SpanWriter(span<T> buf)
: buf_(buf), original_size_(buf_.size()) {}
// Returns true and writes the span `data` into the front of the inner span,
// if there is enough room left. Otherwise, it returns false and does
// nothing.
constexpr bool Write(span<const T> data) {
if (data.size() > remaining()) {
return false;
}
auto [lhs, rhs] = buf_.split_at(data.size());
lhs.copy_from(data);
buf_ = rhs;
return true;
}
// Returns true and writes `value` into the front of the inner span if there
// is space remaining. Otherwise, it returns false and does nothing.
template <typename V>
requires(std::same_as<T, std::remove_cvref_t<V>>)
bool Write(V&& value) {
if (!remaining()) {
return false;
}
buf_[0] = std::forward<V>(value);
buf_ = buf_.last(remaining() - 1);
return true;
}
// Skips over the next `n` objects, and returns a span that points to the
// skipped objects, if there are enough objects left. Otherwise, it returns
// nullopt and does nothing.
constexpr std::optional<span<T>> Skip(StrictNumeric<size_t> n) {
if (n > remaining()) {
return std::nullopt;
}
auto [lhs, rhs] = buf_.split_at(n);
buf_ = rhs;
return lhs;
}
template <size_t N>
constexpr std::optional<span<T, N>> Skip() {
if (N > remaining()) {
return std::nullopt;
}
auto [lhs, rhs] = buf_.template split_at<N>();
buf_ = rhs;
return lhs;
}
// For a SpanWriter over bytes, we can write integer values directly to those
// bytes as a memcpy. Returns true if there was room remaining and the bytes
// were written. The macros below implement the following methods:
//
// bool WriteU8BigEndian(uint8_t)
// bool WriteU16BigEndian(uint16_t)
// bool WriteU32BigEndian(uint32_t)
// bool WriteU64BigEndian(uint64_t)
// bool WriteU8LittleEndian(uint8_t)
// bool WriteU16LittleEndian(uint16_t)
// bool WriteU32LittleEndian(uint32_t)
// bool WriteU64LittleEndian(uint64_t)
// bool WriteU8NativeEndian(uint8_t)
// bool WriteU16NativeEndian(uint16_t)
// bool WriteU32NativeEndian(uint32_t)
// bool WriteU64NativeEndian(uint64_t)
// bool WriteI8BigEndian(int8_t)
// bool WriteI16BigEndian(int16_t)
// bool WriteI32BigEndian(int32_t)
// bool WriteI64BigEndian(int64_t)
// bool WriteI8LittleEndian(int8_t)
// bool WriteI16LittleEndian(int16_t)
// bool WriteI32LittleEndian(int32_t)
// bool WriteI64LittleEndian(int64_t)
// bool WriteI8NativeEndian(int8_t)
// bool WriteI16NativeEndian(int16_t)
// bool WriteI32NativeEndian(int32_t)
// bool WriteI64NativeEndian(int64_t)
//
// Note that "native" order is almost never what you want; it only makes sense
// for byte buffers that stay in memory and are never written to the disk or
// network.
#define BASE_SPANWRITER_WRITE(signchar, bitsize, endian, typeprefix) \
constexpr bool Write##signchar##bitsize##endian##Endian( \
typeprefix##int##bitsize##_t value) \
requires(std::same_as<T, uint8_t>) \
{ \
return Write(signchar##bitsize##To##endian##Endian(value)); \
}
#define BASE_SPANWRITER_WRITE_BOTH_SIGNS(bitsize, endian) \
BASE_SPANWRITER_WRITE(U, bitsize, endian, u) \
BASE_SPANWRITER_WRITE(I, bitsize, endian, )
#define BASE_SPANWRITER_WRITE_BOTH_SIGNS_ALL_SIZES(endian) \
BASE_SPANWRITER_WRITE_BOTH_SIGNS(8, endian) \
BASE_SPANWRITER_WRITE_BOTH_SIGNS(16, endian) \
BASE_SPANWRITER_WRITE_BOTH_SIGNS(32, endian) \
BASE_SPANWRITER_WRITE_BOTH_SIGNS(64, endian)
BASE_SPANWRITER_WRITE_BOTH_SIGNS_ALL_SIZES(Big)
BASE_SPANWRITER_WRITE_BOTH_SIGNS_ALL_SIZES(Little)
BASE_SPANWRITER_WRITE_BOTH_SIGNS_ALL_SIZES(Native)
#undef BASE_SPANWRITER_WRITE_BOTH_SIGNS_ALL_SIZES
#undef BASE_SPANWRITER_WRITE_BOTH_SIGNS
#undef BASE_SPANWRITER_WRITE
// Returns the remaining not-yet-written-to object count.
constexpr size_t remaining() const { return buf_.size(); }
// Returns the remaining not-yet-written-to objects.
constexpr span<T> remaining_span() const { return buf_; }
// Returns the number of objects already written (or skipped).
constexpr size_t num_written() const { return original_size_ - buf_.size(); }
private:
raw_span<T> buf_;
size_t original_size_;
};
template <class T, size_t N>
SpanWriter(span<T, N>) -> SpanWriter<T>;
} // namespace base
#endif // BASE_CONTAINERS_SPAN_WRITER_H_