Home | History | Annotate | Download | only in lib
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (c) 2015 Google, Inc
      4  * Written by Simon Glass <sjg (at) 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(&copy, -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