//
//  File:           hzSMAR.cpp
//
//  Purpose:        Implimentation of hzSMAR - Segmented Memory Allocation (and Management) Regime.
//
//  Legal Notice:   This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
//  The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
//  Software Foundation, either version 3 of the License, or any later version.
//
//  The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
//  A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//  Please note: SSR functions are not intended for direct use in applications
#include <iostream>
#include <stdarg.h>
#include "hzBasedefs.h"
#include "hzTextproc.h"
#include "hzProcess.h"
#include "hzSMAR.h"
/*
**  ssrItem functions
*/
hzSMAR::hzSMAR  (uint32_t nCode)
{
    m_pTopBlock = 0 ;
    m_nAllocNew = m_nAllocOld = m_nReleases = 0 ;
    m_nLive = 0 ;
    m_nCode = nCode ;
    memset(m_arrFL, 0, 4096 * sizeof(uint32_t)) ;
}
uchar*  hzSMAR::Xlate   (uint32_t ssrAddr)
{
    //  Translate a 32-bit unsigned string address, to a pointer to an (encoded) string
    //
    //  Argument:   ssrAddr The address
    //
    //  Returns:    The encoded string OR
    //              NULL if not located
    _hzfunc("hzSMAR::Xlate") ;
    _smarBloc*  pBloc ;     //  Pointer to superblock
    uint32_t*   pSeg ;      //  Data segment
    uint32_t    blocNo ;    //  Offset within vector of blocks
    uint32_t    slotNo ;    //  String slot within block
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    if (!ssrAddr)
    {
        threadLog("SMAR %u: CORRUPT: Should not be called to xlate NULL address\n", m_nCode) ;
        return 0 ;
    }
    slotNo = ssrAddr & SMAR_SLOT_MASK ;
    blocNo = (ssrAddr & SMAR_BLOC_MASK) >> 16 ;
    if (!blocNo)
        return (uchar*) m_Heap[slotNo] ;
    if (blocNo > m_Super.Count())
    {
        threadLog("CORRUPT: Cannot xlate address %u:%u. No such superblock (%u issued)\n", blocNo, slotNo, m_Super.Count()) ;
        return 0 ;
    }
    if (_hzGlobal_MT)
        m_lockSbloc.Lock() ;
        pBloc = m_Super[blocNo] ;
    if (_hzGlobal_MT)
        m_lockSbloc.Unlock() ;
    if (!pBloc)
    {
        threadLog("CORRUPT: No block found for address %u:%u. Total of %u superblocks issued)\n", blocNo, slotNo, m_Super.Count()) ;
        return 0 ;
    }
    pSeg = pBloc->m_Space ;
    pSeg += slotNo ;
    return (uchar*) pSeg ;
}
uint32_t    hzSMAR::Alloc   (uint32_t nSize)
{
    //  Allocate string space. The actual space allocated will be sufficient for a string of length nSize, plus the copy count, length indicator and NULL terminator. 
    //
    //  Argument:   nSize   Required string length (excluding copy count, length indicator and null terminator)
    //
    //  Returns:    Pointer to the required string space. If is a fatal condition if this cannot be obtained.
    _hzfunc("hzSMAR::Alloc") ;
    _smarBloc*  pBloc = 0 ;     //  Pointer to superblock
    void*       pVoid ;         //  Heap addresses only
    uint32_t*   pSlot = 0 ;     //  Pointer to freelist slot
    uint32_t    ssrAddr = 0 ;   //  New slot address
    uint32_t    nUnit ;         //  Number of 4/8-byte units required
    uint32_t    blocNo ;        //  Offset within vector of blocks
    uint32_t    slotNo ;        //  String slot within block
    bool        bNote = false ; //  Set if a new block is created
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    if (!nSize)
        hzexit(E_NODATA, "Cannot allocate a zero size string") ;
    //  Deal with oversized allocations
    if (nSize >= SMAR_OVERSIZE)
    {
        nUnit = (nSize/8) + (nSize%8 ? 1:0) ;
        ssrAddr = m_Heap.Count() + 1 ;
        pVoid = new char[nUnit * 8] ;
        m_Heap.Insert(ssrAddr, pVoid) ;
        return ssrAddr ;
    }
    //  String not oversized
    nUnit = (nSize/4) + (nSize%4 ? 1:0) ;
    if (nUnit < 2)
        nUnit = 2 ;
    //  Is there a free list for the exact size?
    m_lockFlist.Lock() ;
        if (m_arrFL[nUnit])
        {
            ssrAddr = m_arrFL[nUnit] ;
            slotNo = ssrAddr & SMAR_SLOT_MASK ;
            blocNo = (ssrAddr & SMAR_BLOC_MASK) >> 16 ;
            if (blocNo == 0 || blocNo > m_Super.Count())
            {
                hzerr(E_CORRUPT, "SMAR %u: Case 1 ILLEGAL SLot Address %u:%u (units %u)", m_nCode, blocNo, slotNo, nUnit) ;
                m_arrFL[nUnit] = 0 ;
                m_lockFlist.Unlock() ;
                goto top ;
            }
            pBloc = m_Super[blocNo] ;
            if (!pBloc)
            {
                hzerr(E_CORRUPT, "SMAR %u: Case 2 ILLEGAL SLot Address %u:%u (units %u)", m_nCode, blocNo, slotNo, nUnit) ;
                m_arrFL[nUnit] = 0 ;
                m_lockFlist.Unlock() ;
                goto top ;
            }
            pSlot = pBloc->m_Space + slotNo ;
            m_arrFL[nUnit] = pSlot[1] ;
    
            //memset(pSlot, 0, nUnit * sizeof(uint32_t)) ;
            pSlot[0] = pSlot[1] = 0 ;
            m_lockFlist.Unlock() ;
            return ssrAddr ;
        }
    m_lockFlist.Unlock() ;
top:
    //  No free slots so create another superblock
    if (_hzGlobal_MT)
        m_lockSbloc.Lock() ;
    if (!m_pTopBlock || ((m_pTopBlock->m_Usage + nUnit) >= SMAR_BLOC_SPACE))
    {
        //  Assign any remaining free space on the highest block to the small freelist of the size
        //  Then create a new highest block
        m_pTopBlock = pBloc = new _smarBloc() ;
        pBloc->m_blkSelf = m_Super.Count() + 1 ;
        pBloc->m_Usage = 0 ;
        m_Super.Insert(pBloc->m_blkSelf, pBloc) ;
        bNote = true ;
    }
    //  Assign from the superblock free space
    pSlot = m_pTopBlock->m_Space + m_pTopBlock->m_Usage ;
    ssrAddr = ((m_pTopBlock->m_blkSelf) << 16) + m_pTopBlock->m_Usage ;
    m_pTopBlock->m_Usage += nUnit ;
    //memset(pSlot, 0, nUnit * sizeof(uint32_t)) ;
    pSlot[0] = pSlot[1] = 0 ;
    if (_hzGlobal_MT)
        m_lockSbloc.Unlock() ;
    if (bNote)
        printf("SMAR %u: CREATED SUPERBLOCK %u at %p\n", m_nCode, pBloc->m_blkSelf, pBloc) ;
    return ssrAddr ;
}
hzEcode hzSMAR::Free    (uint32_t ssrAddr, uint32_t nSize)
{
    //  Places object in freelist if it is of one of the precribed sizes, otherwise it frees it from the OS managed heap
    //
    //  Arguments:  1)  pMemobj A pointer to what is assumed to be string space to be freed
    //              2)  nSize   The size of the string space
    //
    //  Returns:    None
    _hzfunc("hzSMAR::Free") ;
    _smarBloc*  pBloc ;     //  Pointer to superblock
    uchar*      pItem ;     //  For heap allocations, start of string space
    uint32_t*   pSlot ;     //  Start of string space (as freelist entry)
    uint32_t    nUnit ;     //  Number of 4-byte units in string being freed
    uint32_t    blocNo ;    //  Offset within vector of blocks
    uint32_t    slotNo ;    //  String slot within block
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!ssrAddr)   hzexit(E_CORRUPT, "NULL address") ;
    if (!nSize)     hzexit(E_CORRUPT, "Freeing zero size item %u:%u", blocNo, slotNo) ;
    slotNo = ssrAddr & SMAR_SLOT_MASK ;
    blocNo = (ssrAddr & SMAR_BLOC_MASK) >> 16 ;
    if (blocNo > m_Super.Count())
    {
        threadLog("SMAR %u: ILLEGAL ADDR: Size %u, addr %u:%u\n", m_nCode, nSize, blocNo, slotNo) ;
        return E_CORRUPT ;
    }
    if (!blocNo || nSize >= SMAR_OVERSIZE)
    {
        //  Heap
        pItem = (uchar*) m_Heap[ssrAddr-1] ;
        threadLog("SMAR %u: HEAP FREE: Size %u, addr %u:%u (ptr %p)\n", m_nCode, nSize, blocNo, slotNo, pItem) ;
        delete pItem ;
        m_Heap.Delete(ssrAddr-1) ;
        return E_OK ;
    }
    //  Item is within allocation regime
    pBloc = m_Super[blocNo] ;
    if (!pBloc)
        { threadLog("SMAR %u: CORRUPT: No block found for address %u:%u. Total of %u superblocks issued)\n", m_nCode, blocNo, slotNo, m_Super.Count()) ; return E_MEMORY ; }
    if (pBloc->m_blkSelf != blocNo)
        { threadLog("SMAR %u: CORRUPT: Address %u:%u. Block does not self-address (%u))\n", m_nCode, blocNo, slotNo, pBloc->m_blkSelf) ; return E_MEMORY ; }
    pSlot = pBloc->m_Space ;
    pSlot += slotNo ;
    //  When freeing a slot, the slot must be valid - i.e. the first uint32_t in the slot must be non-zero
    if (!pSlot[0])
        { threadLog("SMAR %u: CORRUPT: Address %u:%u has empty header (already deleted)\n", m_nCode, blocNo, slotNo) ; return E_MEMORY ; }
    //  Set the first part to zero to mark the delete
    pSlot[0] = 0 ;
    //  Don't free slots during shutdown
    if (_hzGlobal_kill)
        return E_OK ;
    //  Calculate units
    nUnit = (nSize/4) + (nSize%4 ? 1:0) ;
    if (nUnit < 2)
        nUnit = 2 ;
    m_lockFlist.Lock() ;
        if (m_arrFL[nUnit])
        {
            //  Check if this value is valid
            blocNo = (m_arrFL[nUnit] & SMAR_BLOC_MASK) >> 16 ;
            if (blocNo > m_Super.Count())
            {
                threadLog("SMAR %u: ILLEGAL OLD ADDR: Size %u (unit %u), blk pop %u, addr %u:%u (called by %s)\n", m_nCode, nSize, nUnit, m_Super.Count(), blocNo, slotNo, _hzCaller()) ;
                m_lockFlist.Unlock() ;
                return E_MEMORY ;
            }
        }
            
        pSlot[1] = m_arrFL[nUnit] ;
        m_arrFL[nUnit] = ssrAddr ;
    m_lockFlist.Unlock() ;
    return E_OK ;
}
void    hzSMAR::Report  (hzChain& report)
{
    _hzfunc("hzSMAR::Report") ;
    uint32_t    nF ;        //  Freelist loop counter
    uint32_t    nAddr ;     //  Addr in FL
    uint32_t    nSize ;     //  Size of free list entries
    //uint32_t  nPop ;      //  Population of free list entries for the given size
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    report.Clear() ;
    report << "SSR Integrity Report\n" ;
    threadLog("case 1\n") ;
    report.Printf("Live Item population: %s\n", FormalNumber(m_nLive)) ;
    threadLog("case 2\n") ;
    report.Printf("Total allocations:    %s\n", FormalNumber(m_nAllocNew + m_nAllocOld)) ;
    threadLog("case 3\n") ;
    report.Printf("New allocs:           %s\n", FormalNumber(m_nAllocNew)) ;
    threadLog("case 4\n") ;
    report.Printf("Reallocations:        %s\n", FormalNumber(m_nAllocOld)) ;
    threadLog("case 5\n") ;
    report.Printf("Total releases (free) %s\n", FormalNumber(m_nReleases)) ;
    threadLog("case 6\n") ;
    report << "Freelists\n" ;
    threadLog("case 7\n") ;
    for (nF = 0 ; nF < 4096 ; nF++)
    {
        if (!m_arrFL[nF])
            continue ;
        nAddr = m_arrFL[nF] ;
        report.Printf("\tUnit %u: %u:%u\n", nF * 4, (nAddr & SMAR_BLOC_MASK) >> 16, nAddr & SMAR_SLOT_MASK) ;
    }
    threadLog("case 8\n") ;
    /*
    for (n = 0 ; n < 32 ; n++)
    {
        if (m_flistItem[n] == 0 && m_flistPopl[n] == 0)
            continue ;
        if (m_flistItem[n] && m_flistPopl[n])
        {
            report.Printf("\tUnit %u: %s\n", n+1, FormalNumber(m_flistPopl[n])) ;
            continue ;
        }
        //Z.Printf("\tError found for n=%u\n", n+1) ;
        report.Printf("\tUnit %u: Error free list addr %u, population %s\n", n+1, m_flistItem[n], FormalNumber(m_flistPopl[n])) ;
    }
    */
    //threadLog("SSR Integrity Report\n") ;
    //threadLog("Live Item population: %u\n", m_nLive) ;
    //threadLog("Total allocations:    %u\n", (m_nAllocNew + m_nAllocOld)) ;
    //threadLog("New allocs:           %u\n", m_nAllocNew) ;
    //threadLog("Reallocations:        %u\n", m_nAllocOld) ;
    //threadLog("Total releases (free) %u\n", m_nReleases) ;
    //threadLog("Freelists\n") ;
    /*
    for (n = 0 ; n < 32 ; n++)
    {
        if (m_flistItem[n] == 0 && m_flistPopl[n] == 0)
            continue ;
        if (m_flistItem[n] && m_flistPopl[n])
        {
            threadLog("\tUnit %u: %u\n", n+1, m_flistPopl[n]) ;
            continue ;
        }
        threadLog("\tError found for n=%u\n", n+1) ;
    }
    */
    //threadLog(Z) ;
}