mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-17 08:17:42 -04:00
Merge branch 'evbuffer_insert_point'
This commit is contained in:
commit
6c83e6c972
395
buffer.c
395
buffer.c
@ -219,18 +219,20 @@ evbuffer_chain_insert(struct evbuffer *buf, struct evbuffer_chain *chain)
|
|||||||
ASSERT_EVBUFFER_LOCKED(buf);
|
ASSERT_EVBUFFER_LOCKED(buf);
|
||||||
if (buf->first == NULL) {
|
if (buf->first == NULL) {
|
||||||
buf->first = buf->last = chain;
|
buf->first = buf->last = chain;
|
||||||
buf->previous_to_last = NULL;
|
buf->last_with_data = chain;
|
||||||
} else {
|
} else {
|
||||||
/* the last chain is empty so we can just drop it */
|
/* the last chain is empty so we can just drop it */
|
||||||
if (buf->last->off == 0 && !CHAIN_PINNED(buf->last)) {
|
if (buf->last->off == 0 && !CHAIN_PINNED(buf->last)) {
|
||||||
|
if (buf->last_with_data == buf->last)
|
||||||
|
buf->last_with_data = chain;
|
||||||
evbuffer_chain_free(buf->last);
|
evbuffer_chain_free(buf->last);
|
||||||
buf->previous_to_last->next = chain;
|
|
||||||
buf->last = chain;
|
buf->last = chain;
|
||||||
} else {
|
} else {
|
||||||
buf->previous_to_last = buf->last;
|
|
||||||
buf->last->next = chain;
|
buf->last->next = chain;
|
||||||
buf->last = chain;
|
buf->last = chain;
|
||||||
}
|
}
|
||||||
|
if (chain->off)
|
||||||
|
buf->last_with_data = chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf->total_len += chain->off;
|
buf->total_len += chain->off;
|
||||||
@ -520,9 +522,9 @@ evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
|
|||||||
vec[0].iov_len = CHAIN_SPACE_LEN(chain);
|
vec[0].iov_len = CHAIN_SPACE_LEN(chain);
|
||||||
n = 1;
|
n = 1;
|
||||||
} else {
|
} else {
|
||||||
if (_evbuffer_expand_fast(buf, size)<0)
|
if (_evbuffer_expand_fast(buf, size, n_vecs)<0)
|
||||||
goto done;
|
goto done;
|
||||||
n = _evbuffer_read_setup_vecs(buf, size, vec, &chain, 0);
|
n = _evbuffer_read_setup_vecs(buf, size, vec, n_vecs, &chain, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -531,50 +533,81 @@ done:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
advance_last_with_data(struct evbuffer *buf)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
ASSERT_EVBUFFER_LOCKED(buf);
|
||||||
|
|
||||||
|
if (!buf->last_with_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (buf->last_with_data->next && buf->last_with_data->next->off) {
|
||||||
|
buf->last_with_data = buf->last_with_data->next;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
evbuffer_commit_space(struct evbuffer *buf,
|
evbuffer_commit_space(struct evbuffer *buf,
|
||||||
struct evbuffer_iovec *vec, int n_vecs)
|
struct evbuffer_iovec *vec, int n_vecs)
|
||||||
{
|
{
|
||||||
struct evbuffer_chain *last, *prev;
|
struct evbuffer_chain *firstchain, *chain;
|
||||||
int result = -1;
|
int result = -1;
|
||||||
size_t added;
|
size_t added = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
EVBUFFER_LOCK(buf);
|
EVBUFFER_LOCK(buf);
|
||||||
|
|
||||||
prev = buf->previous_to_last;
|
|
||||||
last = buf->last;
|
|
||||||
|
|
||||||
if (buf->freeze_end)
|
if (buf->freeze_end)
|
||||||
goto done;
|
goto done;
|
||||||
if (n_vecs < 1 || n_vecs > 2)
|
if (n_vecs == 0) {
|
||||||
|
result = 0;
|
||||||
goto done;
|
goto done;
|
||||||
if (n_vecs == 2) {
|
} else if (n_vecs == 1 &&
|
||||||
if (!prev || !last ||
|
(buf->last && vec[0].iov_base == CHAIN_SPACE_PTR(buf->last))) {
|
||||||
vec[0].iov_base != CHAIN_SPACE_PTR(prev) ||
|
/* The user only got or used one chain; it might not
|
||||||
vec[1].iov_base != CHAIN_SPACE_PTR(last) ||
|
* be the first one with space in it. */
|
||||||
vec[0].iov_len > CHAIN_SPACE_LEN(prev) ||
|
if (vec[0].iov_len > CHAIN_SPACE_LEN(buf->last))
|
||||||
vec[1].iov_len > CHAIN_SPACE_LEN(last))
|
|
||||||
goto done;
|
goto done;
|
||||||
|
buf->last->off += vec[0].iov_len;
|
||||||
prev->off += vec[0].iov_len;
|
|
||||||
last->off += vec[1].iov_len;
|
|
||||||
added = vec[0].iov_len + vec[1].iov_len;
|
|
||||||
} else {
|
|
||||||
/* n_vecs == 1 */
|
|
||||||
struct evbuffer_chain *chain;
|
|
||||||
if (prev && vec[0].iov_base == CHAIN_SPACE_PTR(prev))
|
|
||||||
chain = prev;
|
|
||||||
else if (last && vec[0].iov_base == CHAIN_SPACE_PTR(last))
|
|
||||||
chain = last;
|
|
||||||
else
|
|
||||||
goto done;
|
|
||||||
if (vec[0].iov_len > CHAIN_SPACE_LEN(chain))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
chain->off += vec[0].iov_len;
|
|
||||||
added = vec[0].iov_len;
|
added = vec[0].iov_len;
|
||||||
|
if (added)
|
||||||
|
buf->last_with_data = buf->last;
|
||||||
|
goto okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Advance 'firstchain' to the first chain with space in it. */
|
||||||
|
firstchain = buf->last_with_data;
|
||||||
|
if (!firstchain)
|
||||||
|
goto done;
|
||||||
|
if (CHAIN_SPACE_LEN(firstchain) == 0) {
|
||||||
|
firstchain = firstchain->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain = firstchain;
|
||||||
|
/* pass 1: make sure that the pointers and lengths of vecs[] are in
|
||||||
|
* bounds before we try to commit anything. */
|
||||||
|
for (i=0; i<n_vecs; ++i) {
|
||||||
|
if (!chain)
|
||||||
|
goto done;
|
||||||
|
if (vec[i].iov_base != CHAIN_SPACE_PTR(chain) ||
|
||||||
|
vec[i].iov_len > CHAIN_SPACE_LEN(chain))
|
||||||
|
goto done;
|
||||||
|
chain = chain->next;
|
||||||
|
}
|
||||||
|
/* pass 2: actually adjust all the chains. */
|
||||||
|
chain = firstchain;
|
||||||
|
for (i=0; i<n_vecs; ++i) {
|
||||||
|
chain->off += vec[i].iov_len;
|
||||||
|
added += vec[i].iov_len;
|
||||||
|
if (vec[i].iov_len)
|
||||||
|
buf->last_with_data = chain;
|
||||||
|
chain = chain->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
okay:
|
||||||
buf->total_len += added;
|
buf->total_len += added;
|
||||||
buf->n_add_for_cb += added;
|
buf->n_add_for_cb += added;
|
||||||
result = 0;
|
result = 0;
|
||||||
@ -589,7 +622,7 @@ done:
|
|||||||
ASSERT_EVBUFFER_LOCKED(dst); \
|
ASSERT_EVBUFFER_LOCKED(dst); \
|
||||||
(dst)->first = NULL; \
|
(dst)->first = NULL; \
|
||||||
(dst)->last = NULL; \
|
(dst)->last = NULL; \
|
||||||
(dst)->previous_to_last = NULL; \
|
(dst)->last_with_data = NULL; \
|
||||||
(dst)->total_len = 0; \
|
(dst)->total_len = 0; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
@ -597,7 +630,7 @@ done:
|
|||||||
ASSERT_EVBUFFER_LOCKED(dst); \
|
ASSERT_EVBUFFER_LOCKED(dst); \
|
||||||
ASSERT_EVBUFFER_LOCKED(src); \
|
ASSERT_EVBUFFER_LOCKED(src); \
|
||||||
(dst)->first = (src)->first; \
|
(dst)->first = (src)->first; \
|
||||||
(dst)->previous_to_last = (src)->previous_to_last; \
|
(dst)->last_with_data = (src)->last_with_data; \
|
||||||
(dst)->last = (src)->last; \
|
(dst)->last = (src)->last; \
|
||||||
(dst)->total_len = (src)->total_len; \
|
(dst)->total_len = (src)->total_len; \
|
||||||
} while (0)
|
} while (0)
|
||||||
@ -606,8 +639,8 @@ done:
|
|||||||
ASSERT_EVBUFFER_LOCKED(dst); \
|
ASSERT_EVBUFFER_LOCKED(dst); \
|
||||||
ASSERT_EVBUFFER_LOCKED(src); \
|
ASSERT_EVBUFFER_LOCKED(src); \
|
||||||
(dst)->last->next = (src)->first; \
|
(dst)->last->next = (src)->first; \
|
||||||
(dst)->previous_to_last = (src)->previous_to_last ? \
|
if ((src)->last_with_data) \
|
||||||
(src)->previous_to_last : (dst)->last; \
|
(dst)->last_with_data = (src)->last_with_data; \
|
||||||
(dst)->last = (src)->last; \
|
(dst)->last = (src)->last; \
|
||||||
(dst)->total_len += (src)->total_len; \
|
(dst)->total_len += (src)->total_len; \
|
||||||
} while (0)
|
} while (0)
|
||||||
@ -618,11 +651,10 @@ done:
|
|||||||
(src)->last->next = (dst)->first; \
|
(src)->last->next = (dst)->first; \
|
||||||
(dst)->first = (src)->first; \
|
(dst)->first = (src)->first; \
|
||||||
(dst)->total_len += (src)->total_len; \
|
(dst)->total_len += (src)->total_len; \
|
||||||
if ((dst)->previous_to_last == NULL) \
|
if ((dst)->last_with_data == NULL) \
|
||||||
(dst)->previous_to_last = (src)->last; \
|
(dst)->last_with_data = (src)->last_with_data; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
|
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
|
||||||
{
|
{
|
||||||
@ -734,6 +766,8 @@ evbuffer_drain(struct evbuffer *buf, size_t len)
|
|||||||
for (chain = buf->first; len >= chain->off; chain = next) {
|
for (chain = buf->first; len >= chain->off; chain = next) {
|
||||||
next = chain->next;
|
next = chain->next;
|
||||||
len -= chain->off;
|
len -= chain->off;
|
||||||
|
if (chain == buf->last_with_data)
|
||||||
|
buf->last_with_data = next;
|
||||||
|
|
||||||
if (len == 0 && CHAIN_PINNED_R(chain))
|
if (len == 0 && CHAIN_PINNED_R(chain))
|
||||||
break;
|
break;
|
||||||
@ -741,8 +775,6 @@ evbuffer_drain(struct evbuffer *buf, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf->first = chain;
|
buf->first = chain;
|
||||||
if (buf->first == buf->last)
|
|
||||||
buf->previous_to_last = NULL;
|
|
||||||
chain->misalign += len;
|
chain->misalign += len;
|
||||||
chain->off -= len;
|
chain->off -= len;
|
||||||
}
|
}
|
||||||
@ -789,6 +821,9 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
|
|||||||
data += chain->off;
|
data += chain->off;
|
||||||
datlen -= chain->off;
|
datlen -= chain->off;
|
||||||
|
|
||||||
|
if (chain == buf->last_with_data)
|
||||||
|
buf->last_with_data = chain->next;
|
||||||
|
|
||||||
tmp = chain;
|
tmp = chain;
|
||||||
chain = chain->next;
|
chain = chain->next;
|
||||||
evbuffer_chain_free(tmp);
|
evbuffer_chain_free(tmp);
|
||||||
@ -797,8 +832,6 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
|
|||||||
buf->first = chain;
|
buf->first = chain;
|
||||||
if (chain == NULL)
|
if (chain == NULL)
|
||||||
buf->last = NULL;
|
buf->last = NULL;
|
||||||
if (buf->first == buf->last)
|
|
||||||
buf->previous_to_last = NULL;
|
|
||||||
|
|
||||||
if (datlen) {
|
if (datlen) {
|
||||||
memcpy(data, chain->buffer + chain->misalign, datlen);
|
memcpy(data, chain->buffer + chain->misalign, datlen);
|
||||||
@ -827,7 +860,7 @@ evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
|
|||||||
/*XXX We should have an option to force this to be zero-copy.*/
|
/*XXX We should have an option to force this to be zero-copy.*/
|
||||||
|
|
||||||
/*XXX can fail badly on sendfile case. */
|
/*XXX can fail badly on sendfile case. */
|
||||||
struct evbuffer_chain *chain, *previous, *previous_to_previous = NULL;
|
struct evbuffer_chain *chain, *previous;
|
||||||
size_t nread = 0;
|
size_t nread = 0;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
@ -855,9 +888,12 @@ evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
|
|||||||
|
|
||||||
/* removes chains if possible */
|
/* removes chains if possible */
|
||||||
while (chain->off <= datlen) {
|
while (chain->off <= datlen) {
|
||||||
|
/* We can't remove the last with data from src unless we
|
||||||
|
* remove all chains, in which case we would have done the if
|
||||||
|
* block above */
|
||||||
|
EVUTIL_ASSERT(chain != src->last_with_data);
|
||||||
nread += chain->off;
|
nread += chain->off;
|
||||||
datlen -= chain->off;
|
datlen -= chain->off;
|
||||||
previous_to_previous = previous;
|
|
||||||
previous = chain;
|
previous = chain;
|
||||||
chain = chain->next;
|
chain = chain->next;
|
||||||
}
|
}
|
||||||
@ -869,12 +905,10 @@ evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
|
|||||||
} else {
|
} else {
|
||||||
dst->last->next = src->first;
|
dst->last->next = src->first;
|
||||||
}
|
}
|
||||||
dst->previous_to_last = previous_to_previous;
|
|
||||||
dst->last = previous;
|
dst->last = previous;
|
||||||
|
dst->last_with_data = dst->last;
|
||||||
previous->next = NULL;
|
previous->next = NULL;
|
||||||
src->first = chain;
|
src->first = chain;
|
||||||
if (src->first == src->last)
|
|
||||||
src->previous_to_last = NULL;
|
|
||||||
|
|
||||||
dst->total_len += nread;
|
dst->total_len += nread;
|
||||||
dst->n_add_for_cb += nread;
|
dst->n_add_for_cb += nread;
|
||||||
@ -907,6 +941,7 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
|
|||||||
struct evbuffer_chain *chain, *next, *tmp;
|
struct evbuffer_chain *chain, *next, *tmp;
|
||||||
unsigned char *buffer, *result = NULL;
|
unsigned char *buffer, *result = NULL;
|
||||||
ev_ssize_t remaining;
|
ev_ssize_t remaining;
|
||||||
|
int removed_last_with_data = 0;
|
||||||
|
|
||||||
EVBUFFER_LOCK(buf);
|
EVBUFFER_LOCK(buf);
|
||||||
|
|
||||||
@ -976,6 +1011,8 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
|
|||||||
memcpy(buffer, chain->buffer + chain->misalign, chain->off);
|
memcpy(buffer, chain->buffer + chain->misalign, chain->off);
|
||||||
size -= chain->off;
|
size -= chain->off;
|
||||||
buffer += chain->off;
|
buffer += chain->off;
|
||||||
|
if (chain == buf->last_with_data)
|
||||||
|
removed_last_with_data = 1;
|
||||||
|
|
||||||
evbuffer_chain_free(chain);
|
evbuffer_chain_free(chain);
|
||||||
}
|
}
|
||||||
@ -984,16 +1021,19 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
|
|||||||
memcpy(buffer, chain->buffer + chain->misalign, size);
|
memcpy(buffer, chain->buffer + chain->misalign, size);
|
||||||
chain->misalign += size;
|
chain->misalign += size;
|
||||||
chain->off -= size;
|
chain->off -= size;
|
||||||
if (chain == buf->last)
|
|
||||||
buf->previous_to_last = tmp;
|
|
||||||
} else {
|
} else {
|
||||||
buf->last = tmp;
|
buf->last = tmp;
|
||||||
/* the last is already the first, so we have no previous */
|
|
||||||
buf->previous_to_last = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp->next = chain;
|
tmp->next = chain;
|
||||||
|
|
||||||
|
if (removed_last_with_data) {
|
||||||
|
int n;
|
||||||
|
buf->last_with_data = buf->first;
|
||||||
|
n = advance_last_with_data(buf);
|
||||||
|
EVUTIL_ASSERT(n == 0);
|
||||||
|
}
|
||||||
|
|
||||||
result = (tmp->buffer + tmp->misalign);
|
result = (tmp->buffer + tmp->misalign);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -1380,8 +1420,11 @@ evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen)
|
|||||||
if ((tmp = evbuffer_chain_new(datlen)) == NULL)
|
if ((tmp = evbuffer_chain_new(datlen)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
buf->first = tmp;
|
buf->first = tmp;
|
||||||
if (buf->previous_to_last == NULL)
|
if (buf->last_with_data == NULL)
|
||||||
buf->previous_to_last = tmp;
|
buf->last_with_data = tmp;
|
||||||
|
else if (chain && buf->last_with_data == chain && 0==chain->off)
|
||||||
|
buf->last_with_data = tmp;
|
||||||
|
|
||||||
tmp->next = chain;
|
tmp->next = chain;
|
||||||
|
|
||||||
tmp->off = datlen;
|
tmp->off = datlen;
|
||||||
@ -1462,9 +1505,9 @@ evbuffer_expand(struct evbuffer *buf, size_t datlen)
|
|||||||
/* fix up the chain */
|
/* fix up the chain */
|
||||||
if (buf->first == chain)
|
if (buf->first == chain)
|
||||||
buf->first = tmp;
|
buf->first = tmp;
|
||||||
if (buf->previous_to_last)
|
|
||||||
buf->previous_to_last->next = tmp;
|
|
||||||
buf->last = tmp;
|
buf->last = tmp;
|
||||||
|
if (buf->last->off || buf->last_with_data == chain)
|
||||||
|
buf->last_with_data = tmp;
|
||||||
|
|
||||||
evbuffer_chain_free(chain);
|
evbuffer_chain_free(chain);
|
||||||
|
|
||||||
@ -1475,17 +1518,21 @@ err:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure that datlen bytes are available for writing in the last two
|
/* Make sure that datlen bytes are available for writing in the last n
|
||||||
* chains. Never copies or moves data. */
|
* chains. Never copies or moves data. */
|
||||||
int
|
int
|
||||||
_evbuffer_expand_fast(struct evbuffer *buf, size_t datlen)
|
_evbuffer_expand_fast(struct evbuffer *buf, size_t datlen, int n)
|
||||||
{
|
{
|
||||||
struct evbuffer_chain *chain = buf->last, *tmp;
|
struct evbuffer_chain *chain = buf->last, *tmp, *next;
|
||||||
size_t avail, avail_in_prev = 0;
|
size_t avail;
|
||||||
|
int used;
|
||||||
|
|
||||||
ASSERT_EVBUFFER_LOCKED(buf);
|
ASSERT_EVBUFFER_LOCKED(buf);
|
||||||
|
EVUTIL_ASSERT(n >= 2);
|
||||||
|
|
||||||
if (chain == NULL || (chain->flags & EVBUFFER_IMMUTABLE)) {
|
if (chain == NULL || (chain->flags & EVBUFFER_IMMUTABLE)) {
|
||||||
|
/* There is no last chunk, or we can't touch the last chunk.
|
||||||
|
* Just add a new chunk. */
|
||||||
chain = evbuffer_chain_new(datlen);
|
chain = evbuffer_chain_new(datlen);
|
||||||
if (chain == NULL)
|
if (chain == NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
@ -1494,56 +1541,89 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* How many bytes can we stick at the end of chain? */
|
used = 0; /* number of chains we're using space in. */
|
||||||
|
avail = 0; /* how much space they have. */
|
||||||
if (chain->off) {
|
/* How many bytes can we stick at the end of buffer as it is? Iterate
|
||||||
avail = chain->buffer_len - (chain->off + chain->misalign);
|
* over the chains at the end of the buffer, tring to see how much
|
||||||
avail_in_prev = 0;
|
* space we have in the first n. */
|
||||||
} else {
|
for (chain = buf->last_with_data; chain; chain = chain->next) {
|
||||||
/* No data in chain; realign it. */
|
if (chain->off) {
|
||||||
chain->misalign = 0;
|
size_t space = CHAIN_SPACE_LEN(chain);
|
||||||
avail = chain->buffer_len;
|
EVUTIL_ASSERT(chain == buf->last_with_data);
|
||||||
/* Can we stick some data in the penultimate chain? */
|
if (space) {
|
||||||
if (buf->previous_to_last) {
|
avail += space;
|
||||||
struct evbuffer_chain *prev = buf->previous_to_last;
|
++used;
|
||||||
avail_in_prev = CHAIN_SPACE_LEN(prev);
|
}
|
||||||
|
} else {
|
||||||
|
/* No data in chain; realign it. */
|
||||||
|
chain->misalign = 0;
|
||||||
|
avail += chain->buffer_len;
|
||||||
|
++used;
|
||||||
}
|
}
|
||||||
|
if (avail >= datlen) {
|
||||||
|
/* There is already enough space. Just return */
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (used == n)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we can fit all the data, then we don't have to do anything */
|
/* There wasn't enough space in the first n chains with space in
|
||||||
if (avail+avail_in_prev >= datlen)
|
* them. Either add a new chain with enough space, or replace all
|
||||||
return (0);
|
* empty chains with one that has enough space, depending on n. */
|
||||||
|
if (used < n) {
|
||||||
|
/* The loop ran off the end of the chains before it hit n
|
||||||
|
* chains; we can add another. */
|
||||||
|
EVUTIL_ASSERT(chain == NULL);
|
||||||
|
|
||||||
/* Otherwise, we need a bigger chunk. */
|
tmp = evbuffer_chain_new(datlen - avail);
|
||||||
if (chain->off == 0) {
|
|
||||||
/* If there are no bytes on this chain, free it and
|
|
||||||
replace it with a better one. */
|
|
||||||
/* XXX round up. */
|
|
||||||
tmp = evbuffer_chain_new(datlen-avail_in_prev);
|
|
||||||
if (tmp == NULL)
|
|
||||||
return -1;
|
|
||||||
/* XXX write functions to in new chains */
|
|
||||||
if (buf->first == chain)
|
|
||||||
buf->first = tmp;
|
|
||||||
if (buf->previous_to_last)
|
|
||||||
buf->previous_to_last->next = tmp;
|
|
||||||
buf->last = tmp;
|
|
||||||
evbuffer_chain_free(chain);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* Add a new chunk big enough to hold what won't fit
|
|
||||||
* in chunk. */
|
|
||||||
/*XXX round this up. */
|
|
||||||
tmp = evbuffer_chain_new(datlen-avail);
|
|
||||||
if (tmp == NULL)
|
if (tmp == NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
buf->previous_to_last = chain;
|
buf->last->next = tmp;
|
||||||
chain->next = tmp;
|
|
||||||
buf->last = tmp;
|
buf->last = tmp;
|
||||||
}
|
/* (we would only set last_with_data if we added the first
|
||||||
|
* chain. But if the buffer had no chains, we would have
|
||||||
|
* just allocated a new chain earlier) */
|
||||||
|
return (0);
|
||||||
|
} else {
|
||||||
|
/* Nuke _all_ the empty chains. */
|
||||||
|
int rmv_all = 0; /* True iff we removed last_with_data. */
|
||||||
|
chain = buf->last_with_data;
|
||||||
|
if (!chain->off) {
|
||||||
|
EVUTIL_ASSERT(chain == buf->first);
|
||||||
|
rmv_all = 1;
|
||||||
|
avail = 0;
|
||||||
|
} else {
|
||||||
|
avail = CHAIN_SPACE_LEN(chain);
|
||||||
|
chain = chain->next;
|
||||||
|
}
|
||||||
|
|
||||||
return (0);
|
|
||||||
|
for (; chain; chain = next) {
|
||||||
|
next = chain->next;
|
||||||
|
EVUTIL_ASSERT(chain->off == 0);
|
||||||
|
evbuffer_chain_free(chain);
|
||||||
|
}
|
||||||
|
tmp = evbuffer_chain_new(datlen - avail);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
if (rmv_all) {
|
||||||
|
ZERO_CHAIN(buf);
|
||||||
|
} else {
|
||||||
|
buf->last = buf->last_with_data;
|
||||||
|
buf->last_with_data->next = NULL;
|
||||||
|
}
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmv_all) {
|
||||||
|
buf->first = buf->last = buf->last_with_data = tmp;
|
||||||
|
} else {
|
||||||
|
buf->last_with_data->next = tmp;
|
||||||
|
buf->last = tmp;
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1559,17 +1639,18 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen)
|
|||||||
#ifdef _EVENT_HAVE_SYS_UIO_H
|
#ifdef _EVENT_HAVE_SYS_UIO_H
|
||||||
/* number of iovec we use for writev, fragmentation is going to determine
|
/* number of iovec we use for writev, fragmentation is going to determine
|
||||||
* how much we end up writing */
|
* how much we end up writing */
|
||||||
#define NUM_IOVEC 128
|
#define NUM_WRITE_IOVEC 128
|
||||||
#define IOV_TYPE struct iovec
|
#define IOV_TYPE struct iovec
|
||||||
#define IOV_PTR_FIELD iov_base
|
#define IOV_PTR_FIELD iov_base
|
||||||
#define IOV_LEN_FIELD iov_len
|
#define IOV_LEN_FIELD iov_len
|
||||||
#else
|
#else
|
||||||
#define NUM_IOVEC 16
|
#define NUM_WRITE_IOVEC 16
|
||||||
#define IOV_TYPE WSABUF
|
#define IOV_TYPE WSABUF
|
||||||
#define IOV_PTR_FIELD buf
|
#define IOV_PTR_FIELD buf
|
||||||
#define IOV_LEN_FIELD len
|
#define IOV_LEN_FIELD len
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#define NUM_READ_IOVEC 4
|
||||||
|
|
||||||
#define EVBUFFER_MAX_READ 4096
|
#define EVBUFFER_MAX_READ 4096
|
||||||
|
|
||||||
@ -1578,7 +1659,8 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen)
|
|||||||
|
|
||||||
@param buf The buffer to read into
|
@param buf The buffer to read into
|
||||||
@param howmuch How much we want to read.
|
@param howmuch How much we want to read.
|
||||||
@param vecs An array of two iovecs or WSABUFs.
|
@param vecs An array of two or more iovecs or WSABUFs.
|
||||||
|
@param n_vecs_avail The length of vecs
|
||||||
@param chainp A pointer to a variable to hold the first chain we're
|
@param chainp A pointer to a variable to hold the first chain we're
|
||||||
reading into.
|
reading into.
|
||||||
@param exact Boolean: if true, we do not provide more than 'howmuch'
|
@param exact Boolean: if true, we do not provide more than 'howmuch'
|
||||||
@ -1587,52 +1669,36 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen)
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
_evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
|
_evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
|
||||||
struct evbuffer_iovec *vecs, struct evbuffer_chain **chainp, int exact)
|
struct evbuffer_iovec *vecs, int n_vecs_avail,
|
||||||
|
struct evbuffer_chain **chainp, int exact)
|
||||||
{
|
{
|
||||||
struct evbuffer_chain *chain;
|
struct evbuffer_chain *chain, *firstchain;
|
||||||
int nvecs;
|
size_t so_far;
|
||||||
|
int i;
|
||||||
|
ASSERT_EVBUFFER_LOCKED(buf);
|
||||||
|
|
||||||
if (howmuch < 0)
|
if (howmuch < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
chain = buf->last;
|
so_far = 0;
|
||||||
|
/* Let firstchain be the first chain with any space on it */
|
||||||
|
firstchain = buf->last_with_data;
|
||||||
|
if (CHAIN_SPACE_LEN(firstchain) == 0)
|
||||||
|
firstchain = firstchain->next;
|
||||||
|
|
||||||
if (chain->off == 0 && buf->previous_to_last &&
|
chain = firstchain;
|
||||||
CHAIN_SPACE_LEN(buf->previous_to_last)) {
|
for (i = 0; i < n_vecs_avail && so_far < howmuch; ++i) {
|
||||||
/* The last chain is empty, so it's safe to
|
size_t avail = CHAIN_SPACE_LEN(chain);
|
||||||
use the space in the next-to-last chain.
|
if (avail > howmuch && exact)
|
||||||
*/
|
avail = howmuch;
|
||||||
struct evbuffer_chain *prev = buf->previous_to_last;
|
vecs[i].iov_base = CHAIN_SPACE_PTR(chain);
|
||||||
vecs[0].iov_base = CHAIN_SPACE_PTR(prev);
|
vecs[i].iov_len = avail;
|
||||||
vecs[0].iov_len = CHAIN_SPACE_LEN(prev);
|
so_far += avail;
|
||||||
vecs[1].iov_base = CHAIN_SPACE_PTR(chain);
|
chain = chain->next;
|
||||||
vecs[1].iov_len = CHAIN_SPACE_LEN(chain);
|
|
||||||
if (vecs[0].iov_len >= (size_t)howmuch) {
|
|
||||||
/* The next-to-last chain has enough
|
|
||||||
* space on its own. */
|
|
||||||
chain = prev;
|
|
||||||
nvecs = 1;
|
|
||||||
} else {
|
|
||||||
/* We'll need both chains. */
|
|
||||||
chain = prev;
|
|
||||||
nvecs = 2;
|
|
||||||
if (exact &&
|
|
||||||
(vecs[0].iov_len + vecs[1].iov_len > (size_t)howmuch)) {
|
|
||||||
vecs[1].iov_len = howmuch - vecs[0].iov_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* There's data in the last chain, so we're
|
|
||||||
* not allowed to use the next-to-last. */
|
|
||||||
nvecs = 1;
|
|
||||||
vecs[0].iov_base = CHAIN_SPACE_PTR(chain);
|
|
||||||
vecs[0].iov_len = CHAIN_SPACE_LEN(chain);
|
|
||||||
if (exact && (vecs[0].iov_len > (size_t)howmuch))
|
|
||||||
vecs[0].iov_len = howmuch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*chainp = chain;
|
*chainp = firstchain;
|
||||||
return nvecs;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO(niels): should this function return ev_ssize_t and take ev_ssize_t
|
/* TODO(niels): should this function return ev_ssize_t and take ev_ssize_t
|
||||||
@ -1645,7 +1711,7 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
|
|||||||
int result;
|
int result;
|
||||||
|
|
||||||
#ifdef USE_IOVEC_IMPL
|
#ifdef USE_IOVEC_IMPL
|
||||||
int nvecs;
|
int nvecs, i, remaining;
|
||||||
#else
|
#else
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
#endif
|
#endif
|
||||||
@ -1688,28 +1754,24 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
|
|||||||
|
|
||||||
#ifdef USE_IOVEC_IMPL
|
#ifdef USE_IOVEC_IMPL
|
||||||
/* Since we can use iovecs, we're willing to use the last
|
/* Since we can use iovecs, we're willing to use the last
|
||||||
* _two_ chains. */
|
* NUM_READ_IOVEC chains. */
|
||||||
if (_evbuffer_expand_fast(buf, howmuch) == -1) {
|
if (_evbuffer_expand_fast(buf, howmuch, NUM_READ_IOVEC) == -1) {
|
||||||
result = -1;
|
result = -1;
|
||||||
goto done;
|
goto done;
|
||||||
} else {
|
} else {
|
||||||
IOV_TYPE vecs[2];
|
IOV_TYPE vecs[NUM_READ_IOVEC];
|
||||||
#ifdef _EVBUFFER_IOVEC_IS_NATIVE
|
#ifdef _EVBUFFER_IOVEC_IS_NATIVE
|
||||||
nvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,
|
nvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,
|
||||||
&chain, 1);
|
NUM_READ_IOVEC, &chain, 1);
|
||||||
#else
|
#else
|
||||||
/* We aren't using the native struct iovec. Therefore,
|
/* We aren't using the native struct iovec. Therefore,
|
||||||
we are on win32. */
|
we are on win32. */
|
||||||
struct evbuffer_iovec ev_vecs[2];
|
struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];
|
||||||
nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs,
|
nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs, 2,
|
||||||
&chain, 1);
|
&chain, 1);
|
||||||
|
|
||||||
if (nvecs == 2) {
|
for (i=0; i < nvecs; ++i)
|
||||||
WSABUF_FROM_EVBUFFER_IOV(&vecs[1], &ev_vecs[1]);
|
WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
|
||||||
WSABUF_FROM_EVBUFFER_IOV(&vecs[0], &ev_vecs[0]);
|
|
||||||
} else if (nvecs == 1) {
|
|
||||||
WSABUF_FROM_EVBUFFER_IOV(&vecs[0], &ev_vecs[0]);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
@ -1762,19 +1824,22 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_IOVEC_IMPL
|
#ifdef USE_IOVEC_IMPL
|
||||||
if (nvecs == 2) {
|
remaining = n;
|
||||||
|
for (i=0; i < nvecs; ++i) {
|
||||||
ev_ssize_t space = CHAIN_SPACE_LEN(chain);
|
ev_ssize_t space = CHAIN_SPACE_LEN(chain);
|
||||||
if (space < n) {
|
if (space < remaining) {
|
||||||
chain->off += space;
|
chain->off += space;
|
||||||
chain->next->off += n-space;
|
remaining -= space;
|
||||||
} else {
|
} else {
|
||||||
chain->off += n;
|
chain->off += remaining;
|
||||||
|
buf->last_with_data = chain;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
chain = chain->next;
|
||||||
chain->off += n;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
chain->off += n;
|
chain->off += n;
|
||||||
|
buf->last_with_data = chain;
|
||||||
#endif
|
#endif
|
||||||
buf->total_len += n;
|
buf->total_len += n;
|
||||||
buf->n_add_for_cb += n;
|
buf->n_add_for_cb += n;
|
||||||
@ -1792,7 +1857,7 @@ static inline int
|
|||||||
evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
|
evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
|
||||||
ev_ssize_t howmuch)
|
ev_ssize_t howmuch)
|
||||||
{
|
{
|
||||||
IOV_TYPE iov[NUM_IOVEC];
|
IOV_TYPE iov[NUM_WRITE_IOVEC];
|
||||||
struct evbuffer_chain *chain = buffer->first;
|
struct evbuffer_chain *chain = buffer->first;
|
||||||
int n, i = 0;
|
int n, i = 0;
|
||||||
|
|
||||||
@ -1803,7 +1868,7 @@ evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
|
|||||||
/* XXX make this top out at some maximal data length? if the
|
/* XXX make this top out at some maximal data length? if the
|
||||||
* buffer has (say) 1MB in it, split over 128 chains, there's
|
* buffer has (say) 1MB in it, split over 128 chains, there's
|
||||||
* no way it all gets written in one go. */
|
* no way it all gets written in one go. */
|
||||||
while (chain != NULL && i < NUM_IOVEC && howmuch) {
|
while (chain != NULL && i < NUM_WRITE_IOVEC && howmuch) {
|
||||||
#ifdef USE_SENDFILE
|
#ifdef USE_SENDFILE
|
||||||
/* we cannot write the file info via writev */
|
/* we cannot write the file info via writev */
|
||||||
if (chain->flags & EVBUFFER_SENDFILE)
|
if (chain->flags & EVBUFFER_SENDFILE)
|
||||||
|
@ -249,12 +249,15 @@ evbuffer_launch_read(struct evbuffer *buf, size_t at_most,
|
|||||||
buf_o->n_buffers = 0;
|
buf_o->n_buffers = 0;
|
||||||
memset(buf_o->buffers, 0, sizeof(buf_o->buffers));
|
memset(buf_o->buffers, 0, sizeof(buf_o->buffers));
|
||||||
|
|
||||||
if (_evbuffer_expand_fast(buf, at_most) == -1)
|
if (_evbuffer_expand_fast(buf, at_most, 2) == -1)
|
||||||
goto done;
|
goto done;
|
||||||
evbuffer_freeze(buf, 0);
|
evbuffer_freeze(buf, 0);
|
||||||
|
|
||||||
|
/* XXX This and evbuffer_read_setup_vecs() should say MAX_WSABUFS,
|
||||||
|
* not "2". But commit_read() above can't handle more than two
|
||||||
|
* buffers yet. */
|
||||||
nvecs = _evbuffer_read_setup_vecs(buf, at_most,
|
nvecs = _evbuffer_read_setup_vecs(buf, at_most,
|
||||||
vecs, &chain, 1);
|
vecs, 2, &chain, 1);
|
||||||
for (i=0;i<nvecs;++i) {
|
for (i=0;i<nvecs;++i) {
|
||||||
WSABUF_FROM_EVBUFFER_IOV(
|
WSABUF_FROM_EVBUFFER_IOV(
|
||||||
&buf_o->buffers[i],
|
&buf_o->buffers[i],
|
||||||
|
@ -73,16 +73,11 @@ struct evbuffer {
|
|||||||
struct evbuffer_chain *first;
|
struct evbuffer_chain *first;
|
||||||
/** The last chain in this buffer's linked list of chains. */
|
/** The last chain in this buffer's linked list of chains. */
|
||||||
struct evbuffer_chain *last;
|
struct evbuffer_chain *last;
|
||||||
/** The next-to-last chain in this buffer's linked list of chains.
|
|
||||||
* NULL if the buffer has 0 or 1 chains. Used in case there's an
|
/** The last chain that has any data in it. If all chains in the
|
||||||
* ongoing read that needs to be split across multiple chains: we want
|
* buffer are empty, points to the first chain. If the buffer has no
|
||||||
* to add a new chain as a read target, but we don't want to lose our
|
* chains, this is NULL. */
|
||||||
* pointer to the next-to-last chain if the read turns out to be
|
struct evbuffer_chain *last_with_data;
|
||||||
* incomplete.
|
|
||||||
*/
|
|
||||||
/* FIXME: This should probably be called last_with_space and
|
|
||||||
* repurposed accordingly. */
|
|
||||||
struct evbuffer_chain *previous_to_last;
|
|
||||||
|
|
||||||
/** Total amount of bytes stored in all chains.*/
|
/** Total amount of bytes stored in all chains.*/
|
||||||
size_t total_len;
|
size_t total_len;
|
||||||
@ -234,8 +229,8 @@ void _evbuffer_chain_unpin(struct evbuffer_chain *chain, unsigned flag);
|
|||||||
void _evbuffer_decref_and_unlock(struct evbuffer *buffer);
|
void _evbuffer_decref_and_unlock(struct evbuffer *buffer);
|
||||||
|
|
||||||
/** As evbuffer_expand, but does not guarantee that the newly allocated memory
|
/** As evbuffer_expand, but does not guarantee that the newly allocated memory
|
||||||
* is contiguous. Instead, it may be split across two chunks. */
|
* is contiguous. Instead, it may be split across two or more chunks. */
|
||||||
int _evbuffer_expand_fast(struct evbuffer *, size_t);
|
int _evbuffer_expand_fast(struct evbuffer *, size_t, int);
|
||||||
|
|
||||||
/** Helper: prepares for a readv/WSARecv call by expanding the buffer to
|
/** Helper: prepares for a readv/WSARecv call by expanding the buffer to
|
||||||
* hold enough memory to read 'howmuch' bytes in possibly noncontiguous memory.
|
* hold enough memory to read 'howmuch' bytes in possibly noncontiguous memory.
|
||||||
@ -244,7 +239,7 @@ int _evbuffer_expand_fast(struct evbuffer *, size_t);
|
|||||||
* Returns the number of vecs used.
|
* Returns the number of vecs used.
|
||||||
*/
|
*/
|
||||||
int _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
|
int _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
|
||||||
struct evbuffer_iovec *vecs, struct evbuffer_chain **chainp, int exact);
|
struct evbuffer_iovec *vecs, int n_vecs, struct evbuffer_chain **chainp, int exact);
|
||||||
|
|
||||||
/* Helper macro: copies an evbuffer_iovec in ei to a win32 WSABUF in i. */
|
/* Helper macro: copies an evbuffer_iovec in ei to a win32 WSABUF in i. */
|
||||||
#define WSABUF_FROM_EVBUFFER_IOV(i,ei) do { \
|
#define WSABUF_FROM_EVBUFFER_IOV(i,ei) do { \
|
||||||
|
@ -66,31 +66,44 @@
|
|||||||
static int
|
static int
|
||||||
_evbuffer_validate(struct evbuffer *buf)
|
_evbuffer_validate(struct evbuffer *buf)
|
||||||
{
|
{
|
||||||
struct evbuffer_chain *chain, *previous = NULL;
|
struct evbuffer_chain *chain;
|
||||||
size_t sum = 0;
|
size_t sum = 0;
|
||||||
|
int found_last_with_data = 0;
|
||||||
|
|
||||||
if (buf->first == NULL) {
|
if (buf->first == NULL) {
|
||||||
tt_assert(buf->last == NULL);
|
tt_assert(buf->last == NULL);
|
||||||
tt_assert(buf->previous_to_last == NULL);
|
|
||||||
tt_assert(buf->total_len == 0);
|
tt_assert(buf->total_len == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf->previous_to_last == NULL) {
|
|
||||||
tt_assert(buf->first == buf->last);
|
|
||||||
}
|
|
||||||
|
|
||||||
chain = buf->first;
|
chain = buf->first;
|
||||||
while (chain != NULL) {
|
while (chain != NULL) {
|
||||||
|
if (chain == buf->last_with_data)
|
||||||
|
found_last_with_data = 1;
|
||||||
sum += chain->off;
|
sum += chain->off;
|
||||||
if (chain->next == NULL) {
|
if (chain->next == NULL) {
|
||||||
tt_assert(buf->previous_to_last == previous);
|
|
||||||
tt_assert(buf->last == chain);
|
tt_assert(buf->last == chain);
|
||||||
}
|
}
|
||||||
tt_assert(chain->buffer_len >= chain->misalign + chain->off);
|
tt_assert(chain->buffer_len >= chain->misalign + chain->off);
|
||||||
previous = chain;
|
|
||||||
chain = chain->next;
|
chain = chain->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buf->first)
|
||||||
|
tt_assert(buf->last_with_data);
|
||||||
|
if (buf->last_with_data) {
|
||||||
|
tt_assert(found_last_with_data);
|
||||||
|
chain = buf->last_with_data;
|
||||||
|
if (chain->off == 0 || buf->total_len == 0) {
|
||||||
|
tt_assert(chain->off == 0)
|
||||||
|
tt_assert(chain == buf->first);
|
||||||
|
tt_assert(buf->total_len == 0);
|
||||||
|
}
|
||||||
|
chain = chain->next;
|
||||||
|
while (chain != NULL) {
|
||||||
|
tt_assert(chain->off == 0);
|
||||||
|
chain = chain->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tt_assert(sum == buf->total_len);
|
tt_assert(sum == buf->total_len);
|
||||||
return 1;
|
return 1;
|
||||||
end:
|
end:
|
||||||
@ -239,8 +252,10 @@ test_evbuffer_reserve2(void *ptr)
|
|||||||
cp = v[0].iov_base;
|
cp = v[0].iov_base;
|
||||||
remaining = v[0].iov_len - 512;
|
remaining = v[0].iov_len - 512;
|
||||||
v[0].iov_len = 512;
|
v[0].iov_len = 512;
|
||||||
|
evbuffer_validate(buf);
|
||||||
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
|
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
|
||||||
tt_int_op(evbuffer_get_length(buf), ==, 512);
|
tt_int_op(evbuffer_get_length(buf), ==, 512);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
/* Ask for another same-chunk request, in an existing chunk. Use 8
|
/* Ask for another same-chunk request, in an existing chunk. Use 8
|
||||||
* bytes of it. */
|
* bytes of it. */
|
||||||
@ -253,6 +268,7 @@ test_evbuffer_reserve2(void *ptr)
|
|||||||
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
|
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
|
||||||
tt_int_op(evbuffer_get_length(buf), ==, 520);
|
tt_int_op(evbuffer_get_length(buf), ==, 520);
|
||||||
remaining -= 8;
|
remaining -= 8;
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
/* Now ask for a request that will be split. Use only one byte of it,
|
/* Now ask for a request that will be split. Use only one byte of it,
|
||||||
though. */
|
though. */
|
||||||
@ -268,10 +284,12 @@ test_evbuffer_reserve2(void *ptr)
|
|||||||
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
|
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
|
||||||
tt_int_op(evbuffer_get_length(buf), ==, 521);
|
tt_int_op(evbuffer_get_length(buf), ==, 521);
|
||||||
remaining -= 1;
|
remaining -= 1;
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
/* Now ask for a request that will be split. Use some of the first
|
/* Now ask for a request that will be split. Use some of the first
|
||||||
* part and some of the second. */
|
* part and some of the second. */
|
||||||
n = evbuffer_reserve_space(buf, remaining+64, v, 2);
|
n = evbuffer_reserve_space(buf, remaining+64, v, 2);
|
||||||
|
evbuffer_validate(buf);
|
||||||
tt_int_op(n, ==, 2);
|
tt_int_op(n, ==, 2);
|
||||||
tt_assert(cp + 521 == v[0].iov_base);
|
tt_assert(cp + 521 == v[0].iov_base);
|
||||||
tt_int_op(remaining, ==, v[0].iov_len);
|
tt_int_op(remaining, ==, v[0].iov_len);
|
||||||
@ -283,7 +301,7 @@ test_evbuffer_reserve2(void *ptr)
|
|||||||
v[1].iov_len = 60;
|
v[1].iov_len = 60;
|
||||||
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 2));
|
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 2));
|
||||||
tt_int_op(evbuffer_get_length(buf), ==, 981);
|
tt_int_op(evbuffer_get_length(buf), ==, 981);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
/* Now peek to make sure stuff got made how we like. */
|
/* Now peek to make sure stuff got made how we like. */
|
||||||
memset(v,0,sizeof(v));
|
memset(v,0,sizeof(v));
|
||||||
@ -310,6 +328,80 @@ end:
|
|||||||
evbuffer_free(buf);
|
evbuffer_free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_evbuffer_reserve_many(void *ptr)
|
||||||
|
{
|
||||||
|
/* This is a glass-box test to handle expanding a buffer with more
|
||||||
|
* chunks and reallocating chunks as needed */
|
||||||
|
struct evbuffer *buf = evbuffer_new();
|
||||||
|
struct evbuffer_iovec v[8];
|
||||||
|
int n;
|
||||||
|
size_t sz;
|
||||||
|
int add_data = ptr && !strcmp(ptr, "add");
|
||||||
|
int fill_first = ptr && !strcmp(ptr, "fill");
|
||||||
|
char *cp1, *cp2;
|
||||||
|
|
||||||
|
/* When reserving the the first chunk, we just allocate it */
|
||||||
|
n = evbuffer_reserve_space(buf, 128, v, 2);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
tt_int_op(n, ==, 1);
|
||||||
|
tt_assert(v[0].iov_len >= 128);
|
||||||
|
sz = v[0].iov_len;
|
||||||
|
cp1 = v[0].iov_base;
|
||||||
|
if (add_data) {
|
||||||
|
*(char*)v[0].iov_base = 'X';
|
||||||
|
v[0].iov_len = 1;
|
||||||
|
n = evbuffer_commit_space(buf, v, 1);
|
||||||
|
tt_int_op(n, ==, 0);
|
||||||
|
} else if (fill_first) {
|
||||||
|
memset(v[0].iov_base, 'X', v[0].iov_len);
|
||||||
|
n = evbuffer_commit_space(buf, v, 1);
|
||||||
|
tt_int_op(n, ==, 0);
|
||||||
|
n = evbuffer_reserve_space(buf, 128, v, 2);
|
||||||
|
tt_int_op(n, ==, 1);
|
||||||
|
sz = v[0].iov_len;
|
||||||
|
tt_assert(v[0].iov_base != cp1);
|
||||||
|
cp1 = v[0].iov_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make another chunk get added. */
|
||||||
|
n = evbuffer_reserve_space(buf, sz+128, v, 2);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
tt_int_op(n, ==, 2);
|
||||||
|
sz = v[0].iov_len + v[1].iov_len;
|
||||||
|
tt_int_op(sz, >=, v[0].iov_len+128);
|
||||||
|
if (add_data) {
|
||||||
|
tt_assert(v[0].iov_base == cp1 + 1);
|
||||||
|
} else {
|
||||||
|
tt_assert(v[0].iov_base == cp1);
|
||||||
|
}
|
||||||
|
cp1 = v[0].iov_base;
|
||||||
|
cp2 = v[1].iov_base;
|
||||||
|
|
||||||
|
/* And a third chunk. */
|
||||||
|
n = evbuffer_reserve_space(buf, sz+128, v, 3);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
tt_int_op(n, ==, 3);
|
||||||
|
tt_assert(cp1 == v[0].iov_base);
|
||||||
|
tt_assert(cp2 == v[1].iov_base);
|
||||||
|
sz = v[0].iov_len + v[1].iov_len + v[2].iov_len;
|
||||||
|
|
||||||
|
/* Now force a reallocation by asking for more space in only 2
|
||||||
|
* buffers. */
|
||||||
|
n = evbuffer_reserve_space(buf, sz+128, v, 2);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
if (add_data) {
|
||||||
|
tt_int_op(n, ==, 2);
|
||||||
|
tt_assert(cp1 == v[0].iov_base);
|
||||||
|
} else {
|
||||||
|
tt_int_op(n, ==, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
evbuffer_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int reference_cb_called;
|
static int reference_cb_called;
|
||||||
static void
|
static void
|
||||||
reference_cb(const void *data, size_t len, void *extra)
|
reference_cb(const void *data, size_t len, void *extra)
|
||||||
@ -358,6 +450,7 @@ test_evbuffer_reference(void *ptr)
|
|||||||
|
|
||||||
tt_assert(!memcmp(evbuffer_pullup(dst, strlen(data)),
|
tt_assert(!memcmp(evbuffer_pullup(dst, strlen(data)),
|
||||||
data, strlen(data)));
|
data, strlen(data)));
|
||||||
|
evbuffer_validate(dst);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
evbuffer_free(dst);
|
evbuffer_free(dst);
|
||||||
@ -394,6 +487,7 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
if (memcmp(compare, data, strlen(data)))
|
if (memcmp(compare, data, strlen(data)))
|
||||||
tt_abort_msg("Data from add_file differs.");
|
tt_abort_msg("Data from add_file differs.");
|
||||||
|
|
||||||
|
evbuffer_validate(src);
|
||||||
end:
|
end:
|
||||||
EVUTIL_CLOSESOCKET(pair[0]);
|
EVUTIL_CLOSESOCKET(pair[0]);
|
||||||
EVUTIL_CLOSESOCKET(pair[1]);
|
EVUTIL_CLOSESOCKET(pair[1]);
|
||||||
@ -711,6 +805,7 @@ test_evbuffer_ptr_set(void *ptr)
|
|||||||
v[0].iov_len = 5000;
|
v[0].iov_len = 5000;
|
||||||
memset(v[0].iov_base, 1, v[0].iov_len);
|
memset(v[0].iov_base, 1, v[0].iov_len);
|
||||||
evbuffer_commit_space(buf, v, 1);
|
evbuffer_commit_space(buf, v, 1);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
evbuffer_reserve_space(buf, 4000, v, 1);
|
evbuffer_reserve_space(buf, 4000, v, 1);
|
||||||
v[0].iov_len = 4000;
|
v[0].iov_len = 4000;
|
||||||
@ -721,6 +816,7 @@ test_evbuffer_ptr_set(void *ptr)
|
|||||||
v[0].iov_len = 3000;
|
v[0].iov_len = 3000;
|
||||||
memset(v[0].iov_base, 3, v[0].iov_len);
|
memset(v[0].iov_base, 3, v[0].iov_len);
|
||||||
evbuffer_commit_space(buf, v, 1);
|
evbuffer_commit_space(buf, v, 1);
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
tt_int_op(evbuffer_get_length(buf), ==, 12000);
|
tt_int_op(evbuffer_get_length(buf), ==, 12000);
|
||||||
|
|
||||||
@ -867,7 +963,9 @@ test_evbuffer_callbacks(void *ptr)
|
|||||||
evbuffer_add_printf(buf, "This will not.");
|
evbuffer_add_printf(buf, "This will not.");
|
||||||
tt_str_op(evbuffer_pullup(buf, -1), ==, "This will not.");
|
tt_str_op(evbuffer_pullup(buf, -1), ==, "This will not.");
|
||||||
|
|
||||||
|
evbuffer_validate(buf);
|
||||||
evbuffer_drain(buf, evbuffer_get_length(buf));
|
evbuffer_drain(buf, evbuffer_get_length(buf));
|
||||||
|
evbuffer_validate(buf);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* Now let's try a suspended callback. */
|
/* Now let's try a suspended callback. */
|
||||||
@ -938,6 +1036,7 @@ test_evbuffer_add_reference(void *ptr)
|
|||||||
/* Make sure that prepending does not meddle with immutable data */
|
/* Make sure that prepending does not meddle with immutable data */
|
||||||
tt_int_op(evbuffer_prepend(buf1, "I have ", 7), ==, 0);
|
tt_int_op(evbuffer_prepend(buf1, "I have ", 7), ==, 0);
|
||||||
tt_int_op(memcmp(chunk1, "If you", 6), ==, 0);
|
tt_int_op(memcmp(chunk1, "If you", 6), ==, 0);
|
||||||
|
evbuffer_validate(buf1);
|
||||||
|
|
||||||
/* Make sure that when the chunk is over, the callback is invoked. */
|
/* Make sure that when the chunk is over, the callback is invoked. */
|
||||||
evbuffer_drain(buf1, 7); /* Remove prepended stuff. */
|
evbuffer_drain(buf1, 7); /* Remove prepended stuff. */
|
||||||
@ -949,6 +1048,7 @@ test_evbuffer_add_reference(void *ptr)
|
|||||||
tt_assert(ref_done_cb_called_with_data == chunk1);
|
tt_assert(ref_done_cb_called_with_data == chunk1);
|
||||||
tt_assert(ref_done_cb_called_with_len == len1);
|
tt_assert(ref_done_cb_called_with_len == len1);
|
||||||
tt_int_op(ref_done_cb_called_count, ==, 1);
|
tt_int_op(ref_done_cb_called_count, ==, 1);
|
||||||
|
evbuffer_validate(buf1);
|
||||||
|
|
||||||
/* Drain some of the remaining chunk, then add it to another buffer */
|
/* Drain some of the remaining chunk, then add it to another buffer */
|
||||||
evbuffer_drain(buf1, 6); /* Remove the ", you ". */
|
evbuffer_drain(buf1, 6); /* Remove the ", you ". */
|
||||||
@ -964,6 +1064,7 @@ test_evbuffer_add_reference(void *ptr)
|
|||||||
evbuffer_drain(buf2, evbuffer_get_length(buf2));
|
evbuffer_drain(buf2, evbuffer_get_length(buf2));
|
||||||
tt_int_op(ref_done_cb_called_count, ==, 2);
|
tt_int_op(ref_done_cb_called_count, ==, 2);
|
||||||
tt_assert(ref_done_cb_called_with == (void*)222);
|
tt_assert(ref_done_cb_called_with == (void*)222);
|
||||||
|
evbuffer_validate(buf2);
|
||||||
|
|
||||||
/* Now add more stuff to buf1 and make sure that it gets removed on
|
/* Now add more stuff to buf1 and make sure that it gets removed on
|
||||||
* free. */
|
* free. */
|
||||||
@ -1250,6 +1351,9 @@ static const struct testcase_setup_t nil_setup = {
|
|||||||
struct testcase_t evbuffer_testcases[] = {
|
struct testcase_t evbuffer_testcases[] = {
|
||||||
{ "evbuffer", test_evbuffer, 0, NULL, NULL },
|
{ "evbuffer", test_evbuffer, 0, NULL, NULL },
|
||||||
{ "reserve2", test_evbuffer_reserve2, 0, NULL, NULL },
|
{ "reserve2", test_evbuffer_reserve2, 0, NULL, NULL },
|
||||||
|
{ "reserve_many", test_evbuffer_reserve_many, 0, NULL, NULL },
|
||||||
|
{ "reserve_many2", test_evbuffer_reserve_many, 0, &nil_setup, (void*)"add" },
|
||||||
|
{ "reserve_many3", test_evbuffer_reserve_many, 0, &nil_setup, (void*)"fill" },
|
||||||
{ "reference", test_evbuffer_reference, 0, NULL, NULL },
|
{ "reference", test_evbuffer_reference, 0, NULL, NULL },
|
||||||
{ "iterative", test_evbuffer_iterative, 0, NULL, NULL },
|
{ "iterative", test_evbuffer_iterative, 0, NULL, NULL },
|
||||||
{ "readln", test_evbuffer_readln, TT_NO_LOGS, &basic_setup, NULL },
|
{ "readln", test_evbuffer_readln, TT_NO_LOGS, &basic_setup, NULL },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user