/*!@file \brief IR Memory Implementation \details Intermediary memory management \author Jordan Lavatai \date Aug 2016 ----------------------------------------------------------------------------*/ /* Standard */ #include //exit, malloc #include //print #include //va_args #include //uint64_t #include //memset, str* /* Unicode */ #include //u8_* functions #include //uint8_t as a char #include //u32_cpy /* Local */ #include "apc.h" #include "ir.h" /* Public */ int ir_init(void); void ir_quit(void); int ir_linker(void); int ir_condenser(void); /* Memory allocation structures */ enum dtype { FSDAT, MSDAT, ADAT, LDAT, FBDAT }; struct pagenode_t; struct pagenode_header_t { struct pagenode_t* next; char* head; }; struct pagenode_t { struct pagenode_header_t header; char root[]; }; struct pagelist_t { struct pagenode_t* root, * head; size_t pagesize; }; struct ir_namelist_t; struct ir_namelist_t { struct ir_namelist_t* nextsib; uint8_t* name; }; struct ir_classld_t { struct ir_class_t* root_class; struct ir_namelist_t* namelist, * namelist_head; }; struct ir_setld_t { struct ir_classld_t* classld; long long ref; struct ir_namelist_t* namelist, * namelist_head; }; /* Set data mem */ struct ir_setdata_header_t { enum dtype type; uint8_t* src_filename, * data_name; union ir_setdata_t* nextsib; }; struct ir_frameinfo_t { int facing, w, h; }; struct ir_framedata_t { struct ir_setdata_header_t header; struct ir_frameinfo_t frameinfo; }; struct ir_framebox_t { struct ir_setdata_header_t header; struct ir_framedata_t framesheets[FACING_MAX]; struct ir_framedata_t mapsheets[FACING_MAX]; }; struct ir_simplex_t { struct ir_setdata_header_t header; }; struct ir_link_t { struct ir_setdata_header_t header; struct ir_classld_t* classld; struct ir_setld_t* setld; enum ltype type; }; union ir_setdata_t { struct ir_setdata_header_t header; struct ir_framebox_t framebox; struct ir_framedata_t framesheet; struct ir_framedata_t mapsheet; struct ir_simplex_t audio; struct ir_link_t link; }; struct ir_class_t { struct ir_class_t* nextchild, * nextsib; struct ir_set_t* root_set; uint8_t* name; }; struct ir_set_t { struct ir_set_t* nextchild, * nextsib; struct ir_class_t* class; long long ref; uint8_t* name; struct ir_framebox_t* frameboxes; struct ir_simplex_t* audio; struct ir_link_t* links; }; /* Functions */ static inline int init_pagelist(struct pagelist_t*,size_t); static inline struct ir_framebox_t* ir_set_add_framebox(struct ir_set_t*,const uint8_t*); static void ir_free_pagenodes(struct pagenode_t*); static inline int bytes_identical(const uint8_t*,const uint8_t*); static void* stack_alloc(size_t); static uint8_t* name_alloc(const uint8_t*); static inline union ir_setdata_t* ir_framedata (enum dtype,const uint8_t*,apc_facing,int,int); /* Function-Like Macros */ #define do_warn() do { \ } while (0) #define wprint(str) do { \ fprintf(stderr, str); \ do_warn(); \ } while (0) #define wprintf(fmt,...) do { \ fprintf(stderr, fmt, __VA_ARGS__); \ do_warn(); \ } while (0) #define do_error() do { \ exit(-1); \ } while (0) #define eprint(str) do { \ fprintf(stderr, str); \ do_error(); \ } while (0) #define eprintf(fmt,...) do { \ fprintf(stderr, fmt, __VA_ARGS__); \ do_error(); \ } while (0) #define struct_alloc(_T) ((struct _T*) stack_alloc(sizeof(struct _T))) #define DATA_PAGESIZE (sys_pagesize) #define NAME_PAGESIZE (APC_NAME_MAX * 1024) #define PL_HEADERSIZE (sizeof(struct pagenode_header_t)) #define PL_HEADSIZE(_PL) (_PL.head->header.head - _PL.head->root) #define PL_HEADMEM(_PL) (_PL.pagesize - PL_HEADERSIZE - PL_HEADSIZE(_PL)) /* Memory */ extern //apc.c long sys_pagesize; static struct pagelist_t datapages, namepages; static struct ir_class_t root_class = { .name = (uint8_t*)"." }; /* Init */ int ir_init ( void ) { if (init_pagelist(&datapages, (size_t)DATA_PAGESIZE)) eprint("Memory allocation error\n"); if (init_pagelist(&namepages, (size_t)NAME_PAGESIZE)) eprint("Memory allocation error\n"); return 0; } static inline int init_pagelist ( struct pagelist_t* pl, size_t size ) { pl->pagesize = size; pl->root = (struct pagenode_t*) calloc(size,1); if (pl->root == NULL) return -1; pl->root->header.head = pl->root->root; pl->head = pl->root; return 0; } /* Quit/Cleanup Recursively clean pagenode linked list */ void ir_quit ( void ) { ir_free_pagenodes(datapages.root); ir_free_pagenodes(namepages.root); } static void ir_free_pagenodes ( struct pagenode_t* pagenode ) { if (pagenode->header.next != NULL) ir_free_pagenodes(pagenode->header.next); free(pagenode); } /* Link */ int ir_linker ( void ) { return 0; } /* Condense */ int ir_condenser ( void ) { return 0; } /* Return a pointer to the root class */ struct ir_class_t* ir_class_root ( void ) { return &root_class; } /* Add a subclass to a class Attempts to create a new subclass in the provided class, returning the class if it already exists */ struct ir_class_t* ir_class_addchild ( struct ir_class_t* class, const uint8_t* name ) { struct ir_class_t* iter; printf("Class %s, addchild %s\n", class->name, name); if (class->nextchild == NULL) goto alloc; iter = class->nextchild; if (iter->name == NULL) eprintf("Null name pointer in class %p\n", iter); if (name == NULL) eprintf("Null child added to class %s\n", iter->name); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } alloc: iter = struct_alloc(ir_class_t); iter->nextsib = class->nextchild; iter->name = name_alloc(name); return class->nextchild = iter; } /* Add a set to a class Attempts to create a new root set in the specified class, returning the set if it already exists */ struct ir_set_t* ir_class_addset ( struct ir_class_t* class, const uint8_t* name ) { struct ir_set_t* iter; printf("Class %s, addset %s\n", class->name, name); if (class->root_set == NULL) goto alloc; iter = class->root_set; if (iter->name == NULL) eprintf("Null name pointer in class %p\n", iter); if (name == NULL) eprintf("Null set added to class %s\n", iter->name); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } alloc: iter = struct_alloc(ir_set_t); iter->nextsib = class->root_set; iter->name = name_alloc(name); return class->root_set = iter; } /* Add a set to a set Attempts to create a new subset of the specified set, returning the child if it already exists */ struct ir_set_t* ir_set_addchild ( struct ir_set_t* set, const uint8_t* name ) { struct ir_set_t* iter; printf("Set %s, addchild %s\n", set->name, name); if (set->nextchild == NULL) goto alloc; iter = set->nextchild; if (iter->name == NULL) eprintf("Null name pointer in set %p\n", iter); if (name == NULL) eprintf("Null child added to set %s\n", iter->name); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } alloc: iter = struct_alloc(ir_set_t); iter->nextsib = set->nextchild; iter->name = name_alloc(name); return set->nextchild = iter; } /* Add a framebox to a set Attempts to create a new framebox of the specified set, returning the framebox if it already exists */ static inline struct ir_framebox_t* ir_set_add_framebox ( struct ir_set_t* set, const uint8_t* name ) { struct ir_framebox_t* iter; if (set->frameboxes == NULL) goto alloc; iter = set->frameboxes; check: if (bytes_identical(iter->header.data_name, name)) return iter; if (iter->header.nextsib != NULL) { iter = (struct ir_framebox_t*) iter->header.nextsib; goto check; } alloc: iter = struct_alloc(ir_framebox_t); iter->header.nextsib = (union ir_setdata_t*) set->frameboxes; iter->header.data_name = name_alloc(name); return set->frameboxes = iter; } /* Match two null-terminated bytestrings Return 1 if the two bytestrings are identical, else 0 */ static inline int bytes_identical ( const uint8_t* stra, const uint8_t* strb ) { int ca, cb; do { ca = *stra++; cb = *strb++; } while (ca && ca != '_' && ca == cb); return (ca == cb); } /* Assign Setdata to Set */ void ir_set_assign_data ( struct ir_set_t* set, union ir_setdata_t* setdata ) { struct ir_framebox_t* framebox; struct ir_simplex_t* simplex; switch (setdata->header.type) { case FSDAT: framebox = ir_set_add_framebox(set, setdata->header.data_name); if (framebox->framesheets[setdata->framesheet.frameinfo.facing].header.data_name != NULL) wprintf("Duplicate framesheet [%i] %s\n", setdata->framesheet.frameinfo.facing, setdata->header.data_name); framebox->framesheets[setdata->framesheet.frameinfo.facing] = setdata->framesheet; break; case MSDAT: framebox = ir_set_add_framebox(set, setdata->header.data_name); if (framebox->mapsheets[setdata->mapsheet.frameinfo.facing].header.data_name != NULL) wprintf("Duplicate mapsheet [%i] %s\n", setdata->mapsheet.frameinfo.facing, setdata->header.data_name); framebox->mapsheets[setdata->mapsheet.frameinfo.facing] = setdata->mapsheet; break; case ADAT: if (set->audio == NULL) { set->audio = (struct ir_simplex_t*) setdata; return; } simplex = set->audio; while (simplex->header.nextsib != NULL) if (bytes_identical(simplex->header.data_name, setdata->header.data_name)) { wprintf("Duplicate audio %s\n", setdata->header.data_name); *simplex = setdata->audio; //setdata is now a pointer to redundant, unused memory. return; } else simplex = (struct ir_simplex_t*) simplex->header.nextsib; setdata->audio.header.nextsib = (union ir_setdata_t*) set->audio; set->audio = (struct ir_simplex_t*) setdata; break; case LDAT: setdata->link.header.nextsib = (union ir_setdata_t*) set->links; set->links = (struct ir_link_t*) setdata; break; default: fprintf(stderr, "Unknown setdata type %x\n", setdata->header.type); exit(-1); } } void ir_set_assign_ref ( struct ir_set_t* set, long long ref ) { if (set->ref != 0) wprintf("Ref override: 0x%lx -> 0x%lx for set %s\n", (long unsigned) set->ref, (long unsigned) ref, set->name); set->ref = ref; //TODO: reflist_add(set); } void ir_data_assign_path ( union ir_setdata_t* setdata, const uint8_t* path ) { if (path == NULL) eprintf("Null path in data %s\n", setdata->header.data_name); if (setdata->header.src_filename != NULL) wprintf("Path override: %s -> %s for setdata %s\n", setdata->header.src_filename, path, setdata->header.data_name); setdata->header.src_filename = name_alloc(path); } union ir_setdata_t* ir_framesheet ( const uint8_t* name, apc_facing d, int width, int height ) { return ir_framedata(FSDAT, name, d, width, height); } union ir_setdata_t* ir_mapsheet ( const uint8_t* name, apc_facing d, int width, int height ) { return ir_framedata(MSDAT, name, d, width, height); } static inline union ir_setdata_t* ir_framedata ( enum dtype type, const uint8_t* name, apc_facing d, int width, int height ) { struct ir_framedata_t* framedata = struct_alloc(ir_framedata_t); if (name == NULL) eprint("Null name in set allocation\n"); framedata->header.type = type; framedata->header.data_name = name_alloc(name); framedata->frameinfo.facing = d; framedata->frameinfo.w = width; framedata->frameinfo.h = height; return (union ir_setdata_t*) framedata; } union ir_setdata_t* ir_audio ( const uint8_t* name ) { struct ir_simplex_t* audio = struct_alloc(ir_simplex_t); if (name == NULL) eprint("Null audio\n"); audio->header.type = ADAT; audio->header.data_name = name_alloc(name); return (union ir_setdata_t*) audio; } /* Create classld that points to a class */ struct ir_classld_t* ir_classld_from_class ( struct ir_class_t* class ) { struct ir_classld_t* classld; if (class == NULL) eprint("Null class in classld\n"); classld = struct_alloc(ir_classld_t); classld->root_class = class; return classld; } struct ir_setld_t* ir_setld_from_ref ( long long ref ) { struct ir_setld_t* setld; setld = struct_alloc(ir_setld_t); setld->ref = ref; return setld; } struct ir_setld_t* ir_setld_from_classld ( struct ir_classld_t* classld, const uint8_t* name ) { struct ir_setld_t* setld; setld = struct_alloc(ir_setld_t); setld->namelist = struct_alloc(ir_namelist_t); setld->namelist_head = setld->namelist; setld->namelist_head->name = name_alloc(name); setld->classld = classld; return setld; } struct ir_setld_t* ir_setld_addchild ( struct ir_setld_t* setld, const uint8_t* name ) { if (setld->namelist == NULL) { setld->namelist = struct_alloc(ir_namelist_t); setld->namelist_head = setld->namelist; } else { setld->namelist_head->nextsib = struct_alloc(ir_namelist_t); setld->namelist_head = setld->namelist_head->nextsib; } setld->namelist_head->name = name_alloc(name); return setld; } union ir_setdata_t* ir_link ( enum ltype link_type, struct ir_setld_t* setld, const uint8_t* name ) { struct ir_link_t* link; link = struct_alloc(ir_link_t); link->header.type = LDAT; link->type = link_type; link->classld = setld->classld; link->setld = setld; if (link_type != OLINK && name != NULL) link->header.data_name = name_alloc(name); return (union ir_setdata_t*) link; } static void* stack_alloc ( size_t bytes ) { if (!bytes) //valid behavior to attain current head return datapages.head->header.head; if (PL_HEADMEM(datapages) < bytes) { datapages.head->header.next = (struct pagenode_t*) calloc(datapages.pagesize,1); if (datapages.head->header.next == NULL) eprint("Memory allocation error \n"); datapages.head = datapages.head->header.next; datapages.head->header.head = datapages.head->root; } datapages.head->header.head += bytes; return (void*) datapages.head->header.head - bytes; } static uint8_t* name_alloc ( const uint8_t* name_src ) { const uint8_t* iter; uint8_t* name; int head_mem; copy: name = (uint8_t*)namepages.head->header.head; iter = name_src; for (head_mem = PL_HEADMEM(namepages); *iter && *iter != '_' && head_mem; head_mem--) *(namepages.head->header.head)++ = *iter++; if (head_mem == 0) //not enough room { namepages.head->header.next = (struct pagenode_t*) calloc(namepages.pagesize,1); if (namepages.head->header.next == NULL) eprint("Memory allocation error\n"); namepages.head = namepages.head->header.next; namepages.head->header.head = namepages.head->root; goto copy; } *(namepages.head->header.head)++ = '\0'; return name; }