~vcs-imports/gawk/master

281.1.1 by john haque
Speed/memory performance improvements.
1
/*
2
 * cint_array.c - routines for arrays of (mostly) consecutive positive integer indices.
3
 */
4
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
5
/*
1261 by Arnold D. Robbins
Update copyright years on changed files.
6
 * Copyright (C) 1986, 1988, 1989, 1991-2013, 2016, 2017, 2019, 2020,
408.19.184 by Arnold D. Robbins
Changes toward release and test tarball.
7
 * the Free Software Foundation, Inc.
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
8
 *
281.1.1 by john haque
Speed/memory performance improvements.
9
 * This file is part of GAWK, the GNU implementation of the
10
 * AWK Programming Language.
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
11
 *
281.1.1 by john haque
Speed/memory performance improvements.
12
 * GAWK is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 3 of the License, or
15
 * (at your option) any later version.
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
16
 *
281.1.1 by john haque
Speed/memory performance improvements.
17
 * GAWK is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
21
 *
281.1.1 by john haque
Speed/memory performance improvements.
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
25
 */
26
27
#include "awk.h"
28
319.1.116 by Arnold D. Robbins
Clean up of awk.h.
29
#define INT32_BIT 32
30
281.1.1 by john haque
Speed/memory performance improvements.
31
extern FILE *output_fp;
32
extern void indent(int indent_level);
33
extern NODE **is_integer(NODE *symbol, NODE *subs);
34
35
/*
36
 * NHAT         ---  maximum size of a leaf array (2^NHAT).
37
 * THRESHOLD    ---  Maximum capacity waste; THRESHOLD >= 2^(NHAT + 1).
38
 */
39
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
40
static int NHAT = 10;
281.1.1 by john haque
Speed/memory performance improvements.
41
static long THRESHOLD;
42
321 by john haque
Polish array handling code.
43
/*
44
 * What is the optimium NHAT ? timing results suggest that 10 is a good choice,
281.1.1 by john haque
Speed/memory performance improvements.
45
 * although differences aren't that significant for > 10.
46
 */
47
48
49
static NODE **cint_array_init(NODE *symbol, NODE *subs);
50
static NODE **is_uinteger(NODE *symbol, NODE *subs);
51
static NODE **cint_lookup(NODE *symbol, NODE *subs);
52
static NODE **cint_exists(NODE *symbol, NODE *subs);
53
static NODE **cint_clear(NODE *symbol, NODE *subs);
54
static NODE **cint_remove(NODE *symbol, NODE *subs);
55
static NODE **cint_list(NODE *symbol, NODE *t);
56
static NODE **cint_copy(NODE *symbol, NODE *newsymb);
57
static NODE **cint_dump(NODE *symbol, NODE *ndump);
58
#ifdef ARRAYDEBUG
59
static void cint_print(NODE *symbol);
60
#endif
61
1051 by Andrew J. Schorr
Use a struct instead of an array to contain the array methods.
62
const array_funcs_t cint_array_func = {
63
	"cint",
281.1.1 by john haque
Speed/memory performance improvements.
64
	cint_array_init,
65
	is_uinteger,
66
	cint_lookup,
67
	cint_exists,
68
	cint_clear,
69
	cint_remove,
70
	cint_list,
71
	cint_copy,
72
	cint_dump,
322 by john haque
Improve array interface.
73
	(afunc_t) 0,
281.1.1 by john haque
Speed/memory performance improvements.
74
};
75
1146.1.22 by Arnold D. Robbins
Improve sandbox mode.
76
77
static NODE **argv_store(NODE *symbol, NODE *subs);
78
79
/* special case for ARGV in sandbox mode */
80
const array_funcs_t argv_array_func = {
81
	"argv",
82
	cint_array_init,
83
	is_uinteger,
84
	cint_lookup,
85
	cint_exists,
86
	cint_clear,
87
	cint_remove,
88
	cint_list,
89
	cint_copy,
90
	cint_dump,
91
	argv_store,
92
};
93
281.1.1 by john haque
Speed/memory performance improvements.
94
static inline int cint_hash(long k);
95
static inline NODE **cint_find(NODE *symbol, long k, int h1);
96
97
static inline NODE *make_node(NODETYPE type);
98
99
static NODE **tree_lookup(NODE *symbol, NODE *tree, long k, int m, long base);
100
static NODE **tree_exists(NODE *tree, long k);
101
static void tree_clear(NODE *tree);
102
static int tree_remove(NODE *symbol, NODE *tree, long k);
103
static void tree_copy(NODE *newsymb, NODE *tree, NODE *newtree);
322 by john haque
Improve array interface.
104
static long tree_list(NODE *tree, NODE **list, assoc_kind_t assoc_kind);
281.1.1 by john haque
Speed/memory performance improvements.
105
static inline NODE **tree_find(NODE *tree, long k, int i);
106
static void tree_info(NODE *tree, NODE *ndump, const char *aname);
107
static size_t tree_kilobytes(NODE *tree);
108
#ifdef ARRAYDEBUG
109
static void tree_print(NODE *tree, size_t bi, int indent_level);
110
#endif
111
112
static inline NODE **leaf_lookup(NODE *symbol, NODE *array, long k, long size, long base);
113
static inline NODE **leaf_exists(NODE *array, long k);
114
static void leaf_clear(NODE *array);
115
static int leaf_remove(NODE *symbol, NODE *array, long k);
116
static void leaf_copy(NODE *newsymb, NODE *array, NODE *newarray);
322 by john haque
Improve array interface.
117
static long leaf_list(NODE *array, NODE **list, assoc_kind_t assoc_kind);
281.1.1 by john haque
Speed/memory performance improvements.
118
static void leaf_info(NODE *array, NODE *ndump, const char *aname);
119
#ifdef ARRAYDEBUG
120
static void leaf_print(NODE *array, size_t bi, int indent_level);
121
#endif
122
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
123
/* powers of 2 table upto 2^30 */
281.1.1 by john haque
Speed/memory performance improvements.
124
static const long power_two_table[] = {
125
	1, 2, 4, 8, 16, 32, 64,
126
	128, 256, 512, 1024, 2048, 4096,
127
	8192, 16384, 32768, 65536, 131072, 262144,
128
	524288, 1048576, 2097152, 4194304, 8388608, 16777216,
129
	33554432, 67108864, 134217728, 268435456, 536870912, 1073741824
130
};
131
132
133
#define ISUINT(a, s)	((((s)->flags & NUMINT) != 0 || is_integer(a, s) != NULL) \
134
                                    && (s)->numbr >= 0)
