aos/lib/aos/heap.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

160 lines
4.3 KiB
C

/**
* \file
* \brief Simple heap allocator.
*
* This file implements a very simple heap allocator, based on K&R malloc.
*/
/*
* Copyright (c) 2008, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
*/
#include <aos/aos.h>
#include <aos/heap.h>
/**
* \brief Initialise a new heap
*
* \param heap Heap structure to be filled in
* \param buf Memory buffer out of which to allocate
* \param buflen Size of buffer
* \param morecore_func Function to call to increase heap, or NULL
*/
void heap_init(struct heap *heap, void *buf, size_t buflen,
Morecore_func_t morecore_func)
{
assert(heap != NULL);
assert(buf != NULL);
assert(buflen > sizeof(union heap_header));
// Initialise base header (nothing allocated)
heap->base.s.ptr = heap->freep = &heap->base;
heap->base.s.size = 0;
heap->morecore_func = morecore_func;
// Insert freelist header into new memory buffer
union heap_header *h = buf;
h->s.size = buflen / sizeof(union heap_header);
// Add header to freelist
heap_free(heap, (void *)(h + 1));
}
/**
* \brief Equivalent of malloc; allocates memory out of given heap.
*
* \returns NULL on failure
*/
void *heap_alloc(struct heap *heap, size_t nbytes)
{
union heap_header *p, *prevp;
unsigned nunits;
nunits = (nbytes + sizeof(union heap_header) - 1)
/ sizeof(union heap_header) + 1;
prevp = heap->freep;
assert(prevp != NULL);
for (p = prevp->s.ptr;; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) { /* exactly */
prevp->s.ptr = p->s.ptr;
} else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
heap->freep = prevp;
return (void *) (p + 1);
}
if (p == heap->freep) { /* wrapped around free list */
/* try morecore, if we have one */
if (heap->morecore_func == NULL
|| (p = (union heap_header *)
heap->morecore_func(heap, nunits)) == NULL) {
return NULL; /* none left */
}
}
}
}
/**
* \brief Equivalent of free: put block back in free list.
*/
void heap_free(struct heap *heap, void *ap)
{
union heap_header *bp, *p;
assert(heap != NULL);
assert(heap->freep != NULL);
if (ap == NULL) {
return;
}
bp = (union heap_header *) ap - 1; /* point to block header */
for (p = heap->freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) {
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) {
break; /* freed block at start or end of arena */
}
}
if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr */
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
} else {
bp->s.ptr = p->s.ptr;
}
if (p + p->s.size == bp) { /* join to lower nbr */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else {
p->s.ptr = bp;
}
heap->freep = p;
}
/**
* \brief Allocate and map in one or more pages of memory
*/
static void *pages_alloc(size_t pages)
{
assert(!"not implemented");
return NULL;
}
/**
* \brief sbrk() equivalent.
*
* This function allocates at least the amount given by 'nu' in sizeof(::Header)
* byte units. It returns a pointer to the freelist header of the memory region.
* NULL is returned when out of memory.
*
* \param nu Number of memory units (1 unit == sizeof(::Header) bytes)
*
* \return Pointer to freelist header of new memory region or NULL on out of
* memory.
*/
union heap_header *heap_default_morecore(struct heap *heap, unsigned nu)
{
union heap_header *up;
size_t nb = ROUND_UP(nu * sizeof(union heap_header),
BASE_PAGE_SIZE);
// Allocate requested number of pages and insert freelist header
up = (union heap_header*)pages_alloc(nb / BASE_PAGE_SIZE);
up->s.size = nb / sizeof(union heap_header);
// Add header to freelist
heap_free(heap, (void *)(up + 1));
return up;
}