ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
Utilities

🛠️ Core utility functions for string manipulation, paths, IP parsing, and formatting More...

Modules

 Utility Functions
 🛠️ String, path, IP, UTF-8, CRC32, and formatting utilities
 

Files

file  time.c
 ⏱️ High-precision timing utilities implementation
 
file  time.h
 ⏱️ High-precision timing utilities using sokol_time.h and uthash
 

Data Structures

struct  timer_record
 Individual timer record for a named timing operation. More...
 
struct  adaptive_sleep_config_t
 Configuration for adaptive sleep behavior. More...
 
struct  adaptive_sleep_state_t
 Runtime state for adaptive sleep. More...
 

Macros

#define TIME_ATOMIC_UINT64   atomic_t
 
#define TIME_ATOMIC_UINT64_INIT(val)   {0}
 
#define NS_PER_US   1000.0
 
#define NS_PER_MS   (1000.0 * NS_PER_US)
 
#define NS_PER_SEC   (1000.0 * NS_PER_MS)
 
#define NS_PER_MIN   (60.0 * NS_PER_SEC)
 
#define NS_PER_HOUR   (60.0 * NS_PER_MIN)
 
#define NS_PER_DAY   (24.0 * NS_PER_HOUR)
 
#define NS_PER_YEAR   (365.25 * NS_PER_DAY)
 
#define NS_PER_US_INT   1000ULL
 
#define NS_PER_MS_INT   (1000ULL * NS_PER_US_INT)
 
#define NS_PER_SEC_INT   (1000ULL * NS_PER_MS_INT)
 
#define US_PER_MS_INT   1000ULL
 
#define US_PER_SEC_INT   1000000ULL
 
#define MS_PER_SEC_INT   1000ULL
 
#define SEC_PER_MIN   60ULL
 
#define SEC_PER_HOUR   (SEC_PER_MIN * SEC_PER_MIN)
 
#define SEC_PER_DAY   (24ULL * SEC_PER_HOUR)
 
#define NS_PER_MIN_INT   (SEC_PER_MIN * NS_PER_SEC_INT)
 
#define NS_PER_HOUR_INT   (SEC_PER_HOUR * NS_PER_SEC_INT)
 
#define NS_PER_DAY_INT   (SEC_PER_DAY * NS_PER_SEC_INT)
 
#define START_TIMER(name_fmt, ...)
 Start a timer with formatted name.
 
#define START_TIMER_GLOBAL(name_fmt, ...)
 Start a timer without thread ID (for cross-thread timers)
 
#define STOP_TIMER_GLOBAL(name_fmt, ...)
 Stop a cross-thread timer and return elapsed time.
 
#define STOP_TIMER(name_fmt, ...)
 Stop a timer with formatted name and return elapsed time.
 
#define STOP_TIMER_AND_LOG(log_level, threshold_ns, timer_name, msg_fmt, ...)
 Stop a timer and log the result with a custom message.
 
#define STOP_TIMER_AND_LOG_EVERY(log_level, interval_ns, threshold_ns, timer_name, msg_fmt, ...)
 Stop a timer and log the result with rate limiting.
 

Typedefs

typedef struct timer_record timer_record_t
 Individual timer record for a named timing operation.
 

Functions

uint64_t time_get_ns (void)
 Get current monotonic time in nanoseconds.
 
uint64_t time_get_realtime_ns (void)
 Get current wall-clock (real) time in nanoseconds.
 
void time_sleep_ns (uint64_t ns)
 Sleep for specified nanoseconds.
 
uint64_t time_elapsed_ns (uint64_t start_ns, uint64_t end_ns)
 Calculate elapsed time with wraparound safety.
 
bool timer_system_init (void)
 Initialize the timing system.
 
void timer_system_destroy (void)
 Cleanup the timing system.
 
bool timer_start (const char *name)
 Start a named timer.
 
double timer_stop (const char *name)
 Stop a named timer and return elapsed time.
 
bool timer_is_initialized (void)
 Check if timing system is initialized.
 
int time_pretty (uint64_t nanoseconds, int decimals, char *buffer, size_t buffer_size)
 Format nanoseconds as pretty duration with spaces and configurable precision.
 
int time_pretty_now (int decimals, char *buffer, size_t buffer_size)
 Format current monotonic time as pretty duration.
 
int time_human_readable (int64_t nanoseconds, char *buffer, size_t buffer_size)
 Format nanoseconds as human-readable relative duration (moment.js style)
 
int time_human_readable_unsigned (uint64_t nanoseconds, char *buffer, size_t buffer_size)
 Format elapsed time as human-readable relative duration (unsigned version)
 
int time_human_readable_now (char *buffer, size_t buffer_size)
 Format current monotonic time as human-readable relative duration.
 
int format_uptime_hms (int hours, int minutes, int seconds, char *buffer, size_t buffer_size)
 Format uptime as HH:MM:SS string.
 
void adaptive_sleep_init (adaptive_sleep_state_t *state, const adaptive_sleep_config_t *config)
 Initialize adaptive sleep state with configuration.
 
uint64_t adaptive_sleep_calculate (adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
 Calculate adaptive sleep time based on queue depth.
 
void adaptive_sleep_do (adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
 Calculate sleep time and immediately sleep for that duration.
 
bool time_format_is_valid_strftime (const char *format_str)
 Validate strftime format string against known safe specifiers.
 
int time_format_now (const char *format_str, char *buf, size_t buf_size)
 Format current time using strftime format string.
 
asciichat_error_t time_format_safe (const char *format_str, char *buf, size_t buf_size)
 Safe wrapper for time formatting with validation and error codes.
 
uint64_t time_timespec_to_ns (const struct timespec *ts)
 Convert struct timespec to nanoseconds.
 
void time_ns_to_timespec (uint64_t ns, struct timespec *ts)
 Convert nanoseconds to struct timespec.
 

Detailed Description

🛠️ Core utility functions for string manipulation, paths, IP parsing, and formatting

String utilities, path handling, IP address parsing, UTF-8 support, CRC32, and formatting.

This module provides a simple timing API for performance measurement:

Features:

Example usage:

START_TIMER("process_frame_%d", frame_num);
// ... do work ...
double elapsed_ns = STOP_TIMER("process_frame_%d", frame_num);
// Use time_pretty() for human-readable formatted output
char duration_str[32];
time_pretty((uint64_t)elapsed_ns, -1, duration_str, sizeof(duration_str));
log_info("Frame took %s", duration_str);
unsigned long long uint64_t
Definition common.h:59
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
int time_pretty(uint64_t nanoseconds, int decimals, char *buffer, size_t buffer_size)
Format nanoseconds as pretty duration with spaces and configurable precision.
Definition util/time.c:424
#define START_TIMER(name_fmt,...)
Start a timer with formatted name.
Definition time.h:333
#define STOP_TIMER(name_fmt,...)
Stop a timer with formatted name and return elapsed time.
Definition time.h:403
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

Macro Definition Documentation

◆ MS_PER_SEC_INT

#define MS_PER_SEC_INT   1000ULL

#include <time.h>

Definition at line 164 of file time.h.

◆ NS_PER_DAY

#define NS_PER_DAY   (24.0 * NS_PER_HOUR)

#include <time.h>

Definition at line 151 of file time.h.

◆ NS_PER_DAY_INT

#define NS_PER_DAY_INT   (SEC_PER_DAY * NS_PER_SEC_INT)

#include <time.h>

Definition at line 174 of file time.h.

◆ NS_PER_HOUR

#define NS_PER_HOUR   (60.0 * NS_PER_MIN)

#include <time.h>

Definition at line 150 of file time.h.

◆ NS_PER_HOUR_INT

#define NS_PER_HOUR_INT   (SEC_PER_HOUR * NS_PER_SEC_INT)

#include <time.h>

Definition at line 173 of file time.h.

◆ NS_PER_MIN

#define NS_PER_MIN   (60.0 * NS_PER_SEC)

#include <time.h>

Definition at line 149 of file time.h.

◆ NS_PER_MIN_INT

#define NS_PER_MIN_INT   (SEC_PER_MIN * NS_PER_SEC_INT)

#include <time.h>

Definition at line 172 of file time.h.

◆ NS_PER_MS

#define NS_PER_MS   (1000.0 * NS_PER_US)

#include <time.h>

Definition at line 147 of file time.h.

◆ NS_PER_MS_INT

#define NS_PER_MS_INT   (1000ULL * NS_PER_US_INT)

#include <time.h>

Definition at line 156 of file time.h.

◆ NS_PER_SEC

#define NS_PER_SEC   (1000.0 * NS_PER_MS)

#include <time.h>

Definition at line 148 of file time.h.

◆ NS_PER_SEC_INT

#define NS_PER_SEC_INT   (1000ULL * NS_PER_MS_INT)

#include <time.h>

Definition at line 157 of file time.h.

◆ NS_PER_US

#define NS_PER_US   1000.0

#include <time.h>

Definition at line 146 of file time.h.

◆ NS_PER_US_INT

#define NS_PER_US_INT   1000ULL

#include <time.h>

Definition at line 155 of file time.h.

◆ NS_PER_YEAR

#define NS_PER_YEAR   (365.25 * NS_PER_DAY)

#include <time.h>

Definition at line 152 of file time.h.

◆ SEC_PER_DAY

#define SEC_PER_DAY   (24ULL * SEC_PER_HOUR)

#include <time.h>

Definition at line 169 of file time.h.

◆ SEC_PER_HOUR

#define SEC_PER_HOUR   (SEC_PER_MIN * SEC_PER_MIN)

#include <time.h>

Definition at line 168 of file time.h.

◆ SEC_PER_MIN

#define SEC_PER_MIN   60ULL

#include <time.h>

Definition at line 167 of file time.h.

◆ START_TIMER

#define START_TIMER (   name_fmt,
  ... 
)

#include <time.h>

Value:
do { \
char _timer_name_buf[256]; \
char _timer_name_with_tid[280]; \
snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
snprintf(_timer_name_with_tid, sizeof(_timer_name_with_tid), "%s_tid%llu", _timer_name_buf, \
(unsigned long long)asciichat_thread_current_id()); \
(void)timer_start(_timer_name_with_tid); \
} \
} while (0)
bool timer_start(const char *name)
Start a named timer.
Definition util/time.c:240
bool timer_is_initialized(void)
Check if timing system is initialized.
Definition util/time.c:346
uint64_t asciichat_thread_current_id(void)
Get the current thread's unique numeric ID.
Definition threading.c:92