135
136
/*
137
 * To store 2^n integers, allocate top-level array of size n, elements
138
 * of which are 1-Dimensional (leaf-array) of geometrically increasing
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
139
 * size (power of 2).
281.1.1 by john haque
Speed/memory performance improvements.
140
 *
141
 *  [0]   -->  [ 0 ]
142
 *  [1]   -->  [ 1 ]
143
 *  |2|   -->  [ 2 | 3 ]
144
 *  |3|   -->  [ 4 | 5 | 6 | 7 ]
145
 *  |.|
146
 *  |k|   -->  [ 2^(k - 1)| ...  | 2^k - 1 ]
147
 *  ...
148
 *
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
149
 * For a given integer n (> 0), the leaf-array is at 1 + floor(log2(n)).
281.1.1 by john haque
Speed/memory performance improvements.
150
 *
151
 * The idea for the geometrically increasing array sizes is from:
152
 * 	Fast Functional Lists, Hash-Lists, Deques and Variable Length Arrays.
153
 * 	Bagwell, Phil (2002).
154
 * 	http://infoscience.epfl.ch/record/64410/files/techlists.pdf
155
 *
156
 * Disadvantage:
157
 * Worst case memory waste > 99% and will happen when each of the
158
 * leaf arrays contains only a single element. Even with consecutive
159
 * integers, memory waste can be as high as 50%.
160
 *
161
 * Solution: Hashed Array Trees (HATs).
162
 *
163
 */
