/**
* \file This holds all stufff related our memory managent.
* I try the best as far as I can to reduce memory fragmentation
* and unneccessary calls to alloc and free.
*
* To achive this I try an approach described here as "Quick Fit".
* http://www.flounder.com/memory_allocation.htm
*
* The basic idea is to keep allocated memory segments and don't free
* them again. Instead I will put them in a tree indexed by their size.
* To get new memory I first have a look in the tree if there is
* a fitting memory segment. Fitting mean, larger or exactly the size
* I need. If there is one, use it. If not create a new one using
* usual malloc approach.
* I won't split the reagions at all because most likely they will be
* free soon again. This way I might waste some memory, so I have to
* keep an eye on this.
*
* Right now I don't build an upper limit for allocation. The limit
* still is the system memory itself.
*
* This is not implemented as a class because it will be used in the
* process of object creation.
*
* \author Georg Hopp
*
* \copyright
* Copyright © 2012 Georg Hopp
*
* 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 3 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, see .
*/
#define _GNU_SOURCE
#include
#include
#include
#include "utils/memory.h"
struct memSegment {
size_t size;
void * ptr;
};
void * segments = NULL;
/**
* this will interpret any memory segment that is not smaller
* than the expected size as fitting.
*
* @param void * size_ptr a pointer to a size_t value searched for
* @param void * subject a pointer to the currently analysed tree element
*/
static
int
segmentFindCmp(const void * size_ptr, const void * subject)
{
if (*(size_t *)size_ptr > ((struct memSegment *)subject)->size)
return 1;
return 0;
}
/**
* this returns exact fits....uhh.....I can't relate solely on
* the size argument as then same sized segments will never
* be stored.
* Maybe a tree is not the best data structure to use to store
* these.
* Anyway, right now take the ptr into account if size if equal.
*/
static
int
segmentSearchCmp(const void * search, const void * subject)
{
size_t idx =
((struct memSegment *)search)->size -
((struct memSegment *)subject)->size;
if (0 == idx) {
return
((struct memSegment *)search)->ptr -
((struct memSegment *)subject)->ptr;
}
return idx;
}
static
void
segmentFree(void * segment)
{
free(segment);
}
void *
memMalloc(size_t size)
{
struct memSegment ** seg_ptr = tfind(&size, &segments, segmentFindCmp);
struct memSegment * seg;
if (NULL == seg_ptr) {
seg = (struct memSegment *)malloc(sizeof(struct memSegment) + size);
seg->size = size;
seg->ptr = (void *)seg + sizeof(struct memSegment);
} else {
seg = *seg_ptr;
// remove the found one from the tree as we use it now.
tdelete((void *)seg, &segments, segmentSearchCmp);
}
return seg->ptr;
}
/**
* this is a really memory wasting solution....just to be able to
* use calloc, which might be faster then malloc/memset solution.
*
* Maybe this is a bad idea, as we need to memset the buffer anyway
* if it comes from our tree, which hopefully should be the majority
* of cases.
*/
void *
memCalloc(size_t nmemb, size_t size)
{
size_t _size = nmemb * size;
void * mem = memMalloc(_size);
memset(mem, 0, _size);
return mem;
}
void
memFree(void ** mem)
{
if (NULL != *mem) {
tsearch(*mem - sizeof(struct memSegment), &segments, segmentSearchCmp);
*mem = NULL;
}
}
void
memCleanup()
{
tdestroy(segments, segmentFree);
}
void
ffree(void ** data)
{
if (NULL != *data) {
free(*data);
*data = NULL;
}
}
// vim: set ts=4 sw=4: