Blame int_array.c

Packit 575503
/*
Packit 575503
 * int_array.c - routines for arrays of integer indices.
Packit 575503
 */
Packit 575503
Packit 575503
/*
Packit 575503
 * Copyright (C) 1986, 1988, 1989, 1991-2013, 2016, 2017,
Packit 575503
 * the Free Software Foundation, Inc.
Packit 575503
 *
Packit 575503
 * This file is part of GAWK, the GNU implementation of the
Packit 575503
 * AWK Programming Language.
Packit 575503
 *
Packit 575503
 * GAWK is free software; you can redistribute it and/or modify
Packit 575503
 * it under the terms of the GNU General Public License as published by
Packit 575503
 * the Free Software Foundation; either version 3 of the License, or
Packit 575503
 * (at your option) any later version.
Packit 575503
 *
Packit 575503
 * GAWK is distributed in the hope that it will be useful,
Packit 575503
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 575503
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 575503
 * GNU General Public License for more details.
Packit 575503
 *
Packit 575503
 * You should have received a copy of the GNU General Public License
Packit 575503
 * along with this program; if not, write to the Free Software
Packit 575503
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
Packit 575503
 */
Packit 575503
Packit 575503
#include "awk.h"
Packit 575503
Packit 575503
extern FILE *output_fp;
Packit 575503
extern void indent(int indent_level);
Packit 575503
extern NODE **is_integer(NODE *symbol, NODE *subs);
Packit 575503
Packit 575503
static size_t INT_CHAIN_MAX = 2;
Packit 575503
Packit 575503
static NODE **int_array_init(NODE *symbol, NODE *subs);
Packit 575503
static NODE **int_lookup(NODE *symbol, NODE *subs);
Packit 575503
static NODE **int_exists(NODE *symbol, NODE *subs);
Packit 575503
static NODE **int_clear(NODE *symbol, NODE *subs);
Packit 575503
static NODE **int_remove(NODE *symbol, NODE *subs);
Packit 575503
static NODE **int_list(NODE *symbol, NODE *t);
Packit 575503
static NODE **int_copy(NODE *symbol, NODE *newsymb);
Packit 575503
static NODE **int_dump(NODE *symbol, NODE *ndump);
Packit 575503
Packit 575503
static uint32_t int_hash(uint32_t k, uint32_t hsize);
Packit 575503
static inline NODE **int_find(NODE *symbol, long k, uint32_t hash1);
Packit 575503
static NODE **int_insert(NODE *symbol, long k, uint32_t hash1);
Packit 575503
static void grow_int_table(NODE *symbol);
Packit 575503
Packit 575503
afunc_t int_array_func[] = {
Packit 575503
	int_array_init,
Packit 575503
	is_integer,
Packit 575503
	null_length,
Packit 575503
	int_lookup,
Packit 575503
	int_exists,
Packit 575503
	int_clear,
Packit 575503
	int_remove,
Packit 575503
	int_list,
Packit 575503
	int_copy,
Packit 575503
	int_dump,
Packit 575503
	(afunc_t) 0,
Packit 575503
};
Packit 575503
Packit 575503
Packit 575503
/* int_array_init --- array initialization routine */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_array_init(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
Packit 575503
{
Packit 575503
	if (symbol == NULL) {	/* first time */
Packit 575503
		long newval;
Packit 575503
Packit 575503
		/* check relevant environment variables */
Packit 575503
		if ((newval = getenv_long("INT_CHAIN_MAX")) > 0)
Packit 575503
			INT_CHAIN_MAX = newval;
Packit 575503
	} else
Packit 575503
		null_array(symbol);
Packit 575503
Packit 575503
	return & success_node;
Packit 575503
}
Packit 575503
Packit 575503
/*
Packit 575503
 * standard_integer_string -- check whether the string matches what
Packit 575503
 * sprintf("%ld", <value>) would produce. This is accomplished by accepting
Packit 575503
 * only strings that look like /^0$/ or /^-?[1-9][0-9]*$/. This should be
Packit 575503
 * faster than comparing vs. the results of actually calling sprintf.
Packit 575503
 */
Packit 575503
Packit 575503
static bool
Packit 575503
standard_integer_string(const char *s, size_t len)
Packit 575503
{
Packit 575503
	const char *end;
Packit 575503
Packit 575503
	if (len == 0)
Packit 575503
		return false;
Packit 575503
	if (*s == '0' && len == 1)
Packit 575503
		return true;
Packit 575503
	end = s + len;
Packit 575503
	/* ignore leading minus sign */
Packit 575503
	if (*s == '-' && ++s == end)
Packit 575503
		return false;
Packit 575503
	/* check first char is [1-9] */
Packit 575503
	if (*s < '1' || *s > '9')
Packit 575503
		return false;
Packit 575503
	while (++s < end) {
Packit 575503
		if (*s < '0' || *s > '9')
Packit 575503
			return false;
Packit 575503
	}
Packit 575503
	return true;
Packit 575503
}
Packit 575503
Packit 575503
/* is_integer --- check if subscript is an integer */
Packit 575503
Packit 575503
NODE **
Packit 575503
is_integer(NODE *symbol, NODE *subs)
Packit 575503
{
Packit 575503
#ifndef CHECK_INTEGER_USING_FORCE_NUMBER
Packit 575503
	long l;
Packit 575503
#endif
Packit 575503
	AWKNUM d;
Packit 575503
Packit 575503
	if ((subs->flags & NUMINT) != 0)
Packit 575503
		/* quick exit */
Packit 575503
		return & success_node;
Packit 575503
Packit 575503
	if (subs == Nnull_string || do_mpfr)
Packit 575503
		return NULL;
Packit 575503
Packit 575503
#ifdef CHECK_INTEGER_USING_FORCE_NUMBER
Packit 575503
	/*
Packit 575503
	 * This approach is much simpler, because we remove all of the strtol
Packit 575503
	 * logic below. But this may be slower in some usage cases.
Packit 575503
	 */
Packit 575503
	if ((subs->flags & NUMCUR) == 0) {
Packit 575503
		str2number(subs);
Packit 575503
Packit 575503
		/* check again in case force_number set NUMINT */
Packit 575503
		if ((subs->flags & NUMINT) != 0)
Packit 575503
			return & success_node;
Packit 575503
	}
Packit 575503
#else /* CHECK_INTEGER_USING_FORCE_NUMBER */
Packit 575503
	if ((subs->flags & NUMCUR) != 0) {
Packit 575503
#endif /* CHECK_INTEGER_USING_FORCE_NUMBER */
Packit 575503
		d = subs->numbr;
Packit 575503
		if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) {
Packit 575503
			/*
Packit 575503
			 * The numeric value is an integer, but we must
Packit 575503
			 * protect against strings that cannot be generated
Packit 575503
			 * from sprintf("%ld", <subscript>). This can happen
Packit 575503
			 * with strnum or string values. We could skip this
Packit 575503
			 * check for pure NUMBER values, but unfortunately the
Packit 575503
			 * code does not currently distinguish between NUMBER
Packit 575503
			 * and strnum values.
Packit 575503
			 */
Packit 575503
			if (   (subs->flags & STRCUR) == 0
Packit 575503
			    || standard_integer_string(subs->stptr, subs->stlen)) {
Packit 575503
				subs->flags |= NUMINT;
Packit 575503
				return & success_node;
Packit 575503
			}
Packit 575503
		}
Packit 575503
		return NULL;
Packit 575503
#ifndef CHECK_INTEGER_USING_FORCE_NUMBER
Packit 575503
	}
Packit 575503
Packit 575503
	/* a[3]=1; print "3" in a    -- true
Packit 575503
	 * a[3]=1; print "+3" in a   -- false
Packit 575503
	 * a[3]=1; print "03" in a   -- false
Packit 575503
	 * a[-3]=1; print "-3" in a  -- true
Packit 575503
	 */
Packit 575503
Packit 575503
	/* must be a STRING */
Packit 575503
	char *cp = subs->stptr, *cpend, *ptr;
Packit 575503
	char save;
Packit 575503
	size_t len = subs->stlen;
Packit 575503
Packit 575503
	if (len == 0 || (! isdigit((unsigned char) *cp) && *cp != '-'))
Packit 575503
		return NULL;
Packit 575503
Packit 575503
	if (len > 1 &&
Packit 575503
		((*cp == '0')		/* "00", "011" .. */
Packit 575503
			|| (*cp == '-' && *(cp + 1) == '0')	/* "-0", "-011" .. */
Packit 575503
		)
Packit 575503
	)
Packit 575503
		return NULL;
Packit 575503
	if (len == 1 && *cp != '-') {	/* single digit */
Packit 575503
		subs->numbr = (long) (*cp - '0');
Packit 575503
		if ((subs->flags & USER_INPUT) != 0) {
Packit 575503
			/* leave USER_INPUT set */
Packit 575503
			subs->flags &= ~STRING;
Packit 575503
			subs->flags |= NUMBER;
Packit 575503
		}
Packit 575503
		subs->flags |= (NUMCUR|NUMINT);
Packit 575503
		return & success_node;
Packit 575503
	}
Packit 575503
Packit 575503
	cpend = cp + len;
Packit 575503
	save = *cpend;
Packit 575503
	*cpend = '\0';
Packit 575503
Packit 575503
	errno = 0;
Packit 575503
	l = strtol(cp, & ptr, 10);
Packit 575503
	*cpend = save;
Packit 575503
	if (errno != 0 || ptr != cpend)
Packit 575503
		return NULL;
Packit 575503
Packit 575503
	subs->numbr = l;
Packit 575503
	if ((subs->flags & USER_INPUT) != 0) {
Packit 575503
		/* leave USER_INPUT set */
Packit 575503
		subs->flags &= ~STRING;
Packit 575503
		subs->flags |= NUMBER;
Packit 575503
	}
Packit 575503
	subs->flags |= NUMCUR;
Packit 575503
	if (l <= INT32_MAX && l >= INT32_MIN) {
Packit 575503
		subs->flags |= NUMINT;
Packit 575503
		return & success_node;
Packit 575503
	}
Packit 575503
Packit 575503
	return NULL;
Packit 575503
#endif /* CHECK_INTEGER_USING_FORCE_NUMBER */
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/*
Packit 575503
 * int_lookup --- Find SYMBOL[SUBS] in the assoc array.  Install it with value ""
Packit 575503
 * if it isn't there. Returns a pointer ala get_lhs to where its value is stored.
Packit 575503
 */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_lookup(NODE *symbol, NODE *subs)
Packit 575503
{
Packit 575503
	uint32_t hash1;
Packit 575503
	long k;
Packit 575503
	unsigned long size;
Packit 575503
	NODE **lhs;
Packit 575503
	NODE *xn;
Packit 575503
Packit 575503
	/*
Packit 575503
	 * N.B: symbol->table_size is the total # of non-integers (symbol->xarray)
Packit 575503
	 *	and integer elements. Also, symbol->xarray must have at least one
Packit 575503
	 *	item in it, and can not exist if there are no integer elements.
Packit 575503
	 * 	In that case, symbol->xarray is promoted to 'symbol' (See int_remove).
Packit 575503
	 */
Packit 575503
Packit 575503
Packit 575503
	if (! is_integer(symbol, subs)) {
Packit 575503
		xn = symbol->xarray;
Packit 575503
		if (xn == NULL) {
Packit 575503
			xn = symbol->xarray = make_array();
Packit 575503
			xn->vname = symbol->vname;	/* shallow copy */
Packit 575503
			xn->flags |= XARRAY;
Packit 575503
		} else if ((lhs = xn->aexists(xn, subs)) != NULL)
Packit 575503
			return lhs;
Packit 575503
		symbol->table_size++;
Packit 575503
		return assoc_lookup(xn, subs);
Packit 575503
	}
Packit 575503
Packit 575503
	k = subs->numbr;
Packit 575503
	if (symbol->buckets == NULL)
Packit 575503
		grow_int_table(symbol);
Packit 575503
Packit 575503
 	hash1 = int_hash(k, symbol->array_size);
Packit 575503
	if ((lhs = int_find(symbol, k, hash1)) != NULL)
Packit 575503
		return lhs;
Packit 575503
Packit 575503
	/* It's not there, install it */
Packit 575503
Packit 575503
	symbol->table_size++;
Packit 575503
Packit 575503
	/* first see if we would need to grow the array, before installing */
Packit 575503
	size = symbol->table_size;
Packit 575503
	if ((xn = symbol->xarray) != NULL)
Packit 575503
		size -= xn->table_size;
Packit 575503
Packit 575503
	if ((symbol->flags & ARRAYMAXED) == 0
Packit 575503
		    && (size / symbol->array_size) > INT_CHAIN_MAX) {
Packit 575503
		grow_int_table(symbol);
Packit 575503
		/* have to recompute hash value for new size */
Packit 575503
		hash1 = int_hash(k, symbol->array_size);
Packit 575503
	}
Packit 575503
Packit 575503
	return int_insert(symbol, k, hash1);
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/*
Packit 575503
 * int_exists --- test whether the array element symbol[subs] exists or not,
Packit 575503
 *	return pointer to value if it does.
Packit 575503
 */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_exists(NODE *symbol, NODE *subs)
Packit 575503
{
Packit 575503
	long k;
Packit 575503
	uint32_t hash1;
Packit 575503
Packit 575503
	if (! is_integer(symbol, subs)) {
Packit 575503
		NODE *xn = symbol->xarray;
Packit 575503
		if (xn == NULL)
Packit 575503
			return NULL;
Packit 575503
		return xn->aexists(xn, subs);
Packit 575503
	}
Packit 575503
	if (symbol->buckets == NULL)
Packit 575503
		return NULL;
Packit 575503
Packit 575503
	k = subs->numbr;
Packit 575503
	hash1 = int_hash(k, symbol->array_size);
Packit 575503
	return int_find(symbol, k, hash1);
Packit 575503
}
Packit 575503
Packit 575503
/* int_clear --- flush all the values in symbol[] */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
Packit 575503
{
Packit 575503
	unsigned long i;
Packit 575503
	int j;
Packit 575503
	BUCKET *b, *next;
Packit 575503
	NODE *r;
Packit 575503
Packit 575503
	if (symbol->xarray != NULL) {
Packit 575503
		NODE *xn = symbol->xarray;
Packit 575503
		assoc_clear(xn);
Packit 575503
		freenode(xn);
Packit 575503
		symbol->xarray = NULL;
Packit 575503
	}
Packit 575503
Packit 575503
	for (i = 0; i < symbol->array_size; i++) {
Packit 575503
		for (b = symbol->buckets[i]; b != NULL;	b = next) {
Packit 575503
			next = b->ainext;
Packit 575503
			for (j = 0; j < b->aicount; j++) {
Packit 575503
				r = b->aivalue[j];
Packit 575503
				if (r->type == Node_var_array) {
Packit 575503
					assoc_clear(r);	/* recursively clear all sub-arrays */
Packit 575503
					efree(r->vname);
Packit 575503
					freenode(r);
Packit 575503
				} else
Packit 575503
					unref(r);
Packit 575503
			}
Packit 575503
			freebucket(b);
Packit 575503
		}
Packit 575503
		symbol->buckets[i] = NULL;
Packit 575503
	}
Packit 575503
	if (symbol->buckets != NULL)
Packit 575503
		efree(symbol->buckets);
Packit 575503
	symbol->ainit(symbol, NULL);	/* re-initialize symbol */
Packit 575503
	return NULL;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_remove --- If SUBS is already in the table, remove it. */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_remove(NODE *symbol, NODE *subs)
Packit 575503
{
Packit 575503
	uint32_t hash1;
Packit 575503
	BUCKET *b, *prev = NULL;
Packit 575503
	long k;
Packit 575503
	int i;
Packit 575503
	NODE *xn = symbol->xarray;
Packit 575503
Packit 575503
	if (symbol->table_size == 0 || symbol->buckets == NULL)
Packit 575503
		return NULL;
Packit 575503
Packit 575503
	if (! is_integer(symbol, subs)) {
Packit 575503
		if (xn == NULL || xn->aremove(xn, subs) == NULL)
Packit 575503
			return NULL;
Packit 575503
		if (xn->table_size == 0) {
Packit 575503
			freenode(xn);
Packit 575503
			symbol->xarray = NULL;
Packit 575503
		}
Packit 575503
		symbol->table_size--;
Packit 575503
		assert(symbol->table_size > 0);
Packit 575503
		return & success_node;
Packit 575503
	}
Packit 575503
Packit 575503
	k = subs->numbr;
Packit 575503
	hash1 = int_hash(k, symbol->array_size);
Packit 575503
Packit 575503
	for (b = symbol->buckets[hash1]; b != NULL; prev = b, b = b->ainext) {
Packit 575503
		for (i = 0; i < b->aicount; i++) {
Packit 575503
			if (k != b->ainum[i])
Packit 575503
				continue;
Packit 575503
Packit 575503
			/* item found */
Packit 575503
			if (i == 0 && b->aicount == 2) {
Packit 575503
				/* removing the 1st item; move 2nd item from position 1 to 0 */
Packit 575503
Packit 575503
				b->ainum[0] = b->ainum[1];
Packit 575503
				b->aivalue[0] = b->aivalue[1];
Packit 575503
			} /* else
Packit 575503
				removing the only item or the 2nd item */
Packit 575503
Packit 575503
			goto removed;
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	if (b == NULL)	/* item not in array */
Packit 575503
		return NULL;
Packit 575503
Packit 575503
removed:
Packit 575503
	b->aicount--;
Packit 575503
Packit 575503
	if (b->aicount == 0) {
Packit 575503
		/* detach bucket */
Packit 575503
		if (prev != NULL)
Packit 575503
			prev->ainext = b->ainext;
Packit 575503
		else
Packit 575503
			symbol->buckets[hash1] = b->ainext;
Packit 575503
Packit 575503
		/* delete bucket */
Packit 575503
		freebucket(b);
Packit 575503
	} else if (b != symbol->buckets[hash1]) {
Packit 575503
		BUCKET *head = symbol->buckets[hash1];
Packit 575503
Packit 575503
		assert(b->aicount == 1);
Packit 575503
		/* move the last element from head to bucket to make it full. */
Packit 575503
		i = --head->aicount;	/* head has one less element */
Packit 575503
		b->ainum[1] = head->ainum[i];
Packit 575503
		b->aivalue[1] = head->aivalue[i];
Packit 575503
		b->aicount++;	/* bucket has one more element */
Packit 575503
		if (i == 0) {
Packit 575503
			/* head is now empty; delete head */
Packit 575503
			symbol->buckets[hash1] = head->ainext;
Packit 575503
			freebucket(head);
Packit 575503
		}
Packit 575503
	} /* else
Packit 575503
		do nothing */
Packit 575503
Packit 575503
	symbol->table_size--;
Packit 575503
	if (xn == NULL && symbol->table_size == 0) {
Packit 575503
		efree(symbol->buckets);
Packit 575503
		symbol->ainit(symbol, NULL);	/* re-initialize array 'symbol' */
Packit 575503
	} else if (xn != NULL && symbol->table_size == xn->table_size) {
Packit 575503
		/* promote xn (str_array) to symbol */
Packit 575503
		xn->flags &= ~XARRAY;
Packit 575503
		xn->parent_array = symbol->parent_array;
Packit 575503
		efree(symbol->buckets);
Packit 575503
		*symbol = *xn;
Packit 575503
		freenode(xn);
Packit 575503
	}
Packit 575503
Packit 575503
	return & success_node;	/* return success */
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_copy --- duplicate input array "symbol" */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_copy(NODE *symbol, NODE *newsymb)
Packit 575503
{
Packit 575503
	BUCKET **old, **new, **pnew;
Packit 575503
	BUCKET *chain, *newchain;
Packit 575503
	int j;
Packit 575503
	unsigned long i, cursize;
Packit 575503
Packit 575503
	assert(symbol->buckets != NULL);
Packit 575503
Packit 575503
	/* find the current hash size */
Packit 575503
	cursize = symbol->array_size;
Packit 575503
Packit 575503
	/* allocate new table */
Packit 575503
	ezalloc(new, BUCKET **, cursize * sizeof(BUCKET *), "int_copy");
Packit 575503
Packit 575503
	old = symbol->buckets;
Packit 575503
Packit 575503
	for (i = 0; i < cursize; i++) {
Packit 575503
		for (chain = old[i], pnew = & new[i]; chain != NULL;
Packit 575503
				chain = chain->ainext
Packit 575503
		) {
Packit 575503
			getbucket(newchain);
Packit 575503
			newchain->aicount = chain->aicount;
Packit 575503
			newchain->ainext = NULL;
Packit 575503
			for (j = 0; j < chain->aicount; j++) {
Packit 575503
				NODE *oldval;
Packit 575503
Packit 575503
				/*
Packit 575503
				 * copy the corresponding key and
Packit 575503
				 * value from the original input list
Packit 575503
				 */
Packit 575503
				newchain->ainum[j] = chain->ainum[j];
Packit 575503
Packit 575503
				oldval = chain->aivalue[j];
Packit 575503
				if (oldval->type == Node_val)
Packit 575503
					newchain->aivalue[j] = dupnode(oldval);
Packit 575503
				else {
Packit 575503
					NODE *r;
Packit 575503
					r = make_array();
Packit 575503
					r->vname = estrdup(oldval->vname, strlen(oldval->vname));
Packit 575503
					r->parent_array = newsymb;
Packit 575503
					newchain->aivalue[j] = assoc_copy(oldval, r);
Packit 575503
				}
Packit 575503
			}
Packit 575503
Packit 575503
			*pnew = newchain;
Packit 575503
			newchain->ainext = NULL;
Packit 575503
			pnew = & newchain->ainext;
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	if (symbol->xarray != NULL) {
Packit 575503
		NODE *xn, *n;
Packit 575503
		xn = symbol->xarray;
Packit 575503
		n = make_array();
Packit 575503
		n->vname = newsymb->vname;	/* shallow copy */
Packit 575503
		(void) xn->acopy(xn, n);
Packit 575503
		newsymb->xarray = n;
Packit 575503
	} else
Packit 575503
		newsymb->xarray = NULL;
Packit 575503
Packit 575503
	newsymb->table_size = symbol->table_size;
Packit 575503
	newsymb->buckets = new;
Packit 575503
	newsymb->array_size = cursize;
Packit 575503
	newsymb->flags = symbol->flags;
Packit 575503
Packit 575503
	return NULL;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_list --- return a list of array items */
Packit 575503
Packit 575503
static NODE**
Packit 575503
int_list(NODE *symbol, NODE *t)
Packit 575503
{
Packit 575503
	NODE **list = NULL;
Packit 575503
	unsigned long num_elems, list_size, i, k = 0;
Packit 575503
	BUCKET *b;
Packit 575503
	NODE *r, *subs, *xn;
Packit 575503
	int j, elem_size = 1;
Packit 575503
	long num;
Packit 575503
	static char buf[100];
Packit 575503
	assoc_kind_t assoc_kind;
Packit 575503
Packit 575503
	if (symbol->table_size == 0)
Packit 575503
		return NULL;
Packit 575503
Packit 575503
	assoc_kind = (assoc_kind_t) t->flags;
Packit 575503
	num_elems = symbol->table_size;
Packit 575503
	if ((assoc_kind & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE))
Packit 575503
		num_elems = 1;
Packit 575503
Packit 575503
	if ((assoc_kind & (AINDEX|AVALUE)) == (AINDEX|AVALUE))
Packit 575503
		elem_size = 2;
Packit 575503
	list_size = elem_size * num_elems;
Packit 575503
Packit 575503
	if (symbol->xarray != NULL) {
Packit 575503
		xn = symbol->xarray;
Packit 575503
		list = xn->alist(xn, t);
Packit 575503
		assert(list != NULL);
Packit 575503
		if (num_elems == 1 || num_elems == xn->table_size)
Packit 575503
			return list;
Packit 575503
		erealloc(list, NODE **, list_size * sizeof(NODE *), "int_list");
Packit 575503
		k = elem_size * xn->table_size;
Packit 575503
	} else
Packit 575503
		emalloc(list, NODE **, list_size * sizeof(NODE *), "int_list");
Packit 575503
Packit 575503
	/* populate it */
Packit 575503
Packit 575503
	for (i = 0; i < symbol->array_size; i++) {
Packit 575503
		for (b = symbol->buckets[i]; b != NULL;	b = b->ainext) {
Packit 575503
			for (j = 0; j < b->aicount; j++) {
Packit 575503
				/* index */
Packit 575503
				num = b->ainum[j];
Packit 575503
				if ((assoc_kind & AISTR) != 0) {
Packit 575503
					sprintf(buf, "%ld", num);
Packit 575503
					subs = make_string(buf, strlen(buf));
Packit 575503
					subs->numbr = num;
Packit 575503
					subs->flags |= (NUMCUR|NUMINT);
Packit 575503
				} else {
Packit 575503
					subs = make_number((AWKNUM) num);
Packit 575503
					subs->flags |= (INTIND|NUMINT);
Packit 575503
				}
Packit 575503
				list[k++] = subs;
Packit 575503
Packit 575503
				/* value */
Packit 575503
				if ((assoc_kind & AVALUE) != 0) {
Packit 575503
					r = b->aivalue[j];
Packit 575503
					if (r->type == Node_val) {
Packit 575503
						if ((assoc_kind & AVNUM) != 0)
Packit 575503
							(void) force_number(r);
Packit 575503
						else if ((assoc_kind & AVSTR) != 0)
Packit 575503
							r = force_string(r);
Packit 575503
					}
Packit 575503
					list[k++] = r;
Packit 575503
				}
Packit 575503
Packit 575503
				if (k >= list_size)
Packit 575503
					return list;
Packit 575503
			}
Packit 575503
		}
Packit 575503
	}
Packit 575503
	return list;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_kilobytes --- calculate memory consumption of the assoc array */
Packit 575503
Packit 575503
AWKNUM
Packit 575503
int_kilobytes(NODE *symbol)
Packit 575503
{
Packit 575503
	unsigned long i, bucket_cnt = 0;
Packit 575503
	BUCKET *b;
Packit 575503
	AWKNUM kb;
Packit 575503
	extern AWKNUM str_kilobytes(NODE *symbol);
Packit 575503
Packit 575503
	for (i = 0; i < symbol->array_size; i++) {
Packit 575503
		for (b = symbol->buckets[i]; b != NULL; b = b->ainext)
Packit 575503
			bucket_cnt++;
Packit 575503
	}
Packit 575503
	kb = (((AWKNUM) bucket_cnt) * sizeof (BUCKET) +
Packit 575503
			((AWKNUM) symbol->array_size) * sizeof (BUCKET *)) / 1024.0;
Packit 575503
Packit 575503
	if (symbol->xarray != NULL)
Packit 575503
		kb += str_kilobytes(symbol->xarray);
Packit 575503
Packit 575503
	return kb;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_dump --- dump array info */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_dump(NODE *symbol, NODE *ndump)
Packit 575503
{
Packit 575503
#define HCNT	31
Packit 575503
Packit 575503
	int indent_level;
Packit 575503
	BUCKET *b;
Packit 575503
	NODE *xn = NULL;
Packit 575503
	unsigned long str_size = 0, int_size = 0;
Packit 575503
	unsigned long i;
Packit 575503
	size_t j, bucket_cnt;
Packit 575503
	static size_t hash_dist[HCNT + 1];
Packit 575503
Packit 575503
	indent_level = ndump->alevel;
Packit 575503
Packit 575503
	if (symbol->xarray != NULL) {
Packit 575503
		xn = symbol->xarray;
Packit 575503
		str_size = xn->table_size;
Packit 575503
	}
Packit 575503
	int_size = symbol->table_size - str_size;
Packit 575503
Packit 575503
	if ((symbol->flags & XARRAY) == 0)
Packit 575503
		fprintf(output_fp, "%s `%s'\n",
Packit 575503
				(symbol->parent_array == NULL) ? "array" : "sub-array",
Packit 575503
				array_vname(symbol));
Packit 575503
Packit 575503
	indent_level++;
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "array_func: int_array_func\n");
Packit 575503
	if (symbol->flags != 0) {
Packit 575503
		indent(indent_level);
Packit 575503
		fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags));
Packit 575503
	}
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "INT_CHAIN_MAX: %lu\n", (unsigned long) INT_CHAIN_MAX);
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "array_size: %lu (int)\n", (unsigned long) symbol->array_size);
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "table_size: %lu (total), %lu (int), %lu (str)\n",
Packit 575503
			(unsigned long) symbol->table_size, int_size, str_size);
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "Avg # of items per chain (int): %.2g\n",
Packit 575503
			((AWKNUM) int_size) / symbol->array_size);
Packit 575503
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "memory: %.2g kB (total)\n", int_kilobytes(symbol));
Packit 575503
Packit 575503
	/* hash value distribution */
Packit 575503
Packit 575503
	memset(hash_dist, '\0', (HCNT + 1) * sizeof(size_t));
Packit 575503
	for (i = 0; i < symbol->array_size; i++) {
Packit 575503
		bucket_cnt = 0;
Packit 575503
		for (b = symbol->buckets[i]; b != NULL;	b = b->ainext)
Packit 575503
			bucket_cnt += b->aicount;
Packit 575503
		if (bucket_cnt >= HCNT)
Packit 575503
			bucket_cnt = HCNT;
Packit 575503
		hash_dist[bucket_cnt]++;
Packit 575503
	}
Packit 575503
Packit 575503
	indent(indent_level);
Packit 575503
	fprintf(output_fp, "Hash distribution:\n");
Packit 575503
	indent_level++;
Packit 575503
	for (j = 0; j <= HCNT; j++) {
Packit 575503
		if (hash_dist[j] > 0) {
Packit 575503
			indent(indent_level);
Packit 575503
			if (j == HCNT)
Packit 575503
				fprintf(output_fp, "[>=%lu]:%lu\n",
Packit 575503
					(unsigned long) HCNT, (unsigned long) hash_dist[j]);
Packit 575503
			else
Packit 575503
				fprintf(output_fp, "[%lu]:%lu\n",
Packit 575503
					(unsigned long) j, (unsigned long) hash_dist[j]);
Packit 575503
		}
Packit 575503
	}
Packit 575503
	indent_level--;
Packit 575503
Packit 575503
	/* dump elements */
Packit 575503
Packit 575503
	if (ndump->adepth >= 0) {
Packit 575503
		NODE *subs;
Packit 575503
		const char *aname;
Packit 575503
Packit 575503
		fprintf(output_fp, "\n");
Packit 575503
Packit 575503
		aname = make_aname(symbol);
Packit 575503
		subs = make_number((AWKNUM) 0);
Packit 575503
		subs->flags |= (INTIND|NUMINT);
Packit 575503
Packit 575503
		for (i = 0; i < symbol->array_size; i++) {
Packit 575503
			for (b = symbol->buckets[i]; b != NULL; b = b->ainext) {
Packit 575503
				for (j = 0; j < b->aicount; j++) {
Packit 575503
					subs->numbr = b->ainum[j];
Packit 575503
					assoc_info(subs, b->aivalue[j], ndump, aname);
Packit 575503
				}
Packit 575503
			}
Packit 575503
		}
Packit 575503
		unref(subs);
Packit 575503
	}
Packit 575503
Packit 575503
	if (xn != NULL)	{
Packit 575503
		fprintf(output_fp, "\n");
Packit 575503
		xn->adump(xn, ndump);
Packit 575503
	}
Packit 575503
Packit 575503
	return NULL;
Packit 575503
Packit 575503
#undef HCNT
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_hash --- calculate the hash function of the integer subs */
Packit 575503
Packit 575503
static uint32_t
Packit 575503
int_hash(uint32_t k, uint32_t hsize)
Packit 575503
{
Packit 575503
Packit 575503
/*
Packit 575503
 * Code snippet copied from:
Packit 575503
 *	Hash functions (http://www.azillionmonkeys.com/qed/hash.html).
Packit 575503
 *	Copyright 2004-2008 by Paul Hsieh. Licenced under LGPL 2.1.
Packit 575503
 */
Packit 575503
Packit 575503
	/* This is the final mixing function used by Paul Hsieh in SuperFastHash. */
Packit 575503
Packit 575503
	k ^= k << 3;
Packit 575503
	k += k >> 5;
Packit 575503
	k ^= k << 4;
Packit 575503
	k += k >> 17;
Packit 575503
	k ^= k << 25;
Packit 575503
	k += k >> 6;
Packit 575503
Packit 575503
	if (k >= hsize)
Packit 575503
		k %= hsize;
Packit 575503
	return k;
Packit 575503
}
Packit 575503
Packit 575503
/* int_find --- locate symbol[subs] */
Packit 575503
Packit 575503
static inline NODE **
Packit 575503
int_find(NODE *symbol, long k, uint32_t hash1)
Packit 575503
{
Packit 575503
	BUCKET *b;
Packit 575503
	int i;
Packit 575503
Packit 575503
	assert(symbol->buckets != NULL);
Packit 575503
	for (b = symbol->buckets[hash1]; b != NULL; b = b->ainext) {
Packit 575503
		for (i = 0; i < b->aicount; i++) {
Packit 575503
			if (b->ainum[i] == k)
Packit 575503
				return (b->aivalue + i);
Packit 575503
		}
Packit 575503
	}
Packit 575503
	return NULL;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* int_insert --- install subs in the assoc array */
Packit 575503
Packit 575503
static NODE **
Packit 575503
int_insert(NODE *symbol, long k, uint32_t hash1)
Packit 575503
{
Packit 575503
	BUCKET *b;
Packit 575503
	int i;
Packit 575503
Packit 575503
	b = symbol->buckets[hash1];
Packit 575503
Packit 575503
	/* Only the first bucket in the chain can be partially full, but is never empty. */
Packit 575503
Packit 575503
	if (b == NULL || (i = b->aicount) == 2) {
Packit 575503
		getbucket(b);
Packit 575503
		b->aicount = 0;
Packit 575503
		b->ainext = symbol->buckets[hash1];
Packit 575503
		symbol->buckets[hash1] = b;
Packit 575503
		i = 0;
Packit 575503
	}
Packit 575503
Packit 575503
	b->ainum[i] = k;
Packit 575503
	b->aivalue[i] = dupnode(Nnull_string);
Packit 575503
	b->aicount++;
Packit 575503
	return & b->aivalue[i];
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* grow_int_table --- grow the hash table */
Packit 575503
Packit 575503
static void
Packit 575503
grow_int_table(NODE *symbol)
Packit 575503
{
Packit 575503
	BUCKET **old, **new;
Packit 575503
	BUCKET *chain, *next;
Packit 575503
	int i, j;
Packit 575503
	unsigned long oldsize, newsize, k;
Packit 575503
Packit 575503
	/*
Packit 575503
	 * This is an array of primes. We grow the table by an order of
Packit 575503
	 * magnitude each time (not just doubling) so that growing is a
Packit 575503
	 * rare operation. We expect, on average, that it won't happen
Packit 575503
	 * more than twice.  The final size is also chosen to be small
Packit 575503
	 * enough so that MS-DOG mallocs can handle it. When things are
Packit 575503
	 * very large (> 8K), we just double more or less, instead of
Packit 575503
	 * just jumping from 8K to 64K.
Packit 575503
	 */
Packit 575503
Packit 575503
	static const unsigned long sizes[] = {
Packit 575503
		13, 127, 1021, 8191, 16381, 32749, 65497,
Packit 575503
		131101, 262147, 524309, 1048583, 2097169,
Packit 575503
		4194319, 8388617, 16777259, 33554467,
Packit 575503
		67108879, 134217757, 268435459, 536870923,
Packit 575503
		1073741827
Packit 575503
	};
Packit 575503
Packit 575503
	/* find next biggest hash size */
Packit 575503
	newsize = oldsize = symbol->array_size;
Packit 575503
Packit 575503
	for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) {
Packit 575503
		if (oldsize < sizes[i]) {
Packit 575503
			newsize = sizes[i];
Packit 575503
			break;
Packit 575503
		}
Packit 575503
	}
Packit 575503
	if (newsize == oldsize) {	/* table already at max (!) */
Packit 575503
		symbol->flags |= ARRAYMAXED;
Packit 575503
		return;
Packit 575503
	}
Packit 575503
Packit 575503
	/* allocate new table */
Packit 575503
	ezalloc(new, BUCKET **, newsize * sizeof(BUCKET *), "grow_int_table");
Packit 575503
Packit 575503
	old = symbol->buckets;
Packit 575503
	symbol->buckets = new;
Packit 575503
	symbol->array_size = newsize;
Packit 575503
Packit 575503
	/* brand new hash table */
Packit 575503
	if (old == NULL)
Packit 575503
		return;		/* DO NOT initialize symbol->table_size */
Packit 575503
Packit 575503
	/* old hash table there, move stuff to new, free old */
Packit 575503
	/* note that symbol->table_size does not change if an old array. */
Packit 575503
Packit 575503
	for (k = 0; k < oldsize; k++) {
Packit 575503
		long num;
Packit 575503
		for (chain = old[k]; chain != NULL; chain = next) {
Packit 575503
			for (i = 0; i < chain->aicount; i++) {
Packit 575503
				num = chain->ainum[i];
Packit 575503
				*int_insert(symbol, num, int_hash(num, newsize)) = chain->aivalue[i];
Packit 575503
			}
Packit 575503
			next = chain->ainext;
Packit 575503
			freebucket(chain);
Packit 575503
		}
Packit 575503
	}
Packit 575503
	efree(old);
Packit 575503
}