From c4f6bf9f8b48e8ead7c1ef759bb087438b9d145c Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 28 Nov 2011 20:58:02 +0100 Subject: EXP: buffers: add buffer_is_between() and buffer_delete_pending() These primitives are used respectively to check that a pointer is within a known area, and to delete part of a buffers's pending data. The later still needs some polishing. It might make sense to return the delta offset for pending data so that transaction pointers may be adjusted accordingly. --- include/proto/buffers.h | 22 +++++++++++++++++++ src/buffers.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 0 deletions(-) diff --git a/include/proto/buffers.h b/include/proto/buffers.h index f0ea83d..64ebd6f 100644 --- a/include/proto/buffers.h +++ b/include/proto/buffers.h @@ -216,6 +216,28 @@ static inline int buffer_pending(const struct buffer *buf) return buf->l - buf->send_max; } +/* Returns non-zero if pointer p is between pointer low (included) and pointer + * high (excluded) in buffer b, that is : + * low <= p < high + * Wrapping is supported. All pointers must be valid for the buffer. If low and + * high are equal, an empty area is assumed and p never matches. + */ +static inline int buffer_is_between(const struct buffer *b, const char *p, + const char *low, const char *high) +{ + if (p >= low) { + if (p < high) + return 1; /* p is in the contiguous area */ + if (high < low) + return 1; /* p is before a wrapping area */ + return 0; /* p >= high >= low => outside */ + } + else if (p < high) + return 1; /* p is after a wrapping area */ + + return 0; /* p < low && p >= high => outside */ +} + /* Returns the size of the working area which the caller knows ends at . * If equals buf->r (modulo size), then it means that the free area which * follows is part of the working area. Otherwise, the working area stops at diff --git a/src/buffers.c b/src/buffers.c index 88d40ff..f65a8b2 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -316,6 +316,60 @@ int buffer_get_block(struct buffer *buf, char *blk, int len, int offset) return len; } +/* Deletes part of a buffer starting at and for bytes, without + * going further than b->r. It is illegal to delete data outside the pending + * area located between b->w+sendmax and b->r, and the function assumes only + * those data are concerned. + * The b->lr pointer is adjusted if it is within or after the deleted area. + * The function supports wrapping at the end of the buffer. If the data to + * delete starts at the beginning of the pending data and no outgoing data + * is pending, or if the stop pointer matches ->r, the function only adjusts + * pointers. Both the pos pointer and the count value must be valid. + */ +void buffer_delete_pending(struct buffer *b, char *pos, int count) +{ + char *src, *end; + + if (!count) + return; + + src = buffer_pointer(b, pos + count); + + if (unlikely(pos == b->w)) { + /* easy case, no data before the starting point, we only move + * pointers. + */ + b->w = src; + if (buffer_is_between(b, b->lr, pos, src)) + b->lr = src; + b->l -= count; + return; + } + + /* Update b->lr now, it's easier to do it now that we have the pointers. + * b->r will be done later. + */ + if (buffer_is_between(b, b->lr, pos, src)) + b->lr = src; + else if (buffer_is_between(b, b->lr, src, b->w)) + b->lr = buffer_pointer(b, b->lr - count); + + /* Move the data one byte at a time. Doing it using memove requires + * up to 3 moves on complex pointers, which isn't worth the complexity. + */ + end = b->data + b->size; + while (src != b->r) { + *pos++ = *src++; + if (pos == end) + pos = b->data; + if (src == end) + src = b->data; + } + + b->r = pos; + b->l -= count; +} + /* This function writes the string at position which must be in * buffer , and moves just after the end of . 's parameters * (l, r, lr) are updated to be valid after the shift. the shift value -- 1.7.2.3