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 * 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 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 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 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 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 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 * 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 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 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 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