#ifndef INCLUDED_C_STOPWATCH_H #define INCLUDED_C_STOPWATCH_H #ifndef INCLUDED_C_TYPES_H #include #endif /*INCLUDED_C_TYPES_H*/ /* ------------------------------------------------------------------------------------------------------------------ */ /* */ #if defined(_WIN32) || defined(_WIN64) #include typedef LARGE_INTEGER c_TimePoint_t; #else #include #include typedef struct timespec c_TimePoint_t; #endif typedef struct { c_TimePoint_t start_time; double elapsed_milliseconds; c_bool_t is_running; } c_Stopwatch_t; /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /** * Internal private helper: Captures the system's hardware clock monotonic timestamp. */ C_STATIC_FORCE_INLINE void c_Stopwatch_GetTimePoint(c_TimePoint_t* tp) { #if defined(_WIN32) || defined(_WIN64) QueryPerformanceCounter(tp); #else // Using CLOCK_MONOTONIC to guarantee safety against system time changes/NTP adjustments clock_gettime(CLOCK_MONOTONIC, tp); #endif } /** * Internal private helper: Computes the difference in seconds between two points. */ C_STATIC_FORCE_INLINE double c_Stopwatch_ComputeDiffInS(const c_TimePoint_t* start, const c_TimePoint_t* end) { #if defined(_WIN32) || defined(_WIN64) LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return (double)(end->QuadPart - start->HighPart) / (double)freq.QuadPart; #else double start_sec = (double)start->tv_sec + (double)start->tv_nsec / 1e9; double end_sec = (double)end->tv_sec + (double)end->tv_nsec / 1e9; return end_sec - start_sec; #endif } /** * Calculate the delta time in milliseconds between two distinct time points. * Time Complexity: O(1) | Auxiliary Space: O(1) * @param start Pointer to the starting time point timestamp. * @param end Pointer to the ending time point timestamp. * @return The double precision scalar difference value in milliseconds, * or -1.0 if any parameter pointer is NULL. */ C_STATIC_FORCE_INLINE double c_Stopwatch_ComputeDiffInMS(const c_TimePoint_t* start, const c_TimePoint_t* end) { if (start == NULL || end == NULL) return -1.0; #if defined(_WIN32) || defined(_WIN64) LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); // Convert to seconds first, then scale up to milliseconds double seconds = (double)(end->QuadPart - start->QuadPart) / (double)freq.QuadPart; return seconds * 1000.0; #else double start_ms = ((double)start->tv_sec * 1000.0) + ((double)start->tv_nsec / 1e6); double end_ms = ((double)end->tv_sec * 1000.0) + ((double)end->tv_nsec / 1e6); return end_ms - start_ms; #endif } /** * Initialize the Stopwatch. Registers parameters to default states cleanly. */ C_STATIC_FORCE_INLINE c_err_t c_Stopwatch_Init(c_Stopwatch_t* sw) { if (sw == NULL) return C_ERR_PARAM; sw->elapsed_milliseconds = 0.0; sw->is_running = C_FALSE; memset(&sw->start_time, 0, sizeof(c_TimePoint_t)); return C_ERR_OK; } /** * Start or resume tracking time. */ C_STATIC_FORCE_INLINE c_err_t c_Stopwatch_Start(c_Stopwatch_t* sw) { if (sw == NULL) return C_ERR_PARAM; if (sw->is_running) return C_ERR_OK; // Safe skip if already processing c_Stopwatch_GetTimePoint(&sw->start_time); sw->is_running = C_TRUE; return C_ERR_OK; } /** * Stop tracking time and cache the elapsed segment into the accumulator. */ C_STATIC_FORCE_INLINE c_err_t c_Stopwatch_Stop(c_Stopwatch_t* sw) { if (sw == NULL) return C_ERR_PARAM; if (!sw->is_running) return C_ERR_OK; c_TimePoint_t end_time; c_Stopwatch_GetTimePoint(&end_time); // Add the delta directly to the millisecond buffer field sw->elapsed_milliseconds += c_Stopwatch_ComputeDiffInMS(&sw->start_time, &end_time); sw->is_running = C_FALSE; return C_ERR_OK; } /** * Soft Reset: Blasts elapsed counts down to zero while preserving the active running state. */ C_STATIC_FORCE_INLINE c_err_t c_Stopwatch_Clear(c_Stopwatch_t* sw) { if (sw == NULL) return C_ERR_PARAM; sw->elapsed_milliseconds = 0.0; if (sw->is_running) { c_Stopwatch_GetTimePoint(&sw->start_time); // Re-anchor start time mark to prevent jumps } return C_ERR_OK; } /** * Utility helper: Extract elapsed timing down to millisecond intervals. */ C_STATIC_FORCE_INLINE double c_Stopwatch_GetElapsedMilliseconds(const c_Stopwatch_t* sw) { if (sw == NULL) return 0.0; if (!sw->is_running) return sw->elapsed_milliseconds; // Pure O(1) cache read c_TimePoint_t active_tick; c_Stopwatch_GetTimePoint(&active_tick); // Dynamically add current active delta segment to the base accumulator return sw->elapsed_milliseconds + c_Stopwatch_ComputeDiffInMS(&sw->start_time, &active_tick); } /** * Extract total measured elapsed time in seconds up to this exact moment. * Works perfectly whether the stopwatch is running or stopped (Lap peeking feature). * Time Complexity: O(1) | Auxiliary Space: O(1) in-place * @param sw Pointer to the constant stopwatch instance context. * @return The double precision scalar elapsed time value in seconds, * or 0.0 if the stopwatch instance handle is NULL. */ C_STATIC_FORCE_INLINE double c_Stopwatch_GetElapsedSeconds(const c_Stopwatch_t* sw) { if (sw == NULL) return 0.0; // Leverage the existing GetElapsedMilliseconds API and scale it down to second precision. // This maintains perfect abstraction layer unity without duplicating clock read conditions. return c_Stopwatch_GetElapsedMilliseconds(sw) / 1000.0; } #endif /*INCLUDED_C_STOPWATCH_H*/