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 };