efi_loader: release file buffer after loading image
[platform/kernel/u-boot.git] / lib / membuff.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2015 Google, Inc
4  * Written by Simon Glass <sjg@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 }