I64 HashTypeNum(CHash *tmph)
{//Return bit num of hash type, limited to just types.
	if (tmph)
		return Bsf(tmph->type & HTG_TYPE_MASK);

	return -1;
}

I64 HashVal(CHash *tmph)
{//Returns most likely desired value.
	switch [HashTypeNum(tmph)]
	{
		case HTt_EXPORT_SYS_SYM:
			return tmph(CHashExport *)->val;

		case HTt_IMPORT_SYS_SYM:
			return tmph(CHashImport *)->module_base;

		case HTt_DEFINE_STR:
		case HTt_CLASS:
		case HTt_INTERNAL_TYPE:
		case HTt_WORD:
		case HTt_DICT_WORD:
		case HTt_OPCODE:
		case HTt_HELP_FILE:
			return tmph;

		case HTt_GLOBAL_VAR:
			if (tmph(CHashGlobalVar *)->flags & GVF_EXTERN)
				return &tmph(CHashGlobalVar *)->data_addr;
			else
				return tmph(CHashGlobalVar *)->data_addr;

		case HTt_FUN:
			if (Bt(&tmph(CHashFun *)->flags, Cf_EXTERN))
				return tmph;
			else
				return tmph(CHashFun *)->exe_addr;

		case HTt_REG:
			return tmph(CHashReg *)->reg_num | tmph(CHashReg *)->reg_type << 8;

		case HTt_KEYWORD:
		case HTt_ASM_KEYWORD:
		case HTt_MODULE:
		case HTt_FILE:
		case HTt_FRAME_PTR:
			return tmph(CHashGeneric *)->user_data0;

		case -1:			//nobound switch
		case HTt_TYPES_NUM: //nobound switch
		default:
			return 0;
	}
}

CHashTable *HashTableNew(I64 size, CTask *mem_task=NULL)
{//New hash table, power-of-two in size.
	CHashTable *table;

	table = CAlloc(sizeof(CHashTable), mem_task);
	table->body = CAlloc(size << 3, mem_task);
	table->mask = size - 1;

	return table;
}

U0 HashDel(CHashSrcSym *tmph)
{//Free a std ZealOS system hash entry.
	if (!tmph)
		return;
	if (!(tmph->type & HTT_DICT_WORD))
		Free(tmph->str);
	if (tmph->type & HTG_SRC_SYM)
	{
		Free(tmph->src_link);
		Free(tmph->idx);
		Free(tmph->import_name);
		LinkedListDel(tmph->ie_list);
		if (tmph->type & (HTT_FUN | HTT_EXPORT_SYS_SYM))
			Free(tmph->debug_info);
		if (tmph->type & (HTT_FUN | HTT_CLASS))
//Assumes code not on heap, so doesn't Free.
		//$LK,"ClassMemberListDel",A="MN:ClassMemberListDel"$() is an import to the Kernel module
			ClassMemberListDel(tmph);
		else if (tmph->type & HTT_DEFINE_STR)
			Free(tmph(CHashDefineStr *)->data);
		else if (tmph->type & HTT_GLOBAL_VAR)
		{
			if (!(tmph(CHashGlobalVar *)->flags & GVF_ALIAS))
				Free(tmph(CHashGlobalVar *)->data_addr);
			LinkedListDel(tmph(CHashGlobalVar *)->dim.next);
			if (tmph(CHashGlobalVar *)->fun_ptr)
				HashDel(tmph(CHashGlobalVar *)->fun_ptr - tmph(CHashGlobalVar *)->fun_ptr->ptr_stars_count);
		}
	}
	else if (tmph->type & HTT_FILE)
		Free(tmph(CHashGeneric *)->user_data0);
	Free(tmph);
}

U0 HashTableDel(CHashTable *table)
{//Free std system hash table, calling $LK,"HashDel",A="MN:HashDel"$() on entries.
	I64			 i;
	CHashSrcSym	*tmph, *tmph1;

	if (!table)
		return;

	for (i = 0; i <= table->mask; i++)
	{
		tmph = table->body[i];
		while (tmph)
		{
			tmph1 = tmph->next;
			HashDel(tmph);
			tmph = tmph1;
		}
	}
	Free(table->body);
	Free(table);
}

I64 HashTablePurge(CHashTable *table)
{//Eliminate ExportSysSyms that have been usurped.
	I64			 i, res = 0;
	CHashSrcSym	*tmph, *tmph1, *tmph2;

	if (!table)
		return 0;

	PUSHFD
	CLI 	 //Precaution
	for (i = 0; i <= table->mask; i++)
	{
		tmph = table->body[i];
		while (tmph)
		{
			tmph1 = tmph->next; //We delete only older ones
			if (tmph->type & (HTT_FUN | HTT_GLOBAL_VAR))
			{
				tmph2 = tmph->next; //Older always later in chain
				while (tmph2)
				{
					if ((tmph2->type & HTT_EXPORT_SYS_SYM ||
						tmph2->type & HTG_TYPE_MASK == HTT_INVALID) &&
						!StrCompare(tmph2->str, tmph->str))
					{
						if (tmph2->type & HTG_TYPE_MASK == HTT_INVALID)
							tmph2->type = HTT_KEYWORD;//Won't delete HTT_INVALID
						HashRemDel(tmph2, table);
						res++;
						break;
					}
					tmph2 = tmph2->next;
				}
			}
			tmph = tmph1;
		}
	}
	POPFD

	return res;
}

