aboutsummaryrefslogtreecommitdiff
path: root/da.h
blob: 8fea4db977c2ec78af16331ac8b42d060e6ed0fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Simple & stupid dynamic array single-header implementation.  You can use the
 * macros defined in this file with any structure that has the following fields:
 *
 *  struct dyn_array {
 *      T *buf  // Array of items
 *      N  len  // Length of array
 *      N  cap  // Capacity of array
 *  }
 *
 * The type ‘T’ is whatever type you want to store.  The type ‘N’ is any numeric
 * type — most likely ‘size_t’ — but it could be sized as well.
 *
 * The daremove() macro also doesn’t bother with shrinking your array when the
 * length is far lower than the capacity.  If you care about that, do it
 * yourself.
 *
 * Remember to call free() on your dynamic arrays ‘buf’ field after use.
 *
 *
 * Macro Overview
 * ――――――――――――――
 * The argument ‘a’ to all of the below macros is a pointer to the dynamic array
 * structure.
 *
 * dainit(a, n)             Initialize the array with a capacity of ‘n’ items
 * dapush(a, x)             Append the item ‘x’ to the array
 * daextend(a, xs, n)       Append the ‘n’ items pointed to by ‘xs’ to the array
 * daremove(a, x)           Remove the item at index ‘x’ from the array
 * da_remove_range(a, x, y) Remove the items between the range [x, y)
 * da_foreach(a, p)         Iterate the pointer ‘p’ over each element of the
 *                          array.  The type of ‘p’ is inferred.
 *
 * The ‘dapush()’ and ‘daextend()’ macros will double the arrays capacity when
 * it gets full.  If you would like your arrays to grow with a different scale,
 * edit this file.
 *
 *
 * Example
 * ―――――――
 *
 * struct {
 *     int *buf;
 *     size_t len, cap;
 * } nums;
 *
 * // Initialize nums with capacity == 4
 * dainit(&nums, 4);
 *
 * // Append 69, 1337, and 420 to nums
 * dapush(&nums, 69);
 * dapush(&nums, 1337);
 * dapush(&nums, 420);
 *
 * da_foreach (&nums, n) {
 *     int x = *n << 1;
 *     printf("n = %d; n² = %d\n", *n, x);
 * }
 *
 * // Remove 1337 and 420 from nums
 * da_remove_range(&nums, 1, 3);
 *
 * // Remove 69 from nums
 * daremove(&nums, 0);
 */

#ifndef MANGO_DA_H
#define MANGO_DA_H

#include <err.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#if __STDC_VERSION__ >= 202311L
#	define DA_NULL nullptr
#else
#	define DA_NULL NULL
#endif

#define DA_ALLOC(p, n) \
	do { \
		if ((n) && SIZE_MAX / (n) < sizeof(*(p))) { \
			errno = EOVERFLOW; \
			err(EXIT_FAILURE, "realloc"); \
		} \
		if (!((p) = realloc((p), (n) * sizeof(*(p))))) \
			err(EXIT_FAILURE, "realloc"); \
	} while (0)

#define dainit(a, n) \
	do { \
		(a)->buf = DA_NULL; \
		(a)->cap = (n); \
		(a)->len = 0; \
		if (n) \
			DA_ALLOC((a)->buf, (a)->cap); \
	} while (0)

#define dapush(a, x) \
	do { \
		if ((a)->len >= (a)->cap) { \
			(a)->cap = (a)->cap ? (a)->cap * 2 : 1; \
			DA_ALLOC((a)->buf, (a)->cap); \
		} \
		(a)->buf[(a)->len++] = (x); \
	} while (0)

#define daextend(a, xs, n) \
	do { \
		if ((a)->len + (n) >= (a)->cap) { \
			do \
				(a)->cap = (a)->cap ? (a)->cap * 2 : 1; \
			while ((a)->len + (n) >= (a)->cap); \
			DA_ALLOC((a)->buf, (a)->cap); \
		} \
		memcpy((a)->buf + (a)->len, (xs), (n)); \
		(a)->len += (n); \
	} while (0)

#define daremove(a, i) da_remove_range((a), (i), (i) + 1)

#define da_remove_range(a, i, j) \
	do { \
		memmove((a)->buf + (i), (a)->buf + (j), \
		        ((a)->len - (j)) * sizeof(*(a)->buf)); \
		(a)->len -= j - i; \
	} while (0)

#define da_foreach(a, p) \
	for (auto p = (a)->buf; (size_t)(p - (a)->buf) < (a)->len; p++)

#endif /* !MANGO_DA_H */