if (buf->first == NULL) {
buf->first = buf->last = chain;
buf->previous_to_last = NULL;
+ buf->last_with_data = chain;
} else {
/* the last chain is empty so we can just drop it */
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);
buf->previous_to_last->next = chain;
buf->last = chain;
buf->last->next = chain;
buf->last = chain;
}
+ if (chain->off)
+ buf->last_with_data = chain;
}
buf->total_len += chain->off;
}
+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
evbuffer_commit_space(struct evbuffer *buf,
struct evbuffer_iovec *vec, int n_vecs)
buf->total_len += added;
buf->n_add_for_cb += added;
result = 0;
+ advance_last_with_data(buf);
evbuffer_invoke_callbacks(buf);
done:
(dst)->first = NULL; \
(dst)->last = NULL; \
(dst)->previous_to_last = NULL; \
+ (dst)->last_with_data = NULL; \
(dst)->total_len = 0; \
} while (0)
ASSERT_EVBUFFER_LOCKED(src); \
(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)->total_len = (src)->total_len; \
} while (0)
(dst)->last->next = (src)->first; \
(dst)->previous_to_last = (src)->previous_to_last ? \
(src)->previous_to_last : (dst)->last; \
+ if ((src)->last_with_data) \
+ (dst)->last_with_data = (src)->last_with_data; \
(dst)->last = (src)->last; \
(dst)->total_len += (src)->total_len; \
} while (0)
(dst)->total_len += (src)->total_len; \
if ((dst)->previous_to_last == NULL) \
(dst)->previous_to_last = (src)->last; \
+ if ((dst)->last_with_data == NULL) \
+ (dst)->last_with_data = (src)->last_with_data; \
} while (0)
-
int
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
{
for (chain = buf->first; len >= chain->off; chain = next) {
next = chain->next;
len -= chain->off;
+ if (chain == buf->last_with_data)
+ buf->last_with_data = next;
if (len == 0 && CHAIN_PINNED_R(chain))
break;
data += chain->off;
datlen -= chain->off;
+ if (chain == buf->last_with_data)
+ buf->last_with_data = chain->next;
+
tmp = chain;
chain = chain->next;
evbuffer_chain_free(tmp);
/* removes chains if possible */
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;
datlen -= chain->off;
previous_to_previous = previous;
}
dst->previous_to_last = previous_to_previous;
dst->last = previous;
+ dst->last_with_data = dst->last;
previous->next = NULL;
src->first = chain;
if (src->first == src->last)
struct evbuffer_chain *chain, *next, *tmp;
unsigned char *buffer, *result = NULL;
ev_ssize_t remaining;
+ int removed_last_with_data = 0;
EVBUFFER_LOCK(buf);
memcpy(buffer, chain->buffer + chain->misalign, chain->off);
size -= chain->off;
buffer += chain->off;
+ if (chain == buf->last_with_data)
+ removed_last_with_data = 1;
evbuffer_chain_free(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);
done:
buf->first = tmp;
if (buf->previous_to_last == NULL)
buf->previous_to_last = tmp;
+ if (buf->last_with_data == NULL)
+ 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->off = datlen;
if (buf->previous_to_last)
buf->previous_to_last->next = tmp;
buf->last = tmp;
+ if (buf->last->off || buf->last_with_data == chain)
+ buf->last_with_data = tmp;
evbuffer_chain_free(chain);
if (buf->previous_to_last)
buf->previous_to_last->next = tmp;
buf->last = tmp;
+ if (chain == buf->last_with_data)
+ buf->last_with_data = tmp;
evbuffer_chain_free(chain);
-
} else {
/* Add a new chunk big enough to hold what won't fit
* in chunk. */
{
struct evbuffer_chain *chain, *previous = NULL;
size_t sum = 0;
+ int found_last_with_data = 0;
if (buf->first == NULL) {
tt_assert(buf->last == NULL);
chain = buf->first;
while (chain != NULL) {
+ if (chain == buf->last_with_data)
+ found_last_with_data = 1;
sum += chain->off;
if (chain->next == NULL) {
tt_assert(buf->previous_to_last == previous);
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);
return 1;
end:
cp = v[0].iov_base;
remaining = 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(evbuffer_get_length(buf), ==, 512);
+ evbuffer_validate(buf);
/* Ask for another same-chunk request, in an existing chunk. Use 8
* bytes of it. */
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
tt_int_op(evbuffer_get_length(buf), ==, 520);
remaining -= 8;
+ evbuffer_validate(buf);
/* Now ask for a request that will be split. Use only one byte of it,
though. */
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1));
tt_int_op(evbuffer_get_length(buf), ==, 521);
remaining -= 1;
+ evbuffer_validate(buf);
/* Now ask for a request that will be split. Use some of the first
* part and some of the second. */
v[1].iov_len = 60;
tt_int_op(0, ==, evbuffer_commit_space(buf, v, 2));
tt_int_op(evbuffer_get_length(buf), ==, 981);
-
+ evbuffer_validate(buf);
/* Now peek to make sure stuff got made how we like. */
memset(v,0,sizeof(v));
tt_assert(!memcmp(evbuffer_pullup(dst, strlen(data)),
data, strlen(data)));
+ evbuffer_validate(dst);
end:
evbuffer_free(dst);
if (memcmp(compare, data, strlen(data)))
tt_abort_msg("Data from add_file differs.");
+ evbuffer_validate(src);
end:
EVUTIL_CLOSESOCKET(pair[0]);
EVUTIL_CLOSESOCKET(pair[1]);
v[0].iov_len = 5000;
memset(v[0].iov_base, 1, v[0].iov_len);
evbuffer_commit_space(buf, v, 1);
+ evbuffer_validate(buf);
evbuffer_reserve_space(buf, 4000, v, 1);
v[0].iov_len = 4000;
v[0].iov_len = 3000;
memset(v[0].iov_base, 3, v[0].iov_len);
evbuffer_commit_space(buf, v, 1);
+ evbuffer_validate(buf);
tt_int_op(evbuffer_get_length(buf), ==, 12000);
evbuffer_add_printf(buf, "This will not.");
tt_str_op(evbuffer_pullup(buf, -1), ==, "This will not.");
+ evbuffer_validate(buf);
evbuffer_drain(buf, evbuffer_get_length(buf));
+ evbuffer_validate(buf);
#if 0
/* Now let's try a suspended callback. */
/* Make sure that prepending does not meddle with immutable data */
tt_int_op(evbuffer_prepend(buf1, "I have ", 7), ==, 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. */
evbuffer_drain(buf1, 7); /* Remove prepended stuff. */
tt_assert(ref_done_cb_called_with_data == chunk1);
tt_assert(ref_done_cb_called_with_len == len1);
tt_int_op(ref_done_cb_called_count, ==, 1);
+ evbuffer_validate(buf1);
/* Drain some of the remaining chunk, then add it to another buffer */
evbuffer_drain(buf1, 6); /* Remove the ", you ". */
evbuffer_drain(buf2, evbuffer_get_length(buf2));
tt_int_op(ref_done_cb_called_count, ==, 2);
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
* free. */