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