Start a timer with formatted name.

The timer name is created by formatting the name_fmt string with the provided arguments. This complete formatted string becomes the unique key in the hashtable.

IMPORTANT: Timer names must be unique. If a timer with the same formatted name already exists, timer_start() will return false and log an error.

Usage: START_TIMER("lock_%p", lock_ptr); // Unique per lock address START_TIMER("process_frame_%d", frame_id); // Unique per frame START_TIMER("client_%u_decode", client_id); // Unique per client

Parameters
name_fmtPrintf-style format string for timer name
...Format arguments to create unique key

Definition at line 333 of file time.h.

334 { \
335 if (timer_is_initialized()) { \
336 char _timer_name_buf[256]; \
337 char _timer_name_with_tid[280]; \
338 snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
339 snprintf(_timer_name_with_tid, sizeof(_timer_name_with_tid), "%s_tid%llu", _timer_name_buf, \
340 (unsigned long long)asciichat_thread_current_id()); \
341 (void)timer_start(_timer_name_with_tid); \
342 } \
343 } while (0)

◆ START_TIMER_GLOBAL

#define START_TIMER_GLOBAL (   name_fmt,
  ... 
)

#include <time.h>

Value:
do { \
char _timer_name_buf[256]; \
snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
(void)timer_start(_timer_name_buf); \
} \
} while (0)

Start a timer without thread ID (for cross-thread timers)

Use this variant when a timer may be stopped on a different thread than it was started. The timer name will NOT include thread ID, so it must be globally unique through other means (e.g., including a unique identifier in the format string).

Parameters
name_fmtPrintf-style format string for timer name
...Format arguments to create unique key

Definition at line 357 of file time.h.

358 { \
359 if (timer_is_initialized()) { \
360 char _timer_name_buf[256]; \
361 snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
362 (void)timer_start(_timer_name_buf); \
363 } \
364 } while (0)

◆ STOP_TIMER

#define STOP_TIMER (   name_fmt,
  ... 
)

#include <time.h>

Value:
({ \
double _elapsed = -1.0; \
char _timer_name_buf[256]; \
char _timer_name_with_tid[280]; \
snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
snprintf(_timer_name_with_tid, sizeof(_timer_name_with_tid), "%s_tid%llu", _timer_name_buf, \
(unsigned long long)asciichat_thread_current_id()); \
_elapsed = timer_stop(_timer_name_with_tid); \
} \
_elapsed; \
})
double timer_stop(const char *name)
Stop a named timer and return elapsed time.
Definition util/time.c:291

Stop a timer with formatted name and return elapsed time.

The timer name must match exactly (including all format arguments) with the name used in START_TIMER(). The formatted string is used as the hashtable key lookup.

Usage: double ns = STOP_TIMER("lock_%p", lock_ptr); double ns = STOP_TIMER("process_frame_%d", frame_id);

Parameters
name_fmtPrintf-style format string for timer name (must match START_TIMER)
...Format arguments (must match START_TIMER to create same key)
Returns
Elapsed time in nanoseconds, or -1.0 if timer not found

Definition at line 403 of file time.h.

404 { \
405 double _elapsed = -1.0; \
406 if (timer_is_initialized()) { \
407 char _timer_name_buf[256]; \
408 char _timer_name_with_tid[280]; \
409 snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
410 snprintf(_timer_name_with_tid, sizeof(_timer_name_with_tid), "%s_tid%llu", _timer_name_buf, \
411 (unsigned long long)asciichat_thread_current_id()); \
412 _elapsed = timer_stop(_timer_name_with_tid); \
413 } \
414 _elapsed; \
415 })

◆ STOP_TIMER_AND_LOG

#define STOP_TIMER_AND_LOG (   log_level,
  threshold_ns,
  timer_name,
  msg_fmt,
  ... 
)

#include <time.h>

