lib: add bf_vector and use it in cgen#487
Conversation
Claude review of PR #487 (73c0440)Suggestions
Nits
|
2bbe579 to
419e434
Compare
419e434 to
e77f462
Compare
e77f462 to
305dd19
Compare
305dd19 to
f5a94cd
Compare
f5a94cd to
e9d06a6
Compare
e9d06a6 to
4cd032f
Compare
703e225 to
a8759b5
Compare
a8759b5 to
c828488
Compare
309f17f to
fba6566
Compare
fba6566 to
c37916b
Compare
c37916b to
975b503
Compare
975b503 to
dd1fe11
Compare
| (elem) && (elem) < (vec)->data + ((vec)->size * (vec)->elem_size); \ | ||
| (elem) = (elem) + (vec)->elem_size) | ||
|
|
||
| /** |
There was a problem hiding this comment.
Claude: suggestion: bf_vector_foreach performs pointer arithmetic on void * ((elem) + (vec)->elem_size and (vec)->data + ...), which is a GCC extension not valid in standard C.
Since vector.h is a public header under include/bpfilter/core/, any downstream consumer using this macro with -pedantic -std=c17 will get a compile error. The pedantic integration test (tests/integration/pedantic_c.sh) includes all public headers with -pedantic-errors -std=c17 -Werror — the macro definition alone won't trigger an error (unexpanded macros aren't compiled), but any call site under those flags will fail.
For comparison, bf_list_foreach in list.h avoids this by iterating over typed pointers (bf_list_node *). Consider casting through char *:
| /** | |
| #define bf_vector_foreach(vec, elem) \ | |
| for (void *(elem) = (vec)->data; \ | |
| (elem) && (char *)(elem) < (char *)(vec)->data + ((vec)->size * (vec)->elem_size); \ | |
| (elem) = (void *)((char *)(elem) + (vec)->elem_size)) |
There was a problem hiding this comment.
We could do that, but the codebase is already not C17 compliant in different places as well.
| * @brief Iterate over every element of a `bf_vector`. | ||
| * | ||
| * `elem` is declared as a pointer to the element type and will point to each | ||
| * element in turn. Do not add or remove elements during iteration. |
There was a problem hiding this comment.
Do not add
This should not be an issue.
remove elements during iteration
Same here, as we don't shrink the memory buffer.
There was a problem hiding this comment.
Do not add
If that addition causes growth/reallocation, void *(elem) = (vec)->data; will point to the old memory address. I could turn the iterator into index int i = 0; and use double-for trick to have void *(elem) = (vec)->data + i * (vec)->elem_size;, but I think it's an overkill?
remove elements during iteration
But we shift the next elements left, i.e. 5th element becomes 4th, 4th becomes 3rd, etc. The foreach will skip an element because of (elem) = (elem) + (vec)->elem_size.
eb242f7 to
9c2bbd0
Compare
456d0b3 to
d5af70d
Compare
d5af70d to
73c0440
Compare
| assert(vec); | ||
| assert(elem); | ||
|
|
||
| r = _bf_vector_grow(vec, vec->size + 1); |
There was a problem hiding this comment.
Claude: suggestion: bf_vector_add passes vec->size + 1 to _bf_vector_grow without overflow protection. If vec->size is SIZE_MAX, this wraps to 0, causing _bf_vector_grow to see required <= vec->cap and return success, after which the memcpy writes past the allocated buffer.
bf_vector_add_many already guards the equivalent addition with __builtin_add_overflow (line 186). The same pattern should be applied here for consistency:
size_t required;
int r;
assert(vec);
assert(elem);
if (__builtin_add_overflow(vec->size, 1, &required))
return -ENOMEM;
r = _bf_vector_grow(vec, required);While SIZE_MAX elements is unreachable in practice, the inconsistency between the two functions makes the code harder to reason about.
| for (void *(elem) = (vec)->data, \ | ||
| *_end = (vec)->data ? \ | ||
| (vec)->data + ((vec)->size * (vec)->elem_size) : \ | ||
| NULL; \ |
There was a problem hiding this comment.
Claude: nit: The internal sentinel variable _end uses a single-underscore prefix, while other macros in the codebase consistently use double underscores for internal variables: __next in bf_list_foreach (list.h:107), __r in EMIT (program.h:103), __idx in bf_jmpctx_get (jmp.h:60). Consider renaming to __end to match the established convention.
| #include "bpfilter/helper.h" | ||
|
|
||
| #define _BF_VECTOR_INIT_CAP 8 | ||
| // Largest number that won't overflow when multiplying by 1.5x. |
There was a problem hiding this comment.
Claude: nit: The comment says "Largest number that won't overflow when multiplying by 1.5x" but the growth factor is applied as cap + cap / 2 (integer addition), not a literal multiplication. A more precise comment would be: "Largest capacity where cap + cap / 2 does not exceed SIZE_MAX."
I initially planned to use bf_vector in bf_hashset, but I got convinced by @yaakov-stein that it's not a good idea. However, we could use bf_vector in other places, especially where we use bf_dynbuf or where we handroll vector-like behavior.
Commits:
lib: core: add bf_vector- implementation. Vector doubles in size when it hits the limits. Has a helper to remove elements as well, though shrinking is not implemented.cgen: use bf_vector for img- Instead of managingimg,img_size, andimg_capby hand, use bf_vector.