Squashed 'third_party/libevent/' content from commit d8ceae8

Change-Id: I84e1b23561eb225b9f288717b028e6bc9e5a27e1
git-subtree-dir: third_party/libevent
git-subtree-split: d8ceae81b83f3d0a56e2f41d4c429b2702a8e302
diff --git a/test/regress_buffer.c b/test/regress_buffer.c
new file mode 100644
index 0000000..d7cc538
--- /dev/null
+++ b/test/regress_buffer.c
@@ -0,0 +1,1677 @@
+/*
+ * 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.
+ */
+
+#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 "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) != 0);
+
+	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);
+}
+
+int _evbuffer_testing_use_sendfile(void);
+int _evbuffer_testing_use_mmap(void);
+int _evbuffer_testing_use_linear_file_access(void);
+
+static void
+test_evbuffer_add_file(void *ptr)
+{
+	const char *impl = ptr;
+	struct evbuffer *src = evbuffer_new();
+	const char *data = "this is what we add as file system data.";
+	size_t datalen;
+	const char *compare;
+	int fd = -1;
+	evutil_socket_t pair[2] = {-1, -1};
+	int r=0, n_written=0;
+
+	/* Add a test for a big file. XXXX */
+
+	tt_assert(impl);
+	if (!strcmp(impl, "sendfile")) {
+		if (!_evbuffer_testing_use_sendfile())
+			tt_skip();
+		TT_BLATHER(("Using sendfile-based implementaion"));
+	} else if (!strcmp(impl, "mmap")) {
+		if (!_evbuffer_testing_use_mmap())
+			tt_skip();
+		TT_BLATHER(("Using mmap-based implementaion"));
+	} else if (!strcmp(impl, "linear")) {
+		if (!_evbuffer_testing_use_linear_file_access())
+			tt_skip();
+		TT_BLATHER(("Using read-based implementaion"));
+	} else {
+		TT_DIE(("Didn't recognize the implementation"));
+	}
+
+	/* 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
+
+	datalen = strlen(data);
+	fd = regress_make_tmpfile(data, datalen);
+
+	tt_assert(fd != -1);
+
+	tt_assert(evbuffer_add_file(src, fd, 0, datalen) != -1);
+
+	evbuffer_validate(src);
+
+	while (evbuffer_get_length(src) &&
+	    (r = evbuffer_write(src, pair[0])) > 0) {
+		evbuffer_validate(src);
+		n_written += r;
+	}
+	tt_int_op(r, !=, -1);
+	tt_int_op(n_written, ==, datalen);
+
+	evbuffer_validate(src);
+	tt_int_op(evbuffer_read(src, pair[1], (int)strlen(data)), ==, datalen);
+	evbuffer_validate(src);
+	compare = (char *)evbuffer_pullup(src, datalen);
+	tt_assert(compare != NULL);
+	if (memcmp(compare, data, datalen))
+		tt_abort_msg("Data from add_file differs.");
+
+	evbuffer_validate(src);
+ end:
+	if (pair[0] >= 0)
+		evutil_closesocket(pair[0]);
+	if (pair[1] >= 0)
+		evutil_closesocket(pair[1]);
+	evbuffer_free(src);
+}
+
+#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 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);
+
+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)
+{
+	u_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, (u_char*)test1, strlen(test1));
+	evbuffer_validate(buf);
+	evbuffer_drain(buf, strlen(test1));
+	evbuffer_validate(buf);
+	evbuffer_add(buf, (u_char*)test2, strlen(test2));
+	evbuffer_validate(buf);
+	p = evbuffer_find(buf, (u_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, (u_char *)test3, EVBUFFER_INITIAL_LENGTH);
+	evbuffer_validate(buf);
+	p = evbuffer_find(buf, (u_char *)"xy", 2);
+	tt_want(p == NULL);
+
+	/* simple test for match at end of allocated buffer */
+	p = evbuffer_find(buf, (u_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);
+
+	/* 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) == -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);
+
+	/* 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);
+
+
+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(evbuffer_pullup(buf_out1, -1), ==,
+		  "0->36; 36->26; 26->31; 31->38; ");
+	tt_str_op(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(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.", 42);
+	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);
+}
+
+/* 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);
+	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);
+	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(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*/
+
+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 == 0) {
+		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 *
+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 },
+	{ "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL },
+	{ "peek", test_evbuffer_peek, 0, NULL, NULL },
+	{ "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" },
+	{ "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" },
+	/* TODO: need a temp file implementation for Windows */
+	{ "add_file_sendfile", test_evbuffer_add_file, TT_FORK, &nil_setup,
+	  (void*)"sendfile" },
+	{ "add_file_mmap", test_evbuffer_add_file, TT_FORK, &nil_setup,
+	  (void*)"mmap" },
+	{ "add_file_linear", test_evbuffer_add_file, TT_FORK, &nil_setup,
+	  (void*)"linear" },
+
+	END_OF_TESTCASES
+};