mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-14 06:49:35 -04:00
Add a new improved search function.
The old evbuffer_find didn't allow iterative searching, and forced us to repack the buffer completely every time we searched in it. The new evbuffer_search addresses both of these. As a side-effect, the evbuffer_find implementation is now a little more efficient. svn:r1130
This commit is contained in:
parent
0afb1f7ffb
commit
f90500a5df
143
buffer.c
143
buffer.c
@ -1340,22 +1340,143 @@ evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd)
|
||||
unsigned char *
|
||||
evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len)
|
||||
{
|
||||
unsigned char *search = evbuffer_pullup(buffer, -1);
|
||||
unsigned char *end = search + buffer->total_len;
|
||||
unsigned char *p;
|
||||
unsigned char *search;
|
||||
struct evbuffer_ptr ptr;
|
||||
|
||||
while (search < end &&
|
||||
(p = memchr(search, *what, end - search)) != NULL) {
|
||||
if (p + len > end)
|
||||
break;
|
||||
if (memcmp(p, what, len) == 0)
|
||||
return (p);
|
||||
search = p + 1;
|
||||
ptr = evbuffer_search(buffer, (const char *)what, len, NULL);
|
||||
if (ptr.pos < 0)
|
||||
return (NULL);
|
||||
|
||||
search = evbuffer_pullup(buffer, ptr.pos + len);
|
||||
return search + ptr.pos;
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
|
||||
size_t position, enum evbuffer_ptr_how how)
|
||||
{
|
||||
size_t left = position;
|
||||
struct evbuffer_chain *chain = NULL;
|
||||
|
||||
switch (how) {
|
||||
case EVBUFFER_PTR_SET:
|
||||
chain = buf->first;
|
||||
pos->pos = position;
|
||||
position = 0;
|
||||
break;
|
||||
case EVBUFFER_PTR_ADD:
|
||||
/* this avoids iterating over all previous chains if
|
||||
we just want to advance the position */
|
||||
chain = pos->_internal.chain;
|
||||
pos->pos += position;
|
||||
position = pos->_internal.pos_in_chain;
|
||||
break;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
while (chain && position + left >= chain->off) {
|
||||
left -= chain->off - position;
|
||||
chain = chain->next;
|
||||
position = 0;
|
||||
}
|
||||
if (chain) {
|
||||
pos->_internal.chain = chain;
|
||||
pos->_internal.pos_in_chain = position + left;
|
||||
} else {
|
||||
pos->_internal.chain = NULL;
|
||||
pos->pos = -1;
|
||||
}
|
||||
|
||||
return chain != NULL ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
Compare the bytes in buf at position pos to the len bytes in mem. Return
|
||||
less than 0, 0, or greater than 0 as memcmp.
|
||||
*/
|
||||
static int
|
||||
evbuffer_ptr_memcmp(const struct evbuffer *buf, const struct evbuffer_ptr *pos,
|
||||
const char *mem, size_t len)
|
||||
{
|
||||
struct evbuffer_chain *chain;
|
||||
size_t position;
|
||||
int r;
|
||||
|
||||
if (pos->pos + len > buf->total_len)
|
||||
return -1;
|
||||
|
||||
chain = pos->_internal.chain;
|
||||
position = pos->_internal.pos_in_chain;
|
||||
while (len && chain) {
|
||||
size_t n_comparable;
|
||||
if (len + position > chain->off)
|
||||
n_comparable = chain->off - position;
|
||||
else
|
||||
n_comparable = len;
|
||||
r = memcmp(chain->buffer + chain->misalign + position, mem,
|
||||
n_comparable);
|
||||
if (r)
|
||||
return r;
|
||||
mem += n_comparable;
|
||||
len -= n_comparable;
|
||||
position = 0;
|
||||
chain = chain->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct evbuffer_ptr
|
||||
evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start)
|
||||
{
|
||||
struct evbuffer_ptr pos;
|
||||
struct evbuffer_chain *chain;
|
||||
const unsigned char *p;
|
||||
char first;
|
||||
|
||||
if (start) {
|
||||
memcpy(&pos, start, sizeof(pos));
|
||||
chain = pos._internal.chain;
|
||||
} else {
|
||||
pos.pos = 0;
|
||||
chain = pos._internal.chain = buffer->first;
|
||||
pos._internal.pos_in_chain = 0;
|
||||
}
|
||||
|
||||
if (!len)
|
||||
return pos;
|
||||
|
||||
first = what[0];
|
||||
|
||||
while (chain) {
|
||||
const unsigned char *start_at =
|
||||
chain->buffer + chain->misalign +
|
||||
pos._internal.pos_in_chain;
|
||||
p = memchr(start_at, first,
|
||||
chain->off - pos._internal.pos_in_chain);
|
||||
if (p) {
|
||||
pos.pos += p - start_at;
|
||||
pos._internal.pos_in_chain += p - start_at;
|
||||
if (!evbuffer_ptr_memcmp(buffer, &pos, what, len))
|
||||
return pos;
|
||||
++pos.pos;
|
||||
++pos._internal.pos_in_chain;
|
||||
if (pos._internal.pos_in_chain == chain->off) {
|
||||
chain = pos._internal.chain = chain->next;
|
||||
pos._internal.pos_in_chain = 0;
|
||||
}
|
||||
} else {
|
||||
pos.pos += chain->off - pos._internal.pos_in_chain;
|
||||
chain = pos._internal.chain = chain->next;
|
||||
pos._internal.pos_in_chain = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pos.pos = -1;
|
||||
pos._internal.chain = NULL;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
|
||||
{
|
||||
|
@ -70,6 +70,21 @@ extern "C" {
|
||||
|
||||
struct evbuffer;
|
||||
|
||||
/** Points to a position within an evbuffer. Used when repeatedly searching
|
||||
through a buffer. Calls to any function that modifies or re-packs the
|
||||
buffer contents may invalidate all evbuffer_ptrs for that buffer. Do not
|
||||
modify these values except with evbuffer_ptr_set.
|
||||
*/
|
||||
struct evbuffer_ptr {
|
||||
ssize_t pos;
|
||||
|
||||
/* Do not alter the values of fields. */
|
||||
struct {
|
||||
void *chain;
|
||||
size_t pos_in_chain;
|
||||
} _internal;
|
||||
};
|
||||
|
||||
/**
|
||||
Allocate storage for a new evbuffer.
|
||||
|
||||
@ -345,17 +360,42 @@ int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
|
||||
*/
|
||||
int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);
|
||||
|
||||
/**
|
||||
Search for a string within an evbuffer.
|
||||
|
||||
@param buffer the evbuffer to be searched
|
||||
@param what the string to be searched for
|
||||
@param len the length of the search string
|
||||
@param start NULL or a pointer to a valid struct evbuffer_ptr.
|
||||
@return a struct evbuffer_ptr whose 'pos' field has the offset of the
|
||||
first occurrence of the string in the buffer after 'start'. The 'pos'
|
||||
field of the result is -1 if the string was not found.
|
||||
*/
|
||||
struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start);
|
||||
|
||||
enum evbuffer_ptr_how {
|
||||
/** Sets the pointer to the position; can be called on with an
|
||||
uninitalized evbuffer_ptr. */
|
||||
EVBUFFER_PTR_SET,
|
||||
/** Advances the pointer by adding to the current position. */
|
||||
EVBUFFER_PTR_ADD
|
||||
};
|
||||
|
||||
/**
|
||||
Find a string within an evbuffer.
|
||||
Sets the search pointer in the buffer to positiion.
|
||||
|
||||
@param buffer the evbuffer to be searched
|
||||
@param what the string to be searched for
|
||||
@param len the length of the search string
|
||||
@return a pointer to the beginning of the search string, or NULL if the search failed.
|
||||
*/
|
||||
unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len);
|
||||
If evbuffer_ptr is not initalized. This function can only be called
|
||||
with EVBUFFER_PTR_SET.
|
||||
|
||||
@param buffer the evbuffer to be search
|
||||
@param ptr a pointer to a struct evbuffer_ptr
|
||||
@param position the position at which to start the next search
|
||||
@param how determines how the pointer should be manipulated.
|
||||
@returns 0 on success or -1 otherwise
|
||||
*/
|
||||
int
|
||||
evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos,
|
||||
size_t position, enum evbuffer_ptr_how how);
|
||||
|
||||
/** Type definition for a callback that is invoked whenever data is added or
|
||||
removed from an evbuffer.
|
||||
|
@ -43,5 +43,16 @@ char *evbuffer_readline(struct evbuffer *buffer);
|
||||
*/
|
||||
void evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);
|
||||
|
||||
|
||||
/**
|
||||
Find a string within an evbuffer.
|
||||
|
||||
@param buffer the evbuffer to be searched
|
||||
@param what the string to be searched for
|
||||
@param len the length of the search string
|
||||
@return a pointer to the beginning of the search string, or NULL if the search failed.
|
||||
*/
|
||||
unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -542,6 +542,85 @@ end:
|
||||
evbuffer_free(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
test_evbuffer_ptr_set(void *ptr)
|
||||
{
|
||||
struct evbuffer *buf = evbuffer_new();
|
||||
struct evbuffer_ptr pos;
|
||||
|
||||
/* create some chains */
|
||||
evbuffer_reserve_space(buf, 5000);
|
||||
evbuffer_commit_space(buf, 5000);
|
||||
evbuffer_reserve_space(buf, 4000);
|
||||
evbuffer_commit_space(buf, 4000);
|
||||
evbuffer_reserve_space(buf, 3000);
|
||||
evbuffer_commit_space(buf, 3000);
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
end:
|
||||
if (buf)
|
||||
evbuffer_free(buf);
|
||||
if (tmp)
|
||||
evbuffer_free(tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
log_change_callback(struct evbuffer *buffer, size_t old_len, size_t new_len,
|
||||
void *arg)
|
||||
@ -775,6 +854,8 @@ struct testcase_t evbuffer_testcases[] = {
|
||||
{ "iterative", test_evbuffer_iterative, 0, NULL, NULL },
|
||||
{ "readln", test_evbuffer_readln, 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, 0, NULL, NULL },
|
||||
|
Loading…
x
Reference in New Issue
Block a user