www.pudn.com > COS0.0.1.rar > heap.c


/*
 heap.c - allocation thru a heap
  
 Author:	Paul Barker
 Part of:	COS
 Created:	01/09/04
 Last Modified:	16/10/04

 Copyright (C) 2004 Paul Barker
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

                     (See file "Copying")
*/

#include 

#include 
#include 
#include 
#include 

#define SIZE_OF_NODE(n) ((n->end - (iptr_t)n) - sizeof(heap_node_t))

// minimum allocation size
#define QUANTUM		8
#define QUANTUM_MASK	(~(QUANTUM - 1))

// given ptr must already be alloc'd, of at least size init_size
// I suggest that the type PG_CLUSTER_HEAP is *always* used for heaps.
heap_t* heap_create(size_t init_size, ptr_t start)
{
	TRACE(("Creating Heap\n"));
	// handle no given pointer
	if (start == 0)
	{
		TRACE(("Was not given a pointer,"));
		// handle 0 initial size
		if (init_size == 0)
			init_size = PAGE_SIZE;
		
		init_size = (init_size & PAGE_MASK) +
			     ((init_size & ~PAGE_MASK) ? 4096 : 0);
		
		TRACE((" will allocate %d (0x%x) bytes\n",
			init_size, init_size));
		
		start = phys_alloc(init_size / PAGE_SIZE, PG_CLUSTER_HEAP,
				   PG_CLUSTER_FREE);
	}
	
	if (init_size == 0)
		return 0;	// invalid if start given
	
	TRACE(("Using pointer 0x%x\n", start));
	
	memzero(start, init_size);
	
	heap_t* h = (heap_t*)start;
	iptr_t p = (iptr_t)start;		// need to do addition below
	
	h->end = p + init_size;
	h->first_free = (heap_node_t*)(p + sizeof(heap_t));
	
	heap_node_t* node = h->first_free;
	node->flags = HEAP_FREE;
	node->end = h->end;
	
	TRACE(("Done\n"));
	
	return h;
}

// add free node
void heap_add_free(heap_t* heap, heap_node_t* p)
{
	TRACE(("Adding free node at 0x%x to heap at 0x%x\n", p, heap));

	p->flags |= HEAP_FREE;
	
	// first find end of free list
	heap_node_t* free = heap->first_free;
	
	TRACE(("First free node at 0x%x\n", free));
	
	if (free == NULL)
	{
		heap->first_free = p;
		return;
	}
	
	while (free->next)
	{
		free = free->next;
		TRACE(("Moving to next free node at 0x%x\n", free));
	}
	
	// now set the next free to the given node
	free->next = p;
	
	TRACE(("Done\n"));
}

// allocate from a heap
ptr_t heap_alloc(heap_t* heap, size_t sz)
{
	heap_node_t* node = heap->first_free;
	u32_t s = 0;
	count_t n = 0;
	
	sz &= QUANTUM_MASK;
	
	TRACE(("Allocating memory of size %d (0x%x) bytes\n", sz, sz));
	TRACE(("from heap at 0x%x, first free is at 0x%x\n", heap, node));
	
	while (node)
	{
		s = SIZE_OF_NODE(node);
		TRACE(("Loop (%d): Node of size %d (0x%x) bytes\n", n, s, s));
		TRACE(("\tstart=0x%x, end=0x%x\n",
			((iptr_t)node) + sizeof(heap_node_t), node->end));
		if ((s >= sz) && (node->flags & HEAP_FREE))
			goto finish;
		node = node->next;
		TRACE(("\tMoving to next node at 0x%x\n", node));
		++n;
	}
	
	TRACE(("Not enough free memory!\n"));
	
	// could not find a node big enough
	return NULL;
	
finish:
	TRACE(("Found a node large enough\n"));
	
	iptr_t i = (iptr_t)node;
	
	// cut out a new node if possible
	if (s >= (sz + sizeof(heap_node_t) + QUANTUM))
	{
		heap_node_t* new_node = (heap_node_t*)
					(i + sizeof(heap_node_t) + sz);
		TRACE(("Splitting a free node at 0x%x\n", new_node));
		
		new_node->end = node->end;
		node->end = (iptr_t)new_node;
		heap_add_free(heap, new_node);
	}
	
	node->flags &= ~HEAP_FREE;
	
	iptr_t ret = i + sizeof(heap_node_t);
	TRACE(("Returning pointer 0x%x\n", ret));
	return (ptr_t)ret;
}

// free previously allocated memory
void heap_free(heap_t* heap, ptr_t p)
{
	TRACE(("Freeing memory at 0x%x from heap at 0x%x\n", p, heap));
	// mark the node as free
	iptr_t i = (iptr_t)p;
	heap_node_t* node = (heap_node_t*)(i - sizeof(heap_node_t));
	
	node->flags |= HEAP_FREE;
	
	TRACE(("\tnode at 0x%x, end at 0x%x\n", node, node->end));
	
	count_t sz = SIZE_OF_NODE(node);
	TRACE(("\tmeaning size is %d (0x%x) bytes\n", sz, sz));
}

/*
  TODO:	Support best fit allocation.
	Use phys_alloc() to fulfil requests for more memory than is free in
		the requested heap.
	Support requesting alignment.
	Compaction of a heap (meaning remove unused pages).
	Validation function.
	In heap_free(), we must condense adjacent free nodes into a single
		node.
	Add mutexes.
	heap_destroy().
*/