libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
util.h
Go to the documentation of this file.
1
9#pragma once
10
11
12#include <promeki/config.h>
13#if PROMEKI_ENABLE_CORE
14#include <stdexcept>
15#include <cstdint>
16#include <limits>
17#include <type_traits>
18#include <utility>
19#include <promeki/array.h>
20#include <promeki/namespace.h>
21#include <promeki/platform.h>
22#include <promeki/error.h>
23
24PROMEKI_NAMESPACE_BEGIN
25
26class StringList;
27
28// Runtime time assert
29#define PROMEKI_ASSERT(x) \
30 if (!(x)) { \
31 promekiErr("Assertion failed: " PROMEKI_STRINGIFY(x)); \
32 promekiLogStackTrace(Logger::Err); \
33 promekiLogSync(); \
34 throw std::runtime_error(__FILE__ \
35 ":" PROMEKI_STRINGIFY(__LINE__) " Assertion failed: " PROMEKI_STRINGIFY(x)); \
36 }
37
38// Compile time assert
39#define PROMEKI_STATIC_ASSERT(x) \
40 static_assert(x, __LINE__ ":" PROMEKI_STRINGIFY(__LINE__) " Assertion failed: " PROMEKI_STRINGIFY(x));
41
42// Macro string conversion and concatination
43#define PROMEKI_STRINGIFY_ARGS(...) #__VA_ARGS__
44#define PROMEKI_STRINGIFY_IMPL(value) #value
45#define PROMEKI_STRINGIFY(value) PROMEKI_STRINGIFY_IMPL(value)
46#define PROMEKI_CONCAT_IMPL(v1, v2) v1##v2
47#define PROMEKI_CONCAT(v1, v2) PROMEKI_CONCAT_IMPL(v1, v2)
48
49// Returns a unique ID that can be used to make unique macro items
50#define PROMEKI_UNIQUE_ID PROMEKI_CONCAT(__LINE__, __COUNTER__)
51
52// Returns the number of elements in a statically defined simple array.
53#define PROMEKI_ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
54
55// Marks a heap pointer as "intentionally leaked" so leak detectors
56// (AddressSanitizer / LeakSanitizer) stop reporting it. Use at every
57// allocation site whose contents legitimately live for the process
58// lifetime — registered Definitions, StringLiteralData caches, etc.
59//
60// Under sanitizer builds this delegates to __lsan_ignore_object() (a
61// one-line registry entry, no runtime cost at the allocation site
62// beyond the call). In all other builds it evaluates its argument and
63// discards the result, so the macro is safe to wrap any expression.
64// Valgrind does not offer a runtime "intentional leak" API — ship a
65// suppression file (tools/valgrind.supp) for that tool instead.
66#if defined(__has_feature)
67#if __has_feature(address_sanitizer)
68#define PROMEKI_ADDRESS_SANITIZER_ENABLED 1
69#endif
70#endif
71#if defined(__SANITIZE_ADDRESS__)
72#define PROMEKI_ADDRESS_SANITIZER_ENABLED 1
73#endif
74#if defined(PROMEKI_ADDRESS_SANITIZER_ENABLED) && __has_include(<sanitizer/lsan_interface.h>)
75#include <sanitizer/lsan_interface.h>
76#define PROMEKI_INTENTIONAL_LEAK(ptr) __lsan_ignore_object(ptr)
77#else
78#define PROMEKI_INTENTIONAL_LEAK(ptr) ((void)(ptr))
79#endif
80
81// Some useful alignment macros
82#define PROMEKI_ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
83
84// Use the PROMEKI_PRINTF_FUNC to hint to the compiler a particular variadic function
85// uses printf() symantics. This will allow the compiler to check that the variable
86// arguments match up with the format.
87// The m argument should be the index that contains the format
88// The n argument should be the index of the start of the variadic
89// Index starts at 1
90#if defined(PROMEKI_COMPILER_GCC_COMPAT)
91#define PROMEKI_PRINTF_FUNC(m, n) __attribute__((format(printf, m, n)))
92#elif defined(PROMEKI_COMPILER_MSVC)
93#define PROMEKI_PRINTF_FUNC(m, n) _Printf_format_string_
94#else
95#define PROMEKI_PRINTF_FUNC(m, n)
96#endif
97
98// If you'd like the compiler to implement the structure as
99// a packed data structure (i.e. no platform alignment)
100// surround the declaration with PROMEKI_PACKED_STRUCT_BEGIN
101// and PROMEKI_PACKED_STRUCT_END
102//
103// Ex:
104//
105// PROMEKI_PACKED_STRUCT_BEGIN
106// struct MyStruct {
107// int8_t value1;
108// int32_t value2;
109// int16_t value3;
110// }
111// PROMEKI_PACKED_STRUCT_END
112//
113// You can validate the packed-ness of the structure as it should
114// now show a sizeof(MyStruct) == to the exact size of all the data
115// in the structure.
116#if defined(PROMEKI_COMPILER_GCC_COMPAT)
117#define PROMEKI_PACKED_STRUCT_BEGIN
118#define PROMEKI_PACKED_STRUCT_END __attribute__((packed))
119#elif defined(PROMEKI_COMPILER_MSVC)
120#define PROMEKI_PACKED_STRUCT_BEGIN __pragma(pack(push, 1))
121#define PROMEKI_PACKED_STRUCT_END __pragma(pack(pop))
122#else
123#define PROMEKI_PACKED_STRUCT_BEGIN
124#define PROMEKI_PACKED_STRUCT_END
125#endif
126
127StringList promekiStackTrace(bool demangle = true);
128
129template <typename OutputType, typename InputType>
130OutputType promekiConvert(const InputType &input, Error *err = nullptr) {
131 static_assert(std::is_integral<InputType>::value || std::is_floating_point<InputType>::value,
132 "InputType must be an integer or floating point type");
133 static_assert(std::is_integral<OutputType>::value || std::is_floating_point<OutputType>::value,
134 "OutputType must be an integer or floating point type");
135 // For mixed signed/unsigned integer comparisons, the operands
136 // would otherwise undergo C++'s usual arithmetic conversions and
137 // produce a false "out of range" verdict (e.g. a uint64_t input
138 // of 1 compared against int64_t::lowest() gets the negative
139 // bound silently widened to a huge unsigned value, so 1 looks
140 // smaller). std::cmp_less / std::cmp_greater are the C++20
141 // sign-correct comparison primitives that route around this.
142 // bool is excluded from this path because std::cmp_* is
143 // constrained to "standard integer" types and rejects bool with
144 // a static_assert. bool is also excluded from the generic
145 // comparison path because comparing a bool against
146 // numeric_limits<int64_t>::max() trips -Wbool-compare; a bool
147 // input always fits in any numeric output, and a bool output's
148 // numeric_limits range is meaningless for clamping (the final
149 // static_cast<bool> handles the truthy conversion).
150 if constexpr (std::is_same_v<InputType, bool> || std::is_same_v<OutputType, bool>) {
151 // No range check needed.
152 } else if constexpr (std::is_integral_v<InputType> && std::is_integral_v<OutputType>) {
153 if (std::cmp_greater(input, std::numeric_limits<OutputType>::max()) ||
154 std::cmp_less(input, std::numeric_limits<OutputType>::lowest())) {
155 if (err != nullptr) *err = Error::Invalid;
156 return OutputType();
157 }
158 } else {
159 if (input > std::numeric_limits<OutputType>::max() ||
160 input < std::numeric_limits<OutputType>::lowest()) {
161 if (err != nullptr) *err = Error::Invalid;
162 return OutputType();
163 }
164 }
165 if (err != nullptr) *err = Error::Ok;
166 return static_cast<OutputType>(input);
167}
168
169template <typename T> inline T promekiLerp(const T &a, const T &b, const double &t) {
170 return a + t * (b - a);
171}
172
173template <typename T> T promekiCatmullRom(const Array<T, 4> &points, T t) {
174 T t2 = t * t;
175 T t3 = t * t2;
176 T c1 = -0.5 * points[0] + 1.5 * points[1] - 1.5 * points[2] + 0.5 * points[3];
177 T c2 = points[0] - 2.5 * points[1] + 2 * points[2] - 0.5 * points[3];
178 T c3 = -0.5 * points[0] + 0.5 * points[2];
179 T c4 = points[1];
180 return c1 * t3 + c2 * t2 + c3 * t + c4;
181}
182
183template <typename T> T promekiBezier(const Array<T, 4> &points, T t) {
184 T u = 1 - t;
185 T t2 = t * t;
186 T u2 = u * u;
187 T t3 = t2 * t;
188 T u3 = u2 * u;
189 T b0 = u3;
190 T b1 = 3 * u2 * t;
191 T b2 = 3 * u * t2;
192 T b3 = t3;
193 return b0 * points[0] + b1 * points[1] + b2 * points[2] + b3 * points[3];
194}
195
196template <typename T> T promekiBicubic(const Array<Array<T, 4>, 4> &points, T x, T y) {
197 Array<T, 4> arr;
198 for (int i = 0; i < 4; ++i) {
199 Array<T, 4> row;
200 for (int j = 0; j < 4; ++j) {
201 row[j] = points[i][j];
202 }
203 arr[i] = cubic_lerp(row, y);
204 }
205 return cubic_lerp(arr, x);
206}
207
208template <typename T> T promekiCubic(const Array<T, 4> &points, T t) {
209 T a = points[3] - points[2] - points[0] + points[1];
210 T b = points[0] - points[1] - a;
211 T c = points[2] - points[0];
212 T d = points[1];
213 return a * t * t * t + b * t * t + c * t + d;
214}
215
216PROMEKI_NAMESPACE_END
217
218#endif // PROMEKI_ENABLE_CORE