Value:
do { \
double _elapsed_ns = STOP_TIMER(timer_name, ##__VA_ARGS__); \
if (_elapsed_ns >= 0.0 && (threshold_ns == 0 || _elapsed_ns >= (double)(threshold_ns))) { \
char _duration_str[32]; \
time_pretty((uint64_t)_elapsed_ns, -1, _duration_str, sizeof(_duration_str)); \
log_##log_level(msg_fmt " in %s", ##__VA_ARGS__, _duration_str); \
} \
} while (0)

Stop a timer and log the result with a custom message.

Combines STOP_TIMER() with logging. The timer is stopped, elapsed time is retrieved, and a log message is generated with the elapsed time appended in human-readable format. Optionally filters based on a minimum elapsed time threshold.

Usage: STOP_TIMER_AND_LOG(info, 0, "client_handshake", "Crypto handshake completed successfully"); STOP_TIMER_AND_LOG(debug, 5000000, "process_frame_%d", "Frame %d processed", frame_id, frame_id); STOP_TIMER_AND_LOG(dev, NS_PER_MS_INT, "render", "Rendering complete");

The macro will append " in X.XXms" (or appropriate unit) to your message automatically. Supported log levels: dev, debug, info, warn, error, fatal

Parameters
log_levelLog level name (dev, debug, info, warn, error, fatal - without log_ prefix)
threshold_nsMinimum elapsed time in nanoseconds to trigger log (0 = always log)
timer_nameTimer name (must match START_TIMER call)
msg_fmtPrintf-style format string for the message
...Format arguments for both timer name and message

Definition at line 612 of file time.h.

613 { \
614 double _elapsed_ns = STOP_TIMER(timer_name, ##__VA_ARGS__); \
615 if (_elapsed_ns >= 0.0 && (threshold_ns == 0 || _elapsed_ns >= (double)(threshold_ns))) { \
616 char _duration_str[32]; \
617 time_pretty((uint64_t)_elapsed_ns, -1, _duration_str, sizeof(_duration_str)); \
618 log_##log_level(msg_fmt " in %s", ##__VA_ARGS__, _duration_str); \
619 } \
620 } while (0)

◆ STOP_TIMER_AND_LOG_EVERY

#define STOP_TIMER_AND_LOG_EVERY (   log_level,
  interval_ns,
  threshold_ns,
  timer_name,
  msg_fmt,
  ... 
)

#include <time.h>

Value:
do { \
double _elapsed_ns = STOP_TIMER(timer_name, ##__VA_ARGS__); \
if (_elapsed_ns >= 0.0 && (threshold_ns == 0 || _elapsed_ns >= (double)(threshold_ns))) { \
static atomic_t _log_every_last_time = {0}; \
uint64_t _log_every_now = time_get_ns(); \
uint64_t _log_every_last = atomic_load_u64(&_log_every_last_time); \
if (_log_every_now - _log_every_last >= (uint64_t)(interval_ns)) { \
if (atomic_cas_u64(&_log_every_last_time, &_log_every_last, _log_every_now)) { \
char _duration_str[32]; \
time_pretty((uint64_t)_elapsed_ns, -1, _duration_str, sizeof(_duration_str)); \
log_##log_level(msg_fmt " in %s", ##__VA_ARGS__, _duration_str); \
} \
} \
} \
} while (0)
bool atomic_cas_u64(atomic_t *a, uint64_t *expected, uint64_t new_value)
Atomically compare-and-swap a uint64_t.
Definition atomic.c:264
uint64_t atomic_load_u64(atomic_t *a)
Atomically load a uint64_t value.
Definition atomic.c:233
uint64_t time_get_ns(void)
Get current monotonic time in nanoseconds.
Definition util/time.c:108
Atomic value wrapper for integral/boolean types.
Definition atomic.h:76

Stop a timer and log the result with rate limiting.

Combines STOP_TIMER() with rate-limited logging. The timer is stopped, elapsed time is retrieved, and a log message is generated only if:

  1. The elapsed time exceeds the threshold (if non-zero), AND
  2. The specified time interval has passed since the last logged occurrence

This prevents log spam from frequent operations while also filtering out fast operations.

Usage: STOP_TIMER_AND_LOG_EVERY(dev, 1000000000, 0, "opus_encode", "Opus encode completed"); STOP_TIMER_AND_LOG_EVERY(info, 5000000000, NS_PER_MS_INT, "process_frame_%d", "Frame %d processed", frame_id);

The macro will append " in X.XXms" (or appropriate unit) to your message automatically. Supported log levels: dev, debug, info, warn, error, fatal

Parameters
log_levelLog level name (dev, debug, info, warn, error, fatal - without log_ prefix)
interval_nsTime interval in nanoseconds between log emissions (rate limiting)
threshold_nsMinimum elapsed time in nanoseconds to trigger log (0 = no threshold)
timer_nameTimer name (must match START_TIMER call)
msg_fmtPrintf-style format string for the message
...Format arguments for both timer name and message

Definition at line 647 of file time.h.

648 { \
649 double _elapsed_ns = STOP_TIMER(timer_name, ##__VA_ARGS__); \
650 if (_elapsed_ns >= 0.0 && (threshold_ns == 0 || _elapsed_ns >= (double)(threshold_ns))) { \
651 static atomic_t _log_every_last_time = {0}; \
652 uint64_t _log_every_now = time_get_ns(); \
653 uint64_t _log_every_last = atomic_load_u64(&_log_every_last_time); \
654 if (_log_every_now - _log_every_last >= (uint64_t)(interval_ns)) { \
655 if (atomic_cas_u64(&_log_every_last_time, &_log_every_last, _log_every_now)) { \
656 char _duration_str[32]; \
657 time_pretty((uint64_t)_elapsed_ns, -1, _duration_str, sizeof(_duration_str)); \
658 log_##log_level(msg_fmt " in %s", ##__VA_ARGS__, _duration_str); \
659 } \
660 } \
661 } \
662 } while (0)

◆ STOP_TIMER_GLOBAL

#define STOP_TIMER_GLOBAL (   name_fmt,
  ... 
)

#include <time.h>

Value:
({ \
double _elapsed = -1.0; \
char _timer_name_buf[256]; \
snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
_elapsed = timer_stop(_timer_name_buf); \
} \
_elapsed; \
})

Stop a cross-thread timer and return elapsed time.

Use this variant when stopping a timer started with START_TIMER_GLOBAL. The timer name must match exactly (without thread ID appended).

Parameters
name_fmtPrintf-style format string for timer name (must match START_TIMER_GLOBAL)
...Format arguments (must match START_TIMER_GLOBAL to create same key)
Returns
Elapsed time in nanoseconds, or -1.0 if timer not found

Definition at line 377 of file time.h.

378 { \
379 double _elapsed = -1.0; \
380 if (timer_is_initialized()) { \
381 char _timer_name_buf[256]; \
382 snprintf(_timer_name_buf, sizeof(_timer_name_buf), name_fmt, ##__VA_ARGS__); \
383 _elapsed = timer_stop(_timer_name_buf); \
384 } \
385 _elapsed; \
386 })

◆ TIME_ATOMIC_UINT64

#define TIME_ATOMIC_UINT64   atomic_t

#include <time.h>

Definition at line 52 of file time.h.

◆ TIME_ATOMIC_UINT64_INIT

#define TIME_ATOMIC_UINT64_INIT (   val)    {0}

#include <time.h>

Definition at line 53 of file time.h.

◆ US_PER_MS_INT

#define US_PER_MS_INT   1000ULL

#include <time.h>

Definition at line 160 of file time.h.

◆ US_PER_SEC_INT

#define US_PER_SEC_INT   1000000ULL

#include <time.h>

Definition at line 161 of file time.h.

Typedef Documentation

◆ timer_record_t

typedef struct timer_record timer_record_t

#include <time.h>

Individual timer record for a named timing operation.

Function Documentation

◆ adaptive_sleep_calculate()

uint64_t adaptive_sleep_calculate ( adaptive_sleep_state_t *  state,
size_t  queue_depth,
size_t  target_depth 
)

#include <time.h>

Calculate adaptive sleep time based on queue depth.

Evaluates current queue depth against target and adjusts the speed multiplier:

  • If queue_depth > target_depth: speed up (reduce sleep, drain queue faster)
  • If queue_depth < target_depth: slow down (increase sleep back to baseline)
  • Speed multiplier changes gradually based on speedup_rate/slowdown_rate

The actual sleep time is calculated as: baseline_sleep_ns / current_speed_multiplier

Example usage:

.baseline_sleep_ns = 16666667, // ~60 FPS (16.67ms)
.min_speed_multiplier = 1.0, // Never slower than baseline
.max_speed_multiplier = 4.0, // Can process up to 4x faster (240 FPS)
.speedup_rate = 0.1, // Ramp up 10% per frame
.slowdown_rate = 0.05 // Ramp down 5% per frame
};
adaptive_sleep_init(&sleep_state, &config);
while (running) {
// Process one frame/packet/unit of work
process_data();
// Sleep adaptively based on queue depth
size_t queue_depth = get_queue_size();
uint64_t sleep_ns = adaptive_sleep_calculate(&sleep_state, queue_depth, 10);
platform_sleep_us(sleep_ns / 1000);
}
uint64_t adaptive_sleep_calculate(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
Calculate adaptive sleep time based on queue depth.
Definition util/time.c:630
void adaptive_sleep_init(adaptive_sleep_state_t *state, const adaptive_sleep_config_t *config)
Initialize adaptive sleep state with configuration.
Definition util/time.c:609
void platform_sleep_us(unsigned int us)
High-precision sleep function with microsecond precision.
Configuration for adaptive sleep behavior.
Definition time.h:677
uint64_t baseline_sleep_ns
Normal sleep time in nanoseconds (when queue is at target)
Definition time.h:678
Runtime state for adaptive sleep.
Definition time.h:693
Parameters
statePointer to adaptive sleep state (will be updated)
queue_depthCurrent number of items in queue/buffer
target_depthDesired queue depth (0 = empty queue target)
Returns
Sleep time in nanoseconds

Definition at line 630 of file util/time.c.

630 {
631 if (!state) {
632 return 0;
633 }
634
635 const adaptive_sleep_config_t *cfg = &state->config;
636
637 // Determine if we need to speed up or slow down
638 double desired_multiplier;
639
640 if (queue_depth > target_depth) {
641 // Queue is building up - speed up processing
642 // The more items in queue, the faster we should process
643 size_t backlog = queue_depth - target_depth;
644
645 // Calculate desired speed based on backlog
646 // Each item above target increases speed proportionally
647 // This ensures we drain the queue faster the more backed up we are
648 double backlog_factor = 1.0 + ((double)backlog / (double)(target_depth + 1));
649 desired_multiplier = cfg->min_speed_multiplier * backlog_factor;
650
651 // Clamp to max speed
652 if (desired_multiplier > cfg->max_speed_multiplier) {
653 desired_multiplier = cfg->max_speed_multiplier;
654 }
655
656 // Ramp up gradually based on speedup_rate
657 double delta = desired_multiplier - state->current_speed_multiplier;
658 state->current_speed_multiplier += delta * cfg->speedup_rate;
659
660 } else {
661 // Queue is at or below target - slow down to baseline
662 desired_multiplier = 1.0;
663 if (desired_multiplier < cfg->min_speed_multiplier) {
664 desired_multiplier = cfg->min_speed_multiplier;
665 }
666 if (desired_multiplier > cfg->max_speed_multiplier) {
667 desired_multiplier = cfg->max_speed_multiplier;
668 }
669
670 // Ramp down gradually based on slowdown_rate
671 double delta = desired_multiplier - state->current_speed_multiplier;
672 state->current_speed_multiplier += delta * cfg->slowdown_rate;
673 }
674
675 // Ensure we stay within configured bounds
678 }
681 }
682
683 // Calculate actual sleep time: baseline / speed_multiplier
684 // Higher multiplier = shorter sleep = faster processing
686
687 // Store for debugging
688 state->last_sleep_ns = sleep_ns;
689
690 return sleep_ns;
691}
double min_speed_multiplier
Minimum speed (max sleep) - usually 1.0 (baseline speed)
Definition time.h:679
double max_speed_multiplier
Maximum speed (min sleep) - e.g., 4.0 = process 4x faster.
Definition time.h:680
double speedup_rate
Ramp-up rate when queue builds (0.0-1.0, higher = faster ramp)
Definition time.h:681
double slowdown_rate
Ramp-down rate when queue empties (0.0-1.0, higher = faster ramp)
Definition time.h:682
double current_speed_multiplier
Current speed state (1.0 = baseline, >1.0 = faster)
Definition time.h:695
uint64_t last_sleep_ns
Last calculated sleep time (for debugging)
Definition time.h:696
adaptive_sleep_config_t config
Configuration (copied, not referenced)
Definition time.h:694

