d902f94313
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@596 a1c6a512-1295-4272-9138-f99709370657
451 lines
9.9 KiB
C
451 lines
9.9 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2002 by Daniel Stenberg
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
/*****************************************************************************
|
|
*
|
|
* Size-sorted list/tree functions.
|
|
*
|
|
* Author: Daniel Stenberg
|
|
* Date: March 7, 1997
|
|
* Version: 2.0
|
|
* Email: daniel@haxx.se
|
|
*
|
|
* v2.0
|
|
* - Added SPLAY TREE functionality.
|
|
*
|
|
* Adds and removes CHUNKS from a list or tree.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define SPLAY /* we use the splay version as that is much faster when the
|
|
amount of blocks grow */
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#ifndef SPLAY /* these routines are for the non-splay version */
|
|
|
|
struct ChunkInfo {
|
|
struct ChunkInfo *larger;
|
|
struct ChunkInfo *smaller;
|
|
size_t size;
|
|
};
|
|
|
|
/* the CHUNK list anchor */
|
|
struct ChunkInfo *chunkHead=NULL;
|
|
|
|
/***********************************************************************
|
|
|
|
findchunkbysize()
|
|
|
|
Find the chunk that is smaller than the input size. Returns
|
|
NULL if none is.
|
|
|
|
**********************************************************************/
|
|
|
|
static struct ChunkInfo *findchunkbysize(size_t size)
|
|
{
|
|
struct ChunkInfo *test = chunkHead;
|
|
struct ChunkInfo *smaller = NULL;
|
|
while(test && (test->size < size)) {
|
|
smaller = test;
|
|
test = test->larger;
|
|
}
|
|
return smaller;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
remove_chunksize()
|
|
|
|
Remove the chunk from the size-sorted list.
|
|
***********************************************************************/
|
|
|
|
void bmalloc_remove_chunksize(void *data)
|
|
{
|
|
struct ChunkInfo *chunk = (struct ChunkInfo *)data;
|
|
if(chunk->smaller)
|
|
chunk->smaller->larger = chunk->larger;
|
|
else {
|
|
/* if this has no smaller, this is the head */
|
|
chunkHead = chunk->larger; /* new head */
|
|
}
|
|
if(chunk->larger)
|
|
chunk->larger->smaller = chunk->smaller;
|
|
}
|
|
|
|
void bmalloc_insert_bysize(char *data, size_t size)
|
|
{
|
|
struct ChunkInfo *newchunk = (struct ChunkInfo *)data;
|
|
struct ChunkInfo *chunk = findchunkbysize ( size );
|
|
|
|
newchunk->size = size;
|
|
|
|
if(chunk) {
|
|
/* 'chunk' is smaller than size, append the new chunk ahead of this */
|
|
newchunk->smaller = chunk;
|
|
newchunk->larger = chunk->larger;
|
|
if(chunk->larger)
|
|
chunk->larger->smaller = newchunk;
|
|
chunk->larger = newchunk;
|
|
}
|
|
else {
|
|
/* smallest CHUNK around, append first in the list */
|
|
newchunk->larger = chunkHead;
|
|
newchunk->smaller = NULL;
|
|
|
|
if(chunkHead)
|
|
chunkHead->smaller = newchunk;
|
|
chunkHead = newchunk;
|
|
}
|
|
}
|
|
|
|
char *bmalloc_obtainbysize( size_t size)
|
|
{
|
|
struct ChunkInfo *chunk = findchunkbysize( size );
|
|
|
|
if(!chunk) {
|
|
if(size <= (chunkHead->size))
|
|
/* there is no smaller CHUNK, use the first one (if we fit within that)
|
|
*/
|
|
chunk = chunkHead;
|
|
}
|
|
else
|
|
/* we're on the last CHUNK that is smaller than requested, step onto
|
|
the bigger one */
|
|
chunk = chunk->larger;
|
|
|
|
if(chunk) {
|
|
bmalloc_remove_chunksize( chunk ); /* unlink size-wise */
|
|
return (char *)chunk;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void bmalloc_print_sizes(void)
|
|
{
|
|
struct ChunkInfo *chunk = chunkHead;
|
|
printf("List of CHUNKS (in size order):\n");
|
|
#if 1
|
|
while(chunk) {
|
|
printf(" START %p END %p SIZE %d\n",
|
|
chunk, (char *)chunk+chunk->size, chunk->size);
|
|
chunk = chunk->larger;
|
|
}
|
|
#endif
|
|
printf("End of CHUNKS:\n");
|
|
}
|
|
|
|
#else /* Here follows all routines dealing with the SPLAY TREES */
|
|
|
|
typedef struct tree_node Tree;
|
|
struct tree_node {
|
|
Tree *smaller; /* smaller node */
|
|
Tree *larger; /* larger node */
|
|
Tree *same; /* points to a node with identical key */
|
|
int key; /* the "sort" key */
|
|
};
|
|
|
|
Tree *chunkHead = NULL; /* the root */
|
|
|
|
#define compare(i,j) ((i)-(j))
|
|
|
|
/* Set this to a key value that will *NEVER* appear otherwise */
|
|
#define KEY_NOTUSED -1
|
|
|
|
/*
|
|
* Splay using the key i (which may or may not be in the tree.) The starting
|
|
* root is t. Weight fields are maintained.
|
|
*/
|
|
static
|
|
Tree * splay (int i, Tree *t)
|
|
{
|
|
Tree N, *l, *r, *y;
|
|
int comp;
|
|
|
|
if (t == NULL)
|
|
return t;
|
|
N.smaller = N.larger = NULL;
|
|
l = r = &N;
|
|
|
|
for (;;) {
|
|
comp = compare(i, t->key);
|
|
if (comp < 0) {
|
|
if (t->smaller == NULL)
|
|
break;
|
|
if (compare(i, t->smaller->key) < 0) {
|
|
y = t->smaller; /* rotate smaller */
|
|
t->smaller = y->larger;
|
|
y->larger = t;
|
|
|
|
t = y;
|
|
if (t->smaller == NULL)
|
|
break;
|
|
}
|
|
r->smaller = t; /* link smaller */
|
|
r = t;
|
|
t = t->smaller;
|
|
}
|
|
else if (comp > 0) {
|
|
if (t->larger == NULL)
|
|
break;
|
|
if (compare(i, t->larger->key) > 0) {
|
|
y = t->larger; /* rotate larger */
|
|
t->larger = y->smaller;
|
|
y->smaller = t;
|
|
t = y;
|
|
if (t->larger == NULL)
|
|
break;
|
|
}
|
|
l->larger = t; /* link larger */
|
|
l = t;
|
|
t = t->larger;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
l->larger = r->smaller = NULL;
|
|
|
|
l->larger = t->smaller; /* assemble */
|
|
r->smaller = t->larger;
|
|
t->smaller = N.larger;
|
|
t->larger = N.smaller;
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Insert key i into the tree t. Return a pointer to the resulting tree or
|
|
NULL if something went wrong. */
|
|
static
|
|
Tree *insert(int i, Tree *t, Tree *new)
|
|
{
|
|
if (new == NULL) {
|
|
return t;
|
|
}
|
|
|
|
if (t != NULL) {
|
|
t = splay(i,t);
|
|
if (compare(i, t->key)==0) {
|
|
/* it already exists one of this size */
|
|
|
|
new->same = t;
|
|
new->key = i;
|
|
new->smaller = t->smaller;
|
|
new->larger = t->larger;
|
|
|
|
t->smaller = new;
|
|
t->key = KEY_NOTUSED;
|
|
|
|
return new; /* new root node */
|
|
}
|
|
}
|
|
|
|
if (t == NULL) {
|
|
new->smaller = new->larger = NULL;
|
|
}
|
|
else if (compare(i, t->key) < 0) {
|
|
new->smaller = t->smaller;
|
|
new->larger = t;
|
|
t->smaller = NULL;
|
|
}
|
|
else {
|
|
new->larger = t->larger;
|
|
new->smaller = t;
|
|
t->larger = NULL;
|
|
}
|
|
new->key = i;
|
|
|
|
new->same = NULL; /* no identical node (yet) */
|
|
|
|
return new;
|
|
}
|
|
|
|
/* Finds and deletes the best-fit node from the tree. Return a pointer to the
|
|
resulting tree. best-fit means the smallest node that fits the requested
|
|
size. */
|
|
static
|
|
Tree *removebestfit(int i, Tree *t, Tree **removed)
|
|
{
|
|
Tree *x;
|
|
|
|
if (t==NULL)
|
|
return NULL;
|
|
t = splay(i,t);
|
|
if(compare(i, t->key) > 0) {
|
|
/* too small node, try the larger chain */
|
|
if(t->larger)
|
|
t=splay(t->larger->key, t);
|
|
else {
|
|
/* fail */
|
|
*removed = NULL;
|
|
return t;
|
|
}
|
|
}
|
|
|
|
if (compare(i, t->key) <= 0) { /* found it */
|
|
|
|
/* FIRST! Check if there is a list with identical sizes */
|
|
x = t->same;
|
|
if(x) {
|
|
/* there is, pick one from the list */
|
|
|
|
/* 'x' is the new root node */
|
|
|
|
x->key = t->key;
|
|
x->larger = t->larger;
|
|
x->smaller = t->smaller;
|
|
*removed = t;
|
|
return x; /* new root */
|
|
}
|
|
|
|
if (t->smaller == NULL) {
|
|
x = t->larger;
|
|
}
|
|
else {
|
|
x = splay(i, t->smaller);
|
|
x->larger = t->larger;
|
|
}
|
|
*removed = t;
|
|
|
|
return x;
|
|
}
|
|
else {
|
|
*removed = NULL; /* no match */
|
|
return t; /* It wasn't there */
|
|
}
|
|
}
|
|
|
|
|
|
/* Deletes the node we point out from the tree if it's there. Return a pointer
|
|
to the resulting tree. */
|
|
static
|
|
Tree *removebyaddr(Tree *t, Tree *remove)
|
|
{
|
|
Tree *x;
|
|
|
|
if (!t || !remove)
|
|
return NULL;
|
|
|
|
if(KEY_NOTUSED == remove->key) {
|
|
/* just unlink ourselves nice and quickly: */
|
|
remove->smaller->same = remove->same;
|
|
if(remove->same)
|
|
remove->same->smaller = remove->smaller;
|
|
/* voila, we're done! */
|
|
return t;
|
|
}
|
|
|
|
t = splay(remove->key,t);
|
|
|
|
/* Check if there is a list with identical sizes */
|
|
|
|
x = t->same;
|
|
if(x) {
|
|
/* 'x' is the new root node */
|
|
|
|
x->key = t->key;
|
|
x->larger = t->larger;
|
|
x->smaller = t->smaller;
|
|
|
|
return x; /* new root */
|
|
}
|
|
|
|
/* Remove the actualy root node: */
|
|
|
|
if (t->smaller == NULL) {
|
|
x = t->larger;
|
|
}
|
|
else {
|
|
x = splay(remove->key, t->smaller);
|
|
x->larger = t->larger;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
#ifdef DEBUG_MALLOC
|
|
static
|
|
int printtree(Tree * t, int d, char output)
|
|
{
|
|
int distance=0;
|
|
Tree *node;
|
|
int i;
|
|
if (t == NULL)
|
|
return 0;
|
|
distance += printtree(t->larger, d+1, output);
|
|
for (i=0; i<d; i++)
|
|
if(output)
|
|
printf(" ");
|
|
|
|
if(output) {
|
|
printf("%d[%d]", t->key, i);
|
|
}
|
|
|
|
for(node = t->same; node; node = node->same) {
|
|
distance += i; /* this has the same "virtual" distance */
|
|
|
|
if(output)
|
|
printf(" [+]");
|
|
}
|
|
if(output)
|
|
puts("");
|
|
|
|
distance += i;
|
|
distance += printtree(t->smaller, d+1, output);
|
|
return distance;
|
|
}
|
|
#endif
|
|
|
|
/* Here follow the look-alike interface so that the tree-function names are
|
|
the same as the list-ones to enable easy interchange */
|
|
|
|
void bmalloc_remove_chunksize(void *data)
|
|
{
|
|
chunkHead = removebyaddr(chunkHead, data);
|
|
}
|
|
|
|
void bmalloc_insert_bysize(char *data, size_t size)
|
|
{
|
|
chunkHead = insert(size, chunkHead, (Tree *)data);
|
|
}
|
|
|
|
char *bmalloc_obtainbysize( size_t size)
|
|
{
|
|
Tree *receive;
|
|
chunkHead = removebestfit(size, chunkHead, &receive);
|
|
return (char *)receive;
|
|
}
|
|
|
|
#ifdef DEBUG_MALLOC
|
|
void bmalloc_print_sizes(void)
|
|
{
|
|
printtree(chunkHead, 0, 1);
|
|
}
|
|
#endif
|
|
|
|
#endif
|