From d41347722a8599d6daf82a765efe53de573afd2b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 31 Jul 2009 17:35:42 +0000 Subject: [PATCH] Refactor evbuffer_readln() into a search-for-eol function and an extract-line function. svn:r1404 --- ChangeLog | 1 + buffer.c | 118 ++++++++++++++++++++++++++-------------- include/event2/buffer.h | 18 ++++++ 3 files changed, 95 insertions(+), 42 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9e5be063..ff8bec50 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ Changes in 2.0.3-alpha: o Fix a bug in bufferevent_socket_connect() when the connection succeeds too quickly. o Export an evutil_sockaddr_cmp() to compare to sockaddr objects for equality. o Add a bufferevent_get_enabled() to tell what a bufferevent has been configured to do. + o Add an evbuffer_search_eol() function to locate the end of a line nondestructively. Changes in 2.0.2-alpha: diff --git a/buffer.c b/buffer.c index ae1b8253..e54d7df4 100644 --- a/buffer.c +++ b/buffer.c @@ -139,6 +139,8 @@ static int use_mmap = 1; static void evbuffer_chain_align(struct evbuffer_chain *chain); static void evbuffer_deferred_callback(struct deferred_cb *cb, void *arg); +static int evbuffer_ptr_memcmp(const struct evbuffer *buf, + const struct evbuffer_ptr *pos, const char *mem, size_t len); static struct evbuffer_chain * evbuffer_chain_new(size_t size) @@ -1070,67 +1072,101 @@ evbuffer_getchr(struct evbuffer_ptr *it) return chain->buffer[chain->misalign + off]; } -char * -evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, - enum evbuffer_eol_style eol_style) +struct evbuffer_ptr +evbuffer_search_eol(struct evbuffer *buffer, + struct evbuffer_ptr *start, size_t *eol_len_out, + enum evbuffer_eol_style eol_style) { - struct evbuffer_ptr it; - char *line, chr; - unsigned int n_to_copy, extra_drain; - int count = 0; - char *result = NULL; + struct evbuffer_ptr it, it2; + size_t extra_drain = 0; + int ok = 0; - EVBUFFER_LOCK(buffer, EVTHREAD_WRITE); + EVBUFFER_LOCK(buffer, EVTHREAD_READ); - if (buffer->freeze_start) { - goto done; + if (start) { + memcpy(&it, start, sizeof(it)); + } else { + it.pos = 0; + it._internal.chain = buffer->first; + it._internal.pos_in_chain = 0; } - if (evbuffer_ptr_set(buffer, &it, 0, EVBUFFER_PTR_SET) < 0) - goto done; - /* the eol_style determines our first stop character and how many * characters we are going to drain afterwards. */ switch (eol_style) { case EVBUFFER_EOL_ANY: - count = evbuffer_strpbrk(&it, "\r\n"); - if (count == -1) + if (evbuffer_strpbrk(&it, "\r\n") < 0) goto done; - - n_to_copy = count; - extra_drain = evbuffer_strspn(&it, "\r\n"); + memcpy(&it2, &it, sizeof(it)); + extra_drain = evbuffer_strspn(&it2, "\r\n"); break; case EVBUFFER_EOL_CRLF_STRICT: { - int tmp; - while ((tmp = evbuffer_strchr(&it, '\r')) != -1) { - count += tmp; - if (evbuffer_ptr_set(buffer, &it, 1, EVBUFFER_PTR_ADD) - < 0) - goto done; - chr = evbuffer_getchr(&it); - if (chr == '\n') { - n_to_copy = count; - break; - } - ++count; - } - if (tmp == -1) + it = evbuffer_search(buffer, "\r\n", 2, &it); + if (it.pos < 0) goto done; extra_drain = 2; break; } case EVBUFFER_EOL_CRLF: - /* we might strip a preceding '\r' */ + while (1) { + if (evbuffer_strpbrk(&it, "\r\n") < 0) + goto done; + if (evbuffer_getchr(&it) == '\n') { + extra_drain = 1; + break; + } else if (!evbuffer_ptr_memcmp( + buffer, &it, "\r\n", 2)) { + extra_drain = 2; + break; + } else { + if (evbuffer_ptr_set(buffer, &it, 1, + EVBUFFER_PTR_ADD)<0) + goto done; + } + } + break; case EVBUFFER_EOL_LF: - if ((count = evbuffer_strchr(&it, '\n')) == -1) + if (evbuffer_strchr(&it, '\n') < 0) goto done; - n_to_copy = count; extra_drain = 1; break; default: goto done; } + ok = 1; +done: + EVBUFFER_UNLOCK(buffer, EVTHREAD_READ); + + if (!ok) { + it.pos = -1; + } + if (eol_len_out) + *eol_len_out = extra_drain; + + return it; +} + +char * +evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style) +{ + struct evbuffer_ptr it; + char *line; + size_t n_to_copy=0, extra_drain=0; + char *result = NULL; + + EVBUFFER_LOCK(buffer, EVTHREAD_WRITE); + + if (buffer->freeze_start) { + goto done; + } + + it = evbuffer_search_eol(buffer, NULL, &extra_drain, eol_style); + if (it.pos < 0) + goto done; + n_to_copy = it.pos; + if ((line = mm_malloc(n_to_copy+1)) == NULL) { event_warn("%s: out of memory\n", __func__); evbuffer_drain(buffer, n_to_copy + extra_drain); @@ -1138,18 +1174,16 @@ evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, } evbuffer_remove(buffer, line, n_to_copy); - if (eol_style == EVBUFFER_EOL_CRLF && - n_to_copy && line[n_to_copy-1] == '\r') - --n_to_copy; line[n_to_copy] = '\0'; evbuffer_drain(buffer, extra_drain); - if (n_read_out) - *n_read_out = (size_t)n_to_copy; - result = line; done: EVBUFFER_UNLOCK(buffer, EVTHREAD_WRITE); + + if (n_read_out) + *n_read_out = result ? n_to_copy : 0; + return result; } diff --git a/include/event2/buffer.h b/include/event2/buffer.h index 41e6a619..50ea90f9 100644 --- a/include/event2/buffer.h +++ b/include/event2/buffer.h @@ -480,6 +480,24 @@ int evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos, size_t position, enum evbuffer_ptr_how how); +/** + Search for an end-of-line string within an evbuffer. + + @param buffer the evbuffer to be searched + @param start NULL or a pointer to a valid struct evbuffer_ptr to start + searching at. + @param eol_len_out If non-NULL, the pointed-to value will be set to + the length of the end-of-line string. + @param eol_style The kind of EOL to look for; see evbuffer_readln() for + more information + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence EOL in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer, + struct evbuffer_ptr *start, size_t *eol_len_out, + enum evbuffer_eol_style eol_style); + /** Structure passed to an evbuffer callback */ struct evbuffer_cb_info { /** The size of */ -- 2.40.0