/* Copyright (c) 2021 Intel Corporation * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include void sys_multi_heap_init(struct sys_multi_heap *heap, sys_multi_heap_fn_t choice_fn) { heap->nheaps = 0; heap->choice = choice_fn; } void sys_multi_heap_add_heap(struct sys_multi_heap *mheap, struct sys_heap *heap, void *user_data) { __ASSERT_NO_MSG(mheap->nheaps < ARRAY_SIZE(mheap->heaps)); mheap->heaps[mheap->nheaps].heap = heap; mheap->heaps[mheap->nheaps++].user_data = user_data; /* Now sort them in memory order, simple extraction sort */ for (int i = 0; i < mheap->nheaps; i++) { struct sys_multi_heap_rec swap; int lowest = -1; uintptr_t lowest_addr = UINTPTR_MAX; for (int j = i; j < mheap->nheaps; j++) { uintptr_t haddr = (uintptr_t)mheap->heaps[j].heap->heap; if (haddr < lowest_addr) { lowest = j; lowest_addr = haddr; } } swap = mheap->heaps[i]; mheap->heaps[i] = mheap->heaps[lowest]; mheap->heaps[lowest] = swap; } } void *sys_multi_heap_alloc(struct sys_multi_heap *mheap, void *cfg, size_t bytes) { return mheap->choice(mheap, cfg, 0, bytes); } void *sys_multi_heap_aligned_alloc(struct sys_multi_heap *mheap, void *cfg, size_t align, size_t bytes) { return mheap->choice(mheap, cfg, align, bytes); } const struct sys_multi_heap_rec *sys_multi_heap_get_heap(const struct sys_multi_heap *mheap, void *addr) { uintptr_t haddr, baddr = (uintptr_t) addr; int i; /* Search the heaps array to find the correct heap * * FIXME: just a linear search currently, as the list is * always short for reasonable apps and this code is very * quick. The array is stored in sorted order though, so a * binary search based on the block address is the design * goal. */ for (i = 0; i < mheap->nheaps; i++) { haddr = (uintptr_t)mheap->heaps[i].heap->heap; if (baddr < haddr) { break; } } /* Now i stores the index of the heap after our target (even * if it's invalid and our target is the last!) * FIXME: return -ENOENT when a proper heap is not found */ return &mheap->heaps[i-1]; } void sys_multi_heap_free(struct sys_multi_heap *mheap, void *block) { const struct sys_multi_heap_rec *heap; heap = sys_multi_heap_get_heap(mheap, block); if (heap != NULL) { sys_heap_free(heap->heap, block); } } void *sys_multi_heap_aligned_realloc(struct sys_multi_heap *mheap, void *cfg, void *ptr, size_t align, size_t bytes) { /* special realloc semantics */ if (ptr == NULL) { return sys_multi_heap_aligned_alloc(mheap, cfg, align, bytes); } if (bytes == 0) { sys_multi_heap_free(mheap, ptr); return NULL; } const struct sys_multi_heap_rec *rec = sys_multi_heap_get_heap(mheap, ptr); __ASSERT_NO_MSG(rec); /* Invoke the realloc function on the same heap, to try to reuse in place */ void *new_ptr = sys_heap_aligned_realloc(rec->heap, ptr, align, bytes); if (new_ptr != NULL) { return new_ptr; } size_t old_size = sys_heap_usable_size(rec->heap, ptr); /* Otherwise, allocate a new block and copy the data */ new_ptr = sys_multi_heap_aligned_alloc(mheap, cfg, align, bytes); if (new_ptr != NULL) { memcpy(new_ptr, ptr, MIN(old_size, bytes)); sys_multi_heap_free(mheap, ptr); } return new_ptr; }