/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* GMime
* Copyright (C) 2000-2007 Jeffrey Stedfast
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "memchunk.h"
/* in glib2, GSearchFunc was combined with GCompareFunc */
typedef GCompareFunc GSearchFunc;
typedef struct _MemChunkFreeNode {
struct _MemChunkFreeNode *next;
unsigned int atoms;
} MemChunkFreeNode;
typedef struct _MemChunkNodeInfo {
struct _MemChunkNodeInfo *next;
unsigned char *block;
size_t blocksize;
size_t atoms;
} MemChunkNodeInfo;
struct _MemChunk {
size_t atomsize;
size_t atomcount;
size_t blocksize;
GPtrArray *blocks;
gboolean autoclean;
MemChunkFreeNode *free;
};
static int
tree_compare (MemChunkNodeInfo *a, MemChunkNodeInfo *b)
{
if (a->block > b->block)
return 1;
else if (a->block < b->block)
return -1;
else
return 0;
}
static int
tree_search (MemChunkNodeInfo *info, unsigned char *mem)
{
if (info->block <= mem) {
if (mem < info->block + info->blocksize)
return 0;
return 1;
}
return -1;
}
MemChunk *
memchunk_new (size_t atomsize, size_t atomcount, gboolean autoclean)
{
MemChunk *chunk;
/* each atom has to be at least the size of a MemChunkFreeNode
for this to work */
atomsize = MAX (atomsize, sizeof (MemChunkFreeNode));
chunk = g_malloc (sizeof (MemChunk));
chunk->atomsize = atomsize;
chunk->atomcount = atomcount;
chunk->blocksize = atomsize * atomcount;
chunk->autoclean = autoclean;
chunk->blocks = g_ptr_array_new ();
chunk->free = NULL;
return chunk;
}
void *
memchunk_alloc (MemChunk *memchunk)
{
MemChunkFreeNode *node;
char *block;
if (memchunk->free) {
block = (char *) (node = memchunk->free);
node->atoms--;
if (node->atoms > 0)
return (void *) (block + (node->atoms * memchunk->atomsize));
memchunk->free = node->next;
return (void *) block;
} else {
block = g_malloc (memchunk->blocksize);
g_ptr_array_add (memchunk->blocks, block);
node = (MemChunkFreeNode *) (block + memchunk->atomsize);
node->next = NULL;
node->atoms = memchunk->atomcount - 1;
memchunk->free = node;
return (void *) block;
}
}
void *
memchunk_alloc0 (MemChunk *memchunk)
{
void *mem;
mem = memchunk_alloc (memchunk);
memset (mem, 0, memchunk->atomsize);
return mem;
}
void
memchunk_free (MemChunk *memchunk, void *mem)
{
MemChunkFreeNode *node;
node = (MemChunkFreeNode *) mem;
node->next = memchunk->free;
node->atoms = 1;
memchunk->free = node;
/* yuck, this'll be slow... */
if (memchunk->autoclean)
memchunk_clean (memchunk);
}
void
memchunk_reset (MemChunk *memchunk)
{
MemChunkFreeNode *node, *next = NULL;
int i;
for (i = 0; i < memchunk->blocks->len; i++) {
node = memchunk->blocks->pdata[i];
node->atoms = memchunk->atomcount;
node->next = next;
next = node;
}
memchunk->free = next;
}
void
memchunk_clean (MemChunk *memchunk)
{
MemChunkNodeInfo *info, *next = NULL;
MemChunkFreeNode *node;
GTree *tree;
int i;
node = memchunk->free;
if (memchunk->blocks->len == 0 || node == NULL)
return;
tree = g_tree_new ((GCompareFunc) tree_compare);
for (i = 0; i < memchunk->blocks->len; i++) {
info = g_alloca (sizeof (MemChunkNodeInfo));
info->next = next;
info->block = memchunk->blocks->pdata[i];
info->blocksize = memchunk->blocksize;
info->atoms = 0;
next = info;
g_tree_insert (tree, info, info);
}
while (node) {
info = g_tree_search (tree, (GSearchFunc) tree_search, node);
if (info)
info->atoms += node->atoms;
node = node->next;
}
info = next;
while (info) {
if (info->atoms == memchunk->atomcount) {
MemChunkFreeNode *prev = NULL;
node = memchunk->free;
while (node) {
if (tree_search (info, (void *) node) == 0) {
/* prune this node from our free-node list */
if (prev)
prev->next = node->next;
else
memchunk->free = node->next;
} else {
prev = node;
}
node = node->next;
}
/* free the block */
g_ptr_array_remove_fast (memchunk->blocks, info->block);
g_free (info->block);
}
info = info->next;
}
g_tree_destroy (tree);
}
void
memchunk_destroy (MemChunk *memchunk)
{
int i;
for (i = 0; i < memchunk->blocks->len; i++)
g_free (memchunk->blocks->pdata[i]);
g_ptr_array_free (memchunk->blocks, TRUE);
g_free (memchunk);
}
syntax highlighted by Code2HTML, v. 0.9.1