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