git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
5611 lines
132 KiB
C++
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
|