Path: blob/master/src/common/binary_reader_writer.h
4223 views
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#include "types.h"45#include <cstdio>6#include <optional>7#include <span>8#include <string>9#include <string_view>1011class Error;12class SmallStringBase;1314class BinarySpanReader15{16public:17BinarySpanReader();18explicit BinarySpanReader(std::span<const u8> buf);1920BinarySpanReader(const BinarySpanReader&) = delete;21BinarySpanReader& operator=(const BinarySpanReader&) = delete;2223BinarySpanReader(BinarySpanReader&& move);24BinarySpanReader& operator=(BinarySpanReader&& move);2526ALWAYS_INLINE const std::span<const u8>& GetSpan() const { return m_buf; }27ALWAYS_INLINE bool IsValid() const { return !m_buf.empty(); }28ALWAYS_INLINE bool CheckRemaining(size_t size) { return ((m_pos + size) <= m_buf.size()); }29ALWAYS_INLINE size_t GetBufferRemaining() const { return (m_buf.size() - m_pos); }30ALWAYS_INLINE size_t GetBufferConsumed() const { return m_pos; }3132std::span<const u8> GetRemainingSpan() const;33std::span<const u8> GetRemainingSpan(size_t size) const;34void IncrementPosition(size_t size);3536// clang-format off37template<typename T> ALWAYS_INLINE bool ReadT(T* dst) { return Read(dst, sizeof(T)); }38ALWAYS_INLINE bool ReadBool(bool* dst) { u8 val; if (!Read(&val, sizeof(val))) [[unlikely]] { return false; } *dst = (val != 0); return true; }39ALWAYS_INLINE bool ReadS8(s8* dst) { return ReadT(dst); }40ALWAYS_INLINE bool ReadU8(u8* dst) { return ReadT(dst); }41ALWAYS_INLINE bool ReadS16(s16* dst) { return ReadT(dst); }42ALWAYS_INLINE bool ReadU16(u16* dst) { return ReadT(dst); }43ALWAYS_INLINE bool ReadS32(s32* dst) { return ReadT(dst); }44ALWAYS_INLINE bool ReadU32(u32* dst) { return ReadT(dst); }45ALWAYS_INLINE bool ReadS64(s64* dst) { return ReadT(dst); }46ALWAYS_INLINE bool ReadU64(u64* dst) { return ReadT(dst); }47ALWAYS_INLINE bool ReadFloat(float* dst) { return ReadT(dst); }48bool ReadCString(std::string* dst);49bool ReadCString(std::string_view* dst);50bool ReadCString(SmallStringBase* dst);51bool ReadSizePrefixedString(std::string* dst);52bool ReadSizePrefixedString(std::string_view* dst);53bool ReadSizePrefixedString(SmallStringBase* dst);5455template<typename T> ALWAYS_INLINE T ReadT() { T ret; if (!Read(&ret, sizeof(ret))) [[unlikely]] { ret = {}; } return ret; }56ALWAYS_INLINE bool ReadBool() { return (ReadT<u8>() != 0); }57ALWAYS_INLINE s8 ReadS8() { return ReadT<s8>(); }58ALWAYS_INLINE u8 ReadU8() { return ReadT<u8>(); }59ALWAYS_INLINE s16 ReadS16() { return ReadT<s16>(); }60ALWAYS_INLINE u16 ReadU16() { return ReadT<u16>(); }61ALWAYS_INLINE s32 ReadS32() { return ReadT<s32>(); }62ALWAYS_INLINE u32 ReadU32() { return ReadT<u32>(); }63ALWAYS_INLINE s64 ReadS64() { return ReadT<s64>(); }64ALWAYS_INLINE u64 ReadU64() { return ReadT<u64>(); }65ALWAYS_INLINE float ReadFloat() { return ReadT<float>(); }66std::string_view ReadCString();67std::string_view ReadSizePrefixedString();6869template<typename T> ALWAYS_INLINE bool PeekT(T* dst) { return Peek(dst, sizeof(T)); }70ALWAYS_INLINE bool PeekBool(bool* dst) { u8 val; if (!Peek(&val, sizeof(val))) [[unlikely]] { return false; } *dst = (val != 0); return true; }71ALWAYS_INLINE bool PeekU8(u8* dst) { return PeekT(dst); }72ALWAYS_INLINE bool PeekU16(u16* dst) { return PeekT(dst); }73ALWAYS_INLINE bool PeekU32(u32* dst) { return PeekT(dst); }74ALWAYS_INLINE bool PeekU64(u64* dst) { return PeekT(dst); }75ALWAYS_INLINE bool PeekFloat(float* dst) { return PeekT(dst); }76bool PeekCString(std::string* dst);77bool PeekCString(std::string_view* dst);78bool PeekCString(SmallStringBase* dst);79bool PeekSizePrefixedString(std::string* dst);80bool PeekSizePrefixedString(std::string_view* dst);81bool PeekSizePrefixedString(SmallStringBase* dst);8283ALWAYS_INLINE BinarySpanReader& operator>>(s8& val) { val = ReadT<s8>(); return *this; }84ALWAYS_INLINE BinarySpanReader& operator>>(u8& val) { val = ReadT<u8>(); return *this; }85ALWAYS_INLINE BinarySpanReader& operator>>(s16& val) { val = ReadT<s16>(); return *this; }86ALWAYS_INLINE BinarySpanReader& operator>>(u16& val) { val = ReadT<u16>(); return *this; }87ALWAYS_INLINE BinarySpanReader& operator>>(s32& val) { val = ReadT<s32>(); return *this; }88ALWAYS_INLINE BinarySpanReader& operator>>(u32& val) { val = ReadT<u32>(); return *this; }89ALWAYS_INLINE BinarySpanReader& operator>>(s64& val) { val = ReadT<s64>(); return *this; }90ALWAYS_INLINE BinarySpanReader& operator>>(u64& val) { val = ReadT<u64>(); return *this; }91ALWAYS_INLINE BinarySpanReader& operator>>(float& val) { val = ReadT<float>(); return *this; }92ALWAYS_INLINE BinarySpanReader& operator>>(std::string_view& val) { val = ReadCString(); return *this; }93// clang-format on9495template<typename T>96ALWAYS_INLINE bool ReadOptionalT(std::optional<T>* dst)97{98u8 has_value;99if (!ReadT(&has_value)) [[unlikely]]100return false;101102if (has_value == 0)103{104dst->reset();105return true;106}107108T value;109if (!ReadT(&value)) [[unlikely]]110return false;111112*dst = value;113return true;114}115116ALWAYS_INLINE bool Read(void* buf, size_t size)117{118if ((m_pos + size) <= m_buf.size()) [[likely]]119{120std::memcpy(buf, &m_buf[m_pos], size);121m_pos += size;122return true;123}124125return false;126}127128ALWAYS_INLINE bool Peek(void* buf, size_t size)129{130if ((m_pos + size) <= m_buf.size()) [[likely]]131{132std::memcpy(buf, &m_buf[m_pos], size);133return true;134}135136return false;137}138139private:140std::span<const u8> m_buf;141size_t m_pos = 0;142};143144class BinarySpanWriter145{146public:147BinarySpanWriter();148explicit BinarySpanWriter(std::span<u8> buf);149150BinarySpanWriter(const BinarySpanWriter&) = delete;151BinarySpanWriter& operator=(const BinarySpanWriter&) = delete;152153BinarySpanWriter(BinarySpanWriter&& move);154BinarySpanWriter& operator=(BinarySpanWriter&& move);155156ALWAYS_INLINE const std::span<u8>& GetSpan() const { return m_buf; }157ALWAYS_INLINE bool IsValid() const { return !m_buf.empty(); }158ALWAYS_INLINE size_t GetBufferRemaining() const { return (m_buf.size() - m_pos); }159ALWAYS_INLINE size_t GetBufferWritten() const { return m_pos; }160161std::span<u8> GetRemainingSpan() const;162std::span<u8> GetRemainingSpan(size_t size) const;163void IncrementPosition(size_t size);164165// clang-format off166template<typename T> ALWAYS_INLINE bool WriteT(T dst) { return Write(&dst, sizeof(T)); }167ALWAYS_INLINE bool WriteBool(bool val) { const bool bval = static_cast<u8>(val); return Write(&bval, sizeof(bval)); }168ALWAYS_INLINE bool WriteS8(s8 val) { return WriteT(val); }169ALWAYS_INLINE bool WriteU8(u8 val) { return WriteT(val); }170ALWAYS_INLINE bool WriteS16(s16 val) { return WriteT(val); }171ALWAYS_INLINE bool WriteU16(u16 val) { return WriteT(val); }172ALWAYS_INLINE bool WriteS32(s32 val) { return WriteT(val); }173ALWAYS_INLINE bool WriteU32(u32 val) { return WriteT(val); }174ALWAYS_INLINE bool WriteS64(s64 val) { return WriteT(val); }175ALWAYS_INLINE bool WriteU64(u64 val) { return WriteT(val); }176ALWAYS_INLINE bool WriteFloat(float val) { return WriteT(val); }177bool WriteCString(std::string_view val);178bool WriteSizePrefixedString(std::string_view val);179180ALWAYS_INLINE BinarySpanWriter& operator<<(s8 val) { WriteS8(val); return *this; }181ALWAYS_INLINE BinarySpanWriter& operator<<(u8 val) { WriteU8(val); return *this; }182ALWAYS_INLINE BinarySpanWriter& operator<<(s16 val) { WriteS16(val); return *this; }183ALWAYS_INLINE BinarySpanWriter& operator<<(u16 val) { WriteU16(val); return *this; }184ALWAYS_INLINE BinarySpanWriter& operator<<(s32 val) { WriteS32(val); return *this; }185ALWAYS_INLINE BinarySpanWriter& operator<<(u32 val) { WriteU32(val); return *this; }186ALWAYS_INLINE BinarySpanWriter& operator<<(s64 val) { WriteS64(val); return *this; }187ALWAYS_INLINE BinarySpanWriter& operator<<(u64 val) { WriteU64(val); return *this; }188ALWAYS_INLINE BinarySpanWriter& operator<<(float val) { WriteFloat(val); return *this; }189ALWAYS_INLINE BinarySpanWriter& operator<<(std::string_view val) { WriteCString(val); return *this; }190// clang-format on191192template<typename T>193ALWAYS_INLINE bool WriteOptionalT(const std::optional<T>& val)194{195return (WriteBool(val.has_value()) && (!val.has_value() || WriteT(val.value())));196}197198ALWAYS_INLINE bool Write(const void* buf, size_t size)199{200if ((m_pos + size) <= m_buf.size()) [[likely]]201{202std::memcpy(&m_buf[m_pos], buf, size);203m_pos += size;204return true;205}206207return false;208}209210private:211std::span<u8> m_buf;212size_t m_pos = 0;213};214215class BinaryFileReader216{217public:218BinaryFileReader();219explicit BinaryFileReader(std::FILE* fp);220221BinaryFileReader(const BinaryFileReader&) = delete;222BinaryFileReader& operator=(const BinaryFileReader&) = delete;223224BinaryFileReader(BinaryFileReader&& move);225BinaryFileReader& operator=(BinaryFileReader&& move);226227ALWAYS_INLINE const std::FILE* GetFile() const { return m_fp; }228ALWAYS_INLINE bool HasError() const { return !m_good; }229ALWAYS_INLINE bool IsGood() const { return m_good; }230ALWAYS_INLINE bool IsOpen() const { return (m_fp != nullptr); }231232bool IsAtEnd();233234// clang-format off235template<typename T> ALWAYS_INLINE bool ReadT(T* dst) { return Read(dst, sizeof(T)); }236ALWAYS_INLINE bool ReadBool(bool* dst) { u8 val; if (!Read(&val, sizeof(val))) [[unlikely]] { return false; } *dst = (val != 0); return true; }237ALWAYS_INLINE bool ReadS8(s8* dst) { return ReadT(dst); }238ALWAYS_INLINE bool ReadU8(u8* dst) { return ReadT(dst); }239ALWAYS_INLINE bool ReadS16(s16* dst) { return ReadT(dst); }240ALWAYS_INLINE bool ReadU16(u16* dst) { return ReadT(dst); }241ALWAYS_INLINE bool ReadS32(s32* dst) { return ReadT(dst); }242ALWAYS_INLINE bool ReadU32(u32* dst) { return ReadT(dst); }243ALWAYS_INLINE bool ReadS64(s64* dst) { return ReadT(dst); }244ALWAYS_INLINE bool ReadU64(u64* dst) { return ReadT(dst); }245ALWAYS_INLINE bool ReadFloat(float* dst) { return ReadT(dst); }246bool ReadCString(std::string* dst);247bool ReadCString(SmallStringBase* dst);248bool ReadSizePrefixedString(std::string* dst);249bool ReadSizePrefixedString(SmallStringBase* dst);250251template<typename T> ALWAYS_INLINE T ReadT() { T ret; if (!Read(&ret, sizeof(ret))) [[unlikely]] { ret = {}; } return ret; }252ALWAYS_INLINE bool ReadBool() { return (ReadT<u8>() != 0); }253ALWAYS_INLINE s8 ReadS8() { return ReadT<s8>(); }254ALWAYS_INLINE u8 ReadU8() { return ReadT<u8>(); }255ALWAYS_INLINE s16 ReadS16() { return ReadT<s16>(); }256ALWAYS_INLINE u16 ReadU16() { return ReadT<u16>(); }257ALWAYS_INLINE s32 ReadS32() { return ReadT<s32>(); }258ALWAYS_INLINE u32 ReadU32() { return ReadT<u32>(); }259ALWAYS_INLINE s64 ReadS64() { return ReadT<s64>(); }260ALWAYS_INLINE u64 ReadU64() { return ReadT<u64>(); }261ALWAYS_INLINE float ReadFloat() { return ReadT<float>(); }262std::string ReadCString();263std::string ReadSizePrefixedString();264265ALWAYS_INLINE BinaryFileReader& operator>>(s8& val) { val = ReadT<s8>(); return *this; }266ALWAYS_INLINE BinaryFileReader& operator>>(u8& val) { val = ReadT<u8>(); return *this; }267ALWAYS_INLINE BinaryFileReader& operator>>(s16& val) { val = ReadT<s16>(); return *this; }268ALWAYS_INLINE BinaryFileReader& operator>>(u16& val) { val = ReadT<u16>(); return *this; }269ALWAYS_INLINE BinaryFileReader& operator>>(s32& val) { val = ReadT<s32>(); return *this; }270ALWAYS_INLINE BinaryFileReader& operator>>(u32& val) { val = ReadT<u32>(); return *this; }271ALWAYS_INLINE BinaryFileReader& operator>>(s64& val) { val = ReadT<s64>(); return *this; }272ALWAYS_INLINE BinaryFileReader& operator>>(u64& val) { val = ReadT<u64>(); return *this; }273ALWAYS_INLINE BinaryFileReader& operator>>(float& val) { val = ReadT<float>(); return *this; }274ALWAYS_INLINE BinaryFileReader& operator>>(std::string& val) { val = ReadCString(); return *this; }275// clang-format on276277template<typename T>278ALWAYS_INLINE bool ReadOptionalT(std::optional<T>* dst)279{280u8 has_value;281if (!ReadT(&has_value)) [[unlikely]]282return false;283284if (has_value == 0)285{286dst->reset();287return true;288}289290T value;291if (!ReadT(&value)) [[unlikely]]292return false;293294*dst = value;295return true;296}297298ALWAYS_INLINE bool Read(void* buf, size_t size) { return (m_good = (m_good && std::fread(buf, size, 1, m_fp) == 1)); }299300private:301std::FILE* m_fp;302s64 m_size;303bool m_good = true;304};305306class BinaryFileWriter307{308public:309BinaryFileWriter();310explicit BinaryFileWriter(std::FILE* fp);311312BinaryFileWriter(const BinaryFileWriter&) = delete;313BinaryFileWriter& operator=(const BinaryFileWriter&) = delete;314315BinaryFileWriter(BinaryFileWriter&& move);316BinaryFileWriter& operator=(BinaryFileWriter&& move);317318ALWAYS_INLINE const std::FILE* GetFile() const { return m_fp; }319ALWAYS_INLINE bool HasError() const { return !m_good; }320ALWAYS_INLINE bool IsGood() const { return m_good; }321ALWAYS_INLINE bool IsOpen() const { return (m_fp != nullptr); }322323// clang-format off324template<typename T> ALWAYS_INLINE bool WriteT(T dst) { return Write(&dst, sizeof(T)); }325ALWAYS_INLINE bool WriteBool(bool val) { const bool bval = static_cast<u8>(val); return Write(&bval, sizeof(bval)); }326ALWAYS_INLINE bool WriteS8(s8 val) { return WriteT(val); }327ALWAYS_INLINE bool WriteU8(u8 val) { return WriteT(val); }328ALWAYS_INLINE bool WriteS16(s16 val) { return WriteT(val); }329ALWAYS_INLINE bool WriteU16(u16 val) { return WriteT(val); }330ALWAYS_INLINE bool WriteS32(s32 val) { return WriteT(val); }331ALWAYS_INLINE bool WriteU32(u32 val) { return WriteT(val); }332ALWAYS_INLINE bool WriteS64(s64 val) { return WriteT(val); }333ALWAYS_INLINE bool WriteU64(u64 val) { return WriteT(val); }334ALWAYS_INLINE bool WriteFloat(float val) { return WriteT(val); }335bool WriteCString(std::string_view val);336bool WriteSizePrefixedString(std::string_view val);337338ALWAYS_INLINE BinaryFileWriter& operator<<(s8 val) { WriteS8(val); return *this; }339ALWAYS_INLINE BinaryFileWriter& operator<<(u8 val) { WriteU8(val); return *this; }340ALWAYS_INLINE BinaryFileWriter& operator<<(s16 val) { WriteS16(val); return *this; }341ALWAYS_INLINE BinaryFileWriter& operator<<(u16 val) { WriteU16(val); return *this; }342ALWAYS_INLINE BinaryFileWriter& operator<<(s32 val) { WriteS32(val); return *this; }343ALWAYS_INLINE BinaryFileWriter& operator<<(u32 val) { WriteU32(val); return *this; }344ALWAYS_INLINE BinaryFileWriter& operator<<(s64 val) { WriteS64(val); return *this; }345ALWAYS_INLINE BinaryFileWriter& operator<<(u64 val) { WriteU64(val); return *this; }346ALWAYS_INLINE BinaryFileWriter& operator<<(float val) { WriteFloat(val); return *this; }347ALWAYS_INLINE BinaryFileWriter& operator<<(std::string_view val) { WriteCString(val); return *this; }348// clang-format on349350template<typename T>351ALWAYS_INLINE bool WriteOptionalT(const std::optional<T>& val)352{353return (WriteBool(val.has_value()) && (!val.has_value() || WriteT(val.value())));354}355356ALWAYS_INLINE bool Write(const void* buf, size_t size)357{358return (m_good = (m_good && std::fwrite(buf, size, 1, m_fp) == 1));359}360361bool Flush(Error* error = nullptr);362363private:364std::FILE* m_fp;365bool m_good;366};367368369