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