summaryrefslogtreecommitdiff
path: root/libs/libevent/docs/test/regress_buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libevent/docs/test/regress_buffer.c')
-rw-r--r--libs/libevent/docs/test/regress_buffer.c2281
1 files changed, 2281 insertions, 0 deletions
diff --git a/libs/libevent/docs/test/regress_buffer.c b/libs/libevent/docs/test/regress_buffer.c
new file mode 100644
index 0000000000..957e59f178
--- /dev/null
+++ b/libs/libevent/docs/test/regress_buffer.c
@@ -0,0 +1,2281 @@
+/*
+ * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
+ * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "util-internal.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#include "event2/event-config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef EVENT__HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <netdb.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "event2/event.h"
+#include "event2/buffer.h"
+#include "event2/buffer_compat.h"
+#include "event2/util.h"
+
+#include "defer-internal.h"
+#include "evbuffer-internal.h"
+#include "log-internal.h"
+
+#include "regress.h"
+
+/* Validates that an evbuffer is good. Returns false if it isn't, true if it
+ * is*/
+static int
+evbuffer_validate_(struct evbuffer *buf)
+{
+ struct evbuffer_chain *chain;
+ size_t sum = 0;
+ int found_last_with_datap = 0;
+
+ if (buf->first == NULL) {
+ tt_assert(buf->last == NULL);
+ tt_assert(buf->total_len == 0);
+ }
+
+ chain = buf->first;
+
+ tt_assert(buf->last_with_datap);
+ if (buf->last_with_datap == &buf->first)
+ found_last_with_datap = 1;
+
+ while (chain != NULL) {
+ if (&chain->next == buf->last_with_datap)
+ found_last_with_datap = 1;
+ sum += chain->off;
+ if (chain->next == NULL) {
+ tt_assert(buf->last == chain);
+ }
+ tt_assert(chain->buffer_len >= chain->misalign + chain->off);
+ chain = chain->next;
+ }
+
+ if (buf->first)
+ tt_assert(*buf->last_with_datap);
+
+ if (*buf->last_with_datap) {
+ chain = *buf->last_with_datap;
+ 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;
+ }
+ } else {
+ tt_assert(buf->last_with_datap == &buf->first);
+ }
+ tt_assert(found_last_with_datap);
+
+ tt_assert(sum == buf->total_len);
+ return 1;
+ end:
+ return 0;
+}
+
+static void
+evbuffer_get_waste(struct evbuffer *buf, size_t *allocatedp, size_t *wastedp, size_t *usedp)
+{
+ struct evbuffer_chain *chain;
+ size_t a, w, u;
+ int n = 0;
+ u = a = w = 0;
+
+ chain = buf->first;
+ /* skip empty at start */
+ while (chain && chain->off==0) {
+ ++n;
+ a += chain->buffer_len;
+ chain = chain->next;
+ }
+ /* first nonempty chain: stuff at the end only is wasted. */
+ if (chain) {
+ ++n;
+ a += chain->buffer_len;
+ u += chain->off;
+ if (chain->next && chain->next->off)
+ w += (size_t)(chain->buffer_len - (chain->misalign + chain->off));
+ chain = chain->next;
+ }
+ /* subsequent nonempty chains */
+ while (chain && chain->off) {
+ ++n;
+ a += chain->buffer_len;
+ w += (size_t)chain->misalign;
+ u += chain->off;
+ if (chain->next && chain->next->off)
+ w += (size_t) (chain->buffer_len - (chain->misalign + chain->off));
+ chain = chain->next;
+ }
+ /* subsequent empty chains */
+ while (chain) {
+ ++n;
+ a += chain->buffer_len;
+ }
+ *allocatedp = a;
+ *wastedp = w;
+ *usedp = u;
+}
+
+#define evbuffer_validate(buf) \
+ TT_STMT_BEGIN if (!evbuffer_validate_(buf)) TT_DIE(("Buffer format invalid")); TT_STMT_END
+
+static void
+test_evbuffer(void *ptr)
+{
+ static char buffer[512], *tmp;
+ struct evbuffer *evb = evbuffer_new();
+ struct evbuffer *evb_two = evbuffer_new();
+ size_t sz_tmp;
+ int i;
+
+ evbuffer_validate(evb);
+ evbuffer_add_printf(evb, "%s/%d", "hello", 1);
+ evbuffer_validate(evb);
+
+ tt_assert(evbuffer_get_length(evb) == 7);
+ tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "hello/1", 1));
+
+ evbuffer_add_buffer(evb, evb_two);
+ evbuffer_validate(evb);
+
+ evbuffer_drain(evb, strlen("hello/"));
+ evbuffer_validate(evb);
+ tt_assert(evbuffer_get_length(evb) == 1);
+ tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "1", 1));
+
+ evbuffer_add_printf(evb_two, "%s", "/hello");
+ evbuffer_validate(evb);
+ evbuffer_add_buffer(evb, evb_two);
+ evbuffer_validate(evb);
+
+ tt_assert(evbuffer_get_length(evb_two) == 0);
+ tt_assert(evbuffer_get_length(evb) == 7);
+ tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "1/hello", 7));
+
+ memset(buffer, 0, sizeof(buffer));
+ evbuffer_add(evb, buffer, sizeof(buffer));
+ evbuffer_validate(evb);
+ tt_assert(evbuffer_get_length(evb) == 7 + 512);
+
+ tmp = (char *)evbuffer_pullup(evb, 7 + 512);
+ tt_assert(tmp);
+ tt_assert(!strncmp(tmp, "1/hello", 7));
+ tt_assert(!memcmp(tmp + 7, buffer, sizeof(buffer)));
+ evbuffer_validate(evb);
+
+ evbuffer_prepend(evb, "something", 9);
+ evbuffer_validate(evb);
+ evbuffer_prepend(evb, "else", 4);
+ evbuffer_validate(evb);
+
+ tmp = (char *)evbuffer_pullup(evb, 4 + 9 + 7);
+ tt_assert(!strncmp(tmp, "elsesomething1/hello", 4 + 9 + 7));
+ evbuffer_validate(evb);
+
+ evbuffer_drain(evb, -1);
+ evbuffer_validate(evb);
+ evbuffer_drain(evb_two, -1);
+ evbuffer_validate(evb);
+
+ for (i = 0; i < 3; ++i) {
+ evbuffer_add(evb_two, buffer, sizeof(buffer));
+ evbuffer_validate(evb_two);
+ evbuffer_add_buffer(evb, evb_two);
+ evbuffer_validate(evb);
+ evbuffer_validate(evb_two);
+ }
+
+ tt_assert(evbuffer_get_length(evb_two) == 0);
+ tt_assert(evbuffer_get_length(evb) == i * sizeof(buffer));
+
+ /* test remove buffer */
+ sz_tmp = (size_t)(sizeof(buffer)*2.5);
+ evbuffer_remove_buffer(evb, evb_two, sz_tmp);
+ tt_assert(evbuffer_get_length(evb_two) == sz_tmp);
+ tt_assert(evbuffer_get_length(evb) == sizeof(buffer) / 2);
+ evbuffer_validate(evb);
+
+ if (memcmp(evbuffer_pullup(
+ evb, -1), buffer, sizeof(buffer) / 2) != 0 ||
+ memcmp(evbuffer_pullup(
+ evb_two, -1), buffer, sizeof(buffer)) != 0)
+ tt_abort_msg("Pullup did not preserve content");
+
+ evbuffer_validate(evb);
+
+
+ /* testing one-vector reserve and commit */
+ {
+ struct evbuffer_iovec v[1];
+ char *buf;
+ int i, j, r;
+
+ for (i = 0; i < 3; ++i) {
+ r = evbuffer_reserve_space(evb, 10000, v, 1);
+ tt_int_op(r, ==, 1);
+ tt_assert(v[0].iov_len >= 10000);
+ tt_assert(v[0].iov_base != NULL);
+
+ evbuffer_validate(evb);
+ buf = v[0].iov_base;
+ for (j = 0; j < 10000; ++j) {
+ buf[j] = j;
+ }
+ evbuffer_validate(evb);
+
+ tt_int_op(evbuffer_commit_space(evb, v, 1), ==, 0);
+ evbuffer_validate(evb);
+
+ tt_assert(evbuffer_get_length(evb) >= 10000);
+
+ evbuffer_drain(evb, j * 5000);
+ evbuffer_validate(evb);
+ }
+ }
+
+ end:
+ evbuffer_free(evb);
+ evbuffer_free(evb_two);
+}
+
+static void
+no_cleanup(const void *data, size_t datalen, void *extra)
+{
+}
+
+static void
+test_evbuffer_remove_buffer_with_empty(void *ptr)
+{
+ struct evbuffer *src = evbuffer_new();
+ struct evbuffer *dst = evbuffer_new();
+ char buf[2];
+
+ evbuffer_validate(src);
+ evbuffer_validate(dst);
+
+ /* setup the buffers */
+ /* we need more data in src than we will move later */
+ evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL);
+ evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL);
+ /* we need one buffer in dst and one empty buffer at the end */
+ evbuffer_add(dst, buf, sizeof(buf));
+ evbuffer_add_reference(dst, buf, 0, no_cleanup, NULL);
+
+ evbuffer_validate(src);
+ evbuffer_validate(dst);
+
+ /* move three bytes over */
+ evbuffer_remove_buffer(src, dst, 3);
+
+ evbuffer_validate(src);
+ evbuffer_validate(dst);
+
+end:
+ evbuffer_free(src);
+ evbuffer_free(dst);
+}
+
+static void
+test_evbuffer_reserve2(void *ptr)
+{
+ /* Test the two-vector cases of reserve/commit. */
+ struct evbuffer *buf = evbuffer_new();
+ int n, i;
+ struct evbuffer_iovec v[2];
+ size_t remaining;
+ char *cp, *cp2;
+
+ /* First chunk will necessarily be one chunk. Use 512 bytes of it.*/
+ n = evbuffer_reserve_space(buf, 1024, v, 2);
+ tt_int_op(n, ==, 1);
+ tt_int_op(evbuffer_get_length(buf), ==, 0);
+ tt_assert(v[0].iov_base != NULL);
+ tt_int_op(v[0].iov_len, >=, 1024);
+ memset(v[0].iov_base, 'X', 512);
+ 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. */
+ n = evbuffer_reserve_space(buf, 32, v, 2);
+ tt_int_op(n, ==, 1);
+ tt_assert(cp + 512 == v[0].iov_base);
+ tt_int_op(remaining, ==, v[0].iov_len);
+ memset(v[0].iov_base, 'Y', 8);
+ v[0].iov_len = 8;
+ 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. */
+ n = evbuffer_reserve_space(buf, remaining+64, v, 2);
+ tt_int_op(n, ==, 2);
+ tt_assert(cp + 520 == v[0].iov_base);
+ tt_int_op(remaining, ==, v[0].iov_len);
+ tt_assert(v[1].iov_base);
+ tt_assert(v[1].iov_len >= 64);
+ cp2 = v[1].iov_base;
+ memset(v[0].iov_base, 'Z', 1);
+ v[0].iov_len = 1;
+ 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. */
+ n = evbuffer_reserve_space(buf, remaining+64, v, 2);
+ evbuffer_validate(buf);
+ tt_int_op(n, ==, 2);
+ tt_assert(cp + 521 == v[0].iov_base);
+ tt_int_op(remaining, ==, v[0].iov_len);
+ tt_assert(v[1].iov_base == cp2);
+ tt_assert(v[1].iov_len >= 64);
+ memset(v[0].iov_base, 'W', 400);
+ v[0].iov_len = 400;
+ memset(v[1].iov_base, 'x', 60);
+ 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));
+ n = evbuffer_peek(buf, -1, NULL, v, 2);
+ tt_int_op(n, ==, 2);
+ tt_int_op(v[0].iov_len, ==, 921);
+ tt_int_op(v[1].iov_len, ==, 60);
+
+ cp = v[0].iov_base;
+ for (i=0; i<512; ++i)
+ tt_int_op(cp[i], ==, 'X');
+ for (i=512; i<520; ++i)
+ tt_int_op(cp[i], ==, 'Y');
+ for (i=520; i<521; ++i)
+ tt_int_op(cp[i], ==, 'Z');
+ for (i=521; i<921; ++i)
+ tt_int_op(cp[i], ==, 'W');
+
+ cp = v[1].iov_base;
+ for (i=0; i<60; ++i)
+ tt_int_op(cp[i], ==, 'x');
+
+end:
+ 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 void
+test_evbuffer_expand(void *ptr)
+{
+ char data[4096];
+ struct evbuffer *buf;
+ size_t a,w,u;
+ void *buffer;
+
+ memset(data, 'X', sizeof(data));
+
+ /* Make sure that expand() works on an empty buffer */
+ buf = evbuffer_new();
+ tt_int_op(evbuffer_expand(buf, 20000), ==, 0);
+ evbuffer_validate(buf);
+ a=w=u=0;
+ evbuffer_get_waste(buf, &a,&w,&u);
+ tt_assert(w == 0);
+ tt_assert(u == 0);
+ tt_assert(a >= 20000);
+ tt_assert(buf->first);
+ tt_assert(buf->first == buf->last);
+ tt_assert(buf->first->off == 0);
+ tt_assert(buf->first->buffer_len >= 20000);
+
+ /* Make sure that expand() works as a no-op when there's enough
+ * contiguous space already. */
+ buffer = buf->first->buffer;
+ evbuffer_add(buf, data, 1024);
+ tt_int_op(evbuffer_expand(buf, 1024), ==, 0);
+ tt_assert(buf->first->buffer == buffer);
+ evbuffer_validate(buf);
+ evbuffer_free(buf);
+
+ /* Make sure that expand() can work by moving misaligned data
+ * when it makes sense to do so. */
+ buf = evbuffer_new();
+ evbuffer_add(buf, data, 400);
+ {
+ int n = (int)(buf->first->buffer_len - buf->first->off - 1);
+ tt_assert(n < (int)sizeof(data));
+ evbuffer_add(buf, data, n);
+ }
+ tt_assert(buf->first == buf->last);
+ tt_assert(buf->first->off == buf->first->buffer_len - 1);
+ evbuffer_drain(buf, buf->first->off - 1);
+ tt_assert(1 == evbuffer_get_length(buf));
+ tt_assert(buf->first->misalign > 0);
+ tt_assert(buf->first->off == 1);
+ buffer = buf->first->buffer;
+ tt_assert(evbuffer_expand(buf, 40) == 0);
+ tt_assert(buf->first == buf->last);
+ tt_assert(buf->first->off == 1);
+ tt_assert(buf->first->buffer == buffer);
+ tt_assert(buf->first->misalign == 0);
+ evbuffer_validate(buf);
+ evbuffer_free(buf);
+
+ /* add, expand, pull-up: This used to crash libevent. */
+ buf = evbuffer_new();
+
+ evbuffer_add(buf, data, sizeof(data));
+ evbuffer_add(buf, data, sizeof(data));
+ evbuffer_add(buf, data, sizeof(data));
+
+ evbuffer_validate(buf);
+ evbuffer_expand(buf, 1024);
+ evbuffer_validate(buf);
+ evbuffer_pullup(buf, -1);
+ evbuffer_validate(buf);
+
+end:
+ evbuffer_free(buf);
+}
+
+
+static int reference_cb_called;
+static void
+reference_cb(const void *data, size_t len, void *extra)
+{
+ tt_str_op(data, ==, "this is what we add as read-only memory.");
+ tt_int_op(len, ==, strlen(data));
+ tt_want(extra == (void *)0xdeadaffe);
+ ++reference_cb_called;
+end:
+ ;
+}
+
+static void
+test_evbuffer_reference(void *ptr)
+{
+ struct evbuffer *src = evbuffer_new();
+ struct evbuffer *dst = evbuffer_new();
+ struct evbuffer_iovec v[1];
+ const char *data = "this is what we add as read-only memory.";
+ reference_cb_called = 0;
+
+ tt_assert(evbuffer_add_reference(src, data, strlen(data),
+ reference_cb, (void *)0xdeadaffe) != -1);
+
+ evbuffer_reserve_space(dst, strlen(data), v, 1);
+ tt_assert(evbuffer_remove(src, v[0].iov_base, 10) != -1);
+
+ evbuffer_validate(src);
+ evbuffer_validate(dst);
+
+ /* make sure that we don't write data at the beginning */
+ evbuffer_prepend(src, "aaaaa", 5);
+ evbuffer_validate(src);
+ evbuffer_drain(src, 5);
+
+ tt_assert(evbuffer_remove(src, ((char*)(v[0].iov_base)) + 10,
+ strlen(data) - 10) != -1);
+
+ v[0].iov_len = strlen(data);
+
+ evbuffer_commit_space(dst, v, 1);
+ evbuffer_validate(src);
+ evbuffer_validate(dst);
+
+ tt_int_op(reference_cb_called, ==, 1);
+
+ tt_assert(!memcmp(evbuffer_pullup(dst, strlen(data)),
+ data, strlen(data)));
+ evbuffer_validate(dst);
+
+ end:
+ evbuffer_free(dst);
+ evbuffer_free(src);
+}
+
+static struct event_base *addfile_test_event_base = NULL;
+static int addfile_test_done_writing = 0;
+static int addfile_test_total_written = 0;
+static int addfile_test_total_read = 0;
+
+static void
+addfile_test_writecb(evutil_socket_t fd, short what, void *arg)
+{
+ struct evbuffer *b = arg;
+ int r;
+ evbuffer_validate(b);
+ while (evbuffer_get_length(b)) {
+ r = evbuffer_write(b, fd);
+ if (r > 0) {
+ addfile_test_total_written += r;
+ TT_BLATHER(("Wrote %d/%d bytes", r, addfile_test_total_written));
+ } else {
+ int e = evutil_socket_geterror(fd);
+ if (EVUTIL_ERR_RW_RETRIABLE(e))
+ return;
+ tt_fail_perror("write");
+ event_base_loopexit(addfile_test_event_base,NULL);
+ }
+ evbuffer_validate(b);
+ }
+ addfile_test_done_writing = 1;
+ return;
+end:
+ event_base_loopexit(addfile_test_event_base,NULL);
+}
+
+static void
+addfile_test_readcb(evutil_socket_t fd, short what, void *arg)
+{
+ struct evbuffer *b = arg;
+ int e, r = 0;
+ do {
+ r = evbuffer_read(b, fd, 1024);
+ if (r > 0) {
+ addfile_test_total_read += r;
+ TT_BLATHER(("Read %d/%d bytes", r, addfile_test_total_read));
+ }
+ } while (r > 0);
+ if (r < 0) {
+ e = evutil_socket_geterror(fd);
+ if (! EVUTIL_ERR_RW_RETRIABLE(e)) {
+ tt_fail_perror("read");
+ event_base_loopexit(addfile_test_event_base,NULL);
+ }
+ }
+ if (addfile_test_done_writing &&
+ addfile_test_total_read >= addfile_test_total_written) {
+ event_base_loopexit(addfile_test_event_base,NULL);
+ }
+}
+
+static void
+test_evbuffer_add_file(void *ptr)
+{
+ struct basic_test_data *testdata = ptr;
+ const char *impl = testdata->setup_data;
+ struct evbuffer *src = evbuffer_new(), *dest = evbuffer_new();
+ char *tmpfilename = NULL;
+ char *data = NULL;
+ const char *expect_data;
+ size_t datalen, expect_len;
+ const char *compare;
+ int fd = -1;
+ int want_ismapping = -1, want_cansendfile = -1;
+ unsigned flags = 0;
+ int use_segment = 1, use_bigfile = 0, map_from_offset = 0,
+ view_from_offset = 0;
+ struct evbuffer_file_segment *seg = NULL;
+ ev_off_t starting_offset = 0, mapping_len = -1;
+ ev_off_t segment_offset = 0, segment_len = -1;
+ struct event *rev=NULL, *wev=NULL;
+ struct event_base *base = testdata->base;
+ evutil_socket_t pair[2] = {-1, -1};
+ struct evutil_weakrand_state seed = { 123456789U };
+
+ /* This test is highly parameterized based on substrings of its
+ * argument. The strings are: */
+ tt_assert(impl);
+ if (strstr(impl, "nosegment")) {
+ /* If nosegment is set, use the older evbuffer_add_file
+ * interface */
+ use_segment = 0;
+ }
+ if (strstr(impl, "bigfile")) {
+ /* If bigfile is set, use a 512K file. Else use a smaller
+ * one. */
+ use_bigfile = 1;
+ }
+ if (strstr(impl, "map_offset")) {
+ /* If map_offset is set, we build the file segment starting
+ * from a point other than byte 0 and ending somewhere other
+ * than the last byte. Otherwise we map the whole thing */
+ map_from_offset = 1;
+ }
+ if (strstr(impl, "offset_in_segment")) {
+ /* If offset_in_segment is set, we add a subsection of the
+ * file semgment starting from a point other than byte 0 of
+ * the segment. */
+ view_from_offset = 1;
+ }
+ if (strstr(impl, "sendfile")) {
+ /* If sendfile is set, we try to use a sendfile/splice style
+ * backend. */
+ flags = EVBUF_FS_DISABLE_MMAP;
+ want_cansendfile = 1;
+ want_ismapping = 0;
+ } else if (strstr(impl, "mmap")) {
+ /* If sendfile is set, we try to use a mmap/CreateFileMapping
+ * style backend. */
+ flags = EVBUF_FS_DISABLE_SENDFILE;
+ want_ismapping = 1;
+ want_cansendfile = 0;
+ } else if (strstr(impl, "linear")) {
+ /* If linear is set, we try to use a read-the-whole-thing
+ * backend. */
+ flags = EVBUF_FS_DISABLE_SENDFILE|EVBUF_FS_DISABLE_MMAP;
+ want_ismapping = 0;
+ want_cansendfile = 0;
+ } else if (strstr(impl, "default")) {
+ /* The caller doesn't care which backend we use. */
+ ;
+ } else {
+ /* The caller must choose a backend. */
+ TT_DIE(("Didn't recognize the implementation"));
+ }
+
+ if (use_bigfile) {
+ unsigned int i;
+ datalen = 1024*512;
+ data = malloc(1024*512);
+ tt_assert(data);
+ for (i = 0; i < datalen; ++i)
+ data[i] = (char)evutil_weakrand_(&seed);
+ } else {
+ data = strdup("here is a relatively small string.");
+ tt_assert(data);
+ datalen = strlen(data);
+ }
+
+ fd = regress_make_tmpfile(data, datalen, &tmpfilename);
+
+ if (map_from_offset) {
+ starting_offset = datalen/4 + 1;
+ mapping_len = datalen / 2 - 1;
+ expect_data = data + starting_offset;
+ expect_len = mapping_len;
+ } else {
+ expect_data = data;
+ expect_len = datalen;
+ }
+ if (view_from_offset) {
+ tt_assert(use_segment); /* Can't do this with add_file*/
+ segment_offset = expect_len / 3;
+ segment_len = expect_len / 2;
+ expect_data = expect_data + segment_offset;
+ expect_len = segment_len;
+ }
+
+ if (use_segment) {
+ seg = evbuffer_file_segment_new(fd, starting_offset,
+ mapping_len, flags);
+ tt_assert(seg);
+ if (want_ismapping >= 0) {
+ if (seg->is_mapping != (unsigned)want_ismapping)
+ tt_skip();
+ }
+ if (want_cansendfile >= 0) {
+ if (seg->can_sendfile != (unsigned)want_cansendfile)
+ tt_skip();
+ }
+ }
+
+ /* Say that it drains to a fd so that we can use sendfile. */
+ evbuffer_set_flags(src, EVBUFFER_FLAG_DRAINS_TO_FD);
+
+#if defined(EVENT__HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__)
+ /* We need to use a pair of AF_INET sockets, since Solaris
+ doesn't support sendfile() over AF_UNIX. */
+ if (evutil_ersatz_socketpair_(AF_INET, SOCK_STREAM, 0, pair) == -1)
+ tt_abort_msg("ersatz_socketpair failed");
+#else
+ if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
+ tt_abort_msg("socketpair failed");
+#endif
+ evutil_make_socket_nonblocking(pair[0]);
+ evutil_make_socket_nonblocking(pair[1]);
+
+ tt_assert(fd != -1);
+
+ if (use_segment) {
+ tt_assert(evbuffer_add_file_segment(src, seg,
+ segment_offset, segment_len)!=-1);
+ } else {
+ tt_assert(evbuffer_add_file(src, fd, starting_offset,
+ mapping_len) != -1);
+ }
+
+ evbuffer_validate(src);
+
+ addfile_test_event_base = base;
+ wev = event_new(base, pair[0], EV_WRITE|EV_PERSIST,
+ addfile_test_writecb, src);
+ rev = event_new(base, pair[1], EV_READ|EV_PERSIST,
+ addfile_test_readcb, dest);
+
+ event_add(wev, NULL);
+ event_add(rev, NULL);
+ event_base_dispatch(base);
+
+ evbuffer_validate(src);
+ evbuffer_validate(dest);
+
+ tt_assert(addfile_test_done_writing);
+ tt_int_op(addfile_test_total_written, ==, expect_len);
+ tt_int_op(addfile_test_total_read, ==, expect_len);
+
+ compare = (char *)evbuffer_pullup(dest, expect_len);
+ tt_assert(compare != NULL);
+ if (memcmp(compare, expect_data, expect_len)) {
+ tt_abort_msg("Data from add_file differs.");
+ }
+
+ evbuffer_validate(dest);
+ end:
+ if (data)
+ free(data);
+ if (seg)
+ evbuffer_file_segment_free(seg);
+ if (src)
+ evbuffer_free(src);
+ if (dest)
+ evbuffer_free(dest);
+ if (pair[0] >= 0)
+ evutil_closesocket(pair[0]);
+ if (pair[1] >= 0)
+ evutil_closesocket(pair[1]);
+ if (wev)
+ event_free(wev);
+ if (rev)
+ event_free(rev);
+ if (tmpfilename) {
+ unlink(tmpfilename);
+ free(tmpfilename);
+ }
+}
+
+static int file_segment_cleanup_cb_called_count = 0;
+static struct evbuffer_file_segment const* file_segment_cleanup_cb_called_with = NULL;
+static int file_segment_cleanup_cb_called_with_flags = 0;
+static void* file_segment_cleanup_cb_called_with_arg = NULL;
+static void
+file_segment_cleanup_cp(struct evbuffer_file_segment const* seg, int flags, void* arg)
+{
+ ++file_segment_cleanup_cb_called_count;
+ file_segment_cleanup_cb_called_with = seg;
+ file_segment_cleanup_cb_called_with_flags = flags;
+ file_segment_cleanup_cb_called_with_arg = arg;
+}
+
+static void
+test_evbuffer_file_segment_add_cleanup_cb(void* ptr)
+{
+ char *tmpfilename = NULL;
+ int fd = -1;
+ struct evbuffer *evb = NULL;
+ struct evbuffer_file_segment *seg = NULL, *segptr;
+ char const* arg = "token";
+
+ fd = regress_make_tmpfile("file_segment_test_file", 22, &tmpfilename);
+ tt_int_op(fd, >=, 0);
+
+ evb = evbuffer_new();
+ tt_assert(evb);
+
+ segptr = seg = evbuffer_file_segment_new(fd, 0, -1, 0);
+ tt_assert(seg);
+
+ evbuffer_file_segment_add_cleanup_cb(
+ seg, &file_segment_cleanup_cp, (void*)arg);
+
+ tt_assert(fd != -1);
+
+ tt_assert(evbuffer_add_file_segment(evb, seg, 0, -1)!=-1);
+
+ evbuffer_validate(evb);
+
+ tt_int_op(file_segment_cleanup_cb_called_count, ==, 0);
+ evbuffer_file_segment_free(seg);
+ seg = NULL; /* Prevent double-free. */
+
+ tt_int_op(file_segment_cleanup_cb_called_count, ==, 0);
+ evbuffer_free(evb);
+ evb = NULL; /* pevent double-free */
+
+ tt_int_op(file_segment_cleanup_cb_called_count, ==, 1);
+ tt_assert(file_segment_cleanup_cb_called_with == segptr);
+ tt_assert(file_segment_cleanup_cb_called_with_flags == 0);
+ tt_assert(file_segment_cleanup_cb_called_with_arg == (void*)arg);
+
+end:
+ if (evb)
+ evbuffer_free(evb);
+ if (seg)
+ evbuffer_file_segment_free(seg);
+ if (tmpfilename) {
+ unlink(tmpfilename);
+ free(tmpfilename);
+ }
+}
+
+#ifndef EVENT__DISABLE_MM_REPLACEMENT
+static void *
+failing_malloc(size_t how_much)
+{
+ errno = ENOMEM;
+ return NULL;
+}
+#endif
+
+static void
+test_evbuffer_readln(void *ptr)
+{
+ struct evbuffer *evb = evbuffer_new();
+ struct evbuffer *evb_tmp = evbuffer_new();
+ const char *s;
+ char *cp = NULL;
+ size_t sz;
+
+#define tt_line_eq(content) \
+ TT_STMT_BEGIN \
+ if (!cp || sz != strlen(content) || strcmp(cp, content)) { \
+ TT_DIE(("Wanted %s; got %s [%d]", content, cp, (int)sz)); \
+ } \
+ TT_STMT_END
+
+ /* Test EOL_ANY. */
+ s = "complex silly newline\r\n\n\r\n\n\rmore\0\n";
+ evbuffer_add(evb, s, strlen(s)+2);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY);
+ tt_line_eq("complex silly newline");
+ free(cp);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY);
+ if (!cp || sz != 5 || memcmp(cp, "more\0\0", 6))
+ tt_abort_msg("Not as expected");
+ tt_uint_op(evbuffer_get_length(evb), ==, 0);
+ evbuffer_validate(evb);
+ s = "\nno newline";
+ evbuffer_add(evb, s, strlen(s));
+ free(cp);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY);
+ tt_line_eq("");
+ free(cp);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY);
+ tt_assert(!cp);
+ evbuffer_validate(evb);
+ evbuffer_drain(evb, evbuffer_get_length(evb));
+ tt_assert(evbuffer_get_length(evb) == 0);
+ evbuffer_validate(evb);
+
+ /* Test EOL_CRLF */
+ s = "Line with\rin the middle\nLine with good crlf\r\n\nfinal\n";
+ evbuffer_add(evb, s, strlen(s));
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF);
+ tt_line_eq("Line with\rin the middle");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF);
+ tt_line_eq("Line with good crlf");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF);
+ tt_line_eq("");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF);
+ tt_line_eq("final");
+ s = "x";
+ evbuffer_validate(evb);
+ evbuffer_add(evb, s, 1);
+ evbuffer_validate(evb);
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF);
+ tt_assert(!cp);
+ evbuffer_validate(evb);
+
+ /* Test CRLF_STRICT */
+ s = " and a bad crlf\nand a good one\r\n\r\nMore\r";
+ evbuffer_add(evb, s, strlen(s));
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("x and a bad crlf\nand a good one");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_assert(!cp);
+ evbuffer_validate(evb);
+ evbuffer_add(evb, "\n", 1);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("More");
+ free(cp);
+ tt_assert(evbuffer_get_length(evb) == 0);
+ evbuffer_validate(evb);
+
+ s = "An internal CR\r is not an eol\r\nNor is a lack of one";
+ evbuffer_add(evb, s, strlen(s));
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("An internal CR\r is not an eol");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_assert(!cp);
+ evbuffer_validate(evb);
+
+ evbuffer_add(evb, "\r\n", 2);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("Nor is a lack of one");
+ free(cp);
+ tt_assert(evbuffer_get_length(evb) == 0);
+ evbuffer_validate(evb);
+
+ /* Test LF */
+ s = "An\rand a nl\n\nText";
+ evbuffer_add(evb, s, strlen(s));
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_line_eq("An\rand a nl");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_line_eq("");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_assert(!cp);
+ free(cp);
+ evbuffer_add(evb, "\n", 1);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_line_eq("Text");
+ free(cp);
+ evbuffer_validate(evb);
+
+ /* Test NUL */
+ tt_int_op(evbuffer_get_length(evb), ==, 0);
+ {
+ char x[] =
+ "NUL\n\0\0"
+ "The all-zeros character which may serve\0"
+ "to accomplish time fill\0and media fill";
+ /* Add all but the final NUL of x. */
+ evbuffer_add(evb, x, sizeof(x)-1);
+ }
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("NUL\n");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("The all-zeros character which may serve");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("to accomplish time fill");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_ptr_op(cp, ==, NULL);
+ evbuffer_drain(evb, -1);
+
+ /* Test CRLF_STRICT - across boundaries*/
+ s = " and a bad crlf\nand a good one\r";
+ evbuffer_add(evb_tmp, s, strlen(s));
+ evbuffer_validate(evb);
+ evbuffer_add_buffer(evb, evb_tmp);
+ evbuffer_validate(evb);
+ s = "\n\r";
+ evbuffer_add(evb_tmp, s, strlen(s));
+ evbuffer_validate(evb);
+ evbuffer_add_buffer(evb, evb_tmp);
+ evbuffer_validate(evb);
+ s = "\nMore\r";
+ evbuffer_add(evb_tmp, s, strlen(s));
+ evbuffer_validate(evb);
+ evbuffer_add_buffer(evb, evb_tmp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq(" and a bad crlf\nand a good one");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("");
+ free(cp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_assert(!cp);
+ free(cp);
+ evbuffer_validate(evb);
+ evbuffer_add(evb, "\n", 1);
+ evbuffer_validate(evb);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT);
+ tt_line_eq("More");
+ free(cp); cp = NULL;
+ evbuffer_validate(evb);
+ tt_assert(evbuffer_get_length(evb) == 0);
+
+ /* Test memory problem*/
+ s = "one line\ntwo line\nblue line";
+ evbuffer_add(evb_tmp, s, strlen(s));
+ evbuffer_validate(evb);
+ evbuffer_add_buffer(evb, evb_tmp);
+ evbuffer_validate(evb);
+
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_line_eq("one line");
+ free(cp); cp = NULL;
+ evbuffer_validate(evb);
+
+ /* the next call to readline should fail */
+#ifndef EVENT__DISABLE_MM_REPLACEMENT
+ event_set_mem_functions(failing_malloc, realloc, free);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_assert(cp == NULL);
+ evbuffer_validate(evb);
+
+ /* now we should get the next line back */
+ event_set_mem_functions(malloc, realloc, free);
+#endif
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF);
+ tt_line_eq("two line");
+ free(cp); cp = NULL;
+ evbuffer_validate(evb);
+
+ end:
+ evbuffer_free(evb);
+ evbuffer_free(evb_tmp);
+ if (cp) free(cp);
+}
+
+static void
+test_evbuffer_search_eol(void *ptr)
+{
+ struct evbuffer *buf = evbuffer_new();
+ struct evbuffer_ptr ptr1, ptr2;
+ const char *s;
+ size_t eol_len;
+
+ s = "string! \r\n\r\nx\n";
+ evbuffer_add(buf, s, strlen(s));
+ eol_len = -1;
+ ptr1 = evbuffer_search_eol(buf, NULL, &eol_len, EVBUFFER_EOL_CRLF);
+ tt_int_op(ptr1.pos, ==, 8);
+ tt_int_op(eol_len, ==, 2);
+
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_CRLF);
+ tt_int_op(ptr2.pos, ==, 8);
+ tt_int_op(eol_len, ==, 2);
+
+ evbuffer_ptr_set(buf, &ptr1, 1, EVBUFFER_PTR_ADD);
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_CRLF);
+ tt_int_op(ptr2.pos, ==, 9);
+ tt_int_op(eol_len, ==, 1);
+
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_CRLF_STRICT);
+ tt_int_op(ptr2.pos, ==, 10);
+ tt_int_op(eol_len, ==, 2);
+
+ eol_len = -1;
+ ptr1 = evbuffer_search_eol(buf, NULL, &eol_len, EVBUFFER_EOL_LF);
+ tt_int_op(ptr1.pos, ==, 9);
+ tt_int_op(eol_len, ==, 1);
+
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF);
+ tt_int_op(ptr2.pos, ==, 9);
+ tt_int_op(eol_len, ==, 1);
+
+ evbuffer_ptr_set(buf, &ptr1, 1, EVBUFFER_PTR_ADD);
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF);
+ tt_int_op(ptr2.pos, ==, 11);
+ tt_int_op(eol_len, ==, 1);
+
+ tt_assert(evbuffer_ptr_set(buf, &ptr1, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0);
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF);
+ tt_int_op(ptr2.pos, ==, -1);
+ tt_int_op(eol_len, ==, 0);
+
+end:
+ evbuffer_free(buf);
+}
+
+static void
+test_evbuffer_iterative(void *ptr)
+{
+ struct evbuffer *buf = evbuffer_new();
+ const char *abc = "abcdefghijklmnopqrstvuwxyzabcdefghijklmnopqrstvuwxyzabcdefghijklmnopqrstvuwxyzabcdefghijklmnopqrstvuwxyz";
+ unsigned i, j, sum, n;
+
+ sum = 0;
+ n = 0;
+ for (i = 0; i < 1000; ++i) {
+ for (j = 1; j < strlen(abc); ++j) {
+ char format[32];
+ evutil_snprintf(format, sizeof(format), "%%%u.%us", j, j);
+ evbuffer_add_printf(buf, format, abc);
+
+ /* Only check for rep violations every so often.
+ Walking over the whole list of chains can get
+ pretty expensive as it gets long.
+ */
+ if ((n % 337) == 0)
+ evbuffer_validate(buf);
+
+ sum += j;
+ n++;
+ }
+ }
+ evbuffer_validate(buf);
+
+ tt_uint_op(sum, ==, evbuffer_get_length(buf));
+
+ {
+ size_t a,w,u;
+ a=w=u=0;
+ evbuffer_get_waste(buf, &a, &w, &u);
+ if (0)
+ printf("Allocated: %u.\nWasted: %u.\nUsed: %u.",
+ (unsigned)a, (unsigned)w, (unsigned)u);
+ tt_assert( ((double)w)/a < .125);
+ }
+ end:
+ evbuffer_free(buf);
+
+}
+
+static void
+test_evbuffer_find(void *ptr)
+{
+ unsigned char* p;
+ const char* test1 = "1234567890\r\n";
+ const char* test2 = "1234567890\r";
+#define EVBUFFER_INITIAL_LENGTH 256
+ char test3[EVBUFFER_INITIAL_LENGTH];
+ unsigned int i;
+ struct evbuffer * buf = evbuffer_new();
+
+ tt_assert(buf);
+
+ /* make sure evbuffer_find doesn't match past the end of the buffer */
+ evbuffer_add(buf, (unsigned char*)test1, strlen(test1));
+ evbuffer_validate(buf);
+ evbuffer_drain(buf, strlen(test1));
+ evbuffer_validate(buf);
+ evbuffer_add(buf, (unsigned char*)test2, strlen(test2));
+ evbuffer_validate(buf);
+ p = evbuffer_find(buf, (unsigned char*)"\r\n", 2);
+ tt_want(p == NULL);
+
+ /*
+ * drain the buffer and do another find; in r309 this would
+ * read past the allocated buffer causing a valgrind error.
+ */
+ evbuffer_drain(buf, strlen(test2));
+ evbuffer_validate(buf);
+ for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i)
+ test3[i] = 'a';
+ test3[EVBUFFER_INITIAL_LENGTH - 1] = 'x';
+ evbuffer_add(buf, (unsigned char *)test3, EVBUFFER_INITIAL_LENGTH);
+ evbuffer_validate(buf);
+ p = evbuffer_find(buf, (unsigned char *)"xy", 2);
+ tt_want(p == NULL);
+
+ /* simple test for match at end of allocated buffer */
+ p = evbuffer_find(buf, (unsigned char *)"ax", 2);
+ tt_assert(p != NULL);
+ tt_want(strncmp((char*)p, "ax", 2) == 0);
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+}
+
+static void
+test_evbuffer_ptr_set(void *ptr)
+{
+ struct evbuffer *buf = evbuffer_new();
+ struct evbuffer_ptr pos;
+ struct evbuffer_iovec v[1];
+
+ tt_assert(buf);
+
+ tt_int_op(evbuffer_get_length(buf), ==, 0);
+
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ tt_assert(pos.pos == 0);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_ADD) == -1);
+ tt_assert(pos.pos == -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_SET) == -1);
+ tt_assert(pos.pos == -1);
+
+ /* create some chains */
+ evbuffer_reserve_space(buf, 5000, v, 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;
+ memset(v[0].iov_base, 2, v[0].iov_len);
+ evbuffer_commit_space(buf, v, 1);
+
+ evbuffer_reserve_space(buf, 3000, v, 1);
+ 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);
+
+ tt_assert(evbuffer_ptr_set(buf, &pos, 13000, EVBUFFER_PTR_SET) == -1);
+ tt_assert(pos.pos == -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ tt_assert(pos.pos == 0);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 13000, EVBUFFER_PTR_ADD) == -1);
+
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ tt_assert(pos.pos == 0);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 10000, EVBUFFER_PTR_ADD) == 0);
+ tt_assert(pos.pos == 10000);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == 0);
+ tt_assert(pos.pos == 11000);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == 0);
+ tt_assert(pos.pos == 12000);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == -1);
+ tt_assert(pos.pos == -1);
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+}
+
+static void
+test_evbuffer_search(void *ptr)
+{
+ struct evbuffer *buf = evbuffer_new();
+ struct evbuffer *tmp = evbuffer_new();
+ struct evbuffer_ptr pos, end;
+
+ tt_assert(buf);
+ tt_assert(tmp);
+
+ pos = evbuffer_search(buf, "x", 1, NULL);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search(buf, "x", 1, &pos);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "x", 1, &pos, &pos);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "x", 1, &pos, NULL);
+ tt_int_op(pos.pos, ==, -1);
+
+ /* set up our chains */
+ evbuffer_add_printf(tmp, "hello"); /* 5 chars */
+ evbuffer_add_buffer(buf, tmp);
+ evbuffer_add_printf(tmp, "foo"); /* 3 chars */
+ evbuffer_add_buffer(buf, tmp);
+ evbuffer_add_printf(tmp, "cat"); /* 3 chars */
+ evbuffer_add_buffer(buf, tmp);
+ evbuffer_add_printf(tmp, "attack");
+ evbuffer_add_buffer(buf, tmp);
+
+ pos = evbuffer_search(buf, "attack", 6, NULL);
+ tt_int_op(pos.pos, ==, 11);
+ pos = evbuffer_search(buf, "attacker", 8, NULL);
+ tt_int_op(pos.pos, ==, -1);
+
+ /* test continuing search */
+ pos = evbuffer_search(buf, "oc", 2, NULL);
+ tt_int_op(pos.pos, ==, 7);
+ pos = evbuffer_search(buf, "cat", 3, &pos);
+ tt_int_op(pos.pos, ==, 8);
+ pos = evbuffer_search(buf, "tacking", 7, &pos);
+ tt_int_op(pos.pos, ==, -1);
+
+ evbuffer_ptr_set(buf, &pos, 5, EVBUFFER_PTR_SET);
+ pos = evbuffer_search(buf, "foo", 3, &pos);
+ tt_int_op(pos.pos, ==, 5);
+
+ evbuffer_ptr_set(buf, &pos, 2, EVBUFFER_PTR_ADD);
+ pos = evbuffer_search(buf, "tat", 3, &pos);
+ tt_int_op(pos.pos, ==, 10);
+
+ /* test bounded search. */
+ /* Set "end" to the first t in "attack". */
+ evbuffer_ptr_set(buf, &end, 12, EVBUFFER_PTR_SET);
+ pos = evbuffer_search_range(buf, "foo", 3, NULL, &end);
+ tt_int_op(pos.pos, ==, 5);
+ pos = evbuffer_search_range(buf, "foocata", 7, NULL, &end);
+ tt_int_op(pos.pos, ==, 5);
+ pos = evbuffer_search_range(buf, "foocatat", 8, NULL, &end);
+ tt_int_op(pos.pos, ==, -1);
+ pos = evbuffer_search_range(buf, "ack", 3, NULL, &end);
+ tt_int_op(pos.pos, ==, -1);
+
+ /* Set "end" after the last byte in the buffer. */
+ tt_assert(evbuffer_ptr_set(buf, &end, 17, EVBUFFER_PTR_SET) == 0);
+
+ pos = evbuffer_search_range(buf, "attack", 6, NULL, &end);
+ tt_int_op(pos.pos, ==, 11);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 11, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "attack", 6, &pos, &end);
+ tt_int_op(pos.pos, ==, 11);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "attack", 6, &pos, &end);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "attack", 6, &pos, NULL);
+ tt_int_op(pos.pos, ==, -1);
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+ if (tmp)
+ evbuffer_free(tmp);
+}
+
+static void
+log_change_callback(struct evbuffer *buffer,
+ const struct evbuffer_cb_info *cbinfo,
+ void *arg)
+{
+
+ size_t old_len = cbinfo->orig_size;
+ size_t new_len = old_len + cbinfo->n_added - cbinfo->n_deleted;
+ struct evbuffer *out = arg;
+ evbuffer_add_printf(out, "%lu->%lu; ", (unsigned long)old_len,
+ (unsigned long)new_len);
+}
+static void
+self_draining_callback(struct evbuffer *evbuffer, size_t old_len,
+ size_t new_len, void *arg)
+{
+ if (new_len > old_len)
+ evbuffer_drain(evbuffer, new_len);
+}
+
+static void
+test_evbuffer_callbacks(void *ptr)
+{
+ struct evbuffer *buf = evbuffer_new();
+ struct evbuffer *buf_out1 = evbuffer_new();
+ struct evbuffer *buf_out2 = evbuffer_new();
+ struct evbuffer_cb_entry *cb1, *cb2;
+
+ tt_assert(buf);
+ tt_assert(buf_out1);
+ tt_assert(buf_out2);
+
+ cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1);
+ cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2);
+
+ /* Let's run through adding and deleting some stuff from the buffer
+ * and turning the callbacks on and off and removing them. The callback
+ * adds a summary of length changes to buf_out1/buf_out2 when called. */
+ /* size: 0-> 36. */
+ evbuffer_add_printf(buf, "The %d magic words are spotty pudding", 2);
+ evbuffer_validate(buf);
+ evbuffer_cb_clear_flags(buf, cb2, EVBUFFER_CB_ENABLED);
+ evbuffer_drain(buf, 10); /*36->26*/
+ evbuffer_validate(buf);
+ evbuffer_prepend(buf, "Hello", 5);/*26->31*/
+ evbuffer_cb_set_flags(buf, cb2, EVBUFFER_CB_ENABLED);
+ evbuffer_add_reference(buf, "Goodbye", 7, NULL, NULL); /*31->38*/
+ evbuffer_remove_cb_entry(buf, cb1);
+ evbuffer_validate(buf);
+ evbuffer_drain(buf, evbuffer_get_length(buf)); /*38->0*/;
+ tt_assert(-1 == evbuffer_remove_cb(buf, log_change_callback, NULL));
+ evbuffer_add(buf, "X", 1); /* 0->1 */
+ tt_assert(!evbuffer_remove_cb(buf, log_change_callback, buf_out2));
+ evbuffer_validate(buf);
+
+ tt_str_op((const char *) evbuffer_pullup(buf_out1, -1), ==,
+ "0->36; 36->26; 26->31; 31->38; ");
+ tt_str_op((const char *) evbuffer_pullup(buf_out2, -1), ==,
+ "0->36; 31->38; 38->0; 0->1; ");
+ evbuffer_drain(buf_out1, evbuffer_get_length(buf_out1));
+ evbuffer_drain(buf_out2, evbuffer_get_length(buf_out2));
+ /* Let's test the obsolete buffer_setcb function too. */
+ cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1);
+ tt_assert(cb1 != NULL);
+ cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2);
+ tt_assert(cb2 != NULL);
+ evbuffer_setcb(buf, self_draining_callback, NULL);
+ evbuffer_add_printf(buf, "This should get drained right away.");
+ tt_uint_op(evbuffer_get_length(buf), ==, 0);
+ tt_uint_op(evbuffer_get_length(buf_out1), ==, 0);
+ tt_uint_op(evbuffer_get_length(buf_out2), ==, 0);
+ evbuffer_setcb(buf, NULL, NULL);
+ evbuffer_add_printf(buf, "This will not.");
+ tt_str_op((const char *) 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. */
+ cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1);
+ cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2);
+ evbuffer_cb_suspend(buf,cb2);
+ evbuffer_prepend(buf,"Hello world",11); /*0->11*/
+ evbuffer_validate(buf);
+ evbuffer_cb_suspend(buf,cb1);
+ evbuffer_add(buf,"more",4); /* 11->15 */
+ evbuffer_cb_unsuspend(buf,cb2);
+ evbuffer_drain(buf, 4); /* 15->11 */
+ evbuffer_cb_unsuspend(buf,cb1);
+ evbuffer_drain(buf, evbuffer_get_length(buf)); /* 11->0 */
+
+ tt_str_op(evbuffer_pullup(buf_out1, -1), ==,
+ "0->11; 11->11; 11->0; ");
+ tt_str_op(evbuffer_pullup(buf_out2, -1), ==,
+ "0->15; 15->11; 11->0; ");
+#endif
+
+ end:
+ if (buf)
+ evbuffer_free(buf);
+ if (buf_out1)
+ evbuffer_free(buf_out1);
+ if (buf_out2)
+ evbuffer_free(buf_out2);
+}
+
+static int ref_done_cb_called_count = 0;
+static void *ref_done_cb_called_with = NULL;
+static const void *ref_done_cb_called_with_data = NULL;
+static size_t ref_done_cb_called_with_len = 0;
+static void ref_done_cb(const void *data, size_t len, void *info)
+{
+ ++ref_done_cb_called_count;
+ ref_done_cb_called_with = info;
+ ref_done_cb_called_with_data = data;
+ ref_done_cb_called_with_len = len;
+}
+
+static void
+test_evbuffer_add_reference(void *ptr)
+{
+ const char chunk1[] = "If you have found the answer to such a problem";
+ const char chunk2[] = "you ought to write it up for publication";
+ /* -- Knuth's "Notes on the Exercises" from TAOCP */
+ char tmp[16];
+ size_t len1 = strlen(chunk1), len2=strlen(chunk2);
+
+ struct evbuffer *buf1 = NULL, *buf2 = NULL;
+
+ buf1 = evbuffer_new();
+ tt_assert(buf1);
+
+ evbuffer_add_reference(buf1, chunk1, len1, ref_done_cb, (void*)111);
+ evbuffer_add(buf1, ", ", 2);
+ evbuffer_add_reference(buf1, chunk2, len2, ref_done_cb, (void*)222);
+ tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2);
+
+ /* Make sure we can drain a little from a reference. */
+ tt_int_op(evbuffer_remove(buf1, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "If you", 6), ==, 0);
+ tt_int_op(evbuffer_remove(buf1, tmp, 5), ==, 5);
+ tt_int_op(memcmp(tmp, " have", 5), ==, 0);
+
+ /* 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. */
+ evbuffer_drain(buf1, len1-11-1); /* remove all but one byte of chunk1 */
+ tt_int_op(ref_done_cb_called_count, ==, 0);
+ evbuffer_remove(buf1, tmp, 1);
+ tt_int_op(tmp[0], ==, 'm');
+ tt_assert(ref_done_cb_called_with == (void*)111);
+ 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 ". */
+ buf2 = evbuffer_new();
+ tt_assert(buf2);
+ tt_int_op(ref_done_cb_called_count, ==, 1);
+ evbuffer_add(buf2, "I ", 2);
+
+ evbuffer_add_buffer(buf2, buf1);
+ tt_int_op(ref_done_cb_called_count, ==, 1);
+ evbuffer_remove(buf2, tmp, 16);
+ tt_int_op(memcmp("I ought to write", tmp, 16), ==, 0);
+ 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. */
+ evbuffer_add(buf1, "You shake and shake the ", 24);
+ evbuffer_add_reference(buf1, "ketchup bottle", 14, ref_done_cb,
+ (void*)3333);
+ evbuffer_add(buf1, ". Nothing comes and then a lot'll.", 35);
+ evbuffer_free(buf1);
+ buf1 = NULL;
+ tt_int_op(ref_done_cb_called_count, ==, 3);
+ tt_assert(ref_done_cb_called_with == (void*)3333);
+
+end:
+ if (buf1)
+ evbuffer_free(buf1);
+ if (buf2)
+ evbuffer_free(buf2);
+}
+
+static void
+test_evbuffer_multicast(void *ptr)
+{
+ const char chunk1[] = "If you have found the answer to such a problem";
+ const char chunk2[] = "you ought to write it up for publication";
+ /* -- Knuth's "Notes on the Exercises" from TAOCP */
+ char tmp[16];
+ size_t len1 = strlen(chunk1), len2=strlen(chunk2);
+
+ struct evbuffer *buf1 = NULL, *buf2 = NULL;
+
+ buf1 = evbuffer_new();
+ tt_assert(buf1);
+
+ evbuffer_add(buf1, chunk1, len1);
+ evbuffer_add(buf1, ", ", 2);
+ evbuffer_add(buf1, chunk2, len2);
+ tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2);
+
+ buf2 = evbuffer_new();
+ tt_assert(buf2);
+
+ tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0);
+ /* nested references are not allowed */
+ tt_int_op(evbuffer_add_buffer_reference(buf2, buf2), ==, -1);
+ tt_int_op(evbuffer_add_buffer_reference(buf1, buf2), ==, -1);
+
+ /* both buffers contain the same amount of data */
+ tt_int_op(evbuffer_get_length(buf1), ==, evbuffer_get_length(buf1));
+
+ /* Make sure we can drain a little from the first buffer. */
+ tt_int_op(evbuffer_remove(buf1, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "If you", 6), ==, 0);
+ tt_int_op(evbuffer_remove(buf1, tmp, 5), ==, 5);
+ tt_int_op(memcmp(tmp, " have", 5), ==, 0);
+
+ /* 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 we can drain a little from the second buffer. */
+ tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "If you", 6), ==, 0);
+ tt_int_op(evbuffer_remove(buf2, tmp, 5), ==, 5);
+ tt_int_op(memcmp(tmp, " have", 5), ==, 0);
+
+ /* Make sure that prepending does not meddle with immutable data */
+ tt_int_op(evbuffer_prepend(buf2, "I have ", 7), ==, 0);
+ tt_int_op(memcmp(chunk1, "If you", 6), ==, 0);
+ evbuffer_validate(buf2);
+
+ /* Make sure the data can be read from the second buffer when the first is freed */
+ evbuffer_free(buf1);
+ buf1 = NULL;
+
+ tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "I have", 6), ==, 0);
+
+ tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, " foun", 6), ==, 0);
+
+end:
+ if (buf1)
+ evbuffer_free(buf1);
+ if (buf2)
+ evbuffer_free(buf2);
+}
+
+static void
+test_evbuffer_multicast_drain(void *ptr)
+{
+ const char chunk1[] = "If you have found the answer to such a problem";
+ const char chunk2[] = "you ought to write it up for publication";
+ /* -- Knuth's "Notes on the Exercises" from TAOCP */
+ size_t len1 = strlen(chunk1), len2=strlen(chunk2);
+
+ struct evbuffer *buf1 = NULL, *buf2 = NULL;
+
+ buf1 = evbuffer_new();
+ tt_assert(buf1);
+
+ evbuffer_add(buf1, chunk1, len1);
+ evbuffer_add(buf1, ", ", 2);
+ evbuffer_add(buf1, chunk2, len2);
+ tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2);
+
+ buf2 = evbuffer_new();
+ tt_assert(buf2);
+
+ tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0);
+ tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2);
+ tt_int_op(evbuffer_drain(buf1, evbuffer_get_length(buf1)), ==, 0);
+ tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2);
+ tt_int_op(evbuffer_drain(buf2, evbuffer_get_length(buf2)), ==, 0);
+ evbuffer_validate(buf1);
+ evbuffer_validate(buf2);
+
+end:
+ if (buf1)
+ evbuffer_free(buf1);
+ if (buf2)
+ evbuffer_free(buf2);
+}
+
+/* Some cases that we didn't get in test_evbuffer() above, for more coverage. */
+static void
+test_evbuffer_prepend(void *ptr)
+{
+ struct evbuffer *buf1 = NULL, *buf2 = NULL;
+ char tmp[128];
+ int n;
+
+ buf1 = evbuffer_new();
+ tt_assert(buf1);
+
+ /* Case 0: The evbuffer is entirely empty. */
+ evbuffer_prepend(buf1, "This string has 29 characters", 29);
+ evbuffer_validate(buf1);
+
+ /* Case 1: Prepend goes entirely in new chunk. */
+ evbuffer_prepend(buf1, "Short.", 6);
+ evbuffer_validate(buf1);
+
+ /* Case 2: prepend goes entirely in first chunk. */
+ evbuffer_drain(buf1, 6+11);
+ evbuffer_prepend(buf1, "it", 2);
+ evbuffer_validate(buf1);
+ tt_assert(!memcmp(buf1->first->buffer+buf1->first->misalign,
+ "it has", 6));
+
+ /* Case 3: prepend is split over multiple chunks. */
+ evbuffer_prepend(buf1, "It is no longer true to say ", 28);
+ evbuffer_validate(buf1);
+ n = evbuffer_remove(buf1, tmp, sizeof(tmp)-1);
+ tt_int_op(n, >=, 0);
+ tmp[n]='\0';
+ tt_str_op(tmp,==,"It is no longer true to say it has 29 characters");
+
+ buf2 = evbuffer_new();
+ tt_assert(buf2);
+
+ /* Case 4: prepend a buffer to an empty buffer. */
+ n = 999;
+ evbuffer_add_printf(buf1, "Here is string %d. ", n++);
+ evbuffer_prepend_buffer(buf2, buf1);
+ evbuffer_validate(buf2);
+
+ /* Case 5: prepend a buffer to a nonempty buffer. */
+ evbuffer_add_printf(buf1, "Here is string %d. ", n++);
+ evbuffer_prepend_buffer(buf2, buf1);
+ evbuffer_validate(buf2);
+ evbuffer_validate(buf1);
+ n = evbuffer_remove(buf2, tmp, sizeof(tmp)-1);
+ tt_int_op(n, >=, 0);
+ tmp[n]='\0';
+ tt_str_op(tmp,==,"Here is string 1000. Here is string 999. ");
+
+end:
+ if (buf1)
+ evbuffer_free(buf1);
+ if (buf2)
+ evbuffer_free(buf2);
+
+}
+
+static void
+test_evbuffer_peek_first_gt(void *info)
+{
+ struct evbuffer *buf = NULL, *tmp_buf = NULL;
+ struct evbuffer_ptr ptr;
+ struct evbuffer_iovec v[2];
+
+ buf = evbuffer_new();
+ tmp_buf = evbuffer_new();
+ evbuffer_add_printf(tmp_buf, "Contents of chunk 100\n");
+ evbuffer_add_buffer(buf, tmp_buf);
+ evbuffer_add_printf(tmp_buf, "Contents of chunk 1\n");
+ evbuffer_add_buffer(buf, tmp_buf);
+
+ evbuffer_ptr_set(buf, &ptr, 0, EVBUFFER_PTR_SET);
+
+ /** The only case that matters*/
+ tt_int_op(evbuffer_peek(buf, -1, &ptr, NULL, 0), ==, 2);
+ /** Just in case */
+ tt_int_op(evbuffer_peek(buf, -1, &ptr, v, 2), ==, 2);
+
+ evbuffer_ptr_set(buf, &ptr, 20, EVBUFFER_PTR_ADD);
+ tt_int_op(evbuffer_peek(buf, -1, &ptr, NULL, 0), ==, 2);
+ tt_int_op(evbuffer_peek(buf, -1, &ptr, v, 2), ==, 2);
+ tt_int_op(evbuffer_peek(buf, 2, &ptr, NULL, 0), ==, 1);
+ tt_int_op(evbuffer_peek(buf, 2, &ptr, v, 2), ==, 1);
+ tt_int_op(evbuffer_peek(buf, 3, &ptr, NULL, 0), ==, 2);
+ tt_int_op(evbuffer_peek(buf, 3, &ptr, v, 2), ==, 2);
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+ if (tmp_buf)
+ evbuffer_free(tmp_buf);
+}
+
+static void
+test_evbuffer_peek(void *info)
+{
+ struct evbuffer *buf = NULL, *tmp_buf = NULL;
+ int i;
+ struct evbuffer_iovec v[20];
+ struct evbuffer_ptr ptr;
+
+#define tt_iov_eq(v, s) \
+ tt_int_op((v)->iov_len, ==, strlen(s)); \
+ tt_assert(!memcmp((v)->iov_base, (s), strlen(s)))
+
+ /* Let's make a very fragmented buffer. */
+ buf = evbuffer_new();
+ tmp_buf = evbuffer_new();
+ for (i = 0; i < 16; ++i) {
+ evbuffer_add_printf(tmp_buf, "Contents of chunk [%d]\n", i);
+ evbuffer_add_buffer(buf, tmp_buf);
+ }
+
+ /* How many chunks do we need for everything? */
+ i = evbuffer_peek(buf, -1, NULL, NULL, 0);
+ tt_int_op(i, ==, 16);
+
+ /* Simple peek: get everything. */
+ i = evbuffer_peek(buf, -1, NULL, v, 20);
+ tt_int_op(i, ==, 16); /* we used only 16 chunks. */
+ tt_iov_eq(&v[0], "Contents of chunk [0]\n");
+ tt_iov_eq(&v[3], "Contents of chunk [3]\n");
+ tt_iov_eq(&v[12], "Contents of chunk [12]\n");
+ tt_iov_eq(&v[15], "Contents of chunk [15]\n");
+
+ /* Just get one chunk worth. */
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(buf, -1, NULL, v, 1);
+ tt_int_op(i, ==, 1);
+ tt_iov_eq(&v[0], "Contents of chunk [0]\n");
+ tt_assert(v[1].iov_base == NULL);
+
+ /* Suppose we want at least the first 40 bytes. */
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(buf, 40, NULL, v, 16);
+ tt_int_op(i, ==, 2);
+ tt_iov_eq(&v[0], "Contents of chunk [0]\n");
+ tt_iov_eq(&v[1], "Contents of chunk [1]\n");
+ tt_assert(v[2].iov_base == NULL);
+
+ /* How many chunks do we need for 100 bytes? */
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(buf, 100, NULL, NULL, 0);
+ tt_int_op(i, ==, 5);
+ tt_assert(v[0].iov_base == NULL);
+
+ /* Now we ask for more bytes than we provide chunks for */
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(buf, 60, NULL, v, 1);
+ tt_int_op(i, ==, 3);
+ tt_iov_eq(&v[0], "Contents of chunk [0]\n");
+ tt_assert(v[1].iov_base == NULL);
+
+ /* Now we ask for more bytes than the buffer has. */
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(buf, 65536, NULL, v, 20);
+ tt_int_op(i, ==, 16); /* we used only 16 chunks. */
+ tt_iov_eq(&v[0], "Contents of chunk [0]\n");
+ tt_iov_eq(&v[3], "Contents of chunk [3]\n");
+ tt_iov_eq(&v[12], "Contents of chunk [12]\n");
+ tt_iov_eq(&v[15], "Contents of chunk [15]\n");
+ tt_assert(v[16].iov_base == NULL);
+
+ /* What happens if we try an empty buffer? */
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(tmp_buf, -1, NULL, v, 20);
+ tt_int_op(i, ==, 0);
+ tt_assert(v[0].iov_base == NULL);
+ memset(v, 0, sizeof(v));
+ i = evbuffer_peek(tmp_buf, 50, NULL, v, 20);
+ tt_int_op(i, ==, 0);
+ tt_assert(v[0].iov_base == NULL);
+
+ /* Okay, now time to have fun with pointers. */
+ memset(v, 0, sizeof(v));
+ evbuffer_ptr_set(buf, &ptr, 30, EVBUFFER_PTR_SET);
+ i = evbuffer_peek(buf, 50, &ptr, v, 20);
+ tt_int_op(i, ==, 3);
+ tt_iov_eq(&v[0], " of chunk [1]\n");
+ tt_iov_eq(&v[1], "Contents of chunk [2]\n");
+ tt_iov_eq(&v[2], "Contents of chunk [3]\n"); /*more than we asked for*/
+
+ /* advance to the start of another chain. */
+ memset(v, 0, sizeof(v));
+ evbuffer_ptr_set(buf, &ptr, 14, EVBUFFER_PTR_ADD);
+ i = evbuffer_peek(buf, 44, &ptr, v, 20);
+ tt_int_op(i, ==, 2);
+ tt_iov_eq(&v[0], "Contents of chunk [2]\n");
+ tt_iov_eq(&v[1], "Contents of chunk [3]\n"); /*more than we asked for*/
+
+ /* peek at the end of the buffer */
+ memset(v, 0, sizeof(v));
+ tt_assert(evbuffer_ptr_set(buf, &ptr, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0);
+ i = evbuffer_peek(buf, 44, &ptr, v, 20);
+ tt_int_op(i, ==, 0);
+ tt_assert(v[0].iov_base == NULL);
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+ if (tmp_buf)
+ evbuffer_free(tmp_buf);
+}
+
+/* Check whether evbuffer freezing works right. This is called twice,
+ once with the argument "start" and once with the argument "end".
+ When we test "start", we freeze the start of an evbuffer and make sure
+ that modifying the start of the buffer doesn't work. When we test
+ "end", we freeze the end of an evbuffer and make sure that modifying
+ the end of the buffer doesn't work.
+ */
+static void
+test_evbuffer_freeze(void *ptr)
+{
+ struct evbuffer *buf = NULL, *tmp_buf=NULL;
+ const char string[] = /* Year's End, Richard Wilbur */
+ "I've known the wind by water banks to shake\n"
+ "The late leaves down, which frozen where they fell\n"
+ "And held in ice as dancers in a spell\n"
+ "Fluttered all winter long into a lake...";
+ const int start = !strcmp(ptr, "start");
+ char *cp;
+ char charbuf[128];
+ int r;
+ size_t orig_length;
+ struct evbuffer_iovec v[1];
+
+ if (!start)
+ tt_str_op(ptr, ==, "end");
+
+ buf = evbuffer_new();
+ tmp_buf = evbuffer_new();
+ tt_assert(tmp_buf);
+
+ evbuffer_add(buf, string, strlen(string));
+ evbuffer_freeze(buf, start); /* Freeze the start or the end.*/
+
+#define FREEZE_EQ(a, startcase, endcase) \
+ do { \
+ if (start) { \
+ tt_int_op((a), ==, (startcase)); \
+ } else { \
+ tt_int_op((a), ==, (endcase)); \
+ } \
+ } while (0)
+
+
+ orig_length = evbuffer_get_length(buf);
+
+ /* These functions all manipulate the end of buf. */
+ r = evbuffer_add(buf, "abc", 0);
+ FREEZE_EQ(r, 0, -1);
+ r = evbuffer_reserve_space(buf, 10, v, 1);
+ FREEZE_EQ(r, 1, -1);
+ if (r == 1) {
+ memset(v[0].iov_base, 'X', 10);
+ v[0].iov_len = 10;
+ }
+ r = evbuffer_commit_space(buf, v, 1);
+ FREEZE_EQ(r, 0, -1);
+ r = evbuffer_add_reference(buf, string, 5, NULL, NULL);
+ FREEZE_EQ(r, 0, -1);
+ r = evbuffer_add_printf(buf, "Hello %s", "world");
+ FREEZE_EQ(r, 11, -1);
+ /* TODO: test add_buffer, add_file, read */
+
+ if (!start)
+ tt_int_op(orig_length, ==, evbuffer_get_length(buf));
+
+ orig_length = evbuffer_get_length(buf);
+
+ /* These functions all manipulate the start of buf. */
+ r = evbuffer_remove(buf, charbuf, 1);
+ FREEZE_EQ(r, -1, 1);
+ r = evbuffer_drain(buf, 3);
+ FREEZE_EQ(r, -1, 0);
+ r = evbuffer_prepend(buf, "dummy", 5);
+ FREEZE_EQ(r, -1, 0);
+ cp = evbuffer_readln(buf, NULL, EVBUFFER_EOL_LF);
+ FREEZE_EQ(cp==NULL, 1, 0);
+ if (cp)
+ free(cp);
+ /* TODO: Test remove_buffer, add_buffer, write, prepend_buffer */
+
+ if (start)
+ tt_int_op(orig_length, ==, evbuffer_get_length(buf));
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+
+ if (tmp_buf)
+ evbuffer_free(tmp_buf);
+}
+
+static void
+test_evbuffer_add_iovec(void * ptr)
+{
+ struct evbuffer * buf = NULL;
+ struct evbuffer_iovec vec[4];
+ const char * data[] = {
+ "Guilt resembles a sword with two edges.",
+ "On the one hand, it cuts for Justice, imposing practical morality upon those who fear it.",
+ "Conscience does not always adhere to rational judgment.",
+ "Guilt is always a self-imposed burden, but it is not always rightly imposed."
+ /* -- R.A. Salvatore, _Sojurn_ */
+ };
+ size_t expected_length = 0;
+ size_t returned_length = 0;
+ int i;
+
+ buf = evbuffer_new();
+
+ tt_assert(buf);
+
+ for (i = 0; i < 4; i++) {
+ vec[i].iov_len = strlen(data[i]);
+ vec[i].iov_base = (char*) data[i];
+ expected_length += vec[i].iov_len;
+ }
+
+ returned_length = evbuffer_add_iovec(buf, vec, 4);
+
+ tt_int_op(returned_length, ==, evbuffer_get_length(buf));
+ tt_int_op(evbuffer_get_length(buf), ==, expected_length);
+
+ for (i = 0; i < 4; i++) {
+ char charbuf[1024];
+
+ memset(charbuf, 0, 1024);
+ evbuffer_remove(buf, charbuf, strlen(data[i]));
+ tt_assert(strcmp(charbuf, data[i]) == 0);
+ }
+
+ tt_assert(evbuffer_get_length(buf) == 0);
+end:
+ if (buf) {
+ evbuffer_free(buf);
+ }
+}
+
+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[1024];
+ 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)
+{
+ return testcase->setup_data;
+}
+static int
+cleanup_passthrough(const struct testcase_t *testcase, void *ptr)
+{
+ (void) ptr;
+ return 1;
+}
+
+static const struct testcase_setup_t nil_setup = {
+ setup_passthrough,
+ cleanup_passthrough
+};
+
+struct testcase_t evbuffer_testcases[] = {
+ { "evbuffer", test_evbuffer, 0, NULL, NULL },
+ { "remove_buffer_with_empty", test_evbuffer_remove_buffer_with_empty, 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" },
+ { "expand", test_evbuffer_expand, 0, NULL, NULL },
+ { "reference", test_evbuffer_reference, 0, NULL, NULL },
+ { "iterative", test_evbuffer_iterative, 0, NULL, NULL },
+ { "readln", test_evbuffer_readln, TT_NO_LOGS, &basic_setup, NULL },
+ { "search_eol", test_evbuffer_search_eol, 0, NULL, NULL },
+ { "find", test_evbuffer_find, 0, NULL, NULL },
+ { "ptr_set", test_evbuffer_ptr_set, 0, NULL, NULL },
+ { "search", test_evbuffer_search, 0, NULL, NULL },
+ { "callbacks", test_evbuffer_callbacks, 0, NULL, NULL },
+ { "add_reference", test_evbuffer_add_reference, 0, NULL, NULL },
+ { "multicast", test_evbuffer_multicast, 0, NULL, NULL },
+ { "multicast_drain", test_evbuffer_multicast_drain, 0, NULL, NULL },
+ { "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL },
+ { "peek", test_evbuffer_peek, 0, NULL, NULL },
+ { "peek_first_gt", test_evbuffer_peek_first_gt, 0, NULL, NULL },
+ { "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},
+ { "file_segment_add_cleanup_cb", test_evbuffer_file_segment_add_cleanup_cb, 0, NULL, NULL },
+
+#define ADDFILE_TEST(name, parameters) \
+ { name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \
+ &basic_setup, (void*)(parameters) }
+
+#define ADDFILE_TEST_GROUP(name, parameters) \
+ ADDFILE_TEST(name "_sendfile", "sendfile " parameters), \
+ ADDFILE_TEST(name "_mmap", "mmap " parameters), \
+ ADDFILE_TEST(name "_linear", "linear " parameters)
+
+ ADDFILE_TEST_GROUP("add_file", ""),
+ ADDFILE_TEST("add_file_nosegment", "default nosegment"),
+
+ ADDFILE_TEST_GROUP("add_big_file", "bigfile"),
+ ADDFILE_TEST("add_big_file_nosegment", "default nosegment bigfile"),
+
+ ADDFILE_TEST_GROUP("add_file_offset", "bigfile map_offset"),
+ ADDFILE_TEST("add_file_offset_nosegment",
+ "default nosegment bigfile map_offset"),
+
+ ADDFILE_TEST_GROUP("add_file_offset2", "bigfile offset_in_segment"),
+
+ ADDFILE_TEST_GROUP("add_file_offset3",
+ "bigfile offset_in_segment map_offset"),
+
+ END_OF_TESTCASES
+};