e985b92992
Change-Id: Ic75f4cac5eb07ffaba8f97d10673d7d7e2b1762d
1678 lines
47 KiB
C
1678 lines
47 KiB
C
/* $NetBSD: regress_buffer.c,v 1.2 2013/04/11 16:56:42 christos Exp $ */
|
|
/*
|
|
* 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/cdefs.h>
|
|
__RCSID("$NetBSD: regress_buffer.c,v 1.2 2013/04/11 16:56:42 christos Exp $");
|
|
|
|
#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 ii, j, r;
|
|
|
|
for (ii = 0; ii < 3; ++ii) {
|
|
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 xpair[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, xpair) == -1)
|
|
tt_abort_msg("ersatz_socketpair failed");
|
|
#else
|
|
if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, xpair) == -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, xpair[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, xpair[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 (xpair[0] >= 0)
|
|
evutil_closesocket(xpair[0]);
|
|
if (xpair[1] >= 0)
|
|
evutil_closesocket(xpair[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) {
|
|
evbuffer_add_printf(buf, "%*.*s", j, j, 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, __UNCONST(test1), strlen(test1));
|
|
evbuffer_validate(buf);
|
|
evbuffer_drain(buf, strlen(test1));
|
|
evbuffer_validate(buf);
|
|
evbuffer_add(buf, __UNCONST(test2), strlen(test2));
|
|
evbuffer_validate(buf);
|
|
p = evbuffer_find(buf, __UNCONST("\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, __UNCONST("xy"), 2);
|
|
tt_want(p == NULL);
|
|
|
|
/* simple test for match at end of allocated buffer */
|
|
p = evbuffer_find(buf, __UNCONST("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 (/*CONSTCOND*/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, __UNCONST("add") },
|
|
{ "reserve_many3", test_evbuffer_reserve_many, 0, &nil_setup, __UNCONST("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, __UNCONST("start") },
|
|
{ "freeze_end", test_evbuffer_freeze, 0, &nil_setup, __UNCONST("end") },
|
|
/* TODO: need a temp file implementation for Windows */
|
|
{ "add_file_sendfile", test_evbuffer_add_file, TT_FORK, &nil_setup,
|
|
__UNCONST("sendfile") },
|
|
{ "add_file_mmap", test_evbuffer_add_file, TT_FORK, &nil_setup,
|
|
__UNCONST("mmap") },
|
|
{ "add_file_linear", test_evbuffer_add_file, TT_FORK, &nil_setup,
|
|
__UNCONST("linear") },
|
|
|
|
END_OF_TESTCASES
|
|
};
|