U0 SysBadFree(I64 *ptr)
{
        Panic("Bad Free:", ptr);
}

U0 SysBadMAlloc(I64 *ptr)
{
        Panic("Bad MAlloc:", ptr);
}

U8 *MemPagAlloc(I64 pags, CBlkPool *bp=NULL)
{/*Alloc pags from BlkPool. Don't link to task.
(Linking to a task means they will be freed when the task dies.)
It might give you more than you asked for.

Return: NULL if out of memory.
*/
        CMemBlk *res = NULL, *m;
        I64              i;

        if (!bp)
                bp = sys_code_bp;

        PUSHFD
        CLI
        while (LBts(&bp->locked_flags, BPlf_LOCKED))
                PAUSE
        if (pags < MEM_FREE_PAG_HASH_SIZE)
        {
                if (res = bp->free_pag_hash[pags])
                {
                        bp->free_pag_hash[pags] = res->next;
                        goto at_done;
                }
                i = Bsr(MEM_FREE_PAG_HASH_SIZE) + 1;
        }
        else
        {
                //We'll now round-up to a power of two.
                //There is some overhead on allocations and
                //we wouldn't want to round to the next
                //power of two if a power of two was requested.
                //So we use a little more than a power of two.
                pags -= MEM_EXTRA_HASH2_PAGS;
                i = Bsr(pags) + 1;
                pags = 1 << i + MEM_EXTRA_HASH2_PAGS;
                if (res = bp->free_pag_hash2[i])
                {
                        bp->free_pag_hash2[i] = res->next;
                        goto at_done;
                }
        }
        m = &bp->mem_free_list;
        while (TRUE)
        {
                if (!(res = m->next))
                {
                        //We're probably out of luck, but lets search for a
                        //freed larger size block... and, screw-it, return the whole thing.
                        do
                        {
                                if (res = bp->free_pag_hash2[++i])
                                {
                                        pags = 1 << i + MEM_EXTRA_HASH2_PAGS;
                                        bp->free_pag_hash2[i] = res->next;
                                        goto at_done;
                                }
                        }
                        while (i < 64 - MEM_PAG_BITS - 1);

                        pags = 0;
                        res = NULL; //Out of memory
                        goto at_done2;
                }
                if (res->pags < pags)
                        m = res;
                else
                {
                        if (res->pags == pags)
                        {
                                m->next = res->next;
                                goto at_done;
                        }
                        else
                        {
                                res->pags -= pags;
                                res(U8 *) += res->pags << MEM_PAG_BITS;
                                res->pags = pags;
                                goto at_done;
                        }
                }
        }
at_done:
        bp->used_u8s += res->pags << MEM_PAG_BITS;
at_done2:
        LBtr(&bp->locked_flags, BPlf_LOCKED);
        POPFD

        return res;
}

U0 MemPagFree(CMemBlk *m, CBlkPool *bp=NULL)
{//Return non-task pags to BlkPool.
        I64 i, pags;

        if (m)
        {
                if (!bp)
                        bp = sys_code_bp;

                PUSHFD
                CLI
                while (LBts(&bp->locked_flags, BPlf_LOCKED))
                        PAUSE
                pags = m->pags;
                m->mb_signature = MBS_UNUSED_SIGNATURE_VAL;
                bp->used_u8s -= pags << MEM_PAG_BITS;
                if (pags < MEM_FREE_PAG_HASH_SIZE)
                {
                        m->next = bp->free_pag_hash[pags];
                        bp->free_pag_hash[pags] = m;
                }
                else
                {
//We'll now round-up to a power of two.
                        //There is some overhead on allocations and
                        //we wouldn't want to round to the next
                        //power of two if a power of two was requested.
                        //So we use a little more than a power of two.
                        pags -= MEM_EXTRA_HASH2_PAGS;
                        i = Bsr(pags);
                        m->next = bp->free_pag_hash2[i];
                        bp->free_pag_hash2[i] = m;
                }
                LBtr(&bp->locked_flags, BPlf_LOCKED);
                POPFD
        }
}

CMemBlk *MemPagTaskAlloc(I64 pags, CHeapCtrl *hc)
{/*hc must be locked.  Don't preempt this routine.
Currently, this is only called from MAlloc().
Return: NULL if out of memory.
*/
        CMemBlk         *res;
        I64                      threshold, count, size;
        CMemUnused      *uum, **_uum, **_ptr;

        if (res = MemPagAlloc(pags, hc->bp))
        {
                QueueInsert(res, hc->last_mem_blk);
                res->mb_signature = MBS_USED_SIGNATURE_VAL;
                hc->alloced_u8s += res->pags << MEM_PAG_BITS;

                //Tidy-up free list (Move into heap hash)
                //because if free list gets long, delay causes crash.
                threshold = MEM_HEAP_HASH_SIZE >> 4;
#assert MEM_HEAP_HASH_SIZE >> 4 >= sizeof(U8 *)
                do
                {
                        count = 0;
                        _uum = &hc->malloc_free_list;
                        while (uum = *_uum)
                        {
#assert !offset(CMemUnused.next)
                                size = uum->size;
                                if (size < threshold)
                                {
                                        *_uum = uum->next;
                                        _ptr = (&hc->heap_hash)(U8 *) + size;
                                        uum->next = *_ptr;
                                        *_ptr = uum;
                                }
                                else
                                {
                                        count++;
                                        _uum = uum;
                                }
                        }
                        threshold <<= 1;
                }
                while (count > 8 && threshold <= MEM_HEAP_HASH_SIZE);
        }
        return res;
}

U0 MemPagTaskFree(CMemBlk *m, CHeapCtrl *hc)
{//hc must be locked
        if (m)
        {
                PUSHFD
                CLI
                if (m->mb_signature != MBS_USED_SIGNATURE_VAL)
                        SysBadFree(m);
                else
                {
                        QueueRemove(m);
                        hc->alloced_u8s -= m->pags << MEM_PAG_BITS;
                        MemPagFree(m, hc->bp);
                }
                POPFD
        }
}