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