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
 37typedef int8_t i8;
 38typedef uint8_t u8;
 39typedef int16_t i16;
 40typedef uint16_t u16;
 41typedef int32_t i32;
 42typedef uint32_t u32;
 43typedef int64_t i64;
 44typedef uint64_t u64;
 45typedef unsigned int uint;
 46typedef unsigned long ulong;
 47typedef intptr_t isize;
 48typedef uintptr_t usize;
 49typedef 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
 65NONSTD_DEF void *safe_malloc(size_t item_size, size_t count);
 66NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count);
 67
 68#ifndef MIN
 69#define MIN(a, b) ((a) < (b) ? (a) : (b))
 70#endif
 71#ifndef MAX
 72#define MAX(a, b) ((a) > (b) ? (a) : (b))
 73#endif
 74#define CLAMP(x, lo, hi) (MIN((hi), MAX((lo), (x))))
 75
 76// From https://github.com/tsoding/nob.h/blob/e2c9a46f01d052ab740140e74453665dc3334832/nob.h#L205-L206.
 77#define UNUSED(value) (void)(value)
 78#define TODO(message)                                                      \
 79	do {                                                                   \
 80		TraceLog(LOG_ERROR, "%s:%d: TODO: %s", __FILE__, __LINE__, message); \
 81		abort();                                                           \
 82	} while (0)
 83#define UNREACHABLE(message)                                                      \
 84	do {                                                                          \
 85		TraceLog(LOG_ERROR, "%s:%d: UNREACHABLE: %s", __FILE__, __LINE__, message); \
 86		abort();                                                                  \
 87	} while (0)
 88
 89#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
 90#define STATIC_ASSERT(expr, msg) _Static_assert((expr), msg)
 91#else
 92#define STATIC_ASSERT(expr, msg) \
 93	typedef char static_assertion_##msg[(expr) ? 1 : -1]
 94#endif
 95
 96// String view - read-only, non-owning reference to a string
 97typedef struct {
 98	const char *data;
 99	size_t length;
100} stringv;
101
102NONSTD_DEF stringv sv_from_cstr(const char *s);
103NONSTD_DEF stringv sv_from_parts(const char *data, size_t length);
104NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end);
105NONSTD_DEF int sv_equals(stringv a, stringv b);
106NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix);
107NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix);
108
109// String builder - owning, mutable, dynamically growing string buffer
110typedef struct {
111	char *data;
112	size_t length;
113	size_t capacity;
114} stringb;
115
116NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap);
117NONSTD_DEF void sb_free(stringb *sb);
118NONSTD_DEF void sb_ensure(stringb *sb, size_t additional);
119NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s);
120NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv);
121NONSTD_DEF void sb_append_char(stringb *sb, char c);
122NONSTD_DEF stringv sb_as_sv(const stringb *sb);
123
124// Slice - generic non-owning view into an array
125// Usage: SLICE_DEF(int); slice(int) view = ...;
126#define SLICE_DEF(T)   \
127	typedef struct {   \
128		T *data;       \
129		size_t length; \
130	} slice_##T
131
132#define slice(T) slice_##T
133
134#define make_slice(T, ptr, len) ((slice(T)){.data = (ptr), .length = (len)})
135
136#define array_as_slice(T, arr) \
137	((slice(T)){.data = (arr).data, .length = (arr).length})
138
139// Dynamic array - generic type-safe growable array using macros
140// Usage: array(int) numbers; array_init(numbers);
141#define array(T)         \
142	struct {             \
143		T *data;         \
144		size_t length;   \
145		size_t capacity; \
146	}
147
148#define array_init(arr)     \
149	do {                    \
150		(arr).capacity = 0; \
151		(arr).data = NULL;  \
152		(arr).length = 0;   \
153	} while (0)
154
155#define array_init_cap(arr, initial_cap)                             \
156	do {                                                             \
157		(arr).capacity = (initial_cap) ? (initial_cap) : 16;         \
158		(arr).data = ALLOC(__typeof__(*(arr).data), (arr).capacity); \
159		(arr).length = 0;                                            \
160	} while (0)
161
162#define array_free(arr)     \
163	do {                    \
164		FREE((arr).data);   \
165		(arr).length = 0;   \
166		(arr).capacity = 0; \
167	} while (0)
168
169#define array_ensure(arr, additional)                                    \
170	do {                                                                 \
171		size_t _needed = (arr).length + (additional);                    \
172		if (_needed > (arr).capacity) {                                  \
173			size_t _new_cap = (arr).capacity ? (arr).capacity : 16;      \
174			while (_new_cap < _needed) {                                 \
175				if (_new_cap > SIZE_MAX / 2) {                           \
176					_new_cap = SIZE_MAX;                                 \
177					break;                                               \
178				}                                                        \
179				_new_cap *= 2;                                           \
180			}                                                            \
181			if (_new_cap < _needed) { /* Overflow or OOM */              \
182				break;                                                   \
183			}                                                            \
184			void *_new_data =                                            \
185				safe_realloc((arr).data, sizeof(*(arr).data), _new_cap); \
186			if (_new_data) {                                             \
187				(arr).data = _new_data;                                  \
188				(arr).capacity = _new_cap;                               \
189			}                                                            \
190		}                                                                \
191	} while (0)
192
193#define array_push(arr, value)                    \
194	do {                                          \
195		array_ensure((arr), 1);                   \
196		if ((arr).length < (arr).capacity) {      \
197			(arr).data[(arr).length++] = (value); \
198		}                                         \
199	} while (0)
200
201#define array_pop(arr) ((arr).length > 0 ? (arr).data[--(arr).length] : 0)
202
203#define array_get(arr, index) ((arr).data[index])
204
205#define array_set(arr, index, value)     \
206	do {                                 \
207		if ((index) < (arr).length) {    \
208			(arr).data[index] = (value); \
209		}                                \
210	} while (0)
211
212#define array_insert(arr, index, value)                              \
213	do {                                                             \
214		if ((index) <= (arr).length) {                               \
215			array_ensure((arr), 1);                                  \
216			if ((arr).length < (arr).capacity) {                     \
217				for (size_t _i = (arr).length; _i > (index); --_i) { \
218					(arr).data[_i] = (arr).data[_i - 1];             \
219				}                                                    \
220				(arr).data[index] = (value);                         \
221				(arr).length++;                                      \
222			}                                                        \
223		}                                                            \
224	} while (0)
225
226#define array_remove(arr, index)                                     \
227	do {                                                             \
228		if ((index) < (arr).length) {                                \
229			for (size_t _i = (index); _i < (arr).length - 1; ++_i) { \
230				(arr).data[_i] = (arr).data[_i + 1];                 \
231			}                                                        \
232			(arr).length--;                                          \
233		}                                                            \
234	} while (0)
235
236#define array_clear(arr)  \
237	do {                  \
238		(arr).length = 0; \
239	} while (0)
240
241#define array_reserve(arr, new_capacity)                                       \
242	do {                                                                       \
243		if ((new_capacity) > (arr).capacity) {                                 \
244			void *_new_data =                                                  \
245				safe_realloc((arr).data, sizeof(*(arr).data), (new_capacity)); \
246			if (_new_data) {                                                   \
247				(arr).data = _new_data;                                        \
248				(arr).capacity = (new_capacity);                               \
249			}                                                                  \
250		}                                                                      \
251	} while (0)
252
253#define array_foreach(arr, var)                                        \
254	for (size_t _i_##var = 0;                                          \
255		 _i_##var < (arr).length && ((var) = (arr).data[_i_##var], 1); \
256		 ++_i_##var)
257
258#define array_foreach_idx(arr, var, index) \
259	for (size_t index = 0;                 \
260		 index < (arr).length && ((var) = (arr).data[index], 1); ++index)
261
262// Arena - block-based memory allocator
263typedef struct {
264	char *ptr;
265	char *end;
266	array(char *) blocks;
267} Arena;
268
269#define ARENA_DEFAULT_BLOCK_SIZE (4096)
270
271NONSTD_DEF Arena arena_make(void);
272NONSTD_DEF void arena_grow(Arena *a, size_t min_size);
273NONSTD_DEF void *arena_alloc(Arena *a, size_t size);
274NONSTD_DEF void arena_free(Arena *a);
275
276// Image - simple RGB image structure
277typedef struct {
278	u8 r, g, b;
279} Color;
280
281typedef struct {
282	u32 width;
283	u32 height;
284	Color *pixels;
285} Canvas;
286
287#define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)})
288#define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)})
289
290#define COLOR_BLACK COLOR_RGB(0, 0, 0)
291#define COLOR_WHITE COLOR_RGB(255, 255, 255)
292#define COLOR_RED COLOR_RGB(255, 0, 0)
293#define COLOR_GREEN COLOR_RGB(0, 255, 0)
294#define COLOR_BLUE COLOR_RGB(0, 0, 255)
295#define COLOR_YELLOW COLOR_RGB(255, 255, 0)
296#define COLOR_MAGENTA COLOR_RGB(255, 0, 255)
297#define COLOR_CYAN COLOR_RGB(0, 255, 255)
298
299NONSTD_DEF Canvas ppm_init(u32 width, u32 height);
300NONSTD_DEF void ppm_free(Canvas *img);
301NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color);
302NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y);
303NONSTD_DEF int ppm_save(const Canvas *img, const char *filename);
304NONSTD_DEF Canvas ppm_read(const char *filename);
305NONSTD_DEF void ppm_fill(Canvas *canvas, Color color);
306NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color);
307NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color);
308NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 x, i32 y, i32 r, Color color);
309NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color);
310
311// File I/O helpers
312NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size);
313NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size);
314NONSTD_DEF stringb read_entire_file_sb(const char *filepath);
315NONSTD_DEF int write_file_sv(const char *filepath, stringv sv);
316NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb);
317
318#ifdef NONSTD_IMPLEMENTATION
319
320NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) {
321	if (count != 0 && item_size > SIZE_MAX / count) {
322		return NULL;
323	}
324	return malloc(item_size * count);
325}
326
327NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count) {
328	if (count != 0 && item_size > SIZE_MAX / count) {
329		return NULL;
330	}
331	return realloc(ptr, item_size * count);
332}
333
334// String View Implementation
335
336NONSTD_DEF stringv sv_from_cstr(const char *s) {
337	return (stringv){.data = s, .length = s ? strlen(s) : 0};
338}
339
340NONSTD_DEF stringv sv_from_parts(const char *data, size_t length) {
341	return (stringv){.data = data, .length = length};
342}
343
344NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end) {
345	if (start > sv.length) {
346		start = sv.length;
347	}
348	if (end > sv.length) {
349		end = sv.length;
350	}
351	if (start > end) {
352		start = end;
353	}
354	return (stringv){.data = sv.data + start, .length = end - start};
355}
356
357NONSTD_DEF int sv_equals(stringv a, stringv b) {
358	return a.length == b.length && (a.length == 0 || memcmp(a.data, b.data, a.length) == 0);
359}
360
361NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix) {
362	return sv.length >= prefix.length && memcmp(sv.data, prefix.data, prefix.length) == 0;
363}
364
365NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) {
366	return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0;
367}
368
369// String Builder Implementation
370
371NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) {
372	sb->capacity = initial_cap ? initial_cap : 16;
373	sb->data = ALLOC(char, sb->capacity);
374	sb->length = 0;
375	if (sb->data) {
376		sb->data[0] = '\0';
377	}
378}
379
380NONSTD_DEF void sb_free(stringb *sb) {
381	FREE(sb->data);
382	sb->length = 0;
383	sb->capacity = 0;
384}
385
386NONSTD_DEF void sb_ensure(stringb *sb, size_t additional) {
387	size_t needed = sb->length + additional + 1;
388	size_t new_cap = sb->capacity;
389
390	if (needed > new_cap) {
391		while (new_cap < needed) {
392			if (new_cap > SIZE_MAX / 2) {
393				new_cap = SIZE_MAX;
394				break;
395			}
396			new_cap *= 2;
397		}
398		if (new_cap < needed)
399			return; // Overflow
400
401		char *new_data = safe_realloc(sb->data, sizeof(char), new_cap);
402		if (new_data) {
403			sb->data = new_data;
404			sb->capacity = new_cap;
405		}
406	}
407}
408
409NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s) {
410	if (!s) {
411		return;
412	}
413	size_t slength = strlen(s);
414	sb_ensure(sb, slength);
415	if (sb->length + slength + 1 <= sb->capacity) {
416		memcpy(sb->data + sb->length, s, slength);
417		sb->length += slength;
418		sb->data[sb->length] = '\0';
419	}
420}
421
422NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv) {
423	if (!sv.data || sv.length == 0) {
424		return;
425	}
426	sb_ensure(sb, sv.length);
427	if (sb->length + sv.length + 1 <= sb->capacity) {
428		memcpy(sb->data + sb->length, sv.data, sv.length);
429		sb->length += sv.length;
430		sb->data[sb->length] = '\0';
431	}
432}
433
434NONSTD_DEF void sb_append_char(stringb *sb, char c) {
435	sb_ensure(sb, 1);
436	if (sb->length + 2 <= sb->capacity) {
437		sb->data[sb->length++] = c;
438		sb->data[sb->length] = '\0';
439	}
440}
441
442NONSTD_DEF stringv sb_as_sv(const stringb *sb) {
443	return (stringv){.data = sb->data, .length = sb->length};
444}
445
446NONSTD_DEF Arena arena_make(void) {
447	Arena a = {0};
448	array_init(a.blocks);
449	return a;
450}
451
452// Arena Implementation
453
454NONSTD_DEF void arena_grow(Arena *a, size_t min_size) {
455	size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size);
456	char *block = ALLOC(char, size);
457	a->ptr = block;
458	a->end = block + size;
459	array_push(a->blocks, block);
460}
461
462NONSTD_DEF void *arena_alloc(Arena *a, size_t size) {
463	// Align to 8 bytes basically
464	size_t align = sizeof(void *);
465	uintptr_t current = (uintptr_t)a->ptr;
466	uintptr_t aligned = (current + align - 1) & ~(align - 1);
467	uintptr_t end = (uintptr_t)a->end;
468
469	// Check for overflow (aligned wrapped around) or out of bounds (aligned >= end)
470	// or not enough space ((end - aligned) < size)
471	if (aligned < current || aligned >= end || (end - aligned) < size) {
472		arena_grow(a, size);
473		current = (uintptr_t)a->ptr;
474		aligned = (current + align - 1) & ~(align - 1);
475		end = (uintptr_t)a->end;
476	}
477
478	// Double check after grow (in case grow failed or size is just too huge)
479	if (aligned < current || aligned >= end || (end - aligned) < size) {
480		return NULL;
481	}
482
483	a->ptr = (char *)(aligned + size);
484	return (void *)aligned;
485}
486
487NONSTD_DEF void arena_free(Arena *a) {
488	char *block;
489	array_foreach(a->blocks, block) { FREE(block); }
490	array_free(a->blocks);
491	a->ptr = NULL;
492	a->end = NULL;
493}
494
495// File I/O Implementation
496
497NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) {
498	FILE *f = fopen(filepath, "rb");
499	if (!f) {
500		return NULL;
501	}
502
503	fseek(f, 0, SEEK_END);
504	long length = ftell(f);
505	fseek(f, 0, SEEK_SET);
506
507	if (length < 0) {
508		fclose(f);
509		return NULL;
510	}
511
512	size_t size = (size_t)length;
513	char *buffer = ALLOC(char, size + 1);
514	if (!buffer) {
515		fclose(f);
516		return NULL;
517	}
518
519	size_t read = fread(buffer, 1, size, f);
520	fclose(f);
521
522	if (read != size) {
523		FREE(buffer);
524		return NULL;
525	}
526
527	buffer[size] = '\0';
528	if (out_size) {
529		*out_size = size;
530	}
531
532	return buffer;
533}
534
535NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size) {
536	FILE *f = fopen(filepath, "wb");
537	if (!f) {
538		return 0;
539	}
540
541	size_t written = fwrite(data, 1, size, f);
542	fclose(f);
543
544	return written == size;
545}
546
547NONSTD_DEF stringb read_entire_file_sb(const char *filepath) {
548	size_t size = 0;
549	char *data = read_entire_file(filepath, &size);
550	stringb sb = {0};
551	if (data) {
552		sb.data = data;
553		sb.length = size;
554		sb.capacity = size + 1;
555	}
556	return sb;
557}
558
559NONSTD_DEF int write_file_sv(const char *filepath, stringv sv) {
560	return write_entire_file(filepath, sv.data, sv.length);
561}
562
563NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb) {
564	return write_entire_file(filepath, sb->data, sb->length);
565}
566
567// PPM Image Implementation
568
569NONSTD_DEF Canvas ppm_init(u32 width, u32 height) {
570	Canvas img = {0};
571	img.width = width;
572	img.height = height;
573	img.pixels = ALLOC(Color, width * height);
574	if (img.pixels) {
575		memset(img.pixels, 0, sizeof(Color) * width * height);
576	}
577	return img;
578}
579
580NONSTD_DEF void ppm_free(Canvas *img) {
581	if (img->pixels) {
582		FREE(img->pixels);
583	}
584	img->width = 0;
585	img->height = 0;
586}
587
588NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color) {
589	if (x < img->width && y < img->height) {
590		img->pixels[y * img->width + x] = color;
591	}
592}
593
594NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y) {
595	if (x < img->width && y < img->height) {
596		return img->pixels[y * img->width + x];
597	}
598	return (Color){0, 0, 0};
599}
600
601NONSTD_DEF int ppm_save(const Canvas *img, const char *filename) {
602	FILE *f = fopen(filename, "w");
603	if (!f) {
604		return 0;
605	}
606
607	fprintf(f, "P3\n%u %u\n255\n", img->width, img->height);
608	for (u32 y = 0; y < img->height; ++y) {
609		for (u32 x = 0; x < img->width; ++x) {
610			Color c = ppm_get_pixel(img, x, y);
611			fprintf(f, "%d %d %d ", c.r, c.g, c.b);
612		}
613		fprintf(f, "\n");
614	}
615
616	fclose(f);
617	return 1;
618}
619
620NONSTD_DEF Canvas ppm_read(const char *filename) {
621	Canvas img = {0};
622	FILE *f = fopen(filename, "r");
623	if (!f) {
624		return img;
625	}
626
627	char magic[3];
628	if (fscanf(f, "%2s", magic) != 1 || strcmp(magic, "P3") != 0) {
629		fclose(f);
630		return img;
631	}
632
633	u32 w, h, max_val;
634	if (fscanf(f, "%u %u %u", &w, &h, &max_val) != 3) {
635		fclose(f);
636		return img;
637	}
638
639	img = ppm_init(w, h);
640	if (!img.pixels) {
641		fclose(f);
642		return img;
643	}
644
645	for (u32 i = 0; i < w * h; ++i) {
646		int r, g, b;
647		if (fscanf(f, "%d %d %d", &r, &g, &b) != 3) {
648			ppm_free(&img);
649			fclose(f);
650			return (Canvas){0};
651		}
652		img.pixels[i] = (Color){(u8)r, (u8)g, (u8)b};
653	}
654
655	fclose(f);
656	return img;
657}
658
659NONSTD_DEF void ppm_fill(Canvas *canvas, Color color) {
660	for (u32 i = 0; i < canvas->width * canvas->height; ++i) {
661		canvas->pixels[i] = color;
662	}
663}
664
665NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color) {
666	if (w == 0 || h == 0) {
667		return;
668	}
669	for (u32 i = x; i < x + w; ++i) {
670		ppm_set_pixel(canvas, i, y, color);
671		ppm_set_pixel(canvas, i, y + h - 1, color);
672	}
673	for (u32 j = y; j < y + h; ++j) {
674		ppm_set_pixel(canvas, x, j, color);
675		ppm_set_pixel(canvas, x + w - 1, j, color);
676	}
677}
678
679NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color) {
680	i32 dx = abs(x1 - x0);
681	i32 dy = -abs(y1 - y0);
682	i32 sx = x0 < x1 ? 1 : -1;
683	i32 sy = y0 < y1 ? 1 : -1;
684	i32 err = dx + dy;
685
686	while (1) {
687		ppm_set_pixel(canvas, (u32)x0, (u32)y0, color);
688		if (x0 == x1 && y0 == y1) {
689			break;
690		}
691
692		i32 e2 = 2 * err;
693		if (e2 >= dy) {
694			err += dy;
695			x0 += sx;
696		}
697		if (e2 <= dx) {
698			err += dx;
699			y0 += sy;
700		}
701	}
702}
703
704NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 xm, i32 ym, i32 r, Color color) {
705	i32 x = -r, y = 0, err = 2 - 2 * r;
706	do {
707		ppm_set_pixel(canvas, (u32)(xm - x), (u32)(ym + y), color);
708		ppm_set_pixel(canvas, (u32)(xm - y), (u32)(ym - x), color);
709		ppm_set_pixel(canvas, (u32)(xm + x), (u32)(ym - y), color);
710		ppm_set_pixel(canvas, (u32)(xm + y), (u32)(ym + x), color);
711		r = err;
712		if (r <= y) {
713			err += ++y * 2 + 1;
714		}
715		if (r > x || err > y) {
716			err += ++x * 2 + 1;
717		}
718	} while (x < 0);
719}
720
721NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color) {
722	ppm_draw_line(canvas, x0, y0, x1, y1, color);
723	ppm_draw_line(canvas, x1, y1, x2, y2, color);
724	ppm_draw_line(canvas, x2, y2, x0, y0, color);
725}
726
727#endif // NONSTD_IMPLEMENTATION
728
729#endif // NONSTD_H
730
731/*
732BSD 2-Clause License
733
734Copyright (c) 2026, Mitja Felicijan <mitja.felicijan@gmail.com>
735
736Redistribution and use in source and binary forms, with or without
737modification, are permitted provided that the following conditions are met:
738
7391. Redistributions of source code must retain the above copyright notice, this
740   list of conditions and the following disclaimer.
741
7422. Redistributions in binary form must reproduce the above copyright notice,
743   this list of conditions and the following disclaimer in the documentation
744   and/or other materials provided with the distribution.
745
746THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
747AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
748IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
749DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
750FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
751DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
752SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
753CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
754OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
755OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
756*/