From d77bad5a29eb5e3f5348e9d70bdd6115c13158f3 Mon Sep 17 00:00:00 2001
From: Nir Soffer <nirsof@gmail.com>
Date: Thu, 9 Jun 2011 03:26:37 +0300
Subject: [PATCH 3/3] Allow searching up to the end of the buffer

evbuffer_ptr can be set now to the special "end of the buffer" point,
where pos is equal to the buffer length.

This fixes evbuffer_search_range when you search for the last byte with
a non-null end ptr. Previously you could find only the byte before the
last byte, using a ptr set to the last byte.

Signed-off-by: Nir Soffer <nirsof@gmail.com>
---
 buffer.c              |   23 ++++++++++++++++++++++-
 test/regress_buffer.c |   48 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/buffer.c b/buffer.c
index 98de504..76f4aaf 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1336,6 +1336,9 @@ evbuffer_getchr(struct evbuffer_ptr *it)
 	struct evbuffer_chain *chain = it->_internal.chain;
 	size_t off = it->_internal.pos_in_chain;
 
+	if (chain == NULL)
+		return -1; /* XXX Better invalid char value? */
+
 	return chain->buffer[chain->misalign + off];
 }
 
@@ -1348,6 +1351,14 @@ evbuffer_search_eol(struct evbuffer *buffer,
 	size_t extra_drain = 0;
 	int ok = 0;
 
+	/* Avoid locking in trivial edge cases */
+	if (start && start->pos >= buffer->total_len) {
+		it.pos = -1;
+		if (eol_len_out)
+			*eol_len_out = extra_drain;
+		return it;
+	}
+
 	EVBUFFER_LOCK(buffer);
 
 	if (start) {
@@ -2309,6 +2320,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);
 
@@ -2335,14 +2347,19 @@ evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
 	if (chain) {
 		pos->_internal.chain = chain;
 		pos->_internal.pos_in_chain = position + left;
+	} else if (left == 0) {
+		/* The first byte in the (nonexistent) chain after the last chain */
+		pos->_internal.chain = NULL;
+		pos->_internal.pos_in_chain = 0;
 	} else {
 		pos->_internal.chain = NULL;
 		pos->pos = -1;
+		result = -1;
 	}
 
 	EVBUFFER_UNLOCK(buf);
 
-	return chain != NULL ? 0 : -1;
+	return result;
 }
 
 /**
@@ -2463,6 +2480,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->pos >= buffer->total_len)
+        return 0;
+
 	EVBUFFER_LOCK(buffer);
 
 	if (start_at) {
diff --git a/test/regress_buffer.c b/test/regress_buffer.c
index ddb0ee7..fac285b 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);
@@ -1281,9 +1310,19 @@ test_evbuffer_search(void *ptr)
 	tt_int_op(pos.pos, ==, -1);
     
 	/* Point "end" after the last byte in the buffer */
-	tt_int_op(evbuffer_ptr_set(buf, &end, 17, EVBUFFER_PTR_SET), ==, 0);
+	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)
@@ -1634,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);
-- 
1.7.3.5

