blob: f38ff36cb0bd5c5cc6c4c2820ec0070b1b2f38bc [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass1294ecf2015-11-08 23:47:47 -07002/*
3 * Copyright (c) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
5 *
6 * Copyright (c) 1992 Simon Glass
Simon Glass1294ecf2015-11-08 23:47:47 -07007 */
8
Simon Glass1294ecf2015-11-08 23:47:47 -07009#include <errno.h>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Simon Glass1294ecf2015-11-08 23:47:47 -070011#include <malloc.h>
Simon Glasse7476ee2025-03-18 16:20:43 +010012#include "membuf.h"
Simon Glass1294ecf2015-11-08 23:47:47 -070013
Simon Glassf7e87f92025-03-18 16:20:44 +010014void membuf_purge(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -070015{
16 /* set mb->head and mb->tail so the buffers look empty */
17 mb->head = mb->start;
18 mb->tail = mb->start;
19}
20
Simon Glassf7e87f92025-03-18 16:20:44 +010021static int membuf_putrawflex(struct membuf *mb, int maxlen, bool update,
Simon Glassceefc782025-03-18 16:20:42 +010022 char ***data, int *offsetp)
Simon Glass1294ecf2015-11-08 23:47:47 -070023{
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
Simon Glassf7e87f92025-03-18 16:20:44 +010075int membuf_putraw(struct membuf *mb, int maxlen, bool update, char **data)
Simon Glass1294ecf2015-11-08 23:47:47 -070076{
77 char **datap;
78 int offset;
79 int size;
80
Simon Glassceefc782025-03-18 16:20:42 +010081 size = membuf_putrawflex(mb, maxlen, update, &datap, &offset);
Simon Glass1294ecf2015-11-08 23:47:47 -070082 *data = *datap + offset;
83
84 return size;
85}
86
Simon Glassf7e87f92025-03-18 16:20:44 +010087bool membuf_putbyte(struct membuf *mb, int ch)
Simon Glass1294ecf2015-11-08 23:47:47 -070088{
89 char *data;
90
Simon Glassceefc782025-03-18 16:20:42 +010091 if (membuf_putraw(mb, 1, true, &data) != 1)
Simon Glass1294ecf2015-11-08 23:47:47 -070092 return false;
93 *data = ch;
94
95 return true;
96}
97
Simon Glassf7e87f92025-03-18 16:20:44 +010098int membuf_getraw(struct membuf *mb, int maxlen, bool update, char **data)
Simon Glass1294ecf2015-11-08 23:47:47 -070099{
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
Simon Glassf7e87f92025-03-18 16:20:44 +0100149int membuf_getbyte(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700150{
151 char *data = 0;
152
Simon Glassceefc782025-03-18 16:20:42 +0100153 return membuf_getraw(mb, 1, true, &data) != 1 ? -1 : *(uint8_t *)data;
Simon Glass1294ecf2015-11-08 23:47:47 -0700154}
155
Simon Glassf7e87f92025-03-18 16:20:44 +0100156int membuf_peekbyte(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700157{
158 char *data = 0;
159
Simon Glassceefc782025-03-18 16:20:42 +0100160 return membuf_getraw(mb, 1, false, &data) != 1 ? -1 : *(uint8_t *)data;
Simon Glass1294ecf2015-11-08 23:47:47 -0700161}
162
Simon Glassf7e87f92025-03-18 16:20:44 +0100163int membuf_get(struct membuf *mb, char *buff, int maxlen)
Simon Glass1294ecf2015-11-08 23:47:47 -0700164{
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 */
Simon Glassceefc782025-03-18 16:20:42 +0100174 len = membuf_getraw(mb, maxlen, true, &data);
Simon Glass1294ecf2015-11-08 23:47:47 -0700175
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
Simon Glassf7e87f92025-03-18 16:20:44 +0100186int membuf_put(struct membuf *mb, const char *buff, int length)
Simon Glass1294ecf2015-11-08 23:47:47 -0700187{
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 */
Simon Glassceefc782025-03-18 16:20:42 +0100193 towrite = membuf_putraw(mb, length, true, &data);
Simon Glass1294ecf2015-11-08 23:47:47 -0700194
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
Simon Glassf7e87f92025-03-18 16:20:44 +0100206bool membuf_isempty(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700207{
208 return mb->head == mb->tail;
209}
210
Simon Glassf7e87f92025-03-18 16:20:44 +0100211int membuf_avail(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700212{
Simon Glassf7e87f92025-03-18 16:20:44 +0100213 struct membuf copy;
Simon Glass1294ecf2015-11-08 23:47:47 -0700214 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++)
Simon Glassceefc782025-03-18 16:20:42 +0100222 avail += membuf_getraw(&copy, -1, true, &data);
Simon Glass1294ecf2015-11-08 23:47:47 -0700223
224 /* and return how much we read */
225 return avail;
226}
227
Simon Glassf7e87f92025-03-18 16:20:44 +0100228int membuf_size(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700229{
230 return mb->end - mb->start;
231}
232
Simon Glassf7e87f92025-03-18 16:20:44 +0100233bool membuf_makecontig(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700234{
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
Simon Glassf7e87f92025-03-18 16:20:44 +0100284int membuf_free(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700285{
286 return mb->end == mb->start ? 0 :
Simon Glassceefc782025-03-18 16:20:42 +0100287 (mb->end - mb->start) - 1 - membuf_avail(mb);
Simon Glass1294ecf2015-11-08 23:47:47 -0700288}
289
Simon Glassfb8895b2025-03-18 16:20:48 +0100290int membuf_readline(struct membuf *mb, char *str, int maxlen, int minch,
291 bool must_fit)
Simon Glass1294ecf2015-11-08 23:47:47 -0700292{
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 */
Ion Agorria7e06c1f2024-01-05 09:22:10 +0200313 if (!ok && must_fit) {
Simon Glass1294ecf2015-11-08 23:47:47 -0700314 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
Simon Glassf7e87f92025-03-18 16:20:44 +0100326int membuf_extend_by(struct membuf *mb, int by, int max)
Simon Glass1294ecf2015-11-08 23:47:47 -0700327{
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
Simon Glassf7e87f92025-03-18 16:20:44 +0100362void membuf_init(struct membuf *mb, char *buff, int size)
Simon Glass1294ecf2015-11-08 23:47:47 -0700363{
364 mb->start = buff;
365 mb->end = mb->start + size;
Simon Glassceefc782025-03-18 16:20:42 +0100366 membuf_purge(mb);
Simon Glass1294ecf2015-11-08 23:47:47 -0700367}
368
Simon Glassf7e87f92025-03-18 16:20:44 +0100369int membuf_new(struct membuf *mb, int size)
Simon Glass1294ecf2015-11-08 23:47:47 -0700370{
371 mb->start = malloc(size);
372 if (!mb->start)
373 return -ENOMEM;
374
Simon Glassceefc782025-03-18 16:20:42 +0100375 membuf_init(mb, mb->start, size);
Simon Glass1294ecf2015-11-08 23:47:47 -0700376 return 0;
377}
378
Simon Glassf7e87f92025-03-18 16:20:44 +0100379void membuf_uninit(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700380{
381 mb->end = NULL;
382 mb->start = NULL;
Simon Glassceefc782025-03-18 16:20:42 +0100383 membuf_purge(mb);
Simon Glass1294ecf2015-11-08 23:47:47 -0700384}
385
Simon Glassf7e87f92025-03-18 16:20:44 +0100386void membuf_dispose(struct membuf *mb)
Simon Glass1294ecf2015-11-08 23:47:47 -0700387{
Simon Glass42f5b8c2025-03-18 16:20:46 +0100388 free(mb->start);
Simon Glassceefc782025-03-18 16:20:42 +0100389 membuf_uninit(mb);
Simon Glass1294ecf2015-11-08 23:47:47 -0700390}