Simon Glass | e1edadf | 2024-07-30 08:39:37 -0600 | [diff] [blame] | 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 | #ifndef __ALIST_H |
| 10 | #define __ALIST_H |
| 11 | |
| 12 | #include <stdbool.h> |
| 13 | #include <linux/bitops.h> |
| 14 | #include <linux/types.h> |
| 15 | |
| 16 | /** |
| 17 | * struct alist - object list that can be allocated and freed |
| 18 | * |
| 19 | * Holds a list of objects, each of the same size. The object is typically a |
| 20 | * C struct. The array is alloced in memory can change in size. |
| 21 | * |
| 22 | * The list rememebers the size of the list, but has a separate count of how |
| 23 | * much space is allocated, This allows it increase in size in steps as more |
| 24 | * elements are added, which is more efficient that reallocating the list every |
| 25 | * time a single item is added |
| 26 | * |
| 27 | * Two types of access are provided: |
| 28 | * |
| 29 | * alist_get...(index) |
| 30 | * gets an existing element, if its index is less that size |
| 31 | * |
| 32 | * alist_ensure(index) |
| 33 | * address an existing element, or creates a new one if not present |
| 34 | * |
| 35 | * @data: object data of size `@obj_size * @alloc`. The list can grow as |
| 36 | * needed but never shrinks |
| 37 | * @obj_size: Size of each object in bytes |
| 38 | * @count: number of objects in array |
| 39 | * @alloc: allocated length of array, to which @count can grow |
| 40 | * @flags: flags for the alist (ALISTF_...) |
| 41 | */ |
| 42 | struct alist { |
| 43 | void *data; |
| 44 | u16 obj_size; |
| 45 | u16 count; |
| 46 | u16 alloc; |
| 47 | u16 flags; |
| 48 | }; |
| 49 | |
| 50 | /** |
| 51 | * enum alist_flags - Flags for the alist |
| 52 | * |
| 53 | * @ALIST_FAIL: true if any allocation has failed. Once this has happened, the |
| 54 | * alist is dead and cannot grow further |
| 55 | */ |
| 56 | enum alist_flags { |
| 57 | ALISTF_FAIL = BIT(0), |
| 58 | }; |
| 59 | |
| 60 | /** |
| 61 | * alist_has() - Check if an index is within the list range |
| 62 | * |
| 63 | * Checks if index is within the current alist count |
| 64 | * |
| 65 | * @lst: alist to check |
| 66 | * @index: Index to check |
| 67 | * Returns: true if value, else false |
| 68 | */ |
| 69 | static inline bool alist_has(struct alist *lst, uint index) |
| 70 | { |
| 71 | return index < lst->count; |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * alist_err() - Check if the alist is still valid |
| 76 | * |
| 77 | * @lst: List to check |
| 78 | * Return: false if OK, true if any previous allocation failed |
| 79 | */ |
| 80 | static inline bool alist_err(struct alist *lst) |
| 81 | { |
| 82 | return lst->flags & ALISTF_FAIL; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * alist_get_ptr() - Get the value of a pointer |
| 87 | * |
| 88 | * @lst: alist to check |
| 89 | * @index: Index to read from |
| 90 | * Returns: pointer, if present, else NULL |
| 91 | */ |
| 92 | const void *alist_get_ptr(const struct alist *lst, uint index); |
| 93 | |
| 94 | /** |
| 95 | * alist_getd() - Get the value of a pointer directly, with no checking |
| 96 | * |
| 97 | * This must only be called on indexes for which alist_has() returns true |
| 98 | * |
| 99 | * @lst: alist to check |
| 100 | * @index: Index to read from |
| 101 | * Returns: pointer value (may be NULL) |
| 102 | */ |
| 103 | static inline const void *alist_getd(struct alist *lst, uint index) |
| 104 | { |
| 105 | return lst->data + index * lst->obj_size; |
| 106 | } |
| 107 | |
| 108 | /** get an entry as a constant */ |
| 109 | #define alist_get(_lst, _index, _struct) \ |
| 110 | ((const _struct *)alist_get_ptr(_lst, _index)) |
| 111 | |
| 112 | /** get an entry which can be written to */ |
| 113 | #define alist_getw(_lst, _index, _struct) \ |
| 114 | ((_struct *)alist_get_ptr(_lst, _index)) |
| 115 | |
| 116 | /** |
| 117 | * alist_ensure_ptr() - Ensure an object exists at a given index |
| 118 | * |
| 119 | * This provides read/write access to an array element. If it does not exist, |
| 120 | * it is allocated, reading for the caller to store the object into |
| 121 | * |
| 122 | * Allocates a object at the given index if needed |
| 123 | * |
| 124 | * @lst: alist to check |
| 125 | * @index: Index to address |
| 126 | * Returns: pointer where struct can be read/written, or NULL if out of memory |
| 127 | */ |
| 128 | void *alist_ensure_ptr(struct alist *lst, uint index); |
| 129 | |
| 130 | /** |
| 131 | * alist_ensure() - Address a struct, the correct object type |
| 132 | * |
| 133 | * Use as: |
| 134 | * struct my_struct *ptr = alist_ensure(&lst, 4, struct my_struct); |
| 135 | */ |
| 136 | #define alist_ensure(_lst, _index, _struct) \ |
| 137 | ((_struct *)alist_ensure_ptr(_lst, _index)) |
| 138 | |
| 139 | /** |
| 140 | * alist_add_placeholder() - Add a new item to the end of the list |
| 141 | * |
| 142 | * @lst: alist to add to |
| 143 | * Return: Pointer to the newly added position. Note that this is not inited so |
| 144 | * the caller must copy the requested struct to the returned pointer |
| 145 | */ |
| 146 | void *alist_add_placeholder(struct alist *lst); |
| 147 | |
| 148 | /** |
| 149 | * alist_add_ptr() - Ad a new object to the list |
| 150 | * |
| 151 | * @lst: alist to add to |
| 152 | * @obj: Pointer to object to copy in |
| 153 | * Returns: pointer to where the object was copied, or NULL if out of memory |
| 154 | */ |
| 155 | void *alist_add_ptr(struct alist *lst, void *obj); |
| 156 | |
| 157 | /** |
| 158 | * alist_expand_by() - Expand a list by the given amount |
| 159 | * |
| 160 | * @lst: alist to expand |
| 161 | * @inc_by: Amount to expand by |
| 162 | * Return: true if OK, false if out of memory |
| 163 | */ |
| 164 | bool alist_expand_by(struct alist *lst, uint inc_by); |
| 165 | |
| 166 | /** |
| 167 | * alist_add() - Used to add an object type with the correct type |
| 168 | * |
| 169 | * Use as: |
| 170 | * struct my_struct obj; |
| 171 | * struct my_struct *ptr = alist_add(&lst, &obj); |
| 172 | */ |
| 173 | #define alist_add(_lst, _obj) \ |
| 174 | ((typeof(_obj) *)alist_add_ptr(_lst, &(_obj))) |
| 175 | |
| 176 | /** |
| 177 | * alist_init() - Set up a new object list |
| 178 | * |
| 179 | * Sets up a list of objects, initially empty |
| 180 | * |
| 181 | * @lst: alist to set up |
| 182 | * @obj_size: Size of each element in bytes |
| 183 | * @alloc_size: Number of items to allowed to start, before reallocation is |
| 184 | * needed (0 to start with no space) |
| 185 | * Return: true if OK, false if out of memory |
| 186 | */ |
| 187 | bool alist_init(struct alist *lst, uint obj_size, uint alloc_size); |
| 188 | |
| 189 | #define alist_init_struct(_lst, _struct) \ |
| 190 | alist_init(_lst, sizeof(_struct), 0) |
| 191 | |
| 192 | /** |
| 193 | * alist_uninit_move_ptr() - Return the allocated contents and uninit the alist |
| 194 | * |
| 195 | * This returns the alist data to the caller, so that the caller receives data |
| 196 | * that it can be sure will hang around. The caller is responsible for freeing |
| 197 | * the data. |
| 198 | * |
| 199 | * If the alist size is 0, this returns NULL |
| 200 | * |
| 201 | * The alist is uninited as part of this. |
| 202 | * |
| 203 | * The alist must be inited before this can be called. |
| 204 | * |
| 205 | * @alist: alist to uninit |
| 206 | * @countp: if non-NULL, returns the number of objects in the returned data |
| 207 | * (which is @alist->size) |
| 208 | * Return: data contents, allocated with malloc(), or NULL if the data could not |
| 209 | * be allocated, or the data size is 0 |
| 210 | */ |
| 211 | void *alist_uninit_move_ptr(struct alist *alist, size_t *countp); |
| 212 | |
| 213 | /** |
| 214 | * alist_uninit_move() - Typed version of alist_uninit_move_ptr() |
| 215 | */ |
| 216 | #define alist_uninit_move(_lst, _countp, _struct) \ |
| 217 | (_struct *)alist_uninit_move_ptr(_lst, _countp) |
| 218 | |
| 219 | /** |
| 220 | * alist_uninit() - Free any memory used by an alist |
| 221 | * |
| 222 | * The alist must be inited before this can be called. |
| 223 | * |
| 224 | * @alist: alist to uninit |
| 225 | */ |
| 226 | void alist_uninit(struct alist *alist); |
| 227 | |
| 228 | #endif /* __ALIST_H */ |