]>
Commit | Line | Data |
---|---|---|
75581e41 SG |
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 <[email protected]> | |
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 | ||
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 | ||
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 | ||
5bd4ead8 SG |
44 | void alist_empty(struct alist *lst) |
45 | { | |
46 | lst->count = 0; | |
47 | } | |
48 | ||
75581e41 SG |
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 | */ | |
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 | ||
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 | */ | |
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 | ||
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 | ||
1d49f78c SG |
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 | ||
5dfc1c80 SG |
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 | ||
d785a77d SG |
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 | ||
1d49f78c SG |
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 | ||
75581e41 SG |
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 | ||
165 | void *alist_add_placeholder(struct alist *lst) | |
166 | { | |
167 | return alist_ensure_ptr(lst, lst->count); | |
168 | } | |
169 | ||
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 | ||
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 | } |