/
/ File: atomizer.c
/
/ Description: Kernel module implementing kernel-space atomizer API
/
/ Copyright 1999, Be Incorporated, All Rights Reserved.
/ This file may be used under the terms of the Be Sample Code License.
/
*******************************************************************************/
#include <Drivers.h>
#include <KernelExport.h>
#include <string.h>
#include <stdlib.h>
#include <atomizer.h>
#if DEBUG > 0
#define ddprintf(x) dprintf x
#else
#define ddprintf(x)
#endif
typedef struct {
sem_id sem;
int32 ben;
} benaphore;
#define INIT_BEN(x) x.sem = create_sem(0, "an atomizer benaphore"); x.ben = 0;
#define ACQUIRE_BEN(x) if((atomic_add(&(x.ben), 1)) >= 1) acquire_sem(x.sem);
#define ACQUIRE_BEN_ON_ERROR(x, y) if((atomic_add(&(x.ben), 1)) >= 1) if (acquire_sem(x.sem) != B_OK) y;
#define RELEASE_BEN(x) if((atomic_add(&(x.ben), -1)) > 1) release_sem(x.sem);
#define DELETE_BEN(x) delete_sem(x.sem); x.sem = -1; x.ben = 1;
#define MIN_BLOCK_SIZE B_PAGE_SIZE
typedef struct block_list block_list;
struct block_list{
block_list *next;
uint8 *free_space;
uint32 free_bytes;
};
typedef struct {
benaphore lock;
block_list *blocks;
uint32 count;
uint32 slots;
uint8 **vector;
} atomizer_data;
typedef struct atomizer_data_list atomizer_data_list;
struct atomizer_data_list {
char *name;
atomizer_data *the_atomizer;
atomizer_data_list *next;
};
const char atomizer_module_name[] = B_ATOMIZER_MODULE_NAME;
const char system_atomizer_name[] = B_SYSTEM_ATOMIZER_NAME;
static benaphore module_lock;
static atomizer_data_list *atomizer_list;
static atomizer_data *
make_atomizer(void) {
atomizer_data *ad;
ad = (atomizer_data *)malloc(sizeof(atomizer_data));
if (ad) {
INIT_BEN(ad->lock);
if (ad->lock.sem >= 0) {
ad->blocks = (block_list *)malloc(MIN_BLOCK_SIZE);
if (ad->blocks) {
ad->vector = (uint8 **)malloc(MIN_BLOCK_SIZE);
if (ad->vector) {
ad->blocks->next = 0;
ad->blocks->free_space = ((uint8 *)ad->blocks)+sizeof(block_list);
ad->blocks->free_bytes = MIN_BLOCK_SIZE - sizeof(block_list);
ad->count = 0;
ad->slots = MIN_BLOCK_SIZE / sizeof(uint8 *);
goto exit0;
}
free(ad->blocks);
}
DELETE_BEN(ad->lock);
}
free(ad);
ad = 0;
}
exit0:
ddprintf(("make_atomizer() returns %p\n", ad));
return ad;
}
static void
free_atomizer(atomizer_data *ad) {
block_list *bl, *blnext;
the data than corrupt the kernel heap */
ACQUIRE_BEN_ON_ERROR(ad->lock, return);
bl = ad->blocks;
while (bl) {
blnext = bl->next;
free(bl);
bl = blnext;
}
free(ad->vector);
DELETE_BEN(ad->lock);
free(ad);
}
static const void *
find_or_make_atomizer(const char *string) {
atomizer_data_list *adl = atomizer_list;
atomizer_data_list *last = 0;
void * at = (void *)0;
if (!string || (strlen(string) == 0)) string = system_atomizer_name;
ACQUIRE_BEN_ON_ERROR(module_lock, return at);
ddprintf(("success locking module\n"));
while (adl && strcmp(adl->name, string)) {
last = adl;
adl = adl->next;
}
if (adl) {
at = (void *)adl->the_atomizer;
ddprintf(("asked for %s, returning %p\n", string, at));
goto exit0;
}
ddprintf(("didn't find atomizer %s, making it\n", string));
adl = (atomizer_data_list *)malloc(sizeof(atomizer_data_list));
if (adl) {
adl->the_atomizer = make_atomizer();
if (adl->the_atomizer) {
adl->name = strdup(string);
if (adl->name) {
if (last) last->next = adl;
else atomizer_list = adl;
adl->next = 0;
at = (void *)adl->the_atomizer;
goto exit0;
}
free_atomizer(adl->the_atomizer);
}
free(adl);
}
exit0:
RELEASE_BEN(module_lock);
ddprintf(("find_or_make_atomizer() returning %p\n", at));
return at;
}
static status_t
init()
{
ddprintf((B_ATOMIZER_MODULE_NAME": init()\n"));
INIT_BEN(module_lock);
if (module_lock.sem >= 0) {
atomizer_list = 0;
if (find_or_make_atomizer(0))
return B_OK;
DELETE_BEN(module_lock);
module_lock.sem = B_ERROR;
}
return module_lock.sem;
}
static status_t
uninit()
{
atomizer_data_list *adl;
If it fails, all hell as broken loose, but we won't contribute
by corrupting the heap. */
ddprintf((B_ATOMIZER_MODULE_NAME": uninit()\n"));
ACQUIRE_BEN_ON_ERROR(module_lock, return B_ERROR);
if (atomizer_list->next) {
ddprintf((B_ATOMIZER_MODULE_NAME": uninit called with non-system atomizers still active!\n"));
}
be the system atomizer left */
adl = atomizer_list;
while (adl) {
free_atomizer(adl->the_atomizer);
free(adl->name);
adl = adl->next;
}
DELETE_BEN(module_lock);
return B_OK;
}
static status_t
std_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
return init();
case B_MODULE_UNINIT:
return uninit();
default:
;
}
return -1;
}
static status_t
delete_atomizer(const void * at) {
atomizer_data_list *adl;
atomizer_data_list *last = 0;
status_t result = B_ERROR;
ACQUIRE_BEN_ON_ERROR(module_lock, return B_ERROR);
adl = atomizer_list;
while (adl && (adl->the_atomizer != (atomizer_data *)at)) {
last = adl;
adl = adl->next;
}
if (adl) {
if (strcmp(adl->name, system_atomizer_name) != 0) {
free_atomizer(adl->the_atomizer);
free(adl->name);
last->next = adl->next;
free(adl);
result = B_OK;
}
}
RELEASE_BEN(module_lock);
return result;
}
static const void *
atomize(const void * at, const char *string, int create) {
atomizer_data_list *adl;
void *result = 0;
uint32 len;
if (!string || !(len = strlen(string))) return 0;
len++;
ACQUIRE_BEN_ON_ERROR(module_lock, return 0);
if (at == ((void *)(-1))) at = atomizer_list->the_atomizer;
adl = atomizer_list;
while (adl && (adl->the_atomizer != (atomizer_data *)at)) {
adl = adl->next;
}
if (adl) ACQUIRE_BEN(adl->the_atomizer->lock);
RELEASE_BEN(module_lock);
if (adl) {
atomizer_data *ad = adl->the_atomizer;
uint8 **vector = ad->vector;
uint32 count = ad->count;
uint32 low = 0;
uint32 high = count;
uint32 index = 0;
int test = -1;
while (low < high) {
index = (low + high) / 2;
test = strcmp(string, (const char *)vector[index]);
if (test < 0)
high = index;
else if (test > 0)
low = ++index;
else
break;
}
if (test == 0) {
result = vector[index];
} else {
if (create) {
block_list *bl = ad->blocks;
while (bl->next) {
if (bl->free_bytes >= len) break;
bl = bl->next;
}
if (bl->free_bytes < len) {
size_t block_size = ((sizeof(block_list)+len+(MIN_BLOCK_SIZE-1)) & ~(MIN_BLOCK_SIZE-1));
bl->next = (block_list *)malloc(block_size);
if (!bl->next) {
RELEASE_BEN(ad->lock);
return 0;
}
bl = bl->next;
bl->free_space = (uint8 *)(bl+1);
bl->free_bytes = block_size - sizeof(block_list);
}
result = bl->free_space;
memcpy(result, string, len);
bl->free_space += len;
bl->free_bytes -= len;
if (ad->count == ad->slots) {
size_t newslots = ad->slots + (MIN_BLOCK_SIZE / sizeof(uint8 *));
uint8 **newvec = realloc(vector, newslots * sizeof(uint8 *));
if (!newvec) {
RELEASE_BEN(ad->lock);
return 0;
}
ad->slots = newslots;
vector = ad->vector = newvec;
}
memmove(vector + index + 1, vector + index, (count - index) * sizeof(uint8 *));
vector[index] = result;
ad->count++;
}
}
RELEASE_BEN(ad->lock);
}
return result;
}
static const char *
string_for_token(const void *atomizer, const void *atom) {
return atom;
}
static status_t
get_next_atomizer_info(void **cookie, atomizer_info *info) {
atomizer_data_list *adl;
atomizer_data_list *atomizer = (atomizer_data_list *)*cookie;
status_t result = B_ERROR;
ddprintf(("get_next_atomizer_info(cookie: %p, info: %p)\n", *cookie, info));
if (!info) return result;
ACQUIRE_BEN_ON_ERROR(module_lock, return result);
if (!atomizer) atomizer = atomizer_list;
adl = atomizer_list;
while (adl && (adl != atomizer))
adl = adl->next;
if (adl) {
*cookie = atomizer->next;
if (!*cookie) *cookie = (void *)-1;
info->atomizer = atomizer->the_atomizer;
strncpy(info->name, adl->name, B_OS_NAME_LENGTH);
info->name[B_OS_NAME_LENGTH - 1] = '\0';
info->atom_count = atomizer->the_atomizer->count;
result = B_OK;
}
RELEASE_BEN(module_lock);
return result;
}
static const void *
get_next_atom(const void *_atomizer, uint32 *cookie) {
atomizer_data_list *adl;
atomizer_data *atomizer = (atomizer_data *)_atomizer;
void *result = 0;
ACQUIRE_BEN_ON_ERROR(module_lock, return 0);
adl = atomizer_list;
while (adl && (adl->the_atomizer != atomizer))
adl = adl->next;
if (adl) ACQUIRE_BEN(atomizer->lock);
RELEASE_BEN(module_lock);
if (adl) {
if (*cookie < atomizer->count) {
result = atomizer->vector[*cookie];
(*cookie)++;
}
RELEASE_BEN(atomizer->lock);
}
return result;
}
static atomizer_module_info atomizer = {
{
atomizer_module_name,
0,
std_ops
},
find_or_make_atomizer,
delete_atomizer,
atomize,
string_for_token,
get_next_atomizer_info,
get_next_atom
};
_EXPORT atomizer_module_info *modules[] = {
&atomizer,
NULL
};