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 "membuff.h"
13
14void 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
21static 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
75int 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
87bool 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
98int 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
149int 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
156int 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
163int 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
186int 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
206bool membuff_isempty(struct membuff *mb)
207{
208	return mb->head == mb->tail;
209}
210
211int 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
228int membuff_size(struct membuff *mb)
229{
230	return mb->end - mb->start;
231}
232
233bool 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
284int membuff_free(struct membuff *mb)
285{
286	return mb->end == mb->start ? 0 :
287			(mb->end - mb->start) - 1 - membuff_avail(mb);
288}
289
290int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch, bool must_fit)
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 && must_fit) {
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
325int 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
361void 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
368int 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
378void membuff_uninit(struct membuff *mb)
379{
380	mb->end = NULL;
381	mb->start = NULL;
382	membuff_purge(mb);
383}
384
385void membuff_dispose(struct membuff *mb)
386{
387	free(&mb->start);
388	membuff_uninit(mb);
389}
390