xref: /libevent-2.1.12/buffer.c (revision 5b063049)
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