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 <log.h>
12 #include <malloc.h>
13 #include "membuff.h"
14 
membuff_purge(struct membuff * mb)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 
membuff_putrawflex(struct membuff * mb,int maxlen,bool update,char *** data,int * offsetp)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 
membuff_putraw(struct membuff * mb,int maxlen,bool update,char ** data)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 
membuff_putbyte(struct membuff * mb,int ch)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 
membuff_getraw(struct membuff * mb,int maxlen,bool update,char ** data)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 
membuff_getbyte(struct membuff * mb)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 
membuff_peekbyte(struct membuff * mb)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 
membuff_get(struct membuff * mb,char * buff,int maxlen)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 
membuff_put(struct membuff * mb,const char * buff,int length)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 
membuff_isempty(struct membuff * mb)207 bool membuff_isempty(struct membuff *mb)
208 {
209 	return mb->head == mb->tail;
210 }
211 
membuff_avail(struct membuff * mb)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(&copy, -1, true, &data);
224 
225 	/* and return how much we read */
226 	return avail;
227 }
228 
membuff_size(struct membuff * mb)229 int membuff_size(struct membuff *mb)
230 {
231 	return mb->end - mb->start;
232 }
233 
membuff_makecontig(struct membuff * mb)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 
membuff_free(struct membuff * mb)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 
membuff_readline(struct membuff * mb,char * str,int maxlen,int minch)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 
membuff_extend_by(struct membuff * mb,int by,int max)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 
membuff_init(struct membuff * mb,char * buff,int size)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 
membuff_new(struct membuff * mb,int size)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 
membuff_uninit(struct membuff * mb)379 void membuff_uninit(struct membuff *mb)
380 {
381 	mb->end = NULL;
382 	mb->start = NULL;
383 	membuff_purge(mb);
384 }
385 
membuff_dispose(struct membuff * mb)386 void membuff_dispose(struct membuff *mb)
387 {
388 	free(&mb->start);
389 	membuff_uninit(mb);
390 }
391