Home | History | Annotate | Line # | Download | only in dist
buffer.c revision 1.1
      1 /*	$NetBSD: buffer.c,v 1.1 2009/11/02 10:00:52 plunky Exp $	*/
      2 /*
      3  * Copyright (c) 2002, 2003 Niels Provos <provos (at) citi.umich.edu>
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 #include "config.h"
     31 #endif
     32 
     33 #ifdef WIN32
     34 #include <winsock2.h>
     35 #include <windows.h>
     36 #endif
     37 
     38 #ifdef HAVE_VASPRINTF
     39 /* If we have vasprintf, we need to define this before we include stdio.h. */
     40 #define _GNU_SOURCE
     41 #endif
     42 
     43 #include <sys/types.h>
     44 
     45 #ifdef HAVE_SYS_TIME_H
     46 #include <sys/time.h>
     47 #endif
     48 
     49 #ifdef HAVE_SYS_IOCTL_H
     50 #include <sys/ioctl.h>
     51 #endif
     52 
     53 #include <assert.h>
     54 #include <errno.h>
     55 #include <stdio.h>
     56 #include <stdlib.h>
     57 #include <string.h>
     58 #ifdef HAVE_STDARG_H
     59 #include <stdarg.h>
     60 #endif
     61 #ifdef HAVE_UNISTD_H
     62 #include <unistd.h>
     63 #endif
     64 
     65 #include "event.h"
     66 #include "config.h"
     67 #include "evutil.h"
     68 
     69 struct evbuffer *
     70 evbuffer_new(void)
     71 {
     72 	struct evbuffer *buffer;
     73 
     74 	buffer = calloc(1, sizeof(struct evbuffer));
     75 
     76 	return (buffer);
     77 }
     78 
     79 void
     80 evbuffer_free(struct evbuffer *buffer)
     81 {
     82 	if (buffer->orig_buffer != NULL)
     83 		free(buffer->orig_buffer);
     84 	free(buffer);
     85 }
     86 
     87 /*
     88  * This is a destructive add.  The data from one buffer moves into
     89  * the other buffer.
     90  */
     91 
     92 #define SWAP(x,y) do { \
     93 	(x)->buffer = (y)->buffer; \
     94 	(x)->orig_buffer = (y)->orig_buffer; \
     95 	(x)->misalign = (y)->misalign; \
     96 	(x)->totallen = (y)->totallen; \
     97 	(x)->off = (y)->off; \
     98 } while (0)
     99 
    100 int
    101 evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
    102 {
    103 	int res;
    104 
    105 	/* Short cut for better performance */
    106 	if (outbuf->off == 0) {
    107 		struct evbuffer tmp;
    108 		size_t oldoff = inbuf->off;
    109 
    110 		/* Swap them directly */
    111 		SWAP(&tmp, outbuf);
    112 		SWAP(outbuf, inbuf);
    113 		SWAP(inbuf, &tmp);
    114 
    115 		/*
    116 		 * Optimization comes with a price; we need to notify the
    117 		 * buffer if necessary of the changes. oldoff is the amount
    118 		 * of data that we transfered from inbuf to outbuf
    119 		 */
    120 		if (inbuf->off != oldoff && inbuf->cb != NULL)
    121 			(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
    122 		if (oldoff && outbuf->cb != NULL)
    123 			(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
    124 
    125 		return (0);
    126 	}
    127 
    128 	res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
    129 	if (res == 0) {
    130 		/* We drain the input buffer on success */
    131 		evbuffer_drain(inbuf, inbuf->off);
    132 	}
    133 
    134 	return (res);
    135 }
    136 
    137 int
    138 evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
    139 {
    140 	char *buffer;
    141 	size_t space;
    142 	size_t oldoff = buf->off;
    143 	int sz;
    144 	va_list aq;
    145 
    146 	/* make sure that at least some space is available */
    147 	evbuffer_expand(buf, 64);
    148 	for (;;) {
    149 		size_t used = buf->misalign + buf->off;
    150 		buffer = (char *)buf->buffer + buf->off;
    151 		assert(buf->totallen >= used);
    152 		space = buf->totallen - used;
    153 
    154 #ifndef va_copy
    155 #define	va_copy(dst, src)	memcpy(&(dst), &(src), sizeof(va_list))
    156 #endif
    157 		va_copy(aq, ap);
    158 
    159 		sz = evutil_vsnprintf(buffer, space, fmt, aq);
    160 
    161 		va_end(aq);
    162 
    163 		if (sz < 0)
    164 			return (-1);
    165 		if ((size_t)sz < space) {
    166 			buf->off += sz;
    167 			if (buf->cb != NULL)
    168 				(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
    169 			return (sz);
    170 		}
    171 		if (evbuffer_expand(buf, sz + 1) == -1)
    172 			return (-1);
    173 
    174 	}
    175 	/* NOTREACHED */
    176 }
    177 
    178 int
    179 evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
    180 {
    181 	int res = -1;
    182 	va_list ap;
    183 
    184 	va_start(ap, fmt);
    185 	res = evbuffer_add_vprintf(buf, fmt, ap);
    186 	va_end(ap);
    187 
    188 	return (res);
    189 }
    190 
    191 /* Reads data from an event buffer and drains the bytes read */
    192 
    193 int
    194 evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
    195 {
    196 	size_t nread = datlen;
    197 	if (nread >= buf->off)
    198 		nread = buf->off;
    199 
    200 	memcpy(data, buf->buffer, nread);
    201 	evbuffer_drain(buf, nread);
    202 
    203 	return (nread);
    204 }
    205 
    206 /*
    207  * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
    208  * The returned buffer needs to be freed by the called.
    209  */
    210 
    211 char *
    212 evbuffer_readline(struct evbuffer *buffer)
    213 {
    214 	u_char *data = EVBUFFER_DATA(buffer);
    215 	size_t len = EVBUFFER_LENGTH(buffer);
    216 	char *line;
    217 	unsigned int i;
    218 
    219 	for (i = 0; i < len; i++) {
    220 		if (data[i] == '\r' || data[i] == '\n')
    221 			break;
    222 	}
    223 
    224 	if (i == len)
    225 		return (NULL);
    226 
    227 	if ((line = malloc(i + 1)) == NULL) {
    228 		fprintf(stderr, "%s: out of memory\n", __func__);
    229 		evbuffer_drain(buffer, i);
    230 		return (NULL);
    231 	}
    232 
    233 	memcpy(line, data, i);
    234 	line[i] = '\0';
    235 
    236 	/*
    237 	 * Some protocols terminate a line with '\r\n', so check for
    238 	 * that, too.
    239 	 */
    240 	if ( i < len - 1 ) {
    241 		char fch = data[i], sch = data[i+1];
    242 
    243 		/* Drain one more character if needed */
    244 		if ( (sch == '\r' || sch == '\n') && sch != fch )
    245 			i += 1;
    246 	}
    247 
    248 	evbuffer_drain(buffer, i + 1);
    249 
    250 	return (line);
    251 }
    252 
    253 /* Adds data to an event buffer */
    254 
    255 static void
    256 evbuffer_align(struct evbuffer *buf)
    257 {
    258 	memmove(buf->orig_buffer, buf->buffer, buf->off);
    259 	buf->buffer = buf->orig_buffer;
    260 	buf->misalign = 0;
    261 }
    262 
    263 /* Expands the available space in the event buffer to at least datlen */
    264 
    265 int
    266 evbuffer_expand(struct evbuffer *buf, size_t datlen)
    267 {
    268 	size_t need = buf->misalign + buf->off + datlen;
    269 
    270 	/* If we can fit all the data, then we don't have to do anything */
    271 	if (buf->totallen >= need)
    272 		return (0);
    273 
    274 	/*
    275 	 * If the misalignment fulfills our data needs, we just force an
    276 	 * alignment to happen.  Afterwards, we have enough space.
    277 	 */
    278 	if (buf->misalign >= datlen) {
    279 		evbuffer_align(buf);
    280 	} else {
    281 		void *newbuf;
    282 		size_t length = buf->totallen;
    283 
    284 		if (length < 256)
    285 			length = 256;
    286 		while (length < need)
    287 			length <<= 1;
    288 
    289 		if (buf->orig_buffer != buf->buffer)
    290 			evbuffer_align(buf);
    291 		if ((newbuf = realloc(buf->buffer, length)) == NULL)
    292 			return (-1);
    293 
    294 		buf->orig_buffer = buf->buffer = newbuf;
    295 		buf->totallen = length;
    296 	}
    297 
    298 	return (0);
    299 }
    300 
    301 int
    302 evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
    303 {
    304 	size_t need = buf->misalign + buf->off + datlen;
    305 	size_t oldoff = buf->off;
    306 
    307 	if (buf->totallen < need) {
    308 		if (evbuffer_expand(buf, datlen) == -1)
    309 			return (-1);
    310 	}
    311 
    312 	memcpy(buf->buffer + buf->off, data, datlen);
    313 	buf->off += datlen;
    314 
    315 	if (datlen && buf->cb != NULL)
    316 		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
    317 
    318 	return (0);
    319 }
    320 
    321 void
    322 evbuffer_drain(struct evbuffer *buf, size_t len)
    323 {
    324 	size_t oldoff = buf->off;
    325 
    326 	if (len >= buf->off) {
    327 		buf->off = 0;
    328 		buf->buffer = buf->orig_buffer;
    329 		buf->misalign = 0;
    330 		goto done;
    331 	}
    332 
    333 	buf->buffer += len;
    334 	buf->misalign += len;
    335 
    336 	buf->off -= len;
    337 
    338  done:
    339 	/* Tell someone about changes in this buffer */
    340 	if (buf->off != oldoff && buf->cb != NULL)
    341 		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
    342 
    343 }
    344 
    345 /*
    346  * Reads data from a file descriptor into a buffer.
    347  */
    348 
    349 #define EVBUFFER_MAX_READ	4096
    350 
    351 int
    352 evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
    353 {
    354 	u_char *p;
    355 	size_t oldoff = buf->off;
    356 	int n = EVBUFFER_MAX_READ;
    357 
    358 #if defined(FIONREAD)
    359 #ifdef WIN32
    360 	long lng = n;
    361 	if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) == 0) {
    362 #else
    363 	if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
    364 #endif
    365 		n = EVBUFFER_MAX_READ;
    366 	} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
    367 		/*
    368 		 * It's possible that a lot of data is available for
    369 		 * reading.  We do not want to exhaust resources
    370 		 * before the reader has a chance to do something
    371 		 * about it.  If the reader does not tell us how much
    372 		 * data we should read, we artifically limit it.
    373 		 */
    374 		if ((size_t)n > buf->totallen << 2)
    375 			n = buf->totallen << 2;
    376 		if (n < EVBUFFER_MAX_READ)
    377 			n = EVBUFFER_MAX_READ;
    378 	}
    379 #endif
    380 	if (howmuch < 0 || howmuch > n)
    381 		howmuch = n;
    382 
    383 	/* If we don't have FIONREAD, we might waste some space here */
    384 	if (evbuffer_expand(buf, howmuch) == -1)
    385 		return (-1);
    386 
    387 	/* We can append new data at this point */
    388 	p = buf->buffer + buf->off;
    389 
    390 #ifndef WIN32
    391 	n = read(fd, p, howmuch);
    392 #else
    393 	n = recv(fd, p, howmuch, 0);
    394 #endif
    395 	if (n == -1)
    396 		return (-1);
    397 	if (n == 0)
    398 		return (0);
    399 
    400 	buf->off += n;
    401 
    402 	/* Tell someone about changes in this buffer */
    403 	if (buf->off != oldoff && buf->cb != NULL)
    404 		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
    405 
    406 	return (n);
    407 }
    408 
    409 int
    410 evbuffer_write(struct evbuffer *buffer, int fd)
    411 {
    412 	int n;
    413 
    414 #ifndef WIN32
    415 	n = write(fd, buffer->buffer, buffer->off);
    416 #else
    417 	n = send(fd, buffer->buffer, buffer->off, 0);
    418 #endif
    419 	if (n == -1)
    420 		return (-1);
    421 	if (n == 0)
    422 		return (0);
    423 	evbuffer_drain(buffer, n);
    424 
    425 	return (n);
    426 }
    427 
    428 u_char *
    429 evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
    430 {
    431 	u_char *search = buffer->buffer, *end = search + buffer->off;
    432 	u_char *p;
    433 
    434 	while (search < end &&
    435 	    (p = memchr(search, *what, end - search)) != NULL) {
    436 		if (p + len > end)
    437 			break;
    438 		if (memcmp(p, what, len) == 0)
    439 			return (p);
    440 		search = p + 1;
    441 	}
    442 
    443 	return (NULL);
    444 }
    445 
    446 void evbuffer_setcb(struct evbuffer *buffer,
    447     void (*cb)(struct evbuffer *, size_t, size_t, void *),
    448     void *cbarg)
    449 {
    450 	buffer->cb = cb;
    451 	buffer->cbarg = cbarg;
    452 }
    453