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