bond/lib/salloc.c

223 lines
7.5 KiB
C

/* Copyright 2016 secXsQuared
* Distributed under GPL license
* See COPYING under root for details
*/
#include "type.h"
#include "lib/sxtdlib.h"
typedef union
{
uint32_t size;
uint32_t flags;
} _salloc_header;
#define ALLOC_FLAG_NUM 2
#define ALLOC_HEADER_FLAG_FREE 0
#define ALLOC_HEADER_FLAG_LAST 1
static void lbp_set_salloc_header_size(_salloc_header *header, uint32_t size)
{
// align the integer, ignoring overflowed bits
size <<= ALLOC_FLAG_NUM;
// clear ALLOC_FLAG_NUM-th to 31-th bits
header->size &= ~(uint32_t) lb_bit_field_mask(ALLOC_FLAG_NUM, 31);
// set bits
header->size |= size;
return;
}
static uint32_t lbp_read_salloc_header_size(_salloc_header *header)
{
return header->size >> ALLOC_FLAG_NUM;
}
static uint32_t lbp_read_salloc_header_flag(_salloc_header *header, uint32_t bit)
{
return (header->flags & (uint32_t) lb_bit_mask(bit)) == 0 ? 0 : 1;
}
static void lbp_set_salloc_header_flag(_salloc_header *header, uint32_t bit, uint32_t value)
{
value &= (uint32_t) lb_bit_mask(0);
if (value == 1)
{
header->flags |= (uint32_t) lb_bit_mask(bit);
}
else
{
header->flags &= ~(uint32_t) lb_bit_mask(bit);
}
return;
}
static void lbp_salloc_join(void *base)
{
if (base != NULL)
{
char *c_ptr = (char *) base;
while (1)
{
uint32_t c_blk_free = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE);
uint32_t c_blk_last = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_LAST);
uint32_t c_blk_size = lbp_read_salloc_header_size((_salloc_header *) c_ptr);
char *n_ptr = c_blk_last == 1 ? NULL : c_ptr + c_blk_size;
if (n_ptr != NULL && c_blk_free == 1)
{
// if this is not the last block and the prev block is free
uint32_t n_blk_free = lbp_read_salloc_header_flag((_salloc_header *) n_ptr, ALLOC_HEADER_FLAG_FREE);
uint32_t n_blk_last = lbp_read_salloc_header_flag((_salloc_header *) n_ptr, ALLOC_HEADER_FLAG_LAST);
uint32_t n_blk_size = lbp_read_salloc_header_size((_salloc_header *) n_ptr);
if (n_blk_free == 1)
{
// logically gone
lbp_set_salloc_header_size((_salloc_header *) c_ptr, n_blk_size + c_blk_size);
lbp_set_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_LAST, n_blk_last);
continue;
}
}
// update the c_ptr
if (c_blk_last == 0)
{
c_ptr += c_blk_size;
}
else
{
break;
}
}
}
}
bool KABI lb_salloc_assert(void *base, uint32_t *blk_size, bool *blk_free, uint32_t size)
{
if (base == NULL || blk_free == NULL || blk_size == NULL)
{
return NULL;
}
uint32_t i = 0;
char *c_ptr = (char *) base;
while (1)
{
uint32_t cur_blk_free = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE);
uint32_t cur_blk_last = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_LAST);
uint32_t cur_blk_size = lbp_read_salloc_header_size((_salloc_header *) c_ptr);
if (cur_blk_free != blk_free[i] || cur_blk_size != blk_size[i])
{
return false;
}
else
{
c_ptr += cur_blk_size;
i++;
}
if (cur_blk_last == 1)
{
return i == size;
}
}
}
void KABI lb_salloc_init(void *base, uint32_t size)
{
if (base != NULL && size >= sizeof(_salloc_header))
{
_salloc_header *ptr = (_salloc_header *) base;
lbp_set_salloc_header_size(ptr, size);
lbp_set_salloc_header_flag(ptr, ALLOC_HEADER_FLAG_FREE, 1);
lbp_set_salloc_header_flag(ptr, ALLOC_HEADER_FLAG_LAST, 1);
}
return;
}
void *KABI lb_salloc(void *base, uint32_t size)
{
void *result = NULL;
if (base != NULL && size != 0)
{
uint32_t total_size = size + sizeof(_salloc_header);
char *c_ptr = (char *) base;
while (1)
{
uint32_t cur_blk_free = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE);
uint32_t cur_blk_size = lbp_read_salloc_header_size((_salloc_header *) c_ptr);
uint32_t cur_blk_last = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_LAST);
if (cur_blk_free == 0 || cur_blk_size < total_size)
{
//if cur block not a free block
//or the current block size is less than the size we want
if (cur_blk_last == 1)
{
//if last one, break and fail.
break;
}
else
{
c_ptr += cur_blk_size;
}
}
else
{
// we have a free block with enough size
if (total_size == cur_blk_size ||
cur_blk_size - total_size < sizeof(_salloc_header))
{
// since the space left is not enough for salloc_header
// we alloc the whole block
lbp_set_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE, 0);
}
else
{
// we split the block here
// set properties for the first block
lbp_set_salloc_header_size((_salloc_header *) c_ptr, total_size);
lbp_set_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_LAST, 0);
lbp_set_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE, 0);
// set properties for the second block
lbp_set_salloc_header_size((_salloc_header *) (c_ptr + total_size), cur_blk_size - total_size);
lbp_set_salloc_header_flag((_salloc_header *) (c_ptr + total_size), ALLOC_HEADER_FLAG_LAST,
cur_blk_last);
lbp_set_salloc_header_flag((_salloc_header *) (c_ptr + total_size), ALLOC_HEADER_FLAG_FREE, 1);
}
// return the pointer, skip the alloc header
result = c_ptr + sizeof(_salloc_header);
break;
}
}
}
return result;
}
void KABI lb_sfree(void *base, void *ptr)
{
if (base != NULL && ptr != NULL)
{
char *c_ptr = (char *) base;
while (1)
{
uint32_t cur_blk_free = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE);
uint32_t cur_blk_last = lbp_read_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_LAST);
uint32_t cur_blk_size = lbp_read_salloc_header_size((_salloc_header *) c_ptr);
if (cur_blk_free == 0 && ptr == c_ptr + sizeof(_salloc_header))
{
// we found the block, mark it as free
lbp_set_salloc_header_flag((_salloc_header *) c_ptr, ALLOC_HEADER_FLAG_FREE, 1);
// merge blocks
lbp_salloc_join(base);
break;
}
if (cur_blk_last == 1)
{
break;
}
else
{
c_ptr += cur_blk_size;
}
}
}
return;
}