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 *
|
unsigned char *
|
||||||
evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len)
|
evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len)
|
||||||
{
|
{
|
||||||
unsigned char *search = evbuffer_pullup(buffer, -1);
|
unsigned char *search;
|
||||||
unsigned char *end = search + buffer->total_len;
|
struct evbuffer_ptr ptr;
|
||||||
unsigned char *p;
|
|
||||||
|
|
||||||
while (search < end &&
|
ptr = evbuffer_search(buffer, (const char *)what, len, NULL);
|
||||||
(p = memchr(search, *what, end - search)) != NULL) {
|
if (ptr.pos < 0)
|
||||||
if (p + len > end)
|
return (NULL);
|
||||||
break;
|
|
||||||
if (memcmp(p, what, len) == 0)
|
search = evbuffer_pullup(buffer, ptr.pos + len);
|
||||||
return (p);
|
return search + ptr.pos;
|
||||||
search = p + 1;
|
}
|
||||||
|
|
||||||
|
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
|
int
|
||||||
evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
|
evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
|
@ -70,6 +70,21 @@ extern "C" {
|
|||||||
|
|
||||||
struct evbuffer;
|
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.
|
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);
|
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
|
If evbuffer_ptr is not initalized. This function can only be called
|
||||||
@param what the string to be searched for
|
with EVBUFFER_PTR_SET.
|
||||||
@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);
|
|
||||||
|
|
||||||
|
@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
|
/** Type definition for a callback that is invoked whenever data is added or
|
||||||
removed from an evbuffer.
|
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);
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -542,6 +542,85 @@ end:
|
|||||||
evbuffer_free(buf);
|
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
|
static void
|
||||||
log_change_callback(struct evbuffer *buffer, size_t old_len, size_t new_len,
|
log_change_callback(struct evbuffer *buffer, size_t old_len, size_t new_len,
|
||||||
void *arg)
|
void *arg)
|
||||||
@ -775,6 +854,8 @@ struct testcase_t evbuffer_testcases[] = {
|
|||||||
{ "iterative", test_evbuffer_iterative, 0, NULL, NULL },
|
{ "iterative", test_evbuffer_iterative, 0, NULL, NULL },
|
||||||
{ "readln", test_evbuffer_readln, 0, NULL, NULL },
|
{ "readln", test_evbuffer_readln, 0, NULL, NULL },
|
||||||
{ "find", test_evbuffer_find, 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 },
|
{ "callbacks", test_evbuffer_callbacks, 0, NULL, NULL },
|
||||||
{ "add_reference", test_evbuffer_add_reference, 0, NULL, NULL },
|
{ "add_reference", test_evbuffer_add_reference, 0, NULL, NULL },
|
||||||
{ "prepend", test_evbuffer_prepend, 0, NULL, NULL },
|
{ "prepend", test_evbuffer_prepend, 0, NULL, NULL },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user