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