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(©, -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