ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
video/ascii/common.c
Go to the documentation of this file.
1
7#include <ascii-chat/common.h>
9#include <ascii-chat/uthash.h>
15#include <time.h>
16#include <math.h>
17#include <ascii-chat/atomic.h>
18
19// Include SIMD architecture headers for cleanup functions
20// Note: Only ONE SIMD implementation is compiled based on highest available instruction set
21#if SIMD_SUPPORT_NEON
23#elif SIMD_SUPPORT_AVX2
25#elif SIMD_SUPPORT_SSSE3
27#elif SIMD_SUPPORT_SSE2
29#elif SIMD_SUPPORT_SVE
31#endif
32
33// Default luminance palette initialization lifecycle
34static lifecycle_t g_default_palette_lc = LIFECYCLE_INIT;
35
36// Global variable definitions for extern declarations in common.h
39
40// Build 64-entry glyph LUT for vqtbl4q_u8 and other architecture's instrinsics (UTF-8 aware)
41void build_ramp64(uint8_t ramp64[RAMP64_SIZE], const char *ascii_chars) {
42 if (!ascii_chars) {
43 // Fallback to space character
44 for (int i = 0; i < RAMP64_SIZE; i++) {
45 ramp64[i] = ' ';
46 }
47 return;
48 }
49
50 // Use UTF-8 palette functions for proper character handling
51 utf8_palette_t *utf8_pal = utf8_palette_create(ascii_chars);
52 if (!utf8_pal) {
53 // Fallback to space character
54 for (int i = 0; i < RAMP64_SIZE; i++) {
55 ramp64[i] = ' ';
56 }
57 return;
58 }
59
60 size_t char_count = utf8_palette_get_char_count(utf8_pal);
61 if (char_count == 0) {
62 // No valid characters found, use space
63 for (int i = 0; i < RAMP64_SIZE; i++) {
64 ramp64[i] = ' ';
65 }
66 utf8_palette_destroy(utf8_pal);
67 return;
68 }
69
70 // Build the ramp64 lookup using UTF-8 character indices
71 for (int i = 0; i < RAMP64_SIZE; i++) {
72 // Map 0-63 to 0-(char_count-1) using proper character indexing
73 size_t char_idx = (i * (char_count - 1) + (RAMP64_SIZE - 1) / 2) / (RAMP64_SIZE - 1);
74 if (char_idx >= char_count) {
75 char_idx = char_count - 1;
76 }
77
78 // Get the first byte of the character at this character index
79 const utf8_char_info_t *char_info = utf8_palette_get_char(utf8_pal, char_idx);
80 if (char_info) {
81 ramp64[i] = (uint8_t)char_info->bytes[0];
82 } else {
83 ramp64[i] = ' '; // Fallback
84 }
85 }
86
87 utf8_palette_destroy(utf8_pal);
88}
89
90// Cache eviction helper functions
91double calculate_cache_eviction_score(uint64_t last_access_time, uint32_t access_count, uint64_t creation_time,
92 uint64_t current_time) {
93 // Protect against unsigned underflow if times are inconsistent (clock adjustments, etc.)
94 uint64_t age_ns = (current_time >= last_access_time) ? (current_time - last_access_time) : 0;
95 uint64_t total_age_ns = (current_time >= creation_time) ? (current_time - creation_time) : 0;
96
97 uint64_t age_seconds = age_ns / NS_PER_SEC_INT;
98 uint64_t total_age_seconds = total_age_ns / NS_PER_SEC_INT;
99
100 // Frequency factor: high-use palettes get protection (logarithmic scaling)
101 double frequency_factor = 1.0 + log10(1.0 + access_count);
102
103 // Aging factor: frequency bonus decays over time (5-minute half-life)
104 double aging_factor = exp(-(double)age_seconds / CACHE_FREQUENCY_DECAY_TIME);
105
106 // Recent access bonus: strong protection for recently used (1-minute protection)
107 double recency_bonus = exp(-(double)age_seconds / CACHE_RECENCY_SCALE);
108
109 // Cache lifetime penalty: prevent immortal caches (1-hour max lifetime)
110 double lifetime_penalty = total_age_seconds > CACHE_MAX_LIFETIME ? 0.5 : 1.0;
111
112 // Final score: higher = keep longer
113 return (frequency_factor * aging_factor + recency_bonus) * lifetime_penalty;
114}
115
116// Fast palette string hashing using shared FNV-1a hash function
117static inline uint32_t hash_palette_string(const char *palette) {
118 return fnv1a_hash_string(palette);
119}
120
121// Forward declarations for eviction functions (defined after globals)
122static bool try_insert_with_eviction_utf8(uint32_t hash, utf8_palette_cache_t *new_cache);
123
124// UTF-8 palette cache system with min-heap eviction
125static utf8_palette_cache_t *g_utf8_cache_table = NULL; // uthash uses structure pointer as head
126static rwlock_t g_utf8_cache_rwlock = {0};
127static lifecycle_t g_utf8_cache_lc = LIFECYCLE_INIT;
128
129// Min-heap for O(log n) intelligent eviction
130static utf8_palette_cache_t **g_utf8_heap = NULL; // Min-heap array
131static size_t g_utf8_heap_size = 0; // Current entries in heap
132static const size_t g_utf8_heap_capacity = 2048; // Max heap size (matching uthash capacity)
133
134// char_index_ramp_cache removed - data is already stored in utf8_palette_cache_t.char_index_ramp[64]
135
136// Initialize UTF-8 cache system with min-heap (thread-safe)
137static void init_utf8_cache_system(void) {
138 // Lock-free init: lifecycle handles synchronization
139 g_utf8_cache_lc.sync_type = LIFECYCLE_SYNC_RWLOCK;
140 g_utf8_cache_lc.sync.rwlock = &g_utf8_cache_rwlock;
141 if (!lifecycle_init(&g_utf8_cache_lc, "utf8_cache")) {
142 return; // Already initialized by another thread
143 }
144
145 // Initialize uthash head to NULL (required)
146 g_utf8_cache_table = NULL;
147 g_utf8_heap = SAFE_MALLOC(g_utf8_heap_capacity * sizeof(utf8_palette_cache_t *), utf8_palette_cache_t **);
148 g_utf8_heap_size = 0;
149}
150
151// Min-heap management functions for UTF-8 cache
152static void utf8_heap_swap(size_t i, size_t j) {
153 utf8_palette_cache_t *temp = g_utf8_heap[i];
154 g_utf8_heap[i] = g_utf8_heap[j];
155 g_utf8_heap[j] = temp;
156
157 // Update heap indices in cache objects
158 g_utf8_heap[i]->heap_index = i;
159 g_utf8_heap[j]->heap_index = j;
160}
161
162static void utf8_heap_bubble_up(size_t index) {
163 while (index > 0) {
164 size_t parent = (index - 1) / 2;
165 if (g_utf8_heap[index]->cached_score >= g_utf8_heap[parent]->cached_score)
166 break;
167 utf8_heap_swap(index, parent);
168 index = parent;
169 }
170}
171
172static void utf8_heap_bubble_down(size_t index) {
173 while (true) {
174 size_t left = 2 * index + 1;
175 size_t right = 2 * index + 2;
176 size_t smallest = index;
177
178 if (left < g_utf8_heap_size && g_utf8_heap[left]->cached_score < g_utf8_heap[smallest]->cached_score) {
179 smallest = left;
180 }
181 if (right < g_utf8_heap_size && g_utf8_heap[right]->cached_score < g_utf8_heap[smallest]->cached_score) {
182 smallest = right;
183 }
184
185 if (smallest == index)
186 break;
187 utf8_heap_swap(index, smallest);
188 index = smallest;
189 }
190}
191
192static void utf8_heap_insert(utf8_palette_cache_t *cache, double score) {
193 if (g_utf8_heap_size >= g_utf8_heap_capacity) {
194 log_error("UTF8_HEAP: Heap capacity exceeded");
195 return;
196 }
197
198 cache->cached_score = score;
199 cache->heap_index = g_utf8_heap_size;
200 g_utf8_heap[g_utf8_heap_size] = cache;
201 g_utf8_heap_size++;
202
203 utf8_heap_bubble_up(cache->heap_index);
204}
205
206static utf8_palette_cache_t *utf8_heap_extract_min(void) {
207 if (g_utf8_heap_size == 0) {
208 return NULL;
209 }
210
211 utf8_palette_cache_t *min_cache = g_utf8_heap[0];
212
213 // Move last element to root and bubble down
214 g_utf8_heap_size--;
215 if (g_utf8_heap_size > 0) {
216 g_utf8_heap[0] = g_utf8_heap[g_utf8_heap_size];
217 g_utf8_heap[0]->heap_index = 0;
218 utf8_heap_bubble_down(0);
219 }
220
221 return min_cache;
222}
223
224static void utf8_heap_update_score(utf8_palette_cache_t *cache, double new_score) {
225 double old_score = cache->cached_score;
226 cache->cached_score = new_score;
227
228 if (new_score < old_score) {
229 utf8_heap_bubble_up(cache->heap_index);
230 } else {
231 utf8_heap_bubble_down(cache->heap_index);
232 }
233}
234
235// Min-heap functions now replace LRU list management
236
237// Thread-safe cache eviction implementations
238static bool try_insert_with_eviction_utf8(uint32_t hash, utf8_palette_cache_t *new_cache) {
239 // Already holding write lock
240 // Note: key should already be set by caller, but ensure it's set
241 new_cache->key = hash;
242
243 // Check if hash table is near capacity and evict proactively (80% threshold)
244 size_t entry_count = HASH_COUNT(g_utf8_cache_table);
245 if (entry_count >= g_utf8_heap_capacity) {
246 // Proactive eviction: free space before attempting insertion
247 utf8_palette_cache_t *victim_cache = utf8_heap_extract_min();
248 if (victim_cache) {
249 // WASM: Skip debug log to avoid blocking render loop
250
251 HASH_DEL(g_utf8_cache_table, victim_cache);
252 SAFE_FREE(victim_cache);
253 }
254 }
255
256 // Now attempt insertion (should succeed with freed space)
257 // Note: uthash doesn't have a failure path for insertion, so we handle eviction proactively
258 HASH_ADD_INT(g_utf8_cache_table, key, new_cache);
259
260 // Success: add to min-heap
261 uint64_t current_time = time_get_ns();
262 double initial_score = calculate_cache_eviction_score(current_time, 1, current_time, current_time);
263 utf8_heap_insert(new_cache, initial_score);
264 return true;
265}
266
267// char_ramp_cache functions removed - data already available in utf8_palette_cache_t
268
269// Get or create UTF-8 palette cache for a given palette
271 if (!ascii_chars)
272 return NULL;
273
274 // Check for empty string
275 if (ascii_chars[0] == '\0')
276 return NULL;
277
278 // Create hash of palette for cache key
279 uint32_t palette_hash = hash_palette_string(ascii_chars);
280
281 // Ensure the cache system is initialized
282 init_utf8_cache_system();
283
284 // First try: read lock for cache lookup (allows multiple concurrent readers)
285 rwlock_rdlock(&g_utf8_cache_rwlock);
286
287 // Check if cache exists
288 utf8_palette_cache_t *cache = NULL;
289 HASH_FIND_INT(g_utf8_cache_table, &palette_hash, cache);
290 if (cache) {
291 // Cache hit: Update access tracking (atomics are thread-safe under rdlock)
292 uint64_t current_time = time_get_ns();
293 atomic_store_u64(&cache->last_access_time, current_time);
294 uint32_t new_access_count = atomic_fetch_add_u64(&cache->access_count, 1) + 1;
295
296 // Every 10th access: Update heap position (requires write lock)
297 if (new_access_count % 10 == 0) {
298 // Save the key before releasing read lock
299 uint32_t saved_key = cache->key;
300
301 // Release read lock and upgrade to write lock
302 rwlock_rdunlock(&g_utf8_cache_rwlock);
303 rwlock_wrlock(&g_utf8_cache_rwlock);
304
305 // Re-lookup cache entry after lock upgrade
306 // Another thread could have evicted this entry while we were upgrading locks
307 utf8_palette_cache_t *cache_relookup = NULL;
308 HASH_FIND_INT(g_utf8_cache_table, &saved_key, cache_relookup);
309 if (cache_relookup) {
310 // Cache entry still exists - safe to update heap position
311 uint64_t last_access = atomic_load_u64(&cache_relookup->last_access_time);
312 uint32_t access_count = atomic_load_u64(&cache_relookup->access_count);
313 double new_score =
314 calculate_cache_eviction_score(last_access, access_count, cache_relookup->creation_time, current_time);
315 utf8_heap_update_score(cache_relookup, new_score);
316 }
317 // If cache_relookup is NULL, entry was evicted - skip update (not an error)
318
319 rwlock_wrunlock(&g_utf8_cache_rwlock);
320 } else {
321 rwlock_rdunlock(&g_utf8_cache_rwlock);
322 }
323
324 return cache;
325 }
326
327 // Cache miss: Need to create new entry
328 // Release read lock and acquire write lock
329 rwlock_rdunlock(&g_utf8_cache_rwlock);
330 rwlock_wrlock(&g_utf8_cache_rwlock);
331
332 // Double-check: another thread might have created it while we upgraded locks
333 cache = NULL;
334 HASH_FIND_INT(g_utf8_cache_table, &palette_hash, cache);
335 if (cache) {
336 // Found it! Just update access tracking and return
337 uint64_t current_time = time_get_ns();
338 atomic_store_u64(&cache->last_access_time, current_time);
340 rwlock_wrunlock(&g_utf8_cache_rwlock);
341 return cache;
342 }
343
344 // Create new cache entry (holding write lock)
346 if (!cache) {
347 rwlock_wrunlock(&g_utf8_cache_rwlock);
348 return NULL;
349 }
350 memset(cache, 0, sizeof(utf8_palette_cache_t));
351
352 // Set the key for uthash
353 cache->key = palette_hash;
354
355 // Build both cache types
356 build_utf8_luminance_cache(ascii_chars, cache->cache);
357 build_utf8_ramp64_cache(ascii_chars, cache->cache64, cache->char_index_ramp);
358
359 // Store palette hash for validation
360 SAFE_STRNCPY(cache->palette_hash, ascii_chars, sizeof(cache->palette_hash));
361 cache->is_valid = true;
362
363 // Initialize eviction tracking
364 uint64_t current_time = time_get_ns();
365 atomic_store_u64(&cache->last_access_time, current_time);
366 atomic_store_u64(&cache->access_count, 1); // First access
367 cache->creation_time = current_time;
368
369 // Store in hash table with guaranteed eviction support
370 try_insert_with_eviction_utf8(palette_hash, cache);
371
372 // WASM: Skip debug log to avoid blocking render loop on main thread
373 // log_debug("UTF8_CACHE: Created new cache for palette='%s' (hash=0x%x)", ascii_chars, palette_hash);
374
375 rwlock_wrunlock(&g_utf8_cache_rwlock);
376 return cache;
377}
378
379// Build 256-entry UTF-8 cache for direct luminance lookup (monochrome renderers)
380void build_utf8_luminance_cache(const char *ascii_chars, utf8_char_t cache[256]) {
381 if (!ascii_chars || !cache)
382 return;
383
384 // Parse characters
385 typedef struct {
386 const char *start;
387 int byte_len;
388 } char_info_t;
389
390 char_info_t char_infos[256];
391 int char_count = 0;
392 const char *p = ascii_chars;
393
394 while (*p && char_count < 255) {
395 char_infos[char_count].start = p;
396
397 if ((*p & 0xE0) == 0xC0) {
398 char_infos[char_count].byte_len = 2;
399 p += 2;
400 } else if ((*p & 0xF0) == 0xE0) {
401 char_infos[char_count].byte_len = 3;
402 p += 3;
403 } else if ((*p & 0xF8) == 0xF0) {
404 char_infos[char_count].byte_len = 4;
405 p += 4;
406 } else {
407 // ASCII characters (0x00-0x7F) and invalid sequences: treat as single byte
408 char_infos[char_count].byte_len = 1;
409 p++;
410 }
411 char_count++;
412 }
413
414 // Handle empty string case
415 if (char_count == 0)
416 return;
417
418 // Build 256-entry cache
419 for (int i = 0; i < 256; i++) {
420 int char_idx = char_count > 1 ? (i * (char_count - 1) + 127) / 255 : 0;
421 if (char_idx >= char_count)
422 char_idx = char_count - 1;
423
424 cache[i].byte_len = char_infos[char_idx].byte_len;
425 memcpy(cache[i].utf8_bytes, char_infos[char_idx].start, char_infos[char_idx].byte_len);
426 if (cache[i].byte_len < 4) {
427 cache[i].utf8_bytes[cache[i].byte_len] = '\0';
428 }
429 }
430}
431
432// Build 64-entry UTF-8 cache for SIMD color lookup
433void build_utf8_ramp64_cache(const char *ascii_chars, utf8_char_t cache64[64], uint8_t char_index_ramp[256]) {
434 if (!ascii_chars || !cache64 || !char_index_ramp)
435 return;
436
437 // Reuse the luminance cache building logic but for 64 entries
438 // (Same UTF-8 parsing as above, but map to 64 entries instead of 256)
439
440 // Parse characters (same as above)
441 typedef struct {
442 const char *start;
443 int byte_len;
444 } char_info_t;
445
446 char_info_t char_infos[256];
447 int char_count = 0;
448 const char *p = ascii_chars;
449
450 while (*p && char_count < 255) {
451 char_infos[char_count].start = p;
452
453 if ((*p & 0xE0) == 0xC0) {
454 char_infos[char_count].byte_len = 2;
455 p += 2;
456 } else if ((*p & 0xF0) == 0xE0) {
457 char_infos[char_count].byte_len = 3;
458 p += 3;
459 } else if ((*p & 0xF8) == 0xF0) {
460 char_infos[char_count].byte_len = 4;
461 p += 4;
462 } else {
463 // ASCII characters (0x00-0x7F) and invalid sequences: treat as single byte
464 char_infos[char_count].byte_len = 1;
465 p++;
466 }
467 char_count++;
468 }
469
470 // Handle empty string case
471 if (char_count == 0)
472 return;
473
474 // Build 64-entry cache and index ramp
475 for (int i = 0; i < 64; i++) {
476 int char_idx = char_count > 1 ? (i * (char_count - 1) + 31) / 63 : 0;
477 if (char_idx >= char_count)
478 char_idx = char_count - 1;
479
480 // Store character index for SIMD lookup
481 char_index_ramp[i] = (uint8_t)char_idx;
482
483 // Cache UTF-8 character
484 cache64[i].byte_len = char_infos[char_idx].byte_len;
485 memcpy(cache64[i].utf8_bytes, char_infos[char_idx].start, char_infos[char_idx].byte_len);
486 if (cache64[i].byte_len < 4) {
487 cache64[i].utf8_bytes[cache64[i].byte_len] = '\0';
488 }
489 }
490}
491
492// char_index_ramp_cache functions removed - data already available in utf8_palette_cache_t.char_index_ramp[64]
493
494// No callback needed - uthash iteration handles cleanup directly
495
496// Central cleanup function for all SIMD caches
498 log_dev("SIMD_CACHE: Starting cleanup of all SIMD caches");
499
500 // Only destroy caches if they were ever initialized
501 if (lifecycle_is_initialized(&g_utf8_cache_lc)) {
502 // Destroy shared UTF-8 palette cache (write lock for cleanup)
503 rwlock_wrlock(&g_utf8_cache_rwlock);
504 if (g_utf8_cache_table) {
505 // Free all UTF-8 cache entries using HASH_ITER
506 utf8_palette_cache_t *cache, *tmp;
507 HASH_ITER(hh, g_utf8_cache_table, cache, tmp) {
508 HASH_DEL(g_utf8_cache_table, cache);
509 SAFE_FREE(cache);
510 }
511 g_utf8_cache_table = NULL;
512 // WASM: Skip debug log to avoid blocking
513 // log_debug("UTF8_CACHE: Destroyed shared UTF-8 palette cache");
514 }
515 // Clean up heap arrays
516 if (g_utf8_heap) {
517 SAFE_FREE(g_utf8_heap);
518 g_utf8_heap = NULL;
519 g_utf8_heap_size = 0;
520 }
521 rwlock_wrunlock(&g_utf8_cache_rwlock);
522 // Shutdown lifecycle and destroy rwlock together
523 lifecycle_shutdown(&g_utf8_cache_lc);
524 }
525
526 // Call architecture-specific cache cleanup functions
527 // Note: Only ONE SIMD implementation is compiled based on highest available instruction set
528 // Higher instruction sets (AVX2, SSSE3) handle cleanup for lower ones (SSE2)
529#if SIMD_SUPPORT_SSSE3
530 ssse3_caches_destroy();
531#elif SIMD_SUPPORT_SSE2
532 sse2_caches_destroy();
533#elif SIMD_SUPPORT_SVE
534 sve_caches_destroy();
535#endif
536
537 log_dev("SIMD_CACHE: All SIMD caches destroyed");
538}
539
546void init_dec3(void) {
548 return;
549 for (int v = 0; v < 256; ++v) {
550 int d2 = v / 100; // 0..2
551 int r = v - d2 * 100; // 0..99
552 int d1 = r / 10; // 0..9
553 int d0 = r - d1 * 10; // 0..9
554
555 if (d2) {
557 g_dec3_cache.dec3_table[v].s[0] = '0' + d2;
558 g_dec3_cache.dec3_table[v].s[1] = '0' + d1;
559 g_dec3_cache.dec3_table[v].s[2] = '0' + d0;
560 } else if (d1) {
562 g_dec3_cache.dec3_table[v].s[0] = '0' + d1;
563 g_dec3_cache.dec3_table[v].s[1] = '0' + d0;
564 } else {
566 g_dec3_cache.dec3_table[v].s[0] = '0' + d0;
567 }
568 }
570}
571
578static void do_init_default_luminance_palette(void) {
579 const size_t len = DEFAULT_ASCII_PALETTE_LEN;
580 for (int i = 0; i < 256; i++) {
581 size_t palette_index = (i * (len - 1) + 127) / 255;
582 if (palette_index >= len) {
583 palette_index = len - 1;
584 }
586 }
587}
588
590 if (!lifecycle_init(&g_default_palette_lc, "default_palette")) {
591 return; // Already initialized
592 }
593 do_init_default_luminance_palette();
594}
595
605
606// Output buffer functions moved to lib/video/output_buffer.c
uint64_t atomic_fetch_add_u64(atomic_t *a, uint64_t delta)
Atomically add to a uint64_t and return the previous value.
Definition atomic.c:248
void atomic_store_u64(atomic_t *a, uint64_t value)
Atomically store a uint64_t value.
Definition atomic.c:241
uint64_t atomic_load_u64(atomic_t *a)
Atomically load a uint64_t value.
Definition atomic.c:233
⚛️ Atomic operations abstraction layer with debug tracking
AVX2-optimized ASCII rendering functions.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
#️⃣ FNV-1a Hash Function Implementation
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned long long uint64_t
Definition common.h:59
unsigned char uint8_t
Definition common.h:56
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
uint64_t time_get_ns(void)
Get current monotonic time in nanoseconds.
Definition util/time.c:108
#define NS_PER_SEC_INT
Definition time.h:157
void utf8_palette_destroy(utf8_palette_t *palette)
Destroy a UTF-8 palette and free resources.
Definition palette.c:485
char bytes[4]
UTF-8 byte sequence (max 4 bytes per character)
Definition palette.h:215
size_t utf8_palette_get_char_count(const utf8_palette_t *palette)
Get number of characters in UTF-8 palette.
Definition palette.c:502
const size_t DEFAULT_ASCII_PALETTE_LEN
Length of default ASCII palette.
Definition palette.c:26
const char DEFAULT_ASCII_PALETTE[]
Default ASCII palette for legacy functions.
Definition palette.c:25
const utf8_char_info_t * utf8_palette_get_char(const utf8_palette_t *palette, size_t index)
Get UTF-8 character information at index.
Definition palette.c:494
utf8_palette_t * utf8_palette_create(const char *palette_string)
Create a UTF-8 palette from string.
Definition palette.c:363
#define rwlock_wrunlock(lock)
Release a write lock (with debug tracking in debug builds)
Definition rwlock.h:349
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds)
Definition rwlock.h:294
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds)
Definition rwlock.h:313
#define rwlock_rdunlock(lock)
Release a read lock (with debug tracking in debug builds)
Definition rwlock.h:331
global_dec3_cache_t g_dec3_cache
Global decimal cache instance.
void build_utf8_luminance_cache(const char *ascii_chars, utf8_char_t cache[256])
Build UTF-8 luminance cache for all 256 levels.
void simd_caches_destroy_all(void)
Destroy all SIMD caches (palette and other caches)
#define RAMP64_SIZE
UTF-8 character palette cache.
uint8_t utf8_bytes[4]
utf8_palette_cache_t * get_utf8_palette_cache(const char *ascii_chars)
Get UTF-8 palette cache for a character set.
#define CACHE_RECENCY_SCALE
void init_dec3(void)
Initialize decimal-to-ASCII cache (0-255 → "0" to "255")
char g_default_luminance_palette[256]
Default luminance palette (256 characters)
#define CACHE_FREQUENCY_DECAY_TIME
utf8_char_t cache64[64]
void build_utf8_ramp64_cache(const char *ascii_chars, utf8_char_t cache64[64], uint8_t char_index_ramp[256])
Build UTF-8 ramp64 cache and character index ramp.
void init_default_luminance_palette(void)
Initialize default luminance palette.
utf8_char_t cache[256]
void ascii_simd_init(void)
Initialize SIMD subsystem.
#define CACHE_MAX_LIFETIME
Platform initialization and static synchronization helpers.
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108
Lock-free module lifecycle state machine using pure stdatomic.
@ LIFECYCLE_SYNC_RWLOCK
Contains rwlock_t pointer.
Definition lifecycle.h:51
#define LIFECYCLE_INIT
Static initializer for module-global lifecycle variables (no sync primitive)
Definition lifecycle.h:68
🔢 Mathematical Utility Functions
NEON-optimized ASCII rendering functions.
ASCII Palette Management for Video-to-ASCII Conversion.
SSE2-optimized ASCII rendering functions.
SSSE3-optimized ASCII rendering functions.
Global decimal cache for digit conversion.
union lifecycle_t::@24 sync
rwlock_t * rwlock
Pointer to rwlock (if sync_type == LIFECYCLE_SYNC_RWLOCK)
Definition lifecycle.h:63
lifecycle_sync_type_t sync_type
Type of sync primitive (if any)
Definition lifecycle.h:60
Read-write lock type (POSIX: pthread_rwlock_t with debug tracking)
Definition rwlock.h:61
UTF-8 character information structure.
Definition palette.h:213
UTF-8 palette structure.
Definition palette.h:236
SVE-optimized ASCII rendering functions.
⏱️ High-precision timing utilities using sokol_time.h and uthash
void build_ramp64(uint8_t ramp64[RAMP64_SIZE], const char *ascii_chars)
double calculate_cache_eviction_score(uint64_t last_access_time, uint32_t access_count, uint64_t creation_time, uint64_t current_time)
SIMD-optimized ASCII conversion interface.