164
321 by john haque
Polish array handling code.
165
/* cint_array_init ---  array initialization routine */
281.1.1 by john haque
Speed/memory performance improvements.
166
167
static NODE **
168
cint_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED)
169
{
321 by john haque
Polish array handling code.
170
	if (symbol == NULL) {
171
		long newval;
408.4.32 by Arnold D. Robbins
Fixes based on problems from a static checker.
172
		size_t nelems = (sizeof(power_two_table) / sizeof(power_two_table[0]));
321 by john haque
Polish array handling code.
173
174
		/* check relevant environment variables */
175
		if ((newval = getenv_long("NHAT")) > 1 && newval < INT32_BIT)
176
			NHAT = newval;
408.4.32 by Arnold D. Robbins
Fixes based on problems from a static checker.
177
		/* don't allow overflow off the end of the table */
1253.1.1 by Andrew J. Schorr
Fix cint off-by-one array bounds overflow check for NHAT set in the environment.
178
		if (NHAT > nelems - 2)
408.4.32 by Arnold D. Robbins
Fixes based on problems from a static checker.
179
			NHAT = nelems - 2;
321 by john haque
Polish array handling code.
180
		THRESHOLD = power_two_table[NHAT + 1];
181
	} else
182
		null_array(symbol);
183
408.19.164 by Arnold D. Robbins
Replace `! NULL' with `& success_node' in array routines.
184
	return & success_node;
281.1.1 by john haque
Speed/memory performance improvements.
185
}
186
187
188
/* is_uinteger --- test if the subscript is an integer >= 0 */
189
190
NODE **
191
is_uinteger(NODE *symbol, NODE *subs)
192
{
193
	if (is_integer(symbol, subs) != NULL && subs->numbr >= 0)
408.19.164 by Arnold D. Robbins
Replace `! NULL' with `& success_node' in array routines.
194
		return & success_node;
281.1.1 by john haque
Speed/memory performance improvements.
195
	return NULL;
196
}
197
198
199
/* cint_lookup --- Find the subscript in the array; Install it if it isn't there. */
200
201
static NODE **
202
cint_lookup(NODE *symbol, NODE *subs)
203
{
204
	NODE **lhs;
205
	long k;
206
	int h1 = -1, m, li;
207
	NODE *tn, *xn;
208
	long cint_size, capacity;
209
210
	k = -1;
211
	if (ISUINT(symbol, subs)) {
212
		k = subs->numbr;	/* k >= 0 */
213
		h1 = cint_hash(k);	/* h1 >= NHAT */
214
		if ((lhs = cint_find(symbol, k, h1)) != NULL)
215
			return lhs;
216
	}
217
	xn = symbol->xarray;
218
	if (xn != NULL && (lhs = xn->aexists(xn, subs)) != NULL)
219
		return lhs;
220
221
	/* It's not there, install it */
222
223
	if (k < 0)
224
		goto xinstall;
225
226
	m = h1 - 1;	/* m >= (NHAT- 1) */
227
228
	/* Estimate capacity upper bound.
229
	 * capacity upper bound = current capacity + leaf array size.
230
	 */
231
	li = m > NHAT ? m : NHAT;
232
	while (li >= NHAT) {
233
		/* leaf-array of a HAT */
234
		li = (li + 1) / 2;
235
	}
236
	capacity = symbol->array_capacity + power_two_table[li];
237
238
	cint_size = (xn == NULL) ? symbol->table_size
239
				: (symbol->table_size - xn->table_size);
240
	assert(cint_size >= 0);
241
	if ((capacity - cint_size) > THRESHOLD)
242
		goto xinstall;
243
244
	if (symbol->nodes == NULL) {
245
		symbol->array_capacity = 0;
246
		assert(symbol->table_size == 0);
247
248
		/* nodes[0] .. nodes[NHAT- 1] not used */
731.9.23 by Andrew J. Schorr
Replace malloc+memset with calloc, mostly by using the new ezalloc macro.
249
		ezalloc(symbol->nodes, NODE **, INT32_BIT * sizeof(NODE *), "cint_lookup");
281.1.1 by john haque
Speed/memory performance improvements.
250
	}
251
252
	symbol->table_size++;	/* one more element in array */
253
254
	tn = symbol->nodes[h1];
255
	if (tn == NULL) {
256
		tn = make_node(Node_array_tree);
257
		symbol->nodes[h1] = tn;
258
	}
259
260
	if (m < NHAT)
261
		return tree_lookup(symbol, tn, k, NHAT, 0);
262
	return tree_lookup(symbol, tn, k, m, power_two_table[m]);
263
264
xinstall:
265
266
	symbol->table_size++;
267
	if (xn == NULL) {
268
		xn = symbol->xarray = make_array();
269
		xn->vname = symbol->vname;	/* shallow copy */
270
321 by john haque
Polish array handling code.
271
		/*
272
		 * Avoid using assoc_lookup(xn, subs) which may lead
281.1.1 by john haque
Speed/memory performance improvements.
273
		 * to infinite recursion.
274
		 */
275
276
		if (is_integer(xn, subs))
1051 by Andrew J. Schorr
Use a struct instead of an array to contain the array methods.
277
			xn->array_funcs = & int_array_func;
281.1.1 by john haque
Speed/memory performance improvements.
278
		else
1051 by Andrew J. Schorr
Use a struct instead of an array to contain the array methods.
279
			xn->array_funcs = & str_array_func;
281.1.1 by john haque
Speed/memory performance improvements.
280
		xn->flags |= XARRAY;
281
	}
282
	return xn->alookup(xn, subs);
283
}
284
285
286
/* cint_exists --- test whether an index is in the array or not. */
287
288
static NODE **
289
cint_exists(NODE *symbol, NODE *subs)
290
{
291
	NODE *xn;
292
293
	if (ISUINT(symbol, subs)) {
294
		long k = subs->numbr;
295
		NODE **lhs;
296
		if ((lhs = cint_find(symbol, k, cint_hash(k))) != NULL)
297
			return lhs;
298
	}
299
	if ((xn = symbol->xarray) == NULL)
300
		return NULL;
301
	return xn->aexists(xn, subs);
302
}
303
304
305
/* cint_clear --- flush all the values in symbol[] */
306
307
static NODE **
308
cint_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
309
{
310
	size_t i;
311
	NODE *tn;
312
313
	assert(symbol->nodes != NULL);
314
315
	if (symbol->xarray != NULL) {
316
		NODE *xn = symbol->xarray;
317
		assoc_clear(xn);
318
		freenode(xn);
319
		symbol->xarray = NULL;
320
	}
321
322
	for (i = NHAT; i < INT32_BIT; i++) {
323
		tn = symbol->nodes[i];
324
		if (tn != NULL) {
325
			tree_clear(tn);
326
			freenode(tn);
327
		}
328
	}
329
330
	efree(symbol->nodes);
321 by john haque
Polish array handling code.
331
	symbol->ainit(symbol, NULL);	/* re-initialize symbol */
281.1.1 by john haque
Speed/memory performance improvements.
332
	return NULL;
333
}
334
335
336
/* cint_remove --- remove an index from the array */
337
338
static NODE **
339
cint_remove(NODE *symbol, NODE *subs)
340
{
341
	long k;
342
	int h1;
343
	NODE *tn, *xn = symbol->xarray;
344
291 by Arnold D. Robbins
The grand merge: dgawk and pgawk folded into gawk.
345
	if (symbol->table_size == 0)
346
		return NULL;
347
281.1.1 by john haque
Speed/memory performance improvements.
348
	if (! ISUINT(symbol, subs))
349
		goto xremove;
350
291 by Arnold D. Robbins
The grand merge: dgawk and pgawk folded into gawk.
351
	assert(symbol->nodes != NULL);
352
281.1.1 by john haque
Speed/memory performance improvements.
353
	k = subs->numbr;
354
	h1 = cint_hash(k);
355
	tn = symbol->nodes[h1];
356
	if (tn == NULL || ! tree_remove(symbol, tn, k))
357
		goto xremove;
358
359
	if (tn->table_size == 0) {
360
		freenode(tn);
361
		symbol->nodes[h1] = NULL;
362
	}
363
364
	symbol->table_size--;
365
366
	if (xn == NULL && symbol->table_size == 0) {
367
		efree(symbol->nodes);
321 by john haque
Polish array handling code.
368
		symbol->ainit(symbol, NULL);	/* re-initialize array 'symbol' */
281.1.1 by john haque
Speed/memory performance improvements.
369
	} else if(xn != NULL && symbol->table_size == xn->table_size) {
370
		/* promote xn to symbol */
291 by Arnold D. Robbins
The grand merge: dgawk and pgawk folded into gawk.
371
281.1.1 by john haque
Speed/memory performance improvements.
372
		xn->flags &= ~XARRAY;
373
		xn->parent_array = symbol->parent_array;
374
		efree(symbol->nodes);
375
		*symbol = *xn;
376
		freenode(xn);
377
	}
378
408.19.164 by Arnold D. Robbins
Replace `! NULL' with `& success_node' in array routines.
379
	return & success_node;
281.1.1 by john haque
Speed/memory performance improvements.
380
381
xremove:
382
	xn = symbol->xarray;
383
	if (xn == NULL || xn->aremove(xn, subs) == NULL)
384
		return NULL;
385
	if (xn->table_size == 0) {
386
		freenode(xn);
387
		symbol->xarray = NULL;
388
	}
389
	symbol->table_size--;
390
	assert(symbol->table_size > 0);
391
408.19.164 by Arnold D. Robbins
Replace `! NULL' with `& success_node' in array routines.
392
	return & success_node;
281.1.1 by john haque
Speed/memory performance improvements.
393
}
394
395
396
/* cint_copy --- duplicate input array "symbol" */
397
398
static NODE **
399
cint_copy(NODE *symbol, NODE *newsymb)
400
{
401
	NODE **old, **new;
402
	size_t i;
403
404
	assert(symbol->nodes != NULL);
405
406
	/* allocate new table */
731.9.23 by Andrew J. Schorr
Replace malloc+memset with calloc, mostly by using the new ezalloc macro.
407
	ezalloc(new, NODE **, INT32_BIT * sizeof(NODE *), "cint_copy");
281.1.1 by john haque
Speed/memory performance improvements.
408
409
	old = symbol->nodes;
410
	for (i = NHAT; i < INT32_BIT; i++) {
411
		if (old[i] == NULL)
412
			continue;
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
413
		new[i] = make_node(Node_array_tree);
281.1.1 by john haque
Speed/memory performance improvements.
414
		tree_copy(newsymb, old[i], new[i]);
415
	}
416
417
	if (symbol->xarray != NULL) {
418
		NODE *xn, *n;
419
		xn = symbol->xarray;
420
		n = make_array();
421
		n->vname = newsymb->vname;
422
		(void) xn->acopy(xn, n);
423
		newsymb->xarray = n;
424
	} else
425
		newsymb->xarray = NULL;
426
427
	newsymb->nodes = new;
428
	newsymb->table_size = symbol->table_size;
429
	newsymb->array_capacity = symbol->array_capacity;
430
	newsymb->flags = symbol->flags;
431
432
	return NULL;
433
}
434
435
436
/* cint_list --- return a list of items */
437
438
static NODE**
439
cint_list(NODE *symbol, NODE *t)
440
{
441
	NODE **list = NULL;
442
	NODE *tn, *xn;
443
	unsigned long k = 0, num_elems, list_size;
444
	size_t j, ja, jd;
445
	int elem_size = 1;
322 by john haque
Improve array interface.
446
	assoc_kind_t assoc_kind;
281.1.1 by john haque
Speed/memory performance improvements.
447
448
	num_elems = symbol->table_size;
449
	if (num_elems == 0)
450
		return NULL;
322 by john haque
Improve array interface.
451
	assoc_kind = (assoc_kind_t) t->flags;
452
	if ((assoc_kind & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE))
281.1.1 by john haque
Speed/memory performance improvements.
453
		num_elems = 1;
454
322 by john haque
Improve array interface.
455
	if ((assoc_kind & (AINDEX|AVALUE)) == (AINDEX|AVALUE))
281.1.1 by john haque
Speed/memory performance improvements.
456
		elem_size = 2;
457
	list_size = num_elems * elem_size;
458
459
	if (symbol->xarray != NULL) {
460
		xn = symbol->xarray;
461
		list = xn->alist(xn, t);
462
		assert(list != NULL);
322 by john haque
Improve array interface.
463
		assoc_kind &= ~(AASC|ADESC);
464
		t->flags = (unsigned int) assoc_kind;
281.1.1 by john haque
Speed/memory performance improvements.
465
		if (num_elems == 1 || num_elems == xn->table_size)
466
			return list;
467
		erealloc(list, NODE **, list_size * sizeof(NODE *), "cint_list");
468
		k = elem_size * xn->table_size;
469
	} else
470
		emalloc(list, NODE **, list_size * sizeof(NODE *), "cint_list");
471
322 by john haque
Improve array interface.
472
	if ((assoc_kind & AINUM) == 0) {
473
		/* not sorting by "index num" */
474
		assoc_kind &= ~(AASC|ADESC);
475
		t->flags = (unsigned int) assoc_kind;
476
	}
281.1.1 by john haque
Speed/memory performance improvements.
477
478
	/* populate it with index in ascending or descending order */
479
480
	for (ja = NHAT, jd = INT32_BIT - 1; ja < INT32_BIT && jd >= NHAT; ) {
333 by Arnold D. Robbins
Merge branch 'master' into array-iface
481
		j = (assoc_kind & ADESC) != 0 ? jd-- : ja++;
281.1.1 by john haque
Speed/memory performance improvements.
482
		tn = symbol->nodes[j];
483
		if (tn == NULL)
484
			continue;
322 by john haque
Improve array interface.
485
		k += tree_list(tn, list + k, assoc_kind);
281.1.1 by john haque
Speed/memory performance improvements.
486
		if (k >= list_size)
487
			return list;
488
	}
489
	return list;
490
}
491
492
493
/* cint_dump --- dump array info */
494
495
static NODE **
496
cint_dump(NODE *symbol, NODE *ndump)
497
{
498
	NODE *tn, *xn = NULL;
499
	int indent_level;
500
	size_t i;
281.1.2 by john haque
Add a test file, cleanup code and update doc.
501
	long cint_size = 0, xsize = 0;
281.1.1 by john haque
Speed/memory performance improvements.
502
	AWKNUM kb = 0;
503
	extern AWKNUM int_kilobytes(NODE *symbol);
281.1.2 by john haque
Add a test file, cleanup code and update doc.
504
	extern AWKNUM str_kilobytes(NODE *symbol);
281.1.1 by john haque
Speed/memory performance improvements.
505
506
	indent_level = ndump->alevel;
507
508
	if (symbol->xarray != NULL) {
509
		xn = symbol->xarray;
281.1.2 by john haque
Add a test file, cleanup code and update doc.
510
		xsize = xn->table_size;
281.1.1 by john haque
Speed/memory performance improvements.
511
	}
281.1.2 by john haque
Add a test file, cleanup code and update doc.
512
	cint_size = symbol->table_size - xsize;
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
513
281.1.1 by john haque
Speed/memory performance improvements.
514
	if ((symbol->flags & XARRAY) == 0)
515
		fprintf(output_fp, "%s `%s'\n",
516
			(symbol->parent_array == NULL) ? "array" : "sub-array",
517
			array_vname(symbol));
518
	indent_level++;
519
	indent(indent_level);
520
	fprintf(output_fp, "array_func: cint_array_func\n");
521
	if (symbol->flags != 0) {
522
		indent(indent_level);
523
		fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags));
