Mercurial > molko
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 } |