|
diff --git a/minunit.h b/minunit.h
|
|
|
1 |
/* |
|
|
2 |
* Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com |
|
|
3 |
* |
|
|
4 |
* Permission is hereby granted, free of charge, to any person obtaining |
|
|
5 |
* a copy of this software and associated documentation files (the |
|
|
6 |
* "Software"), to deal in the Software without restriction, including |
|
|
7 |
* without limitation the rights to use, copy, modify, merge, publish, |
|
|
8 |
* distribute, sublicense, and/or sell copies of the Software, and to |
|
|
9 |
* permit persons to whom the Software is furnished to do so, subject to |
|
|
10 |
* the following conditions: |
|
|
11 |
* |
|
|
12 |
* The above copyright notice and this permission notice shall be |
|
|
13 |
* included in all copies or substantial portions of the Software. |
|
|
14 |
* |
|
|
15 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
|
16 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
|
17 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
|
18 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
|
19 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
|
20 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
|
21 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
22 |
*/ |
|
|
23 |
#ifndef MINUNIT_MINUNIT_H |
|
|
24 |
#define MINUNIT_MINUNIT_H |
|
|
25 |
|
|
|
26 |
#ifdef __cplusplus |
|
|
27 |
extern "C" { |
|
|
28 |
#endif |
|
|
29 |
|
|
|
30 |
#if defined(_WIN32) |
|
|
31 |
#include <Windows.h> |
|
|
32 |
#if defined(_MSC_VER) && _MSC_VER < 1900 |
|
|
33 |
#define snprintf _snprintf |
|
|
34 |
#define __func__ __FUNCTION__ |
|
|
35 |
#endif |
|
|
36 |
|
|
|
37 |
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) |
|
|
38 |
|
|
|
39 |
/* Change POSIX C SOURCE version for pure c99 compilers */ |
|
|
40 |
#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L |
|
|
41 |
#undef _POSIX_C_SOURCE |
|
|
42 |
#define _POSIX_C_SOURCE 200112L |
|
|
43 |
#endif |
|
|
44 |
|
|
|
45 |
#include <unistd.h> /* POSIX flags */ |
|
|
46 |
#include <time.h> /* clock_gettime(), time() */ |
|
|
47 |
#include <sys/time.h> /* gethrtime(), gettimeofday() */ |
|
|
48 |
#include <sys/resource.h> |
|
|
49 |
#include <sys/times.h> |
|
|
50 |
#include <string.h> |
|
|
51 |
|
|
|
52 |
#if defined(__MACH__) && defined(__APPLE__) |
|
|
53 |
#include <mach/mach.h> |
|
|
54 |
#include <mach/mach_time.h> |
|
|
55 |
#endif |
|
|
56 |
|
|
|
57 |
#if __GNUC__ >= 5 && !defined(__STDC_VERSION__) |
|
|
58 |
#define __func__ __extension__ __FUNCTION__ |
|
|
59 |
#endif |
|
|
60 |
|
|
|
61 |
#else |
|
|
62 |
#error "Unable to define timers for an unknown OS." |
|
|
63 |
#endif |
|
|
64 |
|
|
|
65 |
#include <stdio.h> |
|
|
66 |
#include <math.h> |
|
|
67 |
|
|
|
68 |
/* Maximum length of last message */ |
|
|
69 |
#define MINUNIT_MESSAGE_LEN 1024 |
|
|
70 |
/* Accuracy with which floats are compared */ |
|
|
71 |
#define MINUNIT_EPSILON 1E-12 |
|
|
72 |
|
|
|
73 |
/* Misc. counters */ |
|
|
74 |
static int minunit_run = 0; |
|
|
75 |
static int minunit_assert = 0; |
|
|
76 |
static int minunit_fail = 0; |
|
|
77 |
static int minunit_status = 0; |
|
|
78 |
|
|
|
79 |
/* Timers */ |
|
|
80 |
static double minunit_real_timer = 0; |
|
|
81 |
static double minunit_proc_timer = 0; |
|
|
82 |
|
|
|
83 |
/* Last message */ |
|
|
84 |
static char minunit_last_message[MINUNIT_MESSAGE_LEN]; |
|
|
85 |
|
|
|
86 |
/* Test setup and teardown function pointers */ |
|
|
87 |
static void (*minunit_setup)(void) = NULL; |
|
|
88 |
static void (*minunit_teardown)(void) = NULL; |
|
|
89 |
|
|
|
90 |
/* Definitions */ |
|
|
91 |
#define MU_TEST(method_name) static void method_name(void) |
|
|
92 |
#define MU_TEST_SUITE(suite_name) static void suite_name(void) |
|
|
93 |
|
|
|
94 |
#define MU__SAFE_BLOCK(block) do {\ |
|
|
95 |
block\ |
|
|
96 |
} while(0) |
|
|
97 |
|
|
|
98 |
/* Run test suite and unset setup and teardown functions */ |
|
|
99 |
#define MU_RUN_SUITE(suite_name) MU__SAFE_BLOCK(\ |
|
|
100 |
suite_name();\ |
|
|
101 |
minunit_setup = NULL;\ |
|
|
102 |
minunit_teardown = NULL;\ |
|
|
103 |
) |
|
|
104 |
|
|
|
105 |
/* Configure setup and teardown functions */ |
|
|
106 |
#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) MU__SAFE_BLOCK(\ |
|
|
107 |
minunit_setup = setup_fun;\ |
|
|
108 |
minunit_teardown = teardown_fun;\ |
|
|
109 |
) |
|
|
110 |
|
|
|
111 |
/* Test runner */ |
|
|
112 |
#define MU_RUN_TEST(test) MU__SAFE_BLOCK(\ |
|
|
113 |
if (minunit_real_timer==0 && minunit_proc_timer==0) {\ |
|
|
114 |
minunit_real_timer = mu_timer_real();\ |
|
|
115 |
minunit_proc_timer = mu_timer_cpu();\ |
|
|
116 |
}\ |
|
|
117 |
if (minunit_setup) (*minunit_setup)();\ |
|
|
118 |
minunit_status = 0;\ |
|
|
119 |
test();\ |
|
|
120 |
minunit_run++;\ |
|
|
121 |
if (minunit_status) {\ |
|
|
122 |
minunit_fail++;\ |
|
|
123 |
printf("F");\ |
|
|
124 |
printf("\n%s\n", minunit_last_message);\ |
|
|
125 |
}\ |
|
|
126 |
(void)fflush(stdout);\ |
|
|
127 |
if (minunit_teardown) (*minunit_teardown)();\ |
|
|
128 |
) |
|
|
129 |
|
|
|
130 |
/* Report */ |
|
|
131 |
#define MU_REPORT() MU__SAFE_BLOCK(\ |
|
|
132 |
double minunit_end_real_timer;\ |
|
|
133 |
double minunit_end_proc_timer;\ |
|
|
134 |
printf("\n\n%d tests, %d assertions, %d failures\n", minunit_run, minunit_assert, minunit_fail);\ |
|
|
135 |
minunit_end_real_timer = mu_timer_real();\ |
|
|
136 |
minunit_end_proc_timer = mu_timer_cpu();\ |
|
|
137 |
printf("\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n",\ |
|
|
138 |
minunit_end_real_timer - minunit_real_timer,\ |
|
|
139 |
minunit_end_proc_timer - minunit_proc_timer);\ |
|
|
140 |
) |
|
|
141 |
#define MU_EXIT_CODE minunit_fail |
|
|
142 |
|
|
|
143 |
/* Assertions */ |
|
|
144 |
#define mu_check(test) MU__SAFE_BLOCK(\ |
|
|
145 |
minunit_assert++;\ |
|
|
146 |
if (!(test)) {\ |
|
|
147 |
(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, #test);\ |
|
|
148 |
minunit_status = 1;\ |
|
|
149 |
return;\ |
|
|
150 |
} else {\ |
|
|
151 |
printf(".");\ |
|
|
152 |
}\ |
|
|
153 |
) |
|
|
154 |
|
|
|
155 |
#define mu_fail(message) MU__SAFE_BLOCK(\ |
|
|
156 |
minunit_assert++;\ |
|
|
157 |
(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ |
|
|
158 |
minunit_status = 1;\ |
|
|
159 |
return;\ |
|
|
160 |
) |
|
|
161 |
|
|
|
162 |
#define mu_assert(test, message) MU__SAFE_BLOCK(\ |
|
|
163 |
minunit_assert++;\ |
|
|
164 |
if (!(test)) {\ |
|
|
165 |
(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ |
|
|
166 |
minunit_status = 1;\ |
|
|
167 |
return;\ |
|
|
168 |
} else {\ |
|
|
169 |
printf(".");\ |
|
|
170 |
}\ |
|
|
171 |
) |
|
|
172 |
|
|
|
173 |
#define mu_assert_int_eq(expected, result) MU__SAFE_BLOCK(\ |
|
|
174 |
int minunit_tmp_e;\ |
|
|
175 |
int minunit_tmp_r;\ |
|
|
176 |
minunit_assert++;\ |
|
|
177 |
minunit_tmp_e = (expected);\ |
|
|
178 |
minunit_tmp_r = (result);\ |
|
|
179 |
if (minunit_tmp_e != minunit_tmp_r) {\ |
|
|
180 |
(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d expected but was %d", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ |
|
|
181 |
minunit_status = 1;\ |
|
|
182 |
return;\ |
|
|
183 |
} else {\ |
|
|
184 |
printf(".");\ |
|
|
185 |
}\ |
|
|
186 |
) |
|
|
187 |
|
|
|
188 |
#define mu_assert_double_eq(expected, result) MU__SAFE_BLOCK(\ |
|
|
189 |
double minunit_tmp_e;\ |
|
|
190 |
double minunit_tmp_r;\ |
|
|
191 |
minunit_assert++;\ |
|
|
192 |
minunit_tmp_e = (expected);\ |
|
|
193 |
minunit_tmp_r = (result);\ |
|
|
194 |
if (fabs(minunit_tmp_e-minunit_tmp_r) > MINUNIT_EPSILON) {\ |
|
|
195 |
int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON);\ |
|
|
196 |
(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %.*g expected but was %.*g", __func__, __FILE__, __LINE__, minunit_significant_figures, minunit_tmp_e, minunit_significant_figures, minunit_tmp_r);\ |
|
|
197 |
minunit_status = 1;\ |
|
|
198 |
return;\ |
|
|
199 |
} else {\ |
|
|
200 |
printf(".");\ |
|
|
201 |
}\ |
|
|
202 |
) |
|
|
203 |
|
|
|
204 |
#define mu_assert_string_eq(expected, result) MU__SAFE_BLOCK(\ |
|
|
205 |
const char* minunit_tmp_e = expected;\ |
|
|
206 |
const char* minunit_tmp_r = result;\ |
|
|
207 |
minunit_assert++;\ |
|
|
208 |
if (!minunit_tmp_e) {\ |
|
|
209 |
minunit_tmp_e = "<null pointer>";\ |
|
|
210 |
}\ |
|
|
211 |
if (!minunit_tmp_r) {\ |
|
|
212 |
minunit_tmp_r = "<null pointer>";\ |
|
|
213 |
}\ |
|
|
214 |
if(strcmp(minunit_tmp_e, minunit_tmp_r) != 0) {\ |
|
|
215 |
(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: '%s' expected but was '%s'", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ |
|
|
216 |
minunit_status = 1;\ |
|
|
217 |
return;\ |
|
|
218 |
} else {\ |
|
|
219 |
printf(".");\ |
|
|
220 |
}\ |
|
|
221 |
) |
|
|
222 |
|
|
|
223 |
/* |
|
|
224 |
* The following two functions were written by David Robert Nadeau |
|
|
225 |
* from http://NadeauSoftware.com/ and distributed under the |
|
|
226 |
* Creative Commons Attribution 3.0 Unported License |
|
|
227 |
*/ |
|
|
228 |
|
|
|
229 |
/** |
|
|
230 |
* Returns the real time, in seconds, or -1.0 if an error occurred. |
|
|
231 |
* |
|
|
232 |
* Time is measured since an arbitrary and OS-dependent start time. |
|
|
233 |
* The returned real time is only useful for computing an elapsed time |
|
|
234 |
* between two calls to this function. |
|
|
235 |
*/ |
|
|
236 |
static double mu_timer_real(void) |
|
|
237 |
{ |
|
|
238 |
#if defined(_WIN32) |
|
|
239 |
/* Windows 2000 and later. ---------------------------------- */ |
|
|
240 |
LARGE_INTEGER Time; |
|
|
241 |
LARGE_INTEGER Frequency; |
|
|
242 |
|
|
|
243 |
QueryPerformanceFrequency(&Frequency); |
|
|
244 |
QueryPerformanceCounter(&Time); |
|
|
245 |
|
|
|
246 |
Time.QuadPart *= 1000000; |
|
|
247 |
Time.QuadPart /= Frequency.QuadPart; |
|
|
248 |
|
|
|
249 |
return (double)Time.QuadPart / 1000000.0; |
|
|
250 |
|
|
|
251 |
#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) |
|
|
252 |
/* HP-UX, Solaris. ------------------------------------------ */ |
|
|
253 |
return (double)gethrtime( ) / 1000000000.0; |
|
|
254 |
|
|
|
255 |
#elif defined(__MACH__) && defined(__APPLE__) |
|
|
256 |
/* OSX. ----------------------------------------------------- */ |
|
|
257 |
static double timeConvert = 0.0; |
|
|
258 |
if ( timeConvert == 0.0 ) |
|
|
259 |
{ |
|
|
260 |
mach_timebase_info_data_t timeBase; |
|
|
261 |
(void)mach_timebase_info( &timeBase ); |
|
|
262 |
timeConvert = (double)timeBase.numer / |
|
|
263 |
(double)timeBase.denom / |
|
|
264 |
1000000000.0; |
|
|
265 |
} |
|
|
266 |
return (double)mach_absolute_time( ) * timeConvert; |
|
|
267 |
|
|
|
268 |
#elif defined(_POSIX_VERSION) |
|
|
269 |
/* POSIX. --------------------------------------------------- */ |
|
|
270 |
struct timeval tm; |
|
|
271 |
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) |
|
|
272 |
{ |
|
|
273 |
struct timespec ts; |
|
|
274 |
#if defined(CLOCK_MONOTONIC_PRECISE) |
|
|
275 |
/* BSD. --------------------------------------------- */ |
|
|
276 |
const clockid_t id = CLOCK_MONOTONIC_PRECISE; |
|
|
277 |
#elif defined(CLOCK_MONOTONIC_RAW) |
|
|
278 |
/* Linux. ------------------------------------------- */ |
|
|
279 |
const clockid_t id = CLOCK_MONOTONIC_RAW; |
|
|
280 |
#elif defined(CLOCK_HIGHRES) |
|
|
281 |
/* Solaris. ----------------------------------------- */ |
|
|
282 |
const clockid_t id = CLOCK_HIGHRES; |
|
|
283 |
#elif defined(CLOCK_MONOTONIC) |
|
|
284 |
/* AIX, BSD, Linux, POSIX, Solaris. ----------------- */ |
|
|
285 |
const clockid_t id = CLOCK_MONOTONIC; |
|
|
286 |
#elif defined(CLOCK_REALTIME) |
|
|
287 |
/* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */ |
|
|
288 |
const clockid_t id = CLOCK_REALTIME; |
|
|
289 |
#else |
|
|
290 |
const clockid_t id = (clockid_t)-1; /* Unknown. */ |
|
|
291 |
#endif /* CLOCK_* */ |
|
|
292 |
if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) |
|
|
293 |
return (double)ts.tv_sec + |
|
|
294 |
(double)ts.tv_nsec / 1000000000.0; |
|
|
295 |
/* Fall thru. */ |
|
|
296 |
} |
|
|
297 |
#endif /* _POSIX_TIMERS */ |
|
|
298 |
|
|
|
299 |
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */ |
|
|
300 |
gettimeofday( &tm, NULL ); |
|
|
301 |
return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0; |
|
|
302 |
#else |
|
|
303 |
return -1.0; /* Failed. */ |
|
|
304 |
#endif |
|
|
305 |
} |
|
|
306 |
|
|
|
307 |
/** |
|
|
308 |
* Returns the amount of CPU time used by the current process, |
|
|
309 |
* in seconds, or -1.0 if an error occurred. |
|
|
310 |
*/ |
|
|
311 |
static double mu_timer_cpu(void) |
|
|
312 |
{ |
|
|
313 |
#if defined(_WIN32) |
|
|
314 |
/* Windows -------------------------------------------------- */ |
|
|
315 |
FILETIME createTime; |
|
|
316 |
FILETIME exitTime; |
|
|
317 |
FILETIME kernelTime; |
|
|
318 |
FILETIME userTime; |
|
|
319 |
|
|
|
320 |
/* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */ |
|
|
321 |
if ( GetProcessTimes( GetCurrentProcess( ), |
|
|
322 |
&createTime, &exitTime, &kernelTime, &userTime ) != 0 ) |
|
|
323 |
{ |
|
|
324 |
ULARGE_INTEGER userSystemTime; |
|
|
325 |
memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER)); |
|
|
326 |
return (double)userSystemTime.QuadPart / 10000000.0; |
|
|
327 |
} |
|
|
328 |
|
|
|
329 |
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) |
|
|
330 |
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */ |
|
|
331 |
|
|
|
332 |
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) |
|
|
333 |
/* Prefer high-res POSIX timers, when available. */ |
|
|
334 |
{ |
|
|
335 |
clockid_t id; |
|
|
336 |
struct timespec ts; |
|
|
337 |
#if _POSIX_CPUTIME > 0 |
|
|
338 |
/* Clock ids vary by OS. Query the id, if possible. */ |
|
|
339 |
if ( clock_getcpuclockid( 0, &id ) == -1 ) |
|
|
340 |
#endif |
|
|
341 |
#if defined(CLOCK_PROCESS_CPUTIME_ID) |
|
|
342 |
/* Use known clock id for AIX, Linux, or Solaris. */ |
|
|
343 |
id = CLOCK_PROCESS_CPUTIME_ID; |
|
|
344 |
#elif defined(CLOCK_VIRTUAL) |
|
|
345 |
/* Use known clock id for BSD or HP-UX. */ |
|
|
346 |
id = CLOCK_VIRTUAL; |
|
|
347 |
#else |
|
|
348 |
id = (clockid_t)-1; |
|
|
349 |
#endif |
|
|
350 |
if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) |
|
|
351 |
return (double)ts.tv_sec + |
|
|
352 |
(double)ts.tv_nsec / 1000000000.0; |
|
|
353 |
} |
|
|
354 |
#endif |
|
|
355 |
|
|
|
356 |
#if defined(RUSAGE_SELF) |
|
|
357 |
{ |
|
|
358 |
struct rusage rusage; |
|
|
359 |
if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) |
|
|
360 |
return (double)rusage.ru_utime.tv_sec + |
|
|
361 |
(double)rusage.ru_utime.tv_usec / 1000000.0; |
|
|
362 |
} |
|
|
363 |
#endif |
|
|
364 |
|
|
|
365 |
#if defined(_SC_CLK_TCK) |
|
|
366 |
{ |
|
|
367 |
const double ticks = (double)sysconf( _SC_CLK_TCK ); |
|
|
368 |
struct tms tms; |
|
|
369 |
if ( times( &tms ) != (clock_t)-1 ) |
|
|
370 |
return (double)tms.tms_utime / ticks; |
|
|
371 |
} |
|
|
372 |
#endif |
|
|
373 |
|
|
|
374 |
#if defined(CLOCKS_PER_SEC) |
|
|
375 |
{ |
|
|
376 |
clock_t cl = clock( ); |
|
|
377 |
if ( cl != (clock_t)-1 ) |
|
|
378 |
return (double)cl / (double)CLOCKS_PER_SEC; |
|
|
379 |
} |
|
|
380 |
#endif |
|
|
381 |
|
|
|
382 |
#endif |
|
|
383 |
|
|
|
384 |
return -1; /* Failed. */ |
|
|
385 |
} |
|
|
386 |
|
|
|
387 |
#ifdef __cplusplus |
|
|
388 |
} |
|
|
389 |
#endif |
|
|
390 |
|
|
|
391 |
#endif /* MINUNIT_MINUNIT_H */ |
|
diff --git a/nonstd.h b/nonstd.h
|
|
|
1 |
// nonstd.h |
|
|
2 |
// A collection of useful functions and macros. |
|
|
3 |
// This library is licensed under the BSD 2-Clause License. |
|
|
4 |
// |
|
|
5 |
// This file provides both the interface and the implementation. |
|
|
6 |
// To instantiate the implementation, |
|
|
7 |
// #define NONSTD_IMPLEMENTATION |
|
|
8 |
// before including this file. |
|
|
9 |
|
|
|
10 |
#ifdef NONSTD_IMPLEMENTATION |
|
|
11 |
#ifndef _POSIX_C_SOURCE |
|
|
12 |
#define _POSIX_C_SOURCE 200809L |
|
|
13 |
#endif |
|
|
14 |
#endif |
|
|
15 |
|
|
|
16 |
#ifndef NONSTD_H |
|
|
17 |
#define NONSTD_H |
|
|
18 |
|
|
|
19 |
#include <stdarg.h> |
|
|
20 |
#include <stddef.h> |
|
|
21 |
#include <stdint.h> |
|
|
22 |
#include <stdio.h> |
|
|
23 |
#include <stdlib.h> |
|
|
24 |
#include <string.h> |
|
|
25 |
#include <sys/time.h> |
|
|
26 |
#include <time.h> |
|
|
27 |
#include <unistd.h> |
|
|
28 |
|
|
|
29 |
#ifndef NONSTD_DEF |
|
|
30 |
#ifdef NONSTD_STATIC |
|
|
31 |
#define NONSTD_DEF static |
|
|
32 |
#else |
|
|
33 |
#define NONSTD_DEF extern |
|
|
34 |
#endif |
|
|
35 |
#endif |
|
|
36 |
|
|
|
37 |
typedef int8_t i8; |
|
|
38 |
typedef uint8_t u8; |
|
|
39 |
typedef int16_t i16; |
|
|
40 |
typedef uint16_t u16; |
|
|
41 |
typedef int32_t i32; |
|
|
42 |
typedef uint32_t u32; |
|
|
43 |
typedef int64_t i64; |
|
|
44 |
typedef uint64_t u64; |
|
|
45 |
typedef unsigned int uint; |
|
|
46 |
typedef unsigned long ulong; |
|
|
47 |
typedef intptr_t isize; |
|
|
48 |
typedef uintptr_t usize; |
|
|
49 |
typedef char c8; |
|
|
50 |
|
|
|
51 |
#define countof(a) (sizeof(a) / sizeof((a)[0])) |
|
|
52 |
|
|
|
53 |
#define static_foreach(type, var, array) \ |
|
|
54 |
for (size_t _i_##var = 0, _n_##var = countof(array); \ |
|
|
55 |
_i_##var < _n_##var && ((var) = (array)[_i_##var], 1); ++_i_##var) |
|
|
56 |
|
|
|
57 |
#define ALLOC(type, n) ((type *)safe_malloc(sizeof(type), (n))) |
|
|
58 |
#define REALLOC(ptr, type, n) ((type *)safe_realloc((ptr), sizeof(type), (n))) |
|
|
59 |
#define FREE(ptr) \ |
|
|
60 |
do { \ |
|
|
61 |
free(ptr); \ |
|
|
62 |
ptr = NULL; \ |
|
|
63 |
} while (0) |
|
|
64 |
|
|
|
65 |
NONSTD_DEF void *safe_malloc(size_t item_size, size_t count); |
|
|
66 |
NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count); |
|
|
67 |
|
|
|
68 |
#define MIN(a, b) ((a) < (b) ? (a) : (b)) |
|
|
69 |
#define MAX(a, b) ((a) > (b) ? (a) : (b)) |
|
|
70 |
#define CLAMP(x, lo, hi) (MIN((hi), MAX((lo), (x)))) |
|
|
71 |
|
|
|
72 |
// From https://github.com/tsoding/nob.h/blob/e2c9a46f01d052ab740140e74453665dc3334832/nob.h#L205-L206. |
|
|
73 |
#define UNUSED(value) (void)(value) |
|
|
74 |
#define TODO(message) \ |
|
|
75 |
do { \ |
|
|
76 |
fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); \ |
|
|
77 |
abort(); \ |
|
|
78 |
} while (0) |
|
|
79 |
#define UNREACHABLE(message) \ |
|
|
80 |
do { \ |
|
|
81 |
fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); \ |
|
|
82 |
abort(); \ |
|
|
83 |
} while (0) |
|
|
84 |
|
|
|
85 |
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L |
|
|
86 |
#define STATIC_ASSERT(expr, msg) _Static_assert((expr), msg) |
|
|
87 |
#else |
|
|
88 |
#define STATIC_ASSERT(expr, msg) \ |
|
|
89 |
typedef char static_assertion_##msg[(expr) ? 1 : -1] |
|
|
90 |
#endif |
|
|
91 |
|
|
|
92 |
// String view - read-only, non-owning reference to a string |
|
|
93 |
typedef struct { |
|
|
94 |
const char *data; |
|
|
95 |
size_t length; |
|
|
96 |
} stringv; |
|
|
97 |
|
|
|
98 |
NONSTD_DEF stringv sv_from_cstr(const char *s); |
|
|
99 |
NONSTD_DEF stringv sv_from_parts(const char *data, size_t length); |
|
|
100 |
NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end); |
|
|
101 |
NONSTD_DEF int sv_equals(stringv a, stringv b); |
|
|
102 |
NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix); |
|
|
103 |
NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix); |
|
|
104 |
|
|
|
105 |
// String builder - owning, mutable, dynamically growing string buffer |
|
|
106 |
typedef struct { |
|
|
107 |
char *data; |
|
|
108 |
size_t length; |
|
|
109 |
size_t capacity; |
|
|
110 |
} stringb; |
|
|
111 |
|
|
|
112 |
NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap); |
|
|
113 |
NONSTD_DEF void sb_free(stringb *sb); |
|
|
114 |
NONSTD_DEF void sb_ensure(stringb *sb, size_t additional); |
|
|
115 |
NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s); |
|
|
116 |
NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv); |
|
|
117 |
NONSTD_DEF void sb_append_char(stringb *sb, char c); |
|
|
118 |
NONSTD_DEF stringv sb_as_sv(const stringb *sb); |
|
|
119 |
|
|
|
120 |
// Slice - generic non-owning view into an array |
|
|
121 |
// Usage: SLICE_DEF(int); slice(int) view = ...; |
|
|
122 |
#define SLICE_DEF(T) \ |
|
|
123 |
typedef struct { \ |
|
|
124 |
T *data; \ |
|
|
125 |
size_t length; \ |
|
|
126 |
} slice_##T |
|
|
127 |
|
|
|
128 |
#define slice(T) slice_##T |
|
|
129 |
|
|
|
130 |
#define make_slice(T, ptr, len) ((slice(T)){.data = (ptr), .length = (len)}) |
|
|
131 |
|
|
|
132 |
#define array_as_slice(T, arr) \ |
|
|
133 |
((slice(T)){.data = (arr).data, .length = (arr).length}) |
|
|
134 |
|
|
|
135 |
// Dynamic array - generic type-safe growable array using macros |
|
|
136 |
// Usage: array(int) numbers; array_init(numbers); |
|
|
137 |
#define array(T) \ |
|
|
138 |
struct { \ |
|
|
139 |
T *data; \ |
|
|
140 |
size_t length; \ |
|
|
141 |
size_t capacity; \ |
|
|
142 |
} |
|
|
143 |
|
|
|
144 |
#define array_init(arr) \ |
|
|
145 |
do { \ |
|
|
146 |
(arr).capacity = 0; \ |
|
|
147 |
(arr).data = NULL; \ |
|
|
148 |
(arr).length = 0; \ |
|
|
149 |
} while (0) |
|
|
150 |
|
|
|
151 |
#define array_init_cap(arr, initial_cap) \ |
|
|
152 |
do { \ |
|
|
153 |
(arr).capacity = (initial_cap) ? (initial_cap) : 16; \ |
|
|
154 |
(arr).data = ALLOC(__typeof__(*(arr).data), (arr).capacity); \ |
|
|
155 |
(arr).length = 0; \ |
|
|
156 |
} while (0) |
|
|
157 |
|
|
|
158 |
#define array_free(arr) \ |
|
|
159 |
do { \ |
|
|
160 |
FREE((arr).data); \ |
|
|
161 |
(arr).length = 0; \ |
|
|
162 |
(arr).capacity = 0; \ |
|
|
163 |
} while (0) |
|
|
164 |
|
|
|
165 |
#define array_ensure(arr, additional) \ |
|
|
166 |
do { \ |
|
|
167 |
size_t _needed = (arr).length + (additional); \ |
|
|
168 |
if (_needed > (arr).capacity) { \ |
|
|
169 |
size_t _new_cap = (arr).capacity ? (arr).capacity : 16; \ |
|
|
170 |
while (_new_cap < _needed) { \ |
|
|
171 |
if (_new_cap > SIZE_MAX / 2) { \ |
|
|
172 |
_new_cap = SIZE_MAX; \ |
|
|
173 |
break; \ |
|
|
174 |
} \ |
|
|
175 |
_new_cap *= 2; \ |
|
|
176 |
} \ |
|
|
177 |
if (_new_cap < _needed) { /* Overflow or OOM */ \ |
|
|
178 |
break; \ |
|
|
179 |
} \ |
|
|
180 |
void *_new_data = \ |
|
|
181 |
safe_realloc((arr).data, sizeof(*(arr).data), _new_cap); \ |
|
|
182 |
if (_new_data) { \ |
|
|
183 |
(arr).data = _new_data; \ |
|
|
184 |
(arr).capacity = _new_cap; \ |
|
|
185 |
} \ |
|
|
186 |
} \ |
|
|
187 |
} while (0) |
|
|
188 |
|
|
|
189 |
#define array_push(arr, value) \ |
|
|
190 |
do { \ |
|
|
191 |
array_ensure((arr), 1); \ |
|
|
192 |
if ((arr).length < (arr).capacity) { \ |
|
|
193 |
(arr).data[(arr).length++] = (value); \ |
|
|
194 |
} \ |
|
|
195 |
} while (0) |
|
|
196 |
|
|
|
197 |
#define array_pop(arr) ((arr).length > 0 ? (arr).data[--(arr).length] : 0) |
|
|
198 |
|
|
|
199 |
#define array_get(arr, index) ((arr).data[index]) |
|
|
200 |
|
|
|
201 |
#define array_set(arr, index, value) \ |
|
|
202 |
do { \ |
|
|
203 |
if ((index) < (arr).length) { \ |
|
|
204 |
(arr).data[index] = (value); \ |
|
|
205 |
} \ |
|
|
206 |
} while (0) |
|
|
207 |
|
|
|
208 |
#define array_insert(arr, index, value) \ |
|
|
209 |
do { \ |
|
|
210 |
if ((index) <= (arr).length) { \ |
|
|
211 |
array_ensure((arr), 1); \ |
|
|
212 |
if ((arr).length < (arr).capacity) { \ |
|
|
213 |
for (size_t _i = (arr).length; _i > (index); --_i) { \ |
|
|
214 |
(arr).data[_i] = (arr).data[_i - 1]; \ |
|
|
215 |
} \ |
|
|
216 |
(arr).data[index] = (value); \ |
|
|
217 |
(arr).length++; \ |
|
|
218 |
} \ |
|
|
219 |
} \ |
|
|
220 |
} while (0) |
|
|
221 |
|
|
|
222 |
#define array_remove(arr, index) \ |
|
|
223 |
do { \ |
|
|
224 |
if ((index) < (arr).length) { \ |
|
|
225 |
for (size_t _i = (index); _i < (arr).length - 1; ++_i) { \ |
|
|
226 |
(arr).data[_i] = (arr).data[_i + 1]; \ |
|
|
227 |
} \ |
|
|
228 |
(arr).length--; \ |
|
|
229 |
} \ |
|
|
230 |
} while (0) |
|
|
231 |
|
|
|
232 |
#define array_clear(arr) \ |
|
|
233 |
do { \ |
|
|
234 |
(arr).length = 0; \ |
|
|
235 |
} while (0) |
|
|
236 |
|
|
|
237 |
#define array_reserve(arr, new_capacity) \ |
|
|
238 |
do { \ |
|
|
239 |
if ((new_capacity) > (arr).capacity) { \ |
|
|
240 |
void *_new_data = \ |
|
|
241 |
safe_realloc((arr).data, sizeof(*(arr).data), (new_capacity)); \ |
|
|
242 |
if (_new_data) { \ |
|
|
243 |
(arr).data = _new_data; \ |
|
|
244 |
(arr).capacity = (new_capacity); \ |
|
|
245 |
} \ |
|
|
246 |
} \ |
|
|
247 |
} while (0) |
|
|
248 |
|
|
|
249 |
#define array_foreach(arr, var) \ |
|
|
250 |
for (size_t _i_##var = 0; \ |
|
|
251 |
_i_##var < (arr).length && ((var) = (arr).data[_i_##var], 1); \ |
|
|
252 |
++_i_##var) |
|
|
253 |
|
|
|
254 |
#define array_foreach_idx(arr, var, index) \ |
|
|
255 |
for (size_t index = 0; \ |
|
|
256 |
index < (arr).length && ((var) = (arr).data[index], 1); ++index) |
|
|
257 |
|
|
|
258 |
// Arena - block-based memory allocator |
|
|
259 |
typedef struct { |
|
|
260 |
char *ptr; |
|
|
261 |
char *end; |
|
|
262 |
array(char *) blocks; |
|
|
263 |
} Arena; |
|
|
264 |
|
|
|
265 |
#define ARENA_DEFAULT_BLOCK_SIZE (4096) |
|
|
266 |
|
|
|
267 |
NONSTD_DEF Arena arena_make(void); |
|
|
268 |
NONSTD_DEF void arena_grow(Arena *a, size_t min_size); |
|
|
269 |
NONSTD_DEF void *arena_alloc(Arena *a, size_t size); |
|
|
270 |
NONSTD_DEF void arena_free(Arena *a); |
|
|
271 |
|
|
|
272 |
// Image - simple RGB image structure |
|
|
273 |
typedef struct { |
|
|
274 |
u8 r, g, b; |
|
|
275 |
} Color; |
|
|
276 |
|
|
|
277 |
typedef struct { |
|
|
278 |
u32 width; |
|
|
279 |
u32 height; |
|
|
280 |
Color *pixels; |
|
|
281 |
} Canvas; |
|
|
282 |
|
|
|
283 |
#define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)}) |
|
|
284 |
#define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)}) |
|
|
285 |
|
|
|
286 |
#define COLOR_BLACK COLOR_RGB(0, 0, 0) |
|
|
287 |
#define COLOR_WHITE COLOR_RGB(255, 255, 255) |
|
|
288 |
#define COLOR_RED COLOR_RGB(255, 0, 0) |
|
|
289 |
#define COLOR_GREEN COLOR_RGB(0, 255, 0) |
|
|
290 |
#define COLOR_BLUE COLOR_RGB(0, 0, 255) |
|
|
291 |
#define COLOR_YELLOW COLOR_RGB(255, 255, 0) |
|
|
292 |
#define COLOR_MAGENTA COLOR_RGB(255, 0, 255) |
|
|
293 |
#define COLOR_CYAN COLOR_RGB(0, 255, 255) |
|
|
294 |
|
|
|
295 |
NONSTD_DEF Canvas ppm_init(u32 width, u32 height); |
|
|
296 |
NONSTD_DEF void ppm_free(Canvas *img); |
|
|
297 |
NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color); |
|
|
298 |
NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y); |
|
|
299 |
NONSTD_DEF int ppm_save(const Canvas *img, const char *filename); |
|
|
300 |
NONSTD_DEF Canvas ppm_read(const char *filename); |
|
|
301 |
NONSTD_DEF void ppm_fill(Canvas *canvas, Color color); |
|
|
302 |
NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color); |
|
|
303 |
NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color); |
|
|
304 |
NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 x, i32 y, i32 r, Color color); |
|
|
305 |
NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color); |
|
|
306 |
|
|
|
307 |
// File I/O helpers |
|
|
308 |
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size); |
|
|
309 |
NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size); |
|
|
310 |
NONSTD_DEF stringb read_entire_file_sb(const char *filepath); |
|
|
311 |
NONSTD_DEF int write_file_sv(const char *filepath, stringv sv); |
|
|
312 |
NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb); |
|
|
313 |
|
|
|
314 |
// Logging |
|
|
315 |
typedef enum { |
|
|
316 |
LOG_ERROR, |
|
|
317 |
LOG_WARN, |
|
|
318 |
LOG_INFO, |
|
|
319 |
LOG_DEBUG, |
|
|
320 |
} LogLevel; |
|
|
321 |
|
|
|
322 |
NONSTD_DEF void set_log_level(LogLevel level); |
|
|
323 |
NONSTD_DEF LogLevel get_log_level_from_env(void); |
|
|
324 |
NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...); |
|
|
325 |
|
|
|
326 |
#define LOG_INFO_MSG(...) log_message(stdout, LOG_INFO, __VA_ARGS__) |
|
|
327 |
#define LOG_DEBUG_MSG(...) log_message(stdout, LOG_DEBUG, __VA_ARGS__) |
|
|
328 |
#define LOG_WARN_MSG(...) log_message(stderr, LOG_WARN, __VA_ARGS__) |
|
|
329 |
#define LOG_ERROR_MSG(...) log_message(stderr, LOG_ERROR, __VA_ARGS__) |
|
|
330 |
|
|
|
331 |
#define COLOR_RESET "\033[0m" |
|
|
332 |
#define COLOR_INFO "\033[32m" |
|
|
333 |
#define COLOR_DEBUG "\033[36m" |
|
|
334 |
#define COLOR_WARNING "\033[33m" |
|
|
335 |
#define COLOR_ERROR "\033[31m" |
|
|
336 |
|
|
|
337 |
#endif // NONSTD_H |
|
|
338 |
|
|
|
339 |
#ifdef NONSTD_IMPLEMENTATION |
|
|
340 |
|
|
|
341 |
NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) { |
|
|
342 |
if (count != 0 && item_size > SIZE_MAX / count) { |
|
|
343 |
return NULL; |
|
|
344 |
} |
|
|
345 |
return malloc(item_size * count); |
|
|
346 |
} |
|
|
347 |
|
|
|
348 |
NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count) { |
|
|
349 |
if (count != 0 && item_size > SIZE_MAX / count) { |
|
|
350 |
return NULL; |
|
|
351 |
} |
|
|
352 |
return realloc(ptr, item_size * count); |
|
|
353 |
} |
|
|
354 |
|
|
|
355 |
// String View Implementation |
|
|
356 |
|
|
|
357 |
NONSTD_DEF stringv sv_from_cstr(const char *s) { |
|
|
358 |
return (stringv){.data = s, .length = s ? strlen(s) : 0}; |
|
|
359 |
} |
|
|
360 |
|
|
|
361 |
NONSTD_DEF stringv sv_from_parts(const char *data, size_t length) { |
|
|
362 |
return (stringv){.data = data, .length = length}; |
|
|
363 |
} |
|
|
364 |
|
|
|
365 |
NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end) { |
|
|
366 |
if (start > sv.length) { |
|
|
367 |
start = sv.length; |
|
|
368 |
} |
|
|
369 |
if (end > sv.length) { |
|
|
370 |
end = sv.length; |
|
|
371 |
} |
|
|
372 |
if (start > end) { |
|
|
373 |
start = end; |
|
|
374 |
} |
|
|
375 |
return (stringv){.data = sv.data + start, .length = end - start}; |
|
|
376 |
} |
|
|
377 |
|
|
|
378 |
NONSTD_DEF int sv_equals(stringv a, stringv b) { |
|
|
379 |
return a.length == b.length && (a.length == 0 || memcmp(a.data, b.data, a.length) == 0); |
|
|
380 |
} |
|
|
381 |
|
|
|
382 |
NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix) { |
|
|
383 |
return sv.length >= prefix.length && memcmp(sv.data, prefix.data, prefix.length) == 0; |
|
|
384 |
} |
|
|
385 |
|
|
|
386 |
NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) { |
|
|
387 |
return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0; |
|
|
388 |
} |
|
|
389 |
|
|
|
390 |
// String Builder Implementation |
|
|
391 |
|
|
|
392 |
NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) { |
|
|
393 |
sb->capacity = initial_cap ? initial_cap : 16; |
|
|
394 |
sb->data = ALLOC(char, sb->capacity); |
|
|
395 |
sb->length = 0; |
|
|
396 |
if (sb->data) { |
|
|
397 |
sb->data[0] = '\0'; |
|
|
398 |
} |
|
|
399 |
} |
|
|
400 |
|
|
|
401 |
NONSTD_DEF void sb_free(stringb *sb) { |
|
|
402 |
FREE(sb->data); |
|
|
403 |
sb->length = 0; |
|
|
404 |
sb->capacity = 0; |
|
|
405 |
} |
|
|
406 |
|
|
|
407 |
NONSTD_DEF void sb_ensure(stringb *sb, size_t additional) { |
|
|
408 |
size_t needed = sb->length + additional + 1; |
|
|
409 |
size_t new_cap = sb->capacity; |
|
|
410 |
|
|
|
411 |
if (needed > new_cap) { |
|
|
412 |
while (new_cap < needed) { |
|
|
413 |
if (new_cap > SIZE_MAX / 2) { |
|
|
414 |
new_cap = SIZE_MAX; |
|
|
415 |
break; |
|
|
416 |
} |
|
|
417 |
new_cap *= 2; |
|
|
418 |
} |
|
|
419 |
if (new_cap < needed) |
|
|
420 |
return; // Overflow |
|
|
421 |
|
|
|
422 |
char *new_data = safe_realloc(sb->data, sizeof(char), new_cap); |
|
|
423 |
if (new_data) { |
|
|
424 |
sb->data = new_data; |
|
|
425 |
sb->capacity = new_cap; |
|
|
426 |
} |
|
|
427 |
} |
|
|
428 |
} |
|
|
429 |
|
|
|
430 |
NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s) { |
|
|
431 |
if (!s) { |
|
|
432 |
return; |
|
|
433 |
} |
|
|
434 |
size_t slength = strlen(s); |
|
|
435 |
sb_ensure(sb, slength); |
|
|
436 |
if (sb->length + slength + 1 <= sb->capacity) { |
|
|
437 |
memcpy(sb->data + sb->length, s, slength); |
|
|
438 |
sb->length += slength; |
|
|
439 |
sb->data[sb->length] = '\0'; |
|
|
440 |
} |
|
|
441 |
} |
|
|
442 |
|
|
|
443 |
NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv) { |
|
|
444 |
if (!sv.data || sv.length == 0) { |
|
|
445 |
return; |
|
|
446 |
} |
|
|
447 |
sb_ensure(sb, sv.length); |
|
|
448 |
if (sb->length + sv.length + 1 <= sb->capacity) { |
|
|
449 |
memcpy(sb->data + sb->length, sv.data, sv.length); |
|
|
450 |
sb->length += sv.length; |
|
|
451 |
sb->data[sb->length] = '\0'; |
|
|
452 |
} |
|
|
453 |
} |
|
|
454 |
|
|
|
455 |
NONSTD_DEF void sb_append_char(stringb *sb, char c) { |
|
|
456 |
sb_ensure(sb, 1); |
|
|
457 |
if (sb->length + 2 <= sb->capacity) { |
|
|
458 |
sb->data[sb->length++] = c; |
|
|
459 |
sb->data[sb->length] = '\0'; |
|
|
460 |
} |
|
|
461 |
} |
|
|
462 |
|
|
|
463 |
NONSTD_DEF stringv sb_as_sv(const stringb *sb) { |
|
|
464 |
return (stringv){.data = sb->data, .length = sb->length}; |
|
|
465 |
} |
|
|
466 |
|
|
|
467 |
NONSTD_DEF Arena arena_make(void) { |
|
|
468 |
Arena a = {0}; |
|
|
469 |
array_init(a.blocks); |
|
|
470 |
return a; |
|
|
471 |
} |
|
|
472 |
|
|
|
473 |
// Arena Implementation |
|
|
474 |
|
|
|
475 |
NONSTD_DEF void arena_grow(Arena *a, size_t min_size) { |
|
|
476 |
size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size); |
|
|
477 |
char *block = ALLOC(char, size); |
|
|
478 |
a->ptr = block; |
|
|
479 |
a->end = block + size; |
|
|
480 |
array_push(a->blocks, block); |
|
|
481 |
} |
|
|
482 |
|
|
|
483 |
NONSTD_DEF void *arena_alloc(Arena *a, size_t size) { |
|
|
484 |
// Align to 8 bytes basically |
|
|
485 |
size_t align = sizeof(void *); |
|
|
486 |
uintptr_t current = (uintptr_t)a->ptr; |
|
|
487 |
uintptr_t aligned = (current + align - 1) & ~(align - 1); |
|
|
488 |
uintptr_t end = (uintptr_t)a->end; |
|
|
489 |
|
|
|
490 |
// Check for overflow (aligned wrapped around) or out of bounds (aligned >= end) |
|
|
491 |
// or not enough space ((end - aligned) < size) |
|
|
492 |
if (aligned < current || aligned >= end || (end - aligned) < size) { |
|
|
493 |
arena_grow(a, size); |
|
|
494 |
current = (uintptr_t)a->ptr; |
|
|
495 |
aligned = (current + align - 1) & ~(align - 1); |
|
|
496 |
end = (uintptr_t)a->end; |
|
|
497 |
} |
|
|
498 |
|
|
|
499 |
// Double check after grow (in case grow failed or size is just too huge) |
|
|
500 |
if (aligned < current || aligned >= end || (end - aligned) < size) { |
|
|
501 |
return NULL; |
|
|
502 |
} |
|
|
503 |
|
|
|
504 |
a->ptr = (char *)(aligned + size); |
|
|
505 |
return (void *)aligned; |
|
|
506 |
} |
|
|
507 |
|
|
|
508 |
NONSTD_DEF void arena_free(Arena *a) { |
|
|
509 |
char *block; |
|
|
510 |
array_foreach(a->blocks, block) { FREE(block); } |
|
|
511 |
array_free(a->blocks); |
|
|
512 |
a->ptr = NULL; |
|
|
513 |
a->end = NULL; |
|
|
514 |
} |
|
|
515 |
|
|
|
516 |
// File I/O Implementation |
|
|
517 |
|
|
|
518 |
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) { |
|
|
519 |
FILE *f = fopen(filepath, "rb"); |
|
|
520 |
if (!f) { |
|
|
521 |
return NULL; |
|
|
522 |
} |
|
|
523 |
|
|
|
524 |
fseek(f, 0, SEEK_END); |
|
|
525 |
long length = ftell(f); |
|
|
526 |
fseek(f, 0, SEEK_SET); |
|
|
527 |
|
|
|
528 |
if (length < 0) { |
|
|
529 |
fclose(f); |
|
|
530 |
return NULL; |
|
|
531 |
} |
|
|
532 |
|
|
|
533 |
size_t size = (size_t)length; |
|
|
534 |
char *buffer = ALLOC(char, size + 1); |
|
|
535 |
if (!buffer) { |
|
|
536 |
fclose(f); |
|
|
537 |
return NULL; |
|
|
538 |
} |
|
|
539 |
|
|
|
540 |
size_t read = fread(buffer, 1, size, f); |
|
|
541 |
fclose(f); |
|
|
542 |
|
|
|
543 |
if (read != size) { |
|
|
544 |
FREE(buffer); |
|
|
545 |
return NULL; |
|
|
546 |
} |
|
|
547 |
|
|
|
548 |
buffer[size] = '\0'; |
|
|
549 |
if (out_size) { |
|
|
550 |
*out_size = size; |
|
|
551 |
} |
|
|
552 |
|
|
|
553 |
return buffer; |
|
|
554 |
} |
|
|
555 |
|
|
|
556 |
NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size) { |
|
|
557 |
FILE *f = fopen(filepath, "wb"); |
|
|
558 |
if (!f) { |
|
|
559 |
return 0; |
|
|
560 |
} |
|
|
561 |
|
|
|
562 |
size_t written = fwrite(data, 1, size, f); |
|
|
563 |
fclose(f); |
|
|
564 |
|
|
|
565 |
return written == size; |
|
|
566 |
} |
|
|
567 |
|
|
|
568 |
NONSTD_DEF stringb read_entire_file_sb(const char *filepath) { |
|
|
569 |
size_t size = 0; |
|
|
570 |
char *data = read_entire_file(filepath, &size); |
|
|
571 |
stringb sb = {0}; |
|
|
572 |
if (data) { |
|
|
573 |
sb.data = data; |
|
|
574 |
sb.length = size; |
|
|
575 |
sb.capacity = size + 1; |
|
|
576 |
} |
|
|
577 |
return sb; |
|
|
578 |
} |
|
|
579 |
|
|
|
580 |
NONSTD_DEF int write_file_sv(const char *filepath, stringv sv) { |
|
|
581 |
return write_entire_file(filepath, sv.data, sv.length); |
|
|
582 |
} |
|
|
583 |
|
|
|
584 |
NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb) { |
|
|
585 |
return write_entire_file(filepath, sb->data, sb->length); |
|
|
586 |
} |
|
|
587 |
|
|
|
588 |
// Logging Implementation |
|
|
589 |
|
|
|
590 |
static LogLevel max_level = LOG_INFO; |
|
|
591 |
|
|
|
592 |
static const char *level_strings[] = { |
|
|
593 |
"ERROR", |
|
|
594 |
"WARN", |
|
|
595 |
"INFO", |
|
|
596 |
"DEBUG", |
|
|
597 |
}; |
|
|
598 |
|
|
|
599 |
static const char *level_colors[] = { |
|
|
600 |
COLOR_ERROR, |
|
|
601 |
COLOR_WARNING, |
|
|
602 |
COLOR_INFO, |
|
|
603 |
COLOR_DEBUG, |
|
|
604 |
}; |
|
|
605 |
|
|
|
606 |
NONSTD_DEF void set_log_level(LogLevel level) { |
|
|
607 |
max_level = level; |
|
|
608 |
} |
|
|
609 |
|
|
|
610 |
NONSTD_DEF LogLevel get_log_level_from_env(void) { |
|
|
611 |
const char *env = getenv("LOG_LEVEL"); |
|
|
612 |
if (env) { |
|
|
613 |
int level = atoi(env); |
|
|
614 |
if (level >= 0 && level <= 3) { |
|
|
615 |
return (LogLevel)level; |
|
|
616 |
} |
|
|
617 |
} |
|
|
618 |
|
|
|
619 |
return max_level; |
|
|
620 |
} |
|
|
621 |
|
|
|
622 |
NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...) { |
|
|
623 |
if (max_level < level) |
|
|
624 |
return; |
|
|
625 |
|
|
|
626 |
struct timeval tv; |
|
|
627 |
gettimeofday(&tv, NULL); |
|
|
628 |
struct tm *tm_info = localtime(&tv.tv_sec); |
|
|
629 |
|
|
|
630 |
char time_str[24]; |
|
|
631 |
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); |
|
|
632 |
|
|
|
633 |
const char *color = isatty(fileno(stream)) ? level_colors[level] : ""; |
|
|
634 |
const char *reset = isatty(fileno(stream)) ? COLOR_RESET : ""; |
|
|
635 |
|
|
|
636 |
const char *log_format = "%s[%s.%03d] [%-5s] "; |
|
|
637 |
fprintf(stream, log_format, color, time_str, (int)(tv.tv_usec / 1000), level_strings[level]); |
|
|
638 |
|
|
|
639 |
va_list args; |
|
|
640 |
va_start(args, format); |
|
|
641 |
vfprintf(stream, format, args); |
|
|
642 |
va_end(args); |
|
|
643 |
|
|
|
644 |
fprintf(stream, "%s\n", reset); |
|
|
645 |
fflush(stream); |
|
|
646 |
} |
|
|
647 |
|
|
|
648 |
// PPM Image Implementation |
|
|
649 |
|
|
|
650 |
NONSTD_DEF Canvas ppm_init(u32 width, u32 height) { |
|
|
651 |
Canvas img = {0}; |
|
|
652 |
img.width = width; |
|
|
653 |
img.height = height; |
|
|
654 |
img.pixels = ALLOC(Color, width * height); |
|
|
655 |
if (img.pixels) { |
|
|
656 |
memset(img.pixels, 0, sizeof(Color) * width * height); |
|
|
657 |
} |
|
|
658 |
return img; |
|
|
659 |
} |
|
|
660 |
|
|
|
661 |
NONSTD_DEF void ppm_free(Canvas *img) { |
|
|
662 |
if (img->pixels) { |
|
|
663 |
FREE(img->pixels); |
|
|
664 |
} |
|
|
665 |
img->width = 0; |
|
|
666 |
img->height = 0; |
|
|
667 |
} |
|
|
668 |
|
|
|
669 |
NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color) { |
|
|
670 |
if (x < img->width && y < img->height) { |
|
|
671 |
img->pixels[y * img->width + x] = color; |
|
|
672 |
} |
|
|
673 |
} |
|
|
674 |
|
|
|
675 |
NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y) { |
|
|
676 |
if (x < img->width && y < img->height) { |
|
|
677 |
return img->pixels[y * img->width + x]; |
|
|
678 |
} |
|
|
679 |
return (Color){0, 0, 0}; |
|
|
680 |
} |
|
|
681 |
|
|
|
682 |
NONSTD_DEF int ppm_save(const Canvas *img, const char *filename) { |
|
|
683 |
FILE *f = fopen(filename, "w"); |
|
|
684 |
if (!f) { |
|
|
685 |
return 0; |
|
|
686 |
} |
|
|
687 |
|
|
|
688 |
fprintf(f, "P3\n%u %u\n255\n", img->width, img->height); |
|
|
689 |
for (u32 y = 0; y < img->height; ++y) { |
|
|
690 |
for (u32 x = 0; x < img->width; ++x) { |
|
|
691 |
Color c = ppm_get_pixel(img, x, y); |
|
|
692 |
fprintf(f, "%d %d %d ", c.r, c.g, c.b); |
|
|
693 |
} |
|
|
694 |
fprintf(f, "\n"); |
|
|
695 |
} |
|
|
696 |
|
|
|
697 |
fclose(f); |
|
|
698 |
return 1; |
|
|
699 |
} |
|
|
700 |
|
|
|
701 |
NONSTD_DEF Canvas ppm_read(const char *filename) { |
|
|
702 |
Canvas img = {0}; |
|
|
703 |
FILE *f = fopen(filename, "r"); |
|
|
704 |
if (!f) { |
|
|
705 |
return img; |
|
|
706 |
} |
|
|
707 |
|
|
|
708 |
char magic[3]; |
|
|
709 |
if (fscanf(f, "%2s", magic) != 1 || strcmp(magic, "P3") != 0) { |
|
|
710 |
fclose(f); |
|
|
711 |
return img; |
|
|
712 |
} |
|
|
713 |
|
|
|
714 |
u32 w, h, max_val; |
|
|
715 |
if (fscanf(f, "%u %u %u", &w, &h, &max_val) != 3) { |
|
|
716 |
fclose(f); |
|
|
717 |
return img; |
|
|
718 |
} |
|
|
719 |
|
|
|
720 |
img = ppm_init(w, h); |
|
|
721 |
if (!img.pixels) { |
|
|
722 |
fclose(f); |
|
|
723 |
return img; |
|
|
724 |
} |
|
|
725 |
|
|
|
726 |
for (u32 i = 0; i < w * h; ++i) { |
|
|
727 |
int r, g, b; |
|
|
728 |
if (fscanf(f, "%d %d %d", &r, &g, &b) != 3) { |
|
|
729 |
ppm_free(&img); |
|
|
730 |
fclose(f); |
|
|
731 |
return (Canvas){0}; |
|
|
732 |
} |
|
|
733 |
img.pixels[i] = (Color){(u8)r, (u8)g, (u8)b}; |
|
|
734 |
} |
|
|
735 |
|
|
|
736 |
fclose(f); |
|
|
737 |
return img; |
|
|
738 |
} |
|
|
739 |
|
|
|
740 |
NONSTD_DEF void ppm_fill(Canvas *canvas, Color color) { |
|
|
741 |
for (u32 i = 0; i < canvas->width * canvas->height; ++i) { |
|
|
742 |
canvas->pixels[i] = color; |
|
|
743 |
} |
|
|
744 |
} |
|
|
745 |
|
|
|
746 |
NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color) { |
|
|
747 |
if (w == 0 || h == 0) { |
|
|
748 |
return; |
|
|
749 |
} |
|
|
750 |
for (u32 i = x; i < x + w; ++i) { |
|
|
751 |
ppm_set_pixel(canvas, i, y, color); |
|
|
752 |
ppm_set_pixel(canvas, i, y + h - 1, color); |
|
|
753 |
} |
|
|
754 |
for (u32 j = y; j < y + h; ++j) { |
|
|
755 |
ppm_set_pixel(canvas, x, j, color); |
|
|
756 |
ppm_set_pixel(canvas, x + w - 1, j, color); |
|
|
757 |
} |
|
|
758 |
} |
|
|
759 |
|
|
|
760 |
NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color) { |
|
|
761 |
i32 dx = abs(x1 - x0); |
|
|
762 |
i32 dy = -abs(y1 - y0); |
|
|
763 |
i32 sx = x0 < x1 ? 1 : -1; |
|
|
764 |
i32 sy = y0 < y1 ? 1 : -1; |
|
|
765 |
i32 err = dx + dy; |
|
|
766 |
|
|
|
767 |
while (1) { |
|
|
768 |
ppm_set_pixel(canvas, (u32)x0, (u32)y0, color); |
|
|
769 |
if (x0 == x1 && y0 == y1) { |
|
|
770 |
break; |
|
|
771 |
} |
|
|
772 |
|
|
|
773 |
i32 e2 = 2 * err; |
|
|
774 |
if (e2 >= dy) { |
|
|
775 |
err += dy; |
|
|
776 |
x0 += sx; |
|
|
777 |
} |
|
|
778 |
if (e2 <= dx) { |
|
|
779 |
err += dx; |
|
|
780 |
y0 += sy; |
|
|
781 |
} |
|
|
782 |
} |
|
|
783 |
} |
|
|
784 |
|
|
|
785 |
NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 xm, i32 ym, i32 r, Color color) { |
|
|
786 |
i32 x = -r, y = 0, err = 2 - 2 * r; |
|
|
787 |
do { |
|
|
788 |
ppm_set_pixel(canvas, (u32)(xm - x), (u32)(ym + y), color); |
|
|
789 |
ppm_set_pixel(canvas, (u32)(xm - y), (u32)(ym - x), color); |
|
|
790 |
ppm_set_pixel(canvas, (u32)(xm + x), (u32)(ym - y), color); |
|
|
791 |
ppm_set_pixel(canvas, (u32)(xm + y), (u32)(ym + x), color); |
|
|
792 |
r = err; |
|
|
793 |
if (r <= y) { |
|
|
794 |
err += ++y * 2 + 1; |
|
|
795 |
} |
|
|
796 |
if (r > x || err > y) { |
|
|
797 |
err += ++x * 2 + 1; |
|
|
798 |
} |
|
|
799 |
} while (x < 0); |
|
|
800 |
} |
|
|
801 |
|
|
|
802 |
NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color) { |
|
|
803 |
ppm_draw_line(canvas, x0, y0, x1, y1, color); |
|
|
804 |
ppm_draw_line(canvas, x1, y1, x2, y2, color); |
|
|
805 |
ppm_draw_line(canvas, x2, y2, x0, y0, color); |
|
|
806 |
} |
|
|
807 |
|
|
|
808 |
#endif // NONSTD_IMPLEMENTATION |
|
|
809 |
|
|
|
810 |
/* |
|
|
811 |
BSD 2-Clause License |
|
|
812 |
|
|
|
813 |
Copyright (c) 2026, Mitja Felicijan <mitja.felicijan@gmail.com> |
|
|
814 |
|
|
|
815 |
Redistribution and use in source and binary forms, with or without |
|
|
816 |
modification, are permitted provided that the following conditions are met: |
|
|
817 |
|
|
|
818 |
1. Redistributions of source code must retain the above copyright notice, this |
|
|
819 |
list of conditions and the following disclaimer. |
|
|
820 |
|
|
|
821 |
2. Redistributions in binary form must reproduce the above copyright notice, |
|
|
822 |
this list of conditions and the following disclaimer in the documentation |
|
|
823 |
and/or other materials provided with the distribution. |
|
|
824 |
|
|
|
825 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
|
826 |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
|
827 |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
|
828 |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|
|
829 |
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
|
830 |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
|
831 |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
|
832 |
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
|
833 |
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
|
834 |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
835 |
*/ |