Conversation
Claude review of PR #470 (c911d48)Suggestions
Nits
CLAUDE.md improvements
|
314af18 to
02938a3
Compare
qdeslandes
left a comment
There was a problem hiding this comment.
Alright, first pass of review and a few things to fix. I'll do a second, deeper pass, when those are solved. That being said, it's a very welcome addition! :D
02938a3 to
1aae907
Compare
1aae907 to
550c254
Compare
550c254 to
d6883e1
Compare
2111d2d to
8f94d51
Compare
|
Ready for review. Remaining open threads:
|
| /** | ||
| * @brief Iterate over all elements in a hashset in insertion order. | ||
| * | ||
| * Unsafe to add elements during iteration (can cause rehashing). |
There was a problem hiding this comment.
Claude: nit: bf_hashset_foreach doc should clarify dangling pointer after bf_hashset_delete
The doc says "Safe to bf_hashset_delete the current element during iteration" but does not mention that elem_var becomes a dangling pointer after the delete call and must not be accessed in the same iteration body. Consider:
Safe to call `bf_hashset_delete()` on the current element during iteration.
Do not access `elem_var` after the delete call.
There was a problem hiding this comment.
I think it's obvious?
| */ | ||
| typedef struct bf_hashset_elem | ||
| { | ||
| /// User-provided data pointer. |
There was a problem hiding this comment.
Claude: nit: bf_hashset_elem.data accessor pattern contradicts @warning
The @warning on bf_hashset_elem (line 44) says "the inside of this structure should not be directly accessed", yet the bf_hashset_foreach macro doc (line 107) instructs users to "Access the stored data via elem_var->data". This is the intended access pattern (there's no getter function like bf_list_node_get_data). Either relax the @warning to exempt data, or add a bf_hashset_elem_get_data() inline accessor for consistency with the list API.
There was a problem hiding this comment.
Same pattern in bf_list_foreach. Macros need access to struct internals.
| for (; n0 || n1; n0 = bf_list_node_next(n0), n1 = bf_list_node_next(n1)) { | ||
| if (0 != memcmp(bf_list_node_get_data(n0), bf_list_node_get_data(n1), | ||
| lhs->elem_size)) | ||
| for (bf_hashset_elem *n0 = lhs->elems.head, *n1 = rhs->elems.head; n0 || n1; |
There was a problem hiding this comment.
Claude: nit: bft_set_eq_ordered directly accesses bf_hashset struct internals
lhs->elems.head and n0->next bypass the public API, which the @warning on bf_hashset discourages. This could be rewritten using bf_hashset_foreach:
bf_hashset_elem *n1 = rhs->elems.head; // or use a second manual iteration
bf_hashset_foreach (&lhs->elems, n0) {
if (!n1 || memcmp(n0->data, n1->data, lhs->elem_size) != 0)
return false;
n1 = n1->next;
}Alternatively, since this is test harness code, a comment noting the intentional internal access would suffice.
There was a problem hiding this comment.
Test code, should be fine.
fd7b629 to
3fd44fb
Compare
3fd44fb to
c911d48
Compare
| // Take ownership of data to stop to_add cleanup from freeing it. | ||
| bf_list_node_take_data(elem_node); | ||
| } | ||
| bf_hashset_foreach (&(*to_add)->elems, elem) { |
There was a problem hiding this comment.
Claude: nit: The old list-based code used bf_list_node_take_data to transfer ownership from to_add to dest without copying. The new implementation calls bf_set_add_elem which does malloc + memcpy for each element. This doubles per-element allocations for large sets. Worth noting as a potential future optimization (e.g., a bf_hashset_take that moves an element between sets without reallocation).
There was a problem hiding this comment.
Fair point, something like bf_hashset_take_node and bf_hashset_add_node` (that rewires existing bf_hashset_elem*) would work, though I'm not sure if it's worth it?
In my intuition, a free(<bf_hashset_elem*>) followed by malloc(sizeof(bf_hashset_elem)) should be pretty cheap, since we have a perfect gap of unallocated memory to reuse.
Currently,
bf_setusesbf_listunder the hood. This is suboptimal performance-wise. Here I am changingbf_setto be backed bybf_hashset.lib: helper: add FNV-1a hash function- imported from another PR, a simple hash function.lib: add bf_hashset- main commit. Implements a simple hashset (tombstones on deletion, grows 2x when load factor exceeds 50%, never shrinks, linear probing on collision). See https://en.wikipedia.org/wiki/Linear_probing (with "special flag value" strategy in "Deletion" section).lib: set: use bf_hashset for elems field- changesbf_set's inner structureSee #460 for previous attempt.
Fixes #418