-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.hpp
567 lines (509 loc) · 18.3 KB
/
utils.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
#ifndef UTILS_HPP
#define UTILS_HPP
#include <algorithm>
#include <functional>
#include <vector>
#include <fstream>
#include <cctype>
#include <typeinfo>
#include <type_traits>
#include <sstream>
#include <memory>
#include <chrono>
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <cxxabi.h>
#if __cplusplus < 201703L
#error A C++17 compiler is required!
#endif
#endif
#include "Exceptions.hpp"
namespace std {
/** \brief Trim whitespace from the start of the given string (in-place).
*
* \param s
* A reference to the string to perform the operation.
*/
[[maybe_unused]] static inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](int c) {return !std::isspace(c);}));
}
/** \brief Trim whitespace from the end of the given string (in-place).
*
* \param s
* A reference to the string to perform the operation.
*/
[[maybe_unused]] static inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
[](int c) {return !std::isspace(c);}).base(), s.end());
}
/** \brief Trim whitespace from both start and end of the given string (in-place).
*
* \param s
* A reference to the string to perform the operation.
*/
[[maybe_unused]] static inline void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
/** \brief Transform the string contents to uppercase (within the current locale) (in-place).
*
* \param str
* A reference to the string to perform the operation.
*/
[[maybe_unused]] static inline void strToUpper(string &str) {
std::transform(str.begin(), str.end(), str.begin(),
[](std::string::value_type ch) {
return std::use_facet<std::ctype<std::string::value_type>>(std::locale()).toupper(ch);
}
);
}
/** \brief Transform the string contents to uppercase (within the current locale) (copying).
*
* \param str
* A copy of the string to perform the operation.
*/
[[maybe_unused]] static inline std::string strToUppercase(string str) {
std::strToUpper(str);
return str;
}
/** \brief Replace all consecutive occurrences of the given char within the given string (in-place).
*
* \param str
* A reference to the string to replace the character.
* \param ch
* The characters to replace.
*/
[[maybe_unused]] static inline void strReplaceConsecutive(string &str, const char ch) {
str.erase(std::unique(str.begin(), str.end(),
[&](const char lhs, const char rhs) {
return (lhs == ch) && (lhs == rhs);
}
), str.end());
}
/** \brief Replace all occurrences of from with to in the given std::string str.
*
* \param str
* A reference to the string to replace a substring.
* \param from
* A reference to a string to replace.
* \param to
* A reference to a string to replace with.
*/
[[maybe_unused]] static inline void strReplaceAll(string &str, const string& from, const string& to) {
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
/** \brief Returns the internal actual class name of the given object o.
*
* **Uses __abi::__cxa_demangle__ which is part of <cxxabi.h> included in all GCC compilers.**
*
* If GCC is not used, type2name will revert to typeid(o).name() instead.
*
* \tparam T
* The type of object to get the name demangled from.
* \param o
* The object to demangle the name from.
* \return
* Returns the class name of o.
*/
template <class T>
[[maybe_unused]] static const string type2name(T const& o) {
#ifdef _CXXABI_H
char *demang = abi::__cxa_demangle(typeid(o).name(), nullptr, nullptr, nullptr);
string s(demang);
std::free(demang);
#else
string s(typeid(o).name());
#endif
std::strReplaceAll(s, "std::", ""); // Remove std:: from output
std::strReplaceAll(s, "dc::" , ""); // Remove dc:: from output
return s;
}
/**
* \brief Format the given args into the format string.
*
* \tparam ...Type
* Variable argument list of params to expand in the format string.
* \param format
* The format string to expand.
* \param args
* The args tro fill in.
* \return
* Returns the format expanded with the args.
*/
template<typename ... Type>
[[maybe_unused]] static std::string string_format(const std::string& format, Type ...args) {
const size_t size = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
}
namespace util {
/**
* Chrono::time_point alias.
*/
using timepoint_t = std::chrono::time_point<std::chrono::steady_clock>;
/**
* @brief Return a timepoint at the current time.
*/
[[maybe_unused]] static inline timepoint_t TimerStart(void) {
return std::chrono::steady_clock::now();
}
/**
* @brief Return the time in ns that elepsed from start.
*/
[[maybe_unused]]
static inline int64_t TimerDuration_ns(const timepoint_t& start) {
const timepoint_t end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
}
/**
* @brief Return the time in ms that elepsed from start.
*/
[[maybe_unused]] static inline double TimerDuration_ms(const timepoint_t& start) {
return double(util::TimerDuration_ns(start)) / 1.0e6;
}
/**
* @brief Return the time in s that elepsed from start.
*/
[[maybe_unused]] static inline double TimerDuration_s(const timepoint_t& start) {
return double(util::TimerDuration_ns(start)) / 1.0e9;
}
/**
* \brief Find First Set
* This function identifies the least significant index or position of the
* bits set to one in the word.
*
* \param value
* Value to find least significant index
* \retval bitIndex
* Index of least significat bit at one
*/
[[maybe_unused]] static inline uint8_t ffs(uint32_t value ) {
#ifdef _MSC_VER
return uint8_t(32 - __lzcnt(value));
#else
return uint8_t(32 - __builtin_clz(value));
#endif
}
/**
* \brief Determine the amount of bits needed to represent the given int16_t.
*
* \param value
* The int16_t value to check.
* \return
* Returns the amount of bits required to represent the value if reshifted to 16 bits.
*/
[[maybe_unused]] static inline uint8_t bits_needed(int16_t value) {
uint8_t bits = 1;
// 1. Mask value with amount of current bits : (value & ((1 << bits) - 1))
// 2. Shift left to size of 16 bits : << (16 - bits)
// 3. Cast to int16_t
// 4. Shift back to original size
// 5. Check if value represented with <bits> bits and re-shifted to 16 bits
// equals the starting value.
// 6. Repeat until a minimal amount of bits has been found to represent the int16_t.
// (between 1 and 16)
while ((int16_t((value & ((1 << bits) - 1)) << (16 - bits)) >> (16 - bits)) != value) {
bits++;
}
return bits;
}
/**
* @brief Round the given bits to the next byte.
*
* @param bits
* The amount of bits to round to a byte.
* @return Returns the next byte if bits has 1-7 surplus bits
* or the current byte if no surplus bits.
*/
[[maybe_unused]] static inline size_t round_to_byte(size_t bits) {
return (bits + (8u - (bits % 8u)) % 8u) / 8u;
}
/**
* \brief Return the size in bits of the given type.
*/
template<class T>
constexpr inline size_t size_of(void) {
return sizeof(T) * 8u;
}
template<class T>
[[maybe_unused]] static inline T shift_signed(size_t value, size_t src_bits) {
const size_t bit_length = util::size_of<T>() - src_bits;
return T(value << bit_length) >> bit_length;
}
/**
* \brief Cast enum type to underlining data type.
* \param e
* The enum value to cast.
*/
template <typename E>
constexpr inline auto to_underlying(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
/** \brief
* Convert the given char* to a variable of type T.
* Use this method instead of the raw C functions: atoi, atof, atol, atoll.
*
* \tparam T
* The type of object to cast to.
* \param buffer
* The character buffer to convert.
* \return
* Returns a variable of type T with the value as given in buffer.
*/
template <class T>
[[maybe_unused]] static inline T lexical_cast(const char* buffer) {
T out;
std::stringstream cast;
if (std::strToUppercase(std::string(buffer)).substr(0, 2) == "0X")
cast << std::hex << buffer;
else cast << buffer;
if (!(cast >> out))
throw Exceptions::CastingException(buffer, std::type2name(out));
return out;
}
/** \brief Read the given file and return a pointer to a string containing its contents.
*
* \param filename
* The (path and) name of the file to read.
*
* \return Returns a string pointer.
*
* \exception FileReadException
* Throws FileReadException if the file could not be read properly.
*/
[[maybe_unused]] static inline const std::string* readStringFromFile(const std::string &filename) {
std::string *str = new std::string();
std::fstream file(filename);
try {
file.seekg(0, std::ios::end);
str->reserve(size_t(file.tellg()));
file.seekg(0, std::ios::beg);
str->assign((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
} catch (...) {
delete str;
file.close();
throw Exceptions::FileReadException(filename);
}
file.close();
return str;
}
/**
* \brief Read the given file and return a pointer to a list containing its contents in binary data (raw chars).
*
* \param filename
* The (path and) name of the file to read.
*
* \return std::vector<char>*
* A vector with buffer->size() bytes containing a program in raw binary.
* Print with cast to (unsigned char) for proper viewing.
*
* \exception FileReadException
* Throws FileReadException if the file could not be read properly.
*/
[[maybe_unused]] static inline std::vector<uint8_t>* readBinaryFile(const std::string &filename) {
std::ifstream file(filename, std::ifstream::binary | std::ifstream::ate);
if (!file.good()) {
file.close();
throw Exceptions::FileReadException(filename);
}
std::vector<uint8_t> *v_buff = new std::vector<uint8_t>();
try {
// Filepointer is already at end due to ::ate option, so tellg() gives filesize
v_buff->reserve(size_t(file.tellg()));
file.seekg(0, std::ios::beg);
v_buff->assign((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
} catch (...) {
delete v_buff;
file.close();
throw Exceptions::FileReadException(filename);
}
file.close();
return v_buff;
}
/**
* \brief Write the given char buffer to the given file.
*
* \param filename
* The (path and) name of the file to write to (will be created if it does not exist).
* \param buffer
* The char buffer to write to a file.
* \param length
* The length of the given char buffer.
*
* \exception FileWriteException
* Throws FileWriteException if the file could not be written properly.
*/
[[maybe_unused]] static inline void writeBinaryFile(const std::string &filename, const uint8_t* buffer, size_t length) {
std::ofstream file(filename, std::ofstream::binary);
try {
file.write(reinterpret_cast<const char*>(buffer), std::streamsize(length));
} catch (...) {
file.close();
throw Exceptions::FileWriteException(filename);
}
file.close();
}
/*
* Overloaded methods to allocate an array of T of size x, y, z.
*/
/** \brief Allocate an object of type T on the heap using `new T()`.
* Any argument will be passed down to the ctor of T.
*
* \tparam T
* The type of object to allocate.
* \param Type... args
* Variable argument list passed down to ctor of T.
* \return
* A pointer to the newly allocated object.
*/
template <class T, class ... Type>
[[maybe_unused]] static inline T* allocVar(Type ... args) {
return new T(args...);
}
/** \brief Deallocate an object of type T that was allocated using SysUtils::allocVar<T>().
*
* \tparam T
* The type of object to deallocate.
* \param *v
* A pointer to the object to deallocate.
*/
template <class T>
[[maybe_unused]] static inline void deallocVar(T* v) {
delete v;
}
/** \brief Allocate an array of objects of type T and length x on the heap using `new T[x]()`.
*
* \tparam T
* The type of object to allocate.
* \param x
* The length of the array in the first dimension.
* \return
* A pointer to the newly allocated object.
*/
template <class T>
[[maybe_unused]] static inline T* allocArray(size_t x) {
return new T[x]();
}
/** \brief Deallocate an array of type T that was allocated using SysUtils::allocArray<T>(size_t).
*
* \tparam T
* The type of object to deallocate.
* \param *a
* A pointer to the object to deallocate.
*/
template <class T>
[[maybe_unused]] static inline void deallocArray(T* a) {
delete[] a;
}
/** \brief Reallocate the given array to a new array with different size.
* Elements will be copied to the new array.
*
* \tparam T
* The type of object to allocate.
* \param *&a
* A reference to a pointer to the object to reallocate.
* \param &old_size
* The old length of the array in the first dimension, by reference.
* old_size will contain the new length after reallocation.
* \param new_size
* The new length of the array in the first dimension.
*/
template <class T>
[[maybe_unused]] static inline void reallocArray(T*& a, size_t& old_size, size_t new_size) {
T* new_array = util::allocArray<uint8_t>(new_size);
std::copy_n(a, std::min(old_size, new_size), new_array);
util::deallocArray(a);
a = new_array;
old_size = new_size;
}
/** \brief Allocate y arrays of objects of type T and length x on the heap.
*
* \tparam T
* The type of object to allocate.
* \param x
* The length of the array in the first dimension.
* \param y
* The length of the array in the second dimension.
* \return
* A pointer to the newly allocated object.
*/
template <class T>
[[maybe_unused]] static inline T** allocArray(size_t x, size_t y) {
T **arr = new T*[x];
for(size_t i = 0; i < x; i++) arr[i] = util::allocArray<T>(y);
return arr;
}
/** \brief Deallocate an array of type T that was allocated using SysUtils::allocArray<T>(size_t, size_t).
*
* \tparam T
* The type of object to deallocate.
* \param **a
* A pointer to the object to deallocate.
* \param y
* The length of the array in the second dimension.
*/
template <class T>
[[maybe_unused]] static inline void deallocArray(T** a, size_t y) {
for(size_t i = 0; i < y; i++) util::deallocArray(a[i]);
util::deallocArray(a);
}
/** \brief Allocate z arrays of y arrays of objects of type T and length x on the heap.
*
* \tparam T
* The type of object to allocate.
* \param x
* The length of the array in the first dimension.
* \param y
* The length of the array in the second dimension.
* \param z
* The length of the array in the third dimension.
* \return
* A pointer to the newly allocated object.
*/
template <class T>
[[maybe_unused]] static inline T*** allocArray(size_t x, size_t y, size_t z) {
T ***arr = new T**[x];
for(size_t i = 0; i < x; i++) arr[i] = util::allocArray<T>(y, z);
return arr;
}
/** \brief Deallocate an array of type T that was allocated using SysUtils::allocArray<T>(size_t, size_t, size_t).
*
* \tparam T
* The type of object to deallocate.
* \param ***a
* A pointer to the object to deallocate.
* \param y
* The length of the array in the second dimension.
* \param z
* The length of the array in the third dimension.
*/
template <class T>
[[maybe_unused]] static inline void deallocArray(T*** a, size_t y, size_t z) {
for(size_t i = 0; i < z; i++) util::deallocArray(a[i], y);
util::deallocArray(a);
}
/** \brief Deallocate a vector containing pointers to type T,
* that was allocated using SysUtils::allocVar<T>() and then filled by push_back(SysUtils::allocVar<T>()).
*
* \tparam T
* The type of pointer to an object inside the std::vector to deallocate.
* \param *v
* A pointer to the object to deallocate.
*/
template <class T>
[[maybe_unused]] static inline void deallocVector(std::vector<T*> *v) {
for (T *i : *v) util::deallocVar(i);
util::deallocVar(v);
}
}
#endif // UTILS_HPP