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