xref: /freebsd/sbin/hastd/ebuf.c (revision 0abd71f39a5c296bf5bf41a0d98cfe849967d8f9)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  *
6  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 
33 #include <errno.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include <pjdlog.h>
40 
41 #include "ebuf.h"
42 
43 #ifndef	PJDLOG_ASSERT
44 #include <assert.h>
45 #define	PJDLOG_ASSERT(...)	assert(__VA_ARGS__)
46 #endif
47 
48 #define	EBUF_MAGIC	0xeb0f41c
49 struct ebuf {
50 	/* Magic to assert the caller uses valid structure. */
51 	int		 eb_magic;
52 	/* Address where we did the allocation. */
53 	unsigned char	*eb_buf;
54 	/* Allocation end address. */
55 	unsigned char	*eb_end;
56 	/* Start of real data. */
57 	unsigned char	*eb_start;
58 	/* Size of real data. */
59 	size_t		 eb_size;
60 };
61 
62 static int ebuf_head_extend(struct ebuf *eb, size_t size);
63 static int ebuf_tail_extend(struct ebuf *eb, size_t size);
64 
65 /*
66  * Allocate an empty ebuf with the expectation that it will later need to
67  * hold at least `size` bytes.
68  */
69 struct ebuf *
ebuf_alloc(size_t size)70 ebuf_alloc(size_t size)
71 {
72 	struct ebuf *eb;
73 	size_t page_size;
74 	int rerrno;
75 
76 	eb = malloc(sizeof(*eb));
77 	if (eb == NULL)
78 		return (NULL);
79 	page_size = getpagesize();
80 	size += page_size;
81 	eb->eb_buf = malloc(size);
82 	if (eb->eb_buf == NULL) {
83 		rerrno = errno;
84 		free(eb);
85 		errno = rerrno;
86 		return (NULL);
87 	}
88 	eb->eb_end = eb->eb_buf + size;
89 	/*
90 	 * We set start address for real data not at the first entry, because
91 	 * we want to be able to add data at the front.
92 	 */
93 	eb->eb_start = eb->eb_buf + page_size / 4;
94 	eb->eb_size = 0;
95 	eb->eb_magic = EBUF_MAGIC;
96 
97 	return (eb);
98 }
99 
100 /*
101  * Free `eb`.
102  */
103 void
ebuf_free(struct ebuf * eb)104 ebuf_free(struct ebuf *eb)
105 {
106 
107 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
108 
109 	eb->eb_magic = 0;
110 
111 	free(eb->eb_buf);
112 	free(eb);
113 }
114 
115 /*
116  * Add `size` bytes to the front of `eb`, copied from `data` if not null
117  * and otherwise left uninitialized.
118  */
119 int
ebuf_add_head(struct ebuf * eb,const void * data,size_t size)120 ebuf_add_head(struct ebuf *eb, const void *data, size_t size)
121 {
122 
123 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
124 
125 	if (size > (size_t)(eb->eb_start - eb->eb_buf)) {
126 		/*
127 		 * We can't add more entries at the front, so we have to extend
128 		 * our buffer.
129 		 */
130 		if (ebuf_head_extend(eb, size) == -1)
131 			return (-1);
132 	}
133 	PJDLOG_ASSERT(size <= (size_t)(eb->eb_start - eb->eb_buf));
134 
135 	eb->eb_size += size;
136 	eb->eb_start -= size;
137 	/*
138 	 * If data is NULL the caller just wants to reserve place.
139 	 */
140 	if (data != NULL)
141 		memcpy(eb->eb_start, data, size);
142 
143 	return (0);
144 }
145 
146 /*
147  * Add `size` bytes to the back of `eb`, copied from `data` if not null
148  * and otherwise left uninitialized.
149  */
150 int
ebuf_add_tail(struct ebuf * eb,const void * data,size_t size)151 ebuf_add_tail(struct ebuf *eb, const void *data, size_t size)
152 {
153 
154 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
155 
156 	if (size > (size_t)(eb->eb_end - (eb->eb_start + eb->eb_size))) {
157 		/*
158 		 * We can't add more entries at the back, so we have to extend
159 		 * our buffer.
160 		 */
161 		if (ebuf_tail_extend(eb, size) == -1)
162 			return (-1);
163 	}
164 	PJDLOG_ASSERT(size <=
165 	    (size_t)(eb->eb_end - (eb->eb_start + eb->eb_size)));
166 
167 	/*
168 	 * If data is NULL the caller just wants to reserve space.
169 	 */
170 	if (data != NULL)
171 		memcpy(eb->eb_start + eb->eb_size, data, size);
172 	eb->eb_size += size;
173 
174 	return (0);
175 }
176 
177 /*
178  * Trim `size` bytes from the front of `eb`.
179  */
180 void
ebuf_del_head(struct ebuf * eb,size_t size)181 ebuf_del_head(struct ebuf *eb, size_t size)
182 {
183 
184 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
185 	PJDLOG_ASSERT(size <= eb->eb_size);
186 
187 	eb->eb_start += size;
188 	eb->eb_size -= size;
189 }
190 
191 /*
192  * Trim size bytes from the back of `eb`.
193  */
194 void
ebuf_del_tail(struct ebuf * eb,size_t size)195 ebuf_del_tail(struct ebuf *eb, size_t size)
196 {
197 
198 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
199 	PJDLOG_ASSERT(size <= eb->eb_size);
200 
201 	eb->eb_size -= size;
202 }
203 
204 /*
205  * Return a pointer to the data contained by `eb`.  The size of the data
206  * is returned in `sizep` if not null.
207  */
208 void *
ebuf_data(struct ebuf * eb,size_t * sizep)209 ebuf_data(struct ebuf *eb, size_t *sizep)
210 {
211 
212 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
213 
214 	if (sizep != NULL)
215 		*sizep = eb->eb_size;
216 	return (eb->eb_size > 0 ? eb->eb_start : NULL);
217 }
218 
219 /*
220  * Return the size of the data contained in `eb`.
221  */
222 size_t
ebuf_size(struct ebuf * eb)223 ebuf_size(struct ebuf *eb)
224 {
225 
226 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
227 
228 	return (eb->eb_size);
229 }
230 
231 /*
232  * Function adds size + (PAGE_SIZE / 4) bytes at the front of the buffer..
233  */
234 static int
ebuf_head_extend(struct ebuf * eb,size_t size)235 ebuf_head_extend(struct ebuf *eb, size_t size)
236 {
237 	unsigned char *newbuf, *newstart;
238 	size_t newsize, page_size;
239 
240 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
241 
242 	page_size = getpagesize();
243 	newsize = eb->eb_end - eb->eb_buf + (page_size / 4) + size;
244 
245 	newbuf = malloc(newsize);
246 	if (newbuf == NULL)
247 		return (-1);
248 	newstart =
249 	    newbuf + (page_size / 4) + size + (eb->eb_start - eb->eb_buf);
250 
251 	memcpy(newstart, eb->eb_start, eb->eb_size);
252 
253 	eb->eb_buf = newbuf;
254 	eb->eb_start = newstart;
255 	eb->eb_end = newbuf + newsize;
256 
257 	return (0);
258 }
259 
260 /*
261  * Function adds size + ((3 * PAGE_SIZE) / 4) bytes at the back.
262  */
263 static int
ebuf_tail_extend(struct ebuf * eb,size_t size)264 ebuf_tail_extend(struct ebuf *eb, size_t size)
265 {
266 	unsigned char *newbuf;
267 	size_t newsize, page_size;
268 
269 	PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC);
270 
271 	page_size = getpagesize();
272 	newsize = eb->eb_end - eb->eb_buf + size + ((3 * page_size) / 4);
273 
274 	newbuf = realloc(eb->eb_buf, newsize);
275 	if (newbuf == NULL)
276 		return (-1);
277 
278 	eb->eb_start = newbuf + (eb->eb_start - eb->eb_buf);
279 	eb->eb_buf = newbuf;
280 	eb->eb_end = newbuf + newsize;
281 
282 	return (0);
283 }
284