mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-12 05:48:51 -04:00
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:
parent
35c5c9558a
commit
27e222559a
37
buffer.c
37
buffer.c
@ -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);
|
||||
|
||||
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;
|
||||
|
||||
if (datlen >= buf->total_len)
|
||||
pos_in_chain = 0;
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -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, \
|
||||
|
Loading…
x
Reference in New Issue
Block a user