1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Handles a buffer that can be allocated and freed
4 *
5 * Copyright 2021 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#ifndef USE_HOSTCC
10#include <malloc.h>
11#include <mapmem.h>
12#include <string.h>
13#endif
14
15#include <abuf.h>
16
17void abuf_set(struct abuf *abuf, void *data, size_t size)
18{
19	abuf_uninit(abuf);
20	abuf->data = data;
21	abuf->size = size;
22}
23
24#ifndef USE_HOSTCC
25void abuf_map_sysmem(struct abuf *abuf, ulong addr, size_t size)
26{
27	abuf_set(abuf, map_sysmem(addr, size), size);
28}
29#else
30/* copied from lib/string.c for convenience */
31static char *memdup(const void *src, size_t len)
32{
33	char *p;
34
35	p = malloc(len);
36	if (!p)
37		return NULL;
38
39	memcpy(p, src, len);
40
41	return p;
42}
43#endif
44
45bool abuf_realloc(struct abuf *abuf, size_t new_size)
46{
47	void *ptr;
48
49	if (!new_size) {
50		/* easy case, just need to uninit, freeing any allocation */
51		abuf_uninit(abuf);
52		return true;
53	} else if (abuf->alloced) {
54		/* currently allocated, so need to reallocate */
55		ptr = realloc(abuf->data, new_size);
56		if (!ptr)
57			return false;
58		abuf->data = ptr;
59		abuf->size = new_size;
60		return true;
61	} else if (new_size <= abuf->size) {
62		/*
63		 * not currently alloced and new size is no larger. Just update
64		 * it. Data is lost off the end if new_size < abuf->size
65		 */
66		abuf->size = new_size;
67		return true;
68	} else {
69		/* not currently allocated and new size is larger. Alloc and
70		 * copy in data. The new space is not inited.
71		 */
72		ptr = malloc(new_size);
73		if (!ptr)
74			return false;
75		if (abuf->size)
76			memcpy(ptr, abuf->data, abuf->size);
77		abuf->data = ptr;
78		abuf->size = new_size;
79		abuf->alloced = true;
80		return true;
81	}
82}
83
84bool abuf_realloc_inc(struct abuf *abuf, size_t inc)
85{
86	return abuf_realloc(abuf, abuf->size + inc);
87}
88
89void *abuf_uninit_move(struct abuf *abuf, size_t *sizep)
90{
91	void *ptr;
92
93	if (sizep)
94		*sizep = abuf->size;
95	if (!abuf->size)
96		return NULL;
97	if (abuf->alloced) {
98		ptr = abuf->data;
99	} else {
100		ptr = memdup(abuf->data, abuf->size);
101		if (!ptr)
102			return NULL;
103	}
104	/* Clear everything out so there is no record of the data */
105	abuf_init(abuf);
106
107	return ptr;
108}
109
110void abuf_init_set(struct abuf *abuf, void *data, size_t size)
111{
112	abuf_init(abuf);
113	abuf_set(abuf, data, size);
114}
115
116void abuf_init_move(struct abuf *abuf, void *data, size_t size)
117{
118	abuf_init_set(abuf, data, size);
119	abuf->alloced = true;
120}
121
122void abuf_uninit(struct abuf *abuf)
123{
124	if (abuf->alloced)
125		free(abuf->data);
126	abuf_init(abuf);
127}
128
129void abuf_init(struct abuf *abuf)
130{
131	abuf->data = NULL;
132	abuf->size = 0;
133	abuf->alloced = false;
134}
135