stdex Additional custom or not Standard C++ covered algorithms

Loading...
Searching...
No Matches
idrec.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2024 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
14 namespace 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
65 template <class T_size, T_size N_align>
66 T_size padding(_In_ T_size size)
67 {
68 return (N_align - (size % N_align)) % N_align;
69 }
70
80 template <class T_size, T_size N_align>
81 bool ignore(_In_ std::istream& stream)
82 {
83 // Read record size.
84 T_size size;
85 stream.read((char*)&size, sizeof(size));
86 if (!stream.good()) _Unlikely_ return false;
87
88 // Skip the record data.
89 size += padding<T_size, N_align>(size);
90 stream.ignore(size);
91 if (!stream.good()) _Unlikely_ return false;
92
93 return true;
94 }
95
105 template <class T_size, T_size N_align>
106 bool ignore(_In_ stdex::stream::basic& stream)
107 {
108 // Read record size.
109 T_size size;
110 stream >> size;
111 if (!stream.ok()) _Unlikely_ return false;
112
113 // Skip the record data.
114 size += padding<T_size, N_align>(size);
115 stream.skip(size);
116 if (!stream.ok()) _Unlikely_ return false;
117
118 return true;
119 }
120
132 template <class T_id, class T_size, T_size N_align>
133 bool find(_In_ std::istream& stream, _In_ T_id id, _In_opt_ std::streamoff end = (std::streamoff)-1)
134 {
135 T_id _id;
136 while (end == (std::streamoff)-1 || stream.tellg() < end) {
137 stream.read((char*)&_id, sizeof(_id));
138 if (!stream.good()) _Unlikely_ return false;
139 if (_id == id) {
140 // The record was found.
141 return true;
142 } else
143 ignore<T_size, N_align>(stream);
144 }
145 return false;
146 }
147
159 template <class T_id, class T_size, T_size N_align>
160 bool find(_In_ stdex::stream::basic_file& stream, _In_ T_id id, _In_opt_ stdex::stream::fpos_t end = stdex::stream::fpos_max)
161 {
162 T_id _id;
163 while (end == stdex::stream::fpos_max || stream.tell() < end) {
164 stream >> _id;
165 if (!stream.ok()) _Unlikely_ return false;
166 if (_id == id) {
167 // The record was found.
168 return true;
169 } else
170 ignore<T_size, N_align>(stream);
171 }
172 return false;
173 }
174
183 template <class T_id, class T_size>
184 std::streamoff open(_In_ std::ostream& stream, _In_ T_id id)
185 {
186 std::streamoff start = stream.tellp();
187
188 // Write ID.
189 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
190 stream.write((const char*)&id, sizeof(id));
191
192 // Write 0 as a placeholder for data size.
193 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
194 T_size size = 0;
195 stream.write((const char*)&size, sizeof(size));
196
197 return start;
198 }
199
208 template <class T_id, class T_size>
209 stdex::stream::fpos_t open(_In_ stdex::stream::basic_file& stream, _In_ T_id id)
210 {
211 auto start = stream.tell();
212
213 // Write ID.
214 stream << id;
215
216 // Write 0 as a placeholder for data size.
217 stream << static_cast<T_size>(0);
218
219 return start;
220 }
221
230 template <class T_id, class T_size, T_size N_align>
231 std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
232 {
233 std::streamoff end = stream.tellp();
234 T_size
235 size = static_cast<T_size>(end - start - sizeof(T_id) - sizeof(T_size)),
236 remainder = padding<T_size, N_align>(size);
237
238 if (remainder) {
239 // Append padding.
240 static const char padding[N_align] = {};
241 stream.write(padding, remainder);
242 end += remainder;
243 }
244
245 // Update the data size.
246 if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
247 stream.seekp(start + sizeof(T_id));
248 stream.write(reinterpret_cast<const char*>(&size), sizeof(size));
249 stream.seekp(end);
250
251 return end;
252 }
253
262 template <class T_id, class T_size, T_size N_align>
263 stdex::stream::fpos_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::fpos_t start)
264 {
265 auto end = stream.tell();
266 T_size
267 size = static_cast<T_size>(end - start - sizeof(T_id) - sizeof(T_size)),
268 remainder = padding<T_size, N_align>(size);
269
270 if (remainder) {
271 // Append padding.
272 static const char padding[N_align] = {};
273 stream.write_array(padding, sizeof(char), remainder);
274 end += remainder;
275 }
276
277 // Update the data size.
278 if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max;
279 stream.seekbeg(start + sizeof(T_id));
280 stream << size;
281 stream.seekbeg(end);
282
283 return end;
284 }
285
289 template <class T, class T_id, const T_id ID, class T_size, T_size N_align>
290 class record
291 {
292 public:
298 record(_In_ T &d) : data(d) {}
299
305 record(_In_ const T &d) : data((T&)d) {}
306
310 static constexpr T_id id()
311 {
312 return ID;
313 }
314
322 const record<T, T_id, ID, T_size, N_align>& operator =(_In_ const record<T, T_id, ID, T_size, N_align> &r)
323 {
324 data = r.data;
325 return *this;
326 }
327
335 static std::streamoff open(_In_ std::ostream& stream)
336 {
337 return stdex::idrec::open<T_id, T_size>(stream, ID);
338 }
339
347 static stdex::stream::fpos_t open(_In_ stdex::stream::basic_file& stream)
348 {
349 return stdex::idrec::open<T_id, T_size>(stream, ID);
350 }
351
360 static std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
361 {
362 return stdex::idrec::close<T_id, T_size, N_align>(stream, start);
363 }
364
373 static stdex::stream::fpos_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::fpos_t start)
374 {
375 return stdex::idrec::close<T_id, T_size, N_align>(stream, start);
376 }
377
388 static bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1)
389 {
390 return stdex::idrec::find<T_id, T_size, N_align>(stream, ID, end);
391 }
392
403 static bool find(_In_ stdex::stream::basic_file& stream, _In_opt_ stdex::stream::fpos_t end = stdex::stream::fpos_max)
404 {
405 return stdex::idrec::find<T_id, T_size, N_align>(stream, ID, end);
406 }
407
408 T &data;
409
418 friend std::ostream& operator <<(_In_ std::ostream& stream, _In_ const record<T, T_id, ID, T_size, N_align> r)
419 {
420 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
421
422 auto start = r.open(stream);
423 if (stream.fail()) _Unlikely_ return stream;
424 stream << r.data;
425 r.close(stream, start);
426
427 return stream;
428 }
429
438 friend stdex::stream::basic_file& operator <<(_In_ stdex::stream::basic_file& stream, _In_ const record<T, T_id, ID, T_size, N_align> r)
439 {
440 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
441
442 auto start = r.open(stream);
443 if (!stream.ok()) _Unlikely_ return stream;
444 stream << r.data;
445 r.close(stream, start);
446
447 return stream;
448 }
449
458 friend stdex::stream::basic& operator <<(_In_ stdex::stream::basic& stream, _In_ const record<T, T_id, ID, T_size, N_align> r)
459 {
460 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
461
462 stdex::stream::memory_file temp;
463 auto start = r.open(temp);
464 if (!temp.ok()) _Unlikely_ return stream;
465 temp << r.data;
466 r.close(temp, start);
467 temp.seekbeg(0);
468 stream.write_stream(temp);
469
470 return stream;
471 }
472
481 friend std::istream& operator >>(_In_ std::istream& stream, _In_ record<T, T_id, ID, T_size, N_align> r)
482 {
483 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
484
485 // Read data size.
486 T_size size;
487 stream.read((char*)&size, sizeof(size));
488 if (!stream.good()) _Unlikely_ return stream;
489
490 // Read data.
491 std::streamoff start = stream.tellg();
492 stream >> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead.
493 if (!stream.good()) _Unlikely_ return stream;
494
495 size += padding<T_size, N_align>(size);
496 stream.seekg(start + size);
497
498 return stream;
499 }
500
509 friend stdex::stream::basic_file& operator >>(_In_ stdex::stream::basic_file& stream, _In_ record<T, T_id, ID, T_size, N_align> r)
510 {
511 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
512
513 // Read data size.
514 T_size size;
515 stream >> size;
516 if (!stream.ok()) _Unlikely_ return stream;
517
518 // Read data.
519 auto start = stream.tell();
520 {
521 stdex::stream::limiter limiter(stream, size, 0);
522 limiter >> r.data;
523 if (limiter.state() == stdex::stream::state_t::fail) _Unlikely_ return stream;
524 }
525
526 size += padding<T_size, N_align>(size);
527 stream.seekbeg(start + static_cast<stdex::stream::fpos_t>(size));
528
529 return stream;
530 }
531
540 friend stdex::stream::basic& operator >>(_In_ stdex::stream::basic& stream, _In_ record<T, T_id, ID, T_size, N_align> r)
541 {
542 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already.
543
544 // Read data size.
545 T_size size;
546 stream >> size;
547 if (!stream.ok()) _Unlikely_ return stream;
548
549 {
550 stdex::stream::limiter limiter(stream, size, 0);
551 limiter >> r.data;
552 if (limiter.state() == stdex::stream::state_t::fail) _Unlikely_ return stream;
553 limiter.skip(limiter.read_limit);
554 }
555 stream.skip(padding<T_size, N_align>(size));
556
557 return stream;
558 }
559 };
560 };
561};
stdex::idrec::record
Helper class for read/write of records to/from memory.
Definition idrec.hpp:291
stdex::idrec::record::operator=
const record< T, T_id, ID, T_size, N_align > & operator=(const record< T, T_id, ID, T_size, N_align > &r)
Assignment operator.
Definition idrec.hpp:322
stdex::idrec::record::open
static stdex::stream::fpos_t open(stdex::stream::basic_file &stream)
Writes record header.
Definition idrec.hpp:347
stdex::idrec::record::close
static stdex::stream::fpos_t close(stdex::stream::basic_file &stream, stdex::stream::fpos_t start)
Updates record header.
Definition idrec.hpp:373
stdex::idrec::record::data
T & data
Record data reference.
Definition idrec.hpp:408
stdex::idrec::record::operator>>
friend std::istream & operator>>(std::istream &stream, record< T, T_id, ID, T_size, N_align > r)
Reads record from a stream.
Definition idrec.hpp:481
stdex::idrec::record::find
static bool find(stdex::stream::basic_file &stream, stdex::stream::fpos_t end=stdex::stream::fpos_max)
Finds record data.
Definition idrec.hpp:403
stdex::idrec::record::close
static std::streamoff close(std::ostream &stream, std::streamoff start)
Updates record header.
Definition idrec.hpp:360
stdex::idrec::record::find
static bool find(std::istream &stream, std::streamoff end=(std::streamoff) -1)
Finds record data.
Definition idrec.hpp:388
stdex::idrec::record::record
record(T &d)
Constructs the class.
Definition idrec.hpp:298
stdex::idrec::record::record
record(const T &d)
Constructs the class.
Definition idrec.hpp:305
stdex::idrec::record::id
static constexpr T_id id()
Returns record id.
Definition idrec.hpp:310
stdex::idrec::record::operator<<
friend std::ostream & operator<<(std::ostream &stream, const record< T, T_id, ID, T_size, N_align > r)
Writes record to a stream.
Definition idrec.hpp:418
stdex::idrec::record::open
static std::streamoff open(std::ostream &stream)
Writes record header.
Definition idrec.hpp:335
stdex::stream::basic_file
Basic seekable stream operations.
Definition stream.hpp:815
stdex::stream::basic_file::seekbeg
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:842
stdex::stream::basic
Basic stream operations.
Definition stream.hpp:85
stdex::stream::basic::ok
bool ok() const
Returns true if the stream state is clean i.e. previous operation was successful.
Definition stream.hpp:181
stdex::stream::basic::state
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:176
stdex::stream::basic::skip
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:148
stdex::stream::limiter
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1549
stdex::stream::limiter::read_limit
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1601
stdex::stream::memory_file
In-memory file.
Definition stream.hpp:3219
stdex::stream::memory_file::data
const void * data() const
Returns pointer to data.
Definition stream.hpp:3549