#pragma once
#include <cstring>
#include "ioBase.hpp"
#include "fwd.hpp"
NAMESPACE_SOUP
{
class Reader : public ioBase<true>
{
public:
using ioBase::ioBase;
[[nodiscard]] virtual bool hasMore() noexcept = 0;
virtual void seek(size_t pos) = 0;
void seekBegin() { seek(0); }
virtual void seekEnd() = 0;
[[nodiscard]] size_t getRemainingBytes()
{
const size_t pos = getPosition();
seekEnd();
const size_t remaining = (getPosition() - pos);
seek(pos);
return remaining;
}
bool skip(size_t len)
{
seek(getPosition() + len);
return true;
}
bool u64_dyn(uint64_t& v) noexcept;
bool i64_dyn(int64_t& v) noexcept;
bool u64_dyn_v2(uint64_t& v) noexcept;
bool i64_dyn_v2(int64_t& v) noexcept;
template <typename Int>
bool om(Int& v) noexcept
{
v = {};
uint8_t byte;
while (u8(byte))
{
v <<= 7;
v |= (byte & 0x7F);
if (!(byte & 0x80))
{
return true;
}
}
return false;
}
template <typename Int>
bool oml(Int& v) noexcept
{
v = {};
uint8_t byte;
uint8_t shift = 0;
while (u8(byte))
{
v |= (static_cast<Int>(byte & 0x7F) << shift);
if (!(byte & 0x80))
{
return true;
}
shift += 7;
}
return false;
}
template <typename Int>
bool soml(Int& v) noexcept
{
v = {};
uint8_t byte;
uint8_t shift = 0;
while (u8(byte))
{
v |= (static_cast<Int>(byte & 0x7F) << shift);
if (!(byte & 0x80))
{
if (shift < (sizeof(Int) * 8) && (byte & 0x40))
{
v |= (~0 << shift);
}
return true;
}
shift += 7;
}
return false;
}
bool mysql_lenenc(uint64_t& v) noexcept
{
uint8_t first;
if (u8(first))
{
if (first < 0xFB)
{
v = first;
return true;
}
if (first == 0xFC)
{
uint16_t val;
if (u16le(val))
{
v = val;
return true;
}
}
if (first == 0xFD)
{
uint32_t val;
if (u24le(val))
{
v = val;
return true;
}
}
if (first == 0xFE)
{
uint64_t val;
if (u64le(val))
{
v = val;
return true;
}
}
}
return false;
}
bool str_nt(std::string& v) SOUP_EXCAL
{
v.clear();
while (true)
{
char c;
SOUP_RETHROW_FALSE(ioBase::c(c));
if (c == 0)
{
break;
}
v.push_back(c);
}
return true;
}
template <typename T>
bool str_lp(std::string& v, const T max_len = -1) SOUP_EXCAL
{
T len;
return ser<T>(len) && len <= max_len && str(static_cast<size_t>(len), v);
}
bool str_lp_u64_dyn(std::string& v) SOUP_EXCAL
{
uint64_t len;
return u64_dyn(len) && str((size_t)len, v);
}
bool str_lp_mysql(std::string& v)
{
uint64_t len;
return mysql_lenenc(len) && str((size_t)len, v);
}
bool str(size_t len, std::string& v) SOUP_EXCAL
{
v = std::string(len, '\0');
return raw(v.data(), len);
}
bool str(size_t len, char* v) SOUP_EXCAL
{
memset(v, 0, len);
return raw(v, len);
}
bool vec_u8_u8(std::vector<uint8_t>& v) SOUP_EXCAL
{
uint8_t len;
SOUP_RETHROW_FALSE(u8(len));
v.clear();
v.reserve(len);
while (len--)
{
uint8_t entry;
SOUP_IF_UNLIKELY (!u8(entry))
{
return false;
}
v.emplace_back(std::move(entry));
}
return true;
}
bool vec_u16be_bl_u16be(std::vector<uint16_t>& v) SOUP_EXCAL
{
uint16_t len;
SOUP_RETHROW_FALSE(ioBase::u16be(len));
v.clear();
v.reserve(len / 2);
for (; len >= sizeof(uint16_t); len -= sizeof(uint16_t))
{
uint16_t entry;
SOUP_IF_UNLIKELY (!ioBase::u16be(entry))
{
return false;
}
v.emplace_back(std::move(entry));
}
return true;
}
bool vec_str_nt_u64_dyn(std::vector<std::string>& v) SOUP_EXCAL
{
uint64_t len;
SOUP_RETHROW_FALSE(u64_dyn(len));
v.clear();
v.reserve((size_t)len);
for (; len != 0; --len)
{
std::string entry;
SOUP_IF_UNLIKELY (!str_nt(entry))
{
return false;
}
v.emplace_back(std::move(entry));
}
return true;
}
bool vec_str_lp_u24be_bl_u24be(std::vector<std::string>& v) SOUP_EXCAL
{
uint32_t len;
SOUP_RETHROW_FALSE(ioBase::u24be(len));
v.clear();
v.reserve(len / 3);
while (len >= 3)
{
std::string entry;
SOUP_IF_UNLIKELY (!str_lp<u24be_t>(entry))
{
return false;
}
len -= ((uint32_t)entry.size() + 3);
v.emplace_back(std::move(entry));
}
return true;
}
bool vec_nt_str_lp_u8(std::vector<std::string>& v) SOUP_EXCAL
{
v.clear();
while (true)
{
std::string entry;
SOUP_RETHROW_FALSE(str_lp<u8_t>(entry));
if (entry.empty())
{
break;
}
v.emplace_back(std::move(entry));
}
return true;
}
virtual bool getLine(std::string& line) SOUP_EXCAL
{
line.clear();
char c;
while (ioBase::c(c))
{
if (c == '\n')
{
return true;
}
line.push_back(c);
}
return !line.empty();
}
};
}