References adaptive_sleep_config_t::baseline_sleep_ns, adaptive_sleep_state_t::config, adaptive_sleep_state_t::current_speed_multiplier, adaptive_sleep_state_t::last_sleep_ns, adaptive_sleep_config_t::max_speed_multiplier, adaptive_sleep_config_t::min_speed_multiplier, adaptive_sleep_config_t::slowdown_rate, and adaptive_sleep_config_t::speedup_rate.

Referenced by adaptive_sleep_do().

◆ adaptive_sleep_do()

void adaptive_sleep_do ( adaptive_sleep_state_t *  state,
size_t  queue_depth,
size_t  target_depth 
)

#include <time.h>

Calculate sleep time and immediately sleep for that duration.

Convenience wrapper that combines adaptive_sleep_calculate() with platform_sleep_us(). Useful for simple loops that don't need to inspect the calculated sleep time.

Parameters
statePointer to adaptive sleep state (will be updated)
queue_depthCurrent number of items in queue/buffer
target_depthDesired queue depth (0 = empty queue target)

Definition at line 693 of file util/time.c.

693 {
694 uint64_t sleep_ns = adaptive_sleep_calculate(state, queue_depth, target_depth);
695
696 if (sleep_ns > 0) {
697 platform_sleep_ns(sleep_ns);
698 }
699}
void platform_sleep_ns(uint64_t ns)
Platform-safe sleep function with nanosecond precision.

References adaptive_sleep_calculate(), and platform_sleep_ns().

Referenced by session_capture_sleep_for_fps().

◆ adaptive_sleep_init()

void adaptive_sleep_init ( adaptive_sleep_state_t *  state,
const adaptive_sleep_config_t *  config 
)

#include <time.h>

Initialize adaptive sleep state with configuration.

Sets up an adaptive sleep state structure with the provided configuration. The configuration is copied internally, so the caller doesn't need to keep the config structure alive.

Parameters
statePointer to adaptive sleep state to initialize
configPointer to configuration (will be copied)

Definition at line 609 of file util/time.c.

609 {
610 if (!state || !config) {
611 return;
612 }
613
614 // Copy configuration
615 state->config = *config;
616
617 // A multiplier of 1.0 preserves the configured interval. Clamp it for
618 // configurations whose allowed range does not include the baseline.
619 state->current_speed_multiplier = 1.0;
620 if (state->current_speed_multiplier < config->min_speed_multiplier) {
622 }
623 if (state->current_speed_multiplier > config->max_speed_multiplier) {
625 }
626
628}

References adaptive_sleep_config_t::baseline_sleep_ns, adaptive_sleep_state_t::config, adaptive_sleep_state_t::current_speed_multiplier, adaptive_sleep_state_t::last_sleep_ns, adaptive_sleep_config_t::max_speed_multiplier, and adaptive_sleep_config_t::min_speed_multiplier.

Referenced by session_capture_create(), and session_network_capture_create().

◆ format_uptime_hms()

int format_uptime_hms ( int  hours,
int  minutes,
int  seconds,
char *  buffer,
size_t  buffer_size 
)

#include <time.h>

Format uptime as HH:MM:SS string.

Parameters
hoursHours component (0-999)
minutesMinutes component (0-59)
secondsSeconds component (0-59)
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Formats an uptime duration as a fixed "HH:MM:SS" string with zero-padding. Useful for status displays where consistent formatting is preferred over adaptive human-readable formats.

Examples:

  • format_uptime_hms(0, 0, 5, buf, size) -> "00:00:05"
  • format_uptime_hms(1, 30, 45, buf, size) -> "01:30:45"
  • format_uptime_hms(123, 45, 6, buf, size) -> "123:45:06"
Note
Buffer should be at least 12 bytes (HH:MM:SS + null terminator)
Thread-safe (no global state)
Hours component can exceed 99 (e.g., "123:45:06" for 123 hours)

Definition at line 354 of file util/time.c.

354 {
355 if (!buffer || buffer_size == 0) {
356 return -1;
357 }
358
359 // Validate components
360 if (hours < 0 || minutes < 0 || minutes >= 60 || seconds < 0 || seconds >= 60) {
361 return -1;
362 }
363
364 // Format as HH:MM:SS with zero-padding
365 int written = safe_snprintf(buffer, buffer_size, "%02d:%02d:%02d", hours, minutes, seconds);
366
367 if (written < 0 || (size_t)written >= buffer_size) {
368 return -1;
369 }
370
371 return written;
372}
int buffer_size
Size of circular buffer.
Definition grep.c:90
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148

References buffer_size, and safe_snprintf().

◆ time_elapsed_ns()

uint64_t time_elapsed_ns ( uint64_t  start_ns,
uint64_t  end_ns 
)

#include <time.h>

Calculate elapsed time with wraparound safety.

Computes the difference between two time values, handling potential wraparound of uint64_t (which won't happen in practice - uint64_t wraps after ~584 years at nanosecond resolution, but this is defensive).

Parameters
start_nsStart time in nanoseconds
end_nsEnd time in nanoseconds
Returns
Elapsed time in nanoseconds (end - start)

Usage:

// ... do work ...
uint64_t elapsed = time_elapsed_ns(start, time_get_ns());
uint64_t time_elapsed_ns(uint64_t start_ns, uint64_t end_ns)
Calculate elapsed time with wraparound safety.
Definition util/time.c:150

Definition at line 150 of file util/time.c.

150 {
151 // Handle wraparound (defensive - uint64_t won't wrap in practice at nanosecond resolution)
152 // but this is safe and handles any theoretical edge cases
153 if (end_ns >= start_ns) {
154 return end_ns - start_ns;
155 }
156 // Wraparound case (extremely unlikely)
157 return (UINT64_MAX - start_ns) + end_ns + 1;
158}

Referenced by ascii_convert_with_capabilities(), asciichat_instr_log_line(), audio_analysis_print_report(), broadcast_server_state_to_all_clients(), client_send_thread_func(), fps_frame_ns(), media_source_read_video(), media_source_seek(), mixer_process_excluding_source(), session_capture_get_current_fps(), session_capture_read_frame(), and session_client_like_run().

◆ time_format_is_valid_strftime()

bool time_format_is_valid_strftime ( const char *  format_str)

#include <time.h>

Validate strftime format string against known safe specifiers.

Parameters
format_strFormat string to validate (e.g., "%H:%M:%S")
Returns
true if valid, false if contains invalid specifiers or syntax

Validates format string by checking each % specifier against a whitelist of known safe POSIX strftime specifiers. Returns false if:

  • Contains unterminated % sequences
  • Contains invalid/unsupported specifiers
  • Contains malformed width/precision
  • Contains unsupported locale-dependent specifiers

Supported specifiers:

  • Date: Y (4-digit year), m (month), d (day), j (day of year)
  • Date (ISO): F (full date), G (ISO year), g (ISO year short), V (ISO week)
  • Time: H (24-hour), M (minute), S (second), I (12-hour), p (AM/PM)
  • Time (combined): T (full time HH:MM:SS), s (seconds since epoch)
  • Locale: a (abbrev weekday), A (full weekday), b (abbrev month), B (full month)
  • Locale: c (locale date/time), x (locale date), X (locale time)
  • Timezone: z (offset), Z (name)
  • Weekday: w (0-6), u (1-7)

Definition at line 711 of file util/time.c.

711 {
712 if (!format_str) {
713 return false;
714 }
715
716 for (const char *p = format_str; *p; p++) {
717 if (*p == '%') {
718 p++;
719 if (!*p) {
720 /* Unterminated % at end of string */
721 log_error("Invalid time format: unterminated %% at end");
722 return false;
723 }
724
725 if (*p == '%') {
726 /* Escaped %% - this is valid, just skip it */
727 continue;
728 }
729
730 /* Optional: flag characters (-, 0, +, space) */
731 if (*p == '-' || *p == '0' || *p == '+' || *p == ' ') {
732 p++;
733 if (!*p) {
734 log_error("Invalid time format: flag character without specifier");
735 return false;
736 }
737 }
738
739 /* Optional: width specifier (digits or *) */
740 if (*p == '*' || isdigit((unsigned char)*p)) {
741 while (*p && (isdigit((unsigned char)*p) || *p == '*')) {
742 p++;
743 }
744 if (!*p) {
745 log_error("Invalid time format: width specifier without specifier character");
746 return false;
747 }
748 }
749
750 /* Optional: precision specifier (.digits or .*) */
751 if (*p == '.') {
752 p++;
753 if (!*p) {
754 log_error("Invalid time format: precision specifier incomplete");
755 return false;
756 }
757 while (*p && (isdigit((unsigned char)*p) || *p == '*')) {
758 p++;
759 }
760 if (!*p) {
761 log_error("Invalid time format: precision specifier without specifier character");
762 return false;
763 }
764 }
765
766 /* Optional: modifier characters (E for %Ex, O for %Ox) */
767 if (*p == 'E' || *p == 'O') {
768 p++;
769 if (!*p) {
770 log_error("Invalid time format: modifier character without specifier");
771 return false;
772 }
773 }
774
775 /* Validate that the final character is a supported specifier */
776 if (!strchr(SUPPORTED_SPECIFIERS, *p)) {
777 log_error("Invalid time format: unsupported specifier %%%c (at position %ld)", *p, (p - format_str));
778 return false;
779 }
780 }
781 }
782
783 return true;
784}
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587

References log_error.

Referenced by time_format_safe().

◆ time_format_now()

int time_format_now ( const char *  format_str,
char *  buf,
size_t  buf_size 
)

#include <time.h>

Format current time using strftime format string.

Parameters
format_strstrftime format string (should be validated with time_format_is_valid_strftime)
bufOutput buffer
buf_sizeOutput buffer size
Returns
Number of characters written (excluding null terminator), or 0 on error

Formats current wall-clock time (CLOCK_REALTIME) using the provided strftime format. Handles nanosecond precision separately: if format contains S and buffer has room, appends ".NNNNNN" for microseconds (rounded from nanoseconds).

Example outputs:

  • Format "%H:%M:%S" → "14:30:45.123456"
  • Format "%Y-%m-%d" → "2026-02-16"
  • Format "%F %T" → "2026-02-16 14:30:45.123456"

Notes:

  • Buffer size should be at least 64 bytes
  • Automatic microsecond appending occurs only when S is in format
  • Output is locale-aware (UTF-8 safe from strftime)
  • Returns 0 on error (strftime failure or buffer overflow)

Definition at line 786 of file util/time.c.

786 {
787 if (!format_str || !buf || buf_size < 2) {
788 SET_ERRNO(ERROR_INVALID_PARAM, "time_format_now: invalid arguments - format_str=%p, buf=%p, buf_size=%zu",
789 format_str, buf, buf_size);
790 return 0;
791 }
792
793 /* Get current wall-clock time in nanoseconds */
795
796 /* Extract seconds and nanoseconds */
797 time_t seconds = (time_t)(ts_ns / NS_PER_SEC_INT);
798
799 /* Convert seconds to struct tm */
800 struct tm tm_info;
801 platform_localtime(&seconds, &tm_info);
802
803 /* Format using strftime */
804 size_t len = strftime(buf, buf_size, format_str, &tm_info);
805
806 /* Check for strftime errors or buffer overflow */
807 if (len == 0) {
808 /* strftime returned 0 - either error or exact buffer fill */
809 log_debug("strftime returned 0 for format: %s", format_str);
810 return 0;
811 }
812
813 if (len >= buf_size - 1) {
814 /* Buffer would overflow - strftime filled entire buffer or hit exact size */
815 log_error("time_format_now: buffer too small (need %zu, have %zu)", len + 1, buf_size);
816 return 0;
817 }
818
819 return (int)len;
820}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_PARAM
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
uint64_t time_get_realtime_ns(void)
Get current wall-clock (real) time in nanoseconds.
Definition util/time.c:119
#define NS_PER_SEC_INT
Definition time.h:157
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)
Platform-safe localtime wrapper.
Definition util.c:50

