#ifndef BASE_CONTAINERS_SPAN_READER_H_
#define BASE_CONTAINERS_SPAN_READER_H_
#include <concepts>
#include <optional>
#include "base/containers/span.h"
#include "base/memory/stack_allocated.h"
#include "base/numerics/byte_conversions.h"
#include "base/numerics/safe_conversions.h"
namespace base {
template <class T>
class SpanReader {
STACK_ALLOCATED();
public:
explicit SpanReader(span<T> buf) : buf_(buf), original_size_(buf_.size()) {}
std::optional<span<T>> Read(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>
std::optional<span<T, N>> Read() {
if (N > remaining()) {
return std::nullopt;
}
auto [lhs, rhs] = buf_.template split_at<N>();
buf_ = rhs;
return lhs;
}
bool ReadInto(StrictNumeric<size_t> n, span<T>& out) {
if (n > remaining()) {
return false;
}
auto [lhs, rhs] = buf_.split_at(n);
out = lhs;
buf_ = rhs;
return true;
}
bool ReadCopy(span<std::remove_const_t<T>> out) {
if (out.size() > remaining()) {
return false;
}
auto [lhs, rhs] = buf_.split_at(out.size());
out.copy_from(lhs);
buf_ = rhs;
return true;
}
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;
}
bool ReadU8BigEndian(uint8_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = U8FromBigEndian(buf); });
}
bool ReadU16BigEndian(uint16_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<2>([&](auto buf) { value = U16FromBigEndian(buf); });
}
bool ReadU32BigEndian(uint32_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<4>([&](auto buf) { value = U32FromBigEndian(buf); });
}
bool ReadU64BigEndian(uint64_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<8>([&](auto buf) { value = U64FromBigEndian(buf); });
}
bool ReadU8LittleEndian(uint8_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = U8FromLittleEndian(buf); });
}
bool ReadU16LittleEndian(uint16_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<2>([&](auto buf) { value = U16FromLittleEndian(buf); });
}
bool ReadU32LittleEndian(uint32_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<4>([&](auto buf) { value = U32FromLittleEndian(buf); });
}
bool ReadU64LittleEndian(uint64_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<8>([&](auto buf) { value = U64FromLittleEndian(buf); });
}
bool ReadU8NativeEndian(uint8_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = U8FromNativeEndian(buf); });
}
bool ReadU16NativeEndian(uint16_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<2>([&](auto buf) { value = U16FromNativeEndian(buf); });
}
bool ReadU32NativeEndian(uint32_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<4>([&](auto buf) { value = U32FromNativeEndian(buf); });
}
bool ReadU64NativeEndian(uint64_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<8>([&](auto buf) { value = U64FromNativeEndian(buf); });
}
bool ReadI8BigEndian(int8_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = I8FromBigEndian(buf); });
}
bool ReadI16BigEndian(int16_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<2>([&](auto buf) { value = I16FromBigEndian(buf); });
}
bool ReadI32BigEndian(int32_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<4>([&](auto buf) { value = I32FromBigEndian(buf); });
}
bool ReadI64BigEndian(int64_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<8>([&](auto buf) { value = I64FromBigEndian(buf); });
}
bool ReadI8LittleEndian(int8_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = I8FromLittleEndian(buf); });
}
bool ReadI16LittleEndian(int16_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<2>([&](auto buf) { value = I16FromLittleEndian(buf); });
}
bool ReadI32LittleEndian(int32_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<4>([&](auto buf) { value = I32FromLittleEndian(buf); });
}
bool ReadI64LittleEndian(int64_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<8>([&](auto buf) { value = I64FromLittleEndian(buf); });
}
bool ReadI8NativeEndian(int8_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = I8FromNativeEndian(buf); });
}
bool ReadI16NativeEndian(int16_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<2>([&](auto buf) { value = I16FromNativeEndian(buf); });
}
bool ReadI32NativeEndian(int32_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<4>([&](auto buf) { value = I32FromNativeEndian(buf); });
}
bool ReadI64NativeEndian(int64_t& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<8>([&](auto buf) { value = I64FromNativeEndian(buf); });
}
bool ReadChar(char& value)
requires(std::same_as<std::remove_const_t<T>, uint8_t>)
{
return ReadAnd<1>([&](auto buf) { value = static_cast<char>(buf[0u]); });
}
size_t remaining() const { return buf_.size(); }
span<T> remaining_span() const { return buf_; }
size_t num_read() const { return original_size_ - buf_.size(); }
private:
template <size_t N, class F>
requires(std::invocable<F, span<T, N>>)
bool ReadAnd(F f) {
auto buf = Read<N>();
if (buf.has_value()) {
f(*buf);
}
return buf.has_value();
}
span<T> buf_;
size_t original_size_;
};
template <typename ElementType, size_t Extent, typename InternalPtrType>
SpanReader(span<ElementType, Extent, InternalPtrType>)
-> SpanReader<ElementType>;
}
#endif