524
	}
525
	indent(indent_level);
526
	fprintf(output_fp, "NHAT: %d\n", NHAT);
527
	indent(indent_level);
528
	fprintf(output_fp, "THRESHOLD: %ld\n", THRESHOLD);
529
	indent(indent_level);
1379.1.24 by Arnold D. Robbins
Fix some printf formatting issues.
530
	fprintf(output_fp, "table_size: %lu (total), %ld (cint), %ld (int + str)\n",
531
				(unsigned long) symbol->table_size, cint_size, xsize);
281.1.1 by john haque
Speed/memory performance improvements.
532
	indent(indent_level);
533
	fprintf(output_fp, "array_capacity: %lu\n", (unsigned long) symbol->array_capacity);
534
	indent(indent_level);
535
	fprintf(output_fp, "Load Factor: %.2g\n", (AWKNUM) cint_size / symbol->array_capacity);
536
537
	for (i = NHAT; i < INT32_BIT; i++) {
538
		tn = symbol->nodes[i];
539
		if (tn == NULL)
540
			continue;
541
		/* Node_array_tree  + HAT */
542
		kb += (sizeof(NODE) + tree_kilobytes(tn)) / 1024.0;
543
	}
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
544
	kb += (INT32_BIT * sizeof(NODE *)) / 1024.0;	/* symbol->nodes */
281.1.1 by john haque
Speed/memory performance improvements.
545
	kb += (symbol->array_capacity * sizeof(NODE *)) / 1024.0;	/* value nodes in Node_array_leaf(s) */
281.1.2 by john haque
Add a test file, cleanup code and update doc.
546
	if (xn != NULL) {
1051 by Andrew J. Schorr
Use a struct instead of an array to contain the array methods.
547
		if (xn->array_funcs == & int_array_func)
281.1.2 by john haque
Add a test file, cleanup code and update doc.
548
			kb += int_kilobytes(xn);
549
		else
550
			kb += str_kilobytes(xn);
551
	}
552
281.1.1 by john haque
Speed/memory performance improvements.
553
	indent(indent_level);
554
	fprintf(output_fp, "memory: %.2g kB (total)\n", kb);
555
556
	/* dump elements */
557
558
	if (ndump->adepth >= 0) {
559
		const char *aname;
560
561
		fprintf(output_fp, "\n");
562
		aname = make_aname(symbol);
563
		for (i = NHAT; i < INT32_BIT; i++) {
564
			tn = symbol->nodes[i];
565
			if (tn != NULL)
566
				tree_info(tn, ndump, aname);
567
		}
568
	}
569
570
	if (xn != NULL) {
571
		fprintf(output_fp, "\n");
572
		xn->adump(xn, ndump);
573
	}
574
575
#ifdef ARRAYDEBUG
576
	if (ndump->adepth < -999)
577
		cint_print(symbol);
578
#endif
579
580
	return NULL;