References ERROR_INVALID_PARAM, log_debug, log_error, NS_PER_SEC_INT, platform_localtime(), SET_ERRNO, and time_get_realtime_ns().

Referenced by log_template_apply(), and time_format_safe().

◆ time_format_safe()

asciichat_error_t time_format_safe ( const char *  format_str,
char *  buf,
size_t  buf_size 
)

#include <time.h>

Safe wrapper for time formatting with validation and error codes.

Parameters
format_strFormat string to use
bufOutput buffer
buf_sizeOutput buffer size
Returns
ASCIICHAT_OK on success, error code on failure (includes error context)

Validates format string first, then formats time. Returns detailed error if:

  • format_str is NULL
  • buf is NULL
  • buf_size < 64 (minimum safe size)
  • format_str contains invalid specifiers
  • strftime fails

All errors include context message via SET_ERRNO for debugging.

Example:

char timebuf[64];
asciichat_error_t err = time_format_safe("%H:%M:%S", timebuf, sizeof(timebuf));
if (err != ASCIICHAT_OK) {
log_error("Failed to format time");
return err;
}
log_info("Current time: %s", timebuf);
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
asciichat_error_t time_format_safe(const char *format_str, char *buf, size_t buf_size)
Safe wrapper for time formatting with validation and error codes.
Definition util/time.c:822

Definition at line 822 of file util/time.c.

822 {
823 /* Check NULL pointers */
824 if (!format_str) {
825 return SET_ERRNO(ERROR_INVALID_STATE, "time_format_safe: format_str is NULL");
826 }
827
828 if (!buf) {
829 return SET_ERRNO(ERROR_INVALID_STATE, "time_format_safe: buf is NULL");
830 }
831
832 /* Check minimum buffer size */
833 if (buf_size < 64) {
834 return SET_ERRNO(ERROR_INVALID_STATE, "time_format_safe: buffer too small (minimum 64 bytes, got %zu)", buf_size);
835 }
836
837 /* Validate format string */
838 if (!time_format_is_valid_strftime(format_str)) {
839 return SET_ERRNO(ERROR_INVALID_STATE, "time_format_safe: invalid time format: %s", format_str);
840 }
841
842 /* Format the time */
843 int result = time_format_now(format_str, buf, buf_size);
844 if (result <= 0) {
845 return SET_ERRNO(ERROR_INVALID_STATE, "time_format_safe: strftime formatting failed");
846 }
847
848 return ASCIICHAT_OK;
849}
@ ERROR_INVALID_STATE
bool time_format_is_valid_strftime(const char *format_str)
Validate strftime format string against known safe specifiers.
Definition util/time.c:711
int time_format_now(const char *format_str, char *buf, size_t buf_size)
Format current time using strftime format string.
Definition util/time.c:786

References ASCIICHAT_OK, ERROR_INVALID_STATE, SET_ERRNO, time_format_is_valid_strftime(), and time_format_now().

◆ time_get_ns()

uint64_t time_get_ns ( void  )

#include <time.h>

Get current monotonic time in nanoseconds.

Returns high-precision monotonic time that never goes backwards. Suitable for measuring elapsed time, FPS tracking, and performance metrics.

Returns
Current time in nanoseconds (monotonic, never decreases)

Usage:

// ... do work ...
uint64_t elapsed_ns = time_elapsed_ns(start, time_get_ns());

Definition at line 108 of file util/time.c.

108 {
109 // sokol_time provides monotonic clock that never goes backwards
110 // stm_ns() converts ticks to nanoseconds
111 // Ensure sokol_time is initialized before use (defensive programming)
112 if (!g_sokol_time_initialized) {
113 stm_setup();
114 g_sokol_time_initialized = true;
115 }
116 return (uint64_t)stm_ns(stm_now());
117}

Referenced by acds_client_handler(), acip_server_receive_and_dispatch(), ascii_convert_with_capabilities(), asciichat_instr_log_line(), atomic_on_cas(), atomic_on_fetch(), atomic_on_load(), atomic_on_store(), atomic_ptr_on_cas(), atomic_ptr_on_exchange(), atomic_ptr_on_load(), atomic_ptr_on_store(), audio_analysis_init(), audio_analysis_print_report(), audio_analysis_track_received_packet(), audio_analysis_track_sent_packet(), audio_write_samples(), broadcast_server_state_to_all_clients(), buffer_pool_free(), buffer_pool_shrink(), client_audio_render_thread(), client_dispatch_thread(), client_send_thread_func(), client_video_render_thread(), cond_format_state(), cond_on_broadcast(), cond_on_signal(), cond_on_wait(), consensus_coordinator_create(), consensus_coordinator_on_collection_start(), consensus_coordinator_process(), consensus_coordinator_time_until_next_round(), create_mixed_ascii_frame_for_client(), debug_atomic_format_timing(), debug_atomic_ptr_format_timing(), discovery_mdns_query(), discovery_session_process(), fps_counter_tick(), get_utf8_palette_cache(), media_source_read_video(), media_source_seek(), mixer_process_excluding_source(), mutex_format_state(), mutex_on_lock(), mutex_on_trylock(), mutex_on_unlock(), mutex_stack_mark_locked(), mutex_stack_push_pending(), nat_measure_bandwidth(), rwlock_format_state(), rwlock_on_rdlock(), rwlock_on_unlock(), rwlock_on_wrlock(), session_capture_create(), session_capture_get_current_fps(), session_capture_read_frame(), session_client_like_run(), session_display_convert_to_ascii(), session_display_create(), session_display_render_frame(), session_display_write_ascii(), session_network_capture_create(), session_pipeline_run_main(), session_render_loop(), simple_frame_swap_update(), splash_intro_done(), splash_intro_start(), time_human_readable_now(), time_pretty_now(), and websocket_server_run().

