]>
Commit | Line | Data |
---|---|---|
b7b65090 SG |
1 | /* |
2 | * Copyright (c) 2015 Google, Inc | |
3 | * Written by Simon Glass <[email protected]> | |
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 | ||
15 | void 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 | ||
22 | static 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 | ||
76 | int 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 | ||
88 | bool 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 | ||
99 | int 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 | ||
150 | int 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 | ||
157 | int 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 | ||
164 | int 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 | ||
187 | int 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 | ||
207 | bool membuff_isempty(struct membuff *mb) | |
208 | { | |
209 | return mb->head == mb->tail; | |
210 | } | |
211 | ||
212 | int 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(©, -1, true, &data); | |
224 | ||
225 | /* and return how much we read */ | |
226 | return avail; | |
227 | } | |
228 | ||
229 | int membuff_size(struct membuff *mb) | |
230 | { | |
231 | return mb->end - mb->start; | |
232 | } | |
233 | ||
234 | bool 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 | ||
285 | int membuff_free(struct membuff *mb) | |
286 | { | |
287 | return mb->end == mb->start ? 0 : | |
288 | (mb->end - mb->start) - 1 - membuff_avail(mb); | |
289 | } | |
290 | ||
291 | int 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 | ||
326 | int 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 | ||
362 | void 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 | ||
369 | int 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 | ||
379 | void membuff_uninit(struct membuff *mb) | |
380 | { | |
381 | mb->end = NULL; | |
382 | mb->start = NULL; | |
383 | membuff_purge(mb); | |
384 | } | |
385 | ||
386 | void membuff_dispose(struct membuff *mb) | |
387 | { | |
388 | free(&mb->start); | |
389 | membuff_uninit(mb); | |
390 | } |