xref: /openbmc/u-boot/lib/membuff.c (revision 3ba98ed8)
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 }
390