◆ time_get_realtime_ns()

uint64_t time_get_realtime_ns ( void  )

#include <time.h>

Get current wall-clock (real) time in nanoseconds.

Returns high-precision wall-clock time that can jump forwards or backwards when system time is adjusted. Use this for timestamps, database records, and user-facing time displays.

Returns
Current wall-clock time in nanoseconds (CLOCK_REALTIME)

Note: This time can jump backward if system clock is adjusted. For measuring elapsed time, use time_get_ns() instead.

Definition at line 119 of file util/time.c.

119 {
120#ifdef _WIN32
121 // Windows: Use GetSystemTimePreciseAsFileTime (Win8+) for high-precision wall clock
122 // FILETIME is 100-nanosecond intervals since January 1, 1601
123 FILETIME ft;
124 GetSystemTimePreciseAsFileTime(&ft);
125 // Convert FILETIME to nanoseconds
126 uint64_t filetime = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
127 // Convert 100-nanosecond intervals to nanoseconds
128 return filetime * 100;
129#else
130 // Get wall-clock (real-time) timestamp
131 struct timespec ts;
132 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
133 // Fallback on error (unlikely)
134 return 0;
135 }
136 return time_timespec_to_ns(&ts);
137#endif
138}
uint64_t time_timespec_to_ns(const struct timespec *ts)
Convert struct timespec to nanoseconds.
Definition util/time.c:164

References time_timespec_to_ns().

Referenced by acds_validate_timestamp(), asciichat_error_stats_record(), asciichat_instr_log_line(), asciichat_set_errno(), connection_attempt_tcp(), connection_attempt_websocket(), connection_check_timeout(), connection_context_init(), consensus_metrics_measure(), consensus_state_compute_election(), database_session_is_migration_ready(), database_session_start_migration(), get_current_time_formatted(), handle_image_frame_h265_packet(), log_json_async_safe(), log_msg(), log_plain_msg(), log_terminal_msg(), and time_format_now().

◆ time_human_readable()

int time_human_readable ( int64_t  nanoseconds,
char *  buffer,
size_t  buffer_size 
)

#include <time.h>

Format nanoseconds as human-readable relative duration (moment.js style)

Parameters
nanosecondsDuration in nanoseconds
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Formats a duration using moment.js-compatible thresholds to produce natural language relative time strings like "a few seconds ago", "3 minutes ago", "2 hours ago", etc.

Threshold table (moment.js compatible):

  • < 45 s → "a few seconds ago"
  • < 90 s → "a minute ago"
  • < 45 min → "%d minutes ago"
  • < 90 min → "an hour ago"
  • < 22 h → "%d hours ago"
  • < 36 h → "a day ago"
  • < 25 d → "%d days ago"
  • < 45 d → "a month ago"
  • < 11 months → "%d months ago"
  • < 18 months → "a year ago"
  • ≥ 18 months → "%d years ago"
Note
Buffer should be at least 32 bytes for all possible outputs
Thread-safe (no global state)

Format elapsed time as human-readable relative duration with past/future support

Parameters
nanosecondsSigned elapsed nanoseconds (negative for future times)
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Formats a duration as a human-readable string with moment.js-compatible thresholds. Supports both past times ("3 hours ago") and future times ("in 5 minutes").

Examples:

  • Positive: 3600000000000 ns → "an hour ago"
  • Negative: -300000000000 ns → "in 5 minutes"
Note
Use positive values for elapsed time (past), negative for countdown (future)
Buffer should be at least 32 bytes for all possible outputs
Thread-safe (no global state)

Definition at line 572 of file util/time.c.

572 {
573 if (!buffer || buffer_size == 0) {
574 return -1;
575 }
576
577 // Determine direction (past vs future)
578 bool is_future = nanoseconds < 0;
579 uint64_t abs_ns = is_future ? (uint64_t)(-nanoseconds) : (uint64_t)nanoseconds;
580
581 // Convert nanoseconds to seconds for threshold checking
582 double total_seconds = (double)abs_ns / NS_PER_SEC;
583 int minutes = (int)(total_seconds / 60);
584 int hours = minutes / 60;
585 int days = hours / 24;
586 int months = days / 30; // Approximate
587 int years = months / 12;
588
589 const char *suffix = is_future ? "from now" : "ago";
590 int written =
591 format_human_readable_time_ago(total_seconds, minutes, hours, days, months, years, suffix, buffer, buffer_size);
592
593 if (written < 0 || (size_t)written >= buffer_size) {
594 return -1;
595 }
596
597 return written;
598}
#define NS_PER_SEC
Definition time.h:148

References buffer_size, and NS_PER_SEC.

Referenced by time_human_readable_now().

◆ time_human_readable_now()

int time_human_readable_now ( char *  buffer,
size_t  buffer_size 
)

#include <time.h>

Format current monotonic time as human-readable relative duration.

Parameters
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Convenience wrapper that calls time_get_ns() and delegates to time_human_readable(). Useful for measuring time since program start in a single call.

Note
Buffer should be at least 32 bytes for all possible outputs
Thread-safe (no global state)

Definition at line 600 of file util/time.c.

600 {
601 int64_t ns = (int64_t)time_get_ns();
602 return time_human_readable(ns, buffer, buffer_size);
603}
int time_human_readable(int64_t nanoseconds, char *buffer, size_t buffer_size)
Format nanoseconds as human-readable relative duration (moment.js style)
Definition util/time.c:572

References buffer_size, time_get_ns(), and time_human_readable().

◆ time_human_readable_unsigned()

int time_human_readable_unsigned ( uint64_t  nanoseconds,
char *  buffer,
size_t  buffer_size 
)

#include <time.h>

Format elapsed time as human-readable relative duration (unsigned version)

Parameters
nanosecondsUnsigned elapsed nanoseconds (always past times)
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Formats unsigned nanosecond durations as human-readable past-time strings. Uses moment.js-compatible thresholds. All values are interpreted as elapsed time and formatted with "ago" suffix.

Examples:

  • 3600000000000 ns → "an hour ago"
  • 300000000000 ns → "5 minutes ago"
Note
Use this for values from time_get_ns() or other unsigned sources
Buffer should be at least 32 bytes for all possible outputs
Thread-safe (no global state)

Definition at line 549 of file util/time.c.

549 {
550 if (!buffer || buffer_size == 0) {
551 return -1;
552 }
553
554 // Convert nanoseconds to seconds for threshold checking
555 double total_seconds = (double)nanoseconds / NS_PER_SEC;
556 int minutes = (int)(total_seconds / 60);
557 int hours = minutes / 60;
558 int days = hours / 24;
559 int months = days / 30; // Approximate
560 int years = months / 12;
561
562 int written =
563 format_human_readable_time_ago(total_seconds, minutes, hours, days, months, years, "ago", buffer, buffer_size);
564
565 if (written < 0 || (size_t)written >= buffer_size) {
566 return -1;
567 }
568
569 return written;
570}

References buffer_size, and NS_PER_SEC.

◆ time_ns_to_timespec()

void time_ns_to_timespec ( uint64_t  ns,
struct timespec *  ts 
)

#include <time.h>

Convert nanoseconds to struct timespec.

Parameters
nsTime in nanoseconds
tsPointer to timespec structure (output)

Useful for nanosleep() or other system calls that require struct timespec.

Definition at line 178 of file util/time.c.

178 {
179 if (!ts) {
180 SET_ERRNO(ERROR_INVALID_PARAM, "null ts");
181 return;
182 }
183 ts->tv_sec = (time_t)(ns / NS_PER_SEC_INT);
184 ts->tv_nsec = (long)(ns % NS_PER_SEC_INT);
185}

References ERROR_INVALID_PARAM, NS_PER_SEC_INT, and SET_ERRNO.

◆ time_pretty()

int time_pretty ( uint64_t  nanoseconds,
int  decimals,
char *  buffer,
size_t  buffer_size 
)

#include <time.h>

Format nanoseconds as pretty duration with spaces and configurable precision.

Parameters
nanosecondsDuration in nanoseconds
decimalsNumber of decimal places (0-9), or -1 for sensible defaults per range
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Formats a duration with space-separated number and unit, with configurable precision. Automatically strips trailing zeros and decimal points.

Examples (with decimals=-1):

  • 500 ns → "500 ns"
  • 3500 ns → "3.5 µs"
  • 1253000 ns → "1.253 ms"
  • 2500000000 ns → "2.5 s"
  • 83456000000000 ns → "1:23.456" (minutes range, MM:SS.fraction)
  • 7530000000000 ns → "2:05:30" (hours range, H:MM:SS)

Unit selection logic:

  • [0, 1k) ns → "NNN ns"
  • [1k, 1M) ns → "N.N µs" (up to 3 decimals)
  • [1M, 1G) ns → "N.NNN ms" (up to 3 decimals)
  • [1G, 60G) ns → "N.NN s" (up to 2 decimals)
  • [60G, 3.6T) ns → "M:SS.fraction" (colon notation, up to 3 decimals)
  • [3.6T+) ns → "H:MM:SS" (colon notation)
