From: <np...@us...> - 2008-02-28 02:47:50
|
Revision: 674 http://levent.svn.sourceforge.net/levent/?rev=674&view=rev Author: nprovos Date: 2008-02-27 18:47:43 -0800 (Wed, 27 Feb 2008) Log Message: ----------- improved code for evbuffer; avoids memcpy Modified Paths: -------------- trunk/libevent/ChangeLog trunk/libevent/Doxyfile trunk/libevent/Makefile.am trunk/libevent/buffer.c trunk/libevent/configure.in trunk/libevent/evbuffer.c trunk/libevent/event.h trunk/libevent/event_rpcgen.py trunk/libevent/event_tagging.c trunk/libevent/http.c trunk/libevent/test/regress.c Added Paths: ----------- trunk/libevent/evbuffer-internal.h trunk/libevent/include/ trunk/libevent/include/Makefile.am trunk/libevent/include/event2/ trunk/libevent/include/event2/buffer.h Modified: trunk/libevent/ChangeLog =================================================================== --- trunk/libevent/ChangeLog 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/ChangeLog 2008-02-28 02:47:43 UTC (rev 674) @@ -53,6 +53,7 @@ o increase listen queue for http sockets to 128; if that is not enough the evhttp_accpet_socket() api can be used with a prepared socket. o Patch from Tani Hosokawa: make some functions in http.c threadsafe. o test support for PUT/DELETE requests; from Josh Rotenberg + o rewrite of the evbuffer code to reduce memory copies Changes in 1.4.0: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. Modified: trunk/libevent/Doxyfile =================================================================== --- trunk/libevent/Doxyfile 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/Doxyfile 2008-02-28 02:47:43 UTC (rev 674) @@ -54,7 +54,8 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = event.h evdns.h evhttp.h evrpc.h +INPUT = event.h evdns.h evhttp.h evrpc.h \ + include/event2/buffer.h #--------------------------------------------------------------------------- # configuration options related to the HTML output Modified: trunk/libevent/Makefile.am =================================================================== --- trunk/libevent/Makefile.am 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/Makefile.am 2008-02-28 02:47:43 UTC (rev 674) @@ -49,7 +49,7 @@ lib_LTLIBRARIES = libevent.la libevent_core.la libevent_extra.la -SUBDIRS = . sample test +SUBDIRS = . include sample test if BUILD_WIN32 @@ -100,7 +100,7 @@ include_HEADERS = event.h evhttp.h evdns.h evrpc.h evutil.h event-config.h -INCLUDES = -I$(srcdir)/compat $(SYS_INCLUDES) +INCLUDES = -I$(srcdir)/compat -I$(srcdir)/include $(SYS_INCLUDES) man_MANS = event.3 evdns.3 Modified: trunk/libevent/buffer.c =================================================================== --- trunk/libevent/buffer.c 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/buffer.c 2008-02-28 02:47:43 UTC (rev 674) @@ -45,6 +45,10 @@ #include <sys/time.h> #endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif @@ -64,7 +68,32 @@ #include "event.h" #include "config.h" #include "mm-internal.h" +#include "evbuffer-internal.h" +static struct evbuffer_chain * +evbuffer_chain_new(size_t size) +{ + struct evbuffer_chain *chain; + size_t to_alloc; + + size += EVBUFFER_CHAIN_SIZE; + + /* get the next largest memory that can hold the buffer */ + to_alloc = MIN_BUFFER_SIZE; + while (to_alloc < size) + to_alloc <<= 1; + + /* we get everything in one chunk */ + if ((chain = event_malloc(to_alloc)) == NULL) + return (NULL); + + memset(chain, 0, EVBUFFER_CHAIN_SIZE); + + chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE; + + return (chain); +} + struct evbuffer * evbuffer_new(void) { @@ -78,134 +107,274 @@ void evbuffer_free(struct evbuffer *buffer) { - if (buffer->buffer != NULL) - event_free(buffer->buffer); + struct evbuffer_chain *chain, *next; + for (chain = buffer->first; chain != NULL; chain = next) { + next = chain->next; + event_free(chain); + } event_free(buffer); } -/* - * This is a destructive add. The data from one buffer moves into - * the other buffer. - */ +size_t +evbuffer_length(struct evbuffer *buffer) +{ + return (buffer->total_len); +} -#define SWAP(x,y) do { \ - (x)->buffer = (y)->buffer; \ - (x)->misalign = (y)->misalign; \ - (x)->totallen = (y)->totallen; \ - (x)->off = (y)->off; \ -} while (0) - int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) { - int res; + size_t out_total_len = outbuf->total_len; + size_t in_total_len = inbuf->total_len; - /* Short cut for better performance */ - if (outbuf->off == 0) { - struct evbuffer tmp; - size_t oldoff = inbuf->off; + if (out_total_len == 0) { + outbuf->first = inbuf->first; + outbuf->last = inbuf->last; + outbuf->total_len = in_total_len; + } else { + outbuf->last->next = inbuf->first; + outbuf->last = inbuf->last; + outbuf->total_len += in_total_len; + } - /* Swap them directly */ - SWAP(&tmp, outbuf); - SWAP(outbuf, inbuf); - SWAP(inbuf, &tmp); + /* remove everything from inbuf */ + inbuf->first = NULL; + inbuf->last = NULL; + inbuf->total_len = 0; - /* - * Optimization comes with a price; we need to notify the - * buffer if necessary of the changes. oldoff is the amount - * of data that we transfered from inbuf to outbuf - */ - if (inbuf->off != oldoff && inbuf->cb != NULL) - (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg); - if (oldoff && outbuf->cb != NULL) - (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg); + if (inbuf->cb != NULL && inbuf->total_len != in_total_len) + (*inbuf->cb)(inbuf, in_total_len, inbuf->total_len, + inbuf->cbarg); + if (outbuf->cb != NULL && outbuf->total_len != out_total_len) + (*outbuf->cb)(outbuf, out_total_len, outbuf->total_len, + outbuf->cbarg); - return (0); - } + return (0); +} - res = evbuffer_add(outbuf, inbuf->buffer + inbuf->misalign, inbuf->off); - if (res == 0) { - /* We drain the input buffer on success */ - evbuffer_drain(inbuf, inbuf->off); +void +evbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) +{ + size_t out_total_len = outbuf->total_len; + size_t in_total_len = inbuf->total_len; + + if (!in_total_len) + return; + + if (out_total_len == 0) { + outbuf->first = inbuf->first; + outbuf->last = inbuf->last; + outbuf->total_len = in_total_len; + } else { + inbuf->last->next = outbuf->first; + outbuf->first = inbuf->first; + outbuf->total_len += in_total_len; } - return (res); + /* remove everything from inbuf */ + inbuf->first = NULL; + inbuf->last = NULL; + inbuf->total_len = 0; + + if (inbuf->cb != NULL && inbuf->total_len != in_total_len) + (*inbuf->cb)(inbuf, in_total_len, inbuf->total_len, + inbuf->cbarg); + if (outbuf->cb != NULL && outbuf->total_len != out_total_len) + (*outbuf->cb)(outbuf, out_total_len, outbuf->total_len, + outbuf->cbarg); } -int -evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) +void +evbuffer_drain(struct evbuffer *buf, size_t len) { - char *buffer; - size_t space; - size_t oldoff = buf->off; - int sz; - va_list aq; + struct evbuffer_chain *chain, *next; + size_t old_len = buf->total_len; - /* make sure that at least some space is available */ - evbuffer_expand(buf, 64); - for (;;) { - size_t used = buf->misalign + buf->off; - buffer = (char *)buf->buffer + buf->misalign + buf->off; - assert(buf->totallen >= used); - space = buf->totallen - used; + if (old_len == 0) + return; -#ifndef va_copy -#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) -#endif - va_copy(aq, ap); + if (len >= old_len) { + for (chain = buf->first; chain != NULL; chain = next) { + next = chain->next; -#ifdef WIN32 - sz = vsnprintf(buffer, space - 1, fmt, aq); - buffer[space - 1] = '\0'; -#else - sz = vsnprintf(buffer, space, fmt, aq); -#endif + event_free(chain); + } - va_end(aq); + buf->total_len = 0; + buf->first = buf->last = NULL; + } else { + buf->total_len -= len; - if (sz < 0) - return (-1); - if (sz < space) { - buf->off += sz; - if (buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); - return (sz); + for (chain = buf->first; len >= chain->off; chain = next) { + next = chain->next; + len -= chain->off; + + event_free(chain); } - if (evbuffer_expand(buf, sz + 1) == -1) - return (-1); + buf->first = chain; + chain->misalign += len; + chain->off -= len; } - /* NOTREACHED */ + + /* Tell someone about changes in this buffer */ + if (buf->cb != NULL && buf->total_len != old_len) + (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg); + } +/* Reads data from an event buffer and drains the bytes read */ + int -evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) { - int res = -1; - va_list ap; + struct evbuffer_chain *chain = buf->first, *tmp; + size_t nread; - va_start(ap, fmt); - res = evbuffer_add_vprintf(buf, fmt, ap); - va_end(ap); + if (datlen >= buf->total_len) + datlen = buf->total_len; - return (res); + if (datlen == 0) + return (0); + + nread = datlen; + + while (datlen && datlen >= chain->off) { + memcpy(data, chain->buffer + chain->misalign, chain->off); + data += chain->off; + datlen -= chain->off; + + tmp = chain; + chain = chain->next; + event_free(tmp); + } + + buf->first = chain; + if (chain == NULL) + buf->last = NULL; + + if (datlen) { + memcpy(data, chain->buffer + chain->misalign, datlen); + chain->misalign += datlen; + chain->off -= datlen; + } + + buf->total_len -= nread; + + if (buf->cb != NULL && nread) + (*buf->cb)(buf, buf->total_len + nread, buf->total_len, + buf->cbarg); + + return (nread); } -/* Reads data from an event buffer and drains the bytes read */ - +/* reads data from the src buffer to the dst buffer, avoids memcpy as + * possible. */ int -evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) +evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, + size_t datlen) { - size_t nread = datlen; - if (nread >= buf->off) - nread = buf->off; + struct evbuffer_chain *chain = src->first, *previous = chain; + size_t nread = 0; - memcpy(data, buf->buffer + buf->misalign, nread); - evbuffer_drain(buf, nread); - + /* short-cut if there is no more data buffered */ + if (datlen >= src->total_len) { + datlen = src->total_len; + evbuffer_add_buffer(dst, src); + return (datlen); + } + + if (datlen == 0) + return (0); + + /* removes chains if possible */ + while (chain->off <= datlen) { + nread += chain->off; + datlen -= chain->off; + previous = chain; + chain = chain->next; + } + + if (nread) { + /* we can remove the chain */ + if (dst->first == NULL) { + dst->first = src->first; + } else { + dst->last->next = src->first; + } + dst->last = previous; + previous->next = NULL; + src->first = chain; + + dst->total_len += nread; + } + + /* we know that there is more data in the src buffer than + * we want to read, so we manually drain the chain */ + evbuffer_add(dst, chain->buffer + chain->misalign, datlen); + chain->misalign += datlen; + chain->off -= datlen; + nread += datlen; + + src->total_len -= nread; + + if (dst->cb != NULL && nread) + (*dst->cb)(dst, dst->total_len - nread, dst->total_len, + dst->cbarg); + if (src->cb != NULL && nread) + (*src->cb)(src, src->total_len + nread, src->total_len, + src->cbarg); + return (nread); } +u_char * +evbuffer_pullup(struct evbuffer *buf, int size) +{ + struct evbuffer_chain *chain = buf->first, *next, *tmp; + void *buffer; + + if (size == -1) + size = buf->total_len; + if (size == 0 || size > buf->total_len) + return (NULL); + + if (chain->off >= size) + return chain->buffer + chain->misalign; + + if ((tmp = evbuffer_chain_new(size)) == NULL) { + fprintf(stderr, "%s: out of memory\n", __func__); + return (NULL); + } + + tmp->off = size; + buffer = tmp->buffer; + + for (chain = buf->first; + chain != NULL && size >= chain->off; chain = next) { + next = chain->next; + + memcpy(buffer, chain->buffer + chain->misalign, chain->off); + size -= chain->off; + buffer += chain->off; + + event_free(chain); + } + + if (chain != NULL) { + memcpy(buffer, chain->buffer + chain->misalign, size); + chain->misalign += size; + chain->off -= size; + } else { + buf->last = tmp; + } + + buf->first = tmp; + tmp->next = chain; + + return (tmp->buffer); +} + /* * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. * The returned buffer needs to be freed by the called. @@ -216,83 +385,167 @@ return evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY); } +struct evbuffer_iterator { + struct evbuffer_chain *chain; + int off; +}; + +static inline int +evbuffer_strchr(struct evbuffer_iterator *it, const char chr) +{ + struct evbuffer_chain *chain = it->chain; + int i = it->off, count = 0; + while (chain != NULL) { + char *buffer = (char *)chain->buffer + chain->misalign; + for (; i < chain->off; ++i, ++count) { + if (buffer[i] == chr) { + it->chain = chain; + it->off = i; + return (count); + } + } + i = 0; + chain = chain->next; + } + + return (-1); +} + +static inline int +evbuffer_strpbrk(struct evbuffer_iterator *it, const char *chrset) +{ + struct evbuffer_chain *chain = it->chain; + int i = it->off, count = 0; + while (chain != NULL) { + char *buffer = (char *)chain->buffer + chain->misalign; + for (; i < chain->off; ++i, ++count) { + const char *p = chrset; + while (*p) { + if (buffer[i] == *p++) { + it->chain = chain; + it->off = i; + return (count); + } + } + } + i = 0; + chain = chain->next; + } + + return (-1); +} + +static inline int +evbuffer_strspn( + struct evbuffer_chain *chain, int i, const char *chrset) +{ + int count = 0; + while (chain != NULL) { + char *buffer = (char *)chain->buffer + chain->misalign; + for (; i < chain->off; ++i) { + const char *p = chrset; + while (*p) { + if (buffer[i] == *p++) + goto next; + } + return count; + next: + ++count; + } + i = 0; + chain = chain->next; + } + + return (count); +} + +static inline int +evbuffer_getchr(struct evbuffer_iterator *it, char *pchr) +{ + struct evbuffer_chain *chain = it->chain; + int off = it->off; + + while (off >= chain->off) { + off -= chain->off; + chain = chain->next; + if (chain == NULL) + return (-1); + } + + *pchr = chain->buffer[chain->misalign + off]; + + it->chain = chain; + it->off = off; + + return (0); +} + char * evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, enum evbuffer_eol_style eol_style) { - u_char *data = EVBUFFER_DATA(buffer); - u_char *start_of_eol, *end_of_eol; - size_t len = EVBUFFER_LENGTH(buffer); - char *line; - unsigned int i, n_to_copy, n_to_drain; + struct evbuffer_iterator it; + char *line, chr; + unsigned int n_to_copy, extra_drain; + int count = 0; - /* depending on eol_style, set start_of_eol to the first character - * in the newline, and end_of_eol to one after the last character. */ + it.chain = buffer->first; + it.off = 0; + + /* 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: - for (i = 0; i < len; i++) { - if (data[i] == '\r' || data[i] == '\n') - break; - } - if (i == len) + count = evbuffer_strpbrk(&it, "\r\n"); + if (count == -1) return (NULL); - start_of_eol = data+i; - ++i; - for ( ; i < len; i++) { - if (data[i] != '\r' && data[i] != '\n') - break; - } - end_of_eol = data+i; + + n_to_copy = count; + extra_drain = evbuffer_strspn(it.chain, it.off, "\r\n"); break; - case EVBUFFER_EOL_CRLF: - end_of_eol = memchr(data, '\n', len); - if (!end_of_eol) - return (NULL); - if (end_of_eol > data && *(end_of_eol-1) == '\r') - start_of_eol = end_of_eol - 1; - else - start_of_eol = end_of_eol; - end_of_eol++; /*point to one after the LF. */ - break; case EVBUFFER_EOL_CRLF_STRICT: { - u_char *cp = data; - while ((cp = memchr(cp, '\r', len-(cp-data)))) { - if (cp < data+len-1 && *(cp+1) == '\n') + int tmp; + while ((tmp = evbuffer_strchr(&it, '\r')) != -1) { + count += tmp; + ++it.off; + if (evbuffer_getchr(&it, &chr) == -1) + return (NULL); + if (chr == '\n') { + n_to_copy = count; break; - if (++cp >= data+len) { - cp = NULL; - break; } + ++count; } - if (!cp) + if (tmp == -1) return (NULL); - start_of_eol = cp; - end_of_eol = cp+2; + extra_drain = 2; break; } + case EVBUFFER_EOL_CRLF: + /* we might strip a preceding '\r' */ case EVBUFFER_EOL_LF: - start_of_eol = memchr(data, '\n', len); - if (!start_of_eol) + if ((count = evbuffer_strchr(&it, '\n')) == -1) return (NULL); - end_of_eol = start_of_eol + 1; + n_to_copy = count; + extra_drain = 1; break; default: return (NULL); } - n_to_copy = start_of_eol - data; - n_to_drain = end_of_eol - data; - if ((line = event_malloc(n_to_copy+1)) == NULL) { fprintf(stderr, "%s: out of memory\n", __func__); - evbuffer_drain(buffer, n_to_drain); + evbuffer_drain(buffer, n_to_copy + extra_drain); return (NULL); } - memcpy(line, data, n_to_copy); + 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, n_to_drain); + evbuffer_drain(buffer, extra_drain); if (n_read_out) *n_read_out = (size_t)n_to_copy; @@ -301,91 +554,146 @@ /* Adds data to an event buffer */ -static void -evbuffer_align(struct evbuffer *buf) +int +evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen) { - memmove(buf->buffer, buf->buffer + buf->misalign, buf->off); - buf->misalign = 0; -} + struct evbuffer_chain *chain = buf->last; + size_t old_len = buf->total_len, remain, to_alloc; -/* Expands the available space in the event buffer to at least datlen */ + if (chain == NULL) { + if (evbuffer_expand(buf, datlen) == -1) + return (-1); + chain = buf->last; + } -int -evbuffer_expand(struct evbuffer *buf, size_t datlen) -{ - size_t need = buf->misalign + buf->off + datlen; + remain = chain->buffer_len - chain->misalign - chain->off; + if (remain >= datlen) { + /* we have enough space */ + memcpy(chain->buffer + chain->misalign + chain->off, + data, datlen); + chain->off += datlen; + buf->total_len += datlen; + goto out; + } - /* If we can fit all the data, then we don't have to do anything */ - if (buf->totallen >= need) - return (0); + /* we need to add another chain */ + to_alloc = chain->buffer_len << 1; + if (datlen > to_alloc) + to_alloc = datlen; + chain->next = evbuffer_chain_new(to_alloc); + if (chain->next == NULL) + return (-1); + buf->last = chain->next; + buf->total_len += datlen; - /* - * If the misalignment fulfills our data needs, we just force an - * alignment to happen. Afterwards, we have enough space. - */ - if (buf->misalign >= datlen) { - evbuffer_align(buf); - } else { - void *newbuf; - size_t length = buf->totallen; + memcpy(chain->buffer + chain->misalign + chain->off, + data, remain); + chain->off += remain; + + data += remain; + datlen -= remain; - if (length < 256) - length = 256; - while (length < need) - length <<= 1; + chain = chain->next; + memcpy(chain->buffer, data, datlen); + chain->off = datlen; - if (buf->misalign) - evbuffer_align(buf); - if ((newbuf = event_realloc(buf->buffer, length)) == NULL) - return (-1); +out: + if (buf->cb != NULL && datlen) + (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg); - buf->buffer = newbuf; - buf->totallen = length; - } - return (0); } int -evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen) +evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen) { - size_t need = buf->misalign + buf->off + datlen; - size_t oldoff = buf->off; + struct evbuffer_chain *chain = buf->first; + size_t old_len = buf->total_len; - if (buf->totallen < need) { + if (chain == NULL) { if (evbuffer_expand(buf, datlen) == -1) return (-1); + chain = buf->first; + chain->misalign = chain->buffer_len; } - memcpy(buf->buffer + buf->misalign + buf->off, data, datlen); - buf->off += datlen; + if (chain->misalign >= datlen) { + /* we have enough space */ + memcpy(chain->buffer + chain->misalign - datlen, + data, datlen); + chain->off += datlen; + chain->misalign -= datlen; + } else { + struct evbuffer_chain *tmp; + /* we need to add another chain */ + if ((tmp = evbuffer_chain_new(datlen)) == NULL) + return (-1); + buf->first = tmp; + tmp->next = chain; - if (datlen && buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + tmp->off = datlen; + tmp->misalign = tmp->buffer_len - datlen; + memcpy(tmp->buffer + tmp->misalign, data, datlen); + } + buf->total_len += datlen; + + if (buf->cb != NULL && datlen) + (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg); + return (0); } -void -evbuffer_drain(struct evbuffer *buf, size_t len) +static void +evbuffer_chain_align(struct evbuffer_chain *chain) { - size_t oldoff = buf->off; + memmove(chain->buffer, chain->buffer + chain->misalign, chain->off); + chain->misalign = 0; +} - if (len >= buf->off) { - buf->off = 0; - buf->misalign = 0; - goto done; +/* Expands the available space in the event buffer to at least datlen */ + +int +evbuffer_expand(struct evbuffer *buf, size_t datlen) +{ + struct evbuffer_chain *chain = buf->last; + size_t need, length; + + if (chain == NULL) { + chain = evbuffer_chain_new(datlen); + if (chain == NULL) + return (-1); + + buf->first = buf->last = chain; + return (0); } - buf->misalign += len; + need = chain->misalign + chain->off + datlen; - buf->off -= len; + /* If we can fit all the data, then we don't have to do anything */ + if (chain->buffer_len >= need) + return (0); - done: - /* Tell someone about changes in this buffer */ - if (buf->off != oldoff && buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + /* + * If the misalignment fulfills our data needs, we just force an + * alignment to happen. Afterwards, we have enough space. + */ + if (chain->misalign >= datlen) { + evbuffer_chain_align(chain); + return (0); + } + /* avoid a memcpy if we can just a new chain */ + length = chain->buffer_len << 1; + if (length < datlen) + length = datlen; + struct evbuffer_chain *tmp = evbuffer_chain_new(length); + if (tmp == NULL) + return (-1); + chain->next = tmp; + buf->last = tmp; + + return (0); } /* @@ -397,8 +705,9 @@ int evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) { + struct evbuffer_chain *chain = buf->last; u_char *p; - size_t oldoff = buf->off; + size_t old_len = buf->total_len; int n = EVBUFFER_MAX_READ; #if defined(FIONREAD) @@ -417,10 +726,10 @@ * about it. If the reader does not tell us how much * data we should read, we artifically limit it. */ - if (n > buf->totallen << 2) - n = buf->totallen << 2; - if (n < EVBUFFER_MAX_READ) + if (chain == NULL || n < EVBUFFER_MAX_READ) n = EVBUFFER_MAX_READ; + else if (n > chain->buffer_len << 2) + n = chain->buffer_len << 2; } #endif if (howmuch < 0 || howmuch > n) @@ -430,8 +739,10 @@ if (evbuffer_expand(buf, howmuch) == -1) return (-1); + chain = buf->last; + /* We can append new data at this point */ - p = buf->buffer + buf->misalign + buf->off; + p = chain->buffer + chain->misalign + chain->off; #ifndef WIN32 n = read(fd, p, howmuch); @@ -443,11 +754,12 @@ if (n == 0) return (0); - buf->off += n; + chain->off += n; + buf->total_len += n; /* Tell someone about changes in this buffer */ - if (buf->off != oldoff && buf->cb != NULL) - (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + if (buf->cb != NULL) + (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg); return (n); } @@ -458,9 +770,24 @@ int n; #ifndef WIN32 - n = write(fd, buffer->buffer + buffer->misalign, buffer->off); + #ifdef HAVE_SYS_UIO_H + struct iovec iov[NUM_IOVEC]; + struct evbuffer_chain *chain = buffer->first; + int i = 0; + while (chain != NULL && i < NUM_IOVEC) { + iov[i].iov_base = chain->buffer + chain->misalign; + iov[i++].iov_len = chain->off; + chain = chain->next; + } + n = writev(fd, iov, i); + #else /* !HAVE_SYS_UIO_H */ + void *p = evbuffer_pullup(buffer, -1); + n = write(fd, p, buffer->total_len, 0); + #endif #else - n = send(fd, buffer->buffer + buffer->misalign, buffer->off, 0); + /* XXX(niels): investigate if windows has writev */ + void *p = evbuffer_pullup(buffer, -1); + n = send(fd, p, buffer->total_len, 0); #endif if (n == -1) return (-1); @@ -474,8 +801,8 @@ u_char * evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len) { - u_char *search = buffer->buffer + buffer->misalign; - u_char *end = search + buffer->off; + u_char *search = evbuffer_pullup(buffer, -1); + u_char *end = search + buffer->total_len; u_char *p; while (search < end && @@ -490,6 +817,71 @@ return (NULL); } +int +evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) +{ + struct evbuffer_chain *chain; + char *buffer; + size_t space; + size_t old_len = buf->total_len; + int sz; + va_list aq; + + /* make sure that at least some space is available */ + if (evbuffer_expand(buf, 64) == -1) + return (-1); + + chain = buf->last; + for (;;) { + size_t used = chain->misalign + chain->off; + buffer = (char *)chain->buffer + chain->misalign + chain->off; + assert(chain->buffer_len >= used); + space = chain->buffer_len - used; + +#ifndef va_copy +#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) +#endif + va_copy(aq, ap); + +#ifdef WIN32 + sz = vsnprintf(buffer, space - 1, fmt, aq); + buffer[space - 1] = '\0'; +#else + sz = vsnprintf(buffer, space, fmt, aq); +#endif + + va_end(aq); + + if (sz < 0) + return (-1); + if (sz < space) { + chain->off += sz; + buf->total_len += sz; + if (buf->cb != NULL) + (*buf->cb)(buf, old_len, + buf->total_len, buf->cbarg); + return (sz); + } + if (evbuffer_expand(buf, sz + 1) == -1) + return (-1); + + } + /* NOTREACHED */ +} + +int +evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +{ + int res = -1; + va_list ap; + + va_start(ap, fmt); + res = evbuffer_add_vprintf(buf, fmt, ap); + va_end(ap); + + return (res); +} + void evbuffer_setcb(struct evbuffer *buffer, void (*cb)(struct evbuffer *, size_t, size_t, void *), void *cbarg) Modified: trunk/libevent/configure.in =================================================================== --- trunk/libevent/configure.in 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/configure.in 2008-02-28 02:47:43 UTC (rev 674) @@ -39,7 +39,7 @@ dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h) +AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h sys/uio.h) if test "x$ac_cv_header_sys_queue_h" = "xyes"; then AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h) AC_EGREP_CPP(yes, @@ -384,4 +384,4 @@ fi -AC_OUTPUT(Makefile test/Makefile sample/Makefile) +AC_OUTPUT(Makefile include/Makefile test/Makefile sample/Makefile) Added: trunk/libevent/evbuffer-internal.h =================================================================== --- trunk/libevent/evbuffer-internal.h (rev 0) +++ trunk/libevent/evbuffer-internal.h 2008-02-28 02:47:43 UTC (rev 674) @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2000-2004 Niels Provos <pr...@ci...> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _EVBUFFER_INTERNAL_H_ +#define _EVBUFFER_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" + +/* minimum allocation */ +#define MIN_BUFFER_SIZE 256 + +/* number of iovec we use for writev, fragmentation is going to determine + * how much we end up writing */ +#define NUM_IOVEC 128 + +struct evbuffer_chain; +struct evbuffer { + struct evbuffer_chain *first; + struct evbuffer_chain *last; + + size_t total_len; /* total length of all buffers */ + + void (*cb)(struct evbuffer *, size_t, size_t, void *); + void *cbarg; +}; + +struct evbuffer_chain { + /** points to next buffer in the chain */ + struct evbuffer_chain *next; + + size_t buffer_len; + + size_t misalign;/** unused space at the beginning of buffer */ + size_t off; /** write pointer into buffer + misalign */ + + u_char buffer[1]; +}; + +#ifndef offsetof +#define offsetof(type, field) ((size_t)(&((type *)0)->field)) +#endif + +#define EVBUFFER_CHAIN_SIZE offsetof(struct evbuffer_chain, buffer[0]) + +#ifdef __cplusplus +} +#endif + +#endif /* _EVBUFFER_INTERNAL_H_ */ Modified: trunk/libevent/evbuffer.c =================================================================== --- trunk/libevent/evbuffer.c 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/evbuffer.c 2008-02-28 02:47:43 UTC (rev 674) @@ -313,30 +313,13 @@ int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf) { - int res; - - res = bufferevent_write(bufev, buf->buffer, buf->off); - if (res != -1) - evbuffer_drain(buf, buf->off); - - return (res); + return (evbuffer_add_buffer(bufev->output, buf)); } size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size) { - struct evbuffer *buf = bufev->input; - - if (buf->off < size) - size = buf->off; - - /* Copy the available data to the user buffer */ - memcpy(data, buf->buffer, size); - - if (size) - evbuffer_drain(buf, size); - - return (size); + return (evbuffer_remove(bufev->input, data, size)); } int Modified: trunk/libevent/event.h =================================================================== --- trunk/libevent/event.h 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/event.h 2008-02-28 02:47:43 UTC (rev 674) @@ -138,6 +138,9 @@ event.h The primary libevent header + event2/buffer.h + Buffer management for network reading and writing + evdns.h Asynchronous DNS resolution @@ -721,19 +724,10 @@ int event_priority_set(struct event *, int); +#include <event2/buffer.h> + /* These functions deal with buffering input and output */ -struct evbuffer { - u_char *buffer; - - size_t misalign; - size_t totallen; - size_t off; - - void (*cb)(struct evbuffer *, size_t, size_t, void *); - void *cbarg; -}; - /* Just for error reporting - use other constants otherwise */ #define EVBUFFER_READ 0x01 #define EVBUFFER_WRITE 0x02 @@ -913,190 +907,9 @@ int timeout_read, int timeout_write); -#define EVBUFFER_LENGTH(x) (x)->off -#define EVBUFFER_DATA(x) ((x)->buffer + (x)->misalign) #define EVBUFFER_INPUT(x) (x)->input #define EVBUFFER_OUTPUT(x) (x)->output - -/** - Allocate storage for a new evbuffer. - - @return a pointer to a newly allocated evbuffer struct, or NULL if an error - occurred - */ -struct evbuffer *evbuffer_new(void); - - -/** - Deallocate storage for an evbuffer. - - @param pointer to the evbuffer to be freed - */ -void evbuffer_free(struct evbuffer *); - - -/** - Expands the available space in an event buffer. - - Expands the available space in the event buffer to at least datlen - - @param buf the event buffer to be expanded - @param datlen the new minimum length requirement - @return 0 if successful, or -1 if an error occurred -*/ -int evbuffer_expand(struct evbuffer *, size_t); - - -/** - Append data to the end of an evbuffer. - - @param buf the event buffer to be appended to - @param data pointer to the beginning of the data buffer - @param datlen the number of bytes to be copied from the data buffer - */ -int evbuffer_add(struct evbuffer *, const void *, size_t); - - - -/** - Read data from an event buffer and drain the bytes read. - - @param buf the event buffer to be read from - @param data the destination buffer to store the result - @param datlen the maximum size of the destination buffer - @return the number of bytes read - */ -int evbuffer_remove(struct evbuffer *, void *, size_t); - -/** Used to tell evbuffer_readln what kind of line-ending to look for. - */ -enum evbuffer_eol_style { - /** Any sequence of CR and LF characters is acceptable as an EOL. */ - EVBUFFER_EOL_ANY, - /** An EOL is an LF, optionally preceded by a CR. This style is - * most useful for implementing text-based internet protocols. */ - EVBUFFER_EOL_CRLF, - /** An EOL is a CR followed by an LF. */ - EVBUFFER_EOL_CRLF_STRICT, - /** An EOL is a LF. */ - EVBUFFER_EOL_LF -}; - -/** - * Read a single line from an event buffer. - * - * Reads a line terminated by an EOL as determined by the evbuffer_eol_style - * argument. Returns a newly allocated nul-terminated string; the caller must - * free the returned value. The EOL is not included in the returned string. - * - * @param buffer the evbuffer to read from - * @param n_read_out if non-NULL, points to a size_t that is set to the - * number of characters in the returned string. This is useful for - * strings that can contain NUL characters. - * @param eol_style the style of line-ending to use. - * @return pointer to a single line, or NULL if an error occurred - */ -char *evbuffer_readln(struct evbuffer *, size_t *, enum evbuffer_eol_style); - -/** - Obsolete alias for evbuffer_readln(buffer, NULL, EOL_STYLE_ANY). - **/ -char *evbuffer_readline(struct evbuffer *); - -/** - Move data from one evbuffer into another evbuffer. - - This is a destructive add. The data from one buffer moves into - the other buffer. The destination buffer is expanded as needed. - - @param outbuf the output buffer - @param inbuf the input buffer - @return 0 if successful, or -1 if an error occurred - */ -int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *); - - -/** - Append a formatted string to the end of an evbuffer. - - @param buf the evbuffer that will be appended to - @param fmt a format string - @param ... arguments that will be passed to printf(3) - @return 0 if successful, or -1 if an error occurred - */ -int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 2, 3))) -#endif -; - - -/** - Append a va_list formatted string to the end of an evbuffer. - - @param buf the evbuffer that will be appended to - @param fmt a format string - @param ap a varargs va_list argument array that will be passed to vprintf(3) - @return 0 if successful, or -1 if an error occurred - */ -int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap); - - -/** - Remove a specified number of bytes data from the beginning of an evbuffer. - - @param buf the evbuffer to be drained - @param len the number of bytes to drain from the beginning of the buffer - @return 0 if successful, or -1 if an error occurred - */ -void evbuffer_drain(struct evbuffer *, size_t); - - -/** - Write the contents of an evbuffer to a file descriptor. - - The evbuffer will be drained after the bytes have been successfully written. - - @param buffer the evbuffer to be written and drained - @param fd the file descriptor to be written to - @return the number of bytes written, or -1 if an error occurred - @see evbuffer_read() - */ -int evbuffer_write(struct evbuffer *, evutil_socket_t); - - -/** - Read from a file descriptor and store the result in an evbuffer. - - @param buf the evbuffer to store the result - @param fd the file descriptor to read from - @param howmuch the number of bytes to be read - @return the number of bytes read, or -1 if an error occurred - @see evbuffer_write() - */ -int evbuffer_read(struct evbuffer *, evutil_socket_t, int); - - -/** - 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. - */ -u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t); - -/** - Set a callback to invoke when the evbuffer is modified. - - @param buffer the evbuffer to be monitored - @param cb the callback function to invoke when the evbuffer is modified - @param cbarg an argument to be provided to the callback function - */ -void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *); - /* * Marshaling tagged data - We assume that all tags are inserted in their * numeric order - so that unknown tags will always be higher than the @@ -1105,8 +918,19 @@ void evtag_init(void); +/** + Unmarshals the header and returns the length of the payload + + @param evbuf the buffer from which to unmarshal data + @param ptag a pointer in which the tag id is being stored + @returns -1 on failure or the number of bytes in the remaining payload. +*/ +int evtag_unmarshal_header(struct evbuffer *evbuf, ev_uint32_t *ptag); + void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, ev_uint32_t len); +void evtag_marshal_buffer(struct evbuffer *evbuf, ev_uint32_t tag, + struct evbuffer *data); /** Encode an integer and store it in an evbuffer. Modified: trunk/libevent/event_rpcgen.py =================================================================== --- trunk/libevent/event_rpcgen.py 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/event_rpcgen.py 2008-02-28 02:47:43 UTC (rev 674) @@ -304,10 +304,8 @@ '{\n' ' struct evbuffer *_buf = evbuffer_new();\n' ' assert(_buf != NULL);\n' - ' evbuffer_drain(_buf, -1);\n' ' %(name)s_marshal(_buf, msg);\n' - ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), ' - 'EVBUFFER_LENGTH(_buf));\n' + ' evtag_marshal_buffer(evbuf, tag, _buf);\n ' ' evbuffer_free(_buf);\n' '}\n' ) % { 'name' : self._name } Modified: trunk/libevent/event_tagging.c =================================================================== --- trunk/libevent/event_tagging.c 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/event_tagging.c 2008-02-28 02:47:43 UTC (rev 674) @@ -87,11 +87,10 @@ * integers. This function is byte-order independent. */ -void -encode_int(struct evbuffer *evbuf, ev_uint32_t number) +static inline int +encode_int_internal(ev_uint8_t *data, ev_uint32_t number) { int off = 1, nibbles = 0; - ev_uint8_t data[5]; memset(data, 0, sizeof(data)); while (number) { @@ -110,9 +109,17 @@ /* Off - 1 is the number of encoded nibbles */ data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4); - evbuffer_add(evbuf, data, (off + 1) / 2); + return ((off + 1) / 2); } +void +encode_int(struct evbuffer *evbuf, ev_uint32_t number) +{ + ev_uint8_t data[5]; + int len = encode_int_internal(data, number); + evbuffer_add(evbuf, data, len); +} + /* * Support variable length encoding of tags; we use the high bit in each * octet as a continuation signal. @@ -145,10 +152,17 @@ decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain) { ev_uint32_t number = 0; - ev_uint8_t *data = EVBUFFER_DATA(evbuf); int len = EVBUFFER_LENGTH(evbuf); + ev_uint8_t *data; int count = 0, shift = 0, done = 0; + /* + * the encoding of a number is at most one byte more than its + * storage size. however, it may also be much smaller. + */ + data = evbuffer_pullup( + evbuf, len < sizeof(number) + 1 ? len : sizeof(number) + 1); + while (count++ < len) { ev_uint8_t lower = *data++; number |= (lower & 0x7f) << shift; @@ -193,16 +207,25 @@ evbuffer_add(evbuf, (void *)data, len); } +void +evtag_marshal_buffer(struct evbuffer *evbuf, ev_uint32_t tag, + struct evbuffer *data) +{ + evtag_encode_tag(evbuf, tag); + encode_int(evbuf, EVBUFFER_LENGTH(data)); + evbuffer_add_buffer(evbuf, data); +} + /* Marshaling for integers */ void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer) { - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - encode_int(_buf, integer); + ev_uint8_t data[5]; + int len = encode_int_internal(data, integer); evtag_encode_tag(evbuf, tag); - encode_int(evbuf, EVBUFFER_LENGTH(_buf)); - evbuffer_add_buffer(evbuf, _buf); + encode_int(evbuf, len); + evbuffer_add(evbuf, data, len); } void @@ -214,31 +237,33 @@ void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv) { - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - - encode_int(_buf, tv->tv_sec); - encode_int(_buf, tv->tv_usec); - - evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), - EVBUFFER_LENGTH(_buf)); + ev_uint8_t data[10]; + int len = encode_int_internal(data, tv->tv_sec); + len += encode_int_internal(data + len, tv->tv_usec); + evtag_marshal(evbuf, tag, data, len); } static int -decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) +decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int offset) { ev_uint32_t number = 0; - ev_uint8_t *data = EVBUFFER_DATA(evbuf); - int len = EVBUFFER_LENGTH(evbuf); + ev_uint8_t *data; + int len = EVBUFFER_LENGTH(evbuf) - offset; int nibbles = 0; - if (!len) + if (len <= 0) return (-1); + /* XXX(niels): faster? */ + data = evbuffer_pullup(evbuf, offset + 1) + offset; + nibbles = ((data[0] & 0xf0) >> 4) + 1; if (nibbles > 8 || (nibbles >> 1) + 1 > len) return (-1); len = (nibbles >> 1) + 1; + data = evbuffer_pullup(evbuf, offset + len) + offset; + while (nibbles > 0) { number <<= 4; if (nibbles & 0x1) @@ -248,9 +273,6 @@ nibbles--; } - if (dodrain) - evbuffer_drain(evbuf, len); - *pnumber = number; return (len); @@ -259,7 +281,11 @@ int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf) { - return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0); + int res = decode_int_internal(pnumber, evbuf, 0); + if (res != -1) + evbuffer_drain(evbuf, res); + + return (res == -1 ? -1 : 0); } int @@ -271,18 +297,13 @@ int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength) { - struct evbuffer tmp; int res, len; len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); if (len == -1) return (-1); - tmp = *evbuf; - tmp.buffer += len; - tmp.off -= len; - - res = decode_int_internal(plength, &tmp, 0); + res = decode_int_internal(plength, evbuf, len); if (res == -1) return (-1); @@ -294,32 +315,43 @@ int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength) { - struct evbuffer tmp; int res, len; len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); if (len == -1) return (-1); - tmp = *evbuf; - tmp.buffer += len; - tmp.off -= len; - - res = decode_int_internal(plength, &tmp, 0); + res = decode_int_internal(plength, evbuf, len); if (res == -1) return (-1); return (0); } +/* just unmarshals the header and returns the length of the remaining data */ + int -evtag_consume(struct evbuffer *evbuf) +evtag_unmarshal_header(struct evbuffer *evbuf, ev_uint32_t *ptag) { ev_uint32_t len; - if (decode_tag_internal(NULL, evbuf, 1 /* dodrain */) == -1) + + if (decode_tag_internal(ptag, evbuf, 1 /* dodrain */) == -1) return (-1); if (evtag_decode_int(&len, evbuf) == -1) return (-1); + + if (EVBUFFER_LENGTH(evbuf) < len) + return (-1); + + return (len); +} + +int +evtag_consume(struct evbuffer *evbuf) +{ + int len; + if ((len = evtag_unmarshal_header(evbuf, NULL)) == -1) + return (-1); evbuffer_drain(evbuf, len); return (0); @@ -330,21 +362,14 @@ int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst) { - ev_uint32_t len; - ev_uint32_t integer; + int len; - if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1) + if ((len = evtag_unmarshal_header(src, ptag)) == -1) return (-1); - if (evtag_decode_int(&integer, src) == -1) - return (-1); - len = integer; - if (EVBUFFER_LENGTH(src) < len) + if (evbuffer_add(dst, evbuffer_pullup(src, len), len) == -1) return (-1); - if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1) - return (-1); - evbuffer_drain(src, len); return (len); @@ -372,7 +397,7 @@ return (-1); evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1) + if (evbuffer_add(_buf, evbuffer_pullup(evbuf, len), len) == -1) return (-1); evbuffer_drain(evbuf, len); @@ -387,18 +412,17 @@ size_t len) { ev_uint32_t tag; + int tag_len; - /* Initialize this event buffer so that we can read into it */ - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - /* Now unmarshal a tag and check that it matches the tag we want */ - if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag) + if ((tag_len = evtag_unmarshal_header(src, &tag)) == -1 || + tag != need_tag) return (-1); - if (EVBUFFER_LENGTH(_buf) != len) + if (tag_len != len) return (-1); - - memcpy(data, EVBUFFER_DATA(_buf), len); + + evbuffer_remove(src, data, len); return (0); } @@ -407,16 +431,17 @@ char **pstring) { ev_uint32_t tag; + int tag_len; - evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); - - if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) + if ((tag_len = evtag_unmarshal_header(evbuf, &tag)) == -1 || + tag != need_tag) return (-1); - *pstring = event_calloc(EVBUFFER_LENGTH(_buf) + 1, 1); + *pstring = event_malloc(tag_len + 1); if (*pstring == NULL) - event_err(1, "%s: calloc", __func__); - evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf)); + event_err(1, "%s: malloc", __func__); + evbuffer_remove(evbuf, *pstring, tag_len); + (*pstring)[tag_len] = '\0'; return (0); } Modified: trunk/libevent/http.c =================================================================== --- trunk/libevent/http.c 2008-02-27 06:20:48 UTC (rev 673) +++ trunk/libevent/http.c 2008-02-28 02:47:43 UTC (rev 674) @@ -313,7 +313,6 @@ evhttp_make_header_request(struct evhttp_connection *evcon, struct evhttp_request *req) { - char line[1024]; const char *method; evhttp_remove_header(req->output_headers, "Accept-Encoding"); @@ -321,9 +320,8 @@ /* Generate request line */ method = evhttp_method(req->type); - snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n", + evbuffer_add_printf(evcon->output_buffer, "%s %s HTTP/%d.%d\r\n", method, req->uri, req->major, req->minor); - evbuffer_add(evcon->output_buffer, line, strlen(line)); /* Add the content length on a post or put request if missing */ if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) && @@ -399,11 +397,9 @@ evhttp_make_header_response(struct evhttp_connection *evcon, struct evhttp_request *req) { - char line[1024]; - snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n", + evbuffer_add_printf(evcon->output_buffer, "HTTP/%d.%d %d %s\r\n", req->major, req->minor, req->response_code, req->response_code_line); - evbuffer_add(evcon->output_buffer, line, strlen(line)); if (req->major == 1 && req->minor == 1) evhttp_maybe_add_date_header(req->output_headers); @@ -441,7 +437,6 @@ void evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req) { - char line[1024]; struct evkeyval *header; /* @@ -455,9 +450,8 @@ } TAILQ_FOREACH(header, req->output_headers, next) { - snprintf(line, sizeof(line), "%s: %s\r\n", + evbuffer_add_printf(evcon->output_buffer, "%s: %s\r\n", header->key, header->value); - evbuffer_add(evcon->output_buffer, line, strlen(line)); } evbuffer_add(evcon->output_buffer, "\r\n", 2); @@ -735,9 +729,7 @@ return (0); /* Completed chunk */ - evbuffer_add(req->input_buffer, - EVBUFFER_DATA(buf), req->ntoread); - evbuffer_drain(buf, req->ntoread); + evbuffer_remove_buffer(buf, req->input_buffer, req->ntoread); req->ntoread = -1; if (req->chunk_cb != NULL) { (*req->chunk_cb)(req, req->cb_arg); @@ -771,9 +763,7 @@ evbuffer_add_buffer(req->input_buffer, buf); } else if (EVBUFFER_LENGTH(buf) >= req->ntoread) { /* Completed content length */ - evbuffer_add(req->input_buffer, EVBUFFER_DATA(buf), - req->ntoread); - evbuffer_drain(buf, req->ntoread); + evbuffer_remove_buffer(buf, req->input_buffer, req->ntoread); req->ntoread = 0; evhttp_connection_done(evcon); return; Added: trunk/libevent/include/Makefile.am =================================================================== --- trunk/libevent/include/Makefile.am (rev 0) +++ trunk/libevent/include/Makefile.am 2008-02-28 02:47:43 UTC (rev 674) @@ -0,0 +1,4 @@ +AUTOMAKE_OPTIONS = foreign + +EXTRA_SRC = event2/buffer.h +nobase_include_HEADERS = event2/buffer.h Added: trunk/libevent/include/event2/buffer.h =================================================================== --- trunk/libevent/include/event2/buffer.h (rev 0) +++ trunk/libevent/include/event2/buffer.h 2008-02-28 02:47:43 UTC (rev 674) @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2007 Niels Provos <pr...@ci...> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _EVENT2_BUFFER_H_ +#define _EVENT2_BUFFER_H_ + +/** @file buffer.h + + Functions for buffering data for network sending or receiving. + + An evbuffer can be used for preparing data before sending it to + the network or conversely for reading data from the network. + Evbuffers try to avoid memory copies as much as possible. As a + result evbuffers can be used to pass data around witout actually + incurring the overhead of copying the data. + + A new evbuffer can be allocated with evbuffer_new(), and can be + freed with evbuffer_free(). + + There are several guide lines for using evbuffers. + + - if you already know how much data you are going to add as a result + of calling evbuffer_add() multiple times, it makes sense to use + evbuffer_expand() first to make sure that enough memory is allocated + before hand. + + - evbuffer_add_buffer() adds the contents of one buffer to the other + without incurring any memory copies. + + - evbuffer_add() and evbuffer_add_buffer() do not mix very well. + + As the contents of an evbuffer can be stored into multiple different + memory blocks, it cannot be accessed directly. Instead, evbuffer_pullup() + can be used to force a specified number of bytes to be continuous. This + will cause memory reallocation and memory copies if the data is split + across multiple blocks. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <event-config.h> + +struct evbuffer; + +/** + Allocate storage for a new evbuffer. + + @return a pointer to a newly allocated evbuffer struct, or NULL if an error + occurred + */ +struct evbuffer *evbuffer_new(void); + + +/** + Deallocate storage for an evbuffer. + + @param buf pointer to the evbuffer to be freed + */ +void evbuffer_free(struct evbuffer *buf); + +/** + Returns the total number of bytes stored in the event buffer + + @param buf pointer to the evbuffer + @return the number of bytes stored in the event buffer +*/ +size_t evbuffer_length(struct evbuffer *buf); + +/** + Expands the available space in an event buffer. + + Expands the available space in the event buffer to at least datlen + + @param buf the event buffer to be expanded + @param datlen the new minimum length requirement + @return 0 if successful, or -1 if an error occurred +*/ +int evbuffer_expand(struct evbuffer *buf, size_t datlen); + + +/** + Append data to the end of an evbuffer. + + @param buf the event buffer to be appended to + @param data pointer to the beginning of the data buffer + @param datlen the number of bytes to be copied from the data buffer + */ +int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); + + +/** + Read data from an event buffer and drain the bytes read. + + @param buf the event buffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read + */ +int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); + +/** + Read data from an event buffer into another event buffer draining + the bytes from the src buffer read. This function avoids memcpy + as possible. + + @param src the event buffer to be read from + @param dst the destination event buffer to store the result into + @param datlen the maximum numbers of bytes to transfer + @return the number of bytes read + */ +int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, + size_t datlen); + +/** Used to tell evbuffer_readln what kind of line-ending to look for. + */ +enum evbuffer_eol_style { + /** Any sequence of CR and LF characters is acceptable as an EOL. */ + EVBUFFER_EOL_ANY, + /** An EOL is an LF, optionally preceded by a CR. This style is + * most useful for implementing text-based internet protocols. */ + EVBUFFER_EOL_CRLF, + /** An EOL is a CR followed by an LF. */ + EVBUFFER_EOL_CRLF_STRICT, + /** An EOL is a LF. */ + EVBUFFER_EOL_LF +}; + +/** + * Read a single line from an event buffer. + * + * Reads a line terminated by an EOL as determined by the evbuffer_eol_style + * argument. Returns a newly allocated nul-terminated string; the caller must + * free the returned value. The EOL is not included in the returned string. + * + * @param buffer the evbuffer to read from + * @param n_read_out if non-NULL, points to a size_t that is set to the + * number of characters in the returned string. This is useful for + * strings that can contain NUL characters. + * @param eol_style the style of line-ending to use. + * @return pointer to a single line, or NULL if an error occurred + */ +char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style); + +/** + Obsolete alias for evbuffer_readln(buffer, NULL, EOL_STYLE_ANY). + + @param buffer the evbuffer to read from + @return pointer to a single line, or NULL if an error occurred +*/ +char *evbuffer_readline(struct evbuffer *buffer); + +/** + Move data from one evbuffer into another evbuffer. + + This is a destructive add. The data from one buffer moves into + the other buffer. However, no memory copies occur. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + */ +int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf); + + +/** + Append a formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ... arguments that will be passed to printf(3) + @return 0 if successful, or -1 if an error occurred + */ +int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; + + +/** + Append a va_list formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ap a varargs va_list argument array that will be passed to vprintf(3) + @return 0 if successful, or -1 if an error occurred + */ +int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap); + + +/** + Remove a specified number of bytes data from the beginning of an evbuffer. + + @param buf the evbuffer to be drained + @param len the number of bytes to drain from the beginning of the buffer + @return 0 if successful, or -1 if an error occurred + */ +void evbuffer_drain(struct evbuffer *buf, size_t len); + + +/** + Write the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd); + + +/** + Read from a file descriptor and store the result in an evbuffer. + + @param buf the evbuffer to store the result + @param fd the file descriptor to read from + @param howmuch the number of bytes to be read + @return the number of bytes read, or -1 if an error occurred + @see evbuffer_write() + */ +int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch); + + +/** + 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. + */ +u_char *evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len); + +/** + Set a callback to invoke when the evbuffer is modified. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified + @param cbarg an argument to be provided to the callback function + */ +void evbuffer_setcb(struct evbuffer *buffer, + void (*cb)(struct evbuffer *, size_t, size_t, void *), void *cbarg); + +/** + Makes the memory in an evbuffer contiguous + + @param buf the evbuffer to make contiguous + @param size the number of bytes to make contiguous + @return a pointer to the contigous memory areay +*/ + +u_char *evbuffer_pullup(struct evbuffer *buf, int size); + +/** + Prepends data to the beginning of the evbuffer + + @param b... [truncated message content] |