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.ZC.
    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;
}