Files
youssefnoob003-SindriKit/include/sindri/common/helpers.h
T
2026-06-22 12:38:04 +01:00

306 lines
8.7 KiB
C

#ifndef SND_COMMON_HELPERS_H
#define SND_COMMON_HELPERS_H
#include <stddef.h>
#include <windows.h>
/**
* @def SND_DEBUG
* @brief Global toggle for debug output.
* Set to 1 to enable logging macros, or 0 to compile them out entirely for
* Release builds.
*/
#ifndef SND_DEBUG
#define SND_DEBUG 0
#endif
/**
* @def SND_USE_PRINTF
* @brief Toggles the debug output destination.
* If 1, debug macros route to standard stdout via `printf`.
* If 0, debug macros route to the Windows kernel debugger via
* `OutputDebugStringA`.
*/
#ifndef SND_USE_PRINTF
#define SND_USE_PRINTF 0
#endif
/**
* @def SND_BEGIN_EXTERN_C
* @brief Opens an `extern "C"` linkage block for C++ compatibility.
*/
/**
* @def SND_END_EXTERN_C
* @brief Closes an `extern "C"` linkage block.
*/
#ifdef __cplusplus
#define SND_BEGIN_EXTERN_C extern "C" {
#define SND_END_EXTERN_C }
#else
#define SND_BEGIN_EXTERN_C
#define SND_END_EXTERN_C
#endif
SND_BEGIN_EXTERN_C
/**
* @def SND_FORCE_INLINE
* @brief Compiler-agnostic macro to force function inlining.
* Useful for embedding bounds checks and offset calculations directly into the
* caller's assembly.
*/
#if defined(_MSC_VER)
#define SND_FORCE_INLINE static __forceinline
#else
#define SND_FORCE_INLINE static inline __attribute__((always_inline))
#endif
#if SND_DEBUG
#include <stdarg.h>
#include <stdio.h>
#if SND_USE_PRINTF
/**
* @def SND_FDEBUG_PRINT
* @brief Formats and prints a debug message to a specific file stream (e.g.,
* stderr, stdout).
*/
#define SND_FDEBUG_PRINT(stream, fmt, ...) \
do { \
FILE *_s = (stream) ? (FILE *)(stream) : stdout; \
fprintf(_s, fmt, ##__VA_ARGS__); \
fflush(_s); \
} while (0)
/**
* @def SND_DEBUG_PRINT
* @brief Formats and prints a debug message to standard output.
*/
#define SND_DEBUG_PRINT(fmt, ...) SND_FDEBUG_PRINT(stdout, fmt, ##__VA_ARGS__)
#else
/**
* @def SND_DEBUG_MAX_LEN
* @brief Maximum string length for the internal debug formatting buffer.
*/
#define SND_DEBUG_MAX_LEN 1024
/**
* @brief Internal variadic wrapper to format strings for the Windows Debugger.
* @note This is completely stripped out of Release builds.
*/
static inline void snd_debug_print_internal(const char *fmt, ...) {
char buf[SND_DEBUG_MAX_LEN];
va_list args;
va_start(args, fmt);
// Safely format the string into our local buffer (requires CRT in debug mode)
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
// Send the formatted string to the Windows kernel debug buffer
OutputDebugStringA(buf);
}
// Map both macros to the new OutputDebugString wrapper
#define SND_FDEBUG_PRINT(stream, fmt, ...) snd_debug_print_internal(fmt, ##__VA_ARGS__)
#define SND_DEBUG_PRINT(fmt, ...) snd_debug_print_internal(fmt, ##__VA_ARGS__)
#endif // SND_USE_PRINTF
#else
// When SND_DEBUG is 0, these macros compile to empty no-ops to strip strings
// from the binary.
#define SND_FDEBUG_PRINT(stream, fmt, ...) \
do { \
(void)(stream); \
} while (0)
#define SND_DEBUG_PRINT(fmt, ...) \
do { \
(void)0; \
} while (0)
#endif // SND_DEBUG
/**
* @def SND_PTR_ADD
* @brief Safely adds a byte offset to a base pointer by casting to a BYTE
* pointer first.
*/
#define SND_PTR_ADD(base, offset) ((BYTE *)(base) + (offset))
/**
* @brief Prints a combined hexadecimal and ASCII view of a byte buffer.
* @param dat Pointer to the first byte to print.
* @param len_dat Number of bytes to dump from @p dat.
* @param base_off Base address used to compute printed relative offsets.
*
* @note Output is gated by `SND_DEBUG` through debug print macros.
*/
void snd_dump_hex(const void *dat, size_t len_dat, ULONG_PTR base_off);
/**
* @brief Checks if a given size and offset are within the bounds of a total
* size.
* @param total_size The total size of the buffer.
* @param offset The offset from the start.
* @param size The size of the region to check.
* @return TRUE if within bounds, FALSE otherwise.
*/
SND_FORCE_INLINE BOOL snd_bounds_check(SIZE_T total_size, SIZE_T offset, SIZE_T size) {
if (offset > total_size)
return FALSE;
if (size > total_size - offset)
return FALSE;
return TRUE;
}
/**
* @brief Checks if a pointer and size are within the bounds of a base pointer
* and total size.
* @param base The base pointer.
* @param total_size The total size of the base buffer.
* @param ptr The target pointer to check.
* @param size The size of the region at the target pointer.
* @return TRUE if within bounds, FALSE otherwise.
*/
SND_FORCE_INLINE BOOL snd_ptr_bounds_check(const void *base, SIZE_T total_size, const void *ptr, SIZE_T size) {
if (base == NULL || ptr == NULL)
return FALSE;
ULONG_PTR ubase = (ULONG_PTR)base;
ULONG_PTR utarget = (ULONG_PTR)ptr;
if (utarget < ubase)
return FALSE;
SIZE_T offset = (SIZE_T)(utarget - ubase);
return snd_bounds_check(total_size, offset, size);
}
/**
* @brief Bounded, CRT-independent memory zeroing routine.
* Replaces memset.
*/
SND_FORCE_INLINE void snd_memzero(void *dest, size_t size) {
if (!dest)
return;
volatile BYTE *p = (volatile BYTE *)dest;
for (size_t i = 0; i < size; i++) {
p[i] = 0;
}
}
/**
* @brief Raw byte copy routine.
* Replaces memcpy.
*/
SND_FORCE_INLINE void snd_memcpy(void *dest, const void *src, size_t count) {
if (!dest || !src)
return;
BYTE *d = (BYTE *)dest;
const BYTE *s = (const BYTE *)src;
for (size_t i = 0; i < count; i++) {
d[i] = s[i];
}
}
/**
* @brief Bounded, CRT-independent string length evaluation.
* Replaces strnlen.
*/
SND_FORCE_INLINE size_t snd_strnlen(const char *str, size_t max_len) {
if (!str)
return 0;
size_t len = 0;
while (len < max_len && str[len] != '\0') {
len++;
}
return len;
}
/**
* @brief Bounded, truncation-safe string copying.
* Replaces strncpy / strncpy_s.
*/
SND_FORCE_INLINE void snd_strncpy(char *dest, size_t dest_size, const char *src, size_t max_src_len) {
if (!dest || dest_size == 0 || !src)
return;
size_t i = 0;
while (i < (dest_size - 1) && i < max_src_len && src[i] != '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
/**
* @brief Bounded, CRT-independent string concatenation.
* Replaces strcat / strcat_s.
*/
static inline void snd_strncat(char *dest, SIZE_T dest_size, const char *src, SIZE_T max_src_len) {
if (!dest || dest_size == 0 || !src)
return;
SIZE_T dest_len = 0;
while (dest_len < dest_size && dest[dest_len] != '\0') {
dest_len++;
}
if (dest_len >= dest_size - 1) {
dest[dest_size - 1] = '\0';
return;
}
SIZE_T i = 0;
while (i < max_src_len && src[i] != '\0' && (dest_len + i) < (dest_size - 1)) {
dest[dest_len + i] = src[i];
i++;
}
dest[dest_len + i] = '\0';
}
/**
* @brief Bounded, CRT-independent character search.
* Replaces strchr.
*/
static inline const char *snd_strnchr(const char *str, char c, SIZE_T max_len) {
if (!str)
return NULL;
for (SIZE_T i = 0; i < max_len; i++) {
if (str[i] == c) {
return &str[i];
}
if (str[i] == '\0') {
break;
}
}
return NULL;
}
/**
* @brief Bounded, CRT-independent string comparison.
* Replaces strcmp / strncmp.
*/
static inline int snd_strncmp(const char *s1, const char *s2, SIZE_T max_len) {
if (!s1 || !s2)
return (s1 == s2) ? 0 : ((s1 < s2) ? -1 : 1);
for (SIZE_T i = 0; i < max_len; i++) {
if (s1[i] != s2[i]) {
return (int)((unsigned char)s1[i] - (unsigned char)s2[i]);
}
if (s1[i] == '\0') {
return 0;
}
}
return 0;
}
SND_END_EXTERN_C
#endif // SND_COMMON_HELPERS_H