/* $RCSfile: array.c,v $$Revision: 4.0.1.3 $$Date: 92/06/08 11:45:05 $
 *
 *    Copyright (c) 1991, Larry Wall
 *
 *    You may distribute under the terms of either the GNU General Public
 *    License or the Artistic License, as specified in the README file.
 *
 * $Log:	array.c,v $
 * Revision 4.0.1.3  92/06/08  11:45:05  lwall
 * patch20: Perl now distinguishes overlapped copies from non-overlapped
 * 
 * Revision 4.0.1.2  91/11/05  16:00:14  lwall
 * patch11: random cleanup
 * patch11: passing non-existend array elements to subrouting caused core dump
 * 
 * Revision 4.0.1.1  91/06/07  10:19:08  lwall
 * patch4: new copyright notice
 * 
 * Revision 4.0  91/03/20  01:03:32  lwall
 * 4.0 baseline.
 * 
 */

#include "EXTERN.h"
#include "perl.h"

STR *
afetch(ar,key,lval)
register ARRAY *ar;
int key;
int lval;
{
    STR *str;

    if (key < 0 || key > ar->ary_fill) {
	if (lval && key >= 0) {
	    if (ar->ary_flags & ARF_REAL)
		str = Str_new(5,0);
	    else
		str = str_mortal(&str_undef);
	    (void)astore(ar,key,str);
	    return str;
	}
	else
	    return &str_undef;
    }
    if (!ar->ary_array[key]) {
	if (lval) {
	    str = Str_new(6,0);
	    (void)astore(ar,key,str);
	    return str;
	}
	return &str_undef;
    }
    return ar->ary_array[key];
}

bool
astore(ar,key,val)
register ARRAY *ar;
int key;
STR *val;
{
    int retval;

    if (key < 0)
	return FALSE;
    if (key > ar->ary_max) {
	int newmax;

	if (ar->ary_alloc != ar->ary_array) {
	    retval = ar->ary_array - ar->ary_alloc;
	    Move(ar->ary_array, ar->ary_alloc, ar->ary_max+1, STR*);
	    Zero(ar->ary_alloc+ar->ary_max+1, retval, STR*);
	    ar->ary_max += retval;
	    ar->ary_array -= retval;
	    if (key > ar->ary_max - 10) {
		newmax = key + ar->ary_max;
		goto resize;
	    }
	}
	else {
	    if (ar->ary_alloc) {
		newmax = key + ar->ary_max / 5;
	      resize:
		Renew(ar->ary_alloc,newmax+1, STR*);
		Zero(&ar->ary_alloc[ar->ary_max+1], newmax - ar->ary_max, STR*);
	    }
	    else {
		newmax = key < 4 ? 4 : key;
		Newz(2,ar->ary_alloc, newmax+1, STR*);
	    }
	    ar->ary_array = ar->ary_alloc;
	    ar->ary_max = newmax;
	}
    }
    if (ar->ary_flags & ARF_REAL) {
	if (ar->ary_fill < key) {
	    while (++ar->ary_fill < key) {
		if (ar->ary_array[ar->ary_fill] != Nullstr) {
		    str_free(ar->ary_array[ar->ary_fill]);
		    ar->ary_array[ar->ary_fill] = Nullstr;
		}
	    }
	}
	retval = (ar->ary_array[key] != Nullstr);
	if (retval)
	    str_free(ar->ary_array[key]);
    }
    else
	retval = 0;
    ar->ary_array[key] = val;
    return retval;
}

ARRAY *
anew(stab)
STAB *stab;
{
    register ARRAY *ar;

    New(1,ar,1,ARRAY);
    ar->ary_magic = Str_new(7,0);
    ar->ary_alloc = ar->ary_array = 0;
    str_magic(ar->ary_magic, stab, '#', Nullch, 0);
    ar->ary_max = ar->ary_fill = -1;
    ar->ary_flags = ARF_REAL;
    return ar;
}

