1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Handles a contiguous list of pointers which be allocated and freed
4  *
5  * Copyright 2023 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #include <alist.h>
10 #include <display_options.h>
11 #include <malloc.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 enum {
16 	ALIST_INITIAL_SIZE	= 4,	/* default size of unsized list */
17 };
18 
alist_init(struct alist * lst,uint obj_size,uint start_size)19 bool alist_init(struct alist *lst, uint obj_size, uint start_size)
20 {
21 	/* Avoid realloc for the initial size to help malloc_simple */
22 	memset(lst, '\0', sizeof(struct alist));
23 	if (start_size) {
24 		lst->data = calloc(obj_size, start_size);
25 		if (!lst->data) {
26 			lst->flags = ALISTF_FAIL;
27 			return false;
28 		}
29 		lst->alloc = start_size;
30 	}
31 	lst->obj_size = obj_size;
32 
33 	return true;
34 }
35 
alist_uninit(struct alist * lst)36 void alist_uninit(struct alist *lst)
37 {
38 	free(lst->data);
39 
40 	/* Clear fields to avoid any confusion */
41 	memset(lst, '\0', sizeof(struct alist));
42 }
43 
alist_empty(struct alist * lst)44 void alist_empty(struct alist *lst)
45 {
46 	lst->count = 0;
47 }
48 
49 /**
50  * alist_expand_to() - Expand a list to the given size
51  *
52  * @lst: List to modify
53  * @inc_by: Amount to expand to
54  * Return: true if OK, false if out of memory
55  */
alist_expand_to(struct alist * lst,uint new_alloc)56 static bool alist_expand_to(struct alist *lst, uint new_alloc)
57 {
58 	void *new_data;
59 
60 	if (lst->flags & ALISTF_FAIL)
61 		return false;
62 
63 	/* avoid using realloc() since it increases code size */
64 	new_data = malloc(lst->obj_size * new_alloc);
65 	if (!new_data) {
66 		lst->flags |= ALISTF_FAIL;
67 		return false;
68 	}
69 
70 	memcpy(new_data, lst->data, lst->obj_size * lst->alloc);
71 	free(lst->data);
72 
73 	memset(new_data + lst->obj_size * lst->alloc, '\0',
74 	       lst->obj_size * (new_alloc - lst->alloc));
75 	lst->alloc = new_alloc;
76 	lst->data = new_data;
77 
78 	return true;
79 }
80 
alist_expand_by(struct alist * lst,uint inc_by)81 bool alist_expand_by(struct alist *lst, uint inc_by)
82 {
83 	return alist_expand_to(lst, lst->alloc + inc_by);
84 }
85 
86 /**
87  * alist_expand_min() - Expand to at least the provided size
88  *
89  * Expands to the lowest power of two which can incorporate the new size
90  *
91  * @lst: alist to expand
92  * @min_alloc: Minimum new allocated size; if 0 then ALIST_INITIAL_SIZE is used
93  * Return: true if OK, false if out of memory
94  */
alist_expand_min(struct alist * lst,uint min_alloc)95 static bool alist_expand_min(struct alist *lst, uint min_alloc)
96 {
97 	uint new_alloc;
98 
99 	for (new_alloc = lst->alloc ?: ALIST_INITIAL_SIZE;
100 	     new_alloc < min_alloc;)
101 		new_alloc *= 2;
102 
103 	return alist_expand_to(lst, new_alloc);
104 }
105 
alist_get_ptr(const struct alist * lst,uint index)106 const void *alist_get_ptr(const struct alist *lst, uint index)
107 {
108 	if (index >= lst->count)
109 		return NULL;
110 
111 	return lst->data + index * lst->obj_size;
112 }
113 
alist_calc_index(const struct alist * lst,const void * ptr)114 int alist_calc_index(const struct alist *lst, const void *ptr)
115 {
116 	uint index;
117 
118 	if (!lst->count || ptr < lst->data)
119 		return -1;
120 
121 	index = (ptr - lst->data) / lst->obj_size;
122 
123 	return index;
124 }
125 
alist_update_end(struct alist * lst,const void * ptr)126 void alist_update_end(struct alist *lst, const void *ptr)
127 {
128 	int index;
129 
130 	index = alist_calc_index(lst, ptr);
131 	lst->count = index == -1 ? 0 : index;
132 }
133 
alist_chk_ptr(const struct alist * lst,const void * ptr)134 bool alist_chk_ptr(const struct alist *lst, const void *ptr)
135 {
136 	int index = alist_calc_index(lst, ptr);
137 
138 	return index >= 0 && index < lst->count;
139 }
140 
alist_next_ptrd(const struct alist * lst,const void * ptr)141 const void *alist_next_ptrd(const struct alist *lst, const void *ptr)
142 {
143 	int index = alist_calc_index(lst, ptr);
144 
145 	assert(index != -1);
146 
147 	return alist_get_ptr(lst, index + 1);
148 }
149 
alist_ensure_ptr(struct alist * lst,uint index)150 void *alist_ensure_ptr(struct alist *lst, uint index)
151 {
152 	uint minsize = index + 1;
153 	void *ptr;
154 
155 	if (index >= lst->alloc && !alist_expand_min(lst, minsize))
156 		return NULL;
157 
158 	ptr = lst->data + index * lst->obj_size;
159 	if (minsize >= lst->count)
160 		lst->count = minsize;
161 
162 	return ptr;
163 }
164 
alist_add_placeholder(struct alist * lst)165 void *alist_add_placeholder(struct alist *lst)
166 {
167 	return alist_ensure_ptr(lst, lst->count);
168 }
169 
alist_add_ptr(struct alist * lst,void * obj)170 void *alist_add_ptr(struct alist *lst, void *obj)
171 {
172 	void *ptr;
173 
174 	ptr = alist_add_placeholder(lst);
175 	if (!ptr)
176 		return NULL;
177 	memcpy(ptr, obj, lst->obj_size);
178 
179 	return ptr;
180 }
181 
alist_uninit_move_ptr(struct alist * alist,size_t * countp)182 void *alist_uninit_move_ptr(struct alist *alist, size_t *countp)
183 {
184 	void *ptr;
185 
186 	if (countp)
187 		*countp = alist->count;
188 	if (!alist->count) {
189 		alist_uninit(alist);
190 		return NULL;
191 	}
192 
193 	ptr = alist->data;
194 
195 	/* Clear everything out so there is no record of the data */
196 	alist_init(alist, alist->obj_size, 0);
197 
198 	return ptr;
199 }
200