581
}
582
583
584
/* cint_hash --- locate the HAT for a given number 'k' */
585
586
static inline int
587
cint_hash(long k)
588
{
589
	uint32_t num, r, shift;
590
591
	assert(k >= 0);
592
	if (k == 0)
593
		return NHAT;
594
	num = k;
595
596
	/* Find the Floor(log base 2 of 32-bit integer) */
597
321 by john haque
Polish array handling code.
598
	/*
599
	 * Warren Jr., Henry S. (2002). Hacker's Delight.
281.1.1 by john haque
Speed/memory performance improvements.
600
	 * Addison Wesley. pp. pp. 215. ISBN 978-0201914658.
601
	 *
602
	 *	r = 0;
603
	 *	if (num >= 1<<16) { num >>= 16;	r += 16; }
604
	 *	if (num >= 1<< 8) { num >>=  8;	r +=  8; }
605
	 *	if (num >= 1<< 4) { num >>=  4;	r +=  4; }
606
	 *	if (num >= 1<< 2) { num >>=  2;	r +=  2; }
607
	 *	if (num >= 1<< 1) {		r +=  1; }
608
	 */
609
610
321 by john haque
Polish array handling code.
611
	/*
612
	 * Slightly different code copied from:
281.1.1 by john haque
Speed/memory performance improvements.
613
	 *
614
	 * http://www-graphics.stanford.edu/~seander/bithacks.html
615
	 * Bit Twiddling Hacks
616
	 * By Sean Eron Anderson
617
	 * seander@cs.stanford.edu
618
	 * Individually, the code snippets here are in the public domain
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
619
	 * (unless otherwise noted) --- feel free to use them however you please.
620
	 * The aggregate collection and descriptions are (C) 1997-2005
281.1.1 by john haque
Speed/memory performance improvements.
621
	 * Sean Eron Anderson. The code and descriptions are distributed in the
622
	 * hope that they will be useful, but WITHOUT ANY WARRANTY and without
623
	 * even the implied warranty of merchantability or fitness for a particular
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
624
	 * purpose.
281.1.1 by john haque
Speed/memory performance improvements.
625
	 *
626
	 */
627
628
	r = (num > 0xFFFF) << 4; num >>= r;
629
	shift = (num > 0xFF) << 3; num >>= shift; r |= shift;
630
	shift = (num > 0x0F) << 2; num >>= shift; r |= shift;
631
	shift = (num > 0x03) << 1; num >>= shift; r |= shift;
632
	r |= (num >> 1);
633
634
	/* We use a single HAT for 0 <= num < 2^NHAT */
635
	if (r < NHAT)
636
		return NHAT;
637
638
	return (1 + r);
639
}
640
641
642
/* cint_find --- locate the integer subscript */
643
644
static inline NODE **
645
cint_find(NODE *symbol, long k, int h1)
646
{
647
	NODE *tn;
648
649
	if (symbol->nodes == NULL || (tn = symbol->nodes[h1]) == NULL)
650
		return NULL;
651
	return tree_exists(tn, k);
652
}
653
654
655
#ifdef ARRAYDEBUG
656
657
/* cint_print --- print structural info */
658
659
static void
660
cint_print(NODE *symbol)
661
{
662
	NODE *tn;
663
	size_t i;
664
665
	fprintf(output_fp, "I[%4lu:%-4lu]\n", (unsigned long) INT32_BIT,
666
				(unsigned long) symbol->table_size);
667
	for (i = NHAT; i < INT32_BIT; i++) {
668
		tn = symbol->nodes[i];
669
		if (tn == NULL)
670
			continue;
671
		tree_print(tn, i, 1);
672
	}
673
}
674
675
#endif
676
677
678
/*------------------------ Hashed Array Trees -----------------------------*/
679
680
/*
681
 * HATs: Hashed Array Trees
682
 * Fast variable-length arrays
683
 * Edward Sitarski
684
 * http://www.drdobbs.com/architecture-and-design/184409965
685
 *
686
 *  HAT has a top-level array containing a power of two
687
 *  number of leaf arrays. All leaf arrays are the same size as the
688
 *  top-level array. A full HAT can hold n^2 elements,
689
 *  where n (some power of 2) is the size of each leaf array.
690
 *  [i/n][i & (n - 1)] locates the `i th' element in a HAT.
691
 *
692
 */
693
694
/*
695
 *  A half HAT is defined here as a HAT with a top-level array of size n^2/2
696
 *  and holds the first n^2/2 elements.
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
697
 *
281.1.1 by john haque
Speed/memory performance improvements.
698
 *   1. 2^8 elements can be stored in a full HAT of size 2^4.
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
699
 *   2. 2^9 elements can be stored in a half HAT of size 2^5.
281.1.1 by john haque
Speed/memory performance improvements.
700
 *   3. When the number of elements is some power of 2, it
701
 *      can be stored in a full or a half HAT.
702
 *   4. When the number of elements is some power of 2, it
703
 *      can be stored in a HAT (full or half) with HATs as leaf elements
704
 *      (full or half),  and so on (e.g. 2^8 elements in a HAT of size 2^4 (top-level
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
705
 *      array dimension) with each leaf array being a HAT of size 2^2).
281.1.1 by john haque
Speed/memory performance improvements.
706
 *
707
 *  IMPLEMENTATION DETAILS:
708
 *    1. A HAT of 2^12 elements needs 2^6 house-keeping NODEs
709
 *       of Node_array_leaf.
710
 *
711
 *    2. A HAT of HATS of 2^12 elements needs
712
 *       2^6 * (1 Node_array_tree + 2^3 Node_array_leaf)
713
 *       ~ 2^9 house-keeping NODEs.
714
 *
715
 *    3. When a leaf array (or leaf HAT) becomes empty, the memory
716
 *       is deallocated, and when there is no leaf array (or leaf HAT) left,
717
 *       the HAT is deleted.
718
 *
719
 *    4. A HAT stores the base (first) element, and locates the leaf array/HAT
720
 *       for the `i th' element using integer division
721
 *       (i - base)/n where n is the size of the top-level array.
722
 *
723
 */
