1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2015 Google, Inc 4 * Written by Simon Glass <sjg@chromium.org> 5 * 6 * Copyright (c) 1992 Simon Glass 7 */ 8 9 #include <common.h> 10 #include <errno.h> 11 #include <malloc.h> 12 #include "membuff.h" 13 14 void membuff_purge(struct membuff *mb) 15 { 16 /* set mb->head and mb->tail so the buffers look empty */ 17 mb->head = mb->start; 18 mb->tail = mb->start; 19 } 20 21 static int membuff_putrawflex(struct membuff *mb, int maxlen, bool update, 22 char ***data, int *offsetp) 23 { 24 int len; 25 26 /* always write to 'mb->head' */ 27 assert(data && offsetp); 28 *data = &mb->start; 29 *offsetp = mb->head - mb->start; 30 31 /* if there is no buffer, we can do nothing */ 32 if (!mb->start) 33 return 0; 34 35 /* 36 * if head is ahead of tail, we can write from head until the end of 37 * the buffer 38 */ 39 if (mb->head >= mb->tail) { 40 /* work out how many bytes can fit here */ 41 len = mb->end - mb->head - 1; 42 if (maxlen >= 0 && len > maxlen) 43 len = maxlen; 44 45 /* update the head pointer to mark these bytes as written */ 46 if (update) 47 mb->head += len; 48 49 /* 50 * if the tail isn't at start of the buffer, then we can 51 * write one more byte right at the end 52 */ 53 if ((maxlen < 0 || len < maxlen) && mb->tail != mb->start) { 54 len++; 55 if (update) 56 mb->head = mb->start; 57 } 58 59 /* otherwise now we can write until head almost reaches tail */ 60 } else { 61 /* work out how many bytes can fit here */ 62 len = mb->tail - mb->head - 1; 63 if (maxlen >= 0 && len > maxlen) 64 len = maxlen; 65 66 /* update the head pointer to mark these bytes as written */ 67 if (update) 68 mb->head += len; 69 } 70 71 /* return the number of bytes which can be/must be written */ 72 return len; 73 } 74 75 int membuff_putraw(struct membuff *mb, int maxlen, bool update, char **data) 76 { 77 char **datap; 78 int offset; 79 int size; 80 81 size = membuff_putrawflex(mb, maxlen, update, &datap, &offset); 82 *data = *datap + offset; 83 84 return size; 85 } 86 87 bool membuff_putbyte(struct membuff *mb, int ch) 88 { 89 char *data; 90 91 if (membuff_putraw(mb, 1, true, &data) != 1) 92 return false; 93 *data = ch; 94 95 return true; 96 } 97 98 int membuff_getraw(struct membuff *mb, int maxlen, bool update, char **data) 99 { 100 int len; 101 102 /* assume for now there is no data to get */ 103 len = 0; 104 105 /* 106 * in this case head is ahead of tail, so we must return data between 107 *'tail' and 'head' 108 */ 109 if (mb->head > mb->tail) { 110 /* work out the amount of data */ 111 *data = mb->tail; 112 len = mb->head - mb->tail; 113 114 /* check it isn't too much */ 115 if (maxlen >= 0 && len > maxlen) 116 len = maxlen; 117 118 /* & mark it as read from the buffer */ 119 if (update) 120 mb->tail += len; 121 } 122 123 /* 124 * if head is before tail, then we have data between 'tail' and 'end' 125 * and some more data between 'start' and 'head'(which we can't 126 * return this time 127 */ 128 else if (mb->head < mb->tail) { 129 /* work out the amount of data */ 130 *data = mb->tail; 131 len = mb->end - mb->tail; 132 if (maxlen >= 0 && len > maxlen) 133 len = maxlen; 134 if (update) { 135 mb->tail += len; 136 if (mb->tail == mb->end) 137 mb->tail = mb->start; 138 } 139 } 140 141 debug("getraw: maxlen=%d, update=%d, head=%d, tail=%d, data=%d, len=%d", 142 maxlen, update, (int)(mb->head - mb->start), 143 (int)(mb->tail - mb->start), (int)(*data - mb->start), len); 144 145 /* return the number of bytes we found */ 146 return len; 147 } 148 149 int membuff_getbyte(struct membuff *mb) 150 { 151 char *data = 0; 152 153 return membuff_getraw(mb, 1, true, &data) != 1 ? -1 : *(uint8_t *)data; 154 } 155 156 int membuff_peekbyte(struct membuff *mb) 157 { 158 char *data = 0; 159 160 return membuff_getraw(mb, 1, false, &data) != 1 ? -1 : *(uint8_t *)data; 161 } 162 163 int membuff_get(struct membuff *mb, char *buff, int maxlen) 164 { 165 char *data = 0, *buffptr = buff; 166 int len = 1, i; 167 168 /* 169 * do this in up to two lots(see GetRaw for why) stopping when there 170 * is no more data 171 */ 172 for (i = 0; len && i < 2; i++) { 173 /* get a pointer to the data available */ 174 len = membuff_getraw(mb, maxlen, true, &data); 175 176 /* copy it into the buffer */ 177 memcpy(buffptr, data, len); 178 buffptr += len; 179 maxlen -= len; 180 } 181 182 /* return the number of bytes read */ 183 return buffptr - buff; 184 } 185 186 int membuff_put(struct membuff *mb, const char *buff, int length) 187 { 188 char *data; 189 int towrite, i, written; 190 191 for (i = written = 0; i < 2; i++) { 192 /* ask where some data can be written */ 193 towrite = membuff_putraw(mb, length, true, &data); 194 195 /* and write it, updating the bytes length */ 196 memcpy(data, buff, towrite); 197 written += towrite; 198 buff += towrite; 199 length -= towrite; 200 } 201 202 /* return the number of bytes written */ 203 return written; 204 } 205 206 bool membuff_isempty(struct membuff *mb) 207 { 208 return mb->head == mb->tail; 209 } 210 211 int membuff_avail(struct membuff *mb) 212 { 213 struct membuff copy; 214 int i, avail; 215 char *data = 0; 216 217 /* make a copy of this buffer's control data */ 218 copy = *mb; 219 220 /* now read everything out of the copied buffer */ 221 for (i = avail = 0; i < 2; i++) 222 avail += membuff_getraw(©, -1, true, &data); 223 224 /* and return how much we read */ 225 return avail; 226 } 227 228 int membuff_size(struct membuff *mb) 229 { 230 return mb->end - mb->start; 231 } 232 233 bool membuff_makecontig(struct membuff *mb) 234 { 235 int topsize, botsize; 236 237 debug("makecontig: head=%d, tail=%d, size=%d", 238 (int)(mb->head - mb->start), (int)(mb->tail - mb->start), 239 (int)(mb->end - mb->start)); 240 241 /* 242 * first we move anything at the start of the buffer into the correct 243 * place some way along 244 */ 245 if (mb->tail > mb->head) { 246 /* 247 * the data is split into two parts, from 0 to ->head and 248 * from ->tail to ->end. We move the stuff from 0 to ->head 249 * up to make space for the other data before it 250 */ 251 topsize = mb->end - mb->tail; 252 botsize = mb->head - mb->start; 253 254 /* 255 * must move data at bottom up by 'topsize' bytes - check if 256 * there's room 257 */ 258 if (mb->head + topsize >= mb->tail) 259 return false; 260 memmove(mb->start + topsize, mb->start, botsize); 261 debug(" - memmove(%d, %d, %d)", topsize, 0, botsize); 262 263 /* nothing at the start, so skip that step */ 264 } else { 265 topsize = mb->head - mb->tail; 266 botsize = 0; 267 } 268 269 /* now move data at top down to the bottom */ 270 memcpy(mb->start, mb->tail, topsize); 271 debug(" - memcpy(%d, %d, %d)", 0, (int)(mb->tail - mb->start), topsize); 272 273 /* adjust pointers */ 274 mb->tail = mb->start; 275 mb->head = mb->start + topsize + botsize; 276 277 debug(" - head=%d, tail=%d", (int)(mb->head - mb->start), 278 (int)(mb->tail - mb->start)); 279 280 /* all ok */ 281 return true; 282 } 283 284 int membuff_free(struct membuff *mb) 285 { 286 return mb->end == mb->start ? 0 : 287 (mb->end - mb->start) - 1 - membuff_avail(mb); 288 } 289 290 int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch) 291 { 292 int len; /* number of bytes read (!= string length) */ 293 char *s, *end; 294 bool ok = false; 295 char *orig = str; 296 297 end = mb->head >= mb->tail ? mb->head : mb->end; 298 for (len = 0, s = mb->tail; s < end && len < maxlen - 1; str++) { 299 *str = *s++; 300 len++; 301 if (*str == '\n' || *str < minch) { 302 ok = true; 303 break; 304 } 305 if (s == end && mb->tail > mb->head) { 306 s = mb->start; 307 end = mb->head; 308 } 309 } 310 311 /* couldn't get the whole string */ 312 if (!ok) { 313 if (maxlen) 314 *orig = '\0'; 315 return 0; 316 } 317 318 /* terminate the string, update the membuff and return success */ 319 *str = '\0'; 320 mb->tail = s == mb->end ? mb->start : s; 321 322 return len; 323 } 324 325 int membuff_extend_by(struct membuff *mb, int by, int max) 326 { 327 int oldhead, oldtail; 328 int size, orig; 329 char *ptr; 330 331 /* double the buffer size until it is big enough */ 332 assert(by >= 0); 333 for (orig = mb->end - mb->start, size = orig; size < orig + by;) 334 size *= 2; 335 if (max != -1) 336 size = min(size, max); 337 by = size - orig; 338 339 /* if we're already at maximum, give up */ 340 if (by <= 0) 341 return -E2BIG; 342 343 oldhead = mb->head - mb->start; 344 oldtail = mb->tail - mb->start; 345 ptr = realloc(mb->start, size); 346 if (!ptr) 347 return -ENOMEM; 348 mb->start = ptr; 349 mb->head = mb->start + oldhead; 350 mb->tail = mb->start + oldtail; 351 352 if (mb->head < mb->tail) { 353 memmove(mb->tail + by, mb->tail, orig - oldtail); 354 mb->tail += by; 355 } 356 mb->end = mb->start + size; 357 358 return 0; 359 } 360 361 void membuff_init(struct membuff *mb, char *buff, int size) 362 { 363 mb->start = buff; 364 mb->end = mb->start + size; 365 membuff_purge(mb); 366 } 367 368 int membuff_new(struct membuff *mb, int size) 369 { 370 mb->start = malloc(size); 371 if (!mb->start) 372 return -ENOMEM; 373 374 membuff_init(mb, mb->start, size); 375 return 0; 376 } 377 378 void membuff_uninit(struct membuff *mb) 379 { 380 mb->end = NULL; 381 mb->start = NULL; 382 membuff_purge(mb); 383 } 384 385 void membuff_dispose(struct membuff *mb) 386 { 387 free(&mb->start); 388 membuff_uninit(mb); 389 } 390