//
//  File:           hzSSR.cpp
//  Purpose:        Implimentation of the Small String 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 "hzSSR.h"
//static    _ssrRegime* s_pSSR ;    //  The one and only string regime
uchar*  hzSSR::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("hzSSR::Xlate") ;
    _ssrBloc*   pBloc ;     //  Pointer to superblock
    uint64_t*   pSeg ;      //  Data segment
    uint32_t    blkNo ;     //  Offset within vector of blocks
    uint32_t    slotNo ;    //  String slot within block
    if (!ssrAddr)
        return 0 ;
    slotNo = ssrAddr & 0xffff ;
    blkNo = (ssrAddr & SSR_BLOC_MASK) >> 16 ;
    if (blkNo == 0 || blkNo > m_Super.Count())
        { threadLog("CORRUPT: Cannot xlate address %u:%u. No such superblock (%u issued)\n", blkNo, slotNo, m_Super.Count()) ; return 0 ; }
    pBloc = m_Super[blkNo-1] ;
    if (!pBloc)
        { threadLog("CORRUPT: No block found for address %u:%u. Total of %u superblocks issued)\n", blkNo, slotNo, m_Super.Count()) ; return 0 ; }
    pSeg = pBloc->m_Space + slotNo ;
    return (uchar*) pSeg ;
}
uint32_t    hzSSR::Alloc    (uint32_t nSize)
{
    //  Allocate memory from the memory regime if required size is 64 bytes or less and from the heap otherwise. Under the regime, allocation is
    //  always from a freelist of segments of either 16,24,32,48 or 64 bytes. If the appropriate freelist is empty, a new block is allocated and
    //  divided into multiple segments.
    //
    //  Argument:   nSize   Total size required (including sting space meta data and null terminator)
    //
    //  Returns:    Pointer to the required string space. If is a fatal condition if this cannot be obtained.
    _hzfunc("hzSSR::Alloc") ;
    uint32_t    ssrAddr = 0 ;   //  Address of string (block + slot)
    uint32_t    nUnit ;         //  Number of 8-byte units required
    //  Check required size
    if (nSize >= 256)
    {
        threadLog("SIZE VIOLATION: Max size is 256 bytes\n") ;
        return 0 ;
    }
    if (!nSize)
        return 0 ;
    nUnit = (nSize/8) + (nSize%8 ? 1:0) ;
    _ssrBloc*   pBloc = 0 ;     //  Pointer to superblock
    _ssrFLE*    pSlot = 0 ;     //  Pointer to freelist slot
    uint64_t*   pSeg = 0 ;      //  Pointer to segment
    //  Small string space (8 to 256 bytes)
    if (m_flistItem[nUnit-1])
    {
        if (_hzGlobal_MT)
            m_lockItem[nUnit-1].LockWrite() ;
        if (m_flistItem[nUnit-1])
        {
            //  Slot of the exact size is free so grab it
            _hzGlobal_Memstats.m_strSm_u[nUnit-1]++ ;
            _hzGlobal_Memstats.m_strSm_f[nUnit-1]-- ;
            m_flistPopl[nUnit-1]-- ;
            m_nAllocOld++ ;
            ssrAddr = m_flistItem[nUnit-1] ;
            if (!(ssrAddr & SSR_BLOC_MASK))
                hzexit(E_CORRUPT, "Case 1 Illegal String Address %u:%u", (ssrAddr&0x7fff0000)>>16, ssrAddr&0xffff) ;
            pSlot = (_ssrFLE*) Xlate(ssrAddr) ;
            if (!pSlot)
                hzexit(E_CORRUPT, "Illegal freelist (%d) string address %u:%u", nUnit-1, (ssrAddr&0x7fff0000)>>16, ssrAddr&0xffff) ;
            //  Since the slot is being allocated from the free list, it should contain its own address
            //if (pSlot->m_fleSelf != ssrAddr)
            if (pSlot->m_Blank[0] != 0xff)
                threadLog("CORRUPT: %u unit Slot in free list addr (%u:%u), points elsewhere\n", nUnit, (ssrAddr&0xffff0000)>>16, ssrAddr&0xffff) ;
            m_flistItem[nUnit-1] = pSlot->m_fleNext ;
            memset(pSlot, 0, nUnit * 8) ;
        }
        if (_hzGlobal_MT)
            m_lockItem[nUnit-1].Unlock() ;
        if (ssrAddr)
        {
            //pBloc = m_Super[((ssrAddr & SSR_BLOC_MASK)>>16)-1] ;
            //pBloc->m_Alloc[ssrAddr&0xffff] = nSize-1 ;
            return ssrAddr ;
        }
    }
    //  No free slots so allocate from the top superblock
    if (_hzGlobal_MT)
        m_lockSbloc.LockWrite() ;
    //  Allocate new top superblock if needed
    if (!m_pTopBlock || ((m_pTopBlock->m_Usage + nUnit) > SSR_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 _ssrBloc() ;
        memset(pBloc, 0, sizeof(_ssrBloc)) ;
        //m_Super[m_nBloc] = pBloc ;
        m_Super.Add(pBloc) ;
        //m_nBloc++ ;
        //_hzGlobal_Memstats.m_numSblks = m_nBloc ;
        pBloc->m_blkSelf = (m_Super.Count() << 16) ;
        pBloc->m_Usage = 0 ;
        threadLog("CREATED SUPERBLOCK %u at %p\n", pBloc->m_blkSelf >> 16, pBloc) ;
    }
    //  Assign from the superblock free space
    pSeg = m_pTopBlock->m_Space + m_pTopBlock->m_Usage ;
    ssrAddr = m_pTopBlock->m_blkSelf + m_pTopBlock->m_Usage ;
    if (!(ssrAddr & SSR_BLOC_MASK))
        hzexit(E_CORRUPT, "Case 2 Illegal String Address %u:%u", (ssrAddr&0x7fff0000)>>16, ssrAddr&0xffff) ;
    //m_pTopBlock->m_Alloc[ssrAddr&0xffff] = nSize-1 ;
    m_pTopBlock->m_Usage += nUnit ;
    memset(pSeg, 0, nUnit * 8) ;
    _hzGlobal_Memstats.m_strSm_u[nUnit-1]++ ;
    m_nAllocNew++ ;
    if (_hzGlobal_MT)
        m_lockSbloc.Unlock() ;
    return ssrAddr ;
}
hzEcode hzSSR::Free (uint32_t strAddr, 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:    E_CORRUPT   If no string item found at the supplied address or the stated size is zero
    //              E_OK        Operation successful
    _hzfunc("hzSSR::Free") ;
    static  uint32_t    _noCalls = 0 ;
    //_ssrBloc* pBloc ;     //  Pointer to superblock
    _ssrFLE*    pSlot ;     //  Item cast to _ssrFLE (to self point on free and to check it is not already free)
    //uint32_t  blkNo ;     //  Offset within vector of blocks
    //uint32_t  slotNo ;    //  String slot within block
    uint32_t    nUnit ;     //  Number of 8-byte units in string being freed
    //  Increment call count
    _noCalls++ ;
    //  Check for null item
    if (!strAddr || !nSize)
    {
        if (!strAddr)   threadLog("Warning: Cannot delete null item\n") ;
        if (!nSize)     threadLog("Warning: Cannot delete zero size item\n") ;
        return E_OK ;
    }
    pSlot = (_ssrFLE*) Xlate(strAddr) ;
    if (!pSlot)
        return hzerr(E_CORRUPT, "WARNING freeing invalid obj %u:%u of size %u bytes\n", (strAddr & 0xffff0000)>>16, strAddr & 0xffff, nSize) ;
    if (pSlot->m_Blank[0] == 0xff)
        return hzerr(E_CORRUPT, "WARNING already deleted obj %u:%u of size %u bytes\n", (strAddr & 0xffff0000)>>16, strAddr & 0xffff, nSize) ;
    //slotNo = strAddr & 0xffff ;
    //blkNo = (strAddr & SSR_BLOC_MASK) >> 16 ;
    //pBloc = m_Super[blkNo-1] ;
    nUnit = (nSize/8) + (nSize%8 ? 1:0) ;
    //  Check that the stated address is compatible with the stated size
    /*
    if (pBloc->m_Alloc[slotNo] != (nSize-1))
    {
        threadLog("TEST: CORRUPT: Address %u:%u has size %u, not %u\n", blkNo, slotNo, pBloc->m_Alloc[slotNo]+1, nSize) ;
        threadLog("Bad string address %u:%u (nocalls %u)\n", (strAddr&0x7fff0000)>>16, strAddr&0xffff, _noCalls) ;
        threadLog("Str value approx is [%s]\n", pItem->m_data + 3) ;
        return E_CORRUPT ;
    }
    */
    if (_hzGlobal_MT)
        m_lockItem[nUnit-1].LockWrite() ;
    pSlot->m_Blank[0] = 0xff ;
    pSlot->m_fleNext = m_flistItem[nUnit-1] ;
    m_flistItem[nUnit-1] = strAddr ;
    m_flistPopl[nUnit-1]++ ;
    m_nReleases++ ;
    if (_hzGlobal_MT)
        m_lockItem[nUnit-1].Unlock() ;
    return E_OK ;
}
void    hzSSR::Report   (hzChain& report)
{
    _hzfunc("hzSSR::Report") ;
    uint32_t    n ;         //  Loop counter
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    report.Clear() ;
    report << "SSR Integrity Report\n" ;
    report.Printf("Live Item population: %s\n", FormalNumber(m_nLive)) ;
    report.Printf("Total allocations:    %s\n", FormalNumber(m_nAllocNew + m_nAllocOld)) ;
    report.Printf("New allocs:           %s\n", FormalNumber(m_nAllocNew)) ;
    report.Printf("Reallocations:        %s\n", FormalNumber(m_nAllocOld)) ;
    report.Printf("Total releases (free) %s\n", FormalNumber(m_nReleases)) ;
    report << "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])
        {
            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) ;
}