Annotation of embedaddon/libevent/buffer.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
! 3: * All rights reserved.
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: * 3. The name of the author may not be used to endorse or promote products
! 14: * derived from this software without specific prior written permission.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 19: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 20: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 21: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 22: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 23: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 24: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 25: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 26: */
! 27:
! 28: #ifdef HAVE_CONFIG_H
! 29: #include "config.h"
! 30: #endif
! 31:
! 32: #ifdef WIN32
! 33: #include <winsock2.h>
! 34: #include <windows.h>
! 35: #endif
! 36:
! 37: #ifdef HAVE_VASPRINTF
! 38: /* If we have vasprintf, we need to define this before we include stdio.h. */
! 39: #define _GNU_SOURCE
! 40: #endif
! 41:
! 42: #include <sys/types.h>
! 43:
! 44: #ifdef HAVE_SYS_TIME_H
! 45: #include <sys/time.h>
! 46: #endif
! 47:
! 48: #ifdef HAVE_SYS_IOCTL_H
! 49: #include <sys/ioctl.h>
! 50: #endif
! 51:
! 52: #include <assert.h>
! 53: #include <errno.h>
! 54: #include <stdio.h>
! 55: #include <stdlib.h>
! 56: #include <string.h>
! 57: #ifdef HAVE_STDARG_H
! 58: #include <stdarg.h>
! 59: #endif
! 60: #ifdef HAVE_UNISTD_H
! 61: #include <unistd.h>
! 62: #endif
! 63:
! 64: #include "event.h"
! 65: #include "config.h"
! 66: #include "evutil.h"
! 67: #include "./log.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: return (NULL);
! 230: }
! 231:
! 232: memcpy(line, data, i);
! 233: line[i] = '\0';
! 234:
! 235: /*
! 236: * Some protocols terminate a line with '\r\n', so check for
! 237: * that, too.
! 238: */
! 239: if ( i < len - 1 ) {
! 240: char fch = data[i], sch = data[i+1];
! 241:
! 242: /* Drain one more character if needed */
! 243: if ( (sch == '\r' || sch == '\n') && sch != fch )
! 244: i += 1;
! 245: }
! 246:
! 247: evbuffer_drain(buffer, i + 1);
! 248:
! 249: return (line);
! 250: }
! 251:
! 252:
! 253: char *
! 254: evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
! 255: enum evbuffer_eol_style eol_style)
! 256: {
! 257: u_char *data = EVBUFFER_DATA(buffer);
! 258: u_char *start_of_eol, *end_of_eol;
! 259: size_t len = EVBUFFER_LENGTH(buffer);
! 260: char *line;
! 261: unsigned int i, n_to_copy, n_to_drain;
! 262:
! 263: if (n_read_out)
! 264: *n_read_out = 0;
! 265:
! 266: /* depending on eol_style, set start_of_eol to the first character
! 267: * in the newline, and end_of_eol to one after the last character. */
! 268: switch (eol_style) {
! 269: case EVBUFFER_EOL_ANY:
! 270: for (i = 0; i < len; i++) {
! 271: if (data[i] == '\r' || data[i] == '\n')
! 272: break;
! 273: }
! 274: if (i == len)
! 275: return (NULL);
! 276: start_of_eol = data+i;
! 277: ++i;
! 278: for ( ; i < len; i++) {
! 279: if (data[i] != '\r' && data[i] != '\n')
! 280: break;
! 281: }
! 282: end_of_eol = data+i;
! 283: break;
! 284: case EVBUFFER_EOL_CRLF:
! 285: end_of_eol = memchr(data, '\n', len);
! 286: if (!end_of_eol)
! 287: return (NULL);
! 288: if (end_of_eol > data && *(end_of_eol-1) == '\r')
! 289: start_of_eol = end_of_eol - 1;
! 290: else
! 291: start_of_eol = end_of_eol;
! 292: end_of_eol++; /*point to one after the LF. */
! 293: break;
! 294: case EVBUFFER_EOL_CRLF_STRICT: {
! 295: u_char *cp = data;
! 296: while ((cp = memchr(cp, '\r', len-(cp-data)))) {
! 297: if (cp < data+len-1 && *(cp+1) == '\n')
! 298: break;
! 299: if (++cp >= data+len) {
! 300: cp = NULL;
! 301: break;
! 302: }
! 303: }
! 304: if (!cp)
! 305: return (NULL);
! 306: start_of_eol = cp;
! 307: end_of_eol = cp+2;
! 308: break;
! 309: }
! 310: case EVBUFFER_EOL_LF:
! 311: start_of_eol = memchr(data, '\n', len);
! 312: if (!start_of_eol)
! 313: return (NULL);
! 314: end_of_eol = start_of_eol + 1;
! 315: break;
! 316: default:
! 317: return (NULL);
! 318: }
! 319:
! 320: n_to_copy = start_of_eol - data;
! 321: n_to_drain = end_of_eol - data;
! 322:
! 323: if ((line = malloc(n_to_copy+1)) == NULL) {
! 324: event_warn("%s: out of memory\n", __func__);
! 325: return (NULL);
! 326: }
! 327:
! 328: memcpy(line, data, n_to_copy);
! 329: line[n_to_copy] = '\0';
! 330:
! 331: evbuffer_drain(buffer, n_to_drain);
! 332: if (n_read_out)
! 333: *n_read_out = (size_t)n_to_copy;
! 334:
! 335: return (line);
! 336: }
! 337:
! 338: /* Adds data to an event buffer */
! 339:
! 340: static void
! 341: evbuffer_align(struct evbuffer *buf)
! 342: {
! 343: memmove(buf->orig_buffer, buf->buffer, buf->off);
! 344: buf->buffer = buf->orig_buffer;
! 345: buf->misalign = 0;
! 346: }
! 347:
! 348: /* Expands the available space in the event buffer to at least datlen */
! 349:
! 350: int
! 351: evbuffer_expand(struct evbuffer *buf, size_t datlen)
! 352: {
! 353: size_t need = buf->misalign + buf->off + datlen;
! 354:
! 355: /* If we can fit all the data, then we don't have to do anything */
! 356: if (buf->totallen >= need)
! 357: return (0);
! 358:
! 359: /*
! 360: * If the misalignment fulfills our data needs, we just force an
! 361: * alignment to happen. Afterwards, we have enough space.
! 362: */
! 363: if (buf->misalign >= datlen) {
! 364: evbuffer_align(buf);
! 365: } else {
! 366: void *newbuf;
! 367: size_t length = buf->totallen;
! 368:
! 369: if (length < 256)
! 370: length = 256;
! 371: while (length < need)
! 372: length <<= 1;
! 373:
! 374: if (buf->orig_buffer != buf->buffer)
! 375: evbuffer_align(buf);
! 376: if ((newbuf = realloc(buf->buffer, length)) == NULL)
! 377: return (-1);
! 378:
! 379: buf->orig_buffer = buf->buffer = newbuf;
! 380: buf->totallen = length;
! 381: }
! 382:
! 383: return (0);
! 384: }
! 385:
! 386: int
! 387: evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
! 388: {
! 389: size_t need = buf->misalign + buf->off + datlen;
! 390: size_t oldoff = buf->off;
! 391:
! 392: if (buf->totallen < need) {
! 393: if (evbuffer_expand(buf, datlen) == -1)
! 394: return (-1);
! 395: }
! 396:
! 397: memcpy(buf->buffer + buf->off, data, datlen);
! 398: buf->off += datlen;
! 399:
! 400: if (datlen && buf->cb != NULL)
! 401: (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
! 402:
! 403: return (0);
! 404: }
! 405:
! 406: void
! 407: evbuffer_drain(struct evbuffer *buf, size_t len)
! 408: {
! 409: size_t oldoff = buf->off;
! 410:
! 411: if (len >= buf->off) {
! 412: buf->off = 0;
! 413: buf->buffer = buf->orig_buffer;
! 414: buf->misalign = 0;
! 415: goto done;
! 416: }
! 417:
! 418: buf->buffer += len;
! 419: buf->misalign += len;
! 420:
! 421: buf->off -= len;
! 422:
! 423: done:
! 424: /* Tell someone about changes in this buffer */
! 425: if (buf->off != oldoff && buf->cb != NULL)
! 426: (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
! 427:
! 428: }
! 429:
! 430: /*
! 431: * Reads data from a file descriptor into a buffer.
! 432: */
! 433:
! 434: #define EVBUFFER_MAX_READ 4096
! 435:
! 436: int
! 437: evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
! 438: {
! 439: u_char *p;
! 440: size_t oldoff = buf->off;
! 441: int n = EVBUFFER_MAX_READ;
! 442:
! 443: #if defined(FIONREAD)
! 444: #ifdef WIN32
! 445: long lng = n;
! 446: if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) <= 0) {
! 447: #else
! 448: if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) {
! 449: #endif
! 450: n = EVBUFFER_MAX_READ;
! 451: } else if (n > EVBUFFER_MAX_READ && n > howmuch) {
! 452: /*
! 453: * It's possible that a lot of data is available for
! 454: * reading. We do not want to exhaust resources
! 455: * before the reader has a chance to do something
! 456: * about it. If the reader does not tell us how much
! 457: * data we should read, we artifically limit it.
! 458: */
! 459: if ((size_t)n > buf->totallen << 2)
! 460: n = buf->totallen << 2;
! 461: if (n < EVBUFFER_MAX_READ)
! 462: n = EVBUFFER_MAX_READ;
! 463: }
! 464: #endif
! 465: if (howmuch < 0 || howmuch > n)
! 466: howmuch = n;
! 467:
! 468: /* If we don't have FIONREAD, we might waste some space here */
! 469: if (evbuffer_expand(buf, howmuch) == -1)
! 470: return (-1);
! 471:
! 472: /* We can append new data at this point */
! 473: p = buf->buffer + buf->off;
! 474:
! 475: #ifndef WIN32
! 476: n = read(fd, p, howmuch);
! 477: #else
! 478: n = recv(fd, p, howmuch, 0);
! 479: #endif
! 480: if (n == -1)
! 481: return (-1);
! 482: if (n == 0)
! 483: return (0);
! 484:
! 485: buf->off += n;
! 486:
! 487: /* Tell someone about changes in this buffer */
! 488: if (buf->off != oldoff && buf->cb != NULL)
! 489: (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
! 490:
! 491: return (n);
! 492: }
! 493:
! 494: int
! 495: evbuffer_write(struct evbuffer *buffer, int fd)
! 496: {
! 497: int n;
! 498:
! 499: #ifndef WIN32
! 500: n = write(fd, buffer->buffer, buffer->off);
! 501: #else
! 502: n = send(fd, buffer->buffer, buffer->off, 0);
! 503: #endif
! 504: if (n == -1)
! 505: return (-1);
! 506: if (n == 0)
! 507: return (0);
! 508: evbuffer_drain(buffer, n);
! 509:
! 510: return (n);
! 511: }
! 512:
! 513: u_char *
! 514: evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
! 515: {
! 516: u_char *search = buffer->buffer, *end = search + buffer->off;
! 517: u_char *p;
! 518:
! 519: while (search < end &&
! 520: (p = memchr(search, *what, end - search)) != NULL) {
! 521: if (p + len > end)
! 522: break;
! 523: if (memcmp(p, what, len) == 0)
! 524: return (p);
! 525: search = p + 1;
! 526: }
! 527:
! 528: return (NULL);
! 529: }
! 530:
! 531: void evbuffer_setcb(struct evbuffer *buffer,
! 532: void (*cb)(struct evbuffer *, size_t, size_t, void *),
! 533: void *cbarg)
! 534: {
! 535: buffer->cb = cb;
! 536: buffer->cbarg = cbarg;
! 537: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>