diff --git a/buffer.c b/buffer.c index f4a19438..df6402e8 100644 --- a/buffer.c +++ b/buffer.c @@ -132,6 +132,13 @@ #define CHAIN_PINNED(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_ANY) != 0) #define CHAIN_PINNED_R(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_R) != 0) +/* evbuffer_ptr support */ +#define PTR_NOT_FOUND(ptr) do { \ + (ptr)->pos = -1; \ + (ptr)->_internal.chain = NULL; \ + (ptr)->_internal.pos_in_chain = 0; \ +} while (0) + static void evbuffer_chain_align(struct evbuffer_chain *chain); static int evbuffer_chain_should_realign(struct evbuffer_chain *chain, size_t datalen); @@ -1302,7 +1309,7 @@ evbuffer_strspn( size_t i = ptr->_internal.pos_in_chain; if (!chain) - return -1; + return 0; while (1) { char *buffer = (char *)chain->buffer + chain->misalign; @@ -1333,13 +1340,16 @@ evbuffer_strspn( } -static inline char +static inline int evbuffer_getchr(struct evbuffer_ptr *it) { struct evbuffer_chain *chain = it->_internal.chain; size_t off = it->_internal.pos_in_chain; - return chain->buffer[chain->misalign + off]; + if (chain == NULL) + return -1; + + return (unsigned char)chain->buffer[chain->misalign + off]; } struct evbuffer_ptr @@ -1351,6 +1361,14 @@ evbuffer_search_eol(struct evbuffer *buffer, size_t extra_drain = 0; int ok = 0; + /* Avoid locking in trivial edge cases */ + if (start && start->_internal.chain == NULL) { + PTR_NOT_FOUND(&it); + if (eol_len_out) + *eol_len_out = extra_drain; + return it; + } + EVBUFFER_LOCK(buffer); if (start) { @@ -1411,9 +1429,8 @@ evbuffer_search_eol(struct evbuffer *buffer, done: EVBUFFER_UNLOCK(buffer); - if (!ok) { - it.pos = -1; - } + if (!ok) + PTR_NOT_FOUND(&it); if (eol_len_out) *eol_len_out = extra_drain; @@ -2294,7 +2311,7 @@ evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos, { if (howfar > (size_t)pos->pos) return -1; - if (howfar <= pos->_internal.pos_in_chain) { + if (pos->_internal.chain && howfar <= pos->_internal.pos_in_chain) { pos->_internal.pos_in_chain -= howfar; pos->pos -= howfar; return 0; @@ -2312,6 +2329,7 @@ evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos, { size_t left = position; struct evbuffer_chain *chain = NULL; + int result = 0; EVBUFFER_LOCK(buf); @@ -2338,14 +2356,18 @@ evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos, if (chain) { pos->_internal.chain = chain; pos->_internal.pos_in_chain = position + left; - } else { + } else if (left == 0) { + /* The first byte in the (nonexistent) chain after the last chain */ pos->_internal.chain = NULL; - pos->pos = -1; + pos->_internal.pos_in_chain = 0; + } else { + PTR_NOT_FOUND(pos); + result = -1; } EVBUFFER_UNLOCK(buf); - return chain != NULL ? 0 : -1; + return result; } /** @@ -2450,8 +2472,7 @@ evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, con } not_found: - pos.pos = -1; - pos._internal.chain = NULL; + PTR_NOT_FOUND(&pos); done: EVBUFFER_UNLOCK(buffer); return pos; @@ -2466,6 +2487,10 @@ evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, int idx = 0; ev_ssize_t len_so_far = 0; + /* Avoid locking in trivial edge cases */ + if (start_at && start_at->_internal.chain == NULL) + return 0; + EVBUFFER_LOCK(buffer); if (start_at) { diff --git a/include/event2/buffer.h b/include/event2/buffer.h index b5acbb5b..66cd3d13 100644 --- a/include/event2/buffer.h +++ b/include/event2/buffer.h @@ -105,11 +105,22 @@ struct evbuffer 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. + + 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. + + An evbuffer_ptr can represent any position from the start of a buffer up + to a position immediately after the end of a buffer. + + @see evbuffer_ptr_set() */ struct evbuffer_ptr { ev_ssize_t pos; - /* Do not alter the values of fields. */ + /* Do not alter or rely on the values of fields: they are for internal + * use */ struct { void *chain; size_t pos_in_chain; @@ -421,17 +432,9 @@ int evbuffer_add_reference(struct evbuffer *outbuf, @param outbuf the output buffer @param fd the file descriptor -<<<<<<< HEAD - @param off the offset from which to read data + @param offset the offset from which to read data @param length how much data to read, or -1 to read as much as possible. (-1 requires that 'fd' support fstat.) -||||||| merged common ancestors - @param off the offset from which to read data - @param length how much data to read -======= - @param offset the offset from which to read data - @param length how much data to read ->>>>>>> origin/patches-2.0 @return 0 if successful, or -1 if an error occurred */ @@ -657,9 +660,18 @@ enum evbuffer_ptr_how { /** Sets the search pointer in the buffer to position. - If evbuffer_ptr is not initialized. This function can only be called + There are two ways to use this function: you can call + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET) + to move 'pos' to a position 'N' bytes after the start of the buffer, or + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET) + to move 'pos' forward by 'N' bytes. + + If evbuffer_ptr is not initialized, this function can only be called with EVBUFFER_PTR_SET. + An evbuffer_ptr can represent any position from the start of the buffer to + a position immediately after the end of the buffer. + @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 diff --git a/test/regress_buffer.c b/test/regress_buffer.c index 9c4b47c7..03a27ead 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -1090,6 +1090,12 @@ test_evbuffer_search_eol(void *ptr) tt_int_op(ptr2.pos, ==, 11); tt_int_op(eol_len, ==, 1); + tt_assert(evbuffer_ptr_set(buf, &ptr1, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0); + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF); + tt_int_op(ptr2.pos, ==, -1); + tt_int_op(eol_len, ==, 0); + end: evbuffer_free(buf); } @@ -1190,6 +1196,15 @@ test_evbuffer_ptr_set(void *ptr) struct evbuffer_ptr pos; struct evbuffer_iovec v[1]; + tt_int_op(evbuffer_get_length(buf), ==, 0); + + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + tt_assert(pos.pos == 0); + tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_ADD) == -1); + tt_assert(pos.pos == -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_SET) == -1); + tt_assert(pos.pos == -1); + /* create some chains */ evbuffer_reserve_space(buf, 5000, v, 1); v[0].iov_len = 5000; @@ -1222,6 +1237,8 @@ test_evbuffer_ptr_set(void *ptr) 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) == 0); + tt_assert(pos.pos == 12000); tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == -1); tt_assert(pos.pos == -1); @@ -1237,6 +1254,18 @@ test_evbuffer_search(void *ptr) struct evbuffer *tmp = evbuffer_new(); struct evbuffer_ptr pos, end; + pos = evbuffer_search(buf, "x", 1, NULL); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search(buf, "x", 1, &pos); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "x", 1, &pos, &pos); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "x", 1, &pos, NULL); + tt_int_op(pos.pos, ==, -1); + /* set up our chains */ evbuffer_add_printf(tmp, "hello"); /* 5 chars */ evbuffer_add_buffer(buf, tmp); @@ -1280,6 +1309,20 @@ test_evbuffer_search(void *ptr) pos = evbuffer_search_range(buf, "ack", 3, NULL, &end); tt_int_op(pos.pos, ==, -1); + /* Set "end" after the last byte in the buffer. */ + tt_assert(evbuffer_ptr_set(buf, &end, 17, EVBUFFER_PTR_SET) == 0); + + pos = evbuffer_search_range(buf, "attack", 6, NULL, &end); + tt_int_op(pos.pos, ==, 11); + tt_assert(evbuffer_ptr_set(buf, &pos, 11, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "attack", 6, &pos, &end); + tt_int_op(pos.pos, ==, 11); + tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "attack", 6, &pos, &end); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "attack", 6, &pos, NULL); + tt_int_op(pos.pos, ==, -1); end: if (buf) @@ -1630,6 +1673,13 @@ test_evbuffer_peek(void *info) 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*/ + /* peek at the end of the buffer */ + memset(v, 0, sizeof(v)); + tt_assert(evbuffer_ptr_set(buf, &ptr, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0); + i = evbuffer_peek(buf, 44, &ptr, v, 20); + tt_int_op(i, ==, 0); + tt_assert(v[0].iov_base == NULL); + end: if (buf) evbuffer_free(buf);