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.
This commit is contained in:
Nick Mathewson 2010-04-12 22:24:54 -04:00
parent 819f949f4a
commit eb86c8c5ff
2 changed files with 28 additions and 21 deletions

View File

@ -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);

View File

@ -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