From: Aurelien DARRAGON Date: Wed, 12 Aug 2026 09:39:54 +0000 (+0200) Subject: MINOR: buf: make b_rep_blk() wrapping-proof X-Git-Tag: v3.5-dev6~54 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=9addb6729d9cb98faedc23ad6c1a3598ade13a62;p=haproxy.git MINOR: buf: make b_rep_blk() wrapping-proof b_rep_blk() was not versatile because there were a lot of cases (including wrapping resulting from the insertion or deletion) that were not supported, this actually made the function not very useful. Since there a no more known usages of this API function in the code, but yet the decompression filters could make use of it in upcoming commits let's revisit the function by leveraging b_move() to properly support edge cases such as wrapping. Now the function is pretty versatile and can safely be used to expand as well as to shrink buffer when replacing existing data with new data. There is still one known limitation to this day, and have to be contiguous (not interrupted by wrapping buffer), but that could be improved in the future if needed. The function assumes the caller already checked that inserted data will fit in the buffer (removed data also taken into account). --- diff --git a/src/buf.c b/src/buf.c index 867c7671a..32388d6d3 100644 --- a/src/buf.c +++ b/src/buf.c @@ -473,35 +473,85 @@ void b_move(const struct buffer *b, size_t src, size_t len, ssize_t shift) * located between and with a copy of of length . The * buffer's length is automatically updated. This is used to replace a block * with another one inside a buffer. The shift value (positive or negative) is - * returned. If there's no space left, the move is not done. If is null, - * the pointer is allowed to be null, in order to erase a block. + * returned. If is null, the pointer is allowed to be null, in + * order to erase a block. The caller has to check that the delta (inserted + * data - removed data) effectively fits into the buffer space (b_size()), + * else a BUG_ON() will be triggered, because the function is built to never + * fail when this condition is met. + * + * This function supports wrapping, as long as and are on the same + * side of the buffer ( and have to be contigous, ie not interrupted + * by wrapping buffer) */ int b_rep_blk(struct buffer *b, char *pos, char *end, const char *blk, size_t len) { + char *tail = b_tail(b); int delta; + size_t block1; + size_t block2; + size_t after; BUG_ON(pos < b->area || pos >= b->area + b->size); delta = len - (end - pos); - if (__b_tail(b) + delta > b_wrap(b)) - return 0; /* no space left */ + /* check if out of space, which is assumed it cannot happen as the + * caller is supposed to check that prior to calling the function + */ + BUG_ON(b_data(b) + delta > b_size(b)); - if (b_data(b) && - b_tail(b) + delta > b_head(b) && - b_head(b) >= b_tail(b)) - return 0; /* no space left before wrapping data */ + block1 = len; + block2 = 0; - /* first, protect the end of the buffer */ - memmove(end + delta, end, b_tail(b) - end); + if (delta > 0) { + /* Shift buffer data to make room for additional data. We take extra + * precautions to ensure that we properly handle wrapping (either it + * wrapped before the expansion or due to the expansion. + */ + if (tail < end) { + /* buffer already wrapped BEFORE expansion */ + after = b_size(b) - (end - tail); + } + else { + /* buffer didn't wrap yet or about to wrap because of expansion */ + after = tail - end; + } + /* nothing to move when is the tail, and may then be + * as large as the whole buffer, which b_move() refuses + */ + if (after) + b_move(b, end - b_orig(b), after, delta); + block1 = MIN(len, b_wrap(b) - pos); + block2 = len - block1; + } /* now, copy blk over pos */ - if (len) - memcpy(pos, blk, len); + if (block1) + memcpy(pos, blk, block1); + if (block2) { + /* remaining data after wrapping */ + memcpy(b_orig(b), blk + block1, block2); + } b_add(b, delta); b_realign_if_empty(b); + if (delta >= 0) + return delta; + + if (tail < end) { + /* we arrive here if a wrapping buffer was shrunk, meaning we left a + * hole. In this case we must shift data back to prevent fragmentation + */ + after = b_size(b) - (end - tail); + } + else { + /* normal shrink */ + after = tail - end; + } + if (after) + b_move(b, end - b_orig(b), after, delta); + return delta; }