MEDIUM: 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.
This commit is contained in:
Willy Tarreau 2014-12-08 18:14:53 +01:00
parent 33cb065348
commit b034b2598d
2 changed files with 46 additions and 0 deletions

View File

@ -43,6 +43,7 @@ unsigned long long __channel_forward(struct channel *chn, unsigned long long byt
/* SI-to-channel functions working with buffers */ /* SI-to-channel functions working with buffers */
int bi_putblk(struct channel *chn, const char *str, int len); 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 bi_putchr(struct channel *chn, char c);
int bo_inject(struct channel *chn, const char *msg, int len); int bo_inject(struct channel *chn, const char *msg, int len);
int bo_getline(struct channel *chn, char *str, int len); int bo_getline(struct channel *chn, char *str, int len);

View File

@ -196,6 +196,51 @@ int bi_putblk(struct channel *chn, const char *blk, int len)
return len; return len;
} }
/* Tries to copy the whole buffer <buf> 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 <buf> 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. /* Gets one text line out of a channel's buffer from a stream interface.
* Return values : * Return values :
* >0 : number of bytes read. Includes the \n if present before len or end. * >0 : number of bytes read. Includes the \n if present before len or end.