Files
picom/src/utils.h
Yuxuan Shui 96fcc6089e c2: silence a compiler warning
GCC warns about a use after free, because a pointer is used in pointer
comparison after it was freed. This particular case is completely safe,
but GCC warns anyway.

Move the free after the comparison requires me duplicating the free a
couple of times, so instead I made use of the cleanup attribute to auto
free the pointer when it goes out of scope.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2022-07-16 15:05:03 +01:00

307 lines
13 KiB
C

// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#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 <time.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;
}
#define min2(a, b) ((a) > (b) ? (b) : (a))
#define max2(a, b) ((a) > (b) ? (a) : (b))
#define min3(a, b, c) min2(a, min2(b, c))
/// 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);
static inline void free_charpp(char **str) {
if (str) {
free(*str);
*str = NULL;
}
}
/// An allocated char* that is automatically freed when it goes out of scope.
#define scoped_charp char *cleanup(free_charpp)
///
/// 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);
// Some versions of the Android libc do not have timespec_get(), use
// clock_gettime() instead.
#ifdef __ANDROID__
#ifndef TIME_UTC
#define TIME_UTC 1
#endif
static inline int timespec_get(struct timespec *ts, int base) {
assert(base == TIME_UTC);
return clock_gettime(CLOCK_REALTIME, ts);
}
#endif
// vim: set noet sw=8 ts=8 :