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)
{
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.
*/
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.
*/
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
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)
{ "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 },