v4k-git-backup/engine/split/fwk_obj.c

505 lines
14 KiB
C

void *obj_initialize( void **ptr, char *type ) {
unsigned payload = ((unsigned char)type[0]) << 8; type[0] = '\1'; // @fixme: actually use this '\1' custom tag
#ifndef NDEBUG
strcatf(&type, "%s", callstack(+16)); // debug: for debugging purposes only
#endif
ptr[0] = OBJBOX((void *)type, payload);
return &ptr[1];
}
void obj_free( void *obj ) {
void *ptr = *((void**)obj - 1);
FREE( OBJUNBOX(ptr) );
obj = (void**)obj - 1;
FREE( obj );
}
void obj_del( void *obj ) {
unsigned type = obj_typeid(obj);
(type[dtor])(obj);
obj_free( obj );
}
unsigned obj_instances( const void *obj ) {
return OBJPAYLOAD3(obj) + 1;
}
// ---
const char *obj_output( const void *obj ) {
void* ptr = *((void**)obj - 1);
char *str = OBJUNBOX(ptr);
while( *str != '\n' ) ++str;
return (const char *)str + 1;
}
void (obj_printf)( void *obj, const char *text ) {
void* ptr = *((void**)obj - 1);
char* str = OBJUNBOX(ptr);
unsigned payload = OBJPAYLOAD16(ptr);
strcatf(&str, "%s", text);
*((void**)obj - 1) = OBJBOX(str, payload);
}
// ---
const char *obj_typeof( const void *obj ) {
void* ptr = *((void**)obj - 1);
ptr = OBJUNBOX(ptr);
char name[256]; sscanf((const char*)ptr + 1, "%[^\n]", name); // @fixme: overflow
return va("%s", name);
}
unsigned obj_typeid(const void *obj) {
void* ptr = *((void**)obj - 1);
unsigned payload = OBJPAYLOAD16(ptr);
return payload >> 8;
}
bool obj_typeeq( const void *obj1, const void *obj2 ) {
if( obj_typeid(obj1) != obj_typeid(obj2) ) return false;
return !strcmp( obj_typeof(obj1), obj_typeof(obj2) );
}
unsigned obj_typeid_from_name(const char *name) {
name += strbegi(name, "struct ") ? 8 : strbegi(name, "union ") ? 7 : 0;
unsigned typeid = hash_str(name) & 255; // @fixme: increase bits / decrease colliders (256 types only!)
ASSERT( typeid, "Name of given class has an empty (zeroed) hash. Limitation by design" );
static map(unsigned, char *) registered; // @fixme: add mutex
do_once map_init(registered, less_int, hash_int);
do_once map_insert(registered, 1, "(typeless)");
char **found = map_find(registered, typeid);
if(!found) map_insert(registered, typeid, STRDUP(name));
else ASSERT( !strcmp(name, *found), "Uh-oh, types collided. Please rename one of these two classes '%s'/'%s'", name, *found);
return typeid;
}
// ---
unsigned obj_sizeof(const void *obj) {
void *ptr = (void**)obj - 1;
return ALLOCSIZE(ptr) - sizeof(void*);
}
void obj_zero(void *obj) {
// clear console log
void* ptr = *((void**)obj - 1);
char* str = OBJUNBOX(ptr);
if( str[0] ) {
unsigned payload = OBJPAYLOAD16(ptr);
unsigned namelen = strlen(obj_typeof(obj));
str = REALLOC(str, 1+namelen+2); // preserve \1+name+\n+\0
str[1+namelen+2-1] = '\0';
*((void**)obj - 1) = OBJBOX(str, payload);
}
// reset data
dtor(obj);
memset(obj, 0, obj_sizeof(obj));
ctor(obj);
}
void obj_hexdumpf(FILE *out, const void *obj) {
hexdumpf(out, obj, obj_sizeof(obj), 16);
const char *output = obj_output(obj);
fprintf( out, "; ptr=[%p] sizeof=%d typeof=%s typeid=%#x refs=%d\n%s%s\n",
obj, obj_sizeof(obj), obj_typeof(obj), obj_typeid(obj), (int)(OBJPAYLOAD16(obj) & 0xFF),
output[0] ? output : "(no output)", output[0] ? "---" : "");
}
void obj_hexdump(const void *obj) {
obj_hexdumpf( stdout, obj );
}
// object: load/save
unsigned obj_load_buffer(void *obj, const void *src, unsigned srclen) {
unsigned objlen = obj_sizeof(obj);
if( srclen > objlen ) return 0; // @fixme: do something clever
memcpy(obj, src, srclen); // @fixme: do something clever
return objlen;
}
unsigned obj_load(void *obj, const array(char) buffer) {
unsigned bytes = buffer ? obj_load_buffer(obj, buffer, array_count((char*)buffer)) : 0;
return bytes;
}
unsigned obj_load_file(void *obj, FILE *fp) {
unsigned len = obj_sizeof(obj);
char *buffer = va("%*.s", len, "");
unsigned read = fread(buffer, 1, len, fp);
if( read != (1*len) ) {
return 0;
}
unsigned bytes = obj_load_buffer(obj, buffer, len);
return bytes;
}
unsigned obj_save_buffer(void *dst, unsigned cap, const void *obj) {
unsigned len = obj_sizeof(obj);
if( len > cap ) return 0;
memcpy(dst, obj, len); // @fixme: do something clever
return len;
}
array(char) obj_save(const void *obj) { // empty if error. must array_free() after use
array(char) data = 0;
unsigned len = obj_sizeof(obj);
array_resize(data, len);
unsigned bytes = obj_save_buffer(data, len, obj);
array_resize(data, bytes);
return data;
}
unsigned obj_save_file(FILE *fp, const void *obj) {
unsigned len = obj_sizeof(obj);
char *buffer = va("%*.s", len, "");
unsigned bytes = obj_save_buffer(buffer, len, obj);
if( bytes > 0 ) {
unsigned written = fwrite(buffer, 1, len, fp);
if( written == (1*len) ) {
return written;
}
}
return 0; // error
}
// ---
static int __thread global_ref_count; // @fixme: make it atomic
static void objref_check_atexit(void) {
if(global_ref_count > 0) fprintf(stderr, "Warn! Possible memory_leaks: %d refs not destroyed\n", global_ref_count);
if(global_ref_count < 0) fprintf(stderr, "Warn! Possible double free: %d refs double destroyed\n", -global_ref_count);
}
void* obj_ref(void *obj) {
do_once atexit(objref_check_atexit);
if( obj ) {
void *ptr = *((void**)obj - 1);
unsigned payload = OBJPAYLOAD16(ptr);
unsigned ref_count = payload & 255;
ASSERT(ref_count < 255, "Object cannot hold more than 256 refs. Limitation by design.");
*((void**)obj - 1) = OBJBOX(OBJUNBOX(ptr), payload + 1);
global_ref_count++;
}
return obj;
}
void* obj_unref(void *obj) {
if( obj ) {
void *ptr = *((void**)obj - 1);
unsigned payload = OBJPAYLOAD16(ptr);
unsigned ref_count = payload & 255;
*((void**)obj - 1) = OBJBOX(OBJUNBOX(ptr), payload - 1);
global_ref_count--;
if( ref_count <= 1 ) {
obj_del(obj);
return 0;
}
}
return obj;
}
// ---
void dummy1() {}
#define dummy8 dummy1,dummy1,dummy1,dummy1,dummy1,dummy1,dummy1,dummy1
#define dummy64 dummy8,dummy8,dummy8,dummy8,dummy8,dummy8,dummy8,dummy8
#define dummy256 dummy64,dummy64,dummy64,dummy64
void (*dtor[256])() = { dummy256 };
void (*ctor[256])() = { dummy256 };
// ---
static set(uintptr_t) vtables; // @fixme: add mutex
void (obj_override)(const char *objclass, void (**vtable)(), void(*fn)()) {
do_once set_init(vtables, less_64, hash_64);
set_find_or_add(vtables, (uintptr_t)vtable);
vtable[ obj_typeid_from_name(objclass) ] = fn;
}
void (obj_extend)(const char *dstclass, const char *srcclass) { // wip, @testme
unsigned dst_id = obj_typeid_from_name(dstclass);
unsigned src_id = obj_typeid_from_name(srcclass);
// iterate src vtables, and assign them to dst
if(dst_id != src_id)
for each_set(vtables, void **, src_table) {
if( src_table[src_id] ) {
src_table[dst_id] = (void(*)()) (src_table[ src_id ]);
}
}
}
// ---
void *obj_clone(const void *obj) { // @fixme: clone object console as well?
unsigned sz = obj_sizeof(obj);
const char *nm = obj_typeof(obj);
unsigned id = obj_typeid(obj);
void *obj2 = obj_initialize((void**)MALLOC(sizeof(void*)+sz), stringf("%c%s\n" "cloned" "\n", id, nm)); // STRDUP( OBJUNBOX(*((void**)obj - 1)) ));
memcpy(obj2, obj, sz);
return obj2;
}
void *obj_copy(void **dst, const void *src) {
if(!*dst) return *dst = obj_clone(src);
if( obj_typeeq(*dst, src) ) {
return memcpy(*dst, src, obj_sizeof(src));
}
return NULL;
}
void *obj_mutate(void **dst_, const void *src) {
// mutate a class. ie, convert a given object class into a different one,
// while preserving the original metas and references as much as possible.
//
// @fixme: systems might be tracking objects in the future. the fact that we
// can reallocate a pointer (and hence, change its address) seems way too dangerous,
// as the tracking systems could crash when referencing a mutated object.
// solutions: do not reallocate if sizeof(new_class) > sizeof(old_class) maybe? good enough?
// also, optimization hint: no need to reallocate if both sizes matches, just copy contents.
if(!*dst_) return *dst_ = obj_clone(src);
void *dst = *dst_;
dtor(dst);
unsigned src_sz = obj_sizeof(src);
unsigned src_id = obj_typeid(src);
void *dst_ptr = *((void**)dst - 1);
unsigned payload = (OBJPAYLOAD16(dst_ptr) & 255) | src_id << 8;
FREE( OBJUNBOX(dst_ptr) );
*((void**)dst - 1) = OBJBOX( STRDUP( OBJUNBOX(*((void**)src - 1)) ), payload);
void *base = (void*)((void**)dst - 1);
base = REALLOC(base, src_sz + sizeof(void*));
*dst_ = (char*)base + sizeof(void*);
dst = (char*)base + sizeof(void*);
memcpy(dst, src, src_sz);
ctor(dst);
return dst;
}
#ifdef OBJ_DEMO
typedef struct MyObject {
char* id;
int x,y;
float rotation;
struct MyObject *next;
} MyObject;
void tests1() {
// Construct two objects
MyObject *root = obj_new(MyObject, 0);
MyObject *obj = obj_new(MyObject, "An identifier!", 0x11, 0x22, 3.1415f, root );
// Log some lines
obj_printf(root, "this is a logline #1\n");
obj_printf(root, "this is a logline #2\n");
obj_printf(root, "this is a logline #3\n");
obj_printf(obj, "yet another logline #1\n");
obj_printf(obj, "yet another logline #2\n");
// Dump contents of our objects
obj_hexdump(root);
obj_hexdump(obj);
// Save to mem
array(char) buffer = obj_save(obj);
printf("%d bytes\n", (int)array_count(buffer));
// Clear
obj_zero( obj );
obj_hexdump( obj );
// Reload
obj_load( obj, buffer );
obj_hexdump( obj );
// Copy tests
{
MyObject *clone = obj_clone(obj);
obj_hexdump(clone);
obj_del(clone);
}
{
MyObject *copy = 0;
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
{
MyObject *copy = obj_new(MyObject, "A different identifier!", 0x33, 0x44, 0.0f, root );
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
{
void *copy = obj_malloc(100, "an untyped class" );
obj_mutate(&copy, obj);
obj_hexdump(copy);
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
// Benchmarking call overhead.
// We're here using dtor as a method to test. Since there is actually no
// destructor associated to this class, it will be safe to call it extensively (no double frees).
//
// results:
// 427 million calls/s @ old i5-4300/1.90Ghz laptop. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
#ifndef N
#define N (INT32_MAX-1)
#endif
double t = (puts("benchmarking..."), -clock() / (double)CLOCKS_PER_SEC);
for( int i = 0; i < N; ++i ) {
dtor(root);
}
t += clock() / (double)CLOCKS_PER_SEC;
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
}
// --------------
#define dump(obj) dump[obj_typeid(obj)](obj)
#define area(obj) area[obj_typeid(obj)](obj)
extern void (*dump[256])();
extern float (*area[256])();
void (*dump[256])() = {0};
float (*area[256])() = {0};
// --------------
typedef struct box {
float w;
} box;
void box_ctor(box *b) { printf("box already constructed: box-w:%f\n", b->w); }
void box_dtor(box *b) { puts("deleting box..."); }
void box_dump(box *b) { printf("box-w:%f\n", b->w); }
float box_area(box *b) { return b->w * b->w; }
#define REGISTER_BOX \
obj_override(box, ctor); \
obj_override(box, dump); \
obj_override(box, area); \
obj_override(box, dtor);
typedef struct rect {
float w, h;
} rect;
void rect_dump(rect *r) { printf("rect-w:%f rect-h:%f\n", r->w, r->h); }
float rect_area(rect *r) { return r->w * r->h; }
void rect_dtor(rect *r) { puts("deleting rect..."); }
#define REGISTER_RECT \
obj_override(rect, dump); \
obj_override(rect, area); \
obj_override(rect, dtor);
void tests2() {
REGISTER_BOX
REGISTER_RECT
box *b = obj_new(box, 100);
rect *r = obj_new(rect, 100, 200);
dump(b);
dump(r);
printf("%f\n", area(b));
printf("%f\n", area(r));
obj_del(b);
obj_ref(r); obj_unref(r); //obj_del(r);
int *untyped = obj_malloc( sizeof(int) );
int *my_number = obj_malloc( sizeof(int), "a comment about my_number" );
char *my_text = obj_malloc( 32, "some debug info here" );
*untyped = 100;
*my_number = 123;
sprintf( my_text, "hello world" );
struct my_bitmap { int w, h, bpp; const char *pixels; };
struct my_bitmap *my_bitmap = obj_new(struct my_bitmap, 2,2,8, "\1\2\3\4");
printf( "%p(%s,%u)\n", my_bitmap, obj_typeof(my_bitmap), obj_typeid(my_bitmap) );
printf( "%d(%s,%d)\n", *untyped, obj_typeof(untyped), obj_typeid(untyped) );
printf( "%d(%s,%d)\n", *my_number, obj_typeof(my_number), obj_typeid(my_number) );
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
obj_printf(my_text, "hello world #1\n");
obj_printf(my_text, "hello world #2\n");
puts(obj_output(my_text));
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
printf( "equal?:%d\n", obj_typeeq(my_number, untyped) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_number) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_text) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_bitmap) );
obj_free( untyped );
obj_free( my_text );
obj_free( my_bitmap );
obj_del( my_number ); // should not crash, even if allocated with obj_malloc()
}
int main() {
tests1();
tests2();
puts("ok");
}
/*
MEMBER( MyObject, char*, id );
MEMBER( MyObject, int, x );
MEMBER( MyObject, int, y );
MEMBER( MyObject, float, rotation, "(degrees)" );
MEMBER( MyObject, MyObject*, next, "(linked list)" );
*/
#define main main__
#endif