724
725
/* make_node --- initialize a NODE */
726
727
static inline NODE *
728
make_node(NODETYPE type)
729
{
730
	NODE *n;
731
	getnode(n);
732
	memset(n, '\0', sizeof(NODE));
733
	n->type = type;
734
	return n;
735
}
736
737
738
/* tree_lookup --- Find an integer subscript in a HAT; Install it if it isn't there */
739
740
static NODE **
741
tree_lookup(NODE *symbol, NODE *tree, long k, int m, long base)
742
{
743
	NODE **lhs;
744
	NODE *tn;
745
	int i, n;
746
	size_t size;
747
	long num = k;
748
749
	/*
750
	 * HAT size (size of Top & Leaf array) = 2^n
751
	 * where n = Floor ((m + 1)/2). For an odd value of m,
752
	 * only the first half of the HAT is needed.
753
	 */
754
755
	n = (m + 1) / 2;
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
756
281.1.1 by john haque
Speed/memory performance improvements.
757
	if (tree->table_size == 0) {
758
		size_t actual_size;
759
		NODE **table;
760
761
		assert(tree->nodes == NULL);
762
763
		/* initialize top-level array */
764
		size = actual_size = power_two_table[n];
765
		tree->array_base = base;
766
		tree->array_size = size;
767
		tree->table_size = 0;	/* # of elements in the array */
768
		if (n > m/2) {
769
			/* only first half of the array used */
770
			actual_size /= 2;
771
			tree->flags |= HALFHAT;
772
		}
731.9.23 by Andrew J. Schorr
Replace malloc+memset with calloc, mostly by using the new ezalloc macro.
773
		ezalloc(table, NODE **, actual_size * sizeof(NODE *), "tree_lookup");
281.1.1 by john haque
Speed/memory performance improvements.
774
		tree->nodes = table;
775
	} else
776
		size = tree->array_size;
777
778
	num -= tree->array_base;
779
	i = num / size;	/* top-level array index */
780
	assert(i >= 0);
781
782
	if ((lhs = tree_find(tree, k, i)) != NULL)
783
		return lhs;
784
785
	/* It's not there, install it */
786
787
	tree->table_size++;
788
	base += (size * i);
789
	tn = tree->nodes[i];
790
	if (n > NHAT) {
791
		if (tn == NULL)
792
			tn = tree->nodes[i] = make_node(Node_array_tree);
793
		return tree_lookup(symbol, tn, k, n, base);
794
	} else {
795
		if (tn == NULL)
796
			tn = tree->nodes[i] = make_node(Node_array_leaf);
797
		return leaf_lookup(symbol, tn, k, size, base);
798
	}
799
}
800
801
802
/* tree_exists --- test whether integer subscript `k' exists or not */
803
804
static NODE **
805
tree_exists(NODE *tree, long k)
806
{
807
	int i;
808
	NODE *tn;
809
810
	i = (k - tree->array_base) / tree->array_size;
811
	assert(i >= 0);
812
	tn = tree->nodes[i];
813
	if (tn == NULL)
814
		return NULL;
815
	if (tn->type == Node_array_tree)
816
		return tree_exists(tn, k);
817
	return leaf_exists(tn, k);
818
}
819
820
/* tree_clear --- flush all the values */
821
822
static void
823
tree_clear(NODE *tree)
824
{
825
	NODE *tn;
826
	size_t	j, hsize;
827
828
	hsize = tree->array_size;
829
	if ((tree->flags & HALFHAT) != 0)
830
		hsize /= 2;
831
832
	for (j = 0; j < hsize; j++) {
833
		tn = tree->nodes[j];
834
		if (tn == NULL)
835
			continue;
836
		if (tn->type == Node_array_tree)
837
			tree_clear(tn);
838
		else
839
			leaf_clear(tn);
840
		freenode(tn);
841
	}
842
843
	efree(tree->nodes);
844
	memset(tree, '\0', sizeof(NODE));
845
	tree->type = Node_array_tree;
846
}
847
848
849
/* tree_remove --- If the integer subscript is in the HAT, remove it */
850
851
static int
852
tree_remove(NODE *symbol, NODE *tree, long k)
853
{
854
	int i;
855
	NODE *tn;
856
857
	i = (k - tree->array_base) / tree->array_size;
858
	assert(i >= 0);
859
	tn = tree->nodes[i];
860
	if (tn == NULL)
319.1.9 by Arnold D. Robbins
Move to use of bool type, true, false, everywhere.
861
		return false;
281.1.1 by john haque
Speed/memory performance improvements.
862
863
	if (tn->type == Node_array_tree
864
			&& ! tree_remove(symbol, tn, k))
319.1.9 by Arnold D. Robbins
Move to use of bool type, true, false, everywhere.
865
		return false;
281.1.1 by john haque
Speed/memory performance improvements.
866
	else if (tn->type == Node_array_leaf
867
			&& ! leaf_remove(symbol, tn, k))
319.1.9 by Arnold D. Robbins
Move to use of bool type, true, false, everywhere.
868
		return false;
281.1.1 by john haque
Speed/memory performance improvements.
869
870
	if (tn->table_size == 0) {
871
		freenode(tn);
872
		tree->nodes[i] = NULL;
873
	}
874
875
	/* one less item in array */
876
	if (--tree->table_size == 0) {
877
		efree(tree->nodes);
878
		memset(tree, '\0', sizeof(NODE));
879
		tree->type = Node_array_tree;
880
	}
319.1.9 by Arnold D. Robbins
Move to use of bool type, true, false, everywhere.
881
	return true;
281.1.1 by john haque
Speed/memory performance improvements.
882
}
883
884
885
/* tree_find --- locate an interger subscript in the HAT */
886
887
static inline NODE **
888
tree_find(NODE *tree, long k, int i)
889
{
890
	NODE *tn;
891
892
	assert(tree->nodes != NULL);
893
	tn = tree->nodes[i];
894
	if (tn != NULL) {
895
		if (tn->type == Node_array_tree)
896
			return tree_exists(tn, k);
897
		return leaf_exists(tn, k);
898
	}
899
	return NULL;
900
}
901
902
903
/* tree_list --- return a list of items in the HAT */
904
905
static long
322 by john haque
Improve array interface.
906
tree_list(NODE *tree, NODE **list, assoc_kind_t assoc_kind)
281.1.1 by john haque
Speed/memory performance improvements.
907
{
908
	NODE *tn;
909
	size_t j, cj, hsize;
910
	long k = 0;
911
912
	assert(list != NULL);
913
914
	hsize = tree->array_size;
915
	if ((tree->flags & HALFHAT) != 0)
916
		hsize /= 2;
917
918
	for (j = 0; j < hsize; j++) {
333 by Arnold D. Robbins
Merge branch 'master' into array-iface
919
		cj = (assoc_kind & ADESC) != 0 ? (hsize - 1 - j) : j;
281.1.1 by john haque
Speed/memory performance improvements.
920
		tn = tree->nodes[cj];
921
		if (tn == NULL)
922
			continue;
923
		if (tn->type == Node_array_tree)
322 by john haque
Improve array interface.
924
			k += tree_list(tn, list + k, assoc_kind);
281.1.1 by john haque
Speed/memory performance improvements.
925
		else
322 by john haque
Improve array interface.
926
			k += leaf_list(tn, list + k, assoc_kind);
927
		if ((assoc_kind & ADELETE) != 0 && k >= 1)
281.1.1 by john haque
Speed/memory performance improvements.
928
			return k;
929
	}
930
	return k;
931
}
932
933
934
/* tree_copy --- duplicate a HAT */
935
936
static void
937
tree_copy(NODE *newsymb, NODE *tree, NODE *newtree)
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
938
{
281.1.1 by john haque
Speed/memory performance improvements.
939
	NODE **old, **new;
940
	size_t j, hsize;
941
942
	hsize = tree->array_size;
943
	if ((tree->flags & HALFHAT) != 0)
944
		hsize /= 2;
945
731.9.23 by Andrew J. Schorr
Replace malloc+memset with calloc, mostly by using the new ezalloc macro.
946
	ezalloc(new, NODE **, hsize * sizeof(NODE *), "tree_copy");
281.1.1 by john haque
Speed/memory performance improvements.
947
	newtree->nodes = new;
948
	newtree->array_base = tree->array_base;
949
	newtree->array_size = tree->array_size;
950
	newtree->table_size = tree->table_size;
951
	newtree->flags = tree->flags;
952
953
	old = tree->nodes;
954
	for (j = 0; j < hsize; j++) {
955
		if (old[j] == NULL)
956
			continue;
957
		if (old[j]->type == Node_array_tree) {
958
			new[j] = make_node(Node_array_tree);
959
			tree_copy(newsymb, old[j], new[j]);
960
		} else {
961
			new[j] = make_node(Node_array_leaf);
962
			leaf_copy(newsymb, old[j], new[j]);
963
		}
964
	}
965
}
966
967
968
/* tree_info --- print index, value info */
969
970
static void
971
tree_info(NODE *tree, NODE *ndump, const char *aname)
972
{
973
	NODE *tn;
974
	size_t j, hsize;
975
976
	hsize = tree->array_size;
977
	if ((tree->flags & HALFHAT) != 0)
978
		hsize /= 2;
979
980
	for (j = 0; j < hsize; j++) {
981
		tn = tree->nodes[j];
982
		if (tn == NULL)
983
			continue;
984
		if (tn->type == Node_array_tree)
985
			tree_info(tn, ndump, aname);
986
		else
987
			leaf_info(tn, ndump, aname);
988
	}
989
}
990
991
992
/* tree_kilobytes --- calculate memory consumption of a HAT */
993
994
static size_t
995
tree_kilobytes(NODE *tree)
996
{
997
	NODE *tn;
998
	size_t j, hsize;
999
	size_t sz = 0;
1000
1001
	hsize = tree->array_size;
1002
	if ((tree->flags & HALFHAT) != 0)
1003
		hsize /= 2;
1004
	for (j = 0; j < hsize; j++) {
1005
		tn = tree->nodes[j];
1006
		if (tn == NULL)
1007
			continue;
1008
		sz += sizeof(NODE);	/* Node_array_tree or Node_array_leaf */
1009
		if (tn->type == Node_array_tree)
1010
			sz += tree_kilobytes(tn);
1011
	}
1012
	sz += hsize * sizeof(NODE *);	/* tree->nodes */
1013
	return sz;
1014
}
1015
1016
#ifdef ARRAYDEBUG
1017
1018
/* tree_print --- print the HAT structures */
1019
1020
static void
1021
tree_print(NODE *tree, size_t bi, int indent_level)
1022
{
1023
	NODE *tn;
1024
	size_t j, hsize;
1025
1026
	indent(indent_level);
1027
1028
	hsize = tree->array_size;
1029
	if ((tree->flags & HALFHAT) != 0)
1030
		hsize /= 2;
319.2.21 by Arnold D. Robbins
Minor fixes for printf compile warnings.
1031
	fprintf(output_fp, "%4lu:%s[%4lu:%-4lu]\n",
1032
			(unsigned long) bi,
319.1.140 by Arnold D. Robbins
Make bitflag checking consistent everywhere.
1033
			(tree->flags & HALFHAT) != 0 ? "HH" : "H",
281.1.1 by john haque
Speed/memory performance improvements.
1034
			(unsigned long) hsize, (unsigned long) tree->table_size);
1035
1036
	for (j = 0; j < hsize; j++) {
1037
		tn = tree->nodes[j];
1038
		if (tn == NULL)
1039
			continue;
1040
		if (tn->type == Node_array_tree)
1041
			tree_print(tn, j, indent_level + 1);
1042
		else
1043
			leaf_print(tn, j, indent_level + 1);
1044
	}
1045
}
1046
#endif
1047
1048
/*--------------------- leaf (linear 1-D) array --------------------*/
1049
321 by john haque
Polish array handling code.
1050
/*
1051
 * leaf_lookup --- find an integer subscript in the array; Install it if
1052
 *	it isn't there.
1053
 */
