duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

binary_reader_writer.h (15270B)


      1 // SPDX-FileCopyrightText: 2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "types.h"
      5 
      6 #include <cstdio>
      7 #include <optional>
      8 #include <span>
      9 #include <string>
     10 #include <string_view>
     11 
     12 class Error;
     13 class SmallStringBase;
     14 
     15 class BinarySpanReader
     16 {
     17 public:
     18   BinarySpanReader();
     19   BinarySpanReader(std::span<const u8> buf);
     20 
     21   BinarySpanReader(const BinarySpanReader&) = delete;
     22   BinarySpanReader& operator=(const BinarySpanReader&) = delete;
     23 
     24   BinarySpanReader(BinarySpanReader&& move);
     25   BinarySpanReader& operator=(BinarySpanReader&& move);
     26 
     27   ALWAYS_INLINE const std::span<const u8>& GetSpan() const { return m_buf; }
     28   ALWAYS_INLINE bool IsValid() const { return !m_buf.empty(); }
     29   ALWAYS_INLINE bool CheckRemaining(size_t size) { return ((m_pos + size) <= m_buf.size()); }
     30   ALWAYS_INLINE size_t GetBufferRemaining() const { return (m_buf.size() - m_pos); }
     31   ALWAYS_INLINE size_t GetBufferConsumed() const { return m_pos; }
     32 
     33   std::span<const u8> GetRemainingSpan() const;
     34   std::span<const u8> GetRemainingSpan(size_t size) const;
     35   void IncrementPosition(size_t size);
     36 
     37   // clang-format off
     38   template<typename T> ALWAYS_INLINE bool ReadT(T* dst) { return Read(dst, sizeof(T)); }
     39   ALWAYS_INLINE bool ReadBool(bool* dst) { u8 val; if (!Read(&val, sizeof(val))) [[unlikely]] { return false; } *dst = (val != 0); return true; }
     40   ALWAYS_INLINE bool ReadS8(s8* dst) { return ReadT(dst); }
     41   ALWAYS_INLINE bool ReadU8(u8* dst) { return ReadT(dst); }
     42   ALWAYS_INLINE bool ReadS16(s16* dst) { return ReadT(dst); }
     43   ALWAYS_INLINE bool ReadU16(u16* dst) { return ReadT(dst); }
     44   ALWAYS_INLINE bool ReadS32(s32* dst) { return ReadT(dst); }
     45   ALWAYS_INLINE bool ReadU32(u32* dst) { return ReadT(dst); }
     46   ALWAYS_INLINE bool ReadS64(s64* dst) { return ReadT(dst); }
     47   ALWAYS_INLINE bool ReadU64(u64* dst) { return ReadT(dst); }
     48   ALWAYS_INLINE bool ReadFloat(float* dst) { return ReadT(dst); }
     49   bool ReadCString(std::string* dst);
     50   bool ReadCString(std::string_view* dst);
     51   bool ReadCString(SmallStringBase* dst);
     52   bool ReadSizePrefixedString(std::string* dst);
     53   bool ReadSizePrefixedString(std::string_view* dst);
     54   bool ReadSizePrefixedString(SmallStringBase* dst);
     55 
     56   template<typename T> ALWAYS_INLINE T ReadT() { T ret; if (!Read(&ret, sizeof(ret))) [[unlikely]] { ret = {}; } return ret; }
     57   ALWAYS_INLINE bool ReadBool() { return (ReadT<u8>() != 0); }
     58   ALWAYS_INLINE s8 ReadS8() { return ReadT<s8>(); }
     59   ALWAYS_INLINE u8 ReadU8() { return ReadT<u8>(); }
     60   ALWAYS_INLINE s16 ReadS16() { return ReadT<s16>(); }
     61   ALWAYS_INLINE u16 ReadU16() { return ReadT<u16>(); }
     62   ALWAYS_INLINE s32 ReadS32() { return ReadT<s32>(); }
     63   ALWAYS_INLINE u32 ReadU32() { return ReadT<u32>(); }
     64   ALWAYS_INLINE s64 ReadS64() { return ReadT<s64>(); }
     65   ALWAYS_INLINE u64 ReadU64() { return ReadT<u64>(); }
     66   ALWAYS_INLINE float ReadFloat() { return ReadT<float>(); }
     67   std::string_view ReadCString();
     68   std::string_view ReadSizePrefixedString();
     69 
     70   template<typename T> ALWAYS_INLINE bool PeekT(T* dst) { return Peek(dst, sizeof(T)); }
     71   ALWAYS_INLINE bool PeekBool(bool* dst) { u8 val; if (!Peek(&val, sizeof(val))) [[unlikely]] { return false; } *dst = (val != 0); return true; }
     72   ALWAYS_INLINE bool PeekU8(u8* dst) { return PeekT(dst); }
     73   ALWAYS_INLINE bool PeekU16(u16* dst) { return PeekT(dst); }
     74   ALWAYS_INLINE bool PeekU32(u32* dst) { return PeekT(dst); }
     75   ALWAYS_INLINE bool PeekU64(u64* dst) { return PeekT(dst); }
     76   ALWAYS_INLINE bool PeekFloat(float* dst) { return PeekT(dst); }
     77   bool PeekCString(std::string* dst);
     78   bool PeekCString(std::string_view* dst);
     79   bool PeekCString(SmallStringBase* dst);
     80   bool PeekSizePrefixedString(std::string* dst);
     81   bool PeekSizePrefixedString(std::string_view* dst);
     82   bool PeekSizePrefixedString(SmallStringBase* dst);
     83 
     84   ALWAYS_INLINE BinarySpanReader& operator>>(s8& val) { val = ReadT<s8>(); return *this; }
     85   ALWAYS_INLINE BinarySpanReader& operator>>(u8& val) { val = ReadT<u8>(); return *this; }
     86   ALWAYS_INLINE BinarySpanReader& operator>>(s16& val) { val = ReadT<s16>(); return *this; }
     87   ALWAYS_INLINE BinarySpanReader& operator>>(u16& val) { val = ReadT<u16>(); return *this; }
     88   ALWAYS_INLINE BinarySpanReader& operator>>(s32& val) { val = ReadT<s32>(); return *this; }
     89   ALWAYS_INLINE BinarySpanReader& operator>>(u32& val) { val = ReadT<u32>(); return *this; }
     90   ALWAYS_INLINE BinarySpanReader& operator>>(s64& val) { val = ReadT<s64>(); return *this; }
     91   ALWAYS_INLINE BinarySpanReader& operator>>(u64& val) { val = ReadT<u64>(); return *this; }
     92   ALWAYS_INLINE BinarySpanReader& operator>>(float& val) { val = ReadT<float>(); return *this; }
     93   ALWAYS_INLINE BinarySpanReader& operator>>(std::string_view& val) { val = ReadCString(); return *this; }
     94   // clang-format on
     95 
     96   template<typename T>
     97   ALWAYS_INLINE bool ReadOptionalT(std::optional<T>* dst)
     98   {
     99     u8 has_value;
    100     if (!ReadT(&has_value)) [[unlikely]]
    101       return false;
    102 
    103     if (has_value == 0)
    104     {
    105       dst->reset();
    106       return true;
    107     }
    108 
    109     T value;
    110     if (!ReadT(&value)) [[unlikely]]
    111       return false;
    112 
    113     *dst = value;
    114     return true;
    115   }
    116 
    117   ALWAYS_INLINE bool Read(void* buf, size_t size)
    118   {
    119     if ((m_pos + size) <= m_buf.size()) [[likely]]
    120     {
    121       std::memcpy(buf, &m_buf[m_pos], size);
    122       m_pos += size;
    123       return true;
    124     }
    125 
    126     return false;
    127   }
    128 
    129   ALWAYS_INLINE bool Peek(void* buf, size_t size)
    130   {
    131     if ((m_pos + size) <= m_buf.size()) [[likely]]
    132     {
    133       std::memcpy(buf, &m_buf[m_pos], size);
    134       return true;
    135     }
    136 
    137     return false;
    138   }
    139 
    140 private:
    141   std::span<const u8> m_buf;
    142   size_t m_pos = 0;
    143 };
    144 
    145 class BinarySpanWriter
    146 {
    147 public:
    148   BinarySpanWriter();
    149   BinarySpanWriter(std::span<u8> buf);
    150 
    151   BinarySpanWriter(const BinarySpanWriter&) = delete;
    152   BinarySpanWriter& operator=(const BinarySpanWriter&) = delete;
    153 
    154   BinarySpanWriter(BinarySpanWriter&& move);
    155   BinarySpanWriter& operator=(BinarySpanWriter&& move);
    156 
    157   ALWAYS_INLINE const std::span<u8>& GetSpan() const { return m_buf; }
    158   ALWAYS_INLINE bool IsValid() const { return !m_buf.empty(); }
    159   ALWAYS_INLINE size_t GetBufferRemaining() const { return (m_buf.size() - m_pos); }
    160   ALWAYS_INLINE size_t GetBufferWritten() const { return m_pos; }
    161 
    162   std::span<u8> GetRemainingSpan() const;
    163   std::span<u8> GetRemainingSpan(size_t size) const;
    164   void IncrementPosition(size_t size);
    165 
    166   // clang-format off
    167   template<typename T> ALWAYS_INLINE bool WriteT(T dst) { return Write(&dst, sizeof(T)); }
    168   ALWAYS_INLINE bool WriteBool(bool val) { const bool bval = static_cast<u8>(val); return Write(&bval, sizeof(bval)); }
    169   ALWAYS_INLINE bool WriteS8(s8 val) { return WriteT(val); }
    170   ALWAYS_INLINE bool WriteU8(u8 val) { return WriteT(val); }
    171   ALWAYS_INLINE bool WriteS16(s16 val) { return WriteT(val); }
    172   ALWAYS_INLINE bool WriteU16(u16 val) { return WriteT(val); }
    173   ALWAYS_INLINE bool WriteS32(s32 val) { return WriteT(val); }
    174   ALWAYS_INLINE bool WriteU32(u32 val) { return WriteT(val); }
    175   ALWAYS_INLINE bool WriteS64(s64 val) { return WriteT(val); }
    176   ALWAYS_INLINE bool WriteU64(u64 val) { return WriteT(val); }
    177   ALWAYS_INLINE bool WriteFloat(float val) { return WriteT(val); }
    178   bool WriteCString(std::string_view val);
    179   bool WriteSizePrefixedString(std::string_view val);
    180 
    181   ALWAYS_INLINE BinarySpanWriter& operator<<(s8 val) { WriteS8(val); return *this; }
    182   ALWAYS_INLINE BinarySpanWriter& operator<<(u8 val) { WriteU8(val); return *this; }
    183   ALWAYS_INLINE BinarySpanWriter& operator<<(s16 val) { WriteS16(val); return *this; }
    184   ALWAYS_INLINE BinarySpanWriter& operator<<(u16 val) { WriteU16(val); return *this; }
    185   ALWAYS_INLINE BinarySpanWriter& operator<<(s32 val) { WriteS32(val); return *this; }
    186   ALWAYS_INLINE BinarySpanWriter& operator<<(u32 val) { WriteU32(val); return *this; }
    187   ALWAYS_INLINE BinarySpanWriter& operator<<(s64 val) { WriteS64(val); return *this; }
    188   ALWAYS_INLINE BinarySpanWriter& operator<<(u64 val) { WriteU64(val); return *this; }
    189   ALWAYS_INLINE BinarySpanWriter& operator<<(float val) { WriteFloat(val); return *this; }
    190   ALWAYS_INLINE BinarySpanWriter& operator<<(std::string_view val) { WriteCString(val); return *this; }
    191   // clang-format on
    192 
    193   template<typename T>
    194   ALWAYS_INLINE bool WriteOptionalT(const std::optional<T>& val)
    195   {
    196     return (WriteBool(val.has_value()) && (!val.has_value() || WriteT(val.value())));
    197   }
    198 
    199   ALWAYS_INLINE bool Write(const void* buf, size_t size)
    200   {
    201     if ((m_pos + size) <= m_buf.size()) [[likely]]
    202     {
    203       std::memcpy(&m_buf[m_pos], buf, size);
    204       m_pos += size;
    205       return true;
    206     }
    207 
    208     return false;
    209   }
    210 
    211 private:
    212   std::span<u8> m_buf;
    213   size_t m_pos = 0;
    214 };
    215 
    216 class BinaryFileReader
    217 {
    218 public:
    219   BinaryFileReader();
    220   BinaryFileReader(std::FILE* fp);
    221   
    222   BinaryFileReader(const BinaryFileReader&) = delete;
    223   BinaryFileReader& operator=(const BinaryFileReader&) = delete;
    224 
    225   BinaryFileReader(BinaryFileReader&& move);
    226   BinaryFileReader& operator=(BinaryFileReader&& move);
    227 
    228   ALWAYS_INLINE const std::FILE* GetFile() const { return m_fp; }
    229   ALWAYS_INLINE bool HasError() const { return !m_good; }
    230   ALWAYS_INLINE bool IsGood() const { return m_good; }
    231   ALWAYS_INLINE bool IsOpen() const { return (m_fp != nullptr); }
    232 
    233   bool IsAtEnd();
    234 
    235   // clang-format off
    236   template<typename T> ALWAYS_INLINE bool ReadT(T* dst) { return Read(dst, sizeof(T)); }
    237   ALWAYS_INLINE bool ReadBool(bool* dst) { u8 val; if (!Read(&val, sizeof(val))) [[unlikely]] { return false; } *dst = (val != 0); return true; }
    238   ALWAYS_INLINE bool ReadS8(s8* dst) { return ReadT(dst); }
    239   ALWAYS_INLINE bool ReadU8(u8* dst) { return ReadT(dst); }
    240   ALWAYS_INLINE bool ReadS16(s16* dst) { return ReadT(dst); }
    241   ALWAYS_INLINE bool ReadU16(u16* dst) { return ReadT(dst); }
    242   ALWAYS_INLINE bool ReadS32(s32* dst) { return ReadT(dst); }
    243   ALWAYS_INLINE bool ReadU32(u32* dst) { return ReadT(dst); }
    244   ALWAYS_INLINE bool ReadS64(s64* dst) { return ReadT(dst); }
    245   ALWAYS_INLINE bool ReadU64(u64* dst) { return ReadT(dst); }
    246   ALWAYS_INLINE bool ReadFloat(float* dst) { return ReadT(dst); }
    247   bool ReadCString(std::string* dst);
    248   bool ReadCString(SmallStringBase* dst);
    249   bool ReadSizePrefixedString(std::string* dst);
    250   bool ReadSizePrefixedString(SmallStringBase* dst);
    251 
    252   template<typename T> ALWAYS_INLINE T ReadT() { T ret; if (!Read(&ret, sizeof(ret))) [[unlikely]] { ret = {}; } return ret; }
    253   ALWAYS_INLINE bool ReadBool() { return (ReadT<u8>() != 0); }
    254   ALWAYS_INLINE s8 ReadS8() { return ReadT<s8>(); }
    255   ALWAYS_INLINE u8 ReadU8() { return ReadT<u8>(); }
    256   ALWAYS_INLINE s16 ReadS16() { return ReadT<s16>(); }
    257   ALWAYS_INLINE u16 ReadU16() { return ReadT<u16>(); }
    258   ALWAYS_INLINE s32 ReadS32() { return ReadT<s32>(); }
    259   ALWAYS_INLINE u32 ReadU32() { return ReadT<u32>(); }
    260   ALWAYS_INLINE s64 ReadS64() { return ReadT<s64>(); }
    261   ALWAYS_INLINE u64 ReadU64() { return ReadT<u64>(); }
    262   ALWAYS_INLINE float ReadFloat() { return ReadT<float>(); }
    263   std::string ReadCString();
    264   std::string ReadSizePrefixedString();
    265 
    266   ALWAYS_INLINE BinaryFileReader& operator>>(s8& val) { val = ReadT<s8>(); return *this; }
    267   ALWAYS_INLINE BinaryFileReader& operator>>(u8& val) { val = ReadT<u8>(); return *this; }
    268   ALWAYS_INLINE BinaryFileReader& operator>>(s16& val) { val = ReadT<s16>(); return *this; }
    269   ALWAYS_INLINE BinaryFileReader& operator>>(u16& val) { val = ReadT<u16>(); return *this; }
    270   ALWAYS_INLINE BinaryFileReader& operator>>(s32& val) { val = ReadT<s32>(); return *this; }
    271   ALWAYS_INLINE BinaryFileReader& operator>>(u32& val) { val = ReadT<u32>(); return *this; }
    272   ALWAYS_INLINE BinaryFileReader& operator>>(s64& val) { val = ReadT<s64>(); return *this; }
    273   ALWAYS_INLINE BinaryFileReader& operator>>(u64& val) { val = ReadT<u64>(); return *this; }
    274   ALWAYS_INLINE BinaryFileReader& operator>>(float& val) { val = ReadT<float>(); return *this; }
    275   ALWAYS_INLINE BinaryFileReader& operator>>(std::string_view& val) { val = ReadCString(); return *this; }
    276   // clang-format on
    277 
    278   template<typename T>
    279   ALWAYS_INLINE bool ReadOptionalT(std::optional<T>* dst)
    280   {
    281     u8 has_value;
    282     if (!ReadT(&has_value)) [[unlikely]]
    283       return false;
    284 
    285     if (has_value == 0)
    286     {
    287       dst->reset();
    288       return true;
    289     }
    290 
    291     T value;
    292     if (!ReadT(&value)) [[unlikely]]
    293       return false;
    294 
    295     *dst = value;
    296     return true;
    297   }
    298 
    299   ALWAYS_INLINE bool Read(void* buf, size_t size) { return (m_good = (m_good && std::fread(buf, size, 1, m_fp) == 1)); }
    300 
    301 private:
    302   std::FILE* m_fp;
    303   s64 m_size;
    304   bool m_good = true;
    305 };
    306 
    307 class BinaryFileWriter
    308 {
    309 public:
    310   BinaryFileWriter();
    311   BinaryFileWriter(std::FILE* fp);
    312   
    313   BinaryFileWriter(const BinaryFileWriter&) = delete;
    314   BinaryFileWriter& operator=(const BinaryFileWriter&) = delete;
    315 
    316   BinaryFileWriter(BinaryFileWriter&& move);
    317   BinaryFileWriter& operator=(BinaryFileWriter&& move);
    318 
    319   ALWAYS_INLINE const std::FILE* GetFile() const { return m_fp; }
    320   ALWAYS_INLINE bool HasError() const { return !m_good; }
    321   ALWAYS_INLINE bool IsGood() const { return m_good; }
    322   ALWAYS_INLINE bool IsOpen() const { return (m_fp != nullptr); }
    323 
    324   // clang-format off
    325   template<typename T> ALWAYS_INLINE bool WriteT(T dst) { return Write(&dst, sizeof(T)); }
    326   ALWAYS_INLINE bool WriteBool(bool val) { const bool bval = static_cast<u8>(val); return Write(&bval, sizeof(bval)); }
    327   ALWAYS_INLINE bool WriteS8(s8 val) { return WriteT(val); }
    328   ALWAYS_INLINE bool WriteU8(u8 val) { return WriteT(val); }
    329   ALWAYS_INLINE bool WriteS16(s16 val) { return WriteT(val); }
    330   ALWAYS_INLINE bool WriteU16(u16 val) { return WriteT(val); }
    331   ALWAYS_INLINE bool WriteS32(s32 val) { return WriteT(val); }
    332   ALWAYS_INLINE bool WriteU32(u32 val) { return WriteT(val); }
    333   ALWAYS_INLINE bool WriteS64(s64 val) { return WriteT(val); }
    334   ALWAYS_INLINE bool WriteU64(u64 val) { return WriteT(val); }
    335   ALWAYS_INLINE bool WriteFloat(float val) { return WriteT(val); }
    336   bool WriteCString(std::string_view val);
    337   bool WriteSizePrefixedString(std::string_view val);
    338 
    339   ALWAYS_INLINE BinaryFileWriter& operator<<(s8 val) { WriteS8(val); return *this; }
    340   ALWAYS_INLINE BinaryFileWriter& operator<<(u8 val) { WriteU8(val); return *this; }
    341   ALWAYS_INLINE BinaryFileWriter& operator<<(s16 val) { WriteS16(val); return *this; }
    342   ALWAYS_INLINE BinaryFileWriter& operator<<(u16 val) { WriteU16(val); return *this; }
    343   ALWAYS_INLINE BinaryFileWriter& operator<<(s32 val) { WriteS32(val); return *this; }
    344   ALWAYS_INLINE BinaryFileWriter& operator<<(u32 val) { WriteU32(val); return *this; }
    345   ALWAYS_INLINE BinaryFileWriter& operator<<(s64 val) { WriteS64(val); return *this; }
    346   ALWAYS_INLINE BinaryFileWriter& operator<<(u64 val) { WriteU64(val); return *this; }
    347   ALWAYS_INLINE BinaryFileWriter& operator<<(float val) { WriteFloat(val); return *this; }
    348   ALWAYS_INLINE BinaryFileWriter& operator<<(std::string_view val) { WriteCString(val); return *this; }
    349   // clang-format on
    350 
    351   template<typename T>
    352   ALWAYS_INLINE bool WriteOptionalT(const std::optional<T>& val)
    353   {
    354     return (WriteBool(val.has_value()) && (!val.has_value() || WriteT(val.value())));
    355   }
    356 
    357   ALWAYS_INLINE bool Write(const void* buf, size_t size)
    358   {
    359     return (m_good = (m_good && std::fwrite(buf, size, 1, m_fp) == 1));
    360   }
    361 
    362   bool Flush(Error* error = nullptr);
    363 
    364 private:
    365   std::FILE* m_fp;
    366   bool m_good;
    367 };