From 3f271692f37af0edd8bf207fc88b093199854aeb Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 8 Dec 2014 18:14:53 +0100 Subject: EXP: channel: implement a zero-copy buffer transfer bi_swpbuf() swaps the buffer passed in argument with the one attached to the channel, but only if this last one is empty. The idea is to avoid a copy when buffers can simply be swapped. --- include/proto/channel.h | 1 + src/channel.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/proto/channel.h b/include/proto/channel.h index 1cee05a..e38e375 100644 --- a/include/proto/channel.h +++ b/include/proto/channel.h @@ -43,6 +43,7 @@ unsigned long long __channel_forward(struct channel *chn, unsigned long long byt /* SI-to-channel functions working with buffers */ int bi_putblk(struct channel *chn, const char *str, int len); +struct buffer *bi_swpbuf(struct channel *chn, struct buffer *buf); int bi_putchr(struct channel *chn, char c); int bo_inject(struct channel *chn, const char *msg, int len); int bo_getline(struct channel *chn, char *str, int len); diff --git a/src/channel.c b/src/channel.c index 2f98396..04301fb 100644 --- a/src/channel.c +++ b/src/channel.c @@ -196,6 +196,51 @@ int bi_putblk(struct channel *chn, const char *blk, int len) return len; } +/* Tries to copy the whole buffer into the channel's buffer after length + * controls. It will only succeed if the target buffer is empty, in which case + * it will simply swap the buffers. The buffer not attached to the channel is + * returned so that the caller can store it locally. The chn->buf->o and + * to_forward pointers are updated. If the output buffer is a dummy buffer or + * if it still contains data is returned, indicating that nothing could + * be done. Channel flag READ_PARTIAL is updated if some data can be transferred. + * The chunk's length is updated with the number of bytes sent. On errors, NULL + * is returned. Note that only buf->i is considered. + */ +struct buffer *bi_swpbuf(struct channel *chn, struct buffer *buf) +{ + struct buffer *old; + + if (unlikely(channel_input_closed(chn))) + return NULL; + + if (!chn->buf->size || !buffer_empty(chn->buf)) { + chn->flags |= CF_WAKE_WRITE; + return buf; + } + + old = chn->buf; + chn->buf = buf; + + if (!buf->i) + return old; + + chn->total += buf->i; + + if (chn->to_forward) { + unsigned long fwd = buf->i; + if (chn->to_forward != CHN_INFINITE_FORWARD) { + if (fwd > chn->to_forward) + fwd = chn->to_forward; + chn->to_forward -= fwd; + } + b_adv(chn->buf, fwd); + } + + /* notify that some data was read from the SI into the buffer */ + chn->flags |= CF_READ_PARTIAL; + return old; +} + /* Gets one text line out of a channel's buffer from a stream interface. * Return values : * >0 : number of bytes read. Includes the \n if present before len or end. -- 1.7.12.1