Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common/binary_reader_writer.h
4223 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: 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
explicit 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
explicit 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
explicit 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& 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
explicit 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
};
368
369