efl/legacy/eina/src/modules/mm_policies/ememoa.c

831 lines
21 KiB
C

#include "Eina.h"
#include "eina_private.h"
#include "mempool_struct.h"
/*============================================================================*
* Local *
*============================================================================*/
#define EMEMOA_MAGIC 0xDEAD5007
#ifdef DEBUG
#define EMEMOA_CHECK_MAGIC(Memory) \
assert(Memory->magic == EMEMOA_MAGIC);
#else
#define EMEMOA_CHECK_MAGIC(Memory) ;
#endif
static int total = 0;
/**
* @defgroup Ememoa_Mempool_Base_64m Static buffer allocator.
*
*/
/**
* Global context for the static buffer allocator.
* @ingroup Ememoa_Mempool_Base_64m
* TODO this context is mempool relative
*/
//static struct ememoa_memory_base_s *base_64m = NULL;
/**
* Remove an item from the base_64m free block list.
*
* @param index Item to be removed.
* @return Will never fail.
* @ingroup Ememoa_Mempool_Base_64m
*/
static void ememoa_memory_base_remove_from_list(struct ememoa_memory_base_s *base_64m, uint16_t index)
{
uint16_t prev = base_64m->chunks[index].prev;
uint16_t next = base_64m->chunks[index].next;
if (prev != 0xFFFF)
base_64m->chunks[prev].next = next;
if (next != 0xFFFF)
base_64m->chunks[next].prev = prev;
if (base_64m->start == index)
base_64m->start = next;
if (base_64m->over == index)
base_64m->over = prev;
base_64m->chunks[index].prev = 0xFFFF;
base_64m->chunks[index].next = 0xFFFF;
}
/**
* Insert an item in the base_64m free block list.
*
* @param index Item to be inserted.
* @return Will break completely if the item is already in the list.
* @ingroup Ememoa_Mempool_Base_64m
*/
static void ememoa_memory_base_insert_in_list(struct ememoa_memory_base_s *base_64m, uint16_t index)
{
uint16_t length = base_64m->chunks[index].length;
uint16_t prev = 0xFFFF;
uint16_t next;
if (base_64m->chunks[index].start == 0xFFFF)
return;
for (next = base_64m->start; next != 0xFFFF
&& base_64m->chunks[next].length > length; next
= base_64m->chunks[next].next)
prev = next;
assert(index != next);
assert(index != prev);
base_64m->chunks[index].next = next;
base_64m->chunks[index].prev = prev;
if (next != 0xFFFF)
base_64m->chunks[next].prev = index;
else
base_64m->over = index;
if (prev != 0xFFFF)
base_64m->chunks[prev].next = index;
else
base_64m->start = index;
}
/**
* Merge two chunk of memory together. Choose the index of the resulting
* chunk as the one requiring less effort for committing the change. No
* requirement on parameters order or any other characteristic exist.
*
* @param one First part of the chunk to be merged.
* @param two Second part of the chunk to be merged.
* @return Index of the new chunk.
* @ingroup Ememoa_Mempool_Base_64m
*/
static uint16_t ememoa_memory_base_merge_64m(struct ememoa_memory_base_s *base_64m, uint16_t one, uint16_t two)
{
uint16_t index;
uint16_t tmp;
if (base_64m->chunks[one].length < base_64m->chunks[two].length)
{
tmp = one;
one = two;
two = tmp;
}
/* All page refering to 'two' now refere to 'one'. */
for (index = base_64m->chunks[two].start; index
!= base_64m->chunks[two].end; ++index)
base_64m->pages[index] = one;
base_64m->pages[index] = one;
if (base_64m->chunks[one].start < base_64m->chunks[two].start)
base_64m->chunks[one].end = base_64m->chunks[two].end;
else
base_64m->chunks[one].start = base_64m->chunks[two].start;
base_64m->chunks[one].length += base_64m->chunks[two].length;
base_64m->chunks[two].start = 0xFFFF;
base_64m->chunks[two].use = 0;
if (base_64m->jump > two)
base_64m->jump = two;
return one;
}
/**
* Split a chunk in two, allocating a new one on the fly and doing as few as possible
* memory update. The new allocated chunk could be used as the left or right part of
* the splitted chunk depending on the fastest strategie. You will need to guess by your
* self what was our choice.
*
* @param index The item to split.
* @param length The required size for one of the two resulting chunk.
* @return 0xFFFF, if the chunk already has the right size otherwise the new allocated chunk.
* @ingroup Ememoa_Mempool_Base_64m
*/
static uint16_t ememoa_memory_base_split_64m(struct ememoa_memory_base_s *base_64m, uint16_t index, unsigned int length)
{
if (base_64m->chunks[index].length != length)
{
struct ememoa_memory_base_chunck_s a;
struct ememoa_memory_base_chunck_s b;
uint16_t i;
uint16_t splitted;
while (!(base_64m->chunks[base_64m->jump].start == 0xFFFF
&& base_64m->chunks[base_64m->jump].prev
== 0xFFFF
&& base_64m->chunks[base_64m->jump].next
== 0xFFFF))
base_64m->jump++;
splitted = base_64m->jump++;
ememoa_memory_base_remove_from_list(base_64m, index);
a = base_64m->chunks[index];
b.length = a.length - length;
b.end = a.end;
b.start = a.start + length;
b.next = 0xFFFF;
b.prev = 0xFFFF;
b.use = 0;
a.length = length;
a.end = a.start + length - 1;
a.use = 1;
if (a.length < b.length)
{
base_64m->chunks[index] = b;
base_64m->chunks[splitted] = a;
ememoa_memory_base_insert_in_list(base_64m, index);
} else
{
base_64m->chunks[index] = a;
base_64m->chunks[splitted] = b;
ememoa_memory_base_insert_in_list(base_64m, splitted);
}
for (i = base_64m->chunks[splitted].start; i
!= base_64m->chunks[splitted].end; ++i)
base_64m->pages[i] = splitted;
base_64m->pages[i] = splitted;
return splitted;
}
return 0xFFFF;
}
/**
* @defgroup Ememoa_Mempool_Base_Resize_List Function enabling manipulation of array with linked list properties.
*
*/
#define RESIZE_POOL_SIZE 128
struct ememoa_memory_base_resize_list_pool_s {
struct ememoa_memory_base_resize_list_pool_s *next;
unsigned int count;
#ifdef USE64
uint64_t map[2];
#else
uint32_t map[4];
#endif
struct ememoa_memory_base_resize_list_s array[RESIZE_POOL_SIZE];
};
static struct ememoa_memory_base_resize_list_pool_s *resize_pool = NULL;
#if 0
/**
* Allocate a new resizable list (it's an array now). It currently use to much memory
* when using the base_64m allocator, it could be fixed if really usefull (Not high priority at this time).
*
* @param size items size inside the list.
* @return Will return a pointer to the base array.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
struct ememoa_memory_base_resize_list_s* ememoa_memory_base_resize_list_new(
unsigned int size)
{
struct ememoa_memory_base_resize_list_s *tmp;
struct ememoa_memory_base_resize_list_pool_s *over;
unsigned int i;
int pos;
if (size == 0)
return NULL;
for (over = resize_pool; over && over->count == RESIZE_POOL_SIZE; over
= over->next)
;
if (!over)
{
over
= ememoa_memory_base_alloc(sizeof(struct ememoa_memory_base_resize_list_pool_s));
if (!over)
return NULL;
over->next = resize_pool;
over->count = 0;
#ifdef USE64
for (i = 0; i < 2; ++i)
#else
for (i = 0; i < 4; ++i)
#endif
over->map[i] = ~0;
resize_pool = over;
}
#ifdef USE64
for (i = 0, pos = 0; i < 2 && pos == 0; ++i)
#else
for (i = 0, pos = 0; i < 4 && pos == 0; ++i)
#endif
#ifdef USE64
pos = ffsll (over->map[i]);
#else
pos = ffs(over->map[i]);
#endif
over->count++;
pos--;
i--;
#ifdef USE64
assert(i >= 0 && i < 2);
assert(pos >= 0 && pos < 64);
tmp = over->array + pos + i * 64;
#else
assert(i >= 0 && i < 4);
assert(pos >= 0 && pos < 32);
tmp = over->array + pos + i * 32;
#endif
over->map[i] &= ~(1 << pos);
bzero(tmp, sizeof(struct ememoa_memory_base_resize_list_s));
tmp->size = size;
#ifdef DEBUG
tmp->magic = EMEMOA_MAGIC;
#endif
return tmp;
}
#endif
/**
* Clean a list and all it's item.
*
* @param base List
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
void ememoa_memory_base_resize_list_clean(
struct ememoa_memory_base_resize_list_s* base)
{
struct ememoa_memory_base_resize_list_pool_s *over;
int index;
if (!base)
return;
EMEMOA_CHECK_MAGIC(base);
ememoa_memory_base_free(base->pool);
ememoa_memory_base_free(base->bitmap);
#ifdef DEBUG
if (base->actif != 0)
fprintf(stderr, "Warning some element where not freed from this list.\n");
#endif
#ifdef DEBUG
base->magic = 0;
#endif
for (over = resize_pool; over && !(over->array <= base && base
< over->array + RESIZE_POOL_SIZE); over = over->next)
;
assert(over != NULL);
index = base - over->array;
#ifdef USE64
over->map[index / 64] |= (1 << (index % 64));
#else
over->map[index / 32] |= (1 << (index % 32));
#endif
}
#if 0
/**
* Allocate a new item in the list "base".
*
* @param base Pointer to a valid and activ list.
* @return Will return the new item index.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
int ememoa_memory_base_resize_list_new_item(
struct ememoa_memory_base_resize_list_s *base)
{
int i;
if (base == NULL)
return -1;
EMEMOA_CHECK_MAGIC(base);
if (base->count < base->actif + 1)
{
unsigned int count;
uint32_t *tmp_bitmap;
void *tmp_pool;
count = base->count + 32;
tmp_pool = ememoa_memory_base_realloc(base->pool, count
* base->size);
if (!tmp_pool)
return -1;
base->pool = tmp_pool;
tmp_bitmap = ememoa_memory_base_realloc(base->bitmap, (count
>> 5) * sizeof(uint32_t));
if (!tmp_bitmap)
return -1;
base->bitmap = tmp_bitmap;
base->bitmap[base->count >> 5] = 0xFFFFFFFF;
#ifdef DEBUG
memset ((uint8_t*) tmp_pool + base->count * base->size, 43, base->size * 32);
#endif
base->count = count;
}
for (; base->jump < (base->count >> 5) && base->bitmap[base->jump] == 0; ++base->jump)
;
assert(base->jump < (base->count >> 5));
i = ffs(base->bitmap[base->jump]) - 1;
assert(i >= 0 && i < 32);
base->bitmap[base->jump] &= ~(1 << i);
base->actif++;
return (base->jump << 5) + i;
}
#endif
/**
* Give the pointer corresponding to an item index.
*
* @param base Pointer to a valid and activ list.
* @param index Item index given by ememoa_memory_base_resize_list_new_item.
* @return Will return a pointer to the item.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
void* ememoa_memory_base_resize_list_get_item(
struct ememoa_memory_base_resize_list_s *base, int index)
{
EMEMOA_CHECK_MAGIC(base);
if (index < 0)
return NULL;
return (void*) ((uint8_t*) base->pool + index * base->size);
}
/**
* Give back an item to the list.
*
* @param base Pointer to a valid and activ list.
* @param index Item index given by ememoa_memory_base_resize_list_new_item.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
void ememoa_memory_base_resize_list_back(
struct ememoa_memory_base_resize_list_s *base, int index)
{
unsigned int shift;
unsigned int i;
EMEMOA_CHECK_MAGIC(base);
if (index < 0)
return;
shift = index >> 5;
i = index & 0x1F;
base->bitmap[shift] |= (1 << i);
base->actif--;
if (shift < base->jump)
base->jump = shift;
#ifdef DEBUG
memset ((uint8_t*) base->pool + base->size * index, 44, base->size);
#endif
}
#if 0
/**
* Make some attempt to resize the size of the list.
*
* @param base Pointer to a valid and activ list.
* @return Will 0 is nothing where freed, -1 if not enought memory
* is available for the operation and anything else if successfull.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
int ememoa_memory_base_resize_list_garbage_collect(
struct ememoa_memory_base_resize_list_s *base)
{
uint32_t *tmp_bitmap;
void *tmp_pool;
unsigned int count;
EMEMOA_CHECK_MAGIC(base);
count = base->count;
for (; base->count > 0 && base->bitmap[(base->count >> 5)] == 0xFFFFFFFF; base->count -= 32)
;
tmp_pool = ememoa_memory_base_realloc(base->pool, base->count
* base->size);
if (!tmp_pool)
return -1;
base->pool = tmp_pool;
tmp_bitmap = ememoa_memory_base_realloc(base->bitmap,
(base->count >> 5) * sizeof(uint32_t));
if (!tmp_bitmap)
return -1;
base->bitmap = tmp_bitmap;
return count != base->count;
}
#endif
/**
* Call fct on all allocated item of the list and return the sum of fct result.
*
* @param base Pointer to a valid and activ list.
* @param start Index to start at.
* @param end Index to end the walk.
* @param fct The callback function.
* @param ctx An obscure pointer that will be directly passed, without any
* check/change to each fct call.
* @return Will return the sum of fct result.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
int ememoa_memory_base_resize_list_walk_over(
struct ememoa_memory_base_resize_list_s *base, int start,
int end, int (*fct)(void *ctx, int index, void *data), void *ctx)
{
int bitmap;
int first;
int end_shift;
int end_i;
int shift;
int i;
int result = 0;
EMEMOA_CHECK_MAGIC(base);
i = start & 0x1F;
if (end < 0)
end = base->count - 1;
if (end == -1)
return 0;
end_shift = end >> 5;
end_i = end & 0x1F;
for (shift = start >> 5; shift < end_shift; ++shift)
{
bitmap = ~base->bitmap[shift];
first = ffs(bitmap) - 1;
i = i > first ? i : first;
for (bitmap >>= i; i < 32; ++i, bitmap >>= 1, ++start)
if (bitmap & 0x1)
result += fct(ctx, start, base->pool + start
* base->size);
i = 0;
}
bitmap = ~base->bitmap[shift];
for (bitmap >>= i; i < end_i; ++i, bitmap >>= 1, ++start)
if (bitmap & 0x1)
result += fct(ctx, start, base->pool + start
* base->size);
return result;
}
/**
* Call fct as long as fct doesn't return a value different from 0.
*
* @param base Pointer to a valid and activ list.
* @param start Index to start at.
* @param end Index to end the walk.
* @param fct The callback function.
* @param ctx An obscure pointer that will be directly passed, without any
* check/change to each fct call.
* @param index If different from NULL, put the index on which we stop in it.
* @return Will return a pointer to the item where we stop the search.
* @ingroup Ememoa_Mempool_Base_Resize_List
*/
void* ememoa_memory_base_resize_list_search_over(
struct ememoa_memory_base_resize_list_s *base, int start,
int end, int (*fct)(void *ctx, int index, void *data), void *ctx,
int *index)
{
int bitmap;
int first;
int end_shift;
int end_i;
int shift;
int i;
EMEMOA_CHECK_MAGIC(base);
i = start & 0x1F;
if (end < 0)
end = base->count - 1;
if (end == -1)
return NULL;
end_shift = end >> 5;
end_i = end & 0x1F;
for (shift = start >> 5; shift < end_shift; ++shift)
{
bitmap = ~base->bitmap[shift];
first = ffs(bitmap) - 1;
i = i > first ? i : first;
for (bitmap >>= i; i < 32; ++i, bitmap >>= 1, ++start)
if (bitmap & 0x1)
if (fct(ctx, start, base->pool + start
* base->size))
goto found;
i = 0;
}
bitmap = ~base->bitmap[shift];
for (bitmap >>= i; i < end_i; ++i, bitmap >>= 1, ++start)
if (bitmap & 0x1)
if (fct(ctx, start, base->pool + start * base->size))
goto found;
if (index)
*index = 0;
return NULL;
found: if (index)
*index = start;
return base->pool + start * base->size;
}
static void ememoa_free(void *data, void *element)
{
struct ememoa_memory_base_s *base_64m = data;
unsigned int delta = element - base_64m->base;
uint16_t index;
uint16_t chunk_index;
uint16_t prev_chunk_index;
uint16_t next_chunk_index;
if (element == NULL)
return;
assert(element > base_64m->base);
index = delta >> 12;
chunk_index = base_64m->pages[index];
total -= base_64m->chunks[chunk_index].length;
#ifdef ALLOC_REPORT
fprintf(stderr, "free %i [%i] => %p\n", base_64m->chunks[chunk_index].length, total << 12, ptr);
#endif
prev_chunk_index = base_64m->pages[index - 1];
next_chunk_index
= base_64m->pages[base_64m->chunks[chunk_index].end + 1];
if (index > 0 && prev_chunk_index < base_64m->chunks_count)
if (base_64m->chunks[prev_chunk_index].use == 0)
{
ememoa_memory_base_remove_from_list(base_64m, prev_chunk_index);
chunk_index = ememoa_memory_base_merge_64m(base_64m, chunk_index,
prev_chunk_index);
}
if (base_64m->chunks[chunk_index].end < base_64m->chunks_count
&& next_chunk_index < base_64m->chunks_count)
if (base_64m->chunks[next_chunk_index].use == 0)
{
ememoa_memory_base_remove_from_list(base_64m, next_chunk_index);
chunk_index = ememoa_memory_base_merge_64m(base_64m, chunk_index,
next_chunk_index);
}
ememoa_memory_base_insert_in_list(base_64m, chunk_index);
base_64m->chunks[chunk_index].use = 0;
}
static void * ememoa_alloc(void *data, unsigned int size)
{
struct ememoa_memory_base_s *base_64m = data;
uint16_t real = (size >> 12) + (size & 0xFFF ? 1 : 0);
uint16_t jump = base_64m->start;
uint16_t prev = 0xFFFF;
while (jump != 0xFFFF && base_64m->chunks[jump].length > real)
{
prev = jump;
jump = base_64m->chunks[jump].next;
}
if (prev != 0xFFFF)
{
uint16_t splitted = ememoa_memory_base_split_64m(base_64m, prev, real);
uint16_t allocated;
uint16_t empty;
/* Guess who is who */
allocated = base_64m->chunks[prev].use == 1 ? prev : splitted;
empty = base_64m->chunks[prev].use == 1 ? splitted : prev;
total += real;
#ifdef ALLOC_REPORT
fprintf(stderr, "alloc %i(%i) [%i] => %p\n", real << 12, size, total << 12, ((uint8_t*) base_64m->base) + (base_64m->chunks[allocated].start << 12));
#endif
return ((uint8_t*) base_64m->base)
+ (base_64m->chunks[allocated].start << 12);
}
return NULL;
}
static void *ememoa_realloc(void *data, void *element, unsigned int size)
{
struct ememoa_memory_base_s *base_64m = data;
void* tmp;
unsigned int delta = element - base_64m->base;
uint16_t real = (size >> 12) + (size & 0xFFF ? 1 : 0);
uint16_t index;
uint16_t chunk_index;
uint16_t next_chunk_index;
if (element == NULL)
return ememoa_alloc(base_64m, size);
assert(element > base_64m->base);
index = delta >> 12;
chunk_index = base_64m->pages[index];
/* FIXME: Not resizing when the size is big enough */
if (real <= base_64m->chunks[chunk_index].length)
return element;
next_chunk_index
= base_64m->pages[base_64m->chunks[chunk_index].end + 1];
if (base_64m->chunks[next_chunk_index].use == 0)
if (real <= base_64m->chunks[next_chunk_index].length
+ base_64m->chunks[chunk_index].length)
{
uint16_t splitted;
uint16_t allocated;
int tmp;
total -= base_64m->chunks[chunk_index].length;
tmp = base_64m->chunks[chunk_index].length;
ememoa_memory_base_remove_from_list(base_64m, next_chunk_index);
chunk_index = ememoa_memory_base_merge_64m(base_64m, chunk_index,
next_chunk_index);
splitted = ememoa_memory_base_split_64m(base_64m, chunk_index,
real);
allocated
= base_64m->chunks[chunk_index].use
== 1 ? chunk_index
: splitted;
ememoa_memory_base_remove_from_list(base_64m, allocated);
total += real;
#ifdef ALLOC_REPORT
fprintf(stderr, "realloc %i(%i) [%i] => %p\n", (real - tmp) << 12, size, total << 12, ((uint8_t*) base_64m->base) + (base_64m->chunks[allocated].start << 12));
#endif
return ((uint8_t*) base_64m->base)
+ (base_64m->chunks[allocated].start
<< 12);
}
tmp = ememoa_alloc(base_64m, size);
if (!tmp)
return NULL;
memcpy(tmp, element, base_64m->chunks[chunk_index].length << 12);
ememoa_memory_base_free_64m(element);
return tmp;
}
static void *ememoa_init(void *buffer, unsigned int size, const char *options, va_list args)
{
struct ememoa_memory_base_s *new_64m = buffer;
unsigned int temp_size;
temp_size = (size - sizeof(struct ememoa_memory_base_s)) >> 12;
if (temp_size <= 1)
{
/* TODO handle an error */
return NULL;
}
#ifdef DEBUG
new_64m->magic = EMEMOA_MAGIC;
#endif
new_64m->chunks
= (struct ememoa_memory_base_chunck_s*) ((struct ememoa_memory_base_s*) new_64m
+ 1);
new_64m->pages
= (uint16_t*)((struct ememoa_memory_base_chunck_s*) new_64m->chunks
+ temp_size + 1);
new_64m->base = ((uint16_t*) new_64m->pages + temp_size + 1);
new_64m->start = 0;
new_64m->chunks_count
= (size - sizeof(struct ememoa_memory_base_s)
- temp_size
* (sizeof(struct ememoa_memory_base_chunck_s)
+ sizeof(uint16_t)))
/ 4096;
memset(new_64m->chunks, 0xFF,
sizeof(struct ememoa_memory_base_chunck_s) * temp_size);
memset(new_64m->pages, 0, sizeof(uint16_t) * temp_size);
new_64m->chunks[0].start = 0;
new_64m->chunks[0].end = new_64m->chunks_count - 1;
new_64m->chunks[0].length = new_64m->chunks_count;
new_64m->chunks[0].next = 0xFFFF;
new_64m->chunks[0].prev = 0xFFFF;
new_64m->chunks[0].use = 0;
new_64m->over = 0;
new_64m->start = 0;
new_64m->jump = 1;
return new_64m;
}
static void ememoa_shutdown(void *data)
{
}
/*============================================================================*
* API *
*============================================================================*/
Eina_Mempool_Backend mp_backend = {
.init = &ememoa_init,
.shutdown = &ememoa_shutdown,
.realloc = &ememoa_realloc,
.alloc = &ememoa_alloc,
.free = &ememoa_free,
};