//
// File: hzDomain.cpp
//
// 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.
//
//
// Implimentation of the hzDomain (domain name) class.
//
#include <iostream>
#include <stdarg.h>
#include "hzChars.h"
#include "hzTextproc.h"
#include "hzProcess.h"
#include "hzSSR.h"
/*
** Definitions
*/
struct _dom_space
{
// Internal structure for domain name stringspace. Note there is no constructor as string spaces are allocated by Alloc() from superblocks.
uchar m_copy ; // Copy counter
uchar m_len ; // Length
uchar m_tld ; // Offset to top-level domain (right of the last period)
char m_data[5] ; // First part of data
} ;
#define DOM_FACTOR 4 // This is added to the domain string size to accomodate the copy count and the size of the domain name string plus the null terminator.
/*
** Global constants
*/
global const hzDomain _hz_null_hzDomain ; // Null domain name
/*
** Small String Regime
*/
extern hzSSR g_ssrInet ;
/*
** hzDomain public methods
*/
void hzDomain::Clear (void)
{
// Clear the contents
//
// Arguments: None
// Returns: None
_hzfunc("hzDomain::Clear") ;
_dom_space* thisCtl ; // This domain name space
if (m_addr)
{
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
if (!thisCtl)
hzexit(E_CORRUPT, "Illegal domain address %u:%u", (m_addr&0x7ffff0000)>>16, m_addr&0xffff) ;
if (!thisCtl->m_len)
hzexit(E_CORRUPT, "Zero domain length %u:%u", (m_addr&0x7ffff0000)>>16, m_addr&0xffff) ;
if (_hzGlobal_MT)
{
//__sync_add_and_fetch(&(thisCtl->m_copy), -1) ;
thisCtl->m_copy-- ;
if (!thisCtl->m_copy)
g_ssrInet.Free(m_addr, thisCtl->m_len + DOM_FACTOR) ;
}
else
{
thisCtl->m_copy-- ;
if (!thisCtl->m_copy)
g_ssrInet.Free(m_addr, thisCtl->m_len + DOM_FACTOR) ;
}
m_addr = 0 ;
}
}
uint32_t hzDomain::Length (void) const
{
// Return length in bytes of the whole domain name
_hzfunc("hzDomain::Length") ;
_dom_space* thisCtl ; // This domain name space
if (!m_addr)
return 0 ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return thisCtl->m_len ;
}
uint32_t hzDomain::Copies (void) const
{
// Return number of copies for diagnostics
_hzfunc("hzDomain::Copies") ;
_dom_space* thisCtl ; // This email address space
if (!m_addr)
return 0 ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
if (!thisCtl)
{ threadLog("Ivalid domain space %u:%u\n", (m_addr&0x7fff000)>>16, m_addr&0xffff) ; return 0 ; }
return thisCtl->m_copy ;
}
void hzDomain::_inc_copy (void) const
{
_dom_space* thisCtl ; // This string's control area
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
if (thisCtl->m_copy < 100)
thisCtl->m_copy++ ;
}
void hzDomain::_dec_copy (void) const
{
_dom_space* thisCtl ; // This string's control area
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
if (thisCtl->m_copy == 1)
threadLog("WARNING: DOM _dec_copy would zero copy count\n") ;
else
thisCtl->m_copy-- ;
}
bool hzDomain::valid (void) const
{
_dom_space* pCtrl ; // This string's control area
//g_ssrInet.FLE* pSlot ; // Item cast to g_ssrInet.FLE (to self point on free and to check it is not already free)
pCtrl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
//pSlot = (g_ssrInet.FLE*) pCtrl ;
if (pCtrl->m_copy == 0 || pCtrl->m_copy == 0xff || !pCtrl->m_len || !pCtrl->m_tld) // || pSlot->m_fleSelf == m_addr)
return false ;
return true ;
}
const char* hzDomain::GetDomain (void) const
{
// Return domain name as null terminated string
_hzfunc("hzDomain::GetDomain") ;
_dom_space* thisCtl ; // This domain name space
if (!m_addr)
return 0 ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return thisCtl->m_data ;
}
const char* hzDomain::GetTLD (void) const
{
// Return whole domain name as null terminated string
_hzfunc("hzDomain::GetAddress") ;
_dom_space* thisCtl ; // This domain name space
if (!m_addr)
return 0 ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return thisCtl->m_data + thisCtl->m_tld + 1 ;
}
static uint32_t _ckDomain (uint32_t& lastPeriod, const char* cpStr)
{
// Tests if a supplied string (or text at a hzChain iterator) is of the form of an domain name.
//
// Permitted chars are: [a-z], [A-Z], [0-9] and the period (.) or minus sign (-) if they are not the last character in the string.
//
// Arguments: 1) cpStr The char pointer to be tested
//
// Returns: True If the supplied cstr sets the domain name
// False Otherwise
_hzfunc("IsDomain") ;
const char* i ; // Source string iterator
uint32_t nC ; // Char count
uint32_t nPeriod ; // Confirmed period, last position
if (!cpStr || !cpStr[0])
return false ;
// Count chars beyond @
for (nC = nPeriod = 0, i = cpStr ; *i && nC < 256 ; i++)
{
if (*i == CHAR_PERIOD)
nPeriod = nC ;
if ((*i >= 'a' && *i <= 'z') || (*i >= 'A' && *i <= 'Z') || (*i >= '0' && *i <= '9')
|| (*i == CHAR_MINUS && i[1] > CHAR_SPACE && nC) || (*i == CHAR_PERIOD && i[1] > CHAR_SPACE && nC))
{
nC++ ;
continue ;
}
return 0 ;
}
if (nC < 2 || nC > 253)
return 0 ;
lastPeriod = nPeriod ;
return nC ;
}
hzDomain& hzDomain::operator= (const char* cpStr)
{
// Assign the hzDomain to an domain name held in a character string
//
// Arguments: 1) cpStr A null terminated string assumed to be an domain name
//
// Returns: Reference to this email adress instance in all cases.
//
// Note: This function will record an E_FORMAT error if the supplied cstr did not amount to an domain name
_hzfunc("hzDomain::operator=(cstr)") ;
_dom_space* destCtl ; // This domain name space
const char* i ; // Email iterator
char* j ; // Email iterator
uint32_t nLen = 0 ; // Length of domain name
uint32_t nLP = 0 ; // Offset to last period
Clear() ;
if (!cpStr || !cpStr[0])
return *this ;
nLen = _ckDomain(nLP, cpStr) ;
if (!nLen)
{
hzerr(E_FORMAT, "Cannot assign %s", cpStr) ;
return *this ;
}
m_addr = g_ssrInet.Alloc(nLen + DOM_FACTOR) ;
if (!m_addr)
hzexit(E_MEMORY, "Cannot assign %s", cpStr) ;
destCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
// Assign the value
destCtl->m_copy = 1 ;
destCtl->m_len = nLen & 0xff ;
destCtl->m_tld = nLP & 0xff ;
for (j = destCtl->m_data, i = cpStr ; *i ; *j++ = _tolower(*i++)) ;
*j = 0 ;
return *this ;
}
hzDomain& hzDomain::operator= (const hzString& S)
{
// Assign the hzDomain to an domain name held in a hzString instance
//
// Arguments: 1) S A string assumed to be an domain name
//
// Returns: Reference to this email adress instance in all cases.
//
// Note: This function will record an E_FORMAT error if the supplied cstr did not amount to an domain name
_hzfunc("hzDomain::operator=(hzStr)") ;
Clear() ;
return operator=(*S) ;
}
hzDomain& hzDomain::operator= (const hzChain::Iter& ci)
{
// Determines if the supplied iterator is at the start of a valid domain name and if it is, assignes this as the value to the calling instance.
//
// Arguments: 1) ci Chain iterator
//
// Returns: Reference to this domain name instance. This will be empty if the input did not amount to an domain name
_hzfunc("hzDomain::operator=(chIter)") ;
hzChain::Iter xi ; // External chain iterator
_dom_space* thisCtl ; // This domain name space
char* i ; // For populating string
uint32_t n ; // Counter
uchar nC ; // Chars count
uint32_t nPeriod ; // Position of last period
Clear() ;
if (ci.eof())
return *this ;
// Process the string and set char incidence aggregates
for (nC = nPeriod = 0, xi = ci ; !xi.eof() && *xi > CHAR_SPACE & nC < 256 ; nC++, *xi, xi++)
{
if (*xi == CHAR_PERIOD)
nPeriod = nC ;
if (!((*xi >= 'a' && *xi <= 'z') || (*xi >= 'A' && *xi <= 'Z') || (*xi >= '0' && *xi <= '9')
|| (*xi == CHAR_MINUS && i[1] > CHAR_SPACE && nC) || (*xi == CHAR_PERIOD && i[1] > CHAR_SPACE && nC)))
break ;
}
if (*xi > CHAR_SPACE || !nPeriod || nC > 253 || nC < 2)
{
hzerr(E_FORMAT, "Cannot assign") ;
return *this ;
}
m_addr = g_ssrInet.Alloc(nC + DOM_FACTOR) ;
if (!m_addr)
hzexit(E_MEMORY, "Cannot assign %d bytes", nC + 5) ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
i = (char*) thisCtl->m_data ;
for (xi = ci, n = 0 ; n <= nC ; n++, xi++)
{
i[n] = _tolower(*xi) ;
}
i[n] = 0 ;
thisCtl->m_copy = 1 ;
thisCtl->m_len = nC & 0xff ;
thisCtl->m_tld = nPeriod & 0xff ;
return *this ;
}
hzDomain& hzDomain::operator= (const hzDomain& dom)
{
// Assign the hzDomain to an domain name held in another hzDomain instance
//
// Arguments: 1) E The supplied domain name as a hzEmail instance
//
// Returns: Reference to this hzEmail instance
// It this internal pointer and that of the operand already point to the same space in memory, do nothing
_hzfunc("hzDomain::operator=(hzDomain)") ;
_dom_space* suppCtl ; // Supplied domain name space
if (m_addr == dom.m_addr)
return *this ;
Clear() ;
if (dom.m_addr)
{
//_copyadd() ;
suppCtl = (_dom_space*) g_ssrInet.Xlate(dom.m_addr) ;
if (_hzGlobal_MT)
suppCtl->m_copy++ ;
//__sync_add_and_fetch(&(suppCtl->m_copy), 1) ;
else
suppCtl->m_copy++ ;
m_addr = dom.m_addr ;
}
return *this ;
}
/*
** Compare operators
*/
bool hzDomain::operator== (const hzDomain& E) const
{
// Test for equality between this hzDomain and an operand instance
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is equal to the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator==") ;
_dom_space* thisCtl ; // This domain name space
_dom_space* suppCtl ; // Supplied domain name space
if (m_addr == E.m_addr) return true ;
if (m_addr && E.m_addr == 0) return false ;
if (E.m_addr && m_addr == 0) return false ;
// Compare domains first then LHS
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
suppCtl = (_dom_space*) g_ssrInet.Xlate(E.m_addr) ;
if (!strcmp(thisCtl->m_data, suppCtl->m_data))
return true ;
return false ;
}
bool hzDomain::operator< (const hzDomain& E) const
{
// Return true if this hzDomain instance is lexically less than the operand. Note that comparison is first done on the domain part (the RHS
// of the @) and then done on the address part (the LHS of the @)
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is lexically less than the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator<") ;
int32_t res ;
// Are this and the operand connected?
if (m_addr == E.m_addr) return false ;
if (m_addr && E.m_addr == 0) return false ;
if (E.m_addr && m_addr == 0) return true ;
// Compare domains first
res = strcmp(GetDomain(), E.GetDomain()) ;
if (res < 0)
return true ;
return false ;
}
bool hzDomain::operator<= (const hzDomain& E) const
{
// Return true if this hzDomain instance is lexically less than or equal to the operand. Note that comparison is first done on the domain
// part (the RHS of the @) and then done on the address part (the LHS of the @)
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is lexically less than or equal to the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator<=") ;
int32_t res ; // Comparison result
// Are this and the operand connected?
if (m_addr == E.m_addr) return true ;
if (m_addr && E.m_addr == 0) return false ;
if (E.m_addr && m_addr == 0) return true ;
// Compare domains first
res = strcmp(GetDomain(), E.GetDomain()) ;
if (res > 0)
return false ;
return true ;
}
bool hzDomain::operator> (const hzDomain& E) const
{
// Return true if this hzDomain instance is lexically greater than the operand. Note that comparison is first done on the domain
// part (the RHS of the @) and then done on the address part (the LHS of the @)
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is lexically greater than the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator>") ;
int32_t res ; // Comparison result
// Are this and the operand connected?
if (m_addr == E.m_addr) return false ;
if (m_addr && E.m_addr == 0) return true ;
if (E.m_addr && m_addr == 0) return false ;
// Compare domains first
res = strcmp(GetDomain(), E.GetDomain()) ;
if (res > 0)
return true ;
return false ;
}
bool hzDomain::operator>= (const hzDomain& E) const
{
// Return true if this hzDomain instance is lexically greater than or equal to the operand. Note that comparison is first done on the domain
// part (the RHS of the @) and then done on the address part (the LHS of the @)
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is lexically greater than or equal to the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator>=") ;
int32_t res ; // Comparison result
// Are this and the operand connected?
if (m_addr == E.m_addr) return true ;
if (m_addr && E.m_addr == 0) return true ;
if (E.m_addr && m_addr == 0) return false ;
// Compare domains first
res = strcmp(GetDomain(), E.GetDomain()) ;
if (res < 0)
return false ;
return true ;
}
bool hzDomain::operator== (const hzString& S) const
{
// Test for equality between this hzDomain and an domain name held in a hzString
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is lexically equal to the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator==") ;
_dom_space* thisCtl ; // This domain name space
if (!S && !m_addr) return true ;
if (!S) return false ;
if (!m_addr) return false ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return CstrCompare((char*) thisCtl->m_data, *S) == 0 ? true : false ;
}
bool hzDomain::operator!= (const hzString& S) const
{
// Test for inequality between this hzDomain and an domain name held in a hzString
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is not lexically equal to the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator!=") ;
_dom_space* thisCtl ; // This domain name space
if (!S && !m_addr) return false ;
if (!S) return true ;
if (!m_addr) return true ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return CstrCompare((char*) thisCtl->m_data, *S) == 0 ? false : true ;
}
bool hzDomain::operator== (const char* cpStr) const
{
// Test for equality between this hzDomain and an domain name held in a character string
//
// Arguments: 1) E Test domain name
//
// Returns: True If this addesss is lexically equal to the supplied test domain name
// False Otherwise
_hzfunc("hzDomain::operator==") ;
_dom_space* thisCtl ; // This domain name space
if ((!cpStr || !cpStr[0]) && !m_addr)
return true ;
if (!cpStr || !cpStr[0])
return false ;
if (!m_addr)
return false ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return CstrCompare((char*) thisCtl->m_data, cpStr) == 0 ? true : false ;
}
bool hzDomain::operator!= (const char* cpStr) const
{
// Test for inequality between this hzDomain and an domain name held in a character string
//
// Arguments: 1) E Test domain name
//
// Returns: True If this email addesss is not lexically equal to the supplied
// False If this domain name has the same value as the supplied
_hzfunc("hzDomain::operator!=") ;
_dom_space* thisCtl ; // This domain name space
if ((!cpStr || !cpStr[0]) && !m_addr)
return false ;
if (!cpStr || !cpStr[0])
return true ;
if (!m_addr)
return true ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return CstrCompare((char*) thisCtl->m_data, cpStr) == 0 ? false:true ;
}
const char* hzDomain::operator* (void) const
{
// Returns the URL data (a null terminated string)
//
// Arguments: None
// Returns: Content as null terminated string
_hzfunc("hzDomain::operator*") ;
_dom_space* thisCtl ; // This string's control area
if (!m_addr)
return 0 ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return thisCtl->m_data ;
}
hzDomain::operator const char* (void) const
{
// Returns the string data (a null terminated string)
//
// Arguments: None
// Returns: Content as null terminated string
_hzfunc("hzDomain::operator const char*") ;
_dom_space* thisCtl ; // This string's control area
if (!m_addr)
return 0 ;
thisCtl = (_dom_space*) g_ssrInet.Xlate(m_addr) ;
return thisCtl->m_data ;
}
/*
** Stream operator
*/
std::ostream& operator<< (std::ostream& os, const hzDomain& obj)
{
// Category: Data Output
//
// Friend function to hzDomain class to stream out the domain name.
//
// Arguments: 1) os Reference to output stream
// 2) ema Const reference to an domain name
//
// Returns: Reference to the supplied output stream
_hzfunc("operator<<(ostream,hzDomain)") ;
os << *obj ;
return os ;
}
bool IsDomain (const char* cpStr)
{
// Category: Text Processing
//
// Tests if a supplied string (or text at a hzChain iterator) is of the form of an domain name.
//
// Permitted chars are: [a-z], [A-Z], [0-9] and the period (.) or minus sign (-) if they are not the last character in the string.
//
// Arguments: 1) cpStr The char pointer to be tested
//
// Returns: True If the supplied cstr sets the domain name
// False Otherwise
_hzfunc("IsDomain") ;
const char* i ; // Source string iterator
uint32_t nC = 0 ; // Char count
bool bPeriod = false ; // Confirmed period
if (!cpStr || !cpStr[0])
return false ;
// Count chars beyond @
for (i = cpStr ; *i ; i++)
{
if (*i == CHAR_PERIOD)
bPeriod = true ;
if ((*i >= 'a' && *i <= 'z') || (*i >= 'A' && *i <= 'Z') || (*i >= '0' && *i <= '9')
|| (*i == CHAR_MINUS && i[1] > CHAR_SPACE && nC) || (*i == CHAR_PERIOD && i[1] > CHAR_SPACE && nC))
{
nC++ ;
continue ;
}
return false ;
}
if (bPeriod && nC > 2)
return true ;
return false ;
}
bool AtDomain (hzDomain& dom, uint32_t& nLen, hzChain::Iter& ci)
{
// Category: Text Processing
//
// If the supplied iterator is at the start of a valid domain name, then the supplied domain name instance will be populated with this
// address as the value and the supplied length will be set.
//
// Arguments: 1) dom Reference to an domain name, populated by this function
// 2) nLen Reference to an integer set to the dom length
// 3) ci Input chain iterator
//
// Returns: True If the supplied cstr sets the domain name
// False Otherwise
_hzfunc("AtDomain") ;
dom.Clear() ;
dom = ci ;
if (!dom)
return false ;
nLen = dom.Length() ;
return true ;
}