Files
mars-flaim/xflaim/src/ncache.cpp
dsandersoremutah c55dab446f Renamed version4 to flaim and version5 to xflaim
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-01-27 21:06:39 +00:00

5611 lines
132 KiB
C++

//------------------------------------------------------------------------------
// Desc: This is the DOM Node cache for XFLAIM
//
// Tabs: 3
//
// Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: ncache.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "flaimsys.h"
#if defined( FLM_NLM) && !defined( __MWERKS__)
// Disable "Warning! W549: col(XX) 'sizeof' operand contains
// compiler generated information"
#pragma warning 549 9
#endif
/****************************************************************************
Desc: Constructor
****************************************************************************/
F_NodeCacheMgr::F_NodeCacheMgr()
{
m_pPurgeList = NULL;
m_pHeapList = NULL;
m_pOldList = NULL;
f_memset( &m_Usage, 0, sizeof( m_Usage));
m_ppHashBuckets = NULL;
m_uiNumBuckets = 0;
m_uiHashFailTime = 0;
m_uiHashMask = 0;
m_uiPendingReads = 0;
m_uiIoWaits = 0;
m_pFirstNode = NULL;
m_bReduceInProgress = FALSE;
#ifdef FLM_DEBUG
m_bDebug = FALSE;
#endif
}
/****************************************************************************
Desc: Constructor for F_CachedNode
****************************************************************************/
F_CachedNode::F_CachedNode()
{
m_pPrevInBucket = NULL;
m_pNextInBucket = NULL;
m_pPrevInDatabase = NULL;
m_pNextInDatabase = NULL;
m_pOlderVersion = NULL;
m_pNewerVersion = NULL;
m_pPrevInHeapList = NULL;
m_pNextInHeapList = NULL;
m_pPrevInOldList = NULL;
m_pNextInOldList = NULL;
m_ui64LowTransId = 0;
// Set the high transaction ID to FLM_MAX_UINT64 so that this will NOT
// be treated as one that had memory assigned to the old version nodes.
m_ui64HighTransId = FLM_MAX_UINT64;
m_pNotifyList = NULL;
m_uiCacheFlags = 0;
m_uiStreamUseCount = 0;
// Items initialized in constructor
m_uiDataBufSize = 0;
m_pucData = NULL;
m_pNodeList = NULL;
m_ppAttrList = NULL;
m_uiAttrCount = 0;
m_uiTotalAttrSize = 0;
m_uiFlags = 0;
f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO));
#ifdef FLM_CACHE_PROTECT
protectCachedItem();
#endif
}
/****************************************************************************
Desc: Destructor for F_CachedNode object.
This routine assumes the global mutex is already locked.
****************************************************************************/
F_CachedNode::~F_CachedNode()
{
// Don't include attribute size, because it will be subtracted out
// when we delete the attr items.
FLMUINT uiSize = memSize() - m_uiTotalAttrSize;
FLMBYTE * pucActualAlloc;
flmAssert( !m_uiStreamUseCount);
f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex);
// If this is an old version, decrement the old version counters.
if (m_ui64HighTransId != FLM_MAX_UINT64)
{
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize &&
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount--;
unlinkFromOldList();
}
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize &&
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount--;
if( m_uiFlags & FDOM_HEAP_ALLOC)
{
unlinkFromHeapList();
}
// Free the m_pucData, if any
if (m_pucData)
{
pucActualAlloc = getActualPointer( m_pucData);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
m_uiDataBufSize, &pucActualAlloc);
m_pucData = NULL;
}
if (m_pNodeList)
{
pucActualAlloc = getActualPointer( m_pNodeList);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
calcNodeListBufSize( m_nodeInfo.uiChildElmCount),
&pucActualAlloc);
m_pNodeList = NULL;
}
if (m_uiAttrCount)
{
FLMUINT uiLoop;
for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++)
{
delete m_ppAttrList [uiLoop];
}
flmAssert( !m_uiTotalAttrSize);
pucActualAlloc = getActualPointer( m_ppAttrList);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
calcAttrListBufSize( m_uiAttrCount),
&pucActualAlloc);
m_ppAttrList = NULL;
m_uiAttrCount = 0;
}
if (shouldRehash( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount,
gv_XFlmSysData.pNodeCacheMgr->m_uiNumBuckets))
{
if (checkHashFailTime( &gv_XFlmSysData.pNodeCacheMgr->m_uiHashFailTime))
{
(void)gv_XFlmSysData.pNodeCacheMgr->rehash();
}
}
}
/****************************************************************************
Desc: This routine frees a purged node from node cache. This routine assumes
that the node cache mutex has already been locked.
****************************************************************************/
void F_CachedNode::freePurged( void)
{
// Unlink the node from the purged list.
unlinkFromPurged();
// Free the F_CachedNode object.
unsetPurged();
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
#endif
delete this;
}
/****************************************************************************
Desc: This routine frees a node in the node cache. This routine assumes
that the node cache mutex has already been locked.
****************************************************************************/
void F_CachedNode::freeCache(
FLMBOOL bPutInPurgeList)
{
FLMBOOL bOldVersion;
bOldVersion = (FLMBOOL)((m_ui64HighTransId != FLM_MAX_UINT64)
? TRUE
: FALSE);
// Unlink the node from its various lists.
gv_XFlmSysData.pNodeCacheMgr->m_MRUList.unlinkGlobal(
(F_CachedItem *)this);
unlinkFromDatabase();
if (!m_pNewerVersion)
{
F_CachedNode * pOlderVersion = m_pOlderVersion;
unlinkFromHashBucket();
// If there was an older version, it now needs to be
// put into the hash bucket.
if (pOlderVersion)
{
unlinkFromVerList();
pOlderVersion->linkToHashBucket();
}
}
else
{
unlinkFromVerList();
}
if( m_uiFlags & FDOM_HEAP_ALLOC)
{
unlinkFromHeapList();
}
// Free the F_CachedNode structure if not putting in purge list.
if (!bPutInPurgeList)
{
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
#endif
delete this;
}
else
{
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
#endif
if ((m_pNextInGlobal = gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList) != NULL)
{
#ifdef FLM_CACHE_PROTECT
m_pNextInGlobal->unprotectCachedItem();
#endif
m_pNextInGlobal->m_pPrevInGlobal = this;
#ifdef FLM_CACHE_PROTECT
m_pNextInGlobal->protectCachedItem();
#endif
}
gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList = this;
// Unset the dirty flags - don't want anything in the purge list
// to be dirty.
m_uiFlags &= ~(FDOM_DIRTY | FDOM_NEW);
setPurged();
#ifdef FLM_CACHE_PROTECT
protectCachedItem();
#endif
flmAssert( !m_pPrevInGlobal);
}
}
/****************************************************************************
Desc: This routine initializes node cache manager.
****************************************************************************/
RCODE F_NodeCacheMgr::initCache( void)
{
RCODE rc = NE_XFLM_OK;
// Allocate the hash buckets.
if (RC_BAD( rc = f_calloc(
(FLMUINT)sizeof( F_CachedNode *) *
(FLMUINT)MIN_HASH_BUCKETS,
&m_ppHashBuckets)))
{
goto Exit;
}
m_uiNumBuckets = MIN_HASH_BUCKETS;
m_uiHashMask = m_uiNumBuckets - 1;
gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets));
// Set up the F_CachedNode object allocator
if (RC_BAD( rc = m_nodeAllocator.setup( &m_nodeRelocator,
gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager,
TRUE, sizeof( F_CachedNode), &m_Usage.slabUsage)))
{
goto Exit;
}
// Set up the buffer allocator for F_CachedNode objects
if (RC_BAD( rc = m_bufAllocator.setup(
gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, FALSE,
&m_Usage.slabUsage)))
{
goto Exit;
}
// Set up the allocator for attribute items
if( RC_BAD( rc = m_attrItemAllocator.setup( &m_attrItemRelocator,
gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, FALSE,
sizeof( F_AttrItem), &m_Usage.slabUsage)))
{
goto Exit;
}
#ifdef FLM_DEBUG
m_bDebug = TRUE;
#endif
Exit:
return( rc);
}
/****************************************************************************
Desc: Determine if a node can be moved.
Notes: This routine assumes the node cache mutex is locked
This is a static method, so there is no "this" pointer to the
F_NodeCacheMgr object.
****************************************************************************/
FLMBOOL F_NodeRelocator::canRelocate(
void * pvAlloc)
{
return( ((F_CachedNode *)pvAlloc)->nodeInUse() ? FALSE : TRUE);
}
/****************************************************************************
Desc: Fixes up all pointers needed to allow an F_CachedNode object to be
moved to a different location in memory
Notes: This routine assumes the node cache mutex is locked.
This is a static method, so there is no "this" pointer to the
F_NodeCacheMgr object.
****************************************************************************/
void F_NodeRelocator::relocate(
void * pvOldAlloc,
void * pvNewAlloc)
{
F_CachedNode * pOldNode = (F_CachedNode *)pvOldAlloc;
F_CachedNode * pNewNode = (F_CachedNode *)pvNewAlloc;
F_CachedNode ** ppBucket;
F_Database * pDatabase = pOldNode->m_pDatabase;
F_NodeCacheMgr * pNodeCacheMgr = gv_XFlmSysData.pNodeCacheMgr;
FLMBYTE * pucActualAlloc;
flmAssert( !pOldNode->nodeInUse());
flmAssert( pvNewAlloc < pvOldAlloc);
// Update the F_CachedNode pointer in the data buffer
if( pNewNode->m_pucData)
{
pucActualAlloc = getActualPointer( pNewNode->m_pucData);
flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode);
pNewNode->setNodeAndDataPtr( pucActualAlloc);
}
if( pNewNode->m_pNodeList)
{
pucActualAlloc = getActualPointer( pNewNode->m_pNodeList);
flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode);
pNewNode->setNodeListPtr( pucActualAlloc);
}
if( pNewNode->m_ppAttrList)
{
FLMUINT uiLoop;
pucActualAlloc = getActualPointer( pNewNode->m_ppAttrList);
flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode);
pNewNode->setAttrListPtr( pucActualAlloc);
for (uiLoop = 0; uiLoop < pNewNode->m_uiAttrCount; uiLoop++)
{
pNewNode->m_ppAttrList [uiLoop]->m_pCachedNode = pNewNode;
}
}
if (pNewNode->m_pPrevInDatabase)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInDatabase->unprotectCachedItem();
#endif
pNewNode->m_pPrevInDatabase->m_pNextInDatabase = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInDatabase->protectCachedItem();
#endif
}
if (pNewNode->m_pNextInDatabase)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInDatabase->unprotectCachedItem();
#endif
pNewNode->m_pNextInDatabase->m_pPrevInDatabase = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInDatabase->protectCachedItem();
#endif
}
if (pNewNode->m_pPrevInGlobal)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInGlobal->unprotectCachedItem();
#endif
pNewNode->m_pPrevInGlobal->m_pNextInGlobal = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInGlobal->protectCachedItem();
#endif
}
if (pNewNode->m_pNextInGlobal)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInGlobal->unprotectCachedItem();
#endif
pNewNode->m_pNextInGlobal->m_pPrevInGlobal = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInGlobal->protectCachedItem();
#endif
}
if (pNewNode->m_pPrevInBucket)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInBucket->unprotectCachedItem();
#endif
pNewNode->m_pPrevInBucket->m_pNextInBucket = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInBucket->protectCachedItem();
#endif
}
if (pNewNode->m_pNextInBucket)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInBucket->unprotectCachedItem();
#endif
pNewNode->m_pNextInBucket->m_pPrevInBucket = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInBucket->protectCachedItem();
#endif
}
if (pNewNode->m_pOlderVersion)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pOlderVersion->unprotectCachedItem();
#endif
pNewNode->m_pOlderVersion->m_pNewerVersion = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pOlderVersion->protectCachedItem();
#endif
}
if (pNewNode->m_pNewerVersion)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNewerVersion->unprotectCachedItem();
#endif
pNewNode->m_pNewerVersion->m_pOlderVersion = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNewerVersion->protectCachedItem();
#endif
}
if (pNewNode->m_pPrevInHeapList)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInHeapList->unprotectCachedItem();
#endif
pNewNode->m_pPrevInHeapList->m_pNextInHeapList = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInHeapList->protectCachedItem();
#endif
}
if (pNewNode->m_pNextInHeapList)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInHeapList->unprotectCachedItem();
#endif
pNewNode->m_pNextInHeapList->m_pPrevInHeapList = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInHeapList->protectCachedItem();
#endif
}
if (pNewNode->m_pPrevInOldList)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInOldList->unprotectCachedItem();
#endif
pNewNode->m_pPrevInOldList->m_pNextInOldList = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pPrevInOldList->protectCachedItem();
#endif
}
if (pNewNode->m_pNextInOldList)
{
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInOldList->unprotectCachedItem();
#endif
pNewNode->m_pNextInOldList->m_pPrevInOldList = pNewNode;
#ifdef FLM_CACHE_PROTECT
pNewNode->m_pNextInOldList->protectCachedItem();
#endif
}
if( pDatabase)
{
if (pDatabase->m_pFirstNode == pOldNode)
{
pDatabase->m_pFirstNode = pNewNode;
}
if( pDatabase->m_pLastNode == pOldNode)
{
pDatabase->m_pLastNode = pNewNode;
}
if( pDatabase->m_pLastDirtyNode == pOldNode)
{
pDatabase->m_pLastDirtyNode = pNewNode;
}
}
ppBucket = pNodeCacheMgr->nodeHash( pOldNode->m_nodeInfo.ui64NodeId);
if( *ppBucket == pOldNode)
{
*ppBucket = pNewNode;
}
if (pNodeCacheMgr->m_MRUList.m_pMRUItem == (F_CachedItem *)pOldNode)
{
pNodeCacheMgr->m_MRUList.m_pMRUItem = pNewNode;
}
if (pNodeCacheMgr->m_MRUList.m_pLRUItem == (F_CachedItem *)pOldNode)
{
pNodeCacheMgr->m_MRUList.m_pLRUItem = pNewNode;
}
if (pNodeCacheMgr->m_pHeapList == pOldNode)
{
pNodeCacheMgr->m_pHeapList = pNewNode;
}
if (pNodeCacheMgr->m_pOldList == pOldNode)
{
pNodeCacheMgr->m_pOldList = pNewNode;
}
if (pNodeCacheMgr->m_pPurgeList == pOldNode)
{
pNodeCacheMgr->m_pPurgeList = pNewNode;
}
}
/****************************************************************************
Desc: Determine if a data buffer of an F_CachedNode object can be moved.
This routine assumes that the node cache mutex is locked.
****************************************************************************/
FLMBOOL F_NodeDataRelocator::canRelocate(
void * pvAlloc)
{
F_CachedNode * pNode = *((F_CachedNode **)pvAlloc);
if( pNode->nodeInUse())
{
return( FALSE);
}
else
{
flmAssert( getActualPointer( pNode->m_pucData) == (FLMBYTE *)pvAlloc);
return( TRUE);
}
}
/****************************************************************************
Desc: Relocate the data buffer of an F_CachedNode object. This routine assumes
that the node cache mutex is locked.
****************************************************************************/
void F_NodeDataRelocator::relocate(
void * pvOldAlloc,
void * pvNewAlloc)
{
F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc);
flmAssert( !pNode->nodeInUse());
flmAssert( pvNewAlloc < pvOldAlloc);
flmAssert( getActualPointer( pNode->m_pucData) == (FLMBYTE *)pvOldAlloc);
pNode->setNodeAndDataPtr( (FLMBYTE *)pvNewAlloc);
}
/****************************************************************************
Desc: Determine if a node list of an F_CachedNode object can be moved.
This routine assumes that the node cache mutex is locked.
****************************************************************************/
FLMBOOL F_NodeListRelocator::canRelocate(
void * pvAlloc)
{
F_CachedNode * pNode = *((F_CachedNode **)pvAlloc);
if( pNode->nodeInUse())
{
return( FALSE);
}
else
{
flmAssert( getActualPointer( pNode->m_pNodeList) == (FLMBYTE *)pvAlloc);
return( TRUE);
}
}
/****************************************************************************
Desc: Relocate the node list of an F_CachedNode object. This routine assumes
that the node cache mutex is locked.
****************************************************************************/
void F_NodeListRelocator::relocate(
void * pvOldAlloc,
void * pvNewAlloc)
{
F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc);
flmAssert( !pNode->nodeInUse());
flmAssert( pvNewAlloc < pvOldAlloc);
flmAssert( getActualPointer( pNode->m_pNodeList) == (FLMBYTE *)pvOldAlloc);
pNode->setNodeListPtr( (FLMBYTE *)pvNewAlloc);
}
/****************************************************************************
Desc: Determine if an attr list of an F_CachedNode object can be moved.
This routine assumes that the node cache mutex is locked.
****************************************************************************/
FLMBOOL F_AttrListRelocator::canRelocate(
void * pvAlloc)
{
F_CachedNode * pNode = *((F_CachedNode **)pvAlloc);
if( pNode->nodeInUse())
{
return( FALSE);
}
else
{
flmAssert( getActualPointer( pNode->m_ppAttrList) == (FLMBYTE *)pvAlloc);
return( TRUE);
}
}
/****************************************************************************
Desc: Relocate the attr list of an F_CachedNode object. This routine assumes
that the node cache mutex is locked.
****************************************************************************/
void F_AttrListRelocator::relocate(
void * pvOldAlloc,
void * pvNewAlloc)
{
F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc);
flmAssert( !pNode->nodeInUse());
flmAssert( pvNewAlloc < pvOldAlloc);
flmAssert( getActualPointer( pNode->m_ppAttrList) == (FLMBYTE *)pvOldAlloc);
pNode->setAttrListPtr( (FLMBYTE *)pvNewAlloc);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL F_AttrItemRelocator::canRelocate(
void * pvAlloc)
{
F_AttrItem * pAttrItem = (F_AttrItem *)pvAlloc;
if( pAttrItem->m_pCachedNode && !pAttrItem->m_pCachedNode->nodeInUse())
{
return( TRUE);
}
return( FALSE);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_AttrItemRelocator::relocate(
void * pvOldAlloc,
void * pvNewAlloc)
{
F_AttrItem * pOldAttrItem = (F_AttrItem *)pvOldAlloc;
F_AttrItem * pNewAttrItem = (F_AttrItem *)pvNewAlloc;
F_CachedNode * pCachedNode = pNewAttrItem->m_pCachedNode;
FLMUINT uiPos;
flmAssert( !pCachedNode->nodeInUse());
// Find the new attr item slot
if (pCachedNode->getAttribute( pNewAttrItem->m_uiNameId, &uiPos) ==
pOldAttrItem)
{
pCachedNode->m_ppAttrList [uiPos] = pNewAttrItem;
}
else
{
flmAssert( 0);
}
if( pOldAttrItem->m_uiPayloadLen > sizeof( FLMBYTE *))
{
*((F_AttrItem **)(pNewAttrItem->m_pucPayload - sizeof( F_AttrItem *))) =
pNewAttrItem;
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL F_AttrBufferRelocator::canRelocate(
void * pvAlloc)
{
F_AttrItem * pAttrItem = *((F_AttrItem **)pvAlloc);
flmAssert( pAttrItem->m_pucPayload ==
(FLMBYTE *)pvAlloc + sizeof( F_AttrItem *));
if( pAttrItem->m_pCachedNode && !pAttrItem->m_pCachedNode->nodeInUse())
{
return( TRUE);
}
return( FALSE);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_AttrBufferRelocator::relocate(
void * pvOldAlloc,
void * pvNewAlloc)
{
F_AttrItem * pAttrItem = *((F_AttrItem **)pvOldAlloc);
flmAssert( !pAttrItem->m_pCachedNode->nodeInUse());
flmAssert( pvNewAlloc < pvOldAlloc);
flmAssert( pAttrItem->m_pucPayload ==
(FLMBYTE *)pvOldAlloc + sizeof( F_AttrItem *));
pAttrItem->m_pucPayload =
((FLMBYTE *)pvNewAlloc) + sizeof( F_AttrItem *);
}
/****************************************************************************
Desc: This routine resizes the hash table for the cache manager.
NOTE: This routine assumes that the node cache mutex has been locked.
****************************************************************************/
RCODE F_NodeCacheMgr::rehash( void)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiNewHashTblSize;
F_CachedNode ** ppOldHashTbl;
FLMUINT uiOldHashTblSize;
F_CachedNode ** ppBucket;
FLMUINT uiLoop;
F_CachedNode * pTmpNode;
F_CachedNode * pTmpNextNode;
FLMUINT uiOldMemSize;
uiNewHashTblSize = caGetBestHashTblSize( m_Usage.uiCount);
// At this point we better have a different hash table size
// or something is mucked up!
flmAssert( uiNewHashTblSize != m_uiNumBuckets);
// Save the old hash table and its size.
if ((ppOldHashTbl = m_ppHashBuckets) != NULL)
{
uiOldMemSize = f_msize( ppOldHashTbl);
}
else
{
uiOldMemSize = 0;
}
uiOldHashTblSize = m_uiNumBuckets;
// Allocate a new hash table.
if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_CachedNode *) *
(FLMUINT)uiNewHashTblSize, &m_ppHashBuckets)))
{
m_uiHashFailTime = FLM_GET_TIMER();
m_ppHashBuckets = ppOldHashTbl;
goto Exit;
}
// Subtract off old size and add in new size.
gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiOldMemSize);
gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets));
m_uiNumBuckets = uiNewHashTblSize;
m_uiHashMask = uiNewHashTblSize - 1;
// Relink all of the nodes into the new hash table.
for (uiLoop = 0, ppBucket = ppOldHashTbl;
uiLoop < uiOldHashTblSize;
uiLoop++, ppBucket++)
{
pTmpNode = *ppBucket;
while (pTmpNode)
{
pTmpNextNode = pTmpNode->m_pNextInBucket;
pTmpNode->linkToHashBucket();
pTmpNode = pTmpNextNode;
}
}
// Throw away the old hash table.
f_free( &ppOldHashTbl);
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine shuts down the node cache manager and frees all
resources allocated by it. NOTE: Node cache mutex must be locked
already, or we must be shutting down so that only one thread is
calling this routine.
****************************************************************************/
F_NodeCacheMgr::~F_NodeCacheMgr()
{
F_CachedItem * pItem;
F_CachedItem * pNextItem;
F_DOMNode * pTmp;
// Free the DOM Node Pool
while( (pTmp = m_pFirstNode) != NULL)
{
m_pFirstNode = m_pFirstNode->m_pNextInPool;
pTmp->m_ui32RefCnt = 0;
pTmp->m_pNextInPool = NULL;
pTmp->m_pCachedNode = NULL;
delete pTmp;
}
// Free all of the node cache objects.
pItem = m_MRUList.m_pMRUItem;
while (pItem)
{
pNextItem = pItem->m_pNextInGlobal;
((F_CachedNode *)pItem)->freeCache( FALSE);
pItem = pNextItem;
}
flmAssert( !m_MRUList.m_pMRUItem && !m_MRUList.m_pLRUItem);
// Must free those in the purge list too.
while (m_pPurgeList)
{
m_pPurgeList->freePurged();
}
// The math better be consistent!
flmAssert( m_Usage.uiCount == 0);
flmAssert( m_Usage.uiOldVerCount == 0);
flmAssert( m_Usage.uiOldVerBytes == 0);
// Free the hash bucket array
if (m_ppHashBuckets)
{
FLMUINT uiTotalMemory = f_msize( m_ppHashBuckets);
f_free( &m_ppHashBuckets);
gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiTotalMemory);
}
}
/****************************************************************************
Desc: This routine links a notify request into a node's notification list and
then waits to be notified that the event has occurred.
NOTE: This routine assumes that the node cache mutex is locked and that
it is supposed to unlock it. It will relock the mutex on its way out.
****************************************************************************/
RCODE F_NodeCacheMgr::waitNotify(
F_Db * pDb,
F_CachedNode ** ppNode)
{
return( flmWaitNotifyReq( gv_XFlmSysData.hNodeCacheMutex,
pDb->m_hWaitSem, &((*ppNode)->m_pNotifyList), ppNode));
}
/****************************************************************************
Desc: This routine notifies threads waiting for a pending read to complete.
NOTE: This routine assumes that the node cache mutex is already locked.
****************************************************************************/
void F_NodeCacheMgr::notifyWaiters(
FNOTIFY * pNotify,
F_CachedNode * pUseNode,
RCODE NotifyRc)
{
while (pNotify)
{
F_SEM hSem;
*(pNotify->pRc) = NotifyRc;
if (RC_OK( NotifyRc))
{
*((F_CachedNode **)pNotify->pvUserData) = pUseNode;
pUseNode->incrNodeUseCount();
}
hSem = pNotify->hSem;
pNotify = pNotify->pNext;
f_semSignal( hSem);
}
}
/****************************************************************************
Desc: Allocate an F_CachedNode object.
****************************************************************************/
RCODE F_NodeCacheMgr::allocNode(
F_CachedNode ** ppNode,
FLMBOOL bMutexLocked)
{
RCODE rc = NE_XFLM_OK;
FLMBOOL bUnlockMutex = FALSE;
if( !bMutexLocked)
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bUnlockMutex = TRUE;
}
if ((*ppNode = new F_CachedNode) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
// Increment statistics.
m_Usage.uiCount++;
m_Usage.uiByteCount += (*ppNode)->memSize();
if (shouldRehash( m_Usage.uiCount, m_uiNumBuckets))
{
if (checkHashFailTime( &m_uiHashFailTime))
{
if (RC_BAD( rc = rehash()))
{
goto Exit;
}
}
}
Exit:
if( bUnlockMutex)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
return( rc);
}
/****************************************************************************
Desc: Cleanup old nodes in cache that are no longer needed by any
transaction. This routine assumes that the node cache mutex
has been locked.
****************************************************************************/
void F_NodeCacheMgr::cleanupOldCache( void)
{
F_CachedNode * pCurNode;
F_CachedNode * pNextNode;
pCurNode = m_pOldList;
// Stay in the loop until we have freed all old nodes, or
// we have run through the entire list.
while( pCurNode)
{
flmAssert( pCurNode->m_ui64HighTransId != FLM_MAX_UINT64);
// Save the pointer to the next entry in the list because
// we may end up unlinking pCurNode below, in which case we would
// have lost the next node.
pNextNode = pCurNode->m_pNextInOldList;
if (!pCurNode->nodeInUse() &&
!pCurNode->readingInNode() &&
(!pCurNode->nodeLinkedToDatabase() ||
!pCurNode->m_pDatabase->neededByReadTrans(
pCurNode->m_ui64LowTransId, pCurNode->m_ui64HighTransId)))
{
pCurNode->freeNode();
}
pCurNode = pNextNode;
}
}
/****************************************************************************
Desc: Cleanup nodes that have been purged. This routine assumes that the
node cache mutex has been locked.
****************************************************************************/
void F_NodeCacheMgr::cleanupPurgedCache( void)
{
F_CachedNode * pCurNode;
F_CachedNode * pNextNode;
pCurNode = m_pPurgeList;
// Stay in the loop until we have freed all purged nodes, or
// we have run through the entire list.
while( pCurNode)
{
// Save the pointer to the next entry in the list because
// we may end up unlinking pCurNode below, in which case we would
// have lost the next node.
pNextNode = (F_CachedNode *)pCurNode->m_pNextInGlobal;
flmAssert( pCurNode->nodePurged());
if (!pCurNode->nodeInUse())
{
pCurNode->freePurged();
}
pCurNode = pNextNode;
}
}
/****************************************************************************
Desc: Reduce node cache to below the cache limit. NOTE: This routine assumes
that the node cache mutex is locked upon entering the routine, but
it may unlock and re-lock the mutex.
****************************************************************************/
void F_NodeCacheMgr::reduceCache( void)
{
F_CachedNode * pTmpNode;
F_CachedNode * pPrevNode;
F_CachedNode * pNextNode;
FLMUINT uiSlabSize;
FLMUINT uiByteThreshold;
FLMUINT uiSlabThreshold;
FLMBOOL bDoingReduce = FALSE;
// Discard items that are allocated on the heap. These are large
// allocations that could not be satisfied by the buffer allocator and have
// the side effect of causing memory fragmentation.
pTmpNode = m_pHeapList;
while( pTmpNode)
{
// Need to save the pointer to the next entry in the list because
// we may end up freeing pTmpNode below.
pNextNode = pTmpNode->m_pNextInHeapList;
// See if the item can be freed.
if( pTmpNode->canBeFreed())
{
// NOTE: This call will free the memory pointed to by
// pTmpNode. Hence, pTmpNode should NOT be used after
// this point.
pTmpNode->freeNode();
}
pTmpNode = pNextNode;
}
// If cache is not full, we are done.
if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || m_bReduceInProgress)
{
goto Exit;
}
m_bReduceInProgress = TRUE;
bDoingReduce = TRUE;
// Cleanup cache that is no longer needed by anyone
cleanupOldCache();
cleanupPurgedCache();
// Determine the cache threshold
uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1;
uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize();
// Are we over the threshold?
if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold)
{
goto Exit;
}
// Remove items from cache starting from the LRU
pTmpNode = (F_CachedNode *)m_MRUList.m_pLRUItem;
uiByteThreshold = m_Usage.uiByteCount > uiSlabSize
? m_Usage.uiByteCount - uiSlabSize
: 0;
while( pTmpNode)
{
// Need to save the pointer to the next entry in the list because
// we may end up freeing pTmpNode below.
pPrevNode = (F_CachedNode *)pTmpNode->m_pPrevInGlobal;
// See if the item can be freed.
if( pTmpNode->canBeFreed())
{
pTmpNode->freeNode();
if( m_Usage.uiByteCount <= uiByteThreshold)
{
if( pPrevNode)
{
pPrevNode->incrNodeUseCount();
}
gv_XFlmSysData.pNodeCacheMgr->defragmentMemory( TRUE);
if( !pPrevNode)
{
break;
}
pPrevNode->decrNodeUseCount();
// We're going to quit when we get under 50 percent for node cache
// or we aren't over the global limit. Note that this means we
// may quit reducing before we get under the global limit. We
// don't want to get into a situation where we are starving node
// cache because block cache is over its limit.
if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold ||
!gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit())
{
goto Exit;
}
uiByteThreshold = uiByteThreshold > uiSlabSize
? uiByteThreshold - uiSlabSize
: 0;
}
}
pTmpNode = pPrevNode;
}
Exit:
if( bDoingReduce)
{
m_bReduceInProgress = FALSE;
}
return;
}
/****************************************************************************
Desc: This routine finds a node in the node cache. If it cannot
find the node, it will return the position where the node should
be inserted.
NOTE: This routine assumes that the node cache mutex has been locked.
****************************************************************************/
void F_NodeCacheMgr::findNode(
F_Db * pDb,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
FLMUINT64 ui64VersionNeeded,
FLMBOOL bDontPoisonCache,
FLMUINT * puiNumLooks,
F_CachedNode ** ppNode,
F_CachedNode ** ppNewerNode,
F_CachedNode ** ppOlderNode)
{
F_CachedNode * pNode;
FLMUINT uiNumLooks = 0;
FLMBOOL bFound;
F_CachedNode * pNewerNode;
F_CachedNode * pOlderNode;
F_Database * pDatabase = pDb->m_pDatabase;
// Search down the hash bucket for the matching item.
Start_Find:
// NOTE: Need to always calculate hash bucket because
// the hash table may have been changed while we
// were waiting to be notified below - mutex can
// be unlocked, but it is guaranteed to be locked
// here.
pNode = *(nodeHash( ui64NodeId));
bFound = FALSE;
uiNumLooks = 1;
while (pNode &&
(pNode->m_nodeInfo.ui64NodeId != ui64NodeId ||
pNode->m_nodeInfo.uiCollection != uiCollection ||
pNode->m_pDatabase != pDatabase))
{
if ((pNode = pNode->m_pNextInBucket) != NULL)
{
uiNumLooks++;
}
}
// If we found the node, see if we have the right version.
if (!pNode)
{
pNewerNode = pOlderNode = NULL;
}
else
{
pNewerNode = NULL;
pOlderNode = pNode;
for (;;)
{
// If this one is being read in, we need to wait on it.
if (pNode->readingInNode())
{
// We need to wait for this record to be read in
// in case it coalesces with other versions, resulting
// in a version that satisfies our request.
m_uiIoWaits++;
if (RC_BAD( waitNotify( pDb, &pNode)))
{
// Don't care what error the other thread had reading
// the thing in from disk - we'll bail out and start
// our find again.
goto Start_Find;
}
// The thread doing the notify "uses" the record cache
// on behalf of this thread to prevent the record
// from being replaced after it unlocks the mutex.
// At this point, since we have locked the mutex,
// we need to release the record - because we
// will put a "use" on it below.
pNode->decrNodeUseCount();
if (pNode->nodePurged())
{
if (!pNode->nodeInUse())
{
pNode->freePurged();
}
}
// Start over with the find because the list
// structure has changed.
goto Start_Find;
}
// See if this record version is the one we need.
if (ui64VersionNeeded < pNode->m_ui64LowTransId)
{
pNewerNode = pNode;
if ((pOlderNode = pNode = pNode->m_pOlderVersion) == NULL)
{
break;
}
uiNumLooks++;
}
else if (ui64VersionNeeded <= pNode->m_ui64HighTransId)
{
// Make this the MRU record.
if (puiNumLooks)
{
if (bDontPoisonCache)
{
m_MRUList.stepUpInGlobal( (F_CachedItem *)pNode);
}
else if (pNode->m_pPrevInGlobal)
{
m_MRUList.unlinkGlobal( (F_CachedItem *)pNode);
m_MRUList.linkGlobalAsMRU( (F_CachedItem *)pNode);
}
m_Usage.uiCacheHits++;
m_Usage.uiCacheHitLooks += uiNumLooks;
}
bFound = TRUE;
break;
}
else
{
pOlderNode = pNode;
pNewerNode = pNode->m_pNewerVersion;
// Set pNode to NULL as an indicator that we did not
// find the version we needed.
pNode = NULL;
break;
}
}
}
*ppNode = pNode;
if( ppOlderNode)
{
*ppOlderNode = pOlderNode;
}
if( ppNewerNode)
{
*ppNewerNode = pNewerNode;
}
if (puiNumLooks)
{
*puiNumLooks = uiNumLooks;
}
}
/****************************************************************************
Desc: This routine links a new node into the global list and
into the correct place in its hash bucket. This routine assumes that
the node cache mutex is already locked.
****************************************************************************/
void F_NodeCacheMgr::linkIntoNodeCache(
F_CachedNode * pNewerNode,
F_CachedNode * pOlderNode,
F_CachedNode * pNode,
FLMBOOL bLinkAsMRU
)
{
if( bLinkAsMRU)
{
m_MRUList.linkGlobalAsMRU( (F_CachedItem *)pNode);
}
else
{
m_MRUList.linkGlobalAsLRU( (F_CachedItem *)pNode);
}
if (pNewerNode)
{
pNode->linkToVerList( pNewerNode, pOlderNode);
}
else
{
if (pOlderNode)
{
pOlderNode->unlinkFromHashBucket();
}
pNode->linkToHashBucket();
pNode->linkToVerList( NULL, pOlderNode);
}
}
/****************************************************************************
Desc: This routine links a new node to its F_Database according to whether
or not it is an update transaction or a read transaction.
It coalesces out any unnecessary versions. This routine assumes
that the node cache mutex is already locked.
****************************************************************************/
void F_CachedNode::linkToDatabase(
F_Database * pDatabase,
F_Db * pDb,
FLMUINT64 ui64LowTransId,
FLMBOOL bMostCurrent)
{
F_CachedNode * pTmpNode;
m_ui64LowTransId = ui64LowTransId;
// Before coalescing, link to F_Database.
// The following test determines if the node is an
// uncommitted version generated by the update transaction.
// If so, we mark it as such, and link it at the head of the
// F_Database list - so we can get rid of it quickly if we abort
// the transaction.
if (pDb->getTransType() == XFLM_UPDATE_TRANS)
{
// If we are in an update transaction, there better not
// be any newer versions in the list and the high
// transaction ID returned better be FLM_MAX_UINT64.
flmAssert( m_pNewerVersion == NULL);
setTransID( FLM_MAX_UINT64);
// If the low transaction ID is the same as the transaction,
// we may have modified this node during the transaction.
// Unfortunately, there is no sure way to tell, so we are
// forced to assume it may have been modified. If the
// transaction aborts, we will get rid if this version out
// of cache.
if (ui64LowTransId == pDb->getTransID())
{
setUncommitted();
linkToDatabaseAtHead( pDatabase);
}
else
{
unsetUncommitted();
linkToDatabaseAtEnd( pDatabase);
}
}
else
{
FLMUINT64 ui64HighTransId;
// Adjust the high transaction ID to be the same as
// the transaction ID - we may have gotten a FLM_MAX_UINT64
// back, but that is possible even if the node is
// not the most current version. Besides that, it is
// possible that in the mean time one or more update
// transactions have come along and created one or
// more newer versions of the node.
if (bMostCurrent)
{
// This may be showing up as most current simply because we have
// a newer node that was dirty - meaning it would not have been
// written to block cache yet - so our read operation would have
// read the "most current" version of the block that contains
// this node - but it isn't really the most current version of
// the node.
if (m_pNewerVersion && !m_pNewerVersion->readingInNode())
{
ui64HighTransId = m_pNewerVersion->getLowTransId() - 1;
}
else
{
ui64HighTransId = FLM_MAX_UINT64;
}
}
else
{
ui64HighTransId = pDb->getTransID();
}
setTransID( ui64HighTransId);
// For a read transaction, if there is a newer version,
// it better have a higher "low transaction ID"
#ifdef FLM_DEBUG
if (m_pNewerVersion && !m_pNewerVersion->readingInNode())
{
flmAssert( m_ui64HighTransId < m_pNewerVersion->m_ui64LowTransId);
if( m_ui64HighTransId >= m_pNewerVersion->m_ui64LowTransId)
{
checkReadFromDisk( pDb);
}
}
#endif
unsetUncommitted();
linkToDatabaseAtEnd( pDatabase);
}
// Coalesce any versions that overlap - can only
// coalesce older versions. For an updater, there
// should not be any newer versions. For a reader, it
// is impossible to know how high up it can coalesce.
// The read operation that read the node may have
// gotten back a FLM_MAX_UINT64 for its high transaction
// ID - but after that point in time, it is possible
// that one or more update transactions may have come
// along and created one or more newer versions that
// it would be incorrect to coalesce with.
// In reality, a read transaction has to ignore the
// FLM_MAX_UINT64 in the high transaction ID anyway
// because there is no way to know if it is correct.
// Coalesce older versions.
for (;;)
{
if ((pTmpNode = m_pOlderVersion) == NULL)
{
break;
}
// Stop if we encounter one that is being read in.
if (pTmpNode->readingInNode())
{
break;
}
// If there is no overlap between these two, there is
// nothing more to coalesce.
if (m_ui64LowTransId > pTmpNode->m_ui64HighTransId)
{
break;
}
if (m_ui64HighTransId <= pTmpNode->m_ui64HighTransId)
{
// This assert represents the following case,
// which should not be possible to hit:
// pOlder->m_ui64HighTransId > m_ui64HighTransId.
// This cannot be, because if pOlder has a higher
// transaction ID, we would have found it up above and
// not tried to have read it in.
flmAssert( 0);
#ifdef FLM_DEBUG
checkReadFromDisk( pDb);
#endif
}
else if (m_ui64LowTransId >= pTmpNode->m_ui64LowTransId)
{
m_ui64LowTransId = pTmpNode->m_ui64LowTransId;
pTmpNode->freeCache(
(FLMBOOL)((pTmpNode->nodeInUse() ||
pTmpNode->readingInNode())
? TRUE
: FALSE));
}
else
{
// This assert represents the following case,
// which should not be possible to hit:
// m_ui64LowTransId < pOlder->m_ui64LowTransId.
// This cannot be, because pOlder has to have been read
// in to memory by a transaction whose transaction ID is
// less than or equal to our own. That being the case,
// it would be impossible for our transaction to have
// found a version of the node that is older than pOlder.
flmAssert( 0);
#ifdef FLM_DEBUG
checkReadFromDisk( pDb);
#endif
}
}
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::resizeDataBuffer(
FLMUINT uiSize,
FLMBOOL bMutexAlreadyLocked)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiOldSize;
FLMUINT uiNewSize;
FLMUINT uiDataBufSize = calcDataBufSize( uiSize);
FLMBYTE * pucActualAlloc;
FLMBOOL bHeapAlloc = FALSE;
void * pvThis = this;
FLMBOOL bLockedMutex = FALSE;
flmAssert( !m_uiDataBufSize || m_pucData);
flmAssert( !m_uiStreamUseCount);
if( uiDataBufSize == m_uiDataBufSize)
{
goto Exit;
}
if( !bMutexAlreadyLocked)
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bLockedMutex = TRUE;
}
uiOldSize = memSize();
if (!m_pucData)
{
pucActualAlloc = NULL;
if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf(
&gv_XFlmSysData.pNodeCacheMgr->m_nodeDataRelocator,
uiDataBufSize, &pvThis, sizeof( void *),
&pucActualAlloc, &bHeapAlloc)))
{
goto Exit;
}
}
else
{
pucActualAlloc = getActualPointer( m_pucData);
if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf(
&gv_XFlmSysData.pNodeCacheMgr->m_nodeDataRelocator,
m_uiDataBufSize, uiDataBufSize, &pvThis, sizeof( void *),
&pucActualAlloc, &bHeapAlloc)))
{
goto Exit;
}
}
flmAssert( *((F_CachedNode **)pucActualAlloc) == this);
setNodeAndDataPtr( pucActualAlloc);
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
#endif
m_uiDataBufSize = uiDataBufSize;
#ifdef FLM_CACHE_PROTECT
protectCachedItem();
#endif
uiNewSize = memSize();
if (m_ui64HighTransId != FLM_MAX_UINT64)
{
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize;
}
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize;
if( bHeapAlloc)
{
linkToHeapList();
}
else if( m_uiFlags & FDOM_HEAP_ALLOC)
{
unlinkFromHeapList();
}
Exit:
if( bLockedMutex)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
flmAssert( !m_uiDataBufSize || m_pucData);
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::resizeChildElmList(
FLMUINT uiChildElmCount,
FLMBOOL bMutexAlreadyLocked)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiOldSize;
FLMBYTE * pucActualAlloc;
FLMBOOL bHeapAlloc = FALSE;
void * pvThis = this;
#ifdef FLM_CACHE_PROTECT
FLMBOOL bProtectNode = FALSE;
#endif
if( uiChildElmCount == m_nodeInfo.uiChildElmCount)
{
goto Exit;
}
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
bProtectNode = TRUE;
#endif
if( !bMutexAlreadyLocked)
{
flmAssert( nodeInUse());
}
uiOldSize = memSize();
if( !uiChildElmCount)
{
// The only thing we better be doing if we pass in a zero, is
// reducing the number of child elements. Hence, the current
// child element count better be non-zero.
flmAssert( m_nodeInfo.uiChildElmCount);
pucActualAlloc = getActualPointer( m_pNodeList);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
calcNodeListBufSize( m_nodeInfo.uiChildElmCount),
&pucActualAlloc);
}
else
{
if( !m_nodeInfo.uiChildElmCount)
{
pucActualAlloc = NULL;
rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf(
&gv_XFlmSysData.pNodeCacheMgr->m_nodeListRelocator,
calcNodeListBufSize( uiChildElmCount),
&pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc);
}
else
{
pucActualAlloc = getActualPointer( m_pNodeList);
rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf(
&gv_XFlmSysData.pNodeCacheMgr->m_nodeListRelocator,
calcNodeListBufSize( m_nodeInfo.uiChildElmCount),
calcNodeListBufSize( uiChildElmCount),
&pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc);
}
flmAssert( *((F_CachedNode **)pucActualAlloc) == this);
}
if (!bMutexAlreadyLocked)
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
}
if (RC_OK( rc))
{
FLMUINT uiNewSize;
m_nodeInfo.uiChildElmCount = uiChildElmCount;
if (m_nodeInfo.uiChildElmCount)
{
setNodeListPtr( pucActualAlloc);
}
else
{
m_pNodeList = NULL;
}
uiNewSize = memSize();
if (m_ui64HighTransId != FLM_MAX_UINT64)
{
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize;
}
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize;
if( bHeapAlloc)
{
linkToHeapList();
}
else if( m_uiFlags & FDOM_HEAP_ALLOC)
{
unlinkFromHeapList();
}
}
if (!bMutexAlreadyLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
Exit:
#ifdef FLM_CACHE_PROTECT
if( bProtectNode)
{
protectCachedItem();
}
#endif
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::resizeAttrList(
FLMUINT uiAttrCount,
FLMBOOL bMutexAlreadyLocked)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiOldSize;
FLMBYTE * pucActualAlloc;
FLMBOOL bHeapAlloc = FALSE;
void * pvThis = this;
#ifdef FLM_CACHE_PROTECT
FLMBOOL bProtectNode = FALSE;
#endif
if( uiAttrCount == m_uiAttrCount)
{
goto Exit;
}
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
bProtectNode = TRUE;
#endif
if( !bMutexAlreadyLocked)
{
flmAssert( nodeInUse());
}
uiOldSize = memSize();
if( !uiAttrCount)
{
// The only thing we better be doing if we pass in a zero, is
// reducing the number of attributes. Hence, the current
// attribute count better be non-zero.
flmAssert( m_uiAttrCount);
pucActualAlloc = getActualPointer( m_ppAttrList);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
calcAttrListBufSize( m_uiAttrCount),
&pucActualAlloc);
}
else
{
if( !m_uiAttrCount)
{
pucActualAlloc = NULL;
rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf(
&gv_XFlmSysData.pNodeCacheMgr->m_attrListRelocator,
calcAttrListBufSize( uiAttrCount),
&pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc);
}
else
{
pucActualAlloc = getActualPointer( m_ppAttrList);
rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf(
&gv_XFlmSysData.pNodeCacheMgr->m_attrListRelocator,
calcAttrListBufSize( m_uiAttrCount),
calcAttrListBufSize( uiAttrCount),
&pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc);
}
flmAssert( *((F_CachedNode **)pucActualAlloc) == this);
}
if (!bMutexAlreadyLocked)
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
}
if (RC_OK( rc))
{
FLMUINT uiNewSize;
m_uiAttrCount = uiAttrCount;
if (m_uiAttrCount)
{
setAttrListPtr( pucActualAlloc);
}
else
{
m_ppAttrList = NULL;
}
uiNewSize = memSize();
if (m_ui64HighTransId != FLM_MAX_UINT64)
{
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize;
}
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize;
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize;
if( bHeapAlloc)
{
linkToHeapList();
}
else if( m_uiFlags & FDOM_HEAP_ALLOC)
{
unlinkFromHeapList();
}
}
if (!bMutexAlreadyLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
Exit:
#ifdef FLM_CACHE_PROTECT
if( bProtectNode)
{
protectCachedItem();
}
#endif
return( rc);
}
/****************************************************************************
Desc: This routine retrieves a node from disk.
****************************************************************************/
RCODE F_NodeCacheMgr::readNodeFromDisk(
F_Db * pDb,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
F_CachedNode * pNode,
FLMUINT64 * pui64LowTransId,
FLMBOOL * pbMostCurrent)
{
RCODE rc = NE_XFLM_OK;
F_Btree * pBTree = NULL;
FLMBOOL bCloseIStream = FALSE;
F_BTreeIStream btreeIStream;
if( RC_BAD( rc = pDb->getCachedBTree( uiCollection, &pBTree)))
{
goto Exit;
}
if (RC_BAD( rc = btreeIStream.open( pDb, pBTree, XFLM_EXACT,
uiCollection, ui64NodeId, 0, 0)))
{
goto Exit;
}
bCloseIStream = TRUE;
// Read the node from the B-Tree
if (RC_BAD( rc = pNode->readNode( pDb, uiCollection,
ui64NodeId, &btreeIStream, (FLMUINT)btreeIStream.remainingSize(),
NULL)))
{
if( rc == NE_XFLM_EOF_HIT)
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
pNode->m_uiOffsetIndex = btreeIStream.getOffsetIndex();
pNode->m_ui32BlkAddr = btreeIStream.getBlkAddr();
pBTree->btGetTransInfo( pui64LowTransId, pbMostCurrent);
Exit:
if (bCloseIStream)
{
btreeIStream.close();
}
if (pBTree)
{
pBTree->Release();
}
return( rc);
}
/****************************************************************************
Desc: This routine retrieves a node from the node cache.
****************************************************************************/
RCODE F_NodeCacheMgr::retrieveNode(
F_Db * pDb,
FLMUINT uiCollection, // Collection node is in.
FLMUINT64 ui64NodeId, // Node ID
F_DOMNode ** ppDOMNode
)
{
RCODE rc = NE_XFLM_OK;
FLMBOOL bMutexLocked = FALSE;
F_Database * pDatabase = pDb->m_pDatabase;
F_CachedNode * pNode;
F_CachedNode * pNewerNode;
F_CachedNode * pOlderNode;
FLMUINT64 ui64LowTransId;
FLMBOOL bMostCurrent;
FLMUINT64 ui64CurrTransId;
FNOTIFY * pNotify;
FLMUINT uiNumLooks;
FLMBOOL bDontPoisonCache = pDb->m_uiFlags & FDB_DONT_POISON_CACHE
? TRUE
: FALSE;
if (RC_BAD( rc = pDb->checkState( __FILE__, __LINE__)))
{
goto Exit;
}
// Get the current transaction ID
flmAssert( pDb->m_eTransType != XFLM_NO_TRANS);
ui64CurrTransId = pDb->getTransID();
flmAssert( ui64NodeId != 0);
// Lock the node cache mutex
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bMutexLocked = TRUE;
// Reset the DB's inactive time
pDb->m_uiInactiveTime = 0;
Start_Find:
findNode( pDb, uiCollection, ui64NodeId,
ui64CurrTransId, bDontPoisonCache,
&uiNumLooks, &pNode,
&pNewerNode, &pOlderNode);
if (pNode)
{
// Have the DOM Node point to the node we found
goto Exit1;
}
// Did not find the node, fetch from disk
// Increment the number of faults only if we retrieve the record from disk.
m_Usage.uiCacheFaults++;
m_Usage.uiCacheFaultLooks += uiNumLooks;
// Create a place holder for the object.
if (RC_BAD( rc = allocNode( &pNode, TRUE)))
{
goto Exit;
}
#ifdef FLM_CACHE_PROTECT
pNode->unprotectCachedItem();
#endif
pNode->m_nodeInfo.ui64NodeId = ui64NodeId;
pNode->m_nodeInfo.uiCollection = uiCollection;
// Set the F_Database so that other threads looking for this node in
// cache will find it and wait until the read has completed. If
// the F_Database is not set, other threads will attempt their own read,
// because they won't match a NULL F_Database. The result of not setting
// the F_Database is that multiple copies of the same version of a particular
// node could end up in cache.
pNode->m_pDatabase = pDatabase;
linkIntoNodeCache( pNewerNode, pOlderNode, pNode, !bDontPoisonCache);
pNode->setReadingIn();
pNode->incrNodeUseCount();
pNode->m_pNotifyList = NULL;
// Unlock mutex before reading in from disk.
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
bMutexLocked = FALSE;
// Read node from disk.
rc = readNodeFromDisk( pDb, uiCollection, ui64NodeId, pNode,
&ui64LowTransId, &bMostCurrent);
// Relock mutex
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bMutexLocked = TRUE;
// If read was successful, link the node to its place in
// the F_Database list and coalesce any versions that overlap
// this one.
if (RC_OK( rc))
{
pNode->linkToDatabase(
pDb->m_pDatabase, pDb, ui64LowTransId, bMostCurrent);
}
pNode->unsetReadingIn();
// Notify any threads waiting for the read to complete.
pNotify = pNode->m_pNotifyList;
pNode->m_pNotifyList = NULL;
if (pNotify)
{
notifyWaiters( pNotify,
(F_CachedNode *)((RC_BAD( rc))
? (F_CachedNode *)NULL
: pNode), rc);
}
pNode->decrNodeUseCount();
#ifdef FLM_CACHE_PROTECT
pNode->protectCachedItem();
#endif
// If we did not succeed, free the F_CachedNode structure.
if (RC_BAD( rc))
{
pNode->freeCache( FALSE);
goto Exit;
}
// If this item was purged while we were reading it in,
// start over with the search.
if (pNode->nodePurged())
{
if (!pNode->nodeInUse())
{
pNode->freePurged();
}
// Start over with the find - this one has
// been marked for purging.
goto Start_Find;
}
Exit1:
// Have the DOM Node point to the node we read in from disk
if( *ppDOMNode == NULL)
{
if( RC_BAD( rc = allocDOMNode( ppDOMNode)))
{
goto Exit;
}
}
if ( (*ppDOMNode)->m_pCachedNode)
{
(*ppDOMNode)->m_pCachedNode->decrNodeUseCount();
}
(*ppDOMNode)->m_pCachedNode = pNode;
pNode->incrNodeUseCount();
Exit:
if (bMutexLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
return( rc);
}
/****************************************************************************
Desc: This routine creates a node into the node cache. This is ONLY
called when a new node is being created.
****************************************************************************/
RCODE F_NodeCacheMgr::createNode(
F_Db * pDb,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
F_DOMNode ** ppDOMNode)
{
RCODE rc = NE_XFLM_OK;
F_Database * pDatabase = pDb->m_pDatabase;
F_COLLECTION * pCollection;
F_CachedNode * pNode = NULL;
F_CachedNode * pNewerNode = NULL;
F_CachedNode * pOlderNode = NULL;
FLMBOOL bMutexLocked = FALSE;
// A zero ui64NodeId means we are to use the next node ID for the
// collection.
if( !ui64NodeId)
{
if (RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection)))
{
goto Exit;
}
ui64NodeId = pCollection->ui64NextNodeId;
// Lock the node cache mutex
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bMutexLocked = TRUE;
}
else
{
// Lock the node cache mutex
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bMutexLocked = TRUE;
// See if we can find the node in cache
findNode( pDb, uiCollection, ui64NodeId,
pDb->m_ui64CurrTransID, TRUE, NULL, &pNode,
&pNewerNode, &pOlderNode);
if (pNode)
{
// If we found the last committed version, instead of replacing it,
// we want to change its high transaction ID, and go create a new
// node to put in cache.
if (pNode->m_ui64LowTransId < pDb->m_ui64CurrTransID)
{
// pOlderNode and pNode should be the same at this point if we
// found something. Furthermore, the high transaction ID on what
// we found better be -1 - most current version.
flmAssert( pOlderNode == pNode);
flmAssert( pOlderNode->m_ui64HighTransId == FLM_MAX_UINT64);
pOlderNode->setTransID( (pDb->m_ui64CurrTransID - 1));
flmAssert( pOlderNode->m_ui64HighTransId >=
pOlderNode->m_ui64LowTransId);
pOlderNode->setUncommitted();
pOlderNode->setLatestVer();
pOlderNode->unlinkFromDatabase();
pOlderNode->linkToDatabaseAtHead( pDatabase);
}
else
{
// Found latest UNCOMMITTED VERSION
pNode = NULL;
rc = RC_SET_AND_ASSERT( NE_XFLM_EXISTS);
goto Exit;
}
}
}
// We are positioned to insert the new node. For an update, it
// must always be the newest version.
flmAssert( !pNewerNode);
// Create a new object.
if (RC_BAD( rc = allocNode( &pNode, bMutexLocked)))
{
goto Exit;
}
#ifdef FLM_CACHE_PROTECT
pNode->unprotectCachedItem();
#endif
pNode->m_nodeInfo.ui64NodeId = ui64NodeId;
pNode->m_nodeInfo.uiCollection = uiCollection;
pNode->m_uiOffsetIndex = 0;
pNode->m_ui32BlkAddr = 0;
// NOTE: Not everything is initialized in pNode at this point, but
// no other thread should be accessing it anyway. The caller of this
// function must ensure that all of the necessary items get set before
// releasing the node.
// Set the F_Database so that other threads looking for this node in
// cache will find it and wait until the read has completed. If
// the F_Database is not set, other threads will attempt their own read,
// because they won't match a NULL F_Database. The result of not setting
// the F_Database is that multiple copies of the same version of a particular
// node could end up in cache.
pNode->m_pDatabase = pDatabase;
linkIntoNodeCache( pNewerNode, pOlderNode, pNode, TRUE);
// Link the node to its place in the F_Database list
pNode->linkToDatabase( pDatabase, pDb, pDb->m_ui64CurrTransID, TRUE);
// Have the DOM node point to the node we created
flmAssert( *ppDOMNode == NULL);
if( RC_BAD( rc = allocDOMNode( ppDOMNode)))
{
goto Exit;
}
if ( (*ppDOMNode)->m_pCachedNode)
{
(*ppDOMNode)->m_pCachedNode->decrNodeUseCount();
}
(*ppDOMNode)->m_pCachedNode = pNode;
pNode->incrNodeUseCount();
#ifdef FLM_CACHE_PROTECT
pNode->protectCachedItem();
#endif
Exit:
if (bMutexLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
return( rc);
}
/****************************************************************************
Desc: This routine makes a writeable copy of the node pointed to by F_DOMNode.
****************************************************************************/
RCODE F_NodeCacheMgr::_makeWriteCopy(
F_Db * pDb,
F_CachedNode ** ppCachedNode)
{
RCODE rc = NE_XFLM_OK;
F_Database * pDatabase = pDb->m_pDatabase;
F_CachedNode * pNewerNode = NULL;
F_CachedNode * pOlderNode = *ppCachedNode;
FLMBOOL bMutexLocked = FALSE;
#ifdef FLM_CACHE_PROTECT
FLMBOOL bProtectNewerNode = FALSE;
#endif
flmAssert( pOlderNode->m_ui64HighTransId == FLM_MAX_UINT64);
flmAssert( !pOlderNode->m_pNewerVersion);
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
bMutexLocked = TRUE;
// Create a new object.
if (RC_BAD( rc = allocNode( &pNewerNode, TRUE)))
{
goto Exit;
}
// If we found the last committed version, instead of replacing it,
// we want to change its high transaction ID, and go create a new
// node to put in cache.
// Although this routine could be written to not do anything if we
// are already on the uncommitted version of the node, for performance
// reasons, we would prefer that they make the check on the outside
// before calling this routine.
flmAssert( pOlderNode->m_ui64LowTransId < pDb->m_ui64CurrTransID);
pOlderNode->setTransID( pDb->m_ui64CurrTransID - 1);
flmAssert( pOlderNode->m_ui64HighTransId >= pOlderNode->m_ui64LowTransId);
pOlderNode->setUncommitted();
pOlderNode->setLatestVer();
pOlderNode->unlinkFromDatabase();
pOlderNode->linkToDatabaseAtHead( pDatabase);
#ifdef FLM_CACHE_PROTECT
pNewerNode->unprotectCachedItem();
bProtectNewerNode = TRUE;
#endif
pNewerNode->m_pDatabase = pDatabase;
pNewerNode->m_uiFlags = pOlderNode->m_uiFlags;
pNewerNode->m_uiOffsetIndex = pOlderNode->m_uiOffsetIndex;
pNewerNode->m_ui32BlkAddr = pOlderNode->m_ui32BlkAddr;
if( pNewerNode->m_uiFlags & FDOM_HEAP_ALLOC)
{
pNewerNode->m_uiFlags &= ~FDOM_HEAP_ALLOC;
}
f_memcpy( &pNewerNode->m_nodeInfo,
&pOlderNode->m_nodeInfo, sizeof( F_NODE_INFO));
if (pNewerNode->m_uiFlags & (FDOM_SIGNED_QUICK_VAL | FDOM_UNSIGNED_QUICK_VAL))
{
pNewerNode->m_numberVal = pOlderNode->m_numberVal;
}
if( pNewerNode->m_uiFlags & FDOM_HAVE_CELM_LIST)
{
// Need to set to zero, because we really haven't allocated
// space for it yet.
pNewerNode->m_nodeInfo.uiChildElmCount = 0;
if( pOlderNode->m_nodeInfo.uiChildElmCount)
{
if( RC_BAD( rc = pNewerNode->resizeChildElmList(
pOlderNode->m_nodeInfo.uiChildElmCount, TRUE)))
{
goto Exit;
}
f_memcpy( pNewerNode->m_pNodeList, pOlderNode->m_pNodeList,
sizeof( NODE_ITEM) * pNewerNode->m_nodeInfo.uiChildElmCount);
}
}
else
{
flmAssert( !pOlderNode->m_nodeInfo.uiChildElmCount);
}
if( !(pNewerNode->m_uiFlags & FDOM_VALUE_ON_DISK))
{
if( pNewerNode->getDataLength())
{
if (RC_BAD( rc = pNewerNode->resizeDataBuffer(
pNewerNode->getDataLength(), TRUE)))
{
goto Exit;
}
f_memcpy( pNewerNode->getDataPtr(), pOlderNode->getDataPtr(),
pNewerNode->getDataLength());
}
}
else
{
flmAssert( pNewerNode->getDataLength());
flmAssert( !pNewerNode->m_nodeInfo.uiChildElmCount);
}
if( pOlderNode->m_uiAttrCount)
{
if( RC_BAD( rc = pNewerNode->importAttributeList(
pDb, pOlderNode, TRUE)))
{
goto Exit;
}
}
linkIntoNodeCache( NULL, pOlderNode, pNewerNode, TRUE);
// Link the node to its place in the F_Database list
pNewerNode->linkToDatabase( pDatabase, pDb, pDb->m_ui64CurrTransID, TRUE);
// Update the node pointer passed into the routine
if( *ppCachedNode)
{
(*ppCachedNode)->decrNodeUseCount();
}
*ppCachedNode = pNewerNode;
pNewerNode->incrNodeUseCount();
#ifdef FLM_CACHE_PROTECT
pNewerNode->protectCachedItem();
bProtectNewerNode = FALSE;
#endif
// Set pNewerNode to NULL so it won't get freed at Exit
pNewerNode = NULL;
Exit:
// A non-NULL pNewerNode means there was an error of some kind where we will
// need to free up the cached item.
if (pNewerNode)
{
flmAssert( RC_BAD( rc));
delete pNewerNode;
}
if( bMutexLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
return( rc);
}
/****************************************************************************
Desc: This routine is called to remove a node from cache. If this is
an uncommitted version of the node, it should remove that version
from cache. If the last committed version is in
cache, it should set the high transaction ID on that version to be
one less than the transaction ID of the update transaction.
****************************************************************************/
void F_NodeCacheMgr::removeNode(
F_Db * pDb,
F_CachedNode * pNode,
FLMBOOL bDecrementUseCount,
FLMBOOL bMutexLocked)
{
F_Database * pDatabase = pDb->m_pDatabase;
flmAssert( pNode);
// Lock the mutex
if( !bMutexLocked)
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
}
// Decrement the node use count if told to by the caller.
if (bDecrementUseCount)
{
pNode->decrNodeUseCount();
}
// Unset the new and dirty flags
pNode->unsetNodeDirtyAndNew( pDb, TRUE);
// Determine if pNode is the last committed version
// or a node that was added by this same transaction.
// If it is the last committed version, set its high transaction ID.
// Otherwise, remove the node from cache.
if (pNode->m_ui64LowTransId < pDb->m_ui64CurrTransID)
{
// The high transaction ID on pNode better be -1 - most current version.
flmAssert( pNode->m_ui64HighTransId == FLM_MAX_UINT64);
pNode->setTransID( (pDb->m_ui64CurrTransID - 1));
flmAssert( pNode->m_ui64HighTransId >= pNode->m_ui64LowTransId);
pNode->setUncommitted();
pNode->setLatestVer();
pNode->unlinkFromDatabase();
pNode->linkToDatabaseAtHead( pDatabase);
}
else
{
pNode->freeCache( pNode->nodeInUse());
}
if( !bMutexLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
}
/****************************************************************************
Desc: This routine is called to remove a node from cache.
****************************************************************************/
void F_NodeCacheMgr::removeNode(
F_Db * pDb,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId)
{
F_CachedNode * pNode;
// Lock the mutex
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
// Find the node in cache
findNode( pDb, uiCollection, ui64NodeId,
pDb->m_ui64CurrTransID, TRUE, NULL, &pNode, NULL, NULL);
if( pNode)
{
removeNode( pDb, pNode, FALSE, TRUE);
}
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
/****************************************************************************
Desc: This routine is called when an F_Database object is going to be removed
from the shared memory area. At that point, we also need to get rid
of all nodes that have been cached for that F_Database.
****************************************************************************/
void F_Database::freeNodeCache( void)
{
FLMUINT uiNumFreed = 0;
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
while (m_pFirstNode)
{
m_pFirstNode->freeCache( m_pFirstNode->nodeInUse());
// Release the CPU every 100 nodes freed.
if (++uiNumFreed == 100)
{
f_yieldCPU();
uiNumFreed = 0;
}
}
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
/****************************************************************************
Desc: This routine is called when an update transaction aborts. At that
point, we need to get rid of any uncommitted versions of nodes in
the node cache.
****************************************************************************/
void F_Database::freeModifiedNodes(
F_Db * pDb,
FLMUINT64 ui64OlderTransId)
{
F_CachedNode * pNode;
F_CachedNode * pOlderVersion;
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
pNode = m_pFirstNode;
while (pNode)
{
if (pNode->nodeUncommitted())
{
if (pNode->nodeIsLatestVer())
{
pNode->setTransID( FLM_MAX_UINT64);
pNode->unsetUncommitted();
pNode->unsetLatestVer();
pNode->unlinkFromDatabase();
pNode->linkToDatabaseAtEnd( this);
}
else
{
// Save the older version - we may be changing its
// high transaction ID back to FLM_MAX_UINT64
pOlderVersion = pNode->m_pOlderVersion;
// Clear the dirty and new flags
pNode->unsetNodeDirtyAndNew( pDb, TRUE);
// Free the uncommitted version.
pNode->freeCache(
(FLMBOOL)((pNode->nodeInUse() ||
pNode->readingInNode())
? TRUE
: FALSE));
// If the older version has a high transaction ID that
// is exactly one less than our current transaction,
// it is the most current version. Hence, we need to
// change its high transaction ID back to FLM_MAX_UINT64.
if (pOlderVersion &&
pOlderVersion->m_ui64HighTransId == ui64OlderTransId)
{
pOlderVersion->setTransID( FLM_MAX_UINT64);
}
}
pNode = m_pFirstNode;
}
else
{
// We can stop when we hit a committed version, because
// uncommitted versions are always linked in together at
// the head of the list.
break;
}
}
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
/****************************************************************************
Desc: This routine is called when an update transaction commits. At that
point, we need to unset the "uncommitted" flag on any nodes
currently in node cache for the F_Database object.
****************************************************************************/
void F_Database::commitNodeCache( void)
{
F_CachedNode * pNode;
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
pNode = m_pFirstNode;
while (pNode)
{
if (pNode->nodeUncommitted())
{
pNode->unsetUncommitted();
pNode->unsetLatestVer();
pNode = pNode->m_pNextInDatabase;
}
else
{
// We can stop when we hit a committed version, because
// uncommitted versions are always linked in together at
// the head of the list.
break;
}
}
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
/****************************************************************************
Desc: This routine is called when a collection in the database is deleted.
All nodes in node cache that are in that collection must be
removed from cache.
****************************************************************************/
void F_Db::removeCollectionNodes(
FLMUINT uiCollection,
FLMUINT64 ui64TransId)
{
F_CachedNode * pNode;
F_CachedNode * pNextNode;
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
pNode = m_pDatabase->m_pFirstNode;
// Stay in the loop until we have freed all nodes in the
// collection
while (pNode)
{
// Save the pointer to the previous entry in the list because
// we may end up unlinking pNode below, in which case we would
// have lost the previous entry.
pNextNode = pNode->m_pNextInDatabase;
// Only look at nodes in this collection
if (pNode->m_nodeInfo.uiCollection == uiCollection)
{
flmAssert( pNode->m_pDatabase == m_pDatabase);
// Only look at the most current versions.
if (pNode->m_ui64HighTransId == FLM_MAX_UINT64)
{
// Better not be a newer version.
flmAssert( pNode->m_pNewerVersion == NULL);
if (pNode->m_ui64LowTransId < ui64TransId)
{
// This version was not added or modified by this
// transaction so it's high transaction ID should simply
// be set to one less than the current transaction ID.
pNode->setTransID( ui64TransId - 1);
flmAssert( pNode->m_ui64HighTransId >= pNode->m_ui64LowTransId);
pNode->setUncommitted();
pNode->setLatestVer();
pNode->unlinkFromDatabase();
pNode->linkToDatabaseAtHead( m_pDatabase);
}
else
{
// The node was added or modified in this
// transaction. Simply remove it from cache.
pNode->freeCache( pNode->nodeInUse());
}
}
else
{
// If not most current version, the node's high transaction
// ID better already be less than transaction ID.
flmAssert( pNode->m_ui64HighTransId < ui64TransId);
}
}
pNode = pNextNode;
}
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL F_CachedNode::findChildElm(
FLMUINT uiChildElmNameId,
FLMUINT * puiInsertPos)
{
FLMBOOL bFound = FALSE;
FLMUINT uiLoop;
NODE_ITEM * pChildElmNode;
FLMUINT uiTblSize;
FLMUINT uiLow;
FLMUINT uiMid;
FLMUINT uiHigh;
FLMUINT uiTblNameId;
// If the child element count is <= 4, do a sequential search through
// the array. Otherwise, do a binary search.
if ((uiTblSize = m_nodeInfo.uiChildElmCount) <= 4)
{
for (uiLoop = 0, pChildElmNode = m_pNodeList;
uiLoop < m_nodeInfo.uiChildElmCount &&
pChildElmNode->uiNameId < uiChildElmNameId;
uiLoop++, pChildElmNode++)
{
;
}
if (uiLoop < m_nodeInfo.uiChildElmCount)
{
*puiInsertPos = uiLoop;
if (pChildElmNode->uiNameId == uiChildElmNameId)
{
bFound = TRUE;
}
}
else
{
*puiInsertPos = uiLoop;
}
}
else
{
uiHigh = --uiTblSize;
uiLow = 0;
for (;;)
{
uiMid = (uiLow + uiHigh) / 2;
uiTblNameId = m_pNodeList [uiMid].uiNameId;
if (uiTblNameId == uiChildElmNameId)
{
// Found Match
*puiInsertPos = uiMid;
bFound = TRUE;
goto Exit;
}
// Check if we are done
if (uiLow >= uiHigh)
{
// Done, item not found
*puiInsertPos = (uiChildElmNameId < uiTblNameId)
? uiMid
: uiMid + 1;
goto Exit;
}
if (uiChildElmNameId < uiTblNameId)
{
if (uiMid == 0)
{
*puiInsertPos = 0;
goto Exit;
}
uiHigh = uiMid - 1;
}
else
{
if (uiMid == uiTblSize)
{
*puiInsertPos = uiMid + 1;
goto Exit;
}
uiLow = uiMid + 1;
}
}
}
Exit:
return( bFound);
}
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_DEBUG
void F_CachedNode::checkReadFromDisk(
F_Db * pDb)
{
FLMUINT64 ui64LowTransId;
FLMBOOL bMostCurrent;
RCODE rc;
// Need to unlock the node cache mutex before doing the read.
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
rc = gv_XFlmSysData.pNodeCacheMgr->readNodeFromDisk( pDb,
m_nodeInfo.uiCollection, m_nodeInfo.ui64NodeId,
this, &ui64LowTransId, &bMostCurrent);
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
void F_CachedNode::setNodeDirty(
F_Db * pDb,
FLMBOOL bNew)
{
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
#endif
if (!nodeIsDirty())
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
// Should already be the uncommitted version.
flmAssert( nodeUncommitted());
// Should NOT be the latest version - those cannot
// be set to dirty. - latest ver flag is only set
// for nodes that should be returned to being the
// latest version of the node if the transaction
// aborts.
flmAssert( !nodeIsLatestVer());
// Unlink from its database, set the dirty flag,
// and relink at the head.
unlinkFromDatabase();
if (bNew)
{
m_uiFlags |= (FDOM_DIRTY | FDOM_NEW);
}
else
{
m_uiFlags |= FDOM_DIRTY;
}
linkToDatabaseAtHead( pDb->m_pDatabase);
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
pDb->m_uiDirtyNodeCount++;
}
else if (bNew)
{
m_uiFlags |= FDOM_NEW;
}
#ifdef FLM_CACHE_PROTECT
protectCachedItem();
#endif
}
/****************************************************************************
Desc:
****************************************************************************/
void F_CachedNode::unsetNodeDirtyAndNew(
F_Db * pDb,
FLMBOOL bMutexAlreadyLocked)
{
#ifdef FLM_CACHE_PROTECT
unprotectCachedItem();
#endif
// When outputting a binary or text stream, it is possible that the
// dirty flag was unset when the last buffer was output
if (nodeIsDirty())
{
if( !bMutexAlreadyLocked)
{
f_mutexLock( gv_XFlmSysData.hNodeCacheMutex);
}
// Unlink from its database, unset the dirty flag,
// and relink at the head.
unlinkFromDatabase();
if( m_uiFlags & FDOM_DIRTY)
{
flmAssert( pDb->m_uiDirtyNodeCount);
pDb->m_uiDirtyNodeCount--;
}
m_uiFlags &= ~(FDOM_DIRTY | FDOM_NEW);
linkToDatabaseAtHead( pDb->m_pDatabase);
if( !bMutexAlreadyLocked)
{
f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex);
}
}
#ifdef FLM_CACHE_PROTECT
protectCachedItem();
#endif
}
/*****************************************************************************
Desc: Calculate the SEN length of a number and add it to the node info item.
******************************************************************************/
FINLINE void flmAddInfoSenLen(
XFLM_NODE_INFO_ITEM * pInfoItem,
FLMUINT64 ui64Num,
FLMUINT * puiTotalOverhead)
{
FLMUINT uiSenLen = flmGetSENByteCount( ui64Num);
pInfoItem->ui64Bytes += (FLMUINT64)uiSenLen;
pInfoItem->ui64Count++;
(*puiTotalOverhead) += uiSenLen;
}
/*****************************************************************************
Desc: Node header format
******************************************************************************/
// Header size 1 byte
//
// Node and data type 1 byte (bits = HDDDNNNN)
// H = Have data (1 bit)
// D = Data type (3 bits)
// N = Node type (4 bits)
//
// Storage Flags 1-5 bytes (typically 1 byte)
// NSF_HAVE_BASE_ID_BIT
// NSF_HAVE_META_VALUE_BIT
// NSF_HAVE_SIBLINGS_BIT
// NSF_HAVE_CHILDREN_BIT
// NSF_HAVE_ATTR_LIST_BIT
// NSF_HAVE_CELM_LIST_BIT
// NSF_HAVE_DATA_LEN_BIT
//
// NSF_EXT_HAVE_DCHILD_COUNT_BIT
// NSF_EXT_READ_ONLY_BIT
// NSF_EXT_CANNOT_DELETE_BIT
// NSF_EXT_PREFIX_BIT
// NSF_EXT_ENCRYPTED_BIT
// NSF_EXT_ANNOTATION_BIT
// NSF_EXT_QUARANTINED_BIT
//
// Document ID 0-9 byte SEN
// Base ID 1-9 byte SEN - if NSF_HAVE_BASE_ID_BIT is set
// Parent ID 0-9 byte SEN (offset from base)
// Name ID 0-5 byte SEN
// Prefix ID 0-5 byte SEN
// Meta value 0-9 byte SEN - if NSF_HAVE_META_VALUE_BIT is set
// Prev+Next Siblings 0-9 + 0-9 byte SEN (offset from base) - if NSF_HAVE_SIBLINGS_BIT is set
// First+Last Child ID 0-9 + 0-9 byte SEN (offset from base) - if NSF_HAVE_CHILDREN_BIT is set
// Data node child count 0-5 byte SEN - If NSF_HAVE_CHILDREN_BIT and NSF_EXT_HAVE_DCHILD_COUNT_BIT is set
// Child Element Count 0-5 byte SEN - if NSF_HAVE_CELM_LIST_BIT is set
// Encryption ID 0-5 byte SEN - if NSF_EXT_ENCRYPTED_BIT set
// Annotation node 0-9 byte SEN (offset from base) - if NSF_EXT_ANNOTATION_BIT is set
// Data length 0-5 byte SEN - if NSF_HAVE_DATA_LEN_BIT is set
//
// After the "core" header, one or more of the following may be present:
//
// If HAVE_CELM_LIST_BIT is set and we actually have a non-zero child element count:
// child element list [0-5 SEN for element ID + 0-9 SEN for node id]...
//
// IV 0 if no encryption, 8 or 16 bytes if encrypting
// Value 0+ bytes
/*****************************************************************************
Desc: Creates a variable-sized header for the node and copies it into
the supplied buffer. The buffer must be sized to allow at least
MAX_DOM_HEADER_SIZE bytes.
******************************************************************************/
RCODE F_CachedNode::headerToBuf(
FLMBOOL bFixedSizeHeader,
FLMBYTE * pucBuf,
FLMUINT * puiHeaderStorageSize,
XFLM_NODE_INFO * pNodeInfo,
F_Db * pDb)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE * pucStart = pucBuf;
FLMUINT uiLoop;
FLMUINT uiFlagsLen;
FLMUINT uiStorageFlags = 0;
FLMUINT uiDataChildCount = getDataChildCount();
FLMUINT uiEncDefId = 0;
FLMUINT uiDataLength = getDataLength();
FLMUINT uiNameId = getNameId();
FLMUINT uiPrefixId = getPrefixId();
FLMUINT uiFlags = m_uiFlags;
eDomNodeType eNodeType = getNodeType();
FLMUINT64 ui64NodeId = m_nodeInfo.ui64NodeId;
FLMUINT64 ui64BaseId = ui64NodeId;
FLMUINT64 ui64DocId = getDocumentId();
FLMUINT64 ui64ParentId = getParentId();
FLMUINT64 ui64FirstChildId = getFirstChildId();
FLMUINT64 ui64LastChildId = getLastChildId();
FLMUINT64 ui64PrevSibId = getPrevSibId();
FLMUINT64 ui64NextSibId = getNextSibId();
FLMUINT64 ui64AnnotationId = getAnnotationId();
FLMUINT64 ui64MetaValue = getMetaValue();
FLMUINT64 ui64Tmp;
FLMBYTE ucTmpSEN[ FLM_MAX_SEN_LEN];
FLMBYTE * pucTmpSEN;
FLMUINT uiTotalOverhead = 0;
if( !ui64NodeId)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP);
goto Exit;
}
// Storage flags used by fixed and variable size headers
if( uiFlags & FDOM_READ_ONLY)
{
uiStorageFlags |= NSF_EXT_READ_ONLY_BIT;
}
if( uiFlags & FDOM_CANNOT_DELETE)
{
uiStorageFlags |= NSF_EXT_CANNOT_DELETE_BIT;
}
if( uiFlags & FDOM_QUARANTINED)
{
uiStorageFlags |= NSF_EXT_QUARANTINED_BIT;
}
if( uiFlags & FDOM_HAVE_CELM_LIST)
{
uiStorageFlags |= NSF_HAVE_CELM_LIST_BIT;
}
if( uiFlags & FDOM_NAMESPACE_DECL)
{
uiStorageFlags |= NSF_EXT_NAMESPACE_DECL_BIT;
}
if( m_uiAttrCount)
{
flmAssert( eNodeType == ELEMENT_NODE);
uiStorageFlags |= NSF_HAVE_ATTR_LIST_BIT;
}
if( uiDataLength)
{
if( (uiEncDefId = getEncDefId()) != 0)
{
uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT;
}
}
// Output the header
if( bFixedSizeHeader)
{
if (pucBuf)
{
flmAssert( !pNodeInfo && !pDb && puiHeaderStorageSize);
// Set the header size bytes
*pucBuf++ = XFLM_FIXED_SIZE_HEADER_TOKEN;
// Encode the node type
*pucBuf++ = ((((FLMBYTE)m_nodeInfo.uiDataType) & 0x07) << 4) |
(((FLMBYTE)eNodeType) & 0x0F);
// Document ID
U642FBA( ui64DocId, pucBuf);
pucBuf += sizeof( FLMUINT64);
// Parent ID
U642FBA( ui64ParentId, pucBuf);
pucBuf += sizeof( FLMUINT64);
// Name ID
UD2FBA( uiNameId, pucBuf);
pucBuf += sizeof( FLMUINT32);
// Prefix ID
UD2FBA( uiPrefixId, pucBuf);
pucBuf += sizeof( FLMUINT32);
// Metavalue
U642FBA( ui64MetaValue, pucBuf);
pucBuf += sizeof( FLMUINT64);
// Previous and next siblings
U642FBA( ui64PrevSibId, pucBuf);
pucBuf += sizeof( FLMUINT64);
U642FBA( ui64NextSibId, pucBuf);
pucBuf += sizeof( FLMUINT64);
// First and last children
U642FBA( ui64FirstChildId, pucBuf);
pucBuf += sizeof( FLMUINT64);
U642FBA( ui64LastChildId, pucBuf);
pucBuf += sizeof( FLMUINT64);
// Data child count
UD2FBA( uiDataChildCount, pucBuf);
pucBuf += sizeof( FLMUINT32);
// Child element count
UD2FBA( m_nodeInfo.uiChildElmCount, pucBuf);
pucBuf += sizeof( FLMUINT32);
// Data length
UD2FBA( uiDataLength, pucBuf);
pucBuf += sizeof( FLMUINT32);
// Encryption definition ID
UD2FBA( uiEncDefId, pucBuf);
pucBuf += sizeof( FLMUINT32);
// Annotation ID
U642FBA( ui64AnnotationId, pucBuf);
pucBuf += sizeof( FLMUINT64);
// Storage flags
UD2FBA( uiStorageFlags, pucBuf);
pucBuf += sizeof( FLMUINT32);
flmAssert( (FLMUINT)(pucBuf - pucStart) == FIXED_DOM_HEADER_SIZE);
*puiHeaderStorageSize = FIXED_DOM_HEADER_SIZE;
}
else
{
flmAssert( pNodeInfo && pDb && !puiHeaderStorageSize);
pNodeInfo->headerSize.ui64Bytes++;
pNodeInfo->headerSize.ui64Count++;
pNodeInfo->nodeAndDataType.ui64Bytes++;
pNodeInfo->nodeAndDataType.ui64Count++;
pNodeInfo->documentId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->documentId.ui64Count++;
pNodeInfo->parentId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->parentId.ui64Count++;
pNodeInfo->nameId.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->nameId.ui64Count++;
pNodeInfo->prefixId.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->prefixId.ui64Count++;
pNodeInfo->metaValue.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->metaValue.ui64Count++;
pNodeInfo->prevSibId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->prevSibId.ui64Count++;
pNodeInfo->nextSibId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->nextSibId.ui64Count++;
pNodeInfo->firstChildId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->firstChildId.ui64Count++;
pNodeInfo->lastChildId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->lastChildId.ui64Count++;
pNodeInfo->dataChildCount.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->dataChildCount.ui64Count++;
pNodeInfo->childElmCount.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->childElmCount.ui64Count++;
pNodeInfo->unencDataLen.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->unencDataLen.ui64Count++;
pNodeInfo->encDefId.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->encDefId.ui64Count++;
pNodeInfo->annotationId.ui64Bytes += sizeof( FLMUINT64);
pNodeInfo->annotationId.ui64Count++;
pNodeInfo->flags.ui64Bytes += sizeof( FLMUINT32);
pNodeInfo->flags.ui64Count++;
uiTotalOverhead += FIXED_DOM_HEADER_SIZE;
}
}
else
{
// Determine the base ID
if( ui64DocId < ui64BaseId)
{
flmAssert( ui64DocId);
ui64BaseId = ui64DocId;
}
if( ui64ParentId && ui64ParentId < ui64BaseId)
{
ui64BaseId = m_nodeInfo.ui64ParentId;
}
if( ui64PrevSibId && ui64PrevSibId < ui64BaseId)
{
ui64BaseId = ui64PrevSibId;
}
if( ui64NextSibId && ui64NextSibId < ui64BaseId)
{
ui64BaseId = ui64NextSibId;
}
if( ui64FirstChildId && ui64FirstChildId < ui64BaseId)
{
flmAssert( ui64LastChildId);
ui64BaseId = ui64FirstChildId;
}
if( ui64LastChildId && ui64LastChildId < ui64BaseId)
{
flmAssert( ui64FirstChildId);
ui64BaseId = ui64LastChildId;
}
if( ui64AnnotationId && ui64AnnotationId < ui64BaseId)
{
ui64BaseId = ui64AnnotationId;
}
if (pucBuf)
{
flmAssert( !pNodeInfo && !pDb && puiHeaderStorageSize);
// Reserve a byte for the header length
pucBuf++;
// Encode the node type
*pucBuf++ = ((((FLMBYTE)m_nodeInfo.uiDataType) & 0x07) << 4) |
(((FLMBYTE)eNodeType) & 0x0F) |
(uiDataLength ? 0x80 : 0);
// Document ID
flmEncodeSEN( ui64DocId, &pucBuf);
// Encode the base ID if it isn't equal to the document ID
if( ui64BaseId != ui64DocId)
{
uiStorageFlags |= NSF_HAVE_BASE_ID_BIT;
flmEncodeSEN( ui64BaseId, &pucBuf);
}
// Parent ID
if( (ui64Tmp = ui64ParentId) == 0)
{
ui64Tmp = ui64NodeId;
}
flmEncodeSEN( ui64Tmp - ui64BaseId, &pucBuf);
// Name ID
flmEncodeSEN( uiNameId, &pucBuf);
// Prefix ID
if( uiPrefixId)
{
uiStorageFlags |= NSF_EXT_HAVE_PREFIX_BIT;
flmEncodeSEN( uiPrefixId, &pucBuf);
}
// Metavalue
if( ui64MetaValue)
{
uiStorageFlags |= NSF_HAVE_META_VALUE_BIT;
flmEncodeSEN( ui64MetaValue, &pucBuf);
}
// Previous and next siblings
if( ui64PrevSibId || ui64NextSibId)
{
if( (ui64Tmp = ui64PrevSibId) == 0)
{
ui64Tmp = ui64NodeId;
}
flmEncodeSEN( ui64Tmp - ui64BaseId, &pucBuf);
if( (ui64Tmp = ui64NextSibId) == 0)
{
ui64Tmp = ui64NodeId;
}
flmEncodeSEN( ui64Tmp - ui64BaseId, &pucBuf);
uiStorageFlags |= NSF_HAVE_SIBLINGS_BIT;
}
// First, last, and data children
if( ui64FirstChildId)
{
flmAssert( ui64LastChildId);
flmEncodeSEN( ui64FirstChildId - ui64BaseId, &pucBuf);
flmEncodeSEN( ui64LastChildId - ui64BaseId, &pucBuf);
uiStorageFlags |= NSF_HAVE_CHILDREN_BIT;
if( uiDataChildCount)
{
flmEncodeSEN( uiDataChildCount, &pucBuf);
uiStorageFlags |= NSF_EXT_HAVE_DCHILD_COUNT_BIT;
}
}
// Child element count
if( uiFlags & FDOM_HAVE_CELM_LIST)
{
// NOTE: It is legal for m_nodeInfo.uiChildElmCount to be zero.
// The FDOM_EXT_CHILD_ELM_LIST bit is also used to enforce
// the fact that all of the child elements must have unique
// name IDs.
flmEncodeSEN( m_nodeInfo.uiChildElmCount, &pucBuf);
}
// Encryption ID
if( uiEncDefId)
{
uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT;
flmEncodeSEN( uiEncDefId, &pucBuf);
}
// Annotation ID
if( ui64AnnotationId)
{
uiStorageFlags |= NSF_EXT_ANNOTATION_BIT;
flmEncodeSEN( ui64AnnotationId - ui64BaseId, &pucBuf);
}
// Output the data length if needed
if( uiDataLength &&
(uiEncDefId || (uiFlags & FDOM_HAVE_CELM_LIST) || m_uiAttrCount))
{
uiStorageFlags |= NSF_HAVE_DATA_LEN_BIT;
flmEncodeSEN( uiDataLength, &pucBuf);
}
// Output the storage flags (inverted SEN)
uiFlagsLen = flmGetSENByteCount( uiStorageFlags);
if( uiFlagsLen > 1)
{
pucTmpSEN = ucTmpSEN;
flmEncodeSEN( uiStorageFlags, &pucTmpSEN);
for( uiLoop = uiFlagsLen; uiLoop > 0; uiLoop--)
{
*pucBuf++ = ucTmpSEN[ uiLoop - 1];
}
}
else
{
*pucBuf++ = (FLMBYTE)uiStorageFlags;
}
flmAssert( (FLMUINT)(pucBuf - pucStart) <= MAX_DOM_HEADER_SIZE);
// Set the header size
*puiHeaderStorageSize = (FLMUINT)(pucBuf - pucStart);
*pucStart = (FLMBYTE)(*puiHeaderStorageSize);
}
else
{
flmAssert( pNodeInfo && pDb && !puiHeaderStorageSize);
pNodeInfo->headerSize.ui64Bytes++;
pNodeInfo->headerSize.ui64Count++;
pNodeInfo->nodeAndDataType.ui64Bytes++;
pNodeInfo->nodeAndDataType.ui64Count++;
pNodeInfo->documentId.ui64Bytes += flmGetSENByteCount( ui64DocId);
pNodeInfo->documentId.ui64Count++;
uiTotalOverhead = 3;
// Document ID
flmAddInfoSenLen( &pNodeInfo->documentId, ui64DocId, &uiTotalOverhead);
// Encode the base ID if it isn't equal to the document ID
if (ui64BaseId != ui64DocId)
{
uiStorageFlags |= NSF_HAVE_BASE_ID_BIT;
flmAddInfoSenLen( &pNodeInfo->baseId, ui64BaseId, &uiTotalOverhead);
}
// Parent ID
if ((ui64Tmp = ui64ParentId) == 0)
{
ui64Tmp = ui64NodeId;
}
flmAddInfoSenLen( &pNodeInfo->parentId, ui64Tmp - ui64BaseId,
&uiTotalOverhead);
// Name ID
flmAddInfoSenLen( &pNodeInfo->nameId, uiNameId, &uiTotalOverhead);
// Prefix ID
if (uiPrefixId)
{
uiStorageFlags |= NSF_EXT_HAVE_PREFIX_BIT;
flmAddInfoSenLen( &pNodeInfo->prefixId, uiPrefixId, &uiTotalOverhead);
}
// Meta Value
if (ui64MetaValue)
{
uiStorageFlags |= NSF_HAVE_META_VALUE_BIT;
flmAddInfoSenLen( &pNodeInfo->metaValue, ui64MetaValue, &uiTotalOverhead);
}
// First/Last sibling
if (ui64PrevSibId || ui64NextSibId)
{
if ((ui64Tmp = ui64PrevSibId) == 0)
{
ui64Tmp = ui64NodeId;
}
flmAddInfoSenLen( &pNodeInfo->prevSibId, ui64Tmp, &uiTotalOverhead);
if ((ui64Tmp = ui64NextSibId) == 0)
{
ui64Tmp = ui64NodeId;
}
flmAddInfoSenLen( &pNodeInfo->nextSibId, ui64Tmp, &uiTotalOverhead);
uiStorageFlags |= NSF_HAVE_SIBLINGS_BIT;
}
// First, last, and data children
if (ui64FirstChildId)
{
flmAddInfoSenLen( &pNodeInfo->firstChildId,
ui64FirstChildId - ui64BaseId, &uiTotalOverhead);
flmAddInfoSenLen( &pNodeInfo->lastChildId,
ui64LastChildId - ui64BaseId, &uiTotalOverhead);
uiStorageFlags |= NSF_HAVE_CHILDREN_BIT;
if (uiDataChildCount)
{
flmAddInfoSenLen( &pNodeInfo->dataChildCount,
uiDataChildCount, &uiTotalOverhead);
uiStorageFlags |= NSF_EXT_HAVE_DCHILD_COUNT_BIT;
}
}
// Child element count - may be zero, so we should test the flag.
if (uiFlags & FDOM_HAVE_CELM_LIST)
{
flmAddInfoSenLen( &pNodeInfo->childElmCount,
m_nodeInfo.uiChildElmCount, &uiTotalOverhead);
}
// Encryption ID
if (uiEncDefId)
{
uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT;
flmAddInfoSenLen( &pNodeInfo->encDefId, uiEncDefId, &uiTotalOverhead);
}
// Annotation ID
if( ui64AnnotationId)
{
uiStorageFlags |= NSF_EXT_ANNOTATION_BIT;
flmAddInfoSenLen( &pNodeInfo->annotationId,
ui64AnnotationId - ui64BaseId, &uiTotalOverhead);
}
// Data length if needed
if( uiDataLength &&
(uiEncDefId || (uiFlags & FDOM_HAVE_CELM_LIST) || m_uiAttrCount))
{
uiStorageFlags |= NSF_HAVE_DATA_LEN_BIT;
flmAddInfoSenLen( &pNodeInfo->unencDataLen,
uiDataLength, &uiTotalOverhead);
}
flmAddInfoSenLen( &pNodeInfo->flags, uiStorageFlags, &uiTotalOverhead);
}
}
// Account for other overhead.
if (pNodeInfo)
{
if (eNodeType == ELEMENT_NODE)
{
NODE_ITEM * pNodeItem;
FLMUINT uiNodeCount = getChildElmCount();
FLMUINT uiPrevNameId = 0;
FLMUINT64 ui64ElmNodeId = getNodeId();
// Go through the child element list and calculate the length needed
// to store the name id and node id for each one.
pNodeItem = m_pNodeList;
for( uiLoop = 0; uiLoop < uiNodeCount; pNodeItem++, uiLoop++)
{
flmAssert( pNodeItem->uiNameId > uiPrevNameId);
flmAssert( pNodeItem->ui64NodeId > ui64ElmNodeId);
flmAddInfoSenLen( &pNodeInfo->childElmNameId,
pNodeItem->uiNameId - uiPrevNameId, &uiTotalOverhead);
flmAddInfoSenLen( &pNodeInfo->childElmNodeId,
pNodeItem->ui64NodeId - ui64ElmNodeId, &uiTotalOverhead);
uiPrevNameId = pNodeItem->uiNameId;
}
// Determine space taken by attributes.
if (m_uiAttrCount)
{
if( RC_BAD( rc = exportAttributeList( pDb, NULL, pNodeInfo)))
{
goto Exit;
}
}
}
// Determine space needed to store encryption IV and any
// encryption padding.
if (uiEncDefId)
{
F_ENCDEF * pEncDef;
FLMUINT uiTmp;
if (RC_BAD( rc = pDb->m_pDict->getEncDef( uiEncDefId, &pEncDef)))
{
goto Exit;
}
uiTmp = pEncDef->pCcs->getIVLen();
flmAssert( uiTmp == 8 || uiTmp == 16);
pNodeInfo->encIV.ui64Bytes += (FLMUINT64)uiTmp;
pNodeInfo->encIV.ui64Count++;
uiTotalOverhead += uiTmp;
uiTmp = getEncLen( uiDataLength) - uiDataLength;
if (uiTmp)
{
pNodeInfo->encPadding.ui64Bytes += (FLMUINT64)uiTmp;
pNodeInfo->encPadding.ui64Count++;
uiTotalOverhead += uiTmp;
}
}
pNodeInfo->totalOverhead.ui64Bytes += (FLMUINT64)uiTotalOverhead;
pNodeInfo->totalOverhead.ui64Count++;
switch (eNodeType)
{
case ELEMENT_NODE:
pNodeInfo->elementNode.ui64Bytes +=
(FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead;
pNodeInfo->elementNode.ui64Count++;
break;
case DATA_NODE:
pNodeInfo->dataNode.ui64Bytes +=
(FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead;
pNodeInfo->dataNode.ui64Count++;
break;
case COMMENT_NODE:
pNodeInfo->commentNode.ui64Bytes +=
(FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead;
pNodeInfo->commentNode.ui64Count++;
break;
default:
pNodeInfo->otherNode.ui64Bytes +=
(FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead;
pNodeInfo->otherNode.ui64Count++;
break;
}
switch (m_nodeInfo.uiDataType)
{
case XFLM_NODATA_TYPE:
pNodeInfo->dataNodata.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataNodata.ui64Count++;
break;
case XFLM_TEXT_TYPE:
pNodeInfo->dataString.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataString.ui64Count++;
break;
case XFLM_NUMBER_TYPE:
pNodeInfo->dataNumeric.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataNumeric.ui64Count++;
break;
case XFLM_BINARY_TYPE:
pNodeInfo->dataBinary.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataBinary.ui64Count++;
break;
default:
flmAssert( 0);
break;
}
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE flmReadNodeInfo(
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
IF_IStream * pIStream,
FLMUINT uiOverallLength,
FLMBOOL bAssertOnCorruption,
F_NODE_INFO * pNodeInfo,
FLMUINT * puiStorageFlags,
FLMBOOL * pbFixedSizeHeader)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiLoop;
FLMUINT uiStorageFlags = 0;
FLMUINT uiStorageFlagsLen;
FLMUINT uiHeaderStorageSize;
FLMBYTE ucHeader[ MAX_DOM_HEADER_SIZE];
const FLMBYTE * pucHeader;
const FLMBYTE * pucHeaderEnd;
FLMUINT64 ui64BaseId = 0;
FLMBYTE ucTmpSEN[ FLM_MAX_SEN_LEN];
FLMBYTE * pucTmpSEN;
FLMBOOL bEOFValid = TRUE;
FLMBOOL bHaveData;
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( bAssertOnCorruption);
#endif
// Set the node ID and collection
pNodeInfo->uiCollection = uiCollection;
pNodeInfo->ui64NodeId = ui64NodeId;
// Read the expected length of the header
if( RC_BAD( rc = pIStream->read( &ucHeader[ 0], 1)))
{
goto Exit;
}
bEOFValid = FALSE;
// A length value of XFLM_FIXED_SIZE_HEADER_TOKEN indicates that we have a
// non-compressed header on the node. This type of header is used when a
// large text or binary value is streamed into the database.
if( ucHeader[ 0] == XFLM_FIXED_SIZE_HEADER_TOKEN)
{
// Read the rest of the header
uiHeaderStorageSize = FIXED_DOM_HEADER_SIZE;
if( RC_BAD( rc = pIStream->read( &ucHeader[ 1],
uiHeaderStorageSize - 1, NULL)))
{
goto Exit;
}
pucHeader = ucHeader;
pucHeaderEnd = pucHeader + uiHeaderStorageSize;
// Skip past the header size byte
pucHeader++;
// Get the node type, data type, and data flag
pNodeInfo->eNodeType = (eDomNodeType)((*pucHeader) & 0x0F);
pNodeInfo->uiDataType = ((*pucHeader) >> 4) & 0x07;
pucHeader++;
// Document ID
pNodeInfo->ui64DocumentId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
// Parent ID
pNodeInfo->ui64ParentId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
// Name ID
pNodeInfo->uiNameId = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
// Prefix ID
pNodeInfo->uiPrefixId = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
// Metavalue
pNodeInfo->ui64MetaValue = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
// Previous and next siblings
pNodeInfo->ui64PrevSibId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
pNodeInfo->ui64NextSibId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
// First and last children
pNodeInfo->ui64FirstChildId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
pNodeInfo->ui64LastChildId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
// Data child count
pNodeInfo->uiDataChildCount = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
// Child element count
pNodeInfo->uiChildElmCount = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
if( pNodeInfo->uiChildElmCount &&
pNodeInfo->eNodeType != ELEMENT_NODE)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
// Data length
pNodeInfo->uiDataLength = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
// Encryption Id
pNodeInfo->uiEncDefId = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
if( pNodeInfo->uiEncDefId && !pNodeInfo->uiDataLength)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
// Annotation ID
pNodeInfo->ui64AnnotationId = FB2U64( pucHeader);
pucHeader += sizeof( FLMUINT64);
// Storage flags
uiStorageFlags = FB2UD( pucHeader);
pucHeader += sizeof( FLMUINT32);
// Set the fixed size header flag
if( pbFixedSizeHeader)
{
*pbFixedSizeHeader = TRUE;
}
}
else
{
if( (uiHeaderStorageSize = (FLMUINT)ucHeader[ 0]) > MAX_DOM_HEADER_SIZE)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
// Read the rest of the header
if( RC_BAD( rc = pIStream->read( &ucHeader[ 1],
uiHeaderStorageSize - 1, NULL)))
{
goto Exit;
}
pucHeader = ucHeader;
pucHeaderEnd = pucHeader + uiHeaderStorageSize;
// Get the storage flags
uiStorageFlags = pucHeader[ uiHeaderStorageSize - 1];
uiStorageFlagsLen = flmGetSENLength( (FLMBYTE)uiStorageFlags);
if( uiStorageFlagsLen > 1)
{
pucTmpSEN = ucTmpSEN;
for( uiLoop = 1; uiLoop <= uiStorageFlagsLen; uiLoop++)
{
*pucTmpSEN++ = pucHeader[ uiHeaderStorageSize - uiLoop];
}
pucTmpSEN = ucTmpSEN;
if( RC_BAD( rc = flmDecodeSEN( (const FLMBYTE **)&pucTmpSEN,
pucTmpSEN + uiStorageFlagsLen, &uiStorageFlags)))
{
goto Exit;
}
}
// Skip past the header size byte
pucHeader++;
// Get the node type, data type, and data flag
pNodeInfo->eNodeType = (eDomNodeType)((*pucHeader) & 0x0F);
if( pNodeInfo->eNodeType == INVALID_NODE ||
pNodeInfo->eNodeType > PROCESSING_INSTRUCTION_NODE)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
pNodeInfo->uiDataType = ((*pucHeader) >> 4) & 0x07;
bHaveData = *pucHeader & 0x80 ? TRUE : FALSE;
pucHeader++;
// Document ID
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64DocumentId)))
{
goto Exit;
}
// Base ID
if( uiStorageFlags & NSF_HAVE_BASE_ID_BIT)
{
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader,
pucHeaderEnd, &ui64BaseId)))
{
goto Exit;
}
}
else
{
ui64BaseId = pNodeInfo->ui64DocumentId;
}
// Parent ID
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader,
pucHeaderEnd, &pNodeInfo->ui64ParentId)))
{
goto Exit;
}
if( (pNodeInfo->ui64ParentId += ui64BaseId) == ui64NodeId)
{
pNodeInfo->ui64ParentId = 0;
}
// Name ID
if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd,
&pNodeInfo->uiNameId)))
{
goto Exit;
}
// Prefix ID
if( uiStorageFlags & NSF_EXT_HAVE_PREFIX_BIT)
{
if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd,
&pNodeInfo->uiPrefixId)))
{
goto Exit;
}
}
else
{
pNodeInfo->uiPrefixId = 0;
}
// Metavalue
if( uiStorageFlags & NSF_HAVE_META_VALUE_BIT)
{
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64MetaValue)))
{
goto Exit;
}
}
else
{
pNodeInfo->ui64MetaValue = 0;
}
// Previous and next siblings
if( uiStorageFlags & NSF_HAVE_SIBLINGS_BIT)
{
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64PrevSibId)))
{
goto Exit;
}
if( (pNodeInfo->ui64PrevSibId += ui64BaseId) == ui64NodeId)
{
pNodeInfo->ui64PrevSibId = 0;
}
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64NextSibId)))
{
goto Exit;
}
if( (pNodeInfo->ui64NextSibId += ui64BaseId) == ui64NodeId)
{
pNodeInfo->ui64NextSibId = 0;
}
}
else
{
pNodeInfo->ui64PrevSibId = 0;
pNodeInfo->ui64NextSibId = 0;
}
// First and last children. Also will read the data child count.
if( uiStorageFlags & NSF_HAVE_CHILDREN_BIT)
{
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64FirstChildId)))
{
goto Exit;
}
pNodeInfo->ui64FirstChildId += ui64BaseId;
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64LastChildId)))
{
goto Exit;
}
pNodeInfo->ui64LastChildId += ui64BaseId;
if( uiStorageFlags & NSF_EXT_HAVE_DCHILD_COUNT_BIT)
{
if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd,
&pNodeInfo->uiDataChildCount)))
{
goto Exit;
}
}
else
{
pNodeInfo->uiDataChildCount = 0;
}
}
else
{
pNodeInfo->ui64FirstChildId = 0;
pNodeInfo->ui64LastChildId = 0;
pNodeInfo->uiDataChildCount = 0;
}
// Child element count
if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT)
{
// This bit should only be set for elements.
if( pNodeInfo->eNodeType != ELEMENT_NODE)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
// NOTE: This bit may be set even if there are no children.
// This bit also serves to indicate that this particular
// element is a unique child element, and we should keep
// a list of child elements as they are added and removed.
// We should also enforce that no children have the same
// name id.
if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd,
&pNodeInfo->uiChildElmCount)))
{
goto Exit;
}
// If the count > 0, the NSF_HAVE_CHILDREN_BIT better also be set.
if( pNodeInfo->uiChildElmCount)
{
if( !(uiStorageFlags & NSF_HAVE_CHILDREN_BIT))
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
}
}
else
{
pNodeInfo->uiChildElmCount = 0;
}
// Encryption ID
if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT)
{
if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd,
&pNodeInfo->uiEncDefId)))
{
goto Exit;
}
}
else
{
pNodeInfo->uiEncDefId = 0;
}
// Annotation ID
if( uiStorageFlags & NSF_EXT_ANNOTATION_BIT)
{
if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd,
&pNodeInfo->ui64AnnotationId)))
{
goto Exit;
}
pNodeInfo->ui64AnnotationId += ui64BaseId;
}
else
{
pNodeInfo->ui64AnnotationId = 0;
}
if( uiStorageFlags & NSF_HAVE_DATA_LEN_BIT)
{
if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd,
&pNodeInfo->uiDataLength)))
{
goto Exit;
}
}
else
{
pNodeInfo->uiDataLength = 0;
}
// Account for storage flags
pucHeader += uiStorageFlagsLen;
// Make sure the header was the expected size
if( pucHeader != pucHeaderEnd)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
// Set the data length to whatever is remaining if we didn't
// have a data length in the header
if( bHaveData && !(uiStorageFlags & NSF_HAVE_DATA_LEN_BIT))
{
flmAssert( uiOverallLength >= uiHeaderStorageSize);
pNodeInfo->uiDataLength = uiOverallLength - uiHeaderStorageSize;
}
// Set the fixed size header flag
if( pbFixedSizeHeader)
{
*pbFixedSizeHeader = FALSE;
}
}
if( pNodeInfo->uiEncDefId && !pNodeInfo->uiDataLength)
{
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
goto Exit;
}
if( puiStorageFlags)
{
*puiStorageFlags = uiStorageFlags;
}
Exit:
if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BAD_SEN)
{
if( !bEOFValid)
{
// If one of the calls to read from the stream returned an EOF error,
// the database is corrupt.
#ifdef FLM_DEBUG
if( bAssertOnCorruption)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
else
#endif
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
}
}
}
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::readNode(
F_Db * pDb,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
IF_IStream * pIStream,
FLMUINT uiOverallLength,
FLMBYTE * pucIV)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiStorageLength;
FLMUINT uiStorageFlags;
FLMUINT uiIVLen;
FLMBYTE ucIV[ 16];
FLMBOOL bEOFValid = TRUE;
FLMBOOL bFixedSizeHeader;
if( RC_BAD( rc = flmReadNodeInfo( uiCollection,
ui64NodeId, pIStream, uiOverallLength, FALSE, &m_nodeInfo,
&uiStorageFlags, &bFixedSizeHeader)))
{
goto Exit;
}
bEOFValid = FALSE;
m_uiFlags = 0;
if( bFixedSizeHeader)
{
m_uiFlags |= FDOM_FIXED_SIZE_HEADER;
}
// Read the child element list, if any
if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT)
{
if( m_nodeInfo.uiChildElmCount)
{
FLMUINT uiLen;
FLMUINT uiLoop;
NODE_ITEM * pElmNode;
FLMUINT uiPrevNameId = 0;
FLMUINT64 ui64ElmNodeId = getNodeId();
FLMUINT uiChildElmCount = m_nodeInfo.uiChildElmCount;
flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE);
// Need to set to zero so the resizeChildElmList will work. This
// is the actual size allocated.
m_nodeInfo.uiChildElmCount = 0;
if( RC_BAD( rc = resizeChildElmList( uiChildElmCount, FALSE)))
{
goto Exit;
}
// Read in all of the element name IDs and node IDs.
for( uiLoop = 0, pElmNode = m_pNodeList;
uiLoop < m_nodeInfo.uiChildElmCount;
uiLoop++, pElmNode++)
{
if( RC_BAD( rc = flmReadSEN( pIStream, &pElmNode->uiNameId,
&uiLen)))
{
goto Exit;
}
pElmNode->uiNameId += uiPrevNameId;
uiPrevNameId = pElmNode->uiNameId;
if( RC_BAD( rc = flmReadSEN64( pIStream,
&pElmNode->ui64NodeId, &uiLen)))
{
goto Exit;
}
pElmNode->ui64NodeId += ui64ElmNodeId;
}
}
m_uiFlags |= FDOM_HAVE_CELM_LIST;
}
// Read the attribute list
if( uiStorageFlags & NSF_HAVE_ATTR_LIST_BIT)
{
if( m_nodeInfo.eNodeType != ELEMENT_NODE)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
goto Exit;
}
if( RC_BAD( rc = importAttributeList( pDb, pIStream, FALSE)))
{
goto Exit;
}
}
// Read the initialization vector if this is an encrypted node
if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT)
{
F_Dict * pDict;
F_ENCDEF * pEncDef;
if( RC_BAD( rc = pDb->getDictionary( &pDict)))
{
goto Exit;
}
if( RC_BAD( rc = pDict->getEncDef( getEncDefId(), &pEncDef)))
{
goto Exit;
}
uiIVLen = pEncDef->pCcs->getIVLen();
if( uiIVLen != 8 && uiIVLen != 16)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
if( RC_BAD( rc = pIStream->read( ucIV, uiIVLen)))
{
goto Exit;
}
if( pucIV)
{
f_memcpy( pucIV, ucIV, uiIVLen);
}
uiStorageLength = getEncLen( m_nodeInfo.uiDataLength);
}
else
{
uiStorageLength = m_nodeInfo.uiDataLength;
}
// Read the data part of the node, if any
if( uiStorageLength)
{
// Data size must have room for data to point back to
// the node. Always align on 8 byte boundaries - just to be safe.
// It is the highest alignment we support.
if( calcDataBufSize( uiStorageLength) <= MAX_CELL_SIZE ||
m_nodeInfo.eNodeType == ELEMENT_NODE)
{
if( RC_BAD( rc = resizeDataBuffer( uiStorageLength, FALSE)))
{
goto Exit;
}
if( RC_BAD( rc = pIStream->read( (char *)getDataPtr(),
uiStorageLength)))
{
goto Exit;
}
// Decrypt the data
if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT)
{
if( RC_BAD( rc = pDb->decryptData(
m_nodeInfo.uiEncDefId, ucIV, getDataPtr(),
uiStorageLength, getDataPtr(), uiStorageLength)))
{
goto Exit;
}
}
// If the type is a number, cache a 'quick' number.
if( m_nodeInfo.uiDataType == XFLM_NUMBER_TYPE)
{
FLMUINT64 ui64Num;
FLMBOOL bNeg;
if( RC_BAD( rc = flmStorageNumberToNumber(
getDataPtr(), getDataLength(), &ui64Num, &bNeg)))
{
goto Exit;
}
if( !bNeg)
{
setUINT64( ui64Num);
}
else
{
setINT64( -(FLMINT64)ui64Num);
}
}
}
else
{
flmAssert( m_nodeInfo.eNodeType != ELEMENT_NODE);
flmAssert( m_nodeInfo.uiDataType == XFLM_TEXT_TYPE ||
m_nodeInfo.uiDataType == XFLM_BINARY_TYPE);
m_uiFlags |= FDOM_VALUE_ON_DISK;
}
}
if( uiStorageFlags & NSF_EXT_READ_ONLY_BIT)
{
m_uiFlags |= FDOM_READ_ONLY;
}
if( uiStorageFlags & NSF_EXT_CANNOT_DELETE_BIT)
{
m_uiFlags |= FDOM_CANNOT_DELETE;
}
if( uiStorageFlags & NSF_EXT_QUARANTINED_BIT)
{
m_uiFlags |= FDOM_QUARANTINED;
}
if( uiStorageFlags & NSF_EXT_NAMESPACE_DECL_BIT)
{
m_uiFlags |= FDOM_NAMESPACE_DECL;
}
// Sanity checks
switch( m_nodeInfo.eNodeType)
{
case DOCUMENT_NODE:
{
if( !isRootNode() || getFirstChildId() != getLastChildId())
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
goto Exit;
}
break;
}
case ELEMENT_NODE:
{
if( isRootNode() && getParentId())
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
goto Exit;
}
break;
}
default:
{
break;
}
}
Exit:
if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BAD_SEN)
{
if( !bEOFValid)
{
// If one of the calls to read from the stream returned an EOF error,
// the database is corrupt.
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
}
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::importAttributeList(
F_Db * pDb,
IF_IStream * pIStream,
FLMBOOL bMutexAlreadyLocked)
{
RCODE rc = NE_XFLM_OK;
F_AttrItem * pAttrItem;
FLMUINT uiAttrCount;
FLMUINT uiNameId;
FLMUINT uiBaseNameId;
FLMUINT uiStorageFlags;
FLMUINT uiPayloadLength;
FLMUINT uiLoop;
FLMUINT uiInsertPos;
F_AttrElmInfo defInfo;
flmAssert( !m_uiAttrCount);
flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE);
// Determine the number of attributes
if( RC_BAD( rc = flmReadSEN( pIStream, &uiAttrCount)))
{
goto Exit;
}
if( !uiAttrCount)
{
goto Exit;
}
// Import the attributes
if( RC_BAD( rc = flmReadSEN( pIStream, &uiBaseNameId)))
{
goto Exit;
}
for( uiLoop = 0; uiLoop < uiAttrCount; uiLoop++)
{
if( RC_BAD( rc = flmReadSEN( pIStream, &uiNameId)))
{
goto Exit;
}
uiNameId += uiBaseNameId;
if (getAttribute( uiNameId, &uiInsertPos) != NULL)
{
flmAssert( 0);
}
if( RC_BAD( rc = allocAttribute( pDb,
uiNameId, NULL, uiInsertPos, &pAttrItem, bMutexAlreadyLocked)))
{
goto Exit;
}
if( RC_BAD( rc = flmReadSEN( pIStream, &uiStorageFlags)))
{
goto Exit;
}
if( uiStorageFlags & ASF_READ_ONLY_BIT)
{
pAttrItem->m_uiFlags |= FDOM_READ_ONLY;
}
if( uiStorageFlags & ASF_CANNOT_DELETE_BIT)
{
pAttrItem->m_uiFlags |= FDOM_CANNOT_DELETE;
}
if( uiStorageFlags & ASF_HAVE_PREFIX_BIT)
{
if( RC_BAD( rc = flmReadSEN( pIStream, &pAttrItem->m_uiPrefixId)))
{
goto Exit;
}
}
uiPayloadLength = (uiStorageFlags & ASF_PAYLOAD_LEN_MASK);
if( uiPayloadLength == ASF_HAVE_PAYLOAD_LEN_SEN)
{
if( RC_BAD( rc = flmReadSEN( pIStream, &uiPayloadLength)))
{
goto Exit;
}
}
if( RC_BAD( rc = pDb->m_pDict->getAttribute( pDb, uiNameId, &defInfo)))
{
goto Exit;
}
pAttrItem->m_uiDataType = defInfo.getDataType();
if( uiStorageFlags & ASF_ENCRYPTED_BIT)
{
F_ENCDEF * pEncDef;
if( RC_BAD( rc = flmReadSEN( pIStream, &pAttrItem->m_uiEncDefId)))
{
goto Exit;
}
if( RC_BAD( rc = flmReadSEN( pIStream,
&pAttrItem->m_uiDecryptedDataLen)))
{
goto Exit;
}
if( RC_BAD( rc = pDb->m_pDict->getEncDef(
pAttrItem->m_uiEncDefId, &pEncDef)))
{
goto Exit;
}
pAttrItem->m_uiIVLen = pEncDef->pCcs->getIVLen();
flmAssert( pAttrItem->m_uiIVLen == 8 || pAttrItem->m_uiIVLen == 16);
}
if( uiPayloadLength)
{
if( RC_BAD( rc = pAttrItem->resizePayloadBuffer(
uiPayloadLength, bMutexAlreadyLocked)))
{
goto Exit;
}
if( RC_BAD( rc = pIStream->read( pAttrItem->getAttrPayloadPtr(),
uiPayloadLength)))
{
goto Exit;
}
}
pAttrItem->m_uiPayloadLen = uiPayloadLength;
if( pAttrItem->m_uiDataType == XFLM_NUMBER_TYPE &&
!pAttrItem->m_uiEncDefId)
{
FLMBOOL bNeg;
if( RC_BAD( rc = flmStorageNumberToNumber(
pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength(),
&pAttrItem->m_ui64QuickVal, &bNeg)))
{
goto Exit;
}
if( !bNeg)
{
pAttrItem->m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL;
}
else
{
pAttrItem->m_uiFlags |= FDOM_SIGNED_QUICK_VAL;
}
}
}
Exit:
if( RC_BAD( rc))
{
pDb->setMustAbortTrans( rc);
}
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::importAttributeList(
F_Db * pDb,
F_CachedNode * pSourceNode,
FLMBOOL bMutexAlreadyLocked)
{
RCODE rc = NE_XFLM_OK;
F_AttrItem * pNewItem;
F_AttrItem * pSourceItem;
FLMUINT uiLoop;
flmAssert( !m_uiAttrCount);
if( RC_BAD( rc = resizeAttrList( pSourceNode->m_uiAttrCount,
bMutexAlreadyLocked)))
{
goto Exit;
}
for (uiLoop = 0; uiLoop < pSourceNode->m_uiAttrCount; uiLoop++)
{
pSourceItem = pSourceNode->m_ppAttrList [uiLoop];
if( RC_BAD( rc = allocAttribute( pDb,
pSourceItem->m_uiNameId, pSourceItem, uiLoop, &pNewItem,
bMutexAlreadyLocked)))
{
goto Exit;
}
if( pSourceItem->m_uiPayloadLen > sizeof( FLMBYTE *))
{
if( RC_BAD( rc = pNewItem->setupAttribute(
pDb, pSourceItem->m_uiEncDefId,
pSourceItem->getAttrDataLength(), FALSE,
bMutexAlreadyLocked)))
{
goto Exit;
}
flmAssert( pSourceItem->getAttrPayloadSize() ==
pNewItem->getAttrPayloadSize());
f_memcpy( pNewItem->getAttrPayloadPtr(),
pSourceItem->getAttrPayloadPtr(), pSourceItem->m_uiPayloadLen);
}
}
Exit:
if( RC_BAD( rc))
{
pDb->setMustAbortTrans( rc);
}
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
void F_AttrItem::getAttrSizeNeeded(
FLMUINT uiBaseNameId,
XFLM_NODE_INFO * pNodeInfo,
FLMUINT * puiSaveStorageFlags,
FLMUINT * puiSizeNeeded)
{
FLMUINT uiNameSize;
FLMUINT uiFlagsSize;
FLMUINT uiPrefixSize;
FLMUINT uiPayloadLength;
FLMUINT uiPayloadLenSize;
FLMUINT uiEncIdSize;
FLMUINT uiUnencLenSize;
FLMUINT uiOverhead = 0;
FLMUINT uiStorageFlags;
uiNameSize = flmGetSENByteCount( m_uiNameId - uiBaseNameId);
uiOverhead += uiNameSize;
uiStorageFlags = getAttrStorageFlags();
if (puiSaveStorageFlags)
{
*puiSaveStorageFlags = uiStorageFlags;
}
uiFlagsSize = flmGetSENByteCount( uiStorageFlags);
uiOverhead += uiFlagsSize;
if( m_uiPrefixId)
{
uiPrefixSize = flmGetSENByteCount( m_uiPrefixId);
uiOverhead += uiPrefixSize;
}
else
{
uiPrefixSize = 0;
}
uiPayloadLength = m_uiPayloadLen;
(*puiSizeNeeded) += uiPayloadLength;
if( uiPayloadLength > ASF_MAX_EMBEDDED_PAYLOAD_LEN)
{
uiPayloadLenSize = flmGetSENByteCount( uiPayloadLength);
uiOverhead += uiPayloadLenSize;
}
else
{
uiPayloadLenSize = 0;
}
if( m_uiEncDefId)
{
flmAssert( uiPayloadLength);
uiEncIdSize = flmGetSENByteCount( m_uiEncDefId);
uiOverhead += uiEncIdSize;
uiUnencLenSize = flmGetSENByteCount( m_uiDecryptedDataLen);
uiOverhead += uiUnencLenSize;
}
else
{
uiEncIdSize = 0;
uiUnencLenSize = 0;
}
(*puiSizeNeeded) += uiOverhead;
if (pNodeInfo)
{
FLMUINT uiDataLength;
pNodeInfo->nameId.ui64Bytes += (FLMUINT64)uiNameSize;
pNodeInfo->nameId.ui64Count++;
pNodeInfo->attrFlags.ui64Bytes += (FLMUINT64)uiFlagsSize;
pNodeInfo->attrFlags.ui64Count++;
if (uiPrefixSize)
{
pNodeInfo->prefixId.ui64Bytes += (FLMUINT64)uiPrefixSize;
pNodeInfo->prefixId.ui64Count++;
}
if (uiPayloadLenSize)
{
pNodeInfo->attrPayloadLen.ui64Bytes += (FLMUINT64)uiPayloadLenSize;
pNodeInfo->attrPayloadLen.ui64Count++;
}
uiDataLength = getAttrDataLength();
if (m_uiEncDefId)
{
FLMUINT uiEncPadding;
pNodeInfo->encDefId.ui64Bytes += (FLMUINT64)uiEncIdSize;
pNodeInfo->encDefId.ui64Count++;
pNodeInfo->unencDataLen.ui64Bytes += (FLMUINT64)uiUnencLenSize;
pNodeInfo->unencDataLen.ui64Count++;
pNodeInfo->encIV.ui64Bytes += (FLMUINT64)m_uiIVLen;
pNodeInfo->encIV.ui64Count++;
uiOverhead += m_uiIVLen;
flmAssert( m_uiPayloadLen >= m_uiIVLen - uiDataLength);
uiEncPadding = m_uiPayloadLen - m_uiIVLen - uiDataLength;
if (uiEncPadding)
{
pNodeInfo->encPadding.ui64Bytes += (FLMUINT64)uiEncPadding;
pNodeInfo->encPadding.ui64Count++;
uiOverhead += uiEncPadding;
}
}
pNodeInfo->totalOverhead.ui64Bytes += (FLMUINT64)uiOverhead;
pNodeInfo->totalOverhead.ui64Count++;
pNodeInfo->attributeNode.ui64Bytes += (FLMUINT64)(uiOverhead + uiDataLength);
pNodeInfo->attributeNode.ui64Count++;
switch (m_uiDataType)
{
case XFLM_NODATA_TYPE:
pNodeInfo->dataNodata.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataNodata.ui64Count++;
break;
case XFLM_TEXT_TYPE:
pNodeInfo->dataString.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataString.ui64Count++;
break;
case XFLM_NUMBER_TYPE:
pNodeInfo->dataNumeric.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataNumeric.ui64Count++;
break;
case XFLM_BINARY_TYPE:
pNodeInfo->dataBinary.ui64Bytes += (FLMUINT64)uiDataLength;
pNodeInfo->dataBinary.ui64Count++;
break;
default:
flmAssert( 0);
break;
}
}
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE F_CachedNode::exportAttributeList(
F_Db * pDb,
F_DynaBuf * pDynaBuf,
XFLM_NODE_INFO * pNodeInfo)
{
RCODE rc = NE_XFLM_OK;
F_AttrItem * pAttrItem;
FLMBYTE * pucStart;
FLMBYTE * pucBuf;
FLMBYTE * pucEnd;
FLMUINT uiPayloadLength;
FLMUINT uiLoop;
FLMUINT uiSizeNeeded;
FLMUINT uiStorageFlags;
FLMUINT uiBaseNameId = m_ppAttrList [0]->m_uiNameId;
#define MAX_STORAGE_FLAGS 32
FLMUINT storageFlagsList[ MAX_STORAGE_FLAGS];
// Logging should be done by the caller
#ifdef FLM_DEBUG
if (!pNodeInfo)
{
flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled());
}
#endif
// Determine the size of the node buffer
uiSizeNeeded = 0;
for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++)
{
pAttrItem = m_ppAttrList [uiLoop];
// If uiAttributeCount < MAX_STORAGE_FLAGS, we pass in a pointer
// to that slot in the storageFlagsList array to save the
// storage flags, so we don't have to recalculate them in the
// 2nd pass - up to the first MAX_STORAGE_FLAGS storage flags
// will be saved.
if (uiLoop < MAX_STORAGE_FLAGS)
{
pAttrItem->getAttrSizeNeeded( uiBaseNameId, pNodeInfo,
&storageFlagsList [uiLoop],
&uiSizeNeeded);
}
else
{
pAttrItem->getAttrSizeNeeded( uiBaseNameId, pNodeInfo, NULL,
&uiSizeNeeded);
}
}
flmAssert( m_uiAttrCount);
if (pNodeInfo)
{
flmAssert( !pDynaBuf);
pNodeInfo->attrCount.ui64Bytes += flmGetSENByteCount( m_uiAttrCount);
pNodeInfo->attrCount.ui64Count++;
pNodeInfo->attrBaseId.ui64Bytes += flmGetSENByteCount( uiBaseNameId);
pNodeInfo->attrBaseId.ui64Count++;
}
else
{
uiSizeNeeded += flmGetSENByteCount( m_uiAttrCount);
uiSizeNeeded += flmGetSENByteCount( uiBaseNameId);
flmAssert( pDynaBuf);
if( RC_BAD( rc = pDynaBuf->allocSpace( uiSizeNeeded, (void **)&pucBuf)))
{
goto Exit;
}
pucStart = pucBuf;
pucEnd = pucStart + uiSizeNeeded;
if( RC_BAD( rc = flmEncodeSEN( m_uiAttrCount, &pucBuf, pucEnd)))
{
goto Exit;
}
if( RC_BAD( rc = flmEncodeSEN( uiBaseNameId, &pucBuf, pucEnd)))
{
goto Exit;
}
// Once we have written out attribute count, we can reset it to zero.
for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++)
{
pAttrItem = m_ppAttrList [uiLoop];
if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiNameId - uiBaseNameId,
&pucBuf, pucEnd)))
{
goto Exit;
}
// If we saved the storage flags in the first pass, get them
// from storageFlagsList, otherwise recalculate them.
uiStorageFlags = (uiLoop < MAX_STORAGE_FLAGS)
? storageFlagsList [uiLoop]
: pAttrItem->getAttrStorageFlags();
if( RC_BAD( rc = flmEncodeSEN( uiStorageFlags, &pucBuf, pucEnd)))
{
goto Exit;
}
if( pAttrItem->m_uiPrefixId)
{
if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiPrefixId,
&pucBuf, pucEnd)))
{
goto Exit;
}
}
uiPayloadLength = pAttrItem->m_uiPayloadLen;
if( uiPayloadLength > ASF_MAX_EMBEDDED_PAYLOAD_LEN)
{
if( RC_BAD( rc = flmEncodeSEN( uiPayloadLength, &pucBuf, pucEnd)))
{
goto Exit;
}
}
if( pAttrItem->m_uiEncDefId)
{
flmAssert( uiPayloadLength);
if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiEncDefId,
&pucBuf, pucEnd)))
{
goto Exit;
}
if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiDecryptedDataLen,
&pucBuf, pucEnd)))
{
goto Exit;
}
}
f_memcpy( pucBuf, pAttrItem->getAttrPayloadPtr(), uiPayloadLength);
pucBuf += uiPayloadLength;
}
flmAssert( pucBuf == pucEnd);
}
Exit:
if( RC_BAD( rc))
{
pDb->setMustAbortTrans( rc);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_CachedNode::resetNode( void)
{
FLMBYTE * pucActualAlloc;
// Should not count attribute size here, because it will be subtracted
// when the attributes themselves are deleted.
FLMUINT uiSize = memSize() - m_uiTotalAttrSize;
flmAssert( !m_pPrevInBucket);
flmAssert( !m_pNextInBucket);
flmAssert( !m_pOlderVersion);
flmAssert( !m_pNewerVersion);
flmAssert( !m_pPrevInOldList);
flmAssert( !m_pNextInOldList);
flmAssert( !m_pNotifyList);
flmAssert( !m_uiStreamUseCount);
flmAssert( !nodeInUse());
f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex);
// If this is an old version, decrement the old version counters.
if (m_ui64HighTransId != FLM_MAX_UINT64)
{
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize &&
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize;
}
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize &&
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize;
if( m_uiFlags & FDOM_HEAP_ALLOC)
{
unlinkFromHeapList();
}
if( m_pucData || m_pNodeList || m_ppAttrList)
{
f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex);
if( m_pucData)
{
pucActualAlloc = getActualPointer( m_pucData);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
m_uiDataBufSize, &pucActualAlloc);
m_pucData = NULL;
m_uiDataBufSize = 0;
}
if( m_pNodeList)
{
pucActualAlloc = getActualPointer( m_pNodeList);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
calcNodeListBufSize( m_nodeInfo.uiChildElmCount),
&pucActualAlloc);
m_pNodeList = NULL;
}
if( m_ppAttrList)
{
FLMUINT uiLoop;
for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++)
{
delete m_ppAttrList [uiLoop];
}
pucActualAlloc = getActualPointer( m_ppAttrList);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
calcAttrListBufSize( m_uiAttrCount), &pucActualAlloc);
m_ppAttrList = NULL;
m_uiAttrCount = 0;
}
}
m_ui64LowTransId = 0;
m_ui64HighTransId = FLM_MAX_UINT64;
m_uiCacheFlags = 0;
m_pDatabase = NULL;
m_uiFlags = 0;
m_uiOffsetIndex = 0;
m_ui32BlkAddr = 0;
f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO));
uiSize = memSize();
if (m_ui64HighTransId != FLM_MAX_UINT64)
{
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize;
}
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize;
}
#undef new
#undef delete
/****************************************************************************
Desc:
****************************************************************************/
void * F_CachedNode::operator new(
FLMSIZET uiSize)
#ifndef FLM_NLM
throw()
#endif
{
void * pvCell;
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( uiSize);
#endif
flmAssert( uiSize == sizeof( F_CachedNode));
f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex);
if( (pvCell =
gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.allocCell(
&gv_XFlmSysData.pNodeCacheMgr->m_nodeRelocator)) != NULL)
{
#ifdef FLM_CACHE_PROTECT
gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.unprotectCell( pvCell);
#endif
}
return( pvCell);
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_CachedNode::operator new[]( FLMSIZET)
#ifndef FLM_NLM
throw()
#endif
{
flmAssert( 0);
return( NULL);
}
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_DEBUG
void * F_CachedNode::operator new(
FLMSIZET, // uiSize,
const char *, // pszFileName,
int) // iLineNum)
#ifndef FLM_NLM
throw()
#endif
{
// This new should never be called
flmAssert( 0);
return( NULL);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_DEBUG
void * F_CachedNode::operator new[](
FLMSIZET, // uiSize,
const char *, // pszFileName,
int) // iLine)
#ifndef FLM_NLM
throw()
#endif
{
flmAssert( 0);
return( NULL);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
void F_CachedNode::operator delete(
void * ptr)
{
if( !ptr)
{
return;
}
#ifdef FLM_CACHE_PROTECT
gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.protectCell( ptr);
#endif
gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.freeCell( (FLMBYTE *)ptr, FALSE);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_CachedNode::operator delete[](
void * // ptr)
)
{
flmAssert( 0);
}
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
void F_CachedNode::operator delete(
void * ptr,
const char *, // pszFileName
int // iLineNum
)
{
if( !ptr)
{
return;
}
gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.freeCell( (FLMBYTE *)ptr, FALSE);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
void F_CachedNode::operator delete[](
void *, // ptr,
const char *, // pszFileName
int // iLineNum
)
{
flmAssert( 0);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
F_AttrItem::~F_AttrItem()
{
FLMUINT uiSize = memSize();
if (m_pCachedNode)
{
flmAssert( m_pCachedNode->m_uiTotalAttrSize >= uiSize);
m_pCachedNode->m_uiTotalAttrSize -= uiSize;
if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64)
{
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize;
}
flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize);
gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize;
}
if( m_uiPayloadLen > sizeof( FLMBYTE *))
{
m_pucPayload -= sizeof( F_AttrItem *);
gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf(
m_uiPayloadLen + sizeof( F_AttrItem *),
&m_pucPayload);
}
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_AttrItem::operator new(
FLMSIZET uiSize)
#ifndef FLM_NLM
throw()
#endif
{
void * pvCell;
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( uiSize);
#endif
flmAssert( uiSize == sizeof( F_AttrItem));
f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex);
if( (pvCell =
gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.allocCell(
&gv_XFlmSysData.pNodeCacheMgr->m_attrItemRelocator)) != NULL)
{
#ifdef FLM_CACHE_PROTECT
gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.unprotectCell( pvCell);
#endif
}
return( pvCell);
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_AttrItem::operator new[]( FLMSIZET)
#ifndef FLM_NLM
throw()
#endif
{
flmAssert( 0);
return( NULL);
}
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_DEBUG
void * F_AttrItem::operator new(
FLMSIZET, // uiSize,
const char *, // pszFileName,
int) // iLineNum)
#ifndef FLM_NLM
throw()
#endif
{
// This new should never be called
flmAssert( 0);
return( NULL);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_DEBUG
void * F_AttrItem::operator new[](
FLMSIZET, // uiSize,
const char *, // pszFileName,
int) // iLine)
#ifndef FLM_NLM
throw()
#endif
{
flmAssert( 0);
return( NULL);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
void F_AttrItem::operator delete(
void * ptr)
{
if( !ptr)
{
return;
}
#ifdef FLM_CACHE_PROTECT
gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.protectCell( ptr);
#endif
gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.freeCell( (FLMBYTE *)ptr, FALSE);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_AttrItem::operator delete[](
void * // ptr)
)
{
flmAssert( 0);
}
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
void F_AttrItem::operator delete(
void * ptr,
const char *, // pszFileName
int // iLineNum
)
{
if( !ptr)
{
return;
}
gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.freeCell( (FLMBYTE *)ptr, FALSE);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
void F_AttrItem::operator delete[](
void *, // ptr,
const char *, // pszFileName
int // iLineNum
)
{
flmAssert( 0);
}
#endif