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