Note
Buffer should be at least 32 bytes for all possible outputs
Thread-safe (no global state)
decimals=-1 uses appropriate defaults (3 for ns/µs/ms/colon, 2 for s)
decimals=0 produces no decimal point

Definition at line 424 of file util/time.c.

424 {
425 if (!buffer || buffer_size == 0) {
426 return -1;
427 }
428
429 // Handle zero
430 if (nanoseconds == 0) {
431 int written = safe_snprintf(buffer, buffer_size, "0 ns");
432 return (written < 0 || (size_t)written >= buffer_size) ? -1 : written;
433 }
434
435 // Determine decimals for this range if not explicitly set
436 int actual_decimals = decimals;
437 if (decimals == -1) {
438 if (nanoseconds < NS_PER_MIN_INT) {
439 actual_decimals = (nanoseconds < NS_PER_SEC_INT) ? 3 : 2; // ns/µs/ms or seconds
440 } else {
441 actual_decimals = 3; // colon notation
442 }
443 }
444
445 // Clamp decimals to valid range
446 if (actual_decimals < 0)
447 actual_decimals = 0;
448 if (actual_decimals > 9)
449 actual_decimals = 9;
450
451 int written = 0;
452
453 // Nanoseconds (< 1µs)
454 if (nanoseconds < NS_PER_US_INT) {
455 written = safe_snprintf(buffer, buffer_size, "%llu ns", (unsigned long long)nanoseconds);
456 }
457 // Microseconds (< 1ms)
458 else if (nanoseconds < NS_PER_MS_INT) {
459 double us = (double)nanoseconds / NS_PER_US;
460 char temp_buf[64];
461 // Use a direct snprintf with large enough buffer first
462 snprintf(temp_buf, sizeof(temp_buf), "%.*f µs", actual_decimals, us);
463 strip_trailing_zeros(temp_buf);
464 written = (int)strlen(temp_buf);
465 if (written > 0 && written < (int)buffer_size) {
466 SAFE_STRNCPY(buffer, temp_buf, buffer_size);
467 } else {
468 written = -1;
469 }
470 }
471 // Milliseconds (< 1s)
472 else if (nanoseconds < NS_PER_SEC_INT) {
473 double ms = (double)nanoseconds / NS_PER_MS;
474 char temp_buf[64];
475 snprintf(temp_buf, sizeof(temp_buf), "%.*f ms", actual_decimals, ms);
476 strip_trailing_zeros(temp_buf);
477 written = (int)strlen(temp_buf);
478 if (written > 0 && written < (int)buffer_size) {
479 SAFE_STRNCPY(buffer, temp_buf, buffer_size);
480 } else {
481 written = -1;
482 }
483 }
484 // Seconds (< 1 minute)
485 else if (nanoseconds < NS_PER_MIN_INT) {
486 double s = (double)nanoseconds / NS_PER_SEC;
487 char temp_buf[64];
488 snprintf(temp_buf, sizeof(temp_buf), "%.*f s", actual_decimals, s);
489 strip_trailing_zeros(temp_buf);
490 written = (int)strlen(temp_buf);
491 if (written > 0 && written < (int)buffer_size) {
492 SAFE_STRNCPY(buffer, temp_buf, buffer_size);
493 } else {
494 written = -1;
495 }
496 }
497 // Minutes (< 1 hour) - use colon notation: M:SS.frac
498 else if (nanoseconds < NS_PER_HOUR_INT) {
499 uint64_t total_seconds = nanoseconds / NS_PER_SEC_INT;
500 uint64_t minutes = total_seconds / 60;
501 uint64_t seconds = total_seconds % 60;
502 uint64_t remainder_ns = nanoseconds % NS_PER_SEC_INT;
503 double fraction = (double)remainder_ns / NS_PER_SEC;
504
505 char temp_buf[64];
506 if (actual_decimals == 0) {
507 snprintf(temp_buf, sizeof(temp_buf), "%llu:%02llu", (unsigned long long)minutes, (unsigned long long)seconds);
508 } else {
509 snprintf(temp_buf, sizeof(temp_buf), "%llu:%02llu.%.*f", (unsigned long long)minutes, (unsigned long long)seconds,
510 actual_decimals, fraction);
511 strip_trailing_zeros(temp_buf);
512 }
513
514 written = (int)strlen(temp_buf);
515 if (written > 0 && written < (int)buffer_size) {
516 SAFE_STRNCPY(buffer, temp_buf, buffer_size);
517 } else {
518 written = -1;
519 }
520 }
521 // Hours+ - use colon notation: H:MM:SS or HH:MM:SS
522 else {
523 uint64_t total_seconds = nanoseconds / NS_PER_SEC_INT;
524 uint64_t hours = total_seconds / 3600;
525 uint64_t remaining = total_seconds % 3600;
526 uint64_t minutes = remaining / 60;
527 uint64_t seconds = remaining % 60;
528
529 written = safe_snprintf(buffer, buffer_size, "%llu:%02llu:%02llu", (unsigned long long)hours,
530 (unsigned long long)minutes, (unsigned long long)seconds);
531 }
532
533 if (written < 0 || (size_t)written >= buffer_size) {
534 return -1;
535 }
536
537 return written;
538}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define NS_PER_MIN_INT
Definition time.h:172
#define NS_PER_HOUR_INT
Definition time.h:173
#define NS_PER_MS_INT
Definition time.h:156
#define NS_PER_MS
Definition time.h:147
#define NS_PER_US_INT
Definition time.h:155
#define NS_PER_US
Definition time.h:146

References buffer_size, NS_PER_HOUR_INT, NS_PER_MIN_INT, NS_PER_MS, NS_PER_MS_INT, NS_PER_SEC, NS_PER_SEC_INT, NS_PER_US, NS_PER_US_INT, safe_snprintf(), and SAFE_STRNCPY.

Referenced by acip_server_receive_and_dispatch(), ascii_convert_with_capabilities(), asciichat_instr_log_line(), broadcast_server_state_to_all_clients(), client_send_thread_func(), client_video_render_thread(), cond_format_state(), connection_check_timeout(), create_mixed_ascii_frame_for_client(), crypto_init(), crypto_rekey_init(), debug_atomic_format_timing(), debug_atomic_ptr_format_timing(), media_source_seek(), mixer_process_excluding_source(), mutex_format_state(), rwlock_format_state(), session_display_convert_to_ascii(), session_display_write_ascii(), session_render_loop(), time_pretty_now(), and timer_stop().

◆ time_pretty_now()

int time_pretty_now ( int  decimals,
char *  buffer,
size_t  buffer_size 
)

#include <time.h>

Format current monotonic time as pretty duration.

Parameters
decimalsNumber of decimal places (0-9), or -1 for sensible defaults per range
bufferOutput buffer for formatted string
buffer_sizeSize of output buffer
Returns
Number of characters written (excluding null terminator), or -1 on error

Convenience wrapper that calls time_get_ns() and delegates to time_pretty(). Useful for measuring elapsed time since program start in a single call.

Note
Buffer should be at least 32 bytes for all possible outputs
Thread-safe (no global state)

Definition at line 540 of file util/time.c.

540 {
541 uint64_t ns = time_get_ns();
542 return time_pretty(ns, decimals, buffer, buffer_size);
543}

References buffer_size, time_get_ns(), and time_pretty().

◆ time_sleep_ns()

void time_sleep_ns ( uint64_t  ns)

#include <time.h>

Sleep for specified nanoseconds.

Sleeps the current thread for at least the specified number of nanoseconds. On some systems, actual sleep time may be slightly longer due to scheduler granularity.

Parameters
nsNumber of nanoseconds to sleep

Definition at line 140 of file util/time.c.

140 {
141 // Use platform abstraction for cross-platform sleep
142 // Convert nanoseconds to microseconds (1 us = 1000 ns)
143 unsigned int usec = (unsigned int)(ns / 1000);
144 if (usec == 0 && ns > 0) {
145 usec = 1; // Minimum 1 microsecond
146 }
147 platform_sleep_us(usec);
148}

References platform_sleep_us().

◆ time_timespec_to_ns()

uint64_t time_timespec_to_ns ( const struct timespec *  ts)

#include <time.h>

Convert struct timespec to nanoseconds.

Parameters
tsPointer to timespec structure
Returns
Time in nanoseconds

Useful for converting CLOCK_MONOTONIC or CLOCK_REALTIME readings to nanoseconds.

Definition at line 164 of file util/time.c.

164 {
165 if (!ts) {
166 SET_ERRNO(ERROR_INVALID_PARAM, "null ts");
167 return 0;
168 }
169 // Use __uint128_t to prevent overflow during multiplication of large time values
170 __uint128_t result = (__uint128_t)ts->tv_sec * NS_PER_SEC_INT;
171 if (result > UINT64_MAX) {
172 SET_ERRNO(ERROR_INVALID_PARAM, "time_timespec_to_ns: timespec value would overflow");
173 return UINT64_MAX;
174 }
175 return (uint64_t)result + (uint64_t)ts->tv_nsec;
176}

