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, T_SIZE 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, T_SIZE ALIGN>
93 bool ignore(_In_ stdex::stream::basic& 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, T_SIZE 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, T_SIZE 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 stream << id;
202
203 // Write 0 as a placeholder for data size.
204 stream << (T_SIZE)0;
205
206 return start;
207 }
208
217 template <class T_ID, class T_SIZE, T_SIZE ALIGN>
218 std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
219 {
220 std::streamoff end = stream.tellp();
221 T_SIZE
222 size = static_cast<T_SIZE>(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
223 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
224
225 if (remainder) {
226 // Append padding.
227 static const char padding[ALIGN] = {};
228 stream.write(padding, remainder);
229 end += remainder;
230 }
231
232 // Update the data size.
233 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
234 stream.seekp(start + sizeof(T_ID));
235 stream.write((const char*)&size, sizeof(size));
236 stream.seekp(end);
237
238 return end;
239 }
240
249 template <class T_ID, class T_SIZE, T_SIZE ALIGN>
250 stdex::stream::fpos_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::fpos_t start)
251 {
252 auto end = stream.tell();
253 T_SIZE
254 size = static_cast<T_SIZE>(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
255 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
256
257 if (remainder) {
258 // Append padding.
259 static const char padding[ALIGN] = {};
260 stream.write_array(padding, sizeof(char), remainder);
261 end += remainder;
262 }
263
264 // Update the data size.
265 if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max;
266 stream.seek(start + sizeof(T_ID));
267 stream << size;
268 stream.seek(end);
269
270 return end;
271 }
272
276 template <class T, class T_ID, const T_ID ID, class T_SIZE, T_SIZE ALIGN>
277 class record
278 {
279 public:
285 record(_In_ T &d) : data(d) {}
286
292 record(_In_ const T &d) : data((T&)d) {}
293
297 static const T_ID id()
298 {
299 return ID;
300 }
301
310 {
311 data = r.data;
312 return *this;
313 }
314
322 static std::streamoff open(_In_ std::ostream& stream)
323 {
324 return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
325 }
326
334 static stdex::stream::foff_t open(_In_ stdex::stream::basic_file& stream)
335 {
336 return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
337 }
338
347 static std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
348 {
349 return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
350 }
351
360 static stdex::stream::foff_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::foff_t start)
361 {
362 return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
363 }
364
375 static bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1)
376 {
377 return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, ID, end);
378 }
379
390 static bool find(_In_ stdex::stream::basic_file& stream, _In_opt_ stdex::stream::foff_t end = stdex::stream::foff_max)
391 {
392 return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, ID, end);
393 }
394
395 T &data;
396
405 friend std::ostream& operator <<(_In_ std::ostream& stream, _In_ const record<T, T_ID, ID, T_SIZE, ALIGN> r)
406 {
407 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
408
409 auto start = r.open(stream);
410 if (stream.fail()) _Unlikely_ return stream;
411 stream << r.data;
412 r.close(stream, start);
413
414 return stream;
415 }
416
426 {
427 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
428
429 auto start = r.open(stream);
430 if (!stream.ok()) _Unlikely_ return stream;
431 stream << r.data;
432 r.close(stream, start);
433
434 return stream;
435 }
436
446 {
447 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
448
450 auto start = r.open(temp);
451 if (!temp.ok()) _Unlikely_ return stream;
452 temp << r.data;
453 r.close(temp, start);
454 temp.seek(0);
455 stream.write_stream(temp);
456
457 return stream;
458 }
459
468 friend std::istream& operator >>(_In_ std::istream& stream, _In_ record<T, T_ID, ID, T_SIZE, ALIGN> r)
469 {
470 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
471
472 // Read data size.
473 T_SIZE size;
474 stream.read((char*)&size, sizeof(size));
475 if (!stream.good()) _Unlikely_ return stream;
476
477 // Read data.
478 std::streamoff start = stream.tellg();
479 stream >> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead.
480 if (!stream.good()) _Unlikely_ return stream;
481
482 size += static_cast<T_SIZE>((ALIGN - size) % ALIGN);
483 stream.seekg(start + size);
484
485 return stream;
486 }
487
497 {
498 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
499
500 // Read data size.
501 T_SIZE size;
502 stream >> size;
503 if (!stream.ok()) _Unlikely_ return stream;
504
505 // Read data.
506 auto start = stream.tell();
507 {
508 stdex::stream::limiter limiter(stream, size, 0);
509 limiter >> r.data;
510 if (!limiter.ok()) _Unlikely_ return stream;
511 }
512
513 size += static_cast<T_SIZE>((ALIGN - size) % ALIGN);
514 stream.seek(start + size);
515
516 return stream;
517 }
518
528 {
529 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
530
531 // Read data size.
532 T_SIZE size;
533 stream >> size;
534 if (!stream.ok()) _Unlikely_ return stream;
535
536 {
537 stdex::stream::limiter limiter(stream, size, 0);
538 limiter >> r.data;
539 if (!limiter.ok()) _Unlikely_ return stream;
540 limiter.skip(limiter.read_limit);
541 }
542 stream.skip(static_cast<T_SIZE>((ALIGN - size) % ALIGN));
543
544 return stream;
545 }
546 };
547 };
548};
Helper class for read/write of records to/from memory.
Definition idrec.hpp:278
static stdex::stream::foff_t open(stdex::stream::basic_file &stream)
Writes record header.
Definition idrec.hpp:334
friend std::istream & operator>>(std::istream &stream, record< T, T_ID, ID, T_SIZE, ALIGN > r)
Reads record from a stream.
Definition idrec.hpp:468
static stdex::stream::foff_t close(stdex::stream::basic_file &stream, stdex::stream::foff_t start)
Updates record header.
Definition idrec.hpp:360
static bool find(std::istream &stream, std::streamoff end=(std::streamoff) -1)
Finds record data.
Definition idrec.hpp:375
T & data
Record data reference.
Definition idrec.hpp:395
static const T_ID id()
Returns record id.
Definition idrec.hpp:297
static std::streamoff open(std::ostream &stream)
Writes record header.
Definition idrec.hpp:322
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:309
record(T &d)
Constructs the class.
Definition idrec.hpp:285
static bool find(stdex::stream::basic_file &stream, stdex::stream::foff_t end=stdex::stream::foff_max)
Finds record data.
Definition idrec.hpp:390
friend std::ostream & operator<<(std::ostream &stream, const record< T, T_ID, ID, T_SIZE, ALIGN > r)
Writes record to a stream.
Definition idrec.hpp:405
record(const T &d)
Constructs the class.
Definition idrec.hpp:292
static std::streamoff close(std::ostream &stream, std::streamoff start)
Updates record header.
Definition idrec.hpp:347
Basic seekable stream operations.
Definition stream.hpp:824
‍UTF-8 byte-order-mark
Definition stream.hpp:79
bool ok() const
Returns true if the stream state is clean i.e. previous operation was succesful.
Definition stream.hpp:175
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:142
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1551
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1603
In-memory file.
Definition stream.hpp:3172
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3733
const void * data() const
Returns pointer to data.
Definition stream.hpp:3382