1 /* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
2
3 #ifndef MPT_IO_IO_HPP
4 #define MPT_IO_IO_HPP
5
6
7
8 #include "mpt/base/array.hpp"
9 #include "mpt/base/bit.hpp"
10 #include "mpt/base/integer.hpp"
11 #include "mpt/base/memory.hpp"
12 #include "mpt/base/namespace.hpp"
13 #include "mpt/base/span.hpp"
14 #include "mpt/endian/integer.hpp"
15 #include "mpt/io/base.hpp"
16
17 #include <algorithm>
18 #include <limits>
19 #include <string>
20 #include <vector>
21
22 #include <cassert>
23 #include <cstddef>
24
25
26
27 namespace mpt {
28 inline namespace MPT_INLINE_NS {
29
30
31
32 namespace IO {
33
34
35
36 template <typename Tbyte, typename Tfile>
ReadRaw(Tfile & f,Tbyte * data,std::size_t size)37 inline mpt::byte_span ReadRaw(Tfile & f, Tbyte * data, std::size_t size) {
38 return mpt::IO::ReadRawImpl(f, mpt::as_span(mpt::byte_cast<std::byte *>(data), size));
39 }
40
41 template <typename Tbyte, typename Tfile>
ReadRaw(Tfile & f,mpt::span<Tbyte> data)42 inline mpt::byte_span ReadRaw(Tfile & f, mpt::span<Tbyte> data) {
43 return mpt::IO::ReadRawImpl(f, mpt::byte_cast<mpt::byte_span>(data));
44 }
45
46 template <typename Tbyte, typename Tfile>
WriteRaw(Tfile & f,const Tbyte * data,std::size_t size)47 inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) {
48 return mpt::IO::WriteRawImpl(f, mpt::as_span(mpt::byte_cast<const std::byte *>(data), size));
49 }
50
51 template <typename Tbyte, typename Tfile>
WriteRaw(Tfile & f,mpt::span<Tbyte> data)52 inline bool WriteRaw(Tfile & f, mpt::span<Tbyte> data) {
53 return mpt::IO::WriteRawImpl(f, mpt::byte_cast<mpt::const_byte_span>(data));
54 }
55
56 template <typename Tbinary, typename Tfile>
Read(Tfile & f,Tbinary & v)57 inline bool Read(Tfile & f, Tbinary & v) {
58 return mpt::IO::ReadRaw(f, mpt::as_raw_memory(v)).size() == mpt::as_raw_memory(v).size();
59 }
60
61 template <typename Tbinary, typename Tfile>
Write(Tfile & f,const Tbinary & v)62 inline bool Write(Tfile & f, const Tbinary & v) {
63 return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
64 }
65
66 template <typename Tbinary, typename Tfile>
Write(Tfile & f,const std::vector<Tbinary> & v)67 inline bool Write(Tfile & f, const std::vector<Tbinary> & v) {
68 static_assert(mpt::is_binary_safe<Tbinary>::value);
69 return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
70 }
71
72 template <typename T, typename Tfile>
WritePartial(Tfile & f,const T & v,std::size_t size=sizeof (T))73 inline bool WritePartial(Tfile & f, const T & v, std::size_t size = sizeof(T)) {
74 assert(size <= sizeof(T));
75 return mpt::IO::WriteRaw(f, mpt::as_span(mpt::as_raw_memory(v).data(), size));
76 }
77
78 template <typename Tfile>
ReadByte(Tfile & f,std::byte & v)79 inline bool ReadByte(Tfile & f, std::byte & v) {
80 bool result = false;
81 std::byte byte = mpt::as_byte(0);
82 const std::size_t readResult = mpt::IO::ReadRaw(f, &byte, sizeof(std::byte)).size();
83 result = (readResult == sizeof(std::byte));
84 v = byte;
85 return result;
86 }
87
88 template <typename T, typename Tfile>
ReadBinaryTruncatedLE(Tfile & f,T & v,std::size_t size)89 inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) {
90 bool result = false;
91 static_assert(std::numeric_limits<T>::is_integer);
92 std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
93 const std::size_t readResult = mpt::IO::ReadRaw(f, bytes.data(), std::min(size, sizeof(T))).size();
94 result = (readResult == std::min(size, sizeof(T)));
95 v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
96 return result;
97 }
98
99 template <typename T, typename Tfile>
ReadIntLE(Tfile & f,T & v)100 inline bool ReadIntLE(Tfile & f, T & v) {
101 bool result = false;
102 static_assert(std::numeric_limits<T>::is_integer);
103 std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
104 const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
105 result = (readResult == sizeof(T));
106 v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
107 return result;
108 }
109
110 template <typename T, typename Tfile>
ReadIntBE(Tfile & f,T & v)111 inline bool ReadIntBE(Tfile & f, T & v) {
112 bool result = false;
113 static_assert(std::numeric_limits<T>::is_integer);
114 std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
115 const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
116 result = (readResult == sizeof(T));
117 v = mpt::bit_cast<typename mpt::make_be<T>::type>(bytes);
118 return result;
119 }
120
121 template <typename Tfile>
ReadAdaptiveInt16LE(Tfile & f,uint16 & v)122 inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) {
123 bool result = true;
124 uint8 byte = 0;
125 std::size_t additionalBytes = 0;
126 v = 0;
127 byte = 0;
128 if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
129 result = false;
130 }
131 additionalBytes = (byte & 0x01);
132 v = byte >> 1;
133 for (std::size_t i = 0; i < additionalBytes; ++i) {
134 byte = 0;
135 if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
136 result = false;
137 }
138 v |= (static_cast<uint16>(byte) << (((i + 1) * 8) - 1));
139 }
140 return result;
141 }
142
143 template <typename Tfile>
ReadAdaptiveInt32LE(Tfile & f,uint32 & v)144 inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) {
145 bool result = true;
146 uint8 byte = 0;
147 std::size_t additionalBytes = 0;
148 v = 0;
149 byte = 0;
150 if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
151 result = false;
152 }
153 additionalBytes = (byte & 0x03);
154 v = byte >> 2;
155 for (std::size_t i = 0; i < additionalBytes; ++i) {
156 byte = 0;
157 if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
158 result = false;
159 }
160 v |= (static_cast<uint32>(byte) << (((i + 1) * 8) - 2));
161 }
162 return result;
163 }
164
165 template <typename Tfile>
ReadAdaptiveInt64LE(Tfile & f,uint64 & v)166 inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) {
167 bool result = true;
168 uint8 byte = 0;
169 std::size_t additionalBytes = 0;
170 v = 0;
171 byte = 0;
172 if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
173 result = false;
174 }
175 additionalBytes = (1 << (byte & 0x03)) - 1;
176 v = byte >> 2;
177 for (std::size_t i = 0; i < additionalBytes; ++i) {
178 byte = 0;
179 if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
180 result = false;
181 }
182 v |= (static_cast<uint64>(byte) << (((i + 1) * 8) - 2));
183 }
184 return result;
185 }
186
187 template <typename Tsize, typename Tfile>
ReadSizedStringLE(Tfile & f,std::string & str,Tsize maxSize=std::numeric_limits<Tsize>::max ())188 inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits<Tsize>::max()) {
189 static_assert(std::numeric_limits<Tsize>::is_integer);
190 str.clear();
191 Tsize size = 0;
192 if (!mpt::IO::ReadIntLE(f, size)) {
193 return false;
194 }
195 if (size > maxSize) {
196 return false;
197 }
198 for (Tsize i = 0; i != size; ++i) {
199 char c = '\0';
200 if (!mpt::IO::ReadIntLE(f, c)) {
201 return false;
202 }
203 str.push_back(c);
204 }
205 return true;
206 }
207
208
209
210 template <typename T, typename Tfile>
WriteIntLE(Tfile & f,const T v)211 inline bool WriteIntLE(Tfile & f, const T v) {
212 static_assert(std::numeric_limits<T>::is_integer);
213 return mpt::IO::Write(f, mpt::as_le(v));
214 }
215
216 template <typename T, typename Tfile>
WriteIntBE(Tfile & f,const T v)217 inline bool WriteIntBE(Tfile & f, const T v) {
218 static_assert(std::numeric_limits<T>::is_integer);
219 return mpt::IO::Write(f, mpt::as_be(v));
220 }
221
222 template <typename Tfile>
WriteAdaptiveInt16LE(Tfile & f,const uint16 v,std::size_t fixedSize=0)223 inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t fixedSize = 0) {
224 std::size_t minSize = fixedSize;
225 std::size_t maxSize = fixedSize;
226 assert(minSize == 0 || minSize == 1 || minSize == 2);
227 assert(maxSize == 0 || maxSize == 1 || maxSize == 2);
228 assert(maxSize == 0 || maxSize >= minSize);
229 if (maxSize == 0) {
230 maxSize = 2;
231 }
232 if (v < 0x80 && minSize <= 1 && 1 <= maxSize) {
233 return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 1) | 0x00);
234 } else if (v < 0x8000 && minSize <= 2 && 2 <= maxSize) {
235 return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 1) | 0x01);
236 } else {
237 assert(false);
238 return false;
239 }
240 }
241
242 template <typename Tfile>
WriteAdaptiveInt32LE(Tfile & f,const uint32 v,std::size_t fixedSize=0)243 inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSize = 0) {
244 std::size_t minSize = fixedSize;
245 std::size_t maxSize = fixedSize;
246 assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4);
247 assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4);
248 assert(maxSize == 0 || maxSize >= minSize);
249 if (maxSize == 0) {
250 maxSize = 4;
251 }
252 if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
253 return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
254 } else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
255 return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
256 } else if (v < 0x400000 && minSize <= 3 && 3 <= maxSize) {
257 uint32 value = static_cast<uint32>(v << 2) | 0x02;
258 std::byte bytes[3];
259 bytes[0] = static_cast<std::byte>(value >> 0);
260 bytes[1] = static_cast<std::byte>(value >> 8);
261 bytes[2] = static_cast<std::byte>(value >> 16);
262 return mpt::IO::WriteRaw(f, bytes, 3);
263 } else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
264 return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x03);
265 } else {
266 assert(false);
267 return false;
268 }
269 }
270
271 template <typename Tfile>
WriteAdaptiveInt64LE(Tfile & f,const uint64 v,std::size_t fixedSize=0)272 inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSize = 0) {
273 std::size_t minSize = fixedSize;
274 std::size_t maxSize = fixedSize;
275 assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8);
276 assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8);
277 assert(maxSize == 0 || maxSize >= minSize);
278 if (maxSize == 0) {
279 maxSize = 8;
280 }
281 if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
282 return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
283 } else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
284 return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
285 } else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
286 return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x02);
287 } else if (v < 0x4000000000000000ull && minSize <= 8 && 8 <= maxSize) {
288 return mpt::IO::WriteIntLE<uint64>(f, static_cast<uint64>(v << 2) | 0x03);
289 } else {
290 assert(false);
291 return false;
292 }
293 }
294
295 // Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter.
296 template <typename Tfile, typename T>
WriteVarInt(Tfile & f,const T v,std::size_t * bytesWritten=nullptr)297 bool WriteVarInt(Tfile & f, const T v, std::size_t * bytesWritten = nullptr) {
298 static_assert(std::numeric_limits<T>::is_integer);
299 static_assert(!std::numeric_limits<T>::is_signed);
300 std::byte out[(sizeof(T) * 8 + 6) / 7];
301 std::size_t numBytes = 0;
302 for (uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) {
303 if (v >= (static_cast<T>(1) << (n * 7u))) {
304 out[numBytes++] = static_cast<std::byte>(((v >> (n * 7u)) & 0x7F) | 0x80);
305 }
306 }
307 out[numBytes++] = static_cast<std::byte>(v & 0x7F);
308 assert(numBytes <= std::size(out));
309 if (bytesWritten != nullptr) {
310 *bytesWritten = numBytes;
311 }
312 return mpt::IO::WriteRaw(f, out, numBytes);
313 }
314
315 template <typename Tsize, typename Tfile>
WriteSizedStringLE(Tfile & f,const std::string & str)316 inline bool WriteSizedStringLE(Tfile & f, const std::string & str) {
317 static_assert(std::numeric_limits<Tsize>::is_integer);
318 if (str.size() > std::numeric_limits<Tsize>::max()) {
319 return false;
320 }
321 Tsize size = static_cast<Tsize>(str.size());
322 if (!mpt::IO::WriteIntLE(f, size)) {
323 return false;
324 }
325 if (!mpt::IO::WriteRaw(f, str.data(), str.size())) {
326 return false;
327 }
328 return true;
329 }
330
331 template <typename Tfile>
WriteText(Tfile & f,const std::string & s)332 inline bool WriteText(Tfile & f, const std::string & s) {
333 return mpt::IO::WriteRaw(f, s.data(), s.size());
334 }
335
336 template <typename Tfile>
WriteTextCRLF(Tfile & f)337 inline bool WriteTextCRLF(Tfile & f) {
338 return mpt::IO::WriteText(f, "\r\n");
339 }
340
341 template <typename Tfile>
WriteTextLF(Tfile & f)342 inline bool WriteTextLF(Tfile & f) {
343 return mpt::IO::WriteText(f, "\n");
344 }
345
346 template <typename Tfile>
WriteTextCRLF(Tfile & f,const std::string & s)347 inline bool WriteTextCRLF(Tfile & f, const std::string & s) {
348 return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f);
349 }
350
351 template <typename Tfile>
WriteTextLF(Tfile & f,const std::string & s)352 inline bool WriteTextLF(Tfile & f, const std::string & s) {
353 return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f);
354 }
355
356
357
358 } // namespace IO
359
360
361
362 } // namespace MPT_INLINE_NS
363 } // namespace mpt
364
365
366
367 #endif // MPT_IO_IO_HPP
368