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