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