aboutsummaryrefslogtreecommitdiff
path: root/src/utils.h
diff options
context:
space:
mode:
authorAllusive_ <[email protected]>2023-08-01 09:00:18 +1000
committerGitHub <[email protected]>2023-08-01 09:00:18 +1000
commit0147e7dd165f837662a480938e2083a5ac707586 (patch)
tree3e38624c86c7119703cddb524ac01278491c8a3f /src/utils.h
parentAdd files via upload (diff)
downloadcompfy-0147e7dd165f837662a480938e2083a5ac707586.tar.xz
compfy-0147e7dd165f837662a480938e2083a5ac707586.zip
Add files via upload
Diffstat (limited to 'src/utils.h')
-rw-r--r--src/utils.h294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..6bb8643
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) 2018 Yuxuan Shui <[email protected]>
+#pragma once
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <test.h>
+
+#include "compiler.h"
+#include "types.h"
+
+#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+
+#ifdef __FAST_MATH__
+#warning Use of -ffast-math can cause rendering error or artifacts, \
+ therefore it is not recommended.
+#endif
+
+#ifdef __clang__
+__attribute__((optnone))
+#else
+__attribute__((optimize("-fno-fast-math")))
+#endif
+static inline bool
+safe_isnan(double a) {
+ return __builtin_isnan(a);
+}
+
+/// Same as assert(false), but make sure we abort _even in release builds_.
+/// Silence compiler warning caused by release builds making some code paths reachable.
+#define BUG() \
+ do { \
+ assert(false); \
+ abort(); \
+ } while (0)
+#define CHECK_EXPR(...) ((void)0)
+/// Same as assert, but evaluates the expression even in release builds
+#define CHECK(expr) \
+ do { \
+ auto _ = (expr); \
+ /* make sure the original expression appears in the assertion message */ \
+ assert((CHECK_EXPR(expr), _)); \
+ (void)_; \
+ } while (0)
+
+/// Asserts that var is within [lower, upper]. Silence compiler warning about expressions
+/// being always true or false.
+#define ASSERT_IN_RANGE(var, lower, upper) \
+ do { \
+ auto __tmp attr_unused = (var); \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
+ assert(__tmp >= lower); \
+ assert(__tmp <= upper); \
+ _Pragma("GCC diagnostic pop"); \
+ } while (0)
+
+/// Asserts that var >= lower. Silence compiler warning about expressions
+/// being always true or false.
+#define ASSERT_GEQ(var, lower) \
+ do { \
+ auto __tmp attr_unused = (var); \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
+ assert(__tmp >= lower); \
+ _Pragma("GCC diagnostic pop"); \
+ } while (0)
+
+// Some macros for checked cast
+// Note these macros are not complete, as in, they won't work for every integer types. But
+// they are good enough for our use cases.
+
+#define to_int_checked(val) \
+ ({ \
+ int64_t tmp = (val); \
+ ASSERT_IN_RANGE(tmp, INT_MIN, INT_MAX); \
+ (int)tmp; \
+ })
+
+#define to_char_checked(val) \
+ ({ \
+ int64_t tmp = (val); \
+ ASSERT_IN_RANGE(tmp, CHAR_MIN, CHAR_MAX); \
+ (char)tmp; \
+ })
+
+#define to_u16_checked(val) \
+ ({ \
+ auto tmp = (val); \
+ ASSERT_IN_RANGE(tmp, 0, UINT16_MAX); \
+ (uint16_t) tmp; \
+ })
+
+#define to_i16_checked(val) \
+ ({ \
+ int64_t tmp = (val); \
+ ASSERT_IN_RANGE(tmp, INT16_MIN, INT16_MAX); \
+ (int16_t) tmp; \
+ })
+
+#define to_u32_checked(val) \
+ ({ \
+ auto tmp = (val); \
+ int64_t max attr_unused = UINT32_MAX; /* silence clang tautological \
+ comparison warning*/ \
+ ASSERT_IN_RANGE(tmp, 0, max); \
+ (uint32_t) tmp; \
+ })
+/**
+ * Normalize an int value to a specific range.
+ *
+ * @param i int value to normalize
+ * @param min minimal value
+ * @param max maximum value
+ * @return normalized value
+ */
+static inline int attr_const normalize_i_range(int i, int min, int max) {
+ if (i > max)
+ return max;
+ if (i < min)
+ return min;
+ return i;
+}
+
+/**
+ * Linearly interpolate from a range into another.
+ *
+ * @param a,b first range
+ * @param c,d second range
+ * @param value value to interpolate, should be in range [a,b]
+ * @return interpolated value in range [c,d]
+ */
+static inline int attr_const lerp_range(int a, int b, int c, int d, int value) {
+ ASSERT_IN_RANGE(value, a, b);
+ return (d-c)*(value-a)/(b-a) + c;
+}
+
+#define min2(a, b) ((a) > (b) ? (b) : (a))
+#define max2(a, b) ((a) > (b) ? (a) : (b))
+
+/// clamp `val` into interval [min, max]
+#define clamp(val, min, max) max2(min2(val, max), min)
+
+/**
+ * Normalize a double value to a specific range.
+ *
+ * @param d double value to normalize
+ * @param min minimal value
+ * @param max maximum value
+ * @return normalized value
+ */
+static inline double attr_const normalize_d_range(double d, double min, double max) {
+ if (d > max)
+ return max;
+ if (d < min)
+ return min;
+ return d;
+}
+
+/**
+ * Normalize a double value to 0.\ 0 - 1.\ 0.
+ *
+ * @param d double value to normalize
+ * @return normalized value
+ */
+static inline double attr_const normalize_d(double d) {
+ return normalize_d_range(d, 0.0, 1.0);
+}
+
+/**
+ * Convert a hex RGB string to RGB
+ */
+static inline struct color hex_to_rgb(const char *hex) {
+ struct color rgb;
+ // Ignore the # in front of the string
+ const char *sane_hex = hex + 1;
+ int hex_color = (int)strtol(sane_hex, NULL, 16);
+ rgb.red = (float)(hex_color >> 16) / 256;
+ rgb.green = (float)((hex_color & 0x00ff00) >> 8) / 256;
+ rgb.blue = (float)(hex_color & 0x0000ff) / 256;
+
+ return rgb;
+}
+
+attr_noret void
+report_allocation_failure(const char *func, const char *file, unsigned int line);
+
+/**
+ * @brief Quit if the passed-in pointer is empty.
+ */
+static inline void *
+allocchk_(const char *func_name, const char *file, unsigned int line, void *ptr) {
+ if (unlikely(!ptr)) {
+ report_allocation_failure(func_name, file, line);
+ }
+ return ptr;
+}
+
+/// @brief Wrapper of allocchk_().
+#define allocchk(ptr) allocchk_(__func__, __FILE__, __LINE__, ptr)
+
+/// @brief Wrapper of malloc().
+#define cmalloc(type) ((type *)allocchk(malloc(sizeof(type))))
+
+/// @brief Wrapper of malloc() that takes a size
+#define cvalloc(size) allocchk(malloc(size))
+
+/// @brief Wrapper of calloc().
+#define ccalloc(nmemb, type) \
+ ({ \
+ auto tmp = (nmemb); \
+ ASSERT_GEQ(tmp, 0); \
+ ((type *)allocchk(calloc((size_t)tmp, sizeof(type)))); \
+ })
+
+/// @brief Wrapper of ealloc().
+#define crealloc(ptr, nmemb) \
+ ({ \
+ auto tmp = (nmemb); \
+ ASSERT_GEQ(tmp, 0); \
+ ((__typeof__(ptr))allocchk(realloc((ptr), (size_t)tmp * sizeof(*(ptr))))); \
+ })
+
+/// RC_TYPE generates a reference counted type from `type`
+///
+/// parameters:
+/// name = the generated type will be called `name`_t.
+/// ctor = the constructor of `type`, will be called when
+/// a value of `type` is created. should take one
+/// argument of `type *`.
+/// dtor = the destructor. will be called when all reference
+/// is gone. has same signature as ctor
+/// Q = function qualifier. this is the qualifier that
+/// will be put before generated functions
+//
+/// functions generated:
+/// `name`_new: create a new reference counted object of `type`
+/// `name`_ref: increment the reference counter, return a
+/// reference to the object
+/// `name`_unref: decrement the reference counter. take a `type **`
+/// because it needs to nullify the reference.
+#define RC_TYPE(type, name, ctor, dtor, Q) \
+ typedef struct { \
+ type inner; \
+ int ref_count; \
+ } name##_internal_t; \
+ typedef type name##_t; \
+ Q type *name##_new(void) { \
+ name##_internal_t *ret = cmalloc(name##_internal_t); \
+ ctor((type *)ret); \
+ ret->ref_count = 1; \
+ return (type *)ret; \
+ } \
+ Q type *name##_ref(type *a) { \
+ __auto_type b = (name##_internal_t *)a; \
+ b->ref_count++; \
+ return a; \
+ } \
+ Q void name##_unref(type **a) { \
+ __auto_type b = (name##_internal_t *)*a; \
+ if (!b) \
+ return; \
+ b->ref_count--; \
+ if (!b->ref_count) { \
+ dtor((type *)b); \
+ free(b); \
+ } \
+ *a = NULL; \
+ }
+
+/// Generate prototypes for functions generated by RC_TYPE
+#define RC_TYPE_PROTO(type, name) \
+ typedef type name##_t; \
+ type *name##_new(void); \
+ void name##_ref(type *a); \
+ void name##_unref(type **a);
+
+
+///
+/// Calculates next closest power of two of 32bit integer n
+/// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+///
+int next_power_of_two(int n);
+
+
+// vim: set noet sw=8 ts=8 :