# include "dgd.h" # include "str.h" # include "array.h" # include "object.h" # include "interpret.h" # include "data.h" # include "call_out.h" # include "parse.h" # include "csupport.h" # define COP_ADD 0 /* add callout patch */ # define COP_REMOVE 1 /* remove callout patch */ # define COP_REPLACE 2 /* replace callout patch */ typedef struct _copatch_ { short type; /* add, remove, replace */ uindex handle; /* callout handle */ dataplane *plane; /* dataplane */ Uint time; /* start time */ unsigned short mtime; /* start time millisec component */ cbuf *queue; /* callout queue */ struct _copatch_ *next; /* next in linked list */ dcallout aco; /* added callout */ dcallout rco; /* removed callout */ } copatch; # define COPCHUNKSZ 32 typedef struct _copchunk_ { struct _copchunk_ *next; /* next in linked list */ copatch cop[COPCHUNKSZ]; /* callout patches */ } copchunk; typedef struct _coptable_ { copchunk *chunk; /* callout patch chunk */ unsigned short chunksz; /* size of callout patch chunk */ copatch *flist; /* free list of callout patches */ copatch *cop[COPATCHHTABSZ]; /* hash table of callout patches */ } coptable; typedef struct { array **itab; /* imported array replacement table */ Uint itabsz; /* size of table */ arrmerge *merge; /* array merge table */ Uint narr; /* # of arrays */ } arrimport; static dataplane *plist; /* list of dataplanes */ static uindex ncallout; /* # callouts added */ static dataspace *ifirst; /* list of dataspaces with imports */ /* * NAME: ref_rhs() * DESCRIPTION: reference the right-hand side in an assignment */ static void ref_rhs(data, rhs) register dataspace *data; register value *rhs; { register string *str; register array *arr; switch (rhs->type) { case T_STRING: str = rhs->u.string; if (str->primary != (strref *) NULL && str->primary->data == data) { /* in this object */ str->primary->ref++; data->plane->flags |= MOD_STRINGREF; } else { /* not in this object: ref imported string */ data->plane->schange++; } break; case T_ARRAY: case T_MAPPING: case T_LWOBJECT: arr = rhs->u.array; if (arr->primary->data == data) { /* in this object */ if (arr->primary->arr != (array *) NULL) { /* swapped in */ arr->primary->ref++; data->plane->flags |= MOD_ARRAYREF; } else { /* ref new array */ data->plane->achange++; } } else { /* not in this object: ref imported array */ if (data->plane->imports++ == 0 && ifirst != data && data->iprev == (dataspace *) NULL) { /* add to imports list */ data->iprev = (dataspace *) NULL; data->inext = ifirst; if (ifirst != (dataspace *) NULL) { ifirst->iprev = data; } ifirst = data; } data->plane->achange++; } break; } } /* * NAME: del_lhs() * DESCRIPTION: delete the left-hand side in an assignment */ static void del_lhs(data, lhs) register dataspace *data; register value *lhs; { register string *str; register array *arr; switch (lhs->type) { case T_STRING: str = lhs->u.string; if (str->primary != (strref *) NULL && str->primary->data == data) { /* in this object */ if (--(str->primary->ref) == 0) { str->primary->str = (string *) NULL; str->primary = (strref *) NULL; str_del(str); data->plane->schange++; /* last reference removed */ } data->plane->flags |= MOD_STRINGREF; } else { /* not in this object: deref imported string */ data->plane->schange--; } break; case T_ARRAY: case T_MAPPING: case T_LWOBJECT: arr = lhs->u.array; if (arr->primary->data == data) { /* in this object */ if (arr->primary->arr != (array *) NULL) { /* swapped in */ data->plane->flags |= MOD_ARRAYREF; if ((--(arr->primary->ref) & ~ARR_MOD) == 0) { register unsigned short n; /* last reference removed */ if (arr->hashmod) { map_compact(data, arr); } else { d_get_elts(arr); } arr->primary->arr = (array *) NULL; arr->primary = &arr->primary->plane->alocal; for (n = arr->size, lhs = arr->elts; n != 0; --n, lhs++) { del_lhs(data, lhs); } arr_del(arr); data->plane->achange++; } } else { /* deref new array */ data->plane->achange--; } } else { /* not in this object: deref imported array */ data->plane->imports--; data->plane->achange--; } break; } } /* * NAME: data->alloc_call_out() * DESCRIPTION: allocate a new callout */ static uindex d_alloc_call_out(data, handle, time, nargs, v) register dataspace *data; register uindex handle; Uint time; int nargs; register value *v; { register dcallout *co; if (data->ncallouts == 0) { /* * the first in this object */ co = data->callouts = ALLOC(dcallout, 1); data->ncallouts = handle = 1; data->plane->flags |= MOD_NEWCALLOUT; } else { if (data->callouts == (dcallout *) NULL) { d_get_callouts(data); } if (handle != 0) { /* * get a specific callout from the free list */ co = &data->callouts[handle - 1]; if (handle == data->fcallouts) { data->fcallouts = co->co_next; } else { data->callouts[co->co_prev - 1].co_next = co->co_next; if (co->co_next != 0) { data->callouts[co->co_next - 1].co_prev = co->co_prev; } } } else { handle = data->fcallouts; if (handle != 0) { /* * from free list */ co = &data->callouts[handle - 1]; if (co->co_next == 0 || co->co_next > handle) { /* take 1st free callout */ data->fcallouts = co->co_next; } else { /* take 2nd free callout */ co = &data->callouts[co->co_next - 1]; data->callouts[handle - 1].co_next = co->co_next; if (co->co_next != 0) { data->callouts[co->co_next - 1].co_prev = handle; } handle = co - data->callouts + 1; } data->plane->flags |= MOD_CALLOUT; } else { /* * add new callout */ handle = data->ncallouts; co = data->callouts = REALLOC(data->callouts, dcallout, handle, handle + 1); co += handle; data->ncallouts = ++handle; data->plane->flags |= MOD_NEWCALLOUT; } } } co->time = time; co->nargs = nargs; memcpy(co->val, v, sizeof(co->val)); switch (nargs) { default: ref_rhs(data, &v[3]); case 2: ref_rhs(data, &v[2]); case 1: ref_rhs(data, &v[1]); case 0: ref_rhs(data, &v[0]); break; } return handle; } /* * NAME: data->free_call_out() * DESCRIPTION: free a callout */ static void d_free_call_out(data, handle) register dataspace *data; unsigned int handle; { register dcallout *co; register value *v; uindex n; co = &data->callouts[handle - 1]; v = co->val; switch (co->nargs) { default: del_lhs(data, &v[3]); i_del_value(&v[3]); case 2: del_lhs(data, &v[2]); i_del_value(&v[2]); case 1: del_lhs(data, &v[1]); i_del_value(&v[1]); case 0: del_lhs(data, &v[0]); str_del(v[0].u.string); break; } v[0] = nil_value; n = data->fcallouts; if (n != 0) { data->callouts[n - 1].co_prev = handle; } co->co_next = n; data->fcallouts = handle; data->plane->flags |= MOD_CALLOUT; } /* * NAME: copatch->init() * DESCRIPTION: initialize copatch table */ static void cop_init(plane) dataplane *plane; { memset(plane->coptab = ALLOC(coptable, 1), '\0', sizeof(coptable)); } /* * NAME: copatch->clean() * DESCRIPTION: free copatch table */ static void cop_clean(plane) dataplane *plane; { register copchunk *c, *f; c = plane->coptab->chunk; while (c != (copchunk *) NULL) { f = c; c = c->next; FREE(f); } FREE(plane->coptab); plane->coptab = (coptable *) NULL; } /* * NAME: copatch->new() * DESCRIPTION: create a new callout patch */ static copatch *cop_new(plane, c, type, handle, co, time, mtime, q) dataplane *plane; copatch **c; int type; unsigned int handle, mtime; register dcallout *co; Uint time; cbuf *q; { register coptable *tab; register copatch *cop; register int i; register value *v; /* allocate */ tab = plane->coptab; if (tab->flist != (copatch *) NULL) { /* from free list */ cop = tab->flist; tab->flist = cop->next; } else { /* newly allocated */ if (tab->chunk == (copchunk *) NULL || tab->chunksz == COPCHUNKSZ) { register copchunk *cc; /* create new chunk */ cc = ALLOC(copchunk, 1); cc->next = tab->chunk; tab->chunk = cc; tab->chunksz = 0; } cop = &tab->chunk->cop[tab->chunksz++]; } /* initialize */ cop->type = type; cop->handle = handle; if (type == COP_ADD) { cop->aco = *co; } else { cop->rco = *co; } for (i = (co->nargs > 3) ? 4 : co->nargs + 1, v = co->val; i > 0; --i) { i_ref_value(v++); } cop->time = time; cop->mtime = mtime; cop->plane = plane; cop->queue = q; /* add to hash table */ cop->next = *c; return *c = cop; } /* * NAME: copatch->del() * DESCRIPTION: delete a callout patch */ static void cop_del(plane, c, del) dataplane *plane; copatch **c; bool del; { register copatch *cop; register dcallout *co; register int i; register value *v; coptable *tab; /* remove from hash table */ cop = *c; *c = cop->next; if (del) { /* free referenced callout */ co = (cop->type == COP_ADD) ? &cop->aco : &cop->rco; v = co->val; for (i = (co->nargs > 3) ? 4 : co->nargs + 1; i > 0; --i) { i_del_value(v++); } } /* add to free list */ tab = plane->coptab; cop->next = tab->flist; tab->flist = cop; } /* * NAME: copatch->replace() * DESCRIPTION: replace one callout patch with another */ static void cop_replace(cop, co, time, mtime, q) register copatch *cop; register dcallout *co; Uint time; unsigned int mtime; cbuf *q; { register int i; register value *v; cop->type = COP_REPLACE; cop->aco = *co; for (i = (co->nargs > 3) ? 4 : co->nargs + 1, v = co->val; i > 0; --i) { i_ref_value(v++); } cop->time = time; cop->mtime = mtime; cop->queue = q; } /* * NAME: copatch->commit() * DESCRIPTION: commit a callout replacement */ static void cop_commit(cop) register copatch *cop; { register int i; register value *v; cop->type = COP_ADD; for (i = (cop->rco.nargs > 3) ? 4 : cop->rco.nargs + 1, v = cop->rco.val; i > 0; --i) { i_del_value(v++); } } /* * NAME: copatch->release() * DESCRIPTION: remove a callout replacement */ static void cop_release(cop) register copatch *cop; { register int i; register value *v; cop->type = COP_REMOVE; for (i = (cop->aco.nargs > 3) ? 4 : cop->aco.nargs + 1, v = cop->aco.val; i > 0; --i) { i_del_value(v++); } } /* * NAME: copatch->discard() * DESCRIPTION: discard replacement */ static void cop_discard(cop) copatch *cop; { /* force unref of proper component later */ cop->type = COP_ADD; } /* * NAME: data->new_plane() * DESCRIPTION: create a new dataplane */ void d_new_plane(data, level) register dataspace *data; Int level; { register dataplane *p; register Uint i; p = ALLOC(dataplane, 1); p->level = level; p->flags = data->plane->flags; p->schange = data->plane->schange; p->achange = data->plane->achange; p->imports = data->plane->imports; /* copy value information from previous plane */ p->original = (value *) NULL; p->alocal.arr = (array *) NULL; p->alocal.plane = p; p->alocal.data = data; p->alocal.state = AR_CHANGED; p->coptab = data->plane->coptab; if (data->plane->arrays != (arrref *) NULL) { register arrref *a, *b; p->arrays = ALLOC(arrref, i = data->narrays); for (a = p->arrays, b = data->plane->arrays; i != 0; a++, b++, --i) { if (b->arr != (array *) NULL) { *a = *b; a->arr->primary = a; arr_ref(a->arr); } else { a->arr = (array *) NULL; } } } else { p->arrays = (arrref *) NULL; } p->achunk = (abchunk *) NULL; if (data->plane->strings != (strref *) NULL) { register strref *s, *t; p->strings = ALLOC(strref, i = data->nstrings); for (s = p->strings, t = data->plane->strings; i != 0; s++, t++, --i) { if (t->str != (string *) NULL) { *s = *t; s->str->primary = s; str_ref(s->str); } else { s->str = (string *) NULL; } } } else { p->strings = (strref *) NULL; } p->prev = data->plane; data->plane = p; p->plist = plist; plist = p; } /* * NAME: commit_values() * DESCRIPTION: commit non-swapped arrays among the values */ static void commit_values(v, n, level) register value *v; register unsigned int n; register Int level; { register array *arr; while (n != 0) { if (T_INDEXED(v->type)) { arr = v->u.array; if (arr->primary->arr == (array *) NULL && arr->primary->plane->level > level) { if (arr->hashmod) { map_compact(arr->primary->data, arr); } arr->primary = &arr->primary->plane->prev->alocal; commit_values(arr->elts, arr->size, level); } } v++; --n; } } /* * NAME: commit_callouts() * DESCRIPTION: commit callout patches to previous plane */ static void commit_callouts(plane, merge) register dataplane *plane; bool merge; { register dataplane *prev; register copatch **c, **n, *cop; copatch **t, **next; int i; prev = plane->prev; for (i = COPATCHHTABSZ, t = plane->coptab->cop; --i >= 0; t++) { if (*t != (copatch *) NULL && (*t)->plane == plane) { /* * find previous plane in hash chain */ next = t; do { next = &(*next)->next; } while (*next != (copatch *) NULL && (*next)->plane == plane); c = t; do { cop = *c; if (cop->type != COP_REMOVE) { commit_values(cop->aco.val + 1, (cop->aco.nargs > 3) ? 3 : cop->aco.nargs, prev->level); } if (prev->level == 0) { /* * commit to last plane */ switch (cop->type) { case COP_ADD: co_new(plane->alocal.data->oindex, cop->handle, cop->time, cop->mtime, cop->queue); --ncallout; break; case COP_REMOVE: co_del(plane->alocal.data->oindex, cop->handle, cop->rco.time); ncallout++; break; case COP_REPLACE: co_del(plane->alocal.data->oindex, cop->handle, cop->rco.time); co_new(plane->alocal.data->oindex, cop->handle, cop->time, cop->mtime, cop->queue); cop_commit(cop); break; } if (next == &cop->next) { next = c; } cop_del(plane, c, TRUE); } else { /* * commit to previous plane */ cop->plane = prev; if (merge) { for (n = next; *n != (copatch *) NULL && (*n)->plane == prev; n = &(*n)->next) { if (cop->handle == (*n)->handle) { switch (cop->type) { case COP_ADD: /* turn old remove into replace, del new */ cop_replace(*n, &cop->aco, cop->time, cop->mtime, cop->queue); if (next == &cop->next) { next = c; } cop_del(prev, c, TRUE); break; case COP_REMOVE: if ((*n)->type == COP_REPLACE) { /* turn replace back into remove */ cop_release(*n); } else { /* del old */ cop_del(prev, n, TRUE); } /* del new */ if (next == &cop->next) { next = c; } cop_del(prev, c, TRUE); break; case COP_REPLACE: if ((*n)->type == COP_REPLACE) { /* merge replaces into old, del new */ cop_release(*n); cop_replace(*n, &cop->aco, cop->time, cop->mtime, cop->queue); if (next == &cop->next) { next = c; } cop_del(prev, c, TRUE); } else { /* make replace into add, remove old */ cop_del(prev, n, TRUE); cop_commit(cop); } break; } break; } } } if (*c == cop) { c = &cop->next; } } } while (c != next); } } } /* * NAME: data->commit_plane() * DESCRIPTION: commit the current data plane */ void d_commit_plane(level, retval) Int level; value *retval; { register dataplane *p, *commit, **r, **cr; register dataspace *data; register value *v; register Uint i; dataplane *clist; /* * pass 1: construct commit planes */ clist = (dataplane *) NULL; cr = &clist; for (r = &plist, p = *r; p != (dataplane *) NULL && p->level == level; r = &p->plist, p = *r) { if (p->prev->level != level - 1) { /* insert commit plane */ commit = ALLOC(dataplane, 1); commit->level = level - 1; commit->original = (value *) NULL; commit->alocal.arr = (array *) NULL; commit->alocal.plane = commit; commit->alocal.data = p->alocal.data; commit->alocal.state = AR_CHANGED; commit->arrays = p->arrays; commit->achunk = p->achunk; commit->strings = p->strings; commit->coptab = p->coptab; commit->prev = p->prev; *cr = commit; cr = &commit->plist; p->prev = commit; } else { p->flags |= PLANE_MERGE; } } if (clist != (dataplane *) NULL) { /* insert commit planes in plane list */ *cr = p; *r = clist; } clist = *r; /* sentinel */ /* * pass 2: commit */ for (p = plist; p != clist; p = p->plist) { /* * commit changes to previous plane */ data = p->alocal.data; if (p->original != (value *) NULL) { if (p->level == 1 || p->prev->original != (value *) NULL) { /* free backed-up variable values */ for (v = p->original, i = data->nvariables; i != 0; v++, --i) { i_del_value(v); } FREE(p->original); } else { /* move originals to previous plane */ p->prev->original = p->original; } commit_values(data->variables, data->nvariables, level - 1); } if (p->coptab != (coptable *) NULL) { /* commit callout changes */ commit_callouts(p, p->flags & PLANE_MERGE); if (p->level == 1) { cop_clean(p); } else { p->prev->coptab = p->coptab; } } arr_commit(&p->achunk, p->prev, p->flags & PLANE_MERGE); if (p->flags & PLANE_MERGE) { if (p->arrays != (arrref *) NULL) { register arrref *a; /* remove old array refs */ for (a = p->prev->arrays, i = data->narrays; i != 0; a++, --i) { if (a->arr != (array *) NULL) { if (a->arr->primary == &p->alocal) { a->arr->primary = &p->prev->alocal; } arr_del(a->arr); } } FREE(p->prev->arrays); p->prev->arrays = p->arrays; } if (p->strings != (strref *) NULL) { register strref *s; /* remove old string refs */ for (s = p->prev->strings, i = data->nstrings; i != 0; s++, --i) { if (s->str != (string *) NULL) { str_del(s->str); } } FREE(p->prev->strings); p->prev->strings = p->strings; } } } commit_values(retval, 1, level - 1); /* * pass 3: deallocate */ for (p = plist; p != clist; p = plist) { p->prev->flags = p->flags & MOD_ALL; p->prev->schange = p->schange; p->prev->achange = p->achange; p->prev->imports = p->imports; p->alocal.data->plane = p->prev; plist = p->plist; FREE(p); } } /* * NAME: discard_callouts() * DESCRIPTION: discard callout patches on current plane, restoring old callouts */ static void discard_callouts(plane) register dataplane *plane; { register copatch *cop, **c, **t; register dataspace *data; register int i; data = plane->alocal.data; for (i = COPATCHHTABSZ, t = plane->coptab->cop; --i >= 0; t++) { c = t; while (*c != (copatch *) NULL && (*c)->plane == plane) { cop = *c; switch (cop->type) { case COP_ADD: d_free_call_out(data, cop->handle); cop_del(plane, c, TRUE); --ncallout; break; case COP_REMOVE: d_alloc_call_out(data, cop->handle, cop->rco.time, cop->rco.nargs, cop->rco.val); cop_del(plane, c, FALSE); ncallout++; break; case COP_REPLACE: d_free_call_out(data, cop->handle); d_alloc_call_out(data, cop->handle, cop->rco.time, cop->rco.nargs, cop->rco.val); cop_discard(cop); cop_del(plane, c, TRUE); break; } } } } /* * NAME: data->discard_plane() * DESCRIPTION: discard the current data plane without committing it */ void d_discard_plane(level) Int level; { register dataplane *p; register dataspace *data; register value *v; register Uint i; for (p = plist; p != (dataplane *) NULL && p->level == level; p = p->plist) { /* * discard changes except for callout mods */ p->prev->flags |= p->flags & (MOD_CALLOUT | MOD_NEWCALLOUT); data = p->alocal.data; if (p->original != (value *) NULL) { /* restore original variable values */ for (v = data->variables, i = data->nvariables; i != 0; --i, v++) { i_del_value(v); } memcpy(data->variables, p->original, data->nvariables * sizeof(value)); FREE(p->original); } if (p->coptab != (coptable *) NULL) { /* undo callout changes */ discard_callouts(p); if (p->prev == &data->base) { cop_clean(p); } else { p->prev->coptab = p->coptab; } } arr_discard(&p->achunk); if (p->arrays != (arrref *) NULL) { register arrref *a; /* delete new array refs */ for (a = p->arrays, i = data->narrays; i != 0; a++, --i) { if (a->arr != (array *) NULL) { arr_del(a->arr); } } FREE(p->arrays); /* fix old ones */ for (a = p->prev->arrays, i = data->narrays; i != 0; a++, --i) { if (a->arr != (array *) NULL) { a->arr->primary = a; } } } if (p->strings != (strref *) NULL) { register strref *s; /* delete new string refs */ for (s = p->strings, i = data->nstrings; i != 0; s++, --i) { if (s->str != (string *) NULL) { str_del(s->str); } } FREE(p->strings); /* fix old ones */ for (s = p->prev->strings, i = data->nstrings; i != 0; s++, --i) { if (s->str != (string *) NULL) { s->str->primary = s; } } } data->plane = p->prev; plist = p->plist; FREE(p); } } /* * NAME: data->commit_arr() * DESCRIPTION: commit array to previous plane */ abchunk **d_commit_arr(arr, prev, old) register array *arr; dataplane *prev, *old; { if (arr->primary->plane != prev) { if (arr->hashmod) { map_compact(arr->primary->data, arr); } if (arr->primary->arr == (array *) NULL) { arr->primary = &prev->alocal; } else { arr->primary->plane = prev; } commit_values(arr->elts, arr->size, prev->level); } return (prev == old) ? (abchunk **) NULL : &prev->achunk; } /* * NAME: data->discard_arr() * DESCRIPTION: restore array to previous plane */ void d_discard_arr(arr, plane) array *arr; dataplane *plane; { /* swapped-in arrays will be fixed later */ arr->primary = &plane->alocal; } /* * NAME: data->ref_imports() * DESCRIPTION: check the elements of an array for imports */ void d_ref_imports(arr) array *arr; { register dataspace *data; register unsigned short n; register value *v; data = arr->primary->data; for (n = arr->size, v = arr->elts; n > 0; --n, v++) { if (T_INDEXED(v->type) && data != v->u.array->primary->data) { /* mark as imported */ if (data->plane->imports++ == 0 && ifirst != data && data->iprev == (dataspace *) NULL) { /* add to imports list */ data->iprev = (dataspace *) NULL; data->inext = ifirst; if (ifirst != (dataspace *) NULL) { ifirst->iprev = data; } ifirst = data; } } } } /* * NAME: data->assign_var() * DESCRIPTION: assign a value to a variable */ void d_assign_var(data, var, val) register dataspace *data; register value *var; register value *val; { if (var >= data->variables && var < data->variables + data->nvariables) { if (data->plane->level != 0 && data->plane->original == (value *) NULL) { /* * back up variables */ i_copy(data->plane->original = ALLOC(value, data->nvariables), data->variables, data->nvariables); } ref_rhs(data, val); del_lhs(data, var); data->plane->flags |= MOD_VARIABLE; } i_ref_value(val); i_del_value(var); *var = *val; var->modified = TRUE; } /* * NAME: data->get_extravar() * DESCRIPTION: get an object's special value */ value *d_get_extravar(data) dataspace *data; { return d_get_variable(data, data->nvariables - 1); } /* * NAME: data->set_extravar() * DESCRIPTION: set an object's special value */ void d_set_extravar(data, val) register dataspace *data; value *val; { d_assign_var(data, d_get_variable(data, data->nvariables - 1), val); } /* * NAME: data->wipe_extravar() * DESCRIPTION: wipe out an object's special value */ void d_wipe_extravar(data) register dataspace *data; { d_assign_var(data, d_get_variable(data, data->nvariables - 1), &nil_value); if (data->parser != (struct _parser_ *) NULL) { /* * get rid of the parser, too */ ps_del(data->parser); data->parser = (struct _parser_ *) NULL; } } /* * NAME: data->assign_elt() * DESCRIPTION: assign a value to an array element */ void d_assign_elt(data, arr, elt, val) register dataspace *data; register array *arr; register value *elt, *val; { if (data->plane->level != arr->primary->data->plane->level) { /* * bring dataspace of imported array up to the current plane level */ d_new_plane(arr->primary->data, data->plane->level); } data = arr->primary->data; if (arr->primary->plane != data->plane) { /* * backup array's current elements */ arr_backup(&data->plane->achunk, arr); if (arr->primary->arr != (array *) NULL) { arr->primary->plane = data->plane; } else { arr->primary = &data->plane->alocal; } } if (arr->primary->arr != (array *) NULL) { /* * the array is in the loaded dataspace of some object */ if ((arr->primary->ref & ARR_MOD) == 0) { arr->primary->ref |= ARR_MOD; data->plane->flags |= MOD_ARRAY; } ref_rhs(data, val); del_lhs(data, elt); } else { if (T_INDEXED(val->type) && data != val->u.array->primary->data) { /* mark as imported */ if (data->plane->imports++ == 0 && ifirst != data && data->iprev == (dataspace *) NULL) { /* add to imports list */ data->iprev = (dataspace *) NULL; data->inext = ifirst; if (ifirst != (dataspace *) NULL) { ifirst->iprev = data; } ifirst = data; } } if (T_INDEXED(elt->type) && data != elt->u.array->primary->data) { /* mark as unimported */ data->plane->imports--; } } i_ref_value(val); i_del_value(elt); *elt = *val; elt->modified = TRUE; } /* * NAME: data->change_map() * DESCRIPTION: mark a mapping as changed in size */ void d_change_map(map) array *map; { register arrref *a; a = map->primary; if (a->state == AR_UNCHANGED) { a->plane->achange++; a->state = AR_CHANGED; } } /* * NAME: data->new_call_out() * DESCRIPTION: add a new callout */ uindex d_new_call_out(data, func, delay, mdelay, f, nargs) register dataspace *data; string *func; Int delay; unsigned int mdelay; register frame *f; int nargs; { Uint ct, t; unsigned short m; cbuf *q; value v[4]; uindex handle; ct = co_check(ncallout, delay, mdelay, &t, &m, &q); if (ct == 0 && q == (cbuf *) NULL) { /* callouts are disabled */ return 0; } PUT_STRVAL(&v[0], func); switch (nargs) { case 3: v[3] = f->sp[2]; case 2: v[2] = f->sp[1]; case 1: v[1] = f->sp[0]; case 0: break; default: v[1] = f->sp[0]; v[2] = f->sp[1]; PUT_ARRVAL(&v[3], arr_new(data, nargs - 2L)); memcpy(v[3].u.array->elts, f->sp + 2, (nargs - 2) * sizeof(value)); d_ref_imports(v[3].u.array); break; } f->sp += nargs; handle = d_alloc_call_out(data, 0, ct, nargs, v); if (data->plane->level == 0) { /* * add normal callout */ co_new(data->oindex, handle, t, m, q); } else { register dataplane *plane; register copatch **c, *cop; dcallout *co; copatch **cc; /* * add callout patch */ plane = data->plane; if (plane->coptab == (coptable *) NULL) { cop_init(plane); } co = &data->callouts[handle - 1]; cc = c = &plane->coptab->cop[handle % COPATCHHTABSZ]; for (;;) { cop = *c; if (cop == (copatch *) NULL || cop->plane != plane) { /* add new */ cop_new(plane, cc, COP_ADD, handle, co, t, m, q); break; } if (cop->handle == handle) { /* replace removed */ cop_replace(cop, co, t, m, q); break; } c = &cop->next; } ncallout++; } return handle; } /* * NAME: data->del_call_out() * DESCRIPTION: remove a callout */ Int d_del_call_out(data, handle) dataspace *data; Uint handle; { register dcallout *co; Int t; if (handle == 0 || handle > data->ncallouts) { /* no such callout */ return -1; } if (data->callouts == (dcallout *) NULL) { d_get_callouts(data); } co = &data->callouts[handle - 1]; if (co->val[0].type != T_STRING) { /* invalid callout */ return -1; } t = co_remaining(co->time); if (data->plane->level == 0) { /* * remove normal callout */ co_del(data->oindex, (uindex) handle, co->time); } else { register dataplane *plane; register copatch **c, *cop; copatch **cc; /* * add/remove callout patch */ --ncallout; plane = data->plane; if (plane->coptab == (coptable *) NULL) { cop_init(plane); } cc = c = &plane->coptab->cop[handle % COPATCHHTABSZ]; for (;;) { cop = *c; if (cop == (copatch *) NULL || cop->plane != plane) { /* delete new */ cop_new(plane, cc, COP_REMOVE, (uindex) handle, co, (Uint) 0, 0, (cbuf *) NULL); break; } if (cop->handle == handle) { /* delete existing */ if (cop->type == COP_REPLACE) { cop_release(cop); } else { cop_del(plane, c, TRUE); } break; } c = &cop->next; } } d_free_call_out(data, (uindex) handle); return t; } /* * NAME: data->get_call_out() * DESCRIPTION: get a callout */ string *d_get_call_out(data, handle, f, nargs) dataspace *data; unsigned int handle; register frame *f; int *nargs; { string *str; register dcallout *co; register value *v, *o; register uindex n; if (data->callouts == (dcallout *) NULL) { d_get_callouts(data); } co = &data->callouts[handle - 1]; v = co->val; del_lhs(data, &v[0]); str = v[0].u.string; i_grow_stack(f, (*nargs = co->nargs) + 1); *--f->sp = v[0]; switch (co->nargs) { case 3: del_lhs(data, &v[3]); *--f->sp = v[3]; case 2: del_lhs(data, &v[2]); *--f->sp = v[2]; case 1: del_lhs(data, &v[1]); *--f->sp = v[1]; case 0: break; default: n = co->nargs - 2; f->sp -= n; memcpy(f->sp, d_get_elts(v[3].u.array), n * sizeof(value)); del_lhs(data, &v[3]); FREE(v[3].u.array->elts); v[3].u.array->elts = (value *) NULL; arr_del(v[3].u.array); del_lhs(data, &v[2]); *--f->sp = v[2]; del_lhs(data, &v[1]); *--f->sp = v[1]; break; } /* wipe out destructed objects */ for (n = co->nargs, v = f->sp; n > 0; --n, v++) { switch (v->type) { case T_OBJECT: if (DESTRUCTED(v)) { *v = nil_value; } break; case T_LWOBJECT: o = d_get_elts(v->u.array); if (DESTRUCTED(o)) { arr_del(v->u.array); *v = nil_value; } break; } } co->val[0] = nil_value; n = data->fcallouts; if (n != 0) { data->callouts[n - 1].co_prev = handle; } co->co_next = n; data->fcallouts = handle; data->plane->flags |= MOD_CALLOUT; return str; } /* * NAME: data->list_callouts() * DESCRIPTION: list all call_outs in an object */ array *d_list_callouts(host, data) dataspace *host; register dataspace *data; { register uindex n, count, size; register dcallout *co; register value *v, *v2, *elts; array *list, *a; uindex max_args; if (data->ncallouts == 0) { return arr_new(host, 0L); } if (data->callouts == (dcallout *) NULL) { d_get_callouts(data); } /* get the number of callouts in this object */ count = data->ncallouts; for (n = data->fcallouts; n != 0; n = data->callouts[n - 1].co_next) { --count; } if (count > conf_array_size()) { return (array *) NULL; } list = arr_new(host, (long) count); elts = list->elts; max_args = conf_array_size() - 3; for (co = data->callouts; count > 0; co++) { if (co->val[0].type == T_STRING) { size = co->nargs; if (size > max_args) { /* unlikely, but possible */ size = max_args; } a = arr_new(host, size + 3L); v = a->elts; /* handle */ PUT_INTVAL(v, co - data->callouts + 1); v++; /* function */ PUT_STRVAL(v, co->val[0].u.string); v++; /* time */ PUT_INTVAL(v, co->time); v++; /* copy arguments */ switch (size) { case 3: *v++ = co->val[3]; case 2: *v++ = co->val[2]; case 1: *v++ = co->val[1]; case 0: break; default: n = size - 2; for (v2 = d_get_elts(co->val[3].u.array) + n; n > 0; --n) { *v++ = *--v2; } *v++ = co->val[2]; *v++ = co->val[1]; break; } while (size > 0) { i_ref_value(--v); --size; } d_ref_imports(a); /* put in list */ PUT_ARRVAL(elts, a); elts++; --count; } } co_list(list); return list; } /* * NAME: data->set_varmap() * DESCRIPTION: add a variable mapping to a control block */ void d_set_varmap(ctrl, nvar, vmap) register control *ctrl; unsigned int nvar; unsigned short *vmap; { ctrl->vmapsize = nvar; ctrl->vmap = vmap; /* varmap modified */ ctrl->flags |= CTRL_VARMAP; } /* * NAME: data->get_varmap() * DESCRIPTION: get the variable mapping for an object */ static unsigned short *d_get_varmap(obj, update, nvariables) object **obj; register Uint update; unsigned short *nvariables; { register object *tmpl; register unsigned short nvar, *vmap; tmpl = OBJ((*obj)->prev); if (O_UPGRADING(*obj)) { /* in the middle of an upgrade */ tmpl = OBJ(tmpl->prev); } vmap = o_control(tmpl)->vmap; nvar = tmpl->ctrl->vmapsize; if (tmpl->update != update) { register unsigned short *m1, *m2, n; m1 = vmap; vmap = ALLOC(unsigned short, n = nvar); do { tmpl = OBJ(tmpl->prev); m2 = o_control(tmpl)->vmap; while (n > 0) { *vmap++ = (NEW_VAR(*m1)) ? *m1++ : m2[*m1++]; --n; } n = nvar; vmap -= n; m1 = vmap; } while (tmpl->update != update); } *obj = tmpl; *nvariables = nvar; return vmap; } /* * NAME: data->upgrade_data() * DESCRIPTION: upgrade the dataspace for one object */ void d_upgrade_data(data, nvar, vmap, tmpl) register dataspace *data; register unsigned int nvar; register unsigned short *vmap; object *tmpl; { register value *v; register unsigned short n; value *vars; /* make sure variables are in memory */ vars = d_get_variable(data, 0); /* map variables */ for (n = nvar, v = ALLOC(value, n); n > 0; --n) { switch (*vmap) { case NEW_INT: *v++ = zero_int; break; case NEW_FLOAT: *v++ = zero_float; break; case NEW_POINTER: *v++ = nil_value; break; default: *v = vars[*vmap]; i_ref_value(v); v->modified = TRUE; ref_rhs(data, v++); break; } vmap++; } vars = v - nvar; /* deref old values */ v = data->variables; for (n = data->nvariables; n > 0; --n) { del_lhs(data, v); i_del_value(v++); } /* replace old with new */ FREE(data->variables); data->variables = vars; data->base.flags |= MOD_VARIABLE; if (data->nvariables != nvar) { if (data->svariables != (svalue *) NULL) { FREE(data->svariables); data->svariables = (svalue *) NULL; } data->nvariables = nvar; data->base.achange++; /* force rebuild on swapout */ } o_upgraded(tmpl, OBJ(data->oindex)); } /* * NAME: data->upgrade_clone() * DESCRIPTION: upgrade a clone object */ void d_upgrade_clone(data) dataspace *data; { object *obj; unsigned short *vmap, nvar; Uint update; /* * the program for the clone was upgraded since last swapin */ obj = OBJ(data->oindex); update = obj->update; obj = OBJ(obj->u_master); vmap = d_get_varmap(&obj, update, &nvar); d_upgrade_data(data, nvar, vmap, obj); if (vmap != obj->ctrl->vmap) { FREE(vmap); } } /* * NAME: data->upgrade_lwobj() * DESCRIPTION: upgrade a non-persistent object */ object *d_upgrade_lwobj(lwobj, obj) register array *lwobj; object *obj; { register arrref *a; register unsigned short n; register value *v; Uint update; unsigned short nvar, *vmap; value *vars; a = lwobj->primary; update = obj->update; vmap = d_get_varmap(&obj, (Uint) lwobj->elts[1].u.number, &nvar); --nvar; /* map variables */ v = ALLOC(value, nvar + 2); *v++ = lwobj->elts[0]; *v = lwobj->elts[1]; (v++)->u.objcnt = update; vars = lwobj->elts + 2; for (n = nvar; n > 0; --n) { switch (*vmap) { case NEW_INT: *v++ = zero_int; break; case NEW_FLOAT: *v++ = zero_float; break; case NEW_POINTER: *v++ = nil_value; break; default: *v = vars[*vmap]; if (a->arr != (array *) NULL) { ref_rhs(a->data, v); } i_ref_value(v); (v++)->modified = TRUE; break; } vmap++; } vars = v - (nvar + 2); vmap -= nvar; if (vmap != obj->ctrl->vmap) { FREE(vmap); } v = lwobj->elts + 2; if (a->arr != (array *) NULL) { /* swapped-in */ if (a->state == AR_UNCHANGED) { register dataplane *p; a->state = AR_CHANGED; for (p = a->data->plane; p != (dataplane *) NULL; p = p->prev) { p->achange++; } } /* deref old values */ for (n = lwobj->size - 2; n > 0; --n) { del_lhs(a->data, v); i_del_value(v++); } } else { /* deref old values */ for (n = lwobj->size - 2; n > 0; --n) { i_del_value(v++); } } /* replace old with new */ lwobj->size = nvar + 2; FREE(lwobj->elts); lwobj->elts = vars; return obj; } /* * NAME: data->import() * DESCRIPTION: copy imported arrays to current dataspace */ static void d_import(imp, data, val, n) register arrimport *imp; register dataspace *data; register value *val; register unsigned short n; { while (n > 0) { if (T_INDEXED(val->type)) { register array *a; register Uint i, j; a = val->u.array; if (a->primary->data != data) { /* * imported array */ i = arr_put(imp->merge, a, imp->narr); if (i == imp->narr) { /* * first time encountered */ if (a->hashed != (struct _maphash_ *) NULL) { map_rmhash(a); } if (a->ref == 2) { /* + 1 for array merge table */ /* * move array to new dataspace */ a->primary = &data->base.alocal; a->prev->next = a->next; a->next->prev = a->prev; } else { /* * make new array */ a = arr_alloc(a->size); a->tag = val->u.array->tag; a->odcount = val->u.array->odcount; a->primary = &data->base.alocal; if (a->size > 0) { /* * copy elements */ i_copy(a->elts = ALLOC(value, a->size), d_get_elts(val->u.array), a->size); } /* * replace */ arr_del(val->u.array); arr_ref(val->u.array = a); imp->narr++; } a->prev = &data->alist; a->next = data->alist.next; a->next->prev = a; data->alist.next = a; /* * store in itab */ if (i >= imp->itabsz) { /* * increase size of itab */ for (j = imp->itabsz; j <= i; j += j) ; imp->itab = REALLOC(imp->itab, array*, imp->itabsz, j); imp->itabsz = j; } arr_put(imp->merge, imp->itab[i] = a, imp->narr++); if (a->size > 0) { /* * import elements too */ d_import(imp, data, a->elts, a->size); } } else { /* * array was previously replaced */ arr_ref(a = imp->itab[i]); arr_del(val->u.array); val->u.array = a; } } else if (arr_put(imp->merge, a, imp->narr) == imp->narr) { /* * not previously encountered mapping or array */ imp->narr++; if (a->hashed != (struct _maphash_ *) NULL) { map_rmhash(a); d_import(imp, data, a->elts, a->size); } else if (a->elts != (value *) NULL) { d_import(imp, data, a->elts, a->size); } } } val++; --n; } } /* * NAME: data->export() * DESCRIPTION: handle exporting of arrays shared by more than one object */ void d_export() { register dataspace *data; register Uint n; arrimport imp; if (ifirst != (dataspace *) NULL) { imp.itab = ALLOC(array*, imp.itabsz = 64); for (data = ifirst; data != (dataspace *) NULL; data = data->inext) { if (data->base.imports != 0) { data->base.imports = 0; imp.merge = arr_merge(); imp.narr = 0; if (data->variables != (value *) NULL) { d_import(&imp, data, data->variables, data->nvariables); } if (data->base.arrays != (arrref *) NULL) { register arrref *a; for (n = data->narrays, a = data->base.arrays; n > 0; --n, a++) { if (a->arr != (array *) NULL) { if (a->arr->hashed != (struct _maphash_ *) NULL) { /* mapping */ map_rmhash(a->arr); d_import(&imp, data, a->arr->elts, a->arr->size); } else if (a->arr->elts != (value *) NULL) { d_import(&imp, data, a->arr->elts, a->arr->size); } } } } if (data->callouts != (dcallout *) NULL) { register dcallout *co; co = data->callouts; for (n = data->ncallouts; n > 0; --n) { if (co->val[0].type == T_STRING) { d_import(&imp, data, co->val, (co->nargs > 3) ? 4 : co->nargs + 1); } co++; } } arr_clear(imp.merge); /* clear merge table */ } data->iprev = (dataspace *) NULL; } ifirst = (dataspace *) NULL; FREE(imp.itab); } } /* * NAME: data->del_control() * DESCRIPTION: delete a control block from swap and memory */ void d_del_control(ctrl) register control *ctrl; { if (ctrl->sectors != (sector *) NULL) { sw_wipev(ctrl->sectors, ctrl->nsectors); sw_delv(ctrl->sectors, ctrl->nsectors); } d_free_control(ctrl); } /* * NAME: data->del_dataspace() * DESCRIPTION: delete a dataspace block from swap and memory */ void d_del_dataspace(data) register dataspace *data; { if (data->iprev != (dataspace *) NULL) { data->iprev->inext = data->inext; if (data->inext != (dataspace *) NULL) { data->inext->iprev = data->iprev; } } else if (ifirst == data) { ifirst = data->inext; if (ifirst != (dataspace *) NULL) { ifirst->iprev = (dataspace *) NULL; } } if (data->ncallouts != 0) { register Uint n; register dcallout *co; /* * remove callouts from callout table */ if (data->callouts == (dcallout *) NULL) { d_get_callouts(data); } for (n = data->ncallouts, co = data->callouts + n; n > 0; --n) { if ((--co)->val[0].type == T_STRING) { d_del_call_out(data, n); } } } if (data->sectors != (sector *) NULL) { sw_wipev(data->sectors, data->nsectors); sw_delv(data->sectors, data->nsectors); } d_free_dataspace(data); }