1275970Scy/* 2275970Scy * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu> 3275970Scy * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 4275970Scy * 5275970Scy * Redistribution and use in source and binary forms, with or without 6275970Scy * modification, are permitted provided that the following conditions 7275970Scy * are met: 8275970Scy * 1. Redistributions of source code must retain the above copyright 9275970Scy * notice, this list of conditions and the following disclaimer. 10275970Scy * 2. Redistributions in binary form must reproduce the above copyright 11275970Scy * notice, this list of conditions and the following disclaimer in the 12275970Scy * documentation and/or other materials provided with the distribution. 13275970Scy * 3. The name of the author may not be used to endorse or promote products 14275970Scy * derived from this software without specific prior written permission. 15275970Scy * 16275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26275970Scy */ 27275970Scy 28275970Scy#include "event2/event-config.h" 29275970Scy#include "evconfig-private.h" 30275970Scy 31275970Scy#ifdef _WIN32 32275970Scy#include <winsock2.h> 33275970Scy#include <windows.h> 34275970Scy#include <io.h> 35275970Scy#endif 36275970Scy 37275970Scy#ifdef EVENT__HAVE_VASPRINTF 38275970Scy/* If we have vasprintf, we need to define _GNU_SOURCE before we include 39275970Scy * stdio.h. This comes from evconfig-private.h. 40275970Scy */ 41275970Scy#endif 42275970Scy 43275970Scy#include <sys/types.h> 44275970Scy 45275970Scy#ifdef EVENT__HAVE_SYS_TIME_H 46275970Scy#include <sys/time.h> 47275970Scy#endif 48275970Scy 49275970Scy#ifdef EVENT__HAVE_SYS_SOCKET_H 50275970Scy#include <sys/socket.h> 51275970Scy#endif 52275970Scy 53275970Scy#ifdef EVENT__HAVE_SYS_UIO_H 54275970Scy#include <sys/uio.h> 55275970Scy#endif 56275970Scy 57275970Scy#ifdef EVENT__HAVE_SYS_IOCTL_H 58275970Scy#include <sys/ioctl.h> 59275970Scy#endif 60275970Scy 61275970Scy#ifdef EVENT__HAVE_SYS_MMAN_H 62275970Scy#include <sys/mman.h> 63275970Scy#endif 64275970Scy 65275970Scy#ifdef EVENT__HAVE_SYS_SENDFILE_H 66275970Scy#include <sys/sendfile.h> 67275970Scy#endif 68275970Scy#ifdef EVENT__HAVE_SYS_STAT_H 69275970Scy#include <sys/stat.h> 70275970Scy#endif 71275970Scy 72275970Scy 73275970Scy#include <errno.h> 74275970Scy#include <stdio.h> 75275970Scy#include <stdlib.h> 76275970Scy#include <string.h> 77275970Scy#ifdef EVENT__HAVE_STDARG_H 78275970Scy#include <stdarg.h> 79275970Scy#endif 80275970Scy#ifdef EVENT__HAVE_UNISTD_H 81275970Scy#include <unistd.h> 82275970Scy#endif 83275970Scy#include <limits.h> 84275970Scy 85275970Scy#include "event2/event.h" 86275970Scy#include "event2/buffer.h" 87275970Scy#include "event2/buffer_compat.h" 88275970Scy#include "event2/bufferevent.h" 89275970Scy#include "event2/bufferevent_compat.h" 90275970Scy#include "event2/bufferevent_struct.h" 91275970Scy#include "event2/thread.h" 92275970Scy#include "log-internal.h" 93275970Scy#include "mm-internal.h" 94275970Scy#include "util-internal.h" 95275970Scy#include "evthread-internal.h" 96275970Scy#include "evbuffer-internal.h" 97275970Scy#include "bufferevent-internal.h" 98275970Scy 99275970Scy/* some systems do not have MAP_FAILED */ 100275970Scy#ifndef MAP_FAILED 101275970Scy#define MAP_FAILED ((void *)-1) 102275970Scy#endif 103275970Scy 104275970Scy/* send file support */ 105275970Scy#if defined(EVENT__HAVE_SYS_SENDFILE_H) && defined(EVENT__HAVE_SENDFILE) && defined(__linux__) 106275970Scy#define USE_SENDFILE 1 107275970Scy#define SENDFILE_IS_LINUX 1 108275970Scy#elif defined(EVENT__HAVE_SENDFILE) && defined(__FreeBSD__) 109275970Scy#define USE_SENDFILE 1 110275970Scy#define SENDFILE_IS_FREEBSD 1 111275970Scy#elif defined(EVENT__HAVE_SENDFILE) && defined(__APPLE__) 112275970Scy#define USE_SENDFILE 1 113275970Scy#define SENDFILE_IS_MACOSX 1 114275970Scy#elif defined(EVENT__HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__) 115275970Scy#define USE_SENDFILE 1 116275970Scy#define SENDFILE_IS_SOLARIS 1 117275970Scy#endif 118275970Scy 119275970Scy/* Mask of user-selectable callback flags. */ 120275970Scy#define EVBUFFER_CB_USER_FLAGS 0xffff 121275970Scy/* Mask of all internal-use-only flags. */ 122275970Scy#define EVBUFFER_CB_INTERNAL_FLAGS 0xffff0000 123275970Scy 124275970Scy/* Flag set if the callback is using the cb_obsolete function pointer */ 125275970Scy#define EVBUFFER_CB_OBSOLETE 0x00040000 126275970Scy 127275970Scy/* evbuffer_chain support */ 128275970Scy#define CHAIN_SPACE_PTR(ch) ((ch)->buffer + (ch)->misalign + (ch)->off) 129275970Scy#define CHAIN_SPACE_LEN(ch) ((ch)->flags & EVBUFFER_IMMUTABLE ? \ 130275970Scy 0 : (ch)->buffer_len - ((ch)->misalign + (ch)->off)) 131275970Scy 132275970Scy#define CHAIN_PINNED(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_ANY) != 0) 133275970Scy#define CHAIN_PINNED_R(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_R) != 0) 134275970Scy 135275970Scy/* evbuffer_ptr support */ 136275970Scy#define PTR_NOT_FOUND(ptr) do { \ 137275970Scy (ptr)->pos = -1; \ 138275970Scy (ptr)->internal_.chain = NULL; \ 139275970Scy (ptr)->internal_.pos_in_chain = 0; \ 140275970Scy} while (0) 141275970Scy 142275970Scystatic void evbuffer_chain_align(struct evbuffer_chain *chain); 143275970Scystatic int evbuffer_chain_should_realign(struct evbuffer_chain *chain, 144275970Scy size_t datalen); 145275970Scystatic void evbuffer_deferred_callback(struct event_callback *cb, void *arg); 146275970Scystatic int evbuffer_ptr_memcmp(const struct evbuffer *buf, 147275970Scy const struct evbuffer_ptr *pos, const char *mem, size_t len); 148275970Scystatic struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf, 149275970Scy size_t datlen); 150275970Scystatic int evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos, 151275970Scy size_t howfar); 152275970Scystatic int evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg); 153275970Scystatic inline void evbuffer_chain_incref(struct evbuffer_chain *chain); 154275970Scy 155275970Scystatic struct evbuffer_chain * 156275970Scyevbuffer_chain_new(size_t size) 157275970Scy{ 158275970Scy struct evbuffer_chain *chain; 159275970Scy size_t to_alloc; 160275970Scy 161285612Sdelphij if (size > EVBUFFER_CHAIN_MAX - EVBUFFER_CHAIN_SIZE) 162285612Sdelphij return (NULL); 163285612Sdelphij 164275970Scy size += EVBUFFER_CHAIN_SIZE; 165275970Scy 166275970Scy /* get the next largest memory that can hold the buffer */ 167285612Sdelphij if (size < EVBUFFER_CHAIN_MAX / 2) { 168285612Sdelphij to_alloc = MIN_BUFFER_SIZE; 169285612Sdelphij while (to_alloc < size) { 170285612Sdelphij to_alloc <<= 1; 171285612Sdelphij } 172285612Sdelphij } else { 173285612Sdelphij to_alloc = size; 174285612Sdelphij } 175275970Scy 176275970Scy /* we get everything in one chunk */ 177275970Scy if ((chain = mm_malloc(to_alloc)) == NULL) 178275970Scy return (NULL); 179275970Scy 180275970Scy memset(chain, 0, EVBUFFER_CHAIN_SIZE); 181275970Scy 182275970Scy chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE; 183275970Scy 184275970Scy /* this way we can manipulate the buffer to different addresses, 185275970Scy * which is required for mmap for example. 186275970Scy */ 187275970Scy chain->buffer = EVBUFFER_CHAIN_EXTRA(u_char, chain); 188275970Scy 189275970Scy chain->refcnt = 1; 190275970Scy 191275970Scy return (chain); 192275970Scy} 193275970Scy 194275970Scystatic inline void 195275970Scyevbuffer_chain_free(struct evbuffer_chain *chain) 196275970Scy{ 197275970Scy EVUTIL_ASSERT(chain->refcnt > 0); 198275970Scy if (--chain->refcnt > 0) { 199275970Scy /* chain is still referenced by other chains */ 200275970Scy return; 201275970Scy } 202275970Scy 203275970Scy if (CHAIN_PINNED(chain)) { 204275970Scy /* will get freed once no longer dangling */ 205275970Scy chain->refcnt++; 206275970Scy chain->flags |= EVBUFFER_DANGLING; 207275970Scy return; 208275970Scy } 209275970Scy 210275970Scy /* safe to release chain, it's either a referencing 211275970Scy * chain or all references to it have been freed */ 212275970Scy if (chain->flags & EVBUFFER_REFERENCE) { 213275970Scy struct evbuffer_chain_reference *info = 214275970Scy EVBUFFER_CHAIN_EXTRA( 215275970Scy struct evbuffer_chain_reference, 216275970Scy chain); 217275970Scy if (info->cleanupfn) 218275970Scy (*info->cleanupfn)(chain->buffer, 219275970Scy chain->buffer_len, 220275970Scy info->extra); 221275970Scy } 222275970Scy if (chain->flags & EVBUFFER_FILESEGMENT) { 223275970Scy struct evbuffer_chain_file_segment *info = 224275970Scy EVBUFFER_CHAIN_EXTRA( 225275970Scy struct evbuffer_chain_file_segment, 226275970Scy chain); 227275970Scy if (info->segment) { 228275970Scy#ifdef _WIN32 229275970Scy if (info->segment->is_mapping) 230275970Scy UnmapViewOfFile(chain->buffer); 231275970Scy#endif 232275970Scy evbuffer_file_segment_free(info->segment); 233275970Scy } 234275970Scy } 235275970Scy if (chain->flags & EVBUFFER_MULTICAST) { 236275970Scy struct evbuffer_multicast_parent *info = 237275970Scy EVBUFFER_CHAIN_EXTRA( 238275970Scy struct evbuffer_multicast_parent, 239275970Scy chain); 240275970Scy /* referencing chain is being freed, decrease 241275970Scy * refcounts of source chain and associated 242275970Scy * evbuffer (which get freed once both reach 243275970Scy * zero) */ 244275970Scy EVUTIL_ASSERT(info->source != NULL); 245275970Scy EVUTIL_ASSERT(info->parent != NULL); 246275970Scy EVBUFFER_LOCK(info->source); 247275970Scy evbuffer_chain_free(info->parent); 248275970Scy evbuffer_decref_and_unlock_(info->source); 249275970Scy } 250275970Scy 251275970Scy mm_free(chain); 252275970Scy} 253275970Scy 254275970Scystatic void 255275970Scyevbuffer_free_all_chains(struct evbuffer_chain *chain) 256275970Scy{ 257275970Scy struct evbuffer_chain *next; 258275970Scy for (; chain; chain = next) { 259275970Scy next = chain->next; 260275970Scy evbuffer_chain_free(chain); 261275970Scy } 262275970Scy} 263275970Scy 264275970Scy#ifndef NDEBUG 265275970Scystatic int 266275970Scyevbuffer_chains_all_empty(struct evbuffer_chain *chain) 267275970Scy{ 268275970Scy for (; chain; chain = chain->next) { 269275970Scy if (chain->off) 270275970Scy return 0; 271275970Scy } 272275970Scy return 1; 273275970Scy} 274275970Scy#else 275275970Scy/* The definition is needed for EVUTIL_ASSERT, which uses sizeof to avoid 276275970Scy"unused variable" warnings. */ 277275970Scystatic inline int evbuffer_chains_all_empty(struct evbuffer_chain *chain) { 278275970Scy return 1; 279275970Scy} 280275970Scy#endif 281275970Scy 282275970Scy/* Free all trailing chains in 'buf' that are neither pinned nor empty, prior 283275970Scy * to replacing them all with a new chain. Return a pointer to the place 284275970Scy * where the new chain will go. 285275970Scy * 286275970Scy * Internal; requires lock. The caller must fix up buf->last and buf->first 287275970Scy * as needed; they might have been freed. 288275970Scy */ 289275970Scystatic struct evbuffer_chain ** 290275970Scyevbuffer_free_trailing_empty_chains(struct evbuffer *buf) 291275970Scy{ 292275970Scy struct evbuffer_chain **ch = buf->last_with_datap; 293275970Scy /* Find the first victim chain. It might be *last_with_datap */ 294275970Scy while ((*ch) && ((*ch)->off != 0 || CHAIN_PINNED(*ch))) 295275970Scy ch = &(*ch)->next; 296275970Scy if (*ch) { 297275970Scy EVUTIL_ASSERT(evbuffer_chains_all_empty(*ch)); 298275970Scy evbuffer_free_all_chains(*ch); 299275970Scy *ch = NULL; 300275970Scy } 301275970Scy return ch; 302275970Scy} 303275970Scy 304275970Scy/* Add a single chain 'chain' to the end of 'buf', freeing trailing empty 305275970Scy * chains as necessary. Requires lock. Does not schedule callbacks. 306275970Scy */ 307275970Scystatic void 308275970Scyevbuffer_chain_insert(struct evbuffer *buf, 309275970Scy struct evbuffer_chain *chain) 310275970Scy{ 311275970Scy ASSERT_EVBUFFER_LOCKED(buf); 312275970Scy if (*buf->last_with_datap == NULL) { 313275970Scy /* There are no chains data on the buffer at all. */ 314275970Scy EVUTIL_ASSERT(buf->last_with_datap == &buf->first); 315275970Scy EVUTIL_ASSERT(buf->first == NULL); 316275970Scy buf->first = buf->last = chain; 317275970Scy } else { 318275970Scy struct evbuffer_chain **chp; 319275970Scy chp = evbuffer_free_trailing_empty_chains(buf); 320275970Scy *chp = chain; 321275970Scy if (chain->off) 322275970Scy buf->last_with_datap = chp; 323275970Scy buf->last = chain; 324275970Scy } 325275970Scy buf->total_len += chain->off; 326275970Scy} 327275970Scy 328275970Scystatic inline struct evbuffer_chain * 329275970Scyevbuffer_chain_insert_new(struct evbuffer *buf, size_t datlen) 330275970Scy{ 331275970Scy struct evbuffer_chain *chain; 332275970Scy if ((chain = evbuffer_chain_new(datlen)) == NULL) 333275970Scy return NULL; 334275970Scy evbuffer_chain_insert(buf, chain); 335275970Scy return chain; 336275970Scy} 337275970Scy 338275970Scyvoid 339275970Scyevbuffer_chain_pin_(struct evbuffer_chain *chain, unsigned flag) 340275970Scy{ 341275970Scy EVUTIL_ASSERT((chain->flags & flag) == 0); 342275970Scy chain->flags |= flag; 343275970Scy} 344275970Scy 345275970Scyvoid 346275970Scyevbuffer_chain_unpin_(struct evbuffer_chain *chain, unsigned flag) 347275970Scy{ 348275970Scy EVUTIL_ASSERT((chain->flags & flag) != 0); 349275970Scy chain->flags &= ~flag; 350275970Scy if (chain->flags & EVBUFFER_DANGLING) 351275970Scy evbuffer_chain_free(chain); 352275970Scy} 353275970Scy 354275970Scystatic inline void 355275970Scyevbuffer_chain_incref(struct evbuffer_chain *chain) 356275970Scy{ 357275970Scy ++chain->refcnt; 358275970Scy} 359275970Scy 360275970Scystruct evbuffer * 361275970Scyevbuffer_new(void) 362275970Scy{ 363275970Scy struct evbuffer *buffer; 364275970Scy 365275970Scy buffer = mm_calloc(1, sizeof(struct evbuffer)); 366275970Scy if (buffer == NULL) 367275970Scy return (NULL); 368275970Scy 369275970Scy LIST_INIT(&buffer->callbacks); 370275970Scy buffer->refcnt = 1; 371275970Scy buffer->last_with_datap = &buffer->first; 372275970Scy 373275970Scy return (buffer); 374275970Scy} 375275970Scy 376275970Scyint 377275970Scyevbuffer_set_flags(struct evbuffer *buf, ev_uint64_t flags) 378275970Scy{ 379275970Scy EVBUFFER_LOCK(buf); 380275970Scy buf->flags |= (ev_uint32_t)flags; 381275970Scy EVBUFFER_UNLOCK(buf); 382275970Scy return 0; 383275970Scy} 384275970Scy 385275970Scyint 386275970Scyevbuffer_clear_flags(struct evbuffer *buf, ev_uint64_t flags) 387275970Scy{ 388275970Scy EVBUFFER_LOCK(buf); 389275970Scy buf->flags &= ~(ev_uint32_t)flags; 390275970Scy EVBUFFER_UNLOCK(buf); 391275970Scy return 0; 392275970Scy} 393275970Scy 394275970Scyvoid 395275970Scyevbuffer_incref_(struct evbuffer *buf) 396275970Scy{ 397275970Scy EVBUFFER_LOCK(buf); 398275970Scy ++buf->refcnt; 399275970Scy EVBUFFER_UNLOCK(buf); 400275970Scy} 401275970Scy 402275970Scyvoid 403275970Scyevbuffer_incref_and_lock_(struct evbuffer *buf) 404275970Scy{ 405275970Scy EVBUFFER_LOCK(buf); 406275970Scy ++buf->refcnt; 407275970Scy} 408275970Scy 409275970Scyint 410275970Scyevbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base) 411275970Scy{ 412275970Scy EVBUFFER_LOCK(buffer); 413275970Scy buffer->cb_queue = base; 414275970Scy buffer->deferred_cbs = 1; 415275970Scy event_deferred_cb_init_(&buffer->deferred, 416275970Scy event_base_get_npriorities(base) / 2, 417275970Scy evbuffer_deferred_callback, buffer); 418275970Scy EVBUFFER_UNLOCK(buffer); 419275970Scy return 0; 420275970Scy} 421275970Scy 422275970Scyint 423275970Scyevbuffer_enable_locking(struct evbuffer *buf, void *lock) 424275970Scy{ 425275970Scy#ifdef EVENT__DISABLE_THREAD_SUPPORT 426275970Scy return -1; 427275970Scy#else 428275970Scy if (buf->lock) 429275970Scy return -1; 430275970Scy 431275970Scy if (!lock) { 432275970Scy EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE); 433275970Scy if (!lock) 434275970Scy return -1; 435275970Scy buf->lock = lock; 436275970Scy buf->own_lock = 1; 437275970Scy } else { 438275970Scy buf->lock = lock; 439275970Scy buf->own_lock = 0; 440275970Scy } 441275970Scy 442275970Scy return 0; 443275970Scy#endif 444275970Scy} 445275970Scy 446275970Scyvoid 447275970Scyevbuffer_set_parent_(struct evbuffer *buf, struct bufferevent *bev) 448275970Scy{ 449275970Scy EVBUFFER_LOCK(buf); 450275970Scy buf->parent = bev; 451275970Scy EVBUFFER_UNLOCK(buf); 452275970Scy} 453275970Scy 454275970Scystatic void 455275970Scyevbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred) 456275970Scy{ 457275970Scy struct evbuffer_cb_entry *cbent, *next; 458275970Scy struct evbuffer_cb_info info; 459275970Scy size_t new_size; 460275970Scy ev_uint32_t mask, masked_val; 461275970Scy int clear = 1; 462275970Scy 463275970Scy if (running_deferred) { 464275970Scy mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED; 465275970Scy masked_val = EVBUFFER_CB_ENABLED; 466275970Scy } else if (buffer->deferred_cbs) { 467275970Scy mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED; 468275970Scy masked_val = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED; 469275970Scy /* Don't zero-out n_add/n_del, since the deferred callbacks 470275970Scy will want to see them. */ 471275970Scy clear = 0; 472275970Scy } else { 473275970Scy mask = EVBUFFER_CB_ENABLED; 474275970Scy masked_val = EVBUFFER_CB_ENABLED; 475275970Scy } 476275970Scy 477275970Scy ASSERT_EVBUFFER_LOCKED(buffer); 478275970Scy 479275970Scy if (LIST_EMPTY(&buffer->callbacks)) { 480275970Scy buffer->n_add_for_cb = buffer->n_del_for_cb = 0; 481275970Scy return; 482275970Scy } 483275970Scy if (buffer->n_add_for_cb == 0 && buffer->n_del_for_cb == 0) 484275970Scy return; 485275970Scy 486275970Scy new_size = buffer->total_len; 487275970Scy info.orig_size = new_size + buffer->n_del_for_cb - buffer->n_add_for_cb; 488275970Scy info.n_added = buffer->n_add_for_cb; 489275970Scy info.n_deleted = buffer->n_del_for_cb; 490275970Scy if (clear) { 491275970Scy buffer->n_add_for_cb = 0; 492275970Scy buffer->n_del_for_cb = 0; 493275970Scy } 494275970Scy for (cbent = LIST_FIRST(&buffer->callbacks); 495275970Scy cbent != LIST_END(&buffer->callbacks); 496275970Scy cbent = next) { 497275970Scy /* Get the 'next' pointer now in case this callback decides 498275970Scy * to remove itself or something. */ 499275970Scy next = LIST_NEXT(cbent, next); 500275970Scy 501275970Scy if ((cbent->flags & mask) != masked_val) 502275970Scy continue; 503275970Scy 504275970Scy if ((cbent->flags & EVBUFFER_CB_OBSOLETE)) 505275970Scy cbent->cb.cb_obsolete(buffer, 506275970Scy info.orig_size, new_size, cbent->cbarg); 507275970Scy else 508275970Scy cbent->cb.cb_func(buffer, &info, cbent->cbarg); 509275970Scy } 510275970Scy} 511275970Scy 512275970Scyvoid 513275970Scyevbuffer_invoke_callbacks_(struct evbuffer *buffer) 514275970Scy{ 515275970Scy if (LIST_EMPTY(&buffer->callbacks)) { 516275970Scy buffer->n_add_for_cb = buffer->n_del_for_cb = 0; 517275970Scy return; 518275970Scy } 519275970Scy 520275970Scy if (buffer->deferred_cbs) { 521275970Scy if (event_deferred_cb_schedule_(buffer->cb_queue, &buffer->deferred)) { 522275970Scy evbuffer_incref_and_lock_(buffer); 523275970Scy if (buffer->parent) 524275970Scy bufferevent_incref_(buffer->parent); 525275970Scy } 526275970Scy EVBUFFER_UNLOCK(buffer); 527275970Scy } 528275970Scy 529275970Scy evbuffer_run_callbacks(buffer, 0); 530275970Scy} 531275970Scy 532275970Scystatic void 533275970Scyevbuffer_deferred_callback(struct event_callback *cb, void *arg) 534275970Scy{ 535275970Scy struct bufferevent *parent = NULL; 536275970Scy struct evbuffer *buffer = arg; 537275970Scy 538275970Scy /* XXXX It would be better to run these callbacks without holding the 539275970Scy * lock */ 540275970Scy EVBUFFER_LOCK(buffer); 541275970Scy parent = buffer->parent; 542275970Scy evbuffer_run_callbacks(buffer, 1); 543275970Scy evbuffer_decref_and_unlock_(buffer); 544275970Scy if (parent) 545275970Scy bufferevent_decref_(parent); 546275970Scy} 547275970Scy 548275970Scystatic void 549275970Scyevbuffer_remove_all_callbacks(struct evbuffer *buffer) 550275970Scy{ 551275970Scy struct evbuffer_cb_entry *cbent; 552275970Scy 553275970Scy while ((cbent = LIST_FIRST(&buffer->callbacks))) { 554275970Scy LIST_REMOVE(cbent, next); 555275970Scy mm_free(cbent); 556275970Scy } 557275970Scy} 558275970Scy 559275970Scyvoid 560275970Scyevbuffer_decref_and_unlock_(struct evbuffer *buffer) 561275970Scy{ 562275970Scy struct evbuffer_chain *chain, *next; 563275970Scy ASSERT_EVBUFFER_LOCKED(buffer); 564275970Scy 565275970Scy EVUTIL_ASSERT(buffer->refcnt > 0); 566275970Scy 567275970Scy if (--buffer->refcnt > 0) { 568275970Scy EVBUFFER_UNLOCK(buffer); 569275970Scy return; 570275970Scy } 571275970Scy 572275970Scy for (chain = buffer->first; chain != NULL; chain = next) { 573275970Scy next = chain->next; 574275970Scy evbuffer_chain_free(chain); 575275970Scy } 576275970Scy evbuffer_remove_all_callbacks(buffer); 577275970Scy if (buffer->deferred_cbs) 578275970Scy event_deferred_cb_cancel_(buffer->cb_queue, &buffer->deferred); 579275970Scy 580275970Scy EVBUFFER_UNLOCK(buffer); 581275970Scy if (buffer->own_lock) 582275970Scy EVTHREAD_FREE_LOCK(buffer->lock, EVTHREAD_LOCKTYPE_RECURSIVE); 583275970Scy mm_free(buffer); 584275970Scy} 585275970Scy 586275970Scyvoid 587275970Scyevbuffer_free(struct evbuffer *buffer) 588275970Scy{ 589275970Scy EVBUFFER_LOCK(buffer); 590275970Scy evbuffer_decref_and_unlock_(buffer); 591275970Scy} 592275970Scy 593275970Scyvoid 594275970Scyevbuffer_lock(struct evbuffer *buf) 595275970Scy{ 596275970Scy EVBUFFER_LOCK(buf); 597275970Scy} 598275970Scy 599275970Scyvoid 600275970Scyevbuffer_unlock(struct evbuffer *buf) 601275970Scy{ 602275970Scy EVBUFFER_UNLOCK(buf); 603275970Scy} 604275970Scy 605275970Scysize_t 606275970Scyevbuffer_get_length(const struct evbuffer *buffer) 607275970Scy{ 608275970Scy size_t result; 609275970Scy 610275970Scy EVBUFFER_LOCK(buffer); 611275970Scy 612275970Scy result = (buffer->total_len); 613275970Scy 614275970Scy EVBUFFER_UNLOCK(buffer); 615275970Scy 616275970Scy return result; 617275970Scy} 618275970Scy 619275970Scysize_t 620275970Scyevbuffer_get_contiguous_space(const struct evbuffer *buf) 621275970Scy{ 622275970Scy struct evbuffer_chain *chain; 623275970Scy size_t result; 624275970Scy 625275970Scy EVBUFFER_LOCK(buf); 626275970Scy chain = buf->first; 627275970Scy result = (chain != NULL ? chain->off : 0); 628275970Scy EVBUFFER_UNLOCK(buf); 629275970Scy 630275970Scy return result; 631275970Scy} 632275970Scy 633275970Scysize_t 634275970Scyevbuffer_add_iovec(struct evbuffer * buf, struct evbuffer_iovec * vec, int n_vec) { 635275970Scy int n; 636275970Scy size_t res; 637275970Scy size_t to_alloc; 638275970Scy 639275970Scy EVBUFFER_LOCK(buf); 640275970Scy 641275970Scy res = to_alloc = 0; 642275970Scy 643275970Scy for (n = 0; n < n_vec; n++) { 644275970Scy to_alloc += vec[n].iov_len; 645275970Scy } 646275970Scy 647275970Scy if (evbuffer_expand_fast_(buf, to_alloc, 2) < 0) { 648275970Scy goto done; 649275970Scy } 650275970Scy 651275970Scy for (n = 0; n < n_vec; n++) { 652275970Scy /* XXX each 'add' call here does a bunch of setup that's 653275970Scy * obviated by evbuffer_expand_fast_, and some cleanup that we 654275970Scy * would like to do only once. Instead we should just extract 655275970Scy * the part of the code that's needed. */ 656275970Scy 657275970Scy if (evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len) < 0) { 658275970Scy goto done; 659275970Scy } 660275970Scy 661275970Scy res += vec[n].iov_len; 662275970Scy } 663275970Scy 664275970Scydone: 665275970Scy EVBUFFER_UNLOCK(buf); 666275970Scy return res; 667275970Scy} 668275970Scy 669275970Scyint 670275970Scyevbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, 671275970Scy struct evbuffer_iovec *vec, int n_vecs) 672275970Scy{ 673275970Scy struct evbuffer_chain *chain, **chainp; 674275970Scy int n = -1; 675275970Scy 676275970Scy EVBUFFER_LOCK(buf); 677275970Scy if (buf->freeze_end) 678275970Scy goto done; 679275970Scy if (n_vecs < 1) 680275970Scy goto done; 681275970Scy if (n_vecs == 1) { 682275970Scy if ((chain = evbuffer_expand_singlechain(buf, size)) == NULL) 683275970Scy goto done; 684275970Scy 685275970Scy vec[0].iov_base = CHAIN_SPACE_PTR(chain); 686275970Scy vec[0].iov_len = (size_t) CHAIN_SPACE_LEN(chain); 687275970Scy EVUTIL_ASSERT(size<0 || (size_t)vec[0].iov_len >= (size_t)size); 688275970Scy n = 1; 689275970Scy } else { 690275970Scy if (evbuffer_expand_fast_(buf, size, n_vecs)<0) 691275970Scy goto done; 692275970Scy n = evbuffer_read_setup_vecs_(buf, size, vec, n_vecs, 693275970Scy &chainp, 0); 694275970Scy } 695275970Scy 696275970Scydone: 697275970Scy EVBUFFER_UNLOCK(buf); 698275970Scy return n; 699275970Scy 700275970Scy} 701275970Scy 702275970Scystatic int 703275970Scyadvance_last_with_data(struct evbuffer *buf) 704275970Scy{ 705275970Scy int n = 0; 706275970Scy ASSERT_EVBUFFER_LOCKED(buf); 707275970Scy 708275970Scy if (!*buf->last_with_datap) 709275970Scy return 0; 710275970Scy 711275970Scy while ((*buf->last_with_datap)->next && (*buf->last_with_datap)->next->off) { 712275970Scy buf->last_with_datap = &(*buf->last_with_datap)->next; 713275970Scy ++n; 714275970Scy } 715275970Scy return n; 716275970Scy} 717275970Scy 718275970Scyint 719275970Scyevbuffer_commit_space(struct evbuffer *buf, 720275970Scy struct evbuffer_iovec *vec, int n_vecs) 721275970Scy{ 722275970Scy struct evbuffer_chain *chain, **firstchainp, **chainp; 723275970Scy int result = -1; 724275970Scy size_t added = 0; 725275970Scy int i; 726275970Scy 727275970Scy EVBUFFER_LOCK(buf); 728275970Scy 729275970Scy if (buf->freeze_end) 730275970Scy goto done; 731275970Scy if (n_vecs == 0) { 732275970Scy result = 0; 733275970Scy goto done; 734275970Scy } else if (n_vecs == 1 && 735275970Scy (buf->last && vec[0].iov_base == (void*)CHAIN_SPACE_PTR(buf->last))) { 736275970Scy /* The user only got or used one chain; it might not 737275970Scy * be the first one with space in it. */ 738275970Scy if ((size_t)vec[0].iov_len > (size_t)CHAIN_SPACE_LEN(buf->last)) 739275970Scy goto done; 740275970Scy buf->last->off += vec[0].iov_len; 741275970Scy added = vec[0].iov_len; 742275970Scy if (added) 743275970Scy advance_last_with_data(buf); 744275970Scy goto okay; 745275970Scy } 746275970Scy 747275970Scy /* Advance 'firstchain' to the first chain with space in it. */ 748275970Scy firstchainp = buf->last_with_datap; 749275970Scy if (!*firstchainp) 750275970Scy goto done; 751275970Scy if (CHAIN_SPACE_LEN(*firstchainp) == 0) { 752275970Scy firstchainp = &(*firstchainp)->next; 753275970Scy } 754275970Scy 755275970Scy chain = *firstchainp; 756275970Scy /* pass 1: make sure that the pointers and lengths of vecs[] are in 757275970Scy * bounds before we try to commit anything. */ 758275970Scy for (i=0; i<n_vecs; ++i) { 759275970Scy if (!chain) 760275970Scy goto done; 761275970Scy if (vec[i].iov_base != (void*)CHAIN_SPACE_PTR(chain) || 762275970Scy (size_t)vec[i].iov_len > CHAIN_SPACE_LEN(chain)) 763275970Scy goto done; 764275970Scy chain = chain->next; 765275970Scy } 766275970Scy /* pass 2: actually adjust all the chains. */ 767275970Scy chainp = firstchainp; 768275970Scy for (i=0; i<n_vecs; ++i) { 769275970Scy (*chainp)->off += vec[i].iov_len; 770275970Scy added += vec[i].iov_len; 771275970Scy if (vec[i].iov_len) { 772275970Scy buf->last_with_datap = chainp; 773275970Scy } 774275970Scy chainp = &(*chainp)->next; 775275970Scy } 776275970Scy 777275970Scyokay: 778275970Scy buf->total_len += added; 779275970Scy buf->n_add_for_cb += added; 780275970Scy result = 0; 781275970Scy evbuffer_invoke_callbacks_(buf); 782275970Scy 783275970Scydone: 784275970Scy EVBUFFER_UNLOCK(buf); 785275970Scy return result; 786275970Scy} 787275970Scy 788275970Scystatic inline int 789275970ScyHAS_PINNED_R(struct evbuffer *buf) 790275970Scy{ 791275970Scy return (buf->last && CHAIN_PINNED_R(buf->last)); 792275970Scy} 793275970Scy 794275970Scystatic inline void 795275970ScyZERO_CHAIN(struct evbuffer *dst) 796275970Scy{ 797275970Scy ASSERT_EVBUFFER_LOCKED(dst); 798275970Scy dst->first = NULL; 799275970Scy dst->last = NULL; 800275970Scy dst->last_with_datap = &(dst)->first; 801275970Scy dst->total_len = 0; 802275970Scy} 803275970Scy 804275970Scy/* Prepares the contents of src to be moved to another buffer by removing 805275970Scy * read-pinned chains. The first pinned chain is saved in first, and the 806275970Scy * last in last. If src has no read-pinned chains, first and last are set 807275970Scy * to NULL. */ 808275970Scystatic int 809275970ScyPRESERVE_PINNED(struct evbuffer *src, struct evbuffer_chain **first, 810275970Scy struct evbuffer_chain **last) 811275970Scy{ 812275970Scy struct evbuffer_chain *chain, **pinned; 813275970Scy 814275970Scy ASSERT_EVBUFFER_LOCKED(src); 815275970Scy 816275970Scy if (!HAS_PINNED_R(src)) { 817275970Scy *first = *last = NULL; 818275970Scy return 0; 819275970Scy } 820275970Scy 821275970Scy pinned = src->last_with_datap; 822275970Scy if (!CHAIN_PINNED_R(*pinned)) 823275970Scy pinned = &(*pinned)->next; 824275970Scy EVUTIL_ASSERT(CHAIN_PINNED_R(*pinned)); 825275970Scy chain = *first = *pinned; 826275970Scy *last = src->last; 827275970Scy 828275970Scy /* If there's data in the first pinned chain, we need to allocate 829275970Scy * a new chain and copy the data over. */ 830275970Scy if (chain->off) { 831275970Scy struct evbuffer_chain *tmp; 832275970Scy 833275970Scy EVUTIL_ASSERT(pinned == src->last_with_datap); 834275970Scy tmp = evbuffer_chain_new(chain->off); 835275970Scy if (!tmp) 836275970Scy return -1; 837275970Scy memcpy(tmp->buffer, chain->buffer + chain->misalign, 838275970Scy chain->off); 839275970Scy tmp->off = chain->off; 840275970Scy *src->last_with_datap = tmp; 841275970Scy src->last = tmp; 842275970Scy chain->misalign += chain->off; 843275970Scy chain->off = 0; 844275970Scy } else { 845275970Scy src->last = *src->last_with_datap; 846275970Scy *pinned = NULL; 847275970Scy } 848275970Scy 849275970Scy return 0; 850275970Scy} 851275970Scy 852275970Scystatic inline void 853275970ScyRESTORE_PINNED(struct evbuffer *src, struct evbuffer_chain *pinned, 854275970Scy struct evbuffer_chain *last) 855275970Scy{ 856275970Scy ASSERT_EVBUFFER_LOCKED(src); 857275970Scy 858275970Scy if (!pinned) { 859275970Scy ZERO_CHAIN(src); 860275970Scy return; 861275970Scy } 862275970Scy 863275970Scy src->first = pinned; 864275970Scy src->last = last; 865275970Scy src->last_with_datap = &src->first; 866275970Scy src->total_len = 0; 867275970Scy} 868275970Scy 869275970Scystatic inline void 870275970ScyCOPY_CHAIN(struct evbuffer *dst, struct evbuffer *src) 871275970Scy{ 872275970Scy ASSERT_EVBUFFER_LOCKED(dst); 873275970Scy ASSERT_EVBUFFER_LOCKED(src); 874275970Scy dst->first = src->first; 875275970Scy if (src->last_with_datap == &src->first) 876275970Scy dst->last_with_datap = &dst->first; 877275970Scy else 878275970Scy dst->last_with_datap = src->last_with_datap; 879275970Scy dst->last = src->last; 880275970Scy dst->total_len = src->total_len; 881275970Scy} 882275970Scy 883275970Scystatic void 884275970ScyAPPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src) 885275970Scy{ 886275970Scy ASSERT_EVBUFFER_LOCKED(dst); 887275970Scy ASSERT_EVBUFFER_LOCKED(src); 888275970Scy dst->last->next = src->first; 889275970Scy if (src->last_with_datap == &src->first) 890275970Scy dst->last_with_datap = &dst->last->next; 891275970Scy else 892275970Scy dst->last_with_datap = src->last_with_datap; 893275970Scy dst->last = src->last; 894275970Scy dst->total_len += src->total_len; 895275970Scy} 896275970Scy 897275970Scystatic inline void 898275970ScyAPPEND_CHAIN_MULTICAST(struct evbuffer *dst, struct evbuffer *src) 899275970Scy{ 900275970Scy struct evbuffer_chain *tmp; 901275970Scy struct evbuffer_chain *chain = src->first; 902275970Scy struct evbuffer_multicast_parent *extra; 903275970Scy 904275970Scy ASSERT_EVBUFFER_LOCKED(dst); 905275970Scy ASSERT_EVBUFFER_LOCKED(src); 906275970Scy 907275970Scy for (; chain; chain = chain->next) { 908275970Scy if (!chain->off || chain->flags & EVBUFFER_DANGLING) { 909275970Scy /* skip empty chains */ 910275970Scy continue; 911275970Scy } 912275970Scy 913275970Scy tmp = evbuffer_chain_new(sizeof(struct evbuffer_multicast_parent)); 914275970Scy if (!tmp) { 915275970Scy event_warn("%s: out of memory", __func__); 916275970Scy return; 917275970Scy } 918275970Scy extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_multicast_parent, tmp); 919275970Scy /* reference evbuffer containing source chain so it 920275970Scy * doesn't get released while the chain is still 921275970Scy * being referenced to */ 922275970Scy evbuffer_incref_(src); 923275970Scy extra->source = src; 924275970Scy /* reference source chain which now becomes immutable */ 925275970Scy evbuffer_chain_incref(chain); 926275970Scy extra->parent = chain; 927275970Scy chain->flags |= EVBUFFER_IMMUTABLE; 928275970Scy tmp->buffer_len = chain->buffer_len; 929275970Scy tmp->misalign = chain->misalign; 930275970Scy tmp->off = chain->off; 931275970Scy tmp->flags |= EVBUFFER_MULTICAST|EVBUFFER_IMMUTABLE; 932275970Scy tmp->buffer = chain->buffer; 933275970Scy evbuffer_chain_insert(dst, tmp); 934275970Scy } 935275970Scy} 936275970Scy 937275970Scystatic void 938275970ScyPREPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src) 939275970Scy{ 940275970Scy ASSERT_EVBUFFER_LOCKED(dst); 941275970Scy ASSERT_EVBUFFER_LOCKED(src); 942275970Scy src->last->next = dst->first; 943275970Scy dst->first = src->first; 944275970Scy dst->total_len += src->total_len; 945275970Scy if (*dst->last_with_datap == NULL) { 946275970Scy if (src->last_with_datap == &(src)->first) 947275970Scy dst->last_with_datap = &dst->first; 948275970Scy else 949275970Scy dst->last_with_datap = src->last_with_datap; 950275970Scy } else if (dst->last_with_datap == &dst->first) { 951275970Scy dst->last_with_datap = &src->last->next; 952275970Scy } 953275970Scy} 954275970Scy 955275970Scyint 956275970Scyevbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) 957275970Scy{ 958275970Scy struct evbuffer_chain *pinned, *last; 959275970Scy size_t in_total_len, out_total_len; 960275970Scy int result = 0; 961275970Scy 962275970Scy EVBUFFER_LOCK2(inbuf, outbuf); 963275970Scy in_total_len = inbuf->total_len; 964275970Scy out_total_len = outbuf->total_len; 965275970Scy 966275970Scy if (in_total_len == 0 || outbuf == inbuf) 967275970Scy goto done; 968275970Scy 969275970Scy if (outbuf->freeze_end || inbuf->freeze_start) { 970275970Scy result = -1; 971275970Scy goto done; 972275970Scy } 973275970Scy 974275970Scy if (PRESERVE_PINNED(inbuf, &pinned, &last) < 0) { 975275970Scy result = -1; 976275970Scy goto done; 977275970Scy } 978275970Scy 979275970Scy if (out_total_len == 0) { 980275970Scy /* There might be an empty chain at the start of outbuf; free 981275970Scy * it. */ 982275970Scy evbuffer_free_all_chains(outbuf->first); 983275970Scy COPY_CHAIN(outbuf, inbuf); 984275970Scy } else { 985275970Scy APPEND_CHAIN(outbuf, inbuf); 986275970Scy } 987275970Scy 988275970Scy RESTORE_PINNED(inbuf, pinned, last); 989275970Scy 990275970Scy inbuf->n_del_for_cb += in_total_len; 991275970Scy outbuf->n_add_for_cb += in_total_len; 992275970Scy 993275970Scy evbuffer_invoke_callbacks_(inbuf); 994275970Scy evbuffer_invoke_callbacks_(outbuf); 995275970Scy 996275970Scydone: 997275970Scy EVBUFFER_UNLOCK2(inbuf, outbuf); 998275970Scy return result; 999275970Scy} 1000275970Scy 1001275970Scyint 1002275970Scyevbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf) 1003275970Scy{ 1004275970Scy size_t in_total_len, out_total_len; 1005275970Scy struct evbuffer_chain *chain; 1006275970Scy int result = 0; 1007275970Scy 1008275970Scy EVBUFFER_LOCK2(inbuf, outbuf); 1009275970Scy in_total_len = inbuf->total_len; 1010275970Scy out_total_len = outbuf->total_len; 1011275970Scy chain = inbuf->first; 1012275970Scy 1013275970Scy if (in_total_len == 0) 1014275970Scy goto done; 1015275970Scy 1016275970Scy if (outbuf->freeze_end || outbuf == inbuf) { 1017275970Scy result = -1; 1018275970Scy goto done; 1019275970Scy } 1020275970Scy 1021275970Scy for (; chain; chain = chain->next) { 1022275970Scy if ((chain->flags & (EVBUFFER_FILESEGMENT|EVBUFFER_SENDFILE|EVBUFFER_MULTICAST)) != 0) { 1023275970Scy /* chain type can not be referenced */ 1024275970Scy result = -1; 1025275970Scy goto done; 1026275970Scy } 1027275970Scy } 1028275970Scy 1029275970Scy if (out_total_len == 0) { 1030275970Scy /* There might be an empty chain at the start of outbuf; free 1031275970Scy * it. */ 1032275970Scy evbuffer_free_all_chains(outbuf->first); 1033275970Scy } 1034275970Scy APPEND_CHAIN_MULTICAST(outbuf, inbuf); 1035275970Scy 1036275970Scy outbuf->n_add_for_cb += in_total_len; 1037275970Scy evbuffer_invoke_callbacks_(outbuf); 1038275970Scy 1039275970Scydone: 1040275970Scy EVBUFFER_UNLOCK2(inbuf, outbuf); 1041275970Scy return result; 1042275970Scy} 1043275970Scy 1044275970Scyint 1045275970Scyevbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) 1046275970Scy{ 1047275970Scy struct evbuffer_chain *pinned, *last; 1048275970Scy size_t in_total_len, out_total_len; 1049275970Scy int result = 0; 1050275970Scy 1051275970Scy EVBUFFER_LOCK2(inbuf, outbuf); 1052275970Scy 1053275970Scy in_total_len = inbuf->total_len; 1054275970Scy out_total_len = outbuf->total_len; 1055275970Scy 1056275970Scy if (!in_total_len || inbuf == outbuf) 1057275970Scy goto done; 1058275970Scy 1059275970Scy if (outbuf->freeze_start || inbuf->freeze_start) { 1060275970Scy result = -1; 1061275970Scy goto done; 1062275970Scy } 1063275970Scy 1064275970Scy if (PRESERVE_PINNED(inbuf, &pinned, &last) < 0) { 1065275970Scy result = -1; 1066275970Scy goto done; 1067275970Scy } 1068275970Scy 1069275970Scy if (out_total_len == 0) { 1070275970Scy /* There might be an empty chain at the start of outbuf; free 1071275970Scy * it. */ 1072275970Scy evbuffer_free_all_chains(outbuf->first); 1073275970Scy COPY_CHAIN(outbuf, inbuf); 1074275970Scy } else { 1075275970Scy PREPEND_CHAIN(outbuf, inbuf); 1076275970Scy } 1077275970Scy 1078275970Scy RESTORE_PINNED(inbuf, pinned, last); 1079275970Scy 1080275970Scy inbuf->n_del_for_cb += in_total_len; 1081275970Scy outbuf->n_add_for_cb += in_total_len; 1082275970Scy 1083275970Scy evbuffer_invoke_callbacks_(inbuf); 1084275970Scy evbuffer_invoke_callbacks_(outbuf); 1085275970Scydone: 1086275970Scy EVBUFFER_UNLOCK2(inbuf, outbuf); 1087275970Scy return result; 1088275970Scy} 1089275970Scy 1090275970Scyint 1091275970Scyevbuffer_drain(struct evbuffer *buf, size_t len) 1092275970Scy{ 1093275970Scy struct evbuffer_chain *chain, *next; 1094275970Scy size_t remaining, old_len; 1095275970Scy int result = 0; 1096275970Scy 1097275970Scy EVBUFFER_LOCK(buf); 1098275970Scy old_len = buf->total_len; 1099275970Scy 1100275970Scy if (old_len == 0) 1101275970Scy goto done; 1102275970Scy 1103275970Scy if (buf->freeze_start) { 1104275970Scy result = -1; 1105275970Scy goto done; 1106275970Scy } 1107275970Scy 1108275970Scy if (len >= old_len && !HAS_PINNED_R(buf)) { 1109275970Scy len = old_len; 1110275970Scy for (chain = buf->first; chain != NULL; chain = next) { 1111275970Scy next = chain->next; 1112275970Scy evbuffer_chain_free(chain); 1113275970Scy } 1114275970Scy 1115275970Scy ZERO_CHAIN(buf); 1116275970Scy } else { 1117275970Scy if (len >= old_len) 1118275970Scy len = old_len; 1119275970Scy 1120275970Scy buf->total_len -= len; 1121275970Scy remaining = len; 1122275970Scy for (chain = buf->first; 1123275970Scy remaining >= chain->off; 1124275970Scy chain = next) { 1125275970Scy next = chain->next; 1126275970Scy remaining -= chain->off; 1127275970Scy 1128275970Scy if (chain == *buf->last_with_datap) { 1129275970Scy buf->last_with_datap = &buf->first; 1130275970Scy } 1131275970Scy if (&chain->next == buf->last_with_datap) 1132275970Scy buf->last_with_datap = &buf->first; 1133275970Scy 1134275970Scy if (CHAIN_PINNED_R(chain)) { 1135275970Scy EVUTIL_ASSERT(remaining == 0); 1136275970Scy chain->misalign += chain->off; 1137275970Scy chain->off = 0; 1138275970Scy break; 1139275970Scy } else 1140275970Scy evbuffer_chain_free(chain); 1141275970Scy } 1142275970Scy 1143275970Scy buf->first = chain; 1144285612Sdelphij EVUTIL_ASSERT(chain && remaining <= chain->off); 1145275970Scy chain->misalign += remaining; 1146275970Scy chain->off -= remaining; 1147275970Scy } 1148275970Scy 1149275970Scy buf->n_del_for_cb += len; 1150275970Scy /* Tell someone about changes in this buffer */ 1151275970Scy evbuffer_invoke_callbacks_(buf); 1152275970Scy 1153275970Scydone: 1154275970Scy EVBUFFER_UNLOCK(buf); 1155275970Scy return result; 1156275970Scy} 1157275970Scy 1158275970Scy/* Reads data from an event buffer and drains the bytes read */ 1159275970Scyint 1160275970Scyevbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen) 1161275970Scy{ 1162275970Scy ev_ssize_t n; 1163275970Scy EVBUFFER_LOCK(buf); 1164275970Scy n = evbuffer_copyout_from(buf, NULL, data_out, datlen); 1165275970Scy if (n > 0) { 1166275970Scy if (evbuffer_drain(buf, n)<0) 1167275970Scy n = -1; 1168275970Scy } 1169275970Scy EVBUFFER_UNLOCK(buf); 1170275970Scy return (int)n; 1171275970Scy} 1172275970Scy 1173275970Scyev_ssize_t 1174275970Scyevbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen) 1175275970Scy{ 1176275970Scy return evbuffer_copyout_from(buf, NULL, data_out, datlen); 1177275970Scy} 1178275970Scy 1179275970Scyev_ssize_t 1180275970Scyevbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, 1181275970Scy void *data_out, size_t datlen) 1182275970Scy{ 1183275970Scy /*XXX fails badly on sendfile case. */ 1184275970Scy struct evbuffer_chain *chain; 1185275970Scy char *data = data_out; 1186275970Scy size_t nread; 1187275970Scy ev_ssize_t result = 0; 1188275970Scy size_t pos_in_chain; 1189275970Scy 1190275970Scy EVBUFFER_LOCK(buf); 1191275970Scy 1192275970Scy if (pos) { 1193285612Sdelphij if (datlen > (size_t)(EV_SSIZE_MAX - pos->pos)) { 1194285612Sdelphij result = -1; 1195285612Sdelphij goto done; 1196285612Sdelphij } 1197275970Scy chain = pos->internal_.chain; 1198275970Scy pos_in_chain = pos->internal_.pos_in_chain; 1199275970Scy if (datlen + pos->pos > buf->total_len) 1200275970Scy datlen = buf->total_len - pos->pos; 1201275970Scy } else { 1202275970Scy chain = buf->first; 1203275970Scy pos_in_chain = 0; 1204275970Scy if (datlen > buf->total_len) 1205275970Scy datlen = buf->total_len; 1206275970Scy } 1207275970Scy 1208275970Scy 1209275970Scy if (datlen == 0) 1210275970Scy goto done; 1211275970Scy 1212275970Scy if (buf->freeze_start) { 1213275970Scy result = -1; 1214275970Scy goto done; 1215275970Scy } 1216275970Scy 1217275970Scy nread = datlen; 1218275970Scy 1219275970Scy while (datlen && datlen >= chain->off - pos_in_chain) { 1220275970Scy size_t copylen = chain->off - pos_in_chain; 1221275970Scy memcpy(data, 1222275970Scy chain->buffer + chain->misalign + pos_in_chain, 1223275970Scy copylen); 1224275970Scy data += copylen; 1225275970Scy datlen -= copylen; 1226275970Scy 1227275970Scy chain = chain->next; 1228275970Scy pos_in_chain = 0; 1229275970Scy EVUTIL_ASSERT(chain || datlen==0); 1230275970Scy } 1231275970Scy 1232275970Scy if (datlen) { 1233275970Scy EVUTIL_ASSERT(chain); 1234285612Sdelphij EVUTIL_ASSERT(datlen+pos_in_chain <= chain->off); 1235285612Sdelphij 1236275970Scy memcpy(data, chain->buffer + chain->misalign + pos_in_chain, 1237275970Scy datlen); 1238275970Scy } 1239275970Scy 1240275970Scy result = nread; 1241275970Scydone: 1242275970Scy EVBUFFER_UNLOCK(buf); 1243275970Scy return result; 1244275970Scy} 1245275970Scy 1246275970Scy/* reads data from the src buffer to the dst buffer, avoids memcpy as 1247275970Scy * possible. */ 1248275970Scy/* XXXX should return ev_ssize_t */ 1249275970Scyint 1250275970Scyevbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, 1251275970Scy size_t datlen) 1252275970Scy{ 1253275970Scy /*XXX We should have an option to force this to be zero-copy.*/ 1254275970Scy 1255275970Scy /*XXX can fail badly on sendfile case. */ 1256275970Scy struct evbuffer_chain *chain, *previous; 1257275970Scy size_t nread = 0; 1258275970Scy int result; 1259275970Scy 1260275970Scy EVBUFFER_LOCK2(src, dst); 1261275970Scy 1262275970Scy chain = previous = src->first; 1263275970Scy 1264275970Scy if (datlen == 0 || dst == src) { 1265275970Scy result = 0; 1266275970Scy goto done; 1267275970Scy } 1268275970Scy 1269275970Scy if (dst->freeze_end || src->freeze_start) { 1270275970Scy result = -1; 1271275970Scy goto done; 1272275970Scy } 1273275970Scy 1274275970Scy /* short-cut if there is no more data buffered */ 1275275970Scy if (datlen >= src->total_len) { 1276275970Scy datlen = src->total_len; 1277275970Scy evbuffer_add_buffer(dst, src); 1278275970Scy result = (int)datlen; /*XXXX should return ev_ssize_t*/ 1279275970Scy goto done; 1280275970Scy } 1281275970Scy 1282275970Scy /* removes chains if possible */ 1283275970Scy while (chain->off <= datlen) { 1284275970Scy /* We can't remove the last with data from src unless we 1285275970Scy * remove all chains, in which case we would have done the if 1286275970Scy * block above */ 1287275970Scy EVUTIL_ASSERT(chain != *src->last_with_datap); 1288275970Scy nread += chain->off; 1289275970Scy datlen -= chain->off; 1290275970Scy previous = chain; 1291275970Scy if (src->last_with_datap == &chain->next) 1292275970Scy src->last_with_datap = &src->first; 1293275970Scy chain = chain->next; 1294275970Scy } 1295275970Scy 1296275970Scy if (nread) { 1297275970Scy /* we can remove the chain */ 1298275970Scy struct evbuffer_chain **chp; 1299275970Scy chp = evbuffer_free_trailing_empty_chains(dst); 1300275970Scy 1301275970Scy if (dst->first == NULL) { 1302275970Scy dst->first = src->first; 1303275970Scy } else { 1304275970Scy *chp = src->first; 1305275970Scy } 1306275970Scy dst->last = previous; 1307275970Scy previous->next = NULL; 1308275970Scy src->first = chain; 1309275970Scy advance_last_with_data(dst); 1310275970Scy 1311275970Scy dst->total_len += nread; 1312275970Scy dst->n_add_for_cb += nread; 1313275970Scy } 1314275970Scy 1315275970Scy /* we know that there is more data in the src buffer than 1316275970Scy * we want to read, so we manually drain the chain */ 1317275970Scy evbuffer_add(dst, chain->buffer + chain->misalign, datlen); 1318275970Scy chain->misalign += datlen; 1319275970Scy chain->off -= datlen; 1320275970Scy nread += datlen; 1321275970Scy 1322275970Scy /* You might think we would want to increment dst->n_add_for_cb 1323275970Scy * here too. But evbuffer_add above already took care of that. 1324275970Scy */ 1325275970Scy src->total_len -= nread; 1326275970Scy src->n_del_for_cb += nread; 1327275970Scy 1328275970Scy if (nread) { 1329275970Scy evbuffer_invoke_callbacks_(dst); 1330275970Scy evbuffer_invoke_callbacks_(src); 1331275970Scy } 1332275970Scy result = (int)nread;/*XXXX should change return type */ 1333275970Scy 1334275970Scydone: 1335275970Scy EVBUFFER_UNLOCK2(src, dst); 1336275970Scy return result; 1337275970Scy} 1338275970Scy 1339275970Scyunsigned char * 1340275970Scyevbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) 1341275970Scy{ 1342275970Scy struct evbuffer_chain *chain, *next, *tmp, *last_with_data; 1343275970Scy unsigned char *buffer, *result = NULL; 1344275970Scy ev_ssize_t remaining; 1345275970Scy int removed_last_with_data = 0; 1346275970Scy int removed_last_with_datap = 0; 1347275970Scy 1348275970Scy EVBUFFER_LOCK(buf); 1349275970Scy 1350275970Scy chain = buf->first; 1351275970Scy 1352275970Scy if (size < 0) 1353275970Scy size = buf->total_len; 1354275970Scy /* if size > buf->total_len, we cannot guarantee to the user that she 1355275970Scy * is going to have a long enough buffer afterwards; so we return 1356275970Scy * NULL */ 1357275970Scy if (size == 0 || (size_t)size > buf->total_len) 1358275970Scy goto done; 1359275970Scy 1360275970Scy /* No need to pull up anything; the first size bytes are 1361275970Scy * already here. */ 1362275970Scy if (chain->off >= (size_t)size) { 1363275970Scy result = chain->buffer + chain->misalign; 1364275970Scy goto done; 1365275970Scy } 1366275970Scy 1367275970Scy /* Make sure that none of the chains we need to copy from is pinned. */ 1368275970Scy remaining = size - chain->off; 1369275970Scy EVUTIL_ASSERT(remaining >= 0); 1370275970Scy for (tmp=chain->next; tmp; tmp=tmp->next) { 1371275970Scy if (CHAIN_PINNED(tmp)) 1372275970Scy goto done; 1373275970Scy if (tmp->off >= (size_t)remaining) 1374275970Scy break; 1375275970Scy remaining -= tmp->off; 1376275970Scy } 1377275970Scy 1378275970Scy if (CHAIN_PINNED(chain)) { 1379275970Scy size_t old_off = chain->off; 1380275970Scy if (CHAIN_SPACE_LEN(chain) < size - chain->off) { 1381275970Scy /* not enough room at end of chunk. */ 1382275970Scy goto done; 1383275970Scy } 1384275970Scy buffer = CHAIN_SPACE_PTR(chain); 1385275970Scy tmp = chain; 1386275970Scy tmp->off = size; 1387275970Scy size -= old_off; 1388275970Scy chain = chain->next; 1389275970Scy } else if (chain->buffer_len - chain->misalign >= (size_t)size) { 1390275970Scy /* already have enough space in the first chain */ 1391275970Scy size_t old_off = chain->off; 1392275970Scy buffer = chain->buffer + chain->misalign + chain->off; 1393275970Scy tmp = chain; 1394275970Scy tmp->off = size; 1395275970Scy size -= old_off; 1396275970Scy chain = chain->next; 1397275970Scy } else { 1398275970Scy if ((tmp = evbuffer_chain_new(size)) == NULL) { 1399275970Scy event_warn("%s: out of memory", __func__); 1400275970Scy goto done; 1401275970Scy } 1402275970Scy buffer = tmp->buffer; 1403275970Scy tmp->off = size; 1404275970Scy buf->first = tmp; 1405275970Scy } 1406275970Scy 1407275970Scy /* TODO(niels): deal with buffers that point to NULL like sendfile */ 1408275970Scy 1409275970Scy /* Copy and free every chunk that will be entirely pulled into tmp */ 1410275970Scy last_with_data = *buf->last_with_datap; 1411275970Scy for (; chain != NULL && (size_t)size >= chain->off; chain = next) { 1412275970Scy next = chain->next; 1413275970Scy 1414275970Scy memcpy(buffer, chain->buffer + chain->misalign, chain->off); 1415275970Scy size -= chain->off; 1416275970Scy buffer += chain->off; 1417275970Scy if (chain == last_with_data) 1418275970Scy removed_last_with_data = 1; 1419275970Scy if (&chain->next == buf->last_with_datap) 1420275970Scy removed_last_with_datap = 1; 1421275970Scy 1422275970Scy evbuffer_chain_free(chain); 1423275970Scy } 1424275970Scy 1425275970Scy if (chain != NULL) { 1426275970Scy memcpy(buffer, chain->buffer + chain->misalign, size); 1427275970Scy chain->misalign += size; 1428275970Scy chain->off -= size; 1429275970Scy } else { 1430275970Scy buf->last = tmp; 1431275970Scy } 1432275970Scy 1433275970Scy tmp->next = chain; 1434275970Scy 1435275970Scy if (removed_last_with_data) { 1436275970Scy buf->last_with_datap = &buf->first; 1437275970Scy } else if (removed_last_with_datap) { 1438275970Scy if (buf->first->next && buf->first->next->off) 1439275970Scy buf->last_with_datap = &buf->first->next; 1440275970Scy else 1441275970Scy buf->last_with_datap = &buf->first; 1442275970Scy } 1443275970Scy 1444275970Scy result = (tmp->buffer + tmp->misalign); 1445275970Scy 1446275970Scydone: 1447275970Scy EVBUFFER_UNLOCK(buf); 1448275970Scy return result; 1449275970Scy} 1450275970Scy 1451275970Scy/* 1452275970Scy * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. 1453275970Scy * The returned buffer needs to be freed by the called. 1454275970Scy */ 1455275970Scychar * 1456275970Scyevbuffer_readline(struct evbuffer *buffer) 1457275970Scy{ 1458275970Scy return evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY); 1459275970Scy} 1460275970Scy 1461275970Scystatic inline ev_ssize_t 1462275970Scyevbuffer_strchr(struct evbuffer_ptr *it, const char chr) 1463275970Scy{ 1464275970Scy struct evbuffer_chain *chain = it->internal_.chain; 1465275970Scy size_t i = it->internal_.pos_in_chain; 1466275970Scy while (chain != NULL) { 1467275970Scy char *buffer = (char *)chain->buffer + chain->misalign; 1468275970Scy char *cp = memchr(buffer+i, chr, chain->off-i); 1469275970Scy if (cp) { 1470275970Scy it->internal_.chain = chain; 1471275970Scy it->internal_.pos_in_chain = cp - buffer; 1472275970Scy it->pos += (cp - buffer - i); 1473275970Scy return it->pos; 1474275970Scy } 1475275970Scy it->pos += chain->off - i; 1476275970Scy i = 0; 1477275970Scy chain = chain->next; 1478275970Scy } 1479275970Scy 1480275970Scy return (-1); 1481275970Scy} 1482275970Scy 1483275970Scystatic inline char * 1484275970Scyfind_eol_char(char *s, size_t len) 1485275970Scy{ 1486275970Scy#define CHUNK_SZ 128 1487275970Scy /* Lots of benchmarking found this approach to be faster in practice 1488275970Scy * than doing two memchrs over the whole buffer, doin a memchr on each 1489275970Scy * char of the buffer, or trying to emulate memchr by hand. */ 1490275970Scy char *s_end, *cr, *lf; 1491275970Scy s_end = s+len; 1492275970Scy while (s < s_end) { 1493275970Scy size_t chunk = (s + CHUNK_SZ < s_end) ? CHUNK_SZ : (s_end - s); 1494275970Scy cr = memchr(s, '\r', chunk); 1495275970Scy lf = memchr(s, '\n', chunk); 1496275970Scy if (cr) { 1497275970Scy if (lf && lf < cr) 1498275970Scy return lf; 1499275970Scy return cr; 1500275970Scy } else if (lf) { 1501275970Scy return lf; 1502275970Scy } 1503275970Scy s += CHUNK_SZ; 1504275970Scy } 1505275970Scy 1506275970Scy return NULL; 1507275970Scy#undef CHUNK_SZ 1508275970Scy} 1509275970Scy 1510275970Scystatic ev_ssize_t 1511275970Scyevbuffer_find_eol_char(struct evbuffer_ptr *it) 1512275970Scy{ 1513275970Scy struct evbuffer_chain *chain = it->internal_.chain; 1514275970Scy size_t i = it->internal_.pos_in_chain; 1515275970Scy while (chain != NULL) { 1516275970Scy char *buffer = (char *)chain->buffer + chain->misalign; 1517275970Scy char *cp = find_eol_char(buffer+i, chain->off-i); 1518275970Scy if (cp) { 1519275970Scy it->internal_.chain = chain; 1520275970Scy it->internal_.pos_in_chain = cp - buffer; 1521275970Scy it->pos += (cp - buffer) - i; 1522275970Scy return it->pos; 1523275970Scy } 1524275970Scy it->pos += chain->off - i; 1525275970Scy i = 0; 1526275970Scy chain = chain->next; 1527275970Scy } 1528275970Scy 1529275970Scy return (-1); 1530275970Scy} 1531275970Scy 1532275970Scystatic inline int 1533275970Scyevbuffer_strspn( 1534275970Scy struct evbuffer_ptr *ptr, const char *chrset) 1535275970Scy{ 1536275970Scy int count = 0; 1537275970Scy struct evbuffer_chain *chain = ptr->internal_.chain; 1538275970Scy size_t i = ptr->internal_.pos_in_chain; 1539275970Scy 1540275970Scy if (!chain) 1541275970Scy return 0; 1542275970Scy 1543275970Scy while (1) { 1544275970Scy char *buffer = (char *)chain->buffer + chain->misalign; 1545275970Scy for (; i < chain->off; ++i) { 1546275970Scy const char *p = chrset; 1547275970Scy while (*p) { 1548275970Scy if (buffer[i] == *p++) 1549275970Scy goto next; 1550275970Scy } 1551275970Scy ptr->internal_.chain = chain; 1552275970Scy ptr->internal_.pos_in_chain = i; 1553275970Scy ptr->pos += count; 1554275970Scy return count; 1555275970Scy next: 1556275970Scy ++count; 1557275970Scy } 1558275970Scy i = 0; 1559275970Scy 1560275970Scy if (! chain->next) { 1561275970Scy ptr->internal_.chain = chain; 1562275970Scy ptr->internal_.pos_in_chain = i; 1563275970Scy ptr->pos += count; 1564275970Scy return count; 1565275970Scy } 1566275970Scy 1567275970Scy chain = chain->next; 1568275970Scy } 1569275970Scy} 1570275970Scy 1571275970Scy 1572275970Scystatic inline int 1573275970Scyevbuffer_getchr(struct evbuffer_ptr *it) 1574275970Scy{ 1575275970Scy struct evbuffer_chain *chain = it->internal_.chain; 1576275970Scy size_t off = it->internal_.pos_in_chain; 1577275970Scy 1578275970Scy if (chain == NULL) 1579275970Scy return -1; 1580275970Scy 1581275970Scy return (unsigned char)chain->buffer[chain->misalign + off]; 1582275970Scy} 1583275970Scy 1584275970Scystruct evbuffer_ptr 1585275970Scyevbuffer_search_eol(struct evbuffer *buffer, 1586275970Scy struct evbuffer_ptr *start, size_t *eol_len_out, 1587275970Scy enum evbuffer_eol_style eol_style) 1588275970Scy{ 1589275970Scy struct evbuffer_ptr it, it2; 1590275970Scy size_t extra_drain = 0; 1591275970Scy int ok = 0; 1592275970Scy 1593275970Scy /* Avoid locking in trivial edge cases */ 1594275970Scy if (start && start->internal_.chain == NULL) { 1595275970Scy PTR_NOT_FOUND(&it); 1596275970Scy if (eol_len_out) 1597275970Scy *eol_len_out = extra_drain; 1598275970Scy return it; 1599275970Scy } 1600275970Scy 1601275970Scy EVBUFFER_LOCK(buffer); 1602275970Scy 1603275970Scy if (start) { 1604275970Scy memcpy(&it, start, sizeof(it)); 1605275970Scy } else { 1606275970Scy it.pos = 0; 1607275970Scy it.internal_.chain = buffer->first; 1608275970Scy it.internal_.pos_in_chain = 0; 1609275970Scy } 1610275970Scy 1611275970Scy /* the eol_style determines our first stop character and how many 1612275970Scy * characters we are going to drain afterwards. */ 1613275970Scy switch (eol_style) { 1614275970Scy case EVBUFFER_EOL_ANY: 1615275970Scy if (evbuffer_find_eol_char(&it) < 0) 1616275970Scy goto done; 1617275970Scy memcpy(&it2, &it, sizeof(it)); 1618275970Scy extra_drain = evbuffer_strspn(&it2, "\r\n"); 1619275970Scy break; 1620275970Scy case EVBUFFER_EOL_CRLF_STRICT: { 1621275970Scy it = evbuffer_search(buffer, "\r\n", 2, &it); 1622275970Scy if (it.pos < 0) 1623275970Scy goto done; 1624275970Scy extra_drain = 2; 1625275970Scy break; 1626275970Scy } 1627275970Scy case EVBUFFER_EOL_CRLF: { 1628275970Scy ev_ssize_t start_pos = it.pos; 1629275970Scy /* Look for a LF ... */ 1630275970Scy if (evbuffer_strchr(&it, '\n') < 0) 1631275970Scy goto done; 1632275970Scy extra_drain = 1; 1633275970Scy /* ... optionally preceeded by a CR. */ 1634275970Scy if (it.pos == start_pos) 1635275970Scy break; /* If the first character is \n, don't back up */ 1636275970Scy /* This potentially does an extra linear walk over the first 1637275970Scy * few chains. Probably, that's not too expensive unless you 1638275970Scy * have a really pathological setup. */ 1639275970Scy memcpy(&it2, &it, sizeof(it)); 1640275970Scy if (evbuffer_ptr_subtract(buffer, &it2, 1)<0) 1641275970Scy break; 1642275970Scy if (evbuffer_getchr(&it2) == '\r') { 1643275970Scy memcpy(&it, &it2, sizeof(it)); 1644275970Scy extra_drain = 2; 1645275970Scy } 1646275970Scy break; 1647275970Scy } 1648275970Scy case EVBUFFER_EOL_LF: 1649275970Scy if (evbuffer_strchr(&it, '\n') < 0) 1650275970Scy goto done; 1651275970Scy extra_drain = 1; 1652275970Scy break; 1653275970Scy case EVBUFFER_EOL_NUL: 1654275970Scy if (evbuffer_strchr(&it, '\0') < 0) 1655275970Scy goto done; 1656275970Scy extra_drain = 1; 1657275970Scy break; 1658275970Scy default: 1659275970Scy goto done; 1660275970Scy } 1661275970Scy 1662275970Scy ok = 1; 1663275970Scydone: 1664275970Scy EVBUFFER_UNLOCK(buffer); 1665275970Scy 1666275970Scy if (!ok) 1667275970Scy PTR_NOT_FOUND(&it); 1668275970Scy if (eol_len_out) 1669275970Scy *eol_len_out = extra_drain; 1670275970Scy 1671275970Scy return it; 1672275970Scy} 1673275970Scy 1674275970Scychar * 1675275970Scyevbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, 1676275970Scy enum evbuffer_eol_style eol_style) 1677275970Scy{ 1678275970Scy struct evbuffer_ptr it; 1679275970Scy char *line; 1680275970Scy size_t n_to_copy=0, extra_drain=0; 1681275970Scy char *result = NULL; 1682275970Scy 1683275970Scy EVBUFFER_LOCK(buffer); 1684275970Scy 1685275970Scy if (buffer->freeze_start) { 1686275970Scy goto done; 1687275970Scy } 1688275970Scy 1689275970Scy it = evbuffer_search_eol(buffer, NULL, &extra_drain, eol_style); 1690275970Scy if (it.pos < 0) 1691275970Scy goto done; 1692275970Scy n_to_copy = it.pos; 1693275970Scy 1694275970Scy if ((line = mm_malloc(n_to_copy+1)) == NULL) { 1695275970Scy event_warn("%s: out of memory", __func__); 1696275970Scy goto done; 1697275970Scy } 1698275970Scy 1699275970Scy evbuffer_remove(buffer, line, n_to_copy); 1700275970Scy line[n_to_copy] = '\0'; 1701275970Scy 1702275970Scy evbuffer_drain(buffer, extra_drain); 1703275970Scy result = line; 1704275970Scydone: 1705275970Scy EVBUFFER_UNLOCK(buffer); 1706275970Scy 1707275970Scy if (n_read_out) 1708275970Scy *n_read_out = result ? n_to_copy : 0; 1709275970Scy 1710275970Scy return result; 1711275970Scy} 1712275970Scy 1713275970Scy#define EVBUFFER_CHAIN_MAX_AUTO_SIZE 4096 1714275970Scy 1715275970Scy/* Adds data to an event buffer */ 1716275970Scy 1717275970Scyint 1718275970Scyevbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen) 1719275970Scy{ 1720275970Scy struct evbuffer_chain *chain, *tmp; 1721275970Scy const unsigned char *data = data_in; 1722275970Scy size_t remain, to_alloc; 1723275970Scy int result = -1; 1724275970Scy 1725275970Scy EVBUFFER_LOCK(buf); 1726275970Scy 1727275970Scy if (buf->freeze_end) { 1728275970Scy goto done; 1729275970Scy } 1730285612Sdelphij /* Prevent buf->total_len overflow */ 1731285612Sdelphij if (datlen > EV_SIZE_MAX - buf->total_len) { 1732285612Sdelphij goto done; 1733285612Sdelphij } 1734275970Scy 1735275970Scy chain = buf->last; 1736275970Scy 1737275970Scy /* If there are no chains allocated for this buffer, allocate one 1738275970Scy * big enough to hold all the data. */ 1739275970Scy if (chain == NULL) { 1740275970Scy chain = evbuffer_chain_new(datlen); 1741275970Scy if (!chain) 1742275970Scy goto done; 1743275970Scy evbuffer_chain_insert(buf, chain); 1744275970Scy } 1745275970Scy 1746275970Scy if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) { 1747285612Sdelphij /* Always true for mutable buffers */ 1748285612Sdelphij EVUTIL_ASSERT(chain->misalign >= 0 && 1749285612Sdelphij (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX); 1750285612Sdelphij remain = chain->buffer_len - (size_t)chain->misalign - chain->off; 1751275970Scy if (remain >= datlen) { 1752275970Scy /* there's enough space to hold all the data in the 1753275970Scy * current last chain */ 1754275970Scy memcpy(chain->buffer + chain->misalign + chain->off, 1755275970Scy data, datlen); 1756275970Scy chain->off += datlen; 1757275970Scy buf->total_len += datlen; 1758275970Scy buf->n_add_for_cb += datlen; 1759275970Scy goto out; 1760275970Scy } else if (!CHAIN_PINNED(chain) && 1761275970Scy evbuffer_chain_should_realign(chain, datlen)) { 1762275970Scy /* we can fit the data into the misalignment */ 1763275970Scy evbuffer_chain_align(chain); 1764275970Scy 1765275970Scy memcpy(chain->buffer + chain->off, data, datlen); 1766275970Scy chain->off += datlen; 1767275970Scy buf->total_len += datlen; 1768275970Scy buf->n_add_for_cb += datlen; 1769275970Scy goto out; 1770275970Scy } 1771275970Scy } else { 1772275970Scy /* we cannot write any data to the last chain */ 1773275970Scy remain = 0; 1774275970Scy } 1775275970Scy 1776275970Scy /* we need to add another chain */ 1777275970Scy to_alloc = chain->buffer_len; 1778275970Scy if (to_alloc <= EVBUFFER_CHAIN_MAX_AUTO_SIZE/2) 1779275970Scy to_alloc <<= 1; 1780275970Scy if (datlen > to_alloc) 1781275970Scy to_alloc = datlen; 1782275970Scy tmp = evbuffer_chain_new(to_alloc); 1783275970Scy if (tmp == NULL) 1784275970Scy goto done; 1785275970Scy 1786275970Scy if (remain) { 1787275970Scy memcpy(chain->buffer + chain->misalign + chain->off, 1788275970Scy data, remain); 1789275970Scy chain->off += remain; 1790275970Scy buf->total_len += remain; 1791275970Scy buf->n_add_for_cb += remain; 1792275970Scy } 1793275970Scy 1794275970Scy data += remain; 1795275970Scy datlen -= remain; 1796275970Scy 1797275970Scy memcpy(tmp->buffer, data, datlen); 1798275970Scy tmp->off = datlen; 1799275970Scy evbuffer_chain_insert(buf, tmp); 1800275970Scy buf->n_add_for_cb += datlen; 1801275970Scy 1802275970Scyout: 1803275970Scy evbuffer_invoke_callbacks_(buf); 1804275970Scy result = 0; 1805275970Scydone: 1806275970Scy EVBUFFER_UNLOCK(buf); 1807275970Scy return result; 1808275970Scy} 1809275970Scy 1810275970Scyint 1811275970Scyevbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen) 1812275970Scy{ 1813275970Scy struct evbuffer_chain *chain, *tmp; 1814275970Scy int result = -1; 1815275970Scy 1816275970Scy EVBUFFER_LOCK(buf); 1817275970Scy 1818275970Scy if (buf->freeze_start) { 1819275970Scy goto done; 1820275970Scy } 1821285612Sdelphij if (datlen > EV_SIZE_MAX - buf->total_len) { 1822285612Sdelphij goto done; 1823285612Sdelphij } 1824275970Scy 1825275970Scy chain = buf->first; 1826275970Scy 1827275970Scy if (chain == NULL) { 1828275970Scy chain = evbuffer_chain_new(datlen); 1829275970Scy if (!chain) 1830275970Scy goto done; 1831275970Scy evbuffer_chain_insert(buf, chain); 1832275970Scy } 1833275970Scy 1834275970Scy /* we cannot touch immutable buffers */ 1835275970Scy if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) { 1836285612Sdelphij /* Always true for mutable buffers */ 1837285612Sdelphij EVUTIL_ASSERT(chain->misalign >= 0 && 1838285612Sdelphij (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX); 1839285612Sdelphij 1840275970Scy /* If this chain is empty, we can treat it as 1841275970Scy * 'empty at the beginning' rather than 'empty at the end' */ 1842275970Scy if (chain->off == 0) 1843275970Scy chain->misalign = chain->buffer_len; 1844275970Scy 1845275970Scy if ((size_t)chain->misalign >= datlen) { 1846275970Scy /* we have enough space to fit everything */ 1847275970Scy memcpy(chain->buffer + chain->misalign - datlen, 1848275970Scy data, datlen); 1849275970Scy chain->off += datlen; 1850275970Scy chain->misalign -= datlen; 1851275970Scy buf->total_len += datlen; 1852275970Scy buf->n_add_for_cb += datlen; 1853275970Scy goto out; 1854275970Scy } else if (chain->misalign) { 1855275970Scy /* we can only fit some of the data. */ 1856275970Scy memcpy(chain->buffer, 1857275970Scy (char*)data + datlen - chain->misalign, 1858275970Scy (size_t)chain->misalign); 1859275970Scy chain->off += (size_t)chain->misalign; 1860275970Scy buf->total_len += (size_t)chain->misalign; 1861275970Scy buf->n_add_for_cb += (size_t)chain->misalign; 1862275970Scy datlen -= (size_t)chain->misalign; 1863275970Scy chain->misalign = 0; 1864275970Scy } 1865275970Scy } 1866275970Scy 1867275970Scy /* we need to add another chain */ 1868275970Scy if ((tmp = evbuffer_chain_new(datlen)) == NULL) 1869275970Scy goto done; 1870275970Scy buf->first = tmp; 1871275970Scy if (buf->last_with_datap == &buf->first) 1872275970Scy buf->last_with_datap = &tmp->next; 1873275970Scy 1874275970Scy tmp->next = chain; 1875275970Scy 1876275970Scy tmp->off = datlen; 1877285612Sdelphij EVUTIL_ASSERT(datlen <= tmp->buffer_len); 1878275970Scy tmp->misalign = tmp->buffer_len - datlen; 1879275970Scy 1880275970Scy memcpy(tmp->buffer + tmp->misalign, data, datlen); 1881275970Scy buf->total_len += datlen; 1882275970Scy buf->n_add_for_cb += (size_t)chain->misalign; 1883275970Scy 1884275970Scyout: 1885275970Scy evbuffer_invoke_callbacks_(buf); 1886275970Scy result = 0; 1887275970Scydone: 1888275970Scy EVBUFFER_UNLOCK(buf); 1889275970Scy return result; 1890275970Scy} 1891275970Scy 1892275970Scy/** Helper: realigns the memory in chain->buffer so that misalign is 0. */ 1893275970Scystatic void 1894275970Scyevbuffer_chain_align(struct evbuffer_chain *chain) 1895275970Scy{ 1896275970Scy EVUTIL_ASSERT(!(chain->flags & EVBUFFER_IMMUTABLE)); 1897275970Scy EVUTIL_ASSERT(!(chain->flags & EVBUFFER_MEM_PINNED_ANY)); 1898275970Scy memmove(chain->buffer, chain->buffer + chain->misalign, chain->off); 1899275970Scy chain->misalign = 0; 1900275970Scy} 1901275970Scy 1902275970Scy#define MAX_TO_COPY_IN_EXPAND 4096 1903275970Scy#define MAX_TO_REALIGN_IN_EXPAND 2048 1904275970Scy 1905275970Scy/** Helper: return true iff we should realign chain to fit datalen bytes of 1906275970Scy data in it. */ 1907275970Scystatic int 1908275970Scyevbuffer_chain_should_realign(struct evbuffer_chain *chain, 1909275970Scy size_t datlen) 1910275970Scy{ 1911275970Scy return chain->buffer_len - chain->off >= datlen && 1912275970Scy (chain->off < chain->buffer_len / 2) && 1913275970Scy (chain->off <= MAX_TO_REALIGN_IN_EXPAND); 1914275970Scy} 1915275970Scy 1916275970Scy/* Expands the available space in the event buffer to at least datlen, all in 1917275970Scy * a single chunk. Return that chunk. */ 1918275970Scystatic struct evbuffer_chain * 1919275970Scyevbuffer_expand_singlechain(struct evbuffer *buf, size_t datlen) 1920275970Scy{ 1921275970Scy struct evbuffer_chain *chain, **chainp; 1922275970Scy struct evbuffer_chain *result = NULL; 1923275970Scy ASSERT_EVBUFFER_LOCKED(buf); 1924275970Scy 1925275970Scy chainp = buf->last_with_datap; 1926275970Scy 1927275970Scy /* XXX If *chainp is no longer writeable, but has enough space in its 1928275970Scy * misalign, this might be a bad idea: we could still use *chainp, not 1929275970Scy * (*chainp)->next. */ 1930275970Scy if (*chainp && CHAIN_SPACE_LEN(*chainp) == 0) 1931275970Scy chainp = &(*chainp)->next; 1932275970Scy 1933275970Scy /* 'chain' now points to the first chain with writable space (if any) 1934275970Scy * We will either use it, realign it, replace it, or resize it. */ 1935275970Scy chain = *chainp; 1936275970Scy 1937275970Scy if (chain == NULL || 1938275970Scy (chain->flags & (EVBUFFER_IMMUTABLE|EVBUFFER_MEM_PINNED_ANY))) { 1939275970Scy /* We can't use the last_with_data chain at all. Just add a 1940275970Scy * new one that's big enough. */ 1941275970Scy goto insert_new; 1942275970Scy } 1943275970Scy 1944275970Scy /* If we can fit all the data, then we don't have to do anything */ 1945275970Scy if (CHAIN_SPACE_LEN(chain) >= datlen) { 1946275970Scy result = chain; 1947275970Scy goto ok; 1948275970Scy } 1949275970Scy 1950275970Scy /* If the chain is completely empty, just replace it by adding a new 1951275970Scy * empty chain. */ 1952275970Scy if (chain->off == 0) { 1953275970Scy goto insert_new; 1954275970Scy } 1955275970Scy 1956275970Scy /* If the misalignment plus the remaining space fulfills our data 1957275970Scy * needs, we could just force an alignment to happen. Afterwards, we 1958275970Scy * have enough space. But only do this if we're saving a lot of space 1959275970Scy * and not moving too much data. Otherwise the space savings are 1960275970Scy * probably offset by the time lost in copying. 1961275970Scy */ 1962275970Scy if (evbuffer_chain_should_realign(chain, datlen)) { 1963275970Scy evbuffer_chain_align(chain); 1964275970Scy result = chain; 1965275970Scy goto ok; 1966275970Scy } 1967275970Scy 1968275970Scy /* At this point, we can either resize the last chunk with space in 1969275970Scy * it, use the next chunk after it, or If we add a new chunk, we waste 1970275970Scy * CHAIN_SPACE_LEN(chain) bytes in the former last chunk. If we 1971275970Scy * resize, we have to copy chain->off bytes. 1972275970Scy */ 1973275970Scy 1974275970Scy /* Would expanding this chunk be affordable and worthwhile? */ 1975275970Scy if (CHAIN_SPACE_LEN(chain) < chain->buffer_len / 8 || 1976285612Sdelphij chain->off > MAX_TO_COPY_IN_EXPAND || 1977285612Sdelphij (datlen < EVBUFFER_CHAIN_MAX && 1978285612Sdelphij EVBUFFER_CHAIN_MAX - datlen >= chain->off)) { 1979275970Scy /* It's not worth resizing this chain. Can the next one be 1980275970Scy * used? */ 1981275970Scy if (chain->next && CHAIN_SPACE_LEN(chain->next) >= datlen) { 1982275970Scy /* Yes, we can just use the next chain (which should 1983275970Scy * be empty. */ 1984275970Scy result = chain->next; 1985275970Scy goto ok; 1986275970Scy } else { 1987275970Scy /* No; append a new chain (which will free all 1988275970Scy * terminal empty chains.) */ 1989275970Scy goto insert_new; 1990275970Scy } 1991275970Scy } else { 1992275970Scy /* Okay, we're going to try to resize this chain: Not doing so 1993275970Scy * would waste at least 1/8 of its current allocation, and we 1994275970Scy * can do so without having to copy more than 1995275970Scy * MAX_TO_COPY_IN_EXPAND bytes. */ 1996275970Scy /* figure out how much space we need */ 1997275970Scy size_t length = chain->off + datlen; 1998275970Scy struct evbuffer_chain *tmp = evbuffer_chain_new(length); 1999275970Scy if (tmp == NULL) 2000275970Scy goto err; 2001275970Scy 2002275970Scy /* copy the data over that we had so far */ 2003275970Scy tmp->off = chain->off; 2004275970Scy memcpy(tmp->buffer, chain->buffer + chain->misalign, 2005275970Scy chain->off); 2006275970Scy /* fix up the list */ 2007275970Scy EVUTIL_ASSERT(*chainp == chain); 2008275970Scy result = *chainp = tmp; 2009275970Scy 2010275970Scy if (buf->last == chain) 2011275970Scy buf->last = tmp; 2012275970Scy 2013275970Scy tmp->next = chain->next; 2014275970Scy evbuffer_chain_free(chain); 2015275970Scy goto ok; 2016275970Scy } 2017275970Scy 2018275970Scyinsert_new: 2019275970Scy result = evbuffer_chain_insert_new(buf, datlen); 2020275970Scy if (!result) 2021275970Scy goto err; 2022275970Scyok: 2023275970Scy EVUTIL_ASSERT(result); 2024275970Scy EVUTIL_ASSERT(CHAIN_SPACE_LEN(result) >= datlen); 2025275970Scyerr: 2026275970Scy return result; 2027275970Scy} 2028275970Scy 2029275970Scy/* Make sure that datlen bytes are available for writing in the last n 2030275970Scy * chains. Never copies or moves data. */ 2031275970Scyint 2032275970Scyevbuffer_expand_fast_(struct evbuffer *buf, size_t datlen, int n) 2033275970Scy{ 2034275970Scy struct evbuffer_chain *chain = buf->last, *tmp, *next; 2035275970Scy size_t avail; 2036275970Scy int used; 2037275970Scy 2038275970Scy ASSERT_EVBUFFER_LOCKED(buf); 2039275970Scy EVUTIL_ASSERT(n >= 2); 2040275970Scy 2041275970Scy if (chain == NULL || (chain->flags & EVBUFFER_IMMUTABLE)) { 2042275970Scy /* There is no last chunk, or we can't touch the last chunk. 2043275970Scy * Just add a new chunk. */ 2044275970Scy chain = evbuffer_chain_new(datlen); 2045275970Scy if (chain == NULL) 2046275970Scy return (-1); 2047275970Scy 2048275970Scy evbuffer_chain_insert(buf, chain); 2049275970Scy return (0); 2050275970Scy } 2051275970Scy 2052275970Scy used = 0; /* number of chains we're using space in. */ 2053275970Scy avail = 0; /* how much space they have. */ 2054275970Scy /* How many bytes can we stick at the end of buffer as it is? Iterate 2055275970Scy * over the chains at the end of the buffer, tring to see how much 2056275970Scy * space we have in the first n. */ 2057275970Scy for (chain = *buf->last_with_datap; chain; chain = chain->next) { 2058275970Scy if (chain->off) { 2059275970Scy size_t space = (size_t) CHAIN_SPACE_LEN(chain); 2060275970Scy EVUTIL_ASSERT(chain == *buf->last_with_datap); 2061275970Scy if (space) { 2062275970Scy avail += space; 2063275970Scy ++used; 2064275970Scy } 2065275970Scy } else { 2066275970Scy /* No data in chain; realign it. */ 2067275970Scy chain->misalign = 0; 2068275970Scy avail += chain->buffer_len; 2069275970Scy ++used; 2070275970Scy } 2071275970Scy if (avail >= datlen) { 2072275970Scy /* There is already enough space. Just return */ 2073275970Scy return (0); 2074275970Scy } 2075275970Scy if (used == n) 2076275970Scy break; 2077275970Scy } 2078275970Scy 2079275970Scy /* There wasn't enough space in the first n chains with space in 2080275970Scy * them. Either add a new chain with enough space, or replace all 2081275970Scy * empty chains with one that has enough space, depending on n. */ 2082275970Scy if (used < n) { 2083275970Scy /* The loop ran off the end of the chains before it hit n 2084275970Scy * chains; we can add another. */ 2085275970Scy EVUTIL_ASSERT(chain == NULL); 2086275970Scy 2087275970Scy tmp = evbuffer_chain_new(datlen - avail); 2088275970Scy if (tmp == NULL) 2089275970Scy return (-1); 2090275970Scy 2091275970Scy buf->last->next = tmp; 2092275970Scy buf->last = tmp; 2093275970Scy /* (we would only set last_with_data if we added the first 2094275970Scy * chain. But if the buffer had no chains, we would have 2095275970Scy * just allocated a new chain earlier) */ 2096275970Scy return (0); 2097275970Scy } else { 2098275970Scy /* Nuke _all_ the empty chains. */ 2099275970Scy int rmv_all = 0; /* True iff we removed last_with_data. */ 2100275970Scy chain = *buf->last_with_datap; 2101275970Scy if (!chain->off) { 2102275970Scy EVUTIL_ASSERT(chain == buf->first); 2103275970Scy rmv_all = 1; 2104275970Scy avail = 0; 2105275970Scy } else { 2106285612Sdelphij /* can't overflow, since only mutable chains have 2107285612Sdelphij * huge misaligns. */ 2108275970Scy avail = (size_t) CHAIN_SPACE_LEN(chain); 2109275970Scy chain = chain->next; 2110275970Scy } 2111275970Scy 2112275970Scy 2113275970Scy for (; chain; chain = next) { 2114275970Scy next = chain->next; 2115275970Scy EVUTIL_ASSERT(chain->off == 0); 2116275970Scy evbuffer_chain_free(chain); 2117275970Scy } 2118285612Sdelphij EVUTIL_ASSERT(datlen >= avail); 2119275970Scy tmp = evbuffer_chain_new(datlen - avail); 2120275970Scy if (tmp == NULL) { 2121275970Scy if (rmv_all) { 2122275970Scy ZERO_CHAIN(buf); 2123275970Scy } else { 2124275970Scy buf->last = *buf->last_with_datap; 2125275970Scy (*buf->last_with_datap)->next = NULL; 2126275970Scy } 2127275970Scy return (-1); 2128275970Scy } 2129275970Scy 2130275970Scy if (rmv_all) { 2131275970Scy buf->first = buf->last = tmp; 2132275970Scy buf->last_with_datap = &buf->first; 2133275970Scy } else { 2134275970Scy (*buf->last_with_datap)->next = tmp; 2135275970Scy buf->last = tmp; 2136275970Scy } 2137275970Scy return (0); 2138275970Scy } 2139275970Scy} 2140275970Scy 2141275970Scyint 2142275970Scyevbuffer_expand(struct evbuffer *buf, size_t datlen) 2143275970Scy{ 2144275970Scy struct evbuffer_chain *chain; 2145275970Scy 2146275970Scy EVBUFFER_LOCK(buf); 2147275970Scy chain = evbuffer_expand_singlechain(buf, datlen); 2148275970Scy EVBUFFER_UNLOCK(buf); 2149275970Scy return chain ? 0 : -1; 2150275970Scy} 2151275970Scy 2152275970Scy/* 2153275970Scy * Reads data from a file descriptor into a buffer. 2154275970Scy */ 2155275970Scy 2156275970Scy#if defined(EVENT__HAVE_SYS_UIO_H) || defined(_WIN32) 2157275970Scy#define USE_IOVEC_IMPL 2158275970Scy#endif 2159275970Scy 2160275970Scy#ifdef USE_IOVEC_IMPL 2161275970Scy 2162275970Scy#ifdef EVENT__HAVE_SYS_UIO_H 2163275970Scy/* number of iovec we use for writev, fragmentation is going to determine 2164275970Scy * how much we end up writing */ 2165275970Scy 2166275970Scy#define DEFAULT_WRITE_IOVEC 128 2167275970Scy 2168275970Scy#if defined(UIO_MAXIOV) && UIO_MAXIOV < DEFAULT_WRITE_IOVEC 2169275970Scy#define NUM_WRITE_IOVEC UIO_MAXIOV 2170275970Scy#elif defined(IOV_MAX) && IOV_MAX < DEFAULT_WRITE_IOVEC 2171275970Scy#define NUM_WRITE_IOVEC IOV_MAX 2172275970Scy#else 2173275970Scy#define NUM_WRITE_IOVEC DEFAULT_WRITE_IOVEC 2174275970Scy#endif 2175275970Scy 2176275970Scy#define IOV_TYPE struct iovec 2177275970Scy#define IOV_PTR_FIELD iov_base 2178275970Scy#define IOV_LEN_FIELD iov_len 2179275970Scy#define IOV_LEN_TYPE size_t 2180275970Scy#else 2181275970Scy#define NUM_WRITE_IOVEC 16 2182275970Scy#define IOV_TYPE WSABUF 2183275970Scy#define IOV_PTR_FIELD buf 2184275970Scy#define IOV_LEN_FIELD len 2185275970Scy#define IOV_LEN_TYPE unsigned long 2186275970Scy#endif 2187275970Scy#endif 2188275970Scy#define NUM_READ_IOVEC 4 2189275970Scy 2190275970Scy#define EVBUFFER_MAX_READ 4096 2191275970Scy 2192275970Scy/** Helper function to figure out which space to use for reading data into 2193275970Scy an evbuffer. Internal use only. 2194275970Scy 2195275970Scy @param buf The buffer to read into 2196275970Scy @param howmuch How much we want to read. 2197275970Scy @param vecs An array of two or more iovecs or WSABUFs. 2198275970Scy @param n_vecs_avail The length of vecs 2199275970Scy @param chainp A pointer to a variable to hold the first chain we're 2200275970Scy reading into. 2201275970Scy @param exact Boolean: if true, we do not provide more than 'howmuch' 2202275970Scy space in the vectors, even if more space is available. 2203275970Scy @return The number of buffers we're using. 2204275970Scy */ 2205275970Scyint 2206275970Scyevbuffer_read_setup_vecs_(struct evbuffer *buf, ev_ssize_t howmuch, 2207275970Scy struct evbuffer_iovec *vecs, int n_vecs_avail, 2208275970Scy struct evbuffer_chain ***chainp, int exact) 2209275970Scy{ 2210275970Scy struct evbuffer_chain *chain; 2211275970Scy struct evbuffer_chain **firstchainp; 2212275970Scy size_t so_far; 2213275970Scy int i; 2214275970Scy ASSERT_EVBUFFER_LOCKED(buf); 2215275970Scy 2216275970Scy if (howmuch < 0) 2217275970Scy return -1; 2218275970Scy 2219275970Scy so_far = 0; 2220275970Scy /* Let firstchain be the first chain with any space on it */ 2221275970Scy firstchainp = buf->last_with_datap; 2222275970Scy if (CHAIN_SPACE_LEN(*firstchainp) == 0) { 2223275970Scy firstchainp = &(*firstchainp)->next; 2224275970Scy } 2225275970Scy 2226275970Scy chain = *firstchainp; 2227275970Scy for (i = 0; i < n_vecs_avail && so_far < (size_t)howmuch; ++i) { 2228275970Scy size_t avail = (size_t) CHAIN_SPACE_LEN(chain); 2229275970Scy if (avail > (howmuch - so_far) && exact) 2230275970Scy avail = howmuch - so_far; 2231275970Scy vecs[i].iov_base = CHAIN_SPACE_PTR(chain); 2232275970Scy vecs[i].iov_len = avail; 2233275970Scy so_far += avail; 2234275970Scy chain = chain->next; 2235275970Scy } 2236275970Scy 2237275970Scy *chainp = firstchainp; 2238275970Scy return i; 2239275970Scy} 2240275970Scy 2241275970Scystatic int 2242275970Scyget_n_bytes_readable_on_socket(evutil_socket_t fd) 2243275970Scy{ 2244275970Scy#if defined(FIONREAD) && defined(_WIN32) 2245275970Scy unsigned long lng = EVBUFFER_MAX_READ; 2246275970Scy if (ioctlsocket(fd, FIONREAD, &lng) < 0) 2247275970Scy return -1; 2248285612Sdelphij /* Can overflow, but mostly harmlessly. XXXX */ 2249275970Scy return (int)lng; 2250275970Scy#elif defined(FIONREAD) 2251275970Scy int n = EVBUFFER_MAX_READ; 2252275970Scy if (ioctl(fd, FIONREAD, &n) < 0) 2253275970Scy return -1; 2254275970Scy return n; 2255275970Scy#else 2256275970Scy return EVBUFFER_MAX_READ; 2257275970Scy#endif 2258275970Scy} 2259275970Scy 2260275970Scy/* TODO(niels): should this function return ev_ssize_t and take ev_ssize_t 2261275970Scy * as howmuch? */ 2262275970Scyint 2263275970Scyevbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) 2264275970Scy{ 2265275970Scy struct evbuffer_chain **chainp; 2266275970Scy int n; 2267275970Scy int result; 2268275970Scy 2269275970Scy#ifdef USE_IOVEC_IMPL 2270275970Scy int nvecs, i, remaining; 2271275970Scy#else 2272275970Scy struct evbuffer_chain *chain; 2273275970Scy unsigned char *p; 2274275970Scy#endif 2275275970Scy 2276275970Scy EVBUFFER_LOCK(buf); 2277275970Scy 2278275970Scy if (buf->freeze_end) { 2279275970Scy result = -1; 2280275970Scy goto done; 2281275970Scy } 2282275970Scy 2283275970Scy n = get_n_bytes_readable_on_socket(fd); 2284275970Scy if (n <= 0 || n > EVBUFFER_MAX_READ) 2285275970Scy n = EVBUFFER_MAX_READ; 2286275970Scy if (howmuch < 0 || howmuch > n) 2287275970Scy howmuch = n; 2288275970Scy 2289275970Scy#ifdef USE_IOVEC_IMPL 2290275970Scy /* Since we can use iovecs, we're willing to use the last 2291275970Scy * NUM_READ_IOVEC chains. */ 2292275970Scy if (evbuffer_expand_fast_(buf, howmuch, NUM_READ_IOVEC) == -1) { 2293275970Scy result = -1; 2294275970Scy goto done; 2295275970Scy } else { 2296275970Scy IOV_TYPE vecs[NUM_READ_IOVEC]; 2297275970Scy#ifdef EVBUFFER_IOVEC_IS_NATIVE_ 2298275970Scy nvecs = evbuffer_read_setup_vecs_(buf, howmuch, vecs, 2299275970Scy NUM_READ_IOVEC, &chainp, 1); 2300275970Scy#else 2301275970Scy /* We aren't using the native struct iovec. Therefore, 2302275970Scy we are on win32. */ 2303275970Scy struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC]; 2304275970Scy nvecs = evbuffer_read_setup_vecs_(buf, howmuch, ev_vecs, 2, 2305275970Scy &chainp, 1); 2306275970Scy 2307275970Scy for (i=0; i < nvecs; ++i) 2308275970Scy WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]); 2309275970Scy#endif 2310275970Scy 2311275970Scy#ifdef _WIN32 2312275970Scy { 2313275970Scy DWORD bytesRead; 2314275970Scy DWORD flags=0; 2315275970Scy if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) { 2316275970Scy /* The read failed. It might be a close, 2317275970Scy * or it might be an error. */ 2318275970Scy if (WSAGetLastError() == WSAECONNABORTED) 2319275970Scy n = 0; 2320275970Scy else 2321275970Scy n = -1; 2322275970Scy } else 2323275970Scy n = bytesRead; 2324275970Scy } 2325275970Scy#else 2326275970Scy n = readv(fd, vecs, nvecs); 2327275970Scy#endif 2328275970Scy } 2329275970Scy 2330275970Scy#else /*!USE_IOVEC_IMPL*/ 2331275970Scy /* If we don't have FIONREAD, we might waste some space here */ 2332275970Scy /* XXX we _will_ waste some space here if there is any space left 2333275970Scy * over on buf->last. */ 2334275970Scy if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) { 2335275970Scy result = -1; 2336275970Scy goto done; 2337275970Scy } 2338275970Scy 2339275970Scy /* We can append new data at this point */ 2340275970Scy p = chain->buffer + chain->misalign + chain->off; 2341275970Scy 2342275970Scy#ifndef _WIN32 2343275970Scy n = read(fd, p, howmuch); 2344275970Scy#else 2345275970Scy n = recv(fd, p, howmuch, 0); 2346275970Scy#endif 2347275970Scy#endif /* USE_IOVEC_IMPL */ 2348275970Scy 2349275970Scy if (n == -1) { 2350275970Scy result = -1; 2351275970Scy goto done; 2352275970Scy } 2353275970Scy if (n == 0) { 2354275970Scy result = 0; 2355275970Scy goto done; 2356275970Scy } 2357275970Scy 2358275970Scy#ifdef USE_IOVEC_IMPL 2359275970Scy remaining = n; 2360275970Scy for (i=0; i < nvecs; ++i) { 2361285612Sdelphij /* can't overflow, since only mutable chains have 2362285612Sdelphij * huge misaligns. */ 2363285612Sdelphij size_t space = (size_t) CHAIN_SPACE_LEN(*chainp); 2364285612Sdelphij /* XXXX This is a kludge that can waste space in perverse 2365285612Sdelphij * situations. */ 2366285612Sdelphij if (space > EVBUFFER_CHAIN_MAX) 2367285612Sdelphij space = EVBUFFER_CHAIN_MAX; 2368285612Sdelphij if ((ev_ssize_t)space < remaining) { 2369275970Scy (*chainp)->off += space; 2370275970Scy remaining -= (int)space; 2371275970Scy } else { 2372275970Scy (*chainp)->off += remaining; 2373275970Scy buf->last_with_datap = chainp; 2374275970Scy break; 2375275970Scy } 2376275970Scy chainp = &(*chainp)->next; 2377275970Scy } 2378275970Scy#else 2379275970Scy chain->off += n; 2380275970Scy advance_last_with_data(buf); 2381275970Scy#endif 2382275970Scy buf->total_len += n; 2383275970Scy buf->n_add_for_cb += n; 2384275970Scy 2385275970Scy /* Tell someone about changes in this buffer */ 2386275970Scy evbuffer_invoke_callbacks_(buf); 2387275970Scy result = n; 2388275970Scydone: 2389275970Scy EVBUFFER_UNLOCK(buf); 2390275970Scy return result; 2391275970Scy} 2392275970Scy 2393275970Scy#ifdef USE_IOVEC_IMPL 2394275970Scystatic inline int 2395275970Scyevbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd, 2396275970Scy ev_ssize_t howmuch) 2397275970Scy{ 2398275970Scy IOV_TYPE iov[NUM_WRITE_IOVEC]; 2399275970Scy struct evbuffer_chain *chain = buffer->first; 2400275970Scy int n, i = 0; 2401275970Scy 2402275970Scy if (howmuch < 0) 2403275970Scy return -1; 2404275970Scy 2405275970Scy ASSERT_EVBUFFER_LOCKED(buffer); 2406275970Scy /* XXX make this top out at some maximal data length? if the 2407275970Scy * buffer has (say) 1MB in it, split over 128 chains, there's 2408275970Scy * no way it all gets written in one go. */ 2409275970Scy while (chain != NULL && i < NUM_WRITE_IOVEC && howmuch) { 2410275970Scy#ifdef USE_SENDFILE 2411275970Scy /* we cannot write the file info via writev */ 2412275970Scy if (chain->flags & EVBUFFER_SENDFILE) 2413275970Scy break; 2414275970Scy#endif 2415275970Scy iov[i].IOV_PTR_FIELD = (void *) (chain->buffer + chain->misalign); 2416275970Scy if ((size_t)howmuch >= chain->off) { 2417275970Scy /* XXXcould be problematic when windows supports mmap*/ 2418275970Scy iov[i++].IOV_LEN_FIELD = (IOV_LEN_TYPE)chain->off; 2419275970Scy howmuch -= chain->off; 2420275970Scy } else { 2421275970Scy /* XXXcould be problematic when windows supports mmap*/ 2422275970Scy iov[i++].IOV_LEN_FIELD = (IOV_LEN_TYPE)howmuch; 2423275970Scy break; 2424275970Scy } 2425275970Scy chain = chain->next; 2426275970Scy } 2427275970Scy if (! i) 2428275970Scy return 0; 2429275970Scy 2430275970Scy#ifdef _WIN32 2431275970Scy { 2432275970Scy DWORD bytesSent; 2433275970Scy if (WSASend(fd, iov, i, &bytesSent, 0, NULL, NULL)) 2434275970Scy n = -1; 2435275970Scy else 2436275970Scy n = bytesSent; 2437275970Scy } 2438275970Scy#else 2439275970Scy n = writev(fd, iov, i); 2440275970Scy#endif 2441275970Scy return (n); 2442275970Scy} 2443275970Scy#endif 2444275970Scy 2445275970Scy#ifdef USE_SENDFILE 2446275970Scystatic inline int 2447275970Scyevbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t dest_fd, 2448275970Scy ev_ssize_t howmuch) 2449275970Scy{ 2450275970Scy struct evbuffer_chain *chain = buffer->first; 2451275970Scy struct evbuffer_chain_file_segment *info = 2452275970Scy EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, 2453275970Scy chain); 2454275970Scy const int source_fd = info->segment->fd; 2455275970Scy#if defined(SENDFILE_IS_MACOSX) || defined(SENDFILE_IS_FREEBSD) 2456275970Scy int res; 2457275970Scy ev_off_t len = chain->off; 2458275970Scy#elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS) 2459275970Scy ev_ssize_t res; 2460275970Scy ev_off_t offset = chain->misalign; 2461275970Scy#endif 2462275970Scy 2463275970Scy ASSERT_EVBUFFER_LOCKED(buffer); 2464275970Scy 2465275970Scy#if defined(SENDFILE_IS_MACOSX) 2466275970Scy res = sendfile(source_fd, dest_fd, chain->misalign, &len, NULL, 0); 2467275970Scy if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno)) 2468275970Scy return (-1); 2469275970Scy 2470275970Scy return (len); 2471275970Scy#elif defined(SENDFILE_IS_FREEBSD) 2472275970Scy res = sendfile(source_fd, dest_fd, chain->misalign, chain->off, NULL, &len, 0); 2473275970Scy if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno)) 2474275970Scy return (-1); 2475275970Scy 2476275970Scy return (len); 2477275970Scy#elif defined(SENDFILE_IS_LINUX) 2478275970Scy /* TODO(niels): implement splice */ 2479275970Scy res = sendfile(dest_fd, source_fd, &offset, chain->off); 2480275970Scy if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) { 2481275970Scy /* if this is EAGAIN or EINTR return 0; otherwise, -1 */ 2482275970Scy return (0); 2483275970Scy } 2484275970Scy return (res); 2485275970Scy#elif defined(SENDFILE_IS_SOLARIS) 2486275970Scy { 2487275970Scy const off_t offset_orig = offset; 2488275970Scy res = sendfile(dest_fd, source_fd, &offset, chain->off); 2489275970Scy if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) { 2490275970Scy if (offset - offset_orig) 2491275970Scy return offset - offset_orig; 2492275970Scy /* if this is EAGAIN or EINTR and no bytes were 2493275970Scy * written, return 0 */ 2494275970Scy return (0); 2495275970Scy } 2496275970Scy return (res); 2497275970Scy } 2498275970Scy#endif 2499275970Scy} 2500275970Scy#endif 2501275970Scy 2502275970Scyint 2503275970Scyevbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, 2504275970Scy ev_ssize_t howmuch) 2505275970Scy{ 2506275970Scy int n = -1; 2507275970Scy 2508275970Scy EVBUFFER_LOCK(buffer); 2509275970Scy 2510275970Scy if (buffer->freeze_start) { 2511275970Scy goto done; 2512275970Scy } 2513275970Scy 2514275970Scy if (howmuch < 0 || (size_t)howmuch > buffer->total_len) 2515275970Scy howmuch = buffer->total_len; 2516275970Scy 2517275970Scy if (howmuch > 0) { 2518275970Scy#ifdef USE_SENDFILE 2519275970Scy struct evbuffer_chain *chain = buffer->first; 2520275970Scy if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE)) 2521275970Scy n = evbuffer_write_sendfile(buffer, fd, howmuch); 2522275970Scy else { 2523275970Scy#endif 2524275970Scy#ifdef USE_IOVEC_IMPL 2525275970Scy n = evbuffer_write_iovec(buffer, fd, howmuch); 2526275970Scy#elif defined(_WIN32) 2527275970Scy /* XXX(nickm) Don't disable this code until we know if 2528275970Scy * the WSARecv code above works. */ 2529275970Scy void *p = evbuffer_pullup(buffer, howmuch); 2530285612Sdelphij EVUTIL_ASSERT(p || !howmuch); 2531275970Scy n = send(fd, p, howmuch, 0); 2532275970Scy#else 2533275970Scy void *p = evbuffer_pullup(buffer, howmuch); 2534285612Sdelphij EVUTIL_ASSERT(p || !howmuch); 2535275970Scy n = write(fd, p, howmuch); 2536275970Scy#endif 2537275970Scy#ifdef USE_SENDFILE 2538275970Scy } 2539275970Scy#endif 2540275970Scy } 2541275970Scy 2542275970Scy if (n > 0) 2543275970Scy evbuffer_drain(buffer, n); 2544275970Scy 2545275970Scydone: 2546275970Scy EVBUFFER_UNLOCK(buffer); 2547275970Scy return (n); 2548275970Scy} 2549275970Scy 2550275970Scyint 2551275970Scyevbuffer_write(struct evbuffer *buffer, evutil_socket_t fd) 2552275970Scy{ 2553275970Scy return evbuffer_write_atmost(buffer, fd, -1); 2554275970Scy} 2555275970Scy 2556275970Scyunsigned char * 2557275970Scyevbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len) 2558275970Scy{ 2559275970Scy unsigned char *search; 2560275970Scy struct evbuffer_ptr ptr; 2561275970Scy 2562275970Scy EVBUFFER_LOCK(buffer); 2563275970Scy 2564275970Scy ptr = evbuffer_search(buffer, (const char *)what, len, NULL); 2565275970Scy if (ptr.pos < 0) { 2566275970Scy search = NULL; 2567275970Scy } else { 2568275970Scy search = evbuffer_pullup(buffer, ptr.pos + len); 2569275970Scy if (search) 2570275970Scy search += ptr.pos; 2571275970Scy } 2572275970Scy EVBUFFER_UNLOCK(buffer); 2573275970Scy return search; 2574275970Scy} 2575275970Scy 2576275970Scy/* Subract <b>howfar</b> from the position of <b>pos</b> within 2577275970Scy * <b>buf</b>. Returns 0 on success, -1 on failure. 2578275970Scy * 2579275970Scy * This isn't exposed yet, because of potential inefficiency issues. 2580275970Scy * Maybe it should be. */ 2581275970Scystatic int 2582275970Scyevbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos, 2583275970Scy size_t howfar) 2584275970Scy{ 2585285612Sdelphij if (pos->pos < 0) 2586285612Sdelphij return -1; 2587275970Scy if (howfar > (size_t)pos->pos) 2588275970Scy return -1; 2589275970Scy if (pos->internal_.chain && howfar <= pos->internal_.pos_in_chain) { 2590275970Scy pos->internal_.pos_in_chain -= howfar; 2591275970Scy pos->pos -= howfar; 2592275970Scy return 0; 2593275970Scy } else { 2594275970Scy const size_t newpos = pos->pos - howfar; 2595275970Scy /* Here's the inefficient part: it walks over the 2596275970Scy * chains until we hit newpos. */ 2597275970Scy return evbuffer_ptr_set(buf, pos, newpos, EVBUFFER_PTR_SET); 2598275970Scy } 2599275970Scy} 2600275970Scy 2601275970Scyint 2602275970Scyevbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos, 2603275970Scy size_t position, enum evbuffer_ptr_how how) 2604275970Scy{ 2605275970Scy size_t left = position; 2606275970Scy struct evbuffer_chain *chain = NULL; 2607275970Scy int result = 0; 2608275970Scy 2609275970Scy EVBUFFER_LOCK(buf); 2610275970Scy 2611275970Scy switch (how) { 2612275970Scy case EVBUFFER_PTR_SET: 2613275970Scy chain = buf->first; 2614275970Scy pos->pos = position; 2615275970Scy position = 0; 2616275970Scy break; 2617275970Scy case EVBUFFER_PTR_ADD: 2618275970Scy /* this avoids iterating over all previous chains if 2619275970Scy we just want to advance the position */ 2620285612Sdelphij if (pos->pos < 0 || EV_SIZE_MAX - position < (size_t)pos->pos) { 2621285612Sdelphij EVBUFFER_UNLOCK(buf); 2622285612Sdelphij return -1; 2623285612Sdelphij } 2624275970Scy chain = pos->internal_.chain; 2625275970Scy pos->pos += position; 2626275970Scy position = pos->internal_.pos_in_chain; 2627275970Scy break; 2628275970Scy } 2629275970Scy 2630285612Sdelphij EVUTIL_ASSERT(EV_SIZE_MAX - left >= position); 2631275970Scy while (chain && position + left >= chain->off) { 2632275970Scy left -= chain->off - position; 2633275970Scy chain = chain->next; 2634275970Scy position = 0; 2635275970Scy } 2636275970Scy if (chain) { 2637275970Scy pos->internal_.chain = chain; 2638275970Scy pos->internal_.pos_in_chain = position + left; 2639275970Scy } else if (left == 0) { 2640275970Scy /* The first byte in the (nonexistent) chain after the last chain */ 2641275970Scy pos->internal_.chain = NULL; 2642275970Scy pos->internal_.pos_in_chain = 0; 2643275970Scy } else { 2644275970Scy PTR_NOT_FOUND(pos); 2645275970Scy result = -1; 2646275970Scy } 2647275970Scy 2648275970Scy EVBUFFER_UNLOCK(buf); 2649275970Scy 2650275970Scy return result; 2651275970Scy} 2652275970Scy 2653275970Scy/** 2654275970Scy Compare the bytes in buf at position pos to the len bytes in mem. Return 2655275970Scy less than 0, 0, or greater than 0 as memcmp. 2656275970Scy */ 2657275970Scystatic int 2658275970Scyevbuffer_ptr_memcmp(const struct evbuffer *buf, const struct evbuffer_ptr *pos, 2659275970Scy const char *mem, size_t len) 2660275970Scy{ 2661275970Scy struct evbuffer_chain *chain; 2662275970Scy size_t position; 2663275970Scy int r; 2664275970Scy 2665275970Scy ASSERT_EVBUFFER_LOCKED(buf); 2666275970Scy 2667285612Sdelphij if (pos->pos < 0 || 2668285612Sdelphij EV_SIZE_MAX - len < (size_t)pos->pos || 2669285612Sdelphij pos->pos + len > buf->total_len) 2670275970Scy return -1; 2671275970Scy 2672275970Scy chain = pos->internal_.chain; 2673275970Scy position = pos->internal_.pos_in_chain; 2674275970Scy while (len && chain) { 2675275970Scy size_t n_comparable; 2676275970Scy if (len + position > chain->off) 2677275970Scy n_comparable = chain->off - position; 2678275970Scy else 2679275970Scy n_comparable = len; 2680275970Scy r = memcmp(chain->buffer + chain->misalign + position, mem, 2681275970Scy n_comparable); 2682275970Scy if (r) 2683275970Scy return r; 2684275970Scy mem += n_comparable; 2685275970Scy len -= n_comparable; 2686275970Scy position = 0; 2687275970Scy chain = chain->next; 2688275970Scy } 2689275970Scy 2690275970Scy return 0; 2691275970Scy} 2692275970Scy 2693275970Scystruct evbuffer_ptr 2694275970Scyevbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start) 2695275970Scy{ 2696275970Scy return evbuffer_search_range(buffer, what, len, start, NULL); 2697275970Scy} 2698275970Scy 2699275970Scystruct evbuffer_ptr 2700275970Scyevbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end) 2701275970Scy{ 2702275970Scy struct evbuffer_ptr pos; 2703275970Scy struct evbuffer_chain *chain, *last_chain = NULL; 2704275970Scy const unsigned char *p; 2705275970Scy char first; 2706275970Scy 2707275970Scy EVBUFFER_LOCK(buffer); 2708275970Scy 2709275970Scy if (start) { 2710275970Scy memcpy(&pos, start, sizeof(pos)); 2711275970Scy chain = pos.internal_.chain; 2712275970Scy } else { 2713275970Scy pos.pos = 0; 2714275970Scy chain = pos.internal_.chain = buffer->first; 2715275970Scy pos.internal_.pos_in_chain = 0; 2716275970Scy } 2717275970Scy 2718275970Scy if (end) 2719275970Scy last_chain = end->internal_.chain; 2720275970Scy 2721275970Scy if (!len || len > EV_SSIZE_MAX) 2722275970Scy goto done; 2723275970Scy 2724275970Scy first = what[0]; 2725275970Scy 2726275970Scy while (chain) { 2727275970Scy const unsigned char *start_at = 2728275970Scy chain->buffer + chain->misalign + 2729275970Scy pos.internal_.pos_in_chain; 2730275970Scy p = memchr(start_at, first, 2731275970Scy chain->off - pos.internal_.pos_in_chain); 2732275970Scy if (p) { 2733275970Scy pos.pos += p - start_at; 2734275970Scy pos.internal_.pos_in_chain += p - start_at; 2735275970Scy if (!evbuffer_ptr_memcmp(buffer, &pos, what, len)) { 2736275970Scy if (end && pos.pos + (ev_ssize_t)len > end->pos) 2737275970Scy goto not_found; 2738275970Scy else 2739275970Scy goto done; 2740275970Scy } 2741275970Scy ++pos.pos; 2742275970Scy ++pos.internal_.pos_in_chain; 2743275970Scy if (pos.internal_.pos_in_chain == chain->off) { 2744275970Scy chain = pos.internal_.chain = chain->next; 2745275970Scy pos.internal_.pos_in_chain = 0; 2746275970Scy } 2747275970Scy } else { 2748275970Scy if (chain == last_chain) 2749275970Scy goto not_found; 2750275970Scy pos.pos += chain->off - pos.internal_.pos_in_chain; 2751275970Scy chain = pos.internal_.chain = chain->next; 2752275970Scy pos.internal_.pos_in_chain = 0; 2753275970Scy } 2754275970Scy } 2755275970Scy 2756275970Scynot_found: 2757275970Scy PTR_NOT_FOUND(&pos); 2758275970Scydone: 2759275970Scy EVBUFFER_UNLOCK(buffer); 2760275970Scy return pos; 2761275970Scy} 2762275970Scy 2763275970Scyint 2764275970Scyevbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, 2765275970Scy struct evbuffer_ptr *start_at, 2766275970Scy struct evbuffer_iovec *vec, int n_vec) 2767275970Scy{ 2768275970Scy struct evbuffer_chain *chain; 2769275970Scy int idx = 0; 2770275970Scy ev_ssize_t len_so_far = 0; 2771275970Scy 2772275970Scy /* Avoid locking in trivial edge cases */ 2773275970Scy if (start_at && start_at->internal_.chain == NULL) 2774275970Scy return 0; 2775275970Scy 2776275970Scy EVBUFFER_LOCK(buffer); 2777275970Scy 2778275970Scy if (start_at) { 2779275970Scy chain = start_at->internal_.chain; 2780275970Scy len_so_far = chain->off 2781275970Scy - start_at->internal_.pos_in_chain; 2782275970Scy idx = 1; 2783275970Scy if (n_vec > 0) { 2784275970Scy vec[0].iov_base = chain->buffer + chain->misalign 2785275970Scy + start_at->internal_.pos_in_chain; 2786275970Scy vec[0].iov_len = len_so_far; 2787275970Scy } 2788275970Scy chain = chain->next; 2789275970Scy } else { 2790275970Scy chain = buffer->first; 2791275970Scy } 2792275970Scy 2793275970Scy if (n_vec == 0 && len < 0) { 2794275970Scy /* If no vectors are provided and they asked for "everything", 2795275970Scy * pretend they asked for the actual available amount. */ 2796285612Sdelphij len = buffer->total_len; 2797285612Sdelphij if (start_at) { 2798285612Sdelphij len -= start_at->pos; 2799285612Sdelphij } 2800275970Scy } 2801275970Scy 2802275970Scy while (chain) { 2803275970Scy if (len >= 0 && len_so_far >= len) 2804275970Scy break; 2805275970Scy if (idx<n_vec) { 2806275970Scy vec[idx].iov_base = chain->buffer + chain->misalign; 2807275970Scy vec[idx].iov_len = chain->off; 2808275970Scy } else if (len<0) { 2809275970Scy break; 2810275970Scy } 2811275970Scy ++idx; 2812275970Scy len_so_far += chain->off; 2813275970Scy chain = chain->next; 2814275970Scy } 2815275970Scy 2816275970Scy EVBUFFER_UNLOCK(buffer); 2817275970Scy 2818275970Scy return idx; 2819275970Scy} 2820275970Scy 2821275970Scy 2822275970Scyint 2823275970Scyevbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) 2824275970Scy{ 2825275970Scy char *buffer; 2826275970Scy size_t space; 2827275970Scy int sz, result = -1; 2828275970Scy va_list aq; 2829275970Scy struct evbuffer_chain *chain; 2830275970Scy 2831275970Scy 2832275970Scy EVBUFFER_LOCK(buf); 2833275970Scy 2834275970Scy if (buf->freeze_end) { 2835275970Scy goto done; 2836275970Scy } 2837275970Scy 2838275970Scy /* make sure that at least some space is available */ 2839275970Scy if ((chain = evbuffer_expand_singlechain(buf, 64)) == NULL) 2840275970Scy goto done; 2841275970Scy 2842275970Scy for (;;) { 2843275970Scy#if 0 2844275970Scy size_t used = chain->misalign + chain->off; 2845275970Scy buffer = (char *)chain->buffer + chain->misalign + chain->off; 2846275970Scy EVUTIL_ASSERT(chain->buffer_len >= used); 2847275970Scy space = chain->buffer_len - used; 2848275970Scy#endif 2849275970Scy buffer = (char*) CHAIN_SPACE_PTR(chain); 2850275970Scy space = (size_t) CHAIN_SPACE_LEN(chain); 2851275970Scy 2852275970Scy#ifndef va_copy 2853275970Scy#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) 2854275970Scy#endif 2855275970Scy va_copy(aq, ap); 2856275970Scy 2857275970Scy sz = evutil_vsnprintf(buffer, space, fmt, aq); 2858275970Scy 2859275970Scy va_end(aq); 2860275970Scy 2861275970Scy if (sz < 0) 2862275970Scy goto done; 2863285612Sdelphij if (INT_MAX >= EVBUFFER_CHAIN_MAX && 2864285612Sdelphij (size_t)sz >= EVBUFFER_CHAIN_MAX) 2865285612Sdelphij goto done; 2866275970Scy if ((size_t)sz < space) { 2867275970Scy chain->off += sz; 2868275970Scy buf->total_len += sz; 2869275970Scy buf->n_add_for_cb += sz; 2870275970Scy 2871275970Scy advance_last_with_data(buf); 2872275970Scy evbuffer_invoke_callbacks_(buf); 2873275970Scy result = sz; 2874275970Scy goto done; 2875275970Scy } 2876275970Scy if ((chain = evbuffer_expand_singlechain(buf, sz + 1)) == NULL) 2877275970Scy goto done; 2878275970Scy } 2879275970Scy /* NOTREACHED */ 2880275970Scy 2881275970Scydone: 2882275970Scy EVBUFFER_UNLOCK(buf); 2883275970Scy return result; 2884275970Scy} 2885275970Scy 2886275970Scyint 2887275970Scyevbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) 2888275970Scy{ 2889275970Scy int res = -1; 2890275970Scy va_list ap; 2891275970Scy 2892275970Scy va_start(ap, fmt); 2893275970Scy res = evbuffer_add_vprintf(buf, fmt, ap); 2894275970Scy va_end(ap); 2895275970Scy 2896275970Scy return (res); 2897275970Scy} 2898275970Scy 2899275970Scyint 2900275970Scyevbuffer_add_reference(struct evbuffer *outbuf, 2901275970Scy const void *data, size_t datlen, 2902275970Scy evbuffer_ref_cleanup_cb cleanupfn, void *extra) 2903275970Scy{ 2904275970Scy struct evbuffer_chain *chain; 2905275970Scy struct evbuffer_chain_reference *info; 2906275970Scy int result = -1; 2907275970Scy 2908275970Scy chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_reference)); 2909275970Scy if (!chain) 2910275970Scy return (-1); 2911275970Scy chain->flags |= EVBUFFER_REFERENCE | EVBUFFER_IMMUTABLE; 2912275970Scy chain->buffer = (u_char *)data; 2913275970Scy chain->buffer_len = datlen; 2914275970Scy chain->off = datlen; 2915275970Scy 2916275970Scy info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_reference, chain); 2917275970Scy info->cleanupfn = cleanupfn; 2918275970Scy info->extra = extra; 2919275970Scy 2920275970Scy EVBUFFER_LOCK(outbuf); 2921275970Scy if (outbuf->freeze_end) { 2922275970Scy /* don't call chain_free; we do not want to actually invoke 2923275970Scy * the cleanup function */ 2924275970Scy mm_free(chain); 2925275970Scy goto done; 2926275970Scy } 2927275970Scy evbuffer_chain_insert(outbuf, chain); 2928275970Scy outbuf->n_add_for_cb += datlen; 2929275970Scy 2930275970Scy evbuffer_invoke_callbacks_(outbuf); 2931275970Scy 2932275970Scy result = 0; 2933275970Scydone: 2934275970Scy EVBUFFER_UNLOCK(outbuf); 2935275970Scy 2936275970Scy return result; 2937275970Scy} 2938275970Scy 2939275970Scy/* TODO(niels): we may want to add to automagically convert to mmap, in 2940275970Scy * case evbuffer_remove() or evbuffer_pullup() are being used. 2941275970Scy */ 2942275970Scystruct evbuffer_file_segment * 2943275970Scyevbuffer_file_segment_new( 2944275970Scy int fd, ev_off_t offset, ev_off_t length, unsigned flags) 2945275970Scy{ 2946275970Scy struct evbuffer_file_segment *seg = 2947275970Scy mm_calloc(sizeof(struct evbuffer_file_segment), 1); 2948275970Scy if (!seg) 2949275970Scy return NULL; 2950275970Scy seg->refcnt = 1; 2951275970Scy seg->fd = fd; 2952275970Scy seg->flags = flags; 2953275970Scy seg->file_offset = offset; 2954275970Scy seg->cleanup_cb = NULL; 2955275970Scy seg->cleanup_cb_arg = NULL; 2956275970Scy#ifdef _WIN32 2957275970Scy#ifndef lseek 2958275970Scy#define lseek _lseeki64 2959275970Scy#endif 2960275970Scy#ifndef fstat 2961275970Scy#define fstat _fstat 2962275970Scy#endif 2963275970Scy#ifndef stat 2964275970Scy#define stat _stat 2965275970Scy#endif 2966275970Scy#endif 2967275970Scy if (length == -1) { 2968275970Scy struct stat st; 2969275970Scy if (fstat(fd, &st) < 0) 2970275970Scy goto err; 2971275970Scy length = st.st_size; 2972275970Scy } 2973275970Scy seg->length = length; 2974275970Scy 2975285612Sdelphij if (offset < 0 || length < 0 || 2976285612Sdelphij ((ev_uint64_t)length > EVBUFFER_CHAIN_MAX) || 2977285612Sdelphij (ev_uint64_t)offset > (ev_uint64_t)(EVBUFFER_CHAIN_MAX - length)) 2978285612Sdelphij goto err; 2979285612Sdelphij 2980275970Scy#if defined(USE_SENDFILE) 2981275970Scy if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) { 2982275970Scy seg->can_sendfile = 1; 2983275970Scy goto done; 2984275970Scy } 2985275970Scy#endif 2986275970Scy 2987275970Scy if (evbuffer_file_segment_materialize(seg)<0) 2988275970Scy goto err; 2989275970Scy 2990275970Scy#if defined(USE_SENDFILE) 2991275970Scydone: 2992275970Scy#endif 2993275970Scy if (!(flags & EVBUF_FS_DISABLE_LOCKING)) { 2994275970Scy EVTHREAD_ALLOC_LOCK(seg->lock, 0); 2995275970Scy } 2996275970Scy return seg; 2997275970Scyerr: 2998275970Scy mm_free(seg); 2999275970Scy return NULL; 3000275970Scy} 3001275970Scy 3002275970Scy#ifdef EVENT__HAVE_MMAP 3003275970Scystatic long 3004275970Scyget_page_size(void) 3005275970Scy{ 3006275970Scy#ifdef SC_PAGE_SIZE 3007275970Scy return sysconf(SC_PAGE_SIZE); 3008275970Scy#elif defined(_SC_PAGE_SIZE) 3009275970Scy return sysconf(_SC_PAGE_SIZE); 3010275970Scy#else 3011275970Scy return 1; 3012275970Scy#endif 3013275970Scy} 3014275970Scy#endif 3015275970Scy 3016275970Scy/* DOCDOC */ 3017275970Scy/* Requires lock */ 3018275970Scystatic int 3019275970Scyevbuffer_file_segment_materialize(struct evbuffer_file_segment *seg) 3020275970Scy{ 3021275970Scy const unsigned flags = seg->flags; 3022275970Scy const int fd = seg->fd; 3023275970Scy const ev_off_t length = seg->length; 3024275970Scy const ev_off_t offset = seg->file_offset; 3025275970Scy 3026275970Scy if (seg->contents) 3027275970Scy return 0; /* already materialized */ 3028275970Scy 3029275970Scy#if defined(EVENT__HAVE_MMAP) 3030275970Scy if (!(flags & EVBUF_FS_DISABLE_MMAP)) { 3031275970Scy off_t offset_rounded = 0, offset_leftover = 0; 3032275970Scy void *mapped; 3033275970Scy if (offset) { 3034275970Scy /* mmap implementations don't generally like us 3035275970Scy * to have an offset that isn't a round */ 3036275970Scy long page_size = get_page_size(); 3037275970Scy if (page_size == -1) 3038275970Scy goto err; 3039275970Scy offset_leftover = offset % page_size; 3040275970Scy offset_rounded = offset - offset_leftover; 3041275970Scy } 3042275970Scy mapped = mmap(NULL, length + offset_leftover, 3043275970Scy PROT_READ, 3044275970Scy#ifdef MAP_NOCACHE 3045275970Scy MAP_NOCACHE | /* ??? */ 3046275970Scy#endif 3047275970Scy#ifdef MAP_FILE 3048275970Scy MAP_FILE | 3049275970Scy#endif 3050275970Scy MAP_PRIVATE, 3051275970Scy fd, offset_rounded); 3052275970Scy if (mapped == MAP_FAILED) { 3053275970Scy event_warn("%s: mmap(%d, %d, %zu) failed", 3054275970Scy __func__, fd, 0, (size_t)(offset + length)); 3055275970Scy } else { 3056275970Scy seg->mapping = mapped; 3057275970Scy seg->contents = (char*)mapped+offset_leftover; 3058275970Scy seg->mmap_offset = 0; 3059275970Scy seg->is_mapping = 1; 3060275970Scy goto done; 3061275970Scy } 3062275970Scy } 3063275970Scy#endif 3064275970Scy#ifdef _WIN32 3065275970Scy if (!(flags & EVBUF_FS_DISABLE_MMAP)) { 3066275970Scy intptr_t h = _get_osfhandle(fd); 3067275970Scy HANDLE m; 3068275970Scy ev_uint64_t total_size = length+offset; 3069275970Scy if ((HANDLE)h == INVALID_HANDLE_VALUE) 3070275970Scy goto err; 3071275970Scy m = CreateFileMapping((HANDLE)h, NULL, PAGE_READONLY, 3072275970Scy (total_size >> 32), total_size & 0xfffffffful, 3073275970Scy NULL); 3074275970Scy if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */ 3075275970Scy seg->mapping_handle = m; 3076275970Scy seg->mmap_offset = offset; 3077275970Scy seg->is_mapping = 1; 3078275970Scy goto done; 3079275970Scy } 3080275970Scy } 3081275970Scy#endif 3082275970Scy { 3083275970Scy ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos; 3084275970Scy ev_off_t read_so_far = 0; 3085275970Scy char *mem; 3086275970Scy int e; 3087275970Scy ev_ssize_t n = 0; 3088275970Scy if (!(mem = mm_malloc(length))) 3089275970Scy goto err; 3090275970Scy if (start_pos < 0) { 3091275970Scy mm_free(mem); 3092275970Scy goto err; 3093275970Scy } 3094275970Scy if (lseek(fd, offset, SEEK_SET) < 0) { 3095275970Scy mm_free(mem); 3096275970Scy goto err; 3097275970Scy } 3098275970Scy while (read_so_far < length) { 3099275970Scy n = read(fd, mem+read_so_far, length-read_so_far); 3100275970Scy if (n <= 0) 3101275970Scy break; 3102275970Scy read_so_far += n; 3103275970Scy } 3104275970Scy 3105275970Scy e = errno; 3106275970Scy pos = lseek(fd, start_pos, SEEK_SET); 3107275970Scy if (n < 0 || (n == 0 && length > read_so_far)) { 3108275970Scy mm_free(mem); 3109275970Scy errno = e; 3110275970Scy goto err; 3111275970Scy } else if (pos < 0) { 3112275970Scy mm_free(mem); 3113275970Scy goto err; 3114275970Scy } 3115275970Scy 3116275970Scy seg->contents = mem; 3117275970Scy } 3118275970Scy 3119275970Scydone: 3120275970Scy return 0; 3121275970Scyerr: 3122275970Scy return -1; 3123275970Scy} 3124275970Scy 3125275970Scyvoid evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, 3126275970Scy evbuffer_file_segment_cleanup_cb cb, void* arg) 3127275970Scy{ 3128275970Scy EVUTIL_ASSERT(seg->refcnt > 0); 3129275970Scy seg->cleanup_cb = cb; 3130275970Scy seg->cleanup_cb_arg = arg; 3131275970Scy} 3132275970Scy 3133275970Scyvoid 3134275970Scyevbuffer_file_segment_free(struct evbuffer_file_segment *seg) 3135275970Scy{ 3136275970Scy int refcnt; 3137275970Scy EVLOCK_LOCK(seg->lock, 0); 3138275970Scy refcnt = --seg->refcnt; 3139275970Scy EVLOCK_UNLOCK(seg->lock, 0); 3140275970Scy if (refcnt > 0) 3141275970Scy return; 3142275970Scy EVUTIL_ASSERT(refcnt == 0); 3143275970Scy 3144275970Scy if (seg->is_mapping) { 3145275970Scy#ifdef _WIN32 3146275970Scy CloseHandle(seg->mapping_handle); 3147275970Scy#elif defined (EVENT__HAVE_MMAP) 3148275970Scy off_t offset_leftover; 3149275970Scy offset_leftover = seg->file_offset % get_page_size(); 3150275970Scy if (munmap(seg->mapping, seg->length + offset_leftover) == -1) 3151275970Scy event_warn("%s: munmap failed", __func__); 3152275970Scy#endif 3153275970Scy } else if (seg->contents) { 3154275970Scy mm_free(seg->contents); 3155275970Scy } 3156275970Scy 3157275970Scy if ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) { 3158275970Scy close(seg->fd); 3159275970Scy } 3160275970Scy 3161275970Scy if (seg->cleanup_cb) { 3162275970Scy (*seg->cleanup_cb)((struct evbuffer_file_segment const*)seg, 3163275970Scy seg->flags, seg->cleanup_cb_arg); 3164275970Scy seg->cleanup_cb = NULL; 3165275970Scy seg->cleanup_cb_arg = NULL; 3166275970Scy } 3167275970Scy 3168275970Scy EVTHREAD_FREE_LOCK(seg->lock, 0); 3169275970Scy mm_free(seg); 3170275970Scy} 3171275970Scy 3172275970Scyint 3173275970Scyevbuffer_add_file_segment(struct evbuffer *buf, 3174275970Scy struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length) 3175275970Scy{ 3176275970Scy struct evbuffer_chain *chain; 3177275970Scy struct evbuffer_chain_file_segment *extra; 3178275970Scy int can_use_sendfile = 0; 3179275970Scy 3180275970Scy EVBUFFER_LOCK(buf); 3181275970Scy EVLOCK_LOCK(seg->lock, 0); 3182275970Scy if (buf->flags & EVBUFFER_FLAG_DRAINS_TO_FD) { 3183275970Scy can_use_sendfile = 1; 3184275970Scy } else { 3185275970Scy if (!seg->contents) { 3186275970Scy if (evbuffer_file_segment_materialize(seg)<0) { 3187275970Scy EVLOCK_UNLOCK(seg->lock, 0); 3188275970Scy EVBUFFER_UNLOCK(buf); 3189275970Scy return -1; 3190275970Scy } 3191275970Scy } 3192275970Scy } 3193275970Scy ++seg->refcnt; 3194275970Scy EVLOCK_UNLOCK(seg->lock, 0); 3195275970Scy 3196275970Scy if (buf->freeze_end) 3197275970Scy goto err; 3198275970Scy 3199275970Scy if (length < 0) { 3200275970Scy if (offset > seg->length) 3201275970Scy goto err; 3202275970Scy length = seg->length - offset; 3203275970Scy } 3204275970Scy 3205275970Scy /* Can we actually add this? */ 3206275970Scy if (offset+length > seg->length) 3207275970Scy goto err; 3208275970Scy 3209275970Scy chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_file_segment)); 3210275970Scy if (!chain) 3211275970Scy goto err; 3212275970Scy extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain); 3213275970Scy 3214275970Scy chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT; 3215275970Scy if (can_use_sendfile && seg->can_sendfile) { 3216275970Scy chain->flags |= EVBUFFER_SENDFILE; 3217275970Scy chain->misalign = seg->file_offset + offset; 3218275970Scy chain->off = length; 3219275970Scy chain->buffer_len = chain->misalign + length; 3220275970Scy } else if (seg->is_mapping) { 3221275970Scy#ifdef _WIN32 3222275970Scy ev_uint64_t total_offset = seg->mmap_offset+offset; 3223275970Scy ev_uint64_t offset_rounded=0, offset_remaining=0; 3224275970Scy LPVOID data; 3225275970Scy if (total_offset) { 3226275970Scy SYSTEM_INFO si; 3227275970Scy memset(&si, 0, sizeof(si)); /* cargo cult */ 3228275970Scy GetSystemInfo(&si); 3229275970Scy offset_remaining = total_offset % si.dwAllocationGranularity; 3230275970Scy offset_rounded = total_offset - offset_remaining; 3231275970Scy } 3232275970Scy data = MapViewOfFile( 3233275970Scy seg->mapping_handle, 3234275970Scy FILE_MAP_READ, 3235275970Scy offset_rounded >> 32, 3236275970Scy offset_rounded & 0xfffffffful, 3237275970Scy length + offset_remaining); 3238275970Scy if (data == NULL) { 3239275970Scy mm_free(chain); 3240275970Scy goto err; 3241275970Scy } 3242275970Scy chain->buffer = (unsigned char*) data; 3243275970Scy chain->buffer_len = length+offset_remaining; 3244275970Scy chain->misalign = offset_remaining; 3245275970Scy chain->off = length; 3246275970Scy#else 3247275970Scy chain->buffer = (unsigned char*)(seg->contents + offset); 3248275970Scy chain->buffer_len = length; 3249275970Scy chain->off = length; 3250275970Scy#endif 3251275970Scy } else { 3252275970Scy chain->buffer = (unsigned char*)(seg->contents + offset); 3253275970Scy chain->buffer_len = length; 3254275970Scy chain->off = length; 3255275970Scy } 3256275970Scy 3257275970Scy extra->segment = seg; 3258275970Scy buf->n_add_for_cb += length; 3259275970Scy evbuffer_chain_insert(buf, chain); 3260275970Scy 3261275970Scy evbuffer_invoke_callbacks_(buf); 3262275970Scy 3263275970Scy EVBUFFER_UNLOCK(buf); 3264275970Scy 3265275970Scy return 0; 3266275970Scyerr: 3267275970Scy EVBUFFER_UNLOCK(buf); 3268285612Sdelphij evbuffer_file_segment_free(seg); /* Lowers the refcount */ 3269275970Scy return -1; 3270275970Scy} 3271275970Scy 3272275970Scyint 3273275970Scyevbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length) 3274275970Scy{ 3275275970Scy struct evbuffer_file_segment *seg; 3276275970Scy unsigned flags = EVBUF_FS_CLOSE_ON_FREE; 3277275970Scy int r; 3278275970Scy 3279275970Scy seg = evbuffer_file_segment_new(fd, offset, length, flags); 3280275970Scy if (!seg) 3281275970Scy return -1; 3282275970Scy r = evbuffer_add_file_segment(buf, seg, 0, length); 3283275970Scy if (r == 0) 3284275970Scy evbuffer_file_segment_free(seg); 3285275970Scy return r; 3286275970Scy} 3287275970Scy 3288275970Scyvoid 3289275970Scyevbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg) 3290275970Scy{ 3291275970Scy EVBUFFER_LOCK(buffer); 3292275970Scy 3293275970Scy if (!LIST_EMPTY(&buffer->callbacks)) 3294275970Scy evbuffer_remove_all_callbacks(buffer); 3295275970Scy 3296275970Scy if (cb) { 3297275970Scy struct evbuffer_cb_entry *ent = 3298275970Scy evbuffer_add_cb(buffer, NULL, cbarg); 3299275970Scy ent->cb.cb_obsolete = cb; 3300275970Scy ent->flags |= EVBUFFER_CB_OBSOLETE; 3301275970Scy } 3302275970Scy EVBUFFER_UNLOCK(buffer); 3303275970Scy} 3304275970Scy 3305275970Scystruct evbuffer_cb_entry * 3306275970Scyevbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg) 3307275970Scy{ 3308275970Scy struct evbuffer_cb_entry *e; 3309275970Scy if (! (e = mm_calloc(1, sizeof(struct evbuffer_cb_entry)))) 3310275970Scy return NULL; 3311275970Scy EVBUFFER_LOCK(buffer); 3312275970Scy e->cb.cb_func = cb; 3313275970Scy e->cbarg = cbarg; 3314275970Scy e->flags = EVBUFFER_CB_ENABLED; 3315275970Scy LIST_INSERT_HEAD(&buffer->callbacks, e, next); 3316275970Scy EVBUFFER_UNLOCK(buffer); 3317275970Scy return e; 3318275970Scy} 3319275970Scy 3320275970Scyint 3321275970Scyevbuffer_remove_cb_entry(struct evbuffer *buffer, 3322275970Scy struct evbuffer_cb_entry *ent) 3323275970Scy{ 3324275970Scy EVBUFFER_LOCK(buffer); 3325275970Scy LIST_REMOVE(ent, next); 3326275970Scy EVBUFFER_UNLOCK(buffer); 3327275970Scy mm_free(ent); 3328275970Scy return 0; 3329275970Scy} 3330275970Scy 3331275970Scyint 3332275970Scyevbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg) 3333275970Scy{ 3334275970Scy struct evbuffer_cb_entry *cbent; 3335275970Scy int result = -1; 3336275970Scy EVBUFFER_LOCK(buffer); 3337275970Scy LIST_FOREACH(cbent, &buffer->callbacks, next) { 3338275970Scy if (cb == cbent->cb.cb_func && cbarg == cbent->cbarg) { 3339275970Scy result = evbuffer_remove_cb_entry(buffer, cbent); 3340275970Scy goto done; 3341275970Scy } 3342275970Scy } 3343275970Scydone: 3344275970Scy EVBUFFER_UNLOCK(buffer); 3345275970Scy return result; 3346275970Scy} 3347275970Scy 3348275970Scyint 3349275970Scyevbuffer_cb_set_flags(struct evbuffer *buffer, 3350275970Scy struct evbuffer_cb_entry *cb, ev_uint32_t flags) 3351275970Scy{ 3352275970Scy /* the user isn't allowed to mess with these. */ 3353275970Scy flags &= ~EVBUFFER_CB_INTERNAL_FLAGS; 3354275970Scy EVBUFFER_LOCK(buffer); 3355275970Scy cb->flags |= flags; 3356275970Scy EVBUFFER_UNLOCK(buffer); 3357275970Scy return 0; 3358275970Scy} 3359275970Scy 3360275970Scyint 3361275970Scyevbuffer_cb_clear_flags(struct evbuffer *buffer, 3362275970Scy struct evbuffer_cb_entry *cb, ev_uint32_t flags) 3363275970Scy{ 3364275970Scy /* the user isn't allowed to mess with these. */ 3365275970Scy flags &= ~EVBUFFER_CB_INTERNAL_FLAGS; 3366275970Scy EVBUFFER_LOCK(buffer); 3367275970Scy cb->flags &= ~flags; 3368275970Scy EVBUFFER_UNLOCK(buffer); 3369275970Scy return 0; 3370275970Scy} 3371275970Scy 3372275970Scyint 3373275970Scyevbuffer_freeze(struct evbuffer *buffer, int start) 3374275970Scy{ 3375275970Scy EVBUFFER_LOCK(buffer); 3376275970Scy if (start) 3377275970Scy buffer->freeze_start = 1; 3378275970Scy else 3379275970Scy buffer->freeze_end = 1; 3380275970Scy EVBUFFER_UNLOCK(buffer); 3381275970Scy return 0; 3382275970Scy} 3383275970Scy 3384275970Scyint 3385275970Scyevbuffer_unfreeze(struct evbuffer *buffer, int start) 3386275970Scy{ 3387275970Scy EVBUFFER_LOCK(buffer); 3388275970Scy if (start) 3389275970Scy buffer->freeze_start = 0; 3390275970Scy else 3391275970Scy buffer->freeze_end = 0; 3392275970Scy EVBUFFER_UNLOCK(buffer); 3393275970Scy return 0; 3394275970Scy} 3395275970Scy 3396275970Scy#if 0 3397275970Scyvoid 3398275970Scyevbuffer_cb_suspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb) 3399275970Scy{ 3400275970Scy if (!(cb->flags & EVBUFFER_CB_SUSPENDED)) { 3401275970Scy cb->size_before_suspend = evbuffer_get_length(buffer); 3402275970Scy cb->flags |= EVBUFFER_CB_SUSPENDED; 3403275970Scy } 3404275970Scy} 3405275970Scy 3406275970Scyvoid 3407275970Scyevbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb) 3408275970Scy{ 3409275970Scy if ((cb->flags & EVBUFFER_CB_SUSPENDED)) { 3410275970Scy unsigned call = (cb->flags & EVBUFFER_CB_CALL_ON_UNSUSPEND); 3411275970Scy size_t sz = cb->size_before_suspend; 3412275970Scy cb->flags &= ~(EVBUFFER_CB_SUSPENDED| 3413275970Scy EVBUFFER_CB_CALL_ON_UNSUSPEND); 3414275970Scy cb->size_before_suspend = 0; 3415275970Scy if (call && (cb->flags & EVBUFFER_CB_ENABLED)) { 3416275970Scy cb->cb(buffer, sz, evbuffer_get_length(buffer), cb->cbarg); 3417275970Scy } 3418275970Scy } 3419275970Scy} 3420275970Scy#endif 3421275970Scy 3422275970Scyint 3423275970Scyevbuffer_get_callbacks_(struct evbuffer *buffer, struct event_callback **cbs, 3424275970Scy int max_cbs) 3425275970Scy{ 3426275970Scy int r = 0; 3427275970Scy EVBUFFER_LOCK(buffer); 3428275970Scy if (buffer->deferred_cbs) { 3429275970Scy if (max_cbs < 1) { 3430275970Scy r = -1; 3431275970Scy goto done; 3432275970Scy } 3433275970Scy cbs[0] = &buffer->deferred; 3434275970Scy r = 1; 3435275970Scy } 3436275970Scydone: 3437275970Scy EVBUFFER_UNLOCK(buffer); 3438275970Scy return r; 3439275970Scy} 3440