References ERROR_INVALID_PARAM, NS_PER_SEC_INT, and SET_ERRNO.

Referenced by time_get_realtime_ns().

◆ timer_is_initialized()

bool timer_is_initialized ( void  )

#include <time.h>

Check if timing system is initialized.

Returns
true if initialized, false otherwise

Definition at line 346 of file util/time.c.

346 {
347 return lifecycle_is_initialized(&g_timer_manager.lifecycle);
348}
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155

References lifecycle_is_initialized().

◆ timer_start()

bool timer_start ( const char *  name)

#include <time.h>

Start a named timer.

Creates a new timer record and stores the start time. If a timer with the same name exists, it will be overwritten.

Parameters
nameTimer name (must be unique, will be copied)
Returns
true on success, false on failure

Definition at line 240 of file util/time.c.

240 {
241 if (!lifecycle_is_initialized(&g_timer_manager.lifecycle)) {
242 SET_ERRNO(ERROR_INVALID_STATE, "Timer system not initialized");
243 return false;
244 }
245
246 if (!name) {
247 SET_ERRNO(ERROR_INVALID_PARAM, "Timer name is NULL");
248 return false;
249 }
250
251 rwlock_wrlock(&g_timer_manager.rwlock);
252
253 // Check if timer already exists - this is an error, timers must be unique
254 timer_record_t *existing = NULL;
255 HASH_FIND_STR(g_timer_manager.timers, name, existing);
256
257 if (existing) {
258 rwlock_wrunlock(&g_timer_manager.rwlock);
259 SET_ERRNO(ERROR_INVALID_STATE, "Timer '%s' already exists - timers must have unique names", name);
260 return false;
261 }
262
263 // Create new timer record
265 if (!timer) {
266 rwlock_wrunlock(&g_timer_manager.rwlock);
267 SET_ERRNO(ERROR_MEMORY, "Failed to allocate timer record");
268 return false;
269 }
270
271 // Use platform-safe string duplication
272 size_t name_len = strlen(name) + 1;
273 timer->name = SAFE_MALLOC(name_len, char *);
274 if (!timer->name) {
275 SAFE_FREE(timer);
276 rwlock_wrunlock(&g_timer_manager.rwlock);
277 SET_ERRNO(ERROR_MEMORY, "Failed to allocate timer name");
278 return false;
279 }
280 SAFE_STRNCPY(timer->name, name, name_len);
281
282 timer->start_ticks = stm_now();
283
284 // Add to hashtable
285 HASH_ADD_KEYPTR(hh, g_timer_manager.timers, timer->name, strlen(timer->name), timer);
286
287 rwlock_wrunlock(&g_timer_manager.rwlock);
288 return true;
289}
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
@ ERROR_MEMORY
Definition error_codes.h:56
#define rwlock_wrunlock(lock)
Release a write lock (with debug tracking in debug builds)
Definition rwlock.h:349
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds)
Definition rwlock.h:313
Individual timer record for a named timing operation.
Definition time.h:248
uint64_t start_ticks
Start time in sokol ticks.
Definition time.h:250
char * name
Timer name (heap-allocated, unique key)
Definition time.h:249

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, lifecycle_is_initialized(), timer_record::name, rwlock_wrlock, rwlock_wrunlock, SAFE_FREE, SAFE_MALLOC, SAFE_STRNCPY, SET_ERRNO, and timer_record::start_ticks.

◆ timer_stop()

double timer_stop ( const char *  name)

#include <time.h>

Stop a named timer and return elapsed time.

Looks up the timer by name, calculates elapsed time, logs it, and removes the timer from the hashtable.

Parameters
nameTimer name to stop
Returns
Elapsed time in nanoseconds, or -1.0 if timer not found

Definition at line 291 of file util/time.c.

291 {
292 if (!lifecycle_is_initialized(&g_timer_manager.lifecycle)) {
293 SET_ERRNO(ERROR_INVALID_STATE, "Timer system not initialized");
294 return -1.0;
295 }
296
297 if (!name) {
298 SET_ERRNO(ERROR_INVALID_PARAM, "Timer name is NULL");
299 return -1.0;
300 }
301
302 rwlock_wrlock(&g_timer_manager.rwlock);
303
304 // Find timer
305 timer_record_t *timer = NULL;
306 HASH_FIND_STR(g_timer_manager.timers, name, timer);
307
308 if (!timer) {
309 rwlock_wrunlock(&g_timer_manager.rwlock);
310 log_dev("Timer '%s' not found (may have already been stopped)", name);
311 return -1.0;
312 }
313
314 // Calculate elapsed time in nanoseconds for maximum precision
315 uint64_t end_ticks = stm_now();
316 uint64_t elapsed_ticks = stm_diff(end_ticks, timer->start_ticks);
317 double elapsed_ns = stm_ns(elapsed_ticks);
318
319 // Format duration for human-readable logging
320 char duration_str[32];
321 time_pretty((uint64_t)elapsed_ns, -1, duration_str, sizeof(duration_str));
322
323 // Log the result (dev level - only shown with --verbose)
324 log_dev("Timer '%s': %s", name, duration_str);
325
326 // Save pointers to free BEFORE removing from hashtable
327 char *timer_name = timer->name;
328
329 // Remove from hashtable (HASH_DEL only removes from hash, doesn't free memory)
330 HASH_DEL(g_timer_manager.timers, timer);
331
332 // Free timer->name first (this was allocated separately)
333 SAFE_FREE(timer_name);
334
335 // Nullify the timer->name field so debug_free doesn't try to access it
336 // when freeing the timer structure itself
337 timer->name = NULL;
338
339 // Free the timer record itself
340 SAFE_FREE(timer);
341
342 rwlock_wrunlock(&g_timer_manager.rwlock);
343 return elapsed_ns; // Return nanoseconds
344}
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, lifecycle_is_initialized(), log_dev, timer_record::name, rwlock_wrlock, rwlock_wrunlock, SAFE_FREE, SET_ERRNO, timer_record::start_ticks, and time_pretty().

◆ timer_system_destroy()

void timer_system_destroy ( void  )

#include <time.h>

Cleanup the timing system.

Frees all timer records and destroys mutex. Should be called at program exit.

Definition at line 213 of file util/time.c.

213 {
214 if (!lifecycle_is_initialized(&g_timer_manager.lifecycle)) {
215 return;
216 }
217
218 rwlock_wrlock(&g_timer_manager.rwlock);
219
220 // Free all timer records
221 int timer_count = 0;
222 timer_record_t *current, *tmp;
223 HASH_ITER(hh, g_timer_manager.timers, current, tmp) {
224 HASH_DEL(g_timer_manager.timers, current);
225 SAFE_FREE(current->name);
226 SAFE_FREE(current);
227 timer_count++;
228 }
229
230 g_timer_manager.timers = NULL;
231
232 rwlock_wrunlock(&g_timer_manager.rwlock);
233
234 /* Shutdown lifecycle and destroy rwlock together */
235 lifecycle_shutdown(&g_timer_manager.lifecycle);
236
237 log_debug("Timer system cleaned up (freed %d timers)", timer_count);
238}
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108

References lifecycle_is_initialized(), lifecycle_shutdown(), log_debug, timer_record::name, rwlock_wrlock, rwlock_wrunlock, and SAFE_FREE.

Referenced by asciichat_shared_destroy().

◆ timer_system_init()

bool timer_system_init ( void  )

#include <time.h>

Initialize the timing system.

Must be called before using any timer functions. Automatically calls stm_setup() for sokol_time initialization.

Returns
true on success, false on failure

Definition at line 191 of file util/time.c.

191 {
192 if (lifecycle_is_initialized(&g_timer_manager.lifecycle)) {
193 return true;
194 }
195
196 // Initialize sokol_time
197 stm_setup();
198
199 // Initialize rwlock and lifecycle together
200 g_timer_manager.lifecycle.sync_type = LIFECYCLE_SYNC_RWLOCK;
201 g_timer_manager.lifecycle.sync.rwlock = &g_timer_manager.rwlock;
202 if (!lifecycle_init(&g_timer_manager.lifecycle, "timer_manager")) {
203 SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to initialize timer system (already initialized by another thread)");
204 return true; // Another thread already initialized it
205 }
206
207 g_timer_manager.timers = NULL;
208
209 log_dev("Timer system initialized");
210 return true;
211}
@ ERROR_PLATFORM_INIT
Definition error_codes.h:60
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26
@ LIFECYCLE_SYNC_RWLOCK
Contains rwlock_t pointer.
Definition lifecycle.h:51

References ERROR_PLATFORM_INIT, lifecycle_init(), lifecycle_is_initialized(), LIFECYCLE_SYNC_RWLOCK, log_dev, and SET_ERRNO.

Referenced by asciichat_shared_init().