/*!@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 #include //ulc_fprintf /* Local */ #include "print.h" #include "apc.h" #include "ir.h" #undef do_error #define do_error(...) exit(-1) #define XXH_PRIVATE_API #include "../xxHash/xxhash.h" /* Public */ int ir_init(void); void ir_quit(void); void ir_test(void); int ir_linker(void); int ir_condenser(void); /* Memory allocation structures */ 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; }; #define SYS_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)) /* Set data mem */ enum dtype { FSDAT, MSDAT, ADAT, LDAT, FBDAT }; 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; }; 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; uint32_t ref; uint8_t* name; struct ir_framebox_t* frameboxes; struct ir_simplex_t* audio; struct ir_link_t* links; }; /* Functions */ static inline struct ir_framebox_t* ir_set_add_framebox(struct ir_set_t*,uint8_t*); static inline union ir_setdata_t* ir_framedata (enum dtype,const uint8_t*,apc_facing,int,int); static inline int bytes_identical(const uint8_t*,const uint8_t*); static inline int classnames_identical(const uint8_t*,const uint8_t*); static void* stack_alloc(size_t); #define struct_alloc(_T) ((struct _T*) stack_alloc(sizeof(struct _T))) #define struct_clear(_S) (memset((_S), 0, sizeof(*(_S)))) static uint8_t* name_alloc(const uint8_t*); static uint8_t* classname_alloc(const uint8_t*); static inline void* pagelist_pop(struct pagelist_t*,size_t); #define $($)#$ #define pagelist_alloc(pagelist) do { \ pagelist.head->header.next = (struct pagenode_t*) malloc(pagelist.pagesize); \ if (pagelist.head->header.next == NULL) \ eprintf("Memory allocation error\n"); \ struct_clear(pagelist.head->header.next); \ pagelist.head = pagelist.head->header.next; \ pagelist.head->header.head = pagelist.head->root; \ } while (0) #define pagelist_init(pagelist,size) do { \ pagelist.pagesize = size; \ pagelist.root = (struct pagenode_t*) malloc(size); \ if (pagelist.root == NULL) \ eprintf("Memory allocation error\n"); \ struct_clear(pagelist.root); \ pagelist.head = pagelist.root; \ pagelist.head->header.head = pagelist.head->root; \ } while (0) static void pagenode_free(struct pagenode_t*); #define REFHASH(ref) (XXH32(&ref, sizeof(uint32_t), 0xCEED) & 0xCFF) extern //apc.c long sys_pagesize; static struct pagelist_t datapages, namepages, refhashpages; static struct ir_class_t root_class = { .name = (uint8_t*)"." }; /* Init */ int ir_init ( void ) { pagelist_init(datapages, (size_t)SYS_PAGESIZE); pagelist_init(namepages, (size_t)NAME_PAGESIZE); pagelist_init(refhashpages, (size_t)SYS_PAGESIZE); return 0; } /* Quit/Cleanup */ void ir_quit ( void ) { pagenode_free(datapages.root); pagenode_free(namepages.root); pagenode_free(refhashpages.root); } /* Recursively clean pagenode linked list, freeing last first */ static void pagenode_free ( struct pagenode_t* pagenode ) { if (pagenode->header.next != NULL) pagenode_free(pagenode->header.next); free(pagenode); } /* Link */ int ir_linker ( void ) { return 0; } /* Condense */ int ir_condenser ( void ) { return 0; } /* Return the class's name string */ uint8_t* ir_class_name ( struct ir_class_t* class ) { return class->name; } /* 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; if (class->nextchild == NULL) { class->nextchild = struct_alloc(ir_class_t); struct_clear(class->nextchild); class->nextchild->name = classname_alloc(name); return class->nextchild; } 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 (classnames_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } iter->nextsib = struct_alloc(ir_class_t); struct_clear(iter->nextsib); iter->nextsib->name = classname_alloc(name); return iter->nextsib; } /* 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; if (class->root_set == NULL) { class->root_set = struct_alloc(ir_set_t); struct_clear(class->root_set); class->root_set->name = name_alloc(name); return class->root_set; } 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 %U\n", iter->name); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } iter->nextsib = struct_alloc(ir_set_t); struct_clear(iter->nextsib); iter->nextsib->name = name_alloc(name); return iter->nextsib; } struct ir_set_t* ir_set_from_ref ( uint32_t ref ) { uint16_t hash; struct ir_set_t** iters; struct pagenode_t* iterp; iterp = refhashpages.root; hash = REFHASH(ref); do iters = ((struct ir_set_t**) iterp->root) + hash; while (*iters != NULL && (*iters)->ref != ref && (iterp = iterp->header.next) != NULL); return *iters; } /* 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; if (set->nextchild == NULL) { set->nextchild = struct_alloc(ir_set_t); struct_clear(set->nextchild); set->nextchild->name = name_alloc(name); return set->nextchild; } iter = set->nextchild; if (name == NULL) eprintf("Null child added to set %s\n", iter->name); if (iter->name == NULL) eprintf("Null name pointer in set %p\n", iter); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } iter->nextsib = struct_alloc(ir_set_t); struct_clear(iter->nextsib); iter->nextsib->name = name_alloc(name); return iter->nextsib; } /* Add a framebox to a set Attempts to create a new framebox of the specified set, returning the framebox if it already exists Name is not allocated, but assigned, unlike other "XXX_add" functions where name is duplicated into IR's internal array. */ static inline struct ir_framebox_t* ir_set_add_framebox ( struct ir_set_t* set, uint8_t* name ) { struct ir_framebox_t* iter; if (set->frameboxes == NULL) { set->frameboxes = struct_alloc(ir_framebox_t); struct_clear(set->frameboxes); set->frameboxes->header.data_name = name; return set->frameboxes; } 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; } iter->header.nextsib = (union ir_setdata_t*) struct_alloc(ir_framebox_t); struct_clear(iter->header.nextsib); iter->header.nextsib->header.data_name = name; return (struct ir_framebox_t*) (iter->header.nextsib); } /* 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); } static inline int classnames_identical ( const uint8_t* stra, const uint8_t* strb ) { int ca, cb; do { ca = *stra++; cb = *strb++; } while (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, uint32_t ref ) { uint16_t hash, oldhash; struct ir_set_t** iters; struct pagenode_t* iterp; uint32_t oldref; oldref = set->ref; oldhash = 0; hash = REFHASH(ref); iterp = refhashpages.root; check_depth: iters = ((struct ir_set_t**) iterp->root) + hash; if (*iters == NULL || *iters == set) *iters = set; else { if (iterp->header.next == NULL) pagelist_alloc(refhashpages); iterp = iterp->header.next; goto check_depth; } if (oldref != 0) { wprintf("Ref override: 0x%x -> 0x%x for set %s\n", oldref, ref, set->name); if (oldhash != 0) *iters = NULL; else { oldhash = hash; hash = REFHASH(oldref); goto check_depth; } } set->ref = ref; } 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); struct_clear(framedata); if (name == NULL) eprintf("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); struct_clear(audio); if (name == NULL) eprintf("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) eprintf("Null class in classld\n"); classld = struct_alloc(ir_classld_t); struct_clear(classld); classld->root_class = class; return classld; } struct ir_setld_t* ir_setld_from_ref ( uint32_t ref ) { struct ir_setld_t* setld; setld = struct_alloc(ir_setld_t); struct_clear(setld); 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); struct_clear(setld); setld->namelist = struct_alloc(ir_namelist_t); struct_clear(setld->namelist); 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); struct_clear(setld->namelist); setld->namelist_head = setld->namelist; } else { setld->namelist_head->nextsib = struct_alloc(ir_namelist_t); struct_clear(setld->namelist_head->nextsib); 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); struct_clear(link); 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 inline void* pagelist_pop ( struct pagelist_t* pagelist, size_t size ) { size_t headsize = PL_HEADSIZE((*pagelist)); if (!headsize) { free(pagelist->head); pagelist->head = pagelist->root; while (pagelist->head->header.next != NULL) pagelist->head = pagelist->head->header.next; } if (headsize < size) eprintf("Attempted to pop unaligned value from pagelist\n"); pagelist->head->header.head -= size; return pagelist->head->header.head; } static void* stack_alloc ( size_t bytes ) { void* p; if (PL_HEADMEM(datapages) < bytes) pagelist_alloc(datapages); p = datapages.head->header.head; datapages.head->header.head += bytes; return p; } 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 != '_' && *iter != '.' && head_mem; head_mem--) *(namepages.head->header.head)++ = *iter++; if (head_mem < 1) //not enough room { pagelist_alloc(namepages); goto copy; } *(namepages.head->header.head)++ = '\0'; return name; } static uint8_t* classname_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 && head_mem; head_mem--) *(namepages.head->header.head)++ = *iter++; if (head_mem < 1) //not enough room { pagelist_alloc(namepages); goto copy; } *(namepages.head->header.head)++ = '\0'; return name; } static void crawl_class(struct ir_class_t*); static void crawl_set(struct ir_set_t*,int); void ir_test(void) { uprintf("IR From Directory: %s\n",getcwd(NULL,255)); crawl_class(&root_class); if (root_class.root_set != NULL) crawl_set(root_class.root_set, 0); } static void crawl_class ( struct ir_class_t* class ) { struct ir_class_t* iter; for (iter = class->nextchild; iter != NULL; iter = iter->nextsib) { wprintf("%U/\n", iter->name); if(chdir((char*)iter->name)) eprintf("CHDIR %U from %s\n",iter->name,getcwd(NULL,255)); crawl_class(iter); if (iter->root_set != NULL) crawl_set(iter->root_set, 0); uprintf("%U\\\n",iter->name); if (chdir("..")) eprintf("CHDIR ..\n"); } } static void crawl_set ( struct ir_set_t* set, int depth ) { struct ir_set_t* setlist[64], ** slp, * iter; int i = depth * 12; while (i--) putchar('.'); i = depth; slp = setlist; for(iter = set; iter != NULL; iter = iter->nextchild) { uprintf("[%10U]", iter->name); *slp++ = iter; i++; } --i; putchar('\n'); for(iter = *--slp; slp >= setlist; iter = *--slp, --i) if (iter->nextsib != NULL) crawl_set(iter->nextsib,i); }