1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2018, The OpenClonk Team and contributors
6 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18#pragma once
19
20#include "Standard.h"
21#include "StdCompiler.h"
22
23#include <charconv>
24#include <chrono>
25#include <limits>
26#include <memory>
27#include <optional>
28
29// * Wrappers for C4Compiler-types
30
31// Whole-line string, automatic size deduction (C4Compiler-String)
32#define toC4CStr(szString) mkStringAdaptMA(szString)
33#define toC4CStrBuf(rBuf) mkParAdapt(rBuf, StdCompiler::RCT_All)
34
35// * Null Adaptor
36// Doesn't compile anything
37struct StdNullAdapt
38{
39 inline void CompileFunc(StdCompiler *pComp) const {}
40};
41
42// * Defaulting Adaptor
43// Sets default if CompileFunc fails with a Exception of type NotFoundException
44template <class T, class D>
45struct StdDefaultAdapt
46{
47 T &rValue; const D &rDefault;
48
49 StdDefaultAdapt(T &rValue, const D &rDefault) : rValue(rValue), rDefault(rDefault) {}
50
51 inline void CompileFunc(StdCompiler *pComp) const
52 {
53 try
54 {
55 pComp->Value(rValue);
56 }
57 catch (const StdCompiler::NotFoundException &)
58 {
59 rValue = rDefault;
60 }
61 }
62};
63
64template <class T, class D>
65inline StdDefaultAdapt<T, D> mkDefaultAdapt(T &&rValue, const D &rDefault) { return StdDefaultAdapt<T, D>(rValue, rDefault); }
66
67// * Naming Adaptor
68// Embeds a value into a named section, failsafe
69// (use for values that do defaulting themselves - e.g. objects using naming)
70template <class T>
71struct StdNamingAdapt
72{
73 T &rValue; const char *szName;
74
75 StdNamingAdapt(T &rValue, const char *szName) : rValue(rValue), szName(szName) {}
76
77 inline void CompileFunc(StdCompiler *pComp) const
78 {
79 auto name = pComp->Name(szName);
80 try
81 {
82 pComp->Value(rValue);
83 }
84 catch (const StdCompiler::Exception &)
85 {
86 name.Abort();
87 throw;
88 }
89 }
90
91 template <class D> inline bool operator==(const D &nValue) const { return rValue == nValue; }
92 template <class D> inline StdNamingAdapt &operator=(const D &nValue) { rValue = nValue; return *this; }
93};
94
95template <class T>
96inline StdNamingAdapt<T> mkNamingAdapt(T &&rValue, const char *szName) { return StdNamingAdapt<T>(rValue, szName); }
97
98// * Naming Adaptor (defaulting)
99// Embeds a value into a named section, sets default on fail
100template <class T, class D>
101struct StdNamingDefaultAdapt
102{
103 T &rValue; const char *szName; const D &rDefault; bool fPrefillDefault; bool fStoreDefault;
104
105 StdNamingDefaultAdapt(T &rValue, const char *szName, const D &rDefault, bool fPrefillDefault, bool fStoreDefault) : rValue(rValue), szName(szName), rDefault(rDefault), fPrefillDefault(fPrefillDefault), fStoreDefault(fStoreDefault) {}
106
107 inline void CompileFunc(StdCompiler *pComp) const
108 {
109 // Default check
110 if (pComp->hasNaming() && pComp->isDecompiler() && rValue == rDefault && !fStoreDefault)
111 {
112 if (pComp->Default(szName)) return;
113 }
114 auto name = pComp->Name(szName);
115 try
116 {
117 // Search named section, set default if not found
118 if (name)
119 {
120 if (fPrefillDefault && pComp->isCompiler()) rValue = rDefault; // default prefill if desired
121 pComp->Value(mkDefaultAdapt(rValue, rDefault));
122 }
123 else
124 rValue = rDefault;
125 }
126 catch (const StdCompiler::Exception &)
127 {
128 name.Abort();
129 throw;
130 }
131 }
132};
133
134template <class T, class D>
135inline StdNamingDefaultAdapt<T, D> mkNamingAdapt(T &&rValue, const char *szName, const D &rDefault, bool fPrefillDefault = false, bool fStoreDefault = false) { return StdNamingDefaultAdapt<T, D>(rValue, szName, rDefault, fPrefillDefault, fStoreDefault); }
136
137// * Decompiling Adaptor
138// Allows to use const objects if the compiler won't change the targets
139template <class T>
140struct StdDecompileAdapt
141{
142 const T &rValue;
143
144 explicit StdDecompileAdapt(const T &rValue) : rValue(rValue) {}
145
146 inline void CompileFunc(StdCompiler *pComp) const
147 {
148 assert(pComp->isDecompiler());
149 pComp->Value(const_cast<T &>(rValue));
150 }
151};
152
153template <class T>
154inline StdDecompileAdapt<T> mkDecompileAdapt(const T &rValue) { return StdDecompileAdapt<T>(rValue); }
155
156// * Runtime value Adaptor
157// Allows the C4ValueSetCompiler to set the value
158template <class T>
159struct StdRuntimeValueAdapt
160{
161 T &rValue;
162
163 explicit StdRuntimeValueAdapt(T &rValue) : rValue(rValue) {}
164
165 inline void CompileFunc(StdCompiler *pComp) const
166 {
167 pComp->setRuntimeWritesAllowed(+1);
168 pComp->Value(rValue);
169 pComp->setRuntimeWritesAllowed(-1);
170 }
171
172 template <class D> inline bool operator==(const D &nValue) const { return rValue == nValue; }
173 template <class D> inline StdRuntimeValueAdapt<T> &operator=(const D &nValue) { rValue = nValue; return *this; }
174};
175
176template <class T>
177inline StdRuntimeValueAdapt<T> mkRuntimeValueAdapt(T &&rValue) { return StdRuntimeValueAdapt<T>(rValue); }
178
179// * String adaptor
180struct StdStringAdapt
181{
182 char *szString; int iMaxLength; StdCompiler::RawCompileType eRawType;
183
184 StdStringAdapt(char *szString, int iMaxLength, StdCompiler::RawCompileType eRawType = StdCompiler::RCT_Escaped)
185 : szString(szString), iMaxLength(iMaxLength), eRawType(eRawType) {}
186
187 inline void CompileFunc(StdCompiler *pComp) const
188 {
189 pComp->String(szString, iMaxLength, eType: eRawType);
190 }
191
192 inline bool operator==(const char *szDefault) const { return SEqual(szStr1: szString, szStr2: szDefault); }
193 inline StdStringAdapt &operator=(const char *szDefault) { SCopy(szSource: szDefault, sTarget: szString, iMaxL: iMaxLength); return *this; }
194};
195
196inline StdStringAdapt mkStringAdapt(char *szString, int iMaxLength, StdCompiler::RawCompileType eRawType = StdCompiler::RCT_Escaped)
197{
198 return StdStringAdapt(szString, iMaxLength, eRawType);
199}
200
201#define mkStringAdaptM(szString) mkStringAdapt(szString, (sizeof(szString) / sizeof(*szString)) - 1)
202#define mkStringAdaptMA(szString) mkStringAdapt(szString, (sizeof(szString) / sizeof(*szString)) - 1, StdCompiler::RCT_All)
203#define mkStringAdaptMI(szString) mkStringAdapt(szString, (sizeof(szString) / sizeof(*szString)) - 1, StdCompiler::RCT_Idtf)
204#define mkStringAdaptMIE(szString) mkStringAdapt(szString, (sizeof(szString) / sizeof(*szString)) - 1, StdCompiler::RCT_IdtfAllowEmpty)
205
206// * std::string adaptor
207struct StdStdStringAdapt
208{
209 std::string &string; StdCompiler::RawCompileType eRawType;
210 StdStdStringAdapt(std::string &string, StdCompiler::RawCompileType eRawType = StdCompiler::RCT_Escaped)
211 : string(string), eRawType(eRawType) { }
212 inline void CompileFunc(StdCompiler *pComp) const
213 {
214 pComp->String(str&: string, type: eRawType);
215 }
216 inline bool operator == (const char *szDefault) const { return string == szDefault; }
217 inline StdStdStringAdapt &operator = (const char *szDefault) { string = szDefault; return *this; }
218};
219inline StdStdStringAdapt mkStringAdapt(std::string &string, StdCompiler::RawCompileType eRawType = StdCompiler::RCT_Escaped)
220{ return StdStdStringAdapt(string, eRawType); }
221inline StdStdStringAdapt mkStringAdaptA(std::string &string)
222{ return StdStdStringAdapt(string, StdCompiler::RCT_All); }
223
224// * Raw adaptor
225struct StdRawAdapt
226{
227 void *pData; size_t iSize; StdCompiler::RawCompileType eRawType;
228
229 StdRawAdapt(void *pData, size_t iSize, StdCompiler::RawCompileType eRawType = StdCompiler::RCT_Escaped)
230 : pData(pData), iSize(iSize), eRawType(eRawType) {}
231
232 inline void CompileFunc(StdCompiler *pComp) const
233 {
234 pComp->Raw(pData, iSize, eType: eRawType);
235 }
236
237 inline bool operator==(const void *pDefault) const { return !memcmp(s1: pDefault, s2: pData, n: iSize); }
238 inline StdRawAdapt &operator=(const void *pDefault) { memcpy(dest: pData, src: pDefault, n: iSize); return *this; }
239};
240
241// * Integer Adaptor
242// Stores Integer-like datatypes (Enumerations)
243template <class T, class int_t = int32_t>
244struct StdIntAdapt
245{
246 T &rValue;
247
248 explicit StdIntAdapt(T &rValue) : rValue(rValue) {}
249
250 inline void CompileFunc(StdCompiler *pComp) const
251 {
252 // Cast
253 int_t iVal = int_t(rValue);
254 pComp->Value(iVal);
255 rValue = T(iVal);
256 }
257
258 // Operators for default checking/setting
259 template <class D> inline bool operator==(const D &nValue) const { return rValue == nValue; }
260 template <class D> inline StdIntAdapt &operator=(const D &nValue) { rValue = nValue; return *this; }
261};
262
263template <class T> inline StdIntAdapt<T> mkIntAdapt(T &rValue) { return StdIntAdapt<T>(rValue); }
264template <class int_t, class T> StdIntAdapt<T, int_t> mkIntAdaptT(T &rValue) { return StdIntAdapt<T, int_t>(rValue); }
265
266// * Casting Adaptor
267// Does a reinterprete_cast
268template <class T, class to_t>
269struct StdCastAdapt
270{
271 T &rValue;
272
273 explicit StdCastAdapt(T &rValue) : rValue(rValue) {}
274
275 inline void CompileFunc(StdCompiler *pComp) const
276 {
277 // Cast
278 assert(sizeof(to_t) == sizeof(T));
279 to_t vVal = *reinterpret_cast<to_t *>(&rValue);
280 pComp->Value(vVal);
281 rValue = *reinterpret_cast<T *>(&vVal);
282 }
283
284 // Operators for default checking/setting
285 template <class D> inline bool operator==(const D &nValue) const { return rValue == nValue; }
286 template <class D> inline StdCastAdapt &operator=(const D &nValue) { rValue = nValue; return *this; }
287};
288
289template <class to_t, class T> StdCastAdapt<T, to_t> mkCastAdapt(T &rValue) { return StdCastAdapt<T, to_t>(rValue); }
290template <class T> StdCastAdapt<T, int32_t> mkCastIntAdapt(T &rValue) { return StdCastAdapt<T, int32_t>(rValue); }
291
292// Helper: Identity function class
293template <class T>
294struct _IdFuncClass
295{
296 T &operator()(T &rValue) const { return rValue; }
297};
298
299// * Array Adaptor
300// Stores a separated list
301template <class T, class M = _IdFuncClass<T>>
302struct StdArrayAdapt
303{
304 StdArrayAdapt(T *pArray, std::size_t size, M map = M())
305 : pArray(pArray), size(size), map(map) {}
306
307 T *pArray; std::size_t size; M map;
308
309 inline void CompileFunc(StdCompiler *pComp) const
310 {
311 for (std::size_t i{0}; i < size; i++)
312 {
313 if (i) pComp->Separator(eSep: StdCompiler::SEP_SEP);
314 pComp->Value(map(pArray[i]));
315 }
316 }
317
318 // Operators for default checking/setting
319 inline bool operator==(const T &rDefault) const
320 {
321 for (std::size_t i{0}; i < size; i++)
322 if (pArray[i] != rDefault)
323 return false;
324 return true;
325 }
326
327 inline StdArrayAdapt &operator=(const T &rDefault)
328 {
329 for (std::size_t i{0}; i < size; i++)
330 pArray[i] = rDefault;
331 return *this;
332 }
333
334 inline bool operator==(const T *pDefaults) const
335 {
336 for (std::size_t i{0}; i < size; i++)
337 if (pArray[i] != pDefaults[i])
338 return false;
339 return true;
340 }
341
342 inline StdArrayAdapt &operator=(const T *pDefaults)
343 {
344 for (std::size_t i{0}; i < size; i++)
345 pArray[i] = pDefaults[i];
346 return *this;
347 }
348};
349
350template <class T>
351inline StdArrayAdapt<T> mkArrayAdaptS(T *array, std::size_t size) { return StdArrayAdapt<T>(array, size); }
352
353template <class T, std::size_t N>
354inline StdArrayAdapt<T> mkArrayAdapt(T (&array)[N]) { return StdArrayAdapt<T>(array, N); }
355
356template <class T, class M>
357inline StdArrayAdapt<T, M> mkArrayAdaptMapS(T *array, std::size_t size, M map) { return StdArrayAdapt<T, M>(array, size, map); }
358
359template <class T, class M, std::size_t N>
360inline StdArrayAdapt<T, M> mkArrayAdaptMap(T (&array)[N], M map) { return mkArrayAdaptMapS<T, M>(array, N, map); }
361
362// * Array Adaptor (defaulting)
363// Stores a separated list, sets defaults if a value or separator is missing.
364template <class T, class D, class M = _IdFuncClass<T>>
365struct StdArrayDefaultAdapt
366{
367 StdArrayDefaultAdapt(T *pArray, size_t iSize, const D &rDefault, const M &map = M())
368 : pArray(pArray), iSize(iSize), rDefault(rDefault), map(map) {}
369
370 T *pArray; size_t iSize; const D &rDefault; const M map;
371
372 inline void CompileFunc(StdCompiler *pComp) const
373 {
374 size_t i, iWrite = iSize;
375 bool fCompiler = pComp->isCompiler();
376 // Decompiling: Omit defaults
377 if (!fCompiler && pComp->hasNaming())
378 while (iWrite > 0 && pArray[iWrite - 1] == rDefault)
379 iWrite--;
380 // Read/write values
381 for (i = 0; i < iWrite; i++)
382 {
383 // Separator?
384 if (i) if (!pComp->Separator(eSep: StdCompiler::SEP_SEP)) break;
385 // Expect a value. Default if not found.
386 pComp->Value(mkDefaultAdapt(map(pArray[i]), rDefault));
387 }
388 // Fill rest of array
389 if (fCompiler)
390 for (; i < iSize; i++)
391 pArray[i] = rDefault;
392 }
393
394 // Additional defaulting (whole array)
395 inline bool operator==(const T *pDefaults) const
396 {
397 for (size_t i = 0; i < iSize; i++)
398 if (pArray[i] != pDefaults[i])
399 return false;
400 return true;
401 }
402
403 inline StdArrayDefaultAdapt &operator=(const T *pDefaults)
404 {
405 for (size_t i = 0; i < iSize; i++)
406 pArray[i] = pDefaults[i];
407 return *this;
408 }
409};
410
411template <class T, class D>
412inline StdArrayDefaultAdapt<T, D> mkArrayAdaptS(T *array, std::size_t size, const D &default_) { return StdArrayDefaultAdapt<T, D>(array, size, default_); }
413
414template <class T, class D, std::size_t N>
415inline StdArrayDefaultAdapt<T, D> mkArrayAdapt(T (&array)[N], const D &default_) { return mkArrayAdaptS<T, D>(array, N, default_); }
416
417template <class T, class D, class M>
418inline StdArrayDefaultAdapt<T, D, M> mkArrayAdaptMapS(T *array, std::size_t size, const D &default_, M map) { return StdArrayDefaultAdapt<T, D, M>(array, size, default_, map); }
419
420template <class T, class D, class M, std::size_t N>
421inline StdArrayDefaultAdapt<T, D, M> mkArrayAdaptMap(T (&array)[N], const D &default_, M map) { return mkArrayAdaptMapS<T, D, M>(array, N, default_, map); }
422
423// * Insertion Adaptor
424// Compile a value before / after another
425template <class T, class I>
426struct StdInsertAdapt
427{
428 StdInsertAdapt(T &rObj, I &rIns, bool fBefore = true)
429 : rObj(rObj), rIns(rIns), fBefore(fBefore) {}
430 T &rObj; I &rIns; bool fBefore;
431 void CompileFunc(StdCompiler *pComp) const
432 {
433 if (fBefore) pComp->Value(rIns);
434 pComp->Value(rObj);
435 if (!fBefore) pComp->Value(rIns);
436 }
437};
438
439template <class T, class I>
440inline StdInsertAdapt<T, I> mkInsertAdapt(T &&rObj, I &&rIns, bool fBefore = true) { return StdInsertAdapt<T, I>(rObj, rIns, fBefore); }
441
442// * Parameter Adaptor
443// Specify a second parameter for the CompileFunc
444template <class T, class P>
445struct StdParameterAdapt
446{
447 StdParameterAdapt(T &rObj, const P &rPar) : rObj(rObj), Par(rPar) {}
448
449 T &rObj; const P Par;
450
451 void CompileFunc(StdCompiler *pComp) const
452 {
453 ::CompileFunc(rObj, pComp, Par);
454 }
455
456 // Operators for default checking/setting
457 template <class D> inline bool operator==(const D &nValue) const { return rObj == nValue; }
458 template <class D> inline StdParameterAdapt &operator=(const D &nValue) { rObj = nValue; return *this; }
459
460 // getting value
461 inline T &GetObj() { return rObj; }
462};
463
464template <class T, class P>
465inline StdParameterAdapt<T, P> mkParAdapt(T &rObj, const P &rPar) { return StdParameterAdapt<T, P>(rObj, rPar); }
466
467// * Parameter Adaptor 2
468// Specify a second and a third parameter for the CompileFunc
469template <class T, class P1, class P2>
470struct StdParameter2Adapt
471{
472 StdParameter2Adapt(T &rObj, const P1 &rPar1, const P2 &rPar2) : rObj(rObj), rPar1(rPar1), rPar2(rPar2) {}
473
474 T &rObj; const P1 &rPar1; const P2 &rPar2;
475
476 void CompileFunc(StdCompiler *pComp) const
477 {
478 ::CompileFunc(rObj, pComp, rPar1, rPar2);
479 }
480
481 // Operators for default checking/setting
482 template <class D> inline bool operator==(const D &nValue) const { return rObj == nValue; }
483 template <class D> inline StdParameter2Adapt &operator=(const D &nValue) { rObj = nValue; return *this; }
484};
485
486template <class T, class P1, class P2>
487inline StdParameter2Adapt<T, P1, P2> mkParAdapt(T &rObj, const P1 &rPar1, const P2 &rPar2) { return StdParameter2Adapt<T, P1, P2>(rObj, rPar1, rPar2); }
488
489// * Store pointer (contents)
490// Defaults to null
491template <class PointerWrapper>
492struct StdPtrAdapt
493{
494 using T = typename PointerWrapper::element_type;
495
496 StdPtrAdapt(PointerWrapper &rpObj, bool fAllowNull = true, const char *szNaming = "Data")
497 : rpObj(rpObj), fAllowNull(fAllowNull), szNaming(szNaming) {}
498
499 PointerWrapper &rpObj; bool fAllowNull; const char *szNaming;
500
501 void CompileFunc(StdCompiler *pComp) const
502 {
503 bool fCompiler = pComp->isCompiler(),
504 fNaming = pComp->hasNaming();
505 std::optional<StdCompiler::NameGuard> name;
506 // Compiling? Clear object before
507 if (fCompiler) { rpObj.reset(); }
508 // Null checks - different with naming support.
509 if (fAllowNull)
510 if (fNaming)
511 {
512 // Null check: just omit when writing
513 if (!fCompiler && !rpObj) return;
514 name.emplace(args: pComp->Name(szName: szNaming));
515 // Set up naming
516 if (!*name) { assert(fCompiler); return; }
517 }
518 else
519 {
520 bool fNull = !!rpObj;
521 pComp->Value(rBool&: fNull);
522 // Null? Nothing further to do
523 if (fNull) return;
524 }
525 else if (!fCompiler)
526 assert(rpObj);
527 // Compile value
528 if (fCompiler)
529 {
530 T *rpnObj;
531 CompileNewFunc(rpnObj, pComp);
532 rpObj.reset(rpnObj);
533 }
534 else
535 pComp->Value(mkDecompileAdapt(*rpObj));
536 }
537
538 // Operators for default checking/setting
539 inline bool operator==(const T &nValue) const { return rpObj && *rpObj == nValue; }
540 inline StdPtrAdapt &operator=(const T &nValue) { rpObj.reset(new T(nValue)); return *this; }
541 inline bool operator==(const T *pValue) const { return rpObj.get() == pValue; }
542 inline StdPtrAdapt &operator=(const T *pValue) { rpObj.reset(pValue); return *this; }
543};
544
545template <typename T>
546void CompileFunc(std::unique_ptr<T> &smartPtr, StdCompiler *comp)
547{
548 comp->Value(StdPtrAdapt{smartPtr, false});
549}
550
551// only for use with StdPlainPtrAdapt
552// does not delete on destruction; only on reset
553template <typename T>
554class PlainPtrRef
555{
556 T *&ptr;
557
558public:
559 using element_type = T;
560
561 explicit PlainPtrRef(T *&ptr) noexcept : ptr{ptr} {}
562
563 T *get() const noexcept { return ptr; }
564 void reset(T *ptr_ = nullptr) { delete ptr; ptr = ptr_; }
565 explicit operator bool() const noexcept { return ptr; }
566
567 T &operator*() const noexcept { return *ptr; }
568};
569
570template <typename T>
571class StdPlainPtrAdapt : public StdPtrAdapt<PlainPtrRef<T>>
572{
573 PlainPtrRef<T> ptr;
574
575public:
576 StdPlainPtrAdapt(T *&rpObj, bool fAllowNull = true, const char *szNaming = "Data")
577 : ptr{rpObj}, StdPtrAdapt<PlainPtrRef<T>>{ptr, fAllowNull, szNaming} {}
578};
579
580template <class T>
581inline StdPlainPtrAdapt<T> mkPtrAdapt(T *&rpObj, bool fAllowNull = true) { return StdPlainPtrAdapt<T>(rpObj, fAllowNull); }
582
583template <class T>
584inline StdPlainPtrAdapt<T> mkNamingPtrAdapt(T *&rpObj, const char *szNaming) { return StdPlainPtrAdapt<T>(rpObj, true, szNaming); }
585
586template <class T>
587inline StdPlainPtrAdapt<T> mkPtrAdaptNoNull(T *&rpObj) { return mkPtrAdapt<T>(rpObj, false); }
588
589// * Adaptor for STL containers
590// Writes a comma-separated list for compilers that support it. Otherwise, the size is calculated and safed.
591// The defaulting uses the standard STL operators (full match)
592template <class C>
593struct StdSTLContainerAdapt
594{
595 StdSTLContainerAdapt(C &rStruct, StdCompiler::Sep eSep = StdCompiler::SEP_SEP)
596 : rStruct(rStruct), eSep(eSep) {}
597
598 C &rStruct; const StdCompiler::Sep eSep;
599
600 template<typename... Parameters>
601 inline void CompileFunc(StdCompiler *pComp, Parameters &&...parameters) const
602 {
603 typedef typename C::value_type T;
604 // Get compiler specs
605 bool fCompiler = pComp->isCompiler();
606 bool fNaming = pComp->hasNaming();
607 // Decompiling?
608 if (!fCompiler)
609 {
610 // Write size (binary only)
611 if (!fNaming)
612 {
613 auto iSize = checked_cast<int32_t>(rStruct.size());
614 pComp->Value(iSize);
615 }
616 auto first = true;
617 // Write all entries
618 for (auto it : rStruct)
619 {
620 if (!first)
621 {
622 pComp->Separator(eSep);
623 }
624 else
625 {
626 first = false;
627 }
628 if constexpr (sizeof...(parameters) > 0)
629 {
630 pComp->Value(mkParAdapt(it, std::forward<Parameters>(parameters)...));
631 }
632 else
633 {
634 pComp->Value(it);
635 }
636 }
637 }
638 else
639 {
640 // Compiling: Empty previous
641 rStruct.clear();
642 // Read size (binary only)
643 uint32_t iSize;
644 if (!fNaming) pComp->Value(rInt&: iSize);
645 // Read new
646 do
647 {
648 // No entries left to read?
649 if (!fNaming && !iSize--)
650 break;
651 // Read entries
652 try
653 {
654 T val;
655 if constexpr (sizeof...(parameters) > 0)
656 {
657 pComp->Value(mkParAdapt(val, std::forward<Parameters>(parameters)...));
658 }
659 else
660 {
661 pComp->Value(val);
662 }
663 rStruct.emplace_back(val);
664 }
665 catch (const StdCompiler::NotFoundException &)
666 {
667 // No value found: Stop reading loop
668 break;
669 }
670 } while (pComp->Separator(eSep));
671 }
672 }
673
674 // Operators for default checking/setting
675 inline bool operator==(const C &nValue) const { return rStruct == nValue; }
676 inline StdSTLContainerAdapt &operator=(const C &nValue) { rStruct = nValue; return *this; }
677};
678
679template <class C>
680inline StdSTLContainerAdapt<C> mkSTLContainerAdapt(C &rTarget, StdCompiler::Sep eSep = StdCompiler::SEP_SEP) { return StdSTLContainerAdapt<C>(rTarget, eSep); }
681
682// * Adaptor for maps following the std::map and std::unordered_map interfaces
683// Writes the size of the map followed by a semicolon separated list of key=value pairs
684template <class Map>
685struct StdSTLMapAdapt
686{
687 Map &map;
688
689 StdSTLMapAdapt(Map &map) : map(map) {}
690
691 void CompileFunc(StdCompiler *pComp) const
692 {
693 auto count = checked_cast<int32_t>(map.size());
694 pComp->Value(count);
695 if (pComp->isCompiler())
696 {
697 map.clear();
698 for (std::int32_t i = 0; i < count; ++i)
699 {
700 pComp->Separator(eSep: StdCompiler::SEP_SEP2);
701 typename Map::key_type key;
702 pComp->Value(key);
703 pComp->Separator(eSep: StdCompiler::SEP_SET);
704 typename Map::mapped_type value;
705 pComp->Value(value);
706 map[key] = value;
707 }
708 }
709 else
710 {
711 for (auto &it : map)
712 {
713 auto key = it.first;
714 pComp->Separator(eSep: StdCompiler::SEP_SEP2);
715 pComp->Value(key);
716 pComp->Separator(eSep: StdCompiler::SEP_SET);
717 pComp->Value(it.second);
718 }
719 }
720 }
721
722 StdSTLMapAdapt<Map> &operator=(const Map &otherMap)
723 {
724 map = otherMap;
725 return *this;
726 }
727
728 bool operator==(const Map &otherMap) const
729 {
730 return map == otherMap;
731 }
732};
733
734template <class Map>
735inline StdSTLMapAdapt<Map> mkSTLMapAdapt(Map &map) { return StdSTLMapAdapt<Map>(map); }
736
737// Write an integer that is supposed to be small most of the time. The adaptor writes it in
738// 7-bit-pieces, bit 8 being a continuation marker: If it's set, more data is following, if not,
739// all following bits are 0.
740// Code lengths for uint32_t:
741// 0x00000000 (0) - 0x0000007F (127) : 1 byte
742// 0x00000080 (128) - 0x00003FFF (16383) : 2 byte
743// 0x00004000 (16384) - 0x001FFFFF (2097151) : 3 byte
744// 0x00200000 (2097152) - 0x0FFFFFFF (268435456) : 4 byte
745// 0x10000000 (268435456) - 0xFFFFFFFF (4294967295) : 5 byte
746// So this sort of packing is always useful when the integer in question is almost impossible to
747// grow bigger than 2,097,151.
748template <class T>
749struct StdIntPackAdapt
750{
751 StdIntPackAdapt(T &rVal) : rVal(rVal) {}
752
753 T &rVal;
754
755 inline T clearUpper(T x) const
756 {
757 const int CLEARBITS = 8 * sizeof(T) - 7;
758 return (x << CLEARBITS) >> CLEARBITS;
759 }
760
761 void CompileFunc(StdCompiler *pComp) const
762 {
763 // simply write for textual compilers
764 if (pComp->hasNaming())
765 {
766 pComp->Value(rVal);
767 return;
768 }
769 T val; uint8_t tmp;
770 // writing?
771 if (!pComp->isCompiler())
772 {
773 val = rVal;
774 for (;;)
775 {
776 tmp = uint8_t(clearUpper(x: val));
777 // last byte?
778 if (clearUpper(x: val) == val)
779 break;
780 // write byte
781 tmp ^= 0x80; pComp->Value(rInt&: tmp);
782 // advance
783 val >>= 7;
784 }
785 // write last byte
786 pComp->Value(rInt&: tmp);
787 }
788 // reading?
789 else
790 {
791 // read first byte
792 pComp->Value(rInt&: tmp);
793 val = clearUpper(x: T(tmp));
794 // read remaining bytes
795 int i = 7; T data = val;
796 while (uint8_t(data) != tmp)
797 {
798 // read byte
799 pComp->Value(rInt&: tmp);
800 // add to value
801 data = clearUpper(x: T(tmp));
802 val = (data << i) | (val & ((1 << i) - 1));
803 // next byte
804 i += 7;
805 }
806 // write
807 rVal = val;
808 }
809 }
810
811 template <class D> inline bool operator==(const D &nValue) const { return rVal == nValue; }
812 template <class D> inline StdIntPackAdapt &operator=(const D &nValue) { rVal = nValue; return *this; }
813};
814
815template <class T>
816StdIntPackAdapt<T> mkIntPackAdapt(T &rVal) { return StdIntPackAdapt<T>(rVal); }
817
818template <class T>
819struct StdEnumEntry
820{
821 const char *Name;
822 T Val;
823};
824
825// Enumeration: For text compilers, write a given name for a value.
826// For everything else, just write an integer of given type.
827template <class T, class int_t = int32_t, size_t N = std::numeric_limits<size_t>::max()>
828struct StdEnumAdapt
829{
830 typedef StdEnumEntry<T> Entry;
831
832 StdEnumAdapt(T &rVal, const Entry *pNames) : rVal(rVal), pNames(pNames) { assert(pNames); }
833 T &rVal; const Entry *pNames;
834
835 void CompileFunc(StdCompiler *pComp) const
836 {
837 // Write as int
838 if (!pComp->isVerbose())
839 {
840 pComp->Value(mkIntAdaptT<int_t>(rVal));
841 return;
842 }
843 // writing?
844 if (!pComp->isCompiler())
845 {
846 // Find value
847 const Entry *pName = pNames;
848 size_t i = 0;
849 for (; i < N && pName->Name; ++pName, ++i)
850 if (pName->Val == rVal)
851 {
852 // Put name
853 pComp->String(pName->Name, strlen(pName->Name), StdCompiler::RCT_Idtf);
854 break;
855 }
856 // No name found?
857 if (i >= N || !pName->Name)
858 // Put value as integer
859 pComp->Value(mkIntAdaptT<int_t>(rVal));
860 }
861 // reading?
862 else
863 {
864 int_t val{};
865 // Try to read as number
866 try
867 {
868 pComp->Value(val);
869 rVal = T(val);
870 }
871 catch (const StdCompiler::NotFoundException &)
872 {
873 // Try to read as string
874 StdStrBuf Name;
875 pComp->Value(rStruct: mkParAdapt(rObj&: Name, rPar: StdCompiler::RCT_Idtf));
876 // Search in name list
877 const Entry *pName = pNames;
878 size_t i = 0;
879 for (; i < N && pName->Name; ++pName, ++i)
880 if (Name == pName->Name)
881 {
882 rVal = pName->Val;
883 break;
884 }
885 // Not found? Warn
886 if (i >= N || !pName->Name)
887 pComp->Warn(fmt: "Unknown bit name: {}", args: Name.getData());
888 }
889 }
890 }
891
892 template <class D> inline bool operator==(const D &nValue) const { return rVal == nValue; }
893 template <class D> inline auto &operator=(const D &nValue) { rVal = nValue; return *this; }
894};
895
896template <class int_t, class T, size_t N>
897auto mkEnumAdaptT(T &rVal, const StdEnumEntry<T> (&pNames)[N]) { return StdEnumAdapt<T, int_t, N>(rVal, pNames); }
898
899template <class T>
900struct StdBitfieldEntry
901{
902 const char *Name;
903 T Val;
904};
905
906// Convert a bitfield into/from something like "foo | bar | baz", where "foo", "bar" and "baz" are given constants.
907template <class T, size_t N>
908struct StdBitfieldAdapt
909{
910 typedef StdBitfieldEntry<T> Entry;
911
912 StdBitfieldAdapt(T &rVal, const Entry *pNames) : rVal(rVal), pNames(pNames) { assert(pNames); }
913 T &rVal; const Entry *pNames;
914
915 void CompileFunc(StdCompiler *pComp) const
916 {
917 // simply write for non-verbose compilers
918 if (!pComp->isVerbose())
919 {
920 pComp->Value(rVal);
921 return;
922 }
923 // writing?
924 if (!pComp->isCompiler())
925 {
926 T val = rVal;
927 // Write until value is comsumed
928 bool fFirst = true;
929 size_t i = 0;
930 for (const Entry *pName = pNames; val && i < N && pName->Name; ++pName, ++i)
931 if ((pName->Val & val) == pName->Val)
932 {
933 // Put "|"
934 if (!fFirst) pComp->Separator(eSep: StdCompiler::SEP_VLINE);
935 // Put name
936 pComp->String(pName->Name, strlen(pName->Name), StdCompiler::RCT_Idtf);
937 fFirst = false;
938 // Remove bits
939 val &= ~pName->Val;
940 }
941 // Anything left is written as number
942 if (val)
943 {
944 // Put "|"
945 if (!fFirst) pComp->Separator(eSep: StdCompiler::SEP_VLINE);
946 // Put value
947 pComp->Value(val);
948 }
949 }
950 // reading?
951 else
952 {
953 T val = 0;
954 // Read
955 do
956 {
957 // Try to read as number
958 try
959 {
960 T tmp;
961 pComp->Value(tmp);
962 val |= tmp;
963 }
964 catch (const StdCompiler::NotFoundException &)
965 {
966 // Try to read as string
967 StdStrBuf Name;
968 pComp->Value(rStruct: mkParAdapt(rObj&: Name, rPar: StdCompiler::RCT_Idtf));
969 // Search in name list
970 const Entry *pName = pNames;
971 size_t i = 0;
972 for (; i < N && pName->Name; ++pName, ++i)
973 if (Name == pName->Name)
974 {
975 val |= pName->Val;
976 break;
977 }
978 // Not found? Warn
979 if (i >= N || !pName->Name)
980 pComp->Warn(fmt: "Unknown bit name: {}", args: Name.getData());
981 }
982 // Expect separation
983 } while (pComp->Separator(eSep: StdCompiler::SEP_VLINE));
984 // Write value back
985 rVal = val;
986 }
987 }
988
989 template <class D> inline bool operator==(const D &nValue) const { return rVal == nValue; }
990 template <class D> inline auto &operator=(const D &nValue) { rVal = nValue; return *this; }
991};
992
993template <class T, size_t N>
994auto mkBitfieldAdapt(T &rVal, const StdBitfieldEntry<T> (&pNames)[N]) { return StdBitfieldAdapt<T, N>(rVal, pNames); }
995
996// * Name count adapter
997// For compilers without name support, this just compiles the given value. Otherwise, the count
998// of given namings is returned on compiling, and nothing is done for decompiling (The caller
999// has to make sure that an appropriate number of namings will be created)
1000template <class int_t>
1001struct StdNamingCountAdapt
1002{
1003 int_t &iCount; const char *szName;
1004 StdNamingCountAdapt(int_t &iCount, const char *szName) : iCount(iCount), szName(szName) {}
1005 inline void CompileFunc(StdCompiler *pComp) const
1006 {
1007 if (pComp->hasNaming())
1008 {
1009 if (pComp->isCompiler())
1010 iCount = pComp->NameCount(szName);
1011 }
1012 else
1013 pComp->Value(mkIntPackAdapt(iCount));
1014 }
1015};
1016
1017template <class int_t>
1018inline StdNamingCountAdapt<int_t> mkNamingCountAdapt(int_t &iCount, const char *szName) { return StdNamingCountAdapt<int_t>(iCount, szName); }
1019
1020// * Hex adapter
1021// Writes raw binary data in hexadecimal
1022class StdHexAdapt
1023{
1024 void *pData; size_t iSize;
1025
1026public:
1027 StdHexAdapt(void *pData, size_t iSize) : pData(pData), iSize(iSize) {}
1028
1029 inline void CompileFunc(StdCompiler *pComp) const
1030 {
1031 if (!pComp->isVerbose())
1032 pComp->Raw(pData, iSize);
1033 char szData[2 + 1]; bool fCompiler = pComp->isCompiler();
1034 for (size_t i = 0; i < iSize; i++)
1035 {
1036 uint8_t *pByte = reinterpret_cast<uint8_t *>(pData) + i;
1037 if (!fCompiler)
1038 {
1039 FormatWithNull(buf&: szData, fmt: "{:02x}", args&: *pByte);
1040 }
1041 pComp->String(szString: szData, iMaxLength: 2, eType: StdCompiler::RCT_Idtf);
1042 if (fCompiler)
1043 {
1044 int b;
1045 if (std::from_chars(first: szData, last: szData + 2, value&: b, base: 16).ec != std::errc{})
1046 pComp->excNotFound(message: i ? "hexadecimal data: bytes missing!" : "hexadecimal data missing!");
1047 *pByte = b;
1048 }
1049 }
1050 }
1051};
1052
1053inline StdHexAdapt mkHexAdapt(void *pData, size_t iSize) { return StdHexAdapt(pData, iSize); }
1054template <class T>
1055inline StdHexAdapt mkHexAdapt(T &rData) { return StdHexAdapt(&rData, sizeof(rData)); }
1056
1057template<typename Rep, typename Period>
1058inline void CompileFunc(std::chrono::duration<Rep, Period> &duration, StdCompiler *comp)
1059{
1060 if (comp->isCompiler())
1061 {
1062 Rep temp;
1063 comp->Value(temp);
1064 duration = std::chrono::duration<Rep, Period>{temp};
1065 }
1066 else
1067 {
1068 Rep temp{duration.count()};
1069 comp->Value(temp);
1070 }
1071}
1072
1073template<typename T> requires std::is_scoped_enum_v<T>
1074inline void CompileFunc(T &value, StdCompiler *const comp)
1075{
1076 auto underlying = std::to_underlying(value);
1077 comp->Value(underlying);
1078 value = static_cast<T>(underlying);
1079}
1080