You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

492 lines
13 KiB
C

// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file xpt.h
*
* Cross platform types and definitions.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Defines fixed-size integer types and other general utility types and
* other macros that are ought to be available and equivalent across any
* supported platform.
*
* This header keeps its dependencies at a bare minimum, and **MUST NOT**
* excessively pollute the namespace.
*
* Including this header makes symbols from `stddef.h` and `stdint.h` visible.
*/
#ifndef DF_XPT_H_
#define DF_XPT_H_
#include <stddef.h>
#include <stdint.h>
// Extern function declarations for inline functions on header
/**
* \def EXTERNC
*
* Expands to `extern` or `extern "C"` depending on whether or not the compiler
* supports C++.
*
* This is useful to declare `extern` C functions inside inline functions
* code within .h files, in order to avoid a full fledged `#include`.
*/
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC extern
#endif
// Boolean and fixed size types
typedef int Boolean; ///< A boolean value, legal values are `TRUE` or `FALSE`
typedef int ToggleState; ///< Switch toggle state, legal values are `ON` or `OFF`
typedef int Judgement; ///< Operation result outcome, legal values are `OK` or `NG`
/// Boolean values (`true`, `false`).
enum BooleanLogic {
FALSE = 0, ///< `false` boolean value, guaranteed to be 0.
TRUE = 1 ///< `true` boolean value, guaranteed to be 1.
};
/// Switch toggle states (`on`, `off`).
enum BooleanSwitch {
OFF = 0, ///< `off` value, guaranteed to be 0.
ON = 1 ///< `on` value, guaranteed to be 1.
};
/// Operation success states (success, failure).
enum BooleanJudgement {
OK = 0, ///< Success state, guaranteed to be 0 (no errors).
NG = -1 ///< Failure state, negative value, equals to -1 (no-good).
};
typedef char Boolean8; ///< Packed boolean type, may hold the same values as `Boolean`
typedef int8_t Sint8; ///< Fixed-size signed 8-bits integer type
typedef uint8_t Uint8; ///< Fixed-size unsigned 8-bits integer type
typedef int16_t Sint16; ///< Fixed-size signed 16-bits integer type
typedef uint16_t Uint16; ///< Fixed-size unsigned 16-bits integer type
typedef int32_t Sint32; ///< Fixed-size signed 32-bits integer type
typedef uint32_t Uint32; ///< Fixed-size unsigned 32-bits integer type
typedef int64_t Sint64; ///< Fixed-size signed 64-bits integer type
typedef uint64_t Uint64; ///< Fixed-size unsigned 64-bits integer type
typedef intptr_t Sintptr; ///< Fixed-size signed type large enough to store a pointer
typedef uintptr_t Uintptr; ///< Fixed-size unsigned type large enough to store a pointer
// Fixed size compile time constants
#define S8_C(c) INT8_C(c) ///< Expands constant `c` to a signed 8 bit integer literal
#define U8_C(c) UINT8_C(c) ///< Expands constant `c` to an unsigned 8 bit integer literal
#define S16_C(c) INT16_C(c) ///< Expands constant `c` to a signed 16 bit integer literal
#define U16_C(c) UINT16_C(c) ///< Expands constant `c` to an unsigned 16 bit integer literal
#define S32_C(c) INT32_C(c) ///< Expands constant `c` to a signed 32 bit integer literal
#define U32_C(c) UINT32_C(c) ///< Expands constant `c` to an unsigned 32 bit integer literal
#define S64_C(c) INT64_C(c) ///< Expands constant `c` to a signed 64 bit integer literal
#define U64_C(c) UINT64_C(c) ///< Expands constant `c` to an unsigned 64 bit integer literal
// Compiler support for thread-locals, over/under-alignment,
// inline and no-return functions
// NOTE: we don't support TinyCC because there's no TLS support there
#ifdef __TINYC__
#error "Sorry, no TLS support available on Tiny C Compiler"
#endif
/**
* \def THREAD_LOCAL
*
* \brief Declares a static variable as thread local.
*
* \def ALIGNED(a, what)
*
* \brief Force type, variable, or member alignment.
*
* \param a Required alignment, must be a positive power of two and a compile-time integer literal.
* \param what What should be aligned, a variable, type or member
*
* \def NORETURN
*
* \brief Declare a function shall never return to its caller (like `exit()`).
*
* \warning Behavior is undefined if function does return.
*
* \def UNREACHABLE
*
* \brief Mark code as unreachable.
*
* \warning Behavior is undefined if code is actually reached during execution.
*
* \def FORCE_INLINE
*
* \brief Require function inlining, regardless of compiler policy.
*
* \note Function may be inlined even in debug builds.
*
* \def INLINE
*
* \brief Suggest a function should be inlined, compiler may still choose not to.
*
* \def NOINLINE
*
* \brief Force the compiler **not** to inline a function,
* regardless of its own policy.
*/
#ifdef _MSC_VER
#define THREAD_LOCAL __declspec(thread)
#else
#define THREAD_LOCAL __thread
#endif
#ifdef _MSC_VER
#define ALIGNED(a, what) __declspec(align(a)) what
#else
#define ALIGNED(a, what) what __attribute__((__aligned__(a)))
#endif
#ifdef _MSC_VER
#define NORETURN __declspec(noreturn)
#else
#define NORETURN __attribute__((__noreturn__))
#endif
#ifdef _MSC_VER
#define UNREACHABLE __assume(0)
#elif defined(__GNUC__)
#define UNREACHABLE __builtin_unreachable()
#else
#define UNREACHABLE ((void) 0) /*NOTREACHED*/
#endif
#ifdef _MSC_VER
#define FORCE_INLINE __forceinline
#define INLINE __inline
#define NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#define FORCE_INLINE static __inline__ __attribute__((__always_inline__, __unused__))
#define INLINE static __inline__ __attribute__((__unused__))
#define NOINLINE __attribute__((__noinline__))
#else
// this is a crude fallback and actually loses quite a bit of the intended semantics...
#if 0
#error "No FORCE_INLINE and NOINLINE for this platform"
#endif
#define FORCE_INLINE static inline
#define INLINE static inline
#define NOINLINE
#endif
// Compiler checks
/**
* \def CHECK_PRINTF
*
* \brief Compiler check on `printf()`-like function arguments.
*
* If compiler supports it, check at compile-time that `printf()` and `vprintf()`
* like formatted message arguments are valid.
*
* \param f `const char *` message template argument index (1 is first argument)
* \param a Variadic arguments start (`...` argument index), use 0 if function takes a `va_list` (1 is first argument)
*
* \def STATIC_ASSERT
*
* \brief Compile time assertion.
*
* If compiler supports it, test a condition at compile time, aborting
* compilation on failure.
*
* \param what Expression to be evaluated at compile time
* \param msg Failure message
*/
#ifdef __GNUC__
#define CHECK_PRINTF(f, a) __attribute__((__format__(__printf__, f, a)))
#else
#define CHECK_PRINTF(f, a)
#endif
#ifndef __cplusplus
#define STATIC_ASSERT(what, msg) _Static_assert(what, msg)
#else
#define STATIC_ASSERT(what, msg) static_assert(what, msg)
#endif
// NOTE: if we ever need packing just use the MSVC style #pragma pack(push, N)
// + #pragma pack(pop), it's pretty much universal.
// Platform suitable memory alignment type
/**
* \def ALIGNMENT
*
* \brief Platform memory alignment required for primitive types.
*
* \note This alignment is only safe for primitive and trivial struct types,
* but may be insufficient for SIMD types or overaligned types.
*/
#ifdef _MSC_VER
#ifdef _WIN32
#define ALIGNMENT 4
#else
#define ALIGNMENT 8
#endif
#elif defined(__GNUC__)
#define ALIGNMENT __BIGGEST_ALIGNMENT__
#else
// conservative guess
#define ALIGNMENT 16
#endif
/**
* \brief Perform or test alignment of size `x` or pointer `p` to `align` bytes.
*
* \param x Unsigned integral size in bytes
* \param p A pointer
* \param align Unsigned integral power of two specifying alignment
*
* \warning `align` must be a power of 2, macro arguments are evaluated more
* than once.
*
* @{
* \def ALIGN
* \def ALIGN_DOWN
* \def ALIGN_PTR
* \def ALIGN_PTR_DOWN
*
* \def IS_ALIGNED
* \def IS_PTR_ALIGNED
* @}
*/
#define ALIGN(x, align) \
(((x) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN(x, align) \
ALIGN((x) - ((align) - 1), align)
#define ALIGN_PTR(p, align) \
((void *) ALIGN((uintptr_t) (p), align))
#define ALIGN_PTR_DOWN(p, align) \
((void *) ALIGN_DOWN((uintptr_t) (p), align))
#define IS_ALIGNED(x, align) (((x) & ((align) - 1)) == 0)
#define IS_PTR_ALIGNED(p, align) IS_ALIGNED((Uintptr) p, align)
/**
* \def alloca
*
* \brief Dynamic memory allocation on stack.
*
* \param size Size to be allocated, in bytes, unsigned integral value
*
* \warning Allocating large chunks of memory on stack is a sure one-way ticket
* to a stack overflow, there is no way to indicate memory allocation
* failures for `alloca()`.
*/
#ifdef _MSC_VER
#define alloca(size) _alloca(size)
#else
#define alloca(size) __builtin_alloca(size)
#endif
/**
* \def ARRAY_SIZE
*
* \brief Array element count.
*
* \param array Array to be sized
*/
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif
/**
* \def BIT
*
* \brief Return unsigned integer constant with a single bit set.
*
* \param idx Bit index to be set (0 is LSB)
*/
#ifndef BIT
#define BIT(idx) (1uLL << (idx))
#endif
/**
* \def USED
*
* Suppress unused variable warnings for `x`.
*
* \param x Identifier to mark as used
*/
#ifndef USED
#define USED(x) ((void) (x))
#endif
/**
* \def EDN_LE
*
* \brief Constant identifying little-endian byte ordering (LSB first).
*
* \def EDN_BE
*
* \brief Constant identifying big-endian byte ordering (MSB first).
*
* \def EDN_NATIVE
*
* \brief Constant identifying native byte ordering,
* equals either `EDN_BE` or `EDN_LE`.
*/
#ifdef _WIN32
#define EDN_LE 0
#define EDN_BE 1
#define EDN_NATIVE EDN_LE
#else
#define EDN_LE __ORDER_LITTLE_ENDIAN__
#define EDN_BE __ORDER_BIG_ENDIAN__
#define EDN_NATIVE __BYTE_ORDER__
#endif
#if EDN_NATIVE != EDN_LE && EDN_NATIVE != EDN_BE
#error "Unsupported platform endianness"
#endif
/**
* \brief Swaps bytes inside 16, 32 and 64 bits unsigned integers.
*
* Truncates `x` to the required amount of bytes, if necessary,
* and reverses each byte around. The value is then returned.
* If `x` is a compile-time constant the result is also a constant.
*
* \param x Unsigned integer value to be byte-swapped
*
* \warning `x` is evaluated multiple times.
*
* @{
* \def BSWAP16
* \def BSWAP32
* \def BSWAP64
* @}
*/
#ifndef BSWAP16
#define BSWAP16(x) ((Uint16) ( \
(((x) & 0xff00u) >> 8) | (((x) & 0x00ffu) << 8) \
))
#endif
#ifndef BSWAP32
#define BSWAP32(x) ((Uint32) ( \
(((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
(((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24) \
))
#endif
#ifndef BSWAP64
#define BSWAP64(x) ((Uint64) ( \
(((x) & 0xff00000000000000uLL) >> 56) | \
(((x) & 0x00ff000000000000uLL) >> 40) | \
(((x) & 0x0000ff0000000000uLL) >> 24) | \
(((x) & 0x000000ff00000000uLL) >> 8) | \
(((x) & 0x00000000ff000000uLL) << 8) | \
(((x) & 0x0000000000ff0000uLL) << 24) | \
(((x) & 0x000000000000ff00uLL) << 40) | \
(((x) & 0x00000000000000ffuLL) << 56) \
))
#endif
/**
* \brief Declare a fixed-size big or little endian constant.
*
* Following macros may be used to declare fixed size
* constant of a specific endianness at compile time.
* Original value is swapped if destination endianness
* is not match the host endianness. These macros are usable for
* variables as well, but functions declared in `sys/endian.h` should
* be preferred whenever possible (i.e. use these macros on variables
* only inside headers to reduce `#include`s).
*
* \param x Constant or variable, which is truncated to the required size.
*
* \return `x` as a constant or variable byte-swapped as needed.
*
* \warning `x` is evaluated multiple times.
*
* @{
* \def BE16
* \def BE32
* \def BE64
* \def LE16
* \def LE32
* \def LE64
* @}
*/
#if EDN_NATIVE == EDN_LE
#define BE16(x) BSWAP16(x)
#define BE32(x) BSWAP32(x)
#define BE64(x) BSWAP64(x)
#define LE16(x) ((Uint16) (x))
#define LE32(x) ((Uint32) (x))
#define LE64(x) ((Uint64) (x))
#else
#define BE16(x) ((Uint16) (x))
#define BE32(x) ((Uint32) (x))
#define BE64(x) ((Uint64) (x))
#define LE16(x) BSWAP16(x)
#define LE32(x) BSWAP32(x)
#define LE64(x) BSWAP64(x)
#endif
/**
* \brief Minimum, maximum and clamped to range values.
*
* \param x, y Generic comparable values
* \param a, b Clamping range, inclusive, `a` must be less than or equal to `b`
*
* \warning Arguments are evaluated more than once.
*
* @{
* \def MIN
* \def MAX
* \def CLAMP
* @}
*/
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x, y) (((x) < (y)) ? (y) : (x))
#endif
#ifndef CLAMP
#define CLAMP(x, a, b) MAX(a, MIN(x, b))
#endif
/**
* \def ABS
*
* \brief Absolute value of `x` using ternary `?:` operator.
*
* \param x An expression comparable with 0
*
* \warning `x` is evaluated more than once.
*/
#ifndef ABS
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#endif
/**
* \def FLEX_ARRAY
*
* \brief Clarity macro to convey that a trailing array member inside
* a `struct` is an array of arbitrary size.
*/
#ifdef __cplusplus
#define FLEX_ARRAY 1
#elif defined(_MSC_VER) || defined(__GNUC__)
#define FLEX_ARRAY
#else
#define FLEX_ARRAY 1
#endif
#endif