/*!@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* #include /* 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" #include "pagenode.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); /* 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; uint32_t 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; long filepos; }; 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; long filepos; }; 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; long filepos; }; /* 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 uint8_t* name_alloc(const uint8_t*); static uint8_t* classname_alloc(const uint8_t*); #define struct_clear(_S) (memset((_S), 0, sizeof(*(_S)))) #define REFHASH(ref) (XXH32(&ref, sizeof(uint32_t), 0xCEED) & 0xCFF) #define struct_alloc(_T) ((struct _T*) stack_alloc(&datapages, sizeof(struct _T))) 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); } /* 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; } /* Get the root set of the class */ struct ir_set_t* ir_class_rootset ( struct ir_class_t* class ) { return class->root_set; } 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); } /* Return the name of the set */ uint8_t* ir_set_name ( struct ir_set_t* set) { return set->name; } /* Return the next sib of the class */ struct ir_class_t* ir_class_nextsib ( struct ir_class_t* class ) { return class->nextsib; } /* Return the next sib of the class */ struct ir_class_t* ir_class_nextchild ( struct ir_class_t* class ) { return class->nextchild; } /* Get the file position of the class */ long ir_class_fpos ( struct ir_class_t* class ) { return class->filepos; } /* Set the file position of the class */ void ir_class_assign_fpos ( struct ir_class_t* class, long newpos ) { class->filepos = newpos; } /* Get the next sibling of the provided set */ struct ir_set_t* ir_set_nextsib ( struct ir_set_t* set ) { return set->nextsib; } /* Get the next child of the provided set */ struct ir_set_t* ir_set_nextchild ( struct ir_set_t* set ) { return set->nextchild; } /* Get the file position of the class */ long ir_set_fpos ( struct ir_set_t* set ) { return set->filepos; } /* Set the file position of the class */ void ir_set_assign_fpos ( struct ir_set_t* set, long newpos ) { set->filepos = newpos; } /* 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; } /* Return a set's root framebox */ union ir_setdata_t* ir_set_framebox ( struct ir_set_t* set ) { return (union ir_setdata_t*) set->frameboxes; } /* Return a set's root audio data */ union ir_setdata_t* ir_set_audio ( struct ir_set_t* set ) { return (union ir_setdata_t*) set->audio; } /* Return a set's root link data */ union ir_setdata_t* ir_set_link ( struct ir_set_t* set ) { return (union ir_setdata_t*) set->links; } /* Return the link type */ enum ltype ir_linkdata_type ( union ir_setdata_t* linkdata ) { if (linkdata->header.type != LDAT) eprintf("Data %s is not a link\n", linkdata->header.data_name); return linkdata->link.type; } /* Return the link type */ uint32_t ir_linkdata_ref ( union ir_setdata_t* linkdata ) { if (linkdata->header.type != LDAT) eprintf("Data %s is not a link\n", linkdata->header.data_name); return linkdata->link.setld->ref; } /* Resolve and return the link's target set */ struct ir_set_t* ir_linkdata_set ( union ir_setdata_t* linkdata ) { struct ir_class_t* class_iter; struct ir_namelist_t* namelist_iter; struct ir_setld_t* setld; struct ir_classld_t* classld; struct ir_set_t* set; set = NULL; if (linkdata->header.type != LDAT) eprintf("Data %s is not a link\n", linkdata->header.data_name); setld = linkdata->link.setld; if (linkdata->link.setld == NULL) eprintf("Link data is invalid\n"); classld = linkdata->link.classld; if (classld != NULL) { namelist_iter = classld->namelist; if (classld->root_class == NULL) eprintf("No root class for classld\n"); class_iter = classld->root_class->nextchild; while (class_iter != NULL) { if (classnames_identical(class_iter->name, namelist_iter->name)) { if (namelist_iter == classld->namelist_head) break; class_iter = class_iter->nextchild; namelist_iter = namelist_iter->nextsib; } else class_iter = class_iter->nextsib; } if (class_iter == NULL) eprintf("Class resolution failed\n"); set = class_iter->root_set; } else set = ir_set_from_ref(setld->ref); if (set == NULL) eprintf("Initial set resolution failed\n"); namelist_iter = setld->namelist; while (set != NULL) { if (bytes_identical(set->name, namelist_iter->name)) { if (namelist_iter == setld->namelist_head) break; set = set->nextchild; namelist_iter = namelist_iter->nextsib; } else set = set->nextsib; } return set; } /* Get a setdata's next sibling */ union ir_setdata_t* ir_setdata_nextsib ( union ir_setdata_t* setdata ) { return setdata->header.nextsib; } /* Get a setdata's name */ uint8_t* ir_setdata_name ( union ir_setdata_t* setdata ) { return setdata->header.data_name; } /* Get a setdata's filename */ uint8_t* ir_setdata_filename ( union ir_setdata_t* setdata ) { return setdata->header.src_filename; } /* Get a setdata's file position */ long ir_setdata_fpos ( union ir_setdata_t* setdata ) { return setdata->header.filepos; } /* Set a setdata's file position */ void ir_setdata_assign_fpos ( union ir_setdata_t* setdata, long newpos ) { setdata->header.filepos = newpos; } /* Return a framebox's specified framesheet */ union ir_setdata_t* ir_framebox_framesheet ( union ir_setdata_t* fbox, apc_facing facing ) { if (fbox->header.type != FBDAT) eprintf("Data %s is not a framebox\n", fbox->header.data_name); return (union ir_setdata_t*) &fbox->framebox.framesheets[facing]; } /* Return a framebox's specified mapsheet */ union ir_setdata_t* ir_framebox_mapsheet ( union ir_setdata_t* fbox, apc_facing facing ) { if (fbox->header.type != FBDAT) eprintf("Data %s is not a framebox\n", fbox->header.data_name); return (union ir_setdata_t*) &fbox->framebox.mapsheets[facing]; } /* Return a framedata's frame info */ struct ir_frameinfo_t* ir_framedata_frameinfo ( union ir_setdata_t* framedata ) { if (framedata->header.type != MSDAT && framedata->header.type != FSDAT) eprintf("Data %s is not a framedata\n", framedata->header.data_name); return &framedata->mapsheet.frameinfo; } /** Allocators **/ 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); extern int binout_init(ir_class); 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); uprintf("starting binaryout \n"); binout_init(&root_class); } static void crawl_class ( struct ir_class_t* class ) { struct ir_class_t* iter; for (iter = class->nextchild; iter != NULL; iter = iter->nextsib) { wprintf("Crawling class %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"); wprintf("Finished crawling class %U/\n", iter->name); } } #define push_setp(setp) (*(struct ir_set_t**)stack_alloc(&datapages, sizeof(struct ir_set_t*)) = setp) #define pop_setp() (*(struct ir_set_t**)pagelist_pop(&datapages, sizeof(struct ir_set_t*))) static void crawl_set ( struct ir_set_t* set, int depth ) { struct ir_set_t* iter; int i; i = depth * 12; while (i--) putchar('.'); i = depth; for(iter = set; iter != NULL; iter = iter->nextchild) { uprintf("[%10U]", iter->name); push_setp(iter); i++; } putchar('\n'); while (--i >= depth) if (((iter = pop_setp())->nextsib) != NULL) crawl_set(iter->nextsib,i); }