#ifndef RT_VOYAGE_H #define RT_VOYAGE_H /* basic types and macros */ #include #include #include #include #ifdef __cplusplus extern "C" { #endif #if defined(_MSC_VER) && !defined(RT_STATIC_LIB) #define RT_DLLEXPORT __declspec(dllexport) #define RT_DLLIMPORT __declspec(dllimport) #else #define RT_DLLEXPORT #define RT_DLLIMPORT #endif #if defined(_MSC_VER) #define RT_INLINE __forceinline #elif defined(__GNUC__) || defined(__clang__) #define RT_INLINE inline __attribute__((always_inline)) #endif #define RT_UNUSED(x) ((void)sizeof((x))) #define RT_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0])) #define RT_RESTRICT_VALUE_TO_BOUNDS(v, lower, upper) \ (((v) < (lower)) ? (lower) : (((v) > (upper)) ? (upper) : (v))) #define RT_MIN(a, b) (((a) < (b))?(a):(b)) #define RT_MAX(a, b) (((a) > (b))?(a):(b)) #define RT_KB(n) ((n)*1024U) #define RT_MB(n) ((n)*1024U * 1024U) #define RT_GB(n) ((n)*1024U * 1024U * 1024U) #ifndef __cplusplus #if defined(_MSC_VER) /* For some reason _Thread_local does not work with vs2022, * despite MS documentation claiming it should. */ #define RT_THREAD_LOCAL __declspec(thread) #elif defined(__GNUC__) || defined(__clang__) /* _Thread_local in C11-C17, thread_local in C23 */ #if __STDC_VERSION__ > 201710L #define RT_THREAD_LOCAL thread_local #elif __STDC_VERSION__ >= 201112L #define RT_THREAD_LOCAL _Thread_local #else #error Versions older than C11 not supported. #endif #endif #else // C++ 11 supports thread_local #if __cplusplus >= 201103L #define RT_THREAD_LOCAL thread_local #elif defined(_MSC_VER) #define RT_THREAD_LOCAL __declspec(thread) #else #error Versions older than C++11 not supported. #endif #endif /* Matches windows MAX_PATH */ #define RT_PATH_MAX 260 typedef unsigned int rt_result; /* Default result codes */ enum { RT_SUCCESS = 0, RT_OUT_OF_MEMORY = 1, RT_INVALID_VALUE = 2, RT_CUSTOM_ERROR_START, RT_UNKNOWN_ERROR = (rt_result)INT_MAX, }; typedef struct { const char *start; unsigned int length; } rt_text_span; /* snprintf replacement. * Always returns a zero terminated string. */ RT_DLLEXPORT int rtSPrint(char *dest, size_t n, const char *fmt, ...); RT_DLLEXPORT int rtVSPrint(char *dest, size_t n, const char *fmt, va_list ap); /* Returns results like strcmp(): * - If the first differing character is smaller in span than in cmp: < 0 * - If span and cmp are equal: 0 * - If the first differing character is greater in span than in cmp: > 0 * - If span.length is smaller than strlen(cmp): -1 * - If span.length is greater than strlen(cmp): 1 */ RT_DLLEXPORT int rtCompareSpanToString(rt_text_span span, const char *cmp); RT_DLLEXPORT void rtReportError(const char *subsystem, const char *fmt, ...); RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...); #ifndef NDEBUG #ifdef _MSC_VER #define RT_DEBUGBREAK __debugbreak() #elif defined(__clang__) && __has_builtin(__bultin_debugtrap) #define RT_DEBUGBREAK __builtin_debugtrap() #elif defined(__GNUC__) #define RT_DEBUGBREAK __builtin_trap() #endif RT_DLLEXPORT int rtAssertHandler(const char *expr, const char *msg, const char *file, int line); #define RT_ASSERT(x, msg) \ do { \ if (!(x)) { \ if (rtAssertHandler(#x, (msg), __FILE__, __LINE__) == 0) { \ RT_DEBUGBREAK; \ } \ } \ } while (0) #define RT_ASSERT_ALWAYS_EVAL(x, msg) RT_ASSERT(x, msg) // Asserts if p is "false", evaluates to p // NOTE that this will evaluate p multiple times! #define RT_VERIFY(p) \ ((!(p)) ? (rtAssertHandler(#p, "Verify failed", __FILE__, __LINE__), p) : p) #else #define RT_ASSERT(x, msg) RT_UNUSED(x) #define RT_ASSERT_ALWAYS_EVAL(x, msg) (x) #define RT_VERIFY(p) (p) #endif /* Makes it easier to search for unimplemented functions */ #define RT_NOT_IMPLEMENTED RT_ASSERT_ALWAYS_EVAL(0, "Not implemented.") enum { RT_INVALID_UNICODE = RT_CUSTOM_ERROR_START, RT_INSUFFICIENT_BUFFER, }; /* Returns RT_SUCCESS if the string was successfully converted, * RT_INVALID_UNICODE if invalid unicode characters were encountered or * RT_INSUFFICIENT_BUFFER if the provided output buffer is too small */ RT_DLLEXPORT rt_result rtUTF8ToWStr(const char *utf8, wchar_t *wstr, size_t len); /* Returns RT_SUCCESS if the string was successfully converted, * RT_INVALID_UNICODE if invalid unicode characters were encountered or * RT_INSUFFICIENT_BUFFER if the provided output buffer is too small */ RT_DLLEXPORT rt_result rtWStrToUTF8(const wchar_t *wstr, char *utf8, size_t len); /* Relative pointer */ typedef struct { int32_t off; } rt_relptr; static RT_INLINE void *rtResolveRelptr(rt_relptr *ptr) { if (ptr->off != 0) { char *p = (char *)ptr; return (void *)(p + (ptrdiff_t)ptr->off); } else { return NULL; } } static RT_INLINE const void *rtResolveConstRelptr(const rt_relptr *ptr) { if (ptr->off != 0) { const char *p = (const char *)ptr; return (const void *)(p + (ptrdiff_t)ptr->off); } else { return NULL; } } static RT_INLINE void rtSetRelptr(rt_relptr *ptr, void *target) { if (target) { char *p = (char *)ptr, *t = (char *)target; ptrdiff_t d = t - p; ptr->off = (int32_t)d; } else { ptr->off = 0; } } /* Bitfiddling functions */ /* Portable solution, On x64 using clzl is probably faster. */ static RT_INLINE uint32_t rtNextPowerOfTwo32(uint32_t v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } /* Runtime init. Initializes basic systems. * You need to call this, even if you build a CLI only app. */ RT_DLLEXPORT rt_result rtInitRuntime(void); RT_DLLEXPORT void rtShutdownRuntime(void); #ifdef __cplusplus } #endif #endif