ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
common.h
Go to the documentation of this file.
1
27#pragma once
28
29/* Note: string.h must come before any platform includes
30 * that might use memcpy (used in unaligned access helpers below). */
31#include <string.h> // For memcpy in unaligned access helpers
32
33// DLL export/import macros (must be included first to avoid circular dependencies)
34#include "platform/api.h" // IWYU pragma: keep
35
36/* Feature test macros for POSIX functions */
37#include <stdbool.h>
38#include <stddef.h>
39#include <stdint.h>
40#include <stdlib.h> // For malloc/free in ALLOC_* macros
41
43#define ASCII_CHAT_APP_NAME "ascii-chat"
44
45#if (defined(__clang__) || defined(__GNUC__)) && !defined(__builtin_c23_va_start)
46#define __builtin_c23_va_start(ap, param) __builtin_va_start(ap, param)
47#endif
48
49#if (defined(__clang__) || defined(__GNUC__)) && !defined(__builtin_c23_va_end)
50#define __builtin_c23_va_end(ap) __builtin_va_end(ap)
51#endif
52
53// This fixes clangd errors about missing types. I DID include stdint.h, but
54// it's not enough.
55#ifndef UINT8_MAX
56typedef unsigned char uint8_t;
57typedef unsigned short uint16_t;
58typedef unsigned int uint32_t;
59typedef unsigned long long uint64_t;
60#endif
61
62/* ============================================================================
63 * Platform Maximum Path Length
64 * ============================================================================
65 * Defined here (before logging.h) to avoid circular dependencies.
66 * Also defined in platform/system.h for documentation purposes.
67 */
68#ifdef _WIN32
69#include <limits.h>
70#ifndef PATH_MAX
71#define PATH_MAX 260
72#endif
73// Windows extended-length path maximum (not the legacy 260 MAX_PATH)
74#define PLATFORM_MAX_PATH_LENGTH 32767
75#elif defined(__linux__)
76#include <limits.h>
77#ifndef PATH_MAX
78#define PLATFORM_MAX_PATH_LENGTH 4096
79#else
80#define PLATFORM_MAX_PATH_LENGTH PATH_MAX
81#endif
82#elif defined(__APPLE__)
83#include <sys/syslimits.h>
84#ifndef PATH_MAX
85#define PLATFORM_MAX_PATH_LENGTH 1024
86#else
87#define PLATFORM_MAX_PATH_LENGTH PATH_MAX
88#endif
89#else
90// Fallback for unknown platforms
91#define PLATFORM_MAX_PATH_LENGTH 4096
92#endif
93
94// INFO: flightaware/dump1090 dev made this after my pr: https://github.com/flightaware/dump1090/pull/282
95#if ((defined(__clang__) && __clang_major__ >= 21) || (defined(__GNUC__) && __GNUC__ >= 10))
96#define NONSTRING __attribute__((nonstring))
97#else
98#define NONSTRING /* nothing */
99#endif
100
101/* ============================================================================
102 * Include organized sub-headers for specific domains
103 * ============================================================================ */
104
105#include "common/error_codes.h" // Error codes and error_string function
106#include "common/protocol_constants.h" // Protocol version, features, compression, frames
107#include "common/limits.h" // MAX_CLIENTS, FPS limits, display name length
108#include "common/buffer_sizes.h" // Standard buffer size constants
109#include "common/log_rates.h" // Logging rate limit constants
110#include "common/shutdown.h" // Shutdown detection system
111#include "common/string_constants.h" // String literal constants (STR_TRUE, STR_FALSE, etc.)
112
113#ifdef __cplusplus
114extern "C" {
115#endif
116
117/* Forward declaration for asciichat_fatal_with_context - now after asciichat_error_t is defined */
118void asciichat_fatal_with_context(asciichat_error_t code, const char *file, int line, const char *function,
119 const char *format, ...);
120
121#ifdef __cplusplus
122}
123#endif
124
125/* ============================================================================
126 * Fatal Error Macros - Exit with Error Message and Stack Trace
127 * ============================================================================
128 * These macros provide a convenient way to exit the program with a detailed
129 * error message. In debug builds, they also print a stack trace.
130 *
131 * Usage in src/ code:
132 * FATAL(ERROR_WEBCAM, "Custom msg: %d", val); // Error code + custom message
133 */
134
135/* Include platform system header for platform_print_backtrace */
136#include "platform/system.h" // IWYU pragma: keep
137
155#ifdef NDEBUG
156#define FATAL(code, ...) asciichat_fatal_with_context(code, NULL, 0, NULL, ##__VA_ARGS__)
157#else
158#define FATAL(code, ...) asciichat_fatal_with_context(code, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
159#endif
160
161/* ============================================================================
162 * Utility Macros
163 * ============================================================================ */
164
165/* Common utility macros */
167#ifndef MIN
168#define MIN(a, b) ((a) < (b) ? (a) : (b))
169#endif
170
172#ifndef MAX
173#define MAX(a, b) ((a) > (b) ? (a) : (b))
174#endif
175
177#ifndef ARRAY_SIZE
178#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
179#endif
180
181/* ============================================================================
182 * Burst and Throttle Macro
183 * ============================================================================
184 *
185 * @brief Rate-limit code execution with a burst window followed by throttle
186 *
187 * Allows code to execute frequently for an initial burst period, then refuses
188 * to execute again until a throttle period has elapsed since first invocation.
189 * The timer resets when called after the throttle period expires.
190 *
191 * Uses time_get_ns() from util/time.h for high-resolution monotonic timing.
192 *
193 * Example (backtrace every 500ms, then silence for 10 seconds):
194 * RUN_BURST_AND_THROTTLE(500 * NS_PER_MS_INT, 10 * NS_PER_SEC_INT, {
195 * platform_print_backtrace(1);
196 * });
197 *
198 * Time values in nanoseconds (use NS_PER_MS_INT, NS_PER_SEC_INT from util/time.h):
199 * - 500 milliseconds = 500 * NS_PER_MS_INT
200 * - 10 seconds = 10 * NS_PER_SEC_INT
201 *
202 * @param burst_ns Allow execution for this many nanoseconds
203 * @param throttle_ns Refuse execution until this many nanoseconds pass
204 * @param code_block Code to execute when throttle conditions permit (use braces: { ... })
205 */
206#define RUN_BURST_AND_THROTTLE(burst_ns, throttle_ns, code_block) \
207 do { \
208 static uint64_t burst_start = 0; \
209 uint64_t now = time_get_ns(); \
210 \
211 /* First call or timer reset */ \
212 if (burst_start == 0) { \
213 burst_start = now; \
214 code_block; \
215 } else { \
216 uint64_t elapsed_ns = now - burst_start; \
217 \
218 if (elapsed_ns < (uint64_t)(burst_ns)) { \
219 /* Within burst window */ \
220 code_block; \
221 } else if (elapsed_ns >= (uint64_t)(throttle_ns)) { \
222 /* Throttle period complete, reset and execute */ \
223 burst_start = now; \
224 code_block; \
225 } \
226 /* else: in throttle period, do nothing */ \
227 } \
228 } while (0)
229
230/* ============================================================================
231 * Memory Allocation with mimalloc Support
232 * ============================================================================
233 * When USE_MIMALLOC is enabled:
234 * - MI_OVERRIDE=ON (glibc): malloc/free automatically redirected to mimalloc
235 * - MI_OVERRIDE=OFF (musl): must explicitly use mi_malloc/mi_free
236 */
237
238#ifdef USE_MIMALLOC
239#include <mimalloc.h>
240#define ALLOC_MALLOC(size) mi_malloc(size)
241#define ALLOC_CALLOC(count, size) mi_calloc((count), (size))
242#define ALLOC_REALLOC(ptr, size) mi_realloc((ptr), (size))
243#define ALLOC_FREE(ptr) mi_free(ptr)
244#elif defined(DEBUG_MEMORY) && !defined(NDEBUG)
245#include "debug/memory.h"
246#define ALLOC_MALLOC(size) debug_malloc(size, __FILE__, __LINE__)
247#define ALLOC_CALLOC(count, size) debug_calloc((count), (size), __FILE__, __LINE__)
248#define ALLOC_REALLOC(ptr, size) debug_realloc((ptr), (size), __FILE__, __LINE__)
249#define ALLOC_FREE(ptr) debug_free(ptr, __FILE__, __LINE__)
250#else
251#define ALLOC_MALLOC(size) malloc(size)
252#define ALLOC_CALLOC(count, size) calloc((count), (size))
253#define ALLOC_REALLOC(ptr, size) realloc((ptr), (size))
254#define ALLOC_FREE(ptr) free(ptr)
255#endif
256
263/* Safe memory allocation with error checking - returns allocated pointer */
264#define SAFE_MALLOC(size, cast) \
265 ({ \
266 cast _ptr = (cast)ALLOC_MALLOC(size); \
267 if (!_ptr) { \
268 FATAL(ERROR_MEMORY, "Memory allocation failed: %zu bytes", (size_t)(size)); \
269 } \
270 _ptr; \
271 })
272
273/* Safe zero-initialized memory allocation */
274#define SAFE_CALLOC(count, size, cast) \
275 ({ \
276 cast _ptr = (cast)ALLOC_CALLOC((count), (size)); \
277 if (!_ptr) { \
278 FATAL(ERROR_MEMORY, "Memory allocation failed: %zu elements x %zu bytes", (size_t)(count), (size_t)(size)); \
279 } \
280 _ptr; \
281 })
282
283/* Safe memory reallocation */
284#define SAFE_REALLOC(ptr, size, cast) \
285 ({ \
286 void *tmp_ptr = ALLOC_REALLOC((ptr), (size)); \
287 if (!tmp_ptr) { \
288 FATAL(ERROR_MEMORY, "Memory reallocation failed: %zu bytes", (size_t)(size)); \
289 } \
290 (cast)(tmp_ptr); \
291 })
292
293/* Helper macro to track aligned allocations when DEBUG_MEMORY is enabled */
294#if defined(DEBUG_MEMORY) && !defined(MI_MALLOC_OVERRIDE)
295#ifdef NDEBUG
296/* Production build: don't include file/line info */
297#define TRACK_ALIGNED_ALLOC(ptr, size, file, line) debug_track_aligned((void *)(ptr), (size_t)(size), NULL, 0)
298#else
299/* Debug build: include file/line info */
300#define TRACK_ALIGNED_ALLOC(ptr, size, file, line) debug_track_aligned((void *)(ptr), (size_t)(size), (file), (line))
301#endif
302#else
303#define TRACK_ALIGNED_ALLOC(ptr, size, file, line) ((void)0)
304#endif
305
306/* SIMD-aligned memory allocation macros for optimal NEON/AVX performance */
307#ifdef USE_MIMALLOC
308/* Use mimalloc's aligned allocation (works on all platforms) */
309#define SAFE_MALLOC_ALIGNED(size, alignment, cast) \
310 ({ \
311 cast _ptr = (cast)mi_malloc_aligned((size), (alignment)); \
312 if (!_ptr) { \
313 FATAL(ERROR_MEMORY, "Aligned memory allocation failed: %zu bytes, %zu alignment", (size_t)(size), \
314 (size_t)(alignment)); \
315 } \
316 TRACK_ALIGNED_ALLOC(_ptr, (size), __FILE__, __LINE__); \
317 _ptr; \
318 })
319#else
320/* Fall back to platform-specific aligned allocation when mimalloc is disabled */
321#ifdef _WIN32
322/* Windows uses _aligned_malloc() for aligned allocation */
323#include <malloc.h>
324#define SAFE_MALLOC_ALIGNED(size, alignment, cast) \
325 ({ \
326 cast _ptr = (cast)_aligned_malloc((size), (alignment)); \
327 if (!_ptr) { \
328 FATAL(ERROR_MEMORY, "Aligned memory allocation failed: %zu bytes, %zu alignment", (size_t)(size), \
329 (size_t)(alignment)); \
330 } \
331 TRACK_ALIGNED_ALLOC(_ptr, (size), __FILE__, __LINE__); \
332 _ptr; \
333 })
334#elif defined(__APPLE__)
335/* macOS uses posix_memalign() for aligned allocation */
336#define SAFE_MALLOC_ALIGNED(size, alignment, cast) \
337 ({ \
338 cast _ptr; \
339 int result = posix_memalign((void **)&_ptr, (alignment), (size)); \
340 if (result != 0 || !_ptr) { \
341 FATAL(ERROR_MEMORY, "Aligned memory allocation failed: %zu bytes, %zu alignment", (size_t)(size), \
342 (size_t)(alignment)); \
343 } \
344 TRACK_ALIGNED_ALLOC(_ptr, (size), __FILE__, __LINE__); \
345 _ptr; \
346 })
347#else
348/* Linux/other platforms use aligned_alloc() (C11) */
349#define SAFE_MALLOC_ALIGNED(size, alignment, cast) \
350 ({ \
351 size_t aligned_size = (((size) + (alignment) - 1) / (alignment)) * (alignment); \
352 cast _ptr = (cast)aligned_alloc((alignment), aligned_size); \
353 if (!_ptr) { \
354 FATAL(ERROR_MEMORY, "Aligned memory allocation failed: %zu bytes, %zu alignment", aligned_size, \
355 (size_t)(alignment)); \
356 } \
357 TRACK_ALIGNED_ALLOC(_ptr, aligned_size, __FILE__, __LINE__); \
358 _ptr; \
359 })
360#endif
361#endif
362
363/* 16-byte aligned allocation for SIMD operations */
364#define SAFE_MALLOC_SIMD(size, cast) SAFE_MALLOC_ALIGNED(size, 16, cast)
365
366/* 16-byte aligned zero-initialized allocation */
367#define SAFE_CALLOC_SIMD(count, size, cast) \
368 ({ \
369 size_t total_size = (count) * (size); \
370 cast _ptr = SAFE_MALLOC_SIMD(total_size, cast); \
371 memset(_ptr, 0, total_size); \
372 _ptr; \
373 })
374
375/* Safe free that nulls the pointer - available in all builds */
376#define SAFE_FREE(ptr) \
377 do { \
378 if ((ptr) != NULL) { \
379 ALLOC_FREE((void *)(ptr)); \
380 (ptr) = NULL; \
381 } \
382 } while (0)
383
384/* Safe fclose that nulls the pointer - for use with defer() */
385/* Usage: defer(SAFE_FCLOSE(fp)); */
386#define SAFE_FCLOSE(fp) \
387 do { \
388 if ((fp) != NULL) { \
389 fclose(fp); \
390 (fp) = NULL; \
391 } \
392 } while (0)
393
394/* Untracked malloc/free - bypass memory tracking for special cases like mode_argv */
395/* These use raw malloc/free to avoid appearing in leak reports */
396#define UNTRACKED_MALLOC(size, cast) \
397 ({ \
398 cast _ptr = (cast)malloc(size); \
399 if (!_ptr) { \
400 FATAL(ERROR_MEMORY, "Memory allocation failed: %zu bytes", (size_t)(size)); \
401 } \
402 _ptr; \
403 })
404
405#define UNTRACKED_FREE(ptr) \
406 do { \
407 if ((ptr) != NULL) { \
408 free((void *)(ptr)); \
409 (ptr) = NULL; \
410 } \
411 } while (0)
412
413/* Safe string copy */
414#define SAFE_STRNCPY(dst, src, size) platform_strlcpy((dst), (src), (size))
415
416#include "asciichat_errno.h"
417/* Safe string duplication with memory tracking */
418#define SAFE_STRDUP(dst, src) \
419 do { \
420 if (src) { \
421 size_t _len = strlen(src) + 1; \
422 (dst) = SAFE_MALLOC(_len, char *); \
423 if (dst) { \
424 SAFE_MEMCPY((dst), _len, (src), _len); \
425 } else { \
426 SET_ERRNO(ERROR_MEMORY, "String duplication failed for: %s", (src)); \
427 } \
428 } else { \
429 (dst) = NULL; \
430 } \
431 } while (0)
432
433/* Platform-safe environment variable access */
434#define SAFE_GETENV(name) platform_getenv(name)
435
436/* Platform-safe sscanf (only used with numeric format specifiers) */
437#ifdef _WIN32
438#define SAFE_SSCANF(str, format, ...) sscanf_s(str, format, __VA_ARGS__)
439#else
440#define SAFE_SSCANF(str, format, ...) sscanf(str, format, __VA_ARGS__)
441#endif
442
443/* POSIX case-insensitive string comparison (Windows uses _stricmp/_strnicmp) */
444#ifdef _WIN32
445#include <string.h>
446#define strcasecmp _stricmp
447#define strncasecmp _strnicmp
448/* POSIX access() mode flags */
449#ifndef R_OK
450#define R_OK 4
451#endif
452#ifndef W_OK
453#define W_OK 2
454#endif
455#ifndef X_OK
456#define X_OK 0
457#endif
458#ifndef F_OK
459#define F_OK 0
460#endif
461#endif
462
463/* Platform-safe strerror */
464#include "platform/abstraction.h" // IWYU pragma: keep
465#define SAFE_STRERROR(errnum) platform_strerror(errnum)
466
467/* Safe memory functions */
468#define SAFE_MEMCPY(dest, dest_size, src, count) platform_memcpy((dest), (dest_size), (src), (count))
469#define SAFE_MEMSET(dest, dest_size, ch, count) platform_memset((dest), (dest_size), (ch), (count))
470#define SAFE_MEMMOVE(dest, dest_size, src, count) platform_memmove((dest), (dest_size), (src), (count))
471#define SAFE_STRCPY(dest, dest_size, src) platform_strcpy((dest), (dest_size), (src))
472
473/* ============================================================================
474 * Unaligned Memory Access Helpers (Backward Compatibility)
475 * ============================================================================
476 * NOTE: These functions have been moved to util/bytes.h
477 * This header includes util/bytes.h and provides backward compatibility
478 * macros for existing code using the old names.
479 */
480
481#include "util/bytes.h"
482
483/* Backward compatibility aliases for existing code */
484#define read_u16_unaligned bytes_read_u16_unaligned
485#define read_u32_unaligned bytes_read_u32_unaligned
486#define write_u16_unaligned bytes_write_u16_unaligned
487#define write_u32_unaligned bytes_write_u32_unaligned
488#define safe_size_mul bytes_safe_size_mul
489
490/* Safe string formatting */
491// clang-format off
492#define SAFE_SNPRINTF(buffer, buffer_size, ...) (size_t)safe_snprintf((buffer), (buffer_size), __VA_ARGS__)
493// clang-format on
494
501#define SAFE_BUFFER_SIZE(buffer_size, offset) \
502 ((offset) < 0 || (size_t)(offset) >= (buffer_size) ? 0 : (buffer_size) - (size_t)(offset))
503
504/* ============================================================================
505 * Thread Creation and Synchronization Macros
506 * ============================================================================ */
507
522#define THREAD_CREATE_OR_RETURN(thread, func, arg) \
523 do { \
524 if (asciichat_thread_create(&(thread), (func), (arg)) != 0) { \
525 log_error("Failed to create thread: %s", #func); \
526 return -1; \
527 } \
528 } while (0)
529
542#define MUTEX_INIT_OR_RETURN(m) \
543 do { \
544 if (mutex_init(&(m)) != 0) { \
545 log_error("Failed to initialize mutex: %s", #m); \
546 return -1; \
547 } \
548 } while (0)
549
550/* Include logging.h to provide logging macros to all files that include common.h */
551#include "log/log.h" // IWYU pragma: keep
552
555/* ============================================================================
556 * Shared Initialization
557 * ============================================================================
558 * Common initialization code shared between client and server modes.
559 * This function handles platform setup, logging, buffer pools, cleanup
560 * registration, and other shared initialization tasks.
561 */
562
586asciichat_error_t asciichat_shared_init(const char *log_file, bool is_client, bool quiet_startup);
587
602void asciichat_shared_destroy(void);
603
604/* ============================================================================
605 * Error Handling Macros
606 * ============================================================================
607 * Macros for common error handling patterns to reduce code duplication
608 */
609
629#define ASCIICHAT_CHECK_AND_LOG(expr, ok_value, msg, ...) \
630 do { \
631 if ((expr) != (ok_value)) { \
632 log_error(msg, ##__VA_ARGS__); \
633 return (expr); \
634 } \
635 } while (0)
636
637/* ============================================================================
638 * Global Variables for Early Argv Inspection and Color Flag Detection
639 * ============================================================================ */
640
645extern ASCIICHAT_API int g_argc;
646
651extern ASCIICHAT_API char **g_argv;
652
658
664
Platform abstraction layer umbrella header providing unified cross-platform API.
DLL export/import macros for cross-platform symbol visibility.
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
Common buffer size definitions.
🔢 Byte-Level Access and Arithmetic Utilities
🔍 Memory debugging helpers for tracking allocations in debug builds
Error and exit codes - unified status values (0-255)
unsigned short uint16_t
Definition common.h:57
void asciichat_fatal_with_context(asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
Exit with error code and context (used by FATAL macro)
bool g_color_flag_value
Value of –color flag (true if –color was in argv) Set by options_init() before RCU is initialized,...
Definition common.c:58
char ** g_argv
Global argv for early argument inspection (e.g., –color flag detection) Set by main() for access from...
Definition common.c:54
unsigned int uint32_t
Definition common.h:58
int g_argc
Global argc for early argument inspection (e.g., –color flag detection) Set by main() for access from...
Definition common.c:53
bool g_color_flag_passed
Was –color explicitly passed in command-line arguments? Set by options_init() before RCU is initializ...
Definition common.c:57
asciichat_error_t asciichat_shared_init(const char *log_file, bool is_client, bool quiet_startup)
Initialize common subsystems shared by client and server.
Definition common.c:115
unsigned long long uint64_t
Definition common.h:59
unsigned char uint8_t
Definition common.h:56
void asciichat_shared_destroy(void)
Clean up shared library subsystems.
Definition common.c:195
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
#define log_file(...)
File-only logging - writes to log file only, no stderr output.
Definition log/log.h:646
#define ASCIICHAT_API
Export symbols on Unix platforms (Linux, macOS)
Definition api.h:82
Application limits and constraints.
📝 Logging API with multiple log levels and terminal output control
Logging rate limit constants (in microseconds)
Protocol version, features, compression, and frame flag constants.
Shutdown check system for clean library/application separation.
ClangTool/LibTooling compatibility shim for stdbool.h.
String literal constants.
Cross-platform system functions interface for ascii-chat.