// SPDX-License-Identifier: GPL-2.0+ /* * Handles a contiguous list of pointers which be allocated and freed * * Copyright 2023 Google LLC * Written by Simon Glass */ #include #include #include #include #include enum { ALIST_INITIAL_SIZE = 4, /* default size of unsized list */ }; bool alist_init(struct alist *lst, uint obj_size, uint start_size) { /* Avoid realloc for the initial size to help malloc_simple */ memset(lst, '\0', sizeof(struct alist)); if (start_size) { lst->data = calloc(obj_size, start_size); if (!lst->data) { lst->flags = ALISTF_FAIL; return false; } lst->alloc = start_size; } lst->obj_size = obj_size; return true; } void alist_uninit(struct alist *lst) { free(lst->data); /* Clear fields to avoid any confusion */ memset(lst, '\0', sizeof(struct alist)); } void alist_empty(struct alist *lst) { lst->count = 0; } /** * alist_expand_to() - Expand a list to the given size * * @lst: List to modify * @inc_by: Amount to expand to * Return: true if OK, false if out of memory */ static bool alist_expand_to(struct alist *lst, uint new_alloc) { void *new_data; if (lst->flags & ALISTF_FAIL) return false; /* avoid using realloc() since it increases code size */ new_data = malloc(lst->obj_size * new_alloc); if (!new_data) { lst->flags |= ALISTF_FAIL; return false; } memcpy(new_data, lst->data, lst->obj_size * lst->alloc); free(lst->data); memset(new_data + lst->obj_size * lst->alloc, '\0', lst->obj_size * (new_alloc - lst->alloc)); lst->alloc = new_alloc; lst->data = new_data; return true; } bool alist_expand_by(struct alist *lst, uint inc_by) { return alist_expand_to(lst, lst->alloc + inc_by); } /** * alist_expand_min() - Expand to at least the provided size * * Expands to the lowest power of two which can incorporate the new size * * @lst: alist to expand * @min_alloc: Minimum new allocated size; if 0 then ALIST_INITIAL_SIZE is used * Return: true if OK, false if out of memory */ static bool alist_expand_min(struct alist *lst, uint min_alloc) { uint new_alloc; for (new_alloc = lst->alloc ?: ALIST_INITIAL_SIZE; new_alloc < min_alloc;) new_alloc *= 2; return alist_expand_to(lst, new_alloc); } const void *alist_get_ptr(const struct alist *lst, uint index) { if (index >= lst->count) return NULL; return lst->data + index * lst->obj_size; } int alist_calc_index(const struct alist *lst, const void *ptr) { uint index; if (!lst->count || ptr < lst->data) return -1; index = (ptr - lst->data) / lst->obj_size; return index; } void alist_update_end(struct alist *lst, const void *ptr) { int index; index = alist_calc_index(lst, ptr); lst->count = index == -1 ? 0 : index; } bool alist_chk_ptr(const struct alist *lst, const void *ptr) { int index = alist_calc_index(lst, ptr); return index >= 0 && index < lst->count; } const void *alist_next_ptrd(const struct alist *lst, const void *ptr) { int index = alist_calc_index(lst, ptr); assert(index != -1); return alist_get_ptr(lst, index + 1); } void *alist_ensure_ptr(struct alist *lst, uint index) { uint minsize = index + 1; void *ptr; if (index >= lst->alloc && !alist_expand_min(lst, minsize)) return NULL; ptr = lst->data + index * lst->obj_size; if (minsize >= lst->count) lst->count = minsize; return ptr; } void *alist_add_placeholder(struct alist *lst) { return alist_ensure_ptr(lst, lst->count); } void *alist_add_ptr(struct alist *lst, void *obj) { void *ptr; ptr = alist_add_placeholder(lst); if (!ptr) return NULL; memcpy(ptr, obj, lst->obj_size); return ptr; } void *alist_uninit_move_ptr(struct alist *alist, size_t *countp) { void *ptr; if (countp) *countp = alist->count; if (!alist->count) { alist_uninit(alist); return NULL; } ptr = alist->data; /* Clear everything out so there is no record of the data */ alist_init(alist, alist->obj_size, 0); return ptr; }