CHashGeneric *HashGenericAdd(U8 *name, I64 type, I64 u0=0, I64 u1=0, I64 u2=0, CTask *task=NULL)
{//Add any type to task hash_table, 3 user_data values.
	if (!task)
		task = Fs;
	CHashGeneric *res = CAlloc(sizeof(CHashGeneric), task);

	res->type		= type;
	res->user_data0	= u0;
	res->user_data1	= u1;
	res->user_data2	= u2;
	res->str		= StrNew(name, task);
	HashAdd(res, task->hash_table);

	return res;
}

U0 HashSrcFileSet(CCompCtrl *cc, CHashSrcSym *h, I64 line_num_offset=0)
{//Set $LK,"CHashSrcSym",A="MN:CHashSrcSym"$ link and help_index by cur cc pos.
	CLexFile	*tmpf = cc->lex_include_stack;
	I64			 line_num = tmpf->line_num + line_num_offset;

	if (line_num < 1)
		line_num = 1;
	Free(h->src_link);
	h->src_link = MStrPrint("FL:%s,%d", tmpf->full_name, line_num);
	if (Bt(&cc->opts, OPTf_KEEP_PRIVATE))
		h->type |= HTF_PRIVATE;
	Free(h->idx);
	if (cc->cur_help_idx && *cc->cur_help_idx)
		h->idx = StrNew(cc->cur_help_idx);
	else
		h->idx = NULL;
}

CHashGeneric *HashPublic(U8 *st, I64 mask, Bool val=TRUE)
{//Mark a hash entry as public and $LK,"HashSrcFileSet",A="MN:HashSrcFileSet"$().
	CHashGeneric *res;

	if (res = HashFind(st, Fs->hash_table, mask))
	{
		if (val)
			res->type |= HTF_PUBLIC;
		else
			res->type &= ~HTF_PUBLIC;
		if (res->type & HTG_SRC_SYM)
			HashSrcFileSet(Fs->last_cc, res);
		return res;
	}
	else
		return NULL;
}

I64 HashListAdd(U8 *list, I64 type, CHashTable *table)
{//Add a list to a hash table.
	I64				 i = 0;
	CHashGeneric	*tmph;

	if (list)
	{
		while (*list)
		{
			if (*list == '@')
				list++;
			else
				i++;
			tmph = CAlloc(sizeof(CHashGeneric));
			tmph->user_data0	= i - 1;
			tmph->str			= StrNew(list);
			tmph->type			= type;
			HashAdd(tmph, table);
			while (*list++);
		}
	}

	return i;
}

I64 HashDefineListAdd(U8 *dname, I64 type, CHashTable *table)
{//Add define list to a hash table. See $LK,"::/System/DolDoc/DocInit.CC",A="FF:::/System/DolDoc/DocInit.CC,HashDefineListAdd"$.
	CHashDefineStr *tmph;

	if (tmph = HashFind(dname, Fs->hash_table, HTT_DEFINE_STR))
		return HashListAdd(tmph->data, type, table);
	else
		return 0;
}

I64 FramePtr(U8 *name, CTask *task=NULL)
{//Find entry in task->hash_table, Return user_data.
	CHashGeneric *tmph;

	if (!task)
		task = Fs;
	if (tmph = HashFind(name, task->hash_table, HTT_FRAME_PTR))
		return tmph->user_data0;
	else
		return 0;
}

CHashGeneric *FramePtrAdd(U8 *name, I64 val=0, CTask *task=NULL)
{//Add named value to task->hash_table.
	return HashGenericAdd(name, HTT_FRAME_PTR, val, 0, 0, task);
}

I64 FramePtrSet(U8 *name, I64 val, CTask *task=NULL)
{//Find hash entry in task->hash_table. Change user_data0.
	CHashGeneric *tmph;

	if (!task)
		task = Fs;
	if (tmph = HashFind(name, task->hash_table, HTT_FRAME_PTR))
		return LXchgI64(&tmph->user_data0, val);
	else
		return 0;
}

I64 FramePtrDel(U8 *name, CTask *task=NULL)
{//Remove entry and delete.
	CHashGeneric	*tmph;
	I64				 res = 0;

	if (!task)
		task = Fs;
	if (tmph = HashFind(name, task->hash_table, HTT_FRAME_PTR))
	{
		res = tmph->user_data0;
		HashRemDel(tmph, task->hash_table);
	}

	return res;
}