-
Notifications
You must be signed in to change notification settings - Fork 50
/
tinybuf.h
219 lines (168 loc) · 7.07 KB
/
tinybuf.h
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* TinyBuf - simple dynamic array - public domain - Bernhard Schelling 2020
https://github.com/schellingb/c-data-structures
no warranty implied; use at your own risk
This file implements stretchy buffers as invented (?) by Sean Barrett.
Based on the implementation from the public domain Bitwise project
by Per Vognsen - https://github.com/pervognsen/bitwise
It's a super simple type safe dynamic array for C with no need
to predeclare any type or anything.
The first time an element is added, memory for 16 elements are allocated.
Then every time length is about to exceed capacity, capacity is doubled.
Can be used in C++ with POD types (without any constructor/destructor).
Be careful not to supply modifying statements to the macro arguments.
Something like BUF_REMOVE(buf, i--); would have unintended results.
--- Added by Pierre Gentile - [email protected] - 2024 ---
BUF_MALLOC and BUF_REALLOC can be #defined to specify alternative memory
allocation and reallocation functions.
When not #defined, the standard malloc and realloc functions are used.
--- End ---
Sample usage:
mytype_t* buf = NULL;
BUF_PUSH(buf, some_element);
BUF_PUSH(buf, other_element);
-- now BUF_LEN(buf) == 2, buf[0] == some_element, buf[1] == other_element
-- Free allocated memory:
BUF_FREE(buf);
-- now buf == NULL, BUF_LEN(buf) == 0, BUF_CAP(buf) == 0
-- Explicitly increase allocated memory and set capacity:
BUF_FIT(buf, 100);
-- now BUF_LEN(buf) == 0, BUF_CAP(buf) == 100
-- Resize buffer (does not initialize or zero memory!):
BUF_RESIZE(buf, 200);
-- now BUF_LEN(buf) == 200, BUF_CAP(buf) == 200
-- Remove an element in the middle, keeping order:
BUF_REMOVE(buf, 30);
-- now BUF_LEN(buf) == 199, everything past 30 shifted 1 up
-- Remove an element in the middle, swapping the last element into it:
BUF_SWAPREMOVE(buf, 10);
-- now BUF_LEN(buf) == 198, element 198 was copied into 10
-- Insert an element into the middle of the array:
BUF_INSERT(buf, 100, some_element);
-- now BUF_LEN(buf) == 199, everything past 99 shifted 1 down
-- Make a gap of a given size in the middle of the array:
mytype_t* ptr_gap = BUF_MAKEGAP(buf, 20, 11);
-- now BUF_LEN(buf) == 210, everything past 19 shifted 11 down
-- Add multiple elements at the end (uninitialized):
mytype_t* ptr1 = BUF_ADD(buf, 10);
-- now BUF_LEN(buf) == 220, ptr1 points to buf[210]
-- Add multiple elements at the end (zeroing memory):
mytype_t* ptr2 = BUF_ADDZEROED(buf, 10);
-- now BUF_LEN(buf) == 230, ptr2 points to buf[220]
-- To handle running out of memory:
bool ran_out_of_memory = !BUF_TRYFIT(buf, 1000);
-- before RESIZE or PUSH. When out of memory, buf will stay unmodified.
PUBLIC DOMAIN (UNLICENSE)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#ifndef TINYBUF_H
#define TINYBUF_H
#include <stdlib.h> /* for malloc, realloc */
#include <string.h> /* for memmove, memset */
#include <stddef.h> /* for size_t */
/* Query functions. */
/* """""""""""""""" */
#define BUF_LEN(b) ((b) ? BUF__HDR(b)->len : 0)
#define BUF_CAP(b) ((b) ? BUF__HDR(b)->cap : 0)
#define BUF_END(b) ((b) + BUF_LEN(b))
#define BUF_SIZEOF(b) ((b) ? BUF_LEN(b) * sizeof(*b) : 0)
/* Modifying functions. */
/* """""""""""""""""""" */
#define BUF_FREE(b) \
((b) ? (free(BUF__HDR(b)), (*(void **)(&(b)) = (void *)0)) : 0)
#define BUF_FIT(b, n) \
((size_t)(n) <= BUF_CAP(b) \
? 0 \
: (*(void **)(&(b)) = buf__grow((b), (size_t)(n), sizeof(*(b)))))
#define BUF_PUSH(b, val) \
(BUF_FIT((b), BUF_LEN(b) + 1), (b)[BUF__HDR(b)->len++] = (val))
#define BUF_POP(b) (b)[--BUF__HDR(b)->len]
#define BUF_RESIZE(b, sz) \
(BUF_FIT((b), (sz)), ((b) ? BUF__HDR(b)->len = (sz) : 0))
#define BUF_CLEAR(b) ((b) ? BUF__HDR(b)->len = 0 : 0)
#define BUF_TRYFIT(b, n) \
(BUF_FIT((b), (n)), (((b) && BUF_CAP(b) >= (size_t)(n)) || !(n)))
/* Utility functions. */
/* """""""""""""""""" */
#define BUF_SHIFT(b, to, fr, n) \
((b) ? memmove((b) + (to), (b) + (fr), (n) * sizeof(*(b))) : 0)
#define BUF_REMOVE(b, idx) \
(BUF_SHIFT(b, idx, (idx) + 1, --BUF__HDR(b)->len - (idx)))
#define BUF_SWAPREMOVE(b, idx) (BUF_SHIFT(b, idx, --BUF__HDR(b)->len, 1))
#define BUF_MAKEGAP(b, idx, sz) \
(BUF_RESIZE((b), BUF_LEN(b) + (sz)), \
BUF_SHIFT(b, (idx) + (sz), idx, BUF_LEN(b) - (idx) - (sz)), \
(b) + (idx))
#define BUF_INSERT(b, idx, val) (*BUF_MAKEGAP(b, idx, 1) = (val))
#define BUF_ZERO(b, idx, n) (memset((b) + (idx), 0, (n) * sizeof(*(b))))
#define BUF_ADD(b, n) \
(BUF_FIT(b, BUF_LEN(b) + (n)), (b) + (BUF__HDR(b)->len += (n)) - (n))
#define BUF_ADDZEROED(b, n) \
(BUF_FIT(b, BUF_LEN(b) + (n)), \
BUF_ZERO(b, BUF_LEN(b), n), \
(b) + (BUF__HDR(b)->len += (n)) - (n))
#define BUF__HDR(b) (((struct buf__hdr *)(b)) - 1)
struct buf__hdr
{
size_t len, cap;
};
#ifdef __GNUC__
__attribute__((__unused__))
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4505) /* Unref. local function has been removed. */
#endif
#ifndef BUF_MALLOC
#define BUF_MALLOC malloc
#endif
#ifndef BUF_REALLOC
#define BUF_REALLOC realloc
#endif
static void *
buf__grow(void *buf, size_t new_len, size_t elem_size)
{
struct buf__hdr *new_hdr;
size_t new_cap = 2 * BUF_CAP(buf), new_size;
if (new_cap < new_len)
new_cap = new_len;
if (new_cap < 16)
new_cap = 16;
new_size = sizeof(struct buf__hdr) + new_cap * elem_size;
if (buf)
{
new_hdr = (struct buf__hdr *)BUF_REALLOC(BUF__HDR(buf), new_size);
if (!new_hdr)
return buf; /* Out of memory, return unchanged. */
}
else
{
new_hdr = (struct buf__hdr *)BUF_MALLOC(new_size);
if (!new_hdr)
return (void *)0; /* Out of memory. */
new_hdr->len = 0;
}
new_hdr->cap = new_cap;
return new_hdr + 1;
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif