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