comparison src/libmlk-core/core/buf.c @ 347:0969931b98e4

core: start removing POSIX APIs
author David Demelier <markand@malikania.fr>
date Tue, 19 Oct 2021 13:23:30 +0200
parents
children 460c78706989
comparison
equal deleted inserted replaced
346:323d13f49233 347:0969931b98e4
1 /*
2 * buf.c -- simple string buffer for C
3 *
4 * Copyright (c) 2019-2021 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "buf.h"
27
28 /*
29 * Try to increase the buffer length by a power of two until we have enough
30 * space to fit `desired'.
31 *
32 * Detects overflow and return -1 if happened or reallocation could not
33 * occur.
34 */
35 static int
36 growdbl(struct buf *b, size_t desired)
37 {
38 size_t newcap = b->capacity;
39 void *newptr;
40
41 while (desired > newcap - b->length) {
42 const size_t r = newcap * 2;
43
44 /* Overflow occured. */
45 if (r / newcap != 2) {
46 #if defined(ENOMEM)
47 errno = ENOMEM;
48 #endif
49 return -1;
50 }
51
52 newcap = r;
53 }
54
55 /* At this step we must have enough room. */
56 assert(newcap - b->length >= desired);
57
58 /* Pretty much impossible to reach but always assume it's possible. */
59 if (newcap == SIZE_MAX) {
60 #if defined(ENOMEM)
61 errno = ENOMEM;
62 #endif
63 return -1;
64 }
65
66 if (!(newptr = BUF_REALLOC(b->data, newcap + 1)))
67 return -1;
68
69 b->data = newptr;
70 b->capacity = newcap;
71
72 return 0;
73 }
74
75 /*
76 * Try to allocate just enough space for the `desired' amount of space. This is
77 * only used when the buffer is already too large but hasn't reached SIZE_MAX
78 * yet.
79 *
80 * Returns -1 if allocation failed.
81 */
82 static int
83 growmin(struct buf *b, size_t desired)
84 {
85 size_t newcap;
86 void *newptr;
87
88 if (desired >= SIZE_MAX - b->length) {
89 #if defined(ENOMEM)
90 errno = ENOMEM;
91 #endif
92 return -1;
93 }
94
95 /* Don't forget to keep what's remaining between capacity and length. */
96 newcap = b->capacity + (desired - (b->capacity - b->length));
97
98 /* Try to reallocate. */
99 if (!(newptr = BUF_REALLOC(b->data, newcap + 1)))
100 return -1;
101
102 b->data = newptr;
103 b->capacity = newcap;
104
105 return 0;
106 }
107
108 /*
109 * Entry point for reallocating data. Will try to allocate twice until we have
110 * enough room and then only the minimal amount.
111 */
112 static int
113 grow(struct buf *b, size_t desired)
114 {
115 const size_t avail = b->capacity - b->length;
116
117 if (avail >= desired)
118 return 0;
119
120 if (!b->capacity) {
121 if (!(b->data = BUF_MALLOC(desired + 1)))
122 return -1;
123
124 b->capacity = desired;
125 } else if (growdbl(b, desired) < 0 && growmin(b, desired) < 0)
126 return -1;
127
128 return 0;
129 }
130
131 void
132 buf_init(struct buf *b)
133 {
134 assert(b);
135
136 memset(b, 0, sizeof (*b));
137 }
138
139 int
140 buf_reserve(struct buf *b, size_t amount)
141 {
142 assert(b);
143
144 if (grow(b, amount) < 0)
145 return -1;
146
147 return 0;
148 }
149
150 int
151 buf_resize(struct buf *b, size_t size, char ch)
152 {
153 assert(b);
154
155 /* New size is smaller than curren't length, just update it. */
156 if (size < b->length) {
157 b->data[b->length = size] = 0;
158 return 0;
159 }
160
161 /* New size is bigger, data may be reallocated. */
162 if (grow(b, size - b->length) < 0)
163 return -1;
164
165 memset(&b->data[b->length], ch, size - b->length);
166 b->length = size;
167 b->data[b->length] = 0;
168
169 return 0;
170 }
171
172 int
173 buf_shrink(struct buf *b)
174 {
175 assert(b);
176
177 void *newptr;
178
179 if (b->length == 0) {
180 free(b->data);
181 b->data = NULL;
182 b->length = b->capacity = 0;
183 return 0;
184 }
185
186 if (!(newptr = BUF_REALLOC(b->data, b->length + 1)))
187 return -1;
188
189 b->data = newptr;
190 b->capacity = b->length;
191
192 return 0;
193 }
194
195 void
196 buf_erase(struct buf *b, size_t pos, size_t count)
197 {
198 assert(b);
199 assert(pos <= b->length);
200
201 if (count > b->length - pos) {
202 /* Optimize whole erase at pos. */
203 b->data[pos] = 0;
204 b->length = pos;
205 } else {
206 memmove(&b->data[pos], &b->data[pos + count], b->length - count);
207 b->length -= count;
208 }
209 }
210
211 int
212 buf_putc(struct buf *b, char c)
213 {
214 assert(b);
215
216 if (grow(b, 1) < 0)
217 return -1;
218
219 b->data[b->length++] = c;
220 b->data[b->length] = 0;
221
222 return 0;
223 }
224
225 int
226 buf_puts(struct buf *b, const char *s)
227 {
228 assert(b);
229 assert(s);
230
231 const size_t len = strlen(s);
232
233 if (grow(b, len) < 0)
234 return -1;
235
236 memcpy(&b->data[b->length], s, len + 1);
237 b->length += len;
238
239 return 0;
240 }
241
242 int
243 buf_printf(struct buf *b, const char *fmt, ...)
244 {
245 assert(b);
246 assert(fmt);
247
248 va_list ap;
249 int ret;
250
251 va_start(ap, fmt);
252 ret = buf_vprintf(b, fmt, ap);
253 va_end(ap);
254
255 return ret;
256 }
257
258 int
259 buf_vprintf(struct buf *b, const char *fmt, va_list args)
260 {
261 assert(b);
262 assert(fmt);
263
264 va_list ap;
265 int amount;
266
267 /* Determine length. */
268 va_copy(ap, args);
269 amount = vsnprintf(NULL, 0, fmt, ap);
270 va_end(ap);
271
272 if (amount < 0)
273 return -1;
274
275 /* Do actual copy. */
276 if (grow(b, amount) < 0)
277 return -1;
278
279 va_copy(ap, args);
280 amount = vsprintf(&b->data[b->length], fmt, ap);
281 va_end(ap);
282
283 if (amount < 0)
284 return -1;
285
286 b->length += amount;
287
288 return 0;
289 }
290
291 int
292 buf_sub(struct buf *b, const struct buf *src, size_t pos, size_t count)
293 {
294 assert(b);
295 assert(src);
296 assert(pos <= src->length);
297
298 if (count >= src->length)
299 count = src->length - pos;
300 if (!(b->data = BUF_MALLOC(count + 1)))
301 return -1;
302
303 strncpy(b->data, &src->data[pos], count);
304 b->length = count;
305 b->capacity = count;
306 b->data[b->length] = 0;
307
308 return 0;
309 }
310
311 int
312 buf_dup(struct buf *b, const struct buf *src)
313 {
314 assert(b);
315 assert(src);
316
317 if (!src->data)
318 return 0;
319 if (!(b->data = BUF_MALLOC(src->length + 1)))
320 return -1;
321
322 memcpy(b->data, src->data, src->length + 1);
323 b->capacity = src->length;
324 b->length = src->length;
325
326 return 0;
327 }
328
329 void
330 buf_clear(struct buf *b)
331 {
332 assert(b);
333
334 if (b->data)
335 b->data[b->length = 0] = 0;
336 }
337
338 void
339 buf_finish(struct buf *b)
340 {
341 assert(b);
342
343 BUF_FREE(b->data);
344 b->data = NULL;
345 b->capacity = b->length = 0;
346 }