stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
idrec.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2023 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "stream.hpp"
10#include <ios>
11#include <istream>
12#include <ostream>
13
14namespace stdex {
15 namespace idrec {
27 template <class T_ID>
28 _Success_(return) bool read_id(_In_ std::istream& stream, _Out_ T_ID &id, _In_opt_ std::streamoff end = (std::streamoff)-1)
29 {
30 if (end == (std::streamoff)-1 || stream.tellg() < end) {
31 stream.read((char*)&id, sizeof(id));
32 return stream.good();
33 } else
34 return false;
35 }
36
48 template <class T_ID>
49 _Success_(return) bool read_id(_In_ stdex::stream::basic_file& stream, _Out_ T_ID &id, _In_opt_ stdex::stream::fpos_t end = stdex::stream::fpos_max)
50 {
51 if (end == stdex::stream::fpos_max || stream.tell() < end) {
52 stream >> id;
53 return stream.ok();
54 } else
55 return false;
56 }
57
67 template <class T_SIZE, unsigned int ALIGN>
68 bool ignore(_In_ std::istream& stream)
69 {
70 // Read record size.
71 T_SIZE size;
72 stream.read((char*)&size, sizeof(size));
73 if (!stream.good()) _Unlikely_ return false;
74
75 // Skip the record data.
76 size += (T_SIZE)(ALIGN - size) % ALIGN;
77 stream.ignore(size);
78 if (!stream.good()) _Unlikely_ return false;
79
80 return true;
81 }
82
92 template <class T_SIZE, unsigned int ALIGN>
93 bool ignore(_In_ stdex::stream::basic_file& stream)
94 {
95 // Read record size.
96 T_SIZE size;
97 stream >> size;
98 if (!stream.ok()) _Unlikely_ return false;
99
100 // Skip the record data.
101 size += (T_SIZE)(ALIGN - size) % ALIGN;
102 stream.skip(size);
103 if (!stream.ok()) _Unlikely_ return false;
104
105 return true;
106 }
107
119 template <class T_ID, class T_SIZE, unsigned int ALIGN>
120 bool find(_In_ std::istream& stream, _In_ T_ID id, _In_opt_ std::streamoff end = (std::streamoff)-1)
121 {
122 T_ID _id;
123 while (end == (std::streamoff)-1 || stream.tellg() < end) {
124 stream.read((char*)&_id, sizeof(_id));
125 if (!stream.good()) _Unlikely_ return false;
126 if (_id == id) {
127 // The record was found.
128 return true;
129 } else
130 ignore<T_SIZE, ALIGN>(stream);
131 }
132 return false;
133 }
134
146 template <class T_ID, class T_SIZE, unsigned int ALIGN>
147 bool find(_In_ stdex::stream::basic_file& stream, _In_ T_ID id, _In_opt_ stdex::stream::fpos_t end = stdex::stream::fpos_max)
148 {
149 T_ID _id;
150 while (end == stdex::stream::fpos_max || stream.tell() < end) {
151 stream >> _id;
152 if (!stream.ok()) _Unlikely_ return false;
153 if (_id == id) {
154 // The record was found.
155 return true;
156 } else
157 ignore<T_SIZE, ALIGN>(stream);
158 }
159 return false;
160 }
161
170 template <class T_ID, class T_SIZE>
171 std::streamoff open(_In_ std::ostream& stream, _In_ T_ID id)
172 {
173 std::streamoff start = stream.tellp();
174
175 // Write ID.
176 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
177 stream.write((const char*)&id, sizeof(id));
178
179 // Write 0 as a placeholder for data size.
180 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
181 T_SIZE size = 0;
182 stream.write((const char*)&size, sizeof(size));
183
184 return start;
185 }
186
195 template <class T_ID, class T_SIZE>
196 stdex::stream::fpos_t open(_In_ stdex::stream::basic_file& stream, _In_ T_ID id)
197 {
198 auto start = stream.tell();
199
200 // Write ID.
201 if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max;
202 stream << id;
203
204 // Write 0 as a placeholder for data size.
205 if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max;
206 stream << (T_SIZE)0;
207
208 return start;
209 }
210
219 template <class T_ID, class T_SIZE, unsigned int ALIGN>
220 std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
221 {
222 std::streamoff end = stream.tellp();
223 T_SIZE
224 size = static_cast<T_SIZE>(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
225 remainder = static_cast<T_SIZE>((ALIGN - size) % ALIGN); // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long
226
227 if (remainder) {
228 // Append padding.
229 static const char padding[ALIGN] = {};
230 stream.write(padding, remainder);
231 end += remainder;
232 }
233
234 // Update the data size.
235 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
236 stream.seekp(start + sizeof(T_ID));
237 stream.write((const char*)&size, sizeof(size));
238 stream.seekp(end);
239
240 return end;
241 }
242
251 template <class T_ID, class T_SIZE, unsigned int ALIGN>
252 stdex::stream::fpos_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::fpos_t start)
253 {
254 auto end = stream.tell();
255 T_SIZE
256 size = static_cast<T_SIZE>(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
257 remainder = static_cast<T_SIZE>((ALIGN - size) % ALIGN); // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long
258
259 if (remainder) {
260 // Append padding.
261 static const char padding[ALIGN] = {};
262 stream.write_array(padding, sizeof(char), remainder);
263 end += remainder;
264 }
265
266 // Update the data size.
267 if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max;
268 stream.seek(start + sizeof(T_ID));
269 stream << size;
270 stream.seek(end);
271
272 return end;
273 }
274
278 template <class T, class T_ID, const T_ID ID, class T_SIZE, unsigned int ALIGN>
279 class record
280 {
281 public:
287 record(_In_ T &d) : data(d) {}
288
294 record(_In_ const T &d) : data((T&)d) {}
295
299 static const T_ID id()
300 {
301 return ID;
302 }
303
312 {
313 data = r.data;
314 return *this;
315 }
316
324 static std::streamoff open(_In_ std::ostream& stream)
325 {
326 return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
327 }
328
336 static stdex::stream::foff_t open(_In_ stdex::stream::basic_file& stream)
337 {
338 return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
339 }
340
349 static std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
350 {
351 return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
352 }
353
362 static stdex::stream::foff_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::foff_t start)
363 {
364 return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
365 }
366
377 static bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1)
378 {
379 return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, ID, end);
380 }
381
392 static bool find(_In_ stdex::stream::basic_file& stream, _In_opt_ stdex::stream::foff_t end = stdex::stream::foff_max)
393 {
394 return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, ID, end);
395 }
396
397 T &data;
398 };
399 };
400};
401
410template <class T, class T_ID, T_ID ID, class T_SIZE, unsigned int ALIGN>
411std::ostream& operator <<(_In_ std::ostream& stream, _In_ const stdex::idrec::record<T, T_ID, ID, T_SIZE, ALIGN> r)
412{
413 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
414
415 auto start = r.open(stream);
416 if (stream.fail()) _Unlikely_ return stream;
417 stream << r.data;
418 r.close(stream, start);
419
420 return stream;
421}
422
431template <class T, class T_ID, T_ID ID, class T_SIZE, unsigned int ALIGN>
433{
434 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
435
436 auto start = r.open(stream);
437 if (!stream.ok()) _Unlikely_ return stream;
438 stream << r.data;
439 r.close(stream, start);
440
441 return stream;
442}
443
452template <class T, class T_ID, T_ID ID, class T_SIZE, unsigned int ALIGN>
453std::istream& operator >>(_In_ std::istream& stream, _In_ stdex::idrec::record<T, T_ID, ID, T_SIZE, ALIGN> r)
454{
455 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
456
457 // Read data size.
458 T_SIZE size;
459 stream.read((char*)&size, sizeof(size));
460 if (!stream.good()) _Unlikely_ return stream;
461
462 // Read data.
463 std::streamoff start = stream.tellg();
464 stream >> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead.
465 if (!stream.good()) _Unlikely_ return stream;
466
467 size += static_cast<T_SIZE>((ALIGN - size) % ALIGN);
468 stream.seekg(start + size);
469
470 return stream;
471}
472
481template <class T, class T_ID, T_ID ID, class T_SIZE, unsigned int ALIGN>
483{
484 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
485
486 // Read data size.
487 T_SIZE size;
488 stream >> size;
489 if (!stream.ok()) _Unlikely_ return stream;
490
491 // Read data.
492 auto start = stream.tell();
493 {
494 stdex::stream::limiter limiter(stream, size, size);
495 limiter >> r.data;
496 if (!limiter.ok()) _Unlikely_ return stream;
497 }
498
499 size += static_cast<T_SIZE>((ALIGN - size) % ALIGN);
500 stream.seek(start + size);
501
502 return stream;
503}
Helper class for read/write of records to/from memory.
Definition idrec.hpp:280
static stdex::stream::foff_t open(stdex::stream::basic_file &stream)
Writes record header.
Definition idrec.hpp:336
static stdex::stream::foff_t close(stdex::stream::basic_file &stream, stdex::stream::foff_t start)
Updates record header.
Definition idrec.hpp:362
static bool find(std::istream &stream, std::streamoff end=(std::streamoff) -1)
Finds record data.
Definition idrec.hpp:377
T & data
Record data reference.
Definition idrec.hpp:397
static const T_ID id()
Returns record id.
Definition idrec.hpp:299
static std::streamoff open(std::ostream &stream)
Writes record header.
Definition idrec.hpp:324
const record< T, T_ID, ID, T_SIZE, ALIGN > & operator=(const record< T, T_ID, ID, T_SIZE, ALIGN > &r)
Assignment operator.
Definition idrec.hpp:311
record(T &d)
Constructs the class.
Definition idrec.hpp:287
static bool find(stdex::stream::basic_file &stream, stdex::stream::foff_t end=stdex::stream::foff_max)
Finds record data.
Definition idrec.hpp:392
record(const T &d)
Constructs the class.
Definition idrec.hpp:294
static std::streamoff close(std::ostream &stream, std::streamoff start)
Updates record header.
Definition idrec.hpp:349
Basic seekable stream operations.
Definition stream.hpp:699
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1421