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