281.1.1 by john haque
Speed/memory performance improvements.
1054
1055
static inline NODE **
1056
leaf_lookup(NODE *symbol, NODE *array, long k, long size, long base)
1057
{
1058
	NODE **lhs;
1059
1060
	if (array->nodes == NULL) {
1061
		array->table_size = 0;	/* sanity */
1062
		array->array_size = size;
1063
		array->array_base = base;
731.9.23 by Andrew J. Schorr
Replace malloc+memset with calloc, mostly by using the new ezalloc macro.
1064
		ezalloc(array->nodes, NODE **, size * sizeof(NODE *), "leaf_lookup");
281.1.1 by john haque
Speed/memory performance improvements.
1065
		symbol->array_capacity += size;
1066
	}
1067
1068
	lhs = array->nodes + (k - base); /* leaf element */
1069
	if (*lhs == NULL) {
1070
		array->table_size++;	/* one more element in leaf array */
1071
		*lhs = dupnode(Nnull_string);
1072
	}
1073
	return lhs;
1074
}
1075
1076
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
1077
/* leaf_exists --- check if the array contains an integer subscript */
281.1.1 by john haque
Speed/memory performance improvements.
1078
1079
static inline NODE **
1080
leaf_exists(NODE *array, long k)
1081
{
1082
	NODE **lhs;
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
1083
	lhs = array->nodes + (k - array->array_base);
281.1.1 by john haque
Speed/memory performance improvements.
1084
	return (*lhs != NULL) ? lhs : NULL;
1085
}
1086
1087
1088
/* leaf_clear --- flush all values in the array */
1089
1090
static void
1091
leaf_clear(NODE *array)
1092
{
1093
	long i, size = array->array_size;
1094
	NODE *r;
1095
1096
	for (i = 0; i < size; i++) {
1097
		r = array->nodes[i];
1098
		if (r == NULL)
1099
			continue;
1100
		if (r->type == Node_var_array) {
1101
			assoc_clear(r);		/* recursively clear all sub-arrays */
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
1102
			efree(r->vname);
281.1.1 by john haque
Speed/memory performance improvements.
1103
			freenode(r);
1104
		} else
1105
			unref(r);
1106
	}
1107
	efree(array->nodes);
1108
	array->nodes = NULL;
1109
	array->array_size = array->table_size = 0;
1110
}
1111
1112
1113
/* leaf_remove --- remove an integer subscript from the array */
1114
1115
static int
1116
leaf_remove(NODE *symbol, NODE *array, long k)
1117
{
1118
	NODE **lhs;
1119
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
1120
	lhs = array->nodes + (k - array->array_base);
281.1.1 by john haque
Speed/memory performance improvements.
1121
	if (*lhs == NULL)
319.1.9 by Arnold D. Robbins
Move to use of bool type, true, false, everywhere.
1122
		return false;
281.1.1 by john haque
Speed/memory performance improvements.
1123
	*lhs = NULL;
1124
	if (--array->table_size == 0) {
1125
		efree(array->nodes);
1126
		array->nodes = NULL;
1127
		symbol->array_capacity -= array->array_size;
1128
		array->array_size = 0;	/* sanity */
1129
	}
319.1.9 by Arnold D. Robbins
Move to use of bool type, true, false, everywhere.
1130
	return true;
281.1.1 by john haque
Speed/memory performance improvements.
1131
}
1132
1133
1134
/* leaf_copy --- duplicate a leaf array */
1135
1136
static void
1137
leaf_copy(NODE *newsymb, NODE *array, NODE *newarray)
1138
{
1139
	NODE **old, **new;
1140
	long size, i;
1141
1142
	size = array->array_size;
731.9.23 by Andrew J. Schorr
Replace malloc+memset with calloc, mostly by using the new ezalloc macro.
1143
	ezalloc(new, NODE **, size * sizeof(NODE *), "leaf_copy");
281.1.1 by john haque
Speed/memory performance improvements.
1144
	newarray->nodes = new;
1145
	newarray->array_size = size;
1146
	newarray->array_base = array->array_base;
1147
	newarray->flags = array->flags;
1148
	newarray->table_size = array->table_size;
1149
1150
	old = array->nodes;
1151
	for (i = 0; i < size; i++) {
1152
		if (old[i] == NULL)
1153
			continue;
1154
		if (old[i]->type == Node_val)
1155
			new[i] = dupnode(old[i]);
1156
		else {
1157
			NODE *r;
1158
			r = make_array();
1159
			r->vname = estrdup(old[i]->vname, strlen(old[i]->vname));
1160
			r->parent_array = newsymb;
1161
			new[i] = assoc_copy(old[i], r);
1162
		}
1163
	}
1164
}
1165
1166
1167
/* leaf_list --- return a list of items */
1168
1169
static long
322 by john haque
Improve array interface.
1170
leaf_list(NODE *array, NODE **list, assoc_kind_t assoc_kind)
281.1.1 by john haque
Speed/memory performance improvements.
1171
{
1172
	NODE *r, *subs;
1173
	long num, i, ci, k = 0;
1174
	long size = array->array_size;
1175
	static char buf[100];
1176
1177
	for (i = 0; i < size; i++) {
333 by Arnold D. Robbins
Merge branch 'master' into array-iface
1178
		ci = (assoc_kind & ADESC) != 0 ? (size - 1 - i) : i;
281.1.1 by john haque
Speed/memory performance improvements.
1179
		r = array->nodes[ci];
1180
		if (r == NULL)
1181
			continue;
1182
1183
		/* index */
1184
		num = array->array_base + ci;
333 by Arnold D. Robbins
Merge branch 'master' into array-iface
1185
		if ((assoc_kind & AISTR) != 0) {
408.26.83 by Arnold D. Robbins
Remove trailing whitespace everywhere. Fix Unicode into ASCII.
1186
			sprintf(buf, "%ld", num);
281.1.1 by john haque
Speed/memory performance improvements.
1187
			subs = make_string(buf, strlen(buf));
1188
			subs->numbr = num;
1189
			subs->flags |= (NUMCUR|NUMINT);
1190
		} else {
1191
			subs = make_number((AWKNUM) num);
1192
			subs->flags |= (INTIND|NUMINT);
1193
		}
1194
		list[k++] = subs;
1195
1196
		/* value */
333 by Arnold D. Robbins
Merge branch 'master' into array-iface
1197
		if ((assoc_kind & AVALUE) != 0) {
281.1.1 by john haque
Speed/memory performance improvements.
1198
			if (r->type == Node_val) {
322 by john haque
Improve array interface.
1199
				if ((assoc_kind & AVNUM) != 0)
281.1.1 by john haque
Speed/memory performance improvements.
1200
					(void) force_number(r);
322 by john haque
Improve array interface.
1201
				else if ((assoc_kind & AVSTR) != 0)
281.1.1 by john haque
Speed/memory performance improvements.
1202
					r = force_string(r);
1203
			}
1204
			list[k++] = r;
1205
		}
322 by john haque
Improve array interface.
1206
		if ((assoc_kind & ADELETE) != 0 && k >= 1)
281.1.1 by john haque
Speed/memory performance improvements.
1207
			return k;
1208
	}
1209
1210
	return k;
1211
}
1212
1213
1214
/* leaf_info --- print index, value info */
1215
1216
static void
1217
leaf_info(NODE *array, NODE *ndump, const char *aname)
1218
{
1219
	NODE *subs, *val;
1220
	size_t i, size;
1221
1222
	size = array->array_size;
1223
1224
	subs = make_number((AWKNUM) 0.0);
1225
	subs->flags |= (INTIND|NUMINT);
1226
	for (i = 0; i < size; i++) {
1227
		val = array->nodes[i];
1228
		if (val == NULL)
1229
			continue;
1230
		subs->numbr = array->array_base + i;
1231
		assoc_info(subs, val, ndump, aname);
1232
	}
1233
	unref(subs);
1234
}
1235
1236
#ifdef ARRAYDEBUG
1237
1238
/* leaf_print --- print the leaf-array structure */
1239
1240
1241
static void
1242
leaf_print(NODE *array, size_t bi, int indent_level)
1243
{
1244
	indent(indent_level);
319.2.21 by Arnold D. Robbins
Minor fixes for printf compile warnings.
1245
	fprintf(output_fp, "%4lu:L[%4lu:%-4lu]\n",
1246
			(unsigned long) bi,
281.1.1 by john haque
Speed/memory performance improvements.
1247
			(unsigned long) array->array_size,
1248
			(unsigned long) array->table_size);
1249
}
1250
#endif
1146.1.22 by Arnold D. Robbins
Improve sandbox mode.
1251
1252
static NODE *argv_shadow_array = NULL;
1253
1254
/* argv_store --- post assign function for ARGV in sandbox mode */
1255
1256
static NODE **
1257
argv_store(NODE *symbol, NODE *subs)
1258
{
1259
	NODE **val = cint_exists(symbol, subs);
1260
	NODE *newval = *val;
1261
	char *cp;
1262
1263
	if (newval->stlen == 0)	// empty strings in ARGV are OK
1264
		return val;
1265
1266
	if ((cp = strchr(newval->stptr, '=')) == NULL) {
1267
		if (! in_array(argv_shadow_array, newval))
1268
			fatal(_("cannot add a new file (%.*s) to ARGV in sandbox mode"),
1269
				(int) newval->stlen, newval->stptr);
1270
	} else {
1271
		// check if it's a valid variable assignment
1272
		bool badvar = false;
1273
		char *arg = newval->stptr;
1274
		char *cp2;
1275
1276
		*cp = '\0';	// temporarily
1277
1278
		if (! is_letter((unsigned char) arg[0]))
1279
			badvar = true;
1280
		else
1281
			for (cp2 = arg+1; *cp2; cp2++)
1282
				if (! is_identchar((unsigned char) *cp2) && *cp2 != ':') {
1283
					badvar = true;
1284
					break;
1285
				}
1286
1287
		// further checks
1288
		if (! badvar) {
1289
			char *cp = strchr(arg, ':');
1290
			if (cp && (cp[1] != ':' || strchr(cp + 2, ':') != NULL))
1291
				badvar = true;
1292
		}
1293
		*cp = '=';	// restore the '='
1294
1295
		if (badvar && ! in_array(argv_shadow_array, newval))
1296
			fatal(_("cannot add a new file (%.*s) to ARGV in sandbox mode"),
1297
				(int) newval->stlen, newval->stptr);
1298
1299
		// otherwise, badvar is false, let it through as variable assignment
1300
	}
1301
	return val;
1302
}
1303
1304
/* init_argv_array --- set up the pointers for ARGV in sandbox mode. A bit hacky. */
1305
1306
void
1307
init_argv_array(NODE *argv_node, NODE *shadow_node)
1308
{
1309
	/* If POSIX simply don't reset the vtable and things work as before */
1310
	if (! do_sandbox)
1311
		return;
1312
1313
	argv_node->array_funcs = & argv_array_func;
1314
	argv_shadow_array = shadow_node;
1315
}