stdex
Additional custom or not Standard C++ covered algorithms
idrec.h
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2022 Amebis
4*/
5
6#pragma once
7
8#include <ios>
9#include <istream>
10#include <ostream>
11
12
13namespace stdex {
14 namespace idrec {
26 template <class T_ID>
27 _Success_(return) bool read_id(_In_ std::istream& stream, _Out_ T_ID &id, _In_opt_ std::streamoff end = (std::streamoff)-1)
28 {
29 if (end == (std::streamoff)-1 || stream.tellg() < end) {
30 stream.read((char*)&id, sizeof(id));
31 return stream.good();
32 } else
33 return false;
34 }
35
36
46 template <class T_SIZE, unsigned int ALIGN>
47 bool ignore(_In_ std::istream& stream)
48 {
49 // Read record size.
50 T_SIZE size;
51 stream.read((char*)&size, sizeof(size));
52 if (!stream.good()) return false;
53
54 // Skip the record data.
55 size += (T_SIZE)(ALIGN - size) % ALIGN;
56 stream.ignore(size);
57 if (!stream.good()) return false;
58
59 return true;
60 }
61
62
74 template <class T_ID, class T_SIZE, unsigned int ALIGN>
75 bool find(_In_ std::istream& stream, _In_ T_ID id, _In_opt_ std::streamoff end = (std::streamoff)-1)
76 {
77 T_ID _id;
78
79 while (end == (std::streamoff)-1 || stream.tellg() < end) {
80 stream.read((char*)&_id, sizeof(_id));
81 if (!stream.good()) return false;
82
83 if (_id == id) {
84 // The record was found.
85 return true;
86 } else
87 ignore<T_SIZE, ALIGN>(stream);
88 }
89
90 return false;
91 }
92
93
102 template <class T_ID, class T_SIZE>
103 std::streamoff open(_In_ std::ostream& stream, _In_ T_ID id)
104 {
105 std::streamoff start = stream.tellp();
106
107 // Write ID.
108 if (stream.fail()) return (std::streamoff)-1;
109 stream.write((const char*)&id, sizeof(id));
110
111 // Write 0 as a placeholder for data size.
112 if (stream.fail()) return (std::streamoff)-1;
113 T_SIZE size = 0;
114 stream.write((const char*)&size, sizeof(size));
115
116 return start;
117 }
118
119
128 template <class T_ID, class T_SIZE, unsigned int ALIGN>
129 std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
130 {
131 std::streamoff end = stream.tellp();
132 T_SIZE
133 size = (T_SIZE)(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
134 remainder = (T_SIZE)(ALIGN - size) % ALIGN; // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long
135
136 if (remainder) {
137 // Append padding.
138 static const char padding[ALIGN] = {};
139 stream.write(padding, remainder);
140 end += remainder;
141 }
142
143 // Update the data size.
144 if (stream.fail()) return (std::streamoff)-1;
145 stream.seekp(start + sizeof(T_ID));
146 stream.write((const char*)&size, sizeof(size));
147 stream.seekp(end);
148
149 return end;
150 }
151
152
156 template <class T, class T_ID, class T_SIZE, unsigned int ALIGN>
157 class record
158 {
159 public:
165 record(_In_ T &d) : data(d) {}
166
167
173 record(_In_ const T &d) : data((T&)d) {}
174
175
184 {
185 data = r.data;
186 return *this;
187 }
188
189
197 static std::streamoff open(_In_ std::ostream& stream)
198 {
199 return stdex::idrec::open<T_ID, T_SIZE>(stream, id);
200 }
201
202
211 static std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
212 {
213 return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
214 }
215
216
227 static bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1)
228 {
229 return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, id, end);
230 }
231
232
233 static const T_ID id;
234 T &data;
235 };
236 };
237};
238
239
248template <class T, class T_ID, class T_SIZE, unsigned int ALIGN>
249std::ostream& operator <<(_In_ std::ostream& stream, _In_ const stdex::idrec::record<T, T_ID, T_SIZE, ALIGN> r)
250{
251 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already. The id field is static anyway.
252
253 std::streamoff start = r.open(stream);
254 if (stream.fail()) return stream;
255 stream << r.data;
256 r.close(stream, start);
257
258 return stream;
259}
260
261
270template <class T, class T_ID, class T_SIZE, unsigned int ALIGN>
271std::istream& operator >>(_In_ std::istream& stream, _In_ stdex::idrec::record<T, T_ID, T_SIZE, ALIGN> r)
272{
273 // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already. The id field is static anyway.
274
275 // Read data size.
276 T_SIZE size;
277 stream.read((char*)&size, sizeof(size));
278 if (!stream.good()) return stream;
279
280 // Read data.
281 std::streamoff start = stream.tellg();
282 stream >> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead.
283
284 size += (T_SIZE)(ALIGN - size) % ALIGN;
285 stream.seekg(start + size);
286
287 return stream;
288}
Helper class for read/write of records to/from memory.
Definition: idrec.h:158
T & data
Record data reference.
Definition: idrec.h:234
static std::streamoff close(std::ostream &stream, std::streamoff start)
Updates record header.
Definition: idrec.h:211
static bool find(std::istream &stream, std::streamoff end=(std::streamoff) -1)
Finds record data.
Definition: idrec.h:227
static const T_ID id
Record id.
Definition: idrec.h:233
static std::streamoff open(std::ostream &stream)
Writes record header.
Definition: idrec.h:197
record(const T &d)
Constructs the class.
Definition: idrec.h:173
record(T &d)
Constructs the class.
Definition: idrec.h:165
const record< T, T_ID, T_SIZE, ALIGN > & operator=(const record< T, T_ID, T_SIZE, ALIGN > &r)
Assignment operator.
Definition: idrec.h:183