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