Line data Source code
1 : /*
2 : * lwan - web server
3 : * Copyright (c) 2012 L. A. F. Pereira <l@tia.mat.br>
4 : *
5 : * This program is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU General Public License
7 : * as published by the Free Software Foundation; either version 2
8 : * of the License, or any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with this program; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 : * USA.
19 : */
20 :
21 : #define _GNU_SOURCE
22 : #include <errno.h>
23 : #include <fcntl.h>
24 : #include <limits.h>
25 : #include <stdarg.h>
26 : #include <stddef.h>
27 : #include <stdlib.h>
28 : #include <string.h>
29 : #include <sys/stat.h>
30 :
31 : #include "lwan-private.h"
32 :
33 : static const unsigned int BUFFER_MALLOCD = 1 << 0;
34 : static const unsigned int STRBUF_MALLOCD = 1 << 1;
35 : static const unsigned int BUFFER_FIXED = 1 << 2;
36 :
37 3009 : static inline size_t align_size(size_t unaligned_size)
38 : {
39 3009 : const size_t aligned_size = lwan_nextpow2(unaligned_size);
40 :
41 3009 : if (UNLIKELY(unaligned_size >= aligned_size))
42 0 : return 0;
43 :
44 3009 : return aligned_size;
45 : }
46 :
47 64006 : static bool grow_buffer_if_needed(struct lwan_strbuf *s, size_t size)
48 : {
49 64006 : if (s->flags & BUFFER_FIXED)
50 9 : return size < s->capacity;
51 :
52 63997 : if (!(s->flags & BUFFER_MALLOCD)) {
53 1253 : const size_t aligned_size = align_size(LWAN_MAX(size + 1, s->used));
54 1253 : if (UNLIKELY(!aligned_size))
55 0 : return false;
56 :
57 1253 : char *buffer = malloc(aligned_size);
58 1253 : if (UNLIKELY(!buffer))
59 0 : return false;
60 :
61 1253 : memcpy(buffer, s->buffer, s->used);
62 1253 : buffer[s->used + 1] = '\0';
63 :
64 1253 : s->flags |= BUFFER_MALLOCD;
65 1253 : s->buffer = buffer;
66 1253 : s->capacity = aligned_size;
67 :
68 1253 : return true;
69 : }
70 :
71 62744 : if (UNLIKELY(s->capacity < size)) {
72 : char *buffer;
73 1756 : const size_t aligned_size = align_size(size + 1);
74 :
75 1756 : if (UNLIKELY(!aligned_size))
76 0 : return false;
77 :
78 1756 : if (s->used == 0) {
79 : /* Avoid memcpy() inside realloc() if we were not using the
80 : * allocated buffer at this point. */
81 0 : buffer = malloc(aligned_size);
82 :
83 0 : if (UNLIKELY(!buffer))
84 0 : return false;
85 :
86 0 : free(s->buffer);
87 0 : buffer[0] = '\0';
88 : } else {
89 1756 : buffer = realloc(s->buffer, aligned_size);
90 :
91 1756 : if (UNLIKELY(!buffer))
92 0 : return false;
93 : }
94 :
95 1756 : s->buffer = buffer;
96 1756 : s->capacity = aligned_size;
97 : }
98 :
99 62744 : return true;
100 : }
101 :
102 2152 : bool lwan_strbuf_init_with_size(struct lwan_strbuf *s, size_t size)
103 : {
104 2152 : if (UNLIKELY(!s))
105 0 : return false;
106 :
107 2152 : *s = LWAN_STRBUF_STATIC_INIT;
108 :
109 2152 : if (size) {
110 2 : if (UNLIKELY(!grow_buffer_if_needed(s, size)))
111 0 : return false;
112 :
113 2 : s->buffer[0] = '\0';
114 : }
115 :
116 2152 : return true;
117 : }
118 :
119 2 : bool lwan_strbuf_init_with_fixed_buffer(struct lwan_strbuf *s,
120 : void *buffer,
121 : size_t size)
122 : {
123 2 : if (UNLIKELY(!s))
124 0 : return false;
125 :
126 2 : *s = (struct lwan_strbuf) {
127 : .capacity = size,
128 : .used = 0,
129 : .buffer = buffer,
130 : .flags = BUFFER_FIXED,
131 : };
132 :
133 2 : return true;
134 : }
135 :
136 2150 : ALWAYS_INLINE bool lwan_strbuf_init(struct lwan_strbuf *s)
137 : {
138 2150 : return lwan_strbuf_init_with_size(s, 0);
139 : }
140 :
141 0 : struct lwan_strbuf *lwan_strbuf_new_with_size(size_t size)
142 : {
143 0 : struct lwan_strbuf *s = malloc(sizeof(*s));
144 :
145 0 : if (UNLIKELY(!lwan_strbuf_init_with_size(s, size))) {
146 0 : free(s);
147 :
148 0 : return NULL;
149 : }
150 :
151 0 : s->flags |= STRBUF_MALLOCD;
152 :
153 0 : return s;
154 : }
155 :
156 0 : struct lwan_strbuf *lwan_strbuf_new_with_fixed_buffer(size_t size)
157 : {
158 0 : struct lwan_strbuf *s = malloc(sizeof(*s) + size + 1);
159 :
160 0 : if (UNLIKELY(!lwan_strbuf_init_with_fixed_buffer(s, s + 1, size))) {
161 0 : free(s);
162 :
163 0 : return NULL;
164 : }
165 :
166 0 : s->flags |= STRBUF_MALLOCD;
167 :
168 0 : return s;
169 : }
170 :
171 0 : ALWAYS_INLINE struct lwan_strbuf *lwan_strbuf_new(void)
172 : {
173 0 : return lwan_strbuf_new_with_size(0);
174 : }
175 :
176 1601 : ALWAYS_INLINE struct lwan_strbuf *lwan_strbuf_new_static(const char *str,
177 : size_t size)
178 : {
179 1601 : struct lwan_strbuf *s = malloc(sizeof(*s));
180 :
181 1601 : if (UNLIKELY(!s))
182 0 : return NULL;
183 :
184 1601 : *s = (struct lwan_strbuf) {
185 : .flags = STRBUF_MALLOCD,
186 : .buffer = (char *)str,
187 : .used = size,
188 : .capacity = size,
189 : };
190 :
191 1601 : return s;
192 : }
193 :
194 2293 : void lwan_strbuf_free(struct lwan_strbuf *s)
195 : {
196 2293 : if (UNLIKELY(!s))
197 0 : return;
198 2293 : if (s->flags & BUFFER_MALLOCD) {
199 1157 : assert(!(s->flags & BUFFER_FIXED));
200 1157 : free(s->buffer);
201 : }
202 2293 : if (s->flags & STRBUF_MALLOCD)
203 0 : free(s);
204 : }
205 :
206 20111 : bool lwan_strbuf_append_char(struct lwan_strbuf *s, const char c)
207 : {
208 20111 : if (UNLIKELY(!grow_buffer_if_needed(s, s->used + 2)))
209 0 : return false;
210 :
211 20111 : s->buffer[s->used++] = c;
212 20111 : s->buffer[s->used] = '\0';
213 :
214 20111 : return true;
215 : }
216 :
217 43539 : bool lwan_strbuf_append_str(struct lwan_strbuf *s1, const char *s2, size_t sz)
218 : {
219 43539 : if (UNLIKELY(!grow_buffer_if_needed(s1, s1->used + sz + 2)))
220 0 : return false;
221 :
222 43539 : memcpy(s1->buffer + s1->used, s2, sz);
223 43539 : s1->used += sz;
224 43539 : s1->buffer[s1->used] = '\0';
225 :
226 43539 : return true;
227 : }
228 :
229 158 : bool lwan_strbuf_set_static(struct lwan_strbuf *s1, const char *s2, size_t sz)
230 : {
231 158 : if (s1->flags & BUFFER_MALLOCD)
232 0 : free(s1->buffer);
233 :
234 158 : s1->buffer = (char *)s2;
235 158 : s1->used = s1->capacity = sz;
236 158 : s1->flags &= ~(BUFFER_MALLOCD | BUFFER_FIXED);
237 :
238 158 : return true;
239 : }
240 :
241 301 : bool lwan_strbuf_set(struct lwan_strbuf *s1, const char *s2, size_t sz)
242 : {
243 301 : if (UNLIKELY(!grow_buffer_if_needed(s1, sz + 1)))
244 0 : return false;
245 :
246 301 : memcpy(s1->buffer, s2, sz);
247 301 : s1->used = sz;
248 301 : s1->buffer[sz] = '\0';
249 :
250 301 : return true;
251 : }
252 :
253 : static ALWAYS_INLINE bool
254 : internal_printf(struct lwan_strbuf *s1,
255 : bool (*save_str)(struct lwan_strbuf *, const char *, size_t),
256 : const char *fmt,
257 : va_list values)
258 : {
259 : char *s2;
260 : int len;
261 :
262 541 : if (UNLIKELY((len = vasprintf(&s2, fmt, values)) < 0))
263 0 : return false;
264 :
265 541 : bool success = save_str(s1, s2, (size_t)len);
266 541 : free(s2);
267 :
268 541 : return success;
269 : }
270 :
271 296 : bool lwan_strbuf_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
272 : {
273 296 : return internal_printf(s, lwan_strbuf_set, fmt, ap);
274 : }
275 :
276 296 : bool lwan_strbuf_printf(struct lwan_strbuf *s, const char *fmt, ...)
277 : {
278 : bool could_printf;
279 : va_list values;
280 :
281 296 : va_start(values, fmt);
282 296 : could_printf = lwan_strbuf_vprintf(s, fmt, values);
283 296 : va_end(values);
284 :
285 296 : return could_printf;
286 : }
287 :
288 245 : bool lwan_strbuf_append_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
289 : {
290 245 : return internal_printf(s, lwan_strbuf_append_str, fmt, ap);
291 : }
292 :
293 245 : bool lwan_strbuf_append_printf(struct lwan_strbuf *s, const char *fmt, ...)
294 : {
295 : bool could_printf;
296 : va_list values;
297 :
298 245 : va_start(values, fmt);
299 245 : could_printf = lwan_strbuf_append_vprintf(s, fmt, values);
300 245 : va_end(values);
301 :
302 245 : return could_printf;
303 : }
304 :
305 53 : bool lwan_strbuf_grow_to(struct lwan_strbuf *s, size_t new_size)
306 : {
307 53 : return grow_buffer_if_needed(s, new_size + 1);
308 : }
309 :
310 0 : bool lwan_strbuf_grow_by(struct lwan_strbuf *s, size_t offset)
311 : {
312 : size_t new_size;
313 :
314 0 : if (__builtin_add_overflow(offset, s->used, &new_size))
315 0 : return false;
316 :
317 0 : return lwan_strbuf_grow_to(s, new_size);
318 : }
319 :
320 82644 : void lwan_strbuf_reset(struct lwan_strbuf *s)
321 : {
322 82644 : if (s->flags & BUFFER_MALLOCD) {
323 78029 : s->buffer[0] = '\0';
324 : } else {
325 4615 : s->buffer = "";
326 4615 : s->capacity = 0;
327 : }
328 :
329 82644 : s->used = 0;
330 82644 : }
331 :
332 268 : void lwan_strbuf_reset_trim(struct lwan_strbuf *s, size_t trim_thresh)
333 : {
334 268 : if (s->flags & BUFFER_MALLOCD && s->capacity > trim_thresh) {
335 0 : free(s->buffer);
336 0 : s->flags &= ~BUFFER_MALLOCD;
337 : }
338 :
339 268 : return lwan_strbuf_reset(s);
340 : }
341 :
342 : /* This function is quite dangerous, so the prototype is only in lwan-private.h */
343 0 : char *lwan_strbuf_extend_unsafe(struct lwan_strbuf *s, size_t by)
344 : {
345 0 : if (!lwan_strbuf_grow_by(s, by))
346 0 : return NULL;
347 :
348 0 : size_t prev_used = s->used;
349 0 : s->used += by;
350 :
351 0 : return s->buffer + prev_used;
352 : }
353 :
354 8 : bool lwan_strbuf_init_from_file(struct lwan_strbuf *s, const char *path)
355 : {
356 8 : int fd = open(path, O_RDONLY | O_CLOEXEC);
357 : struct stat st;
358 :
359 8 : if (UNLIKELY(fd < 0))
360 6 : return false;
361 :
362 2 : if (UNLIKELY(fstat(fd, &st) < 0))
363 0 : goto error;
364 :
365 2 : if (UNLIKELY(!lwan_strbuf_init_with_size(s, (size_t)st.st_size)))
366 0 : goto error;
367 :
368 2 : s->used = (size_t)st.st_size;
369 :
370 4 : for (char *buffer = s->buffer; st.st_size; ) {
371 2 : ssize_t n_read = read(fd, buffer, (size_t)st.st_size);
372 :
373 2 : if (UNLIKELY(n_read < 0)) {
374 0 : if (errno == EINTR)
375 0 : continue;
376 0 : goto error;
377 : }
378 :
379 2 : buffer += n_read;
380 2 : st.st_size -= (off_t)n_read;
381 : }
382 :
383 2 : close(fd);
384 2 : return true;
385 :
386 0 : error:
387 0 : lwan_strbuf_free(s);
388 0 : close(fd);
389 0 : return false;
390 : }
391 :
392 0 : struct lwan_strbuf *lwan_strbuf_new_from_file(const char *path)
393 : {
394 0 : struct lwan_strbuf *strbuf = malloc(sizeof(*strbuf));
395 :
396 0 : if (!strbuf)
397 0 : return NULL;
398 :
399 0 : if (lwan_strbuf_init_from_file(strbuf, path)) {
400 0 : strbuf->flags |= STRBUF_MALLOCD;
401 0 : return strbuf;
402 : }
403 :
404 0 : free(strbuf);
405 0 : return NULL;
406 : }
|