Merge branch 'master' of krull:apc
[henge/apc.git] / src / ir.c
1 /*!@file
2 \brief IR Memory Implementation
3 \details Intermediary memory management
4 \author Jordan Lavatai
5 \date Aug 2016
6 ----------------------------------------------------------------------------*/
7 /* Standard */
8 #include <stdlib.h> //exit, malloc
9 #include <stdio.h> //print
10 #include <stdarg.h> //va_args
11 #include <stdint.h> //uint64_t
12 #include <string.h> //memset, str*
13 /* Unicode */
14 #include <unistd.h> //u8_* functions
15 #include <unitypes.h> //uint8_t as a char
16 #include <unistr.h> //u32_cpy
17 #include <unistdio.h> //ulc_fprintf
18 /* Local */
19 #include "print.h"
20 #include "apc.h"
21 #include "ir.h"
22 #undef do_error
23 #define do_error(...) exit(-1)
24 #define XXH_PRIVATE_API
25 #include "../xxHash/xxhash.h"
26 /* Public */
27 int ir_init(void);
28 void ir_quit(void);
29 void ir_test(void);
30 int ir_linker(void);
31 int ir_condenser(void);
32 /* Memory allocation structures */
33 struct pagenode_t;
34 struct pagenode_header_t {
35 struct pagenode_t* next;
36 char* head;
37 };
38 struct pagenode_t {
39 struct pagenode_header_t header;
40 char root[];
41 };
42 struct pagelist_t {
43 struct pagenode_t* root, * head;
44 size_t pagesize;
45 };
46 #define SYS_PAGESIZE (sys_pagesize)
47 #define NAME_PAGESIZE (APC_NAME_MAX * 1024)
48 #define PL_HEADERSIZE (sizeof(struct pagenode_header_t))
49 #define PL_HEADSIZE(_PL) (_PL.head->header.head - _PL.head->root)
50 #define PL_HEADMEM(_PL) (_PL.pagesize - PL_HEADERSIZE - PL_HEADSIZE(_PL))
51 /* Set data mem */
52 enum dtype { FSDAT, MSDAT, ADAT, LDAT, FBDAT };
53 struct ir_namelist_t;
54 struct ir_namelist_t
55 { struct ir_namelist_t* nextsib;
56 uint8_t* name;
57 };
58 struct ir_classld_t
59 { struct ir_class_t* root_class;
60 struct ir_namelist_t* namelist, * namelist_head;
61 };
62 struct ir_setld_t
63 { struct ir_classld_t* classld;
64 long long ref;
65 struct ir_namelist_t* namelist, * namelist_head;
66 };
67 struct ir_setdata_header_t
68 { enum dtype type;
69 uint8_t* src_filename, * data_name;
70 union ir_setdata_t* nextsib;
71 };
72 struct ir_frameinfo_t
73 { int facing, w, h; };
74 struct ir_framedata_t
75 { struct ir_setdata_header_t header;
76 struct ir_frameinfo_t frameinfo;
77 };
78 struct ir_framebox_t
79 { struct ir_setdata_header_t header;
80 struct ir_framedata_t framesheets[FACING_MAX];
81 struct ir_framedata_t mapsheets[FACING_MAX];
82 };
83 struct ir_simplex_t { struct ir_setdata_header_t header; };
84 struct ir_link_t
85 { struct ir_setdata_header_t header;
86 struct ir_classld_t* classld;
87 struct ir_setld_t* setld;
88 enum ltype type;
89 };
90 union ir_setdata_t
91 { struct ir_setdata_header_t header;
92 struct ir_framebox_t framebox;
93 struct ir_framedata_t framesheet;
94 struct ir_framedata_t mapsheet;
95 struct ir_simplex_t audio;
96 struct ir_link_t link;
97 };
98 struct ir_class_t
99 { struct ir_class_t* nextchild, * nextsib;
100 struct ir_set_t* root_set;
101 uint8_t* name;
102 };
103 struct ir_set_t
104 { struct ir_set_t* nextchild, * nextsib;
105 uint32_t ref;
106 uint8_t* name;
107 struct ir_framebox_t* frameboxes;
108 struct ir_simplex_t* audio;
109 struct ir_link_t* links;
110 };
111 /* Functions */
112 static inline
113 struct ir_framebox_t* ir_set_add_framebox(struct ir_set_t*,uint8_t*);
114 static inline
115 union ir_setdata_t* ir_framedata (enum dtype,const uint8_t*,apc_facing,int,int);
116 static inline
117 int bytes_identical(const uint8_t*,const uint8_t*);
118 static inline
119 int classnames_identical(const uint8_t*,const uint8_t*);
120 static
121 void* stack_alloc(size_t);
122 #define struct_alloc(_T) ((struct _T*) stack_alloc(sizeof(struct _T)))
123 #define struct_clear(_S) (memset((_S), 0, sizeof(*(_S))))
124 static
125 uint8_t* name_alloc(const uint8_t*);
126 static
127 uint8_t* classname_alloc(const uint8_t*);
128 static inline
129 void* pagelist_pop(struct pagelist_t*,size_t);
130 #define $($)#$
131 #define pagelist_alloc(pagelist) do { \
132 pagelist.head->header.next = (struct pagenode_t*) malloc(pagelist.pagesize); \
133 if (pagelist.head->header.next == NULL) \
134 eprintf("Memory allocation error\n"); \
135 struct_clear(pagelist.head->header.next); \
136 pagelist.head = pagelist.head->header.next; \
137 pagelist.head->header.head = pagelist.head->root; \
138 } while (0)
139 #define pagelist_init(pagelist,size) do { \
140 pagelist.pagesize = size; \
141 pagelist.root = (struct pagenode_t*) malloc(size); \
142 if (pagelist.root == NULL) \
143 eprintf("Memory allocation error\n"); \
144 struct_clear(pagelist.root); \
145 pagelist.head = pagelist.root; \
146 pagelist.head->header.head = pagelist.head->root; \
147 } while (0)
148 static
149 void pagenode_free(struct pagenode_t*);
150 #define REFHASH(ref) (XXH32(&ref, sizeof(uint32_t), 0xCEED) & 0xCFF)
151
152 extern //apc.c
153 long sys_pagesize;
154 static
155 struct pagelist_t datapages, namepages, refhashpages;
156 static
157 struct ir_class_t root_class = { .name = (uint8_t*)"." };
158
159 /* Init */
160 int ir_init
161 ( void )
162 { pagelist_init(datapages, (size_t)SYS_PAGESIZE);
163 pagelist_init(namepages, (size_t)NAME_PAGESIZE);
164 pagelist_init(refhashpages, (size_t)SYS_PAGESIZE);
165 return 0;
166 }
167
168 /* Quit/Cleanup */
169 void ir_quit
170 ( void )
171 { pagenode_free(datapages.root);
172 pagenode_free(namepages.root);
173 pagenode_free(refhashpages.root);
174 }
175
176 /* Recursively clean pagenode linked list, freeing last first */
177 static
178 void pagenode_free
179 ( struct pagenode_t* pagenode )
180 { if (pagenode->header.next != NULL)
181 pagenode_free(pagenode->header.next);
182 free(pagenode);
183 }
184
185 /* Link */
186 int ir_linker
187 ( void )
188 {
189 return 0;
190 }
191
192 /* Condense */
193 int ir_condenser
194 ( void )
195 { return 0; }
196
197 /* Return the class's name string */
198 uint8_t* ir_class_name
199 ( struct ir_class_t* class )
200 { return class->name; }
201
202 /* Return a pointer to the root class */
203 struct ir_class_t* ir_class_root
204 ( void )
205 { return &root_class; }
206
207 /* Add a subclass to a class
208 Attempts to create a new subclass in the provided class, returning
209 the class if it already exists
210 */
211 struct ir_class_t* ir_class_addchild
212 ( struct ir_class_t* class,
213 const uint8_t* name
214 )
215 { struct ir_class_t* iter;
216 if (class->nextchild == NULL)
217 { class->nextchild = struct_alloc(ir_class_t);
218 struct_clear(class->nextchild);
219 class->nextchild->name = classname_alloc(name);
220 return class->nextchild;
221 }
222 iter = class->nextchild;
223 if (iter->name == NULL)
224 eprintf("Null name pointer in class %p\n", iter);
225 if (name == NULL)
226 eprintf("Null child added to class %s\n", iter->name);
227 check:
228 if (classnames_identical(iter->name, name))
229 return iter;
230 if (iter->nextsib != NULL)
231 { iter = iter->nextsib;
232 goto check;
233 }
234 iter->nextsib = struct_alloc(ir_class_t);
235 struct_clear(iter->nextsib);
236 iter->nextsib->name = classname_alloc(name);
237 return iter->nextsib;
238 }
239
240 /* Add a set to a class
241 Attempts to create a new root set in the specified class, returning
242 the set if it already exists
243 */
244 struct ir_set_t* ir_class_addset
245 ( struct ir_class_t* class,
246 const uint8_t* name
247 )
248 { struct ir_set_t* iter;
249 if (class->root_set == NULL)
250 { class->root_set = struct_alloc(ir_set_t);
251 struct_clear(class->root_set);
252 class->root_set->name = name_alloc(name);
253 return class->root_set;
254 }
255 iter = class->root_set;
256 if (iter->name == NULL)
257 eprintf("Null name pointer in class %p\n", iter);
258 if (name == NULL)
259 eprintf("Null set added to class %U\n", iter->name);
260 check:
261 if (bytes_identical(iter->name, name))
262 return iter;
263 if (iter->nextsib != NULL)
264 { iter = iter->nextsib;
265 goto check;
266 }
267 iter->nextsib = struct_alloc(ir_set_t);
268 struct_clear(iter->nextsib);
269 iter->nextsib->name = name_alloc(name);
270 return iter->nextsib;
271 }
272
273 struct ir_set_t* ir_set_from_ref
274 ( uint32_t ref )
275 { uint16_t hash;
276 struct ir_set_t** iters;
277 struct pagenode_t* iterp;
278 iterp = refhashpages.root;
279 hash = REFHASH(ref);
280 do
281 iters = ((struct ir_set_t**) iterp->root) + hash;
282 while (*iters != NULL && (*iters)->ref != ref && (iterp = iterp->header.next) != NULL);
283 return *iters;
284 }
285
286
287 /* Add a set to a set
288 Attempts to create a new subset of the specified set, returning the
289 child if it already exists
290 */
291 struct ir_set_t* ir_set_addchild
292 ( struct ir_set_t* set,
293 const uint8_t* name
294 )
295 { struct ir_set_t* iter;
296 if (set->nextchild == NULL)
297 { set->nextchild = struct_alloc(ir_set_t);
298 struct_clear(set->nextchild);
299 set->nextchild->name = name_alloc(name);
300 return set->nextchild;
301 }
302 iter = set->nextchild;
303 if (name == NULL)
304 eprintf("Null child added to set %s\n", iter->name);
305 if (iter->name == NULL)
306 eprintf("Null name pointer in set %p\n", iter);
307 check:
308 if (bytes_identical(iter->name, name))
309 return iter;
310 if (iter->nextsib != NULL)
311 { iter = iter->nextsib;
312 goto check;
313 }
314 iter->nextsib = struct_alloc(ir_set_t);
315 struct_clear(iter->nextsib);
316 iter->nextsib->name = name_alloc(name);
317 return iter->nextsib;
318 }
319
320 /* Add a framebox to a set
321 Attempts to create a new framebox of the specified set, returning
322 the framebox if it already exists
323 Name is not allocated, but assigned, unlike other "XXX_add" functions where
324 name is duplicated into IR's internal array.
325 */
326 static inline
327 struct ir_framebox_t* ir_set_add_framebox
328 ( struct ir_set_t* set,
329 uint8_t* name
330 )
331 { struct ir_framebox_t* iter;
332 if (set->frameboxes == NULL)
333 { set->frameboxes = struct_alloc(ir_framebox_t);
334 struct_clear(set->frameboxes);
335 set->frameboxes->header.data_name = name;
336 return set->frameboxes;
337 }
338 iter = set->frameboxes;
339 check:
340 if (bytes_identical(iter->header.data_name, name))
341 return iter;
342 if (iter->header.nextsib != NULL)
343 { iter = (struct ir_framebox_t*) iter->header.nextsib;
344 goto check;
345 }
346 iter->header.nextsib = (union ir_setdata_t*) struct_alloc(ir_framebox_t);
347 struct_clear(iter->header.nextsib);
348 iter->header.nextsib->header.data_name = name;
349 return (struct ir_framebox_t*) (iter->header.nextsib);
350 }
351
352 /* Match two null-terminated bytestrings
353 Return 1 if the two bytestrings are identical, else 0
354 */
355 static inline
356 int bytes_identical
357 ( const uint8_t* stra,
358 const uint8_t* strb
359 )
360 { int ca, cb;
361 do {
362 ca = *stra++;
363 cb = *strb++;
364 } while (ca && ca != '_' && ca == cb);
365 return (ca == cb);
366 }
367
368 static inline
369 int classnames_identical
370 ( const uint8_t* stra,
371 const uint8_t* strb
372 )
373 { int ca, cb;
374 do {
375 ca = *stra++;
376 cb = *strb++;
377 } while (ca && ca == cb);
378 return (ca == cb);
379 }
380
381 /* Assign Setdata to Set */
382 void ir_set_assign_data
383 ( struct ir_set_t* set,
384 union ir_setdata_t* setdata
385 )
386 { struct ir_framebox_t* framebox;
387 struct ir_simplex_t* simplex;
388 switch (setdata->header.type)
389 { case FSDAT:
390 framebox = ir_set_add_framebox(set, setdata->header.data_name);
391 if (framebox->framesheets[setdata->framesheet.frameinfo.facing].header.data_name != NULL)
392 wprintf("Duplicate framesheet [%i] %s\n",
393 setdata->framesheet.frameinfo.facing, setdata->header.data_name);
394 framebox->framesheets[setdata->framesheet.frameinfo.facing] = setdata->framesheet;
395 break;
396 case MSDAT:
397 framebox = ir_set_add_framebox(set, setdata->header.data_name);
398 if (framebox->mapsheets[setdata->mapsheet.frameinfo.facing].header.data_name != NULL)
399 wprintf("Duplicate mapsheet [%i] %s\n",
400 setdata->mapsheet.frameinfo.facing, setdata->header.data_name);
401 framebox->mapsheets[setdata->mapsheet.frameinfo.facing] = setdata->mapsheet;
402 break;
403 case ADAT:
404 if (set->audio == NULL)
405 { set->audio = (struct ir_simplex_t*) setdata;
406 return;
407 }
408 simplex = set->audio;
409 while (simplex->header.nextsib != NULL)
410 if (bytes_identical(simplex->header.data_name, setdata->header.data_name))
411 { wprintf("Duplicate audio %s\n", setdata->header.data_name);
412 *simplex = setdata->audio;
413 //setdata is now a pointer to redundant, unused memory.
414 return;
415 }
416 else
417 simplex = (struct ir_simplex_t*) simplex->header.nextsib;
418 setdata->audio.header.nextsib = (union ir_setdata_t*) set->audio;
419 set->audio = (struct ir_simplex_t*) setdata;
420 break;
421 case LDAT:
422 setdata->link.header.nextsib = (union ir_setdata_t*) set->links;
423 set->links = (struct ir_link_t*) setdata;
424 break;
425 default:
426 fprintf(stderr, "Unknown setdata type %x\n", setdata->header.type);
427 exit(-1);
428 }
429 }
430
431 void ir_set_assign_ref
432 ( struct ir_set_t* set,
433 uint32_t ref
434 )
435 { uint16_t hash, oldhash;
436 struct ir_set_t** iters;
437 struct pagenode_t* iterp;
438 uint32_t oldref;
439 oldref = set->ref;
440 oldhash = 0;
441 hash = REFHASH(ref);
442 iterp = refhashpages.root;
443 check_depth:
444 iters = ((struct ir_set_t**) iterp->root) + hash;
445 if (*iters == NULL || *iters == set)
446 *iters = set;
447 else
448 { if (iterp->header.next == NULL)
449 pagelist_alloc(refhashpages);
450 iterp = iterp->header.next;
451 goto check_depth;
452 }
453 if (oldref != 0)
454 { wprintf("Ref override: 0x%x -> 0x%x for set %s\n", oldref, ref, set->name);
455 if (oldhash != 0)
456 *iters = NULL;
457 else
458 { oldhash = hash;
459 hash = REFHASH(oldref);
460 goto check_depth;
461 }
462 }
463 set->ref = ref;
464 }
465
466 void ir_data_assign_path
467 ( union ir_setdata_t* setdata,
468 const uint8_t* path
469 )
470 { if (path == NULL)
471 eprintf("Null path in data %s\n", setdata->header.data_name);
472 if (setdata->header.src_filename != NULL)
473 wprintf("Path override: %s -> %s for setdata %s\n",
474 setdata->header.src_filename, path, setdata->header.data_name);
475 setdata->header.src_filename = name_alloc(path);
476 }
477
478 union ir_setdata_t* ir_framesheet
479 ( const uint8_t* name,
480 apc_facing d,
481 int width,
482 int height
483 )
484 { return ir_framedata(FSDAT, name, d, width, height); }
485
486 union ir_setdata_t* ir_mapsheet
487 ( const uint8_t* name,
488 apc_facing d,
489 int width,
490 int height
491 )
492 { return ir_framedata(MSDAT, name, d, width, height); }
493
494 static inline
495 union ir_setdata_t* ir_framedata
496 ( enum dtype type,
497 const uint8_t* name,
498 apc_facing d,
499 int width,
500 int height
501 )
502 { struct ir_framedata_t* framedata = struct_alloc(ir_framedata_t);
503 struct_clear(framedata);
504 if (name == NULL)
505 eprintf("Null name in set allocation\n");
506 framedata->header.type = type;
507 framedata->header.data_name = name_alloc(name);
508 framedata->frameinfo.facing = d;
509 framedata->frameinfo.w = width;
510 framedata->frameinfo.h = height;
511 return (union ir_setdata_t*) framedata;
512 }
513
514 union ir_setdata_t* ir_audio
515 ( const uint8_t* name )
516 { struct ir_simplex_t* audio = struct_alloc(ir_simplex_t);
517 struct_clear(audio);
518 if (name == NULL)
519 eprintf("Null audio\n");
520 audio->header.type = ADAT;
521 audio->header.data_name = name_alloc(name);
522 return (union ir_setdata_t*) audio;
523 }
524
525
526 /* Create classld that points to a class */
527 struct ir_classld_t* ir_classld_from_class
528 ( struct ir_class_t* class )
529 { struct ir_classld_t* classld;
530 if (class == NULL)
531 eprintf("Null class in classld\n");
532 classld = struct_alloc(ir_classld_t);
533 struct_clear(classld);
534 classld->root_class = class;
535 return classld;
536 }
537
538 struct ir_setld_t* ir_setld_from_ref
539 ( uint32_t ref )
540 { struct ir_setld_t* setld;
541 setld = struct_alloc(ir_setld_t);
542 struct_clear(setld);
543 setld->ref = ref;
544 return setld;
545 }
546
547 struct ir_setld_t* ir_setld_from_classld
548 ( struct ir_classld_t* classld,
549 const uint8_t* name
550 )
551 { struct ir_setld_t* setld;
552 setld = struct_alloc(ir_setld_t);
553 struct_clear(setld);
554 setld->namelist = struct_alloc(ir_namelist_t);
555 struct_clear(setld->namelist);
556 setld->namelist_head = setld->namelist;
557 setld->namelist_head->name = name_alloc(name);
558 setld->classld = classld;
559 return setld;
560 }
561
562 struct ir_setld_t* ir_setld_addchild
563 ( struct ir_setld_t* setld,
564 const uint8_t* name
565 )
566 { if (setld->namelist == NULL)
567 { setld->namelist = struct_alloc(ir_namelist_t);
568 struct_clear(setld->namelist);
569 setld->namelist_head = setld->namelist;
570 }
571 else
572 { setld->namelist_head->nextsib = struct_alloc(ir_namelist_t);
573 struct_clear(setld->namelist_head->nextsib);
574 setld->namelist_head = setld->namelist_head->nextsib;
575 }
576 setld->namelist_head->name = name_alloc(name);
577 return setld;
578 }
579
580 union ir_setdata_t* ir_link
581 ( enum ltype link_type,
582 struct ir_setld_t* setld,
583 const uint8_t* name
584 )
585 { struct ir_link_t* link;
586 link = struct_alloc(ir_link_t);
587 struct_clear(link);
588 link->header.type = LDAT;
589 link->type = link_type;
590 link->classld = setld->classld;
591 link->setld = setld;
592 if (link_type != OLINK && name != NULL)
593 link->header.data_name = name_alloc(name);
594 return (union ir_setdata_t*) link;
595 }
596
597 static inline
598 void* pagelist_pop
599 ( struct pagelist_t* pagelist,
600 size_t size
601 )
602 { size_t headsize = PL_HEADSIZE((*pagelist));
603 if (!headsize)
604 { free(pagelist->head);
605 pagelist->head = pagelist->root;
606 while (pagelist->head->header.next != NULL)
607 pagelist->head = pagelist->head->header.next;
608 }
609 if (headsize < size)
610 eprintf("Attempted to pop unaligned value from pagelist\n");
611 pagelist->head->header.head -= size;
612 return pagelist->head->header.head;
613 }
614
615 static
616 void* stack_alloc
617 ( size_t bytes )
618 { void* p;
619 if (PL_HEADMEM(datapages) < bytes)
620 pagelist_alloc(datapages);
621 p = datapages.head->header.head;
622 datapages.head->header.head += bytes;
623 return p;
624 }
625
626 static
627 uint8_t* name_alloc
628 ( const uint8_t* name_src )
629 { const uint8_t* iter;
630 uint8_t* name;
631 int head_mem;
632 copy:
633 name = (uint8_t*)namepages.head->header.head;
634 iter = name_src;
635 for (head_mem = PL_HEADMEM(namepages); *iter && *iter != '_' && *iter != '.' && head_mem; head_mem--)
636 *(namepages.head->header.head)++ = *iter++;
637 if (head_mem < 1) //not enough room
638 { pagelist_alloc(namepages);
639 goto copy;
640 }
641 *(namepages.head->header.head)++ = '\0';
642 return name;
643 }
644
645 static
646 uint8_t* classname_alloc
647 ( const uint8_t* name_src )
648 { const uint8_t* iter;
649 uint8_t* name;
650 int head_mem;
651 copy:
652 name = (uint8_t*)namepages.head->header.head;
653 iter = name_src;
654 for (head_mem = PL_HEADMEM(namepages); *iter && head_mem; head_mem--)
655 *(namepages.head->header.head)++ = *iter++;
656 if (head_mem < 1) //not enough room
657 { pagelist_alloc(namepages);
658 goto copy;
659 }
660 *(namepages.head->header.head)++ = '\0';
661 return name;
662 }
663
664 static void crawl_class(struct ir_class_t*);
665 static void crawl_set(struct ir_set_t*,int);
666 void ir_test(void)
667 { uprintf("IR From Directory: %s\n",getcwd(NULL,255));
668 crawl_class(&root_class);
669 }
670
671 #define pspace(num) for (i = 0; i < (num); i++) putchar('.')
672 static
673 void crawl_class
674 ( struct ir_class_t* class )
675 { struct ir_class_t* iter;
676 for (iter = class->nextchild; iter != NULL; iter = iter->nextsib)
677 { wprintf("%U/\n", iter->name);
678 if(chdir((char*)iter->name))
679 eprintf("CHDIR %U from %s\n",iter->name,getcwd(NULL,255));
680 crawl_class(iter);
681 if (iter->root_set != NULL)
682 crawl_set(iter->root_set, 0);
683 uprintf("%U\\\n",iter->name);
684 if (chdir(".."))
685 eprintf("CHDIR ..\n");
686 }
687 }
688
689 static
690 void crawl_set
691 ( struct ir_set_t* set,
692 int depth
693 )
694 { struct ir_set_t* setlist[64], ** slp, * iter;
695 int i;
696 pspace(depth * 12);
697 i = depth;
698 slp = setlist;
699 for(iter = set; iter != NULL; iter = iter->nextchild)
700 { uprintf("[%10U]", iter->name);
701 *slp++ = iter;
702 i++;
703 }
704 --i;
705 putchar('\n');
706 for(iter = *--slp; slp >= setlist; iter = *--slp, --i)
707 if (iter->nextsib != NULL)
708 crawl_set(iter->nextsib,i);
709 }