Add evbuffer_copyout_from to copy data from the middle of a buffer

You could previously do this with evbuffer_peek() and some memcpys,
but it was a bit more work than most folks wanted to get into.

Closes sourceforge ticket 3108072
This commit is contained in:
Nick Mathewson 2011-12-07 13:04:35 -05:00
parent 35c5c9558a
commit 27e222559a
3 changed files with 126 additions and 9 deletions

View File

@ -1036,7 +1036,7 @@ 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);
n = evbuffer_copyout_from(buf, NULL, data_out, datlen);
if (n > 0) {
if (evbuffer_drain(buf, n)<0)
n = -1;
@ -1047,19 +1047,35 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
ev_ssize_t
evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen)
{
return evbuffer_copyout_from(buf, NULL, data_out, datlen);
}
ev_ssize_t
evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos,
void *data_out, size_t datlen)
{
/*XXX fails badly on sendfile case. */
struct evbuffer_chain *chain;
char *data = data_out;
size_t nread;
ev_ssize_t result = 0;
size_t pos_in_chain;
EVBUFFER_LOCK(buf);
chain = buf->first;
if (pos) {
chain = pos->_internal.chain;
pos_in_chain = pos->_internal.pos_in_chain;
if (datlen + pos->pos > buf->total_len)
datlen = buf->total_len - pos->pos;
} else {
chain = buf->first;
pos_in_chain = 0;
if (datlen > buf->total_len)
datlen = buf->total_len;
}
if (datlen >= buf->total_len)
datlen = buf->total_len;
if (datlen == 0)
goto done;
@ -1071,18 +1087,23 @@ evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen)
nread = datlen;
while (datlen && datlen >= chain->off) {
memcpy(data, chain->buffer + chain->misalign, chain->off);
data += chain->off;
datlen -= chain->off;
while (datlen && datlen >= chain->off - pos_in_chain) {
size_t copylen = chain->off - pos_in_chain;
memcpy(data,
chain->buffer + chain->misalign + pos_in_chain,
copylen);
data += copylen;
datlen -= copylen;
chain = chain->next;
pos_in_chain = 0;
EVUTIL_ASSERT(chain || datlen==0);
}
if (datlen) {
EVUTIL_ASSERT(chain);
memcpy(data, chain->buffer + chain->misalign, datlen);
memcpy(data, chain->buffer + chain->misalign + pos_in_chain,
datlen);
}
result = nread;

View File

@ -352,6 +352,20 @@ int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);
*/
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen);
/**
Read data from the middle of an evbuffer, and leave the buffer unchanged.
If more bytes are requested than are available in the evbuffer, we
only extract as many bytes as were available.
@param buf the evbuffer to be read from
@param pos the position to start reading from
@param data_out 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_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen);
/**
Read data from an evbuffer into another evbuffer, draining
the bytes from the source buffer. This function avoids copy

View File

@ -1897,6 +1897,87 @@ end:
}
}
static void
test_evbuffer_copyout(void *dummy)
{
const char string[] =
"Still they skirmish to and fro, men my messmates on the snow "
"When we headed off the aurochs turn for turn; "
"When the rich Allobrogenses never kept amanuenses, "
"And our only plots were piled in lakes at Berne.";
/* -- Kipling, "In The Neolithic Age" */
char tmp[256];
struct evbuffer_ptr ptr;
struct evbuffer *buf;
(void)dummy;
buf = evbuffer_new();
tt_assert(buf);
tt_int_op(strlen(string), ==, 206);
/* Ensure separate chains */
evbuffer_add_reference(buf, string, 80, no_cleanup, NULL);
evbuffer_add_reference(buf, string+80, 80, no_cleanup, NULL);
evbuffer_add(buf, string+160, strlen(string)-160);
tt_int_op(206, ==, evbuffer_get_length(buf));
/* First, let's test plain old copyout. */
/* Copy a little from the beginning. */
tt_int_op(10, ==, evbuffer_copyout(buf, tmp, 10));
tt_int_op(0, ==, memcmp(tmp, "Still they", 10));
/* Now copy more than a little from the beginning */
memset(tmp, 0, sizeof(tmp));
tt_int_op(100, ==, evbuffer_copyout(buf, tmp, 100));
tt_int_op(0, ==, memcmp(tmp, string, 100));
/* Copy too much; ensure truncation. */
memset(tmp, 0, sizeof(tmp));
tt_int_op(206, ==, evbuffer_copyout(buf, tmp, 230));
tt_int_op(0, ==, memcmp(tmp, string, 206));
/* That was supposed to be nondestructive, btw */
tt_int_op(206, ==, evbuffer_get_length(buf));
/* Now it's time to test copyout_from! First, let's start in the
* first chain. */
evbuffer_ptr_set(buf, &ptr, 15, EVBUFFER_PTR_SET);
memset(tmp, 0, sizeof(tmp));
tt_int_op(10, ==, evbuffer_copyout_from(buf, &ptr, tmp, 10));
tt_int_op(0, ==, memcmp(tmp, "mish to an", 10));
/* Right up to the end of the first chain */
memset(tmp, 0, sizeof(tmp));
tt_int_op(65, ==, evbuffer_copyout_from(buf, &ptr, tmp, 65));
tt_int_op(0, ==, memcmp(tmp, string+15, 65));
/* Span into the second chain */
memset(tmp, 0, sizeof(tmp));
tt_int_op(90, ==, evbuffer_copyout_from(buf, &ptr, tmp, 90));
tt_int_op(0, ==, memcmp(tmp, string+15, 90));
/* Span into the third chain */
memset(tmp, 0, sizeof(tmp));
tt_int_op(160, ==, evbuffer_copyout_from(buf, &ptr, tmp, 160));
tt_int_op(0, ==, memcmp(tmp, string+15, 160));
/* Overrun */
memset(tmp, 0, sizeof(tmp));
tt_int_op(206-15, ==, evbuffer_copyout_from(buf, &ptr, tmp, 999));
tt_int_op(0, ==, memcmp(tmp, string+15, 206-15));
/* That was supposed to be nondestructive, too */
tt_int_op(206, ==, evbuffer_get_length(buf));
end:
if (buf)
evbuffer_free(buf);
}
static void *
setup_passthrough(const struct testcase_t *testcase)
{
@ -1936,6 +2017,7 @@ struct testcase_t evbuffer_testcases[] = {
{ "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" },
{ "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" },
{ "add_iovec", test_evbuffer_add_iovec, 0, NULL, NULL},
{ "copyout", test_evbuffer_copyout, 0, NULL, NULL},
#define ADDFILE_TEST(name, parameters) \
{ name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \