From eb86c8c5ffc43a4e52881a20132eb03c3f9da283 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 12 Apr 2010 22:24:54 -0400 Subject: [PATCH] Add evbuffer_copyout to copy data from an evbuffer without draining The evbuffer_remove() function copies data from the front of an evbuffer into an array of char, and removes the data from the buffer. This function behaves the same, but does not remove the data. This behavior can be handy for lots of protocols, where you want the evbuffer to accumulate data until a complete record has arrived. Lots of people have asked for a function more or less like this, and though it isn't too hard to code one from evbuffer_peek(), it is apparently annoying to do it in every app you write. The evbuffer_peek() function is significantly faster, but it requires that the user be able to handle data in separate extents. This patch also reimplements evbufer_remove() as evbuffer_copyout() followed by evbuffer_drain(). I am reasonably confident that this won't be a performance hit: the memcpy() overhead should dominate the cost of walking the list an extra time. --- buffer.c | 39 ++++++++++++++++++--------------------- include/event2/buffer.h | 10 ++++++++++ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/buffer.c b/buffer.c index 14108884..1f4e9236 100644 --- a/buffer.c +++ b/buffer.c @@ -861,15 +861,28 @@ done: } /* Reads data from an event buffer and drains the bytes read */ - int evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen) +{ + ev_ssize_t n; + EVBUFFER_LOCK(buf); + n = evbuffer_copyout(buf, data_out, datlen); + if (n > 0) { + if (evbuffer_drain(buf, n)<0) + n = -1; + } + EVBUFFER_UNLOCK(buf); + return (int)n; +} + +ev_ssize_t +evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen) { /*XXX fails badly on sendfile case. */ - struct evbuffer_chain *chain, *tmp; + struct evbuffer_chain *chain; char *data = data_out; size_t nread; - int result = 0; + ev_ssize_t result = 0; EVBUFFER_LOCK(buf); @@ -893,31 +906,15 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen) data += chain->off; datlen -= chain->off; - if (buf->last_with_datap == &chain->next) { - buf->last_with_datap = &buf->first; - } - - tmp = chain; chain = chain->next; - evbuffer_chain_free(tmp); + EVUTIL_ASSERT(chain || datlen==0); } - buf->first = chain; - if (chain == NULL) - buf->last = NULL; - if (datlen) { + EVUTIL_ASSERT(chain); memcpy(data, chain->buffer + chain->misalign, datlen); - chain->misalign += datlen; - chain->off -= datlen; } - buf->total_len -= nread; - - buf->n_del_for_cb += nread; - if (nread) - evbuffer_invoke_callbacks(buf); - result = nread; done: EVBUFFER_UNLOCK(buf); diff --git a/include/event2/buffer.h b/include/event2/buffer.h index 8fb5c3d0..bd175c25 100644 --- a/include/event2/buffer.h +++ b/include/event2/buffer.h @@ -267,6 +267,16 @@ int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); */ int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); +/** + Read data from an event buffer, and leave the buffer unchanged. + + @param buf the event buffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen); + /** Read data from an event buffer into another event buffer draining the bytes from the src buffer read. This function avoids memcpy -- 2.40.0