Code:
typedef struct foo_addr {
size_t size;
void *page, *data;
struct foo_addr *next, *prev;
} foo_addr_t;
typedef struct foo_page {
size_t cap;
uchar *mem;
struct foo_addr * one;
} foo_page_t;
size_t pages_size = 0, pages_want = 0;
size_t pages_used = 0, pages_upto = 0, pages_node = 0;
foo_page_t* pages;
int foo_find_addr_in_pages( void *ptr, foo_addr_t **dst ) {
size_t i = 0;
foo_page_t *page;
foo_addr_t *addr;
if ( !dst ) return EDESTADDRREQ;
if ( ptr ) {
for ( i = 0; i < pages_used; ++i ) {
page = &(pages[i]);
for ( addr = page->one;
addr->next; addr = addr->next ) {
if ( addr->data == ptr ) {
*dst = addr;
return EXIT_SUCCESS;
}
}
}
return EINVAL;
}
return EXIT_FAILURE;
}
void foo_new_page_addr(
foo_page_t *page, foo_addr_t *addr, foo_addr_t *next, size_t want ) {
addr->size = want;
addr->page = page->mem;
addr->data = (addr + sizeof(foo_addr_t));
if ( next ) addr->next = next;
next = (foo_addr_t*)((uintptr_t)(addr->data) + want);
if ( addr->next ) next->next = addr->next;
addr->next = next;
next->prev = addr;
next->size = sysconf(_SC_PAGESIZE) -
(uintptr_t)(next + sizeof(foo_addr_t));
}
int foo_find_memory( foo_page_t **base,
foo_addr_t **dst, foo_addr_t **nxt, size_t want ) {
foo_page_t *page = NULL;
foo_addr_t *next = NULL, *addr = NULL;
size_t p, size = 0;
for ( p = 0; p < pages_used; ++p ) {
page = &(pages[p]);
addr = page->one;
while ( addr ) {
if ( addr->data ) continue;
next = addr;
size = 0;
while ( size < want ) {
if ( next->data ) break;
size += next->size;
next = next->next;
if ( !next ) break;
}
if ( size >= want ) {
*base = page;
*dst = addr;
*nxt = next;
return EXIT_SUCCESS;
}
if ( !next ) break;
addr = next->next;
}
}
if ( pages_used == pages_upto ) {
pages_want = sizeof(foo_page_t) * (pages_used + 100);
page = (foo_page_t*)realloc((void*)pages, pages_want);
if ( !page ) return ENOMEM;
pages = page;
pages_upto += 100;
pages_size = pages_want;
}
page = &(pages[p]);
page->cap = sysconf(_SC_PAGESIZE);
page->mem = (uchar*)mmap( NULL, page->cap,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, -1, -1);
if ( !(page->mem) ) return errno;
++pages_used;
addr = page->one = (foo_addr_t*)page->mem;
addr->size = sysconf(_SC_PAGESIZE) - sizeof(foo_addr_t); // here is where it segfaults
*base = page;
*dst = addr;
*nxt = NULL;
return EXIT_FAILURE;
}
int foo_size_exemem( void **dst, size_t want ) {
foo_page_t *page = NULL;
foo_addr_t *addr = NULL, *next, *current;
size_t i = 0;
if ( !dst ) return EDESTADDRREQ;
if ( !want ) {
if ((errno = foo_find_addr_in_pages( *dst, &addr ))
!= EXIT_SUCCESS)
return errno;
*dst = NULL;
addr->data = NULL;
return EXIT_SUCCESS;
}
if ( *dst ) {
if ((errno = foo_find_addr_in_pages(*dst,¤t))
!= EXIT_SUCCESS )
return errno;
if ( want < current->size ) {
foo_new_page_addr( page, current, NULL, want );
return EXIT_SUCCESS;
}
if ( want == current->size ) return EXIT_SUCCESS;
i = 0;
addr = current;
while ( i < want ) {
if ( !addr || addr->data ) break;
i += addr->size;
addr = addr->next;
if ( !addr ) break;
}
if ( i >= want ) {
foo_new_page_addr( page, current, addr, want );
return EXIT_SUCCESS;
}
if ( (errno = foo_find_memory( &page, &addr, &next, want ))
!= EXIT_SUCCESS )
return errno;
foo_new_page_addr( page, addr, next, want );
memcpy( current->data, addr->data, current->size );
*dst = addr->data;
current->data = NULL;
return EXIT_SUCCESS;
}
if ( (errno = foo_find_memory( &page, &addr, &next, want ))
!= EXIT_SUCCESS )
return errno;
foo_new_page_addr( page, addr, next, want );
*dst = addr->data;
return EXIT_SUCCESS;
}
void foo_free_exemem() {
size_t i;
for ( i = 0; i < pages_used; ++i )
munmap( pages[i].mem, sysconf(_SC_PAGESIZE) );
free(pages);
pages = NULL;
pages_used = pages_upto = 0;
pages_size = pages_want = 0;
}
void *foo_alloc( void *ud, void *ptr, size_t size, size_t want ) {
uchar *tmp = NULL;
(void)ud;
errno = EXIT_SUCCESS;
if ( want == size ) return ptr;
if ( !want ) {
if ( ptr ) foo_size_exemem( &ptr, 0 );
return NULL;
}
tmp = ptr;
if ( (errno = foo_size_exemem( (void**)(&tmp), want ))
!= EXIT_SUCCESS )
goto nomem;
if ( !ptr ) memset( tmp, 0, want );
if ( size < want ) memset( &tmp[size], 0, want - size );
return tmp;
nomem:
if ( errno == EXIT_SUCCESS ) errno = ENOMEM;
return NULL;
}