/* array.c -- runtime support of arrays */ #include "rts.h" #include static void slices (); /* * mpd_init_array (locn, addr, elemsize, initvalue, ndim, lb1, ub1, ...) * -- initialize an array. * * If addr is 0, first allocate it. If addr is -1, obtain from mpd_new(). * * If elemsize is zero or negative, it is the (negative) maxlength of * a string, for which an initializer will be created internally. * * If ndim is negative, it is followed by two *pointers* to lb/ub arrays, * instead of a list of lb and ub values. */ Array * mpd_init_array ( char *locn, Array *addr, int elemsize, Ptr initvalue, int ndim, ...) { va_list ap; int alcsize, i, n; Dim *d; Ptr p, q; int *lb, *ub, la[MAX_DIMENS], ua[MAX_DIMENS], size[MAX_DIMENS+1]; String s; /* for string initialization */ mpd_check_stk (CUR_STACK); /* * Calculate the size of each dimension. */ va_start (ap, ndim); if (ndim < 0) { ndim = -ndim; lb = va_arg (ap, int *); ub = va_arg (ap, int *); } else { lb = la; ub = ua; for (i = 0; i < ndim; i++) { lb[i] = va_arg (ap, int); ub[i] = va_arg (ap, int); if (ub[i] - lb[i] + 1 < 0) mpd_runerr (locn, E_ABND, lb[i], ub[i]); } } va_end (ap); DEBUG5 (D_ARRAY, "mpd_init_array (%06lX, %ld, %06lX, %ld, ?, %ld, ...)", addr, elemsize, initvalue, ndim, ub[0]); /* * Create a string initializer, if needed. */ if (elemsize <= 0) { s.size = -elemsize + STRING_OVH; s.length = -1; elemsize = MPDALIGN (s.size); initvalue = (Ptr) &s; } /* * Calculate the total size, and allocate. */ size[ndim] = elemsize; for (i = ndim - 1; i >= 0; i--) size[i] = (ub[i] - lb[i] + 1) * size[i+1]; alcsize = MPDALIGN (size[0] + sizeof (Array) + sizeof (Dim) * ndim); if (addr == NULL) addr = (Array *) mpd_alc (alcsize, 1); else if (addr == (Array *) -1) addr = (Array *) mpd_new (locn, alcsize); /* * Initialize array headers. */ addr->offset = sizeof (Array) + ndim * sizeof (Dim); addr->size = addr->offset + size[0]; addr->ndim = ndim; /* * Initialize dimension headers (in reverse order) */ for (i = ndim - 1, d = (Dim *)(addr + 1); i >= 0; i--, d++) { d->lb = lb[i]; d->ub = ub[i]; d->stride = size[i+1]; } /* * Initialize data */ if (initvalue != NULL && size[0] > 0) { p = ADATA (addr); /* destination */ q = p + size[0]; /* end */ memcpy (p, initvalue, elemsize); /* init first element */ initvalue = p; /* now copy from front of array */ p += elemsize; /* bump store pointer */ n = elemsize; /* bytes available to copy */ while (p + n < q) { memcpy (p, initvalue, n); p += n; n *= 2; } memcpy (p, initvalue, q - p); } return addr; } /* * Return the number of elements in an array. */ int mpd_acount (a) Array *a; { Dim *d = (Dim *) (a + 1); int i = a->ndim; int n = 1; while (i-- > 0) { n *= d->ub - d->lb + 1; d++; } return n; } /* * Copy one array to another, returning the pointer to the destination array. * Data is copied but the destination header is left alone. */ Array * mpd_acopy (locn, dest, src) char *locn; Array *dest, *src; { Dim *ld, *rd; int i, ndim; ndim = dest->ndim; ld = (Dim *) (dest + 1) + ndim - 1; rd = (Dim *) (src + 1) + ndim - 1; for (i = 0; i < ndim; i++, ld--, rd--) if (ld->ub - ld->lb != rd->ub - rd->lb) mpd_runerr (locn, E_ASIZ, ld, rd); memcpy ((Ptr) dest + dest->offset, (Ptr) src + src->offset, dest->size - dest->offset); return dest; } /* * Swap two arrays and return the address of the left side (value of R side). */ Array * mpd_aswap (locn, lside, rside) char *locn; Array *lside, *rside; { Dim *ld, *rd; Ptr l, r; char c; int i, n, ndim; ndim = lside->ndim; ld = (Dim *) (lside + 1) + ndim - 1; rd = (Dim *) (rside + 1) + ndim - 1; for (i = 0; i < ndim; i++, ld--, rd--) if (ld->ub - ld->lb != rd->ub - rd->lb) mpd_runerr (locn, E_ASIZ, ld, rd); l = ADATA (lside); /* addrs of l and r data */ r = ADATA (rside); n = lside->size - lside->offset; /* number of bytes to swap */ while (--n >= 0) { /* swap contents */ c = *l; *l++ = *r; *r++ = c; } return lside; /* return left side address */ } /* * Implement a clone operation by making duplicates beyond * the first and incrementing the store address. */ Ptr mpd_clone (locn, addr, len, n) char *locn; Ptr addr; int len, n; { Ptr new = addr; if (n < 0) mpd_runerr (locn, E_AREP, n); if (n == 0) return new; while (--n > 0) memcpy (new += len, addr, len); return new + len; } /* * mpd_slice (locn, a1, a2, elemsize, nbounds, lb1, ub1, ...) * * Extract or store into an array slice, handling multidimensional slicing. * If a1 is zero, allocate and return a slice of a2. * If a1 is nonzero, scatter a1 into a slice of a2. * * a1 is always the contiguous array; a2 is always the sliced array. * nbounds is the number of (lb,ub) index pairs that follow. * A ub value of SINGLEINDEX indicates a singly-indexed dimension of a2 only. * * Build an array of "slcinfo" structs and then call slices() to do the * actual work; it recurses to handle multiple dimensions. */ struct slcinfo { int n; /* number of elements in this dimension */ int d1; /* address incr in contigouous array (0 in last dim) */ int d2; /* address incr in array being sliced */ }; Ptr mpd_slice (char *locn, Array *a1, Array *a2, int elemsize, int nbounds, ...) { va_list ap; int adim, lb[MAX_DIMENS], ub[MAX_DIMENS]; /* array dims*/ int nelem, telem, offset, scatter; int i, j, k, l, u, z, o; struct slcinfo s[MAX_DIMENS+1]; mpd_check_stk (CUR_STACK); /* get fixed arguments */ va_start (ap, nbounds); if (elemsize == 0) elemsize = STRIDE (a2, 0); /* get index arguments */ telem = 1; /* total element count */ adim = 0; /* dimensionality of a1 */ offset = 0; /* offset into a2 */ for (i = 0; i < nbounds; i++) { /* for each dimension of a2 */ j = a2->ndim - i - 1; l = va_arg (ap, int); /* get index bounds */ u = va_arg (ap, int); if (l == ASTERISK) /* translate '*' into actual value */ l = LB (a2, j); if (u == ASTERISK) u = UB (a2, j); o = l - LB (a2, j); /* get a zero-based index */ z = STRIDE (a2, j); /* element size of this dimension */ offset += z * o; /* adjust offset to first element */ if (u == SINGLEINDEX) { if (o < 0 || l > UB (a2, j)) mpd_runerr (locn, E_ASUB, l, & ADIM (a2, j)); } else { /* this dimension is truly sliced */ nelem = u - l + 1; /* number of slice elements */ telem *= nelem; /* calc total elements */ if (o < 0 || nelem < 0 || u > UB (a2, j)) mpd_runerr (locn, E_ASLC, l, u, & ADIM (a2, j)); if (a1 != NULL) { k = a1->ndim - adim - 1; if (UB (a1, k) - LB (a1, k) + 1 != nelem) mpd_runerr (locn, E_ACHG, l, u, & ADIM (a1, k)); } lb[adim] = 1; /* save bounds for a1 */ ub[adim] = u - l + 1; s[adim].n = nelem; s[adim].d2 = z; /* size of element in a2 */ adim++; /* count the dimensions */ } } va_end (ap); /* if loading into new array, need to allocate it */ scatter = (a1 != NULL); if (!scatter) a1 = mpd_init_array (locn, (Array*)0, elemsize, (Ptr)0, -adim, lb, ub); for (i = 0; i < adim; i++) /* calculate step sizes for a1 */ s[i].d1 = STRIDE (a1, adim - i - 1); if (telem > 0) /* if no zero dimensions, copy elems */ slices (adim, ADATA (a1), ADATA (a2) + offset, s, scatter); return (Ptr) a1; /* always return contiguous array */ } /* * slices (lv, p1, p2, s, scatter) -- copy slice data * * lv is the number of nesting levels * p1, p2 are data pointers * s is array of slice info (see above) * scatter indicates direction of copy (nonzero for p1->p2) * * Copy s->n slice elements, recursing as needed for deeper levels. */ static void slices (lv, p1, p2, s, scatter) int lv; Ptr p1, p2; struct slcinfo *s; int scatter; { int n, d1, d2; n = s->n; d1 = s->d1; d2 = s->d2; while (--n >= 0) { if (lv > 1) slices (lv - 1, p1, p2, s + 1, scatter); else { if (scatter) memcpy (p2, p1, d1); else memcpy (p1, p2, d1); } p1 += d1; p2 += d2; } } /* * Copy an array of strings, elementwise if necessary, into a slice * (or the whole thing) of another string array. The maxlengths of * the strings in the two arrays may differ. */ Array * mpd_strarr (locn, dest, lb, ub, src) char *locn; Array *dest; int lb, ub; Array *src; { int ndst, nsrc, i, nelem, maxl; String *pdst, *psrc; if (lb == ASTERISK) lb = LB (dest, 0); if (ub == ASTERISK) ub = UB (dest, 0); nelem = ub - lb + 1; if (nelem < 0 || lb < LB (dest, 0) || ub > UB (dest, 0)) mpd_runerr (locn, E_ASLC, lb, ub, dest); if (UB (src, 0) - LB (src, 0) + 1 != nelem) mpd_runerr (locn, E_ACHG, lb, ub, src); if (nelem == 0) return dest; /* empty arrays */ psrc = (String *) ADATA (src); /* pointer to source element */ pdst = (String *) ADATA (dest); /* pointer to dest element */ nsrc = MPDALIGN (psrc->size); /* distance between source elements */ ndst = MPDALIGN (pdst->size); /* distance between dest elements */ pdst = (String *) ((Ptr) pdst + (lb - LB (dest, 0)) * ndst); /* pointer to first destination strg */ if (psrc->size == pdst->size) { /* if maxlengths are the same */ /* block copy */ memcpy ((Ptr) pdst, (Ptr) psrc, nelem * ndst); } else { /* elementwise assignment */ maxl = MAXLENGTH (pdst); /* maxlength of dest element */ for (i = 0; i < nelem; i++) { if (psrc->length > maxl) mpd_runerr (locn, E_SELM, LB (src, 0) + i, psrc->length, maxl); memcpy (DATA (pdst), DATA (psrc), (pdst->length = psrc->length)); pdst = (String *) ((Ptr) pdst + ndst); psrc = (String *) ((Ptr) psrc + nsrc); } } return dest; } /* * Allocate and initialize a string as a copy of a char array. */ Ptr mpd_astring (a) Array *a; { String *s; int n; n = UB (a, 0) - LB (a, 0) + 1; s = (String *) mpd_alc (n + STRING_OVH, 1); s->size = n + STRING_OVH; s->length = n; memcpy (DATA (s), ADATA (a), n); return (Ptr) s; } /* * Allocate and extract a string slice. */ Ptr mpd_sslice (locn, s, index1, index2) char *locn; String *s; int index1, index2; { int nchars, nbytes; String *r; if (index1 == ASTERISK) index1 = 1; if (index2 == ASTERISK) index2 = s->length; nchars = index2 - index1 + 1; if (nchars < 0 || index1 < 1 || index2 > s->length) mpd_runerr (locn, E_SSLC, index1, index2, s, s); nbytes = nchars + STRING_OVH; r = (String *) mpd_alc (nbytes, 1); r->size = nbytes; r->length = nchars; memcpy (DATA (r), DATA (s) + index1 - 1, nchars); return (Ptr) r; } /* * Change a string slice. */ String * mpd_chgstr (locn, s, index1, index2, v) char *locn; String *s; int index1, index2; String *v; { int nchars; if (index1 == ASTERISK) index1 = 1; if (index2 == ASTERISK) index2 = s->length; nchars = index2 - index1 + 1; if (nchars < 0 || index1 < 1 || index2 > s->length) mpd_runerr (locn, E_SSLC, index1, index2, s, s); if (nchars != v->length) mpd_runerr (locn, E_SCHG, v, index1, index2); memcpy (DATA (s) + index1 - 1, DATA (v), nchars); return v; }