//
// 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) ;
}