Mercurial > libbuf
annotate buf.c @ 19:cf6a2b8642c1
misc: added tag 0.2.0 for changeset 7f5b3f035bef
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 16 Dec 2020 16:20:43 +0100 |
parents | 2694997c4c90 |
children | 939fe74cd80a |
rev | line source |
---|---|
0 | 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 | |
17
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
42 /* Overflow occured. */ |
0 | 43 if (r / newcap != 2) { |
44 #if defined(ENOMEM) | |
45 errno = ENOMEM; | |
46 #endif | |
47 return false; | |
48 } | |
49 | |
50 newcap = r; | |
51 } | |
52 | |
53 /* At this step we must have enough room. */ | |
54 assert(newcap - b->length >= desired); | |
55 | |
17
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
56 /* Pretty much impossible to reach but always assume it's possible. */ |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
57 if (newcap == SIZE_MAX) { |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
58 #if defined(ENOMEM) |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
59 errno = ENOMEM; |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
60 #endif |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
61 return false; |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
62 } |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
63 |
0 | 64 if (!(newptr = BUF_REALLOC(b->data, newcap + 1))) |
65 return false; | |
66 | |
67 b->data = newptr; | |
68 b->capacity = newcap; | |
69 | |
70 return true; | |
71 } | |
72 | |
73 /* | |
74 * Try to allocate just enough space for the `desired' amount of space. This is | |
75 * only used when the buffer is already too large but hasn't reached SIZE_MAX | |
76 * yet. | |
77 * | |
78 * Returns false if allocation failed. | |
79 */ | |
80 bool | |
81 _buf_growmin(struct buf *b, size_t desired) | |
82 { | |
83 size_t newcap; | |
84 void *newptr; | |
85 | |
86 if (desired >= SIZE_MAX - b->length) { | |
87 #if defined(ENOMEM) | |
88 errno = ENOMEM; | |
89 #endif | |
90 return false; | |
91 } | |
92 | |
93 /* Don't forget to keep what's remaining between capacity and length. */ | |
94 newcap = b->capacity + (desired - (b->capacity - b->length)); | |
95 | |
96 /* Try to reallocate. */ | |
97 if (!(newptr = BUF_REALLOC(b->data, newcap + 1))) | |
98 return false; | |
99 | |
100 b->data = newptr; | |
101 b->capacity = newcap; | |
102 | |
103 return true; | |
104 } | |
105 | |
17
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
106 /* |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
107 * Entry point for reallocating data. Will try to allocate twice until we have |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
108 * enough room and then only the minimal amount. |
2694997c4c90
buf: add a test against SIZE_MAX when doubling
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
109 */ |
0 | 110 bool |
111 _buf_grow(struct buf *b, size_t desired) | |
112 { | |
113 const size_t avail = b->capacity - b->length; | |
114 | |
115 if (avail >= desired) | |
116 return true; | |
117 | |
118 if (!b->capacity) { | |
119 if (!(b->data = BUF_MALLOC(desired + 1))) | |
120 return false; | |
121 | |
122 b->capacity = desired; | |
123 } else if (!_buf_growdbl(b, desired) && !_buf_growmin(b, desired)) | |
124 return false; | |
125 | |
126 return true; | |
127 } |