1 /*
2  * This file is part of DGD, https://github.com/dworkin/dgd
3  * Copyright (C) 1993-2010 Dworkin B.V.
4  * Copyright (C) 2010-2013 DGD Authors (see the commit log for details)
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 # include "dgd.h"
21 # include "str.h"
22 # include "array.h"
23 # include "object.h"
24 # include "xfloat.h"
25 # include "interpret.h"
26 # include "data.h"
27 # include "call_out.h"
28 # include "parse.h"
29 # include "csupport.h"
30 
31 
32 # define COP_ADD	0	/* add callout patch */
33 # define COP_REMOVE	1	/* remove callout patch */
34 # define COP_REPLACE	2	/* replace callout patch */
35 
36 typedef struct _copatch_ {
37     short type;			/* add, remove, replace */
38     uindex handle;		/* callout handle */
39     dataplane *plane;		/* dataplane */
40     Uint time;			/* start time */
41     unsigned short mtime;	/* start time millisec component */
42     uindex *queue;		/* callout queue */
43     struct _copatch_ *next;	/* next in linked list */
44     dcallout aco;		/* added callout */
45     dcallout rco;		/* removed callout */
46 } copatch;
47 
48 # define COPCHUNKSZ	32
49 
50 typedef struct _copchunk_ {
51     struct _copchunk_ *next;	/* next in linked list */
52     copatch cop[COPCHUNKSZ];	/* callout patches */
53 } copchunk;
54 
55 typedef struct _coptable_ {
56     copchunk *chunk;			/* callout patch chunk */
57     unsigned short chunksz;		/* size of callout patch chunk */
58     copatch *flist;			/* free list of callout patches */
59     copatch *cop[COPATCHHTABSZ];	/* hash table of callout patches */
60 } coptable;
61 
62 typedef struct {
63     array **itab;			/* imported array replacement table */
64     Uint itabsz;			/* size of table */
65     Uint narr;				/* # of arrays */
66 } arrimport;
67 
68 static dataplane *plist;		/* list of dataplanes */
69 static uindex ncallout;			/* # callouts added */
70 static dataspace *ifirst;		/* list of dataspaces with imports */
71 
72 
73 /*
74  * NAME:	ref_rhs()
75  * DESCRIPTION:	reference the right-hand side in an assignment
76  */
ref_rhs(dataspace * data,value * rhs)77 static void ref_rhs(dataspace *data, value *rhs)
78 {
79     string *str;
80     array *arr;
81 
82     switch (rhs->type) {
83     case T_STRING:
84 	str = rhs->u.string;
85 	if (str->primary != (strref *) NULL && str->primary->data == data) {
86 	    /* in this object */
87 	    str->primary->ref++;
88 	    data->plane->flags |= MOD_STRINGREF;
89 	} else {
90 	    /* not in this object: ref imported string */
91 	    data->plane->schange++;
92 	}
93 	break;
94 
95     case T_ARRAY:
96     case T_MAPPING:
97     case T_LWOBJECT:
98 	arr = rhs->u.array;
99 	if (arr->primary->data == data) {
100 	    /* in this object */
101 	    if (arr->primary->arr != (array *) NULL) {
102 		/* swapped in */
103 		arr->primary->ref++;
104 		data->plane->flags |= MOD_ARRAYREF;
105 	    } else {
106 		/* ref new array */
107 		data->plane->achange++;
108 	    }
109 	} else {
110 	    /* not in this object: ref imported array */
111 	    if (data->plane->imports++ == 0 && ifirst != data &&
112 		data->iprev == (dataspace *) NULL) {
113 		/* add to imports list */
114 		data->iprev = (dataspace *) NULL;
115 		data->inext = ifirst;
116 		if (ifirst != (dataspace *) NULL) {
117 		    ifirst->iprev = data;
118 		}
119 		ifirst = data;
120 	    }
121 	    data->plane->achange++;
122 	}
123 	break;
124     }
125 }
126 
127 /*
128  * NAME:	del_lhs()
129  * DESCRIPTION:	delete the left-hand side in an assignment
130  */
del_lhs(dataspace * data,value * lhs)131 static void del_lhs(dataspace *data, value *lhs)
132 {
133     string *str;
134     array *arr;
135 
136     switch (lhs->type) {
137     case T_STRING:
138 	str = lhs->u.string;
139 	if (str->primary != (strref *) NULL && str->primary->data == data) {
140 	    /* in this object */
141 	    if (--(str->primary->ref) == 0) {
142 		str->primary->str = (string *) NULL;
143 		str->primary = (strref *) NULL;
144 		str_del(str);
145 		data->plane->schange++;	/* last reference removed */
146 	    }
147 	    data->plane->flags |= MOD_STRINGREF;
148 	} else {
149 	    /* not in this object: deref imported string */
150 	    data->plane->schange--;
151 	}
152 	break;
153 
154     case T_ARRAY:
155     case T_MAPPING:
156     case T_LWOBJECT:
157 	arr = lhs->u.array;
158 	if (arr->primary->data == data) {
159 	    /* in this object */
160 	    if (arr->primary->arr != (array *) NULL) {
161 		/* swapped in */
162 		data->plane->flags |= MOD_ARRAYREF;
163 		if ((--(arr->primary->ref) & ~ARR_MOD) == 0) {
164 		    d_get_elts(arr);
165 		    arr->primary->arr = (array *) NULL;
166 		    arr->primary = &arr->primary->plane->alocal;
167 		    arr_del(arr);
168 		    data->plane->achange++;
169 		}
170 	    } else {
171 		/* deref new array */
172 		data->plane->achange--;
173 	    }
174 	} else {
175 	    /* not in this object: deref imported array */
176 	    data->plane->imports--;
177 	    data->plane->achange--;
178 	}
179 	break;
180     }
181 }
182 
183 
184 /*
185  * NAME:	data->alloc_call_out()
186  * DESCRIPTION:	allocate a new callout
187  */
d_alloc_call_out(dataspace * data,uindex handle,Uint time,unsigned short mtime,int nargs,value * v)188 static uindex d_alloc_call_out(dataspace *data, uindex handle, Uint time,
189 	unsigned short mtime, int nargs, value *v)
190 {
191     dcallout *co;
192 
193     if (data->ncallouts == 0) {
194 	/*
195 	 * the first in this object
196 	 */
197 	co = data->callouts = ALLOC(dcallout, 1);
198 	data->ncallouts = handle = 1;
199 	data->plane->flags |= MOD_NEWCALLOUT;
200     } else {
201 	if (data->callouts == (dcallout *) NULL) {
202 	    d_get_callouts(data);
203 	}
204 	if (handle != 0) {
205 	    /*
206 	     * get a specific callout from the free list
207 	     */
208 	    co = &data->callouts[handle - 1];
209 	    if (handle == data->fcallouts) {
210 		data->fcallouts = co->co_next;
211 	    } else {
212 		data->callouts[co->co_prev - 1].co_next = co->co_next;
213 		if (co->co_next != 0) {
214 		    data->callouts[co->co_next - 1].co_prev = co->co_prev;
215 		}
216 	    }
217 	} else {
218 	    handle = data->fcallouts;
219 	    if (handle != 0) {
220 		/*
221 		 * from free list
222 		 */
223 		co = &data->callouts[handle - 1];
224 		if (co->co_next == 0 || co->co_next > handle) {
225 		    /* take 1st free callout */
226 		    data->fcallouts = co->co_next;
227 		} else {
228 		    /* take 2nd free callout */
229 		    co = &data->callouts[co->co_next - 1];
230 		    data->callouts[handle - 1].co_next = co->co_next;
231 		    if (co->co_next != 0) {
232 			data->callouts[co->co_next - 1].co_prev = handle;
233 		    }
234 		    handle = co - data->callouts + 1;
235 		}
236 		data->plane->flags |= MOD_CALLOUT;
237 	    } else {
238 		/*
239 		 * add new callout
240 		 */
241 		handle = data->ncallouts;
242 		co = data->callouts = REALLOC(data->callouts, dcallout, handle,
243 					      handle + 1);
244 		co += handle;
245 		data->ncallouts = ++handle;
246 		data->plane->flags |= MOD_NEWCALLOUT;
247 	    }
248 	}
249     }
250 
251     co->time = time;
252     co->mtime = mtime;
253     co->nargs = nargs;
254     memcpy(co->val, v, sizeof(co->val));
255     switch (nargs) {
256     default:
257 	ref_rhs(data, &v[3]);
258     case 2:
259 	ref_rhs(data, &v[2]);
260     case 1:
261 	ref_rhs(data, &v[1]);
262     case 0:
263 	ref_rhs(data, &v[0]);
264 	break;
265     }
266 
267     return handle;
268 }
269 
270 /*
271  * NAME:	data->free_call_out()
272  * DESCRIPTION:	free a callout
273  */
d_free_call_out(dataspace * data,unsigned int handle)274 static void d_free_call_out(dataspace *data, unsigned int handle)
275 {
276     dcallout *co;
277     value *v;
278     uindex n;
279 
280     co = &data->callouts[handle - 1];
281     v = co->val;
282     switch (co->nargs) {
283     default:
284 	del_lhs(data, &v[3]);
285 	i_del_value(&v[3]);
286     case 2:
287 	del_lhs(data, &v[2]);
288 	i_del_value(&v[2]);
289     case 1:
290 	del_lhs(data, &v[1]);
291 	i_del_value(&v[1]);
292     case 0:
293 	del_lhs(data, &v[0]);
294 	str_del(v[0].u.string);
295 	break;
296     }
297     v[0] = nil_value;
298 
299     n = data->fcallouts;
300     if (n != 0) {
301 	data->callouts[n - 1].co_prev = handle;
302     }
303     co->co_next = n;
304     data->fcallouts = handle;
305 
306     data->plane->flags |= MOD_CALLOUT;
307 }
308 
309 
310 /*
311  * NAME:	copatch->init()
312  * DESCRIPTION:	initialize copatch table
313  */
cop_init(dataplane * plane)314 static void cop_init(dataplane *plane)
315 {
316     memset(plane->coptab = ALLOC(coptable, 1), '\0', sizeof(coptable));
317 }
318 
319 /*
320  * NAME:	copatch->clean()
321  * DESCRIPTION:	free copatch table
322  */
cop_clean(dataplane * plane)323 static void cop_clean(dataplane *plane)
324 {
325     copchunk *c, *f;
326 
327     c = plane->coptab->chunk;
328     while (c != (copchunk *) NULL) {
329 	f = c;
330 	c = c->next;
331 	FREE(f);
332     }
333 
334     FREE(plane->coptab);
335     plane->coptab = (coptable *) NULL;
336 }
337 
338 /*
339  * NAME:	copatch->new()
340  * DESCRIPTION:	create a new callout patch
341  */
cop_new(dataplane * plane,copatch ** c,int type,unsigned int handle,dcallout * co,Uint time,unsigned int mtime,uindex * q)342 static copatch *cop_new(dataplane *plane, copatch **c, int type,
343 	unsigned int handle, dcallout *co, Uint time, unsigned int mtime,
344 	uindex *q)
345 {
346     coptable *tab;
347     copatch *cop;
348     int i;
349     value *v;
350 
351     /* allocate */
352     tab = plane->coptab;
353     if (tab->flist != (copatch *) NULL) {
354 	/* from free list */
355 	cop = tab->flist;
356 	tab->flist = cop->next;
357     } else {
358 	/* newly allocated */
359 	if (tab->chunk == (copchunk *) NULL || tab->chunksz == COPCHUNKSZ) {
360 	    copchunk *cc;
361 
362 	    /* create new chunk */
363 	    cc = ALLOC(copchunk, 1);
364 	    cc->next = tab->chunk;
365 	    tab->chunk = cc;
366 	    tab->chunksz = 0;
367 	}
368 
369 	cop = &tab->chunk->cop[tab->chunksz++];
370     }
371 
372     /* initialize */
373     cop->type = type;
374     cop->handle = handle;
375     if (type == COP_ADD) {
376 	cop->aco = *co;
377     } else {
378 	cop->rco = *co;
379     }
380     for (i = (co->nargs > 3) ? 4 : co->nargs + 1, v = co->val; i > 0; --i) {
381 	i_ref_value(v++);
382     }
383     cop->time = time;
384     cop->mtime = mtime;
385     cop->plane = plane;
386     cop->queue = q;
387 
388     /* add to hash table */
389     cop->next = *c;
390     return *c = cop;
391 }
392 
393 /*
394  * NAME:	copatch->del()
395  * DESCRIPTION:	delete a callout patch
396  */
cop_del(dataplane * plane,copatch ** c,bool del)397 static void cop_del(dataplane *plane, copatch **c, bool del)
398 {
399     copatch *cop;
400     dcallout *co;
401     int i;
402     value *v;
403     coptable *tab;
404 
405     /* remove from hash table */
406     cop = *c;
407     *c = cop->next;
408 
409     if (del) {
410 	/* free referenced callout */
411 	co = (cop->type == COP_ADD) ? &cop->aco : &cop->rco;
412 	v = co->val;
413 	for (i = (co->nargs > 3) ? 4 : co->nargs + 1; i > 0; --i) {
414 	    i_del_value(v++);
415 	}
416     }
417 
418     /* add to free list */
419     tab = plane->coptab;
420     cop->next = tab->flist;
421     tab->flist = cop;
422 }
423 
424 /*
425  * NAME:	copatch->replace()
426  * DESCRIPTION:	replace one callout patch with another
427  */
cop_replace(copatch * cop,dcallout * co,Uint time,unsigned int mtime,uindex * q)428 static void cop_replace(copatch *cop, dcallout *co, Uint time,
429 	unsigned int mtime, uindex *q)
430 {
431     int i;
432     value *v;
433 
434     cop->type = COP_REPLACE;
435     cop->aco = *co;
436     for (i = (co->nargs > 3) ? 4 : co->nargs + 1, v = co->val; i > 0; --i) {
437 	i_ref_value(v++);
438     }
439     cop->time = time;
440     cop->mtime = mtime;
441     cop->queue = q;
442 }
443 
444 /*
445  * NAME:	copatch->commit()
446  * DESCRIPTION:	commit a callout replacement
447  */
cop_commit(copatch * cop)448 static void cop_commit(copatch *cop)
449 {
450     int i;
451     value *v;
452 
453     cop->type = COP_ADD;
454     for (i = (cop->rco.nargs > 3) ? 4 : cop->rco.nargs + 1, v = cop->rco.val;
455 	 i > 0; --i) {
456 	i_del_value(v++);
457     }
458 }
459 
460 /*
461  * NAME:	copatch->release()
462  * DESCRIPTION:	remove a callout replacement
463  */
cop_release(copatch * cop)464 static void cop_release(copatch *cop)
465 {
466     int i;
467     value *v;
468 
469     cop->type = COP_REMOVE;
470     for (i = (cop->aco.nargs > 3) ? 4 : cop->aco.nargs + 1, v = cop->aco.val;
471 	 i > 0; --i) {
472 	i_del_value(v++);
473     }
474 }
475 
476 /*
477  * NAME:	copatch->discard()
478  * DESCRIPTION:	discard replacement
479  */
cop_discard(copatch * cop)480 static void cop_discard(copatch *cop)
481 {
482     /* force unref of proper component later */
483     cop->type = COP_ADD;
484 }
485 
486 
487 /*
488  * NAME:	data->new_plane()
489  * DESCRIPTION:	create a new dataplane
490  */
d_new_plane(dataspace * data,Int level)491 void d_new_plane(dataspace *data, Int level)
492 {
493     dataplane *p;
494     Uint i;
495 
496     p = ALLOC(dataplane, 1);
497 
498     p->level = level;
499     p->flags = data->plane->flags;
500     p->schange = data->plane->schange;
501     p->achange = data->plane->achange;
502     p->imports = data->plane->imports;
503 
504     /* copy value information from previous plane */
505     p->original = (value *) NULL;
506     p->alocal.arr = (array *) NULL;
507     p->alocal.plane = p;
508     p->alocal.data = data;
509     p->alocal.state = AR_CHANGED;
510     p->coptab = data->plane->coptab;
511 
512     if (data->plane->arrays != (arrref *) NULL) {
513 	arrref *a, *b;
514 
515 	p->arrays = ALLOC(arrref, i = data->narrays);
516 	for (a = p->arrays, b = data->plane->arrays; i != 0; a++, b++, --i) {
517 	    if (b->arr != (array *) NULL) {
518 		*a = *b;
519 		a->arr->primary = a;
520 		arr_ref(a->arr);
521 	    } else {
522 		a->arr = (array *) NULL;
523 	    }
524 	}
525     } else {
526 	p->arrays = (arrref *) NULL;
527     }
528     p->achunk = (abchunk *) NULL;
529 
530     if (data->plane->strings != (strref *) NULL) {
531 	strref *s, *t;
532 
533 	p->strings = ALLOC(strref, i = data->nstrings);
534 	for (s = p->strings, t = data->plane->strings; i != 0; s++, t++, --i) {
535 	    if (t->str != (string *) NULL) {
536 		*s = *t;
537 		s->str->primary = s;
538 		str_ref(s->str);
539 	    } else {
540 		s->str = (string *) NULL;
541 	    }
542 	}
543     } else {
544 	p->strings = (strref *) NULL;
545     }
546 
547     p->prev = data->plane;
548     data->plane = p;
549     p->plist = plist;
550     plist = p;
551 }
552 
553 /*
554  * NAME:	commit_values()
555  * DESCRIPTION:	commit non-swapped arrays among the values
556  */
commit_values(value * v,unsigned int n,Int level)557 static void commit_values(value *v, unsigned int n, Int level)
558 {
559     array *arr;
560 
561     while (n != 0) {
562 	if (T_INDEXED(v->type)) {
563 	    arr = v->u.array;
564 	    if (arr->primary->arr == (array *) NULL &&
565 		arr->primary->plane->level > level) {
566 		if (arr->hashmod) {
567 		    map_compact(arr->primary->data, arr);
568 		}
569 		arr->primary = &arr->primary->plane->prev->alocal;
570 		commit_values(arr->elts, arr->size, level);
571 	    }
572 
573 	}
574 	v++;
575 	--n;
576     }
577 }
578 
579 /*
580  * NAME:	commit_callouts()
581  * DESCRIPTION:	commit callout patches to previous plane
582  */
commit_callouts(dataplane * plane,bool merge)583 static void commit_callouts(dataplane *plane, bool merge)
584 {
585     dataplane *prev;
586     copatch **c, **n, *cop;
587     copatch **t, **next;
588     int i;
589 
590     prev = plane->prev;
591     for (i = COPATCHHTABSZ, t = plane->coptab->cop; --i >= 0; t++) {
592 	if (*t != (copatch *) NULL && (*t)->plane == plane) {
593 	    /*
594 	     * find previous plane in hash chain
595 	     */
596 	    next = t;
597 	    do {
598 		next = &(*next)->next;
599 	    } while (*next != (copatch *) NULL && (*next)->plane == plane);
600 
601 	    c = t;
602 	    do {
603 		cop = *c;
604 		if (cop->type != COP_REMOVE) {
605 		    commit_values(cop->aco.val + 1,
606 				  (cop->aco.nargs > 3) ? 3 : cop->aco.nargs,
607 				  prev->level);
608 		}
609 
610 		if (prev->level == 0) {
611 		    /*
612 		     * commit to last plane
613 		     */
614 		    switch (cop->type) {
615 		    case COP_ADD:
616 			co_new(plane->alocal.data->oindex, cop->handle,
617 			       cop->time, cop->mtime, cop->queue);
618 			--ncallout;
619 			break;
620 
621 		    case COP_REMOVE:
622 			co_del(plane->alocal.data->oindex, cop->handle,
623 			       cop->rco.time, cop->rco.mtime);
624 			ncallout++;
625 			break;
626 
627 		    case COP_REPLACE:
628 			co_del(plane->alocal.data->oindex, cop->handle,
629 			       cop->rco.time, cop->rco.mtime);
630 			co_new(plane->alocal.data->oindex, cop->handle,
631 			       cop->time, cop->mtime, cop->queue);
632 			cop_commit(cop);
633 			break;
634 		    }
635 
636 		    if (next == &cop->next) {
637 			next = c;
638 		    }
639 		    cop_del(plane, c, TRUE);
640 		} else {
641 		    /*
642 		     * commit to previous plane
643 		     */
644 		    cop->plane = prev;
645 		    if (merge) {
646 			for (n = next;
647 			     *n != (copatch *) NULL && (*n)->plane == prev;
648 			     n = &(*n)->next) {
649 			    if (cop->handle == (*n)->handle) {
650 				switch (cop->type) {
651 				case COP_ADD:
652 				    /* turn old remove into replace, del new */
653 				    cop_replace(*n, &cop->aco, cop->time,
654 						cop->mtime, cop->queue);
655 				    if (next == &cop->next) {
656 					next = c;
657 				    }
658 				    cop_del(prev, c, TRUE);
659 				    break;
660 
661 				case COP_REMOVE:
662 				    if ((*n)->type == COP_REPLACE) {
663 					/* turn replace back into remove */
664 					cop_release(*n);
665 				    } else {
666 					/* del old */
667 					cop_del(prev, n, TRUE);
668 				    }
669 				    /* del new */
670 				    if (next == &cop->next) {
671 					next = c;
672 				    }
673 				    cop_del(prev, c, TRUE);
674 				    break;
675 
676 				case COP_REPLACE:
677 				    if ((*n)->type == COP_REPLACE) {
678 					/* merge replaces into old, del new */
679 					cop_release(*n);
680 					cop_replace(*n, &cop->aco, cop->time,
681 						    cop->mtime, cop->queue);
682 					if (next == &cop->next) {
683 					    next = c;
684 					}
685 					cop_del(prev, c, TRUE);
686 				    } else {
687 					/* make replace into add, remove old */
688 					cop_del(prev, n, TRUE);
689 					cop_commit(cop);
690 				    }
691 				    break;
692 				}
693 				break;
694 			    }
695 			}
696 		    }
697 
698 		    if (*c == cop) {
699 			c = &cop->next;
700 		    }
701 		}
702 	    } while (c != next);
703 	}
704     }
705 }
706 
707 /*
708  * NAME:	data->commit_plane()
709  * DESCRIPTION:	commit the current data plane
710  */
d_commit_plane(Int level,value * retval)711 void d_commit_plane(Int level, value *retval)
712 {
713     dataplane *p, *commit, **r, **cr;
714     dataspace *data;
715     value *v;
716     Uint i;
717     dataplane *clist;
718 
719     /*
720      * pass 1: construct commit planes
721      */
722     clist = (dataplane *) NULL;
723     cr = &clist;
724     for (r = &plist, p = *r; p != (dataplane *) NULL && p->level == level;
725 	 r = &p->plist, p = *r) {
726 	if (p->prev->level != level - 1) {
727 	    /* insert commit plane */
728 	    commit = ALLOC(dataplane, 1);
729 	    commit->level = level - 1;
730 	    commit->original = (value *) NULL;
731 	    commit->alocal.arr = (array *) NULL;
732 	    commit->alocal.plane = commit;
733 	    commit->alocal.data = p->alocal.data;
734 	    commit->alocal.state = AR_CHANGED;
735 	    commit->arrays = p->arrays;
736 	    commit->achunk = p->achunk;
737 	    commit->strings = p->strings;
738 	    commit->coptab = p->coptab;
739 	    commit->prev = p->prev;
740 	    *cr = commit;
741 	    cr = &commit->plist;
742 
743 	    p->prev = commit;
744 	} else {
745 	    p->flags |= PLANE_MERGE;
746 	}
747     }
748     if (clist != (dataplane *) NULL) {
749 	/* insert commit planes in plane list */
750 	*cr = p;
751 	*r = clist;
752     }
753     clist = *r;	/* sentinel */
754 
755     /*
756      * pass 2: commit
757      */
758     for (p = plist; p != clist; p = p->plist) {
759 	/*
760 	 * commit changes to previous plane
761 	 */
762 	data = p->alocal.data;
763 	if (p->original != (value *) NULL) {
764 	    if (p->level == 1 || p->prev->original != (value *) NULL) {
765 		/* free backed-up variable values */
766 		for (v = p->original, i = data->nvariables; i != 0; v++, --i) {
767 		    i_del_value(v);
768 		}
769 		FREE(p->original);
770 	    } else {
771 		/* move originals to previous plane */
772 		p->prev->original = p->original;
773 	    }
774 	    commit_values(data->variables, data->nvariables, level - 1);
775 	}
776 
777 	if (p->coptab != (coptable *) NULL) {
778 	    /* commit callout changes */
779 	    commit_callouts(p, p->flags & PLANE_MERGE);
780 	    if (p->level == 1) {
781 		cop_clean(p);
782 	    } else {
783 		p->prev->coptab = p->coptab;
784 	    }
785 	}
786 
787 	arr_commit(&p->achunk, p->prev, p->flags & PLANE_MERGE);
788 	if (p->flags & PLANE_MERGE) {
789 	    if (p->arrays != (arrref *) NULL) {
790 		arrref *a;
791 
792 		/* remove old array refs */
793 		for (a = p->prev->arrays, i = data->narrays; i != 0; a++, --i) {
794 		    if (a->arr != (array *) NULL) {
795 			if (a->arr->primary == &p->alocal) {
796 			    a->arr->primary = &p->prev->alocal;
797 			}
798 			arr_del(a->arr);
799 		    }
800 		}
801 		FREE(p->prev->arrays);
802 		p->prev->arrays = p->arrays;
803 	    }
804 
805 	    if (p->strings != (strref *) NULL) {
806 		strref *s;
807 
808 		/* remove old string refs */
809 		for (s = p->prev->strings, i = data->nstrings; i != 0; s++, --i)
810 		{
811 		    if (s->str != (string *) NULL) {
812 			str_del(s->str);
813 		    }
814 		}
815 		FREE(p->prev->strings);
816 		p->prev->strings = p->strings;
817 	    }
818 	}
819     }
820     commit_values(retval, 1, level - 1);
821 
822     /*
823      * pass 3: deallocate
824      */
825     for (p = plist; p != clist; p = plist) {
826 	p->prev->flags = (p->flags & MOD_ALL) | MOD_SAVE;
827 	p->prev->schange = p->schange;
828 	p->prev->achange = p->achange;
829 	p->prev->imports = p->imports;
830 	p->alocal.data->plane = p->prev;
831 	plist = p->plist;
832 	FREE(p);
833     }
834 }
835 
836 /*
837  * NAME:	discard_callouts()
838  * DESCRIPTION:	discard callout patches on current plane, restoring old callouts
839  */
discard_callouts(dataplane * plane)840 static void discard_callouts(dataplane *plane)
841 {
842     copatch *cop, **c, **t;
843     dataspace *data;
844     int i;
845 
846     data = plane->alocal.data;
847     for (i = COPATCHHTABSZ, t = plane->coptab->cop; --i >= 0; t++) {
848 	c = t;
849 	while (*c != (copatch *) NULL && (*c)->plane == plane) {
850 	    cop = *c;
851 	    switch (cop->type) {
852 	    case COP_ADD:
853 		d_free_call_out(data, cop->handle);
854 		cop_del(plane, c, TRUE);
855 		--ncallout;
856 		break;
857 
858 	    case COP_REMOVE:
859 		d_alloc_call_out(data, cop->handle, cop->rco.time,
860 				 cop->rco.mtime, cop->rco.nargs, cop->rco.val);
861 		cop_del(plane, c, FALSE);
862 		ncallout++;
863 		break;
864 
865 	    case COP_REPLACE:
866 		d_free_call_out(data, cop->handle);
867 		d_alloc_call_out(data, cop->handle, cop->rco.time,
868 				 cop->rco.mtime, cop->rco.nargs, cop->rco.val);
869 		cop_discard(cop);
870 		cop_del(plane, c, TRUE);
871 		break;
872 	    }
873 	}
874     }
875 }
876 
877 /*
878  * NAME:	data->discard_plane()
879  * DESCRIPTION:	discard the current data plane without committing it
880  */
d_discard_plane(Int level)881 void d_discard_plane(Int level)
882 {
883     dataplane *p;
884     dataspace *data;
885     value *v;
886     Uint i;
887 
888     for (p = plist; p != (dataplane *) NULL && p->level == level; p = p->plist)
889     {
890 	/*
891 	 * discard changes except for callout mods
892 	 */
893 	p->prev->flags |= p->flags & (MOD_CALLOUT | MOD_NEWCALLOUT);
894 
895 	data = p->alocal.data;
896 	if (p->original != (value *) NULL) {
897 	    /* restore original variable values */
898 	    for (v = data->variables, i = data->nvariables; i != 0; --i, v++) {
899 		i_del_value(v);
900 	    }
901 	    memcpy(data->variables, p->original,
902 		   data->nvariables * sizeof(value));
903 	    FREE(p->original);
904 	}
905 
906 	if (p->coptab != (coptable *) NULL) {
907 	    /* undo callout changes */
908 	    discard_callouts(p);
909 	    if (p->prev == &data->base) {
910 		cop_clean(p);
911 	    } else {
912 		p->prev->coptab = p->coptab;
913 	    }
914 	}
915 
916 	arr_discard(&p->achunk);
917 	if (p->arrays != (arrref *) NULL) {
918 	    arrref *a;
919 
920 	    /* delete new array refs */
921 	    for (a = p->arrays, i = data->narrays; i != 0; a++, --i) {
922 		if (a->arr != (array *) NULL) {
923 		    arr_del(a->arr);
924 		}
925 	    }
926 	    FREE(p->arrays);
927 	    /* fix old ones */
928 	    for (a = p->prev->arrays, i = data->narrays; i != 0; a++, --i) {
929 		if (a->arr != (array *) NULL) {
930 		    a->arr->primary = a;
931 		}
932 	    }
933 	}
934 
935 	if (p->strings != (strref *) NULL) {
936 	    strref *s;
937 
938 	    /* delete new string refs */
939 	    for (s = p->strings, i = data->nstrings; i != 0; s++, --i) {
940 		if (s->str != (string *) NULL) {
941 		    str_del(s->str);
942 		}
943 	    }
944 	    FREE(p->strings);
945 	    /* fix old ones */
946 	    for (s = p->prev->strings, i = data->nstrings; i != 0; s++, --i) {
947 		if (s->str != (string *) NULL) {
948 		    s->str->primary = s;
949 		}
950 	    }
951 	}
952 
953 	data->plane = p->prev;
954 	plist = p->plist;
955 	FREE(p);
956     }
957 }
958 
959 
960 /*
961  * NAME:	data->commit_arr()
962  * DESCRIPTION:	commit array to previous plane
963  */
d_commit_arr(array * arr,dataplane * prev,dataplane * old)964 abchunk **d_commit_arr(array *arr, dataplane *prev, dataplane *old)
965 {
966     if (arr->primary->plane != prev) {
967 	if (arr->hashmod) {
968 	    map_compact(arr->primary->data, arr);
969 	}
970 
971 	if (arr->primary->arr == (array *) NULL) {
972 	    arr->primary = &prev->alocal;
973 	} else {
974 	    arr->primary->plane = prev;
975 	}
976 	commit_values(arr->elts, arr->size, prev->level);
977     }
978 
979     return (prev == old) ? (abchunk **) NULL : &prev->achunk;
980 }
981 
982 /*
983  * NAME:	data->discard_arr()
984  * DESCRIPTION:	restore array to previous plane
985  */
d_discard_arr(array * arr,dataplane * plane)986 void d_discard_arr(array *arr, dataplane *plane)
987 {
988     /* swapped-in arrays will be fixed later */
989     arr->primary = &plane->alocal;
990 }
991 
992 
993 /*
994  * NAME:	data->ref_imports()
995  * DESCRIPTION:	check the elements of an array for imports
996  */
d_ref_imports(array * arr)997 void d_ref_imports(array *arr)
998 {
999     dataspace *data;
1000     unsigned short n;
1001     value *v;
1002 
1003     data = arr->primary->data;
1004     for (n = arr->size, v = arr->elts; n > 0; --n, v++) {
1005 	if (T_INDEXED(v->type) && data != v->u.array->primary->data) {
1006 	    /* mark as imported */
1007 	    if (data->plane->imports++ == 0 && ifirst != data &&
1008 		data->iprev == (dataspace *) NULL) {
1009 		/* add to imports list */
1010 		data->iprev = (dataspace *) NULL;
1011 		data->inext = ifirst;
1012 		if (ifirst != (dataspace *) NULL) {
1013 		    ifirst->iprev = data;
1014 		}
1015 		ifirst = data;
1016 	    }
1017 	}
1018     }
1019 }
1020 
1021 /*
1022  * NAME:	data->assign_var()
1023  * DESCRIPTION:	assign a value to a variable
1024  */
d_assign_var(dataspace * data,value * var,value * val)1025 void d_assign_var(dataspace *data, value *var, value *val)
1026 {
1027     if (var >= data->variables && var < data->variables + data->nvariables) {
1028 	if (data->plane->level != 0 &&
1029 	    data->plane->original == (value *) NULL) {
1030 	    /*
1031 	     * back up variables
1032 	     */
1033 	    i_copy(data->plane->original = ALLOC(value, data->nvariables),
1034 		   data->variables, data->nvariables);
1035 	}
1036 	ref_rhs(data, val);
1037 	del_lhs(data, var);
1038 	data->plane->flags |= MOD_VARIABLE;
1039     }
1040 
1041     i_ref_value(val);
1042     i_del_value(var);
1043 
1044     *var = *val;
1045     var->modified = TRUE;
1046 }
1047 
1048 /*
1049  * NAME:	data->get_extravar()
1050  * DESCRIPTION:	get an object's special value
1051  */
d_get_extravar(dataspace * data)1052 value *d_get_extravar(dataspace *data)
1053 {
1054     return d_get_variable(data, data->nvariables - 1);
1055 }
1056 
1057 /*
1058  * NAME:	data->set_extravar()
1059  * DESCRIPTION:	set an object's special value
1060  */
d_set_extravar(dataspace * data,value * val)1061 void d_set_extravar(dataspace *data, value *val)
1062 {
1063     d_assign_var(data, d_get_variable(data, data->nvariables - 1), val);
1064 }
1065 
1066 /*
1067  * NAME:	data->wipe_extravar()
1068  * DESCRIPTION:	wipe out an object's special value
1069  */
d_wipe_extravar(dataspace * data)1070 void d_wipe_extravar(dataspace *data)
1071 {
1072     d_assign_var(data, d_get_variable(data, data->nvariables - 1), &nil_value);
1073 
1074     if (data->parser != (struct _parser_ *) NULL) {
1075 	/*
1076 	 * get rid of the parser, too
1077 	 */
1078 	ps_del(data->parser);
1079 	data->parser = (struct _parser_ *) NULL;
1080     }
1081 }
1082 
1083 /*
1084  * NAME:	data->assign_elt()
1085  * DESCRIPTION:	assign a value to an array element
1086  */
d_assign_elt(dataspace * data,array * arr,value * elt,value * val)1087 void d_assign_elt(dataspace *data, array *arr, value *elt, value *val)
1088 {
1089     if (data->plane->level != arr->primary->data->plane->level) {
1090 	/*
1091 	 * bring dataspace of imported array up to the current plane level
1092 	 */
1093 	d_new_plane(arr->primary->data, data->plane->level);
1094     }
1095 
1096     data = arr->primary->data;
1097     if (arr->primary->plane != data->plane) {
1098 	/*
1099 	 * backup array's current elements
1100 	 */
1101 	arr_backup(&data->plane->achunk, arr);
1102 	if (arr->primary->arr != (array *) NULL) {
1103 	    arr->primary->plane = data->plane;
1104 	} else {
1105 	    arr->primary = &data->plane->alocal;
1106 	}
1107     }
1108 
1109     if (arr->primary->arr != (array *) NULL) {
1110 	/*
1111 	 * the array is in the loaded dataspace of some object
1112 	 */
1113 	if ((arr->primary->ref & ARR_MOD) == 0) {
1114 	    arr->primary->ref |= ARR_MOD;
1115 	    data->plane->flags |= MOD_ARRAY;
1116 	}
1117 	ref_rhs(data, val);
1118 	del_lhs(data, elt);
1119     } else {
1120 	if (T_INDEXED(val->type) && data != val->u.array->primary->data) {
1121 	    /* mark as imported */
1122 	    if (data->plane->imports++ == 0 && ifirst != data &&
1123 		data->iprev == (dataspace *) NULL) {
1124 		/* add to imports list */
1125 		data->iprev = (dataspace *) NULL;
1126 		data->inext = ifirst;
1127 		if (ifirst != (dataspace *) NULL) {
1128 		    ifirst->iprev = data;
1129 		}
1130 		ifirst = data;
1131 	    }
1132 	}
1133 	if (T_INDEXED(elt->type) && data != elt->u.array->primary->data) {
1134 	    /* mark as unimported */
1135 	    data->plane->imports--;
1136 	}
1137     }
1138 
1139     i_ref_value(val);
1140     i_del_value(elt);
1141 
1142     *elt = *val;
1143     elt->modified = TRUE;
1144 }
1145 
1146 /*
1147  * NAME:	data->change_map()
1148  * DESCRIPTION:	mark a mapping as changed in size
1149  */
d_change_map(array * map)1150 void d_change_map(array *map)
1151 {
1152     arrref *a;
1153 
1154     a = map->primary;
1155     if (a->state == AR_UNCHANGED) {
1156 	a->plane->achange++;
1157 	a->state = AR_CHANGED;
1158     }
1159 }
1160 
1161 
1162 /*
1163  * NAME:	data->new_call_out()
1164  * DESCRIPTION:	add a new callout
1165  */
d_new_call_out(dataspace * data,string * func,Int delay,unsigned int mdelay,frame * f,int nargs)1166 uindex d_new_call_out(dataspace *data, string *func, Int delay,
1167 	unsigned int mdelay, frame *f, int nargs)
1168 {
1169     Uint ct, t;
1170     unsigned short m;
1171     uindex *q;
1172     value v[4];
1173     uindex handle;
1174 
1175     ct = co_check(ncallout, delay, mdelay, &t, &m, &q);
1176     if (ct == 0 && q == (uindex *) NULL) {
1177 	/* callouts are disabled */
1178 	return 0;
1179     }
1180 
1181     PUT_STRVAL(&v[0], func);
1182     switch (nargs) {
1183     case 3:
1184 	v[3] = f->sp[2];
1185     case 2:
1186 	v[2] = f->sp[1];
1187     case 1:
1188 	v[1] = f->sp[0];
1189     case 0:
1190 	break;
1191 
1192     default:
1193 	v[1] = f->sp[0];
1194 	v[2] = f->sp[1];
1195 	PUT_ARRVAL(&v[3], arr_new(data, nargs - 2L));
1196 	memcpy(v[3].u.array->elts, f->sp + 2, (nargs - 2) * sizeof(value));
1197 	d_ref_imports(v[3].u.array);
1198 	break;
1199     }
1200     f->sp += nargs;
1201     handle = d_alloc_call_out(data, 0, ct, m, nargs, v);
1202 
1203     if (data->plane->level == 0) {
1204 	/*
1205 	 * add normal callout
1206 	 */
1207 	co_new(data->oindex, handle, t, m, q);
1208     } else {
1209 	dataplane *plane;
1210 	copatch **c, *cop;
1211 	dcallout *co;
1212 	copatch **cc;
1213 
1214 	/*
1215 	 * add callout patch
1216 	 */
1217 	plane = data->plane;
1218 	if (plane->coptab == (coptable *) NULL) {
1219 	    cop_init(plane);
1220 	}
1221 	co = &data->callouts[handle - 1];
1222 	cc = c = &plane->coptab->cop[handle % COPATCHHTABSZ];
1223 	for (;;) {
1224 	    cop = *c;
1225 	    if (cop == (copatch *) NULL || cop->plane != plane) {
1226 		/* add new */
1227 		cop_new(plane, cc, COP_ADD, handle, co, t, m, q);
1228 		break;
1229 	    }
1230 
1231 	    if (cop->handle == handle) {
1232 		/* replace removed */
1233 		cop_replace(cop, co, t, m, q);
1234 		break;
1235 	    }
1236 
1237 	    c = &cop->next;
1238 	}
1239 
1240 	ncallout++;
1241     }
1242 
1243     return handle;
1244 }
1245 
1246 /*
1247  * NAME:	data->del_call_out()
1248  * DESCRIPTION:	remove a callout
1249  */
d_del_call_out(dataspace * data,Uint handle,unsigned short * mtime)1250 Int d_del_call_out(dataspace *data, Uint handle, unsigned short *mtime)
1251 {
1252     dcallout *co;
1253     Int t;
1254 
1255     *mtime = 0xffff;
1256     if (handle == 0 || handle > data->ncallouts) {
1257 	/* no such callout */
1258 	return -1;
1259     }
1260     if (data->callouts == (dcallout *) NULL) {
1261 	d_get_callouts(data);
1262     }
1263 
1264     co = &data->callouts[handle - 1];
1265     if (co->val[0].type != T_STRING) {
1266 	/* invalid callout */
1267 	return -1;
1268     }
1269 
1270     *mtime = co->mtime;
1271     t = co_remaining(co->time, mtime);
1272     if (data->plane->level == 0) {
1273 	/*
1274 	 * remove normal callout
1275 	 */
1276 	co_del(data->oindex, (uindex) handle, co->time, co->mtime);
1277     } else {
1278 	dataplane *plane;
1279 	copatch **c, *cop;
1280 	copatch **cc;
1281 
1282 	/*
1283 	 * add/remove callout patch
1284 	 */
1285 	--ncallout;
1286 
1287 	plane = data->plane;
1288 	if (plane->coptab == (coptable *) NULL) {
1289 	    cop_init(plane);
1290 	}
1291 	cc = c = &plane->coptab->cop[handle % COPATCHHTABSZ];
1292 	for (;;) {
1293 	    cop = *c;
1294 	    if (cop == (copatch *) NULL || cop->plane != plane) {
1295 		/* delete new */
1296 		cop_new(plane, cc, COP_REMOVE, (uindex) handle, co, (Uint) 0, 0,
1297 			(uindex *) NULL);
1298 		break;
1299 	    }
1300 	    if (cop->handle == handle) {
1301 		/* delete existing */
1302 		if (cop->type == COP_REPLACE) {
1303 		    cop_release(cop);
1304 		} else {
1305 		    cop_del(plane, c, TRUE);
1306 		}
1307 		break;
1308 	    }
1309 	    c = &cop->next;
1310 	}
1311     }
1312     d_free_call_out(data, (uindex) handle);
1313 
1314     return t;
1315 }
1316 
1317 /*
1318  * NAME:	data->get_call_out()
1319  * DESCRIPTION:	get a callout
1320  */
d_get_call_out(dataspace * data,unsigned int handle,frame * f,int * nargs)1321 string *d_get_call_out(dataspace *data, unsigned int handle, frame *f,
1322 	int *nargs)
1323 {
1324     string *str;
1325     dcallout *co;
1326     value *v, *o;
1327     uindex n;
1328 
1329     if (data->callouts == (dcallout *) NULL) {
1330 	d_get_callouts(data);
1331     }
1332 
1333     co = &data->callouts[handle - 1];
1334     v = co->val;
1335     del_lhs(data, &v[0]);
1336     str = v[0].u.string;
1337 
1338     i_grow_stack(f, (*nargs = co->nargs) + 1);
1339     *--f->sp = v[0];
1340 
1341     switch (co->nargs) {
1342     case 3:
1343 	del_lhs(data, &v[3]);
1344 	*--f->sp = v[3];
1345     case 2:
1346 	del_lhs(data, &v[2]);
1347 	*--f->sp = v[2];
1348     case 1:
1349 	del_lhs(data, &v[1]);
1350 	*--f->sp = v[1];
1351     case 0:
1352 	break;
1353 
1354     default:
1355 	n = co->nargs - 2;
1356 	f->sp -= n;
1357 	memcpy(f->sp, d_get_elts(v[3].u.array), n * sizeof(value));
1358 	del_lhs(data, &v[3]);
1359 	FREE(v[3].u.array->elts);
1360 	v[3].u.array->elts = (value *) NULL;
1361 	arr_del(v[3].u.array);
1362 	del_lhs(data, &v[2]);
1363 	*--f->sp = v[2];
1364 	del_lhs(data, &v[1]);
1365 	*--f->sp = v[1];
1366 	break;
1367     }
1368 
1369     /* wipe out destructed objects */
1370     for (n = co->nargs, v = f->sp; n > 0; --n, v++) {
1371 	switch (v->type) {
1372 	case T_OBJECT:
1373 	    if (DESTRUCTED(v)) {
1374 		*v = nil_value;
1375 	    }
1376 	    break;
1377 
1378 	case T_LWOBJECT:
1379 	    o = d_get_elts(v->u.array);
1380 	    if (o->type == T_OBJECT && DESTRUCTED(o)) {
1381 		arr_del(v->u.array);
1382 		*v = nil_value;
1383 	    }
1384 	    break;
1385 	}
1386     }
1387 
1388     co->val[0] = nil_value;
1389     n = data->fcallouts;
1390     if (n != 0) {
1391 	data->callouts[n - 1].co_prev = handle;
1392     }
1393     co->co_next = n;
1394     data->fcallouts = handle;
1395 
1396     data->plane->flags |= MOD_CALLOUT;
1397     return str;
1398 }
1399 
1400 /*
1401  * NAME:	data->list_callouts()
1402  * DESCRIPTION:	list all call_outs in an object
1403  */
d_list_callouts(dataspace * host,dataspace * data)1404 array *d_list_callouts(dataspace *host, dataspace *data)
1405 {
1406     uindex n, count, size;
1407     dcallout *co;
1408     value *v, *v2, *elts;
1409     array *list, *a;
1410     uindex max_args;
1411     xfloat flt;
1412 
1413     if (data->ncallouts == 0) {
1414 	return arr_new(host, 0L);
1415     }
1416     if (data->callouts == (dcallout *) NULL) {
1417 	d_get_callouts(data);
1418     }
1419 
1420     /* get the number of callouts in this object */
1421     count = data->ncallouts;
1422     for (n = data->fcallouts; n != 0; n = data->callouts[n - 1].co_next) {
1423 	--count;
1424     }
1425     if (count > conf_array_size()) {
1426 	return (array *) NULL;
1427     }
1428 
1429     list = arr_new(host, (long) count);
1430     elts = list->elts;
1431     max_args = conf_array_size() - 3;
1432 
1433     for (co = data->callouts; count > 0; co++) {
1434 	if (co->val[0].type == T_STRING) {
1435 	    size = co->nargs;
1436 	    if (size > max_args) {
1437 		/* unlikely, but possible */
1438 		size = max_args;
1439 	    }
1440 	    a = arr_new(host, size + 3L);
1441 	    v = a->elts;
1442 
1443 	    /* handle */
1444 	    PUT_INTVAL(v, co - data->callouts + 1);
1445 	    v++;
1446 	    /* function */
1447 	    PUT_STRVAL(v, co->val[0].u.string);
1448 	    v++;
1449 	    /* time */
1450 	    if (co->mtime == 0xffff) {
1451 		PUT_INTVAL(v, co->time);
1452 	    } else {
1453 		flt.low = co->time;
1454 		flt.high = co->mtime;
1455 		PUT_FLTVAL(v, flt);
1456 	    }
1457 	    v++;
1458 
1459 	    /* copy arguments */
1460 	    switch (size) {
1461 	    case 3:
1462 		*v++ = co->val[3];
1463 	    case 2:
1464 		*v++ = co->val[2];
1465 	    case 1:
1466 		*v++ = co->val[1];
1467 	    case 0:
1468 		break;
1469 
1470 	    default:
1471 		n = size - 2;
1472 		for (v2 = d_get_elts(co->val[3].u.array) + n; n > 0; --n) {
1473 		    *v++ = *--v2;
1474 		}
1475 		*v++ = co->val[2];
1476 		*v++ = co->val[1];
1477 		break;
1478 	    }
1479 	    while (size > 0) {
1480 		i_ref_value(--v);
1481 		--size;
1482 	    }
1483 	    d_ref_imports(a);
1484 
1485 	    /* put in list */
1486 	    PUT_ARRVAL(elts, a);
1487 	    elts++;
1488 	    --count;
1489 	}
1490     }
1491     co_list(list);
1492 
1493     return list;
1494 }
1495 
1496 
1497 /*
1498  * NAME:	data->set_varmap()
1499  * DESCRIPTION:	add a variable mapping to a control block
1500  */
d_set_varmap(control * ctrl,unsigned short * vmap)1501 void d_set_varmap(control *ctrl, unsigned short *vmap)
1502 {
1503     ctrl->vmapsize = ctrl->nvariables + 1;
1504     ctrl->vmap = vmap;
1505 }
1506 
1507 /*
1508  * NAME:	data->get_varmap()
1509  * DESCRIPTION:	get the variable mapping for an object
1510  */
d_get_varmap(object ** obj,Uint update,unsigned short * nvariables)1511 static unsigned short *d_get_varmap(object **obj, Uint update, unsigned short *nvariables)
1512 {
1513     object *tmpl;
1514     unsigned short nvar, *vmap;
1515 
1516     tmpl = OBJ((*obj)->prev);
1517     if (O_UPGRADING(*obj)) {
1518 	/* in the middle of an upgrade */
1519 	tmpl = OBJ(tmpl->prev);
1520     }
1521     vmap = o_control(tmpl)->vmap;
1522     nvar = tmpl->ctrl->vmapsize;
1523 
1524     if (tmpl->update != update) {
1525 	unsigned short *m1, *m2, n;
1526 
1527 	m1 = vmap;
1528 	vmap = ALLOC(unsigned short, n = nvar);
1529 	do {
1530 	    tmpl = OBJ(tmpl->prev);
1531 	    m2 = o_control(tmpl)->vmap;
1532 	    while (n > 0) {
1533 		*vmap++ = (NEW_VAR(*m1)) ? *m1++ : m2[*m1++];
1534 		--n;
1535 	    }
1536 	    n = nvar;
1537 	    vmap -= n;
1538 	    m1 = vmap;
1539 	} while (tmpl->update != update);
1540     }
1541 
1542     *obj = tmpl;
1543     *nvariables = nvar;
1544     return vmap;
1545 }
1546 
1547 /*
1548  * NAME:	data->upgrade_data()
1549  * DESCRIPTION:	upgrade the dataspace for one object
1550  */
d_upgrade_data(dataspace * data,unsigned int nvar,unsigned short * vmap,object * tmpl)1551 void d_upgrade_data(dataspace *data, unsigned int nvar, unsigned short *vmap,
1552 	object *tmpl)
1553 {
1554     value *v;
1555     unsigned short n;
1556     value *vars;
1557 
1558     /* make sure variables are in memory */
1559     vars = d_get_variable(data, 0);
1560 
1561     /* map variables */
1562     for (n = nvar, v = ALLOC(value, n); n > 0; --n) {
1563 	switch (*vmap) {
1564 	case NEW_INT:
1565 	    *v++ = zero_int;
1566 	    break;
1567 
1568 	case NEW_FLOAT:
1569 	    *v++ = zero_float;
1570 	    break;
1571 
1572 	case NEW_POINTER:
1573 	    *v++ = nil_value;
1574 	    break;
1575 
1576 	default:
1577 	    *v = vars[*vmap];
1578 	    i_ref_value(v);
1579 	    v->modified = TRUE;
1580 	    ref_rhs(data, v++);
1581 	    break;
1582 	}
1583 	vmap++;
1584     }
1585     vars = v - nvar;
1586 
1587     /* deref old values */
1588     v = data->variables;
1589     for (n = data->nvariables; n > 0; --n) {
1590 	del_lhs(data, v);
1591 	i_del_value(v++);
1592     }
1593 
1594     /* replace old with new */
1595     FREE(data->variables);
1596     data->variables = vars;
1597 
1598     data->base.flags |= MOD_VARIABLE;
1599     if (data->nvariables != nvar) {
1600 	if (data->svariables != (svalue *) NULL) {
1601 	    FREE(data->svariables);
1602 	    data->svariables = (svalue *) NULL;
1603 	}
1604 	data->nvariables = nvar;
1605 	data->base.achange++;	/* force rebuild on swapout */
1606     }
1607 
1608     o_upgraded(tmpl, OBJ(data->oindex));
1609 }
1610 
1611 /*
1612  * NAME:	data->upgrade_clone()
1613  * DESCRIPTION:	upgrade a clone object
1614  */
d_upgrade_clone(dataspace * data)1615 void d_upgrade_clone(dataspace *data)
1616 {
1617     object *obj;
1618     unsigned short *vmap, nvar;
1619     Uint update;
1620 
1621     /*
1622      * the program for the clone was upgraded since last swapin
1623      */
1624     obj = OBJ(data->oindex);
1625     update = obj->update;
1626     obj = OBJ(obj->u_master);
1627     vmap = d_get_varmap(&obj, update, &nvar);
1628     d_upgrade_data(data, nvar, vmap, obj);
1629     if (vmap != obj->ctrl->vmap) {
1630 	FREE(vmap);
1631     }
1632 }
1633 
1634 /*
1635  * NAME:	data->upgrade_lwobj()
1636  * DESCRIPTION:	upgrade a non-persistent object
1637  */
d_upgrade_lwobj(array * lwobj,object * obj)1638 object *d_upgrade_lwobj(array *lwobj, object *obj)
1639 {
1640     arrref *a;
1641     unsigned short n;
1642     value *v;
1643     Uint update;
1644     unsigned short nvar, *vmap;
1645     value *vars;
1646 
1647     a = lwobj->primary;
1648     update = obj->update;
1649     vmap = d_get_varmap(&obj, (Uint) lwobj->elts[1].u.number, &nvar);
1650     --nvar;
1651 
1652     /* map variables */
1653     v = ALLOC(value, nvar + 2);
1654     *v++ = lwobj->elts[0];
1655     *v = lwobj->elts[1];
1656     (v++)->u.objcnt = update;
1657 
1658     vars = lwobj->elts + 2;
1659     for (n = nvar; n > 0; --n) {
1660 	switch (*vmap) {
1661 	case NEW_INT:
1662 	    *v++ = zero_int;
1663 	    break;
1664 
1665 	case NEW_FLOAT:
1666 	    *v++ = zero_float;
1667 	    break;
1668 
1669 	case NEW_POINTER:
1670 	    *v++ = nil_value;
1671 	    break;
1672 
1673 	default:
1674 	    *v = vars[*vmap];
1675 	    if (a->arr != (array *) NULL) {
1676 		ref_rhs(a->data, v);
1677 	    }
1678 	    i_ref_value(v);
1679 	    (v++)->modified = TRUE;
1680 	    break;
1681 	}
1682 	vmap++;
1683     }
1684     vars = v - (nvar + 2);
1685 
1686     vmap -= nvar;
1687     if (vmap != obj->ctrl->vmap) {
1688 	FREE(vmap);
1689     }
1690 
1691     v = lwobj->elts + 2;
1692     if (a->arr != (array *) NULL) {
1693 	/* swapped-in */
1694 	if (a->state == AR_UNCHANGED) {
1695 	    dataplane *p;
1696 
1697 	    a->state = AR_CHANGED;
1698 	    for (p = a->data->plane; p != (dataplane *) NULL; p = p->prev) {
1699 		p->achange++;
1700 	    }
1701 	}
1702 
1703 	/* deref old values */
1704 	for (n = lwobj->size - 2; n > 0; --n) {
1705 	    del_lhs(a->data, v);
1706 	    i_del_value(v++);
1707 	}
1708     } else {
1709 	/* deref old values */
1710 	for (n = lwobj->size - 2; n > 0; --n) {
1711 	    i_del_value(v++);
1712 	}
1713     }
1714 
1715     /* replace old with new */
1716     lwobj->size = nvar + 2;
1717     FREE(lwobj->elts);
1718     lwobj->elts = vars;
1719 
1720     return obj;
1721 }
1722 
1723 /*
1724  * NAME:	data->import()
1725  * DESCRIPTION:	copy imported arrays to current dataspace
1726  */
d_import(arrimport * imp,dataspace * data,value * val,unsigned short n)1727 static void d_import(arrimport *imp, dataspace *data, value *val,
1728 	unsigned short n)
1729 {
1730     while (n > 0) {
1731 	if (T_INDEXED(val->type)) {
1732 	    array *a;
1733 	    Uint i, j;
1734 
1735 	    a = val->u.array;
1736 	    if (a->primary->data != data) {
1737 		/*
1738 		 * imported array
1739 		 */
1740 		i = arr_put(a, imp->narr);
1741 		if (i == imp->narr) {
1742 		    /*
1743 		     * first time encountered
1744 		     */
1745 		    imp->narr++;
1746 
1747 		    if (a->hashed != (struct _maphash_ *) NULL) {
1748 			map_rmhash(a);
1749 		    }
1750 
1751 		    if (a->ref == 2) {	/* + 1 for array merge table */
1752 			/*
1753 			 * move array to new dataspace
1754 			 */
1755 			a->prev->next = a->next;
1756 			a->next->prev = a->prev;
1757 		    } else {
1758 			/*
1759 			 * make new array
1760 			 */
1761 			a = arr_alloc(a->size);
1762 			a->tag = val->u.array->tag;
1763 			a->odcount = val->u.array->odcount;
1764 
1765 			if (a->size > 0) {
1766 			    /*
1767 			     * copy elements
1768 			     */
1769 			    i_copy(a->elts = ALLOC(value, a->size),
1770 				   d_get_elts(val->u.array), a->size);
1771 			}
1772 
1773 			/*
1774 			 * replace
1775 			 */
1776 			arr_del(val->u.array);
1777 			arr_ref(val->u.array = a);
1778 
1779 			/*
1780 			 * store in itab
1781 			 */
1782 			if (i >= imp->itabsz) {
1783 			    /*
1784 			     * increase size of itab
1785 			     */
1786 			    for (j = imp->itabsz; j <= i; j += j) ;
1787 			    imp->itab = REALLOC(imp->itab, array*, imp->itabsz,
1788 						j);
1789 			    imp->itabsz = j;
1790 			}
1791 			arr_put(imp->itab[i] = a, imp->narr++);
1792 		    }
1793 
1794 		    a->primary = &data->base.alocal;
1795 		    a->prev = &data->alist;
1796 		    a->next = data->alist.next;
1797 		    a->next->prev = a;
1798 		    data->alist.next = a;
1799 
1800 		    if (a->size > 0) {
1801 			/*
1802 			 * import elements too
1803 			 */
1804 			d_import(imp, data, a->elts, a->size);
1805 		    }
1806 		} else {
1807 		    /*
1808 		     * array was previously replaced
1809 		     */
1810 		    arr_ref(a = imp->itab[i]);
1811 		    arr_del(val->u.array);
1812 		    val->u.array = a;
1813 		}
1814 	    } else if (arr_put(a, imp->narr) == imp->narr) {
1815 		/*
1816 		 * not previously encountered mapping or array
1817 		 */
1818 		imp->narr++;
1819 		if (a->hashed != (struct _maphash_ *) NULL) {
1820 		    map_rmhash(a);
1821 		    d_import(imp, data, a->elts, a->size);
1822 		} else if (a->elts != (value *) NULL) {
1823 		    d_import(imp, data, a->elts, a->size);
1824 		}
1825 	    }
1826 	}
1827 	val++;
1828 	--n;
1829     }
1830 }
1831 
1832 /*
1833  * NAME:	data->export()
1834  * DESCRIPTION:	handle exporting of arrays shared by more than one object
1835  */
d_export()1836 void d_export()
1837 {
1838     dataspace *data;
1839     Uint n;
1840     arrimport imp;
1841 
1842     if (ifirst != (dataspace *) NULL) {
1843 	imp.itab = ALLOC(array*, imp.itabsz = 64);
1844 
1845 	for (data = ifirst; data != (dataspace *) NULL; data = data->inext) {
1846 	    if (data->base.imports != 0) {
1847 		data->base.imports = 0;
1848 		arr_merge();
1849 		imp.narr = 0;
1850 
1851 		if (data->variables != (value *) NULL) {
1852 		    d_import(&imp, data, data->variables, data->nvariables);
1853 		}
1854 		if (data->base.arrays != (arrref *) NULL) {
1855 		    arrref *a;
1856 
1857 		    for (n = data->narrays, a = data->base.arrays; n > 0;
1858 			 --n, a++) {
1859 			if (a->arr != (array *) NULL) {
1860 			    if (a->arr->hashed != (struct _maphash_ *) NULL) {
1861 				/* mapping */
1862 				map_rmhash(a->arr);
1863 				d_import(&imp, data, a->arr->elts,
1864 					 a->arr->size);
1865 			    } else if (a->arr->elts != (value *) NULL) {
1866 				d_import(&imp, data, a->arr->elts,
1867 					 a->arr->size);
1868 			    }
1869 			}
1870 		    }
1871 		}
1872 		if (data->callouts != (dcallout *) NULL) {
1873 		    dcallout *co;
1874 
1875 		    co = data->callouts;
1876 		    for (n = data->ncallouts; n > 0; --n) {
1877 			if (co->val[0].type == T_STRING) {
1878 			    d_import(&imp, data, co->val,
1879 				     (co->nargs > 3) ? 4 : co->nargs + 1);
1880 			}
1881 			co++;
1882 		    }
1883 		}
1884 		arr_clear();	/* clear merge table */
1885 	    }
1886 	    data->iprev = (dataspace *) NULL;
1887 	}
1888 	ifirst = (dataspace *) NULL;
1889 
1890 	FREE(imp.itab);
1891     }
1892 }
1893 
1894 
1895 /*
1896  * NAME:	data->del_control()
1897  * DESCRIPTION:	delete a control block from swap and memory
1898  */
d_del_control(control * ctrl)1899 void d_del_control(control *ctrl)
1900 {
1901     if (ctrl->sectors != (sector *) NULL) {
1902 	sw_wipev(ctrl->sectors, ctrl->nsectors);
1903 	sw_delv(ctrl->sectors, ctrl->nsectors);
1904     }
1905     d_free_control(ctrl);
1906 }
1907 
1908 /*
1909  * NAME:	data->del_dataspace()
1910  * DESCRIPTION:	delete a dataspace block from swap and memory
1911  */
d_del_dataspace(dataspace * data)1912 void d_del_dataspace(dataspace *data)
1913 {
1914     if (data->iprev != (dataspace *) NULL) {
1915 	data->iprev->inext = data->inext;
1916 	if (data->inext != (dataspace *) NULL) {
1917 	    data->inext->iprev = data->iprev;
1918 	}
1919     } else if (ifirst == data) {
1920 	ifirst = data->inext;
1921 	if (ifirst != (dataspace *) NULL) {
1922 	    ifirst->iprev = (dataspace *) NULL;
1923 	}
1924     }
1925 
1926     if (data->ncallouts != 0) {
1927 	Uint n;
1928 	dcallout *co;
1929 	unsigned short dummy;
1930 
1931 	/*
1932 	 * remove callouts from callout table
1933 	 */
1934 	if (data->callouts == (dcallout *) NULL) {
1935 	    d_get_callouts(data);
1936 	}
1937 	for (n = data->ncallouts, co = data->callouts + n; n > 0; --n) {
1938 	    if ((--co)->val[0].type == T_STRING) {
1939 		d_del_call_out(data, n, &dummy);
1940 	    }
1941 	}
1942     }
1943     if (data->sectors != (sector *) NULL) {
1944 	sw_wipev(data->sectors, data->nsectors);
1945 	sw_delv(data->sectors, data->nsectors);
1946     }
1947     d_free_dataspace(data);
1948 }
1949