1 #include "first.h" 2 3 #include "array.h" 4 #include "buffer.h" 5 6 #include <string.h> 7 #include <stdlib.h> 8 #include <limits.h> 9 10 __attribute_cold__ 11 static void array_extend(array * const a, uint32_t n) { 12 a->size += n; 13 a->data = realloc(a->data, sizeof(*a->data) * a->size); 14 a->sorted = realloc(a->sorted, sizeof(*a->sorted) * a->size); 15 force_assert(a->data); 16 force_assert(a->sorted); 17 memset(a->data+a->used, 0, (a->size-a->used)*sizeof(*a->data)); 18 } 19 20 array *array_init(uint32_t n) { 21 array *a; 22 23 a = calloc(1, sizeof(*a)); 24 force_assert(a); 25 if (n) array_extend(a, n); 26 27 return a; 28 } 29 30 void array_free_data(array * const a) { 31 if (a->sorted) free(a->sorted); 32 data_unset ** const data = a->data; 33 const uint32_t sz = a->size; 34 for (uint32_t i = 0; i < sz; ++i) { 35 if (data[i]) data[i]->fn->free(data[i]); 36 } 37 free(data); 38 a->data = NULL; 39 a->sorted = NULL; 40 a->used = 0; 41 a->size = 0; 42 } 43 44 void array_copy_array(array * const dst, const array * const src) { 45 array_free_data(dst); 46 if (0 == src->size) return; 47 48 array_extend(dst, src->size); 49 for (uint32_t i = 0; i < src->used; ++i) { 50 array_insert_unique(dst, src->data[i]->fn->copy(src->data[i])); 51 } 52 } 53 54 void array_free(array * const a) { 55 if (!a) return; 56 array_free_data(a); 57 free(a); 58 } 59 60 void array_reset_data_strings(array * const a) { 61 if (!a) return; 62 63 data_string ** const data = (data_string **)a->data; 64 const uint32_t used = a->used; 65 a->used = 0; 66 for (uint32_t i = 0; i < used; ++i) { 67 data_string * const ds = data[i]; 68 /*force_assert(ds->type == TYPE_STRING);*/ 69 buffer_reset(&ds->key); 70 buffer_reset(&ds->value); 71 } 72 } 73 74 #if 0 /*(unused; see array_extract_element_klen())*/ 75 data_unset *array_pop(array * const a) { 76 data_unset *du; 77 78 force_assert(a->used != 0); 79 80 a->used --; 81 du = a->data[a->used]; 82 force_assert(a->sorted[a->used] == du); /* only works on "simple" lists */ 83 a->data[a->used] = NULL; 84 85 return du; 86 } 87 #endif 88 89 __attribute_pure__ 90 static int array_caseless_compare(const char * const a, const char * const b, const uint32_t len) { 91 for (uint32_t i = 0; i < len; ++i) { 92 unsigned int ca = ((unsigned char *)a)[i]; 93 unsigned int cb = ((unsigned char *)b)[i]; 94 if (ca == cb) continue; 95 96 /* always lowercase for transitive results */ 97 if (light_isupper(ca)) ca |= 0x20; 98 if (light_isupper(cb)) cb |= 0x20; 99 100 if (ca == cb) continue; 101 return (int)(ca - cb); 102 } 103 return 0; 104 } 105 106 __attribute_pure__ 107 static int array_keycmp(const char * const a, const uint32_t alen, const char * const b, const uint32_t blen) { 108 return alen < blen ? -1 : alen > blen ? 1 : array_caseless_compare(a, b, blen); 109 } 110 111 __attribute_cold__ 112 __attribute_pure__ 113 static int array_keycmpb(const char * const k, const uint32_t klen, const buffer * const b) { 114 /* key is non-empty (0==b->used), though possibly blank (1==b->used) 115 * if inserted into key-value array */ 116 /*force_assert(b && b->used);*/ 117 return array_keycmp(k, klen, b->ptr, b->used-1); 118 /*return array_keycmp(k, klen, CONST_BUF_LEN(b));*/ 119 } 120 121 /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[] 122 * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[] 123 * where the key needs to be inserted (-1 to avoid -0) 124 */ 125 __attribute_hot__ 126 __attribute_pure__ 127 static int32_t array_get_index_ext(const array * const a, const int ext, const char * const k, const uint32_t klen) { 128 /* invariant: [lower-1] < probe < [upper] 129 * invariant: 0 <= lower <= upper <= a->used 130 */ 131 uint32_t lower = 0, upper = a->used; 132 while (lower != upper) { 133 const uint32_t probe = (lower + upper) / 2; 134 const int x = ((data_string *)a->sorted[probe])->ext; 135 /* (compare strings only if ext is 0 for both)*/ 136 const int e = (ext|x) 137 ? ext 138 : array_keycmpb(k, klen, &a->sorted[probe]->key); 139 if (e < x) /* e < [probe] */ 140 upper = probe; /* still: lower <= upper */ 141 else if (e > x) /* e > [probe] */ 142 lower = probe + 1; /* still: lower <= upper */ 143 else /*(e == x)*/ /* found */ 144 return (int32_t)probe; 145 } 146 /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */ 147 return -(int)lower - 1; 148 } 149 150 data_unset *array_get_element_klen_ext(const array * const a, const int ext, const char *key, const uint32_t klen) { 151 const int32_t ipos = array_get_index_ext(a, ext, key, klen); 152 return ipos >= 0 ? a->sorted[ipos] : NULL; 153 } 154 155 /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[] 156 * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[] 157 * where the key needs to be inserted (-1 to avoid -0) 158 */ 159 __attribute_hot__ 160 __attribute_pure__ 161 static int32_t array_get_index(const array * const a, const char * const k, const uint32_t klen) { 162 /* invariant: [lower-1] < probe < [upper] 163 * invariant: 0 <= lower <= upper <= a->used 164 */ 165 uint32_t lower = 0, upper = a->used; 166 while (lower != upper) { 167 uint32_t probe = (lower + upper) / 2; 168 const buffer * const b = &a->sorted[probe]->key; 169 /* key is non-empty (0==b->used), though possibly blank (1==b->used), 170 * if inserted into key-value array */ 171 /*force_assert(b && b->used);*/ 172 int cmp = array_keycmp(k, klen, b->ptr, b->used-1); 173 /*int cmp = array_keycmp(k, klen, CONST_BUF_LEN(b));*/ 174 if (cmp < 0) /* key < [probe] */ 175 upper = probe; /* still: lower <= upper */ 176 else if (cmp > 0) /* key > [probe] */ 177 lower = probe + 1; /* still: lower <= upper */ 178 else /*(cmp == 0)*/ /* found */ 179 return (int32_t)probe; 180 } 181 /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */ 182 return -(int)lower - 1; 183 } 184 185 __attribute_hot__ 186 data_unset *array_get_element_klen(const array * const a, const char *key, const uint32_t klen) { 187 const int32_t ipos = array_get_index(a, key, klen); 188 return ipos >= 0 ? a->sorted[ipos] : NULL; 189 } 190 191 /* non-const (data_config *) for configparser.y (not array_get_element_klen())*/ 192 data_unset *array_get_data_unset(const array * const a, const char *key, const uint32_t klen) { 193 const int32_t ipos = array_get_index(a, key, klen); 194 return ipos >= 0 ? a->sorted[ipos] : NULL; 195 } 196 197 data_unset *array_extract_element_klen(array * const a, const char *key, const uint32_t klen) { 198 const int32_t ipos = array_get_index(a, key, klen); 199 if (ipos < 0) return NULL; 200 201 /* remove entry from a->sorted: move everything after pos one step left */ 202 data_unset * const entry = a->sorted[ipos]; 203 const uint32_t last_ndx = --a->used; 204 if (last_ndx != (uint32_t)ipos) { 205 data_unset ** const d = a->sorted + ipos; 206 memmove(d, d+1, (last_ndx - (uint32_t)ipos) * sizeof(*d)); 207 } 208 209 if (entry != a->data[last_ndx]) { 210 /* walk a->data[] to find data ptr */ 211 /* (not checking (ndx <= last_ndx) since entry must be in a->data[]) */ 212 uint32_t ndx = 0; 213 while (entry != a->data[ndx]) ++ndx; 214 a->data[ndx] = a->data[last_ndx]; /* swap with last element */ 215 } 216 a->data[last_ndx] = NULL; 217 return entry; 218 } 219 220 static data_unset *array_get_unused_element(array * const a, const data_type_t t) { 221 /* After initial startup and config, most array usage is of homogeneous types 222 * and arrays are cleared once per request, so check only the first unused 223 * element to see if it can be reused */ 224 #if 1 225 data_unset * const du = (a->used < a->size) ? a->data[a->used] : NULL; 226 if (NULL != du && du->type == t) { 227 a->data[a->used] = NULL;/* make empty slot at a->used for next insert */ 228 return du; 229 } 230 return NULL; 231 #else 232 data_unset ** const data = a->data; 233 for (uint32_t i = a->used, sz = a->size; i < sz; ++i) { 234 if (data[i] && data[i]->type == t) { 235 data_unset * const ds = data[i]; 236 237 /* make empty slot at a->used for next insert */ 238 data[i] = data[a->used]; 239 data[a->used] = NULL; 240 241 return ds; 242 } 243 } 244 245 return NULL; 246 #endif 247 } 248 249 __attribute_hot__ 250 static void array_insert_data_at_pos(array * const a, data_unset * const entry, const uint32_t pos) { 251 /* This data structure should not be used for nearly so many entries */ 252 force_assert(a->used + 1 <= INT32_MAX); 253 254 if (a->size == a->used) { 255 array_extend(a, 16); 256 } 257 258 const uint32_t ndx = a->used++; 259 data_unset * const prev = a->data[ndx]; 260 a->data[ndx] = entry; 261 262 /* move everything one step to the right */ 263 if (pos != ndx) { 264 data_unset ** const d = a->sorted + pos; 265 memmove(d+1, d, (ndx - pos) * sizeof(*a->sorted)); 266 } 267 a->sorted[pos] = entry; 268 269 if (prev) prev->fn->free(prev); /* free prior data, if any, from slot */ 270 } 271 272 static data_integer * array_insert_integer_at_pos(array * const a, const uint32_t pos) { 273 #if 0 /*(not currently used by lighttpd in way that reuse would occur)*/ 274 data_integer *di = (data_integer *)array_get_unused_element(a,TYPE_INTEGER); 275 if (NULL == di) di = data_integer_init(); 276 #else 277 data_integer * const di = data_integer_init(); 278 #endif 279 array_insert_data_at_pos(a, (data_unset *)di, pos); 280 return di; 281 } 282 283 __attribute_hot__ 284 static data_string * array_insert_string_at_pos(array * const a, const uint32_t pos) { 285 data_string *ds = (data_string *)array_get_unused_element(a, TYPE_STRING); 286 if (NULL == ds) ds = data_string_init(); 287 array_insert_data_at_pos(a, (data_unset *)ds, pos); 288 return ds; 289 } 290 291 __attribute_hot__ 292 buffer * array_get_buf_ptr_ext(array * const a, const int ext, const char * const k, const uint32_t klen) { 293 int32_t ipos = array_get_index_ext(a, ext, k, klen); 294 if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value; 295 296 data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1)); 297 ds->ext = ext; 298 buffer_copy_string_len(&ds->key, k, klen); 299 buffer_clear(&ds->value); 300 return &ds->value; 301 } 302 303 int * array_get_int_ptr(array * const a, const char * const k, const uint32_t klen) { 304 int32_t ipos = array_get_index(a, k, klen); 305 if (ipos >= 0) return &((data_integer *)a->sorted[ipos])->value; 306 307 data_integer * const di =array_insert_integer_at_pos(a,(uint32_t)(-ipos-1)); 308 buffer_copy_string_len(&di->key, k, klen); 309 di->value = 0; 310 return &di->value; 311 } 312 313 buffer * array_get_buf_ptr(array * const a, const char * const k, const uint32_t klen) { 314 int32_t ipos = array_get_index(a, k, klen); 315 if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value; 316 317 data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1)); 318 buffer_copy_string_len(&ds->key, k, klen); 319 buffer_clear(&ds->value); 320 return &ds->value; 321 } 322 323 void array_insert_value(array * const a, const char * const v, const uint32_t vlen) { 324 data_string * const ds = array_insert_string_at_pos(a, a->used); 325 buffer_clear(&ds->key); 326 buffer_copy_string_len(&ds->value, v, vlen); 327 } 328 329 /* if entry already exists return pointer to existing entry, otherwise insert entry and return NULL */ 330 __attribute_cold__ 331 static data_unset **array_find_or_insert(array * const a, data_unset * const entry) { 332 force_assert(NULL != entry); 333 334 /* push value onto end of array if there is no key */ 335 if (buffer_is_empty(&entry->key)) { 336 array_insert_data_at_pos(a, entry, a->used); 337 return NULL; 338 } 339 340 /* try to find the entry */ 341 const int32_t ipos = array_get_index(a, CONST_BUF_LEN(&entry->key)); 342 if (ipos >= 0) return &a->sorted[ipos]; 343 344 array_insert_data_at_pos(a, entry, (uint32_t)(-ipos - 1)); 345 return NULL; 346 } 347 348 /* replace or insert data (free existing entry) */ 349 void array_replace(array * const a, data_unset * const entry) { 350 if (NULL == array_find_or_insert(a, entry)) return; 351 352 /* find the entry (array_find_or_insert() returned non-NULL) */ 353 const int32_t ipos = array_get_index(a, CONST_BUF_LEN(&entry->key)); 354 force_assert(ipos >= 0); 355 data_unset *old = a->sorted[ipos]; 356 force_assert(old != entry); 357 a->sorted[ipos] = entry; 358 359 uint32_t i = 0; 360 while (i < a->used && a->data[i] != old) ++i; 361 force_assert(i != a->used); 362 a->data[i] = entry; 363 364 old->fn->free(old); 365 } 366 367 void array_insert_unique(array * const a, data_unset * const entry) { 368 data_unset **old; 369 370 if (NULL != (old = array_find_or_insert(a, entry))) { 371 force_assert((*old)->type == entry->type); 372 entry->fn->insert_dup(*old, entry); 373 } 374 } 375 376 int array_is_vlist(const array * const a) { 377 for (uint32_t i = 0; i < a->used; ++i) { 378 data_unset *du = a->data[i]; 379 if (!buffer_is_empty(&du->key) || du->type != TYPE_STRING) return 0; 380 } 381 return 1; 382 } 383 384 int array_is_kvany(const array * const a) { 385 for (uint32_t i = 0; i < a->used; ++i) { 386 data_unset *du = a->data[i]; 387 if (buffer_is_empty(&du->key)) return 0; 388 } 389 return 1; 390 } 391 392 int array_is_kvarray(const array * const a) { 393 for (uint32_t i = 0; i < a->used; ++i) { 394 data_unset *du = a->data[i]; 395 if (buffer_is_empty(&du->key) || du->type != TYPE_ARRAY) return 0; 396 } 397 return 1; 398 } 399 400 int array_is_kvstring(const array * const a) { 401 for (uint32_t i = 0; i < a->used; ++i) { 402 data_unset *du = a->data[i]; 403 if (buffer_is_empty(&du->key) || du->type != TYPE_STRING) return 0; 404 } 405 return 1; 406 } 407 408 /* array_match_*() routines follow very similar pattern, but operate on slightly 409 * different data: array key/value, prefix/suffix match, case-insensitive or not 410 * While these could be combined into fewer routines with flags to modify the 411 * behavior, the interface distinctions are useful to add clarity to the code, 412 * and the specialized routines run slightly faster */ 413 414 data_unset * 415 array_match_key_prefix_klen (const array * const a, const char * const s, const uint32_t slen) 416 { 417 for (uint32_t i = 0; i < a->used; ++i) { 418 const buffer * const key = &a->data[i]->key; 419 const uint32_t klen = buffer_string_length(key); 420 if (klen <= slen && 0 == memcmp(s, key->ptr, klen)) 421 return a->data[i]; 422 } 423 return NULL; 424 } 425 426 data_unset * 427 array_match_key_prefix_nc_klen (const array * const a, const char * const s, const uint32_t slen) 428 { 429 for (uint32_t i = 0; i < a->used; ++i) { 430 const buffer * const key = &a->data[i]->key; 431 const uint32_t klen = buffer_string_length(key); 432 if (klen <= slen && buffer_eq_icase_ssn(s, key->ptr, klen)) 433 return a->data[i]; 434 } 435 return NULL; 436 } 437 438 data_unset * 439 array_match_key_prefix (const array * const a, const buffer * const b) 440 { 441 #ifdef __clang_analyzer__ 442 force_assert(b); 443 #endif 444 return array_match_key_prefix_klen(a, CONST_BUF_LEN(b)); 445 } 446 447 data_unset * 448 array_match_key_prefix_nc (const array * const a, const buffer * const b) 449 { 450 return array_match_key_prefix_nc_klen(a, CONST_BUF_LEN(b)); 451 } 452 453 const buffer * 454 array_match_value_prefix (const array * const a, const buffer * const b) 455 { 456 const uint32_t blen = buffer_string_length(b); 457 458 for (uint32_t i = 0; i < a->used; ++i) { 459 const buffer * const value = &((data_string *)a->data[i])->value; 460 const uint32_t vlen = buffer_string_length(value); 461 if (vlen <= blen && 0 == memcmp(b->ptr, value->ptr, vlen)) 462 return value; 463 } 464 return NULL; 465 } 466 467 const buffer * 468 array_match_value_prefix_nc (const array * const a, const buffer * const b) 469 { 470 const uint32_t blen = buffer_string_length(b); 471 472 for (uint32_t i = 0; i < a->used; ++i) { 473 const buffer * const value = &((data_string *)a->data[i])->value; 474 const uint32_t vlen = buffer_string_length(value); 475 if (vlen <= blen && buffer_eq_icase_ssn(b->ptr, value->ptr, vlen)) 476 return value; 477 } 478 return NULL; 479 } 480 481 data_unset * 482 array_match_key_suffix (const array * const a, const buffer * const b) 483 { 484 const uint32_t blen = buffer_string_length(b); 485 const char * const end = b->ptr + blen; 486 487 for (uint32_t i = 0; i < a->used; ++i) { 488 const buffer * const key = &a->data[i]->key; 489 const uint32_t klen = buffer_string_length(key); 490 if (klen <= blen && 0 == memcmp(end - klen, key->ptr, klen)) 491 return a->data[i]; 492 } 493 return NULL; 494 } 495 496 data_unset * 497 array_match_key_suffix_nc (const array * const a, const buffer * const b) 498 { 499 const uint32_t blen = buffer_string_length(b); 500 const char * const end = b->ptr + blen; 501 502 for (uint32_t i = 0; i < a->used; ++i) { 503 const buffer * const key = &a->data[i]->key; 504 const uint32_t klen = buffer_string_length(key); 505 if (klen <= blen && buffer_eq_icase_ssn(end - klen, key->ptr, klen)) 506 return a->data[i]; 507 } 508 return NULL; 509 } 510 511 const buffer * 512 array_match_value_suffix (const array * const a, const buffer * const b) 513 { 514 const uint32_t blen = buffer_string_length(b); 515 const char * const end = b->ptr + blen; 516 517 for (uint32_t i = 0; i < a->used; ++i) { 518 const buffer * const value = &((data_string *)a->data[i])->value; 519 const uint32_t vlen = buffer_string_length(value); 520 if (vlen <= blen && 0 == memcmp(end - vlen, value->ptr, vlen)) 521 return value; 522 } 523 return NULL; 524 } 525 526 const buffer * 527 array_match_value_suffix_nc (const array * const a, const buffer * const b) 528 { 529 const uint32_t blen = buffer_string_length(b); 530 const char * const end = b->ptr + blen; 531 532 for (uint32_t i = 0; i < a->used; ++i) { 533 const buffer * const value = &((data_string *)a->data[i])->value; 534 const uint32_t vlen = buffer_string_length(value); 535 if (vlen <= blen && buffer_eq_icase_ssn(end - vlen, value->ptr, vlen)) 536 return value; 537 } 538 return NULL; 539 } 540 541 data_unset * 542 array_match_path_or_ext (const array * const a, const buffer * const b) 543 { 544 const uint32_t blen = buffer_string_length(b); 545 546 for (uint32_t i = 0; i < a->used; ++i) { 547 /* check extension in the form "^/path" or ".ext$" */ 548 const buffer * const key = &a->data[i]->key; 549 const uint32_t klen = buffer_string_length(key); 550 if (klen <= blen 551 && 0 == memcmp((*(key->ptr) == '/' ? b->ptr : b->ptr + blen - klen), 552 key->ptr, klen)) 553 return a->data[i]; 554 } 555 return NULL; 556 } 557 558 559 560 561 562 #include <stdio.h> 563 564 void array_print_indent(int depth) { 565 int i; 566 for (i = 0; i < depth; i ++) { 567 fprintf(stdout, " "); 568 } 569 } 570 571 uint32_t array_get_max_key_length(const array * const a) { 572 uint32_t maxlen = 0; 573 for (uint32_t i = 0; i < a->used; ++i) { 574 const buffer * const k = &a->data[i]->key; 575 uint32_t len = buffer_string_length(k); 576 577 if (len > maxlen) { 578 maxlen = len; 579 } 580 } 581 return maxlen; 582 } 583 584 int array_print(const array * const a, int depth) { 585 uint32_t i; 586 uint32_t maxlen; 587 int oneline = 1; 588 589 if (a->used > 5) { 590 oneline = 0; 591 } 592 for (i = 0; i < a->used && oneline; i++) { 593 data_unset *du = a->data[i]; 594 if (!buffer_is_empty(&du->key)) { 595 oneline = 0; 596 break; 597 } 598 switch (du->type) { 599 case TYPE_INTEGER: 600 case TYPE_STRING: 601 break; 602 default: 603 oneline = 0; 604 break; 605 } 606 } 607 if (oneline) { 608 fprintf(stdout, "("); 609 for (i = 0; i < a->used; i++) { 610 data_unset *du = a->data[i]; 611 if (i != 0) { 612 fprintf(stdout, ", "); 613 } 614 du->fn->print(du, depth + 1); 615 } 616 fprintf(stdout, ")"); 617 return 0; 618 } 619 620 maxlen = array_get_max_key_length(a); 621 fprintf(stdout, "(\n"); 622 for (i = 0; i < a->used; i++) { 623 data_unset *du = a->data[i]; 624 array_print_indent(depth + 1); 625 if (!buffer_is_empty(&du->key)) { 626 int j; 627 628 if (i && (i % 5) == 0) { 629 fprintf(stdout, "# %u\n", i); 630 array_print_indent(depth + 1); 631 } 632 fprintf(stdout, "\"%s\"", du->key.ptr); 633 for (j = maxlen - buffer_string_length(&du->key); j > 0; j--) { 634 fprintf(stdout, " "); 635 } 636 fprintf(stdout, " => "); 637 } 638 du->fn->print(du, depth + 1); 639 fprintf(stdout, ",\n"); 640 } 641 if (!(i && (i - 1 % 5) == 0)) { 642 array_print_indent(depth + 1); 643 fprintf(stdout, "# %u\n", i); 644 } 645 array_print_indent(depth); 646 fprintf(stdout, ")"); 647 648 return 0; 649 } 650