Mercurial > libbuf
comparison buf.c @ 0:b1991ee4451d
misc: initial import
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 29 Oct 2020 17:24:30 +0100 |
parents | |
children | 2694997c4c90 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:b1991ee4451d |
---|---|
1 /* | |
2 * buf.c -- simple string buffer for C | |
3 * | |
4 * Copyright (c) 2019-2020 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 | |
24 #include "buf.h" | |
25 | |
26 /* | |
27 * Try to increase the buffer length by a power of two until we have enough | |
28 * space to fit `desired'. | |
29 * | |
30 * Detects overflow and return false if happened or reallocation could not | |
31 * occur. | |
32 */ | |
33 bool | |
34 _buf_growdbl(struct buf *b, size_t desired) | |
35 { | |
36 size_t newcap = b->capacity; | |
37 void *newptr; | |
38 | |
39 while (desired > newcap - b->length) { | |
40 const size_t r = newcap * 2; | |
41 | |
42 if (r / newcap != 2) { | |
43 #if defined(ENOMEM) | |
44 errno = ENOMEM; | |
45 #endif | |
46 return false; | |
47 } | |
48 | |
49 newcap = r; | |
50 } | |
51 | |
52 /* At this step we must have enough room. */ | |
53 assert(newcap - b->length >= desired); | |
54 | |
55 if (!(newptr = BUF_REALLOC(b->data, newcap + 1))) | |
56 return false; | |
57 | |
58 b->data = newptr; | |
59 b->capacity = newcap; | |
60 | |
61 return true; | |
62 } | |
63 | |
64 /* | |
65 * Try to allocate just enough space for the `desired' amount of space. This is | |
66 * only used when the buffer is already too large but hasn't reached SIZE_MAX | |
67 * yet. | |
68 * | |
69 * Returns false if allocation failed. | |
70 */ | |
71 bool | |
72 _buf_growmin(struct buf *b, size_t desired) | |
73 { | |
74 size_t newcap; | |
75 void *newptr; | |
76 | |
77 if (desired >= SIZE_MAX - b->length) { | |
78 #if defined(ENOMEM) | |
79 errno = ENOMEM; | |
80 #endif | |
81 return false; | |
82 } | |
83 | |
84 /* Don't forget to keep what's remaining between capacity and length. */ | |
85 newcap = b->capacity + (desired - (b->capacity - b->length)); | |
86 | |
87 /* Try to reallocate. */ | |
88 if (!(newptr = BUF_REALLOC(b->data, newcap + 1))) | |
89 return false; | |
90 | |
91 b->data = newptr; | |
92 b->capacity = newcap; | |
93 | |
94 return true; | |
95 } | |
96 | |
97 bool | |
98 _buf_grow(struct buf *b, size_t desired) | |
99 { | |
100 const size_t avail = b->capacity - b->length; | |
101 | |
102 if (avail >= desired) | |
103 return true; | |
104 | |
105 if (!b->capacity) { | |
106 if (!(b->data = BUF_MALLOC(desired + 1))) | |
107 return false; | |
108 | |
109 b->capacity = desired; | |
110 } else if (!_buf_growdbl(b, desired) && !_buf_growmin(b, desired)) | |
111 return false; | |
112 | |
113 return true; | |
114 } |