| 374 | } |
| 375 | |
| 376 | void cbm_lsh_insert(cbm_lsh_index_t *idx, const cbm_lsh_entry_t *entry) { |
| 377 | if (!idx || !entry || !entry->fingerprint) { |
| 378 | return; |
| 379 | } |
| 380 | |
| 381 | /* Store a copy of the entry */ |
| 382 | if (idx->entry_count >= idx->entry_cap) { |
| 383 | int new_cap = |
| 384 | idx->entry_cap < ENTRY_INIT_CAP ? ENTRY_INIT_CAP : idx->entry_cap * GROW_FACTOR; |
| 385 | cbm_lsh_entry_t *new_entries = cbm_realloc(CBM_MEM_CLASS_SEMANTIC, idx->entries, |
| 386 | (size_t)new_cap * sizeof(cbm_lsh_entry_t)); |
| 387 | if (!new_entries) { |
| 388 | return; |
| 389 | } |
| 390 | idx->entries = new_entries; |
| 391 | idx->entry_cap = new_cap; |
| 392 | } |
| 393 | int entry_idx = idx->entry_count; |
| 394 | idx->entries[entry_idx] = *entry; |
| 395 | idx->entry_count++; |
| 396 | |
| 397 | /* Insert index into each band's bucket */ |
| 398 | for (int b = 0; b < CBM_LSH_BANDS; b++) { |
| 399 | uint32_t h = band_hash(entry->fingerprint, b); |
| 400 | bucket_push(&idx->bands[b][h], entry_idx); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | /* O(1) seen-set: open-addressing hash table on node_id for dedup. */ |
| 405 | typedef struct { |
no test coverage detected