160 lines
4.3 KiB
C
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;
|
|
}
|