bringing SDL 1.2.14 from vendor into the main branch
[AROS-Contrib.git] / regina / instore.c
blobdca32395b3cc3d2193212d646b95387f85a2c0d4
1 /* This files solves the problems with instore macros.
2 * While parsing a special tree of type tnode is created
3 * and fold together with containing strings to one big
4 * beast (called a tinned tree, american: canned heat :-) ).
5 * Then we have two objects: a special tree and the tinned tree.
6 * There is a one to one releationship between them.
7 * The tinned tree may be expanded to a tree by ExpandTinnedTree().
8 * The tree is tinned by TinTree() which should only be called from
9 * the lexical analyser.
10 * We are protected against multithreading while normal parsing.
11 * Destroying the tree and tinning/expanding is done in
12 * multithreaded mode.
15 #include "rexx.h"
16 #include "rxiface.h"
17 #include <assert.h>
19 /* We begin with some odd tricks. We want a fast but memory
20 * saving routine to build the tree. We don't want to copy
21 * tree leaves more than one. Thus we pack them into buckets.
22 * This allows a very fast kind of searching. Keep in mind
23 * that we must create a pointerless list which is included
24 * in the tinned tree.
26 * This structure is called ttree, have a look at types.h.
28 * DEFAULT_TTREE_ELEMS defines the normal number of elements in the ttree.
29 * The more elements the better is the performance in searching. The less
30 * elements the better is the memory waste overhead. We want some more space
31 * than MAX_INTERNAL_SIZE in memory.c. This leads to "normal" memory
32 * allocation and can be freed without problems if we parse a nested
33 * Rexx program. This is currently 2048. We allocate nearly 8K. This
34 * will give a reasonable performance and a slight overhead for
35 * external(!) functions, 4K in the middle.
37 #define DEFAULT_TTREE_ELEMS (8192 / sizeof(treenode))
39 /* Same as before but for offsrclines: */
40 #define DEFAULT_OTREE_ELEMS (4096 / sizeof(offsrcline))
42 static ttree *CurrentT = NULL;
43 static otree *CurrentO = NULL;
44 static nodeptr Reused = NULL;
45 static const char MagicHeader[] = MAGIC;
47 /* NewProg indicates the start of a new parsing sequence. We don't allow
48 * a nesting call. In case of an error is is the job of fetch_... to do the
49 * cleanup.
51 void NewProg(void)
53 CurrentT = NULL; /* Trivial */
54 CurrentO = NULL; /* Trivial */
55 Reused = NULL; /* Can't reuse stuff of another parsing process */
58 /* EndProg is called at the end of the parsing process. Start is the
59 * starting element of the later interpreter tree.
60 * parser_data.root is assigned.
62 void EndProg(nodeptr Start)
64 parser_data.root = Start; /* trivial, too */
65 CurrentT = NULL;
66 CurrentO = NULL;
67 Reused = NULL; /* Can't reuse stuff of another parsing process */
70 /* FreshNode returns a new ttree element. Call only within a NewProg/EndProg
71 * calling sequence.
72 * If you don't want the returned node WHILE PARSING because you want to do
73 * some tricks the the node at a later time, you are allowed to call
74 * RejectNode(). Rejected nodes are tried to be passed back to the used
75 * nodes.
77 nodeptr FreshNode(void)
79 nodeptr h;
81 if (Reused != NULL) /* This should be put back first */
83 h = Reused;
84 Reused = Reused->next;
85 h->next = NULL; /* Everything except nodeindex is 0 now */
86 return(h);
89 if (CurrentT && (CurrentT->num < CurrentT->max)) /* bucket not full */
91 memset(CurrentT->elems + CurrentT->num, 0, sizeof(treenode));
92 CurrentT->elems[CurrentT->num].nodeindex = CurrentT->sum + CurrentT->num;
93 return(CurrentT->elems + CurrentT->num++);
96 if (CurrentT == NULL) /* First call */
98 parser_data.nodes = Malloc_TSD(parser_data.TSD, sizeof(ttree));
99 CurrentT = parser_data.nodes;
100 CurrentT->sum = 0;
102 else /* current bucket is full */
104 CurrentT->next = Malloc_TSD(parser_data.TSD, sizeof(ttree));
105 CurrentT->next->sum = CurrentT->sum + CurrentT->num;
106 CurrentT = CurrentT->next;
109 /* always */
110 CurrentT->next = NULL;
111 CurrentT->max = DEFAULT_TTREE_ELEMS;
112 CurrentT->num = 1;
113 CurrentT->elems = Malloc_TSD(parser_data.TSD,
114 CurrentT->max * sizeof(treenode));
116 memset(CurrentT->elems, 0, sizeof(treenode));
117 CurrentT->elems[0].nodeindex = CurrentT->sum;
118 return(CurrentT->elems);
121 /* RejectNode gives the argument back to the pool of unused treenode entries
122 * which are managed and passed back by FreshNode().
123 * You should use the function ONLY IF YOU ARE WITHING THE PARSING PROCESS!
124 * It is not guaranteed that the memory of the entry is freed. It can only
125 * be reused.
126 * Note that the content of the entry is NOT FREED in any kind.
128 void RejectNode(nodeptr NoLongerUsed)
130 unsigned long idx;
132 assert(CurrentT != NULL);
133 /* CurrentT == NULL can't happen, since CurrentT is only set within the
134 * parsing process while at least one treenode has been returned.
136 if (CurrentT == NULL) /* In case of no assertion we return simply */
137 return;
139 /* Save exactly the nodeindex and destroy everything else */
140 idx = NoLongerUsed->nodeindex;
141 memset(NoLongerUsed, 0, sizeof(treenode)); /* Clean it up */
142 NoLongerUsed->nodeindex = idx;
144 NoLongerUsed->next = Reused;
145 Reused = NoLongerUsed;
148 /* FreshLine returns a new otree element. Call only within a NewProg/EndProg
149 * calling sequence.
151 offsrcline *FreshLine(void)
153 if (CurrentO && (CurrentO->num < CurrentO->max)) /* bucket not full */
155 memset(CurrentO->elems + CurrentO->num, 0, sizeof(offsrcline));
156 return(CurrentO->elems + CurrentO->num++);
159 if (CurrentO == NULL) /* First call */
161 parser_data.nodes = Malloc_TSD(parser_data.TSD, sizeof(otree));
162 CurrentO = parser_data.srclines;
163 CurrentO->sum = 0;
165 else /* current bucket is full */
167 CurrentO->next = Malloc_TSD(parser_data.TSD, sizeof(otree));
168 CurrentO->next->sum = CurrentO->sum + CurrentO->num;
169 CurrentO = CurrentO->next;
172 /* always */
173 CurrentO->next = NULL;
174 CurrentO->max = DEFAULT_OTREE_ELEMS;
175 CurrentO->num = 1;
176 CurrentO->elems = Malloc_TSD(parser_data.TSD,
177 CurrentO->max * sizeof(offsrcline));
179 memset(CurrentO->elems, 0, sizeof(offsrcline));
180 return(CurrentO->elems);
183 /*****************************************************************************
184 *****************************************************************************
185 * start of the multithreaded part *******************************************
186 *****************************************************************************
187 *****************************************************************************/
190 /* DestroyNode kills all allocated elements within a nodeptr
191 * without freeing the node itself.
193 static void DestroyNode(const tsd_t *TSD, nodeptr p)
195 int type ;
197 if (p->name)
198 Free_stringTSD( p->name ) ;
200 if (p->now)
201 FreeTSD( p->now ) ;
203 type = p->type ;
204 if (type == X_CON_SYMBOL || type == X_STRING)
206 if (p->u.number)
208 FreeTSD( p->u.number->num ) ;
209 FreeTSD( p->u.number ) ;
212 if (type==X_SIM_SYMBOL || type==X_STEM_SYMBOL || type==X_HEAD_SYMBOL ||
213 type==X_CTAIL_SYMBOL || type==X_VTAIL_SYMBOL )
215 if (p->u.varbx)
217 detach( TSD, p->u.varbx ) ;
221 if (type == X_CEXPRLIST)
223 if (p->u.strng)
224 Free_stringTSD( p->u.strng ) ;
228 /* DestroyInternalParsingTree frees all allocated memory used by a parsing
229 * tree. The structure itself is not freed.
231 void DestroyInternalParsingTree(const tsd_t *TSD, internal_parser_type *ipt)
233 ttree *tr, *th;
234 otree *or, *oh;
235 lineboxptr lr, lh;
236 labelboxptr ar, ah;
237 unsigned long i;
239 if (!ipt)
240 return;
242 /* Cleanup all the nodes */
243 if (ipt->nodes != NULL)
245 tr = ipt->nodes;
247 while (tr)
249 for (i = 0; i < tr->num; i++)
250 DestroyNode(TSD, tr->elems + i);
251 th = tr->next;
252 FreeTSD(tr->elems);
253 FreeTSD(tr);
254 tr = th;
257 ipt->nodes = NULL;
259 ipt->root = NULL; /* not really needed */
261 /* Cleanup all the lineboxes */
262 if (ipt->first_source_line != NULL)
264 lr = ipt->first_source_line;
266 while (lr)
268 lh = lr->next;
269 Free_stringTSD(lr->line);
270 FreeTSD(lr);
271 lr = lh;
274 ipt->first_source_line = ipt->last_source_line = NULL;
277 /* Cleanup all the labelboxes */
278 if (ipt->first_label != NULL)
280 ar = ipt->first_label;
282 while (ar)
284 ah = ar->next;
285 FreeTSD(ar);
286 ar = ah;
289 ipt->first_label = ipt->last_label = NULL;
292 if (ipt->sort_labels != NULL)
294 FreeTSD(ipt->sort_labels);
296 ipt->sort_labels = NULL;
299 /* Cleanup the incore sourceline informations */
300 /* Cleanup all the nodes */
301 if (ipt->srclines != NULL)
303 or = ipt->srclines;
305 while (or)
307 oh = or->next;
308 FreeTSD(or->elems);
309 FreeTSD(or);
310 or = oh;
313 ipt->srclines = NULL;
315 if (ipt->kill)
316 Free_stringTSD(ipt->kill);
317 ipt->kill = NULL;
320 /* ExpandTinnedTree expands the external tree from a former parsing operation
321 * to a fully usable tree. All allocations and relacations are done to fake
322 * a normal parsing operation.
323 * The external tree won't be used any longer after this operation but the
324 * external tree must have been checked before this operation.
325 * The freshly allocated tree is returned.
327 internal_parser_type ExpandTinnedTree(const tsd_t *TSD,
328 const external_parser_type *ept,
329 unsigned long size,
330 const char *incore_source,
331 unsigned long incore_source_length)
333 internal_parser_type ipt;
334 unsigned long i,j;
335 const extstring *es;
336 const offsrcline *lastsrcline;
337 nodeptr this;
339 memset(&ipt, 0, sizeof(ipt));
341 /* We build the sourcelines first *****************************************/
342 if (incore_source_length == 0)
343 incore_source = NULL;
344 if (ept->NumberOfSourceLines == 0)
345 incore_source = NULL;
346 if (incore_source) /* Its worth to check exactly */
348 lastsrcline = (const offsrcline *) ((char *) ept + ept->source);
349 lastsrcline += ept->NumberOfSourceLines - 1;
350 j = lastsrcline->length + lastsrcline->offset;
351 /* j shall be very close to the end of the source string. It may
352 * follow a linefeed (or carriage return/linefeed) and probably a
353 * ^Z for CP/M descendents which includes Microsoft products. It's
354 * fais to assume the following check:
356 if ((j > incore_source_length) ||
357 (j + 3 < incore_source_length))
358 incore_source = NULL;
360 if (incore_source) /* We are sure enough to use the source string */
362 ipt.incore_source = incore_source;
363 ipt.srclines = MallocTSD(sizeof(otree));
364 ipt.srclines->sum = 0;
365 ipt.srclines->next = NULL;
366 ipt.srclines->max = ept->NumberOfSourceLines;
367 ipt.srclines->num = ipt.nodes->max;
368 ipt.srclines->elems = MallocTSD(ipt.srclines->num * sizeof(offsrcline));
369 memcpy(ipt.srclines->elems,
370 (char *) ept + ept->source,
371 ipt.srclines->num * sizeof(offsrcline));
373 /**************************************************************************/
375 ipt.tline = -1; /* unused */
376 ipt.tstart = -1; /* unused */
377 ipt.result = 0; /* unused */
378 ipt.first_label = ipt.last_label = NULL; /* initialize it for newlabel() */
379 ipt.numlabels = 0; /* initialize it for newlabel() */
380 ipt.sort_labels = NULL; /* initialize it for newlabel() */
382 ipt.nodes = MallocTSD(sizeof(ttree));
383 ipt.nodes->sum = 0;
384 ipt.nodes->next = NULL;
385 ipt.nodes->max = ept->NumberOfTreeElements;
386 ipt.nodes->num = ipt.nodes->max;
387 ipt.nodes->elems = MallocTSD(ipt.nodes->num * sizeof(treenode));
389 memcpy(ipt.nodes->elems,
390 (char *) ept + ept->tree,
391 ipt.nodes->num * sizeof(treenode));
392 ipt.root = ipt.nodes->elems + ept->TreeStart;
394 /* Everything is ready for a relocation step. Don't forget to *************
395 * create the labelboxes as necessary.
397 for (i = 0;i < ept->NumberOfTreeElements;i++)
399 this = ipt.nodes->elems + i;
400 if (this->name)
402 es = (extstring *) ((char *) ept + (unsigned long) this->name);
403 this->name = Str_makeTSD(es->length);
404 this->name->len = es->length;
405 memcpy(this->name->value,
406 es + 1 /* position of string content */,
407 es->length);
409 if (this->u.strng)
411 es = (extstring *) ((char *) ept + (unsigned long) this->u.strng);
412 this->u.strng = Str_makeTSD(es->length);
413 this->u.strng->len = es->length;
414 memcpy(this->u.strng->value,
415 es + 1 /* position of string content */,
416 es->length);
419 if (this->type == X_LABEL)
420 newlabel(TSD, &ipt, this);
422 if (this->next == (nodeptr) (unsigned long) -1)
423 this->next = NULL;
424 else
425 this->next = ipt.nodes->elems + (unsigned long) this->next;
426 for (j = 0;j < sizeof(this->p) / sizeof(this->p[0]);j++)
428 if (this->p[j] == (nodeptr) (unsigned long) -1)
429 this->p[j] = NULL;
430 else
431 this->p[j] = ipt.nodes->elems + (unsigned long) this->p[j];
434 size = size; /* keep compiler happy */
436 return(ipt);
439 /* We must take care of the alignment of structure. We may get a SIGBUS in
440 * the following if we don't do it. We assume that an alignment for an
441 * unsigned long is sufficient for all types including structures. We also
442 * assume a power of two for an unsigned's size.
444 #define USIZ sizeof(unsigned long)
445 #define USIZ_1 (USIZ-1)
446 /* Wastes one byte in average but is much faster */
447 #define StringSize(s) (((sizeof(extstring)+s->len)|USIZ_1)+1)
449 static unsigned long ComputeExternalSize(const internal_parser_type *ipt,
450 unsigned long *SourceLines,
451 unsigned long *Nodes)
453 otree *otp;
454 ttree *ttp;
455 nodeptr np;
456 unsigned long size = sizeof(external_parser_type);
457 unsigned long i, elems, bufchars;
459 /* sourceline table */
460 elems = 0;
461 otp = ipt->srclines;
462 while (otp != NULL)
464 elems += otp->sum;
465 otp = otp->next;
467 *SourceLines = elems;
468 size += elems * sizeof(offsrcline); /* the table */
470 /* nodetable */
471 elems = bufchars = 0;
472 ttp = ipt->nodes;
473 while (ttp) {
474 for (i = 0;i < ttp->num;i++)
476 elems++;
477 np = ttp->elems + i;
478 if (np->name)
479 bufchars += StringSize(np->name);
481 if ((np->type == X_CEXPRLIST) && np->u.strng)
482 /* should be save, since it isn't computed after the parsing step.*/
483 bufchars += StringSize(np->u.strng);
485 ttp = ttp->next;
487 *Nodes = elems;
488 size += elems * sizeof(treenode);
489 size += bufchars;
491 size += sizeof(((external_parser_type *)0)->Magic);
492 return(size);
495 /* FillStrings copies all offsrclines from the otree to base+start
496 * consecutively.
497 * The index just beyond the last copied byte is returned.
499 static unsigned long FillStrings(char *base, unsigned long start, const otree *otp)
501 while (otp != NULL)
503 memcpy(base + start, otp->elems, otp->num * sizeof(offsrcline));
504 start += otp->num * sizeof(offsrcline);
505 otp = otp->next;
507 return(start);
510 /* FillTree copies all treenodes of the ttree to base+buf in a relocatable
511 * manner. Look at ExpandTinnedTree() or types.h for a description.
512 * Each treenode is copied to the table and the containing strings are copied
513 * as extstrings to base+start which is incremented.
514 * The table must be large enough.
515 * The index just beyond the last copied character is returned.
517 static unsigned long FillTree(treenode *table, char *base, unsigned long start,
518 const ttree *ttp)
520 cnodeptr np;
521 unsigned long i,j;
522 extstring *e;
524 while (ttp)
526 for (i = 0;i < ttp->num;i++)
528 np = (cnodeptr) (ttp->elems + i);
529 *table = *np; /* Full copy includes unnecessary stuff but is fast */
531 if (np->name)
533 table->name = (streng *) start;
534 e = (extstring *) (base + start);
535 e->length = np->name->len;
536 memcpy(e + 1 /* just beyond the head */, np->name->value, e->length);
537 start += StringSize(np->name);
540 if ((np->type == X_CEXPRLIST) && np->u.strng)
542 table->u.strng = (streng *) start;
543 e = (extstring *) (base + start);
544 e->length = np->u.strng->len;
545 memcpy(e + 1, np->u.strng->value, e->length);
546 start += StringSize(np->u.strng);
548 else
549 table->u.strng = 0; /* Be sure not to run into troubles */
551 if (table->next == NULL)
552 table->next = (nodeptr) (unsigned long) -1;
553 else
554 table->next = (nodeptr) np->next->nodeindex;
555 for (j = 0;j < sizeof(np->p) / sizeof(np->p[0]);j++)
557 if (table->p[j] == NULL)
558 table->p[j] = (nodeptr) (unsigned long) -1;
559 else
560 table->p[j] = (nodeptr) np->p[j]->nodeindex;
562 table++;
564 ttp = ttp->next;
567 return(start);
570 /* TinTree "tins" a tree into an external structure. The complete structure
571 * is allocated by one call to IfcAllocateMemory. The returned value shall
572 * be used as an instore macro for RexxStart.
573 * *length is set to the allocated size of the memory block on return.
574 * ExpandedTinnedTree can expand the returned value and IaValidTin checks it.
576 external_parser_type *TinTree(const tsd_t *TSD,
577 const internal_parser_type *ipt,
578 unsigned long *length)
580 external_parser_type *retval;
581 unsigned long srclines, nodecount, len;
583 *length = ComputeExternalSize(ipt, &srclines, &nodecount);
585 retval = IfcAllocateMemory(*length);
586 if (retval == NULL)
587 return(NULL);
588 memset(retval, 0, sizeof(external_parser_type));
590 /* Build the envelope */
591 len = sizeof(MagicHeader); /* includes a terminating 0 */
592 if (len > sizeof(retval->Magic))
593 len = sizeof(retval->Magic);
594 memcpy(retval->Magic, MagicHeader, len);
595 len = sizeof(PARSE_VERSION_STRING);
596 if (len > sizeof(retval->ReginaVersion))
597 len = sizeof(retval->ReginaVersion);
598 memcpy(retval->ReginaVersion, PARSE_VERSION_STRING, len);
600 retval->arch_detector.s.one = 1;
601 retval->arch_detector.s.two = 2;
602 retval->arch_detector.s.ptr3 = (void *)3;
603 retval->arch_detector.s.ptr4 = (void *)4;
604 retval->OverallSize = (unsigned long) *length;
605 retval->NumberOfSourceLines = srclines;
606 retval->version = INSTORE_VERSION;
607 retval->NumberOfTreeElements = nodecount;
609 retval->source = sizeof(external_parser_type);
610 len = FillStrings((char *) retval,
611 sizeof(external_parser_type),
612 ipt->srclines);
614 retval->tree = len;
615 retval->TreeStart = ipt->root->nodeindex;
616 len = FillTree((treenode *) ((char *) retval + len),
617 (char *) retval,
618 len + nodecount*sizeof(treenode),
619 ipt->nodes);
621 memcpy((char *) retval + len, retval->Magic, sizeof(retval->Magic));
623 assert((unsigned long) len + sizeof(retval->Magic) == *length);
625 /* DEBUGGING: return NULL if you don't want tinned trees */
626 TSD = TSD; /* keep compiler happy */
627 return(retval);
630 /* IsValidTin returns 1 if the structure ept if of length eptlen and seems
631 * to contain a valid parsing tree. 0 is returned if this is not the case.
633 int IsValidTin(const external_parser_type *ept, unsigned long eptlen)
635 char Magic[sizeof(((external_parser_type *)0)->Magic)];
636 unsigned long len;
638 /* Some paranoia tests first: */
639 if ((ept == NULL) || (eptlen < sizeof(external_parser_type)))
640 return(0);
642 /* Be sure to fill Magic as described */
643 memset(Magic, 0, sizeof(Magic));
644 len = sizeof(MagicHeader); /* includes a terminating 0 */
645 if (len > sizeof(ept->Magic))
646 len = sizeof(ept->Magic);
647 memcpy(Magic, MagicHeader, len);
649 if (memcmp(Magic, ept->Magic, sizeof(Magic)) != 0)
650 return(0);
652 if ((ept->arch_detector.s.one != 1) ||
653 (ept->arch_detector.s.two != 2) ||
654 (ept->arch_detector.s.ptr3 != (void *)3) ||
655 (ept->arch_detector.s.ptr4 != (void *)4))
656 return(0);
658 if (ept->OverallSize != eptlen)
659 return(0);
661 if (ept->version != INSTORE_VERSION)
662 return(0);
664 if (memcmp(Magic,
665 (char *) ept + eptlen - sizeof(Magic),
666 sizeof(Magic)) != 0)
667 return(0);
669 return(1);