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