]> Sergey Matveev's repositories - bfs.git/blob - src/dstring.c
Formatting fixes
[bfs.git] / src / dstring.c
1 // Copyright © Tavian Barnes <tavianator@tavianator.com>
2 // SPDX-License-Identifier: 0BSD
3
4 #include "dstring.h"
5 #include "alloc.h"
6 #include "bit.h"
7 #include "config.h"
8 #include "diag.h"
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 /**
15  * The memory representation of a dynamic string.  Users get a pointer to data.
16  */
17 struct dstring {
18         size_t capacity;
19         size_t length;
20         alignas(dchar) char data[];
21 };
22
23 /** Get the string header from the string data pointer. */
24 static struct dstring *dstrheader(const dchar *dstr) {
25         return (struct dstring *)(dstr - offsetof(struct dstring, data));
26 }
27
28 /** Get the correct size for a dstring with the given capacity. */
29 static size_t dstrsize(size_t capacity) {
30         return sizeof_flex(struct dstring, data, capacity + 1);
31 }
32
33 /** Allocate a dstring with the given contents. */
34 static dchar *dstralloc_impl(size_t capacity, size_t length, const char *data) {
35         // Avoid reallocations for small strings
36         if (capacity < 7) {
37                 capacity = 7;
38         }
39
40         struct dstring *header = malloc(dstrsize(capacity));
41         if (!header) {
42                 return NULL;
43         }
44
45         header->capacity = capacity;
46         header->length = length;
47
48         memcpy(header->data, data, length);
49         header->data[length] = '\0';
50         return header->data;
51 }
52
53 dchar *dstralloc(size_t capacity) {
54         return dstralloc_impl(capacity, 0, "");
55 }
56
57 dchar *dstrdup(const char *str) {
58         return dstrxdup(str, strlen(str));
59 }
60
61 dchar *dstrndup(const char *str, size_t n) {
62         return dstrxdup(str, strnlen(str, n));
63 }
64
65 dchar *dstrddup(const dchar *dstr) {
66         return dstrxdup(dstr, dstrlen(dstr));
67 }
68
69 dchar *dstrxdup(const char *str, size_t len) {
70         return dstralloc_impl(len, len, str);
71 }
72
73 size_t dstrlen(const dchar *dstr) {
74         return dstrheader(dstr)->length;
75 }
76
77 int dstreserve(dchar **dstr, size_t capacity) {
78         if (!*dstr) {
79                 *dstr = dstralloc(capacity);
80                 return *dstr ? 0 : -1;
81         }
82
83         struct dstring *header = dstrheader(*dstr);
84
85         if (capacity > header->capacity) {
86                 capacity = bit_ceil(capacity + 1) - 1;
87
88                 header = realloc(header, dstrsize(capacity));
89                 if (!header) {
90                         return -1;
91                 }
92                 header->capacity = capacity;
93
94                 *dstr = header->data;
95         }
96
97         return 0;
98 }
99
100 int dstresize(dchar **dstr, size_t length) {
101         if (dstreserve(dstr, length) != 0) {
102                 return -1;
103         }
104
105         struct dstring *header = dstrheader(*dstr);
106         header->length = length;
107         header->data[length] = '\0';
108         return 0;
109 }
110
111 int dstrcat(dchar **dest, const char *src) {
112         return dstrxcat(dest, src, strlen(src));
113 }
114
115 int dstrncat(dchar **dest, const char *src, size_t n) {
116         return dstrxcat(dest, src, strnlen(src, n));
117 }
118
119 int dstrdcat(dchar **dest, const dchar *src) {
120         return dstrxcat(dest, src, dstrlen(src));
121 }
122
123 int dstrxcat(dchar **dest, const char *src, size_t len) {
124         size_t oldlen = dstrlen(*dest);
125         size_t newlen = oldlen + len;
126
127         if (dstresize(dest, newlen) != 0) {
128                 return -1;
129         }
130
131         memcpy(*dest + oldlen, src, len);
132         return 0;
133 }
134
135 int dstrapp(dchar **str, char c) {
136         return dstrxcat(str, &c, 1);
137 }
138
139 int dstrcpy(dchar **dest, const char *src) {
140         return dstrxcpy(dest, src, strlen(src));
141 }
142
143 int dstrncpy(dchar **dest, const char *src, size_t n) {
144         return dstrxcpy(dest, src, strnlen(src, n));
145 }
146
147 int dstrdcpy(dchar **dest, const dchar *src) {
148         return dstrxcpy(dest, src, dstrlen(src));
149 }
150
151 int dstrxcpy(dchar **dest, const char *src, size_t len) {
152         if (dstresize(dest, len) != 0) {
153                 return -1;
154         }
155
156         memcpy(*dest, src, len);
157         return 0;
158 }
159
160 char *dstrprintf(const char *format, ...) {
161         va_list args;
162
163         va_start(args, format);
164         dchar *str = dstrvprintf(format, args);
165         va_end(args);
166
167         return str;
168 }
169
170 char *dstrvprintf(const char *format, va_list args) {
171         // Guess a capacity to try to avoid reallocating
172         dchar *str = dstralloc(2 * strlen(format));
173         if (!str) {
174                 return NULL;
175         }
176
177         if (dstrvcatf(&str, format, args) != 0) {
178                 dstrfree(str);
179                 return NULL;
180         }
181
182         return str;
183 }
184
185 int dstrcatf(dchar **str, const char *format, ...) {
186         va_list args;
187
188         va_start(args, format);
189         int ret = dstrvcatf(str, format, args);
190         va_end(args);
191
192         return ret;
193 }
194
195 int dstrvcatf(dchar **str, const char *format, va_list args) {
196         // Guess a capacity to try to avoid calling vsnprintf() twice
197         size_t len = dstrlen(*str);
198         dstreserve(str, len + 2 * strlen(format));
199         size_t cap = dstrheader(*str)->capacity;
200
201         va_list copy;
202         va_copy(copy, args);
203
204         char *tail = *str + len;
205         int ret = vsnprintf(tail, cap - len + 1, format, args);
206         if (ret < 0) {
207                 goto fail;
208         }
209
210         size_t tail_len = ret;
211         if (tail_len > cap - len) {
212                 cap = len + tail_len;
213                 if (dstreserve(str, cap) != 0) {
214                         goto fail;
215                 }
216
217                 tail = *str + len;
218                 ret = vsnprintf(tail, tail_len + 1, format, copy);
219                 if (ret < 0 || (size_t)ret != tail_len) {
220                         bfs_bug("Length of formatted string changed");
221                         goto fail;
222                 }
223         }
224
225         va_end(copy);
226
227         struct dstring *header = dstrheader(*str);
228         header->length += tail_len;
229         return 0;
230
231 fail:
232         *tail = '\0';
233         return -1;
234 }
235
236 int dstrescat(dchar **dest, const char *str, enum wesc_flags flags) {
237         return dstrnescat(dest, str, SIZE_MAX, flags);
238 }
239
240 int dstrnescat(dchar **dest, const char *str, size_t n, enum wesc_flags flags) {
241         size_t len = *dest ? dstrlen(*dest) : 0;
242
243         // Worst case growth is `ccc...` => $'\xCC\xCC\xCC...'
244         n = strnlen(str, n);
245         size_t cap = len + 4 * n + 3;
246         if (dstreserve(dest, cap) != 0) {
247                 return -1;
248         }
249
250         char *cur = *dest + len;
251         char *end = *dest + cap + 1;
252         cur = wordnesc(cur, end, str, n, flags);
253         bfs_assert(cur != end, "wordesc() result truncated");
254
255         return dstresize(dest, cur - *dest);
256 }
257
258 void dstrfree(dchar *dstr) {
259         if (dstr) {
260                 free(dstrheader(dstr));
261         }
262 }