1 1.5 christos /* $NetBSD: buffer.c,v 1.5 2021/04/07 03:36:48 christos Exp $ */ 2 1.5 christos 3 1.1 plunky /* 4 1.2 christos * Copyright (c) 2002-2007 Niels Provos <provos (at) citi.umich.edu> 5 1.2 christos * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 6 1.1 plunky * 7 1.1 plunky * Redistribution and use in source and binary forms, with or without 8 1.1 plunky * modification, are permitted provided that the following conditions 9 1.1 plunky * are met: 10 1.1 plunky * 1. Redistributions of source code must retain the above copyright 11 1.1 plunky * notice, this list of conditions and the following disclaimer. 12 1.1 plunky * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 plunky * notice, this list of conditions and the following disclaimer in the 14 1.1 plunky * documentation and/or other materials provided with the distribution. 15 1.1 plunky * 3. The name of the author may not be used to endorse or promote products 16 1.1 plunky * derived from this software without specific prior written permission. 17 1.1 plunky * 18 1.1 plunky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 1.1 plunky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 1.1 plunky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 1.1 plunky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 1.1 plunky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 1.1 plunky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 plunky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 plunky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 plunky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 1.1 plunky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 plunky */ 29 1.1 plunky 30 1.2 christos #include "event2/event-config.h" 31 1.2 christos #include <sys/cdefs.h> 32 1.5 christos __RCSID("$NetBSD: buffer.c,v 1.5 2021/04/07 03:36:48 christos Exp $"); 33 1.4 christos #include "evconfig-private.h" 34 1.1 plunky 35 1.4 christos #ifdef _WIN32 36 1.1 plunky #include <winsock2.h> 37 1.1 plunky #include <windows.h> 38 1.2 christos #include <io.h> 39 1.1 plunky #endif 40 1.1 plunky 41 1.4 christos #ifdef EVENT__HAVE_VASPRINTF 42 1.4 christos /* If we have vasprintf, we need to define _GNU_SOURCE before we include 43 1.4 christos * stdio.h. This comes from evconfig-private.h. 44 1.4 christos */ 45 1.1 plunky #endif 46 1.1 plunky 47 1.1 plunky #include <sys/types.h> 48 1.1 plunky 49 1.4 christos #ifdef EVENT__HAVE_SYS_TIME_H 50 1.1 plunky #include <sys/time.h> 51 1.1 plunky #endif 52 1.1 plunky 53 1.4 christos #ifdef EVENT__HAVE_SYS_SOCKET_H 54 1.2 christos #include <sys/socket.h> 55 1.2 christos #endif 56 1.2 christos 57 1.4 christos #ifdef EVENT__HAVE_SYS_UIO_H 58 1.2 christos #include <sys/uio.h> 59 1.2 christos #endif 60 1.2 christos 61 1.4 christos #ifdef EVENT__HAVE_SYS_IOCTL_H 62 1.1 plunky #include <sys/ioctl.h> 63 1.1 plunky #endif 64 1.1 plunky 65 1.4 christos #ifdef EVENT__HAVE_SYS_MMAN_H 66 1.2 christos #include <sys/mman.h> 67 1.2 christos #endif 68 1.2 christos 69 1.4 christos #ifdef EVENT__HAVE_SYS_SENDFILE_H 70 1.2 christos #include <sys/sendfile.h> 71 1.2 christos #endif 72 1.4 christos #ifdef EVENT__HAVE_SYS_STAT_H 73 1.4 christos #include <sys/stat.h> 74 1.4 christos #endif 75 1.4 christos 76 1.2 christos 77 1.1 plunky #include <errno.h> 78 1.1 plunky #include <stdio.h> 79 1.1 plunky #include <stdlib.h> 80 1.1 plunky #include <string.h> 81 1.4 christos #ifdef EVENT__HAVE_STDARG_H 82 1.1 plunky #include <stdarg.h> 83 1.1 plunky #endif 84 1.4 christos #ifdef EVENT__HAVE_UNISTD_H 85 1.1 plunky #include <unistd.h> 86 1.1 plunky #endif 87 1.2 christos #include <limits.h> 88 1.2 christos 89 1.2 christos #include "event2/event.h" 90 1.2 christos #include "event2/buffer.h" 91 1.2 christos #include "event2/buffer_compat.h" 92 1.2 christos #include "event2/bufferevent.h" 93 1.2 christos #include "event2/bufferevent_compat.h" 94 1.2 christos #include "event2/bufferevent_struct.h" 95 1.2 christos #include "event2/thread.h" 96 1.2 christos #include "log-internal.h" 97 1.2 christos #include "mm-internal.h" 98 1.2 christos #include "util-internal.h" 99 1.2 christos #include "evthread-internal.h" 100 1.2 christos #include "evbuffer-internal.h" 101 1.2 christos #include "bufferevent-internal.h" 102 1.5 christos #include "event-internal.h" 103 1.2 christos 104 1.2 christos /* some systems do not have MAP_FAILED */ 105 1.2 christos #ifndef MAP_FAILED 106 1.2 christos #define MAP_FAILED ((void *)-1) 107 1.2 christos #endif 108 1.2 christos 109 1.2 christos /* send file support */ 110 1.4 christos #if defined(EVENT__HAVE_SYS_SENDFILE_H) && defined(EVENT__HAVE_SENDFILE) && defined(__linux__) 111 1.2 christos #define USE_SENDFILE 1 112 1.2 christos #define SENDFILE_IS_LINUX 1 113 1.4 christos #elif defined(EVENT__HAVE_SENDFILE) && defined(__FreeBSD__) 114 1.2 christos #define USE_SENDFILE 1 115 1.2 christos #define SENDFILE_IS_FREEBSD 1 116 1.4 christos #elif defined(EVENT__HAVE_SENDFILE) && defined(__APPLE__) 117 1.2 christos #define USE_SENDFILE 1 118 1.2 christos #define SENDFILE_IS_MACOSX 1 119 1.4 christos #elif defined(EVENT__HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__) 120 1.2 christos #define USE_SENDFILE 1 121 1.2 christos #define SENDFILE_IS_SOLARIS 1 122 1.2 christos #endif 123 1.2 christos 124 1.2 christos /* Mask of user-selectable callback flags. */ 125 1.2 christos #define EVBUFFER_CB_USER_FLAGS 0xffff 126 1.2 christos /* Mask of all internal-use-only flags. */ 127 1.2 christos #define EVBUFFER_CB_INTERNAL_FLAGS 0xffff0000 128 1.2 christos 129 1.2 christos /* Flag set if the callback is using the cb_obsolete function pointer */ 130 1.2 christos #define EVBUFFER_CB_OBSOLETE 0x00040000 131 1.2 christos 132 1.2 christos /* evbuffer_chain support */ 133 1.2 christos #define CHAIN_SPACE_PTR(ch) ((ch)->buffer + (ch)->misalign + (ch)->off) 134 1.2 christos #define CHAIN_SPACE_LEN(ch) ((ch)->flags & EVBUFFER_IMMUTABLE ? \ 135 1.2 christos 0 : (ch)->buffer_len - ((ch)->misalign + (ch)->off)) 136 1.2 christos 137 1.2 christos #define CHAIN_PINNED(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_ANY) != 0) 138 1.2 christos #define CHAIN_PINNED_R(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_R) != 0) 139 1.2 christos 140 1.4 christos /* evbuffer_ptr support */ 141 1.4 christos #define PTR_NOT_FOUND(ptr) do { \ 142 1.4 christos (ptr)->pos = -1; \ 143 1.4 christos (ptr)->internal_.chain = NULL; \ 144 1.4 christos (ptr)->internal_.pos_in_chain = 0; \ 145 1.4 christos } while (0) 146 1.4 christos 147 1.2 christos static void evbuffer_chain_align(struct evbuffer_chain *chain); 148 1.2 christos static int evbuffer_chain_should_realign(struct evbuffer_chain *chain, 149 1.2 christos size_t datalen); 150 1.4 christos static void evbuffer_deferred_callback(struct event_callback *cb, void *arg); 151 1.2 christos static int evbuffer_ptr_memcmp(const struct evbuffer *buf, 152 1.2 christos const struct evbuffer_ptr *pos, const char *mem, size_t len); 153 1.2 christos static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf, 154 1.2 christos size_t datlen); 155 1.4 christos static int evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos, 156 1.4 christos size_t howfar); 157 1.4 christos static int evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg); 158 1.4 christos static inline void evbuffer_chain_incref(struct evbuffer_chain *chain); 159 1.2 christos 160 1.2 christos static struct evbuffer_chain * 161 1.2 christos evbuffer_chain_new(size_t size) 162 1.1 plunky { 163 1.2 christos struct evbuffer_chain *chain; 164 1.2 christos size_t to_alloc; 165 1.2 christos 166 1.3 spz if (size > EVBUFFER_CHAIN_MAX - EVBUFFER_CHAIN_SIZE) 167 1.3 spz return (NULL); 168 1.3 spz 169 1.2 christos size += EVBUFFER_CHAIN_SIZE; 170 1.2 christos 171 1.2 christos /* get the next largest memory that can hold the buffer */ 172 1.3 spz if (size < EVBUFFER_CHAIN_MAX / 2) { 173 1.3 spz to_alloc = MIN_BUFFER_SIZE; 174 1.3 spz while (to_alloc < size) { 175 1.3 spz to_alloc <<= 1; 176 1.3 spz } 177 1.3 spz } else { 178 1.3 spz to_alloc = size; 179 1.3 spz } 180 1.2 christos 181 1.2 christos /* we get everything in one chunk */ 182 1.2 christos if ((chain = mm_malloc(to_alloc)) == NULL) 183 1.2 christos return (NULL); 184 1.2 christos 185 1.2 christos memset(chain, 0, EVBUFFER_CHAIN_SIZE); 186 1.2 christos 187 1.2 christos chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE; 188 1.2 christos 189 1.2 christos /* this way we can manipulate the buffer to different addresses, 190 1.2 christos * which is required for mmap for example. 191 1.2 christos */ 192 1.4 christos chain->buffer = EVBUFFER_CHAIN_EXTRA(unsigned char, chain); 193 1.4 christos 194 1.4 christos chain->refcnt = 1; 195 1.1 plunky 196 1.2 christos return (chain); 197 1.1 plunky } 198 1.1 plunky 199 1.2 christos static inline void 200 1.2 christos evbuffer_chain_free(struct evbuffer_chain *chain) 201 1.1 plunky { 202 1.4 christos EVUTIL_ASSERT(chain->refcnt > 0); 203 1.4 christos if (--chain->refcnt > 0) { 204 1.4 christos /* chain is still referenced by other chains */ 205 1.4 christos return; 206 1.4 christos } 207 1.4 christos 208 1.2 christos if (CHAIN_PINNED(chain)) { 209 1.4 christos /* will get freed once no longer dangling */ 210 1.4 christos chain->refcnt++; 211 1.2 christos chain->flags |= EVBUFFER_DANGLING; 212 1.2 christos return; 213 1.2 christos } 214 1.4 christos 215 1.4 christos /* safe to release chain, it's either a referencing 216 1.4 christos * chain or all references to it have been freed */ 217 1.4 christos if (chain->flags & EVBUFFER_REFERENCE) { 218 1.4 christos struct evbuffer_chain_reference *info = 219 1.4 christos EVBUFFER_CHAIN_EXTRA( 220 1.4 christos struct evbuffer_chain_reference, 221 1.4 christos chain); 222 1.4 christos if (info->cleanupfn) 223 1.4 christos (*info->cleanupfn)(chain->buffer, 224 1.4 christos chain->buffer_len, 225 1.4 christos info->extra); 226 1.4 christos } 227 1.4 christos if (chain->flags & EVBUFFER_FILESEGMENT) { 228 1.4 christos struct evbuffer_chain_file_segment *info = 229 1.4 christos EVBUFFER_CHAIN_EXTRA( 230 1.4 christos struct evbuffer_chain_file_segment, 231 1.4 christos chain); 232 1.4 christos if (info->segment) { 233 1.4 christos #ifdef _WIN32 234 1.4 christos if (info->segment->is_mapping) 235 1.4 christos UnmapViewOfFile(chain->buffer); 236 1.4 christos #endif 237 1.4 christos evbuffer_file_segment_free(info->segment); 238 1.4 christos } 239 1.4 christos } 240 1.4 christos if (chain->flags & EVBUFFER_MULTICAST) { 241 1.4 christos struct evbuffer_multicast_parent *info = 242 1.4 christos EVBUFFER_CHAIN_EXTRA( 243 1.4 christos struct evbuffer_multicast_parent, 244 1.4 christos chain); 245 1.4 christos /* referencing chain is being freed, decrease 246 1.4 christos * refcounts of source chain and associated 247 1.4 christos * evbuffer (which get freed once both reach 248 1.4 christos * zero) */ 249 1.4 christos EVUTIL_ASSERT(info->source != NULL); 250 1.4 christos EVUTIL_ASSERT(info->parent != NULL); 251 1.4 christos EVBUFFER_LOCK(info->source); 252 1.4 christos evbuffer_chain_free(info->parent); 253 1.4 christos evbuffer_decref_and_unlock_(info->source); 254 1.2 christos } 255 1.2 christos 256 1.2 christos mm_free(chain); 257 1.1 plunky } 258 1.1 plunky 259 1.2 christos static void 260 1.2 christos evbuffer_free_all_chains(struct evbuffer_chain *chain) 261 1.2 christos { 262 1.2 christos struct evbuffer_chain *next; 263 1.2 christos for (; chain; chain = next) { 264 1.2 christos next = chain->next; 265 1.2 christos evbuffer_chain_free(chain); 266 1.2 christos } 267 1.2 christos } 268 1.1 plunky 269 1.2 christos #ifndef NDEBUG 270 1.2 christos static int 271 1.2 christos evbuffer_chains_all_empty(struct evbuffer_chain *chain) 272 1.2 christos { 273 1.2 christos for (; chain; chain = chain->next) { 274 1.2 christos if (chain->off) 275 1.2 christos return 0; 276 1.2 christos } 277 1.2 christos return 1; 278 1.2 christos } 279 1.2 christos #else 280 1.2 christos /* The definition is needed for EVUTIL_ASSERT, which uses sizeof to avoid 281 1.2 christos "unused variable" warnings. */ 282 1.2 christos static inline int evbuffer_chains_all_empty(struct evbuffer_chain *chain) { 283 1.2 christos return 1; 284 1.2 christos } 285 1.2 christos #endif 286 1.1 plunky 287 1.2 christos /* Free all trailing chains in 'buf' that are neither pinned nor empty, prior 288 1.2 christos * to replacing them all with a new chain. Return a pointer to the place 289 1.2 christos * where the new chain will go. 290 1.2 christos * 291 1.2 christos * Internal; requires lock. The caller must fix up buf->last and buf->first 292 1.2 christos * as needed; they might have been freed. 293 1.2 christos */ 294 1.2 christos static struct evbuffer_chain ** 295 1.2 christos evbuffer_free_trailing_empty_chains(struct evbuffer *buf) 296 1.1 plunky { 297 1.2 christos struct evbuffer_chain **ch = buf->last_with_datap; 298 1.2 christos /* Find the first victim chain. It might be *last_with_datap */ 299 1.2 christos while ((*ch) && ((*ch)->off != 0 || CHAIN_PINNED(*ch))) 300 1.2 christos ch = &(*ch)->next; 301 1.2 christos if (*ch) { 302 1.2 christos EVUTIL_ASSERT(evbuffer_chains_all_empty(*ch)); 303 1.2 christos evbuffer_free_all_chains(*ch); 304 1.2 christos *ch = NULL; 305 1.1 plunky } 306 1.2 christos return ch; 307 1.2 christos } 308 1.1 plunky 309 1.2 christos /* Add a single chain 'chain' to the end of 'buf', freeing trailing empty 310 1.2 christos * chains as necessary. Requires lock. Does not schedule callbacks. 311 1.2 christos */ 312 1.2 christos static void 313 1.2 christos evbuffer_chain_insert(struct evbuffer *buf, 314 1.2 christos struct evbuffer_chain *chain) 315 1.2 christos { 316 1.2 christos ASSERT_EVBUFFER_LOCKED(buf); 317 1.2 christos if (*buf->last_with_datap == NULL) { 318 1.2 christos /* There are no chains data on the buffer at all. */ 319 1.2 christos EVUTIL_ASSERT(buf->last_with_datap == &buf->first); 320 1.2 christos EVUTIL_ASSERT(buf->first == NULL); 321 1.2 christos buf->first = buf->last = chain; 322 1.2 christos } else { 323 1.4 christos struct evbuffer_chain **chp; 324 1.4 christos chp = evbuffer_free_trailing_empty_chains(buf); 325 1.4 christos *chp = chain; 326 1.4 christos if (chain->off) 327 1.4 christos buf->last_with_datap = chp; 328 1.2 christos buf->last = chain; 329 1.1 plunky } 330 1.2 christos buf->total_len += chain->off; 331 1.2 christos } 332 1.1 plunky 333 1.2 christos static inline struct evbuffer_chain * 334 1.2 christos evbuffer_chain_insert_new(struct evbuffer *buf, size_t datlen) 335 1.2 christos { 336 1.2 christos struct evbuffer_chain *chain; 337 1.2 christos if ((chain = evbuffer_chain_new(datlen)) == NULL) 338 1.2 christos return NULL; 339 1.2 christos evbuffer_chain_insert(buf, chain); 340 1.2 christos return chain; 341 1.1 plunky } 342 1.1 plunky 343 1.2 christos void 344 1.4 christos evbuffer_chain_pin_(struct evbuffer_chain *chain, unsigned flag) 345 1.1 plunky { 346 1.2 christos EVUTIL_ASSERT((chain->flags & flag) == 0); 347 1.2 christos chain->flags |= flag; 348 1.2 christos } 349 1.1 plunky 350 1.2 christos void 351 1.4 christos evbuffer_chain_unpin_(struct evbuffer_chain *chain, unsigned flag) 352 1.2 christos { 353 1.2 christos EVUTIL_ASSERT((chain->flags & flag) != 0); 354 1.2 christos chain->flags &= ~flag; 355 1.2 christos if (chain->flags & EVBUFFER_DANGLING) 356 1.2 christos evbuffer_chain_free(chain); 357 1.2 christos } 358 1.1 plunky 359 1.4 christos static inline void 360 1.4 christos evbuffer_chain_incref(struct evbuffer_chain *chain) 361 1.4 christos { 362 1.4 christos ++chain->refcnt; 363 1.4 christos } 364 1.4 christos 365 1.2 christos struct evbuffer * 366 1.2 christos evbuffer_new(void) 367 1.2 christos { 368 1.2 christos struct evbuffer *buffer; 369 1.1 plunky 370 1.2 christos buffer = mm_calloc(1, sizeof(struct evbuffer)); 371 1.2 christos if (buffer == NULL) 372 1.2 christos return (NULL); 373 1.1 plunky 374 1.4 christos LIST_INIT(&buffer->callbacks); 375 1.2 christos buffer->refcnt = 1; 376 1.2 christos buffer->last_with_datap = &buffer->first; 377 1.1 plunky 378 1.2 christos return (buffer); 379 1.2 christos } 380 1.1 plunky 381 1.2 christos int 382 1.2 christos evbuffer_set_flags(struct evbuffer *buf, ev_uint64_t flags) 383 1.2 christos { 384 1.2 christos EVBUFFER_LOCK(buf); 385 1.2 christos buf->flags |= (ev_uint32_t)flags; 386 1.2 christos EVBUFFER_UNLOCK(buf); 387 1.2 christos return 0; 388 1.1 plunky } 389 1.1 plunky 390 1.1 plunky int 391 1.2 christos evbuffer_clear_flags(struct evbuffer *buf, ev_uint64_t flags) 392 1.1 plunky { 393 1.2 christos EVBUFFER_LOCK(buf); 394 1.2 christos buf->flags &= ~(ev_uint32_t)flags; 395 1.2 christos EVBUFFER_UNLOCK(buf); 396 1.2 christos return 0; 397 1.2 christos } 398 1.1 plunky 399 1.2 christos void 400 1.4 christos evbuffer_incref_(struct evbuffer *buf) 401 1.2 christos { 402 1.2 christos EVBUFFER_LOCK(buf); 403 1.2 christos ++buf->refcnt; 404 1.2 christos EVBUFFER_UNLOCK(buf); 405 1.2 christos } 406 1.1 plunky 407 1.2 christos void 408 1.4 christos evbuffer_incref_and_lock_(struct evbuffer *buf) 409 1.2 christos { 410 1.2 christos EVBUFFER_LOCK(buf); 411 1.2 christos ++buf->refcnt; 412 1.1 plunky } 413 1.1 plunky 414 1.2 christos int 415 1.2 christos evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base) 416 1.2 christos { 417 1.2 christos EVBUFFER_LOCK(buffer); 418 1.4 christos buffer->cb_queue = base; 419 1.2 christos buffer->deferred_cbs = 1; 420 1.4 christos event_deferred_cb_init_(&buffer->deferred, 421 1.4 christos event_base_get_npriorities(base) / 2, 422 1.2 christos evbuffer_deferred_callback, buffer); 423 1.2 christos EVBUFFER_UNLOCK(buffer); 424 1.2 christos return 0; 425 1.2 christos } 426 1.1 plunky 427 1.1 plunky int 428 1.2 christos evbuffer_enable_locking(struct evbuffer *buf, void *lock) 429 1.1 plunky { 430 1.4 christos #ifdef EVENT__DISABLE_THREAD_SUPPORT 431 1.2 christos return -1; 432 1.2 christos #else 433 1.2 christos if (buf->lock) 434 1.2 christos return -1; 435 1.2 christos 436 1.2 christos if (!lock) { 437 1.2 christos EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE); 438 1.2 christos if (!lock) 439 1.2 christos return -1; 440 1.2 christos buf->lock = lock; 441 1.2 christos buf->own_lock = 1; 442 1.2 christos } else { 443 1.2 christos buf->lock = lock; 444 1.2 christos buf->own_lock = 0; 445 1.2 christos } 446 1.2 christos 447 1.2 christos return 0; 448 1.2 christos #endif 449 1.1 plunky } 450 1.1 plunky 451 1.2 christos void 452 1.4 christos evbuffer_set_parent_(struct evbuffer *buf, struct bufferevent *bev) 453 1.2 christos { 454 1.2 christos EVBUFFER_LOCK(buf); 455 1.2 christos buf->parent = bev; 456 1.2 christos EVBUFFER_UNLOCK(buf); 457 1.2 christos } 458 1.1 plunky 459 1.2 christos static void 460 1.2 christos evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred) 461 1.1 plunky { 462 1.2 christos struct evbuffer_cb_entry *cbent, *next; 463 1.2 christos struct evbuffer_cb_info info; 464 1.2 christos size_t new_size; 465 1.2 christos ev_uint32_t mask, masked_val; 466 1.2 christos int clear = 1; 467 1.2 christos 468 1.2 christos if (running_deferred) { 469 1.2 christos mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED; 470 1.2 christos masked_val = EVBUFFER_CB_ENABLED; 471 1.2 christos } else if (buffer->deferred_cbs) { 472 1.2 christos mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED; 473 1.2 christos masked_val = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED; 474 1.2 christos /* Don't zero-out n_add/n_del, since the deferred callbacks 475 1.2 christos will want to see them. */ 476 1.2 christos clear = 0; 477 1.2 christos } else { 478 1.2 christos mask = EVBUFFER_CB_ENABLED; 479 1.2 christos masked_val = EVBUFFER_CB_ENABLED; 480 1.1 plunky } 481 1.1 plunky 482 1.2 christos ASSERT_EVBUFFER_LOCKED(buffer); 483 1.1 plunky 484 1.4 christos if (LIST_EMPTY(&buffer->callbacks)) { 485 1.2 christos buffer->n_add_for_cb = buffer->n_del_for_cb = 0; 486 1.2 christos return; 487 1.1 plunky } 488 1.2 christos if (buffer->n_add_for_cb == 0 && buffer->n_del_for_cb == 0) 489 1.2 christos return; 490 1.1 plunky 491 1.2 christos new_size = buffer->total_len; 492 1.2 christos info.orig_size = new_size + buffer->n_del_for_cb - buffer->n_add_for_cb; 493 1.2 christos info.n_added = buffer->n_add_for_cb; 494 1.2 christos info.n_deleted = buffer->n_del_for_cb; 495 1.2 christos if (clear) { 496 1.2 christos buffer->n_add_for_cb = 0; 497 1.2 christos buffer->n_del_for_cb = 0; 498 1.2 christos } 499 1.4 christos for (cbent = LIST_FIRST(&buffer->callbacks); 500 1.4 christos cbent != LIST_END(&buffer->callbacks); 501 1.2 christos cbent = next) { 502 1.2 christos /* Get the 'next' pointer now in case this callback decides 503 1.2 christos * to remove itself or something. */ 504 1.4 christos next = LIST_NEXT(cbent, next); 505 1.2 christos 506 1.2 christos if ((cbent->flags & mask) != masked_val) 507 1.2 christos continue; 508 1.2 christos 509 1.2 christos if ((cbent->flags & EVBUFFER_CB_OBSOLETE)) 510 1.2 christos cbent->cb.cb_obsolete(buffer, 511 1.2 christos info.orig_size, new_size, cbent->cbarg); 512 1.2 christos else 513 1.2 christos cbent->cb.cb_func(buffer, &info, cbent->cbarg); 514 1.2 christos } 515 1.2 christos } 516 1.1 plunky 517 1.2 christos void 518 1.4 christos evbuffer_invoke_callbacks_(struct evbuffer *buffer) 519 1.2 christos { 520 1.4 christos if (LIST_EMPTY(&buffer->callbacks)) { 521 1.2 christos buffer->n_add_for_cb = buffer->n_del_for_cb = 0; 522 1.2 christos return; 523 1.2 christos } 524 1.1 plunky 525 1.2 christos if (buffer->deferred_cbs) { 526 1.4 christos if (event_deferred_cb_schedule_(buffer->cb_queue, &buffer->deferred)) { 527 1.4 christos evbuffer_incref_and_lock_(buffer); 528 1.4 christos if (buffer->parent) 529 1.4 christos bufferevent_incref_(buffer->parent); 530 1.5 christos EVBUFFER_UNLOCK(buffer); 531 1.4 christos } 532 1.1 plunky } 533 1.1 plunky 534 1.2 christos evbuffer_run_callbacks(buffer, 0); 535 1.2 christos } 536 1.2 christos 537 1.2 christos static void 538 1.4 christos evbuffer_deferred_callback(struct event_callback *cb, void *arg) 539 1.2 christos { 540 1.2 christos struct bufferevent *parent = NULL; 541 1.2 christos struct evbuffer *buffer = arg; 542 1.1 plunky 543 1.2 christos /* XXXX It would be better to run these callbacks without holding the 544 1.2 christos * lock */ 545 1.2 christos EVBUFFER_LOCK(buffer); 546 1.2 christos parent = buffer->parent; 547 1.2 christos evbuffer_run_callbacks(buffer, 1); 548 1.4 christos evbuffer_decref_and_unlock_(buffer); 549 1.2 christos if (parent) 550 1.4 christos bufferevent_decref_(parent); 551 1.1 plunky } 552 1.1 plunky 553 1.1 plunky static void 554 1.2 christos evbuffer_remove_all_callbacks(struct evbuffer *buffer) 555 1.1 plunky { 556 1.2 christos struct evbuffer_cb_entry *cbent; 557 1.2 christos 558 1.4 christos while ((cbent = LIST_FIRST(&buffer->callbacks))) { 559 1.4 christos LIST_REMOVE(cbent, next); 560 1.4 christos mm_free(cbent); 561 1.2 christos } 562 1.1 plunky } 563 1.1 plunky 564 1.2 christos void 565 1.4 christos evbuffer_decref_and_unlock_(struct evbuffer *buffer) 566 1.2 christos { 567 1.2 christos struct evbuffer_chain *chain, *next; 568 1.2 christos ASSERT_EVBUFFER_LOCKED(buffer); 569 1.1 plunky 570 1.2 christos EVUTIL_ASSERT(buffer->refcnt > 0); 571 1.1 plunky 572 1.2 christos if (--buffer->refcnt > 0) { 573 1.2 christos EVBUFFER_UNLOCK(buffer); 574 1.2 christos return; 575 1.2 christos } 576 1.1 plunky 577 1.2 christos for (chain = buffer->first; chain != NULL; chain = next) { 578 1.2 christos next = chain->next; 579 1.2 christos evbuffer_chain_free(chain); 580 1.2 christos } 581 1.2 christos evbuffer_remove_all_callbacks(buffer); 582 1.2 christos if (buffer->deferred_cbs) 583 1.4 christos event_deferred_cb_cancel_(buffer->cb_queue, &buffer->deferred); 584 1.2 christos 585 1.2 christos EVBUFFER_UNLOCK(buffer); 586 1.2 christos if (buffer->own_lock) 587 1.2 christos EVTHREAD_FREE_LOCK(buffer->lock, EVTHREAD_LOCKTYPE_RECURSIVE); 588 1.2 christos mm_free(buffer); 589 1.2 christos } 590 1.1 plunky 591 1.2 christos void 592 1.2 christos evbuffer_free(struct evbuffer *buffer) 593 1.2 christos { 594 1.2 christos EVBUFFER_LOCK(buffer); 595 1.4 christos evbuffer_decref_and_unlock_(buffer); 596 1.2 christos } 597 1.1 plunky 598 1.2 christos void 599 1.2 christos evbuffer_lock(struct evbuffer *buf) 600 1.2 christos { 601 1.2 christos EVBUFFER_LOCK(buf); 602 1.2 christos } 603 1.1 plunky 604 1.2 christos void 605 1.2 christos evbuffer_unlock(struct evbuffer *buf) 606 1.2 christos { 607 1.2 christos EVBUFFER_UNLOCK(buf); 608 1.1 plunky } 609 1.1 plunky 610 1.2 christos size_t 611 1.2 christos evbuffer_get_length(const struct evbuffer *buffer) 612 1.1 plunky { 613 1.2 christos size_t result; 614 1.2 christos 615 1.2 christos EVBUFFER_LOCK(buffer); 616 1.1 plunky 617 1.2 christos result = (buffer->total_len); 618 1.2 christos 619 1.2 christos EVBUFFER_UNLOCK(buffer); 620 1.2 christos 621 1.2 christos return result; 622 1.2 christos } 623 1.1 plunky 624 1.2 christos size_t 625 1.2 christos evbuffer_get_contiguous_space(const struct evbuffer *buf) 626 1.2 christos { 627 1.2 christos struct evbuffer_chain *chain; 628 1.2 christos size_t result; 629 1.1 plunky 630 1.2 christos EVBUFFER_LOCK(buf); 631 1.2 christos chain = buf->first; 632 1.2 christos result = (chain != NULL ? chain->off : 0); 633 1.2 christos EVBUFFER_UNLOCK(buf); 634 1.1 plunky 635 1.2 christos return result; 636 1.1 plunky } 637 1.1 plunky 638 1.4 christos size_t 639 1.4 christos evbuffer_add_iovec(struct evbuffer * buf, struct evbuffer_iovec * vec, int n_vec) { 640 1.4 christos int n; 641 1.4 christos size_t res; 642 1.4 christos size_t to_alloc; 643 1.4 christos 644 1.4 christos EVBUFFER_LOCK(buf); 645 1.4 christos 646 1.4 christos res = to_alloc = 0; 647 1.4 christos 648 1.4 christos for (n = 0; n < n_vec; n++) { 649 1.4 christos to_alloc += vec[n].iov_len; 650 1.4 christos } 651 1.4 christos 652 1.4 christos if (evbuffer_expand_fast_(buf, to_alloc, 2) < 0) { 653 1.4 christos goto done; 654 1.4 christos } 655 1.4 christos 656 1.4 christos for (n = 0; n < n_vec; n++) { 657 1.4 christos /* XXX each 'add' call here does a bunch of setup that's 658 1.4 christos * obviated by evbuffer_expand_fast_, and some cleanup that we 659 1.4 christos * would like to do only once. Instead we should just extract 660 1.4 christos * the part of the code that's needed. */ 661 1.4 christos 662 1.4 christos if (evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len) < 0) { 663 1.4 christos goto done; 664 1.4 christos } 665 1.4 christos 666 1.4 christos res += vec[n].iov_len; 667 1.4 christos } 668 1.4 christos 669 1.4 christos done: 670 1.4 christos EVBUFFER_UNLOCK(buf); 671 1.4 christos return res; 672 1.4 christos } 673 1.4 christos 674 1.2 christos int 675 1.2 christos evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, 676 1.2 christos struct evbuffer_iovec *vec, int n_vecs) 677 1.1 plunky { 678 1.2 christos struct evbuffer_chain *chain, **chainp; 679 1.2 christos int n = -1; 680 1.1 plunky 681 1.2 christos EVBUFFER_LOCK(buf); 682 1.2 christos if (buf->freeze_end) 683 1.2 christos goto done; 684 1.2 christos if (n_vecs < 1) 685 1.1 plunky goto done; 686 1.2 christos if (n_vecs == 1) { 687 1.2 christos if ((chain = evbuffer_expand_singlechain(buf, size)) == NULL) 688 1.2 christos goto done; 689 1.2 christos 690 1.4 christos vec[0].iov_base = (void *)CHAIN_SPACE_PTR(chain); 691 1.4 christos vec[0].iov_len = (size_t)CHAIN_SPACE_LEN(chain); 692 1.2 christos EVUTIL_ASSERT(size<0 || (size_t)vec[0].iov_len >= (size_t)size); 693 1.2 christos n = 1; 694 1.2 christos } else { 695 1.4 christos if (evbuffer_expand_fast_(buf, size, n_vecs)<0) 696 1.2 christos goto done; 697 1.4 christos n = evbuffer_read_setup_vecs_(buf, size, vec, n_vecs, 698 1.2 christos &chainp, 0); 699 1.1 plunky } 700 1.1 plunky 701 1.2 christos done: 702 1.2 christos EVBUFFER_UNLOCK(buf); 703 1.2 christos return n; 704 1.2 christos 705 1.2 christos } 706 1.1 plunky 707 1.2 christos static int 708 1.2 christos advance_last_with_data(struct evbuffer *buf) 709 1.2 christos { 710 1.2 christos int n = 0; 711 1.5 christos struct evbuffer_chain **chainp = buf->last_with_datap; 712 1.5 christos 713 1.2 christos ASSERT_EVBUFFER_LOCKED(buf); 714 1.1 plunky 715 1.5 christos if (!*chainp) 716 1.2 christos return 0; 717 1.1 plunky 718 1.5 christos while ((*chainp)->next) { 719 1.5 christos chainp = &(*chainp)->next; 720 1.5 christos if ((*chainp)->off) 721 1.5 christos buf->last_with_datap = chainp; 722 1.2 christos ++n; 723 1.2 christos } 724 1.2 christos return n; 725 1.1 plunky } 726 1.1 plunky 727 1.1 plunky int 728 1.2 christos evbuffer_commit_space(struct evbuffer *buf, 729 1.2 christos struct evbuffer_iovec *vec, int n_vecs) 730 1.1 plunky { 731 1.2 christos struct evbuffer_chain *chain, **firstchainp, **chainp; 732 1.2 christos int result = -1; 733 1.2 christos size_t added = 0; 734 1.2 christos int i; 735 1.2 christos 736 1.2 christos EVBUFFER_LOCK(buf); 737 1.1 plunky 738 1.2 christos if (buf->freeze_end) 739 1.2 christos goto done; 740 1.2 christos if (n_vecs == 0) { 741 1.2 christos result = 0; 742 1.2 christos goto done; 743 1.2 christos } else if (n_vecs == 1 && 744 1.4 christos (buf->last && vec[0].iov_base == (void *)CHAIN_SPACE_PTR(buf->last))) { 745 1.2 christos /* The user only got or used one chain; it might not 746 1.2 christos * be the first one with space in it. */ 747 1.2 christos if ((size_t)vec[0].iov_len > (size_t)CHAIN_SPACE_LEN(buf->last)) 748 1.2 christos goto done; 749 1.2 christos buf->last->off += vec[0].iov_len; 750 1.2 christos added = vec[0].iov_len; 751 1.2 christos if (added) 752 1.2 christos advance_last_with_data(buf); 753 1.2 christos goto okay; 754 1.1 plunky } 755 1.1 plunky 756 1.2 christos /* Advance 'firstchain' to the first chain with space in it. */ 757 1.2 christos firstchainp = buf->last_with_datap; 758 1.2 christos if (!*firstchainp) 759 1.2 christos goto done; 760 1.2 christos if (CHAIN_SPACE_LEN(*firstchainp) == 0) { 761 1.2 christos firstchainp = &(*firstchainp)->next; 762 1.2 christos } 763 1.1 plunky 764 1.2 christos chain = *firstchainp; 765 1.2 christos /* pass 1: make sure that the pointers and lengths of vecs[] are in 766 1.2 christos * bounds before we try to commit anything. */ 767 1.2 christos for (i=0; i<n_vecs; ++i) { 768 1.2 christos if (!chain) 769 1.2 christos goto done; 770 1.4 christos if (vec[i].iov_base != (void *)CHAIN_SPACE_PTR(chain) || 771 1.2 christos (size_t)vec[i].iov_len > CHAIN_SPACE_LEN(chain)) 772 1.2 christos goto done; 773 1.2 christos chain = chain->next; 774 1.2 christos } 775 1.2 christos /* pass 2: actually adjust all the chains. */ 776 1.2 christos chainp = firstchainp; 777 1.2 christos for (i=0; i<n_vecs; ++i) { 778 1.2 christos (*chainp)->off += vec[i].iov_len; 779 1.2 christos added += vec[i].iov_len; 780 1.2 christos if (vec[i].iov_len) { 781 1.2 christos buf->last_with_datap = chainp; 782 1.2 christos } 783 1.2 christos chainp = &(*chainp)->next; 784 1.2 christos } 785 1.1 plunky 786 1.2 christos okay: 787 1.2 christos buf->total_len += added; 788 1.2 christos buf->n_add_for_cb += added; 789 1.2 christos result = 0; 790 1.4 christos evbuffer_invoke_callbacks_(buf); 791 1.2 christos 792 1.2 christos done: 793 1.2 christos EVBUFFER_UNLOCK(buf); 794 1.2 christos return result; 795 1.2 christos } 796 1.1 plunky 797 1.2 christos static inline int 798 1.2 christos HAS_PINNED_R(struct evbuffer *buf) 799 1.2 christos { 800 1.2 christos return (buf->last && CHAIN_PINNED_R(buf->last)); 801 1.2 christos } 802 1.1 plunky 803 1.2 christos static inline void 804 1.2 christos ZERO_CHAIN(struct evbuffer *dst) 805 1.2 christos { 806 1.2 christos ASSERT_EVBUFFER_LOCKED(dst); 807 1.2 christos dst->first = NULL; 808 1.2 christos dst->last = NULL; 809 1.2 christos dst->last_with_datap = &(dst)->first; 810 1.2 christos dst->total_len = 0; 811 1.1 plunky } 812 1.1 plunky 813 1.2 christos /* Prepares the contents of src to be moved to another buffer by removing 814 1.2 christos * read-pinned chains. The first pinned chain is saved in first, and the 815 1.2 christos * last in last. If src has no read-pinned chains, first and last are set 816 1.2 christos * to NULL. */ 817 1.2 christos static int 818 1.2 christos PRESERVE_PINNED(struct evbuffer *src, struct evbuffer_chain **first, 819 1.2 christos struct evbuffer_chain **last) 820 1.1 plunky { 821 1.2 christos struct evbuffer_chain *chain, **pinned; 822 1.2 christos 823 1.2 christos ASSERT_EVBUFFER_LOCKED(src); 824 1.2 christos 825 1.2 christos if (!HAS_PINNED_R(src)) { 826 1.2 christos *first = *last = NULL; 827 1.2 christos return 0; 828 1.2 christos } 829 1.1 plunky 830 1.2 christos pinned = src->last_with_datap; 831 1.2 christos if (!CHAIN_PINNED_R(*pinned)) 832 1.2 christos pinned = &(*pinned)->next; 833 1.2 christos EVUTIL_ASSERT(CHAIN_PINNED_R(*pinned)); 834 1.2 christos chain = *first = *pinned; 835 1.2 christos *last = src->last; 836 1.2 christos 837 1.2 christos /* If there's data in the first pinned chain, we need to allocate 838 1.2 christos * a new chain and copy the data over. */ 839 1.2 christos if (chain->off) { 840 1.2 christos struct evbuffer_chain *tmp; 841 1.2 christos 842 1.2 christos EVUTIL_ASSERT(pinned == src->last_with_datap); 843 1.2 christos tmp = evbuffer_chain_new(chain->off); 844 1.2 christos if (!tmp) 845 1.2 christos return -1; 846 1.2 christos memcpy(tmp->buffer, chain->buffer + chain->misalign, 847 1.2 christos chain->off); 848 1.2 christos tmp->off = chain->off; 849 1.2 christos *src->last_with_datap = tmp; 850 1.2 christos src->last = tmp; 851 1.2 christos chain->misalign += chain->off; 852 1.2 christos chain->off = 0; 853 1.2 christos } else { 854 1.2 christos src->last = *src->last_with_datap; 855 1.2 christos *pinned = NULL; 856 1.2 christos } 857 1.1 plunky 858 1.2 christos return 0; 859 1.1 plunky } 860 1.1 plunky 861 1.2 christos static inline void 862 1.2 christos RESTORE_PINNED(struct evbuffer *src, struct evbuffer_chain *pinned, 863 1.2 christos struct evbuffer_chain *last) 864 1.1 plunky { 865 1.2 christos ASSERT_EVBUFFER_LOCKED(src); 866 1.1 plunky 867 1.2 christos if (!pinned) { 868 1.2 christos ZERO_CHAIN(src); 869 1.2 christos return; 870 1.1 plunky } 871 1.1 plunky 872 1.2 christos src->first = pinned; 873 1.2 christos src->last = last; 874 1.2 christos src->last_with_datap = &src->first; 875 1.2 christos src->total_len = 0; 876 1.1 plunky } 877 1.1 plunky 878 1.2 christos static inline void 879 1.2 christos COPY_CHAIN(struct evbuffer *dst, struct evbuffer *src) 880 1.1 plunky { 881 1.2 christos ASSERT_EVBUFFER_LOCKED(dst); 882 1.2 christos ASSERT_EVBUFFER_LOCKED(src); 883 1.2 christos dst->first = src->first; 884 1.2 christos if (src->last_with_datap == &src->first) 885 1.2 christos dst->last_with_datap = &dst->first; 886 1.2 christos else 887 1.2 christos dst->last_with_datap = src->last_with_datap; 888 1.2 christos dst->last = src->last; 889 1.2 christos dst->total_len = src->total_len; 890 1.2 christos } 891 1.2 christos 892 1.2 christos static void 893 1.2 christos APPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src) 894 1.2 christos { 895 1.4 christos struct evbuffer_chain **chp; 896 1.4 christos 897 1.2 christos ASSERT_EVBUFFER_LOCKED(dst); 898 1.2 christos ASSERT_EVBUFFER_LOCKED(src); 899 1.4 christos 900 1.4 christos chp = evbuffer_free_trailing_empty_chains(dst); 901 1.4 christos *chp = src->first; 902 1.4 christos 903 1.2 christos if (src->last_with_datap == &src->first) 904 1.4 christos dst->last_with_datap = chp; 905 1.2 christos else 906 1.2 christos dst->last_with_datap = src->last_with_datap; 907 1.2 christos dst->last = src->last; 908 1.2 christos dst->total_len += src->total_len; 909 1.2 christos } 910 1.2 christos 911 1.4 christos static inline void 912 1.4 christos APPEND_CHAIN_MULTICAST(struct evbuffer *dst, struct evbuffer *src) 913 1.4 christos { 914 1.4 christos struct evbuffer_chain *tmp; 915 1.4 christos struct evbuffer_chain *chain = src->first; 916 1.4 christos struct evbuffer_multicast_parent *extra; 917 1.4 christos 918 1.4 christos ASSERT_EVBUFFER_LOCKED(dst); 919 1.4 christos ASSERT_EVBUFFER_LOCKED(src); 920 1.4 christos 921 1.4 christos for (; chain; chain = chain->next) { 922 1.4 christos if (!chain->off || chain->flags & EVBUFFER_DANGLING) { 923 1.4 christos /* skip empty chains */ 924 1.4 christos continue; 925 1.4 christos } 926 1.4 christos 927 1.4 christos tmp = evbuffer_chain_new(sizeof(struct evbuffer_multicast_parent)); 928 1.4 christos if (!tmp) { 929 1.4 christos event_warn("%s: out of memory", __func__); 930 1.4 christos return; 931 1.4 christos } 932 1.4 christos extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_multicast_parent, tmp); 933 1.4 christos /* reference evbuffer containing source chain so it 934 1.4 christos * doesn't get released while the chain is still 935 1.4 christos * being referenced to */ 936 1.4 christos evbuffer_incref_(src); 937 1.4 christos extra->source = src; 938 1.4 christos /* reference source chain which now becomes immutable */ 939 1.4 christos evbuffer_chain_incref(chain); 940 1.4 christos extra->parent = chain; 941 1.4 christos chain->flags |= EVBUFFER_IMMUTABLE; 942 1.4 christos tmp->buffer_len = chain->buffer_len; 943 1.4 christos tmp->misalign = chain->misalign; 944 1.4 christos tmp->off = chain->off; 945 1.4 christos tmp->flags |= EVBUFFER_MULTICAST|EVBUFFER_IMMUTABLE; 946 1.4 christos tmp->buffer = chain->buffer; 947 1.4 christos evbuffer_chain_insert(dst, tmp); 948 1.4 christos } 949 1.4 christos } 950 1.4 christos 951 1.2 christos static void 952 1.2 christos PREPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src) 953 1.2 christos { 954 1.2 christos ASSERT_EVBUFFER_LOCKED(dst); 955 1.2 christos ASSERT_EVBUFFER_LOCKED(src); 956 1.2 christos src->last->next = dst->first; 957 1.2 christos dst->first = src->first; 958 1.2 christos dst->total_len += src->total_len; 959 1.2 christos if (*dst->last_with_datap == NULL) { 960 1.2 christos if (src->last_with_datap == &(src)->first) 961 1.2 christos dst->last_with_datap = &dst->first; 962 1.2 christos else 963 1.2 christos dst->last_with_datap = src->last_with_datap; 964 1.2 christos } else if (dst->last_with_datap == &dst->first) { 965 1.2 christos dst->last_with_datap = &src->last->next; 966 1.2 christos } 967 1.2 christos } 968 1.2 christos 969 1.2 christos int 970 1.2 christos evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) 971 1.2 christos { 972 1.2 christos struct evbuffer_chain *pinned, *last; 973 1.2 christos size_t in_total_len, out_total_len; 974 1.2 christos int result = 0; 975 1.2 christos 976 1.2 christos EVBUFFER_LOCK2(inbuf, outbuf); 977 1.2 christos in_total_len = inbuf->total_len; 978 1.2 christos out_total_len = outbuf->total_len; 979 1.2 christos 980 1.2 christos if (in_total_len == 0 || outbuf == inbuf) 981 1.2 christos goto done; 982 1.2 christos 983 1.2 christos if (outbuf->freeze_end || inbuf->freeze_start) { 984 1.2 christos result = -1; 985 1.2 christos goto done; 986 1.2 christos } 987 1.2 christos 988 1.2 christos if (PRESERVE_PINNED(inbuf, &pinned, &last) < 0) { 989 1.2 christos result = -1; 990 1.2 christos goto done; 991 1.2 christos } 992 1.2 christos 993 1.2 christos if (out_total_len == 0) { 994 1.2 christos /* There might be an empty chain at the start of outbuf; free 995 1.2 christos * it. */ 996 1.2 christos evbuffer_free_all_chains(outbuf->first); 997 1.2 christos COPY_CHAIN(outbuf, inbuf); 998 1.2 christos } else { 999 1.2 christos APPEND_CHAIN(outbuf, inbuf); 1000 1.2 christos } 1001 1.2 christos 1002 1.2 christos RESTORE_PINNED(inbuf, pinned, last); 1003 1.2 christos 1004 1.2 christos inbuf->n_del_for_cb += in_total_len; 1005 1.2 christos outbuf->n_add_for_cb += in_total_len; 1006 1.2 christos 1007 1.4 christos evbuffer_invoke_callbacks_(inbuf); 1008 1.4 christos evbuffer_invoke_callbacks_(outbuf); 1009 1.4 christos 1010 1.4 christos done: 1011 1.4 christos EVBUFFER_UNLOCK2(inbuf, outbuf); 1012 1.4 christos return result; 1013 1.4 christos } 1014 1.4 christos 1015 1.4 christos int 1016 1.4 christos evbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf) 1017 1.4 christos { 1018 1.4 christos size_t in_total_len, out_total_len; 1019 1.4 christos struct evbuffer_chain *chain; 1020 1.4 christos int result = 0; 1021 1.4 christos 1022 1.4 christos EVBUFFER_LOCK2(inbuf, outbuf); 1023 1.4 christos in_total_len = inbuf->total_len; 1024 1.4 christos out_total_len = outbuf->total_len; 1025 1.4 christos chain = inbuf->first; 1026 1.4 christos 1027 1.4 christos if (in_total_len == 0) 1028 1.4 christos goto done; 1029 1.4 christos 1030 1.4 christos if (outbuf->freeze_end || outbuf == inbuf) { 1031 1.4 christos result = -1; 1032 1.4 christos goto done; 1033 1.4 christos } 1034 1.4 christos 1035 1.4 christos for (; chain; chain = chain->next) { 1036 1.4 christos if ((chain->flags & (EVBUFFER_FILESEGMENT|EVBUFFER_SENDFILE|EVBUFFER_MULTICAST)) != 0) { 1037 1.4 christos /* chain type can not be referenced */ 1038 1.4 christos result = -1; 1039 1.4 christos goto done; 1040 1.4 christos } 1041 1.4 christos } 1042 1.4 christos 1043 1.4 christos if (out_total_len == 0) { 1044 1.4 christos /* There might be an empty chain at the start of outbuf; free 1045 1.4 christos * it. */ 1046 1.4 christos evbuffer_free_all_chains(outbuf->first); 1047 1.4 christos } 1048 1.4 christos APPEND_CHAIN_MULTICAST(outbuf, inbuf); 1049 1.4 christos 1050 1.4 christos outbuf->n_add_for_cb += in_total_len; 1051 1.4 christos evbuffer_invoke_callbacks_(outbuf); 1052 1.2 christos 1053 1.2 christos done: 1054 1.2 christos EVBUFFER_UNLOCK2(inbuf, outbuf); 1055 1.2 christos return result; 1056 1.2 christos } 1057 1.2 christos 1058 1.2 christos int 1059 1.2 christos evbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) 1060 1.2 christos { 1061 1.2 christos struct evbuffer_chain *pinned, *last; 1062 1.2 christos size_t in_total_len, out_total_len; 1063 1.2 christos int result = 0; 1064 1.2 christos 1065 1.2 christos EVBUFFER_LOCK2(inbuf, outbuf); 1066 1.2 christos 1067 1.2 christos in_total_len = inbuf->total_len; 1068 1.2 christos out_total_len = outbuf->total_len; 1069 1.2 christos 1070 1.2 christos if (!in_total_len || inbuf == outbuf) 1071 1.2 christos goto done; 1072 1.2 christos 1073 1.2 christos if (outbuf->freeze_start || inbuf->freeze_start) { 1074 1.2 christos result = -1; 1075 1.2 christos goto done; 1076 1.2 christos } 1077 1.2 christos 1078 1.2 christos if (PRESERVE_PINNED(inbuf, &pinned, &last) < 0) { 1079 1.2 christos result = -1; 1080 1.2 christos goto done; 1081 1.2 christos } 1082 1.2 christos 1083 1.2 christos if (out_total_len == 0) { 1084 1.2 christos /* There might be an empty chain at the start of outbuf; free 1085 1.2 christos * it. */ 1086 1.2 christos evbuffer_free_all_chains(outbuf->first); 1087 1.2 christos COPY_CHAIN(outbuf, inbuf); 1088 1.2 christos } else { 1089 1.2 christos PREPEND_CHAIN(outbuf, inbuf); 1090 1.2 christos } 1091 1.2 christos 1092 1.2 christos RESTORE_PINNED(inbuf, pinned, last); 1093 1.2 christos 1094 1.2 christos inbuf->n_del_for_cb += in_total_len; 1095 1.2 christos outbuf->n_add_for_cb += in_total_len; 1096 1.2 christos 1097 1.4 christos evbuffer_invoke_callbacks_(inbuf); 1098 1.4 christos evbuffer_invoke_callbacks_(outbuf); 1099 1.2 christos done: 1100 1.2 christos EVBUFFER_UNLOCK2(inbuf, outbuf); 1101 1.2 christos return result; 1102 1.2 christos } 1103 1.2 christos 1104 1.2 christos int 1105 1.2 christos evbuffer_drain(struct evbuffer *buf, size_t len) 1106 1.2 christos { 1107 1.2 christos struct evbuffer_chain *chain, *next; 1108 1.2 christos size_t remaining, old_len; 1109 1.2 christos int result = 0; 1110 1.2 christos 1111 1.2 christos EVBUFFER_LOCK(buf); 1112 1.2 christos old_len = buf->total_len; 1113 1.2 christos 1114 1.2 christos if (old_len == 0) 1115 1.2 christos goto done; 1116 1.2 christos 1117 1.2 christos if (buf->freeze_start) { 1118 1.2 christos result = -1; 1119 1.2 christos goto done; 1120 1.2 christos } 1121 1.2 christos 1122 1.2 christos if (len >= old_len && !HAS_PINNED_R(buf)) { 1123 1.2 christos len = old_len; 1124 1.2 christos for (chain = buf->first; chain != NULL; chain = next) { 1125 1.2 christos next = chain->next; 1126 1.2 christos evbuffer_chain_free(chain); 1127 1.2 christos } 1128 1.2 christos 1129 1.2 christos ZERO_CHAIN(buf); 1130 1.2 christos } else { 1131 1.2 christos if (len >= old_len) 1132 1.2 christos len = old_len; 1133 1.2 christos 1134 1.2 christos buf->total_len -= len; 1135 1.2 christos remaining = len; 1136 1.2 christos for (chain = buf->first; 1137 1.2 christos remaining >= chain->off; 1138 1.2 christos chain = next) { 1139 1.2 christos next = chain->next; 1140 1.2 christos remaining -= chain->off; 1141 1.2 christos 1142 1.2 christos if (chain == *buf->last_with_datap) { 1143 1.2 christos buf->last_with_datap = &buf->first; 1144 1.2 christos } 1145 1.2 christos if (&chain->next == buf->last_with_datap) 1146 1.2 christos buf->last_with_datap = &buf->first; 1147 1.2 christos 1148 1.2 christos if (CHAIN_PINNED_R(chain)) { 1149 1.2 christos EVUTIL_ASSERT(remaining == 0); 1150 1.2 christos chain->misalign += chain->off; 1151 1.2 christos chain->off = 0; 1152 1.2 christos break; 1153 1.2 christos } else 1154 1.2 christos evbuffer_chain_free(chain); 1155 1.2 christos } 1156 1.2 christos 1157 1.2 christos buf->first = chain; 1158 1.5 christos EVUTIL_ASSERT(remaining <= chain->off); 1159 1.4 christos chain->misalign += remaining; 1160 1.4 christos chain->off -= remaining; 1161 1.2 christos } 1162 1.2 christos 1163 1.2 christos buf->n_del_for_cb += len; 1164 1.2 christos /* Tell someone about changes in this buffer */ 1165 1.4 christos evbuffer_invoke_callbacks_(buf); 1166 1.2 christos 1167 1.2 christos done: 1168 1.2 christos EVBUFFER_UNLOCK(buf); 1169 1.2 christos return result; 1170 1.2 christos } 1171 1.2 christos 1172 1.2 christos /* Reads data from an event buffer and drains the bytes read */ 1173 1.2 christos int 1174 1.2 christos evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen) 1175 1.2 christos { 1176 1.2 christos ev_ssize_t n; 1177 1.2 christos EVBUFFER_LOCK(buf); 1178 1.4 christos n = evbuffer_copyout_from(buf, NULL, data_out, datlen); 1179 1.2 christos if (n > 0) { 1180 1.2 christos if (evbuffer_drain(buf, n)<0) 1181 1.2 christos n = -1; 1182 1.2 christos } 1183 1.2 christos EVBUFFER_UNLOCK(buf); 1184 1.2 christos return (int)n; 1185 1.2 christos } 1186 1.2 christos 1187 1.2 christos ev_ssize_t 1188 1.2 christos evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen) 1189 1.2 christos { 1190 1.4 christos return evbuffer_copyout_from(buf, NULL, data_out, datlen); 1191 1.4 christos } 1192 1.4 christos 1193 1.4 christos ev_ssize_t 1194 1.4 christos evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, 1195 1.4 christos void *data_out, size_t datlen) 1196 1.4 christos { 1197 1.2 christos /*XXX fails badly on sendfile case. */ 1198 1.2 christos struct evbuffer_chain *chain; 1199 1.2 christos char *data = data_out; 1200 1.2 christos size_t nread; 1201 1.2 christos ev_ssize_t result = 0; 1202 1.4 christos size_t pos_in_chain; 1203 1.2 christos 1204 1.2 christos EVBUFFER_LOCK(buf); 1205 1.2 christos 1206 1.4 christos if (pos) { 1207 1.4 christos if (datlen > (size_t)(EV_SSIZE_MAX - pos->pos)) { 1208 1.4 christos result = -1; 1209 1.4 christos goto done; 1210 1.4 christos } 1211 1.4 christos chain = pos->internal_.chain; 1212 1.4 christos pos_in_chain = pos->internal_.pos_in_chain; 1213 1.4 christos if (datlen + pos->pos > buf->total_len) 1214 1.4 christos datlen = buf->total_len - pos->pos; 1215 1.4 christos } else { 1216 1.4 christos chain = buf->first; 1217 1.4 christos pos_in_chain = 0; 1218 1.4 christos if (datlen > buf->total_len) 1219 1.4 christos datlen = buf->total_len; 1220 1.4 christos } 1221 1.2 christos 1222 1.2 christos 1223 1.2 christos if (datlen == 0) 1224 1.2 christos goto done; 1225 1.2 christos 1226 1.2 christos if (buf->freeze_start) { 1227 1.2 christos result = -1; 1228 1.2 christos goto done; 1229 1.2 christos } 1230 1.2 christos 1231 1.2 christos nread = datlen; 1232 1.2 christos 1233 1.4 christos while (datlen && datlen >= chain->off - pos_in_chain) { 1234 1.4 christos size_t copylen = chain->off - pos_in_chain; 1235 1.4 christos memcpy(data, 1236 1.4 christos chain->buffer + chain->misalign + pos_in_chain, 1237 1.4 christos copylen); 1238 1.4 christos data += copylen; 1239 1.4 christos datlen -= copylen; 1240 1.2 christos 1241 1.2 christos chain = chain->next; 1242 1.4 christos pos_in_chain = 0; 1243 1.2 christos EVUTIL_ASSERT(chain || datlen==0); 1244 1.2 christos } 1245 1.2 christos 1246 1.2 christos if (datlen) { 1247 1.2 christos EVUTIL_ASSERT(chain); 1248 1.4 christos EVUTIL_ASSERT(datlen+pos_in_chain <= chain->off); 1249 1.4 christos 1250 1.4 christos memcpy(data, chain->buffer + chain->misalign + pos_in_chain, 1251 1.4 christos datlen); 1252 1.2 christos } 1253 1.2 christos 1254 1.2 christos result = nread; 1255 1.2 christos done: 1256 1.2 christos EVBUFFER_UNLOCK(buf); 1257 1.2 christos return result; 1258 1.2 christos } 1259 1.2 christos 1260 1.2 christos /* reads data from the src buffer to the dst buffer, avoids memcpy as 1261 1.2 christos * possible. */ 1262 1.2 christos /* XXXX should return ev_ssize_t */ 1263 1.2 christos int 1264 1.2 christos evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, 1265 1.2 christos size_t datlen) 1266 1.2 christos { 1267 1.2 christos /*XXX We should have an option to force this to be zero-copy.*/ 1268 1.2 christos 1269 1.2 christos /*XXX can fail badly on sendfile case. */ 1270 1.2 christos struct evbuffer_chain *chain, *previous; 1271 1.2 christos size_t nread = 0; 1272 1.2 christos int result; 1273 1.2 christos 1274 1.2 christos EVBUFFER_LOCK2(src, dst); 1275 1.2 christos 1276 1.2 christos chain = previous = src->first; 1277 1.2 christos 1278 1.2 christos if (datlen == 0 || dst == src) { 1279 1.2 christos result = 0; 1280 1.2 christos goto done; 1281 1.2 christos } 1282 1.2 christos 1283 1.2 christos if (dst->freeze_end || src->freeze_start) { 1284 1.2 christos result = -1; 1285 1.2 christos goto done; 1286 1.2 christos } 1287 1.2 christos 1288 1.2 christos /* short-cut if there is no more data buffered */ 1289 1.2 christos if (datlen >= src->total_len) { 1290 1.2 christos datlen = src->total_len; 1291 1.2 christos evbuffer_add_buffer(dst, src); 1292 1.2 christos result = (int)datlen; /*XXXX should return ev_ssize_t*/ 1293 1.2 christos goto done; 1294 1.2 christos } 1295 1.2 christos 1296 1.2 christos /* removes chains if possible */ 1297 1.2 christos while (chain->off <= datlen) { 1298 1.2 christos /* We can't remove the last with data from src unless we 1299 1.2 christos * remove all chains, in which case we would have done the if 1300 1.2 christos * block above */ 1301 1.2 christos EVUTIL_ASSERT(chain != *src->last_with_datap); 1302 1.2 christos nread += chain->off; 1303 1.2 christos datlen -= chain->off; 1304 1.2 christos previous = chain; 1305 1.2 christos if (src->last_with_datap == &chain->next) 1306 1.2 christos src->last_with_datap = &src->first; 1307 1.2 christos chain = chain->next; 1308 1.2 christos } 1309 1.2 christos 1310 1.5 christos if (chain != src->first) { 1311 1.2 christos /* we can remove the chain */ 1312 1.2 christos struct evbuffer_chain **chp; 1313 1.2 christos chp = evbuffer_free_trailing_empty_chains(dst); 1314 1.2 christos 1315 1.2 christos if (dst->first == NULL) { 1316 1.2 christos dst->first = src->first; 1317 1.2 christos } else { 1318 1.2 christos *chp = src->first; 1319 1.2 christos } 1320 1.2 christos dst->last = previous; 1321 1.2 christos previous->next = NULL; 1322 1.2 christos src->first = chain; 1323 1.2 christos advance_last_with_data(dst); 1324 1.2 christos 1325 1.2 christos dst->total_len += nread; 1326 1.2 christos dst->n_add_for_cb += nread; 1327 1.2 christos } 1328 1.2 christos 1329 1.2 christos /* we know that there is more data in the src buffer than 1330 1.2 christos * we want to read, so we manually drain the chain */ 1331 1.2 christos evbuffer_add(dst, chain->buffer + chain->misalign, datlen); 1332 1.2 christos chain->misalign += datlen; 1333 1.2 christos chain->off -= datlen; 1334 1.2 christos nread += datlen; 1335 1.2 christos 1336 1.2 christos /* You might think we would want to increment dst->n_add_for_cb 1337 1.2 christos * here too. But evbuffer_add above already took care of that. 1338 1.2 christos */ 1339 1.2 christos src->total_len -= nread; 1340 1.2 christos src->n_del_for_cb += nread; 1341 1.2 christos 1342 1.2 christos if (nread) { 1343 1.4 christos evbuffer_invoke_callbacks_(dst); 1344 1.4 christos evbuffer_invoke_callbacks_(src); 1345 1.2 christos } 1346 1.2 christos result = (int)nread;/*XXXX should change return type */ 1347 1.2 christos 1348 1.2 christos done: 1349 1.2 christos EVBUFFER_UNLOCK2(src, dst); 1350 1.2 christos return result; 1351 1.2 christos } 1352 1.2 christos 1353 1.2 christos unsigned char * 1354 1.2 christos evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) 1355 1.2 christos { 1356 1.2 christos struct evbuffer_chain *chain, *next, *tmp, *last_with_data; 1357 1.2 christos unsigned char *buffer, *result = NULL; 1358 1.2 christos ev_ssize_t remaining; 1359 1.2 christos int removed_last_with_data = 0; 1360 1.2 christos int removed_last_with_datap = 0; 1361 1.2 christos 1362 1.2 christos EVBUFFER_LOCK(buf); 1363 1.2 christos 1364 1.2 christos chain = buf->first; 1365 1.2 christos 1366 1.2 christos if (size < 0) 1367 1.2 christos size = buf->total_len; 1368 1.2 christos /* if size > buf->total_len, we cannot guarantee to the user that she 1369 1.2 christos * is going to have a long enough buffer afterwards; so we return 1370 1.2 christos * NULL */ 1371 1.2 christos if (size == 0 || (size_t)size > buf->total_len) 1372 1.2 christos goto done; 1373 1.2 christos 1374 1.2 christos /* No need to pull up anything; the first size bytes are 1375 1.2 christos * already here. */ 1376 1.2 christos if (chain->off >= (size_t)size) { 1377 1.2 christos result = chain->buffer + chain->misalign; 1378 1.2 christos goto done; 1379 1.2 christos } 1380 1.2 christos 1381 1.2 christos /* Make sure that none of the chains we need to copy from is pinned. */ 1382 1.2 christos remaining = size - chain->off; 1383 1.2 christos EVUTIL_ASSERT(remaining >= 0); 1384 1.2 christos for (tmp=chain->next; tmp; tmp=tmp->next) { 1385 1.2 christos if (CHAIN_PINNED(tmp)) 1386 1.2 christos goto done; 1387 1.2 christos if (tmp->off >= (size_t)remaining) 1388 1.2 christos break; 1389 1.2 christos remaining -= tmp->off; 1390 1.2 christos } 1391 1.2 christos 1392 1.2 christos if (CHAIN_PINNED(chain)) { 1393 1.2 christos size_t old_off = chain->off; 1394 1.2 christos if (CHAIN_SPACE_LEN(chain) < size - chain->off) { 1395 1.2 christos /* not enough room at end of chunk. */ 1396 1.2 christos goto done; 1397 1.2 christos } 1398 1.2 christos buffer = CHAIN_SPACE_PTR(chain); 1399 1.2 christos tmp = chain; 1400 1.2 christos tmp->off = size; 1401 1.2 christos size -= old_off; 1402 1.2 christos chain = chain->next; 1403 1.2 christos } else if (chain->buffer_len - chain->misalign >= (size_t)size) { 1404 1.2 christos /* already have enough space in the first chain */ 1405 1.2 christos size_t old_off = chain->off; 1406 1.2 christos buffer = chain->buffer + chain->misalign + chain->off; 1407 1.2 christos tmp = chain; 1408 1.2 christos tmp->off = size; 1409 1.2 christos size -= old_off; 1410 1.2 christos chain = chain->next; 1411 1.2 christos } else { 1412 1.2 christos if ((tmp = evbuffer_chain_new(size)) == NULL) { 1413 1.2 christos event_warn("%s: out of memory", __func__); 1414 1.2 christos goto done; 1415 1.2 christos } 1416 1.2 christos buffer = tmp->buffer; 1417 1.2 christos tmp->off = size; 1418 1.2 christos buf->first = tmp; 1419 1.2 christos } 1420 1.2 christos 1421 1.2 christos /* TODO(niels): deal with buffers that point to NULL like sendfile */ 1422 1.2 christos 1423 1.2 christos /* Copy and free every chunk that will be entirely pulled into tmp */ 1424 1.2 christos last_with_data = *buf->last_with_datap; 1425 1.2 christos for (; chain != NULL && (size_t)size >= chain->off; chain = next) { 1426 1.2 christos next = chain->next; 1427 1.2 christos 1428 1.5 christos if (chain->buffer) { 1429 1.5 christos memcpy(buffer, chain->buffer + chain->misalign, chain->off); 1430 1.5 christos size -= chain->off; 1431 1.5 christos buffer += chain->off; 1432 1.5 christos } 1433 1.2 christos if (chain == last_with_data) 1434 1.2 christos removed_last_with_data = 1; 1435 1.2 christos if (&chain->next == buf->last_with_datap) 1436 1.2 christos removed_last_with_datap = 1; 1437 1.2 christos 1438 1.2 christos evbuffer_chain_free(chain); 1439 1.2 christos } 1440 1.2 christos 1441 1.2 christos if (chain != NULL) { 1442 1.2 christos memcpy(buffer, chain->buffer + chain->misalign, size); 1443 1.2 christos chain->misalign += size; 1444 1.2 christos chain->off -= size; 1445 1.2 christos } else { 1446 1.2 christos buf->last = tmp; 1447 1.2 christos } 1448 1.2 christos 1449 1.2 christos tmp->next = chain; 1450 1.2 christos 1451 1.2 christos if (removed_last_with_data) { 1452 1.2 christos buf->last_with_datap = &buf->first; 1453 1.2 christos } else if (removed_last_with_datap) { 1454 1.2 christos if (buf->first->next && buf->first->next->off) 1455 1.2 christos buf->last_with_datap = &buf->first->next; 1456 1.2 christos else 1457 1.2 christos buf->last_with_datap = &buf->first; 1458 1.2 christos } 1459 1.2 christos 1460 1.2 christos result = (tmp->buffer + tmp->misalign); 1461 1.2 christos 1462 1.2 christos done: 1463 1.2 christos EVBUFFER_UNLOCK(buf); 1464 1.2 christos return result; 1465 1.2 christos } 1466 1.2 christos 1467 1.2 christos /* 1468 1.2 christos * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. 1469 1.2 christos * The returned buffer needs to be freed by the called. 1470 1.2 christos */ 1471 1.2 christos char * 1472 1.2 christos evbuffer_readline(struct evbuffer *buffer) 1473 1.2 christos { 1474 1.2 christos return evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY); 1475 1.2 christos } 1476 1.2 christos 1477 1.2 christos static inline ev_ssize_t 1478 1.2 christos evbuffer_strchr(struct evbuffer_ptr *it, const char chr) 1479 1.2 christos { 1480 1.4 christos struct evbuffer_chain *chain = it->internal_.chain; 1481 1.4 christos size_t i = it->internal_.pos_in_chain; 1482 1.2 christos while (chain != NULL) { 1483 1.2 christos char *buffer = (char *)chain->buffer + chain->misalign; 1484 1.2 christos char *cp = memchr(buffer+i, chr, chain->off-i); 1485 1.2 christos if (cp) { 1486 1.4 christos it->internal_.chain = chain; 1487 1.4 christos it->internal_.pos_in_chain = cp - buffer; 1488 1.2 christos it->pos += (cp - buffer - i); 1489 1.2 christos return it->pos; 1490 1.2 christos } 1491 1.2 christos it->pos += chain->off - i; 1492 1.2 christos i = 0; 1493 1.2 christos chain = chain->next; 1494 1.2 christos } 1495 1.2 christos 1496 1.2 christos return (-1); 1497 1.2 christos } 1498 1.2 christos 1499 1.2 christos static inline char * 1500 1.2 christos find_eol_char(char *s, size_t len) 1501 1.2 christos { 1502 1.2 christos #define CHUNK_SZ 128 1503 1.2 christos /* Lots of benchmarking found this approach to be faster in practice 1504 1.2 christos * than doing two memchrs over the whole buffer, doin a memchr on each 1505 1.2 christos * char of the buffer, or trying to emulate memchr by hand. */ 1506 1.2 christos char *s_end, *cr, *lf; 1507 1.2 christos s_end = s+len; 1508 1.2 christos while (s < s_end) { 1509 1.2 christos size_t chunk = (s + CHUNK_SZ < s_end) ? CHUNK_SZ : (s_end - s); 1510 1.2 christos cr = memchr(s, '\r', chunk); 1511 1.2 christos lf = memchr(s, '\n', chunk); 1512 1.2 christos if (cr) { 1513 1.2 christos if (lf && lf < cr) 1514 1.2 christos return lf; 1515 1.2 christos return cr; 1516 1.2 christos } else if (lf) { 1517 1.2 christos return lf; 1518 1.2 christos } 1519 1.2 christos s += CHUNK_SZ; 1520 1.2 christos } 1521 1.2 christos 1522 1.2 christos return NULL; 1523 1.2 christos #undef CHUNK_SZ 1524 1.2 christos } 1525 1.2 christos 1526 1.2 christos static ev_ssize_t 1527 1.2 christos evbuffer_find_eol_char(struct evbuffer_ptr *it) 1528 1.2 christos { 1529 1.4 christos struct evbuffer_chain *chain = it->internal_.chain; 1530 1.4 christos size_t i = it->internal_.pos_in_chain; 1531 1.2 christos while (chain != NULL) { 1532 1.2 christos char *buffer = (char *)chain->buffer + chain->misalign; 1533 1.2 christos char *cp = find_eol_char(buffer+i, chain->off-i); 1534 1.2 christos if (cp) { 1535 1.4 christos it->internal_.chain = chain; 1536 1.4 christos it->internal_.pos_in_chain = cp - buffer; 1537 1.2 christos it->pos += (cp - buffer) - i; 1538 1.2 christos return it->pos; 1539 1.2 christos } 1540 1.2 christos it->pos += chain->off - i; 1541 1.2 christos i = 0; 1542 1.2 christos chain = chain->next; 1543 1.2 christos } 1544 1.2 christos 1545 1.2 christos return (-1); 1546 1.2 christos } 1547 1.2 christos 1548 1.5 christos static inline size_t 1549 1.2 christos evbuffer_strspn( 1550 1.2 christos struct evbuffer_ptr *ptr, const char *chrset) 1551 1.2 christos { 1552 1.5 christos size_t count = 0; 1553 1.4 christos struct evbuffer_chain *chain = ptr->internal_.chain; 1554 1.4 christos size_t i = ptr->internal_.pos_in_chain; 1555 1.2 christos 1556 1.2 christos if (!chain) 1557 1.4 christos return 0; 1558 1.2 christos 1559 1.2 christos while (1) { 1560 1.2 christos char *buffer = (char *)chain->buffer + chain->misalign; 1561 1.2 christos for (; i < chain->off; ++i) { 1562 1.2 christos const char *p = chrset; 1563 1.2 christos while (*p) { 1564 1.2 christos if (buffer[i] == *p++) 1565 1.2 christos goto next; 1566 1.2 christos } 1567 1.4 christos ptr->internal_.chain = chain; 1568 1.4 christos ptr->internal_.pos_in_chain = i; 1569 1.2 christos ptr->pos += count; 1570 1.2 christos return count; 1571 1.2 christos next: 1572 1.2 christos ++count; 1573 1.2 christos } 1574 1.2 christos i = 0; 1575 1.2 christos 1576 1.2 christos if (! chain->next) { 1577 1.4 christos ptr->internal_.chain = chain; 1578 1.4 christos ptr->internal_.pos_in_chain = i; 1579 1.2 christos ptr->pos += count; 1580 1.2 christos return count; 1581 1.2 christos } 1582 1.2 christos 1583 1.2 christos chain = chain->next; 1584 1.2 christos } 1585 1.2 christos } 1586 1.2 christos 1587 1.2 christos 1588 1.4 christos static inline int 1589 1.2 christos evbuffer_getchr(struct evbuffer_ptr *it) 1590 1.2 christos { 1591 1.4 christos struct evbuffer_chain *chain = it->internal_.chain; 1592 1.4 christos size_t off = it->internal_.pos_in_chain; 1593 1.4 christos 1594 1.4 christos if (chain == NULL) 1595 1.4 christos return -1; 1596 1.2 christos 1597 1.4 christos return (unsigned char)chain->buffer[chain->misalign + off]; 1598 1.2 christos } 1599 1.2 christos 1600 1.2 christos struct evbuffer_ptr 1601 1.2 christos evbuffer_search_eol(struct evbuffer *buffer, 1602 1.2 christos struct evbuffer_ptr *start, size_t *eol_len_out, 1603 1.2 christos enum evbuffer_eol_style eol_style) 1604 1.2 christos { 1605 1.2 christos struct evbuffer_ptr it, it2; 1606 1.2 christos size_t extra_drain = 0; 1607 1.2 christos int ok = 0; 1608 1.2 christos 1609 1.4 christos /* Avoid locking in trivial edge cases */ 1610 1.4 christos if (start && start->internal_.chain == NULL) { 1611 1.4 christos PTR_NOT_FOUND(&it); 1612 1.4 christos if (eol_len_out) 1613 1.4 christos *eol_len_out = extra_drain; 1614 1.4 christos return it; 1615 1.4 christos } 1616 1.4 christos 1617 1.2 christos EVBUFFER_LOCK(buffer); 1618 1.2 christos 1619 1.2 christos if (start) { 1620 1.2 christos memcpy(&it, start, sizeof(it)); 1621 1.2 christos } else { 1622 1.2 christos it.pos = 0; 1623 1.4 christos it.internal_.chain = buffer->first; 1624 1.4 christos it.internal_.pos_in_chain = 0; 1625 1.2 christos } 1626 1.2 christos 1627 1.2 christos /* the eol_style determines our first stop character and how many 1628 1.2 christos * characters we are going to drain afterwards. */ 1629 1.2 christos switch (eol_style) { 1630 1.2 christos case EVBUFFER_EOL_ANY: 1631 1.2 christos if (evbuffer_find_eol_char(&it) < 0) 1632 1.2 christos goto done; 1633 1.2 christos memcpy(&it2, &it, sizeof(it)); 1634 1.2 christos extra_drain = evbuffer_strspn(&it2, "\r\n"); 1635 1.2 christos break; 1636 1.2 christos case EVBUFFER_EOL_CRLF_STRICT: { 1637 1.2 christos it = evbuffer_search(buffer, "\r\n", 2, &it); 1638 1.2 christos if (it.pos < 0) 1639 1.2 christos goto done; 1640 1.2 christos extra_drain = 2; 1641 1.2 christos break; 1642 1.2 christos } 1643 1.4 christos case EVBUFFER_EOL_CRLF: { 1644 1.4 christos ev_ssize_t start_pos = it.pos; 1645 1.4 christos /* Look for a LF ... */ 1646 1.4 christos if (evbuffer_strchr(&it, '\n') < 0) 1647 1.4 christos goto done; 1648 1.4 christos extra_drain = 1; 1649 1.4 christos /* ... optionally preceeded by a CR. */ 1650 1.4 christos if (it.pos == start_pos) 1651 1.4 christos break; /* If the first character is \n, don't back up */ 1652 1.4 christos /* This potentially does an extra linear walk over the first 1653 1.4 christos * few chains. Probably, that's not too expensive unless you 1654 1.4 christos * have a really pathological setup. */ 1655 1.4 christos memcpy(&it2, &it, sizeof(it)); 1656 1.4 christos if (evbuffer_ptr_subtract(buffer, &it2, 1)<0) 1657 1.4 christos break; 1658 1.4 christos if (evbuffer_getchr(&it2) == '\r') { 1659 1.4 christos memcpy(&it, &it2, sizeof(it)); 1660 1.4 christos extra_drain = 2; 1661 1.2 christos } 1662 1.2 christos break; 1663 1.4 christos } 1664 1.2 christos case EVBUFFER_EOL_LF: 1665 1.2 christos if (evbuffer_strchr(&it, '\n') < 0) 1666 1.2 christos goto done; 1667 1.2 christos extra_drain = 1; 1668 1.2 christos break; 1669 1.4 christos case EVBUFFER_EOL_NUL: 1670 1.4 christos if (evbuffer_strchr(&it, '\0') < 0) 1671 1.4 christos goto done; 1672 1.4 christos extra_drain = 1; 1673 1.4 christos break; 1674 1.2 christos default: 1675 1.2 christos goto done; 1676 1.2 christos } 1677 1.2 christos 1678 1.2 christos ok = 1; 1679 1.2 christos done: 1680 1.2 christos EVBUFFER_UNLOCK(buffer); 1681 1.2 christos 1682 1.4 christos if (!ok) 1683 1.4 christos PTR_NOT_FOUND(&it); 1684 1.2 christos if (eol_len_out) 1685 1.2 christos *eol_len_out = extra_drain; 1686 1.2 christos 1687 1.2 christos return it; 1688 1.2 christos } 1689 1.2 christos 1690 1.2 christos char * 1691 1.2 christos evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, 1692 1.2 christos enum evbuffer_eol_style eol_style) 1693 1.2 christos { 1694 1.2 christos struct evbuffer_ptr it; 1695 1.2 christos char *line; 1696 1.2 christos size_t n_to_copy=0, extra_drain=0; 1697 1.2 christos char *result = NULL; 1698 1.2 christos 1699 1.2 christos EVBUFFER_LOCK(buffer); 1700 1.2 christos 1701 1.2 christos if (buffer->freeze_start) { 1702 1.2 christos goto done; 1703 1.2 christos } 1704 1.2 christos 1705 1.2 christos it = evbuffer_search_eol(buffer, NULL, &extra_drain, eol_style); 1706 1.2 christos if (it.pos < 0) 1707 1.2 christos goto done; 1708 1.2 christos n_to_copy = it.pos; 1709 1.2 christos 1710 1.2 christos if ((line = mm_malloc(n_to_copy+1)) == NULL) { 1711 1.2 christos event_warn("%s: out of memory", __func__); 1712 1.2 christos goto done; 1713 1.2 christos } 1714 1.2 christos 1715 1.2 christos evbuffer_remove(buffer, line, n_to_copy); 1716 1.2 christos line[n_to_copy] = '\0'; 1717 1.2 christos 1718 1.2 christos evbuffer_drain(buffer, extra_drain); 1719 1.2 christos result = line; 1720 1.2 christos done: 1721 1.2 christos EVBUFFER_UNLOCK(buffer); 1722 1.2 christos 1723 1.2 christos if (n_read_out) 1724 1.2 christos *n_read_out = result ? n_to_copy : 0; 1725 1.2 christos 1726 1.2 christos return result; 1727 1.2 christos } 1728 1.2 christos 1729 1.2 christos #define EVBUFFER_CHAIN_MAX_AUTO_SIZE 4096 1730 1.2 christos 1731 1.2 christos /* Adds data to an event buffer */ 1732 1.2 christos 1733 1.2 christos int 1734 1.2 christos evbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen) 1735 1.2 christos { 1736 1.2 christos struct evbuffer_chain *chain, *tmp; 1737 1.2 christos const unsigned char *data = data_in; 1738 1.2 christos size_t remain, to_alloc; 1739 1.2 christos int result = -1; 1740 1.2 christos 1741 1.2 christos EVBUFFER_LOCK(buf); 1742 1.2 christos 1743 1.2 christos if (buf->freeze_end) { 1744 1.2 christos goto done; 1745 1.2 christos } 1746 1.3 spz /* Prevent buf->total_len overflow */ 1747 1.3 spz if (datlen > EV_SIZE_MAX - buf->total_len) { 1748 1.3 spz goto done; 1749 1.3 spz } 1750 1.2 christos 1751 1.4 christos if (*buf->last_with_datap == NULL) { 1752 1.4 christos chain = buf->last; 1753 1.4 christos } else { 1754 1.4 christos chain = *buf->last_with_datap; 1755 1.4 christos } 1756 1.2 christos 1757 1.2 christos /* If there are no chains allocated for this buffer, allocate one 1758 1.2 christos * big enough to hold all the data. */ 1759 1.2 christos if (chain == NULL) { 1760 1.2 christos chain = evbuffer_chain_new(datlen); 1761 1.2 christos if (!chain) 1762 1.2 christos goto done; 1763 1.2 christos evbuffer_chain_insert(buf, chain); 1764 1.2 christos } 1765 1.2 christos 1766 1.2 christos if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) { 1767 1.3 spz /* Always true for mutable buffers */ 1768 1.3 spz EVUTIL_ASSERT(chain->misalign >= 0 && 1769 1.3 spz (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX); 1770 1.3 spz remain = chain->buffer_len - (size_t)chain->misalign - chain->off; 1771 1.2 christos if (remain >= datlen) { 1772 1.2 christos /* there's enough space to hold all the data in the 1773 1.2 christos * current last chain */ 1774 1.2 christos memcpy(chain->buffer + chain->misalign + chain->off, 1775 1.2 christos data, datlen); 1776 1.2 christos chain->off += datlen; 1777 1.2 christos buf->total_len += datlen; 1778 1.2 christos buf->n_add_for_cb += datlen; 1779 1.2 christos goto out; 1780 1.2 christos } else if (!CHAIN_PINNED(chain) && 1781 1.2 christos evbuffer_chain_should_realign(chain, datlen)) { 1782 1.2 christos /* we can fit the data into the misalignment */ 1783 1.2 christos evbuffer_chain_align(chain); 1784 1.2 christos 1785 1.2 christos memcpy(chain->buffer + chain->off, data, datlen); 1786 1.2 christos chain->off += datlen; 1787 1.2 christos buf->total_len += datlen; 1788 1.2 christos buf->n_add_for_cb += datlen; 1789 1.2 christos goto out; 1790 1.2 christos } 1791 1.2 christos } else { 1792 1.2 christos /* we cannot write any data to the last chain */ 1793 1.2 christos remain = 0; 1794 1.2 christos } 1795 1.2 christos 1796 1.2 christos /* we need to add another chain */ 1797 1.2 christos to_alloc = chain->buffer_len; 1798 1.2 christos if (to_alloc <= EVBUFFER_CHAIN_MAX_AUTO_SIZE/2) 1799 1.2 christos to_alloc <<= 1; 1800 1.2 christos if (datlen > to_alloc) 1801 1.2 christos to_alloc = datlen; 1802 1.2 christos tmp = evbuffer_chain_new(to_alloc); 1803 1.2 christos if (tmp == NULL) 1804 1.2 christos goto done; 1805 1.2 christos 1806 1.2 christos if (remain) { 1807 1.2 christos memcpy(chain->buffer + chain->misalign + chain->off, 1808 1.2 christos data, remain); 1809 1.2 christos chain->off += remain; 1810 1.2 christos buf->total_len += remain; 1811 1.2 christos buf->n_add_for_cb += remain; 1812 1.2 christos } 1813 1.2 christos 1814 1.2 christos data += remain; 1815 1.2 christos datlen -= remain; 1816 1.2 christos 1817 1.2 christos memcpy(tmp->buffer, data, datlen); 1818 1.2 christos tmp->off = datlen; 1819 1.2 christos evbuffer_chain_insert(buf, tmp); 1820 1.2 christos buf->n_add_for_cb += datlen; 1821 1.2 christos 1822 1.2 christos out: 1823 1.4 christos evbuffer_invoke_callbacks_(buf); 1824 1.2 christos result = 0; 1825 1.2 christos done: 1826 1.2 christos EVBUFFER_UNLOCK(buf); 1827 1.2 christos return result; 1828 1.2 christos } 1829 1.2 christos 1830 1.2 christos int 1831 1.2 christos evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen) 1832 1.2 christos { 1833 1.2 christos struct evbuffer_chain *chain, *tmp; 1834 1.2 christos int result = -1; 1835 1.2 christos 1836 1.2 christos EVBUFFER_LOCK(buf); 1837 1.2 christos 1838 1.5 christos if (datlen == 0) { 1839 1.5 christos result = 0; 1840 1.5 christos goto done; 1841 1.5 christos } 1842 1.2 christos if (buf->freeze_start) { 1843 1.2 christos goto done; 1844 1.2 christos } 1845 1.3 spz if (datlen > EV_SIZE_MAX - buf->total_len) { 1846 1.3 spz goto done; 1847 1.3 spz } 1848 1.2 christos 1849 1.2 christos chain = buf->first; 1850 1.2 christos 1851 1.2 christos if (chain == NULL) { 1852 1.2 christos chain = evbuffer_chain_new(datlen); 1853 1.2 christos if (!chain) 1854 1.2 christos goto done; 1855 1.2 christos evbuffer_chain_insert(buf, chain); 1856 1.2 christos } 1857 1.2 christos 1858 1.2 christos /* we cannot touch immutable buffers */ 1859 1.2 christos if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) { 1860 1.3 spz /* Always true for mutable buffers */ 1861 1.3 spz EVUTIL_ASSERT(chain->misalign >= 0 && 1862 1.3 spz (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX); 1863 1.3 spz 1864 1.2 christos /* If this chain is empty, we can treat it as 1865 1.2 christos * 'empty at the beginning' rather than 'empty at the end' */ 1866 1.2 christos if (chain->off == 0) 1867 1.2 christos chain->misalign = chain->buffer_len; 1868 1.2 christos 1869 1.2 christos if ((size_t)chain->misalign >= datlen) { 1870 1.2 christos /* we have enough space to fit everything */ 1871 1.2 christos memcpy(chain->buffer + chain->misalign - datlen, 1872 1.2 christos data, datlen); 1873 1.2 christos chain->off += datlen; 1874 1.2 christos chain->misalign -= datlen; 1875 1.2 christos buf->total_len += datlen; 1876 1.2 christos buf->n_add_for_cb += datlen; 1877 1.2 christos goto out; 1878 1.2 christos } else if (chain->misalign) { 1879 1.2 christos /* we can only fit some of the data. */ 1880 1.2 christos memcpy(chain->buffer, 1881 1.2 christos (const char*)data + datlen - chain->misalign, 1882 1.2 christos (size_t)chain->misalign); 1883 1.2 christos chain->off += (size_t)chain->misalign; 1884 1.2 christos buf->total_len += (size_t)chain->misalign; 1885 1.2 christos buf->n_add_for_cb += (size_t)chain->misalign; 1886 1.2 christos datlen -= (size_t)chain->misalign; 1887 1.2 christos chain->misalign = 0; 1888 1.2 christos } 1889 1.2 christos } 1890 1.2 christos 1891 1.2 christos /* we need to add another chain */ 1892 1.2 christos if ((tmp = evbuffer_chain_new(datlen)) == NULL) 1893 1.2 christos goto done; 1894 1.2 christos buf->first = tmp; 1895 1.5 christos if (buf->last_with_datap == &buf->first && chain->off) 1896 1.2 christos buf->last_with_datap = &tmp->next; 1897 1.2 christos 1898 1.2 christos tmp->next = chain; 1899 1.2 christos 1900 1.2 christos tmp->off = datlen; 1901 1.3 spz EVUTIL_ASSERT(datlen <= tmp->buffer_len); 1902 1.2 christos tmp->misalign = tmp->buffer_len - datlen; 1903 1.2 christos 1904 1.2 christos memcpy(tmp->buffer + tmp->misalign, data, datlen); 1905 1.2 christos buf->total_len += datlen; 1906 1.4 christos buf->n_add_for_cb += datlen; 1907 1.2 christos 1908 1.2 christos out: 1909 1.4 christos evbuffer_invoke_callbacks_(buf); 1910 1.2 christos result = 0; 1911 1.2 christos done: 1912 1.2 christos EVBUFFER_UNLOCK(buf); 1913 1.2 christos return result; 1914 1.2 christos } 1915 1.2 christos 1916 1.2 christos /** Helper: realigns the memory in chain->buffer so that misalign is 0. */ 1917 1.2 christos static void 1918 1.2 christos evbuffer_chain_align(struct evbuffer_chain *chain) 1919 1.2 christos { 1920 1.2 christos EVUTIL_ASSERT(!(chain->flags & EVBUFFER_IMMUTABLE)); 1921 1.2 christos EVUTIL_ASSERT(!(chain->flags & EVBUFFER_MEM_PINNED_ANY)); 1922 1.2 christos memmove(chain->buffer, chain->buffer + chain->misalign, chain->off); 1923 1.2 christos chain->misalign = 0; 1924 1.2 christos } 1925 1.2 christos 1926 1.2 christos #define MAX_TO_COPY_IN_EXPAND 4096 1927 1.2 christos #define MAX_TO_REALIGN_IN_EXPAND 2048 1928 1.2 christos 1929 1.2 christos /** Helper: return true iff we should realign chain to fit datalen bytes of 1930 1.2 christos data in it. */ 1931 1.2 christos static int 1932 1.2 christos evbuffer_chain_should_realign(struct evbuffer_chain *chain, 1933 1.2 christos size_t datlen) 1934 1.2 christos { 1935 1.2 christos return chain->buffer_len - chain->off >= datlen && 1936 1.2 christos (chain->off < chain->buffer_len / 2) && 1937 1.2 christos (chain->off <= MAX_TO_REALIGN_IN_EXPAND); 1938 1.2 christos } 1939 1.2 christos 1940 1.2 christos /* Expands the available space in the event buffer to at least datlen, all in 1941 1.2 christos * a single chunk. Return that chunk. */ 1942 1.2 christos static struct evbuffer_chain * 1943 1.2 christos evbuffer_expand_singlechain(struct evbuffer *buf, size_t datlen) 1944 1.2 christos { 1945 1.2 christos struct evbuffer_chain *chain, **chainp; 1946 1.2 christos struct evbuffer_chain *result = NULL; 1947 1.2 christos ASSERT_EVBUFFER_LOCKED(buf); 1948 1.2 christos 1949 1.2 christos chainp = buf->last_with_datap; 1950 1.2 christos 1951 1.2 christos /* XXX If *chainp is no longer writeable, but has enough space in its 1952 1.2 christos * misalign, this might be a bad idea: we could still use *chainp, not 1953 1.2 christos * (*chainp)->next. */ 1954 1.2 christos if (*chainp && CHAIN_SPACE_LEN(*chainp) == 0) 1955 1.2 christos chainp = &(*chainp)->next; 1956 1.2 christos 1957 1.2 christos /* 'chain' now points to the first chain with writable space (if any) 1958 1.2 christos * We will either use it, realign it, replace it, or resize it. */ 1959 1.2 christos chain = *chainp; 1960 1.2 christos 1961 1.2 christos if (chain == NULL || 1962 1.2 christos (chain->flags & (EVBUFFER_IMMUTABLE|EVBUFFER_MEM_PINNED_ANY))) { 1963 1.2 christos /* We can't use the last_with_data chain at all. Just add a 1964 1.2 christos * new one that's big enough. */ 1965 1.2 christos goto insert_new; 1966 1.2 christos } 1967 1.2 christos 1968 1.2 christos /* If we can fit all the data, then we don't have to do anything */ 1969 1.2 christos if (CHAIN_SPACE_LEN(chain) >= datlen) { 1970 1.2 christos result = chain; 1971 1.2 christos goto ok; 1972 1.2 christos } 1973 1.2 christos 1974 1.2 christos /* If the chain is completely empty, just replace it by adding a new 1975 1.2 christos * empty chain. */ 1976 1.2 christos if (chain->off == 0) { 1977 1.2 christos goto insert_new; 1978 1.2 christos } 1979 1.2 christos 1980 1.2 christos /* If the misalignment plus the remaining space fulfills our data 1981 1.2 christos * needs, we could just force an alignment to happen. Afterwards, we 1982 1.2 christos * have enough space. But only do this if we're saving a lot of space 1983 1.2 christos * and not moving too much data. Otherwise the space savings are 1984 1.2 christos * probably offset by the time lost in copying. 1985 1.2 christos */ 1986 1.2 christos if (evbuffer_chain_should_realign(chain, datlen)) { 1987 1.2 christos evbuffer_chain_align(chain); 1988 1.2 christos result = chain; 1989 1.2 christos goto ok; 1990 1.2 christos } 1991 1.2 christos 1992 1.2 christos /* At this point, we can either resize the last chunk with space in 1993 1.2 christos * it, use the next chunk after it, or If we add a new chunk, we waste 1994 1.2 christos * CHAIN_SPACE_LEN(chain) bytes in the former last chunk. If we 1995 1.2 christos * resize, we have to copy chain->off bytes. 1996 1.2 christos */ 1997 1.2 christos 1998 1.2 christos /* Would expanding this chunk be affordable and worthwhile? */ 1999 1.2 christos if (CHAIN_SPACE_LEN(chain) < chain->buffer_len / 8 || 2000 1.3 spz chain->off > MAX_TO_COPY_IN_EXPAND || 2001 1.4 christos datlen >= (EVBUFFER_CHAIN_MAX - chain->off)) { 2002 1.2 christos /* It's not worth resizing this chain. Can the next one be 2003 1.2 christos * used? */ 2004 1.2 christos if (chain->next && CHAIN_SPACE_LEN(chain->next) >= datlen) { 2005 1.2 christos /* Yes, we can just use the next chain (which should 2006 1.2 christos * be empty. */ 2007 1.2 christos result = chain->next; 2008 1.2 christos goto ok; 2009 1.2 christos } else { 2010 1.2 christos /* No; append a new chain (which will free all 2011 1.2 christos * terminal empty chains.) */ 2012 1.2 christos goto insert_new; 2013 1.2 christos } 2014 1.2 christos } else { 2015 1.2 christos /* Okay, we're going to try to resize this chain: Not doing so 2016 1.2 christos * would waste at least 1/8 of its current allocation, and we 2017 1.2 christos * can do so without having to copy more than 2018 1.2 christos * MAX_TO_COPY_IN_EXPAND bytes. */ 2019 1.2 christos /* figure out how much space we need */ 2020 1.2 christos size_t length = chain->off + datlen; 2021 1.2 christos struct evbuffer_chain *tmp = evbuffer_chain_new(length); 2022 1.2 christos if (tmp == NULL) 2023 1.2 christos goto err; 2024 1.2 christos 2025 1.2 christos /* copy the data over that we had so far */ 2026 1.2 christos tmp->off = chain->off; 2027 1.2 christos memcpy(tmp->buffer, chain->buffer + chain->misalign, 2028 1.2 christos chain->off); 2029 1.2 christos /* fix up the list */ 2030 1.2 christos EVUTIL_ASSERT(*chainp == chain); 2031 1.2 christos result = *chainp = tmp; 2032 1.2 christos 2033 1.2 christos if (buf->last == chain) 2034 1.2 christos buf->last = tmp; 2035 1.2 christos 2036 1.2 christos tmp->next = chain->next; 2037 1.2 christos evbuffer_chain_free(chain); 2038 1.2 christos goto ok; 2039 1.2 christos } 2040 1.2 christos 2041 1.2 christos insert_new: 2042 1.2 christos result = evbuffer_chain_insert_new(buf, datlen); 2043 1.2 christos if (!result) 2044 1.2 christos goto err; 2045 1.2 christos ok: 2046 1.2 christos EVUTIL_ASSERT(result); 2047 1.2 christos EVUTIL_ASSERT(CHAIN_SPACE_LEN(result) >= datlen); 2048 1.2 christos err: 2049 1.2 christos return result; 2050 1.2 christos } 2051 1.2 christos 2052 1.2 christos /* Make sure that datlen bytes are available for writing in the last n 2053 1.2 christos * chains. Never copies or moves data. */ 2054 1.2 christos int 2055 1.4 christos evbuffer_expand_fast_(struct evbuffer *buf, size_t datlen, int n) 2056 1.2 christos { 2057 1.2 christos struct evbuffer_chain *chain = buf->last, *tmp, *next; 2058 1.2 christos size_t avail; 2059 1.2 christos int used; 2060 1.2 christos 2061 1.2 christos ASSERT_EVBUFFER_LOCKED(buf); 2062 1.2 christos EVUTIL_ASSERT(n >= 2); 2063 1.2 christos 2064 1.2 christos if (chain == NULL || (chain->flags & EVBUFFER_IMMUTABLE)) { 2065 1.2 christos /* There is no last chunk, or we can't touch the last chunk. 2066 1.2 christos * Just add a new chunk. */ 2067 1.2 christos chain = evbuffer_chain_new(datlen); 2068 1.2 christos if (chain == NULL) 2069 1.2 christos return (-1); 2070 1.2 christos 2071 1.2 christos evbuffer_chain_insert(buf, chain); 2072 1.2 christos return (0); 2073 1.2 christos } 2074 1.2 christos 2075 1.2 christos used = 0; /* number of chains we're using space in. */ 2076 1.2 christos avail = 0; /* how much space they have. */ 2077 1.2 christos /* How many bytes can we stick at the end of buffer as it is? Iterate 2078 1.2 christos * over the chains at the end of the buffer, tring to see how much 2079 1.2 christos * space we have in the first n. */ 2080 1.2 christos for (chain = *buf->last_with_datap; chain; chain = chain->next) { 2081 1.2 christos if (chain->off) { 2082 1.2 christos size_t space = (size_t) CHAIN_SPACE_LEN(chain); 2083 1.2 christos EVUTIL_ASSERT(chain == *buf->last_with_datap); 2084 1.2 christos if (space) { 2085 1.2 christos avail += space; 2086 1.2 christos ++used; 2087 1.2 christos } 2088 1.2 christos } else { 2089 1.2 christos /* No data in chain; realign it. */ 2090 1.2 christos chain->misalign = 0; 2091 1.2 christos avail += chain->buffer_len; 2092 1.2 christos ++used; 2093 1.2 christos } 2094 1.2 christos if (avail >= datlen) { 2095 1.2 christos /* There is already enough space. Just return */ 2096 1.2 christos return (0); 2097 1.2 christos } 2098 1.2 christos if (used == n) 2099 1.2 christos break; 2100 1.2 christos } 2101 1.2 christos 2102 1.2 christos /* There wasn't enough space in the first n chains with space in 2103 1.2 christos * them. Either add a new chain with enough space, or replace all 2104 1.2 christos * empty chains with one that has enough space, depending on n. */ 2105 1.2 christos if (used < n) { 2106 1.2 christos /* The loop ran off the end of the chains before it hit n 2107 1.2 christos * chains; we can add another. */ 2108 1.2 christos EVUTIL_ASSERT(chain == NULL); 2109 1.2 christos 2110 1.2 christos tmp = evbuffer_chain_new(datlen - avail); 2111 1.2 christos if (tmp == NULL) 2112 1.2 christos return (-1); 2113 1.2 christos 2114 1.2 christos buf->last->next = tmp; 2115 1.2 christos buf->last = tmp; 2116 1.2 christos /* (we would only set last_with_data if we added the first 2117 1.2 christos * chain. But if the buffer had no chains, we would have 2118 1.2 christos * just allocated a new chain earlier) */ 2119 1.2 christos return (0); 2120 1.2 christos } else { 2121 1.2 christos /* Nuke _all_ the empty chains. */ 2122 1.2 christos int rmv_all = 0; /* True iff we removed last_with_data. */ 2123 1.2 christos chain = *buf->last_with_datap; 2124 1.2 christos if (!chain->off) { 2125 1.2 christos EVUTIL_ASSERT(chain == buf->first); 2126 1.2 christos rmv_all = 1; 2127 1.2 christos avail = 0; 2128 1.2 christos } else { 2129 1.3 spz /* can't overflow, since only mutable chains have 2130 1.3 spz * huge misaligns. */ 2131 1.2 christos avail = (size_t) CHAIN_SPACE_LEN(chain); 2132 1.2 christos chain = chain->next; 2133 1.2 christos } 2134 1.2 christos 2135 1.2 christos 2136 1.2 christos for (; chain; chain = next) { 2137 1.2 christos next = chain->next; 2138 1.2 christos EVUTIL_ASSERT(chain->off == 0); 2139 1.2 christos evbuffer_chain_free(chain); 2140 1.2 christos } 2141 1.3 spz EVUTIL_ASSERT(datlen >= avail); 2142 1.2 christos tmp = evbuffer_chain_new(datlen - avail); 2143 1.2 christos if (tmp == NULL) { 2144 1.2 christos if (rmv_all) { 2145 1.2 christos ZERO_CHAIN(buf); 2146 1.2 christos } else { 2147 1.2 christos buf->last = *buf->last_with_datap; 2148 1.2 christos (*buf->last_with_datap)->next = NULL; 2149 1.2 christos } 2150 1.2 christos return (-1); 2151 1.2 christos } 2152 1.2 christos 2153 1.2 christos if (rmv_all) { 2154 1.2 christos buf->first = buf->last = tmp; 2155 1.2 christos buf->last_with_datap = &buf->first; 2156 1.2 christos } else { 2157 1.2 christos (*buf->last_with_datap)->next = tmp; 2158 1.2 christos buf->last = tmp; 2159 1.2 christos } 2160 1.2 christos return (0); 2161 1.2 christos } 2162 1.2 christos } 2163 1.2 christos 2164 1.2 christos int 2165 1.2 christos evbuffer_expand(struct evbuffer *buf, size_t datlen) 2166 1.2 christos { 2167 1.2 christos struct evbuffer_chain *chain; 2168 1.2 christos 2169 1.2 christos EVBUFFER_LOCK(buf); 2170 1.2 christos chain = evbuffer_expand_singlechain(buf, datlen); 2171 1.2 christos EVBUFFER_UNLOCK(buf); 2172 1.2 christos return chain ? 0 : -1; 2173 1.2 christos } 2174 1.2 christos 2175 1.2 christos /* 2176 1.2 christos * Reads data from a file descriptor into a buffer. 2177 1.2 christos */ 2178 1.2 christos 2179 1.4 christos #if defined(EVENT__HAVE_SYS_UIO_H) || defined(_WIN32) 2180 1.2 christos #define USE_IOVEC_IMPL 2181 1.2 christos #endif 2182 1.2 christos 2183 1.2 christos #ifdef USE_IOVEC_IMPL 2184 1.2 christos 2185 1.4 christos #ifdef EVENT__HAVE_SYS_UIO_H 2186 1.2 christos /* number of iovec we use for writev, fragmentation is going to determine 2187 1.2 christos * how much we end up writing */ 2188 1.2 christos 2189 1.2 christos #define DEFAULT_WRITE_IOVEC 128 2190 1.2 christos 2191 1.2 christos #if defined(UIO_MAXIOV) && UIO_MAXIOV < DEFAULT_WRITE_IOVEC 2192 1.2 christos #define NUM_WRITE_IOVEC UIO_MAXIOV 2193 1.2 christos #elif defined(IOV_MAX) && IOV_MAX < DEFAULT_WRITE_IOVEC 2194 1.2 christos #define NUM_WRITE_IOVEC IOV_MAX 2195 1.2 christos #else 2196 1.2 christos #define NUM_WRITE_IOVEC DEFAULT_WRITE_IOVEC 2197 1.2 christos #endif 2198 1.2 christos 2199 1.2 christos #define IOV_TYPE struct iovec 2200 1.2 christos #define IOV_PTR_FIELD iov_base 2201 1.2 christos #define IOV_LEN_FIELD iov_len 2202 1.2 christos #define IOV_LEN_TYPE size_t 2203 1.2 christos #else 2204 1.2 christos #define NUM_WRITE_IOVEC 16 2205 1.2 christos #define IOV_TYPE WSABUF 2206 1.2 christos #define IOV_PTR_FIELD buf 2207 1.2 christos #define IOV_LEN_FIELD len 2208 1.2 christos #define IOV_LEN_TYPE unsigned long 2209 1.2 christos #endif 2210 1.2 christos #endif 2211 1.2 christos #define NUM_READ_IOVEC 4 2212 1.2 christos 2213 1.2 christos #define EVBUFFER_MAX_READ 4096 2214 1.2 christos 2215 1.2 christos /** Helper function to figure out which space to use for reading data into 2216 1.2 christos an evbuffer. Internal use only. 2217 1.2 christos 2218 1.2 christos @param buf The buffer to read into 2219 1.2 christos @param howmuch How much we want to read. 2220 1.2 christos @param vecs An array of two or more iovecs or WSABUFs. 2221 1.2 christos @param n_vecs_avail The length of vecs 2222 1.2 christos @param chainp A pointer to a variable to hold the first chain we're 2223 1.2 christos reading into. 2224 1.2 christos @param exact Boolean: if true, we do not provide more than 'howmuch' 2225 1.2 christos space in the vectors, even if more space is available. 2226 1.2 christos @return The number of buffers we're using. 2227 1.2 christos */ 2228 1.2 christos int 2229 1.4 christos evbuffer_read_setup_vecs_(struct evbuffer *buf, ev_ssize_t howmuch, 2230 1.2 christos struct evbuffer_iovec *vecs, int n_vecs_avail, 2231 1.2 christos struct evbuffer_chain ***chainp, int exact) 2232 1.2 christos { 2233 1.2 christos struct evbuffer_chain *chain; 2234 1.2 christos struct evbuffer_chain **firstchainp; 2235 1.2 christos size_t so_far; 2236 1.2 christos int i; 2237 1.2 christos ASSERT_EVBUFFER_LOCKED(buf); 2238 1.2 christos 2239 1.2 christos if (howmuch < 0) 2240 1.2 christos return -1; 2241 1.2 christos 2242 1.2 christos so_far = 0; 2243 1.2 christos /* Let firstchain be the first chain with any space on it */ 2244 1.2 christos firstchainp = buf->last_with_datap; 2245 1.5 christos EVUTIL_ASSERT(*firstchainp); 2246 1.2 christos if (CHAIN_SPACE_LEN(*firstchainp) == 0) { 2247 1.2 christos firstchainp = &(*firstchainp)->next; 2248 1.2 christos } 2249 1.2 christos 2250 1.2 christos chain = *firstchainp; 2251 1.5 christos EVUTIL_ASSERT(chain); 2252 1.2 christos for (i = 0; i < n_vecs_avail && so_far < (size_t)howmuch; ++i) { 2253 1.2 christos size_t avail = (size_t) CHAIN_SPACE_LEN(chain); 2254 1.2 christos if (avail > (howmuch - so_far) && exact) 2255 1.2 christos avail = howmuch - so_far; 2256 1.4 christos vecs[i].iov_base = (void *)CHAIN_SPACE_PTR(chain); 2257 1.2 christos vecs[i].iov_len = avail; 2258 1.2 christos so_far += avail; 2259 1.2 christos chain = chain->next; 2260 1.2 christos } 2261 1.2 christos 2262 1.2 christos *chainp = firstchainp; 2263 1.2 christos return i; 2264 1.2 christos } 2265 1.2 christos 2266 1.2 christos static int 2267 1.2 christos get_n_bytes_readable_on_socket(evutil_socket_t fd) 2268 1.2 christos { 2269 1.4 christos #if defined(FIONREAD) && defined(_WIN32) 2270 1.2 christos unsigned long lng = EVBUFFER_MAX_READ; 2271 1.2 christos if (ioctlsocket(fd, FIONREAD, &lng) < 0) 2272 1.2 christos return -1; 2273 1.3 spz /* Can overflow, but mostly harmlessly. XXXX */ 2274 1.2 christos return (int)lng; 2275 1.2 christos #elif defined(FIONREAD) 2276 1.2 christos int n = EVBUFFER_MAX_READ; 2277 1.2 christos if (ioctl(fd, FIONREAD, &n) < 0) 2278 1.2 christos return -1; 2279 1.2 christos return n; 2280 1.2 christos #else 2281 1.2 christos return EVBUFFER_MAX_READ; 2282 1.2 christos #endif 2283 1.2 christos } 2284 1.2 christos 2285 1.2 christos /* TODO(niels): should this function return ev_ssize_t and take ev_ssize_t 2286 1.2 christos * as howmuch? */ 2287 1.2 christos int 2288 1.2 christos evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) 2289 1.2 christos { 2290 1.2 christos struct evbuffer_chain **chainp; 2291 1.2 christos int n; 2292 1.2 christos int result; 2293 1.2 christos 2294 1.2 christos #ifdef USE_IOVEC_IMPL 2295 1.2 christos int nvecs, i, remaining; 2296 1.2 christos #else 2297 1.2 christos struct evbuffer_chain *chain; 2298 1.2 christos unsigned char *p; 2299 1.2 christos #endif 2300 1.2 christos 2301 1.2 christos EVBUFFER_LOCK(buf); 2302 1.2 christos 2303 1.2 christos if (buf->freeze_end) { 2304 1.2 christos result = -1; 2305 1.2 christos goto done; 2306 1.2 christos } 2307 1.2 christos 2308 1.2 christos n = get_n_bytes_readable_on_socket(fd); 2309 1.2 christos if (n <= 0 || n > EVBUFFER_MAX_READ) 2310 1.2 christos n = EVBUFFER_MAX_READ; 2311 1.2 christos if (howmuch < 0 || howmuch > n) 2312 1.2 christos howmuch = n; 2313 1.2 christos 2314 1.2 christos #ifdef USE_IOVEC_IMPL 2315 1.2 christos /* Since we can use iovecs, we're willing to use the last 2316 1.2 christos * NUM_READ_IOVEC chains. */ 2317 1.4 christos if (evbuffer_expand_fast_(buf, howmuch, NUM_READ_IOVEC) == -1) { 2318 1.2 christos result = -1; 2319 1.2 christos goto done; 2320 1.2 christos } else { 2321 1.2 christos IOV_TYPE vecs[NUM_READ_IOVEC]; 2322 1.4 christos #ifdef EVBUFFER_IOVEC_IS_NATIVE_ 2323 1.4 christos nvecs = evbuffer_read_setup_vecs_(buf, howmuch, vecs, 2324 1.2 christos NUM_READ_IOVEC, &chainp, 1); 2325 1.2 christos #else 2326 1.2 christos /* We aren't using the native struct iovec. Therefore, 2327 1.2 christos we are on win32. */ 2328 1.2 christos struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC]; 2329 1.4 christos nvecs = evbuffer_read_setup_vecs_(buf, howmuch, ev_vecs, 2, 2330 1.2 christos &chainp, 1); 2331 1.2 christos 2332 1.2 christos for (i=0; i < nvecs; ++i) 2333 1.2 christos WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]); 2334 1.2 christos #endif 2335 1.2 christos 2336 1.4 christos #ifdef _WIN32 2337 1.2 christos { 2338 1.2 christos DWORD bytesRead; 2339 1.2 christos DWORD flags=0; 2340 1.2 christos if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) { 2341 1.2 christos /* The read failed. It might be a close, 2342 1.2 christos * or it might be an error. */ 2343 1.2 christos if (WSAGetLastError() == WSAECONNABORTED) 2344 1.2 christos n = 0; 2345 1.2 christos else 2346 1.2 christos n = -1; 2347 1.2 christos } else 2348 1.2 christos n = bytesRead; 2349 1.2 christos } 2350 1.2 christos #else 2351 1.2 christos n = readv(fd, vecs, nvecs); 2352 1.2 christos #endif 2353 1.2 christos } 2354 1.2 christos 2355 1.2 christos #else /*!USE_IOVEC_IMPL*/ 2356 1.2 christos /* If we don't have FIONREAD, we might waste some space here */ 2357 1.2 christos /* XXX we _will_ waste some space here if there is any space left 2358 1.2 christos * over on buf->last. */ 2359 1.2 christos if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) { 2360 1.2 christos result = -1; 2361 1.2 christos goto done; 2362 1.2 christos } 2363 1.2 christos 2364 1.2 christos /* We can append new data at this point */ 2365 1.2 christos p = chain->buffer + chain->misalign + chain->off; 2366 1.2 christos 2367 1.4 christos #ifndef _WIN32 2368 1.2 christos n = read(fd, p, howmuch); 2369 1.2 christos #else 2370 1.2 christos n = recv(fd, p, howmuch, 0); 2371 1.2 christos #endif 2372 1.2 christos #endif /* USE_IOVEC_IMPL */ 2373 1.2 christos 2374 1.2 christos if (n == -1) { 2375 1.2 christos result = -1; 2376 1.2 christos goto done; 2377 1.2 christos } 2378 1.2 christos if (n == 0) { 2379 1.2 christos result = 0; 2380 1.2 christos goto done; 2381 1.2 christos } 2382 1.2 christos 2383 1.2 christos #ifdef USE_IOVEC_IMPL 2384 1.2 christos remaining = n; 2385 1.2 christos for (i=0; i < nvecs; ++i) { 2386 1.3 spz /* can't overflow, since only mutable chains have 2387 1.3 spz * huge misaligns. */ 2388 1.3 spz size_t space = (size_t) CHAIN_SPACE_LEN(*chainp); 2389 1.3 spz /* XXXX This is a kludge that can waste space in perverse 2390 1.3 spz * situations. */ 2391 1.3 spz if (space > EVBUFFER_CHAIN_MAX) 2392 1.3 spz space = EVBUFFER_CHAIN_MAX; 2393 1.3 spz if ((ev_ssize_t)space < remaining) { 2394 1.2 christos (*chainp)->off += space; 2395 1.2 christos remaining -= (int)space; 2396 1.2 christos } else { 2397 1.2 christos (*chainp)->off += remaining; 2398 1.2 christos buf->last_with_datap = chainp; 2399 1.2 christos break; 2400 1.2 christos } 2401 1.2 christos chainp = &(*chainp)->next; 2402 1.2 christos } 2403 1.2 christos #else 2404 1.2 christos chain->off += n; 2405 1.2 christos advance_last_with_data(buf); 2406 1.2 christos #endif 2407 1.2 christos buf->total_len += n; 2408 1.2 christos buf->n_add_for_cb += n; 2409 1.2 christos 2410 1.2 christos /* Tell someone about changes in this buffer */ 2411 1.4 christos evbuffer_invoke_callbacks_(buf); 2412 1.2 christos result = n; 2413 1.2 christos done: 2414 1.2 christos EVBUFFER_UNLOCK(buf); 2415 1.2 christos return result; 2416 1.2 christos } 2417 1.2 christos 2418 1.2 christos #ifdef USE_IOVEC_IMPL 2419 1.2 christos static inline int 2420 1.2 christos evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd, 2421 1.2 christos ev_ssize_t howmuch) 2422 1.2 christos { 2423 1.2 christos IOV_TYPE iov[NUM_WRITE_IOVEC]; 2424 1.2 christos struct evbuffer_chain *chain = buffer->first; 2425 1.2 christos int n, i = 0; 2426 1.2 christos 2427 1.2 christos if (howmuch < 0) 2428 1.2 christos return -1; 2429 1.2 christos 2430 1.2 christos ASSERT_EVBUFFER_LOCKED(buffer); 2431 1.2 christos /* XXX make this top out at some maximal data length? if the 2432 1.2 christos * buffer has (say) 1MB in it, split over 128 chains, there's 2433 1.2 christos * no way it all gets written in one go. */ 2434 1.2 christos while (chain != NULL && i < NUM_WRITE_IOVEC && howmuch) { 2435 1.2 christos #ifdef USE_SENDFILE 2436 1.2 christos /* we cannot write the file info via writev */ 2437 1.2 christos if (chain->flags & EVBUFFER_SENDFILE) 2438 1.2 christos break; 2439 1.2 christos #endif 2440 1.2 christos iov[i].IOV_PTR_FIELD = (void *) (chain->buffer + chain->misalign); 2441 1.2 christos if ((size_t)howmuch >= chain->off) { 2442 1.2 christos /* XXXcould be problematic when windows supports mmap*/ 2443 1.2 christos iov[i++].IOV_LEN_FIELD = (IOV_LEN_TYPE)chain->off; 2444 1.2 christos howmuch -= chain->off; 2445 1.2 christos } else { 2446 1.2 christos /* XXXcould be problematic when windows supports mmap*/ 2447 1.2 christos iov[i++].IOV_LEN_FIELD = (IOV_LEN_TYPE)howmuch; 2448 1.2 christos break; 2449 1.2 christos } 2450 1.2 christos chain = chain->next; 2451 1.2 christos } 2452 1.2 christos if (! i) 2453 1.2 christos return 0; 2454 1.4 christos 2455 1.4 christos #ifdef _WIN32 2456 1.2 christos { 2457 1.2 christos DWORD bytesSent; 2458 1.2 christos if (WSASend(fd, iov, i, &bytesSent, 0, NULL, NULL)) 2459 1.2 christos n = -1; 2460 1.2 christos else 2461 1.2 christos n = bytesSent; 2462 1.2 christos } 2463 1.2 christos #else 2464 1.2 christos n = writev(fd, iov, i); 2465 1.2 christos #endif 2466 1.2 christos return (n); 2467 1.2 christos } 2468 1.2 christos #endif 2469 1.2 christos 2470 1.2 christos #ifdef USE_SENDFILE 2471 1.2 christos static inline int 2472 1.4 christos evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t dest_fd, 2473 1.2 christos ev_ssize_t howmuch) 2474 1.2 christos { 2475 1.2 christos struct evbuffer_chain *chain = buffer->first; 2476 1.4 christos struct evbuffer_chain_file_segment *info = 2477 1.4 christos EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, 2478 1.4 christos chain); 2479 1.4 christos const int source_fd = info->segment->fd; 2480 1.2 christos #if defined(SENDFILE_IS_MACOSX) || defined(SENDFILE_IS_FREEBSD) 2481 1.2 christos int res; 2482 1.4 christos ev_off_t len = chain->off; 2483 1.2 christos #elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS) 2484 1.2 christos ev_ssize_t res; 2485 1.5 christos off_t offset = chain->misalign; 2486 1.2 christos #endif 2487 1.2 christos 2488 1.2 christos ASSERT_EVBUFFER_LOCKED(buffer); 2489 1.2 christos 2490 1.2 christos #if defined(SENDFILE_IS_MACOSX) 2491 1.4 christos res = sendfile(source_fd, dest_fd, chain->misalign, &len, NULL, 0); 2492 1.2 christos if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno)) 2493 1.2 christos return (-1); 2494 1.2 christos 2495 1.2 christos return (len); 2496 1.2 christos #elif defined(SENDFILE_IS_FREEBSD) 2497 1.4 christos res = sendfile(source_fd, dest_fd, chain->misalign, chain->off, NULL, &len, 0); 2498 1.2 christos if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno)) 2499 1.2 christos return (-1); 2500 1.2 christos 2501 1.2 christos return (len); 2502 1.2 christos #elif defined(SENDFILE_IS_LINUX) 2503 1.2 christos /* TODO(niels): implement splice */ 2504 1.4 christos res = sendfile(dest_fd, source_fd, &offset, chain->off); 2505 1.2 christos if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) { 2506 1.2 christos /* if this is EAGAIN or EINTR return 0; otherwise, -1 */ 2507 1.2 christos return (0); 2508 1.2 christos } 2509 1.2 christos return (res); 2510 1.2 christos #elif defined(SENDFILE_IS_SOLARIS) 2511 1.2 christos { 2512 1.2 christos const off_t offset_orig = offset; 2513 1.4 christos res = sendfile(dest_fd, source_fd, &offset, chain->off); 2514 1.2 christos if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) { 2515 1.2 christos if (offset - offset_orig) 2516 1.2 christos return offset - offset_orig; 2517 1.2 christos /* if this is EAGAIN or EINTR and no bytes were 2518 1.2 christos * written, return 0 */ 2519 1.2 christos return (0); 2520 1.2 christos } 2521 1.2 christos return (res); 2522 1.2 christos } 2523 1.2 christos #endif 2524 1.2 christos } 2525 1.2 christos #endif 2526 1.2 christos 2527 1.2 christos int 2528 1.2 christos evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, 2529 1.2 christos ev_ssize_t howmuch) 2530 1.2 christos { 2531 1.2 christos int n = -1; 2532 1.2 christos 2533 1.2 christos EVBUFFER_LOCK(buffer); 2534 1.2 christos 2535 1.2 christos if (buffer->freeze_start) { 2536 1.2 christos goto done; 2537 1.2 christos } 2538 1.2 christos 2539 1.2 christos if (howmuch < 0 || (size_t)howmuch > buffer->total_len) 2540 1.2 christos howmuch = buffer->total_len; 2541 1.2 christos 2542 1.2 christos if (howmuch > 0) { 2543 1.2 christos #ifdef USE_SENDFILE 2544 1.2 christos struct evbuffer_chain *chain = buffer->first; 2545 1.2 christos if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE)) 2546 1.2 christos n = evbuffer_write_sendfile(buffer, fd, howmuch); 2547 1.2 christos else { 2548 1.2 christos #endif 2549 1.2 christos #ifdef USE_IOVEC_IMPL 2550 1.2 christos n = evbuffer_write_iovec(buffer, fd, howmuch); 2551 1.4 christos #elif defined(_WIN32) 2552 1.2 christos /* XXX(nickm) Don't disable this code until we know if 2553 1.2 christos * the WSARecv code above works. */ 2554 1.2 christos void *p = evbuffer_pullup(buffer, howmuch); 2555 1.3 spz EVUTIL_ASSERT(p || !howmuch); 2556 1.2 christos n = send(fd, p, howmuch, 0); 2557 1.2 christos #else 2558 1.2 christos void *p = evbuffer_pullup(buffer, howmuch); 2559 1.3 spz EVUTIL_ASSERT(p || !howmuch); 2560 1.2 christos n = write(fd, p, howmuch); 2561 1.2 christos #endif 2562 1.2 christos #ifdef USE_SENDFILE 2563 1.2 christos } 2564 1.2 christos #endif 2565 1.2 christos } 2566 1.2 christos 2567 1.2 christos if (n > 0) 2568 1.2 christos evbuffer_drain(buffer, n); 2569 1.2 christos 2570 1.2 christos done: 2571 1.2 christos EVBUFFER_UNLOCK(buffer); 2572 1.2 christos return (n); 2573 1.2 christos } 2574 1.2 christos 2575 1.2 christos int 2576 1.2 christos evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd) 2577 1.2 christos { 2578 1.2 christos return evbuffer_write_atmost(buffer, fd, -1); 2579 1.2 christos } 2580 1.2 christos 2581 1.2 christos unsigned char * 2582 1.2 christos evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len) 2583 1.2 christos { 2584 1.2 christos unsigned char *search; 2585 1.2 christos struct evbuffer_ptr ptr; 2586 1.2 christos 2587 1.2 christos EVBUFFER_LOCK(buffer); 2588 1.2 christos 2589 1.2 christos ptr = evbuffer_search(buffer, (const char *)what, len, NULL); 2590 1.2 christos if (ptr.pos < 0) { 2591 1.2 christos search = NULL; 2592 1.2 christos } else { 2593 1.2 christos search = evbuffer_pullup(buffer, ptr.pos + len); 2594 1.2 christos if (search) 2595 1.2 christos search += ptr.pos; 2596 1.2 christos } 2597 1.2 christos EVBUFFER_UNLOCK(buffer); 2598 1.2 christos return search; 2599 1.2 christos } 2600 1.2 christos 2601 1.4 christos /* Subract <b>howfar</b> from the position of <b>pos</b> within 2602 1.4 christos * <b>buf</b>. Returns 0 on success, -1 on failure. 2603 1.4 christos * 2604 1.4 christos * This isn't exposed yet, because of potential inefficiency issues. 2605 1.4 christos * Maybe it should be. */ 2606 1.4 christos static int 2607 1.4 christos evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos, 2608 1.4 christos size_t howfar) 2609 1.4 christos { 2610 1.4 christos if (pos->pos < 0) 2611 1.4 christos return -1; 2612 1.4 christos if (howfar > (size_t)pos->pos) 2613 1.4 christos return -1; 2614 1.4 christos if (pos->internal_.chain && howfar <= pos->internal_.pos_in_chain) { 2615 1.4 christos pos->internal_.pos_in_chain -= howfar; 2616 1.4 christos pos->pos -= howfar; 2617 1.4 christos return 0; 2618 1.4 christos } else { 2619 1.4 christos const size_t newpos = pos->pos - howfar; 2620 1.4 christos /* Here's the inefficient part: it walks over the 2621 1.4 christos * chains until we hit newpos. */ 2622 1.4 christos return evbuffer_ptr_set(buf, pos, newpos, EVBUFFER_PTR_SET); 2623 1.4 christos } 2624 1.4 christos } 2625 1.4 christos 2626 1.2 christos int 2627 1.2 christos evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos, 2628 1.2 christos size_t position, enum evbuffer_ptr_how how) 2629 1.2 christos { 2630 1.2 christos size_t left = position; 2631 1.2 christos struct evbuffer_chain *chain = NULL; 2632 1.4 christos int result = 0; 2633 1.2 christos 2634 1.2 christos EVBUFFER_LOCK(buf); 2635 1.2 christos 2636 1.2 christos switch (how) { 2637 1.2 christos case EVBUFFER_PTR_SET: 2638 1.2 christos chain = buf->first; 2639 1.2 christos pos->pos = position; 2640 1.2 christos position = 0; 2641 1.2 christos break; 2642 1.2 christos case EVBUFFER_PTR_ADD: 2643 1.2 christos /* this avoids iterating over all previous chains if 2644 1.2 christos we just want to advance the position */ 2645 1.3 spz if (pos->pos < 0 || EV_SIZE_MAX - position < (size_t)pos->pos) { 2646 1.3 spz EVBUFFER_UNLOCK(buf); 2647 1.3 spz return -1; 2648 1.3 spz } 2649 1.4 christos chain = pos->internal_.chain; 2650 1.2 christos pos->pos += position; 2651 1.4 christos position = pos->internal_.pos_in_chain; 2652 1.2 christos break; 2653 1.2 christos } 2654 1.2 christos 2655 1.3 spz EVUTIL_ASSERT(EV_SIZE_MAX - left >= position); 2656 1.2 christos while (chain && position + left >= chain->off) { 2657 1.2 christos left -= chain->off - position; 2658 1.2 christos chain = chain->next; 2659 1.2 christos position = 0; 2660 1.2 christos } 2661 1.2 christos if (chain) { 2662 1.4 christos pos->internal_.chain = chain; 2663 1.4 christos pos->internal_.pos_in_chain = position + left; 2664 1.4 christos } else if (left == 0) { 2665 1.4 christos /* The first byte in the (nonexistent) chain after the last chain */ 2666 1.4 christos pos->internal_.chain = NULL; 2667 1.4 christos pos->internal_.pos_in_chain = 0; 2668 1.2 christos } else { 2669 1.4 christos PTR_NOT_FOUND(pos); 2670 1.4 christos result = -1; 2671 1.2 christos } 2672 1.2 christos 2673 1.2 christos EVBUFFER_UNLOCK(buf); 2674 1.2 christos 2675 1.4 christos return result; 2676 1.2 christos } 2677 1.2 christos 2678 1.2 christos /** 2679 1.2 christos Compare the bytes in buf at position pos to the len bytes in mem. Return 2680 1.2 christos less than 0, 0, or greater than 0 as memcmp. 2681 1.2 christos */ 2682 1.2 christos static int 2683 1.2 christos evbuffer_ptr_memcmp(const struct evbuffer *buf, const struct evbuffer_ptr *pos, 2684 1.2 christos const char *mem, size_t len) 2685 1.2 christos { 2686 1.2 christos struct evbuffer_chain *chain; 2687 1.2 christos size_t position; 2688 1.2 christos int r; 2689 1.2 christos 2690 1.2 christos ASSERT_EVBUFFER_LOCKED(buf); 2691 1.2 christos 2692 1.3 spz if (pos->pos < 0 || 2693 1.3 spz EV_SIZE_MAX - len < (size_t)pos->pos || 2694 1.3 spz pos->pos + len > buf->total_len) 2695 1.2 christos return -1; 2696 1.2 christos 2697 1.4 christos chain = pos->internal_.chain; 2698 1.4 christos position = pos->internal_.pos_in_chain; 2699 1.2 christos while (len && chain) { 2700 1.2 christos size_t n_comparable; 2701 1.2 christos if (len + position > chain->off) 2702 1.2 christos n_comparable = chain->off - position; 2703 1.2 christos else 2704 1.2 christos n_comparable = len; 2705 1.2 christos r = memcmp(chain->buffer + chain->misalign + position, mem, 2706 1.2 christos n_comparable); 2707 1.2 christos if (r) 2708 1.2 christos return r; 2709 1.2 christos mem += n_comparable; 2710 1.2 christos len -= n_comparable; 2711 1.2 christos position = 0; 2712 1.2 christos chain = chain->next; 2713 1.2 christos } 2714 1.2 christos 2715 1.2 christos return 0; 2716 1.2 christos } 2717 1.2 christos 2718 1.2 christos struct evbuffer_ptr 2719 1.2 christos evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start) 2720 1.2 christos { 2721 1.2 christos return evbuffer_search_range(buffer, what, len, start, NULL); 2722 1.2 christos } 2723 1.2 christos 2724 1.2 christos struct evbuffer_ptr 2725 1.2 christos evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end) 2726 1.2 christos { 2727 1.2 christos struct evbuffer_ptr pos; 2728 1.2 christos struct evbuffer_chain *chain, *last_chain = NULL; 2729 1.2 christos const unsigned char *p; 2730 1.2 christos char first; 2731 1.2 christos 2732 1.2 christos EVBUFFER_LOCK(buffer); 2733 1.2 christos 2734 1.2 christos if (start) { 2735 1.2 christos memcpy(&pos, start, sizeof(pos)); 2736 1.4 christos chain = pos.internal_.chain; 2737 1.2 christos } else { 2738 1.2 christos pos.pos = 0; 2739 1.4 christos chain = pos.internal_.chain = buffer->first; 2740 1.4 christos pos.internal_.pos_in_chain = 0; 2741 1.2 christos } 2742 1.2 christos 2743 1.2 christos if (end) 2744 1.4 christos last_chain = end->internal_.chain; 2745 1.2 christos 2746 1.2 christos if (!len || len > EV_SSIZE_MAX) 2747 1.2 christos goto done; 2748 1.2 christos 2749 1.2 christos first = what[0]; 2750 1.2 christos 2751 1.2 christos while (chain) { 2752 1.2 christos const unsigned char *start_at = 2753 1.2 christos chain->buffer + chain->misalign + 2754 1.4 christos pos.internal_.pos_in_chain; 2755 1.2 christos p = memchr(start_at, first, 2756 1.4 christos chain->off - pos.internal_.pos_in_chain); 2757 1.2 christos if (p) { 2758 1.2 christos pos.pos += p - start_at; 2759 1.4 christos pos.internal_.pos_in_chain += p - start_at; 2760 1.2 christos if (!evbuffer_ptr_memcmp(buffer, &pos, what, len)) { 2761 1.2 christos if (end && pos.pos + (ev_ssize_t)len > end->pos) 2762 1.2 christos goto not_found; 2763 1.2 christos else 2764 1.2 christos goto done; 2765 1.2 christos } 2766 1.2 christos ++pos.pos; 2767 1.4 christos ++pos.internal_.pos_in_chain; 2768 1.4 christos if (pos.internal_.pos_in_chain == chain->off) { 2769 1.4 christos chain = pos.internal_.chain = chain->next; 2770 1.4 christos pos.internal_.pos_in_chain = 0; 2771 1.2 christos } 2772 1.2 christos } else { 2773 1.2 christos if (chain == last_chain) 2774 1.2 christos goto not_found; 2775 1.4 christos pos.pos += chain->off - pos.internal_.pos_in_chain; 2776 1.4 christos chain = pos.internal_.chain = chain->next; 2777 1.4 christos pos.internal_.pos_in_chain = 0; 2778 1.2 christos } 2779 1.2 christos } 2780 1.2 christos 2781 1.2 christos not_found: 2782 1.4 christos PTR_NOT_FOUND(&pos); 2783 1.2 christos done: 2784 1.2 christos EVBUFFER_UNLOCK(buffer); 2785 1.2 christos return pos; 2786 1.2 christos } 2787 1.2 christos 2788 1.2 christos int 2789 1.2 christos evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, 2790 1.2 christos struct evbuffer_ptr *start_at, 2791 1.2 christos struct evbuffer_iovec *vec, int n_vec) 2792 1.2 christos { 2793 1.2 christos struct evbuffer_chain *chain; 2794 1.2 christos int idx = 0; 2795 1.2 christos ev_ssize_t len_so_far = 0; 2796 1.2 christos 2797 1.4 christos /* Avoid locking in trivial edge cases */ 2798 1.4 christos if (start_at && start_at->internal_.chain == NULL) 2799 1.4 christos return 0; 2800 1.4 christos 2801 1.2 christos EVBUFFER_LOCK(buffer); 2802 1.2 christos 2803 1.2 christos if (start_at) { 2804 1.4 christos chain = start_at->internal_.chain; 2805 1.2 christos len_so_far = chain->off 2806 1.4 christos - start_at->internal_.pos_in_chain; 2807 1.2 christos idx = 1; 2808 1.2 christos if (n_vec > 0) { 2809 1.4 christos vec[0].iov_base = (void *)(chain->buffer + chain->misalign 2810 1.4 christos + start_at->internal_.pos_in_chain); 2811 1.2 christos vec[0].iov_len = len_so_far; 2812 1.2 christos } 2813 1.2 christos chain = chain->next; 2814 1.2 christos } else { 2815 1.2 christos chain = buffer->first; 2816 1.2 christos } 2817 1.2 christos 2818 1.2 christos if (n_vec == 0 && len < 0) { 2819 1.2 christos /* If no vectors are provided and they asked for "everything", 2820 1.2 christos * pretend they asked for the actual available amount. */ 2821 1.3 spz len = buffer->total_len; 2822 1.3 spz if (start_at) { 2823 1.3 spz len -= start_at->pos; 2824 1.3 spz } 2825 1.2 christos } 2826 1.2 christos 2827 1.2 christos while (chain) { 2828 1.2 christos if (len >= 0 && len_so_far >= len) 2829 1.2 christos break; 2830 1.2 christos if (idx<n_vec) { 2831 1.4 christos vec[idx].iov_base = (void *)(chain->buffer + chain->misalign); 2832 1.2 christos vec[idx].iov_len = chain->off; 2833 1.2 christos } else if (len<0) { 2834 1.2 christos break; 2835 1.2 christos } 2836 1.2 christos ++idx; 2837 1.2 christos len_so_far += chain->off; 2838 1.2 christos chain = chain->next; 2839 1.2 christos } 2840 1.2 christos 2841 1.2 christos EVBUFFER_UNLOCK(buffer); 2842 1.2 christos 2843 1.2 christos return idx; 2844 1.2 christos } 2845 1.2 christos 2846 1.2 christos 2847 1.2 christos int 2848 1.2 christos evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) 2849 1.2 christos { 2850 1.2 christos char *buffer; 2851 1.2 christos size_t space; 2852 1.2 christos int sz, result = -1; 2853 1.2 christos va_list aq; 2854 1.2 christos struct evbuffer_chain *chain; 2855 1.2 christos 2856 1.2 christos 2857 1.2 christos EVBUFFER_LOCK(buf); 2858 1.2 christos 2859 1.2 christos if (buf->freeze_end) { 2860 1.2 christos goto done; 2861 1.2 christos } 2862 1.2 christos 2863 1.2 christos /* make sure that at least some space is available */ 2864 1.2 christos if ((chain = evbuffer_expand_singlechain(buf, 64)) == NULL) 2865 1.2 christos goto done; 2866 1.2 christos 2867 1.2 christos for (;;) { 2868 1.2 christos #if 0 2869 1.2 christos size_t used = chain->misalign + chain->off; 2870 1.2 christos buffer = (char *)chain->buffer + chain->misalign + chain->off; 2871 1.2 christos EVUTIL_ASSERT(chain->buffer_len >= used); 2872 1.2 christos space = chain->buffer_len - used; 2873 1.2 christos #endif 2874 1.2 christos buffer = (char*) CHAIN_SPACE_PTR(chain); 2875 1.2 christos space = (size_t) CHAIN_SPACE_LEN(chain); 2876 1.2 christos 2877 1.2 christos #ifndef va_copy 2878 1.2 christos #define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) 2879 1.2 christos #endif 2880 1.2 christos va_copy(aq, ap); 2881 1.2 christos 2882 1.2 christos sz = evutil_vsnprintf(buffer, space, fmt, aq); 2883 1.2 christos 2884 1.2 christos va_end(aq); 2885 1.2 christos 2886 1.2 christos if (sz < 0) 2887 1.2 christos goto done; 2888 1.3 spz if (INT_MAX >= EVBUFFER_CHAIN_MAX && 2889 1.3 spz (size_t)sz >= EVBUFFER_CHAIN_MAX) 2890 1.3 spz goto done; 2891 1.2 christos if ((size_t)sz < space) { 2892 1.2 christos chain->off += sz; 2893 1.2 christos buf->total_len += sz; 2894 1.2 christos buf->n_add_for_cb += sz; 2895 1.2 christos 2896 1.2 christos advance_last_with_data(buf); 2897 1.4 christos evbuffer_invoke_callbacks_(buf); 2898 1.2 christos result = sz; 2899 1.2 christos goto done; 2900 1.2 christos } 2901 1.2 christos if ((chain = evbuffer_expand_singlechain(buf, sz + 1)) == NULL) 2902 1.2 christos goto done; 2903 1.2 christos } 2904 1.2 christos /* NOTREACHED */ 2905 1.2 christos 2906 1.2 christos done: 2907 1.2 christos EVBUFFER_UNLOCK(buf); 2908 1.2 christos return result; 2909 1.2 christos } 2910 1.2 christos 2911 1.2 christos int 2912 1.2 christos evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) 2913 1.2 christos { 2914 1.2 christos int res = -1; 2915 1.2 christos va_list ap; 2916 1.2 christos 2917 1.2 christos va_start(ap, fmt); 2918 1.2 christos res = evbuffer_add_vprintf(buf, fmt, ap); 2919 1.2 christos va_end(ap); 2920 1.2 christos 2921 1.2 christos return (res); 2922 1.2 christos } 2923 1.2 christos 2924 1.2 christos int 2925 1.2 christos evbuffer_add_reference(struct evbuffer *outbuf, 2926 1.2 christos const void *data, size_t datlen, 2927 1.2 christos evbuffer_ref_cleanup_cb cleanupfn, void *extra) 2928 1.2 christos { 2929 1.2 christos struct evbuffer_chain *chain; 2930 1.2 christos struct evbuffer_chain_reference *info; 2931 1.2 christos int result = -1; 2932 1.2 christos 2933 1.2 christos chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_reference)); 2934 1.2 christos if (!chain) 2935 1.2 christos return (-1); 2936 1.2 christos chain->flags |= EVBUFFER_REFERENCE | EVBUFFER_IMMUTABLE; 2937 1.2 christos chain->buffer = __UNCONST(data); 2938 1.2 christos chain->buffer_len = datlen; 2939 1.2 christos chain->off = datlen; 2940 1.2 christos 2941 1.2 christos info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_reference, chain); 2942 1.2 christos info->cleanupfn = cleanupfn; 2943 1.2 christos info->extra = extra; 2944 1.2 christos 2945 1.2 christos EVBUFFER_LOCK(outbuf); 2946 1.2 christos if (outbuf->freeze_end) { 2947 1.2 christos /* don't call chain_free; we do not want to actually invoke 2948 1.2 christos * the cleanup function */ 2949 1.2 christos mm_free(chain); 2950 1.2 christos goto done; 2951 1.2 christos } 2952 1.2 christos evbuffer_chain_insert(outbuf, chain); 2953 1.2 christos outbuf->n_add_for_cb += datlen; 2954 1.2 christos 2955 1.4 christos evbuffer_invoke_callbacks_(outbuf); 2956 1.2 christos 2957 1.2 christos result = 0; 2958 1.2 christos done: 2959 1.2 christos EVBUFFER_UNLOCK(outbuf); 2960 1.2 christos 2961 1.2 christos return result; 2962 1.2 christos } 2963 1.2 christos 2964 1.2 christos /* TODO(niels): we may want to add to automagically convert to mmap, in 2965 1.2 christos * case evbuffer_remove() or evbuffer_pullup() are being used. 2966 1.2 christos */ 2967 1.4 christos struct evbuffer_file_segment * 2968 1.4 christos evbuffer_file_segment_new( 2969 1.4 christos int fd, ev_off_t offset, ev_off_t length, unsigned flags) 2970 1.4 christos { 2971 1.4 christos struct evbuffer_file_segment *seg = 2972 1.4 christos mm_calloc(sizeof(struct evbuffer_file_segment), 1); 2973 1.4 christos if (!seg) 2974 1.4 christos return NULL; 2975 1.4 christos seg->refcnt = 1; 2976 1.4 christos seg->fd = fd; 2977 1.4 christos seg->flags = flags; 2978 1.4 christos seg->file_offset = offset; 2979 1.4 christos seg->cleanup_cb = NULL; 2980 1.4 christos seg->cleanup_cb_arg = NULL; 2981 1.4 christos #ifdef _WIN32 2982 1.4 christos #ifndef lseek 2983 1.4 christos #define lseek _lseeki64 2984 1.4 christos #endif 2985 1.4 christos #ifndef fstat 2986 1.4 christos #define fstat _fstat 2987 1.4 christos #endif 2988 1.4 christos #ifndef stat 2989 1.4 christos #define stat _stat 2990 1.2 christos #endif 2991 1.2 christos #endif 2992 1.4 christos if (length == -1) { 2993 1.4 christos struct stat st; 2994 1.4 christos if (fstat(fd, &st) < 0) 2995 1.4 christos goto err; 2996 1.4 christos length = st.st_size; 2997 1.4 christos } 2998 1.4 christos seg->length = length; 2999 1.2 christos 3000 1.3 spz if (offset < 0 || length < 0 || 3001 1.3 spz ((ev_uint64_t)length > EVBUFFER_CHAIN_MAX) || 3002 1.3 spz (ev_uint64_t)offset > (ev_uint64_t)(EVBUFFER_CHAIN_MAX - length)) 3003 1.4 christos goto err; 3004 1.3 spz 3005 1.2 christos #if defined(USE_SENDFILE) 3006 1.4 christos if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) { 3007 1.4 christos seg->can_sendfile = 1; 3008 1.4 christos goto done; 3009 1.2 christos } 3010 1.4 christos #endif 3011 1.2 christos 3012 1.4 christos if (evbuffer_file_segment_materialize(seg)<0) 3013 1.4 christos goto err; 3014 1.2 christos 3015 1.4 christos #if defined(USE_SENDFILE) 3016 1.4 christos done: 3017 1.4 christos #endif 3018 1.4 christos if (!(flags & EVBUF_FS_DISABLE_LOCKING)) { 3019 1.4 christos EVTHREAD_ALLOC_LOCK(seg->lock, 0); 3020 1.4 christos } 3021 1.4 christos return seg; 3022 1.4 christos err: 3023 1.4 christos mm_free(seg); 3024 1.4 christos return NULL; 3025 1.4 christos } 3026 1.2 christos 3027 1.4 christos #ifdef EVENT__HAVE_MMAP 3028 1.4 christos static long 3029 1.4 christos get_page_size(void) 3030 1.4 christos { 3031 1.4 christos #ifdef SC_PAGE_SIZE 3032 1.4 christos return sysconf(SC_PAGE_SIZE); 3033 1.4 christos #elif defined(_SC_PAGE_SIZE) 3034 1.4 christos return sysconf(_SC_PAGE_SIZE); 3035 1.4 christos #else 3036 1.4 christos return 1; 3037 1.4 christos #endif 3038 1.4 christos } 3039 1.4 christos #endif 3040 1.2 christos 3041 1.4 christos /* DOCDOC */ 3042 1.4 christos /* Requires lock */ 3043 1.4 christos static int 3044 1.4 christos evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg) 3045 1.4 christos { 3046 1.4 christos const unsigned flags = seg->flags; 3047 1.4 christos const int fd = seg->fd; 3048 1.4 christos const ev_off_t length = seg->length; 3049 1.4 christos const ev_off_t offset = seg->file_offset; 3050 1.4 christos 3051 1.4 christos if (seg->contents) 3052 1.4 christos return 0; /* already materialized */ 3053 1.4 christos 3054 1.4 christos #if defined(EVENT__HAVE_MMAP) 3055 1.4 christos if (!(flags & EVBUF_FS_DISABLE_MMAP)) { 3056 1.4 christos off_t offset_rounded = 0, offset_leftover = 0; 3057 1.4 christos void *mapped; 3058 1.4 christos if (offset) { 3059 1.4 christos /* mmap implementations don't generally like us 3060 1.4 christos * to have an offset that isn't a round */ 3061 1.4 christos long page_size = get_page_size(); 3062 1.4 christos if (page_size == -1) 3063 1.4 christos goto err; 3064 1.4 christos offset_leftover = offset % page_size; 3065 1.4 christos offset_rounded = offset - offset_leftover; 3066 1.2 christos } 3067 1.4 christos mapped = mmap(NULL, length + offset_leftover, 3068 1.4 christos PROT_READ, 3069 1.2 christos #ifdef MAP_NOCACHE 3070 1.4 christos MAP_NOCACHE | /* ??? */ 3071 1.2 christos #endif 3072 1.2 christos #ifdef MAP_FILE 3073 1.2 christos MAP_FILE | 3074 1.2 christos #endif 3075 1.2 christos MAP_PRIVATE, 3076 1.4 christos fd, offset_rounded); 3077 1.2 christos if (mapped == MAP_FAILED) { 3078 1.2 christos event_warn("%s: mmap(%d, %d, %zu) failed", 3079 1.2 christos __func__, fd, 0, (size_t)(offset + length)); 3080 1.4 christos } else { 3081 1.4 christos seg->mapping = mapped; 3082 1.4 christos seg->contents = (char*)mapped+offset_leftover; 3083 1.4 christos seg->mmap_offset = 0; 3084 1.4 christos seg->is_mapping = 1; 3085 1.4 christos goto done; 3086 1.4 christos } 3087 1.4 christos } 3088 1.4 christos #endif 3089 1.4 christos #ifdef _WIN32 3090 1.4 christos if (!(flags & EVBUF_FS_DISABLE_MMAP)) { 3091 1.4 christos intptr_t h = _get_osfhandle(fd); 3092 1.4 christos HANDLE m; 3093 1.4 christos ev_uint64_t total_size = length+offset; 3094 1.4 christos if ((HANDLE)h == INVALID_HANDLE_VALUE) 3095 1.4 christos goto err; 3096 1.4 christos m = CreateFileMapping((HANDLE)h, NULL, PAGE_READONLY, 3097 1.4 christos (total_size >> 32), total_size & 0xfffffffful, 3098 1.4 christos NULL); 3099 1.4 christos if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */ 3100 1.4 christos seg->mapping_handle = m; 3101 1.4 christos seg->mmap_offset = offset; 3102 1.4 christos seg->is_mapping = 1; 3103 1.4 christos goto done; 3104 1.4 christos } 3105 1.4 christos } 3106 1.4 christos #endif 3107 1.4 christos { 3108 1.4 christos ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos; 3109 1.4 christos ev_off_t read_so_far = 0; 3110 1.4 christos char *mem; 3111 1.4 christos int e; 3112 1.4 christos ev_ssize_t n = 0; 3113 1.4 christos if (!(mem = mm_malloc(length))) 3114 1.4 christos goto err; 3115 1.4 christos if (start_pos < 0) { 3116 1.4 christos mm_free(mem); 3117 1.4 christos goto err; 3118 1.4 christos } 3119 1.4 christos if (lseek(fd, offset, SEEK_SET) < 0) { 3120 1.4 christos mm_free(mem); 3121 1.4 christos goto err; 3122 1.4 christos } 3123 1.4 christos while (read_so_far < length) { 3124 1.4 christos n = read(fd, mem+read_so_far, length-read_so_far); 3125 1.4 christos if (n <= 0) 3126 1.4 christos break; 3127 1.4 christos read_so_far += n; 3128 1.2 christos } 3129 1.4 christos 3130 1.4 christos e = errno; 3131 1.4 christos pos = lseek(fd, start_pos, SEEK_SET); 3132 1.4 christos if (n < 0 || (n == 0 && length > read_so_far)) { 3133 1.4 christos mm_free(mem); 3134 1.4 christos errno = e; 3135 1.4 christos goto err; 3136 1.4 christos } else if (pos < 0) { 3137 1.4 christos mm_free(mem); 3138 1.4 christos goto err; 3139 1.2 christos } 3140 1.2 christos 3141 1.4 christos seg->contents = mem; 3142 1.4 christos } 3143 1.4 christos 3144 1.4 christos done: 3145 1.4 christos return 0; 3146 1.4 christos err: 3147 1.4 christos return -1; 3148 1.4 christos } 3149 1.4 christos 3150 1.4 christos void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, 3151 1.4 christos evbuffer_file_segment_cleanup_cb cb, void* arg) 3152 1.4 christos { 3153 1.4 christos EVUTIL_ASSERT(seg->refcnt > 0); 3154 1.4 christos seg->cleanup_cb = cb; 3155 1.4 christos seg->cleanup_cb_arg = arg; 3156 1.4 christos } 3157 1.2 christos 3158 1.4 christos void 3159 1.4 christos evbuffer_file_segment_free(struct evbuffer_file_segment *seg) 3160 1.4 christos { 3161 1.4 christos int refcnt; 3162 1.4 christos EVLOCK_LOCK(seg->lock, 0); 3163 1.4 christos refcnt = --seg->refcnt; 3164 1.4 christos EVLOCK_UNLOCK(seg->lock, 0); 3165 1.4 christos if (refcnt > 0) 3166 1.4 christos return; 3167 1.4 christos EVUTIL_ASSERT(refcnt == 0); 3168 1.2 christos 3169 1.4 christos if (seg->is_mapping) { 3170 1.4 christos #ifdef _WIN32 3171 1.4 christos CloseHandle(seg->mapping_handle); 3172 1.4 christos #elif defined (EVENT__HAVE_MMAP) 3173 1.4 christos off_t offset_leftover; 3174 1.4 christos offset_leftover = seg->file_offset % get_page_size(); 3175 1.4 christos if (munmap(seg->mapping, seg->length + offset_leftover) == -1) 3176 1.4 christos event_warn("%s: munmap failed", __func__); 3177 1.2 christos #endif 3178 1.4 christos } else if (seg->contents) { 3179 1.4 christos mm_free(seg->contents); 3180 1.4 christos } 3181 1.4 christos 3182 1.4 christos if ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) { 3183 1.4 christos close(seg->fd); 3184 1.4 christos } 3185 1.4 christos 3186 1.4 christos if (seg->cleanup_cb) { 3187 1.4 christos (*seg->cleanup_cb)((struct evbuffer_file_segment const*)seg, 3188 1.4 christos seg->flags, seg->cleanup_cb_arg); 3189 1.4 christos seg->cleanup_cb = NULL; 3190 1.4 christos seg->cleanup_cb_arg = NULL; 3191 1.4 christos } 3192 1.4 christos 3193 1.4 christos EVTHREAD_FREE_LOCK(seg->lock, 0); 3194 1.4 christos mm_free(seg); 3195 1.4 christos } 3196 1.2 christos 3197 1.4 christos int 3198 1.4 christos evbuffer_add_file_segment(struct evbuffer *buf, 3199 1.4 christos struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length) 3200 1.4 christos { 3201 1.4 christos struct evbuffer_chain *chain; 3202 1.4 christos struct evbuffer_chain_file_segment *extra; 3203 1.4 christos int can_use_sendfile = 0; 3204 1.2 christos 3205 1.4 christos EVBUFFER_LOCK(buf); 3206 1.4 christos EVLOCK_LOCK(seg->lock, 0); 3207 1.4 christos if (buf->flags & EVBUFFER_FLAG_DRAINS_TO_FD) { 3208 1.4 christos can_use_sendfile = 1; 3209 1.4 christos } else { 3210 1.4 christos if (!seg->contents) { 3211 1.4 christos if (evbuffer_file_segment_materialize(seg)<0) { 3212 1.4 christos EVLOCK_UNLOCK(seg->lock, 0); 3213 1.4 christos EVBUFFER_UNLOCK(buf); 3214 1.4 christos return -1; 3215 1.4 christos } 3216 1.2 christos } 3217 1.4 christos } 3218 1.4 christos EVLOCK_UNLOCK(seg->lock, 0); 3219 1.4 christos 3220 1.4 christos if (buf->freeze_end) 3221 1.4 christos goto err; 3222 1.2 christos 3223 1.4 christos if (length < 0) { 3224 1.4 christos if (offset > seg->length) 3225 1.4 christos goto err; 3226 1.4 christos length = seg->length - offset; 3227 1.4 christos } 3228 1.2 christos 3229 1.4 christos /* Can we actually add this? */ 3230 1.4 christos if (offset+length > seg->length) 3231 1.4 christos goto err; 3232 1.2 christos 3233 1.4 christos chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_file_segment)); 3234 1.4 christos if (!chain) 3235 1.4 christos goto err; 3236 1.4 christos extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain); 3237 1.2 christos 3238 1.4 christos chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT; 3239 1.4 christos if (can_use_sendfile && seg->can_sendfile) { 3240 1.4 christos chain->flags |= EVBUFFER_SENDFILE; 3241 1.4 christos chain->misalign = seg->file_offset + offset; 3242 1.4 christos chain->off = length; 3243 1.4 christos chain->buffer_len = chain->misalign + length; 3244 1.4 christos } else if (seg->is_mapping) { 3245 1.4 christos #ifdef _WIN32 3246 1.4 christos ev_uint64_t total_offset = seg->mmap_offset+offset; 3247 1.4 christos ev_uint64_t offset_rounded=0, offset_remaining=0; 3248 1.4 christos LPVOID data; 3249 1.4 christos if (total_offset) { 3250 1.4 christos SYSTEM_INFO si; 3251 1.4 christos memset(&si, 0, sizeof(si)); /* cargo cult */ 3252 1.4 christos GetSystemInfo(&si); 3253 1.4 christos offset_remaining = total_offset % si.dwAllocationGranularity; 3254 1.4 christos offset_rounded = total_offset - offset_remaining; 3255 1.4 christos } 3256 1.4 christos data = MapViewOfFile( 3257 1.4 christos seg->mapping_handle, 3258 1.4 christos FILE_MAP_READ, 3259 1.4 christos offset_rounded >> 32, 3260 1.4 christos offset_rounded & 0xfffffffful, 3261 1.4 christos length + offset_remaining); 3262 1.4 christos if (data == NULL) { 3263 1.4 christos mm_free(chain); 3264 1.4 christos goto err; 3265 1.4 christos } 3266 1.4 christos chain->buffer = (unsigned char*) data; 3267 1.4 christos chain->buffer_len = length+offset_remaining; 3268 1.4 christos chain->misalign = offset_remaining; 3269 1.4 christos chain->off = length; 3270 1.4 christos #else 3271 1.4 christos chain->buffer = (unsigned char*)(seg->contents + offset); 3272 1.4 christos chain->buffer_len = length; 3273 1.4 christos chain->off = length; 3274 1.2 christos #endif 3275 1.4 christos } else { 3276 1.4 christos chain->buffer = (unsigned char*)(seg->contents + offset); 3277 1.4 christos chain->buffer_len = length; 3278 1.4 christos chain->off = length; 3279 1.2 christos } 3280 1.2 christos 3281 1.5 christos EVLOCK_LOCK(seg->lock, 0); 3282 1.5 christos ++seg->refcnt; 3283 1.5 christos EVLOCK_UNLOCK(seg->lock, 0); 3284 1.4 christos extra->segment = seg; 3285 1.4 christos buf->n_add_for_cb += length; 3286 1.4 christos evbuffer_chain_insert(buf, chain); 3287 1.4 christos 3288 1.4 christos evbuffer_invoke_callbacks_(buf); 3289 1.4 christos 3290 1.4 christos EVBUFFER_UNLOCK(buf); 3291 1.2 christos 3292 1.4 christos return 0; 3293 1.4 christos err: 3294 1.4 christos EVBUFFER_UNLOCK(buf); 3295 1.4 christos evbuffer_file_segment_free(seg); /* Lowers the refcount */ 3296 1.4 christos return -1; 3297 1.2 christos } 3298 1.2 christos 3299 1.4 christos int 3300 1.4 christos evbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length) 3301 1.4 christos { 3302 1.4 christos struct evbuffer_file_segment *seg; 3303 1.4 christos unsigned flags = EVBUF_FS_CLOSE_ON_FREE; 3304 1.4 christos int r; 3305 1.4 christos 3306 1.4 christos seg = evbuffer_file_segment_new(fd, offset, length, flags); 3307 1.4 christos if (!seg) 3308 1.4 christos return -1; 3309 1.4 christos r = evbuffer_add_file_segment(buf, seg, 0, length); 3310 1.4 christos if (r == 0) 3311 1.4 christos evbuffer_file_segment_free(seg); 3312 1.4 christos return r; 3313 1.4 christos } 3314 1.2 christos 3315 1.5 christos int 3316 1.2 christos evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg) 3317 1.2 christos { 3318 1.2 christos EVBUFFER_LOCK(buffer); 3319 1.2 christos 3320 1.4 christos if (!LIST_EMPTY(&buffer->callbacks)) 3321 1.2 christos evbuffer_remove_all_callbacks(buffer); 3322 1.2 christos 3323 1.2 christos if (cb) { 3324 1.2 christos struct evbuffer_cb_entry *ent = 3325 1.2 christos evbuffer_add_cb(buffer, NULL, cbarg); 3326 1.5 christos if (!ent) { 3327 1.5 christos EVBUFFER_UNLOCK(buffer); 3328 1.5 christos return -1; 3329 1.5 christos } 3330 1.2 christos ent->cb.cb_obsolete = cb; 3331 1.2 christos ent->flags |= EVBUFFER_CB_OBSOLETE; 3332 1.2 christos } 3333 1.2 christos EVBUFFER_UNLOCK(buffer); 3334 1.5 christos return 0; 3335 1.2 christos } 3336 1.2 christos 3337 1.2 christos struct evbuffer_cb_entry * 3338 1.2 christos evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg) 3339 1.2 christos { 3340 1.2 christos struct evbuffer_cb_entry *e; 3341 1.2 christos if (! (e = mm_calloc(1, sizeof(struct evbuffer_cb_entry)))) 3342 1.2 christos return NULL; 3343 1.2 christos EVBUFFER_LOCK(buffer); 3344 1.2 christos e->cb.cb_func = cb; 3345 1.2 christos e->cbarg = cbarg; 3346 1.2 christos e->flags = EVBUFFER_CB_ENABLED; 3347 1.4 christos LIST_INSERT_HEAD(&buffer->callbacks, e, next); 3348 1.2 christos EVBUFFER_UNLOCK(buffer); 3349 1.2 christos return e; 3350 1.2 christos } 3351 1.2 christos 3352 1.2 christos int 3353 1.2 christos evbuffer_remove_cb_entry(struct evbuffer *buffer, 3354 1.2 christos struct evbuffer_cb_entry *ent) 3355 1.2 christos { 3356 1.2 christos EVBUFFER_LOCK(buffer); 3357 1.4 christos LIST_REMOVE(ent, next); 3358 1.2 christos EVBUFFER_UNLOCK(buffer); 3359 1.2 christos mm_free(ent); 3360 1.2 christos return 0; 3361 1.2 christos } 3362 1.2 christos 3363 1.2 christos int 3364 1.2 christos evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg) 3365 1.2 christos { 3366 1.2 christos struct evbuffer_cb_entry *cbent; 3367 1.2 christos int result = -1; 3368 1.2 christos EVBUFFER_LOCK(buffer); 3369 1.4 christos LIST_FOREACH(cbent, &buffer->callbacks, next) { 3370 1.2 christos if (cb == cbent->cb.cb_func && cbarg == cbent->cbarg) { 3371 1.2 christos result = evbuffer_remove_cb_entry(buffer, cbent); 3372 1.2 christos goto done; 3373 1.2 christos } 3374 1.2 christos } 3375 1.2 christos done: 3376 1.2 christos EVBUFFER_UNLOCK(buffer); 3377 1.2 christos return result; 3378 1.2 christos } 3379 1.2 christos 3380 1.2 christos int 3381 1.2 christos evbuffer_cb_set_flags(struct evbuffer *buffer, 3382 1.2 christos struct evbuffer_cb_entry *cb, ev_uint32_t flags) 3383 1.2 christos { 3384 1.2 christos /* the user isn't allowed to mess with these. */ 3385 1.2 christos flags &= ~EVBUFFER_CB_INTERNAL_FLAGS; 3386 1.2 christos EVBUFFER_LOCK(buffer); 3387 1.2 christos cb->flags |= flags; 3388 1.2 christos EVBUFFER_UNLOCK(buffer); 3389 1.2 christos return 0; 3390 1.2 christos } 3391 1.2 christos 3392 1.2 christos int 3393 1.2 christos evbuffer_cb_clear_flags(struct evbuffer *buffer, 3394 1.2 christos struct evbuffer_cb_entry *cb, ev_uint32_t flags) 3395 1.2 christos { 3396 1.2 christos /* the user isn't allowed to mess with these. */ 3397 1.2 christos flags &= ~EVBUFFER_CB_INTERNAL_FLAGS; 3398 1.2 christos EVBUFFER_LOCK(buffer); 3399 1.2 christos cb->flags &= ~flags; 3400 1.2 christos EVBUFFER_UNLOCK(buffer); 3401 1.2 christos return 0; 3402 1.2 christos } 3403 1.2 christos 3404 1.2 christos int 3405 1.2 christos evbuffer_freeze(struct evbuffer *buffer, int start) 3406 1.2 christos { 3407 1.2 christos EVBUFFER_LOCK(buffer); 3408 1.2 christos if (start) 3409 1.2 christos buffer->freeze_start = 1; 3410 1.2 christos else 3411 1.2 christos buffer->freeze_end = 1; 3412 1.2 christos EVBUFFER_UNLOCK(buffer); 3413 1.2 christos return 0; 3414 1.2 christos } 3415 1.2 christos 3416 1.2 christos int 3417 1.2 christos evbuffer_unfreeze(struct evbuffer *buffer, int start) 3418 1.2 christos { 3419 1.2 christos EVBUFFER_LOCK(buffer); 3420 1.2 christos if (start) 3421 1.2 christos buffer->freeze_start = 0; 3422 1.2 christos else 3423 1.2 christos buffer->freeze_end = 0; 3424 1.2 christos EVBUFFER_UNLOCK(buffer); 3425 1.2 christos return 0; 3426 1.2 christos } 3427 1.2 christos 3428 1.2 christos #if 0 3429 1.2 christos void 3430 1.2 christos evbuffer_cb_suspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb) 3431 1.2 christos { 3432 1.2 christos if (!(cb->flags & EVBUFFER_CB_SUSPENDED)) { 3433 1.2 christos cb->size_before_suspend = evbuffer_get_length(buffer); 3434 1.2 christos cb->flags |= EVBUFFER_CB_SUSPENDED; 3435 1.2 christos } 3436 1.2 christos } 3437 1.2 christos 3438 1.2 christos void 3439 1.2 christos evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb) 3440 1.2 christos { 3441 1.2 christos if ((cb->flags & EVBUFFER_CB_SUSPENDED)) { 3442 1.2 christos unsigned call = (cb->flags & EVBUFFER_CB_CALL_ON_UNSUSPEND); 3443 1.2 christos size_t sz = cb->size_before_suspend; 3444 1.2 christos cb->flags &= ~(EVBUFFER_CB_SUSPENDED| 3445 1.2 christos EVBUFFER_CB_CALL_ON_UNSUSPEND); 3446 1.2 christos cb->size_before_suspend = 0; 3447 1.2 christos if (call && (cb->flags & EVBUFFER_CB_ENABLED)) { 3448 1.2 christos cb->cb(buffer, sz, evbuffer_get_length(buffer), cb->cbarg); 3449 1.2 christos } 3450 1.2 christos } 3451 1.2 christos } 3452 1.2 christos #endif 3453 1.2 christos 3454 1.2 christos int 3455 1.4 christos evbuffer_get_callbacks_(struct evbuffer *buffer, struct event_callback **cbs, 3456 1.4 christos int max_cbs) 3457 1.2 christos { 3458 1.4 christos int r = 0; 3459 1.4 christos EVBUFFER_LOCK(buffer); 3460 1.4 christos if (buffer->deferred_cbs) { 3461 1.4 christos if (max_cbs < 1) { 3462 1.4 christos r = -1; 3463 1.4 christos goto done; 3464 1.4 christos } 3465 1.4 christos cbs[0] = &buffer->deferred; 3466 1.4 christos r = 1; 3467 1.4 christos } 3468 1.4 christos done: 3469 1.4 christos EVBUFFER_UNLOCK(buffer); 3470 1.4 christos return r; 3471 1.1 plunky } 3472