ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.38
Committed: Fri Feb 23 03:47:57 2024 UTC (2 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.37: +25 -6 lines
Log Message:
perf: Added array index optimization to calcomp routines

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: caldefn.c,v 2.37 2023/10/01 18:02:23 greg Exp $";
3 #endif
4 /*
5 * Store variable definitions.
6 *
7 * 7/1/85 Greg Ward
8 *
9 * 11/11/85 Added conditional compiles (OUTCHAN) for control output.
10 *
11 * 4/2/86 Added conditional compiles for function definitions (FUNCTION).
12 *
13 * 1/15/88 Added clock for caching of variable values.
14 *
15 * 11/16/88 Added VARDEF structure for hard linking.
16 *
17 * 5/31/90 Added conditional compile (REDEFW) for redefinition warning.
18 *
19 * 4/23/91 Added ':' assignment for constant expressions
20 *
21 * 8/7/91 Added optional context path to append to variable names
22 *
23 * 5/17/2001 Fixed clock counter wrapping behavior
24 *
25 * 2/19/03 Eliminated conditional compiles in favor of esupport extern.
26 */
27
28 #include "copyright.h"
29
30 #include <ctype.h>
31
32 #include "rterror.h"
33 #include "rtio.h"
34 #include "rtmisc.h"
35 #include "calcomp.h"
36
37 #ifndef NHASH
38 #define NHASH 521 /* hash size (a prime!) */
39 #endif
40
41 #define hash(s) (shash(s)%NHASH)
42
43 #define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
44
45 static double dvalue(char *name, EPNODE *d);
46
47 #define MAXCLOCK (1L<<31) /* clock wrap value */
48
49 unsigned long eclock = 0; /* value storage timer */
50
51 #define MAXCNTX 1023 /* maximum context length */
52
53 static char context[MAXCNTX+1]; /* current context path */
54
55 static VARDEF *hashtbl[NHASH]; /* definition list */
56 static int htndx; /* index for */
57 static VARDEF *htpos; /* ...dfirst() and */
58 static EPNODE *ochpos; /* ...dnext */
59 static EPNODE *outchan;
60
61 static int optimized = 0; /* are we optimized? */
62
63 EPNODE *curfunc = NULL;
64
65
66 void
67 fcompile( /* get definitions from a file */
68 char *fname
69 )
70 {
71 FILE *fp;
72
73 if (fname == NULL)
74 fp = stdin;
75 else if ((fp = fopen(fname, "r")) == NULL) {
76 eputs(fname);
77 eputs(": cannot open\n");
78 quit(1);
79 }
80 #ifdef getc_unlocked /* avoid stupid semaphores */
81 flockfile(fp);
82 #endif
83 initfile(fp, fname, 0);
84 while (nextc != EOF)
85 getstatement();
86 if (fname != NULL)
87 fclose(fp);
88 #ifdef getc_unlocked
89 else
90 funlockfile(fp);
91 #endif
92 }
93
94
95 void
96 scompile( /* get definitions from a string */
97 char *str,
98 char *fn,
99 int ln
100 )
101 {
102 initstr(str, fn, ln);
103 while (nextc != EOF)
104 getstatement();
105 }
106
107
108 double
109 varvalue( /* return a variable's value */
110 char *vname
111 )
112 {
113 return(dvalue(vname, dlookup(vname)));
114 }
115
116
117 double
118 evariable( /* evaluate a variable */
119 EPNODE *ep
120 )
121 {
122 VARDEF *dp = ep->v.ln;
123
124 return(dvalue(dp->name, dp->def));
125 }
126
127
128 void
129 varset( /* set a variable's value */
130 char *vname,
131 int assign,
132 double val
133 )
134 {
135 char *qname;
136 EPNODE *ep1, *ep2;
137 /* get qualified name */
138 qname = qualname(vname, 0);
139 /* check for quick set */
140 if ((ep1 = dlookup(qname)) != NULL && ep1->v.kid->type == SYM &&
141 (ep1->type == ':') <= (assign == ':')) {
142 ep2 = ep1->v.kid->sibling;
143 if (ep2->type == NUM) {
144 ep2->v.num = val;
145 ep1->type = assign;
146 return;
147 }
148 }
149 if (ep1 != NULL && esupport&E_REDEFW) {
150 wputs(qname);
151 if (ep1->type == ':')
152 wputs(": reset constant expression\n");
153 else
154 wputs(": reset expression\n");
155 }
156 /* hand build definition */
157 ep1 = newnode();
158 ep1->type = assign;
159 ep2 = newnode();
160 ep2->type = SYM;
161 ep2->v.name = savestr(vname);
162 addekid(ep1, ep2);
163 ep2 = newnode();
164 ep2->type = NUM;
165 ep2->v.num = val;
166 addekid(ep1, ep2);
167 if (assign == ':')
168 dremove(qname);
169 else
170 dclear(qname);
171 dpush(qname, ep1);
172 }
173
174
175 void
176 dclear( /* delete variable definitions of name */
177 char *name
178 )
179 {
180 VARDEF *vp;
181 EPNODE *dp;
182
183 while ((vp = varlookup(name)) != NULL &&
184 (dp = vp->def) != NULL && dp->type == '=') {
185 vp->def = dp->sibling;
186 epfree(dp,1);
187 varfree(vp);
188 }
189 }
190
191
192 void
193 dremove( /* delete all definitions of name */
194 char *name
195 )
196 {
197 EPNODE *ep;
198
199 while ((ep = dpop(name)) != NULL)
200 epfree(ep,1);
201 }
202
203
204 int
205 vardefined( /* return '=' or ':' if variable/constant defined */
206 char *name
207 )
208 {
209 EPNODE *dp = dlookup(name);
210
211 if (dp == NULL || dp->v.kid->type != SYM)
212 return(0);
213
214 return(dp->type);
215 }
216
217
218 char *
219 calcontext( /* set a new context path */
220 char *ctx
221 )
222 {
223 char *cpp;
224
225 if (ctx == NULL)
226 return(context); /* just asking */
227 while (*ctx == CNTXMARK)
228 ctx++; /* skip past marks */
229 if (!*ctx) {
230 context[0] = '\0'; /* empty means clear context */
231 return(context);
232 }
233 cpp = context; /* start context with mark */
234 *cpp++ = CNTXMARK;
235 do { /* carefully copy new context */
236 if (cpp >= context+MAXCNTX)
237 break; /* just copy what we can */
238 if (isid(*ctx))
239 *cpp++ = *ctx++;
240 else {
241 *cpp++ = '_'; ctx++;
242 }
243 } while (*ctx);
244 while (cpp[-1] == CNTXMARK) /* cannot end in context mark */
245 cpp--;
246 *cpp = '\0';
247 return(context);
248 }
249
250
251 char *
252 pushcontext( /* push on another context */
253 char *ctx
254 )
255 {
256 char oldcontext[MAXCNTX+1];
257 int n;
258
259 strcpy(oldcontext, context); /* save old context */
260 calcontext(ctx); /* set new context */
261 n = strlen(context); /* tack on old */
262 if (n+strlen(oldcontext) > MAXCNTX) {
263 strncpy(context+n, oldcontext, MAXCNTX-n);
264 context[MAXCNTX] = '\0';
265 } else
266 strcpy(context+n, oldcontext);
267 return(context);
268 }
269
270
271 char *
272 popcontext(void) /* pop off top context */
273 {
274 char *cp1, *cp2;
275
276 if (!context[0]) /* nothing left to pop */
277 return(context);
278 cp2 = context; /* find mark */
279 while (*++cp2 && *cp2 != CNTXMARK)
280 ;
281 cp1 = context; /* copy tail to front */
282 while ( (*cp1++ = *cp2++) )
283 ;
284 return(context);
285 }
286
287
288 char *
289 qualname( /* get qualified name */
290 char *nam,
291 int lvl
292 )
293 {
294 static char nambuf[RMAXWORD+1];
295 char *cp = nambuf, *cpp;
296 /* check for explicit local */
297 if (*nam == CNTXMARK) {
298 if (lvl > 0) /* only action is to refuse search */
299 return(NULL);
300 nam++;
301 } else if (nam == nambuf) /* check for repeat call */
302 return(lvl > 0 ? NULL : nam);
303 /* copy name to static buffer */
304 while (*nam) {
305 if (cp >= nambuf+RMAXWORD)
306 goto toolong;
307 *cp++ = *nam++;
308 }
309 /* check for explicit global */
310 if (cp > nambuf && cp[-1] == CNTXMARK) {
311 if (lvl > 0)
312 return(NULL);
313 *--cp = '\0';
314 return(nambuf); /* already qualified */
315 }
316 cpp = context; /* else skip the requested levels */
317 while (lvl-- > 0) {
318 if (!*cpp)
319 return(NULL); /* return NULL if past global level */
320 while (*++cpp && *cpp != CNTXMARK)
321 ;
322 }
323 while (*cpp) { /* add remaining context to name */
324 if (cp >= nambuf+RMAXWORD)
325 goto toolong;
326 *cp++ = *cpp++;
327 }
328 toolong:
329 *cp = '\0';
330 return(nambuf); /* return qualified name */
331 }
332
333
334 int
335 incontext( /* is qualified name in current context? */
336 char *qn
337 )
338 {
339 if (!context[0]) /* global context accepts all */
340 return(1);
341 while (*qn && *qn != CNTXMARK) /* find context mark */
342 qn++;
343 return(!strcmp(qn, context));
344 }
345
346
347 void
348 chanout( /* set output channels */
349 void (*cs)(int n, double v)
350 )
351 {
352 EPNODE *ep;
353
354 for (ep = outchan; ep != NULL; ep = ep->sibling)
355 (*cs)(ep->v.kid->v.chan, evalue(ep->v.kid->sibling));
356
357 }
358
359
360 void
361 doptimize(int activate) /* optimize current and future definitions? */
362 {
363 EPNODE *ep;
364
365 if (activate && optimized)
366 return; /* already going */
367
368 if (!(optimized = activate))
369 return; /* switching off */
370
371 for (ep = dfirst(); ep != NULL; ep = dnext())
372 epoptimize(ep);
373 }
374
375
376 void
377 dcleanup( /* clear definitions (0->vars,1->output,2->consts) */
378 int lvl
379 )
380 {
381 int i;
382 VARDEF *vp;
383 EPNODE *ep;
384 /* if context is global, clear all */
385 for (i = 0; i < NHASH; i++)
386 for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
387 if (incontext(vp->name)) {
388 if (lvl >= 2)
389 dremove(vp->name);
390 else
391 dclear(vp->name);
392 }
393 if (lvl >= 1)
394 while (outchan != NULL) {
395 ep = outchan;
396 outchan = ep->sibling;
397 epfree(ep,1);
398 }
399 }
400
401
402 EPNODE *
403 dlookup( /* look up a definition */
404 char *name
405 )
406 {
407 VARDEF *vp;
408
409 if ((vp = varlookup(name)) == NULL)
410 return(NULL);
411 return(vp->def);
412 }
413
414
415 VARDEF *
416 varlookup( /* look up a variable */
417 char *name
418 )
419 {
420 int lvl = 0;
421 char *qname;
422 VARDEF *vp;
423 /* find most qualified match */
424 while ((qname = qualname(name, lvl++)) != NULL)
425 for (vp = hashtbl[hash(qname)]; vp != NULL; vp = vp->next)
426 if (!strcmp(vp->name, qname))
427 return(vp);
428 return(NULL);
429 }
430
431
432 VARDEF *
433 varinsert( /* get a link to a variable */
434 char *name
435 )
436 {
437 VARDEF *vp;
438 int hv;
439
440 if ((vp = varlookup(name)) != NULL) {
441 vp->nlinks++;
442 return(vp);
443 }
444 vp = (VARDEF *)emalloc(sizeof(VARDEF));
445 vp->lib = liblookup(name);
446 if (vp->lib == NULL) /* if name not in library */
447 name = qualname(name, 0); /* use fully qualified version */
448 hv = hash(name);
449 vp->name = savestr(name);
450 vp->nlinks = 1;
451 vp->def = NULL;
452 vp->next = hashtbl[hv];
453 hashtbl[hv] = vp;
454 return(vp);
455 }
456
457
458 void
459 libupdate( /* update library links */
460 char *fn
461 )
462 {
463 int i;
464 VARDEF *vp;
465 /* if fn is NULL then relink all */
466 for (i = 0; i < NHASH; i++)
467 for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
468 if ((vp->lib != NULL) | (fn == NULL) || !strcmp(fn, vp->name))
469 vp->lib = liblookup(vp->name);
470 }
471
472
473 void
474 varfree( /* release link to variable */
475 VARDEF *ln
476 )
477 {
478 VARDEF *vp;
479 int hv;
480
481 if (--ln->nlinks > 0)
482 return; /* still active */
483
484 hv = hash(ln->name);
485 vp = hashtbl[hv];
486 if (vp == ln)
487 hashtbl[hv] = vp->next;
488 else {
489 while (vp->next != ln) /* must be in list */
490 vp = vp->next;
491 vp->next = ln->next;
492 }
493 freestr(ln->name);
494 efree(ln);
495 }
496
497
498 EPNODE *
499 dfirst(void) /* return pointer to first definition */
500 {
501 htndx = 0;
502 htpos = NULL;
503 ochpos = outchan;
504 return(dnext());
505 }
506
507
508 EPNODE *
509 dnext(void) /* return pointer to next definition */
510 {
511 EPNODE *ep;
512 char *nm;
513
514 while (htndx < NHASH) {
515 if (htpos == NULL)
516 htpos = hashtbl[htndx++];
517 while (htpos != NULL) {
518 ep = htpos->def;
519 nm = htpos->name;
520 htpos = htpos->next;
521 if (ep != NULL && incontext(nm))
522 return(ep);
523 }
524 }
525 if ((ep = ochpos) != NULL)
526 ochpos = ep->sibling;
527 return(ep);
528 }
529
530
531 EPNODE *
532 dpop( /* pop a definition */
533 char *name
534 )
535 {
536 VARDEF *vp;
537 EPNODE *dp;
538
539 if ((vp = varlookup(name)) == NULL || vp->def == NULL)
540 return(NULL);
541 dp = vp->def;
542 vp->def = dp->sibling;
543 varfree(vp);
544 return(dp);
545 }
546
547
548 void
549 dpush( /* push on a definition */
550 char *nm,
551 EPNODE *ep
552 )
553 {
554 VARDEF *vp;
555
556 vp = varinsert(nm);
557 ep->sibling = vp->def;
558 vp->def = ep;
559 }
560
561
562 void
563 addchan( /* add an output channel assignment */
564 EPNODE *sp
565 )
566 {
567 int ch = sp->v.kid->v.chan;
568 EPNODE *ep, *epl;
569
570 for (epl = NULL, ep = outchan; ep != NULL; epl = ep, ep = ep->sibling)
571 if (ep->v.kid->v.chan >= ch) {
572 if (epl != NULL)
573 epl->sibling = sp;
574 else
575 outchan = sp;
576 if (ep->v.kid->v.chan > ch)
577 sp->sibling = ep;
578 else {
579 sp->sibling = ep->sibling;
580 epfree(ep,1);
581 }
582 return;
583 }
584 if (epl != NULL)
585 epl->sibling = sp;
586 else
587 outchan = sp;
588 sp->sibling = NULL;
589
590 }
591
592
593 void
594 getstatement(void) /* get next statement */
595 {
596 EPNODE *ep;
597 char *qname;
598 VARDEF *vdef;
599
600 if (nextc == ';') { /* empty statement */
601 scan();
602 return;
603 }
604 if (esupport&E_OUTCHAN &&
605 nextc == '$') { /* channel assignment */
606 ep = getchan();
607 addchan(ep);
608 } else { /* ordinary definition */
609 ep = getdefn();
610 qname = qualname(dfn_name(ep), 0);
611 if (esupport&E_REDEFW && (vdef = varlookup(qname)) != NULL) {
612 if (vdef->def != NULL && epcmp(ep, vdef->def)) {
613 wputs(qname);
614 if (vdef->def->type == ':')
615 wputs(": redefined constant expression\n");
616 else
617 wputs(": redefined\n");
618 } else if (ep->v.kid->type == FUNC && vdef->lib != NULL) {
619 wputs(qname);
620 wputs(": definition hides library function\n");
621 }
622 }
623 if (ep->type == ':')
624 dremove(qname);
625 else
626 dclear(qname);
627 dpush(qname, ep);
628 }
629 if (nextc != EOF) {
630 if (nextc != ';')
631 syntax("';' expected");
632 scan();
633 }
634 if (optimized)
635 epoptimize(ep); /* optimize new statement */
636 }
637
638
639 EPNODE *
640 getdefn(void)
641 /* A -> SYM = E1 */
642 /* SYM : E1 */
643 /* FUNC(SYM,..) = E1 */
644 /* FUNC(SYM,..) : E1 */
645 {
646 EPNODE *ep1, *ep2;
647
648 if (!isalpha(nextc) & (nextc != CNTXMARK))
649 syntax("illegal variable name");
650
651 ep1 = newnode();
652 ep1->type = SYM;
653 ep1->v.name = savestr(getname());
654
655 if (esupport&E_FUNCTION && nextc == '(') {
656 ep2 = newnode();
657 ep2->type = FUNC;
658 addekid(ep2, ep1);
659 ep1 = ep2;
660 do {
661 scan();
662 if (!isalpha(nextc))
663 syntax("illegal parameter name");
664 ep2 = newnode();
665 ep2->type = SYM;
666 ep2->v.name = savestr(getname());
667 if (strchr(ep2->v.name, CNTXMARK) != NULL)
668 syntax("illegal parameter name");
669 addekid(ep1, ep2);
670 } while (nextc == ',');
671 if (nextc != ')')
672 syntax("')' expected");
673 scan();
674 curfunc = ep1;
675 }
676
677 if ((nextc != '=') & (nextc != ':'))
678 syntax("'=' or ':' expected");
679
680 ep2 = newnode();
681 ep2->type = nextc;
682 scan();
683 addekid(ep2, ep1);
684 addekid(ep2, getE1());
685
686 if (ep1->type == SYM && ep1->sibling->type != NUM) {
687 ep1 = newnode();
688 ep1->type = CLKT;
689 ep1->v.tick = 0;
690 addekid(ep2, ep1);
691 ep1 = newnode();
692 ep1->type = NUM;
693 addekid(ep2, ep1);
694 }
695 curfunc = NULL;
696
697 return(ep2);
698 }
699
700
701 EPNODE *
702 getchan(void) /* A -> $N = E1 */
703 {
704 EPNODE *ep1, *ep2;
705
706 if (nextc != '$')
707 syntax("missing '$'");
708 scan();
709
710 ep1 = newnode();
711 ep1->type = CHAN;
712 ep1->v.chan = getinum();
713
714 if (nextc != '=')
715 syntax("'=' expected");
716 scan();
717
718 ep2 = newnode();
719 ep2->type = '=';
720 addekid(ep2, ep1);
721 addekid(ep2, getE1());
722
723 return(ep2);
724 }
725
726
727
728 /*
729 * The following routines are for internal use only:
730 */
731
732
733 static double /* evaluate a variable */
734 dvalue(char *name, EPNODE *d)
735 {
736 EPNODE *ep1, *ep2;
737
738 if (d == NULL || d->v.kid->type != SYM) {
739 eputs(name);
740 eputs(": undefined variable\n");
741 quit(1);
742 }
743 ep1 = d->v.kid->sibling; /* get expression */
744 if (ep1->type == NUM)
745 return(ep1->v.num); /* return if number */
746 if (esupport&E_RCONST && d->type == ':') {
747 wputs(name);
748 wputs(": assigned non-constant value\n");
749 }
750 ep2 = ep1->sibling; /* check time */
751 if (eclock >= MAXCLOCK)
752 eclock = 1; /* wrap clock counter */
753 if (ep2->v.tick < MAXCLOCK &&
754 (ep2->v.tick == 0) | (ep2->v.tick != eclock)) {
755 ep2->v.tick = d->type == ':' ? MAXCLOCK : eclock;
756 ep2 = ep2->sibling;
757 ep2->v.num = evalue(ep1); /* needs new value */
758 } else
759 ep2 = ep2->sibling; /* else reuse old value */
760
761 return(ep2->v.num);
762 }