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