1ec2c1db4SNiels Provos /*
2b85b710cSNick Mathewson * Copyright (c) 2002-2007 Niels Provos <[email protected]>
3e49e2891SNick Mathewson * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4ec2c1db4SNiels Provos *
5849d5249SNiels Provos * Redistribution and use in source and binary forms, with or without
6849d5249SNiels Provos * modification, are permitted provided that the following conditions
7849d5249SNiels Provos * are met:
8849d5249SNiels Provos * 1. Redistributions of source code must retain the above copyright
9849d5249SNiels Provos * notice, this list of conditions and the following disclaimer.
10849d5249SNiels Provos * 2. Redistributions in binary form must reproduce the above copyright
11849d5249SNiels Provos * notice, this list of conditions and the following disclaimer in the
12849d5249SNiels Provos * documentation and/or other materials provided with the distribution.
13849d5249SNiels Provos * 3. The name of the author may not be used to endorse or promote products
14849d5249SNiels Provos * derived from this software without specific prior written permission.
15849d5249SNiels Provos *
16849d5249SNiels Provos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17849d5249SNiels Provos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18849d5249SNiels Provos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19849d5249SNiels Provos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20849d5249SNiels Provos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21849d5249SNiels Provos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22849d5249SNiels Provos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23849d5249SNiels Provos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24849d5249SNiels Provos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25849d5249SNiels Provos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26ec2c1db4SNiels Provos */
27ec2c1db4SNiels Provos
28ec347b92SNick Mathewson #include "event2/event-config.h"
299b27b307SKevin Bowling #include "evconfig-private.h"
30ec2c1db4SNiels Provos
319f560bfaSNick Mathewson #ifdef _WIN32
32db43c1e1SNick Mathewson #include <winsock2.h>
334e1ec3e0SNick Mathewson #include <windows.h>
34e865eb93SNick Mathewson #include <io.h>
35db43c1e1SNick Mathewson #endif
36db43c1e1SNick Mathewson
3768120d9bSNick Mathewson #ifdef EVENT__HAVE_VASPRINTF
38c51ef930SKevin Bowling /* If we have vasprintf, we need to define _GNU_SOURCE before we include
39c13e1859SKevin Bowling * stdio.h. This comes from evconfig-private.h.
40c51ef930SKevin Bowling */
4132bed8f9SNiels Provos #endif
4232bed8f9SNiels Provos
4332bed8f9SNiels Provos #include <sys/types.h>
4432bed8f9SNiels Provos
4568120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_TIME_H
46ec2c1db4SNiels Provos #include <sys/time.h>
47ec2c1db4SNiels Provos #endif
48ec2c1db4SNiels Provos
4968120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_SOCKET_H
50fdf69493SNiels Provos #include <sys/socket.h>
51fdf69493SNiels Provos #endif
52fdf69493SNiels Provos
5368120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_UIO_H
545c70ea4cSNiels Provos #include <sys/uio.h>
555c70ea4cSNiels Provos #endif
565c70ea4cSNiels Provos
5768120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_IOCTL_H
5844d88ea6SNiels Provos #include <sys/ioctl.h>
5944d88ea6SNiels Provos #endif
6044d88ea6SNiels Provos
6168120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_MMAN_H
62fdf69493SNiels Provos #include <sys/mman.h>
63fdf69493SNiels Provos #endif
64fdf69493SNiels Provos
6568120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_SENDFILE_H
66fdf69493SNiels Provos #include <sys/sendfile.h>
67fdf69493SNiels Provos #endif
6868120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_STAT_H
69e72afae0SNick Mathewson #include <sys/stat.h>
70e72afae0SNick Mathewson #endif
71e72afae0SNick Mathewson
72fdf69493SNiels Provos
73ec2c1db4SNiels Provos #include <errno.h>
74ec2c1db4SNiels Provos #include <stdio.h>
75ec2c1db4SNiels Provos #include <stdlib.h>
76ec2c1db4SNiels Provos #include <string.h>
7768120d9bSNick Mathewson #ifdef EVENT__HAVE_STDARG_H
78ec2c1db4SNiels Provos #include <stdarg.h>
79ec2c1db4SNiels Provos #endif
8068120d9bSNick Mathewson #ifdef EVENT__HAVE_UNISTD_H
81ec2c1db4SNiels Provos #include <unistd.h>
82849d5249SNiels Provos #endif
83fdc640b0SNick Mathewson #include <limits.h>
84ec2c1db4SNiels Provos
850ac73078SNick Mathewson #include "event2/event.h"
860ac73078SNick Mathewson #include "event2/buffer.h"
87ec2f4cbcSNick Mathewson #include "event2/buffer_compat.h"
88d7d1f1daSNick Mathewson #include "event2/bufferevent.h"
89d7d1f1daSNick Mathewson #include "event2/bufferevent_compat.h"
90d7d1f1daSNick Mathewson #include "event2/bufferevent_struct.h"
9160e0d59bSNick Mathewson #include "event2/thread.h"
92169321c9SNick Mathewson #include "log-internal.h"
937eb250e9SNick Mathewson #include "mm-internal.h"
94fdf69493SNiels Provos #include "util-internal.h"
9560e0d59bSNick Mathewson #include "evthread-internal.h"
965c70ea4cSNiels Provos #include "evbuffer-internal.h"
97d7d1f1daSNick Mathewson #include "bufferevent-internal.h"
98cdd52e7fSJiri Luznicky #include "event-internal.h"
995c70ea4cSNiels Provos
100fdf69493SNiels Provos /* some systems do not have MAP_FAILED */
101fdf69493SNiels Provos #ifndef MAP_FAILED
102fdf69493SNiels Provos #define MAP_FAILED ((void *)-1)
103fdf69493SNiels Provos #endif
104fdf69493SNiels Provos
105fdf69493SNiels Provos /* send file support */
10668120d9bSNick Mathewson #if defined(EVENT__HAVE_SYS_SENDFILE_H) && defined(EVENT__HAVE_SENDFILE) && defined(__linux__)
107fdf69493SNiels Provos #define USE_SENDFILE 1
108fdf69493SNiels Provos #define SENDFILE_IS_LINUX 1
10968120d9bSNick Mathewson #elif defined(EVENT__HAVE_SENDFILE) && defined(__FreeBSD__)
110fdf69493SNiels Provos #define USE_SENDFILE 1
111fdf69493SNiels Provos #define SENDFILE_IS_FREEBSD 1
11268120d9bSNick Mathewson #elif defined(EVENT__HAVE_SENDFILE) && defined(__APPLE__)
1135c4c13d8SNiels Provos #define USE_SENDFILE 1
1145c4c13d8SNiels Provos #define SENDFILE_IS_MACOSX 1
11568120d9bSNick Mathewson #elif defined(EVENT__HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__)
11622bd5b42SNick Mathewson #define USE_SENDFILE 1
11722bd5b42SNick Mathewson #define SENDFILE_IS_SOLARIS 1
118fdf69493SNiels Provos #endif
119fdf69493SNiels Provos
1208d3a10f8SNick Mathewson /* Mask of user-selectable callback flags. */
1218d3a10f8SNick Mathewson #define EVBUFFER_CB_USER_FLAGS 0xffff
1228d3a10f8SNick Mathewson /* Mask of all internal-use-only flags. */
1238d3a10f8SNick Mathewson #define EVBUFFER_CB_INTERNAL_FLAGS 0xffff0000
124f1b1bad4SNick Mathewson
125f1b1bad4SNick Mathewson /* Flag set if the callback is using the cb_obsolete function pointer */
126f1b1bad4SNick Mathewson #define EVBUFFER_CB_OBSOLETE 0x00040000
1278d3a10f8SNick Mathewson
128fdf69493SNiels Provos /* evbuffer_chain support */
129f6eb1f81SNick Mathewson #define CHAIN_SPACE_PTR(ch) ((ch)->buffer + (ch)->misalign + (ch)->off)
130fdf69493SNiels Provos #define CHAIN_SPACE_LEN(ch) ((ch)->flags & EVBUFFER_IMMUTABLE ? \
131fdf69493SNiels Provos 0 : (ch)->buffer_len - ((ch)->misalign + (ch)->off))
132f6eb1f81SNick Mathewson
133d9086fc0SNick Mathewson #define CHAIN_PINNED(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_ANY) != 0)
1340e32ba54SNick Mathewson #define CHAIN_PINNED_R(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_R) != 0)
135d9086fc0SNick Mathewson
136e3e97ae3SNir Soffer /* evbuffer_ptr support */
137e3e97ae3SNir Soffer #define PTR_NOT_FOUND(ptr) do { \
138e3e97ae3SNir Soffer (ptr)->pos = -1; \
139cb9da0bfSNick Mathewson (ptr)->internal_.chain = NULL; \
140cb9da0bfSNick Mathewson (ptr)->internal_.pos_in_chain = 0; \
141e3e97ae3SNir Soffer } while (0)
142e3e97ae3SNir Soffer
143d9086fc0SNick Mathewson static void evbuffer_chain_align(struct evbuffer_chain *chain);
144e4f34e8aSNick Mathewson static int evbuffer_chain_should_realign(struct evbuffer_chain *chain,
145e4f34e8aSNick Mathewson size_t datalen);
146a4079aa8SNick Mathewson static void evbuffer_deferred_callback(struct event_callback *cb, void *arg);
147d4134772SNick Mathewson static int evbuffer_ptr_memcmp(const struct evbuffer *buf,
148d4134772SNick Mathewson const struct evbuffer_ptr *pos, const char *mem, size_t len);
149d5ebcf37SNick Mathewson static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
150d5ebcf37SNick Mathewson size_t datlen);
1517b9d1395SNick Mathewson static int evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos,
1527b9d1395SNick Mathewson size_t howfar);
153c6bbbf1bSNick Mathewson static int evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg);
1549d7368aeSJoachim Bauch static inline void evbuffer_chain_incref(struct evbuffer_chain *chain);
155d9086fc0SNick Mathewson
1565c70ea4cSNiels Provos static struct evbuffer_chain *
evbuffer_chain_new(size_t size)1575c70ea4cSNiels Provos evbuffer_chain_new(size_t size)
1585c70ea4cSNiels Provos {
1595c70ea4cSNiels Provos struct evbuffer_chain *chain;
1605c70ea4cSNiels Provos size_t to_alloc;
1615c70ea4cSNiels Provos
162841ecbd9SNick Mathewson if (size > EVBUFFER_CHAIN_MAX - EVBUFFER_CHAIN_SIZE)
163841ecbd9SNick Mathewson return (NULL);
164841ecbd9SNick Mathewson
1655c70ea4cSNiels Provos size += EVBUFFER_CHAIN_SIZE;
1665c70ea4cSNiels Provos
1675c70ea4cSNiels Provos /* get the next largest memory that can hold the buffer */
168841ecbd9SNick Mathewson if (size < EVBUFFER_CHAIN_MAX / 2) {
1695c70ea4cSNiels Provos to_alloc = MIN_BUFFER_SIZE;
170841ecbd9SNick Mathewson while (to_alloc < size) {
1715c70ea4cSNiels Provos to_alloc <<= 1;
172841ecbd9SNick Mathewson }
173841ecbd9SNick Mathewson } else {
174841ecbd9SNick Mathewson to_alloc = size;
175841ecbd9SNick Mathewson }
1765c70ea4cSNiels Provos
1775c70ea4cSNiels Provos /* we get everything in one chunk */
17849868b61SNick Mathewson if ((chain = mm_malloc(to_alloc)) == NULL)
1795c70ea4cSNiels Provos return (NULL);
1805c70ea4cSNiels Provos
1815c70ea4cSNiels Provos memset(chain, 0, EVBUFFER_CHAIN_SIZE);
1825c70ea4cSNiels Provos
1835c70ea4cSNiels Provos chain->buffer_len = to_alloc - EVBUFFER_CHAIN_SIZE;
1845c70ea4cSNiels Provos
185fdf69493SNiels Provos /* this way we can manipulate the buffer to different addresses,
186fdf69493SNiels Provos * which is required for mmap for example.
187fdf69493SNiels Provos */
188fd36647aSEd Schouten chain->buffer = EVBUFFER_CHAIN_EXTRA(unsigned char, chain);
189fdf69493SNiels Provos
190a8e5e2fcSJoachim Bauch chain->refcnt = 1;
191a8e5e2fcSJoachim Bauch
1925c70ea4cSNiels Provos return (chain);
1935c70ea4cSNiels Provos }
194ec2c1db4SNiels Provos
195fdf69493SNiels Provos static inline void
evbuffer_chain_free(struct evbuffer_chain * chain)196fdf69493SNiels Provos evbuffer_chain_free(struct evbuffer_chain *chain)
197fdf69493SNiels Provos {
198a8e5e2fcSJoachim Bauch EVUTIL_ASSERT(chain->refcnt > 0);
199a8e5e2fcSJoachim Bauch if (--chain->refcnt > 0) {
2004dee4cc7SNick Mathewson /* chain is still referenced by other chains */
201d9086fc0SNick Mathewson return;
202d9086fc0SNick Mathewson }
203a8e5e2fcSJoachim Bauch
204ec2c1db4SNiels Provos if (CHAIN_PINNED(chain)) {
2054dee4cc7SNick Mathewson /* will get freed once no longer dangling */
206a8e5e2fcSJoachim Bauch chain->refcnt++;
207fdf69493SNiels Provos chain->flags |= EVBUFFER_DANGLING;
208fdf69493SNiels Provos return;
209fdf69493SNiels Provos }
210e72afae0SNick Mathewson
2114dee4cc7SNick Mathewson /* safe to release chain, it's either a referencing
2124dee4cc7SNick Mathewson * chain or all references to it have been freed */
213fdf69493SNiels Provos if (chain->flags & EVBUFFER_REFERENCE) {
214fdf69493SNiels Provos struct evbuffer_chain_reference *info =
215fdf69493SNiels Provos EVBUFFER_CHAIN_EXTRA(
216fdf69493SNiels Provos struct evbuffer_chain_reference,
217fdf69493SNiels Provos chain);
218fdf69493SNiels Provos if (info->cleanupfn)
219dc4c7b95SNick Mathewson (*info->cleanupfn)(chain->buffer,
220dc4c7b95SNick Mathewson chain->buffer_len,
221dc4c7b95SNick Mathewson info->extra);
222fdf69493SNiels Provos }
223e72afae0SNick Mathewson if (chain->flags & EVBUFFER_FILESEGMENT) {
224e72afae0SNick Mathewson struct evbuffer_chain_file_segment *info =
225e72afae0SNick Mathewson EVBUFFER_CHAIN_EXTRA(
226e72afae0SNick Mathewson struct evbuffer_chain_file_segment,
227fdf69493SNiels Provos chain);
2283f405d2dSNick Mathewson if (info->segment) {
2299f560bfaSNick Mathewson #ifdef _WIN32
230c6bbbf1bSNick Mathewson if (info->segment->is_mapping)
2313f405d2dSNick Mathewson UnmapViewOfFile(chain->buffer);
2323f405d2dSNick Mathewson #endif
233e72afae0SNick Mathewson evbuffer_file_segment_free(info->segment);
234fdf69493SNiels Provos }
2353f405d2dSNick Mathewson }
2369d7368aeSJoachim Bauch if (chain->flags & EVBUFFER_MULTICAST) {
2379d7368aeSJoachim Bauch struct evbuffer_multicast_parent *info =
2389d7368aeSJoachim Bauch EVBUFFER_CHAIN_EXTRA(
2399d7368aeSJoachim Bauch struct evbuffer_multicast_parent,
2409d7368aeSJoachim Bauch chain);
2414dee4cc7SNick Mathewson /* referencing chain is being freed, decrease
2424dee4cc7SNick Mathewson * refcounts of source chain and associated
2434dee4cc7SNick Mathewson * evbuffer (which get freed once both reach
2444dee4cc7SNick Mathewson * zero) */
24526041a8eSJoachim Bauch EVUTIL_ASSERT(info->source != NULL);
2469d7368aeSJoachim Bauch EVUTIL_ASSERT(info->parent != NULL);
24726041a8eSJoachim Bauch EVBUFFER_LOCK(info->source);
248a8e5e2fcSJoachim Bauch evbuffer_chain_free(info->parent);
249cb9da0bfSNick Mathewson evbuffer_decref_and_unlock_(info->source);
2509d7368aeSJoachim Bauch }
251b7442f8eSNick Mathewson
252fdf69493SNiels Provos mm_free(chain);
253fdf69493SNiels Provos }
254fdf69493SNiels Provos
255d5ebcf37SNick Mathewson static void
evbuffer_free_all_chains(struct evbuffer_chain * chain)256d5ebcf37SNick Mathewson evbuffer_free_all_chains(struct evbuffer_chain *chain)
257d5ebcf37SNick Mathewson {
258d5ebcf37SNick Mathewson struct evbuffer_chain *next;
259d5ebcf37SNick Mathewson for (; chain; chain = next) {
260d5ebcf37SNick Mathewson next = chain->next;
261d5ebcf37SNick Mathewson evbuffer_chain_free(chain);
262d5ebcf37SNick Mathewson }
263d5ebcf37SNick Mathewson }
264b7442f8eSNick Mathewson
265743f8665SNick Mathewson #ifndef NDEBUG
266d5ebcf37SNick Mathewson static int
evbuffer_chains_all_empty(struct evbuffer_chain * chain)267d5ebcf37SNick Mathewson evbuffer_chains_all_empty(struct evbuffer_chain *chain)
268d5ebcf37SNick Mathewson {
269d5ebcf37SNick Mathewson for (; chain; chain = chain->next) {
270d5ebcf37SNick Mathewson if (chain->off)
271d5ebcf37SNick Mathewson return 0;
272d5ebcf37SNick Mathewson }
273d5ebcf37SNick Mathewson return 1;
274d5ebcf37SNick Mathewson }
275b63ab177SEvan Jones #else
276b63ab177SEvan Jones /* The definition is needed for EVUTIL_ASSERT, which uses sizeof to avoid
277b63ab177SEvan Jones "unused variable" warnings. */
evbuffer_chains_all_empty(struct evbuffer_chain * chain)278b63ab177SEvan Jones static inline int evbuffer_chains_all_empty(struct evbuffer_chain *chain) {
279b63ab177SEvan Jones return 1;
280b63ab177SEvan Jones }
281743f8665SNick Mathewson #endif
282d5ebcf37SNick Mathewson
283c37069cdSNick Mathewson /* Free all trailing chains in 'buf' that are neither pinned nor empty, prior
284c37069cdSNick Mathewson * to replacing them all with a new chain. Return a pointer to the place
285c37069cdSNick Mathewson * where the new chain will go.
286c37069cdSNick Mathewson *
287c37069cdSNick Mathewson * Internal; requires lock. The caller must fix up buf->last and buf->first
288c37069cdSNick Mathewson * as needed; they might have been freed.
289c37069cdSNick Mathewson */
290c37069cdSNick Mathewson static struct evbuffer_chain **
evbuffer_free_trailing_empty_chains(struct evbuffer * buf)291c37069cdSNick Mathewson evbuffer_free_trailing_empty_chains(struct evbuffer *buf)
292c37069cdSNick Mathewson {
293c37069cdSNick Mathewson struct evbuffer_chain **ch = buf->last_with_datap;
294c37069cdSNick Mathewson /* Find the first victim chain. It might be *last_with_datap */
295c37069cdSNick Mathewson while ((*ch) && ((*ch)->off != 0 || CHAIN_PINNED(*ch)))
296c37069cdSNick Mathewson ch = &(*ch)->next;
297c37069cdSNick Mathewson if (*ch) {
298c37069cdSNick Mathewson EVUTIL_ASSERT(evbuffer_chains_all_empty(*ch));
299c37069cdSNick Mathewson evbuffer_free_all_chains(*ch);
300c37069cdSNick Mathewson *ch = NULL;
301c37069cdSNick Mathewson }
302c37069cdSNick Mathewson return ch;
303c37069cdSNick Mathewson }
304c37069cdSNick Mathewson
305c37069cdSNick Mathewson /* Add a single chain 'chain' to the end of 'buf', freeing trailing empty
306c37069cdSNick Mathewson * chains as necessary. Requires lock. Does not schedule callbacks.
307c37069cdSNick Mathewson */
308d5ebcf37SNick Mathewson static void
evbuffer_chain_insert(struct evbuffer * buf,struct evbuffer_chain * chain)309d5ebcf37SNick Mathewson evbuffer_chain_insert(struct evbuffer *buf,
310d5ebcf37SNick Mathewson struct evbuffer_chain *chain)
311fdf69493SNiels Provos {
31260e0d59bSNick Mathewson ASSERT_EVBUFFER_LOCKED(buf);
313d5ebcf37SNick Mathewson if (*buf->last_with_datap == NULL) {
314d5ebcf37SNick Mathewson /* There are no chains data on the buffer at all. */
315d5ebcf37SNick Mathewson EVUTIL_ASSERT(buf->last_with_datap == &buf->first);
316d5ebcf37SNick Mathewson EVUTIL_ASSERT(buf->first == NULL);
317fdf69493SNiels Provos buf->first = buf->last = chain;
318fdf69493SNiels Provos } else {
319b18c04ddSNick Mathewson struct evbuffer_chain **chp;
320b18c04ddSNick Mathewson chp = evbuffer_free_trailing_empty_chains(buf);
321b18c04ddSNick Mathewson *chp = chain;
322b7442f8eSNick Mathewson if (chain->off)
323b18c04ddSNick Mathewson buf->last_with_datap = chp;
324fdf69493SNiels Provos buf->last = chain;
325fdf69493SNiels Provos }
326d5ebcf37SNick Mathewson buf->total_len += chain->off;
327fdf69493SNiels Provos }
328fdf69493SNiels Provos
329d5ebcf37SNick Mathewson static inline struct evbuffer_chain *
evbuffer_chain_insert_new(struct evbuffer * buf,size_t datlen)330d5ebcf37SNick Mathewson evbuffer_chain_insert_new(struct evbuffer *buf, size_t datlen)
331d5ebcf37SNick Mathewson {
332d5ebcf37SNick Mathewson struct evbuffer_chain *chain;
333d5ebcf37SNick Mathewson if ((chain = evbuffer_chain_new(datlen)) == NULL)
334d5ebcf37SNick Mathewson return NULL;
335d5ebcf37SNick Mathewson evbuffer_chain_insert(buf, chain);
336d5ebcf37SNick Mathewson return chain;
337fdf69493SNiels Provos }
338fdf69493SNiels Provos
3399f1a94ecSNick Mathewson void
evbuffer_chain_pin_(struct evbuffer_chain * chain,unsigned flag)340cb9da0bfSNick Mathewson evbuffer_chain_pin_(struct evbuffer_chain *chain, unsigned flag)
341d9086fc0SNick Mathewson {
3422e36dbe1SNick Mathewson EVUTIL_ASSERT((chain->flags & flag) == 0);
343d9086fc0SNick Mathewson chain->flags |= flag;
344d9086fc0SNick Mathewson }
345d9086fc0SNick Mathewson
3469f1a94ecSNick Mathewson void
evbuffer_chain_unpin_(struct evbuffer_chain * chain,unsigned flag)347cb9da0bfSNick Mathewson evbuffer_chain_unpin_(struct evbuffer_chain *chain, unsigned flag)
348d9086fc0SNick Mathewson {
3492e36dbe1SNick Mathewson EVUTIL_ASSERT((chain->flags & flag) != 0);
350d9086fc0SNick Mathewson chain->flags &= ~flag;
351d9086fc0SNick Mathewson if (chain->flags & EVBUFFER_DANGLING)
352d9086fc0SNick Mathewson evbuffer_chain_free(chain);
353d9086fc0SNick Mathewson }
354d9086fc0SNick Mathewson
3559d7368aeSJoachim Bauch static inline void
evbuffer_chain_incref(struct evbuffer_chain * chain)3569d7368aeSJoachim Bauch evbuffer_chain_incref(struct evbuffer_chain *chain)
3579d7368aeSJoachim Bauch {
3589d7368aeSJoachim Bauch ++chain->refcnt;
3599d7368aeSJoachim Bauch }
3609d7368aeSJoachim Bauch
361ec2c1db4SNiels Provos struct evbuffer *
evbuffer_new(void)362ec2c1db4SNiels Provos evbuffer_new(void)
363ec2c1db4SNiels Provos {
364ec2c1db4SNiels Provos struct evbuffer *buffer;
365ec2c1db4SNiels Provos
36649868b61SNick Mathewson buffer = mm_calloc(1, sizeof(struct evbuffer));
367f1691539SNiels Provos if (buffer == NULL)
368f1691539SNiels Provos return (NULL);
369ec2c1db4SNiels Provos
370d313c293SNick Mathewson LIST_INIT(&buffer->callbacks);
371dcda7915SNick Mathewson buffer->refcnt = 1;
372b7442f8eSNick Mathewson buffer->last_with_datap = &buffer->first;
373c735f2b4SNick Mathewson
374ec2c1db4SNiels Provos return (buffer);
375ec2c1db4SNiels Provos }
376ec2c1db4SNiels Provos
3770ba0af9cSNick Mathewson int
evbuffer_set_flags(struct evbuffer * buf,ev_uint64_t flags)3780ba0af9cSNick Mathewson evbuffer_set_flags(struct evbuffer *buf, ev_uint64_t flags)
3790ba0af9cSNick Mathewson {
3800ba0af9cSNick Mathewson EVBUFFER_LOCK(buf);
3810ba0af9cSNick Mathewson buf->flags |= (ev_uint32_t)flags;
3820ba0af9cSNick Mathewson EVBUFFER_UNLOCK(buf);
3830ba0af9cSNick Mathewson return 0;
3840ba0af9cSNick Mathewson }
3850ba0af9cSNick Mathewson
3860ba0af9cSNick Mathewson int
evbuffer_clear_flags(struct evbuffer * buf,ev_uint64_t flags)3870ba0af9cSNick Mathewson evbuffer_clear_flags(struct evbuffer *buf, ev_uint64_t flags)
3880ba0af9cSNick Mathewson {
3890ba0af9cSNick Mathewson EVBUFFER_LOCK(buf);
3900ba0af9cSNick Mathewson buf->flags &= ~(ev_uint32_t)flags;
3910ba0af9cSNick Mathewson EVBUFFER_UNLOCK(buf);
3920ba0af9cSNick Mathewson return 0;
3930ba0af9cSNick Mathewson }
3940ba0af9cSNick Mathewson
395dcda7915SNick Mathewson void
evbuffer_incref_(struct evbuffer * buf)396cb9da0bfSNick Mathewson evbuffer_incref_(struct evbuffer *buf)
397dcda7915SNick Mathewson {
39876cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
399dcda7915SNick Mathewson ++buf->refcnt;
40076cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
401dcda7915SNick Mathewson }
402dcda7915SNick Mathewson
403d7d1f1daSNick Mathewson void
evbuffer_incref_and_lock_(struct evbuffer * buf)404cb9da0bfSNick Mathewson evbuffer_incref_and_lock_(struct evbuffer *buf)
405d7d1f1daSNick Mathewson {
40676cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
407d7d1f1daSNick Mathewson ++buf->refcnt;
408d7d1f1daSNick Mathewson }
409d7d1f1daSNick Mathewson
41060e0d59bSNick Mathewson int
evbuffer_defer_callbacks(struct evbuffer * buffer,struct event_base * base)411b29b875dSNick Mathewson evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base)
412b29b875dSNick Mathewson {
41376cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
414a4079aa8SNick Mathewson buffer->cb_queue = base;
415b29b875dSNick Mathewson buffer->deferred_cbs = 1;
416c0e425abSNick Mathewson event_deferred_cb_init_(&buffer->deferred,
417c0e425abSNick Mathewson event_base_get_npriorities(base) / 2,
418b29b875dSNick Mathewson evbuffer_deferred_callback, buffer);
41976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
420b29b875dSNick Mathewson return 0;
421b29b875dSNick Mathewson }
422b29b875dSNick Mathewson
423b29b875dSNick Mathewson int
evbuffer_enable_locking(struct evbuffer * buf,void * lock)42460e0d59bSNick Mathewson evbuffer_enable_locking(struct evbuffer *buf, void *lock)
42560e0d59bSNick Mathewson {
42668120d9bSNick Mathewson #ifdef EVENT__DISABLE_THREAD_SUPPORT
42760e0d59bSNick Mathewson return -1;
42860e0d59bSNick Mathewson #else
42960e0d59bSNick Mathewson if (buf->lock)
43060e0d59bSNick Mathewson return -1;
43160e0d59bSNick Mathewson
43260e0d59bSNick Mathewson if (!lock) {
433347952ffSNick Mathewson EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
43460e0d59bSNick Mathewson if (!lock)
43560e0d59bSNick Mathewson return -1;
43660e0d59bSNick Mathewson buf->lock = lock;
43760e0d59bSNick Mathewson buf->own_lock = 1;
43860e0d59bSNick Mathewson } else {
43960e0d59bSNick Mathewson buf->lock = lock;
44060e0d59bSNick Mathewson buf->own_lock = 0;
44160e0d59bSNick Mathewson }
44260e0d59bSNick Mathewson
44360e0d59bSNick Mathewson return 0;
44460e0d59bSNick Mathewson #endif
44560e0d59bSNick Mathewson }
44660e0d59bSNick Mathewson
447d7d1f1daSNick Mathewson void
evbuffer_set_parent_(struct evbuffer * buf,struct bufferevent * bev)4488ac3c4c2SNick Mathewson evbuffer_set_parent_(struct evbuffer *buf, struct bufferevent *bev)
449d7d1f1daSNick Mathewson {
45076cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
451d7d1f1daSNick Mathewson buf->parent = bev;
45276cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
453d7d1f1daSNick Mathewson }
454d7d1f1daSNick Mathewson
455b29b875dSNick Mathewson static void
evbuffer_run_callbacks(struct evbuffer * buffer,int running_deferred)456438f9ed2SNick Mathewson evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
457c735f2b4SNick Mathewson {
458c735f2b4SNick Mathewson struct evbuffer_cb_entry *cbent, *next;
459f1b1bad4SNick Mathewson struct evbuffer_cb_info info;
460f1b1bad4SNick Mathewson size_t new_size;
461a47d88d7SNick Mathewson ev_uint32_t mask, masked_val;
462390e0561SNick Mathewson int clear = 1;
463438f9ed2SNick Mathewson
464438f9ed2SNick Mathewson if (running_deferred) {
465438f9ed2SNick Mathewson mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
466438f9ed2SNick Mathewson masked_val = EVBUFFER_CB_ENABLED;
467438f9ed2SNick Mathewson } else if (buffer->deferred_cbs) {
468438f9ed2SNick Mathewson mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
469438f9ed2SNick Mathewson masked_val = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
47029151e65SNick Mathewson /* Don't zero-out n_add/n_del, since the deferred callbacks
47129151e65SNick Mathewson will want to see them. */
472390e0561SNick Mathewson clear = 0;
473438f9ed2SNick Mathewson } else {
474438f9ed2SNick Mathewson mask = EVBUFFER_CB_ENABLED;
475438f9ed2SNick Mathewson masked_val = EVBUFFER_CB_ENABLED;
476438f9ed2SNick Mathewson }
477f1b1bad4SNick Mathewson
47860e0d59bSNick Mathewson ASSERT_EVBUFFER_LOCKED(buffer);
47960e0d59bSNick Mathewson
480d313c293SNick Mathewson if (LIST_EMPTY(&buffer->callbacks)) {
481f1b1bad4SNick Mathewson buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
482f1b1bad4SNick Mathewson return;
483f1b1bad4SNick Mathewson }
484f1b1bad4SNick Mathewson if (buffer->n_add_for_cb == 0 && buffer->n_del_for_cb == 0)
48581dd04a7SNick Mathewson return;
48681dd04a7SNick Mathewson
487f1b1bad4SNick Mathewson new_size = buffer->total_len;
488f1b1bad4SNick Mathewson info.orig_size = new_size + buffer->n_del_for_cb - buffer->n_add_for_cb;
489f1b1bad4SNick Mathewson info.n_added = buffer->n_add_for_cb;
490f1b1bad4SNick Mathewson info.n_deleted = buffer->n_del_for_cb;
49129151e65SNick Mathewson if (clear) {
492f1b1bad4SNick Mathewson buffer->n_add_for_cb = 0;
493f1b1bad4SNick Mathewson buffer->n_del_for_cb = 0;
49429151e65SNick Mathewson }
495d313c293SNick Mathewson for (cbent = LIST_FIRST(&buffer->callbacks);
496d313c293SNick Mathewson cbent != LIST_END(&buffer->callbacks);
497c735f2b4SNick Mathewson cbent = next) {
49881dd04a7SNick Mathewson /* Get the 'next' pointer now in case this callback decides
49981dd04a7SNick Mathewson * to remove itself or something. */
500d313c293SNick Mathewson next = LIST_NEXT(cbent, next);
501438f9ed2SNick Mathewson
502438f9ed2SNick Mathewson if ((cbent->flags & mask) != masked_val)
503438f9ed2SNick Mathewson continue;
504438f9ed2SNick Mathewson
505f1b1bad4SNick Mathewson if ((cbent->flags & EVBUFFER_CB_OBSOLETE))
506f1b1bad4SNick Mathewson cbent->cb.cb_obsolete(buffer,
507f1b1bad4SNick Mathewson info.orig_size, new_size, cbent->cbarg);
508f1b1bad4SNick Mathewson else
509f1b1bad4SNick Mathewson cbent->cb.cb_func(buffer, &info, cbent->cbarg);
5108d3a10f8SNick Mathewson }
511c735f2b4SNick Mathewson }
512c735f2b4SNick Mathewson
5136acfbdd8SNick Mathewson void
evbuffer_invoke_callbacks_(struct evbuffer * buffer)5148ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(struct evbuffer *buffer)
515b29b875dSNick Mathewson {
5165683e2b1SNick Mathewson if (LIST_EMPTY(&buffer->callbacks)) {
517f87f5689SMark Ellzey buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
518f87f5689SMark Ellzey return;
519f87f5689SMark Ellzey }
520f87f5689SMark Ellzey
521b29b875dSNick Mathewson if (buffer->deferred_cbs) {
522ae2b84b2SNick Mathewson if (event_deferred_cb_schedule_(buffer->cb_queue, &buffer->deferred)) {
523cb9da0bfSNick Mathewson evbuffer_incref_and_lock_(buffer);
524d7d1f1daSNick Mathewson if (buffer->parent)
5258ac3c4c2SNick Mathewson bufferevent_incref_(buffer->parent);
52676cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
527b29b875dSNick Mathewson }
5282b4d127dSAzat Khuzhin }
529438f9ed2SNick Mathewson
530438f9ed2SNick Mathewson evbuffer_run_callbacks(buffer, 0);
531b29b875dSNick Mathewson }
532b29b875dSNick Mathewson
533b29b875dSNick Mathewson static void
evbuffer_deferred_callback(struct event_callback * cb,void * arg)534a4079aa8SNick Mathewson evbuffer_deferred_callback(struct event_callback *cb, void *arg)
535b29b875dSNick Mathewson {
536d7d1f1daSNick Mathewson struct bufferevent *parent = NULL;
537b29b875dSNick Mathewson struct evbuffer *buffer = arg;
538b29b875dSNick Mathewson
53924607a39SNick Mathewson /* XXXX It would be better to run these callbacks without holding the
54024607a39SNick Mathewson * lock */
54176cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
542d7d1f1daSNick Mathewson parent = buffer->parent;
543438f9ed2SNick Mathewson evbuffer_run_callbacks(buffer, 1);
544cb9da0bfSNick Mathewson evbuffer_decref_and_unlock_(buffer);
545d7d1f1daSNick Mathewson if (parent)
5468ac3c4c2SNick Mathewson bufferevent_decref_(parent);
547b29b875dSNick Mathewson }
548b29b875dSNick Mathewson
549c735f2b4SNick Mathewson static void
evbuffer_remove_all_callbacks(struct evbuffer * buffer)550c735f2b4SNick Mathewson evbuffer_remove_all_callbacks(struct evbuffer *buffer)
551c735f2b4SNick Mathewson {
552ec2f4cbcSNick Mathewson struct evbuffer_cb_entry *cbent;
55360e0d59bSNick Mathewson
554d313c293SNick Mathewson while ((cbent = LIST_FIRST(&buffer->callbacks))) {
555d313c293SNick Mathewson LIST_REMOVE(cbent, next);
556c735f2b4SNick Mathewson mm_free(cbent);
557c735f2b4SNick Mathewson }
558c735f2b4SNick Mathewson }
559c735f2b4SNick Mathewson
560ec2c1db4SNiels Provos void
evbuffer_decref_and_unlock_(struct evbuffer * buffer)561cb9da0bfSNick Mathewson evbuffer_decref_and_unlock_(struct evbuffer *buffer)
562ec2c1db4SNiels Provos {
5635c70ea4cSNiels Provos struct evbuffer_chain *chain, *next;
5649f1a94ecSNick Mathewson ASSERT_EVBUFFER_LOCKED(buffer);
56560e0d59bSNick Mathewson
566f1bc125eSNick Mathewson EVUTIL_ASSERT(buffer->refcnt > 0);
567f1bc125eSNick Mathewson
568dcda7915SNick Mathewson if (--buffer->refcnt > 0) {
56976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
570dcda7915SNick Mathewson return;
571dcda7915SNick Mathewson }
57260e0d59bSNick Mathewson
5735c70ea4cSNiels Provos for (chain = buffer->first; chain != NULL; chain = next) {
5745c70ea4cSNiels Provos next = chain->next;
575fdf69493SNiels Provos evbuffer_chain_free(chain);
5765c70ea4cSNiels Provos }
577c735f2b4SNick Mathewson evbuffer_remove_all_callbacks(buffer);
578b29b875dSNick Mathewson if (buffer->deferred_cbs)
5798ac3c4c2SNick Mathewson event_deferred_cb_cancel_(buffer->cb_queue, &buffer->deferred);
580dcda7915SNick Mathewson
58176cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
58260e0d59bSNick Mathewson if (buffer->own_lock)
583347952ffSNick Mathewson EVTHREAD_FREE_LOCK(buffer->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
58449868b61SNick Mathewson mm_free(buffer);
585ec2c1db4SNiels Provos }
586ec2c1db4SNiels Provos
58760e0d59bSNick Mathewson void
evbuffer_free(struct evbuffer * buffer)5889f1a94ecSNick Mathewson evbuffer_free(struct evbuffer *buffer)
5899f1a94ecSNick Mathewson {
59076cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
591cb9da0bfSNick Mathewson evbuffer_decref_and_unlock_(buffer);
5929f1a94ecSNick Mathewson }
5939f1a94ecSNick Mathewson
5949f1a94ecSNick Mathewson void
evbuffer_lock(struct evbuffer * buf)59560e0d59bSNick Mathewson evbuffer_lock(struct evbuffer *buf)
59660e0d59bSNick Mathewson {
59776cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
59860e0d59bSNick Mathewson }
59960e0d59bSNick Mathewson
60060e0d59bSNick Mathewson void
evbuffer_unlock(struct evbuffer * buf)60160e0d59bSNick Mathewson evbuffer_unlock(struct evbuffer *buf)
60260e0d59bSNick Mathewson {
60376cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
60460e0d59bSNick Mathewson }
60560e0d59bSNick Mathewson
6065c70ea4cSNiels Provos size_t
evbuffer_get_length(const struct evbuffer * buffer)60784031819SNick Mathewson evbuffer_get_length(const struct evbuffer *buffer)
6085c70ea4cSNiels Provos {
60960e0d59bSNick Mathewson size_t result;
61060e0d59bSNick Mathewson
61176cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
61260e0d59bSNick Mathewson
61360e0d59bSNick Mathewson result = (buffer->total_len);
61460e0d59bSNick Mathewson
61576cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
61660e0d59bSNick Mathewson
61760e0d59bSNick Mathewson return result;
6185c70ea4cSNiels Provos }
619025d1bc2SNiels Provos
620becc89b7SNiels Provos size_t
evbuffer_get_contiguous_space(const struct evbuffer * buf)62184031819SNick Mathewson evbuffer_get_contiguous_space(const struct evbuffer *buf)
622becc89b7SNiels Provos {
62360e0d59bSNick Mathewson struct evbuffer_chain *chain;
62460e0d59bSNick Mathewson size_t result;
625becc89b7SNiels Provos
62676cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
62760e0d59bSNick Mathewson chain = buf->first;
62860e0d59bSNick Mathewson result = (chain != NULL ? chain->off : 0);
62976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
63060e0d59bSNick Mathewson
63160e0d59bSNick Mathewson return result;
632becc89b7SNiels Provos }
633becc89b7SNiels Provos
634aaec5acaSMark Ellzey size_t
evbuffer_add_iovec(struct evbuffer * buf,struct evbuffer_iovec * vec,int n_vec)635aaec5acaSMark Ellzey evbuffer_add_iovec(struct evbuffer * buf, struct evbuffer_iovec * vec, int n_vec) {
636aaec5acaSMark Ellzey int n;
637aaec5acaSMark Ellzey size_t res;
638aaec5acaSMark Ellzey size_t to_alloc;
639aaec5acaSMark Ellzey
640aaec5acaSMark Ellzey EVBUFFER_LOCK(buf);
641aaec5acaSMark Ellzey
642aaec5acaSMark Ellzey res = to_alloc = 0;
643aaec5acaSMark Ellzey
644aaec5acaSMark Ellzey for (n = 0; n < n_vec; n++) {
645aaec5acaSMark Ellzey to_alloc += vec[n].iov_len;
646aaec5acaSMark Ellzey }
647aaec5acaSMark Ellzey
648cb9da0bfSNick Mathewson if (evbuffer_expand_fast_(buf, to_alloc, 2) < 0) {
649aaec5acaSMark Ellzey goto done;
650aaec5acaSMark Ellzey }
651aaec5acaSMark Ellzey
652aaec5acaSMark Ellzey for (n = 0; n < n_vec; n++) {
65327b5398fSNick Mathewson /* XXX each 'add' call here does a bunch of setup that's
654cb9da0bfSNick Mathewson * obviated by evbuffer_expand_fast_, and some cleanup that we
65527b5398fSNick Mathewson * would like to do only once. Instead we should just extract
65627b5398fSNick Mathewson * the part of the code that's needed. */
65727b5398fSNick Mathewson
658aaec5acaSMark Ellzey if (evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len) < 0) {
659aaec5acaSMark Ellzey goto done;
660aaec5acaSMark Ellzey }
661aaec5acaSMark Ellzey
662aaec5acaSMark Ellzey res += vec[n].iov_len;
663aaec5acaSMark Ellzey }
664aaec5acaSMark Ellzey
665aaec5acaSMark Ellzey done:
666aaec5acaSMark Ellzey EVBUFFER_UNLOCK(buf);
667aaec5acaSMark Ellzey return res;
668aaec5acaSMark Ellzey }
669aaec5acaSMark Ellzey
67023243b8aSNick Mathewson int
evbuffer_reserve_space(struct evbuffer * buf,ev_ssize_t size,struct evbuffer_iovec * vec,int n_vecs)6710b22ca19SNick Mathewson evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
67223243b8aSNick Mathewson struct evbuffer_iovec *vec, int n_vecs)
673f04497e4SNiels Provos {
674b7442f8eSNick Mathewson struct evbuffer_chain *chain, **chainp;
67523243b8aSNick Mathewson int n = -1;
67660e0d59bSNick Mathewson
67776cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
678747331d1SNick Mathewson if (buf->freeze_end)
679747331d1SNick Mathewson goto done;
68023243b8aSNick Mathewson if (n_vecs < 1)
68123243b8aSNick Mathewson goto done;
68223243b8aSNick Mathewson if (n_vecs == 1) {
683d5ebcf37SNick Mathewson if ((chain = evbuffer_expand_singlechain(buf, size)) == NULL)
68460e0d59bSNick Mathewson goto done;
685f04497e4SNiels Provos
6862c62062eSAzat Khuzhin vec[0].iov_base = (void *)CHAIN_SPACE_PTR(chain);
687a3245afeSNick Mathewson vec[0].iov_len = (size_t)CHAIN_SPACE_LEN(chain);
6886be589aeSNick Mathewson EVUTIL_ASSERT(size<0 || (size_t)vec[0].iov_len >= (size_t)size);
68923243b8aSNick Mathewson n = 1;
69023243b8aSNick Mathewson } else {
691cb9da0bfSNick Mathewson if (evbuffer_expand_fast_(buf, size, n_vecs)<0)
69223243b8aSNick Mathewson goto done;
693cb9da0bfSNick Mathewson n = evbuffer_read_setup_vecs_(buf, size, vec, n_vecs,
69403afa209SChristopher Davis &chainp, 0);
69523243b8aSNick Mathewson }
69660e0d59bSNick Mathewson
69760e0d59bSNick Mathewson done:
69876cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
69923243b8aSNick Mathewson return n;
70060e0d59bSNick Mathewson
701f04497e4SNiels Provos }
702f04497e4SNiels Provos
7032a6d2a1eSNick Mathewson static int
advance_last_with_data(struct evbuffer * buf)7042a6d2a1eSNick Mathewson advance_last_with_data(struct evbuffer *buf)
7052a6d2a1eSNick Mathewson {
7062a6d2a1eSNick Mathewson int n = 0;
7076a3dd717SAzat Khuzhin struct evbuffer_chain **chainp = buf->last_with_datap;
7086a3dd717SAzat Khuzhin
7092a6d2a1eSNick Mathewson ASSERT_EVBUFFER_LOCKED(buf);
7102a6d2a1eSNick Mathewson
7116a3dd717SAzat Khuzhin if (!*chainp)
7122a6d2a1eSNick Mathewson return 0;
7132a6d2a1eSNick Mathewson
7146a3dd717SAzat Khuzhin while ((*chainp)->next) {
7156a3dd717SAzat Khuzhin chainp = &(*chainp)->next;
7166a3dd717SAzat Khuzhin if ((*chainp)->off)
7176a3dd717SAzat Khuzhin buf->last_with_datap = chainp;
7182a6d2a1eSNick Mathewson ++n;
7192a6d2a1eSNick Mathewson }
7202a6d2a1eSNick Mathewson return n;
7212a6d2a1eSNick Mathewson }
7222a6d2a1eSNick Mathewson
723f04497e4SNiels Provos int
evbuffer_commit_space(struct evbuffer * buf,struct evbuffer_iovec * vec,int n_vecs)72423243b8aSNick Mathewson evbuffer_commit_space(struct evbuffer *buf,
72523243b8aSNick Mathewson struct evbuffer_iovec *vec, int n_vecs)
726f04497e4SNiels Provos {
727b7442f8eSNick Mathewson struct evbuffer_chain *chain, **firstchainp, **chainp;
72860e0d59bSNick Mathewson int result = -1;
729c8ac57f1SNick Mathewson size_t added = 0;
730c8ac57f1SNick Mathewson int i;
73123243b8aSNick Mathewson
73276cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
7337116bf23SNick Mathewson
73423243b8aSNick Mathewson if (buf->freeze_end)
735747331d1SNick Mathewson goto done;
736c8ac57f1SNick Mathewson if (n_vecs == 0) {
737c8ac57f1SNick Mathewson result = 0;
73823243b8aSNick Mathewson goto done;
739c8ac57f1SNick Mathewson } else if (n_vecs == 1 &&
740c44de06cSNick Mathewson (buf->last && vec[0].iov_base == (void *)CHAIN_SPACE_PTR(buf->last))) {
741c8ac57f1SNick Mathewson /* The user only got or used one chain; it might not
742c8ac57f1SNick Mathewson * be the first one with space in it. */
7436be589aeSNick Mathewson if ((size_t)vec[0].iov_len > (size_t)CHAIN_SPACE_LEN(buf->last))
74423243b8aSNick Mathewson goto done;
745c8ac57f1SNick Mathewson buf->last->off += vec[0].iov_len;
74623243b8aSNick Mathewson added = vec[0].iov_len;
747c8ac57f1SNick Mathewson if (added)
748b7442f8eSNick Mathewson advance_last_with_data(buf);
749c8ac57f1SNick Mathewson goto okay;
750747331d1SNick Mathewson }
751747331d1SNick Mathewson
752c8ac57f1SNick Mathewson /* Advance 'firstchain' to the first chain with space in it. */
753b7442f8eSNick Mathewson firstchainp = buf->last_with_datap;
754b7442f8eSNick Mathewson if (!*firstchainp)
755c8ac57f1SNick Mathewson goto done;
756b7442f8eSNick Mathewson if (CHAIN_SPACE_LEN(*firstchainp) == 0) {
757b7442f8eSNick Mathewson firstchainp = &(*firstchainp)->next;
758c8ac57f1SNick Mathewson }
759c8ac57f1SNick Mathewson
760b7442f8eSNick Mathewson chain = *firstchainp;
761c8ac57f1SNick Mathewson /* pass 1: make sure that the pointers and lengths of vecs[] are in
762c8ac57f1SNick Mathewson * bounds before we try to commit anything. */
763c8ac57f1SNick Mathewson for (i=0; i<n_vecs; ++i) {
764c8ac57f1SNick Mathewson if (!chain)
765c8ac57f1SNick Mathewson goto done;
766c44de06cSNick Mathewson if (vec[i].iov_base != (void *)CHAIN_SPACE_PTR(chain) ||
7676be589aeSNick Mathewson (size_t)vec[i].iov_len > CHAIN_SPACE_LEN(chain))
768c8ac57f1SNick Mathewson goto done;
769c8ac57f1SNick Mathewson chain = chain->next;
770c8ac57f1SNick Mathewson }
771c8ac57f1SNick Mathewson /* pass 2: actually adjust all the chains. */
772b7442f8eSNick Mathewson chainp = firstchainp;
773c8ac57f1SNick Mathewson for (i=0; i<n_vecs; ++i) {
774b7442f8eSNick Mathewson (*chainp)->off += vec[i].iov_len;
775c8ac57f1SNick Mathewson added += vec[i].iov_len;
776b7442f8eSNick Mathewson if (vec[i].iov_len) {
777b7442f8eSNick Mathewson buf->last_with_datap = chainp;
778b7442f8eSNick Mathewson }
779b7442f8eSNick Mathewson chainp = &(*chainp)->next;
780c8ac57f1SNick Mathewson }
781c8ac57f1SNick Mathewson
782c8ac57f1SNick Mathewson okay:
78323243b8aSNick Mathewson buf->total_len += added;
78423243b8aSNick Mathewson buf->n_add_for_cb += added;
78560e0d59bSNick Mathewson result = 0;
7868ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
78723243b8aSNick Mathewson
78860e0d59bSNick Mathewson done:
78976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
79060e0d59bSNick Mathewson return result;
791f04497e4SNiels Provos }
792f04497e4SNiels Provos
79303afa209SChristopher Davis static inline int
HAS_PINNED_R(struct evbuffer * buf)79403afa209SChristopher Davis HAS_PINNED_R(struct evbuffer *buf)
79503afa209SChristopher Davis {
79603afa209SChristopher Davis return (buf->last && CHAIN_PINNED_R(buf->last));
79703afa209SChristopher Davis }
79803afa209SChristopher Davis
79996865c47SNick Mathewson static inline void
ZERO_CHAIN(struct evbuffer * dst)80096865c47SNick Mathewson ZERO_CHAIN(struct evbuffer *dst)
80196865c47SNick Mathewson {
80296865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(dst);
80396865c47SNick Mathewson dst->first = NULL;
80496865c47SNick Mathewson dst->last = NULL;
80596865c47SNick Mathewson dst->last_with_datap = &(dst)->first;
80696865c47SNick Mathewson dst->total_len = 0;
80796865c47SNick Mathewson }
808193c06a7SNiels Provos
80903afa209SChristopher Davis /* Prepares the contents of src to be moved to another buffer by removing
81003afa209SChristopher Davis * read-pinned chains. The first pinned chain is saved in first, and the
81103afa209SChristopher Davis * last in last. If src has no read-pinned chains, first and last are set
81203afa209SChristopher Davis * to NULL. */
81303afa209SChristopher Davis static int
PRESERVE_PINNED(struct evbuffer * src,struct evbuffer_chain ** first,struct evbuffer_chain ** last)81403afa209SChristopher Davis PRESERVE_PINNED(struct evbuffer *src, struct evbuffer_chain **first,
81503afa209SChristopher Davis struct evbuffer_chain **last)
81603afa209SChristopher Davis {
81703afa209SChristopher Davis struct evbuffer_chain *chain, **pinned;
81803afa209SChristopher Davis
81903afa209SChristopher Davis ASSERT_EVBUFFER_LOCKED(src);
82003afa209SChristopher Davis
82103afa209SChristopher Davis if (!HAS_PINNED_R(src)) {
82203afa209SChristopher Davis *first = *last = NULL;
82303afa209SChristopher Davis return 0;
82403afa209SChristopher Davis }
82503afa209SChristopher Davis
82603afa209SChristopher Davis pinned = src->last_with_datap;
82703afa209SChristopher Davis if (!CHAIN_PINNED_R(*pinned))
82803afa209SChristopher Davis pinned = &(*pinned)->next;
82903afa209SChristopher Davis EVUTIL_ASSERT(CHAIN_PINNED_R(*pinned));
83003afa209SChristopher Davis chain = *first = *pinned;
83103afa209SChristopher Davis *last = src->last;
83203afa209SChristopher Davis
83303afa209SChristopher Davis /* If there's data in the first pinned chain, we need to allocate
83403afa209SChristopher Davis * a new chain and copy the data over. */
83503afa209SChristopher Davis if (chain->off) {
83603afa209SChristopher Davis struct evbuffer_chain *tmp;
83703afa209SChristopher Davis
83803afa209SChristopher Davis EVUTIL_ASSERT(pinned == src->last_with_datap);
83903afa209SChristopher Davis tmp = evbuffer_chain_new(chain->off);
84003afa209SChristopher Davis if (!tmp)
84103afa209SChristopher Davis return -1;
84203afa209SChristopher Davis memcpy(tmp->buffer, chain->buffer + chain->misalign,
84303afa209SChristopher Davis chain->off);
84403afa209SChristopher Davis tmp->off = chain->off;
84503afa209SChristopher Davis *src->last_with_datap = tmp;
84603afa209SChristopher Davis src->last = tmp;
84703afa209SChristopher Davis chain->misalign += chain->off;
84803afa209SChristopher Davis chain->off = 0;
84903afa209SChristopher Davis } else {
85003afa209SChristopher Davis src->last = *src->last_with_datap;
85103afa209SChristopher Davis *pinned = NULL;
85203afa209SChristopher Davis }
85303afa209SChristopher Davis
85403afa209SChristopher Davis return 0;
85503afa209SChristopher Davis }
85603afa209SChristopher Davis
85703afa209SChristopher Davis static inline void
RESTORE_PINNED(struct evbuffer * src,struct evbuffer_chain * pinned,struct evbuffer_chain * last)85803afa209SChristopher Davis RESTORE_PINNED(struct evbuffer *src, struct evbuffer_chain *pinned,
85903afa209SChristopher Davis struct evbuffer_chain *last)
86003afa209SChristopher Davis {
86103afa209SChristopher Davis ASSERT_EVBUFFER_LOCKED(src);
86203afa209SChristopher Davis
86303afa209SChristopher Davis if (!pinned) {
86403afa209SChristopher Davis ZERO_CHAIN(src);
86503afa209SChristopher Davis return;
86603afa209SChristopher Davis }
86703afa209SChristopher Davis
86803afa209SChristopher Davis src->first = pinned;
86903afa209SChristopher Davis src->last = last;
87003afa209SChristopher Davis src->last_with_datap = &src->first;
87103afa209SChristopher Davis src->total_len = 0;
87203afa209SChristopher Davis }
87303afa209SChristopher Davis
87496865c47SNick Mathewson static inline void
COPY_CHAIN(struct evbuffer * dst,struct evbuffer * src)87596865c47SNick Mathewson COPY_CHAIN(struct evbuffer *dst, struct evbuffer *src)
87696865c47SNick Mathewson {
87796865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(dst);
87896865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(src);
87996865c47SNick Mathewson dst->first = src->first;
88096865c47SNick Mathewson if (src->last_with_datap == &src->first)
88196865c47SNick Mathewson dst->last_with_datap = &dst->first;
88296865c47SNick Mathewson else
88396865c47SNick Mathewson dst->last_with_datap = src->last_with_datap;
88496865c47SNick Mathewson dst->last = src->last;
88596865c47SNick Mathewson dst->total_len = src->total_len;
88696865c47SNick Mathewson }
887193c06a7SNiels Provos
88896865c47SNick Mathewson static void
APPEND_CHAIN(struct evbuffer * dst,struct evbuffer * src)88996865c47SNick Mathewson APPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src)
89096865c47SNick Mathewson {
8918892f4cbSAzat Khuzhin struct evbuffer_chain **chp;
8928892f4cbSAzat Khuzhin
89396865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(dst);
89496865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(src);
89526fd9321SAzat Khuzhin
89626fd9321SAzat Khuzhin chp = evbuffer_free_trailing_empty_chains(dst);
89726fd9321SAzat Khuzhin *chp = src->first;
89826fd9321SAzat Khuzhin
89996865c47SNick Mathewson if (src->last_with_datap == &src->first)
90026fd9321SAzat Khuzhin dst->last_with_datap = chp;
90196865c47SNick Mathewson else
90296865c47SNick Mathewson dst->last_with_datap = src->last_with_datap;
90396865c47SNick Mathewson dst->last = src->last;
90496865c47SNick Mathewson dst->total_len += src->total_len;
90596865c47SNick Mathewson }
906193c06a7SNiels Provos
9079d7368aeSJoachim Bauch static inline void
APPEND_CHAIN_MULTICAST(struct evbuffer * dst,struct evbuffer * src)9089d7368aeSJoachim Bauch APPEND_CHAIN_MULTICAST(struct evbuffer *dst, struct evbuffer *src)
9099d7368aeSJoachim Bauch {
9109d7368aeSJoachim Bauch struct evbuffer_chain *tmp;
9119d7368aeSJoachim Bauch struct evbuffer_chain *chain = src->first;
9129d7368aeSJoachim Bauch struct evbuffer_multicast_parent *extra;
9139d7368aeSJoachim Bauch
9149d7368aeSJoachim Bauch ASSERT_EVBUFFER_LOCKED(dst);
9159d7368aeSJoachim Bauch ASSERT_EVBUFFER_LOCKED(src);
9169d7368aeSJoachim Bauch
9179d7368aeSJoachim Bauch for (; chain; chain = chain->next) {
9189d7368aeSJoachim Bauch if (!chain->off || chain->flags & EVBUFFER_DANGLING) {
9194dee4cc7SNick Mathewson /* skip empty chains */
9209d7368aeSJoachim Bauch continue;
9219d7368aeSJoachim Bauch }
9229d7368aeSJoachim Bauch
9239d7368aeSJoachim Bauch tmp = evbuffer_chain_new(sizeof(struct evbuffer_multicast_parent));
9249d7368aeSJoachim Bauch if (!tmp) {
9259d7368aeSJoachim Bauch event_warn("%s: out of memory", __func__);
9269d7368aeSJoachim Bauch return;
9279d7368aeSJoachim Bauch }
9289d7368aeSJoachim Bauch extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_multicast_parent, tmp);
9294dee4cc7SNick Mathewson /* reference evbuffer containing source chain so it
9304dee4cc7SNick Mathewson * doesn't get released while the chain is still
9314dee4cc7SNick Mathewson * being referenced to */
932cb9da0bfSNick Mathewson evbuffer_incref_(src);
93326041a8eSJoachim Bauch extra->source = src;
9344dee4cc7SNick Mathewson /* reference source chain which now becomes immutable */
9359d7368aeSJoachim Bauch evbuffer_chain_incref(chain);
9369d7368aeSJoachim Bauch extra->parent = chain;
9379d7368aeSJoachim Bauch chain->flags |= EVBUFFER_IMMUTABLE;
9389d7368aeSJoachim Bauch tmp->buffer_len = chain->buffer_len;
9399d7368aeSJoachim Bauch tmp->misalign = chain->misalign;
9409d7368aeSJoachim Bauch tmp->off = chain->off;
9419d7368aeSJoachim Bauch tmp->flags |= EVBUFFER_MULTICAST|EVBUFFER_IMMUTABLE;
9429d7368aeSJoachim Bauch tmp->buffer = chain->buffer;
9439d7368aeSJoachim Bauch evbuffer_chain_insert(dst, tmp);
9449d7368aeSJoachim Bauch }
9459d7368aeSJoachim Bauch }
9469d7368aeSJoachim Bauch
94796865c47SNick Mathewson static void
PREPEND_CHAIN(struct evbuffer * dst,struct evbuffer * src)94896865c47SNick Mathewson PREPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src)
94996865c47SNick Mathewson {
95096865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(dst);
95196865c47SNick Mathewson ASSERT_EVBUFFER_LOCKED(src);
95296865c47SNick Mathewson src->last->next = dst->first;
95396865c47SNick Mathewson dst->first = src->first;
95496865c47SNick Mathewson dst->total_len += src->total_len;
95596865c47SNick Mathewson if (*dst->last_with_datap == NULL) {
95696865c47SNick Mathewson if (src->last_with_datap == &(src)->first)
95796865c47SNick Mathewson dst->last_with_datap = &dst->first;
95896865c47SNick Mathewson else
95996865c47SNick Mathewson dst->last_with_datap = src->last_with_datap;
96096865c47SNick Mathewson } else if (dst->last_with_datap == &dst->first) {
96196865c47SNick Mathewson dst->last_with_datap = &src->last->next;
96296865c47SNick Mathewson }
96396865c47SNick Mathewson }
964193c06a7SNiels Provos
9655908bd72SNiels Provos int
evbuffer_add_buffer(struct evbuffer * outbuf,struct evbuffer * inbuf)966ec2c1db4SNiels Provos evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
967ec2c1db4SNiels Provos {
96803afa209SChristopher Davis struct evbuffer_chain *pinned, *last;
96960e0d59bSNick Mathewson size_t in_total_len, out_total_len;
970747331d1SNick Mathewson int result = 0;
97160e0d59bSNick Mathewson
97260e0d59bSNick Mathewson EVBUFFER_LOCK2(inbuf, outbuf);
97360e0d59bSNick Mathewson in_total_len = inbuf->total_len;
97460e0d59bSNick Mathewson out_total_len = outbuf->total_len;
975025d1bc2SNiels Provos
97601456265SNick Mathewson if (in_total_len == 0 || outbuf == inbuf)
97760e0d59bSNick Mathewson goto done;
9789586a1cbSNiels Provos
979747331d1SNick Mathewson if (outbuf->freeze_end || inbuf->freeze_start) {
980747331d1SNick Mathewson result = -1;
981747331d1SNick Mathewson goto done;
982747331d1SNick Mathewson }
983747331d1SNick Mathewson
98403afa209SChristopher Davis if (PRESERVE_PINNED(inbuf, &pinned, &last) < 0) {
98503afa209SChristopher Davis result = -1;
98603afa209SChristopher Davis goto done;
98703afa209SChristopher Davis }
98803afa209SChristopher Davis
9895c70ea4cSNiels Provos if (out_total_len == 0) {
99045068a31SNick Mathewson /* There might be an empty chain at the start of outbuf; free
99145068a31SNick Mathewson * it. */
99245068a31SNick Mathewson evbuffer_free_all_chains(outbuf->first);
993193c06a7SNiels Provos COPY_CHAIN(outbuf, inbuf);
9945c70ea4cSNiels Provos } else {
995193c06a7SNiels Provos APPEND_CHAIN(outbuf, inbuf);
9965c70ea4cSNiels Provos }
997025d1bc2SNiels Provos
99803afa209SChristopher Davis RESTORE_PINNED(inbuf, pinned, last);
99903afa209SChristopher Davis
1000f1b1bad4SNick Mathewson inbuf->n_del_for_cb += in_total_len;
1001f1b1bad4SNick Mathewson outbuf->n_add_for_cb += in_total_len;
1002025d1bc2SNiels Provos
10038ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(inbuf);
10048ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(outbuf);
1005025d1bc2SNiels Provos
100660e0d59bSNick Mathewson done:
100760e0d59bSNick Mathewson EVBUFFER_UNLOCK2(inbuf, outbuf);
1008747331d1SNick Mathewson return result;
1009025d1bc2SNiels Provos }
1010025d1bc2SNiels Provos
1011747331d1SNick Mathewson int
evbuffer_add_buffer_reference(struct evbuffer * outbuf,struct evbuffer * inbuf)10129d7368aeSJoachim Bauch evbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf)
10139d7368aeSJoachim Bauch {
10149d7368aeSJoachim Bauch size_t in_total_len, out_total_len;
10159d7368aeSJoachim Bauch struct evbuffer_chain *chain;
10169d7368aeSJoachim Bauch int result = 0;
10179d7368aeSJoachim Bauch
10189d7368aeSJoachim Bauch EVBUFFER_LOCK2(inbuf, outbuf);
10199d7368aeSJoachim Bauch in_total_len = inbuf->total_len;
10209d7368aeSJoachim Bauch out_total_len = outbuf->total_len;
10219d7368aeSJoachim Bauch chain = inbuf->first;
10229d7368aeSJoachim Bauch
10239d7368aeSJoachim Bauch if (in_total_len == 0)
10249d7368aeSJoachim Bauch goto done;
10259d7368aeSJoachim Bauch
10269d7368aeSJoachim Bauch if (outbuf->freeze_end || outbuf == inbuf) {
10279d7368aeSJoachim Bauch result = -1;
10289d7368aeSJoachim Bauch goto done;
10299d7368aeSJoachim Bauch }
10309d7368aeSJoachim Bauch
10319d7368aeSJoachim Bauch for (; chain; chain = chain->next) {
103226041a8eSJoachim Bauch if ((chain->flags & (EVBUFFER_FILESEGMENT|EVBUFFER_SENDFILE|EVBUFFER_MULTICAST)) != 0) {
10334dee4cc7SNick Mathewson /* chain type can not be referenced */
10349d7368aeSJoachim Bauch result = -1;
10359d7368aeSJoachim Bauch goto done;
10369d7368aeSJoachim Bauch }
10379d7368aeSJoachim Bauch }
10389d7368aeSJoachim Bauch
10399d7368aeSJoachim Bauch if (out_total_len == 0) {
10409d7368aeSJoachim Bauch /* There might be an empty chain at the start of outbuf; free
10419d7368aeSJoachim Bauch * it. */
10429d7368aeSJoachim Bauch evbuffer_free_all_chains(outbuf->first);
10439d7368aeSJoachim Bauch }
10449d7368aeSJoachim Bauch APPEND_CHAIN_MULTICAST(outbuf, inbuf);
10459d7368aeSJoachim Bauch
10469d7368aeSJoachim Bauch outbuf->n_add_for_cb += in_total_len;
10478ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(outbuf);
10489d7368aeSJoachim Bauch
10499d7368aeSJoachim Bauch done:
10509d7368aeSJoachim Bauch EVBUFFER_UNLOCK2(inbuf, outbuf);
10519d7368aeSJoachim Bauch return result;
10529d7368aeSJoachim Bauch }
10539d7368aeSJoachim Bauch
10549d7368aeSJoachim Bauch int
evbuffer_prepend_buffer(struct evbuffer * outbuf,struct evbuffer * inbuf)10555c70ea4cSNiels Provos evbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
10565c70ea4cSNiels Provos {
105703afa209SChristopher Davis struct evbuffer_chain *pinned, *last;
105860e0d59bSNick Mathewson size_t in_total_len, out_total_len;
1059747331d1SNick Mathewson int result = 0;
106060e0d59bSNick Mathewson
106160e0d59bSNick Mathewson EVBUFFER_LOCK2(inbuf, outbuf);
106260e0d59bSNick Mathewson
106360e0d59bSNick Mathewson in_total_len = inbuf->total_len;
106460e0d59bSNick Mathewson out_total_len = outbuf->total_len;
10655c70ea4cSNiels Provos
106601456265SNick Mathewson if (!in_total_len || inbuf == outbuf)
106760e0d59bSNick Mathewson goto done;
10685c70ea4cSNiels Provos
1069747331d1SNick Mathewson if (outbuf->freeze_start || inbuf->freeze_start) {
1070747331d1SNick Mathewson result = -1;
1071747331d1SNick Mathewson goto done;
1072747331d1SNick Mathewson }
1073747331d1SNick Mathewson
107403afa209SChristopher Davis if (PRESERVE_PINNED(inbuf, &pinned, &last) < 0) {
107503afa209SChristopher Davis result = -1;
107603afa209SChristopher Davis goto done;
107703afa209SChristopher Davis }
107803afa209SChristopher Davis
10795c70ea4cSNiels Provos if (out_total_len == 0) {
108045068a31SNick Mathewson /* There might be an empty chain at the start of outbuf; free
108145068a31SNick Mathewson * it. */
108245068a31SNick Mathewson evbuffer_free_all_chains(outbuf->first);
1083193c06a7SNiels Provos COPY_CHAIN(outbuf, inbuf);
10845c70ea4cSNiels Provos } else {
1085193c06a7SNiels Provos PREPEND_CHAIN(outbuf, inbuf);
1086896bf3a2SNiels Provos }
10875908bd72SNiels Provos
108803afa209SChristopher Davis RESTORE_PINNED(inbuf, pinned, last);
108903afa209SChristopher Davis
1090f1b1bad4SNick Mathewson inbuf->n_del_for_cb += in_total_len;
1091f1b1bad4SNick Mathewson outbuf->n_add_for_cb += in_total_len;
10925c70ea4cSNiels Provos
10938ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(inbuf);
10948ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(outbuf);
109560e0d59bSNick Mathewson done:
109660e0d59bSNick Mathewson EVBUFFER_UNLOCK2(inbuf, outbuf);
1097747331d1SNick Mathewson return result;
10985c70ea4cSNiels Provos }
10995c70ea4cSNiels Provos
1100747331d1SNick Mathewson int
evbuffer_drain(struct evbuffer * buf,size_t len)11015c70ea4cSNiels Provos evbuffer_drain(struct evbuffer *buf, size_t len)
11025c70ea4cSNiels Provos {
11035c70ea4cSNiels Provos struct evbuffer_chain *chain, *next;
110403afa209SChristopher Davis size_t remaining, old_len;
1105747331d1SNick Mathewson int result = 0;
110660e0d59bSNick Mathewson
110776cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
110860e0d59bSNick Mathewson old_len = buf->total_len;
11095c70ea4cSNiels Provos
11105c70ea4cSNiels Provos if (old_len == 0)
111160e0d59bSNick Mathewson goto done;
11125c70ea4cSNiels Provos
1113747331d1SNick Mathewson if (buf->freeze_start) {
1114747331d1SNick Mathewson result = -1;
1115747331d1SNick Mathewson goto done;
1116747331d1SNick Mathewson }
1117747331d1SNick Mathewson
111803afa209SChristopher Davis if (len >= old_len && !HAS_PINNED_R(buf)) {
1119f1b1bad4SNick Mathewson len = old_len;
11205c70ea4cSNiels Provos for (chain = buf->first; chain != NULL; chain = next) {
11215c70ea4cSNiels Provos next = chain->next;
1122fdf69493SNiels Provos evbuffer_chain_free(chain);
11235c70ea4cSNiels Provos }
11245c70ea4cSNiels Provos
1125193c06a7SNiels Provos ZERO_CHAIN(buf);
11265c70ea4cSNiels Provos } else {
11270e32ba54SNick Mathewson if (len >= old_len)
11280e32ba54SNick Mathewson len = old_len;
11290e32ba54SNick Mathewson
11305c70ea4cSNiels Provos buf->total_len -= len;
113103afa209SChristopher Davis remaining = len;
113203afa209SChristopher Davis for (chain = buf->first;
113303afa209SChristopher Davis remaining >= chain->off;
113403afa209SChristopher Davis chain = next) {
11355c70ea4cSNiels Provos next = chain->next;
113603afa209SChristopher Davis remaining -= chain->off;
1137b7442f8eSNick Mathewson
1138b7442f8eSNick Mathewson if (chain == *buf->last_with_datap) {
1139b7442f8eSNick Mathewson buf->last_with_datap = &buf->first;
1140b7442f8eSNick Mathewson }
1141b7442f8eSNick Mathewson if (&chain->next == buf->last_with_datap)
1142b7442f8eSNick Mathewson buf->last_with_datap = &buf->first;
11435c70ea4cSNiels Provos
114403afa209SChristopher Davis if (CHAIN_PINNED_R(chain)) {
114503afa209SChristopher Davis EVUTIL_ASSERT(remaining == 0);
114603afa209SChristopher Davis chain->misalign += chain->off;
114703afa209SChristopher Davis chain->off = 0;
11480e32ba54SNick Mathewson break;
114903afa209SChristopher Davis } else
1150fdf69493SNiels Provos evbuffer_chain_free(chain);
11515c70ea4cSNiels Provos }
11525c70ea4cSNiels Provos
11535c70ea4cSNiels Provos buf->first = chain;
1154d6326104SSuckShit EVUTIL_ASSERT(remaining <= chain->off);
115503afa209SChristopher Davis chain->misalign += remaining;
115603afa209SChristopher Davis chain->off -= remaining;
115703afa209SChristopher Davis }
11585c70ea4cSNiels Provos
1159f1b1bad4SNick Mathewson buf->n_del_for_cb += len;
11605c70ea4cSNiels Provos /* Tell someone about changes in this buffer */
11618ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
116260e0d59bSNick Mathewson
116360e0d59bSNick Mathewson done:
116476cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
1165747331d1SNick Mathewson return result;
11665c70ea4cSNiels Provos }
11675c70ea4cSNiels Provos
11685c70ea4cSNiels Provos /* Reads data from an event buffer and drains the bytes read */
11695c70ea4cSNiels Provos int
evbuffer_remove(struct evbuffer * buf,void * data_out,size_t datlen)11700e7cbe65SNick Mathewson evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
11715c70ea4cSNiels Provos {
1172eb86c8c5SNick Mathewson ev_ssize_t n;
1173eb86c8c5SNick Mathewson EVBUFFER_LOCK(buf);
117427e22255SNick Mathewson n = evbuffer_copyout_from(buf, NULL, data_out, datlen);
1175eb86c8c5SNick Mathewson if (n > 0) {
1176eb86c8c5SNick Mathewson if (evbuffer_drain(buf, n)<0)
1177eb86c8c5SNick Mathewson n = -1;
1178eb86c8c5SNick Mathewson }
1179eb86c8c5SNick Mathewson EVBUFFER_UNLOCK(buf);
1180eb86c8c5SNick Mathewson return (int)n;
1181eb86c8c5SNick Mathewson }
1182eb86c8c5SNick Mathewson
1183eb86c8c5SNick Mathewson ev_ssize_t
evbuffer_copyout(struct evbuffer * buf,void * data_out,size_t datlen)1184eb86c8c5SNick Mathewson evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen)
1185eb86c8c5SNick Mathewson {
118627e22255SNick Mathewson return evbuffer_copyout_from(buf, NULL, data_out, datlen);
118727e22255SNick Mathewson }
118827e22255SNick Mathewson
118927e22255SNick Mathewson ev_ssize_t
evbuffer_copyout_from(struct evbuffer * buf,const struct evbuffer_ptr * pos,void * data_out,size_t datlen)119027e22255SNick Mathewson evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos,
119127e22255SNick Mathewson void *data_out, size_t datlen)
119227e22255SNick Mathewson {
119301456265SNick Mathewson /*XXX fails badly on sendfile case. */
1194eb86c8c5SNick Mathewson struct evbuffer_chain *chain;
11950e7cbe65SNick Mathewson char *data = data_out;
11965c70ea4cSNiels Provos size_t nread;
1197eb86c8c5SNick Mathewson ev_ssize_t result = 0;
119827e22255SNick Mathewson size_t pos_in_chain;
119960e0d59bSNick Mathewson
120076cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
120160e0d59bSNick Mathewson
120227e22255SNick Mathewson if (pos) {
1203841ecbd9SNick Mathewson if (datlen > (size_t)(EV_SSIZE_MAX - pos->pos)) {
1204841ecbd9SNick Mathewson result = -1;
1205841ecbd9SNick Mathewson goto done;
1206841ecbd9SNick Mathewson }
1207cb9da0bfSNick Mathewson chain = pos->internal_.chain;
1208cb9da0bfSNick Mathewson pos_in_chain = pos->internal_.pos_in_chain;
120927e22255SNick Mathewson if (datlen + pos->pos > buf->total_len)
121027e22255SNick Mathewson datlen = buf->total_len - pos->pos;
121127e22255SNick Mathewson } else {
121260e0d59bSNick Mathewson chain = buf->first;
121327e22255SNick Mathewson pos_in_chain = 0;
121427e22255SNick Mathewson if (datlen > buf->total_len)
12155c70ea4cSNiels Provos datlen = buf->total_len;
121627e22255SNick Mathewson }
121727e22255SNick Mathewson
12185c70ea4cSNiels Provos
12195c70ea4cSNiels Provos if (datlen == 0)
122060e0d59bSNick Mathewson goto done;
12215c70ea4cSNiels Provos
1222747331d1SNick Mathewson if (buf->freeze_start) {
1223747331d1SNick Mathewson result = -1;
1224747331d1SNick Mathewson goto done;
1225747331d1SNick Mathewson }
1226747331d1SNick Mathewson
12275c70ea4cSNiels Provos nread = datlen;
12285c70ea4cSNiels Provos
122927e22255SNick Mathewson while (datlen && datlen >= chain->off - pos_in_chain) {
123027e22255SNick Mathewson size_t copylen = chain->off - pos_in_chain;
123127e22255SNick Mathewson memcpy(data,
123227e22255SNick Mathewson chain->buffer + chain->misalign + pos_in_chain,
123327e22255SNick Mathewson copylen);
123427e22255SNick Mathewson data += copylen;
123527e22255SNick Mathewson datlen -= copylen;
12365c70ea4cSNiels Provos
12375c70ea4cSNiels Provos chain = chain->next;
123827e22255SNick Mathewson pos_in_chain = 0;
1239eb86c8c5SNick Mathewson EVUTIL_ASSERT(chain || datlen==0);
12405c70ea4cSNiels Provos }
12415c70ea4cSNiels Provos
12425c70ea4cSNiels Provos if (datlen) {
1243eb86c8c5SNick Mathewson EVUTIL_ASSERT(chain);
1244841ecbd9SNick Mathewson EVUTIL_ASSERT(datlen+pos_in_chain <= chain->off);
1245841ecbd9SNick Mathewson
124627e22255SNick Mathewson memcpy(data, chain->buffer + chain->misalign + pos_in_chain,
124727e22255SNick Mathewson datlen);
12485c70ea4cSNiels Provos }
12495c70ea4cSNiels Provos
125060e0d59bSNick Mathewson result = nread;
125160e0d59bSNick Mathewson done:
125276cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
125360e0d59bSNick Mathewson return result;
12545c70ea4cSNiels Provos }
12555c70ea4cSNiels Provos
12565c70ea4cSNiels Provos /* reads data from the src buffer to the dst buffer, avoids memcpy as
12575c70ea4cSNiels Provos * possible. */
1258545a6114SNick Mathewson /* XXXX should return ev_ssize_t */
12595c70ea4cSNiels Provos int
evbuffer_remove_buffer(struct evbuffer * src,struct evbuffer * dst,size_t datlen)12605c70ea4cSNiels Provos evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
12615c70ea4cSNiels Provos size_t datlen)
12625c70ea4cSNiels Provos {
126301456265SNick Mathewson /*XXX We should have an option to force this to be zero-copy.*/
126401456265SNick Mathewson
126501456265SNick Mathewson /*XXX can fail badly on sendfile case. */
12666f47bd12SNick Mathewson struct evbuffer_chain *chain, *previous;
12675c70ea4cSNiels Provos size_t nread = 0;
126860e0d59bSNick Mathewson int result;
12695c70ea4cSNiels Provos
127060e0d59bSNick Mathewson EVBUFFER_LOCK2(src, dst);
127160e0d59bSNick Mathewson
127260e0d59bSNick Mathewson chain = previous = src->first;
127360e0d59bSNick Mathewson
127460e0d59bSNick Mathewson if (datlen == 0 || dst == src) {
127560e0d59bSNick Mathewson result = 0;
127660e0d59bSNick Mathewson goto done;
127760e0d59bSNick Mathewson }
127801456265SNick Mathewson
1279747331d1SNick Mathewson if (dst->freeze_end || src->freeze_start) {
1280747331d1SNick Mathewson result = -1;
1281747331d1SNick Mathewson goto done;
1282747331d1SNick Mathewson }
1283747331d1SNick Mathewson
12845c70ea4cSNiels Provos /* short-cut if there is no more data buffered */
12855c70ea4cSNiels Provos if (datlen >= src->total_len) {
12865c70ea4cSNiels Provos datlen = src->total_len;
12875c70ea4cSNiels Provos evbuffer_add_buffer(dst, src);
1288545a6114SNick Mathewson result = (int)datlen; /*XXXX should return ev_ssize_t*/
128960e0d59bSNick Mathewson goto done;
12905c70ea4cSNiels Provos }
12915c70ea4cSNiels Provos
12925c70ea4cSNiels Provos /* removes chains if possible */
12935c70ea4cSNiels Provos while (chain->off <= datlen) {
12942a6d2a1eSNick Mathewson /* We can't remove the last with data from src unless we
12952a6d2a1eSNick Mathewson * remove all chains, in which case we would have done the if
12962a6d2a1eSNick Mathewson * block above */
1297b7442f8eSNick Mathewson EVUTIL_ASSERT(chain != *src->last_with_datap);
12985c70ea4cSNiels Provos nread += chain->off;
12995c70ea4cSNiels Provos datlen -= chain->off;
13005c70ea4cSNiels Provos previous = chain;
1301b7442f8eSNick Mathewson if (src->last_with_datap == &chain->next)
1302b7442f8eSNick Mathewson src->last_with_datap = &src->first;
13035c70ea4cSNiels Provos chain = chain->next;
13045c70ea4cSNiels Provos }
13055c70ea4cSNiels Provos
13066a3dd717SAzat Khuzhin if (chain != src->first) {
13075c70ea4cSNiels Provos /* we can remove the chain */
1308c37069cdSNick Mathewson struct evbuffer_chain **chp;
1309c37069cdSNick Mathewson chp = evbuffer_free_trailing_empty_chains(dst);
1310c37069cdSNick Mathewson
13115c70ea4cSNiels Provos if (dst->first == NULL) {
13125c70ea4cSNiels Provos dst->first = src->first;
13135c70ea4cSNiels Provos } else {
1314c37069cdSNick Mathewson *chp = src->first;
13155c70ea4cSNiels Provos }
13165c70ea4cSNiels Provos dst->last = previous;
13175c70ea4cSNiels Provos previous->next = NULL;
13185c70ea4cSNiels Provos src->first = chain;
1319b7442f8eSNick Mathewson advance_last_with_data(dst);
13205c70ea4cSNiels Provos
13215c70ea4cSNiels Provos dst->total_len += nread;
1322f1b1bad4SNick Mathewson dst->n_add_for_cb += nread;
13235c70ea4cSNiels Provos }
13245c70ea4cSNiels Provos
13255c70ea4cSNiels Provos /* we know that there is more data in the src buffer than
13265c70ea4cSNiels Provos * we want to read, so we manually drain the chain */
13275c70ea4cSNiels Provos evbuffer_add(dst, chain->buffer + chain->misalign, datlen);
13285c70ea4cSNiels Provos chain->misalign += datlen;
13295c70ea4cSNiels Provos chain->off -= datlen;
13305c70ea4cSNiels Provos nread += datlen;
13315c70ea4cSNiels Provos
1332c37069cdSNick Mathewson /* You might think we would want to increment dst->n_add_for_cb
1333c37069cdSNick Mathewson * here too. But evbuffer_add above already took care of that.
1334c37069cdSNick Mathewson */
13355c70ea4cSNiels Provos src->total_len -= nread;
1336f1b1bad4SNick Mathewson src->n_del_for_cb += nread;
13375c70ea4cSNiels Provos
1338c735f2b4SNick Mathewson if (nread) {
13398ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(dst);
13408ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(src);
1341c735f2b4SNick Mathewson }
1342545a6114SNick Mathewson result = (int)nread;/*XXXX should change return type */
13435c70ea4cSNiels Provos
134460e0d59bSNick Mathewson done:
134560e0d59bSNick Mathewson EVBUFFER_UNLOCK2(src, dst);
134660e0d59bSNick Mathewson return result;
13475c70ea4cSNiels Provos }
13485c70ea4cSNiels Provos
13496bf1ca78SNick Mathewson unsigned char *
evbuffer_pullup(struct evbuffer * buf,ev_ssize_t size)13500b22ca19SNick Mathewson evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
13515c70ea4cSNiels Provos {
1352d49b92a8SNick Mathewson struct evbuffer_chain *chain, *next, *tmp, *last_with_data;
135360e0d59bSNick Mathewson unsigned char *buffer, *result = NULL;
13540b22ca19SNick Mathewson ev_ssize_t remaining;
13552a6d2a1eSNick Mathewson int removed_last_with_data = 0;
1356b7442f8eSNick Mathewson int removed_last_with_datap = 0;
135760e0d59bSNick Mathewson
135876cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
135960e0d59bSNick Mathewson
136060e0d59bSNick Mathewson chain = buf->first;
13615c70ea4cSNiels Provos
1362e865eb93SNick Mathewson if (size < 0)
13635c70ea4cSNiels Provos size = buf->total_len;
136400382110SNiels Provos /* if size > buf->total_len, we cannot guarantee to the user that she
136500382110SNiels Provos * is going to have a long enough buffer afterwards; so we return
136600382110SNiels Provos * NULL */
1367e865eb93SNick Mathewson if (size == 0 || (size_t)size > buf->total_len)
136860e0d59bSNick Mathewson goto done;
13695c70ea4cSNiels Provos
137000382110SNiels Provos /* No need to pull up anything; the first size bytes are
137100382110SNiels Provos * already here. */
1372e865eb93SNick Mathewson if (chain->off >= (size_t)size) {
137360e0d59bSNick Mathewson result = chain->buffer + chain->misalign;
137460e0d59bSNick Mathewson goto done;
137560e0d59bSNick Mathewson }
13765c70ea4cSNiels Provos
1377d9086fc0SNick Mathewson /* Make sure that none of the chains we need to copy from is pinned. */
1378d9086fc0SNick Mathewson remaining = size - chain->off;
13792e36dbe1SNick Mathewson EVUTIL_ASSERT(remaining >= 0);
1380d9086fc0SNick Mathewson for (tmp=chain->next; tmp; tmp=tmp->next) {
1381d9086fc0SNick Mathewson if (CHAIN_PINNED(tmp))
1382d9086fc0SNick Mathewson goto done;
1383e865eb93SNick Mathewson if (tmp->off >= (size_t)remaining)
1384d9086fc0SNick Mathewson break;
1385d9086fc0SNick Mathewson remaining -= tmp->off;
1386d9086fc0SNick Mathewson }
1387d9086fc0SNick Mathewson
1388d9086fc0SNick Mathewson if (CHAIN_PINNED(chain)) {
1389d9086fc0SNick Mathewson size_t old_off = chain->off;
1390d9086fc0SNick Mathewson if (CHAIN_SPACE_LEN(chain) < size - chain->off) {
1391d9086fc0SNick Mathewson /* not enough room at end of chunk. */
1392d9086fc0SNick Mathewson goto done;
1393d9086fc0SNick Mathewson }
1394d9086fc0SNick Mathewson buffer = CHAIN_SPACE_PTR(chain);
1395d9086fc0SNick Mathewson tmp = chain;
1396d9086fc0SNick Mathewson tmp->off = size;
1397d9086fc0SNick Mathewson size -= old_off;
1398d9086fc0SNick Mathewson chain = chain->next;
1399e865eb93SNick Mathewson } else if (chain->buffer_len - chain->misalign >= (size_t)size) {
140000382110SNiels Provos /* already have enough space in the first chain */
140100382110SNiels Provos size_t old_off = chain->off;
140200382110SNiels Provos buffer = chain->buffer + chain->misalign + chain->off;
140300382110SNiels Provos tmp = chain;
140400382110SNiels Provos tmp->off = size;
140500382110SNiels Provos size -= old_off;
140600382110SNiels Provos chain = chain->next;
140700382110SNiels Provos } else {
14085c70ea4cSNiels Provos if ((tmp = evbuffer_chain_new(size)) == NULL) {
140910cf631eSNick Mathewson event_warn("%s: out of memory", __func__);
141060e0d59bSNick Mathewson goto done;
14115c70ea4cSNiels Provos }
14125c70ea4cSNiels Provos buffer = tmp->buffer;
141300382110SNiels Provos tmp->off = size;
141400382110SNiels Provos buf->first = tmp;
141500382110SNiels Provos }
14165c70ea4cSNiels Provos
1417fdf69493SNiels Provos /* TODO(niels): deal with buffers that point to NULL like sendfile */
1418fdf69493SNiels Provos
14190322ce0aSNick Mathewson /* Copy and free every chunk that will be entirely pulled into tmp */
1420d49b92a8SNick Mathewson last_with_data = *buf->last_with_datap;
1421e865eb93SNick Mathewson for (; chain != NULL && (size_t)size >= chain->off; chain = next) {
14225c70ea4cSNiels Provos next = chain->next;
14235c70ea4cSNiels Provos
1424*5b063049SAzat Khuzhin if (chain->buffer) {
14255c70ea4cSNiels Provos memcpy(buffer, chain->buffer + chain->misalign, chain->off);
14265c70ea4cSNiels Provos size -= chain->off;
14275c70ea4cSNiels Provos buffer += chain->off;
1428*5b063049SAzat Khuzhin }
1429d49b92a8SNick Mathewson if (chain == last_with_data)
14302a6d2a1eSNick Mathewson removed_last_with_data = 1;
1431b7442f8eSNick Mathewson if (&chain->next == buf->last_with_datap)
1432b7442f8eSNick Mathewson removed_last_with_datap = 1;
14335c70ea4cSNiels Provos
1434fdf69493SNiels Provos evbuffer_chain_free(chain);
14355c70ea4cSNiels Provos }
14365c70ea4cSNiels Provos
14375c70ea4cSNiels Provos if (chain != NULL) {
14385c70ea4cSNiels Provos memcpy(buffer, chain->buffer + chain->misalign, size);
14395c70ea4cSNiels Provos chain->misalign += size;
14405c70ea4cSNiels Provos chain->off -= size;
14415c70ea4cSNiels Provos } else {
14425c70ea4cSNiels Provos buf->last = tmp;
14435c70ea4cSNiels Provos }
14445c70ea4cSNiels Provos
14455c70ea4cSNiels Provos tmp->next = chain;
14465c70ea4cSNiels Provos
14472a6d2a1eSNick Mathewson if (removed_last_with_data) {
1448b7442f8eSNick Mathewson buf->last_with_datap = &buf->first;
1449b7442f8eSNick Mathewson } else if (removed_last_with_datap) {
1450b7442f8eSNick Mathewson if (buf->first->next && buf->first->next->off)
1451b7442f8eSNick Mathewson buf->last_with_datap = &buf->first->next;
1452b7442f8eSNick Mathewson else
1453b7442f8eSNick Mathewson buf->last_with_datap = &buf->first;
14542a6d2a1eSNick Mathewson }
14552a6d2a1eSNick Mathewson
145660e0d59bSNick Mathewson result = (tmp->buffer + tmp->misalign);
145760e0d59bSNick Mathewson
145860e0d59bSNick Mathewson done:
145976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
146060e0d59bSNick Mathewson return result;
14615c70ea4cSNiels Provos }
14625c70ea4cSNiels Provos
14635c70ea4cSNiels Provos /*
14645c70ea4cSNiels Provos * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
14655c70ea4cSNiels Provos * The returned buffer needs to be freed by the called.
14665c70ea4cSNiels Provos */
14675c70ea4cSNiels Provos char *
evbuffer_readline(struct evbuffer * buffer)14685c70ea4cSNiels Provos evbuffer_readline(struct evbuffer *buffer)
14695c70ea4cSNiels Provos {
14705c70ea4cSNiels Provos return evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY);
14715c70ea4cSNiels Provos }
14725c70ea4cSNiels Provos
1473545a6114SNick Mathewson static inline ev_ssize_t
evbuffer_strchr(struct evbuffer_ptr * it,const char chr)1474a26d2d1bSNick Mathewson evbuffer_strchr(struct evbuffer_ptr *it, const char chr)
14755c70ea4cSNiels Provos {
1476cb9da0bfSNick Mathewson struct evbuffer_chain *chain = it->internal_.chain;
1477cb9da0bfSNick Mathewson size_t i = it->internal_.pos_in_chain;
14785c70ea4cSNiels Provos while (chain != NULL) {
14795c70ea4cSNiels Provos char *buffer = (char *)chain->buffer + chain->misalign;
1480cc1600afSNick Mathewson char *cp = memchr(buffer+i, chr, chain->off-i);
1481cc1600afSNick Mathewson if (cp) {
1482cb9da0bfSNick Mathewson it->internal_.chain = chain;
1483cb9da0bfSNick Mathewson it->internal_.pos_in_chain = cp - buffer;
14844461f1a0SNick Mathewson it->pos += (cp - buffer - i);
1485cc1600afSNick Mathewson return it->pos;
14865c70ea4cSNiels Provos }
1487cc1600afSNick Mathewson it->pos += chain->off - i;
14885c70ea4cSNiels Provos i = 0;
14895c70ea4cSNiels Provos chain = chain->next;
14905c70ea4cSNiels Provos }
14915c70ea4cSNiels Provos
14925c70ea4cSNiels Provos return (-1);
14935c70ea4cSNiels Provos }
14945c70ea4cSNiels Provos
1495cc1600afSNick Mathewson static inline char *
find_eol_char(char * s,size_t len)1496cc1600afSNick Mathewson find_eol_char(char *s, size_t len)
1497cc1600afSNick Mathewson {
1498cc1600afSNick Mathewson #define CHUNK_SZ 128
1499cc1600afSNick Mathewson /* Lots of benchmarking found this approach to be faster in practice
1500cc1600afSNick Mathewson * than doing two memchrs over the whole buffer, doin a memchr on each
1501cc1600afSNick Mathewson * char of the buffer, or trying to emulate memchr by hand. */
1502cc1600afSNick Mathewson char *s_end, *cr, *lf;
1503cc1600afSNick Mathewson s_end = s+len;
1504cc1600afSNick Mathewson while (s < s_end) {
1505cc1600afSNick Mathewson size_t chunk = (s + CHUNK_SZ < s_end) ? CHUNK_SZ : (s_end - s);
1506cc1600afSNick Mathewson cr = memchr(s, '\r', chunk);
1507cc1600afSNick Mathewson lf = memchr(s, '\n', chunk);
1508cc1600afSNick Mathewson if (cr) {
1509cc1600afSNick Mathewson if (lf && lf < cr)
1510cc1600afSNick Mathewson return lf;
1511cc1600afSNick Mathewson return cr;
1512cc1600afSNick Mathewson } else if (lf) {
1513cc1600afSNick Mathewson return lf;
1514cc1600afSNick Mathewson }
1515cc1600afSNick Mathewson s += CHUNK_SZ;
1516cc1600afSNick Mathewson }
1517cc1600afSNick Mathewson
1518cc1600afSNick Mathewson return NULL;
1519cc1600afSNick Mathewson #undef CHUNK_SZ
1520cc1600afSNick Mathewson }
1521cc1600afSNick Mathewson
1522545a6114SNick Mathewson static ev_ssize_t
evbuffer_find_eol_char(struct evbuffer_ptr * it)1523cc1600afSNick Mathewson evbuffer_find_eol_char(struct evbuffer_ptr *it)
15245c70ea4cSNiels Provos {
1525cb9da0bfSNick Mathewson struct evbuffer_chain *chain = it->internal_.chain;
1526cb9da0bfSNick Mathewson size_t i = it->internal_.pos_in_chain;
15275c70ea4cSNiels Provos while (chain != NULL) {
15285c70ea4cSNiels Provos char *buffer = (char *)chain->buffer + chain->misalign;
1529cc1600afSNick Mathewson char *cp = find_eol_char(buffer+i, chain->off-i);
1530cc1600afSNick Mathewson if (cp) {
1531cb9da0bfSNick Mathewson it->internal_.chain = chain;
1532cb9da0bfSNick Mathewson it->internal_.pos_in_chain = cp - buffer;
1533cc1600afSNick Mathewson it->pos += (cp - buffer) - i;
1534cc1600afSNick Mathewson return it->pos;
15355c70ea4cSNiels Provos }
1536cc1600afSNick Mathewson it->pos += chain->off - i;
15375c70ea4cSNiels Provos i = 0;
15385c70ea4cSNiels Provos chain = chain->next;
15395c70ea4cSNiels Provos }
15405c70ea4cSNiels Provos
15415c70ea4cSNiels Provos return (-1);
15425c70ea4cSNiels Provos }
15435c70ea4cSNiels Provos
154412e0d889SAzat Khuzhin static inline size_t
evbuffer_strspn(struct evbuffer_ptr * ptr,const char * chrset)15455c70ea4cSNiels Provos evbuffer_strspn(
1546a26d2d1bSNick Mathewson struct evbuffer_ptr *ptr, const char *chrset)
15475c70ea4cSNiels Provos {
154812e0d889SAzat Khuzhin size_t count = 0;
1549cb9da0bfSNick Mathewson struct evbuffer_chain *chain = ptr->internal_.chain;
1550cb9da0bfSNick Mathewson size_t i = ptr->internal_.pos_in_chain;
1551a26d2d1bSNick Mathewson
1552a26d2d1bSNick Mathewson if (!chain)
15539ab8ab83SNick Mathewson return 0;
1554a26d2d1bSNick Mathewson
1555a26d2d1bSNick Mathewson while (1) {
15565c70ea4cSNiels Provos char *buffer = (char *)chain->buffer + chain->misalign;
15575c70ea4cSNiels Provos for (; i < chain->off; ++i) {
15585c70ea4cSNiels Provos const char *p = chrset;
15595c70ea4cSNiels Provos while (*p) {
15605c70ea4cSNiels Provos if (buffer[i] == *p++)
15615c70ea4cSNiels Provos goto next;
15625c70ea4cSNiels Provos }
1563cb9da0bfSNick Mathewson ptr->internal_.chain = chain;
1564cb9da0bfSNick Mathewson ptr->internal_.pos_in_chain = i;
1565a26d2d1bSNick Mathewson ptr->pos += count;
15665c70ea4cSNiels Provos return count;
15675c70ea4cSNiels Provos next:
15685c70ea4cSNiels Provos ++count;
15695c70ea4cSNiels Provos }
15705c70ea4cSNiels Provos i = 0;
1571a26d2d1bSNick Mathewson
1572a26d2d1bSNick Mathewson if (! chain->next) {
1573cb9da0bfSNick Mathewson ptr->internal_.chain = chain;
1574cb9da0bfSNick Mathewson ptr->internal_.pos_in_chain = i;
1575a26d2d1bSNick Mathewson ptr->pos += count;
1576a26d2d1bSNick Mathewson return count;
1577a26d2d1bSNick Mathewson }
1578a26d2d1bSNick Mathewson
15795c70ea4cSNiels Provos chain = chain->next;
15805c70ea4cSNiels Provos }
15815c70ea4cSNiels Provos }
15825c70ea4cSNiels Provos
1583a26d2d1bSNick Mathewson
15849ab8ab83SNick Mathewson static inline int
evbuffer_getchr(struct evbuffer_ptr * it)1585a26d2d1bSNick Mathewson evbuffer_getchr(struct evbuffer_ptr *it)
15865c70ea4cSNiels Provos {
1587cb9da0bfSNick Mathewson struct evbuffer_chain *chain = it->internal_.chain;
1588cb9da0bfSNick Mathewson size_t off = it->internal_.pos_in_chain;
15895c70ea4cSNiels Provos
15907aeb2fd4SNir Soffer if (chain == NULL)
15919ab8ab83SNick Mathewson return -1;
15927aeb2fd4SNir Soffer
15939ab8ab83SNick Mathewson return (unsigned char)chain->buffer[chain->misalign + off];
15945c70ea4cSNiels Provos }
15955c70ea4cSNiels Provos
1596d4134772SNick Mathewson struct evbuffer_ptr
evbuffer_search_eol(struct evbuffer * buffer,struct evbuffer_ptr * start,size_t * eol_len_out,enum evbuffer_eol_style eol_style)1597d4134772SNick Mathewson evbuffer_search_eol(struct evbuffer *buffer,
1598d4134772SNick Mathewson struct evbuffer_ptr *start, size_t *eol_len_out,
1599d4134772SNick Mathewson enum evbuffer_eol_style eol_style)
1600d4134772SNick Mathewson {
1601d4134772SNick Mathewson struct evbuffer_ptr it, it2;
1602d4134772SNick Mathewson size_t extra_drain = 0;
1603d4134772SNick Mathewson int ok = 0;
1604d4134772SNick Mathewson
16057aeb2fd4SNir Soffer /* Avoid locking in trivial edge cases */
1606cb9da0bfSNick Mathewson if (start && start->internal_.chain == NULL) {
1607e3e97ae3SNir Soffer PTR_NOT_FOUND(&it);
16087aeb2fd4SNir Soffer if (eol_len_out)
16097aeb2fd4SNir Soffer *eol_len_out = extra_drain;
16107aeb2fd4SNir Soffer return it;
16117aeb2fd4SNir Soffer }
16127aeb2fd4SNir Soffer
161376cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
1614d4134772SNick Mathewson
1615d4134772SNick Mathewson if (start) {
1616d4134772SNick Mathewson memcpy(&it, start, sizeof(it));
1617d4134772SNick Mathewson } else {
1618d4134772SNick Mathewson it.pos = 0;
1619cb9da0bfSNick Mathewson it.internal_.chain = buffer->first;
1620cb9da0bfSNick Mathewson it.internal_.pos_in_chain = 0;
1621d4134772SNick Mathewson }
1622d4134772SNick Mathewson
1623d4134772SNick Mathewson /* the eol_style determines our first stop character and how many
1624d4134772SNick Mathewson * characters we are going to drain afterwards. */
1625d4134772SNick Mathewson switch (eol_style) {
1626d4134772SNick Mathewson case EVBUFFER_EOL_ANY:
1627cc1600afSNick Mathewson if (evbuffer_find_eol_char(&it) < 0)
1628d4134772SNick Mathewson goto done;
1629d4134772SNick Mathewson memcpy(&it2, &it, sizeof(it));
1630d4134772SNick Mathewson extra_drain = evbuffer_strspn(&it2, "\r\n");
1631d4134772SNick Mathewson break;
1632d4134772SNick Mathewson case EVBUFFER_EOL_CRLF_STRICT: {
1633d4134772SNick Mathewson it = evbuffer_search(buffer, "\r\n", 2, &it);
1634d4134772SNick Mathewson if (it.pos < 0)
1635d4134772SNick Mathewson goto done;
1636d4134772SNick Mathewson extra_drain = 2;
1637d4134772SNick Mathewson break;
1638d4134772SNick Mathewson }
1639d927965fSNick Mathewson case EVBUFFER_EOL_CRLF: {
1640d927965fSNick Mathewson ev_ssize_t start_pos = it.pos;
16417b9d1395SNick Mathewson /* Look for a LF ... */
16425dde0f04SMina Naguib if (evbuffer_strchr(&it, '\n') < 0)
1643d4134772SNick Mathewson goto done;
1644d4134772SNick Mathewson extra_drain = 1;
16457b9d1395SNick Mathewson /* ... optionally preceeded by a CR. */
1646d927965fSNick Mathewson if (it.pos == start_pos)
1647d927965fSNick Mathewson break; /* If the first character is \n, don't back up */
16487b9d1395SNick Mathewson /* This potentially does an extra linear walk over the first
16497b9d1395SNick Mathewson * few chains. Probably, that's not too expensive unless you
16507b9d1395SNick Mathewson * have a really pathological setup. */
16515dde0f04SMina Naguib memcpy(&it2, &it, sizeof(it));
16527b9d1395SNick Mathewson if (evbuffer_ptr_subtract(buffer, &it2, 1)<0)
16537b9d1395SNick Mathewson break;
16545dde0f04SMina Naguib if (evbuffer_getchr(&it2) == '\r') {
16555dde0f04SMina Naguib memcpy(&it, &it2, sizeof(it));
1656d4134772SNick Mathewson extra_drain = 2;
1657d4134772SNick Mathewson }
1658d4134772SNick Mathewson break;
1659d927965fSNick Mathewson }
1660d4134772SNick Mathewson case EVBUFFER_EOL_LF:
1661d4134772SNick Mathewson if (evbuffer_strchr(&it, '\n') < 0)
1662d4134772SNick Mathewson goto done;
1663d4134772SNick Mathewson extra_drain = 1;
1664d4134772SNick Mathewson break;
1665d7a8b36eSAndrea Montefusco case EVBUFFER_EOL_NUL:
1666d7a8b36eSAndrea Montefusco if (evbuffer_strchr(&it, '\0') < 0)
1667d7a8b36eSAndrea Montefusco goto done;
1668d7a8b36eSAndrea Montefusco extra_drain = 1;
1669d7a8b36eSAndrea Montefusco break;
1670d4134772SNick Mathewson default:
1671d4134772SNick Mathewson goto done;
1672d4134772SNick Mathewson }
1673d4134772SNick Mathewson
1674d4134772SNick Mathewson ok = 1;
1675d4134772SNick Mathewson done:
167676cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
1677d4134772SNick Mathewson
1678e3e97ae3SNir Soffer if (!ok)
1679e3e97ae3SNir Soffer PTR_NOT_FOUND(&it);
1680d4134772SNick Mathewson if (eol_len_out)
1681d4134772SNick Mathewson *eol_len_out = extra_drain;
1682d4134772SNick Mathewson
1683d4134772SNick Mathewson return it;
1684d4134772SNick Mathewson }
1685d4134772SNick Mathewson
16865c70ea4cSNiels Provos char *
evbuffer_readln(struct evbuffer * buffer,size_t * n_read_out,enum evbuffer_eol_style eol_style)16875c70ea4cSNiels Provos evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
16885c70ea4cSNiels Provos enum evbuffer_eol_style eol_style)
16895c70ea4cSNiels Provos {
1690a26d2d1bSNick Mathewson struct evbuffer_ptr it;
1691d4134772SNick Mathewson char *line;
1692d4134772SNick Mathewson size_t n_to_copy=0, extra_drain=0;
169360e0d59bSNick Mathewson char *result = NULL;
169460e0d59bSNick Mathewson
169576cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
16965c70ea4cSNiels Provos
1697747331d1SNick Mathewson if (buffer->freeze_start) {
1698747331d1SNick Mathewson goto done;
1699747331d1SNick Mathewson }
1700747331d1SNick Mathewson
1701d4134772SNick Mathewson it = evbuffer_search_eol(buffer, NULL, &extra_drain, eol_style);
1702d4134772SNick Mathewson if (it.pos < 0)
1703a26d2d1bSNick Mathewson goto done;
1704d4134772SNick Mathewson n_to_copy = it.pos;
17055c70ea4cSNiels Provos
170649868b61SNick Mathewson if ((line = mm_malloc(n_to_copy+1)) == NULL) {
170710cf631eSNick Mathewson event_warn("%s: out of memory", __func__);
170860e0d59bSNick Mathewson goto done;
17095c70ea4cSNiels Provos }
17105c70ea4cSNiels Provos
17115c70ea4cSNiels Provos evbuffer_remove(buffer, line, n_to_copy);
17125c70ea4cSNiels Provos line[n_to_copy] = '\0';
17135c70ea4cSNiels Provos
17145c70ea4cSNiels Provos evbuffer_drain(buffer, extra_drain);
171560e0d59bSNick Mathewson result = line;
171660e0d59bSNick Mathewson done:
171776cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
1718d4134772SNick Mathewson
1719d4134772SNick Mathewson if (n_read_out)
1720d4134772SNick Mathewson *n_read_out = result ? n_to_copy : 0;
1721d4134772SNick Mathewson
172260e0d59bSNick Mathewson return result;
17235c70ea4cSNiels Provos }
17245c70ea4cSNiels Provos
17253552ac1eSNick Mathewson #define EVBUFFER_CHAIN_MAX_AUTO_SIZE 4096
17263552ac1eSNick Mathewson
17275c70ea4cSNiels Provos /* Adds data to an event buffer */
17285c70ea4cSNiels Provos
17295c70ea4cSNiels Provos int
evbuffer_add(struct evbuffer * buf,const void * data_in,size_t datlen)17300e7cbe65SNick Mathewson evbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen)
17315c70ea4cSNiels Provos {
173260e0d59bSNick Mathewson struct evbuffer_chain *chain, *tmp;
17336bf1ca78SNick Mathewson const unsigned char *data = data_in;
1734f1b1bad4SNick Mathewson size_t remain, to_alloc;
173560e0d59bSNick Mathewson int result = -1;
173660e0d59bSNick Mathewson
173776cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
173860e0d59bSNick Mathewson
1739747331d1SNick Mathewson if (buf->freeze_end) {
1740747331d1SNick Mathewson goto done;
1741747331d1SNick Mathewson }
1742841ecbd9SNick Mathewson /* Prevent buf->total_len overflow */
1743841ecbd9SNick Mathewson if (datlen > EV_SIZE_MAX - buf->total_len) {
1744841ecbd9SNick Mathewson goto done;
1745841ecbd9SNick Mathewson }
1746747331d1SNick Mathewson
1747a8769ef1SMarcus Sundberg if (*buf->last_with_datap == NULL) {
174860e0d59bSNick Mathewson chain = buf->last;
1749a8769ef1SMarcus Sundberg } else {
1750a8769ef1SMarcus Sundberg chain = *buf->last_with_datap;
1751a8769ef1SMarcus Sundberg }
17525c70ea4cSNiels Provos
17530322ce0aSNick Mathewson /* If there are no chains allocated for this buffer, allocate one
17540322ce0aSNick Mathewson * big enough to hold all the data. */
17555c70ea4cSNiels Provos if (chain == NULL) {
17565c0ebb33SNick Mathewson chain = evbuffer_chain_new(datlen);
17575c0ebb33SNick Mathewson if (!chain)
175860e0d59bSNick Mathewson goto done;
17595c0ebb33SNick Mathewson evbuffer_chain_insert(buf, chain);
17605c70ea4cSNiels Provos }
17615c70ea4cSNiels Provos
1762fdf69493SNiels Provos if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) {
1763841ecbd9SNick Mathewson /* Always true for mutable buffers */
1764841ecbd9SNick Mathewson EVUTIL_ASSERT(chain->misalign >= 0 &&
1765841ecbd9SNick Mathewson (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX);
1766841ecbd9SNick Mathewson remain = chain->buffer_len - (size_t)chain->misalign - chain->off;
17675c70ea4cSNiels Provos if (remain >= datlen) {
1768e711ce45SNiels Provos /* there's enough space to hold all the data in the
1769e711ce45SNiels Provos * current last chain */
17705c70ea4cSNiels Provos memcpy(chain->buffer + chain->misalign + chain->off,
17715c70ea4cSNiels Provos data, datlen);
17725c70ea4cSNiels Provos chain->off += datlen;
17735c70ea4cSNiels Provos buf->total_len += datlen;
1774f1b1bad4SNick Mathewson buf->n_add_for_cb += datlen;
17755c70ea4cSNiels Provos goto out;
1776e4f34e8aSNick Mathewson } else if (!CHAIN_PINNED(chain) &&
1777e4f34e8aSNick Mathewson evbuffer_chain_should_realign(chain, datlen)) {
1778e711ce45SNiels Provos /* we can fit the data into the misalignment */
1779d9086fc0SNick Mathewson evbuffer_chain_align(chain);
1780e711ce45SNiels Provos
1781e711ce45SNiels Provos memcpy(chain->buffer + chain->off, data, datlen);
1782e711ce45SNiels Provos chain->off += datlen;
1783e711ce45SNiels Provos buf->total_len += datlen;
1784f1b1bad4SNick Mathewson buf->n_add_for_cb += datlen;
1785e711ce45SNiels Provos goto out;
17865c70ea4cSNiels Provos }
1787fdf69493SNiels Provos } else {
1788fdf69493SNiels Provos /* we cannot write any data to the last chain */
1789fdf69493SNiels Provos remain = 0;
1790fdf69493SNiels Provos }
17915c70ea4cSNiels Provos
17925c70ea4cSNiels Provos /* we need to add another chain */
17933552ac1eSNick Mathewson to_alloc = chain->buffer_len;
17943552ac1eSNick Mathewson if (to_alloc <= EVBUFFER_CHAIN_MAX_AUTO_SIZE/2)
17953552ac1eSNick Mathewson to_alloc <<= 1;
17965c70ea4cSNiels Provos if (datlen > to_alloc)
17975c70ea4cSNiels Provos to_alloc = datlen;
1798fdf69493SNiels Provos tmp = evbuffer_chain_new(to_alloc);
1799fdf69493SNiels Provos if (tmp == NULL)
180060e0d59bSNick Mathewson goto done;
18015c70ea4cSNiels Provos
1802fdf69493SNiels Provos if (remain) {
18035c70ea4cSNiels Provos memcpy(chain->buffer + chain->misalign + chain->off,
18045c70ea4cSNiels Provos data, remain);
18055c70ea4cSNiels Provos chain->off += remain;
1806fdf69493SNiels Provos buf->total_len += remain;
1807f1b1bad4SNick Mathewson buf->n_add_for_cb += remain;
1808fdf69493SNiels Provos }
18095c70ea4cSNiels Provos
18105c70ea4cSNiels Provos data += remain;
18115c70ea4cSNiels Provos datlen -= remain;
18125c70ea4cSNiels Provos
1813fdf69493SNiels Provos memcpy(tmp->buffer, data, datlen);
1814fdf69493SNiels Provos tmp->off = datlen;
1815fdf69493SNiels Provos evbuffer_chain_insert(buf, tmp);
18161ef1f684SNick Mathewson buf->n_add_for_cb += datlen;
18175c70ea4cSNiels Provos
18185c70ea4cSNiels Provos out:
18198ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
182060e0d59bSNick Mathewson result = 0;
182160e0d59bSNick Mathewson done:
182276cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
182360e0d59bSNick Mathewson return result;
18245c70ea4cSNiels Provos }
18255c70ea4cSNiels Provos
18265c70ea4cSNiels Provos int
evbuffer_prepend(struct evbuffer * buf,const void * data,size_t datlen)18275c70ea4cSNiels Provos evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen)
18285c70ea4cSNiels Provos {
182960e0d59bSNick Mathewson struct evbuffer_chain *chain, *tmp;
183060e0d59bSNick Mathewson int result = -1;
183160e0d59bSNick Mathewson
183276cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
1833747331d1SNick Mathewson
183461fa7b7dSAzat Khuzhin if (datlen == 0) {
183561fa7b7dSAzat Khuzhin result = 0;
183661fa7b7dSAzat Khuzhin goto done;
183761fa7b7dSAzat Khuzhin }
1838747331d1SNick Mathewson if (buf->freeze_start) {
1839747331d1SNick Mathewson goto done;
1840747331d1SNick Mathewson }
1841841ecbd9SNick Mathewson if (datlen > EV_SIZE_MAX - buf->total_len) {
1842841ecbd9SNick Mathewson goto done;
1843841ecbd9SNick Mathewson }
1844747331d1SNick Mathewson
184560e0d59bSNick Mathewson chain = buf->first;
18465c70ea4cSNiels Provos
18475c70ea4cSNiels Provos if (chain == NULL) {
18485c0ebb33SNick Mathewson chain = evbuffer_chain_new(datlen);
18495c0ebb33SNick Mathewson if (!chain)
185060e0d59bSNick Mathewson goto done;
18515c0ebb33SNick Mathewson evbuffer_chain_insert(buf, chain);
18525c70ea4cSNiels Provos }
18535c70ea4cSNiels Provos
1854fdf69493SNiels Provos /* we cannot touch immutable buffers */
1855fdf69493SNiels Provos if ((chain->flags & EVBUFFER_IMMUTABLE) == 0) {
1856841ecbd9SNick Mathewson /* Always true for mutable buffers */
1857841ecbd9SNick Mathewson EVUTIL_ASSERT(chain->misalign >= 0 &&
1858841ecbd9SNick Mathewson (ev_uint64_t)chain->misalign <= EVBUFFER_CHAIN_MAX);
1859841ecbd9SNick Mathewson
1860c87272b7SNick Mathewson /* If this chain is empty, we can treat it as
1861c87272b7SNick Mathewson * 'empty at the beginning' rather than 'empty at the end' */
1862c87272b7SNick Mathewson if (chain->off == 0)
1863c87272b7SNick Mathewson chain->misalign = chain->buffer_len;
1864c87272b7SNick Mathewson
1865e865eb93SNick Mathewson if ((size_t)chain->misalign >= datlen) {
1866c87272b7SNick Mathewson /* we have enough space to fit everything */
18675c70ea4cSNiels Provos memcpy(chain->buffer + chain->misalign - datlen,
18685c70ea4cSNiels Provos data, datlen);
18695c70ea4cSNiels Provos chain->off += datlen;
18705c70ea4cSNiels Provos chain->misalign -= datlen;
1871fdf69493SNiels Provos buf->total_len += datlen;
1872f1b1bad4SNick Mathewson buf->n_add_for_cb += datlen;
1873fdf69493SNiels Provos goto out;
1874fdf69493SNiels Provos } else if (chain->misalign) {
1875c87272b7SNick Mathewson /* we can only fit some of the data. */
1876fdf69493SNiels Provos memcpy(chain->buffer,
187788f2b7a0SNick Mathewson (char*)data + datlen - chain->misalign,
1878a3245afeSNick Mathewson (size_t)chain->misalign);
1879a3245afeSNick Mathewson chain->off += (size_t)chain->misalign;
1880a3245afeSNick Mathewson buf->total_len += (size_t)chain->misalign;
1881a3245afeSNick Mathewson buf->n_add_for_cb += (size_t)chain->misalign;
1882a3245afeSNick Mathewson datlen -= (size_t)chain->misalign;
1883fdf69493SNiels Provos chain->misalign = 0;
1884fdf69493SNiels Provos }
1885fdf69493SNiels Provos }
18860322ce0aSNick Mathewson
18875c70ea4cSNiels Provos /* we need to add another chain */
18885c70ea4cSNiels Provos if ((tmp = evbuffer_chain_new(datlen)) == NULL)
188960e0d59bSNick Mathewson goto done;
18905c70ea4cSNiels Provos buf->first = tmp;
18912fea04b3SAzat Khuzhin if (buf->last_with_datap == &buf->first && chain->off)
1892b7442f8eSNick Mathewson buf->last_with_datap = &tmp->next;
18932a6d2a1eSNick Mathewson
18945c70ea4cSNiels Provos tmp->next = chain;
18955c70ea4cSNiels Provos
18965c70ea4cSNiels Provos tmp->off = datlen;
1897841ecbd9SNick Mathewson EVUTIL_ASSERT(datlen <= tmp->buffer_len);
18985c70ea4cSNiels Provos tmp->misalign = tmp->buffer_len - datlen;
18995c70ea4cSNiels Provos
19005c70ea4cSNiels Provos memcpy(tmp->buffer + tmp->misalign, data, datlen);
19015c70ea4cSNiels Provos buf->total_len += datlen;
19020abd0393SAzat Khuzhin buf->n_add_for_cb += datlen;
19035c70ea4cSNiels Provos
1904fdf69493SNiels Provos out:
19058ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
190660e0d59bSNick Mathewson result = 0;
190760e0d59bSNick Mathewson done:
190876cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
190960e0d59bSNick Mathewson return result;
19105c70ea4cSNiels Provos }
19115c70ea4cSNiels Provos
1912fdf69493SNiels Provos /** Helper: realigns the memory in chain->buffer so that misalign is 0. */
19135c70ea4cSNiels Provos static void
evbuffer_chain_align(struct evbuffer_chain * chain)19145c70ea4cSNiels Provos evbuffer_chain_align(struct evbuffer_chain *chain)
19155c70ea4cSNiels Provos {
19162e36dbe1SNick Mathewson EVUTIL_ASSERT(!(chain->flags & EVBUFFER_IMMUTABLE));
19172e36dbe1SNick Mathewson EVUTIL_ASSERT(!(chain->flags & EVBUFFER_MEM_PINNED_ANY));
19185c70ea4cSNiels Provos memmove(chain->buffer, chain->buffer + chain->misalign, chain->off);
19195c70ea4cSNiels Provos chain->misalign = 0;
19205c70ea4cSNiels Provos }
19215c70ea4cSNiels Provos
1922d5ebcf37SNick Mathewson #define MAX_TO_COPY_IN_EXPAND 4096
1923d5ebcf37SNick Mathewson #define MAX_TO_REALIGN_IN_EXPAND 2048
19245c70ea4cSNiels Provos
1925e4f34e8aSNick Mathewson /** Helper: return true iff we should realign chain to fit datalen bytes of
1926e4f34e8aSNick Mathewson data in it. */
1927e4f34e8aSNick Mathewson static int
evbuffer_chain_should_realign(struct evbuffer_chain * chain,size_t datlen)1928e4f34e8aSNick Mathewson evbuffer_chain_should_realign(struct evbuffer_chain *chain,
1929e4f34e8aSNick Mathewson size_t datlen)
1930e4f34e8aSNick Mathewson {
1931e4f34e8aSNick Mathewson return chain->buffer_len - chain->off >= datlen &&
1932e4f34e8aSNick Mathewson (chain->off < chain->buffer_len / 2) &&
1933e4f34e8aSNick Mathewson (chain->off <= MAX_TO_REALIGN_IN_EXPAND);
1934e4f34e8aSNick Mathewson }
1935e4f34e8aSNick Mathewson
1936d5ebcf37SNick Mathewson /* Expands the available space in the event buffer to at least datlen, all in
1937d5ebcf37SNick Mathewson * a single chunk. Return that chunk. */
1938d5ebcf37SNick Mathewson static struct evbuffer_chain *
evbuffer_expand_singlechain(struct evbuffer * buf,size_t datlen)1939d5ebcf37SNick Mathewson evbuffer_expand_singlechain(struct evbuffer *buf, size_t datlen)
19405c70ea4cSNiels Provos {
1941d5ebcf37SNick Mathewson struct evbuffer_chain *chain, **chainp;
1942d5ebcf37SNick Mathewson struct evbuffer_chain *result = NULL;
1943d5ebcf37SNick Mathewson ASSERT_EVBUFFER_LOCKED(buf);
194460e0d59bSNick Mathewson
1945d5ebcf37SNick Mathewson chainp = buf->last_with_datap;
19468c83e995SNick Mathewson
19478c83e995SNick Mathewson /* XXX If *chainp is no longer writeable, but has enough space in its
19488c83e995SNick Mathewson * misalign, this might be a bad idea: we could still use *chainp, not
19498c83e995SNick Mathewson * (*chainp)->next. */
1950d5ebcf37SNick Mathewson if (*chainp && CHAIN_SPACE_LEN(*chainp) == 0)
1951d5ebcf37SNick Mathewson chainp = &(*chainp)->next;
195260e0d59bSNick Mathewson
1953d5ebcf37SNick Mathewson /* 'chain' now points to the first chain with writable space (if any)
1954d5ebcf37SNick Mathewson * We will either use it, realign it, replace it, or resize it. */
1955d5ebcf37SNick Mathewson chain = *chainp;
19565c70ea4cSNiels Provos
1957d9086fc0SNick Mathewson if (chain == NULL ||
1958d9086fc0SNick Mathewson (chain->flags & (EVBUFFER_IMMUTABLE|EVBUFFER_MEM_PINNED_ANY))) {
1959d5ebcf37SNick Mathewson /* We can't use the last_with_data chain at all. Just add a
1960d5ebcf37SNick Mathewson * new one that's big enough. */
1961d5ebcf37SNick Mathewson goto insert_new;
19625c70ea4cSNiels Provos }
19635c70ea4cSNiels Provos
19645c70ea4cSNiels Provos /* If we can fit all the data, then we don't have to do anything */
1965d5ebcf37SNick Mathewson if (CHAIN_SPACE_LEN(chain) >= datlen) {
1966d5ebcf37SNick Mathewson result = chain;
196760e0d59bSNick Mathewson goto ok;
19685c70ea4cSNiels Provos }
19695c70ea4cSNiels Provos
1970d5ebcf37SNick Mathewson /* If the chain is completely empty, just replace it by adding a new
1971d5ebcf37SNick Mathewson * empty chain. */
1972d5ebcf37SNick Mathewson if (chain->off == 0) {
1973d5ebcf37SNick Mathewson goto insert_new;
1974d5ebcf37SNick Mathewson }
1975d5ebcf37SNick Mathewson
1976d5ebcf37SNick Mathewson /* If the misalignment plus the remaining space fulfills our data
1977d5ebcf37SNick Mathewson * needs, we could just force an alignment to happen. Afterwards, we
1978e4f34e8aSNick Mathewson * have enough space. But only do this if we're saving a lot of space
1979e4f34e8aSNick Mathewson * and not moving too much data. Otherwise the space savings are
1980e4f34e8aSNick Mathewson * probably offset by the time lost in copying.
1981d5ebcf37SNick Mathewson */
1982e4f34e8aSNick Mathewson if (evbuffer_chain_should_realign(chain, datlen)) {
1983d5ebcf37SNick Mathewson evbuffer_chain_align(chain);
1984d5ebcf37SNick Mathewson result = chain;
1985d5ebcf37SNick Mathewson goto ok;
1986d5ebcf37SNick Mathewson }
1987d5ebcf37SNick Mathewson
1988d5ebcf37SNick Mathewson /* At this point, we can either resize the last chunk with space in
1989d5ebcf37SNick Mathewson * it, use the next chunk after it, or If we add a new chunk, we waste
1990d5ebcf37SNick Mathewson * CHAIN_SPACE_LEN(chain) bytes in the former last chunk. If we
1991d5ebcf37SNick Mathewson * resize, we have to copy chain->off bytes.
1992d5ebcf37SNick Mathewson */
1993d5ebcf37SNick Mathewson
1994d5ebcf37SNick Mathewson /* Would expanding this chunk be affordable and worthwhile? */
1995d5ebcf37SNick Mathewson if (CHAIN_SPACE_LEN(chain) < chain->buffer_len / 8 ||
1996841ecbd9SNick Mathewson chain->off > MAX_TO_COPY_IN_EXPAND ||
1997a3f4ccd1SAzat Khuzhin datlen >= (EVBUFFER_CHAIN_MAX - chain->off)) {
1998d5ebcf37SNick Mathewson /* It's not worth resizing this chain. Can the next one be
1999d5ebcf37SNick Mathewson * used? */
2000d5ebcf37SNick Mathewson if (chain->next && CHAIN_SPACE_LEN(chain->next) >= datlen) {
2001d5ebcf37SNick Mathewson /* Yes, we can just use the next chain (which should
2002d5ebcf37SNick Mathewson * be empty. */
2003d5ebcf37SNick Mathewson result = chain->next;
2004d5ebcf37SNick Mathewson goto ok;
2005d5ebcf37SNick Mathewson } else {
2006d5ebcf37SNick Mathewson /* No; append a new chain (which will free all
2007d5ebcf37SNick Mathewson * terminal empty chains.) */
2008d5ebcf37SNick Mathewson goto insert_new;
2009d5ebcf37SNick Mathewson }
2010d5ebcf37SNick Mathewson } else {
2011d5ebcf37SNick Mathewson /* Okay, we're going to try to resize this chain: Not doing so
2012d5ebcf37SNick Mathewson * would waste at least 1/8 of its current allocation, and we
2013d5ebcf37SNick Mathewson * can do so without having to copy more than
2014d5ebcf37SNick Mathewson * MAX_TO_COPY_IN_EXPAND bytes. */
2015193c06a7SNiels Provos /* figure out how much space we need */
2016d5ebcf37SNick Mathewson size_t length = chain->off + datlen;
2017d5ebcf37SNick Mathewson struct evbuffer_chain *tmp = evbuffer_chain_new(length);
20185c70ea4cSNiels Provos if (tmp == NULL)
201960e0d59bSNick Mathewson goto err;
2020d5ebcf37SNick Mathewson
2021193c06a7SNiels Provos /* copy the data over that we had so far */
2022193c06a7SNiels Provos tmp->off = chain->off;
2023d5ebcf37SNick Mathewson memcpy(tmp->buffer, chain->buffer + chain->misalign,
2024d5ebcf37SNick Mathewson chain->off);
2025d5ebcf37SNick Mathewson /* fix up the list */
2026d5ebcf37SNick Mathewson EVUTIL_ASSERT(*chainp == chain);
2027d5ebcf37SNick Mathewson result = *chainp = tmp;
2028193c06a7SNiels Provos
2029d5ebcf37SNick Mathewson if (buf->last == chain)
20305c70ea4cSNiels Provos buf->last = tmp;
20315c70ea4cSNiels Provos
2032d5ebcf37SNick Mathewson tmp->next = chain->next;
2033fdf69493SNiels Provos evbuffer_chain_free(chain);
2034d5ebcf37SNick Mathewson goto ok;
2035d5ebcf37SNick Mathewson }
2036193c06a7SNiels Provos
2037d5ebcf37SNick Mathewson insert_new:
2038d5ebcf37SNick Mathewson result = evbuffer_chain_insert_new(buf, datlen);
2039d5ebcf37SNick Mathewson if (!result)
2040d5ebcf37SNick Mathewson goto err;
204160e0d59bSNick Mathewson ok:
2042d5ebcf37SNick Mathewson EVUTIL_ASSERT(result);
2043d5ebcf37SNick Mathewson EVUTIL_ASSERT(CHAIN_SPACE_LEN(result) >= datlen);
204460e0d59bSNick Mathewson err:
204560e0d59bSNick Mathewson return result;
20465c70ea4cSNiels Provos }
20475c70ea4cSNiels Provos
2048c8ac57f1SNick Mathewson /* Make sure that datlen bytes are available for writing in the last n
2049f6eb1f81SNick Mathewson * chains. Never copies or moves data. */
205093d4f884SNick Mathewson int
evbuffer_expand_fast_(struct evbuffer * buf,size_t datlen,int n)2051cb9da0bfSNick Mathewson evbuffer_expand_fast_(struct evbuffer *buf, size_t datlen, int n)
2052f6eb1f81SNick Mathewson {
2053c8ac57f1SNick Mathewson struct evbuffer_chain *chain = buf->last, *tmp, *next;
2054c8ac57f1SNick Mathewson size_t avail;
2055c8ac57f1SNick Mathewson int used;
2056f6eb1f81SNick Mathewson
205760e0d59bSNick Mathewson ASSERT_EVBUFFER_LOCKED(buf);
2058c8ac57f1SNick Mathewson EVUTIL_ASSERT(n >= 2);
205960e0d59bSNick Mathewson
2060fdf69493SNiels Provos if (chain == NULL || (chain->flags & EVBUFFER_IMMUTABLE)) {
2061c8ac57f1SNick Mathewson /* There is no last chunk, or we can't touch the last chunk.
2062c8ac57f1SNick Mathewson * Just add a new chunk. */
2063f6eb1f81SNick Mathewson chain = evbuffer_chain_new(datlen);
2064f6eb1f81SNick Mathewson if (chain == NULL)
2065f6eb1f81SNick Mathewson return (-1);
2066f6eb1f81SNick Mathewson
2067fdf69493SNiels Provos evbuffer_chain_insert(buf, chain);
2068f6eb1f81SNick Mathewson return (0);
2069f6eb1f81SNick Mathewson }
2070f6eb1f81SNick Mathewson
2071c8ac57f1SNick Mathewson used = 0; /* number of chains we're using space in. */
2072c8ac57f1SNick Mathewson avail = 0; /* how much space they have. */
2073c8ac57f1SNick Mathewson /* How many bytes can we stick at the end of buffer as it is? Iterate
2074c8ac57f1SNick Mathewson * over the chains at the end of the buffer, tring to see how much
2075c8ac57f1SNick Mathewson * space we have in the first n. */
2076b7442f8eSNick Mathewson for (chain = *buf->last_with_datap; chain; chain = chain->next) {
2077f6eb1f81SNick Mathewson if (chain->off) {
2078a3245afeSNick Mathewson size_t space = (size_t) CHAIN_SPACE_LEN(chain);
2079b7442f8eSNick Mathewson EVUTIL_ASSERT(chain == *buf->last_with_datap);
2080c8ac57f1SNick Mathewson if (space) {
2081c8ac57f1SNick Mathewson avail += space;
2082c8ac57f1SNick Mathewson ++used;
2083c8ac57f1SNick Mathewson }
2084f6eb1f81SNick Mathewson } else {
2085f6eb1f81SNick Mathewson /* No data in chain; realign it. */
2086f6eb1f81SNick Mathewson chain->misalign = 0;
2087c8ac57f1SNick Mathewson avail += chain->buffer_len;
2088c8ac57f1SNick Mathewson ++used;
2089f6eb1f81SNick Mathewson }
2090c8ac57f1SNick Mathewson if (avail >= datlen) {
2091c8ac57f1SNick Mathewson /* There is already enough space. Just return */
2092f6eb1f81SNick Mathewson return (0);
2093c8ac57f1SNick Mathewson }
2094c8ac57f1SNick Mathewson if (used == n)
2095c8ac57f1SNick Mathewson break;
2096c8ac57f1SNick Mathewson }
2097f6eb1f81SNick Mathewson
2098c8ac57f1SNick Mathewson /* There wasn't enough space in the first n chains with space in
2099c8ac57f1SNick Mathewson * them. Either add a new chain with enough space, or replace all
2100c8ac57f1SNick Mathewson * empty chains with one that has enough space, depending on n. */
2101c8ac57f1SNick Mathewson if (used < n) {
2102c8ac57f1SNick Mathewson /* The loop ran off the end of the chains before it hit n
2103c8ac57f1SNick Mathewson * chains; we can add another. */
2104c8ac57f1SNick Mathewson EVUTIL_ASSERT(chain == NULL);
2105c8ac57f1SNick Mathewson
2106f6eb1f81SNick Mathewson tmp = evbuffer_chain_new(datlen - avail);
2107f6eb1f81SNick Mathewson if (tmp == NULL)
2108f6eb1f81SNick Mathewson return (-1);
2109f6eb1f81SNick Mathewson
2110c8ac57f1SNick Mathewson buf->last->next = tmp;
2111f6eb1f81SNick Mathewson buf->last = tmp;
2112c8ac57f1SNick Mathewson /* (we would only set last_with_data if we added the first
2113c8ac57f1SNick Mathewson * chain. But if the buffer had no chains, we would have
2114c8ac57f1SNick Mathewson * just allocated a new chain earlier) */
2115c8ac57f1SNick Mathewson return (0);
2116c8ac57f1SNick Mathewson } else {
2117c8ac57f1SNick Mathewson /* Nuke _all_ the empty chains. */
2118c8ac57f1SNick Mathewson int rmv_all = 0; /* True iff we removed last_with_data. */
2119b7442f8eSNick Mathewson chain = *buf->last_with_datap;
2120c8ac57f1SNick Mathewson if (!chain->off) {
2121c8ac57f1SNick Mathewson EVUTIL_ASSERT(chain == buf->first);
2122c8ac57f1SNick Mathewson rmv_all = 1;
2123c8ac57f1SNick Mathewson avail = 0;
2124c8ac57f1SNick Mathewson } else {
2125841ecbd9SNick Mathewson /* can't overflow, since only mutable chains have
2126841ecbd9SNick Mathewson * huge misaligns. */
2127a3245afeSNick Mathewson avail = (size_t) CHAIN_SPACE_LEN(chain);
2128c8ac57f1SNick Mathewson chain = chain->next;
2129f6eb1f81SNick Mathewson }
2130f6eb1f81SNick Mathewson
2131c8ac57f1SNick Mathewson
2132c8ac57f1SNick Mathewson for (; chain; chain = next) {
2133c8ac57f1SNick Mathewson next = chain->next;
2134c8ac57f1SNick Mathewson EVUTIL_ASSERT(chain->off == 0);
2135c8ac57f1SNick Mathewson evbuffer_chain_free(chain);
2136c8ac57f1SNick Mathewson }
2137841ecbd9SNick Mathewson EVUTIL_ASSERT(datlen >= avail);
2138c8ac57f1SNick Mathewson tmp = evbuffer_chain_new(datlen - avail);
2139c8ac57f1SNick Mathewson if (tmp == NULL) {
2140c8ac57f1SNick Mathewson if (rmv_all) {
2141c8ac57f1SNick Mathewson ZERO_CHAIN(buf);
2142c8ac57f1SNick Mathewson } else {
2143b7442f8eSNick Mathewson buf->last = *buf->last_with_datap;
2144b7442f8eSNick Mathewson (*buf->last_with_datap)->next = NULL;
2145c8ac57f1SNick Mathewson }
2146c8ac57f1SNick Mathewson return (-1);
2147c8ac57f1SNick Mathewson }
2148c8ac57f1SNick Mathewson
2149c8ac57f1SNick Mathewson if (rmv_all) {
2150b7442f8eSNick Mathewson buf->first = buf->last = tmp;
2151b7442f8eSNick Mathewson buf->last_with_datap = &buf->first;
2152c8ac57f1SNick Mathewson } else {
2153b7442f8eSNick Mathewson (*buf->last_with_datap)->next = tmp;
2154c8ac57f1SNick Mathewson buf->last = tmp;
2155c8ac57f1SNick Mathewson }
2156f6eb1f81SNick Mathewson return (0);
2157f6eb1f81SNick Mathewson }
2158c8ac57f1SNick Mathewson }
2159f6eb1f81SNick Mathewson
2160d5ebcf37SNick Mathewson int
evbuffer_expand(struct evbuffer * buf,size_t datlen)2161d5ebcf37SNick Mathewson evbuffer_expand(struct evbuffer *buf, size_t datlen)
2162d5ebcf37SNick Mathewson {
2163d5ebcf37SNick Mathewson struct evbuffer_chain *chain;
2164d5ebcf37SNick Mathewson
2165d5ebcf37SNick Mathewson EVBUFFER_LOCK(buf);
2166d5ebcf37SNick Mathewson chain = evbuffer_expand_singlechain(buf, datlen);
2167d5ebcf37SNick Mathewson EVBUFFER_UNLOCK(buf);
2168d5ebcf37SNick Mathewson return chain ? 0 : -1;
2169d5ebcf37SNick Mathewson }
2170d5ebcf37SNick Mathewson
21715c70ea4cSNiels Provos /*
21725c70ea4cSNiels Provos * Reads data from a file descriptor into a buffer.
21735c70ea4cSNiels Provos */
21745c70ea4cSNiels Provos
217568120d9bSNick Mathewson #if defined(EVENT__HAVE_SYS_UIO_H) || defined(_WIN32)
2176f6eb1f81SNick Mathewson #define USE_IOVEC_IMPL
2177f6eb1f81SNick Mathewson #endif
2178f6eb1f81SNick Mathewson
2179f6eb1f81SNick Mathewson #ifdef USE_IOVEC_IMPL
2180f6eb1f81SNick Mathewson
218168120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_UIO_H
2182f6eb1f81SNick Mathewson /* number of iovec we use for writev, fragmentation is going to determine
2183f6eb1f81SNick Mathewson * how much we end up writing */
2184fdc640b0SNick Mathewson
2185fdc640b0SNick Mathewson #define DEFAULT_WRITE_IOVEC 128
2186fdc640b0SNick Mathewson
2187fdc640b0SNick Mathewson #if defined(UIO_MAXIOV) && UIO_MAXIOV < DEFAULT_WRITE_IOVEC
2188fdc640b0SNick Mathewson #define NUM_WRITE_IOVEC UIO_MAXIOV
2189fdc640b0SNick Mathewson #elif defined(IOV_MAX) && IOV_MAX < DEFAULT_WRITE_IOVEC
2190fdc640b0SNick Mathewson #define NUM_WRITE_IOVEC IOV_MAX
2191fdc640b0SNick Mathewson #else
2192fdc640b0SNick Mathewson #define NUM_WRITE_IOVEC DEFAULT_WRITE_IOVEC
2193fdc640b0SNick Mathewson #endif
2194fdc640b0SNick Mathewson
2195f6eb1f81SNick Mathewson #define IOV_TYPE struct iovec
2196f6eb1f81SNick Mathewson #define IOV_PTR_FIELD iov_base
2197f6eb1f81SNick Mathewson #define IOV_LEN_FIELD iov_len
2198545a6114SNick Mathewson #define IOV_LEN_TYPE size_t
2199f6eb1f81SNick Mathewson #else
2200e470ad3cSNick Mathewson #define NUM_WRITE_IOVEC 16
2201f6eb1f81SNick Mathewson #define IOV_TYPE WSABUF
2202f6eb1f81SNick Mathewson #define IOV_PTR_FIELD buf
2203f6eb1f81SNick Mathewson #define IOV_LEN_FIELD len
2204545a6114SNick Mathewson #define IOV_LEN_TYPE unsigned long
2205f6eb1f81SNick Mathewson #endif
2206f6eb1f81SNick Mathewson #endif
2207e470ad3cSNick Mathewson #define NUM_READ_IOVEC 4
2208f6eb1f81SNick Mathewson
22095c70ea4cSNiels Provos #define EVBUFFER_MAX_READ 4096
22105c70ea4cSNiels Provos
2211829b52b6SNick Mathewson /** Helper function to figure out which space to use for reading data into
2212829b52b6SNick Mathewson an evbuffer. Internal use only.
2213829b52b6SNick Mathewson
2214829b52b6SNick Mathewson @param buf The buffer to read into
2215829b52b6SNick Mathewson @param howmuch How much we want to read.
2216c8ac57f1SNick Mathewson @param vecs An array of two or more iovecs or WSABUFs.
2217c8ac57f1SNick Mathewson @param n_vecs_avail The length of vecs
2218829b52b6SNick Mathewson @param chainp A pointer to a variable to hold the first chain we're
2219829b52b6SNick Mathewson reading into.
22208997f234SNick Mathewson @param exact Boolean: if true, we do not provide more than 'howmuch'
22218997f234SNick Mathewson space in the vectors, even if more space is available.
2222829b52b6SNick Mathewson @return The number of buffers we're using.
2223829b52b6SNick Mathewson */
2224829b52b6SNick Mathewson int
evbuffer_read_setup_vecs_(struct evbuffer * buf,ev_ssize_t howmuch,struct evbuffer_iovec * vecs,int n_vecs_avail,struct evbuffer_chain *** chainp,int exact)2225cb9da0bfSNick Mathewson evbuffer_read_setup_vecs_(struct evbuffer *buf, ev_ssize_t howmuch,
2226c8ac57f1SNick Mathewson struct evbuffer_iovec *vecs, int n_vecs_avail,
2227b7442f8eSNick Mathewson struct evbuffer_chain ***chainp, int exact)
2228829b52b6SNick Mathewson {
2229b7442f8eSNick Mathewson struct evbuffer_chain *chain;
2230b7442f8eSNick Mathewson struct evbuffer_chain **firstchainp;
2231c8ac57f1SNick Mathewson size_t so_far;
2232c8ac57f1SNick Mathewson int i;
2233c8ac57f1SNick Mathewson ASSERT_EVBUFFER_LOCKED(buf);
2234829b52b6SNick Mathewson
2235e865eb93SNick Mathewson if (howmuch < 0)
2236e865eb93SNick Mathewson return -1;
2237e865eb93SNick Mathewson
2238c8ac57f1SNick Mathewson so_far = 0;
2239c8ac57f1SNick Mathewson /* Let firstchain be the first chain with any space on it */
2240b7442f8eSNick Mathewson firstchainp = buf->last_with_datap;
22415e439e50SAzat Khuzhin EVUTIL_ASSERT(*firstchainp);
2242b7442f8eSNick Mathewson if (CHAIN_SPACE_LEN(*firstchainp) == 0) {
2243b7442f8eSNick Mathewson firstchainp = &(*firstchainp)->next;
2244b7442f8eSNick Mathewson }
2245829b52b6SNick Mathewson
2246b7442f8eSNick Mathewson chain = *firstchainp;
22475e439e50SAzat Khuzhin EVUTIL_ASSERT(chain);
22489c8db0f8SNick Mathewson for (i = 0; i < n_vecs_avail && so_far < (size_t)howmuch; ++i) {
2249a3245afeSNick Mathewson size_t avail = (size_t) CHAIN_SPACE_LEN(chain);
225065abdc20Sniks if (avail > (howmuch - so_far) && exact)
225165abdc20Sniks avail = howmuch - so_far;
22522c62062eSAzat Khuzhin vecs[i].iov_base = (void *)CHAIN_SPACE_PTR(chain);
2253c8ac57f1SNick Mathewson vecs[i].iov_len = avail;
2254c8ac57f1SNick Mathewson so_far += avail;
2255c8ac57f1SNick Mathewson chain = chain->next;
2256829b52b6SNick Mathewson }
2257829b52b6SNick Mathewson
2258b7442f8eSNick Mathewson *chainp = firstchainp;
2259c8ac57f1SNick Mathewson return i;
2260829b52b6SNick Mathewson }
2261829b52b6SNick Mathewson
22623467f2faSNick Mathewson static int
get_n_bytes_readable_on_socket(evutil_socket_t fd)22633467f2faSNick Mathewson get_n_bytes_readable_on_socket(evutil_socket_t fd)
22643467f2faSNick Mathewson {
22659f560bfaSNick Mathewson #if defined(FIONREAD) && defined(_WIN32)
22663467f2faSNick Mathewson unsigned long lng = EVBUFFER_MAX_READ;
22673467f2faSNick Mathewson if (ioctlsocket(fd, FIONREAD, &lng) < 0)
22683467f2faSNick Mathewson return -1;
2269841ecbd9SNick Mathewson /* Can overflow, but mostly harmlessly. XXXX */
22703467f2faSNick Mathewson return (int)lng;
22713467f2faSNick Mathewson #elif defined(FIONREAD)
22723467f2faSNick Mathewson int n = EVBUFFER_MAX_READ;
22733467f2faSNick Mathewson if (ioctl(fd, FIONREAD, &n) < 0)
22743467f2faSNick Mathewson return -1;
22753467f2faSNick Mathewson return n;
22763467f2faSNick Mathewson #else
22773467f2faSNick Mathewson return EVBUFFER_MAX_READ;
22783467f2faSNick Mathewson #endif
22793467f2faSNick Mathewson }
22803467f2faSNick Mathewson
22810b22ca19SNick Mathewson /* TODO(niels): should this function return ev_ssize_t and take ev_ssize_t
2282fdf69493SNiels Provos * as howmuch? */
22835c70ea4cSNiels Provos int
evbuffer_read(struct evbuffer * buf,evutil_socket_t fd,int howmuch)22845c70ea4cSNiels Provos evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
22855c70ea4cSNiels Provos {
22867bcace2dSNick Mathewson struct evbuffer_chain **chainp;
22873467f2faSNick Mathewson int n;
228860e0d59bSNick Mathewson int result;
228960e0d59bSNick Mathewson
2290f6eb1f81SNick Mathewson #ifdef USE_IOVEC_IMPL
2291e470ad3cSNick Mathewson int nvecs, i, remaining;
2292f6eb1f81SNick Mathewson #else
22937bcace2dSNick Mathewson struct evbuffer_chain *chain;
2294f6eb1f81SNick Mathewson unsigned char *p;
2295f6eb1f81SNick Mathewson #endif
229660e0d59bSNick Mathewson
229776cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
22985c70ea4cSNiels Provos
2299747331d1SNick Mathewson if (buf->freeze_end) {
2300747331d1SNick Mathewson result = -1;
2301747331d1SNick Mathewson goto done;
2302747331d1SNick Mathewson }
2303747331d1SNick Mathewson
23043467f2faSNick Mathewson n = get_n_bytes_readable_on_socket(fd);
23053467f2faSNick Mathewson if (n <= 0 || n > EVBUFFER_MAX_READ)
23065c70ea4cSNiels Provos n = EVBUFFER_MAX_READ;
23075c70ea4cSNiels Provos if (howmuch < 0 || howmuch > n)
23085c70ea4cSNiels Provos howmuch = n;
23095c70ea4cSNiels Provos
2310f6eb1f81SNick Mathewson #ifdef USE_IOVEC_IMPL
2311fdf69493SNiels Provos /* Since we can use iovecs, we're willing to use the last
2312e470ad3cSNick Mathewson * NUM_READ_IOVEC chains. */
2313cb9da0bfSNick Mathewson if (evbuffer_expand_fast_(buf, howmuch, NUM_READ_IOVEC) == -1) {
231460e0d59bSNick Mathewson result = -1;
231560e0d59bSNick Mathewson goto done;
2316f6eb1f81SNick Mathewson } else {
2317e470ad3cSNick Mathewson IOV_TYPE vecs[NUM_READ_IOVEC];
2318cb9da0bfSNick Mathewson #ifdef EVBUFFER_IOVEC_IS_NATIVE_
2319cb9da0bfSNick Mathewson nvecs = evbuffer_read_setup_vecs_(buf, howmuch, vecs,
2320b7442f8eSNick Mathewson NUM_READ_IOVEC, &chainp, 1);
23218997f234SNick Mathewson #else
23228997f234SNick Mathewson /* We aren't using the native struct iovec. Therefore,
23238997f234SNick Mathewson we are on win32. */
2324e470ad3cSNick Mathewson struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];
2325cb9da0bfSNick Mathewson nvecs = evbuffer_read_setup_vecs_(buf, howmuch, ev_vecs, 2,
2326b7442f8eSNick Mathewson &chainp, 1);
232723243b8aSNick Mathewson
23281e7b9868SNick Mathewson for (i=0; i < nvecs; ++i)
2329e470ad3cSNick Mathewson WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
23308997f234SNick Mathewson #endif
2331f6eb1f81SNick Mathewson
23329f560bfaSNick Mathewson #ifdef _WIN32
2333f6eb1f81SNick Mathewson {
2334f6eb1f81SNick Mathewson DWORD bytesRead;
2335cc049bfcSNick Mathewson DWORD flags=0;
2336cc049bfcSNick Mathewson if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) {
2337cc049bfcSNick Mathewson /* The read failed. It might be a close,
2338cc049bfcSNick Mathewson * or it might be an error. */
2339cc049bfcSNick Mathewson if (WSAGetLastError() == WSAECONNABORTED)
2340cc049bfcSNick Mathewson n = 0;
2341f6eb1f81SNick Mathewson else
2342cc049bfcSNick Mathewson n = -1;
2343cc049bfcSNick Mathewson } else
2344f6eb1f81SNick Mathewson n = bytesRead;
2345f6eb1f81SNick Mathewson }
2346f6eb1f81SNick Mathewson #else
2347f6eb1f81SNick Mathewson n = readv(fd, vecs, nvecs);
2348f6eb1f81SNick Mathewson #endif
2349f6eb1f81SNick Mathewson }
2350f6eb1f81SNick Mathewson
2351f6eb1f81SNick Mathewson #else /*!USE_IOVEC_IMPL*/
23525c70ea4cSNiels Provos /* If we don't have FIONREAD, we might waste some space here */
23530322ce0aSNick Mathewson /* XXX we _will_ waste some space here if there is any space left
23540322ce0aSNick Mathewson * over on buf->last. */
2355d5ebcf37SNick Mathewson if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) {
235660e0d59bSNick Mathewson result = -1;
235760e0d59bSNick Mathewson goto done;
235860e0d59bSNick Mathewson }
23595c70ea4cSNiels Provos
23605c70ea4cSNiels Provos /* We can append new data at this point */
23615c70ea4cSNiels Provos p = chain->buffer + chain->misalign + chain->off;
23625c70ea4cSNiels Provos
23639f560bfaSNick Mathewson #ifndef _WIN32
23645c70ea4cSNiels Provos n = read(fd, p, howmuch);
23655c70ea4cSNiels Provos #else
23665c70ea4cSNiels Provos n = recv(fd, p, howmuch, 0);
23675c70ea4cSNiels Provos #endif
2368f6eb1f81SNick Mathewson #endif /* USE_IOVEC_IMPL */
2369f6eb1f81SNick Mathewson
237060e0d59bSNick Mathewson if (n == -1) {
237160e0d59bSNick Mathewson result = -1;
237260e0d59bSNick Mathewson goto done;
237360e0d59bSNick Mathewson }
237460e0d59bSNick Mathewson if (n == 0) {
237560e0d59bSNick Mathewson result = 0;
237660e0d59bSNick Mathewson goto done;
237760e0d59bSNick Mathewson }
23785c70ea4cSNiels Provos
2379f6eb1f81SNick Mathewson #ifdef USE_IOVEC_IMPL
2380e470ad3cSNick Mathewson remaining = n;
2381e470ad3cSNick Mathewson for (i=0; i < nvecs; ++i) {
2382841ecbd9SNick Mathewson /* can't overflow, since only mutable chains have
2383841ecbd9SNick Mathewson * huge misaligns. */
2384841ecbd9SNick Mathewson size_t space = (size_t) CHAIN_SPACE_LEN(*chainp);
2385841ecbd9SNick Mathewson /* XXXX This is a kludge that can waste space in perverse
2386841ecbd9SNick Mathewson * situations. */
2387841ecbd9SNick Mathewson if (space > EVBUFFER_CHAIN_MAX)
2388841ecbd9SNick Mathewson space = EVBUFFER_CHAIN_MAX;
2389841ecbd9SNick Mathewson if ((ev_ssize_t)space < remaining) {
2390b7442f8eSNick Mathewson (*chainp)->off += space;
2391545a6114SNick Mathewson remaining -= (int)space;
2392f6eb1f81SNick Mathewson } else {
2393b7442f8eSNick Mathewson (*chainp)->off += remaining;
2394b7442f8eSNick Mathewson buf->last_with_datap = chainp;
2395e470ad3cSNick Mathewson break;
2396f6eb1f81SNick Mathewson }
2397b7442f8eSNick Mathewson chainp = &(*chainp)->next;
2398f6eb1f81SNick Mathewson }
2399f6eb1f81SNick Mathewson #else
2400f6eb1f81SNick Mathewson chain->off += n;
2401b7442f8eSNick Mathewson advance_last_with_data(buf);
2402f6eb1f81SNick Mathewson #endif
24035c70ea4cSNiels Provos buf->total_len += n;
2404f1b1bad4SNick Mathewson buf->n_add_for_cb += n;
24055c70ea4cSNiels Provos
24065c70ea4cSNiels Provos /* Tell someone about changes in this buffer */
24078ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
240860e0d59bSNick Mathewson result = n;
240960e0d59bSNick Mathewson done:
241076cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
241160e0d59bSNick Mathewson return result;
24125c70ea4cSNiels Provos }
24135c70ea4cSNiels Provos
2414fdf69493SNiels Provos #ifdef USE_IOVEC_IMPL
2415fdf69493SNiels Provos static inline int
evbuffer_write_iovec(struct evbuffer * buffer,evutil_socket_t fd,ev_ssize_t howmuch)2416fdf69493SNiels Provos evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
24177f0ad2f6SNick Mathewson ev_ssize_t howmuch)
24185c70ea4cSNiels Provos {
2419e470ad3cSNick Mathewson IOV_TYPE iov[NUM_WRITE_IOVEC];
24205c70ea4cSNiels Provos struct evbuffer_chain *chain = buffer->first;
2421fdf69493SNiels Provos int n, i = 0;
242260e0d59bSNick Mathewson
2423e865eb93SNick Mathewson if (howmuch < 0)
2424e865eb93SNick Mathewson return -1;
2425e865eb93SNick Mathewson
242660e0d59bSNick Mathewson ASSERT_EVBUFFER_LOCKED(buffer);
2427fdf69493SNiels Provos /* XXX make this top out at some maximal data length? if the
2428fdf69493SNiels Provos * buffer has (say) 1MB in it, split over 128 chains, there's
2429fdf69493SNiels Provos * no way it all gets written in one go. */
2430e470ad3cSNick Mathewson while (chain != NULL && i < NUM_WRITE_IOVEC && howmuch) {
2431fdf69493SNiels Provos #ifdef USE_SENDFILE
2432fdf69493SNiels Provos /* we cannot write the file info via writev */
2433fdf69493SNiels Provos if (chain->flags & EVBUFFER_SENDFILE)
2434fdf69493SNiels Provos break;
2435fdf69493SNiels Provos #endif
2436d469c503SGiuseppe Scrivano iov[i].IOV_PTR_FIELD = (void *) (chain->buffer + chain->misalign);
2437e865eb93SNick Mathewson if ((size_t)howmuch >= chain->off) {
2438545a6114SNick Mathewson /* XXXcould be problematic when windows supports mmap*/
2439545a6114SNick Mathewson iov[i++].IOV_LEN_FIELD = (IOV_LEN_TYPE)chain->off;
244099db0e7fSNick Mathewson howmuch -= chain->off;
244199db0e7fSNick Mathewson } else {
2442545a6114SNick Mathewson /* XXXcould be problematic when windows supports mmap*/
2443545a6114SNick Mathewson iov[i++].IOV_LEN_FIELD = (IOV_LEN_TYPE)howmuch;
244499db0e7fSNick Mathewson break;
244599db0e7fSNick Mathewson }
24465c70ea4cSNiels Provos chain = chain->next;
24475c70ea4cSNiels Provos }
24486a4ec5c2SNick Mathewson if (! i)
24496a4ec5c2SNick Mathewson return 0;
24509852107fSNick Mathewson
24519f560bfaSNick Mathewson #ifdef _WIN32
24526d3ed065SNick Mathewson {
2453cc049bfcSNick Mathewson DWORD bytesSent;
2454cc049bfcSNick Mathewson if (WSASend(fd, iov, i, &bytesSent, 0, NULL, NULL))
2455822ca048SNick Mathewson n = -1;
2456822ca048SNick Mathewson else
2457822ca048SNick Mathewson n = bytesSent;
24586d3ed065SNick Mathewson }
24595c70ea4cSNiels Provos #else
24606d3ed065SNick Mathewson n = writev(fd, iov, i);
24616d3ed065SNick Mathewson #endif
2462fdf69493SNiels Provos return (n);
2463fdf69493SNiels Provos }
2464fdf69493SNiels Provos #endif
2465fdf69493SNiels Provos
2466fdf69493SNiels Provos #ifdef USE_SENDFILE
2467fdf69493SNiels Provos static inline int
evbuffer_write_sendfile(struct evbuffer * buffer,evutil_socket_t dest_fd,ev_ssize_t howmuch)2468e72afae0SNick Mathewson evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t dest_fd,
24690b22ca19SNick Mathewson ev_ssize_t howmuch)
2470fdf69493SNiels Provos {
2471fdf69493SNiels Provos struct evbuffer_chain *chain = buffer->first;
2472e72afae0SNick Mathewson struct evbuffer_chain_file_segment *info =
2473e72afae0SNick Mathewson EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment,
2474e72afae0SNick Mathewson chain);
2475e72afae0SNick Mathewson const int source_fd = info->segment->fd;
24765c4c13d8SNiels Provos #if defined(SENDFILE_IS_MACOSX) || defined(SENDFILE_IS_FREEBSD)
2477fdf69493SNiels Provos int res;
2478e72afae0SNick Mathewson ev_off_t len = chain->off;
247922bd5b42SNick Mathewson #elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS)
24800b22ca19SNick Mathewson ev_ssize_t res;
2481a0bfe2c4SAzat Khuzhin off_t offset = chain->misalign;
248260e0d59bSNick Mathewson #endif
248360e0d59bSNick Mathewson
248460e0d59bSNick Mathewson ASSERT_EVBUFFER_LOCKED(buffer);
248560e0d59bSNick Mathewson
248611cab334SNick Mathewson #if defined(SENDFILE_IS_MACOSX)
2487e72afae0SNick Mathewson res = sendfile(source_fd, dest_fd, chain->misalign, &len, NULL, 0);
2488fdf69493SNiels Provos if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
2489fdf69493SNiels Provos return (-1);
2490fdf69493SNiels Provos
2491fdf69493SNiels Provos return (len);
249211cab334SNick Mathewson #elif defined(SENDFILE_IS_FREEBSD)
2493e72afae0SNick Mathewson res = sendfile(source_fd, dest_fd, chain->misalign, chain->off, NULL, &len, 0);
24945c4c13d8SNiels Provos if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
24955c4c13d8SNiels Provos return (-1);
24965c4c13d8SNiels Provos
24975c4c13d8SNiels Provos return (len);
249811cab334SNick Mathewson #elif defined(SENDFILE_IS_LINUX)
24998b5bd774SNiels Provos /* TODO(niels): implement splice */
2500e72afae0SNick Mathewson res = sendfile(dest_fd, source_fd, &offset, chain->off);
2501fdf69493SNiels Provos if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
2502e3fd294aSNick Mathewson /* if this is EAGAIN or EINTR return 0; otherwise, -1 */
2503fdf69493SNiels Provos return (0);
2504fdf69493SNiels Provos }
2505fdf69493SNiels Provos return (res);
250622bd5b42SNick Mathewson #elif defined(SENDFILE_IS_SOLARIS)
2507643922e9SMichael Herf {
2508643922e9SMichael Herf const off_t offset_orig = offset;
2509e72afae0SNick Mathewson res = sendfile(dest_fd, source_fd, &offset, chain->off);
251022bd5b42SNick Mathewson if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
2511643922e9SMichael Herf if (offset - offset_orig)
2512643922e9SMichael Herf return offset - offset_orig;
2513643922e9SMichael Herf /* if this is EAGAIN or EINTR and no bytes were
2514643922e9SMichael Herf * written, return 0 */
251522bd5b42SNick Mathewson return (0);
251622bd5b42SNick Mathewson }
251722bd5b42SNick Mathewson return (res);
2518643922e9SMichael Herf }
2519fdf69493SNiels Provos #endif
2520fdf69493SNiels Provos }
2521fdf69493SNiels Provos #endif
2522fdf69493SNiels Provos
2523fdf69493SNiels Provos int
evbuffer_write_atmost(struct evbuffer * buffer,evutil_socket_t fd,ev_ssize_t howmuch)2524fdf69493SNiels Provos evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
25250b22ca19SNick Mathewson ev_ssize_t howmuch)
2526fdf69493SNiels Provos {
2527747331d1SNick Mathewson int n = -1;
2528fdf69493SNiels Provos
252976cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
253060e0d59bSNick Mathewson
2531747331d1SNick Mathewson if (buffer->freeze_start) {
2532747331d1SNick Mathewson goto done;
2533747331d1SNick Mathewson }
2534747331d1SNick Mathewson
25359c8db0f8SNick Mathewson if (howmuch < 0 || (size_t)howmuch > buffer->total_len)
2536fdf69493SNiels Provos howmuch = buffer->total_len;
2537fdf69493SNiels Provos
25388e227b04SNick Mathewson if (howmuch > 0) {
2539fdf69493SNiels Provos #ifdef USE_SENDFILE
2540fdf69493SNiels Provos struct evbuffer_chain *chain = buffer->first;
2541fdf69493SNiels Provos if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE))
2542fdf69493SNiels Provos n = evbuffer_write_sendfile(buffer, fd, howmuch);
25438e227b04SNick Mathewson else {
2544fdf69493SNiels Provos #endif
2545fdf69493SNiels Provos #ifdef USE_IOVEC_IMPL
2546fdf69493SNiels Provos n = evbuffer_write_iovec(buffer, fd, howmuch);
25479f560bfaSNick Mathewson #elif defined(_WIN32)
2548fdf69493SNiels Provos /* XXX(nickm) Don't disable this code until we know if
2549fdf69493SNiels Provos * the WSARecv code above works. */
255099db0e7fSNick Mathewson void *p = evbuffer_pullup(buffer, howmuch);
255160f8f729SNick Mathewson EVUTIL_ASSERT(p || !howmuch);
255299db0e7fSNick Mathewson n = send(fd, p, howmuch, 0);
25536d3ed065SNick Mathewson #else
25546d3ed065SNick Mathewson void *p = evbuffer_pullup(buffer, howmuch);
255560f8f729SNick Mathewson EVUTIL_ASSERT(p || !howmuch);
25566d3ed065SNick Mathewson n = write(fd, p, howmuch);
25575c70ea4cSNiels Provos #endif
25588e227b04SNick Mathewson #ifdef USE_SENDFILE
25598e227b04SNick Mathewson }
25608e227b04SNick Mathewson #endif
256199db0e7fSNick Mathewson }
256299db0e7fSNick Mathewson
256360e0d59bSNick Mathewson if (n > 0)
25645c70ea4cSNiels Provos evbuffer_drain(buffer, n);
25655c70ea4cSNiels Provos
2566747331d1SNick Mathewson done:
256776cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
25685c70ea4cSNiels Provos return (n);
25695c70ea4cSNiels Provos }
25705c70ea4cSNiels Provos
257199db0e7fSNick Mathewson int
evbuffer_write(struct evbuffer * buffer,evutil_socket_t fd)257299db0e7fSNick Mathewson evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd)
257399db0e7fSNick Mathewson {
257499db0e7fSNick Mathewson return evbuffer_write_atmost(buffer, fd, -1);
257599db0e7fSNick Mathewson }
257699db0e7fSNick Mathewson
25776bf1ca78SNick Mathewson unsigned char *
evbuffer_find(struct evbuffer * buffer,const unsigned char * what,size_t len)25786bf1ca78SNick Mathewson evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len)
25795c70ea4cSNiels Provos {
2580f90500a5SNick Mathewson unsigned char *search;
2581f90500a5SNick Mathewson struct evbuffer_ptr ptr;
25825c70ea4cSNiels Provos
258376cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
2584f90500a5SNick Mathewson
258560e0d59bSNick Mathewson ptr = evbuffer_search(buffer, (const char *)what, len, NULL);
258660e0d59bSNick Mathewson if (ptr.pos < 0) {
258760e0d59bSNick Mathewson search = NULL;
258860e0d59bSNick Mathewson } else {
2589f90500a5SNick Mathewson search = evbuffer_pullup(buffer, ptr.pos + len);
2590747331d1SNick Mathewson if (search)
259160e0d59bSNick Mathewson search += ptr.pos;
259260e0d59bSNick Mathewson }
259376cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
259460e0d59bSNick Mathewson return search;
2595ec2c1db4SNiels Provos }
2596ec2c1db4SNiels Provos
25977b9d1395SNick Mathewson /* Subract <b>howfar</b> from the position of <b>pos</b> within
25987b9d1395SNick Mathewson * <b>buf</b>. Returns 0 on success, -1 on failure.
25997b9d1395SNick Mathewson *
26007b9d1395SNick Mathewson * This isn't exposed yet, because of potential inefficiency issues.
26017b9d1395SNick Mathewson * Maybe it should be. */
26027b9d1395SNick Mathewson static int
evbuffer_ptr_subtract(struct evbuffer * buf,struct evbuffer_ptr * pos,size_t howfar)26037b9d1395SNick Mathewson evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos,
26047b9d1395SNick Mathewson size_t howfar)
26057b9d1395SNick Mathewson {
2606841ecbd9SNick Mathewson if (pos->pos < 0)
2607841ecbd9SNick Mathewson return -1;
26087b9d1395SNick Mathewson if (howfar > (size_t)pos->pos)
26097b9d1395SNick Mathewson return -1;
2610cb9da0bfSNick Mathewson if (pos->internal_.chain && howfar <= pos->internal_.pos_in_chain) {
2611cb9da0bfSNick Mathewson pos->internal_.pos_in_chain -= howfar;
26127b9d1395SNick Mathewson pos->pos -= howfar;
26137b9d1395SNick Mathewson return 0;
26147b9d1395SNick Mathewson } else {
26157b9d1395SNick Mathewson const size_t newpos = pos->pos - howfar;
26167b9d1395SNick Mathewson /* Here's the inefficient part: it walks over the
26177b9d1395SNick Mathewson * chains until we hit newpos. */
26187b9d1395SNick Mathewson return evbuffer_ptr_set(buf, pos, newpos, EVBUFFER_PTR_SET);
26197b9d1395SNick Mathewson }
26207b9d1395SNick Mathewson }
26217b9d1395SNick Mathewson
2622ec2c1db4SNiels Provos int
evbuffer_ptr_set(struct evbuffer * buf,struct evbuffer_ptr * pos,size_t position,enum evbuffer_ptr_how how)2623f90500a5SNick Mathewson evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
2624f90500a5SNick Mathewson size_t position, enum evbuffer_ptr_how how)
2625f90500a5SNick Mathewson {
2626f90500a5SNick Mathewson size_t left = position;
2627f90500a5SNick Mathewson struct evbuffer_chain *chain = NULL;
2628e6fe1da9SNick Mathewson int result = 0;
2629f90500a5SNick Mathewson
263076cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
263160e0d59bSNick Mathewson
2632f90500a5SNick Mathewson switch (how) {
2633f90500a5SNick Mathewson case EVBUFFER_PTR_SET:
2634f90500a5SNick Mathewson chain = buf->first;
2635f90500a5SNick Mathewson pos->pos = position;
2636f90500a5SNick Mathewson position = 0;
2637f90500a5SNick Mathewson break;
2638f90500a5SNick Mathewson case EVBUFFER_PTR_ADD:
2639f90500a5SNick Mathewson /* this avoids iterating over all previous chains if
2640f90500a5SNick Mathewson we just want to advance the position */
2641841ecbd9SNick Mathewson if (pos->pos < 0 || EV_SIZE_MAX - position < (size_t)pos->pos) {
2642841ecbd9SNick Mathewson EVBUFFER_UNLOCK(buf);
2643841ecbd9SNick Mathewson return -1;
2644841ecbd9SNick Mathewson }
2645cb9da0bfSNick Mathewson chain = pos->internal_.chain;
2646f90500a5SNick Mathewson pos->pos += position;
2647cb9da0bfSNick Mathewson position = pos->internal_.pos_in_chain;
2648f90500a5SNick Mathewson break;
2649f90500a5SNick Mathewson }
2650f90500a5SNick Mathewson
2651841ecbd9SNick Mathewson EVUTIL_ASSERT(EV_SIZE_MAX - left >= position);
2652f90500a5SNick Mathewson while (chain && position + left >= chain->off) {
2653f90500a5SNick Mathewson left -= chain->off - position;
2654f90500a5SNick Mathewson chain = chain->next;
2655f90500a5SNick Mathewson position = 0;
2656f90500a5SNick Mathewson }
2657f90500a5SNick Mathewson if (chain) {
2658cb9da0bfSNick Mathewson pos->internal_.chain = chain;
2659cb9da0bfSNick Mathewson pos->internal_.pos_in_chain = position + left;
26607aeb2fd4SNir Soffer } else if (left == 0) {
26617aeb2fd4SNir Soffer /* The first byte in the (nonexistent) chain after the last chain */
2662cb9da0bfSNick Mathewson pos->internal_.chain = NULL;
2663cb9da0bfSNick Mathewson pos->internal_.pos_in_chain = 0;
2664f90500a5SNick Mathewson } else {
2665e3e97ae3SNir Soffer PTR_NOT_FOUND(pos);
2666e6fe1da9SNick Mathewson result = -1;
2667f90500a5SNick Mathewson }
2668f90500a5SNick Mathewson
266976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
267060e0d59bSNick Mathewson
2671e6fe1da9SNick Mathewson return result;
2672f90500a5SNick Mathewson }
2673f90500a5SNick Mathewson
2674f90500a5SNick Mathewson /**
2675f90500a5SNick Mathewson Compare the bytes in buf at position pos to the len bytes in mem. Return
2676f90500a5SNick Mathewson less than 0, 0, or greater than 0 as memcmp.
2677f90500a5SNick Mathewson */
2678f90500a5SNick Mathewson static int
evbuffer_ptr_memcmp(const struct evbuffer * buf,const struct evbuffer_ptr * pos,const char * mem,size_t len)2679f90500a5SNick Mathewson evbuffer_ptr_memcmp(const struct evbuffer *buf, const struct evbuffer_ptr *pos,
2680f90500a5SNick Mathewson const char *mem, size_t len)
2681f90500a5SNick Mathewson {
2682f90500a5SNick Mathewson struct evbuffer_chain *chain;
2683f90500a5SNick Mathewson size_t position;
2684f90500a5SNick Mathewson int r;
2685f90500a5SNick Mathewson
268660e0d59bSNick Mathewson ASSERT_EVBUFFER_LOCKED(buf);
268760e0d59bSNick Mathewson
2688841ecbd9SNick Mathewson if (pos->pos < 0 ||
2689841ecbd9SNick Mathewson EV_SIZE_MAX - len < (size_t)pos->pos ||
2690841ecbd9SNick Mathewson pos->pos + len > buf->total_len)
2691f90500a5SNick Mathewson return -1;
2692f90500a5SNick Mathewson
2693cb9da0bfSNick Mathewson chain = pos->internal_.chain;
2694cb9da0bfSNick Mathewson position = pos->internal_.pos_in_chain;
2695f90500a5SNick Mathewson while (len && chain) {
2696f90500a5SNick Mathewson size_t n_comparable;
2697f90500a5SNick Mathewson if (len + position > chain->off)
2698f90500a5SNick Mathewson n_comparable = chain->off - position;
2699f90500a5SNick Mathewson else
2700f90500a5SNick Mathewson n_comparable = len;
2701f90500a5SNick Mathewson r = memcmp(chain->buffer + chain->misalign + position, mem,
2702f90500a5SNick Mathewson n_comparable);
2703f90500a5SNick Mathewson if (r)
2704f90500a5SNick Mathewson return r;
2705f90500a5SNick Mathewson mem += n_comparable;
2706f90500a5SNick Mathewson len -= n_comparable;
2707f90500a5SNick Mathewson position = 0;
2708f90500a5SNick Mathewson chain = chain->next;
2709f90500a5SNick Mathewson }
2710f90500a5SNick Mathewson
2711f90500a5SNick Mathewson return 0;
2712f90500a5SNick Mathewson }
2713f90500a5SNick Mathewson
2714f90500a5SNick Mathewson struct evbuffer_ptr
evbuffer_search(struct evbuffer * buffer,const char * what,size_t len,const struct evbuffer_ptr * start)2715f90500a5SNick Mathewson evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start)
2716f90500a5SNick Mathewson {
27178a99083fSNick Mathewson return evbuffer_search_range(buffer, what, len, start, NULL);
27188a99083fSNick Mathewson }
27198a99083fSNick Mathewson
27208a99083fSNick Mathewson struct evbuffer_ptr
evbuffer_search_range(struct evbuffer * buffer,const char * what,size_t len,const struct evbuffer_ptr * start,const struct evbuffer_ptr * end)27218a99083fSNick Mathewson evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end)
27228a99083fSNick Mathewson {
2723f90500a5SNick Mathewson struct evbuffer_ptr pos;
27248a99083fSNick Mathewson struct evbuffer_chain *chain, *last_chain = NULL;
2725f90500a5SNick Mathewson const unsigned char *p;
2726f90500a5SNick Mathewson char first;
2727f90500a5SNick Mathewson
272876cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
272960e0d59bSNick Mathewson
2730f90500a5SNick Mathewson if (start) {
2731f90500a5SNick Mathewson memcpy(&pos, start, sizeof(pos));
2732cb9da0bfSNick Mathewson chain = pos.internal_.chain;
2733f90500a5SNick Mathewson } else {
2734f90500a5SNick Mathewson pos.pos = 0;
2735cb9da0bfSNick Mathewson chain = pos.internal_.chain = buffer->first;
2736cb9da0bfSNick Mathewson pos.internal_.pos_in_chain = 0;
2737f90500a5SNick Mathewson }
2738f90500a5SNick Mathewson
27398a99083fSNick Mathewson if (end)
2740cb9da0bfSNick Mathewson last_chain = end->internal_.chain;
27418a99083fSNick Mathewson
27429c8db0f8SNick Mathewson if (!len || len > EV_SSIZE_MAX)
274360e0d59bSNick Mathewson goto done;
2744f90500a5SNick Mathewson
2745f90500a5SNick Mathewson first = what[0];
2746f90500a5SNick Mathewson
2747f90500a5SNick Mathewson while (chain) {
2748f90500a5SNick Mathewson const unsigned char *start_at =
2749f90500a5SNick Mathewson chain->buffer + chain->misalign +
2750cb9da0bfSNick Mathewson pos.internal_.pos_in_chain;
2751f90500a5SNick Mathewson p = memchr(start_at, first,
2752cb9da0bfSNick Mathewson chain->off - pos.internal_.pos_in_chain);
2753f90500a5SNick Mathewson if (p) {
2754f90500a5SNick Mathewson pos.pos += p - start_at;
2755cb9da0bfSNick Mathewson pos.internal_.pos_in_chain += p - start_at;
27568a99083fSNick Mathewson if (!evbuffer_ptr_memcmp(buffer, &pos, what, len)) {
27579c8db0f8SNick Mathewson if (end && pos.pos + (ev_ssize_t)len > end->pos)
27588a99083fSNick Mathewson goto not_found;
27598a99083fSNick Mathewson else
276060e0d59bSNick Mathewson goto done;
27618a99083fSNick Mathewson }
2762f90500a5SNick Mathewson ++pos.pos;
2763cb9da0bfSNick Mathewson ++pos.internal_.pos_in_chain;
2764cb9da0bfSNick Mathewson if (pos.internal_.pos_in_chain == chain->off) {
2765cb9da0bfSNick Mathewson chain = pos.internal_.chain = chain->next;
2766cb9da0bfSNick Mathewson pos.internal_.pos_in_chain = 0;
2767f90500a5SNick Mathewson }
2768f90500a5SNick Mathewson } else {
27698a99083fSNick Mathewson if (chain == last_chain)
27708a99083fSNick Mathewson goto not_found;
2771cb9da0bfSNick Mathewson pos.pos += chain->off - pos.internal_.pos_in_chain;
2772cb9da0bfSNick Mathewson chain = pos.internal_.chain = chain->next;
2773cb9da0bfSNick Mathewson pos.internal_.pos_in_chain = 0;
2774f90500a5SNick Mathewson }
2775f90500a5SNick Mathewson }
2776f90500a5SNick Mathewson
27778a99083fSNick Mathewson not_found:
2778e3e97ae3SNir Soffer PTR_NOT_FOUND(&pos);
277960e0d59bSNick Mathewson done:
278076cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
2781f90500a5SNick Mathewson return pos;
2782f90500a5SNick Mathewson }
2783f90500a5SNick Mathewson
278423243b8aSNick Mathewson int
evbuffer_peek(struct evbuffer * buffer,ev_ssize_t len,struct evbuffer_ptr * start_at,struct evbuffer_iovec * vec,int n_vec)278523243b8aSNick Mathewson evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len,
278623243b8aSNick Mathewson struct evbuffer_ptr *start_at,
278723243b8aSNick Mathewson struct evbuffer_iovec *vec, int n_vec)
278823243b8aSNick Mathewson {
278923243b8aSNick Mathewson struct evbuffer_chain *chain;
279023243b8aSNick Mathewson int idx = 0;
2791ac633aebSNick Mathewson ev_ssize_t len_so_far = 0;
279223243b8aSNick Mathewson
27937aeb2fd4SNir Soffer /* Avoid locking in trivial edge cases */
2794cb9da0bfSNick Mathewson if (start_at && start_at->internal_.chain == NULL)
27957aeb2fd4SNir Soffer return 0;
27967aeb2fd4SNir Soffer
279776cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
279823243b8aSNick Mathewson
279923243b8aSNick Mathewson if (start_at) {
2800cb9da0bfSNick Mathewson chain = start_at->internal_.chain;
280123243b8aSNick Mathewson len_so_far = chain->off
2802cb9da0bfSNick Mathewson - start_at->internal_.pos_in_chain;
280323243b8aSNick Mathewson idx = 1;
280423243b8aSNick Mathewson if (n_vec > 0) {
28052c62062eSAzat Khuzhin vec[0].iov_base = (void *)(chain->buffer + chain->misalign
28062c62062eSAzat Khuzhin + start_at->internal_.pos_in_chain);
280723243b8aSNick Mathewson vec[0].iov_len = len_so_far;
280823243b8aSNick Mathewson }
280923243b8aSNick Mathewson chain = chain->next;
281023243b8aSNick Mathewson } else {
281123243b8aSNick Mathewson chain = buffer->first;
281223243b8aSNick Mathewson }
281323243b8aSNick Mathewson
2814c986f232SZack Weinberg if (n_vec == 0 && len < 0) {
2815c986f232SZack Weinberg /* If no vectors are provided and they asked for "everything",
2816c986f232SZack Weinberg * pretend they asked for the actual available amount. */
2817ba59923aSNick Mathewson len = buffer->total_len;
2818ba59923aSNick Mathewson if (start_at) {
2819ba59923aSNick Mathewson len -= start_at->pos;
2820ba59923aSNick Mathewson }
2821c986f232SZack Weinberg }
2822c986f232SZack Weinberg
282323243b8aSNick Mathewson while (chain) {
282423243b8aSNick Mathewson if (len >= 0 && len_so_far >= len)
282523243b8aSNick Mathewson break;
282623243b8aSNick Mathewson if (idx<n_vec) {
28272c62062eSAzat Khuzhin vec[idx].iov_base = (void *)(chain->buffer + chain->misalign);
282823243b8aSNick Mathewson vec[idx].iov_len = chain->off;
2829c986f232SZack Weinberg } else if (len<0) {
283023243b8aSNick Mathewson break;
2831c986f232SZack Weinberg }
283223243b8aSNick Mathewson ++idx;
283323243b8aSNick Mathewson len_so_far += chain->off;
283423243b8aSNick Mathewson chain = chain->next;
283523243b8aSNick Mathewson }
283623243b8aSNick Mathewson
283776cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
283823243b8aSNick Mathewson
283923243b8aSNick Mathewson return idx;
284023243b8aSNick Mathewson }
284123243b8aSNick Mathewson
2842f90500a5SNick Mathewson
2843f90500a5SNick Mathewson int
evbuffer_add_vprintf(struct evbuffer * buf,const char * fmt,va_list ap)28448d1317d7SNiels Provos evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
28458d1317d7SNiels Provos {
28468d1317d7SNiels Provos char *buffer;
28478d1317d7SNiels Provos size_t space;
284860e0d59bSNick Mathewson int sz, result = -1;
284979d2ca8cSNiels Provos va_list aq;
2850d5ebcf37SNick Mathewson struct evbuffer_chain *chain;
2851d5ebcf37SNick Mathewson
28528d1317d7SNiels Provos
285376cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
285460e0d59bSNick Mathewson
2855747331d1SNick Mathewson if (buf->freeze_end) {
2856747331d1SNick Mathewson goto done;
2857747331d1SNick Mathewson }
2858747331d1SNick Mathewson
28590c280824SNiels Provos /* make sure that at least some space is available */
2860d5ebcf37SNick Mathewson if ((chain = evbuffer_expand_singlechain(buf, 64)) == NULL)
286160e0d59bSNick Mathewson goto done;
28625c70ea4cSNiels Provos
28638d1317d7SNiels Provos for (;;) {
2864d5ebcf37SNick Mathewson #if 0
28655c70ea4cSNiels Provos size_t used = chain->misalign + chain->off;
28665c70ea4cSNiels Provos buffer = (char *)chain->buffer + chain->misalign + chain->off;
28672e36dbe1SNick Mathewson EVUTIL_ASSERT(chain->buffer_len >= used);
28685c70ea4cSNiels Provos space = chain->buffer_len - used;
2869d5ebcf37SNick Mathewson #endif
2870d5ebcf37SNick Mathewson buffer = (char*) CHAIN_SPACE_PTR(chain);
2871a3245afeSNick Mathewson space = (size_t) CHAIN_SPACE_LEN(chain);
28728d1317d7SNiels Provos
287322e53c7aSNiels Provos #ifndef va_copy
287422e53c7aSNiels Provos #define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
287522e53c7aSNiels Provos #endif
287679d2ca8cSNiels Provos va_copy(aq, ap);
287779d2ca8cSNiels Provos
2878c6da86ffSNick Mathewson sz = evutil_vsnprintf(buffer, space, fmt, aq);
287979d2ca8cSNiels Provos
288079d2ca8cSNiels Provos va_end(aq);
288179d2ca8cSNiels Provos
28820c280824SNiels Provos if (sz < 0)
288360e0d59bSNick Mathewson goto done;
2884841ecbd9SNick Mathewson if (INT_MAX >= EVBUFFER_CHAIN_MAX &&
2885841ecbd9SNick Mathewson (size_t)sz >= EVBUFFER_CHAIN_MAX)
2886841ecbd9SNick Mathewson goto done;
2887e865eb93SNick Mathewson if ((size_t)sz < space) {
28885c70ea4cSNiels Provos chain->off += sz;
28895c70ea4cSNiels Provos buf->total_len += sz;
2890f1b1bad4SNick Mathewson buf->n_add_for_cb += sz;
2891c735f2b4SNick Mathewson
2892d5ebcf37SNick Mathewson advance_last_with_data(buf);
28938ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
289460e0d59bSNick Mathewson result = sz;
289560e0d59bSNick Mathewson goto done;
28968d1317d7SNiels Provos }
2897d5ebcf37SNick Mathewson if ((chain = evbuffer_expand_singlechain(buf, sz + 1)) == NULL)
289860e0d59bSNick Mathewson goto done;
28998d1317d7SNiels Provos }
29008d1317d7SNiels Provos /* NOTREACHED */
290160e0d59bSNick Mathewson
290260e0d59bSNick Mathewson done:
290376cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
290460e0d59bSNick Mathewson return result;
29058d1317d7SNiels Provos }
29068d1317d7SNiels Provos
29078d1317d7SNiels Provos int
evbuffer_add_printf(struct evbuffer * buf,const char * fmt,...)29088d1317d7SNiels Provos evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
2909ec2c1db4SNiels Provos {
2910ec2c1db4SNiels Provos int res = -1;
2911ec2c1db4SNiels Provos va_list ap;
2912ec2c1db4SNiels Provos
2913ec2c1db4SNiels Provos va_start(ap, fmt);
29148d1317d7SNiels Provos res = evbuffer_add_vprintf(buf, fmt, ap);
2915ec2c1db4SNiels Provos va_end(ap);
2916ec2c1db4SNiels Provos
2917ec2c1db4SNiels Provos return (res);
2918ec2c1db4SNiels Provos }
2919ec2c1db4SNiels Provos
2920fdf69493SNiels Provos int
evbuffer_add_reference(struct evbuffer * outbuf,const void * data,size_t datlen,evbuffer_ref_cleanup_cb cleanupfn,void * extra)292166b2a7ffSNiels Provos evbuffer_add_reference(struct evbuffer *outbuf,
292266b2a7ffSNiels Provos const void *data, size_t datlen,
2923dc4c7b95SNick Mathewson evbuffer_ref_cleanup_cb cleanupfn, void *extra)
2924fdf69493SNiels Provos {
2925747331d1SNick Mathewson struct evbuffer_chain *chain;
2926fdf69493SNiels Provos struct evbuffer_chain_reference *info;
2927747331d1SNick Mathewson int result = -1;
2928fdf69493SNiels Provos
2929747331d1SNick Mathewson chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_reference));
2930747331d1SNick Mathewson if (!chain)
2931747331d1SNick Mathewson return (-1);
2932fdf69493SNiels Provos chain->flags |= EVBUFFER_REFERENCE | EVBUFFER_IMMUTABLE;
2933fd36647aSEd Schouten chain->buffer = (unsigned char *)data;
2934fdf69493SNiels Provos chain->buffer_len = datlen;
2935fdf69493SNiels Provos chain->off = datlen;
2936fdf69493SNiels Provos
2937fdf69493SNiels Provos info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_reference, chain);
2938fdf69493SNiels Provos info->cleanupfn = cleanupfn;
2939fdf69493SNiels Provos info->extra = extra;
2940fdf69493SNiels Provos
294176cd2b70SNick Mathewson EVBUFFER_LOCK(outbuf);
2942747331d1SNick Mathewson if (outbuf->freeze_end) {
2943747331d1SNick Mathewson /* don't call chain_free; we do not want to actually invoke
2944747331d1SNick Mathewson * the cleanup function */
2945747331d1SNick Mathewson mm_free(chain);
2946747331d1SNick Mathewson goto done;
2947747331d1SNick Mathewson }
2948fdf69493SNiels Provos evbuffer_chain_insert(outbuf, chain);
2949f1b1bad4SNick Mathewson outbuf->n_add_for_cb += datlen;
2950fdf69493SNiels Provos
29518ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(outbuf);
2952747331d1SNick Mathewson
2953747331d1SNick Mathewson result = 0;
2954747331d1SNick Mathewson done:
295576cd2b70SNick Mathewson EVBUFFER_UNLOCK(outbuf);
2956fdf69493SNiels Provos
2957747331d1SNick Mathewson return result;
2958fdf69493SNiels Provos }
2959fdf69493SNiels Provos
2960fdf69493SNiels Provos /* TODO(niels): we may want to add to automagically convert to mmap, in
2961fdf69493SNiels Provos * case evbuffer_remove() or evbuffer_pullup() are being used.
2962fdf69493SNiels Provos */
2963e72afae0SNick Mathewson struct evbuffer_file_segment *
evbuffer_file_segment_new(int fd,ev_off_t offset,ev_off_t length,unsigned flags)2964e72afae0SNick Mathewson evbuffer_file_segment_new(
2965e72afae0SNick Mathewson int fd, ev_off_t offset, ev_off_t length, unsigned flags)
2966fdf69493SNiels Provos {
2967e72afae0SNick Mathewson struct evbuffer_file_segment *seg =
2968e72afae0SNick Mathewson mm_calloc(sizeof(struct evbuffer_file_segment), 1);
2969e72afae0SNick Mathewson if (!seg)
2970e72afae0SNick Mathewson return NULL;
2971e72afae0SNick Mathewson seg->refcnt = 1;
2972e72afae0SNick Mathewson seg->fd = fd;
2973e72afae0SNick Mathewson seg->flags = flags;
2974c6bbbf1bSNick Mathewson seg->file_offset = offset;
2975e9f8febaSyangacer seg->cleanup_cb = NULL;
2976e9f8febaSyangacer seg->cleanup_cb_arg = NULL;
29779f560bfaSNick Mathewson #ifdef _WIN32
29786810908aSNick Mathewson #ifndef lseek
2979e72afae0SNick Mathewson #define lseek _lseeki64
29806810908aSNick Mathewson #endif
29816810908aSNick Mathewson #ifndef fstat
2982e72afae0SNick Mathewson #define fstat _fstat
29836810908aSNick Mathewson #endif
29846810908aSNick Mathewson #ifndef stat
2985e72afae0SNick Mathewson #define stat _stat
2986fdf69493SNiels Provos #endif
29876810908aSNick Mathewson #endif
2988e72afae0SNick Mathewson if (length == -1) {
2989e72afae0SNick Mathewson struct stat st;
2990e72afae0SNick Mathewson if (fstat(fd, &st) < 0)
2991e72afae0SNick Mathewson goto err;
2992e72afae0SNick Mathewson length = st.st_size;
2993e72afae0SNick Mathewson }
2994e72afae0SNick Mathewson seg->length = length;
2995fdf69493SNiels Provos
2996841ecbd9SNick Mathewson if (offset < 0 || length < 0 ||
2997841ecbd9SNick Mathewson ((ev_uint64_t)length > EVBUFFER_CHAIN_MAX) ||
2998841ecbd9SNick Mathewson (ev_uint64_t)offset > (ev_uint64_t)(EVBUFFER_CHAIN_MAX - length))
2999841ecbd9SNick Mathewson goto err;
3000841ecbd9SNick Mathewson
3001fdf69493SNiels Provos #if defined(USE_SENDFILE)
3002e72afae0SNick Mathewson if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) {
3003c6bbbf1bSNick Mathewson seg->can_sendfile = 1;
3004e72afae0SNick Mathewson goto done;
3005fdf69493SNiels Provos }
3006fdf69493SNiels Provos #endif
3007c6bbbf1bSNick Mathewson
3008c6bbbf1bSNick Mathewson if (evbuffer_file_segment_materialize(seg)<0)
3009c6bbbf1bSNick Mathewson goto err;
3010c6bbbf1bSNick Mathewson
3011da45aa74SNick Mathewson #if defined(USE_SENDFILE)
3012c6bbbf1bSNick Mathewson done:
3013da45aa74SNick Mathewson #endif
3014c6bbbf1bSNick Mathewson if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
3015c6bbbf1bSNick Mathewson EVTHREAD_ALLOC_LOCK(seg->lock, 0);
3016c6bbbf1bSNick Mathewson }
3017c6bbbf1bSNick Mathewson return seg;
3018c6bbbf1bSNick Mathewson err:
3019c6bbbf1bSNick Mathewson mm_free(seg);
3020c6bbbf1bSNick Mathewson return NULL;
3021c6bbbf1bSNick Mathewson }
3022c6bbbf1bSNick Mathewson
3023d4095146SNick Mathewson #ifdef EVENT__HAVE_MMAP
3024d4095146SNick Mathewson static long
get_page_size(void)3025d4095146SNick Mathewson get_page_size(void)
3026d4095146SNick Mathewson {
3027d4095146SNick Mathewson #ifdef SC_PAGE_SIZE
3028d4095146SNick Mathewson return sysconf(SC_PAGE_SIZE);
3029d4095146SNick Mathewson #elif defined(_SC_PAGE_SIZE)
3030d4095146SNick Mathewson return sysconf(_SC_PAGE_SIZE);
3031d4095146SNick Mathewson #else
3032d4095146SNick Mathewson return 1;
3033d4095146SNick Mathewson #endif
3034d4095146SNick Mathewson }
3035d4095146SNick Mathewson #endif
3036d4095146SNick Mathewson
3037c6bbbf1bSNick Mathewson /* DOCDOC */
3038c6bbbf1bSNick Mathewson /* Requires lock */
3039c6bbbf1bSNick Mathewson static int
evbuffer_file_segment_materialize(struct evbuffer_file_segment * seg)3040c6bbbf1bSNick Mathewson evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg)
3041c6bbbf1bSNick Mathewson {
3042c6bbbf1bSNick Mathewson const unsigned flags = seg->flags;
3043c6bbbf1bSNick Mathewson const int fd = seg->fd;
3044c6bbbf1bSNick Mathewson const ev_off_t length = seg->length;
3045c6bbbf1bSNick Mathewson const ev_off_t offset = seg->file_offset;
3046c6bbbf1bSNick Mathewson
3047c6bbbf1bSNick Mathewson if (seg->contents)
3048c6bbbf1bSNick Mathewson return 0; /* already materialized */
3049c6bbbf1bSNick Mathewson
305068120d9bSNick Mathewson #if defined(EVENT__HAVE_MMAP)
3051e72afae0SNick Mathewson if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
3052c2d9884aSNick Mathewson off_t offset_rounded = 0, offset_leftover = 0;
3053c2d9884aSNick Mathewson void *mapped;
3054c2d9884aSNick Mathewson if (offset) {
3055c2d9884aSNick Mathewson /* mmap implementations don't generally like us
3056c2d9884aSNick Mathewson * to have an offset that isn't a round */
3057d4095146SNick Mathewson long page_size = get_page_size();
3058c2d9884aSNick Mathewson if (page_size == -1)
3059c2d9884aSNick Mathewson goto err;
3060c2d9884aSNick Mathewson offset_leftover = offset % page_size;
3061c2d9884aSNick Mathewson offset_rounded = offset - offset_leftover;
3062c2d9884aSNick Mathewson }
3063c2d9884aSNick Mathewson mapped = mmap(NULL, length + offset_leftover,
3064c2d9884aSNick Mathewson PROT_READ,
3065e72afae0SNick Mathewson #ifdef MAP_NOCACHE
3066e72afae0SNick Mathewson MAP_NOCACHE | /* ??? */
3067e72afae0SNick Mathewson #endif
3068e72afae0SNick Mathewson #ifdef MAP_FILE
3069e72afae0SNick Mathewson MAP_FILE |
3070e72afae0SNick Mathewson #endif
3071e72afae0SNick Mathewson MAP_PRIVATE,
3072c2d9884aSNick Mathewson fd, offset_rounded);
3073fdf69493SNiels Provos if (mapped == MAP_FAILED) {
30741757cf71SNiels Provos event_warn("%s: mmap(%d, %d, %zu) failed",
307588f2b7a0SNick Mathewson __func__, fd, 0, (size_t)(offset + length));
3076747331d1SNick Mathewson } else {
3077e72afae0SNick Mathewson seg->mapping = mapped;
3078c2d9884aSNick Mathewson seg->contents = (char*)mapped+offset_leftover;
3079c6bbbf1bSNick Mathewson seg->mmap_offset = 0;
3080c6bbbf1bSNick Mathewson seg->is_mapping = 1;
3081e72afae0SNick Mathewson goto done;
3082747331d1SNick Mathewson }
3083e72afae0SNick Mathewson }
3084fdf69493SNiels Provos #endif
30859f560bfaSNick Mathewson #ifdef _WIN32
30863f405d2dSNick Mathewson if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
308756e48c10SNick Mathewson intptr_t h = _get_osfhandle(fd);
30883f405d2dSNick Mathewson HANDLE m;
30893f405d2dSNick Mathewson ev_uint64_t total_size = length+offset;
309056e48c10SNick Mathewson if ((HANDLE)h == INVALID_HANDLE_VALUE)
3091da45aa74SNick Mathewson goto err;
30923f405d2dSNick Mathewson m = CreateFileMapping((HANDLE)h, NULL, PAGE_READONLY,
30933f405d2dSNick Mathewson (total_size >> 32), total_size & 0xfffffffful,
30943f405d2dSNick Mathewson NULL);
30953f405d2dSNick Mathewson if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */
30963f405d2dSNick Mathewson seg->mapping_handle = m;
3097c6bbbf1bSNick Mathewson seg->mmap_offset = offset;
3098c6bbbf1bSNick Mathewson seg->is_mapping = 1;
30993f405d2dSNick Mathewson goto done;
31003f405d2dSNick Mathewson }
31013f405d2dSNick Mathewson }
31023f405d2dSNick Mathewson #endif
3103fdf69493SNiels Provos {
3104e72afae0SNick Mathewson ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos;
3105e72afae0SNick Mathewson ev_off_t read_so_far = 0;
3106e72afae0SNick Mathewson char *mem;
3107e72afae0SNick Mathewson int e;
3108e72afae0SNick Mathewson ev_ssize_t n = 0;
3109e72afae0SNick Mathewson if (!(mem = mm_malloc(length)))
3110e72afae0SNick Mathewson goto err;
3111e72afae0SNick Mathewson if (start_pos < 0) {
3112e72afae0SNick Mathewson mm_free(mem);
3113e72afae0SNick Mathewson goto err;
3114e72afae0SNick Mathewson }
3115e72afae0SNick Mathewson if (lseek(fd, offset, SEEK_SET) < 0) {
3116e72afae0SNick Mathewson mm_free(mem);
3117e72afae0SNick Mathewson goto err;
3118e72afae0SNick Mathewson }
3119e72afae0SNick Mathewson while (read_so_far < length) {
3120e72afae0SNick Mathewson n = read(fd, mem+read_so_far, length-read_so_far);
3121e72afae0SNick Mathewson if (n <= 0)
3122e72afae0SNick Mathewson break;
3123e72afae0SNick Mathewson read_so_far += n;
3124a0cae310SNiels Provos }
3125a0cae310SNiels Provos
3126e72afae0SNick Mathewson e = errno;
3127e72afae0SNick Mathewson pos = lseek(fd, start_pos, SEEK_SET);
3128e72afae0SNick Mathewson if (n < 0 || (n == 0 && length > read_so_far)) {
3129e72afae0SNick Mathewson mm_free(mem);
3130e72afae0SNick Mathewson errno = e;
3131e72afae0SNick Mathewson goto err;
3132e72afae0SNick Mathewson } else if (pos < 0) {
3133e72afae0SNick Mathewson mm_free(mem);
3134e72afae0SNick Mathewson goto err;
3135fdf69493SNiels Provos }
3136fdf69493SNiels Provos
3137e72afae0SNick Mathewson seg->contents = mem;
3138fdf69493SNiels Provos }
3139fdf69493SNiels Provos
3140e72afae0SNick Mathewson done:
3141c6bbbf1bSNick Mathewson return 0;
3142e72afae0SNick Mathewson err:
3143c6bbbf1bSNick Mathewson return -1;
3144e72afae0SNick Mathewson }
3145e72afae0SNick Mathewson
evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment * seg,evbuffer_file_segment_cleanup_cb cb,void * arg)3146e9f8febaSyangacer void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg,
3147e9f8febaSyangacer evbuffer_file_segment_cleanup_cb cb, void* arg)
3148e9f8febaSyangacer {
3149e9f8febaSyangacer EVUTIL_ASSERT(seg->refcnt > 0);
3150e9f8febaSyangacer seg->cleanup_cb = cb;
3151e9f8febaSyangacer seg->cleanup_cb_arg = arg;
3152e9f8febaSyangacer }
3153e9f8febaSyangacer
3154e72afae0SNick Mathewson void
evbuffer_file_segment_free(struct evbuffer_file_segment * seg)3155e72afae0SNick Mathewson evbuffer_file_segment_free(struct evbuffer_file_segment *seg)
3156e72afae0SNick Mathewson {
3157e72afae0SNick Mathewson int refcnt;
3158e72afae0SNick Mathewson EVLOCK_LOCK(seg->lock, 0);
3159e72afae0SNick Mathewson refcnt = --seg->refcnt;
3160e72afae0SNick Mathewson EVLOCK_UNLOCK(seg->lock, 0);
3161e72afae0SNick Mathewson if (refcnt > 0)
3162e72afae0SNick Mathewson return;
3163e72afae0SNick Mathewson EVUTIL_ASSERT(refcnt == 0);
3164e72afae0SNick Mathewson
3165c6bbbf1bSNick Mathewson if (seg->is_mapping) {
31669f560bfaSNick Mathewson #ifdef _WIN32
31673f405d2dSNick Mathewson CloseHandle(seg->mapping_handle);
316868120d9bSNick Mathewson #elif defined (EVENT__HAVE_MMAP)
3169d4095146SNick Mathewson off_t offset_leftover;
3170d4095146SNick Mathewson offset_leftover = seg->file_offset % get_page_size();
3171d4095146SNick Mathewson if (munmap(seg->mapping, seg->length + offset_leftover) == -1)
3172e72afae0SNick Mathewson event_warn("%s: munmap failed", __func__);
31733f405d2dSNick Mathewson #endif
3174c6bbbf1bSNick Mathewson } else if (seg->contents) {
3175e72afae0SNick Mathewson mm_free(seg->contents);
3176747331d1SNick Mathewson }
3177fdf69493SNiels Provos
3178e72afae0SNick Mathewson if ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) {
3179e72afae0SNick Mathewson close(seg->fd);
3180fdf69493SNiels Provos }
3181fdf69493SNiels Provos
3182e9f8febaSyangacer if (seg->cleanup_cb) {
3183e9f8febaSyangacer (*seg->cleanup_cb)((struct evbuffer_file_segment const*)seg,
3184e9f8febaSyangacer seg->flags, seg->cleanup_cb_arg);
3185e9f8febaSyangacer seg->cleanup_cb = NULL;
3186e9f8febaSyangacer seg->cleanup_cb_arg = NULL;
3187e9f8febaSyangacer }
3188e9f8febaSyangacer
3189e72afae0SNick Mathewson EVTHREAD_FREE_LOCK(seg->lock, 0);
3190e72afae0SNick Mathewson mm_free(seg);
3191e72afae0SNick Mathewson }
3192e72afae0SNick Mathewson
3193e72afae0SNick Mathewson int
evbuffer_add_file_segment(struct evbuffer * buf,struct evbuffer_file_segment * seg,ev_off_t offset,ev_off_t length)3194e72afae0SNick Mathewson evbuffer_add_file_segment(struct evbuffer *buf,
3195e72afae0SNick Mathewson struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length)
3196e72afae0SNick Mathewson {
3197e72afae0SNick Mathewson struct evbuffer_chain *chain;
3198e72afae0SNick Mathewson struct evbuffer_chain_file_segment *extra;
3199c6bbbf1bSNick Mathewson int can_use_sendfile = 0;
3200e72afae0SNick Mathewson
3201e72afae0SNick Mathewson EVBUFFER_LOCK(buf);
3202c6bbbf1bSNick Mathewson EVLOCK_LOCK(seg->lock, 0);
3203c6bbbf1bSNick Mathewson if (buf->flags & EVBUFFER_FLAG_DRAINS_TO_FD) {
3204c6bbbf1bSNick Mathewson can_use_sendfile = 1;
3205c6bbbf1bSNick Mathewson } else {
3206c6bbbf1bSNick Mathewson if (!seg->contents) {
3207c6bbbf1bSNick Mathewson if (evbuffer_file_segment_materialize(seg)<0) {
3208c6bbbf1bSNick Mathewson EVLOCK_UNLOCK(seg->lock, 0);
3209c6bbbf1bSNick Mathewson EVBUFFER_UNLOCK(buf);
3210c6bbbf1bSNick Mathewson return -1;
3211c6bbbf1bSNick Mathewson }
3212c6bbbf1bSNick Mathewson }
3213c6bbbf1bSNick Mathewson }
3214c6bbbf1bSNick Mathewson EVLOCK_UNLOCK(seg->lock, 0);
3215e72afae0SNick Mathewson
3216e72afae0SNick Mathewson if (buf->freeze_end)
3217e72afae0SNick Mathewson goto err;
3218e72afae0SNick Mathewson
3219e72afae0SNick Mathewson if (length < 0) {
3220e72afae0SNick Mathewson if (offset > seg->length)
3221e72afae0SNick Mathewson goto err;
3222e72afae0SNick Mathewson length = seg->length - offset;
3223e72afae0SNick Mathewson }
3224e72afae0SNick Mathewson
3225e72afae0SNick Mathewson /* Can we actually add this? */
3226e72afae0SNick Mathewson if (offset+length > seg->length)
3227e72afae0SNick Mathewson goto err;
3228e72afae0SNick Mathewson
3229e72afae0SNick Mathewson chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_file_segment));
3230e72afae0SNick Mathewson if (!chain)
3231e72afae0SNick Mathewson goto err;
3232e72afae0SNick Mathewson extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain);
3233e72afae0SNick Mathewson
3234e72afae0SNick Mathewson chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT;
3235c6bbbf1bSNick Mathewson if (can_use_sendfile && seg->can_sendfile) {
3236e72afae0SNick Mathewson chain->flags |= EVBUFFER_SENDFILE;
3237c6bbbf1bSNick Mathewson chain->misalign = seg->file_offset + offset;
3238e72afae0SNick Mathewson chain->off = length;
3239e72afae0SNick Mathewson chain->buffer_len = chain->misalign + length;
3240c6bbbf1bSNick Mathewson } else if (seg->is_mapping) {
32419f560bfaSNick Mathewson #ifdef _WIN32
3242c6bbbf1bSNick Mathewson ev_uint64_t total_offset = seg->mmap_offset+offset;
32433f405d2dSNick Mathewson ev_uint64_t offset_rounded=0, offset_remaining=0;
32443f405d2dSNick Mathewson LPVOID data;
32453f405d2dSNick Mathewson if (total_offset) {
32463f405d2dSNick Mathewson SYSTEM_INFO si;
32473f405d2dSNick Mathewson memset(&si, 0, sizeof(si)); /* cargo cult */
32483f405d2dSNick Mathewson GetSystemInfo(&si);
32493f405d2dSNick Mathewson offset_remaining = total_offset % si.dwAllocationGranularity;
32503f405d2dSNick Mathewson offset_rounded = total_offset - offset_remaining;
32513f405d2dSNick Mathewson }
32523f405d2dSNick Mathewson data = MapViewOfFile(
32533f405d2dSNick Mathewson seg->mapping_handle,
32543f405d2dSNick Mathewson FILE_MAP_READ,
32553f405d2dSNick Mathewson offset_rounded >> 32,
32563f405d2dSNick Mathewson offset_rounded & 0xfffffffful,
32578254de76SNick Mathewson length + offset_remaining);
32583f405d2dSNick Mathewson if (data == NULL) {
32593f405d2dSNick Mathewson mm_free(chain);
32603f405d2dSNick Mathewson goto err;
32613f405d2dSNick Mathewson }
32623f405d2dSNick Mathewson chain->buffer = (unsigned char*) data;
32633f405d2dSNick Mathewson chain->buffer_len = length+offset_remaining;
32643f405d2dSNick Mathewson chain->misalign = offset_remaining;
32653f405d2dSNick Mathewson chain->off = length;
32663f405d2dSNick Mathewson #else
3267e72afae0SNick Mathewson chain->buffer = (unsigned char*)(seg->contents + offset);
3268e72afae0SNick Mathewson chain->buffer_len = length;
3269e72afae0SNick Mathewson chain->off = length;
32703f405d2dSNick Mathewson #endif
3271e72afae0SNick Mathewson } else {
3272e72afae0SNick Mathewson chain->buffer = (unsigned char*)(seg->contents + offset);
3273e72afae0SNick Mathewson chain->buffer_len = length;
3274e72afae0SNick Mathewson chain->off = length;
3275e72afae0SNick Mathewson }
3276e72afae0SNick Mathewson
327730662a3cSyuangongji EVLOCK_LOCK(seg->lock, 0);
327830662a3cSyuangongji ++seg->refcnt;
327930662a3cSyuangongji EVLOCK_UNLOCK(seg->lock, 0);
3280e72afae0SNick Mathewson extra->segment = seg;
3281e72afae0SNick Mathewson buf->n_add_for_cb += length;
3282e72afae0SNick Mathewson evbuffer_chain_insert(buf, chain);
3283e72afae0SNick Mathewson
32848ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(buf);
3285e72afae0SNick Mathewson
3286e72afae0SNick Mathewson EVBUFFER_UNLOCK(buf);
3287e72afae0SNick Mathewson
3288e72afae0SNick Mathewson return 0;
3289e72afae0SNick Mathewson err:
3290e72afae0SNick Mathewson EVBUFFER_UNLOCK(buf);
329189c1a3b7SNick Mathewson evbuffer_file_segment_free(seg); /* Lowers the refcount */
3292e72afae0SNick Mathewson return -1;
3293e72afae0SNick Mathewson }
3294e72afae0SNick Mathewson
3295e72afae0SNick Mathewson int
evbuffer_add_file(struct evbuffer * buf,int fd,ev_off_t offset,ev_off_t length)3296e72afae0SNick Mathewson evbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length)
3297e72afae0SNick Mathewson {
3298e72afae0SNick Mathewson struct evbuffer_file_segment *seg;
3299e72afae0SNick Mathewson unsigned flags = EVBUF_FS_CLOSE_ON_FREE;
3300e72afae0SNick Mathewson int r;
3301e72afae0SNick Mathewson
3302e72afae0SNick Mathewson seg = evbuffer_file_segment_new(fd, offset, length, flags);
3303e72afae0SNick Mathewson if (!seg)
3304e72afae0SNick Mathewson return -1;
3305e72afae0SNick Mathewson r = evbuffer_add_file_segment(buf, seg, 0, length);
330630662a3cSyuangongji if (r == 0)
3307e72afae0SNick Mathewson evbuffer_file_segment_free(seg);
3308e72afae0SNick Mathewson return r;
3309e72afae0SNick Mathewson }
3310fdf69493SNiels Provos
3311598f247dSAzat Khuzhin int
evbuffer_setcb(struct evbuffer * buffer,evbuffer_cb cb,void * cbarg)3312c735f2b4SNick Mathewson evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
3313fbf01c7fSNiels Provos {
331476cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
331560e0d59bSNick Mathewson
3316d313c293SNick Mathewson if (!LIST_EMPTY(&buffer->callbacks))
3317c735f2b4SNick Mathewson evbuffer_remove_all_callbacks(buffer);
3318c735f2b4SNick Mathewson
3319f1b1bad4SNick Mathewson if (cb) {
3320f1b1bad4SNick Mathewson struct evbuffer_cb_entry *ent =
3321f1b1bad4SNick Mathewson evbuffer_add_cb(buffer, NULL, cbarg);
3322598f247dSAzat Khuzhin if (!ent) {
3323598f247dSAzat Khuzhin EVBUFFER_UNLOCK(buffer);
3324598f247dSAzat Khuzhin return -1;
3325598f247dSAzat Khuzhin }
3326f1b1bad4SNick Mathewson ent->cb.cb_obsolete = cb;
3327f1b1bad4SNick Mathewson ent->flags |= EVBUFFER_CB_OBSOLETE;
3328f1b1bad4SNick Mathewson }
332976cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
3330598f247dSAzat Khuzhin return 0;
3331c735f2b4SNick Mathewson }
3332c735f2b4SNick Mathewson
3333c735f2b4SNick Mathewson struct evbuffer_cb_entry *
evbuffer_add_cb(struct evbuffer * buffer,evbuffer_cb_func cb,void * cbarg)3334f1b1bad4SNick Mathewson evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
3335c735f2b4SNick Mathewson {
3336c735f2b4SNick Mathewson struct evbuffer_cb_entry *e;
3337e84c7656SNick Mathewson if (! (e = mm_calloc(1, sizeof(struct evbuffer_cb_entry))))
3338c735f2b4SNick Mathewson return NULL;
333976cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3340f1b1bad4SNick Mathewson e->cb.cb_func = cb;
3341c735f2b4SNick Mathewson e->cbarg = cbarg;
334281dd04a7SNick Mathewson e->flags = EVBUFFER_CB_ENABLED;
3343d313c293SNick Mathewson LIST_INSERT_HEAD(&buffer->callbacks, e, next);
334476cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
3345c735f2b4SNick Mathewson return e;
3346c735f2b4SNick Mathewson }
3347c735f2b4SNick Mathewson
3348c735f2b4SNick Mathewson int
evbuffer_remove_cb_entry(struct evbuffer * buffer,struct evbuffer_cb_entry * ent)3349c735f2b4SNick Mathewson evbuffer_remove_cb_entry(struct evbuffer *buffer,
3350c735f2b4SNick Mathewson struct evbuffer_cb_entry *ent)
3351c735f2b4SNick Mathewson {
335276cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3353d313c293SNick Mathewson LIST_REMOVE(ent, next);
335476cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
3355c735f2b4SNick Mathewson mm_free(ent);
3356c735f2b4SNick Mathewson return 0;
3357c735f2b4SNick Mathewson }
3358c735f2b4SNick Mathewson
3359c735f2b4SNick Mathewson int
evbuffer_remove_cb(struct evbuffer * buffer,evbuffer_cb_func cb,void * cbarg)3360f1b1bad4SNick Mathewson evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
3361c735f2b4SNick Mathewson {
3362c735f2b4SNick Mathewson struct evbuffer_cb_entry *cbent;
336360e0d59bSNick Mathewson int result = -1;
336476cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3365d313c293SNick Mathewson LIST_FOREACH(cbent, &buffer->callbacks, next) {
3366f1b1bad4SNick Mathewson if (cb == cbent->cb.cb_func && cbarg == cbent->cbarg) {
336760e0d59bSNick Mathewson result = evbuffer_remove_cb_entry(buffer, cbent);
336860e0d59bSNick Mathewson goto done;
3369c735f2b4SNick Mathewson }
3370c735f2b4SNick Mathewson }
337160e0d59bSNick Mathewson done:
337276cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
337360e0d59bSNick Mathewson return result;
3374fbf01c7fSNiels Provos }
337581dd04a7SNick Mathewson
337681dd04a7SNick Mathewson int
evbuffer_cb_set_flags(struct evbuffer * buffer,struct evbuffer_cb_entry * cb,ev_uint32_t flags)337781dd04a7SNick Mathewson evbuffer_cb_set_flags(struct evbuffer *buffer,
33788d3a10f8SNick Mathewson struct evbuffer_cb_entry *cb, ev_uint32_t flags)
337981dd04a7SNick Mathewson {
3380bba69e03SNick Mathewson /* the user isn't allowed to mess with these. */
3381bba69e03SNick Mathewson flags &= ~EVBUFFER_CB_INTERNAL_FLAGS;
338276cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3383bba69e03SNick Mathewson cb->flags |= flags;
338476cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
3385bba69e03SNick Mathewson return 0;
3386bba69e03SNick Mathewson }
3387bba69e03SNick Mathewson
3388bba69e03SNick Mathewson int
evbuffer_cb_clear_flags(struct evbuffer * buffer,struct evbuffer_cb_entry * cb,ev_uint32_t flags)3389bba69e03SNick Mathewson evbuffer_cb_clear_flags(struct evbuffer *buffer,
3390bba69e03SNick Mathewson struct evbuffer_cb_entry *cb, ev_uint32_t flags)
3391bba69e03SNick Mathewson {
3392bba69e03SNick Mathewson /* the user isn't allowed to mess with these. */
3393bba69e03SNick Mathewson flags &= ~EVBUFFER_CB_INTERNAL_FLAGS;
339476cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3395bba69e03SNick Mathewson cb->flags &= ~flags;
339676cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
339781dd04a7SNick Mathewson return 0;
339881dd04a7SNick Mathewson }
33998d3a10f8SNick Mathewson
3400747331d1SNick Mathewson int
evbuffer_freeze(struct evbuffer * buffer,int start)3401747331d1SNick Mathewson evbuffer_freeze(struct evbuffer *buffer, int start)
3402747331d1SNick Mathewson {
340376cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3404747331d1SNick Mathewson if (start)
3405747331d1SNick Mathewson buffer->freeze_start = 1;
3406747331d1SNick Mathewson else
3407747331d1SNick Mathewson buffer->freeze_end = 1;
340876cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
3409747331d1SNick Mathewson return 0;
3410747331d1SNick Mathewson }
3411747331d1SNick Mathewson
3412747331d1SNick Mathewson int
evbuffer_unfreeze(struct evbuffer * buffer,int start)3413747331d1SNick Mathewson evbuffer_unfreeze(struct evbuffer *buffer, int start)
3414747331d1SNick Mathewson {
341576cd2b70SNick Mathewson EVBUFFER_LOCK(buffer);
3416747331d1SNick Mathewson if (start)
3417747331d1SNick Mathewson buffer->freeze_start = 0;
3418747331d1SNick Mathewson else
3419747331d1SNick Mathewson buffer->freeze_end = 0;
342076cd2b70SNick Mathewson EVBUFFER_UNLOCK(buffer);
3421747331d1SNick Mathewson return 0;
3422747331d1SNick Mathewson }
3423747331d1SNick Mathewson
3424f1b1bad4SNick Mathewson #if 0
34258d3a10f8SNick Mathewson void
34268d3a10f8SNick Mathewson evbuffer_cb_suspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb)
34278d3a10f8SNick Mathewson {
34288d3a10f8SNick Mathewson if (!(cb->flags & EVBUFFER_CB_SUSPENDED)) {
3429a8f6d961SNick Mathewson cb->size_before_suspend = evbuffer_get_length(buffer);
34308d3a10f8SNick Mathewson cb->flags |= EVBUFFER_CB_SUSPENDED;
34318d3a10f8SNick Mathewson }
34328d3a10f8SNick Mathewson }
34338d3a10f8SNick Mathewson
34348d3a10f8SNick Mathewson void
34358d3a10f8SNick Mathewson evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb)
34368d3a10f8SNick Mathewson {
34378d3a10f8SNick Mathewson if ((cb->flags & EVBUFFER_CB_SUSPENDED)) {
34388d3a10f8SNick Mathewson unsigned call = (cb->flags & EVBUFFER_CB_CALL_ON_UNSUSPEND);
34398d3a10f8SNick Mathewson size_t sz = cb->size_before_suspend;
34408d3a10f8SNick Mathewson cb->flags &= ~(EVBUFFER_CB_SUSPENDED|
34418d3a10f8SNick Mathewson EVBUFFER_CB_CALL_ON_UNSUSPEND);
34428d3a10f8SNick Mathewson cb->size_before_suspend = 0;
34438d3a10f8SNick Mathewson if (call && (cb->flags & EVBUFFER_CB_ENABLED)) {
3444a8f6d961SNick Mathewson cb->cb(buffer, sz, evbuffer_get_length(buffer), cb->cbarg);
34458d3a10f8SNick Mathewson }
34468d3a10f8SNick Mathewson }
34478d3a10f8SNick Mathewson }
3448f1b1bad4SNick Mathewson #endif
344906a4443aSNick Mathewson
345002fbf687SNick Mathewson int
evbuffer_get_callbacks_(struct evbuffer * buffer,struct event_callback ** cbs,int max_cbs)345102fbf687SNick Mathewson evbuffer_get_callbacks_(struct evbuffer *buffer, struct event_callback **cbs,
345202fbf687SNick Mathewson int max_cbs)
345302fbf687SNick Mathewson {
345402fbf687SNick Mathewson int r = 0;
345502fbf687SNick Mathewson EVBUFFER_LOCK(buffer);
345602fbf687SNick Mathewson if (buffer->deferred_cbs) {
345702fbf687SNick Mathewson if (max_cbs < 1) {
345802fbf687SNick Mathewson r = -1;
345902fbf687SNick Mathewson goto done;
346002fbf687SNick Mathewson }
346102fbf687SNick Mathewson cbs[0] = &buffer->deferred;
346202fbf687SNick Mathewson r = 1;
346302fbf687SNick Mathewson }
346402fbf687SNick Mathewson done:
346502fbf687SNick Mathewson EVBUFFER_UNLOCK(buffer);
346602fbf687SNick Mathewson return r;
346702fbf687SNick Mathewson }
3468