xref: /lighttpd1.4/src/buffer.c (revision 3392e8fb)
1 #include "first.h"
2 
3 #include "buffer.h"
4 
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>       /* strftime() */
8 
9 static const char hex_chars_lc[] = "0123456789abcdef";
10 static const char hex_chars_uc[] = "0123456789ABCDEF";
11 
12 /**
13  * init the buffer
14  *
15  */
16 
17 buffer* buffer_init(void) {
18 	buffer *b;
19 
20 	b = malloc(sizeof(*b));
21 	force_assert(b);
22 
23 	b->ptr = NULL;
24 	b->size = 0;
25 	b->used = 0;
26 
27 	return b;
28 }
29 
30 buffer *buffer_init_buffer(const buffer *src) {
31 	buffer *b = buffer_init();
32 	buffer_copy_buffer(b, src);
33 	return b;
34 }
35 
36 buffer *buffer_init_string(const char *str) {
37 	buffer *b = buffer_init();
38 	buffer_copy_string(b, str);
39 	return b;
40 }
41 
42 void buffer_free(buffer *b) {
43 	if (NULL == b) return;
44 
45 	free(b->ptr);
46 	free(b);
47 }
48 
49 void buffer_free_ptr(buffer *b) {
50 	free(b->ptr);
51 	b->ptr = NULL;
52 	b->used = 0;
53 	b->size = 0;
54 }
55 
56 void buffer_move(buffer * restrict b, buffer * restrict src) {
57 	buffer tmp;
58 	buffer_clear(b);
59 	tmp = *src; *src = *b; *b = tmp;
60 }
61 
62 /* make sure buffer is at least "size" big + 1 for '\0'. keep old data */
63 __attribute_cold__
64 __attribute_noinline__
65 __attribute_nonnull__
66 __attribute_returns_nonnull__
67 static char* buffer_realloc(buffer * const restrict b, const size_t len) {
68     #define BUFFER_PIECE_SIZE 64uL  /*(must be power-of-2)*/
69     const size_t sz = (len + 1 + BUFFER_PIECE_SIZE-1) & ~(BUFFER_PIECE_SIZE-1);
70     force_assert(sz > len);
71 
72     b->size = sz;
73     b->ptr = realloc(b->ptr, sz);
74 
75     force_assert(NULL != b->ptr);
76     return b->ptr;
77 }
78 
79 __attribute_cold__
80 __attribute_noinline__
81 __attribute_nonnull__
82 __attribute_returns_nonnull__
83 static char* buffer_alloc_replace(buffer * const restrict b, const size_t size) {
84     /*(discard old data so realloc() does not copy)*/
85     if (NULL != b->ptr) {
86         free(b->ptr);
87         b->ptr = NULL;
88     }
89     /*(note: if size larger than one lshift, use size instead of power-2)*/
90     return buffer_realloc(b, (b->size << 1) > size ? (b->size << 1)-1 : size);
91 }
92 
93 char* buffer_string_prepare_copy(buffer * const b, const size_t size) {
94     b->used = 0;
95     return (size < b->size)
96       ? b->ptr
97       : buffer_alloc_replace(b, size);
98 }
99 
100 __attribute_cold__
101 __attribute_noinline__
102 __attribute_nonnull__
103 __attribute_returns_nonnull__
104 static char* buffer_string_prepare_append_resize(buffer * const restrict b, const size_t size) {
105     if (b->used < 2)  /* buffer_string_is_empty(b) */
106         return buffer_string_prepare_copy(b, size);
107 
108     /* not empty, b->used already includes a terminating 0 */
109     /*(note: if size larger than one lshift, use size instead of power-2)*/
110     const size_t req_size = ((b->size << 1) - b->used > size)
111       ? (b->size << 1)-1
112       : b->used + size;
113 
114     /* check for overflow: unsigned overflow is defined to wrap around */
115     force_assert(req_size >= b->used);
116 
117     return buffer_realloc(b, req_size) + b->used - 1;
118 }
119 
120 char* buffer_string_prepare_append(buffer * const b, const size_t size) {
121     const uint32_t len = b->used ? b->used-1 : 0;
122     return (b->size - len >= size + 1)
123       ? b->ptr + len
124       : buffer_string_prepare_append_resize(b, size);
125 }
126 
127 /*(prefer smaller code than inlining buffer_extend in many places in buffer.c)*/
128 __attribute_noinline__
129 char*
130 buffer_extend (buffer * const b, const size_t x)
131 {
132     /* extend buffer to append x (reallocate by power-2 (or larger), if needed)
133      * (combine buffer_string_prepare_append() and buffer_commit())
134      * (future: might make buffer.h static inline func for HTTP/1.1 performance)
135      * pre-sets '\0' byte and b->used (unlike buffer_string_prepare_append())*/
136     const uint32_t len = b->used ? b->used-1 : 0;
137     char * const s = (b->size - len >= x + 1)
138       ? b->ptr + len
139       : buffer_string_prepare_append_resize(b, x);
140     b->used = len+x+1;
141     s[x] = '\0';
142     return s;
143 }
144 
145 void buffer_string_set_length(buffer *b, uint32_t len) {
146     /*(intended for string truncate; prefer buffer_extend() to extend string)*/
147     /*(unlike others routines, this routine potentially extends string
148      * extra len + (up to) BUFFER_PIECE_SIZE without power-2 reallocation,
149      * but does not optimize realloc of empty string)*/
150     b->used = len + 1; /*(not resizing for power-2)*/
151     (len < b->size ? b->ptr : buffer_realloc(b, len))[len] = '\0';
152 }
153 
154 void buffer_commit(buffer *b, size_t size)
155 {
156 	force_assert(b->size > 0);
157 
158 	if (0 == b->used) b->used = 1;
159 
160 	if (size > 0) {
161 		/* check for overflow: unsigned overflow is defined to wrap around */
162 		size_t sz = b->used + size;
163 		force_assert(sz > b->used);
164 		force_assert(sz <= b->size);
165 		b->used = sz;
166 	}
167 
168 	b->ptr[b->used - 1] = '\0';
169 }
170 
171 void buffer_copy_string(buffer * restrict b, const char * restrict s) {
172 	buffer_copy_string_len(b, s, NULL != s ? strlen(s) : 0);
173 }
174 
175 void buffer_copy_string_len(buffer * const restrict b, const char * const restrict s, const size_t len) {
176     b->used = len + 1;
177     char * const restrict d = (len < b->size)
178       ? b->ptr
179       : buffer_alloc_replace(b, len);
180     d[len] = '\0';
181     memcpy(d, s, len);
182 }
183 
184 void buffer_append_string(buffer * restrict b, const char * restrict s) {
185 	buffer_append_string_len(b, s, NULL != s ? strlen(s) : 0);
186 }
187 
188 /**
189  * append a string to the end of the buffer
190  *
191  * the resulting buffer is terminated with a '\0'
192  * s is treated as a un-terminated string (a \0 is handled a normal character)
193  *
194  * @param b a buffer
195  * @param s the string
196  * @param s_len size of the string (without the terminating \0)
197  */
198 
199 void buffer_append_string_len(buffer * const restrict b, const char * const restrict s, const size_t len) {
200     memcpy(buffer_extend(b, len), s, len);
201 }
202 
203 void buffer_append_str2(buffer * const restrict b, const char * const s1, const size_t len1, const char * const s2, const size_t len2) {
204     char * const restrict s = buffer_extend(b, len1+len2);
205   #ifdef HAVE_MEMPCPY
206     mempcpy(mempcpy(s, s1, len1), s2, len2);
207   #else
208     memcpy(s,      s1, len1);
209     memcpy(s+len1, s2, len2);
210   #endif
211 }
212 
213 void buffer_append_str3(buffer * const restrict b, const char * const s1, const size_t len1, const char * const s2, const size_t len2, const char * const s3, const size_t len3) {
214     char * restrict s = buffer_extend(b, len1+len2+len3);
215   #ifdef HAVE_MEMPCPY
216     mempcpy(mempcpy(mempcpy(s, s1, len1), s2, len2), s3, len3);
217   #else
218     memcpy(s,         s1, len1);
219     memcpy((s+=len1), s2, len2);
220     memcpy((s+=len2), s3, len3);
221   #endif
222 }
223 
224 void buffer_append_iovec(buffer * const restrict b, const struct const_iovec * const iov, const size_t n) {
225     size_t len = 0;
226     for (size_t i = 0; i < n; ++i)
227         len += iov[i].iov_len;
228     char *s = buffer_extend(b, len);
229     for (size_t i = 0; i < n; ++i) {
230         if (0 == iov[i].iov_len) continue;
231       #ifdef HAVE_MEMPCPY
232         s = mempcpy(s, iov[i].iov_base, iov[i].iov_len);
233       #else
234         memcpy(s, iov[i].iov_base, iov[i].iov_len);
235         s += iov[i].iov_len;
236       #endif
237     }
238 }
239 
240 void buffer_append_path_len(buffer * restrict b, const char * restrict a, size_t alen) {
241     char * restrict s = buffer_string_prepare_append(b, alen+1);
242     const int aslash = (alen && a[0] == '/');
243     if (b->used > 1 && s[-1] == '/') {
244         if (aslash) {
245             ++a;
246             --alen;
247         }
248     }
249     else {
250         if (0 == b->used) b->used = 1;
251         if (!aslash) {
252             *s++ = '/';
253             ++b->used;
254         }
255     }
256     b->used += alen;
257     s[alen] = '\0';
258     memcpy(s, a, alen);
259 }
260 
261 void
262 buffer_copy_path_len2 (buffer * const restrict b, const char * const restrict s1, size_t len1, const char * const restrict s2, size_t len2)
263 {
264     /*(similar to buffer_copy_string_len(b, s1, len1) but combined allocation)*/
265     memcpy(buffer_string_prepare_copy(b, len1+len2+1), s1, len1);
266     b->used = len1 + 1;                    /*('\0' byte will be written below)*/
267 
268     buffer_append_path_len(b, s2, len2);/*(choice: not inlined, special-cased)*/
269 }
270 
271 void buffer_append_uint_hex_lc(buffer *b, uintmax_t value) {
272 	char *buf;
273 	unsigned int shift = 0;
274 
275 	{
276 		uintmax_t copy = value;
277 		do {
278 			copy >>= 8;
279 			shift += 8; /* counting bits */
280 		} while (0 != copy);
281 	}
282 
283 	buf = buffer_extend(b, shift >> 2); /*nibbles (4 bits)*/
284 
285 	while (shift > 0) {
286 		shift -= 4;
287 		*(buf++) = hex_chars_lc[(value >> shift) & 0x0F];
288 	}
289 }
290 
291 __attribute_nonnull__
292 __attribute_returns_nonnull__
293 static char* utostr(char buf[LI_ITOSTRING_LENGTH], uintmax_t val) {
294 	char *cur = buf+LI_ITOSTRING_LENGTH;
295 	do {
296 		int mod = val % 10;
297 		val /= 10;
298 		/* prepend digit mod */
299 		*(--cur) = (char) ('0' + mod);
300 	} while (0 != val);
301 	return cur;
302 }
303 
304 __attribute_nonnull__
305 __attribute_returns_nonnull__
306 static char* itostr(char buf[LI_ITOSTRING_LENGTH], intmax_t val) {
307 	/* absolute value not defined for INTMAX_MIN, but can take absolute
308 	 * value of any negative number via twos complement cast to unsigned.
309 	 * negative sign is prepended after (now unsigned) value is converted
310 	 * to string */
311 	uintmax_t uval = val >= 0 ? (uintmax_t)val : ((uintmax_t)~val) + 1;
312 	char *cur = utostr(buf, uval);
313 	if (val < 0) *(--cur) = '-';
314 
315 	return cur;
316 }
317 
318 void buffer_append_int(buffer *b, intmax_t val) {
319 	char buf[LI_ITOSTRING_LENGTH];
320 	const char * const str = itostr(buf, val);
321 	buffer_append_string_len(b, str, buf+sizeof(buf) - str);
322 }
323 
324 void buffer_append_strftime(buffer * const restrict b, const char * const restrict format, const struct tm * const restrict tm) {
325     force_assert(NULL != format);
326     /*(localtime_r() or gmtime_r() producing tm should not have failed)*/
327     if (__builtin_expect( (NULL == tm), 0)) return;
328 
329     /*(expecting typical format strings to result in < 64 bytes needed;
330      * skipping buffer_string_space() calculation and providing fixed size)*/
331     size_t rv = strftime(buffer_string_prepare_append(b, 63), 64, format, tm);
332 
333     /* 0 (in some apis) signals the string may have been too small;
334      * but the format could also just have lead to an empty string */
335     if (__builtin_expect( (0 == rv), 0) || __builtin_expect( (rv > 63), 0)) {
336         /* unexpected; give it a second try with a larger string */
337         rv = strftime(buffer_string_prepare_append(b, 4095), 4096, format, tm);
338         if (__builtin_expect( (rv > 4095), 0))/*(input format was ridiculous)*/
339             return;
340     }
341 
342     /*buffer_commit(b, rv);*/
343     b->used += (uint32_t)rv + (0 == b->used);
344 }
345 
346 
347 size_t li_itostrn(char *buf, size_t buf_len, intmax_t val) {
348 	char p_buf[LI_ITOSTRING_LENGTH];
349 	char* const str = itostr(p_buf, val);
350 	size_t len = (size_t)(p_buf+sizeof(p_buf)-str);
351 	force_assert(len <= buf_len);
352 	memcpy(buf, str, len);
353 	return len;
354 }
355 
356 size_t li_utostrn(char *buf, size_t buf_len, uintmax_t val) {
357 	char p_buf[LI_ITOSTRING_LENGTH];
358 	char* const str = utostr(p_buf, val);
359 	size_t len = (size_t)(p_buf+sizeof(p_buf)-str);
360 	force_assert(len <= buf_len);
361 	memcpy(buf, str, len);
362 	return len;
363 }
364 
365 #define li_ntox_lc(n) ((n) <= 9 ? (n) + '0' : (n) + 'a' - 10)
366 
367 /* c (char) and n (nibble) MUST be unsigned integer types */
368 #define li_cton(c,n) \
369   (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0))
370 
371 /* converts hex char (0-9, A-Z, a-z) to decimal.
372  * returns 0xFF on invalid input.
373  */
374 char hex2int(unsigned char hex) {
375 	unsigned char n;
376 	return li_cton(hex,n) ? (char)n : 0xFF;
377 }
378 
379 
380 int buffer_eq_icase_ssn(const char * const a, const char * const b, const size_t len) {
381     for (size_t i = 0; i < len; ++i) {
382         unsigned int ca = ((unsigned char *)a)[i];
383         unsigned int cb = ((unsigned char *)b)[i];
384         if (ca != cb) {
385             ca |= 0x20;
386             cb |= 0x20;
387             if (ca != cb) return 0;
388             if (!light_islower(ca)) return 0;
389             if (!light_islower(cb)) return 0;
390         }
391     }
392     return 1;
393 }
394 
395 int buffer_eq_icase_ss(const char * const a, const size_t alen, const char * const b, const size_t blen) {
396     /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */
397     return (alen == blen && buffer_eq_icase_ssn(a, b, blen));
398 }
399 
400 int buffer_eq_icase_slen(const buffer * const b, const char * const s, const size_t slen) {
401     /* Note: b must be initialized, i.e. 0 != b->used; uninitialized is not eq*/
402     /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */
403     return (b->used == slen + 1 && buffer_eq_icase_ssn(b->ptr, s, slen));
404 }
405 
406 int buffer_eq_slen(const buffer * const b, const char * const s, const size_t slen) {
407     /* Note: b must be initialized, i.e. 0 != b->used; uninitialized is not eq*/
408     /* 1 = equal; 0 = not equal */ /* short string sizes expected (< INT_MAX) */
409     return (b->used == slen + 1 && 0 == memcmp(b->ptr, s, slen));
410 }
411 
412 
413 /**
414  * check if two buffer contain the same data
415  */
416 
417 int buffer_is_equal(const buffer *a, const buffer *b) {
418 	/* 1 = equal; 0 = not equal */
419 	return (a->used == b->used && 0 == memcmp(a->ptr, b->ptr, a->used));
420 }
421 
422 int buffer_is_equal_string(const buffer *a, const char *s, size_t b_len) {
423 	force_assert(b_len + 1 > b_len);
424 
425 	/* 1 = equal; 0 = not equal */
426 	return (a->used == b_len + 1 && 0 == memcmp(a->ptr, s, b_len));
427 }
428 
429 int buffer_is_equal_right_len(const buffer *b1, const buffer *b2, size_t len) {
430 	/* no len -> equal */
431 	if (len == 0) return 1;
432 
433 	/* len > 0, but empty buffers -> not equal */
434 	if (b1->used == 0 || b2->used == 0) return 0;
435 
436 	/* buffers too small -> not equal */
437 	if (b1->used - 1 < len || b2->used - 1 < len) return 0;
438 
439 	return 0 == memcmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len);
440 }
441 
442 
443 void li_tohex_lc(char * const restrict buf, size_t buf_len, const char * const restrict s, size_t s_len) {
444 	force_assert(2 * s_len > s_len);
445 	force_assert(2 * s_len < buf_len);
446 
447 	for (size_t i = 0; i < s_len; ++i) {
448 		buf[2*i]   = hex_chars_lc[(s[i] >> 4) & 0x0F];
449 		buf[2*i+1] = hex_chars_lc[s[i] & 0x0F];
450 	}
451 	buf[2*s_len] = '\0';
452 }
453 
454 void li_tohex_uc(char * const restrict buf, size_t buf_len, const char * const restrict s, size_t s_len) {
455 	force_assert(2 * s_len > s_len);
456 	force_assert(2 * s_len < buf_len);
457 
458 	for (size_t i = 0; i < s_len; ++i) {
459 		buf[2*i]   = hex_chars_uc[(s[i] >> 4) & 0x0F];
460 		buf[2*i+1] = hex_chars_uc[s[i] & 0x0F];
461 	}
462 	buf[2*s_len] = '\0';
463 }
464 
465 
466 void buffer_substr_replace (buffer * const restrict b, const size_t offset,
467                             const size_t len, const buffer * const restrict replace)
468 {
469     const size_t blen = buffer_string_length(b);
470     const size_t rlen = buffer_string_length(replace);
471 
472     if (rlen > len) {
473         buffer_string_set_length(b, blen-len+rlen);
474         memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len);
475     }
476 
477     memcpy(b->ptr+offset, replace->ptr, rlen);
478 
479     if (rlen < len) {
480         memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len);
481         buffer_string_set_length(b, blen-len+rlen);
482     }
483 }
484 
485 
486 void buffer_append_string_encoded_hex_lc(buffer * const restrict b, const char * const restrict s, size_t len) {
487     unsigned char * const p = (unsigned char *)buffer_extend(b, len*2);
488     for (size_t i = 0; i < len; ++i) {
489         p[(i<<1)]   = hex_chars_lc[(s[i] >> 4) & 0x0F];
490         p[(i<<1)+1] = hex_chars_lc[(s[i])      & 0x0F];
491     }
492 }
493 
494 void buffer_append_string_encoded_hex_uc(buffer * const restrict b, const char * const restrict s, size_t len) {
495     unsigned char * const p = (unsigned char *)buffer_extend(b, len*2);
496     for (size_t i = 0; i < len; ++i) {
497         p[(i<<1)]   = hex_chars_uc[(s[i] >> 4) & 0x0F];
498         p[(i<<1)+1] = hex_chars_uc[(s[i])      & 0x0F];
499     }
500 }
501 
502 
503 /* everything except: ! ( ) * - . 0-9 A-Z _ a-z */
504 static const char encoded_chars_rel_uri_part[] = {
505 	/*
506 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
507 	*/
508 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
509 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
510 	1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,  /*  20 -  2F space " # $ % & ' + , / */
511 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  /*  30 -  3F : ; < = > ? */
512 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F @ */
513 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  50 -  5F [ \ ] ^ */
514 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
515 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
516 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
517 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
518 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
519 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
520 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
521 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
522 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
523 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
524 };
525 
526 /* everything except: ! ( ) * - . / 0-9 A-Z _ a-z */
527 static const char encoded_chars_rel_uri[] = {
528 	/*
529 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
530 	*/
531 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
532 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
533 	1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0,  /*  20 -  2F space " # $ % & ' + , */
534 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  /*  30 -  3F : ; < = > ? */
535 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F @ */
536 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  50 -  5F [ \ ] ^ */
537 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
538 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
539 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
540 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
541 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
542 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
543 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
544 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
545 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
546 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
547 };
548 
549 static const char encoded_chars_html[] = {
550 	/*
551 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
552 	*/
553 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
554 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
555 	0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F " & ' */
556 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  30 -  3F < > */
557 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F */
558 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  50 -  5F */
559 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
560 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  /*  70 -  7F DEL */
561 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
562 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
563 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
564 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
565 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
566 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
567 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
568 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
569 };
570 
571 static const char encoded_chars_minimal_xml[] = {
572 	/*
573 	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
574 	*/
575 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
576 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
577 	0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F " & ' */
578 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  30 -  3F < > */
579 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F */
580 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  50 -  5F */
581 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
582 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  /*  70 -  7F DEL */
583 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  80 -  8F */
584 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  90 -  9F */
585 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  A0 -  AF */
586 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  B0 -  BF */
587 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  C0 -  CF */
588 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  D0 -  DF */
589 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  E0 -  EF */
590 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  F0 -  FF */
591 };
592 
593 
594 
595 void buffer_append_string_encoded(buffer * const restrict b, const char * const restrict s, size_t s_len, buffer_encoding_t encoding) {
596 	unsigned char *ds, *d;
597 	size_t d_len, ndx;
598 	const char *map = NULL;
599 
600 	if (0 == s_len) return;
601 
602 	force_assert(NULL != s);
603 
604 	switch(encoding) {
605 	case ENCODING_REL_URI:
606 		map = encoded_chars_rel_uri;
607 		break;
608 	case ENCODING_REL_URI_PART:
609 		map = encoded_chars_rel_uri_part;
610 		break;
611 	case ENCODING_HTML:
612 		map = encoded_chars_html;
613 		break;
614 	case ENCODING_MINIMAL_XML:
615 		map = encoded_chars_minimal_xml;
616 		break;
617 	}
618 
619 	force_assert(NULL != map);
620 
621 	/* count to-be-encoded-characters */
622 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
623 		if (map[*ds & 0xFF]) {
624 			switch(encoding) {
625 			case ENCODING_REL_URI:
626 			case ENCODING_REL_URI_PART:
627 				d_len += 3;
628 				break;
629 			case ENCODING_HTML:
630 			case ENCODING_MINIMAL_XML:
631 				d_len += 6;
632 				break;
633 			}
634 		} else {
635 			d_len++;
636 		}
637 	}
638 
639 	d = (unsigned char*) buffer_extend(b, d_len);
640 
641 	if (d_len == s_len) { /*(short-circuit; nothing to encoded)*/
642 		memcpy(d, s, s_len);
643 		return;
644 	}
645 
646 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
647 		if (map[*ds & 0xFF]) {
648 			switch(encoding) {
649 			case ENCODING_REL_URI:
650 			case ENCODING_REL_URI_PART:
651 				d[d_len++] = '%';
652 				d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F];
653 				d[d_len++] = hex_chars_uc[(*ds) & 0x0F];
654 				break;
655 			case ENCODING_HTML:
656 			case ENCODING_MINIMAL_XML:
657 				d[d_len++] = '&';
658 				d[d_len++] = '#';
659 				d[d_len++] = 'x';
660 				d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F];
661 				d[d_len++] = hex_chars_uc[(*ds) & 0x0F];
662 				d[d_len++] = ';';
663 				break;
664 			}
665 		} else {
666 			d[d_len++] = *ds;
667 		}
668 	}
669 }
670 
671 void buffer_append_string_c_escaped(buffer * const restrict b, const char * const restrict s, size_t s_len) {
672 	unsigned char *ds, *d;
673 	size_t d_len, ndx;
674 
675 	if (0 == s_len) return;
676 
677 	/* count to-be-encoded-characters */
678 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
679 		if ((*ds < 0x20) /* control character */
680 				|| (*ds >= 0x7f)) { /* DEL + non-ASCII characters */
681 			switch (*ds) {
682 			case '\t':
683 			case '\r':
684 			case '\n':
685 				d_len += 2;
686 				break;
687 			default:
688 				d_len += 4; /* \xCC */
689 				break;
690 			}
691 		} else {
692 			d_len++;
693 		}
694 	}
695 
696 	d = (unsigned char*) buffer_extend(b, d_len);
697 
698 	if (d_len == s_len) { /*(short-circuit; nothing to encoded)*/
699 		memcpy(d, s, s_len);
700 		return;
701 	}
702 
703 	for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) {
704 		if ((*ds < 0x20) /* control character */
705 				|| (*ds >= 0x7f)) { /* DEL + non-ASCII characters */
706 			d[d_len++] = '\\';
707 			switch (*ds) {
708 			case '\t':
709 				d[d_len++] = 't';
710 				break;
711 			case '\r':
712 				d[d_len++] = 'r';
713 				break;
714 			case '\n':
715 				d[d_len++] = 'n';
716 				break;
717 			default:
718 				d[d_len++] = 'x';
719 				d[d_len++] = hex_chars_lc[((*ds) >> 4) & 0x0F];
720 				d[d_len++] = hex_chars_lc[(*ds) & 0x0F];
721 				break;
722 			}
723 		} else {
724 			d[d_len++] = *ds;
725 		}
726 	}
727 }
728 
729 
730 /* decodes url-special-chars inplace.
731  * replaces non-printable characters with '_'
732  * (If this is used on a portion of query string, then query string should be
733  *  split on '&', and '+' replaced with ' ' before calling this routine)
734  */
735 
736 void buffer_urldecode_path(buffer * const b) {
737     const size_t len = buffer_string_length(b);
738     char *src = len ? memchr(b->ptr, '%', len) : NULL;
739     if (NULL == src) return;
740 
741     char *dst = src;
742     do {
743         /* *src == '%' */
744         unsigned char high = hex2int(*(src + 1));
745         unsigned char low = hex2int(*(src + 2));
746         if (0xFF != high && 0xFF != low) {
747             high = (high << 4) | low;   /* map ctrls to '_' */
748             *dst = (high >= 32 && high != 127) ? high : '_';
749             src += 2;
750         } /* else ignore this '%'; leave as-is and move on */
751 
752         while ((*++dst = *++src) != '%' && *src) ;
753     } while (*src);
754     b->used = (dst - b->ptr) + 1;
755 }
756 
757 int buffer_is_valid_UTF8(const buffer *b) {
758     /* https://www.w3.org/International/questions/qa-forms-utf-8 */
759     const unsigned char *c = (unsigned char *)b->ptr;
760     while (*c) {
761 
762         /*(note: includes ctrls)*/
763         if (                         c[0] <  0x80 ) { ++c;  continue; }
764 
765         if (         0xc2 <= c[0] && c[0] <= 0xdf
766             &&       0x80 <= c[1] && c[1] <= 0xbf ) { c+=2; continue; }
767 
768         if ( (   (   0xe0 == c[0]
769                   && 0xa0 <= c[1] && c[1] <= 0xbf)
770               || (   0xe1 <= c[0] && c[0] <= 0xef && c[0] != 0xed
771                   && 0x80 <= c[1] && c[1] <= 0xbf)
772               || (   0xed == c[0]
773                   && 0x80 <= c[1] && c[1] <= 0x9f)   )
774             &&       0x80 <= c[2] && c[2] <= 0xbf ) { c+=3; continue; }
775 
776         if ( (   (   0xf0 == c[0]
777                   && 0x90 <= c[1] && c[1] <= 0xbf)
778               || (   0xf1 <= c[0] && c[0] <= 0xf3
779                   && 0x80 <= c[1] && c[1] <= 0xbf)
780               || (   0xf4 == c[0]
781                   && 0x80 <= c[1] && c[1] <= 0x8f)   )
782             &&       0x80 <= c[2] && c[2] <= 0xbf
783             &&       0x80 <= c[3] && c[3] <= 0xbf ) { c+=4; continue; }
784 
785         return 0; /* invalid */
786     }
787     return 1; /* valid */
788 }
789 
790 /* - special case: empty string returns empty string
791  * - on windows or cygwin: replace \ with /
792  * - strip leading spaces
793  * - prepends "/" if not present already
794  * - resolve "/../", "//" and "/./" the usual way:
795  *   the first one removes a preceding component, the other two
796  *   get compressed to "/".
797  * - "/." and "/.." at the end are similar, but always leave a trailing
798  *   "/"
799  *
800  * /blah/..         gets  /
801  * /blah/../foo     gets  /foo
802  * /abc/./xyz       gets  /abc/xyz
803  * /abc//xyz        gets  /abc/xyz
804  *
805  * NOTE: src and dest can point to the same buffer, in which case,
806  *       the operation is performed in-place.
807  */
808 
809 void buffer_path_simplify(buffer *dest, buffer *src)
810 {
811 	/* current character, the one before, and the one before that from input */
812 	char c, pre1, pre2;
813 	char *start, *slash, *walk, *out;
814 
815 	if (buffer_string_is_empty(src)) {
816 		buffer_copy_string_len(dest, CONST_STR_LEN(""));
817 		return;
818 	}
819 
820 	force_assert('\0' == src->ptr[src->used-1]);
821 
822 #if defined(__WIN32) || defined(__CYGWIN__)
823 	/* cygwin is treating \ and / the same, so we have to that too */
824 	{
825 		char *p;
826 		for (p = src->ptr; *p; p++) {
827 			if (*p == '\\') *p = '/';
828 		}
829 	}
830 #endif
831 
832 	walk  = src->ptr;
833 	start = dest->ptr;
834 	out   = dest->ptr;
835 	slash = dest->ptr;
836 
837 	/* skip leading spaces */
838 	while (*walk == ' ') {
839 		walk++;
840 	}
841 	if (*walk == '.') {
842 		if (walk[1] == '/' || walk[1] == '\0')
843 			++walk;
844 		else if (walk[1] == '.' && (walk[2] == '/' || walk[2] == '\0'))
845 			walk+=2;
846 	}
847 
848 	pre1 = 0;
849 	c = *(walk++);
850 
851 	while (c != '\0') {
852 		/* assert((src != dest || out <= walk) && slash <= out); */
853 		/* the following comments about out and walk are only interesting if
854 		 * src == dest; otherwise the memory areas don't overlap anyway.
855 		 */
856 		pre2 = pre1;
857 		pre1 = c;
858 
859 		/* possibly: out == walk - need to read first */
860 		c    = *walk;
861 		*out = pre1;
862 
863 		out++;
864 		walk++;
865 		/* (out <= walk) still true; also now (slash < out) */
866 
867 		if (c == '/' || c == '\0') {
868 			const size_t toklen = out - slash;
869 			if (toklen == 3 && pre2 == '.' && pre1 == '.' && *slash == '/') {
870 				/* "/../" or ("/.." at end of string) */
871 				out = slash;
872 				/* if there is something before "/..", there is at least one
873 				 * component, which needs to be removed */
874 				if (out > start) {
875 					out--;
876 					while (out > start && *out != '/') out--;
877 				}
878 
879 				/* don't kill trailing '/' at end of path */
880 				if (c == '\0') out++;
881 				/* slash < out before, so out_new <= slash + 1 <= out_before <= walk */
882 			} else if (toklen == 1 || (pre2 == '/' && pre1 == '.')) {
883 				/* "//" or "/./" or (("/" or "/.") at end of string) */
884 				out = slash;
885 				/* don't kill trailing '/' at end of path */
886 				if (c == '\0') out++;
887 				/* slash < out before, so out_new <= slash + 1 <= out_before <= walk */
888 			}
889 
890 			slash = out;
891 		}
892 	}
893 
894 	buffer_string_set_length(dest, out - start);
895 }
896 
897 void buffer_to_lower(buffer * const b) {
898     unsigned char * const restrict s = (unsigned char *)b->ptr;
899     for (uint32_t i = 0; i < b->used; ++i) {
900         if (light_isupper(s[i])) s[i] |= 0x20;
901     }
902 }
903 
904 
905 void buffer_to_upper(buffer * const b) {
906     unsigned char * const restrict s = (unsigned char *)b->ptr;
907     for (uint32_t i = 0; i < b->used; ++i) {
908         if (light_islower(s[i])) s[i] &= 0xdf;
909     }
910 }
911 
912 
913 #include <stdio.h>
914 
915 #ifdef HAVE_LIBUNWIND
916 # define UNW_LOCAL_ONLY
917 # include <libunwind.h>
918 
919 static void print_backtrace(FILE *file) {
920 	unw_cursor_t cursor;
921 	unw_context_t context;
922 	int ret;
923 	unsigned int frame = 0;
924 
925 	if (0 != (ret = unw_getcontext(&context))) goto error;
926 	if (0 != (ret = unw_init_local(&cursor, &context))) goto error;
927 
928 	fprintf(file, "Backtrace:\n");
929 
930 	while (0 < (ret = unw_step(&cursor))) {
931 		unw_word_t proc_ip = 0;
932 		unw_proc_info_t procinfo;
933 		char procname[256];
934 		unw_word_t proc_offset = 0;
935 
936 		if (0 != (ret = unw_get_reg(&cursor, UNW_REG_IP, &proc_ip))) goto error;
937 
938 		if (0 == proc_ip) {
939 			/* without an IP the other functions are useless; unw_get_proc_name would return UNW_EUNSPEC */
940 			++frame;
941 			fprintf(file, "%u: (nil)\n", frame);
942 			continue;
943 		}
944 
945 		if (0 != (ret = unw_get_proc_info(&cursor, &procinfo))) goto error;
946 
947 		if (0 != (ret = unw_get_proc_name(&cursor, procname, sizeof(procname), &proc_offset))) {
948 			switch (-ret) {
949 			case UNW_ENOMEM:
950 				memset(procname + sizeof(procname) - 4, '.', 3);
951 				procname[sizeof(procname) - 1] = '\0';
952 				break;
953 			case UNW_ENOINFO:
954 				procname[0] = '?';
955 				procname[1] = '\0';
956 				proc_offset = 0;
957 				break;
958 			default:
959 				snprintf(procname, sizeof(procname), "?? (unw_get_proc_name error %d)", -ret);
960 				break;
961 			}
962 		}
963 
964 		++frame;
965 		fprintf(file, "%u: %s (+0x%x) [%p]\n",
966 			frame,
967 			procname,
968 			(unsigned int) proc_offset,
969 			(void*)(uintptr_t)proc_ip);
970 	}
971 
972 	if (0 != ret) goto error;
973 
974 	return;
975 
976 error:
977 	fprintf(file, "Error while generating backtrace: unwind error %i\n", (int) -ret);
978 }
979 #else
980 static void print_backtrace(FILE *file) {
981 	UNUSED(file);
982 }
983 #endif
984 
985 void log_failed_assert(const char *filename, unsigned int line, const char *msg) {
986 	/* can't use buffer here; could lead to recursive assertions */
987 	fprintf(stderr, "%s.%u: %s\n", filename, line, msg);
988 	print_backtrace(stderr);
989 	fflush(stderr);
990 	abort();
991 }
992