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.
                //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 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 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 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 ::/System/DolDoc/DocInit.CC.
        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;
}