ARRAY *
afake(stab,size,strp)
STAB *stab;
register int size;
register STR **strp;
{
    register ARRAY *ar;

    New(3,ar,1,ARRAY);
    New(4,ar->ary_alloc,size+1,STR*);
    Copy(strp,ar->ary_alloc,size,STR*);
    ar->ary_array = ar->ary_alloc;
    ar->ary_magic = Str_new(8,0);
    str_magic(ar->ary_magic, stab, '#', Nullch, 0);
    ar->ary_fill = size - 1;
    ar->ary_max = size - 1;
    ar->ary_flags = 0;
    while (size--) {
	if (*strp)
	    (*strp)->str_pok &= ~SP_TEMP;
	strp++;
    }
    return ar;
}

void
aclear(ar)
register ARRAY *ar;
{
    register int key;

    if (!ar || !(ar->ary_flags & ARF_REAL) || ar->ary_max < 0)
	return;
    /*SUPPRESS 560*/
    if (key = ar->ary_array - ar->ary_alloc) {
	ar->ary_max += key;
	ar->ary_array -= key;
    }
    for (key = 0; key <= ar->ary_max; key++)
	str_free(ar->ary_array[key]);
    ar->ary_fill = -1;
    Zero(ar->ary_array, ar->ary_max+1, STR*);
}

void
afree(ar)
register ARRAY *ar;
{
    register int key;

    if (!ar)
	return;
    /*SUPPRESS 560*/
    if (key = ar->ary_array - ar->ary_alloc) {
	ar->ary_max += key;
	ar->ary_array -= key;
    }
    if (ar->ary_flags & ARF_REAL) {
	for (key = 0; key <= ar->ary_max; key++)
	    str_free(ar->ary_array[key]);
    }
    str_free(ar->ary_magic);
    Safefree(ar->ary_alloc);
    Safefree(ar);
}

bool
apush(ar,val)
register ARRAY *ar;
STR *val;
{
    return astore(ar,++(ar->ary_fill),val);
}

STR *
apop(ar)
register ARRAY *ar;
{
    STR *retval;

    if (ar->ary_fill < 0)
	return Nullstr;
    retval = ar->ary_array[ar->ary_fill];
    ar->ary_array[ar->ary_fill--] = Nullstr;
    return retval;
}

void
aunshift(ar,num)
register ARRAY *ar;
register int num;
{
    register int i;
    register STR **sstr,**dstr;

    if (num <= 0)
	return;
    if (ar->ary_array - ar->ary_alloc >= num) {
	ar->ary_max += num;
	ar->ary_fill += num;
	while (num--)
	    *--ar->ary_array = Nullstr;
    }
    else {
	(void)astore(ar,ar->ary_fill+num,(STR*)0);	/* maybe extend array */
	dstr = ar->ary_array + ar->ary_fill;
	sstr = dstr - num;
#ifdef BUGGY_MSC5
 # pragma loop_opt(off)	/* don't loop-optimize the following code */
#endif /* BUGGY_MSC5 */
	for (i = ar->ary_fill - num; i >= 0; i--) {
	    *dstr-- = *sstr--;
#ifdef BUGGY_MSC5
 # pragma loop_opt()	/* loop-optimization back to command-line setting */
#endif /* BUGGY_MSC5 */
	}
	Zero(ar->ary_array, num, STR*);
    }
}

STR *
ashift(ar)
register ARRAY *ar;
{
    STR *retval;

    if (ar->ary_fill < 0)
	return Nullstr;
    retval = *ar->ary_array;
    *(ar->ary_array++) = Nullstr;
    ar->ary_max--;
    ar->ary_fill--;
    return retval;
}

int
alen(ar)
register ARRAY *ar;
{
    return ar->ary_fill;
}

void
afill(ar, fill)
register ARRAY *ar;
int fill;
{
    if (fill < 0)
	fill = -1;
    if (fill <= ar->ary_max)
	ar->ary_fill = fill;
    else
	(void)astore(ar,fill,Nullstr);
}
