/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
*          Copyright (c) 2020-2023 Contributors to ksh 93u+m           *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 2.0                  *
*                                                                      *
*                A copy of the License is available at                 *
*      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      *
*         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                   Phong Vo <kpv@research.att.com>                    *
*                  Martijn Dekker <martijn@inlv.org>                   *
*                                                                      *
***********************************************************************/
/*
 * Glenn Fowler
 * AT&T Bell Laboratories
 *
 * hash table library
 */

#include "hashlib.h"

/*
 * dump HASH_* flags
 */

static void
dumpflags(int flags)
{
	if (flags & HASH_ALLOCATE) sfprintf(sfstderr, "allocate ");
	if (flags & HASH_BUCKET) sfprintf(sfstderr, "bucket ");
	if (flags & HASH_FIXED) sfprintf(sfstderr, "fixed ");
	if (flags & HASH_HASHED) sfprintf(sfstderr, "hashed ");
	if (flags & HASH_RESIZE) sfprintf(sfstderr, "resize ");
	if (flags & HASH_STATIC) sfprintf(sfstderr, "static ");
	if (flags & HASH_VALUE) sfprintf(sfstderr, "value ");
}

/*
 * dump hash table bucket info
 */

static void
dumpbucket(Hash_table_t* tab, int flags)
{
	Hash_bucket_t**		sp;
	Hash_bucket_t*		b;
	Hash_bucket_t**		sx;
	int			n;
	unsigned char*		s;

	NoP(flags);
	sx = tab->table + tab->size;
	for (sp = tab->table; sp < sx; sp++)
	{
		n = 0;
		for (b = *sp; b; b = b->next)
			if (!(b->hash & HASH_DELETED) && (!(tab->flags & HASH_VALUE) || b->value))
				n++;
		if (n)
		{
			sfprintf(sfstderr, "%5d %2d :", sp - tab->table, n);
			for (b = *sp; b; b = b->next)
				if (!(b->hash & HASH_DELETED) && (!(tab->flags & HASH_VALUE) || b->value))
				{
					if (n = tab->root->namesize)
					{
						sfprintf(sfstderr, " 0x");
						s = (unsigned char*)hashname(b);
						while (n-- > 0)
							sfprintf(sfstderr, "%02x", *s++);
					}
					else sfprintf(sfstderr, " %s", hashname(b));
					if (b->hash & HASH_FLAGS)
					{
						sfprintf(sfstderr, "|");
						if (b->hash & HASH_HIDES) sfprintf(sfstderr, "hides|");
						if (b->hash & HASH_HIDDEN) sfprintf(sfstderr, "hidden|");
						if (b->hash & HASH_KEEP) sfprintf(sfstderr, "keep|");
						if (b->hash & HASH_OPAQUED) sfprintf(sfstderr, "opaque|");
					}
					if (tab->flags & HASH_VALUE) sfprintf(sfstderr, "=0x%08lx", (long)b->value);
				}
			sfprintf(sfstderr, "\n");
		}
	}
	sfprintf(sfstderr, "\n");
}

/*
 * dump info on a single table
 */

static void
dumptable(Hash_table_t* tab, int flags)
{
	Hash_table_t*	scope;
	int		level;

	sfprintf(sfstderr, "        name:        %s", tab->name ? tab->name : "*no name*");
	if (scope = tab->scope)
	{
		level = 1;
		while (scope = scope->scope) level++;
		sfprintf(sfstderr, " level %d scope on 0x%08lx", level, (unsigned long)tab->scope);
	}
	sfprintf(sfstderr, "\n");
	sfprintf(sfstderr, "        address:     0x%08lx\n", (unsigned long)tab);
	sfprintf(sfstderr, "        flags:       ");
	if (tab->frozen) sfprintf(sfstderr, "frozen=%d ", tab->frozen);
	dumpflags(tab->flags);
	sfprintf(sfstderr, "\n");
	sfprintf(sfstderr, "        size:        %d\n", tab->size);
	sfprintf(sfstderr, "        buckets:     %d\n", tab->buckets);
	sfprintf(sfstderr, "        bucketsize:  %d\n", tab->bucketsize * sizeof(char*));
	sfprintf(sfstderr, "\n");
	if ((flags | tab->flags) & HASH_BUCKET) dumpbucket(tab, flags);
}

/*
 * dump hash table root info
 */

static void
dumproot(Hash_root_t* root, int flags)
{
	Hash_table_t*	tab;

	sfprintf(sfstderr, "    root\n");
	sfprintf(sfstderr, "        address:     0x%08lx\n", (unsigned long)root);
	sfprintf(sfstderr, "        flags:       ");
	dumpflags(root->flags);
	if (root->namesize) sfprintf(sfstderr, "namesize=%d ", root->namesize);
	if (root->local->alloc) sfprintf(sfstderr, "alloc=0x%08lx ", (unsigned long)root->local->alloc);
	if (root->local->compare) sfprintf(sfstderr, "compare=0x%08lx ", (unsigned long)root->local->compare);
	if (root->local->free) sfprintf(sfstderr, "free=0x%08lx ", (unsigned long)root->local->free);
	if (root->local->hash) sfprintf(sfstderr, "hash=0x%08lx ", (unsigned long)root->local->hash);
	if (root->local->region) sfprintf(sfstderr, "region=0x%08lx handle=0x%08lx ", (unsigned long)root->local->region, (unsigned long)root->local->handle);
	sfprintf(sfstderr, "\n");
	sfprintf(sfstderr, "        meanchain:   %d\n", root->meanchain);
	sfprintf(sfstderr, "        accesses:    %d\n", root->accesses);
	sfprintf(sfstderr, "        collisions:  %d\n", root->collisions);
	sfprintf(sfstderr, "\n");
	for (tab = root->references; tab; tab = tab->next)
		dumptable(tab, flags);
}

/*
 * dump hash table accounting info
 * if tab is 0 then dump all tables in hash_info.list
 * flags are HASH_* flags that specify optional dump info
 */

void
hashdump(Hash_table_t* tab, int flags)
{
	Hash_root_t*	root;

	sfprintf(sfstderr, "\nhash table information:\n\n");
	if (tab) dumproot(tab->root, flags);
	else for (root = hash_info.list; root; root = root->next)
		dumproot(root, flags);
	sfsync(sfstderr);
}
