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.
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 @@
/* 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 @@
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.
* Return values :
* >0 : number of bytes read. Includes the \n if present before len or end.