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 <errno.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include "membuf.h"
13 
membuf_purge(struct membuf * mb)14 void membuf_purge(struct membuf *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 
membuf_putrawflex(struct membuf * mb,int maxlen,bool update,char *** data,int * offsetp)21 static int membuf_putrawflex(struct membuf *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 
membuf_putraw(struct membuf * mb,int maxlen,bool update,char ** data)75 int membuf_putraw(struct membuf *mb, int maxlen, bool update, char **data)
76 {
77 	char **datap;
78 	int offset;
79 	int size;
80 
81 	size = membuf_putrawflex(mb, maxlen, update, &datap, &offset);
82 	*data = *datap + offset;
83 
84 	return size;
85 }
86 
membuf_putbyte(struct membuf * mb,int ch)87 bool membuf_putbyte(struct membuf *mb, int ch)
88 {
89 	char *data;
90 
91 	if (membuf_putraw(mb, 1, true, &data) != 1)
92 		return false;
93 	*data = ch;
94 
95 	return true;
96 }
97 
membuf_getraw(struct membuf * mb,int maxlen,bool update,char ** data)98 int membuf_getraw(struct membuf *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 
membuf_getbyte(struct membuf * mb)149 int membuf_getbyte(struct membuf *mb)
150 {
151 	char *data = 0;
152 
153 	return membuf_getraw(mb, 1, true, &data) != 1 ? -1 : *(uint8_t *)data;
154 }
155 
membuf_peekbyte(struct membuf * mb)156 int membuf_peekbyte(struct membuf *mb)
157 {
158 	char *data = 0;
159 
160 	return membuf_getraw(mb, 1, false, &data) != 1 ? -1 : *(uint8_t *)data;
161 }
162 
membuf_get(struct membuf * mb,char * buff,int maxlen)163 int membuf_get(struct membuf *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 = membuf_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 
membuf_put(struct membuf * mb,const char * buff,int length)186 int membuf_put(struct membuf *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 = membuf_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 
membuf_isempty(struct membuf * mb)206 bool membuf_isempty(struct membuf *mb)
207 {
208 	return mb->head == mb->tail;
209 }
210 
membuf_avail(struct membuf * mb)211 int membuf_avail(struct membuf *mb)
212 {
213 	struct membuf 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 += membuf_getraw(&copy, -1, true, &data);
223 
224 	/* and return how much we read */
225 	return avail;
226 }
227 
membuf_size(struct membuf * mb)228 int membuf_size(struct membuf *mb)
229 {
230 	return mb->end - mb->start;
231 }
232 
membuf_makecontig(struct membuf * mb)233 bool membuf_makecontig(struct membuf *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 
membuf_free(struct membuf * mb)284 int membuf_free(struct membuf *mb)
285 {
286 	return mb->end == mb->start ? 0 :
287 			(mb->end - mb->start) - 1 - membuf_avail(mb);
288 }
289 
membuf_readline(struct membuf * mb,char * str,int maxlen,int minch,bool must_fit)290 int membuf_readline(struct membuf *mb, char *str, int maxlen, int minch,
291 		    bool must_fit)
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 && must_fit) {
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 
membuf_extend_by(struct membuf * mb,int by,int max)326 int membuf_extend_by(struct membuf *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 
membuf_init(struct membuf * mb,char * buff,int size)362 void membuf_init(struct membuf *mb, char *buff, int size)
363 {
364 	mb->start = buff;
365 	mb->end = mb->start + size;
366 	membuf_purge(mb);
367 }
368 
membuf_new(struct membuf * mb,int size)369 int membuf_new(struct membuf *mb, int size)
370 {
371 	mb->start = malloc(size);
372 	if (!mb->start)
373 		return -ENOMEM;
374 
375 	membuf_init(mb, mb->start, size);
376 	return 0;
377 }
378 
membuf_uninit(struct membuf * mb)379 void membuf_uninit(struct membuf *mb)
380 {
381 	mb->end = NULL;
382 	mb->start = NULL;
383 	membuf_purge(mb);
384 }
385 
membuf_dispose(struct membuf * mb)386 void membuf_dispose(struct membuf *mb)
387 {
388 	free(mb->start);
389 	membuf_uninit(mb);
390 }
391