//
//  File:   hdbObject.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 HadronZoo Proprietary Database Suite
//
#include <iostream>
#include <fstream>
#include <cstdio>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "hzBasedefs.h"
#include "hzString.h"
#include "hzChars.h"
#include "hzChain.h"
#include "hzDate.h"
#include "hzTextproc.h"
#include "hzCodec.h"
#include "hzDocument.h"
#include "hzDirectory.h"
#include "hzDatabase.h"
#include "hzDelta.h"
#include "hzProcess.h"
using namespace std ;
/*
**  Definitions
*/
struct  _val_idx
{
    //  Applicable only to atomic members with multiple values
    uint16_t    m_First ;       //  In the root entry for the member, this is the address of first value in buffer. In subsequent entries it is the position.
    uint16_t    m_Last ;        //  In the root entry for the member, this is the address of last value in buffer. In subsequent entries it is the address of the next entry.
    uint32_t    m_Data ;        //  Data area
} ;
class   _obj_data
{
    //  Support class to hold data object on behalf of hdbObject.
    //
    //  hdbObject is constructed as a wrapper, with the actual object member values held by a tree of one or more _obj_data instances. If the hdbObject native class has no subclass
    //  members, the _obj_data will be singular, otherwise a branch is formed for each subclass member. In each branch there will be an _obj_data instance for each subclass object.
    //  hdbObject has a single _obj_data pointer to the root.
    //
    //  _obj_data has a fixed size core buffer to store member values. Within this members are assigned slots, sized and aligned in accordance with datum size as per the data type.
    //  Values are written to and read from the slots by means of a pointer, cast to the applicable type. In the case of subclass members, which always anticipate multiple subclass
    //  objects, the slot is cast to an array of _obj_data pointers.
    _obj_data   (void)
    {
        m_pArrValues = 0 ;
        m_pClass = 0 ;
        m_ObjId = 0 ;
        m_copy = 0 ;
    }
public:
    //hzMapM    <uint16_t,_atomval>*    m_pArrValues ;      //  This is created if ANY class members expect arrays
    hzXbuf*         m_pArrValues ;  //  This is psudo array, created if ANY class members expect arrays
    const hdbClass* m_pClass ;      //  Applicable data class
    _mut uint32_t   m_ObjId ;       //  Current object id
    _mut int16_t    m_copy ;        //  Copy count
    int16_t         m_Resv ;        //  Reserved
    uchar           m_Core[8] ;     //  Start of core, actual size will fit the data class
    static  _obj_data*  GetInstance (const hdbClass* pClass) ;
    ~_obj_data  (void)
    {
        Clear() ;
    }
    _obj_data&  operator=   (const _obj_data* pOD)
    {
        Clear() ;
        if (m_pArrValues)
            delete m_pArrValues ;
        return *this ;
    }
    uchar*  Litmus      (void) ;
    hzEcode Clear       (void) ;
    hzEcode Integrity   (hzChain& err) ;
    //  Member value SET functions
    hzEcode SetBool         (const hdbMember* pMbr, bool bValue) ;
    hzEcode SetBinAddr      (const hdbMember* pMbr, uint32_t datumId) ;
    hzEcode SetBinary       (const hdbMember* pMbr, const hzChain& Z) ;
    hzEcode SetValue        (const hdbMember* pMbr, const hzAtom& atom) ;
    hzEcode SetObject       (const hdbMember* pMbr, const _obj_data* pOD) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzMD5& md5) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzString& str) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzDomain& dom) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzEmaddr& ema) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzUrl& url) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzXDate& xdate) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzSDate& sdate) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, const hzTime& time) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, uint64_t val) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, int64_t val) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, uint32_t val) ;
    hzEcode _setMbrValue    (const hdbMember* pMbr, int32_t val) ;
    //  Member value GET functions
    hzEcode GetBool     (bool& result,      const hdbMember* pMbr) const ;
    hzEcode GetBinAddr  (uint32_t& datumId, const hdbMember* pMbr) const ;
    hzEcode GetValue    (hzAtom& atom,      const hdbMember* pMbr, uint32_t nOset) const ;
    hzEcode GetMbrValue (hzMD5& md5,        const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzString& str,     const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzDomain& dom,     const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzEmaddr& ema,     const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzUrl& url,        const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzIpaddr& ipa,     const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzXDate& xd,       const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzSDate& sd,       const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (hzTime& time,      const hdbMember* pMbr) const ;
    hzEcode GetMbrValue (uint32_t& val,     const hdbMember* pMbr) const ;
} ;
/*
**  _obj_data Functions
*/
_obj_data*  _obj_data::GetInstance  (const hdbClass* pClass)
{
    //  Constructor is private so this function is the only means of instantiation. This ensures the _obj_data is set to a data class and has a core buffer sized in accordance with
    //  the data class.
    _obj_data*  pOD ;       //  The new object
    uchar*      pSpace ;    //  Space for the new object (16 bytes plus actual length of the core
    if (!pClass)
        hzexit(E_ARGUMENT, "No class supplied") ;
    pSpace = new uchar[pClass->CoreLen() + pClass->MbrCount() + 24] ;
    memset(pSpace, 0, pClass->CoreLen() + pClass->MbrCount() + 24) ;
    pOD = (_obj_data*) pSpace ;
    pOD->m_pClass = pClass ;
    if (pClass->HasArrays())
    {
        //pOD->m_pArrValues = new hzMapM<uint16_t,_atomval> ;
        pOD->m_pArrValues = new hzXbuf ;
        //threadLog("Created value array %p\n", pOD->m_pArrValues) ;
    }
    pOD->m_copy = 1 ;
    return pOD ;
}
uchar*  _obj_data::Litmus   (void)
{
    //  Return array of litmus bytes
    if (m_pClass)
        return m_Core + m_pClass->CoreLen() ;
    return 0 ;
}
hzEcode _obj_data::Clear    (void)
{
    //  Decrement the copy count and when this falls to 0, clear the content.
    //
    //  Note that actual content deletion requires the data class, as this determines whether the _obj_data buffer can contain pointers to further space. Subclass members will need
    //  to call this function recursively.
    //
    //  As a further note, where subclass objects apply, the _obj_data of subclass object are deleted by this function, directly after clearing. However this _obj_data instance is
    //  only cleared. It is not deleted at this stage.
    //
    //  Arguments:  None
    //
    //  Returns:    E_NOINIT    Object not initialized
    //              E_CORRUPT   If there is a mismatch between value amd indicator
    //              E_OK        Operation successful. All atoms at null values
    _hzfunc("_obj_data::Clear") ;
    const hdbMember*    pMbr ;      //  This class member
    hzChain*            pCh ;       //  Cast to chain
    hzString*           pStr ;      //  Cast to string
    hzDomain*           pDom ;      //  Cast to domain
    hzEmaddr*           pEma ;      //  Cast to emaddr
    hzUrl*              pUrl ;      //  Cast to URL
    uchar*              pLitmus ;   //  Litmus bits
    uchar*              pMCS ;      //  Pointer to member core space
    uint32_t            mbrNo ;     //  Member number
    uint32_t            nA ;        //  Counter
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)
        hzexit(E_CORRUPT, "No Class") ;
    if (m_copy < 1)
        return hzerr(E_CORRUPT, "Class %s Object Already cleared", m_pClass->txtName()) ;
    //  Perform an actual delete, but leave the _obj_data to be deleted by hdbObject::Clear()
    pLitmus = Litmus() ;
    //  Clear member data spaces
    for (mbrNo = 0 ; mbrNo < m_pClass->MbrCount() ; mbrNo++)
    {
        pMbr = m_pClass->GetMember(mbrNo) ;
        if (!pLitmus[pMbr->Posn()])
            continue ;
        if (pMbr->Basetype() == BASETYPE_BOOL || pMbr->Basetype() == BASETYPE_TBOOL)
            continue ;
        pMCS = m_Core + pMbr->OsetStd() ;
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            _obj_data*              pSub ;      //  Subclass object
            pArr = (hzArray<_obj_data*>*) pMCS ;
            if (pArr)
            {
                threadLog("Clearing object array %p with %u objects\n", pArr, pArr->Count()) ;
                for (nA = 0 ; nA < pArr->Count() ; nA++)
                {
                    threadLog("Clearing sub %u\n", nA) ;
                    pSub = pArr->operator[](nA) ;
                    if (pSub)
                    {
                        threadLog("Clearing sub %p\n", pSub) ;
                        pSub->Clear() ;
                    }
                }
                pArr->Clear() ;
                threadLog("Cleared object array %p with %u objects\n", pArr, pArr->Count()) ;
            }
            continue ;
        }
        switch  (pMbr->Basetype())
        {
        case BASETYPE_BINARY:
        case BASETYPE_TXTDOC:   pMCS = m_Core + pMbr->OsetAux() ;
                                pCh = (hzChain*) pMCS ;
                                pCh->Clear() ;
                                break ;
        case BASETYPE_STRING:   pStr = (hzString*) pMCS ;   pStr->Clear() ; break ;
        case BASETYPE_DOMAIN:   pDom = (hzDomain*) pMCS ;   pDom->Clear() ; break ;
        case BASETYPE_EMADDR:   pEma = (hzEmaddr*) pMCS ;   pEma->Clear() ; break ;
        case BASETYPE_URL:      pUrl = (hzUrl*) pMCS ;      pUrl->Clear() ; break ;
        }
    }
    //  Empty values array if it exists
    if (m_pArrValues)
    {
        m_pArrValues->Clear() ;
        //delete m_pArrValues ;
        //m_pArrValues = 0 ;
    }
    //  Finally clear the core
    memset(m_Core, 0, m_pClass->CoreLen() + m_pClass->MbrCount()) ;
    return E_OK ;
}
hzEcode _obj_data::Integrity    (hzChain& err)
{
    //  Check integrity
    //
    //  Arguments:  None
    //
    //  Returns:    E_NOINIT    Object not initialized
    //              E_CORRUPT   If there is a mismatch between value amd indicator
    //              E_NODATA    If a member has a min pop of 1 and no value
    //              E_OK        Operation successful. All atoms at null values
    _hzfunc("_obj_data::Integrity") ;
    const hdbMember*    pMbr ;      //  This class member
    uchar*              pMCS ;      //  Member core space
    uchar*              pLitmus ;   //  Litmus bits
    _atomval            av ;        //  Member value
    uint32_t            n ;         //  Counter
    hzEcode             rc ;        //  Return code
    if (!m_pClass)
        return E_NOINIT ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    err.Printf("Class %s, core size %u [", m_pClass->txtName(), m_pClass->CoreLen()) ;
    for (n = 0 ; n < m_pClass->CoreLen() ; n++)
    {
        err.Printf(" %02x", m_Core[n]) ;
    }
    err << " ]\n" ;
    //  Check member data space and litmus values
    for (n = 0 ; n < m_pClass->MbrCount() ; n++)
    {
        pMbr = m_pClass->GetMember(n) ;
        err.Printf("\tMember %u of %u %s (datum size %u oset %u aux %d): ", n+1, m_pClass->MbrCount(), pMbr->txtName(), pMbr->SizeDatum(), pMbr->OsetStd(), pMbr->OsetAux()) ;
        if (pMbr->Basetype() == BASETYPE_BOOL || pMbr->Basetype() == BASETYPE_TBOOL)
        {
            //  Contained in litmus bits so the test does not apply
            err << "Boolean\n" ;
            continue ;
        }
        pMCS = m_Core + pMbr->OsetStd() ;
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            _obj_data*              pSub ;      //  Subclass object
            uint64_t*               pTest ;     //  Test if array exists
            pTest = (uint64_t*) pMCS ;
            if (*pTest == 0)
                continue ;
            pArr = (hzArray<_obj_data*>*) pMCS ;
            for (n = 0 ; n < pArr->Count() ; n++)
            {
                pSub = pArr->operator[](n) ;
                rc = pSub->Integrity(err) ;
            }
            continue ;
        }
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
        {
            pMCS = m_Core + pMbr->OsetStd() ;
            av.m_uInt64 = 0 ;
            memcpy(&av, pMCS, 4) ;
            if (av.m_uInt64)
                err.Printf("Member has datum id\n") ;
            else
            {
                if (pMbr->Compulsory())
                    { rc = E_NODATA ; err.Printf("Member requires datum id\n") ; }
                else
                    err.Printf("Member NULL but OK\n") ;
            }
            continue ;
        }
        //  All other members have core space only
        pMCS = m_Core + pMbr->OsetStd() ;
        av.m_uInt64 = 0 ;
        if (pMbr->SizeDatum() > 8)
            memcpy(&av, pMCS, 8) ;
        else
            memcpy(&av, pMCS, pMbr->SizeDatum()) ;
        if (pLitmus[n] & LITMUS_ERR)
        {
            rc = E_CORRUPT ;
            err.Printf("Bad value\n") ;
            continue ;
        }
        if (av.m_uInt64 == 0)
        {
            if (pMbr->Basetype() >= BASETYPE_INT64 && pMbr->Basetype() <= BASETYPE_UBYTE)
            {
                //  Litmus disambiguates zero so if the litmus is clear, 0 is still legal
                err << "Numeric\n" ;
                continue ;
            }
            if (!pLitmus[n])
                err << "Both NULL\n" ;
            else
            {
                rc = E_CORRUPT ;
                err.Printf("No value but litmus set to %u\n", pLitmus[n]) ;
            }
            if (pMbr->Compulsory())
            {
                rc = E_NODATA ;
                err.Printf("Member requires a value\n") ;
            }
            continue ;
        }
        if (!(pLitmus[n] & LITMUS_SET))
        {
            rc = E_CORRUPT ;
            err.Printf("Value found (%u) but not indicated\n", av.m_uInt64) ;
            continue ;
        }
        err.Printf("Litmus %u value %u\n", pLitmus[n], av.m_uInt64) ;
    }
    if (rc != E_OK)
    {
        threadLog("ERROR:-\n") ;
        threadOut(err) ;
    }
    return rc ;
}
hzEcode _obj_data::SetBool  (const hdbMember* pMbr, bool bValue)
{
    //  Set boolean member within data object. The object must be initialized to a data class, and the supplied member must exist within that class and be of datatype BOOL or TBOOL 
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  bValue  The boolean value the member will be set to
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::SetBool") ;
    uchar*  pLitmus ;   //  Litmus bits
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)      hzexit(E_ARGUMENT, "No member") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    pLitmus = Litmus() ;
        //m_Core + m_pClass->CoreLen() ; 
    if (pMbr->Basetype() == BASETYPE_BOOL)
    {
        pLitmus[pMbr->Posn()] = LITMUS_SET ;
        return E_OK ;
    }
    if (pMbr->Basetype() == BASETYPE_TBOOL)
    {
        pLitmus[pMbr->Posn()] = 0 ;
        pLitmus[pMbr->Posn()] |= LITMUS_SET ;
        if (bValue)
            pLitmus[pMbr->Posn()] |= LITMUS_AUX ;
        return E_OK ;
    }
    return hzerr(E_TYPE, "Member %s is not BOOL or TBOOL", pMbr->txtName()) ;
}
hzEcode _obj_data::SetBinAddr   (const hdbMember* pMbr, uint32_t datumId)
{
    //  Set binary datum address
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  datumId The datum id, as per the asigned binary datum repository
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If no instance or the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::SetBinAddr") ;
    uint32_t*   pInt ;      //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)      hzexit(E_ARGUMENT, "No member") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
        return hzerr(E_TYPE, "%s->%s is not of binary data type", m_pClass->txtName(), pMbr->txtName()) ;
    if (datumId)
    {
        pLitmus = Litmus() ;
        pInt = (uint32_t*) (m_Core + pMbr->OsetStd()) ;
        *pInt = datumId ;
        pLitmus[pMbr->Posn()] |= LITMUS_SET ;
    }
    return E_OK ;
}
hzEcode _obj_data::SetBinary    (const hdbMember* pMbr, const hzChain& Z)
{
    //  Set binary member value within data object.
    //
    //  The object must be initialized to a data class, and the supplied member must exist within that class and be of a BINARY datatype. This function will copy the supplied chain
    //  to the core entry of the applicable member, and sets the member litmus byte to LITMUS_SET to state that the member has a new value. The chain is NOT committed to the target
    //  binary repository at this juncture. This will only happen when the onject as a whole is committed to the applicable data object repository, in either an INSERT or an UPDATE
    //  operation.
    //
    //  Note that BINARY members have an aux core entry in addition to the standard core entry. The aux is used to store the datum id of the chain. This is only set on a FETCH. In
    //  a new object, the aux is 0. In an existing object, the aux will contain the datim id that was previously assigned. On UPDATE, if the chain has changed, the datum id in the
    //  aux is used to DELETE the previous datum.
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  Z       The chain value the member will be set to
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::SetBinary") ;
    hzChain*    pCh ;       //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    uchar*      pUch ;      //  Member space
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)      hzexit(E_ARGUMENT, "No member") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
        return hzerr(E_TYPE, "%s->%s is not of binary data type", m_pClass->txtName(), pMbr->txtName()) ;
    threadLog("Setting member %s to chain of %u bytes\n", pMbr->txtName(), Z.Size()) ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pUch = m_Core + pMbr->OsetAux() ;
    pCh = (hzChain*) pUch ;
    *pCh = Z ;
    if (Z.Size())
        pLitmus[pMbr->Posn()] |= LITMUS_AUX ;
    else
        pLitmus[pMbr->Posn()] |= ~LITMUS_AUX ;
    return E_OK ;
}
hzEcode _obj_data::SetValue (const hdbMember* pMbr, const hzAtom& atom)
{
    //  Set member value within data object. The object must be initialized to a data class, and the supplied member must exist within that class. Note that in the case of BINARY
    //  and TXTDOC members, the expected data type is UINT32, being the binary datum id.
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  atom    The atomic value the member will be set to
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::SetValue") ;
    _atomval    av ;        //  atom data
    uint64_t*   pUI64 ;     //  Cast to uint64 ;
    uint32_t*   pUI32 ;     //  Cast to uint64 ;
    uchar*      pLitmus ;   //  Litmus bits
    uchar*      pMCS ;      //  Pointer to member core space
    //  Object has class?
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    //  Check status
    if (atom.Status() == ATOM_ERROR)
        return hzerr(E_BADVALUE, "Member %s not set", pMbr->txtName()) ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (atom.IsNull())
    {
        threadLog("NULL value - Member %s not set\n", pMbr->txtName()) ;
        pLitmus[pMbr->Posn()] = LITMUS_NULL ;
        return E_OK ;
    }
    if (atom.Type() != pMbr->Basetype())
        return hzerr(E_TYPE, "Type mismatch (Mbr %s %s, atom %s)\n", pMbr->txtName(), Basetype2Txt(pMbr->Basetype()), Basetype2Txt(atom.Type())) ;
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    if (pMbr->Basetype() == BASETYPE_BOOL)
        return E_OK ;
    if (pMbr->Basetype() == BASETYPE_TBOOL)
    {
        if (atom.Bool() == true)
            pLitmus[pMbr->Posn()] |= LITMUS_AUX ;
        return E_OK ;
    }
    pMCS = m_Core + pMbr->OsetStd() ;
    //  Set the value in the object
    if      (pMbr->Basetype() == BASETYPE_STRING)   _setMbrValue(pMbr, atom.Str()) ;
    else if (pMbr->Basetype() == BASETYPE_DOMAIN)   _setMbrValue(pMbr, atom.Domain()) ;
    else if (pMbr->Basetype() == BASETYPE_EMADDR)   _setMbrValue(pMbr, atom.Emaddr()) ;
    else if (pMbr->Basetype() == BASETYPE_URL)      _setMbrValue(pMbr, atom.Url()) ;
    else if (pMbr->Basetype() == BASETYPE_XDATE)    _setMbrValue(pMbr, atom.XDate()) ;
    else if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
    {
        //  Only set address
        uint32_t*   pInt ;
        pUI32 = (uint32_t*) pMCS ;
        *pUI32 = atom.Unt32() ;
    }
    else
    {
        av = atom.Datum() ;
        switch  (pMbr->Basetype())
        {
        //case BASETYPE_XDATE:  memcpy(pMCS, &av, 8) ;          break ;
        //case BASETYPE_SDATE:  memcpy(pMCS, &av.m_uInt32, 4) ; break ;
        //case BASETYPE_TIME:       memcpy(pMCS, &av.m_uInt32, 4) ; break ;
        //case BASETYPE_XDATE:  pUI64 = (uint64_t*) pMCS ;  *pUI64 = av.m_uInt64 ;  break ;
        case BASETYPE_SDATE:    pUI32 = (uint32_t*) pMCS ;  *pUI32 = av.m_uInt32 ;  break ;
        case BASETYPE_TIME:     pUI32 = (uint32_t*) pMCS ;  *pUI32 = av.m_uInt32 ;  break ;
        default:
            memcpy(pMCS, &av, pMbr->SizeDatum()) ;
            break ;
        }
    }
    return E_OK ;
}
hzEcode _obj_data::SetObject    (const hdbMember* pMbr, const _obj_data* pOD)
{
    //  Either add a new subclass data object or return a modified subclass data object, to the supplied host class member. In the case of a new subclass data object, the object id
    //  will be zero. In the case of a modified subclass data object, this must necessarily have been fetched, so will have the object id it was given when originally added.
    //
    //  This object must be initialized to the host data class and the supplied data object must be initialized to subclass introduced by the applicable host class member.
    //
    //  Arguments:  1)  pMbr    The subclass member of this, the host class object
    //              2)  pOD     Pointer to the subclass _obj_data instance to be placed in the host class member space
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetObject") ;
    hzArray <const _obj_data*>* pArr ;      //  Cast of core entry to object array
    uchar*      pMCS ;          //  Member core space
    uchar*      pLitmus ;       //  Litmus bits
    hzEcode     rc ;            //  Return code
    //  Object has class?
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_CLASS)
        return hzerr(E_TYPE, "Member %s is atomic", pMbr->txtName()) ;
    //  Set litmus and core space pointers
    pMCS = m_Core + pMbr->OsetStd() ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    //  Cast the array
    pArr = (hzArray<const _obj_data*>*) pMCS ;
    if (pMbr->OsetStd() % 8)
        hzexit(E_CORRUPT, "8 byte alignement - %u %p\n", pMbr->OsetStd(), pMCS) ;
    
    //  Copy object data into the member data list
    pOD->m_copy++ ;
    rc = pArr->Add(pOD) ;
    if (rc != E_OK)
        return hzerr(rc, "Could not add to array") ;
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    return rc ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzMD5& md5)
{
    //  Set the supplied member with the value of the supplied digest
    //
    //  Arguments:  1)  str     The string to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(md5)") ;
    hzMD5*      pMd5 ;      //  MD5 pointer
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DIGEST)
        return hzerr(E_TYPE, "Member %s is not an MD5 digest", pMbr->txtName()) ;
    //  Cast the member space to an MD5, then set it.
    pMd5 = (hzMD5*) (m_Core + pMbr->OsetStd()) ;
    *pMd5 = md5 ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzString& str)
{
    //  Set value of the supplied member in the object, with the supplied string
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  str     The string value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(str)") ;
    uchar*      pLitmus ;   //  Litmus bits
    hzString*   pStr ;      //  Pointer to string
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_STRING)
        return hzerr(E_TYPE, "Member %s is not a string", pMbr->txtName()) ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    //  Copy string to member entry
    if (pMbr->Singular())
    {
        pStr = (hzString*) (m_Core + pMbr->OsetStd()) ;
        *pStr = str ;
    }
    pLitmus[pMbr->Posn()] = str ? LITMUS_SET : 0 ;
    if (!str)
        return E_OK ;
    //  Ensure string is in the string repository
    _hzGlobal_setStrings.Insert(str) ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzDomain& dom)
{
    //  Set value of the supplied member in the object, with the supplied domain
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The domain name value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(domain)") ;
    uchar*      pLitmus ;   //  Litmus bits
    hzDomain*   pDom ;      //  Member entry cast to domain
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DOMAIN)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    //  Ensure domain is in the domain repository
    _hzGlobal_setDomains.Insert(dom) ;
    //  Copy domain to member entry
    if (pMbr->Singular())
    {
        pDom = (hzDomain*) (m_Core + pMbr->OsetStd()) ;
        *pDom = dom ;
        pLitmus[pMbr->Posn()] = dom ? LITMUS_SET : 0 ;
    }
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzEmaddr& ema)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(emaddr)") ;
    //_atomval  av ;        //  Atomval
    hzEmaddr    newEma ;    //  Make sure the emaddr is in _hzGlobal_setEmaddrs is used
    hzEmaddr*   pEma ;      //  Member entry cast to emaddr
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_EMADDR)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    //  Ensure domain is in the domain repository
    _hzGlobal_setEmaddrs.Insert(ema) ;
    newEma = _hzGlobal_setEmaddrs[ema] ;
    //  Copy emaddr to member entry
    if (pMbr->Singular())
    {
        pEma = (hzEmaddr*) (m_Core + pMbr->OsetStd()) ;
        *pEma = ema ;
        pLitmus[pMbr->Posn()] = ema ? LITMUS_SET : 0 ;
    }
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzUrl& url)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(url)") ;
    //hzUrl newUrl ;    //  Make sure the emaddr is in _hzGlobal_setEmaddrs is used
    hzUrl*  pUrl ;      //  Member entry cast to emaddr
    uchar*  pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_URL)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    //  Ensure URL is in the string repository?
    //  Copy URL to member entry
    if (pMbr->Singular())
    {
        pUrl = (hzUrl*) (m_Core + pMbr->OsetStd()) ;
        *pUrl = url ;
        pLitmus[pMbr->Posn()] = url ? LITMUS_SET : 0 ;
    }
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzXDate& xdate)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(xdate)") ;
    hzXDate*    pXd ;       //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_XDATE)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pXd = (hzXDate*) (m_Core + pMbr->OsetStd()) ;
    *pXd = xdate ;
    pLitmus[pMbr->Posn()] = xdate.IsNull() ? 0 : LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzSDate& sdate)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(sdate)") ;
    hzSDate*    pSd ;       //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_SDATE)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pSd = (hzSDate*) (m_Core + pMbr->OsetStd()) ;
    *pSd = sdate ;
    pLitmus[pMbr->Posn()] = sdate.IsNull() ? 0 : LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, const hzTime& time)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::_setMbrValue(time)") ;
    hzTime*     pTime ;     //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_TIME)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pTime = (hzTime*) (m_Core + pMbr->OsetStd()) ;
    *pTime = time ;
    pLitmus[pMbr->Posn()] = time.IsNull() ? 0 : LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, uint64_t val)
{
    _hzfunc("_obj_data::_setMbrValue(uint64)") ;
    uint64_t*   pUint ;     //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_UINT64)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pUint = (uint64_t*) (m_Core + pMbr->OsetStd()) ;
    *pUint = val ;
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, int64_t val)
{
    _hzfunc("_obj_data::_setMbrValue(int64)") ;
    int64_t*    pInt ;      //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_INT64)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pInt = (int64_t*) (m_Core + pMbr->OsetStd()) ;
    *pInt = val ;
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, uint32_t val)
{
    _hzfunc("_obj_data::_setMbrValue(uint32)") ;
    uint32_t*   pUint ;     //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_UINT32)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pUint = (uint32_t*) (m_Core + pMbr->OsetStd()) ;
    *pUint = val ;
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    return E_OK ;
}
hzEcode _obj_data::_setMbrValue (const hdbMember* pMbr, int32_t val)
{
    _hzfunc("_obj_data::_setMbrValue(int32)") ;
    int32_t*    pInt ;      //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_INT32)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pInt = (int32_t*) (m_Core + pMbr->OsetStd()) ;
    *pInt = val ;
    pLitmus[pMbr->Posn()] = LITMUS_SET ;
    return E_OK ;
}
/*
**  _obj_data Get Functions
*/
hzEcode _obj_data::GetBool  (bool& result, const hdbMember* pMbr) const
{
    //  Get boolean member within data object. The object must be initialized to a data class, and the supplied member must exist within that class and be of datatype BOOL or TBOOL 
    //
    //  Arguments:  1)  result  The bool result
    //              2)  pMbr    Member pointer
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_NODATA    If the member is of type TBOOL and has not been set
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetBool") ;
    const uchar*    pLitmus ;   //  Litmus bits
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (pMbr->Basetype() == BASETYPE_BOOL)
    {
        result = pLitmus[pMbr->Posn()] & LITMUS_SET ? true : false ;
        return E_OK ;
    }
    if (pMbr->Basetype() == BASETYPE_TBOOL)
    {
        if (pLitmus[pMbr->Posn()] & LITMUS_SET)
            result = pLitmus[pMbr->Posn()] & LITMUS_AUX ? true : false ;
        else
            return E_NODATA ;
    }
    result = false ;
    return hzerr(E_TYPE, "Member %s is not BOOL or TBOOL", pMbr->txtName()) ;
}
hzEcode _obj_data::GetBinAddr   (uint32_t& datumId, const hdbMember* pMbr) const
{
    //  Get binary datum address
    //
    //  Arguments:  1)  datumId The datum id, as per the asigned binary datum repository
    //              2)  pMbr    The member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If no instance or the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::SetBinAddr") ;
    uint32_t*       pInt ;      //  Internal cast
    const uchar*    pLitmus ;   //  Litmus bits
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)      hzexit(E_ARGUMENT, "No member") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
        return hzerr(E_TYPE, "%s->%s is not of binary data type", m_pClass->txtName(), pMbr->txtName()) ;
    datumId = 0 ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pInt = (uint32_t*) (m_Core + pMbr->OsetStd()) ;
        datumId = *pInt ;
    }
    return E_OK ;
}
hzEcode _obj_data::GetValue (hzAtom& atom, const hdbMember* pMbr, uint32_t nOset) const
{
    //  Set the supplied atom with the value of the supplied member. Note that in the case of BINARY/TXTDOC members, the atom will be set with the binary datum id, not the binary
    //  datum itself.
    //
    //  Arguments:  1)  atom    The atom to be set
    //              2)  pMbr    Pointer to the data class member
    //              3)  nOset   Default 0, applicable only if the member is an array
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_RANGE     If the requested element (nOset) exceeds the number of member values
    //              E_OK        If the object class member has been set to the supplied atom value.
    
    _hzfunc("_obj_data::GetValue(atom)") ;
    _atomval        av ;        //  Value from atom
    const uchar*    pLitmus ;   //  Litmus bits
    const uchar*    pMCS ;      //  Pointer to member core space
    atom.Clear() ;
    //  Object has class?
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    //  For BOOL and TBOOL members, values are read from the litmus bits
    if (pMbr->Basetype() == BASETYPE_CLASS)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (nOset > 0)
    {
        if (pMbr->Singular())
            return E_RANGE ;
    }
    if (pMbr->Basetype() == BASETYPE_BOOL)
    {
        if (pLitmus[pMbr->Posn()] & LITMUS_SET)
            atom = true ;
        else
            atom = false ;
        return E_OK ;
    }
    if (pMbr->Basetype() == BASETYPE_TBOOL)
    {
        if (pLitmus[pMbr->Posn()] & LITMUS_SET)
        {
            if (pLitmus[pMbr->Posn()] & LITMUS_AUX)
                atom = true ;
            else
                atom = false ;
        }
        
        //  atom = (bool) m_pLitmus[pMbr->Posn()] & 0x02 ? true : false ;
        return E_OK ;
    }
    if (!(pLitmus[pMbr->Posn()] & LITMUS_SET))
        return E_NOTFOUND ;
    //  Get cast value
    pMCS = m_Core + pMbr->OsetStd() ;
    if (pMbr->Basetype() == BASETYPE_STRING)
    {
        hzString*   pStr ;
        pStr = (hzString*) pMCS ;
        atom = *pStr ;
    }
    else if (pMbr->Basetype() == BASETYPE_DOMAIN)
    {
        hzDomain*   pDom ;
        pDom = (hzDomain*) pMCS ;
        atom = *pDom ;
    }
    else if (pMbr->Basetype() == BASETYPE_EMADDR)
    {
        hzEmaddr*   pEma ;
        pEma = (hzEmaddr*) pMCS ;
        atom = *pEma ;
    }
    else if (pMbr->Basetype() == BASETYPE_URL)
    {
        hzUrl*  pUrl ;
        pUrl = (hzUrl*) pMCS ;
        atom = *pUrl ;
    }
    else if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
    {
        uint32_t*   pUint ;
        pUint = (uint32_t*) pMCS ;
        atom = *pUint ;
    }
    else if (pMbr->Basetype() == BASETYPE_DIGEST)
    {
        hzMD5*  pDigest ;
        pDigest = (hzMD5*) pMCS ;
        atom = *pDigest ;
    }
    else
    {
        switch  (pMbr->Basetype())
        {
        case BASETYPE_XDATE:    { hzXDate* pXd ; pXd = (hzXDate*) pMCS ; atom = *pXd ; }    break ;
        case BASETYPE_SDATE:    atom = (hzSDate*) pMCS ;    break ;
        case BASETYPE_TIME:     atom = (hzTime*) pMCS ;     break ;
        default:
            av.m_uInt64 = 0 ;
            memcpy(&av, pMCS, pMbr->SizeDatum()) ;
            atom.SetValue(pMbr->Basetype(), av) ;
        }
    }
    return E_OK ;
}
#if 0
hzEcode _obj_data::GetObject    (_obj_data& obj, const hdbMember* pMbr, uint32_t nOset) const
{
    //  Fetches a subclass object from the applicable member of this (host class) object. The supplied hdbObject must have been previously initialized to the subclass. The supplied
    //  hdbObject is first cleared, then populated by the object in question (assuming it exists). The operation is a simple copy of the _obj_data held in the array for the member,
    //  to the supplied object. This will increment the copy count.
    //
    //  Arguments:  1)  obj     The subclass object container
    //              2)  pMbr    The applicable host class member
    //              3)  nOset   Position in array of subclass objects held by the host class member
    //
    _hzfunc("_obj_data::GetObject") ;
    hzArray <_obj_data*>*   pArr ;      //  Cast of core entry to object array
    _obj_data*      pSub ;      //  Subclass object pointer
    const uchar*    pMCS ;      //  Member core space
    const uchar*    pLitmus ;   //  Litmus bits
    hzEcode         rc ;        //  Return code
    //  Object has class?
    if (!this)  hzexit(E_CORRUPT, "No instance") ;
    if (!pMbr)  return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_CLASS)
        return hzerr(E_TYPE, "Member %s is atomic", pMbr->txtName()) ;
    //  Set litmus and core space pointers
    pMCS = m_Core + pMbr->OsetStd() ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        //  Cast the array
        pArr = (hzArray<_obj_data*>*) pMCS ;
        if (nOset >= pArr->Count())
            return E_NOTFOUND ;
        pSub = pArr->operator[](nOset) ;
        obj.Clear() ;
        //  ???
        memcpy(obj.m_pCore, pSub->m_pCore, m_pClass->CoreLen()) ; 
        //obj.m_pRoot->m_copy++ ;
        obj.m_copy++ ;
        //*obj.m_pRoot = pSub ;
    }
    return E_OK ;
}
#endif
hzEcode _obj_data::GetMbrValue  (hzMD5& md5, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  str     The string to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(md5)") ;
    //_atomval  av ;    //  Member value
    hzMD5*      pMd5 ;  //  MD5 pointer
    //  Clear value and check object set-up
    md5.Clear() ;
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DIGEST)
        return hzerr(E_TYPE, "Member %s is not an MD5 digest", pMbr->txtName()) ;
    //  Fetch the value from the member space.
    pMd5 = (hzMD5*) (m_Core + pMbr->OsetStd()) ;
    md5 = *pMd5 ;
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzString& str, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  str     The string to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(str)") ;
    hzString*   pStr ;  //  Member entry cast to string
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_STRING)
        return hzerr(E_TYPE, "Member %s is not a string", pMbr->txtName()) ;
    //  Fetch the value from the member space.
    pStr = (hzString*) (m_Core + pMbr->OsetStd()) ;
    str = *pStr ;
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzDomain& dom, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  dom     The domain name to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(dom)") ;
    _atomval    av ;    //  Member value
    hzDomain*   pDom ;  //  Member entry cast to domain
    dom.Clear() ;
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DOMAIN)
        return E_TYPE ;
    //  Fetch the value from the member space.
    pDom = (hzDomain*) (m_Core + pMbr->OsetStd()) ;
    dom = *pDom ;
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzEmaddr& ema, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  ema     The email address to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(ema)") ;
    //_atomval  av ;        //  Member value
    hzEmaddr*   pEma ;      //  Member entry cast to emaddr
    //  Clear email address
    ema.Clear() ;
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_EMADDR)
        return E_TYPE ;
    //  Fetch the value from the member space.
    pEma = (hzEmaddr*) (m_Core + pMbr->OsetStd()) ;
    ema = *pEma ;
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzUrl& url, const hdbMember* pMbr) const
{
    //  Set the supplied URL with the value of the supplied member
    //
    //  Arguments:  1)  url     The email address to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(ema)") ;
    hzUrl*  pUrl ;      //  Member entry cast to hzUrl
    //  Clear email address
    url.Clear() ;
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_URL)
        return E_TYPE ;
    //  Fetch the value from the member space.
    pUrl = (hzUrl*) (m_Core + pMbr->OsetStd()) ;
    url = *pUrl ;
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzIpaddr& ipa, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  ipa     The IP address to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(ipa)") ;
    const uchar*    pLitmus ;   //  Litmus bits
    const uchar*    pMCS ;      //  Pointer to member core space
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_EMADDR)
        return E_TYPE ;
    //  Fetch the value from the member space.
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    pMCS = m_Core + pMbr->OsetStd() ;
    ipa.Clear() ;
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        memcpy(&ipa, pMCS, 4) ;
    }
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzXDate& xdate, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(xdate)") ;
    const uchar*    pLitmus ;   //  Litmus bits
    hzXDate*        pXd ;       //  Internal cast
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_XDATE)
        return E_TYPE ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    xdate.Clear() ;
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pXd = (hzXDate*) (m_Core + pMbr->OsetStd()) ;
        xdate = *pXd ;
    }
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzSDate& sdate, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(sdate)") ;
    hzSDate*        pSd ;       //  Internal cast
    const uchar*    pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_SDATE)
        return E_TYPE ;
    sdate.Clear() ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pSd = (hzSDate*) (m_Core + pMbr->OsetStd()) ;
        sdate = *pSd ;
    }
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (hzTime& time, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(time)") ;
    hzTime*         pTime ;     //  Internal cast
    const uchar*    pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_SDATE)
        return E_TYPE ;
    time.Clear() ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pTime = (hzTime*) (m_Core + pMbr->OsetStd()) ;
        time = *pTime ;
    }
    return E_OK ;
}
hzEcode _obj_data::GetMbrValue  (uint32_t& val, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("_obj_data::GetMbrValue(uint32)") ;
    uint32_t*       pUI ;       //  Internal cast
    const uchar*    pLitmus ;   //  Litmus bits
    if (!pMbr)
        return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_UINT32)
        return E_TYPE ;
    val = 0 ;
    pLitmus = m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pUI = (uint32_t*) (m_Core + pMbr->OsetStd()) ;
        val = *pUI ;
    }
    return E_OK ;
}
/*
**  hdbObject Functions
*/
hdbObject::hdbObject    (void)
{
    m_pClass = 0 ;
    m_ReposId = m_ClassId = 0 ;
    m_pRoot = 0 ;
}
hdbObject::~hdbObject   (void)
{
    Clear() ;
    delete m_pRoot ;
}
hzEcode hdbObject::SetName  (const hzString& objKey)
{
    //  Set single object container name if required.
    //
    //  Argument:   objClass    The data class
    if (m_Key)
        return E_DUPLICATE ;
    m_Key = objKey ;
    return E_OK ;
}
void    hdbObject::SetObjId (uint32_t nObjId) const
{
    //  Set the object id. This is upon FETCH of a data object from a repository.
    //
    //  Argument:   nObjId  The Object ID
    //  Returns:    None
    if (!m_pClass)
        hzexit(E_NOINIT, "Object not initialized - Cannot set ID") ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    m_pRoot->m_ObjId = nObjId ;
}
uint32_t    hdbObject::GetObjId (void) const
{
    //  Get the object id.
    //
    //  Argument:   nObjId  The Object ID
    //  Returns:    The object id
    if (!m_pRoot)
        return 0 ;
    return m_pRoot->m_ObjId ;
}
hzEcode hdbObject::SetRepos (const hdbObjRepos* pRepos)
{
    //  Set single object container name if required.
    //
    //  Argument:   pRepos  The associated repository
    if (m_pRepos)
    {
        if (m_pRepos == pRepos)
            return E_OK ;
        return E_TYPE ;
    }
    m_pRepos = pRepos ;
    return E_OK ;
}
hzEcode hdbObject::Integrity    (void)
{
    //  Check integrity
    //
    //  Arguments:  None
    //
    //  Returns:    E_NOINIT    Object not initialized
    //              E_CORRUPT   If there is a mismatch between value amd indicator
    //              E_NODATA    If a member has a min pop of 1 and no value
    //              E_OK        Operation successful. All atoms at null values
    _hzfunc("hdbObject::Integrity") ;
    hzChain     err ;       //  Error report (discarded if no error)
    if (!m_pClass)  return hzerr(E_NOINIT, "No class") ;
    if (!m_pRoot)   return hzerr(E_NOINIT, "No root") ;
    return m_pRoot->Integrity(err) ;
}
hzEcode hdbObject::Clear    (void)
{
    //  Clear all hdbObject values
    //
    //  Arguments:  None
    //
    //  Returns:    E_NOINIT    Object not initialized
    //              E_CORRUPT   If there is a mismatch between value amd indicator
    //              E_OK        Operation successful. All atoms at null values
    _hzfunc("hdbObject::Clear") ;
    if (!m_pClass)
        return E_NOINIT ;
    if (!m_pRoot)
        return E_OK ;
    if (m_pRoot->m_copy > 1)
    {
        m_pRoot->m_copy-- ;
        m_pRoot = 0 ;
        return E_OK ;
    }
    m_pRoot->Clear() ;
    delete m_pRoot ;
    m_pRoot = 0 ;
    return E_OK ;
}
hzEcode hdbObject::Init (const hdbClass* pClass)
{
    //  Initialize a single object container
    //
    //  Single object containers are assigned a data class (the operational native), and a name. The name was added so that hdbObject instances in Dissemino user sessions could be
    //  referenced by form handler commands.
    //
    //  Argument:   objClass    The data class
    //
    //  Returns:    E_CORRUPT   If called without a hdbObject instance
    //              E_ARGUMENT  If the class is not supplied
    //              E_NOINIT    If the supplied class is not initialized
    //              E_DUPLICATE If this hdbObject is already initialized
    //              E_OK        If the hdbObject is initialized to the class
    _hzfunc("hdbObject::Init") ;
    const hdbMember*    pMbr ;      //  This class member
    uint32_t            nMbr ;      //  Member iterator
    hzEcode             rc = E_OK ; //  Return code
    //  Check class and class init state
    if (!this)              hzexit(E_CORRUPT, "No instance") ;
    if (!pClass)            return hzerr(E_ARGUMENT, "No data class supplied") ;
    if (!pClass->IsInit())  return hzerr(E_NOINIT, "Data class is not initialized") ;
    if (m_pClass)           return hzerr(E_DUPLICATE, "Object already initialized") ;
    //  Clear any pre-existing initialization and data
    Clear() ;
    //  Set the class
    m_pClass = pClass ;
    //  Allocate data space for members
    m_pRoot = _obj_data::GetInstance(m_pClass) ;
    //  Go thru members
    for (nMbr = 0 ; rc == E_OK && nMbr < pClass->MbrCount() ; nMbr++)
    {
        pMbr = pClass->GetMember(nMbr) ;
        if (!pMbr)
            return hzerr(E_CORRUPT, "No member in position %d", nMbr) ;
        if (!pMbr->txtName())
            return hzerr(E_CORRUPT, "Member in position %d has no name", nMbr) ;
    }
    return E_OK ;
}
hzEcode hdbObject::Init (const hdbObjRepos* pRepos)
{
    //  Initialize a single object container to a repository and thus a data class
    //
    //  Argument:   pRepos  The initialized data object repository
    //
    //  Returns:    E_CORRUPT   If called without a hdbObject instance
    //              E_ARGUMENT  If the class is not supplied
    //              E_NOINIT    If the supplied class is not initialized
    //              E_DUPLICATE If this hdbObject is already initialized
    //              E_OK        If the hdbObject is initialized to the class
    _hzfunc("hdbObject::Init") ;
    const hdbMember*    pMbr ;      //  This class member
    uint32_t            nMbr ;      //  Member iterator
    hzEcode             rc = E_OK ; //  Return code
    //  Check class and class init state
    if (!this)              hzexit(E_CORRUPT, "No instance") ;
    if (!pRepos)            return hzerr(E_ARGUMENT, "No data object repository supplied") ;
    if (!pRepos->IsInit())  return hzerr(E_NOINIT, "Data object repository is not initialized") ;
    if (m_pClass)           return hzerr(E_DUPLICATE, "Object already initialized") ;
    //  Clear any pre-existing initialization and data
    Clear() ;
    //  Set the class
    m_pRepos = pRepos ;
    m_pClass = pRepos->Class() ;
    //  Allocate data space for members
    m_pRoot = _obj_data::GetInstance(m_pClass) ;
    //  Go thru members
    for (nMbr = 0 ; rc == E_OK && nMbr < m_pClass->MbrCount() ; nMbr++)
    {
        pMbr = m_pClass->GetMember(nMbr) ;
        if (!pMbr)
            return hzerr(E_CORRUPT, "No member in position %d", nMbr) ;
        if (!pMbr->txtName())
            return hzerr(E_CORRUPT, "Member in position %d has no name", nMbr) ;
    }
    return E_OK ;
}
/*
**  Set Member Functions
*/
hzEcode hdbObject::SetBool  (const hdbMember* pMbr, bool bValue)
{
    //  Set boolean member within data object. The object must be initialized to a data class, and the supplied member must exist within that class and be of datatype BOOL or TBOOL 
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  bValue  The boolean value the member will be set to
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetBool") ;
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Single object container not init to a data class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->SetBool(pMbr, bValue) ;
} ;
hzEcode hdbObject::SetBinary    (const hdbMember* pMbr, const hzChain& Z)
{
    //  Set binary member value within data object.
    //
    //  The object must be initialized to a data class, and the supplied member must exist within that class and be of a BINARY datatype. This function will copy the supplied chain
    //  to the core entry of the applicable member, and sets the member litmus byte to LITMUS_SET to state that the member has a new value. The chain is NOT committed to the target
    //  binary repository at this juncture. This will only happen when the onject as a whole is committed to the applicable data object repository, in either an INSERT or an UPDATE
    //  operation.
    //
    //  Note that BINARY members have an aux core entry in addition to the standard core entry. The aux is used to store the datum id of the chain. This is only set on a FETCH. In
    //  a new object, the aux is 0. In an existing object, the aux will contain the datim id that was previously assigned. On UPDATE, if the chain has changed, the datum id in the
    //  aux is used to DELETE the previous datum.
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  Z       The chain value the member will be set to
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetBinary") ;
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Single object container not init to a data class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
        return hzerr(E_TYPE, "Member %s is not BINARY or TXTDOC", pMbr->txtName()) ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->SetBinary(pMbr, Z) ;
}
hzEcode hdbObject::SetValue (const hdbMember* pMbr, const hzAtom& atom)
{
    //  Set member value within data object. The object must be initialized to a data class, and the supplied member must exist within that class
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  atom    The atomic value the member will be set to
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetValue") ;
    //_atomval  av ;        //  atom data
    //uchar*        pLitmus ;   //  Litmus bits
    //uchar*        pMCS ;      //  Pointer to member core space
    //  Object has class?
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Single object container not init to a data class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    //  Check status
    if (atom.Status() == ATOM_ERROR)
        return hzerr(E_BADVALUE, "Member %s not set", pMbr->txtName()) ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->SetValue(pMbr, atom) ;
}
hzEcode hdbObject::SetObject    (const hdbMember* pMbr, const hdbObject& sub)
{
    //  Either add a new subclass data object or return a modified subclass data object, to the supplied host class member. In the case of a new subclass data object, the object id
    //  will be zero. In the case of a modified subclass data object, this must necessarily have been fetched, so will have the object id it was given when originally added.
    //
    //  This object must be initialized to the host data class and the supplied data object must be initialized to subclass introduced by the applicable host class member.
    //
    //  Arguments:  1)  pMbr    The subclass member of this, the host class object
    //              2)  sub     The subclass object to be placed in the pMbr array of objects
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetObject") ;
    //  Object has class?
    if (!this)          hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)      return hzerr(E_NOINIT, "Single object container not init to a data class") ;
    if (!sub.m_pClass)  return hzerr(E_NOINIT, "Operand object container not init to a data class") ;
    if (!pMbr)          return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_CLASS)
        return hzerr(E_TYPE, "Member %s is atomic", pMbr->txtName()) ;
    if (pMbr->Datatype() != sub.m_pClass)
        return hzerr(E_TYPE, "Member %s is not of type class %s", pMbr->txtName(), sub.m_pClass->txtName()) ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->SetObject(pMbr, sub.m_pRoot) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzMD5& md5)
{
    //  Set value of the supplied member in the object, with the supplied string
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  str     The string value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(str)") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DIGEST)
        return hzerr(E_TYPE, "Member %s is not MD5 Digest", pMbr->txtName()) ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, md5) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzString& str)
{
    //  Set value of the supplied member in the object, with the supplied string
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  str     The string value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(str)") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_STRING)
        return hzerr(E_TYPE, "Member %s is not a string", pMbr->txtName()) ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, str) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzDomain& dom)
{
    //  Set value of the supplied member in the object, with the supplied domain
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The domain name value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(domain)") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DOMAIN)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, dom) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzEmaddr& ema)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(emaddr)") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_EMADDR)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, ema) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzUrl& url)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(url)") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_URL)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, url) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzXDate& xd)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(xdate)") ;
    //hzXDate*  pXd ;       //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_XDATE)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, xd) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzSDate& sd)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(sdate)") ;
    //hzSDate*  pSd ;       //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_SDATE)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, sd) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, const hzTime& time)
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::SetMbrValue(time)") ;
    //hzTime*       pTime ;     //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_TIME)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, time) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, uint64_t val)
{
    _hzfunc("hdbObject::SetMbrValue(uint64)") ;
    //uint64_t* pUint ;     //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_UINT64)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, val) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, int64_t val)
{
    _hzfunc("hdbObject::SetMbrValue(int64)") ;
    //int64_t*  pInt ;      //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_INT64)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, val) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, uint32_t val)
{
    _hzfunc("hdbObject::SetMbrValue(uint32)") ;
    //uint32_t* pUint ;     //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_UINT32)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, val) ;
}
hzEcode hdbObject::SetMbrValue  (const hdbMember* pMbr, int32_t val)
{
    _hzfunc("hdbObject::SetMbrValue(int32)") ;
    //int32_t*  pInt ;      //  Internal cast
    //uchar*        pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_INT32)
        return E_TYPE ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    return m_pRoot->_setMbrValue(pMbr, val) ;
}
/*
**  Get Member Functions
*/
hzEcode hdbObject::GetBool  (bool& result, const hdbMember* pMbr) const
{
    //  Get boolean member within data object. The object must be initialized to a data class, and the supplied member must exist within that class and be of datatype BOOL or TBOOL 
    //
    //  Arguments:  1)  result  The bool result
    //              2)  pMbr    Member pointer
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_NODATA    If the member is of type TBOOL and has not been set
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetBool") ;
    result = false ;
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Single object container not init to a data class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (!m_pRoot)
        return E_OK ;
    return m_pRoot->GetBool(result, pMbr) ;
}
hzEcode hdbObject::GetBinary    (hzChain& Z, const hdbMember* pMbr)
{
    //  Place the actual binary datum member value into the supplied chain. This requires the object container to name a source repository.
    //
    //  Arguments:  1)  Z       The hzChain to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_NODATA    If the binary datum repository could not be identified
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetBinary") ;
    hzChain*            pCh ;           //  Internal cast (binary datum)
    uint32_t*           pId ;           //  Internal cast (binary datum id)
    hzEcode             rc = E_OK ;     //  Return code 
    
    //  Object has class?
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    //  Check member data type
    if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
        return E_TYPE ;
    Z.Clear() ;
    //  Check for the default binary datum repository of the associated data object repository
    if (!m_pRepos)
        return hzerr(E_NOINIT, "No repository") ;
    if (!m_pRepos->BinRepos())
        return hzerr(E_NOINIT, "Associated repository has no binary datum repository") ;
    if (!m_pRoot)
        return E_OK ;
    //  The member space may already hold the binary in hzChain form. If so simply copy it
    pCh = (hzChain*) (m_pRoot->m_Core + pMbr->OsetAux()) ;
    if (pCh->Size())
    {
        Z = *pCh ;
        return E_OK ;
    }
    //  Binary datum not in-situ, fetch from the binary datum repository
    pId = (uint32_t*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
    if (*pId)
    {
        //  Fetch chain from the binary datum repository
        rc = m_pRepos->BinRepos()->Fetch(Z, *pId) ;
    }
    return rc ;
}
hzEcode hdbObject::GetValue (hzAtom& atom, const hdbMember* pMbr, uint32_t nOset) const
{
    //  Set the supplied atom with the value of the supplied member. Note that for BINARY/TXTDOC members, the atom is set with the binary datum id, not the binary datum value.
    //
    //  Arguments:  1)  atom    The atom to be set
    //              2)  pMbr    Pointer to the data class member
    //              3)  nOset   Default 0, applicable only if the member is an array
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_RANGE     If the requested element (nOset) exceeds the number of member values
    //              E_OK        If the object class member has been set to the supplied atom value.
    
    _hzfunc("hdbObject::GetValue(atom)") ;
    _atomval    av ;        //  Value from atom
    //uchar*        pLitmus ;   //  Litmus bits
    //uchar*        pMCS ;      //  Pointer to member core space
    atom.Clear() ;
    //  Object has class?
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    //  For BOOL and TBOOL members, values are read from the litmus bits
    if (pMbr->Basetype() == BASETYPE_CLASS)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    return m_pRoot->GetValue(atom, pMbr, nOset) ;
}
hzEcode hdbObject::GetObject    (hdbObject& sub, const hdbMember* pMbr, uint32_t nOset) const
{
    //  Fetches a subclass object from the applicable member of this (host class) object.
    //
    //  The supplied hdbObject (previously initialized to the subclass), is first cleared leaving its m_pRoot pointer as NULL. m_pRoot is then set to the fetched _obj_data, raising
    //  the copy count.
    //
    //  Arguments:  1)  sub     The target subclass object container
    //              2)  pMbr    The applicable host class member
    //              3)  nOset   Position in array of subclass objects held by the host class member
    //
    _hzfunc("hdbObject::GetObject") ;
    hzArray <_obj_data*>*   pArr ;      //  Cast of core entry to object array
    _obj_data*  pSub ;      //  Subclass object pointer
    uchar*      pMCS ;      //  Member core space
    uchar*      pLitmus ;   //  Litmus bits
    hzEcode     rc ;        //  Return code
    //  Object has class?
    if (!this)          hzexit(E_CORRUPT, "No instance") ;
    if (!m_pClass)      return hzerr(E_NOINIT, "Single object container not init to a data class") ;
    if (!sub.m_pClass)  return hzerr(E_NOINIT, "Subclass object container not init to a data class") ;
    if (!pMbr)          return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_CLASS)
        return hzerr(E_TYPE, "Member %s is atomic", pMbr->txtName()) ;
    if (pMbr->Datatype() != sub.m_pClass)
        return hzerr(E_TYPE, "Member %s is not of type class %s", pMbr->txtName(), sub.m_pClass->txtName()) ;
    sub.Clear() ;
    //  Set litmus and core space pointers
    pMCS = m_pRoot->m_Core + pMbr->OsetStd() ;
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        //  Cast the array
        pArr = (hzArray<_obj_data*>*) pMCS ;
        if (nOset >= pArr->Count())
            return E_NOTFOUND ;
        pSub = pArr->operator[](nOset) ;
        sub.m_pRoot = pSub ;
        sub.m_pRoot->m_copy++ ;
    }
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzMD5& md5, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  str     The string to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(md5)") ;
    //_atomval  av ;    //  Member value
    hzMD5*      pMd5 ;  //  MD5 pointer
    //  Clear value and check object set-up
    md5.Clear() ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DIGEST)
        return hzerr(E_TYPE, "Member %s is not an MD5 digest", pMbr->txtName()) ;
    if (!m_pRoot)
        return E_OK ;
    //  Fetch the value from the member space.
    pMd5 = (hzMD5*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
    md5 = *pMd5 ;
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzString& str, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  str     The string to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(str)") ;
    hzString*   pStr ;  //  Member entry cast to string
    //hzString  S ;     //  Temp string
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_STRING)
        return hzerr(E_TYPE, "Member %s is not a string", pMbr->txtName()) ;
    if (!m_pRoot)
        return E_OK ;
    //  Fetch the value from the member space.
    //str.Clear() ;
    pStr = (hzString*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
    str = *pStr ;
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzDomain& dom, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  dom     The domain name to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(dom)") ;
    _atomval    av ;    //  Member value
    hzDomain*   pDom ;  //  Member entry cast to domain
    dom.Clear() ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_DOMAIN)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    //  Fetch the value from the member space.
    pDom = (hzDomain*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
    dom = *pDom ;
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzEmaddr& ema, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  ema     The email address to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(ema)") ;
    //_atomval  av ;        //  Member value
    hzEmaddr*   pEma ;      //  Member entry cast to emaddr
    //  Clear email address
    ema.Clear() ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_EMADDR)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    //  Fetch the value from the member space.
    pEma = (hzEmaddr*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
    ema = *pEma ;
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzUrl& url, const hdbMember* pMbr) const
{
    //  Set the supplied URL with the value of the supplied member
    //
    //  Arguments:  1)  url     The email address to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(ema)") ;
    hzUrl*  pUrl ;      //  Member entry cast to hzUrl
    //  Clear email address
    url.Clear() ;
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_URL)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    //  Fetch the value from the member space.
    pUrl = (hzUrl*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
    url = *pUrl ;
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzIpaddr& ipa, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  ipa     The IP address to be set
    //              2)  pMbr    Pointer to the data class member
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(ipa)") ;
    uchar*  pLitmus ;   //  Litmus bits
    uchar*  pMCS ;      //  Pointer to member core space
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_EMADDR)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    //  Fetch the value from the member space.
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    pMCS = m_pRoot->m_Core + pMbr->OsetStd() ;
    ipa.Clear() ;
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        memcpy(&ipa, pMCS, 4) ;
    }
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzXDate& xdate, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(xdate)") ;
    uchar*      pLitmus ;   //  Litmus bits
    hzXDate*    pXd ;       //  Internal cast
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_XDATE)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    xdate.Clear() ;
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pXd = (hzXDate*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
        xdate = *pXd ;
    }
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzSDate& sdate, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(sdate)") ;
    uchar*      pLitmus ;   //  Litmus bits
    hzSDate*    pSd ;       //  Internal cast
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_SDATE)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    sdate.Clear() ;
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pSd = (hzSDate*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
        sdate = *pSd ;
    }
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (hzTime& time, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(time)") ;
    hzTime*     pTime ;     //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_SDATE)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    time.Clear() ;
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pTime = (hzTime*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
        time = *pTime ;
    }
    return E_OK ;
}
hzEcode hdbObject::GetMbrValue  (uint32_t& val, const hdbMember* pMbr) const
{
    //  Set the supplied string with the value of the supplied member
    //
    //  Arguments:  1)  pMbr    Pointer to the member to be set
    //              2)  dom     The email address value
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not a string or a string-like data type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::GetMbrValue(uint32)") ;
    uint32_t*   pUI ;       //  Internal cast
    uchar*      pLitmus ;   //  Litmus bits
    if (!m_pClass)  return hzerr(E_NOINIT, "Object has no class") ;
    if (!pMbr)      return hzerr(E_ARGUMENT, "No member supplied") ;
    if (pMbr->Class() != m_pClass)
        return hzerr(E_CORRUPT, "Member %s does not belong to class %s", pMbr->txtName(), m_pClass->txtName()) ;
    if (pMbr->Basetype() != BASETYPE_UINT32)
        return E_TYPE ;
    if (!m_pRoot)
        return E_OK ;
    val = 0 ;
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    if (pLitmus[pMbr->Posn()] & LITMUS_SET)
    {
        pUI = (uint32_t*) (m_pRoot->m_Core + pMbr->OsetStd()) ;
        val = *pUI ;
    }
    return E_OK ;
}
/*
**  hdbObject Import/Export JSON Functions
*/
hzEcode _commit_binaries_r  (hdbBinRepos* pRepos, _obj_data* pOD)
{
    //  hdbObject::CommitBinaries recursive support function
    _hzfunc(__func__) ;
    const hdbMember*    pMbr ;      //  Member
    hzChain*            pCh ;       //  Cast for datum
    uint32_t*           pInt ;      //  Cast for datumId
    uchar*              pLitmus ;   //  Litmus bits
    uchar*              pMCS ;      //  Pointer to member core space
    uint32_t            mbrNo ;     //  Member number (position within class)
    uint32_t            datumId ;   //  Datum id allocate by binary datum repository
    hzEcode             rc = E_OK ; //  Return code
    pLitmus = pOD->m_Core + pOD->m_pClass->CoreLen() ; 
    for (mbrNo = 0 ; rc == E_OK && mbrNo < pOD->m_pClass->MbrCount() ; mbrNo++)
    {
        pMbr = pOD->m_pClass->GetMember(mbrNo) ;
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            //  Recurse to handle sub-class
            hzArray <_obj_data*>*   pArr ;  //  Cast to array of subclass objects
            _obj_data*              pSub ;  //  Subclass object
            uint32_t                n ;     //  Object counter
            pMCS = pOD->m_Core + pMbr->OsetStd() ;
            pArr = (hzArray<_obj_data*>*) pMCS ;
            for (n = 0 ; n < pArr->Count() ; n++)
            {
                pSub = pArr->operator[](n) ;
                rc = _commit_binaries_r(pRepos, pSub) ;
            }
            continue ;
        }
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
        {
            if (pLitmus[pMbr->Posn()] & LITMUS_AUX)
            {
                //  Commit
                pCh = (hzChain*) (pOD->m_Core + pMbr->OsetAux()) ;
                pInt = (uint32_t*) (pOD->m_Core + pMbr->OsetStd()) ;
                threadLog("member %s posn %u oset %d aux %d\n", pMbr->txtName(), pMbr->Posn(), pMbr->OsetStd(), pMbr->OsetAux()) ;
                threadLog("Repos name %s\n", pRepos->txtName()) ;
                rc = pRepos->Insert(datumId, *pCh) ;
                threadLog("case 1\n") ;
                threadLog("datum id %u size %u rc=%s\n", datumId, pCh->Size(), Err2Txt(rc)) ;
                threadLog("case 2\n") ;
                if (rc == E_OK)
                {
                    *pInt = datumId ;
                    pLitmus[pMbr->Posn()] |= LITMUS_SET ;
                }
                threadLog("case 3\n") ;
            }
        }
    }
    return E_OK ;
}
hzEcode hdbObject::CommitBinaries   (hdbBinRepos* pRepos) const
{
    //  Commit all binary member values in this, and any subclass objects, to the supplied binary datum repository. It is expected that the binary datum repository will that of the
    //  data object repository, this object is to be placed in (either in an INSERT or UPDATE operation).
    //
    //  With binary members the litmus bit is only set by SetBinary(), which places a binary datum (presumed to be a new binary datum), in the member's core space. It is not set by
    //  retrieving the binary from a binary datum repository. In other words, the litmus bit is set if there is a new value, and not otherwise.
    //
    //  This function checks the litmus bit before committing the datum to the target binary datum repository. Once the commit is done, the address (binary datum id), is placed in
    //  the in member's aux core space, from where it is used in the export of deltas and EDOs.
    //
    //  Arguments:  1)  pMbr    The member
    //              2)  pRepos  The target binary datum repository
    //
    //  Returns:    E_NOINIT    If the object has not been initialized to a class
    //              E_ARGUMENT  If no member is supplied
    //              E_CORRUPT   If the supplied member number does not identify an object class member.
    //              E_TYPE      If the object class member is not atomic (is another class) or if the supplied atom has the wrong type.
    //              E_OK        If the object class member has been set to the supplied atom value.
    _hzfunc("hdbObject::CommitBinaries") ;
    hzEcode     rc = E_OK ; //  Return code
    if (!this)      hzexit(E_CORRUPT, "No instance") ;
    if (!pRepos)    return hzerr(E_NOINIT, "No target binary datum repository") ;
    if (!m_pClass)  return hzerr(E_ARGUMENT, "No data class") ;
    if (!m_pRoot)
        return E_OK ;
    rc = _commit_binaries_r(pRepos, m_pRoot) ;
    threadLog("returned from commit_br\n") ;
    return rc ;
}
hzEcode _import_json_r  (hzChain& err, chIter& zi, _obj_data* pOD)
{
    //  Support function to hdbObject::ImportJSON():- Populate the supplied object data container (_obj_data), by reading from the supplied chain (iterator). 
    //
    //  Subclass objects are handled by recursion.
    //
    //  Argument:   J   The hzChain to be populated by the JSON value
    _hzfunc(__func__) ;
    const hdbMember*    pMbr ;          //  Member
    hzAtom              atom ;          //  Atom value
    hzChain             W ;             //  For building values
    hzXDate             xdate ;         //  Long form date
    _atomval            av ;            //  Temp value
    //uchar*                pLitmus ;       //  Litmus bits
    hzString            mbrName ;       //  Member name
    hzString            str_val ;       //  Temp string
    hzString            num_val ;       //  Temp string
    hzSDate             sdate ;         //  Short form date
    hzTime              stime ;         //  hzTime
    hzIpaddr            ipa ;           //  IP address
    uint32_t            objId ;         //  Object id (start at 1)
    bool                bMultObj ;      //  Expecting multiple objects
    bool                bMultVal ;      //  Expecting multiple objects
    hzEcode             rc = E_OK ;     //  Return code
    if (!pOD)
    {
        err.Printf("No _obj_data supplied\n") ;
        return E_ARGUMENT ;
    }
    //  Lose leading whitespace and check for [] block
    for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
    bMultObj = false ;
    if (*zi == CHAR_SQOPEN)
        { bMultObj = true ; zi++ ; }
    //  Go thru {} object body
    objId = 1 ;
    for (; !zi.eof() && rc == E_OK ;)
    {
        for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
        //  Expect to be at object open
        if (*zi != CHAR_CUROPEN)
        {
            rc = E_FORMAT ;
            err.Printf("line %d col %d. No opening curly brace for object %d (at %c)\n", zi.Line(), zi.Col(), objId, *zi) ;
            break ;
        }
        for (zi++ ; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
        //  Do the members
        for (; rc == E_OK ;)
        {
            //  Expect member name followed by a colon, whitespace and a value
            for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
            if (*zi != CHAR_DQUOTE)
            {
                if (*zi == CHAR_CURCLOSE)
                    break ;
                rc = E_FORMAT ;
                err.Printf("line %d col %d. Expected a quoted member name (at %c)\n", zi.Line(), zi.Col(), *zi) ;
                break ;
            }
            //  Get quoted value (handle escapes)
            for (zi++ ;; zi++)
            {
                if (*zi == CHAR_BKSLASH)
                {
                    zi++ ;
                    switch  (*zi)
                    {
                    case CHAR_DQUOTE:   W.AddByte(CHAR_DQUOTE) ;    break ;
                    case CHAR_LC_N:     W.AddByte(CHAR_NL) ;        break ;
                    default:
                        W.AddByte(*zi) ;
                    }
                }
                if (*zi == CHAR_DQUOTE)
                    { zi++ ; break ; }
                W.AddByte(*zi) ;
            }
            if (!W.Size())
            {
                rc = E_FORMAT ;
                err.Printf("No data class memeber name found on line %d col %d\n", zi.Line(), zi.Col()) ;
                break ;
            }
            if (*zi != CHAR_COLON)
            {
                rc = E_FORMAT ;
                err.Printf("Expected colon after name on line %d col %d\n", zi.Line(), zi.Col()) ;
                break ;
            }
        
            mbrName = W ;
            W.Clear() ;
            for (zi++ ; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
        
            //  LOOKUP MEMBER
            pMbr = pOD->m_pClass->GetMember(mbrName) ;
            if (!pMbr)
            {
                
                rc = E_FORMAT ;
                err.Printf("Line %d col %d. Member %s not found in class %s\n", zi.Line(), zi.Col(), *mbrName, pOD->m_pClass->txtName()) ;
                break ;
            }
            if (pMbr->Basetype() == BASETYPE_CLASS)
            {
                //  Recurse to handle sub-class
                hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
                const hdbClass*         pSubclass ; //  Subclass (if any)
                _obj_data*              pSub ;      //  Subclass object
                uchar*                  pMCS ;      //  Pointer to member core space
                pSubclass = dynamic_cast<const hdbClass*>(pMbr->Datatype()) ;
                pMCS = pOD->m_Core + pMbr->OsetStd() ;
                pArr = (hzArray<_obj_data*>*) pMCS ;
                pSub = _obj_data::GetInstance(pSubclass) ;
                pArr->Add(pSub) ;
                rc = _import_json_r(err, zi, pSub) ;
                continue ;
            }
            bMultVal = false ;
            if (*zi == CHAR_SQOPEN)
                { bMultVal = true ; zi++ ; }
            for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
            //  Expect member value. Can be single value or array but not an object as these are dealt with by sub-class recursion
            for (;;)
            {
                //  Get value
                if (*zi == CHAR_DQUOTE)
                {
                    //  Get quoted value (handle escapes)
                    for (zi++ ;; zi++)
                    {
                        if (*zi == CHAR_BKSLASH)
                        {
                            zi++ ;
                            switch  (*zi)
                            {
                            case CHAR_DQUOTE:   W.AddByte(CHAR_DQUOTE) ;    break ;
                            case CHAR_LC_N:     W.AddByte(CHAR_NL) ;        break ;
                            default:
                                W.AddByte(*zi) ;
                            }
                        }
                        if (*zi == CHAR_DQUOTE)
                            { zi++ ; break ; }
                        W.AddByte(*zi) ;
                    }
                }
                else
                {
                    for (; *zi >= CHAR_SPACE && *zi != CHAR_SCOLON ; zi++)
                        W.AddByte(*zi) ;
                }
                str_val = W ;
                W.Clear() ;
                if (str_val == "null")
                    str_val.Clear() ;
                for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
                //  Set atom
                atom.SetValue(pMbr->Basetype(), str_val) ;
                if (atom.Status() == ATOM_SET)
                    pOD->SetValue(pMbr, atom) ;
                //  Continue if multiple else break
                if (bMultVal)
                {
                    //  Expect either a comma or a closing square brace
                    if (*zi == CHAR_COMMA)
                        { zi++ ; continue ; }
                    for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
                    if (*zi == CHAR_SQCLOSE)
                        zi++ ;
                }
                for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
                if (*zi != CHAR_SCOLON)
                {
                    rc = E_FORMAT ;
                    err.Printf("Line %d Col %d Expected semi-colon to end value (at %c)\n", zi.Line(), zi.Col(), *zi) ;
                    break ;
                }
                zi++ ;
                break ;
            }
        }
        if (rc != E_OK)
            break ;
        for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
        if (*zi != CHAR_CURCLOSE)
        {
            rc = E_FORMAT ;
            err.Printf("Line %d Col %d Expected closing curly brace to end object (at %c)\n", zi.Line(), zi.Col(), *zi) ;
            break ;
        }
        for (zi++ ; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
        //  Continue if multiple object else break
        if (bMultObj)
        {
            //  Expect either a comma or a closing square brace
            if (*zi == CHAR_COMMA)
                { zi++ ; objId++ ; continue ; }
            for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
            if (*zi != CHAR_SQCLOSE)
            {
                rc = E_FORMAT ;
                err.Printf("Line %d Col %d Expected closing square brace to end object array (at %c)\n", zi.Line(), zi.Col(), *zi) ;
                break ;
            }
            zi++ ;
        }
        break ;
    }
    return rc ;
}
hzEcode hdbObject::ImportJSON   (const hzChain& J)
{
    //  Import the hdbObject value as a JSON. Note that the JSON must be compatible with the native data class.
    //
    //  Arguments:  1)  J   The JSON as hzChain
    //
    //  Returns:    E_NOINIT    If this hdbObject is not initialized
    //              E_NODATA    If the supplied JSON is empty
    //              E_FORMAT    If the JSON does not comply with the data class
    //              E_OK        Operation successful
    _hzfunc("hdbObject::ImportJSON") ;
    hzChain     err ;       //  Gather all errors
    chIter      zi ;        //  Chain iterator
    hzEcode     rc ;        //  Return code
    if (!m_pClass)  return E_NOINIT ;
    if (!J.Size())  return E_NODATA ;
    Clear() ;
    zi = J ;
    if (*zi != CHAR_CUROPEN)
        return E_FORMAT ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    rc = _import_json_r(err, zi, m_pRoot) ;
    if (rc != E_OK)
        { threadLog("START IMPORT ERROR\n") ; threadLog(err) ; threadLog("END IMPORT ERROR\n") ; }
    return rc ;
}
/*
**  EDO Import/Export Functions
*/
hzEcode hdbObject::ImportEDO    (const hzChain& Z)
{
    //  Populate the hdbObject with the supplied hzChain content, which must be an EDO. This function is strictly for the purposes of LOADING from a hdbObjRepos cache. Please note
    //  the following:-
    //
    //      a)  In the case of STRING and string-like datum, the string space address is copied from the EDO but then the member core space is cast to the applicable class in order
    //          to keep proper track of the string space copy count.
    //
    //      b)  For BINARY and TXTDOC members, the binary datum address appears in the EDO and is copied to the aux, rather than the core space. The core space only comes into play
    //          when the binary is fetched, at which point the core space is cast to a hzChain (in order to keep track of the copy count).
    //
    //      c)  For single selection ENUM members, the core space is 1 byte. For multiple selection ENUM members, the core space is however many bytes are needed to hold the bitmap
    //          covering the entire ENUM population. In the latter case, the EDO entry for the member will have a length indicator (1 byte), followed by an idset encoding.
    //
    //      d)  For subclass members, the EDO will either contain the address of the subclass objects or a list of EDOs representing the actual subclass objects - depending on the
    //          repository configuration. By default repositories do not store data objects, be those of the host native data class, or of any subclass.
    //
    //  Argument:   Z   The input serialize object
    //
    //  Returns:    E_FORMAT    If the supplied chain does not comply with the expected format
    //              E_OK        If the operation is successful
    _hzfunc("hdbObject::ImportEDO") ;
    const hdbMember*    pMbr ;          //  This class member
    hzMD5               digest ;        //  MD5
    chIter              zi ;            //  Input iterator
    chIter              zend ;          //  Input iterator (expected end)
    _atomval            av ;            //  For 64 and 32 bit values
    const hdbEnum*      pEnum ;         //  ENUM data type (if applicable)
    uchar*              pLitmus ;       //  Litmus bits
    uchar*              pMCS ;          //  Pointer to member core space
    hzString*           pStr ;          //  Cast to string
    hzDomain*           pDom ;          //  Cast to domain
    hzEmaddr*           pEma ;          //  Cast to emaddr
    hzUrl*              pUrl ;          //  Cast to URL
    hzString            S ;             //  Temp string
    uint32_t            mbrNo ;         //  Member number (position within class)
    uint32_t            nLenEDO ;       //  EDO tail length
    uint32_t            nObjId ;        //  Object id
    uint32_t            nC ;            //  Counter
    uint32_t            bitMask ;       //  Litmus bit mask
    hzEcode             rc ;            //  Return code
    //  Object has class?
    if (!m_pClass)
        hzexit(E_NOINIT, "Object has no class") ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    //  Obtain the EDO length and object id
    zi = Z ;
    rc = ReadSerialUINT32(nLenEDO, zi) ;
    if (rc != E_OK)
        return hzerr(rc, "Could not process EDO length") ;
    memset(m_pRoot->m_Core, 0, m_pClass->CoreLen()) ;
    memset(pLitmus, 0, m_pClass->MbrCount()) ;
    zend = zi ;
    zend += nLenEDO ;
    rc = ReadSerialUINT32(nObjId, zi) ;
    if (rc != E_OK)
        return hzerr(rc, "Could not process EDO Object Id") ;
    //threadLog("Reading EDO %u of %u bytes\n", nObjId, nLenEDO) ;
    //  Grab the litmus bits
    bitMask = 128 ;
    for (mbrNo = nC = 0 ; mbrNo < m_pClass->MbrCount() ; mbrNo++)
    {
        if (*zi & bitMask)
            { nC++ ; pLitmus[mbrNo] = LITMUS_SET ; }
        bitMask /= 2 ;
        if (bitMask == 0)
            { bitMask = 128 ; zi++ ; }
    }
    if (bitMask < 128)
        zi++ ;
    //  Count up how many entries the fixed part will have
    for (mbrNo = 0 ; rc == E_OK && mbrNo < m_pClass->MbrCount() ; mbrNo++)
    {
        pMbr = m_pClass->GetMember(mbrNo) ;
        if (pLitmus[mbrNo] == 0)
            continue ;
        if (pMbr->Basetype() == BASETYPE_BOOL || pMbr->Basetype() == BASETYPE_TBOOL)
            continue ;
        //  Set pointer to member core space
        pMCS = m_pRoot->m_Core + pMbr->OsetStd() ;
        if (pMbr->Basetype() == BASETYPE_ENUM)
        {
            if (pMbr->Singular())
            {
                //  Single selection, only 1 byte
                pMCS[0] = *zi++ ;
                continue ;
            }
            //  Multiple selections allowed, so treat the EDO entry as an idset-encoded bitmap
            pEnum = dynamic_cast<const hdbEnum*>(pMbr->Datatype()) ;
            if (pEnum->Count() < 31)
            {
                //  In this case the length is 4 bytes
            }
            continue ;
        }
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
        {
            //  The EDO will only contain the address (uint32_t), which must go in the member AUX area
            for (nC = 0 ; !zi.eof() && nC < 4 ; nC++, zi++)
            {
                pMCS[nC] = *zi ;
            }
            continue ;
        }
        pMCS = m_pRoot->m_Core + pMbr->OsetStd() ;
        for (nC = 0 ; !zi.eof() && nC < pMbr->SizeDatum() ; nC++, zi++)
        {
            pMCS[nC] = *zi ;
        }
        if (nC < pMbr->SizeDatum())
            threadLog("Incomplete read of member %s\n", pMbr->txtName()) ;
        if (pMbr->Basetype() == BASETYPE_STRING || pMbr->Basetype() == BASETYPE_APPDEF)
        {
            pStr = (hzString*) pMCS ;
            if (pStr)
                pStr->_inc_copy() ;
        }
        if (pMbr->Basetype() == BASETYPE_DOMAIN)
        {
            pDom = (hzDomain*) pMCS ;
            if (*pDom)
                pDom->_inc_copy() ;
        }
        if (pMbr->Basetype() == BASETYPE_EMADDR)
        {
            pEma = (hzEmaddr*) pMCS ;
            if (*pEma)
                pEma->_inc_copy() ;
        }
        if (pMbr->Basetype() == BASETYPE_URL)
        {
            pUrl = (hzUrl*) pMCS ;
            if (*pUrl)
                pUrl->_inc_copy() ;
        }
    }
    if (zi != zend)
    {
        rc = hzerr(E_CORRUPT, "Object Id %u: Not at EDO end\n", nObjId) ;
        for (zi = Z, nC = 0 ; !zi.eof() && nC <= nLenEDO ; nC++, zi++)
        {
            threadLog("%u: char is %02x\n", nC, (uchar) *zi) ;
        }
    }
    return rc ;
}
uint32_t    _calc_edo_len   (const hdbClass* pClass, const uchar* pCore, uint32_t objId)
{
    //  Support function to ExportEDO(). Calculates the length of the would-be EDO ahead of its export.
    //
    //  Given the applicable class (arg 1), and the supplied buffer (pCore, assumed to be a hdbObject core), calculate the length of the would-be EDO. Note the following factors:-
    //
    //  The process loops through all the direct members in the object. For single value members, the space needed in the EDO is either 0 (member is NULL), or the datum size (which
    //  depends on the data type). For multiple value members (arrays), of an atomic data type, the space needed is the datum size multiplied by the number of values, plus the size
    //  of the serial integer needed to indicate the number of values. If there are no CLASS members or the CLASS members are empty, the total size returned will be the total space
    //  consumed by members which are present, plus the size of the litmus block as per the class, plus the size of the serial integer needed to indicate the object id.
    //
    //  For CLASS members which are invariably multiple value, this function is recursively called on each subclass object in the array. CLASS members appear in the EDO as a serial
    //  integer stating the aggregate total size of the subclass objects, followed by a concatenation of the subclass objects, each of which has a litmus block, a serial integer tp
    //  indicate the object id, and a serial integer to indicate object size!
    _hzfunc("calc_edo_len") ;
    const hdbMember*    pMbr ;          //  This class member
    const uchar*        pMCS ;          //  Member core space
    const uchar*        pLitmus ;       //  Litmus byte
    uint32_t            mbrNo ;         //  EDO tail length
    //uint32_t          subSize ;       //  Size of subclass EDO
    uint32_t            edoSize ;       //  Size of EDO
    uint32_t            n ;             //  Loop counter (for arrays)
    //  Object has class?
    if (!pClass)    { hzerr(E_ARGUMENT, "No class supplied") ; return 0 ; }
    if (!pCore)     { hzerr(E_ARGUMENT, "No object supplied") ; return 0 ; }
    edoSize = SizeSerialUINT32(objId) ;
    edoSize += pClass->LitmusSize() ;
    pLitmus = pCore + pClass->CoreLen() ; 
    for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; pLitmus++, mbrNo++)
    {
        pMbr = pClass->GetMember(mbrNo) ;
        if (!(*pLitmus & LITMUS_SET))
            continue ;
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            const hdbClass*         pSubclass ; //  Subclass (if any)
            _obj_data*              pSub ;      //  Subclass object
            pSubclass = dynamic_cast<const hdbClass*>(pMbr->Datatype()) ;
            pMCS = pCore + pMbr->OsetStd() ;
            pArr = (hzArray<_obj_data*>*) pMCS ;
            edoSize = SizeSerialUINT32(pArr->Count()) ;
            for (n = 0 ; n < pArr->Count() ; n++)
            {
                pSub = pArr->operator[](n) ;
                edoSize += _calc_edo_len(pSubclass, pSub->m_Core, n+1) ;
            }
            continue ;
        }
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
            edoSize += 4 ;
        else
            edoSize += pMbr->SizeDatum() ;
    }
    return edoSize ;
}
hzEcode _export_edo_r   (hzChain& E, const hdbClass* pClass, const uchar* pCore, uint32_t objId)
{
    //  Support function to the ExportTailEDO() member function.
    //
    //  This exports an EDO from the contents held in this hdbObject's core. The export process begins with the litmus bits and follows with the member data. Subclass members which
    //  are manifest as arrays of subclass hdbObject cores, are handled by recursive calls to this function.
    _hzfunc("hdbObject::_export_edo_r") ;
    const hdbMember*    pMbr ;          //  This class member
    const uchar*        pMCS ;          //  Member core space
    const uchar*        pLitmus ;       //  Litmus byte
    hzAtom              atom ;          //  Staging value
    _atomval            av ;            //  64-bit value from member space
    hzString*           pStr ;          //  Temp string pointer (memeber data cast)
    hzDomain*           pDom ;          //  Temp domain pointer (member data cast)
    hzEmaddr*           pEma ;          //  Temp email pointer (member data cast)
    hzUrl*              pUrl ;          //  Temp URL pointer (member data cast)
    uint32_t*           pInt ;          //  Cast to UINT32
    hzString            str ;           //  Temp string instance
    uint32_t            mbrNo ;         //  EDO tail length
    uint32_t            nSerlen ;       //  Length of serialized integer
    uint32_t            nSize ;         //  Size of EDO based on litmus bits
    uint32_t            n ;             //  Loop counter (for arrays)
    uchar               bitMask ;       //  Bitwise litmus value
    uchar               byte ;          //  Litmus byte to write to EDO tail
    //  Object has class?
    if (!pClass)
        return hzerr(E_NOINIT, "No class supplied") ;
    if (!pCore)
        return hzerr(E_NOINIT, "No core supplied") ;
    pLitmus = pCore + pClass->CoreLen() ; 
    //  Check the litmus bits to determine which members have values. This will also determine the size of the EDO.
    nSize = _calc_edo_len(pClass, pCore, objId) ;
    if (!nSize)
        return hzerr(E_NODATA, "Zero EDO size") ;
    WriteSerialUINT32(E, nSerlen, nSize) ;
    //  Write the object id
    WriteSerialUINT32(E, nSerlen, objId) ;
    //  Build the EDO tail in chain E. Start with checking the object's litmus bits. This will 
    pLitmus = pCore + pClass->CoreLen() ; 
    bitMask = 128 ;
    byte = 0 ;
    for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; pLitmus++, mbrNo++)
    {
        if (*pLitmus & LITMUS_SET)
        {
            //  The member has a value (or is a BOOL)
            byte |= bitMask ;
        }
        bitMask /= 2 ;
        if (bitMask == 0)
        {
            E.AddByte(byte) ;
            byte = 0 ;
            bitMask = 128 ;
        }
    }
    //  Deal with additional litmus bits
    pLitmus = pCore + pClass->CoreLen() ; 
    for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; pLitmus++, mbrNo++)
    {
        pMbr = pClass->GetMember(mbrNo) ;
        if (pMbr->Basetype() == BASETYPE_TBOOL)
        {
            if (*pLitmus & LITMUS_AUX)
                byte |= bitMask ;
        }
        else
        {
            continue ;
        }
        bitMask /= 2 ;
        if (bitMask == 0)
        {
            E.AddByte(byte) ;
            byte = 0 ;
            bitMask = 128 ;
        }
    }
    if (bitMask < 128)
        E.AddByte(byte) ;
    //  Write out values
    pLitmus = pCore + pClass->CoreLen() ; 
    for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; pLitmus++, mbrNo++)
    {
        pMbr = pClass->GetMember(mbrNo) ;
        //threadLog("DOING member %s\n", pMbr->txtName()) ;
        if (pMbr->Basetype() == BASETYPE_BOOL || pMbr->Basetype() == BASETYPE_TBOOL)
            continue ;
        if (!(*pLitmus & LITMUS_SET))
            continue ;
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
        {
            pMCS = pCore + pMbr->OsetStd() ;
            pInt = (uint32_t*) pMCS ;
            E.Append(pMCS, 4) ;
            continue ;
        }
        //  Set member core space pointer
        pMCS = pCore + pMbr->OsetStd() ;
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            const hdbClass*         pSubclass ; //  Subclass (if any)
            _obj_data*              pSub ;      //  Subclass object
            pSubclass = dynamic_cast<const hdbClass*>(pMbr->Datatype()) ;
            if (!pSubclass)
            {
                hzerr(E_TYPE, "Member %s has no subclass", pMbr->txtName()) ;
                continue ;
            }
            pArr = (hzArray<_obj_data*>*) pMCS ;
            for (n = 0 ; n < pArr->Count() ; n++)
            {
                threadLog("DOING subclass object\n") ;
                pSub = pArr->operator[](n) ;
                _export_edo_r(E, pSubclass, pSub->m_Core, n+1) ;
            }
            continue ;
        }
        //  Data will be committed to the EDO. Where applicable, string repositories will be updated.
        switch  (pMbr->Basetype())
        {
        case BASETYPE_DOMAIN:   pDom = (hzDomain*) pMCS ;
                                if (_hzGlobal_setDomains.Exists(*pDom))
                                    _hzGlobal_setDomains[*pDom]._inc_copy() ;
                                else
                                    _hzGlobal_setDomains.Insert(*pDom) ;
                                *pDom = _hzGlobal_setDomains[*pDom] ;
                                break ;
        case BASETYPE_EMADDR:   pEma = (hzEmaddr*) pMCS ;
                                if (_hzGlobal_setEmaddrs.Exists(*pEma))
                                    _hzGlobal_setEmaddrs[*pEma]._inc_copy() ;
                                else
                                    _hzGlobal_setEmaddrs.Insert(*pEma) ;
                                *pEma = _hzGlobal_setEmaddrs[*pEma] ;
                                break ;
        case BASETYPE_URL:      pUrl = (hzUrl*) pMCS ;
                                str = *pUrl ;
                                if (_hzGlobal_setStrings.Exists(str))
                                    _hzGlobal_setStrings[str]._inc_copy() ;
                                else
                                    _hzGlobal_setStrings.Insert(str) ;
                                break ;
        case BASETYPE_STRING:   pStr = (hzString*) pMCS ;
                                str = *pStr ;
                                //threadLog("doing value %s\n", *str) ;
                                if (_hzGlobal_setStrings.Exists(str))
                                    _hzGlobal_setStrings[str]._inc_copy() ;
                                else
                                    _hzGlobal_setStrings.Insert(str) ;
                                *pStr = _hzGlobal_setStrings[str] ;
                                //threadLog("done value %s\n", *str) ;
                                break ;
        }
        E.Append(pMCS, pMbr->SizeDatum()) ;
        //threadLog("done mbr\n") ;
    }
    //threadLog("done object\n") ;
    return E_OK ;
}
hzEcode hdbObject::ExportEDO    (hzChain& E) const
{
    //  Export an EDO tail, which is part of the EDO commit sequence. This begins with the litmus bits and follows with the member data. There is no length indicator at this point.
    //  Note that this function does not clear the supplied chain. It simply appends the chain with the EDO of this object.
    //
    //  Argument:   E   The chain to which the object EDO is appended
    //
    //  Returns:    E_NOINIT    If this object is not initialized to a data class
    //              E_OK        Operation successful
    _hzfunc("hdbObject::ExportEDO") ;
    hzEcode     rc ;    //  Return code
    E.Clear() ;
    return _export_edo_r(E, m_pClass, m_pRoot->m_Core, m_pRoot->m_ObjId) ;
}
/*
**  Delta Import/Export Functions
*/
hzEcode _import_delta_r (chIter& zi, _obj_data* pOD, uint32_t nLevel)
{
    //  Populate object by reading in a whole object delta. This function assumes file deltas, which do not specify the repository or class as these are in the delta file header.
    //
    //  This expects the supplied chain iterator (arg 1) to be iterating a chain whose content is a delta, and to be at a @obj or @sub sequence (or at a preceeding set of TABs).
    _hzfunc(__func__) ;
    const hdbMember*    pMbr ;          //  Member
    hzAtom              atom ;          //  Value carrier
    hzChain             V ;             //  For member value
    hzString            val ;           //  Member value as string
    uint32_t            reposId ;       //  Repos id
    uint32_t            datumId ;       //  Binary datum id
    uint32_t            objId ;         //  Object id
    uint32_t            mbrNo ;         //  Member number
    hzEcode             rc = E_OK ;     //  Return code
    if (!pOD->m_pClass)
        hzexit(E_ARGUMENT, "No class supplied") ;
    //zi.Skipwhite() ;
    if (!nLevel)
    {
        if (zi != "@obj")
            return hzerr(E_FORMAT, "Whole object deltas are expected to begin with '@obj'") ;
        zi += 4 ;
    }
    else
    {
        if (*zi != CHAR_LC_S)
            return hzerr(E_FORMAT, "Class %s: Whole subclass object deltas are expected to begin with 's'", pOD->m_pClass->txtName()) ;
        zi++ ;
    }
    //  Get object id
    for (objId = 0 ; IsDigit(*zi) ; zi++)
        { objId *= 10 ; objId += (*zi - '0') ; }
    pOD->m_ObjId = objId ;
    threadLog("Class %s obj %u\n", pOD->m_pClass->txtName(), objId) ;
    //  Expect start of object
    if (zi != "={\n")
        return hzerr(E_FORMAT, "Class %s: Object %u: Expected a '={' follow by newline, to declare object. Got %c", pOD->m_pClass->txtName(), objId, *zi) ;
    zi += 3 ;
    //  Look for member entries ('m' followed by member number and = sign)
    for (; rc == E_OK && !zi.eof() ;)
    {
        //zi.Skipwhite() ;
        for (; *zi == CHAR_NL || *zi == CHAR_TAB ; zi++) ;
        if (*zi == '}')
            { zi++ ; break ; }
        //  Expect start of member
        if (*zi != CHAR_LC_M)
        {
            rc = hzerr(E_FORMAT, "Class %s: Expected a member value starting with 'm'. Got [%c]", pOD->m_pClass->txtName(), *zi) ;
            for (; !zi.eof() ; zi++)
                threadLog("char is [%c]\n", *zi) ;
            break ;
        }
        //  Get member number
        for (zi++, mbrNo = 0 ; IsDigit(*zi) ; zi++)
            { mbrNo *= 10 ; mbrNo += *zi - '0' ; }
        pMbr = pOD->m_pClass->GetMember(mbrNo) ;
        if (!pMbr)
            { rc = hzerr(E_FORMAT, "Class %s: Member %d does not exist\n", pOD->m_pClass->txtName(), mbrNo) ; break ; }
        threadLog("Doing Class %s Member %s\n", pOD->m_pClass->txtName(), pMbr->txtName()) ;
        //  Deal with subclass member
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            const hdbClass*         pSubclass ; //  Subclass (if any)
            _obj_data*              pSub ;      //  Subclass object
            uchar*                  pMCS ;      //  Pointer to member core space
            pSubclass = dynamic_cast<const hdbClass*>(pMbr->Datatype()) ;
            pSub = _obj_data::GetInstance(pSubclass) ;
            _import_delta_r(zi, pSub, nLevel + 1) ;
            pMCS = pOD->m_Core + pMbr->OsetStd() ;
            pArr = (hzArray<_obj_data*>*) pMCS ;
            //rc = pArr->Add(pSub) ;
            //if (rc != E_OK)
            //  { rc = hzerr(rc, "Could not add subclass object to array") ; break ; }
            //threadLog("Subclass object array now has %u items\n", pArr->Count()) ;
            continue ;
        }
        //  Expect equal sign
        if (*zi != CHAR_EQUAL)
            { rc = hzerr(E_FORMAT, "Class %s: Member %u %s: Expected an '=' sign. Got [%c]", pOD->m_pClass->txtName(), mbrNo, pMbr->txtName(), *zi) ; break ; }
        zi++ ;
        //  Deal with BOOL and TBOOL members
        if (pMbr->Basetype() == BASETYPE_BOOL || pMbr->Basetype() == BASETYPE_TBOOL)
        {
            if (zi == "true")
                pOD->SetBool(pMbr, true) ;
            else
                pOD->SetBool(pMbr, false) ;
            threadLog("Set bool member\n") ;
            for (; !zi.eof() && *zi != CHAR_NL ; zi++) ;
            zi++ ;
            threadLog("Continuing ....\n") ;
            continue ;
        }
        //  Get value or binary datum address
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
        {
            //  Get binary address
            datumId = 0 ;
            for (; !zi.eof() ;)
            {
                if (*zi == CHAR_NL)
                    { zi++ ; break ; }
                if (!IsDigit(*zi))
                    { rc = hzerr(E_FORMAT, "Class %s: Member %u %s: Binary address so digits only", pOD->m_pClass->txtName(), mbrNo, pMbr->txtName()) ; break ; }
                datumId *= 10 ;
                datumId += (*zi - '0') ;
                zi++ ;
            }
            if (rc != E_OK)
                break ;
            //threadLog("datum Id %u char %c\n", datumId, *zi) ;
            rc = pOD->SetBinAddr(pMbr, datumId) ;
            continue ;
        }
        //  For STRING and TEXT members, deal with possible multi-line values
        //  if (pMbr->Basetype() == BASETYPE_STRING || pMbr->Basetype() == BASETYPE_TEXT)
        //  {
        //  }
        //  For all other cases gather the value into a chain, convert to a string and use this to set an atom, use the atom to set the value. 
        for (; !zi.eof() ;)
        {
            if (*zi == CHAR_NL)
            {
                for (zi++ ; *zi == CHAR_TAB ; zi++) ;
                if (*zi == '}') break ;
                if (*zi == 'm') break ;
                //  Otherwise we have a multiple line value
                V.AddByte(CHAR_NL) ;
                continue ;
            }
            V.AddByte(*zi) ;
            zi++ ;
            if (V.Size() > 1000)
                { rc = hzerr(E_FORMAT, "Class %s. Member %u %s: Excess size", pOD->m_pClass->txtName(), mbrNo, pMbr->txtName()) ; break ; }
        }
        val = V ;
        V.Clear() ;
        atom.SetValue(pMbr->Basetype(), val) ;
        rc = pOD->SetValue(pMbr, atom) ;
        if (rc != E_OK)
        {
            hzerr(rc, "Class %s: Could not set member %s with [%s]\n", pOD->m_pClass->txtName(), pMbr->txtName(), *val) ;
            break ;
        }
    }
    return rc ;
}
hzEcode hdbObject::ImportDelta  (const hzChain& Z)
{
    //  Populate object by reading in a whole object delta
    //
    //  Argument:   Z   Chain to which delta is written
    //
    //  Returns:    E_FORMAT    If the delta is malformed
    //              E_OK        Operation successful
    _hzfunc("hdbObject::ImportDelta") ;
    chIter      zi ;    //  Chain iterator
    zi = Z ;
    if (zi != "@obj")
        return hzerr(E_FORMAT, "Whole object deltas are expected to begin with '@obj'") ;
    //  Clear object
    Clear() ;
    if (!m_pRoot)
        m_pRoot = _obj_data::GetInstance(m_pClass) ;
    threadLog("Importing delta of %d bytes\n", Z.Size()) ;
    return _import_delta_r(zi, m_pRoot, 0) ;
}
hzEcode _export_delta_r (hzChain& D, const hdbClass* pClass, const uchar* pCore, uint32_t objId, uint32_t nLevel)
{
    //  Support function to the ExportDelta() member function.
    //
    //  This exports an EDO from the contents held in this hdbObject's core. The export process begins with the litmus bits and follows with the member data. Subclass members which
    //  are manifest as arrays of subclass hdbObject cores, are handled by recursive calls to this function.
    _hzfunc("_export_delta_r") ;
    const hdbMember*    pMbr ;          //  This class member
    const uchar*        pMCS ;          //  Member core space
    const uchar*        pLitmus ;       //  Litmus byte
    hzMD5*              pMd5 ;          //  Cast to hzMD5
    hzString*           pStr ;          //  Cast to string
    hzDomain*           pDom ;          //  Cast to domain
    hzEmaddr*           pEma ;          //  Cast to emaddr
    hzUrl*              pUrl ;          //  Cast to URL
    uint32_t*           pUint ;         //  Cast to uint32
    hzAtom              atom ;          //  Staging value
    _atomval            av ;            //  Atomval
    uint32_t            mbrNo ;         //  Member number
    uint32_t            nI ;            //  Loop counter (for indentation)
    uint32_t            nA ;            //  Loop counter (for object arrays)
    hzEcode             rc ;            //  Return code
    if (nLevel)
    {
        //for (nI = nLevel ; nI ; nI--)
        //  D.AddByte(CHAR_TAB) ;
        //D.Printf("@sub %u:%u\n", pClass->ClassId(), objId) ;
        D.Printf("s%u", objId) ;
    }
    else
        //D.Printf("@obj %u:%u\n", pClass->ClassId(), objId) ;
        D.Printf("@obj%u", objId) ;
    //for (nI = nLevel ; nI ; nI--)
    //  D.AddByte(CHAR_TAB) ;
    D << "={\n" ;
    //threadLog("case 1\n") ;
    pLitmus = pCore + pClass->CoreLen() ; 
    for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; pLitmus++, mbrNo++)
    {
        //threadLog("case 2\n") ;
        pMbr = pClass->GetMember(mbrNo) ;
        if (pMbr->Basetype() == BASETYPE_BOOL)
        {
            //threadLog("case 3\n") ;
            for (nI = nLevel ; nI ; nI--)
                D.AddByte(CHAR_TAB) ;
            if (*pLitmus & LITMUS_SET)
                D.Printf("\tm%d=true\n", mbrNo) ;
            else
                D.Printf("\tm%d=false\n", mbrNo) ;
            continue ;
        }
        if (!(*pLitmus & LITMUS_SET))
            continue ;
        //  Output values
        if (pMbr->Basetype() == BASETYPE_TBOOL)
        {
            //threadLog("case 4\n") ;
            for (nI = nLevel ; nI ; nI--)
                D.AddByte(CHAR_TAB) ;
            if (*pLitmus & LITMUS_AUX)
                D.Printf("\tm%d=true\n", mbrNo) ;
            else
                D.Printf("\tm%d=false\n", mbrNo) ;
            continue ;
        }
        if (pMbr->Basetype() == BASETYPE_BINARY || pMbr->Basetype() == BASETYPE_TXTDOC)
        {
            //threadLog("case 7\n") ;
            pUint = (uint32_t*) (pCore + pMbr->OsetStd()) ;
            D.Printf("\tm%d=%u\n", mbrNo, *pUint) ;
            continue ;
        }
        //  Get pointer to member core space
        pMCS = pCore + pMbr->OsetStd() ;
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            const hdbClass*         pSubclass ; //  Subclass (if any)
            _obj_data*              pSub ;      //  Subclass object
            //threadLog("case 5\n") ;
            pSubclass = dynamic_cast<const hdbClass*>(pMbr->Datatype()) ;
            pArr = (hzArray<_obj_data*>*) pMCS ;
            for (nA = 0 ; nA < pArr->Count() ; nA++)
            {
                for (nI = nLevel ; nI ; nI--)
                    D.AddByte(CHAR_TAB) ;
                D.Printf("\tm%u", mbrNo) ;
                pSub = pArr->operator[](nA) ;
                _export_delta_r(D, pSubclass, pSub->m_Core, nA+1, nLevel+1) ;
                pSub->Clear() ;
            }
            continue ;
        }
        //  Indent
        for (nI = nLevel ; nI ; nI--)
            D.AddByte(CHAR_TAB) ;
            //threadLog("case 6\n") ;
        if (pMbr->Basetype() == BASETYPE_STRING)    { pStr = (hzString*) pMCS ; D.Printf("\tm%d=%s\n", mbrNo, (const char*) *pStr) ; continue ; }
        if (pMbr->Basetype() == BASETYPE_DOMAIN)    { pDom = (hzDomain*) pMCS ; D.Printf("\tm%d=%s\n", mbrNo, (const char*) *pDom) ; continue ; }
        if (pMbr->Basetype() == BASETYPE_EMADDR)    { pEma = (hzEmaddr*) pMCS ; D.Printf("\tm%d=%s\n", mbrNo, (const char*) *pEma) ; continue ; }
        if (pMbr->Basetype() == BASETYPE_URL)       { pUrl = (hzUrl*) pMCS ; D.Printf("\tm%d=%s\n", mbrNo, (const char*) *pUrl) ; continue ; }
        if (pMbr->Basetype() == BASETYPE_DIGEST)
        {
            //threadLog("case A\n") ;
            pMd5 = (hzMD5*) pMCS ;
            D.Printf("\tm%d=%s\n", mbrNo, pMd5->Txt()) ;
            continue ;
        }
            //threadLog("case 8\n") ;
        //  Deal with other types
        switch  (pMbr->Basetype())
        {
        case BASETYPE_XDATE:    { hzXDate* pXd ; pXd = (hzXDate*) pMCS ; atom = *pXd ; }    break ;
        case BASETYPE_SDATE:    atom = (hzSDate*) pMCS ;    break ; //memcpy(pMCS, &av.m_uInt32, 4) ;   break ;
        case BASETYPE_TIME:     atom = (hzTime*) pMCS ;     break ; //memcpy(pMCS, &av.m_uInt32, 4) ;   break ;
        default:
            //threadLog("case 9\n") ;
            av.m_uInt64 = 0 ;
            memcpy(&av, pMCS, pMbr->SizeDatum()) ;
            atom.SetValue(pMbr->Basetype(), av) ;
        }
            //threadLog("case 10\n") ;
        D.Printf("\tm%d=%s\n", mbrNo, atom.Show()) ;
    }
            //threadLog("case 11\n") ;
    for (nI = nLevel ; nI ; nI--)
        D.AddByte(CHAR_TAB) ;
    D << "}\n" ;
    return E_OK ;
}
hzEcode hdbObject::ExportDelta  (hzChain& Z) const
{
    //  Write out whole form delta
    //
    //  Argument:   Z   Chain to which delta is written
    _hzfunc("hdbObject::ExportDelta") ;
    Z.Clear() ;
    return _export_delta_r(Z, m_pClass, m_pRoot->m_Core, m_pRoot->m_ObjId, 0) ;
}
/*
**  JSON Export
*/
static  void    _write_mbr_JSON (hzChain& J, const hdbMember* pMbr, _atomval av, uint32_t nLevel)
{
    //  JSON Export support function: Write out a single value and nothing but a single value, but only if the value is in the supplied _atomval. Values of BOOL and TBOOL members
    //  are in the litmus bits and not handled here.
    hzIpaddr        ipa ;           //  IP address
    hzTime          stime ;         //  hzTime
    hzSDate         sdate ;         //  Short form date
    hzSDate         xdate ;         //  Long form date
    hzString        strVal ;        //  Test string
    switch  (pMbr->Basetype())
    {
    //  64-bit entities
    case BASETYPE_DOUBLE:   J.Printf("%f",  av.m_Double) ;  break ;
    case BASETYPE_INT64:    J.Printf("%dl", av.m_sInt64) ;  break ;
    case BASETYPE_UINT64:   J.Printf("%ul", av.m_uInt64) ;  break ;
    case BASETYPE_XDATE:    xdate = av.m_uInt64 ;
                            J.Printf("\"%s\"", xdate.Txt()) ;
                            break ;
    //  32-bit entities
    case BASETYPE_INT32:    J.Printf("%d",  av.m_sInt32) ;  break ;
    case BASETYPE_INT16:    J.Printf("%d",  av.m_sInt16) ;  break ;
    case BASETYPE_BYTE:     J.Printf("%d",  av.m_sByte) ;   break ;
    case BASETYPE_UINT32:   J.Printf("%u",  av.m_uInt32) ;  break ;
    case BASETYPE_UINT16:   J.Printf("%u",  av.m_uInt16) ;  break ;
    case BASETYPE_UBYTE:    J.Printf("%u",  av.m_uByte) ;   break ;
    case BASETYPE_IPADDR:   ipa = av.m_uInt32 ;     J.Printf("\"%s\"", *ipa) ;      break ;
    case BASETYPE_TIME:     stime = av.m_uInt32 ;   J << stime.Txt() ;  break ;
    case BASETYPE_SDATE:    sdate = av.m_uInt32 ;   J << sdate.Txt() ;  break ;
    //  Strings
    case BASETYPE_EMADDR:
    case BASETYPE_URL:
    case BASETYPE_STRING:
    case BASETYPE_APPDEF:
    case BASETYPE_TEXT:     J.AddByte(CHAR_DQUOTE) ;
                            strVal.Clear() ;
                            strVal._int_set(av.m_uInt32) ;
                            J << strVal ;
                            strVal._int_clr() ;
                            J.AddByte(CHAR_DQUOTE) ;
                            break ;
#if 0
    case BASETYPE_BINARY:   //  File assummed to be un-indexable (eg image). Stored on disk, infrequent change.
    case BASETYPE_TXTDOC:   //  Document from which text can be extracted/indexed. Stored on disk, infrequent change.
                            J.Printf("%u", val) ;
                            break ;
    case BASETYPE_ENUM:     //  Kludge for now
#endif
    }
}
hzEcode hdbObject::_export_json_r   (hzChain& json, const hdbClass* pClass, uint32_t nLevel) const
{
    //  Recursive JSON export by class.
    //
    //  This function writes out the opening '{', member values in the first level of the applicable data object, then the closing '}'. Member values are written in order of member
    //  position within the applicable data class. Recursion is used to export subclass objects. All atomic values are written out in full text form except BINARY and TXTDOC, which
    //  are written as Base64.
    //
    //  Arguments:  1)  J       The hzChain to be populated by the JSON value
    //              2)  nLevel  The object level
    _hzfunc("hdbObject::_export_json_r") ;
    //hzArray<_atomval>*    pArr8 ;     //  Pointer to an array of 64-bit values
    //hzArray<uint32_t>*    pArr4 ;     //  Pointer to an array of 32-bit values
    hzAtom              atom ;      //  Extracted value
    _atomval            av ;        //  64-bit value from member space
    //const hdbClass*       pSub ;      //  Sub-class
    const hdbMember*    pMbr ;      //  Member pointer
    uchar*              pLitmus ;   //  Litmus byte
    uchar*              pMCS ;      //  Member core space
    uint32_t            mbrNo ;     //  Member number
    //uint32_t      objId ;         //  Object number
    uint32_t            nIndent ;       //  Indent iterator
    //int32_t           nV ;            //  Value iterator
    //int32_t           val ;           //  Value from m_Values (value or offset into m_Large or m_Strings)
    uint32_t            n ;             //  Array counter
    //  Object has class?
    if (!m_pClass)
        return hzerr(E_NOINIT, "Object has no class") ;
    //  Write the opening '{'
    json << "{\n" ;
    //  Write out values
    pLitmus = m_pRoot->m_Core + m_pClass->CoreLen() ; 
    for (mbrNo = 0 ; mbrNo < m_pClass->MbrCount() ; pLitmus++, mbrNo++)
    {
        pMbr = m_pClass->GetMember(mbrNo) ;
        for (nIndent = 0 ; nIndent <= nLevel ; nIndent++)
            json.AddByte(CHAR_TAB) ;
        json.Printf("\"%s\": ", pMbr->txtName()) ;
        //  Deal with BOOL and TBOOL members
        if (pMbr->Basetype() == BASETYPE_BOOL)
        {
            if (m_pRoot->m_Core[pMbr->OsetStd()] & LITMUS_SET)
                json << "true;\n" ;
            else
                json << "false;\n" ;
            continue ;
        }
            
        if (pMbr->Basetype() == BASETYPE_TBOOL)
        {
            if (m_pRoot->m_Core[pMbr->OsetStd()] & LITMUS_SET)
            {
                if (m_pRoot->m_Core[pMbr->OsetStd()] & LITMUS_AUX)
                    json << "true;\n" ;
                else
                    json << "false;\n" ;
            }
            else
                json << "null;\n" ;
            continue ;
        }
        //  Deal with empty member
        if (!(*pLitmus & LITMUS_SET))
        {
            json << "null;\n" ;
            continue ;
        }
        //  Recurse if sub-class
        if (pMbr->Basetype() == BASETYPE_CLASS)
        {
            hzArray <_obj_data*>*   pArr ;      //  Cast to array of subclass objects
            const hdbClass*         pSubclass ; //  Subclass (if any)
            //_obj_data*                pSub ;      //  Subclass object
            pSubclass = dynamic_cast<const hdbClass*>(pMbr->Datatype()) ;
            pArr = (hzArray<_obj_data*>*) pMCS ;
            for (n = 0 ; n < pArr->Count() ; n++)
            {
                //pSub = pArr->operator[](n) ;
                _export_json_r(json, pSubclass, nLevel + 1) ;
            }
            continue ;
        }
        //  Write out member values as an array if multiple values are allowed (even if only 1 is present)
        if (pMbr->Multiple())
        {
            //  Array of values
            json << "[\n" ;
            //  array present
            /*
            if (pMbr->SizeVal() == 8)
            {
                //  64-bit values
                pArr8 = (hzArray<_atomval>*) av.m_pVoid ;
                av = pArr8->operator[](0) ;
                _write_mbr_JSON(json, pMbr, av, nLevel) ;
                for (n = 1 ; n < pArr8->Count() ; n++)
                {
                    json.AddByte(CHAR_COMMA) ;
                    av = pArr8->operator[](n) ;
                    _write_mbr_JSON(json, pMbr, av, nLevel) ;
                }
            }
            else
            {
                //  Values of 32-bit size or less
                pArr4 = (hzArray<uint32_t>*) av.m_pVoid ;
                av.m_uInt32 = pArr4->operator[](0) ;
                _write_mbr_JSON(json, pMbr, av, nLevel) ;
                for (n = 1 ; n < pArr4->Count() ; n++)
                {
                    json.AddByte(CHAR_COMMA) ;
                    av.m_uInt32 = pArr4->operator[](n) ;
                    _write_mbr_JSON(json, pMbr, av, nLevel) ;
                }
            }
            */
            json << "]\n" ;
            continue ;
        }
        if (*pLitmus & LITMUS_SET)
        {
            _write_mbr_JSON(json, pMbr, av, nLevel) ;
            json.AddByte(NEWLINE) ;
            continue ;
        }
    }
    return E_OK ;
}
hzEcode hdbObject::ExportJSON   (hzChain& J) const
{
    //  Export the hdbObject value as a JSON
    //
    //  Argument:   J   The hzChain to be populated by the JSON value
    _hzfunc("hdbObject::ExportJSON") ;
    if (!m_pClass)
        return E_NOINIT ;
    J.Clear() ;
    return _export_json_r(J, m_pClass, 0) ;
}