]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
b7b65090 SG |
2 | /* |
3 | * Copyright (c) 2015 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
5 | * | |
6 | * Copyright (c) 1992 Simon Glass | |
b7b65090 SG |
7 | */ |
8 | ||
b7b65090 | 9 | #include <errno.h> |
f7ae49fc | 10 | #include <log.h> |
b7b65090 SG |
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(©, -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 | ||
e58bafc3 | 290 | int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch, bool must_fit) |
b7b65090 SG |
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 */ | |
e58bafc3 | 312 | if (!ok && must_fit) { |
b7b65090 SG |
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 | } |