Files
mars-flaim/flaim/src/frec.cpp
ahodgkinson b81ebe096b Fixed non-debug build issue.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@533 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-06-08 19:09:00 +00:00

6205 lines
136 KiB
C++

//-------------------------------------------------------------------------
// Desc: Methods for the FlmRecord class
// Tabs: 3
//
// Copyright (c) 1999-2000,2002-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: frec.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE importTree(
IF_FileHdl * pFileHdl,
char ** pBuf,
FLMUINT uiBufSize,
F_NameTable * pNameTable,
FlmRecord * pRec);
FSTATIC RCODE importField(
FLMUINT uiLevel,
GED_STREAM * pGedStream,
F_NameTable * pNameTable,
FlmRecord * pRec);
FLMBYTE arr[ 16] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0,
0x7F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xE0
};
#define f_isalnum(c) \
((c) < 128 ? (( ((FLMBYTE)(arr[(c) >> 3])) << ((c) & 0x07)) & 0x80) : 0)
#define f_isdigit(c) \
((c) < 60 ? (( ((FLMBYTE)(arr[(c) >> 3])) << ((c) & 0x07)) & 0x80) : 0)
#define f_isalpha(c) \
((c) < 128 && (c) > 58 ? (( ((FLMBYTE)(arr[(c) >> 3])) << \
((c) & 0x07)) & 0x80) : 0)
/*****************************************************************************
Desc:
*****************************************************************************/
FlmRecord::FlmRecord()
{
m_pucBuffer = NULL;
m_uiBufferSize = 0;
m_uiFldTblSize = 0;
m_uiFlags = 0;
m_pucFieldIdTable = NULL;
clear();
}
/*****************************************************************************
Desc:
*****************************************************************************/
FlmRecord::~FlmRecord()
{
flmAssert( m_uiFlags & RCA_OK_TO_DELETE);
if( m_pucBuffer)
{
flmAssert( *((FlmRecord **)m_pucBuffer) == this);
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
m_uiBufferSize, &m_pucBuffer);
}
if( m_pucFieldIdTable)
{
flmAssert( *((FlmRecord **)m_pucFieldIdTable) == this);
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
fieldIdTableByteSize(), &m_pucFieldIdTable);
}
}
/*****************************************************************************
Desc: Makes a copy of self and returns pointer to the copy.
*****************************************************************************/
FlmRecord * FlmRecord::copy( void)
{
RCODE rc = FERR_OK;
FlmRecord * pNewRec = NULL;
FLMBOOL bHeapAlloc = FALSE;
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
goto Exit;
}
#endif
if( (pNewRec = f_new FlmRecord) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( m_uiBufferSize)
{
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->allocBuf(
NULL, m_uiBufferSize, &pNewRec, sizeof( FlmRecord *),
&pNewRec->m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
f_memcpy( &pNewRec->m_pucBuffer[ sizeof( FlmRecord *)],
&m_pucBuffer[ sizeof( FlmRecord *)],
m_uiBufferSize - sizeof( FlmRecord *));
if( bHeapAlloc)
{
pNewRec->m_uiFlags |= RCA_HEAP_BUFFER;
}
}
if( m_pucFieldIdTable)
{
FLMUINT uiTableByteSize = fieldIdTableByteSize();
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->allocBuf(
NULL, uiTableByteSize, &pNewRec, sizeof( FlmRecord *),
&pNewRec->m_pucFieldIdTable, &bHeapAlloc)))
{
goto Exit;
}
f_memcpy( pNewRec->m_pucFieldIdTable + FLM_ALIGN_SIZE,
m_pucFieldIdTable + FLM_ALIGN_SIZE,
uiTableByteSize - FLM_ALIGN_SIZE);
if( bHeapAlloc)
{
pNewRec->m_uiFlags |= RCA_ID_TABLE_HEAP_BUFFER;
}
if (m_uiFlags & RCA_NEED_TO_SORT_FIELD_ID_TABLE)
{
pNewRec->m_uiFlags |= RCA_NEED_TO_SORT_FIELD_ID_TABLE;
}
}
if (m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED)
{
pNewRec->m_uiFlags |= RCA_FIELD_ID_TABLE_ENABLED;
}
pNewRec->m_uiBufferSize = m_uiBufferSize;
pNewRec->m_uiContainerID = m_uiContainerID;
pNewRec->m_uiRecordID = m_uiRecordID;
pNewRec->m_uiFldTblSize = m_uiFldTblSize;
pNewRec->m_uiFldTblOffset = m_uiFldTblOffset;
pNewRec->m_uiDataBufOffset = m_uiDataBufOffset;
pNewRec->m_uiFirstAvail = m_uiFirstAvail;
pNewRec->m_uiAvailFields = m_uiAvailFields;
pNewRec->m_bHolesInData = m_bHolesInData;
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = pNewRec->checkRecord()))
{
goto Exit;
}
#endif
pNewRec->compressMemory();
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = pNewRec->checkRecord()))
{
goto Exit;
}
#endif
Exit:
if( RC_BAD( rc) && pNewRec)
{
pNewRec->Release();
pNewRec = NULL;
}
return( pNewRec);
}
/*****************************************************************************
Desc: Return a existing record to a new state (no fields)
*****************************************************************************/
RCODE FlmRecord::clear(
FLMBOOL bReleaseMemory)
{
RCODE rc = FERR_OK;
if( isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if( bReleaseMemory)
{
if( m_pucBuffer)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
m_uiBufferSize, &m_pucBuffer);
}
m_uiBufferSize = 0;
m_uiFldTblSize = 0;
}
if (m_pucFieldIdTable)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
fieldIdTableByteSize(), &m_pucFieldIdTable);
}
m_uiFlags = 0;
m_uiContainerID = 0;
m_uiRecordID = 0;
m_uiFldTblOffset = 0;
m_uiDataBufOffset = 0;
m_uiFirstAvail = 0;
m_uiAvailFields = 0;
m_bHolesInData = FALSE;
Exit:
return( rc);
}
/*****************************************************************************
Desc: Retrieve values from a context field.
*****************************************************************************/
RCODE FlmRecord::getRecPointer(
void * pvField,
FLMUINT * puiRecPointer)
{
RCODE rc = FERR_OK;
FlmField * pField = getFieldPointer( pvField);
*puiRecPointer = 0xFFFFFFFF;
if( !pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
if( isEncryptedField( pField) &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
if( getFieldDataType( pField) != FLM_CONTEXT_TYPE)
{
rc = RC_SET( FERR_CONV_ILLEGAL);
goto Exit;
}
if( getFieldDataLength( pField) == 4)
{
*puiRecPointer = (FLMUINT)(FB2UD( getDataPtr( pField)));
}
else
{
flmAssert( getFieldDataLength( pField) == 0);
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc: Create a FlmBlob object
*****************************************************************************/
RCODE FlmRecord::getBlob(
void * pvField,
FlmBlob ** ppBlob)
{
RCODE rc = FERR_OK;
FlmField * pField = getFieldPointer( pvField);
FLMBYTE * pucData;
FLMUINT uiDataLen;
FlmBlobImp * pNewBlob = NULL;
*ppBlob = NULL;
if( !pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
if( isEncryptedField( pField) &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
if( (uiDataLen = getFieldDataLength( pField)) != 0 &&
(getFieldDataType( pField) == FLM_BLOB_TYPE))
{
if( (pNewBlob = f_new FlmBlobImp) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
*ppBlob = pNewBlob;
if( (pucData = pNewBlob->getImportDataPtr( uiDataLen)) != NULL)
{
f_memcpy( pucData, getDataPtr( pField), uiDataLen);
}
else
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
else
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setINT(
void * pvField,
FLMINT iNumber,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMBYTE ucStorageBuf[ F_MAX_NUM_BUF + 1];
FLMUINT uiStorageLen;
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
uiStorageLen = sizeof( ucStorageBuf);
if( RC_BAD( rc = FlmINT2Storage( iNumber, &uiStorageLen, ucStorageBuf)))
{
goto Exit;
}
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
if( uiStorageLen % 16)
{
uiEncDataLen = uiStorageLen + (16 - (uiStorageLen % 16));
}
else
{
uiEncDataLen = uiStorageLen;
}
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_NUMBER_TYPE,
uiStorageLen, uiEncDataLen, uiEncId, uiEncFlags, &pucData, NULL)))
{
goto Exit;
}
f_memcpy( pucData, ucStorageBuf, uiStorageLen);
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setUINT(
void * pvField,
FLMUINT uiNumber,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMBYTE ucStorageBuf[ F_MAX_NUM_BUF + 1];
FLMUINT uiStorageLen;
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
uiStorageLen = sizeof( ucStorageBuf);
if( RC_BAD( rc = FlmUINT2Storage( uiNumber, &uiStorageLen, ucStorageBuf)))
{
goto Exit;
}
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
if( uiStorageLen % 16)
{
uiEncDataLen = uiStorageLen + (16 - (uiStorageLen % 16));
}
else
{
uiEncDataLen = uiStorageLen;
}
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_NUMBER_TYPE,
uiStorageLen, uiEncDataLen, uiEncId, uiEncFlags, &pucData, NULL)))
{
goto Exit;
}
f_memcpy( pucData, ucStorageBuf, uiStorageLen);
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setRecPointer(
void * pvField,
FLMUINT uiRecPointer,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
uiEncDataLen = 16;
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_CONTEXT_TYPE,
4, uiEncDataLen, uiEncId, uiEncFlags, &pucData, NULL)))
{
goto Exit;
}
UD2FBA( (FLMUINT32)uiRecPointer, pucData);
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setUnicode(
void * pvField,
const FLMUNICODE * pUnicode,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMUINT uiBufLen;
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
// A NULL or empty pUnicode string is allowed - on those
// cases just set the field type.
if( !pUnicode || *pUnicode == 0)
{
// Field may have had a value pointer that now
// needs to be set to NULL
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_TEXT_TYPE,
0, 0, 0, 0, &pucData, NULL)))
{
goto Exit;
}
}
else
{
uiBufLen = FlmGetUnicodeStorageLength( pUnicode);
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
if( uiBufLen % 16)
{
uiEncDataLen = uiBufLen + (16 - (uiBufLen % 16));
}
else
{
uiEncDataLen = uiBufLen;
}
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_TEXT_TYPE,
uiBufLen, uiEncDataLen, uiEncId, uiEncFlags, &pucData, NULL)))
{
goto Exit;
}
if( RC_BAD( rc = FlmUnicode2Storage( pUnicode, &uiBufLen, pucData)))
{
goto Exit;
}
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setNative(
void * pvField,
const char * pszString,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMUINT uiBufLen;
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
if( !pszString || *pszString == 0)
{
// Field may have had a value pointer that now
// needs to be set to NULL
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_TEXT_TYPE,
0, 0, 0, 0, &pucData, NULL)))
{
goto Exit;
}
}
else
{
uiBufLen = FlmGetNativeStorageLength( pszString);
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
if( uiBufLen % 16)
{
uiEncDataLen = uiBufLen + (16 - (uiBufLen % 16));
}
else
{
uiEncDataLen = uiBufLen;
}
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_TEXT_TYPE,
uiBufLen, uiEncDataLen, uiEncId, uiEncFlags, &pucData, NULL)))
{
goto Exit;
}
if( RC_BAD( rc = FlmNative2Storage( pszString, 0, &uiBufLen, pucData)))
{
goto Exit;
}
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setBinary(
void * pvField,
const void * pvBuf,
FLMUINT uiBufLen,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
if( !uiBufLen)
{
// Field may have had a value pointer that now
// needs to be set to NULL
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField),
FLM_BINARY_TYPE, 0, 0, 0, 0, &pucData, NULL)))
{
goto Exit;
}
}
else
{
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
if( uiBufLen % 16)
{
uiEncDataLen = uiBufLen + (16 - (uiBufLen % 16));
}
else
{
uiEncDataLen = uiBufLen;
}
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField),
FLM_BINARY_TYPE, uiBufLen, uiEncDataLen, uiEncId, uiEncFlags,
&pucData, NULL)))
{
goto Exit;
}
f_memcpy( pucData, pvBuf, uiBufLen);
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc: Copies the BLOB control data into the record.
*****************************************************************************/
RCODE FlmRecord::setBlob(
void * pvField,
FlmBlob * pBlob,
FLMUINT uiEncId)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMUINT uiDataLen = ((FlmBlobImp *)pBlob)->getDataLength();
FLMUINT uiEncDataLen = 0;
FLMUINT uiEncFlags = 0;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
if( !uiDataLen)
{
// Field may have had a value pointer that now
// needs to be set to NULL
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField), FLM_BLOB_TYPE,
0, 0, 0, 0, &pucData, NULL)))
{
goto Exit;
}
}
else
{
if( uiEncId)
{
// For encrypted fields, we want to make sure we allocate
// enough space for the encrypted data too. The data does
// not get encrypted until the call to FlmRecordModify or
// FlmRecordAdd.
if( uiDataLen % 16)
{
uiEncDataLen = uiDataLen + (16 - (uiDataLen % 16));
}
else
{
uiEncDataLen = uiDataLen;
}
uiEncFlags = FLD_HAVE_DECRYPTED_DATA;
}
if( RC_BAD( rc = getNewDataPtr( getFieldPointer( pvField),
FLM_BLOB_TYPE, uiDataLen, uiEncDataLen, uiEncId, uiEncFlags,
&pucData, NULL)))
{
goto Exit;
}
f_memcpy( pucData, ((FlmBlobImp *)pBlob)->getDataPtr(), uiDataLen);
}
#ifdef FLM_CHECK_RECORD
if( RC_BAD( rc = checkRecord()))
{
flmAssert( 0);
goto Exit;
}
#endif
Exit:
return( rc);
}
/*****************************************************************************
Desc: Create a new field at the specified position. Following the insert
the current record position will be positioned on the new field.
*****************************************************************************/
RCODE FlmRecord::insert(
void * pvField,
FLMUINT uiInsertAt,
FLMUINT uiFieldID,
FLMUINT uiDataType,
void ** ppvField)
{
RCODE rc = FERR_OK;
FlmField * pField;
FlmField * pNewField = NULL;
FlmField * pTmpField;
FLMUINT uiLevel;
if( isReadOnly() || isCached() || !uiFieldID)
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if( !pvField)
{
pField = getFirstField();
}
else
{
pField = getFieldPointer( pvField);
}
uiLevel = getFieldLevel( pField);
// User is adding first field (no fields in record)
if( !pField)
{
if( RC_BAD( rc = createField( NULL, &pNewField)))
{
goto Exit;
}
goto Exit;
}
// Perform desired insert.
switch( uiInsertAt)
{
case INSERT_PREV_SIB:
{
if( (pField = prevField( pField)) == NULL)
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if( RC_BAD( rc = createField( pField, &pNewField)))
{
goto Exit;
}
if( RC_BAD( rc = setFieldLevel( pNewField, uiLevel)))
{
goto Exit;
}
break;
}
case INSERT_NEXT_SIB:
{
// If current field has children then we need to position to the end
// of it's sub-tree before before inserting the new next sibling.
FlmField * pSubTreeEnd = lastSubTreeField( pField);
if( RC_BAD( rc = createField(
pSubTreeEnd ? pSubTreeEnd : pField, &pNewField)))
{
goto Exit;
}
if( RC_BAD( rc = setFieldLevel( pNewField, uiLevel)))
{
goto Exit;
}
break;
}
case INSERT_FIRST_CHILD:
{
First_Child:
if( RC_BAD( rc = createField( pField, &pNewField)))
{
goto Exit;
}
if( RC_BAD( rc = setFieldLevel( pNewField, uiLevel + 1)))
{
goto Exit;
}
break;
}
case INSERT_LAST_CHILD:
{
if( (pTmpField = lastSubTreeField( pField)) != NULL)
{
if( RC_BAD( rc = createField( pTmpField, &pNewField)))
{
goto Exit;
}
if( RC_BAD( rc = setFieldLevel( pNewField, uiLevel + 1)))
{
goto Exit;
}
}
else
{
// There are no children, so add as first child
goto First_Child;
}
break;
}
default:
{
flmAssert( 0);
break;
}
}
Exit:
if( pNewField)
{
pNewField->ui16FieldID = (FLMUINT16)uiFieldID;
setFieldDataType( pNewField, uiDataType);
if (getFieldLevel( pNewField) == 1 &&
(m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED))
{
rc = addToFieldIdTable( (FLMUINT16)uiFieldID,
(FIELDLINK)((FLMUINT)(pNewField - getFieldTable()) + 1));
}
}
if( ppvField)
{
*ppvField = getFieldVoid( pNewField);
}
return( rc);
}
/*****************************************************************************
Desc: Returns the last field within a subtree.
*****************************************************************************/
FlmField * FlmRecord::lastSubTreeField(
FlmField * pField)
{
FlmField * pTempField = lastChildField( pField);
FlmField * pLastChild = NULL;
FLMUINT uiStartLevel = getFieldLevel( pField);
for( ; pTempField && getFieldLevel( pTempField) > uiStartLevel;
pTempField = nextField( pTempField))
{
pLastChild = pTempField;
}
#ifdef FLM_DEBUG
if( pLastChild)
{
flmAssert( pLastChild->ui16FieldID);
}
#endif
return( pLastChild);
}
/*****************************************************************************
Desc: Special level-based insert.
Note: The level must be a value between 0 - ((last field)->level + 1)
*****************************************************************************/
RCODE FlmRecord::insertLast(
FLMUINT uiLevel,
FLMUINT uiFieldID,
FLMUINT uiDataType,
void ** ppvField)
{
RCODE rc = FERR_OK;
FlmField * pField = NULL;
FlmField * pLastField;
if( isReadOnly() || isCached() || !uiFieldID)
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
// Insert new field following current last field
pLastField = getLastField();
#ifdef FLM_DEBUG
if( pLastField && uiLevel > getFieldLevel( pLastField) + 1)
{
flmAssert( 0);
}
#endif
if( RC_BAD( rc = createField( pLastField, &pField)))
{
goto Exit;
}
// Set up the new field and set as the current field
pField->ui16FieldID = (FLMUINT16)uiFieldID;
setFieldDataType( pField, uiDataType);
if( RC_BAD( rc = setFieldLevel( pField, uiLevel)))
{
goto Exit;
}
if (uiLevel == 1 && (m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED))
{
if (RC_BAD( rc = addToFieldIdTable( (FLMUINT16)uiFieldID,
(FIELDLINK)((FLMUINT)(pField - getFieldTable()) + 1))))
{
goto Exit;
}
}
if( ppvField)
{
*ppvField = getFieldVoid( pField);
}
Exit:
return( rc);
}
/*****************************************************************************
Desc: Delete a field (and it's subtree).
*****************************************************************************/
RCODE FlmRecord::remove(
FlmField * pField)
{
RCODE rc = FERR_OK;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if( !pField)
{
goto Exit;
}
if( RC_BAD( rc = removeFields( pField, lastSubTreeField( pField))))
{
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc: Internal position to the next sibling
*****************************************************************************/
FlmField * FlmRecord::nextSiblingField(
FlmField * pField)
{
FLMUINT uiLevel = getFieldLevel( pField);
while( (pField = nextField( pField)) != NULL &&
getFieldLevel( pField) > uiLevel)
{
;
}
pField = (pField && getFieldLevel( pField) == uiLevel)
? pField
: NULL;
#ifdef FLM_DEBUG
if( pField)
{
flmAssert( pField->ui16FieldID);
}
#endif
return( pField);
}
/*****************************************************************************
Desc: Position to the prev sibling
*****************************************************************************/
void * FlmRecord::prevSibling(
void * pvField)
{
FlmField * pField = getFieldPointer( pvField);
FLMUINT uiLevel;
if( !pField)
{
return( NULL);
}
uiLevel = getFieldLevel( pField);
while( (pField = prevField( pField)) != NULL &&
getFieldLevel( pField) > uiLevel)
{
;
}
pField = (pField && getFieldLevel( pField) == uiLevel)
? pField
: NULL;
#ifdef FLM_DEBUG
if( pField)
{
flmAssert( pField->ui16FieldID);
}
#endif
return( getFieldVoid( pField));
}
/*****************************************************************************
Desc: Return the last child field
*****************************************************************************/
FlmField * FlmRecord::lastChildField(
FlmField * pField)
{
FlmField * pLastField = NULL;
if( !pField)
{
return( NULL);
}
for( pField = firstChildField( pField);
pField;
pField = nextSiblingField( pField))
{
pLastField = pField;
}
#ifdef FLM_DEBUG
if( pLastField)
{
flmAssert( pLastField->ui16FieldID);
}
#endif
return( pLastField);
}
/*****************************************************************************
Desc: Return the parent field.
*****************************************************************************/
void * FlmRecord::parent(
FlmField * pField)
{
FLMUINT uiLevel;
if( !pField)
{
return( NULL);
}
uiLevel = getFieldLevel( pField);
while( (pField = prevField( pField)) != NULL &&
getFieldLevel( pField) >= uiLevel)
{
;
}
#ifdef FLM_DEBUG
if( pField)
{
flmAssert( pField->ui16FieldID);
}
#endif
return( getFieldVoid( pField));
}
/*****************************************************************************
Desc: Set the right truncated flag.
*****************************************************************************/
void FlmRecord::setRightTruncated(
FlmField * pField,
FLMBOOL bTrueFalse)
{
flmAssert( !isCached() && !isReadOnly());
if( bTrueFalse)
{
pField->ui8TypeAndLevel |= FLD_DATA_RIGHT_TRUNCATED;
}
else
{
pField->ui8TypeAndLevel &= ~FLD_DATA_RIGHT_TRUNCATED;
}
}
/*****************************************************************************
Desc: Set the left truncated flag.
*****************************************************************************/
void FlmRecord::setLeftTruncated(
FlmField * pField,
FLMBOOL bTrueFalse)
{
flmAssert( !isCached() && !isReadOnly());
if( bTrueFalse)
{
pField->ui8TypeAndLevel |= FLD_DATA_LEFT_TRUNCATED;
}
else
{
pField->ui8TypeAndLevel &= ~FLD_DATA_LEFT_TRUNCATED;
}
}
/*****************************************************************************
Desc: Called from FLAIM's filesystem code to directly populate a record.
*****************************************************************************/
RCODE FlmRecord::allocStorageSpace(
void * pvField,
FLMUINT uiDataType,
FLMUINT uiLength,
FLMUINT uiEncLength,
FLMUINT uiEncId,
FLMUINT uiFlags,
FLMBYTE ** ppucData,
FLMBYTE ** ppucEncData)
{
return( getNewDataPtr( getFieldPointer( pvField), uiDataType,
uiLength, uiEncLength, uiEncId, uiFlags, ppucData, ppucEncData));
}
/*****************************************************************************
Desc: Gives the implementor information about how many fields and how much
data will be placed within this record.
*****************************************************************************/
RCODE FlmRecord::preallocSpace(
FLMUINT uiFieldCount,
FLMUINT uiDataSize)
{
RCODE rc = FERR_OK;
FLMUINT uiNewSize;
FlmRecord * pThis = this;
FLMBOOL bHeapAlloc = FALSE;
if( isCached() || isReadOnly())
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
flmAssert( uiFieldCount);
flmAssert( !m_uiFldTblOffset);
flmAssert( !m_uiDataBufOffset);
uiNewSize = FLM_ALIGN_SIZE +
(uiFieldCount * sizeof( FlmField)) + uiDataSize;
if( m_uiBufferSize < uiNewSize || (m_uiBufferSize - uiNewSize) >= 32)
{
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, m_uiBufferSize, uiNewSize, &pThis, sizeof( FlmRecord *),
&m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
m_uiBufferSize = uiNewSize;
}
m_uiFldTblSize = uiFieldCount;
Exit:
return( rc);
}
/*****************************************************************************
Desc: Copies the binary data into users buffer
*****************************************************************************/
RCODE FlmRecord::getBinary(
void * pvField,
void * pvBuf,
FLMUINT * puiBufLen)
{
RCODE rc = FERR_OK;
FlmField * pField = getFieldPointer( pvField);
if( !pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
if (isEncryptedField( pField) &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
*puiBufLen = f_min( (*puiBufLen), getFieldDataLength( pField));
f_memcpy( pvBuf, getDataPtr( pField), *puiBufLen);
Exit:
return( rc);
}
/*****************************************************************************
Desc: Return most (if not all) unused memory back to the system. Doesn't
compress avail slots out of the field list because the application
could still have "pointers" into the list.
*****************************************************************************/
RCODE FlmRecord::compressMemory( void)
{
RCODE rc = FERR_OK;
FLMUINT uiLength;
FLMUINT uiTmp;
FLMUINT uiNewSize = 0;
FLMUINT uiNewDataSize = 0;
FLMUINT uiNewDataOffset;
FlmField * pFld;
FLMBYTE * pucNewBuf = NULL;
FLMBYTE * pucNewData;
FLMUINT uiPicketFenceSize = 0;
FlmRecord * pThis = this;
FLMBOOL bHeapAlloc = FALSE;
if( isReadOnly() || isCached())
{
flmAssert( 0);
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
if( !m_uiBufferSize ||
(!m_bHolesInData && m_uiDataBufOffset == getDataBufSize()))
{
if( m_uiFldTblOffset == m_uiFldTblSize)
{
goto Exit;
}
}
// Scan the record and determine the compressed size
pFld = getFieldPointer( root());
while( pFld)
{
uiLength = getFieldDataLength( pFld);
if (!isEncryptedField( pFld))
{
if( uiLength > 4 && uiLength < 0xFF)
{
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiNewDataSize & FLM_ALLOC_ALIGN) != 0)
{
uiNewDataSize += (FLM_ALLOC_ALIGN + 1) -
(uiNewDataSize & FLM_ALLOC_ALIGN);
}
}
uiNewDataSize += uiLength;
}
else if( uiLength >= 0xFF)
{
// Add 1 to the length to allow for the flags byte which is only
// present on long fields and encrypted fields.
uiTmp = uiNewDataSize + sizeof( FLMUINT32) + 1;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
uiNewDataSize = uiTmp + uiLength;
}
}
else // Encrypted field
{
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = FLD_PICKET_FENCE_SIZE;
#endif
// FLM_ENC_FLD_OVERHEAD includes 1 byte for the flags
uiTmp = uiNewDataSize + FLM_ENC_FLD_OVERHEAD;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
uiNewDataSize = uiTmp + uiLength + getEncryptedDataLength(pFld) +
uiPicketFenceSize;
}
pFld = nextField( pFld);
}
uiNewSize = FLM_ALIGN_SIZE +
uiNewDataSize + (m_uiFldTblOffset * sizeof( FlmField));
// Re-allocate the buffer
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->allocBuf(
NULL, uiNewSize, &pThis, sizeof( FlmRecord *),
&pucNewBuf, &bHeapAlloc)))
{
goto Exit;
}
uiNewDataOffset = 0;
pucNewData = pucNewBuf + FLM_ALIGN_SIZE + (m_uiFldTblOffset * sizeof( FlmField));
pFld = getFieldPointer( root());
while( pFld)
{
uiLength = getFieldDataLength( pFld);
if (!isEncryptedField( pFld))
{
if( uiLength > 4 && uiLength < 0xFF)
{
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiNewDataOffset & FLM_ALLOC_ALIGN) != 0)
{
uiNewDataOffset += (FLM_ALLOC_ALIGN + 1) -
(uiNewDataOffset & FLM_ALLOC_ALIGN);
}
}
flmAssert( uiNewDataOffset + uiLength <= uiNewDataSize);
f_memcpy( &pucNewData[ uiNewDataOffset],
getDataPtr( pFld), uiLength);
pFld->ui32DataOffset = (FLMUINT32)uiNewDataOffset;
uiNewDataOffset += uiLength;
}
else if( uiLength >= 0xFF)
{
uiTmp = uiNewDataOffset + sizeof( FLMUINT32) + 1;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
flmAssert( uiTmp + uiLength <= uiNewDataSize);
pucNewData[ uiNewDataOffset + FLD_ENC_FLAGS_OFFSET] = 0; // Set the flags byte.
UD2FBA( (FLMUINT32)uiLength, &pucNewData[ uiNewDataOffset + FLD_ENC_ENCID_OFFSET]);
f_memcpy( &pucNewData[ uiTmp], getDataPtr( pFld), uiLength);
pFld->ui32DataOffset = (FLMUINT32)uiNewDataOffset;
uiNewDataOffset = uiTmp + uiLength;
}
}
else // Encrypted field
{
FLMUINT uiFlags;
FLMUINT uiEncId;
FLMUINT uiEncLength;
FLMUINT uiEncTmp;
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = FLD_PICKET_FENCE_SIZE;
#endif
uiFlags = getEncFlags( pFld);
uiEncId = getEncryptionID( pFld);
uiEncLength = getEncryptedDataLength( pFld);
uiTmp = uiNewDataOffset + FLM_ENC_FLD_OVERHEAD;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
uiEncTmp = uiTmp + uiLength + (uiPicketFenceSize / 2);
flmAssert( uiEncTmp + uiEncLength + (uiPicketFenceSize / 2) <= uiNewDataSize);
pucNewData[ uiNewDataOffset + FLD_ENC_FLAGS_OFFSET] = ( FLMBYTE)uiFlags;
UW2FBA( (FLMUINT16)uiEncId, &pucNewData[ uiNewDataOffset + FLD_ENC_ENCID_OFFSET]);
UD2FBA( (FLMUINT32)uiLength, &pucNewData[ uiNewDataOffset + FLD_ENC_DATA_LEN_OFFSET]);
UD2FBA( (FLMUINT32)uiEncLength, &pucNewData[ uiNewDataOffset + FLD_ENC_ENCRYPTED_DATA_LEN_OFFSET]);
#ifdef FLM_PICKET_FENCE
// Set the picket fence
f_sprintf( (char *)&pucNewData[ uiTmp], FLD_RAW_FENCE);
uiTmp += (FLD_PICKET_FENCE_SIZE / 2);
#endif
f_memcpy( &pucNewData[ uiTmp], getDataPtr( pFld), uiLength);
#ifdef FLM_PICKET_FENCE
// Set the picket fence
f_sprintf( (char *)&pucNewData[ uiEncTmp], FLD_ENC_FENCE);
uiEncTmp += (FLD_PICKET_FENCE_SIZE / 2);
#endif
f_memcpy( &pucNewData[ uiEncTmp], getEncryptionDataPtr( pFld), uiEncLength);
pFld->ui32DataOffset = (FLMUINT32)uiNewDataOffset;
uiNewDataOffset = uiEncTmp + uiEncLength;
}
pFld = nextField( pFld);
}
// The field list cannot be compressed because the application may
// still have "pointers" (offsets) into the table
f_memcpy( pucNewBuf, m_pucBuffer,
FLM_ALIGN_SIZE + (m_uiFldTblOffset * sizeof( FlmField)));
#ifdef FLM_DEBUG
uiTmp = (FLM_ALIGN_SIZE + uiNewDataOffset +
(sizeof( FlmField) * m_uiFldTblOffset));
flmAssert( uiTmp == uiNewSize);
#endif
// Update the member variables
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
m_uiBufferSize, &m_pucBuffer);
m_pucBuffer = pucNewBuf;
pucNewBuf = NULL;
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
m_uiBufferSize = uiNewSize;
m_uiFldTblSize = m_uiFldTblOffset;
m_uiDataBufOffset = uiNewDataSize;
m_bHolesInData = FALSE;
Exit:
if( pucNewBuf)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
uiNewSize, &pucNewBuf);
}
return( rc);
}
/******************************************************************************
Desc: Routine for comparing two FIELD_ID structures.
******************************************************************************/
FINLINE FLMINT fieldIdCompare(
FIELD_ID * pFieldIdA,
FIELD_ID * pFieldIdB)
{
if (pFieldIdA->ui16FieldId < pFieldIdB->ui16FieldId)
{
return( -1);
}
else if (pFieldIdA->ui16FieldId > pFieldIdB->ui16FieldId)
{
return( 1);
}
else if (pFieldIdA->ui32FieldOffset < pFieldIdB->ui32FieldOffset)
{
return( -1);
}
return( (pFieldIdA->ui32FieldOffset > pFieldIdB->ui32FieldOffset)
? 1
: 0);
}
/*****************************************************************************
Desc: Return most all unused memory back to the system. This should
be called by the Release method of the record object.
*****************************************************************************/
RCODE FlmRecord::compactMemory( void)
{
RCODE rc = FERR_OK;
FLMUINT uiLength;
FLMUINT uiTmp;
FLMUINT uiNewSize = 0;
FLMUINT uiNewDataSize = 0;
FLMUINT uiNewDataOffset;
FLMUINT uiFields = 0;
FLMUINT uiSlot;
FlmField * pFld;
FlmField * pNewFld;
FlmField * pNewFldTbl;
FLMBYTE * pucNewBuf = NULL;
FLMBYTE * pucNewData;
FLMUINT uiPicketFenceSize = 0;
FlmRecord * pThis = this;
FLMBOOL bHeapAlloc = FALSE;
FLMBYTE * pucNewFieldIdTable = NULL;
FLMBOOL bFieldIdHeapAlloc = FALSE;
FLMUINT uiFieldIdTableItemCount = 0;
FIELD_ID * pNewFieldIdTable = NULL;
FLMUINT uiLevelOneFldCount;
FLMBOOL bNeedToSortFieldIdTable;
flmAssert( isCached());
flmAssert( getRefCount() == 1);
// Temporarily increment the reference count so that we don't hit
// debug asserts while processing
f_atomicInc( &m_refCnt);
if( !m_uiBufferSize ||
(!m_bHolesInData && m_uiDataBufOffset == getDataBufSize()))
{
if( !m_uiFirstAvail && m_uiFldTblOffset == m_uiFldTblSize)
{
goto Exit;
}
}
if( isOldVersion())
{
FLMUINT uiTotalMemory = getTotalMemory();
flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiTotalMemory);
gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalMemory;
}
// Scan the record and determine the compressed size
pFld = getFieldPointer( root());
while( pFld)
{
uiLength = getFieldDataLength( pFld);
if (!isEncryptedField(pFld))
{
if( uiLength > 4 && uiLength < 0xFF)
{
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiNewDataSize & FLM_ALLOC_ALIGN) != 0)
{
uiNewDataSize += (FLM_ALLOC_ALIGN + 1) -
(uiNewDataSize & FLM_ALLOC_ALIGN);
}
}
uiNewDataSize += uiLength;
}
else if( uiLength >= 0xFF)
{
// Add one extra byte for the flags byte.
uiTmp = uiNewDataSize + sizeof( FLMUINT32) + 1;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
uiNewDataSize = uiTmp + uiLength;
}
}
else // Encrypted field
{
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = FLD_PICKET_FENCE_SIZE;
#endif
uiTmp = uiNewDataSize + FLM_ENC_FLD_OVERHEAD;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
uiNewDataSize = uiTmp + uiLength + getEncryptedDataLength(pFld) +
uiPicketFenceSize;
}
uiFields++;
pFld = nextField( pFld);
}
uiNewSize = FLM_ALIGN_SIZE +
uiNewDataSize + (uiFields * sizeof( FlmField));
flmAssert( uiNewSize <= m_uiBufferSize);
// Allocate a new buffer
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->allocBuf(
NULL, uiNewSize, &pThis, sizeof( FlmRecord *),
&pucNewBuf, &bHeapAlloc)))
{
goto Exit;
}
if (m_pucFieldIdTable)
{
uiFieldIdTableItemCount = getFieldIdTableItemCount( m_pucFieldIdTable);
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->allocBuf(
NULL, calcFieldIdTableByteSize( uiFieldIdTableItemCount), &pThis,
sizeof( FlmRecord *), &pucNewFieldIdTable, &bFieldIdHeapAlloc)))
{
goto Exit;
}
// Set the item count and table size in this new item table to be
// the same.
setFieldIdTableItemCount( pucNewFieldIdTable, uiFieldIdTableItemCount);
setFieldIdTableArraySize( pucNewFieldIdTable, uiFieldIdTableItemCount);
pNewFieldIdTable = getFieldIdTable( pucNewFieldIdTable);
}
pNewFldTbl = (FlmField *)(pucNewBuf + FLM_ALIGN_SIZE);
uiSlot = 0;
uiNewDataOffset = 0;
pucNewData = pucNewBuf + FLM_ALIGN_SIZE + (uiFields * sizeof( FlmField));
uiLevelOneFldCount = 0;
bNeedToSortFieldIdTable = FALSE;
pFld = getFieldPointer( root());
while( pFld)
{
uiLength = getFieldDataLength( pFld);
pNewFld = &pNewFldTbl[ uiSlot];
f_memcpy( pNewFld, pFld, sizeof( FlmField));
if (pNewFieldIdTable && getFieldLevel( pFld) == 1)
{
FIELD_ID * pFieldId = pNewFieldIdTable + uiLevelOneFldCount;
flmAssert( uiLevelOneFldCount < uiFieldIdTableItemCount);
pFieldId->ui16FieldId = pFld->ui16FieldID;
pFieldId->ui32FieldOffset = (FLMUINT32)(uiSlot + 1);
uiLevelOneFldCount++;
if (uiLevelOneFldCount > 1 && !bNeedToSortFieldIdTable &&
fieldIdCompare( pFieldId - 1, pFieldId) > 0)
{
bNeedToSortFieldIdTable = TRUE;
}
}
if (!isEncryptedField(pFld))
{
if( uiLength > 4 && uiLength < 0xFF)
{
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiNewDataOffset & FLM_ALLOC_ALIGN) != 0)
{
uiNewDataOffset += (FLM_ALLOC_ALIGN + 1) -
(uiNewDataOffset & FLM_ALLOC_ALIGN);
}
}
flmAssert( uiNewDataOffset + uiLength <= uiNewDataSize);
f_memcpy( &pucNewData[ uiNewDataOffset], getDataPtr( pFld), uiLength);
pNewFld->ui32DataOffset = (FLMUINT32)uiNewDataOffset;
uiNewDataOffset += uiLength;
}
else if( uiLength >= 0xFF)
{
uiTmp = uiNewDataOffset + sizeof( FLMUINT32) + 1;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
flmAssert( uiTmp + uiLength <= uiNewDataSize);
pucNewData[ uiNewDataOffset + FLD_ENC_FLAGS_OFFSET] = 0; // Flags
UD2FBA( (FLMUINT32)uiLength, &pucNewData[ uiNewDataOffset + FLD_ENC_ENCID_OFFSET]);
f_memcpy( &pucNewData[ uiTmp], getDataPtr( pFld), uiLength);
pNewFld->ui32DataOffset = (FLMUINT32)uiNewDataOffset;
uiNewDataOffset = uiTmp + uiLength;
}
}
else
{
FLMUINT uiFlags;
FLMUINT uiEncId;
FLMUINT uiEncLength;
FLMUINT uiEncTmp;
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = FLD_PICKET_FENCE_SIZE;
#endif
uiFlags = getEncFlags( pFld);
uiEncId = getEncryptionID( pFld);
uiEncLength = getEncryptedDataLength( pFld);
uiTmp = uiNewDataOffset + FLM_ENC_FLD_OVERHEAD;
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
if( (uiTmp & FLM_ALLOC_ALIGN) != 0)
{
uiTmp += (FLM_ALLOC_ALIGN + 1) - (uiTmp & FLM_ALLOC_ALIGN);
}
}
uiEncTmp = uiTmp + uiLength + (uiPicketFenceSize / 2);
flmAssert( uiEncTmp + uiEncLength + (uiPicketFenceSize / 2) <= uiNewDataSize);
pucNewData[ uiNewDataOffset + FLD_ENC_FLAGS_OFFSET] = (FLMBYTE)uiFlags;
UW2FBA( (FLMUINT16)uiEncId, &pucNewData[ uiNewDataOffset + FLD_ENC_ENCID_OFFSET]);
UD2FBA( (FLMUINT32)uiLength, &pucNewData[ uiNewDataOffset + FLD_ENC_DATA_LEN_OFFSET]);
UD2FBA( (FLMUINT32)uiEncLength, &pucNewData[ uiNewDataOffset + FLD_ENC_ENCRYPTED_DATA_LEN_OFFSET]);
#ifdef FLM_PICKET_FENCE
f_sprintf( (char *)&pucNewData[ uiTmp], FLD_RAW_FENCE);
uiTmp += (FLD_PICKET_FENCE_SIZE / 2);
#endif
f_memcpy( &pucNewData[ uiTmp], getDataPtr( pFld), uiLength);
#ifdef FLM_PICKET_FENCE
f_sprintf( (char *)&pucNewData[ uiEncTmp], FLD_ENC_FENCE);
uiEncTmp += (FLD_PICKET_FENCE_SIZE / 2);
#endif
f_memcpy( &pucNewData[ uiEncTmp], getEncryptionDataPtr( pFld), uiEncLength);
pNewFld->ui32DataOffset = (FLMUINT32)uiNewDataOffset;
uiNewDataOffset = uiEncTmp + uiEncLength;
}
pFld = nextField( pFld);
pNewFld->uiPrev = (FIELDLINK)uiSlot;
if( pFld)
{
pNewFld->uiNext = (FIELDLINK)(uiSlot + 2);
}
else
{
pNewFld->uiNext = 0;
}
uiSlot++;
}
// Update the member variables
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
m_uiBufferSize, &m_pucBuffer);
m_pucBuffer = pucNewBuf;
pucNewBuf = NULL;
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
m_uiBufferSize = uiNewSize;
if (m_pucFieldIdTable)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
fieldIdTableByteSize(), &m_pucFieldIdTable);
m_pucFieldIdTable = pucNewFieldIdTable;
pucNewFieldIdTable = NULL;
if( bFieldIdHeapAlloc)
{
m_uiFlags |= RCA_ID_TABLE_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_ID_TABLE_HEAP_BUFFER;
}
if (bNeedToSortFieldIdTable)
{
// Need to set the flag, otherwise sortFieldIdTable() won't
// sort the table.
m_uiFlags |= RCA_NEED_TO_SORT_FIELD_ID_TABLE;
sortFieldIdTable();
}
}
m_uiFldTblOffset = uiFields;
m_uiFldTblSize = uiFields;
m_uiDataBufOffset = uiNewDataSize;
m_bHolesInData = FALSE;
m_uiAvailFields = 0;
m_uiFirstAvail = 0;
if( isOldVersion())
{
FLMUINT uiTotalMemory = getTotalMemory();
gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += uiTotalMemory;
}
Exit:
if( pucNewBuf)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
uiNewSize, &pucNewBuf);
}
if( pucNewFieldIdTable)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
calcFieldIdTableByteSize( uiFieldIdTableItemCount),
&pucNewFieldIdTable);
}
f_atomicDec( &m_refCnt);
return( rc);
}
/*****************************************************************************
Desc: Create a new field after pCurField.
*****************************************************************************/
RCODE FlmRecord::createField(
FlmField * pPrevField,
FlmField ** ppNewField)
{
RCODE rc = FERR_OK;
FlmField * pNewField;
FLMUINT uiNewSize;
FlmField * pFldTbl = NULL;
void * pvPrevField;
FlmRecord * pThis = this;
FLMBOOL bHeapAlloc = FALSE;
flmAssert( !isReadOnly());
flmAssert( !isCached());
pvPrevField = getFieldVoid( pPrevField);
pPrevField = NULL;
if( m_uiFirstAvail)
{
flmAssert( m_uiAvailFields);
pNewField = &(getFieldTable()[ m_uiFirstAvail - 1]);
m_uiFirstAvail = pNewField->uiNext;
m_uiAvailFields--;
}
else
{
if( m_uiFldTblOffset == m_uiFldTblSize)
{
// Resize the buffer
if( m_uiBufferSize)
{
uiNewSize = m_uiBufferSize + (sizeof( FlmField) * 8);
}
else
{
uiNewSize = FLM_ALIGN_SIZE + (sizeof( FlmField) * 8);
}
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, m_uiBufferSize, uiNewSize, &pThis, sizeof( FlmRecord *),
&m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
m_uiBufferSize = uiNewSize;
m_uiFldTblSize += 8;
if( m_uiDataBufOffset)
{
f_memmove( &(getFieldTable()[ m_uiFldTblSize]),
&(getFieldTable()[ m_uiFldTblSize - 8]),
m_uiDataBufOffset);
}
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
}
pFldTbl = getFieldTable();
pNewField = &pFldTbl[ m_uiFldTblOffset++];
}
f_memset( pNewField, 0, sizeof( FlmField));
pPrevField = getFieldPointer( pvPrevField);
if( pPrevField)
{
pNewField->ui16FieldID = 0xFFFF;
pNewField->uiPrev = (FIELDLINK)((FLMUINT)pvPrevField);
pNewField->uiNext = pPrevField->uiNext;
if( pPrevField->uiNext)
{
getFieldPointer( (void *)((FLMUINT)(pPrevField->uiNext)))->uiPrev =
(FIELDLINK)((FLMUINT)getFieldVoid( pNewField));
}
pPrevField->uiNext = (FIELDLINK)((FLMUINT)getFieldVoid( pNewField));
}
*ppNewField = pNewField;
Exit:
return( rc);
}
/*****************************************************************************
Desc: Remove a specific field, or a range of fields.
*****************************************************************************/
RCODE FlmRecord::removeFields(
FlmField * pFirstField,
FlmField * pLastField)
{
RCODE rc = FERR_OK;
FlmField * pCurField;
FLMUINT uiFieldsRemoved = 0;
FlmField * pFieldTable = getFieldTable();
flmAssert( !isReadOnly());
flmAssert( !isCached());
// Setup the common case first of removing a single field.
if( !pLastField)
{
pLastField = pFirstField;
}
// Fix up the prev and next pointers
if( pFirstField->uiPrev)
{
getFieldPointer( (void *)((FLMUINT)(pFirstField->uiPrev)))->uiNext =
pLastField->uiNext;
}
if( pLastField->uiNext)
{
getFieldPointer( (void *)((FLMUINT)(pLastField->uiNext)))->uiPrev =
pFirstField->uiPrev;
pLastField->uiNext = 0;
}
// Clear the field slots
pCurField = pFirstField;
while( pCurField)
{
if (getFieldLevel( pCurField) == 1 &&
(m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED))
{
rc = removeFromFieldIdTable( (FLMUINT16)pCurField->ui16FieldID,
(FIELDLINK)((FLMUINT)(pCurField - pFieldTable) + 1));
}
pCurField->uiPrev = 0;
pCurField->ui32DataOffset = 0;
pCurField->ui16FieldID = 0;
pCurField->ui8DataLen = 0;
pCurField->ui8TypeAndLevel = 0;
pCurField = nextField( pCurField);
uiFieldsRemoved++;
}
pLastField->uiNext = m_uiFirstAvail;
m_uiFirstAvail = (FIELDLINK)((FLMUINT)getFieldVoid( pFirstField));
m_uiAvailFields += uiFieldsRemoved;
return( rc);
}
/*****************************************************************************
Desc: Returns a pointer that is of size 'uiNewLength' that the caller
can then write a fields data to. If possible the fields current
data pointer will be reused.
*****************************************************************************/
RCODE FlmRecord::getNewDataPtr(
FlmField * pField,
FLMUINT uiDataType,
FLMUINT uiNewLength,
FLMUINT uiEncNewLength,
FLMUINT uiEncId,
FLMUINT uiFlags,
FLMBYTE ** ppDataPtr,
FLMBYTE ** ppEncDataPtr)
{
RCODE rc = FERR_OK;
FLMBYTE * pucDataPtr = NULL;
FLMBYTE * pucEncDataPtr = NULL;
FLMUINT uiOldLength;
FLMUINT uiAlignment = 0;
FLMUINT uiSlot = (FLMUINT)getFieldVoid( pField);
FLMUINT uiDataBufSize;
FLMUINT uiHeader = 0;
FLMUINT uiOldHeader = 0;
FLMUINT uiAllocStart = 0;
FLMUINT uiTmp;
FLMBOOL bNewEncrypted = (uiEncId ? TRUE : FALSE);
FLMBOOL bOldEncrypted;
FLMBYTE * pucTmp;
FLMUINT uiNewSize;
FLMUINT uiPicketFenceSize = 0;
FlmRecord * pThis = this;
FLMBOOL bHeapAlloc = FALSE;
flmAssert( !isCached());
flmAssert( !isReadOnly());
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = (bNewEncrypted ? FLD_PICKET_FENCE_SIZE : 0);
#endif
// Test for an invalid encryption Id. This could be
// indicative of an uninitialized variaqble.
if (uiEncId > FLM_RESERVED_TAG_NUMS)
{
flmAssert( 0);
rc = RC_SET( FERR_BAD_ENCDEF_ID);
goto Exit;
}
bOldEncrypted = isEncryptedField( pField);
// If the new field is encrypted, we need to prefix the value
// with the encryption overhead. Otherwise, if the new length
// is >= 0xFF, we need to prefix the value with a 4-byte length
uiHeader = (bNewEncrypted ? FLM_ENC_FLD_OVERHEAD
: (uiNewLength >= 0xFF ? sizeof( FLMUINT32) + 1
: 0));
// Determine the true original data length
uiOldLength = getFieldDataLength( pField);
if( uiOldLength >= 0xFF ||
bOldEncrypted)
{
flmAssert( pField->ui8DataLen == 0xFF);
// Account for the header
uiOldHeader = (bOldEncrypted ? FLM_ENC_FLD_OVERHEAD : sizeof( FLMUINT32) + 1);
uiOldLength += uiOldHeader;
// Special work if this is a binary field
if( getFieldDataType( pField) == FLM_BINARY_TYPE)
{
// Since the value is binary, need to account for any
// alignment bytes
if( ((pField->ui32DataOffset + uiOldHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((pField->ui32DataOffset + uiOldHeader) & FLM_ALLOC_ALIGN);
uiOldLength += uiAlignment;
}
if (bOldEncrypted)
{
// Add the length of the encrypted data.
uiOldLength += getEncryptedDataLength( pField);
}
}
}
#ifdef FLM_PICKET_FENCE
if (bOldEncrypted)
{
uiOldLength += FLD_PICKET_FENCE_SIZE; // Add the picket fence size
}
#endif
// If the old length is > 4 and the new length is <= 4 the value MUST
// be stored in ui32DataOffset. To be simple, this is coded for
// the four cases.
uiAlignment = 0;
if( uiOldLength <= sizeof(FLMUINT32))
{
if( uiNewLength <= sizeof(FLMUINT32))
{
if (!bNewEncrypted)
{
pField->ui32DataOffset = 0;
pucDataPtr = (FLMBYTE *)&(pField->ui32DataOffset);
pField->ui8DataLen = (FLMUINT8)uiNewLength;
}
else
{
// The new field is encrypted.
// If this is a binary field, it must start on an aligned byte.
if( uiDataType == FLM_BINARY_TYPE &&
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN);
}
uiAllocStart = m_uiDataBufOffset;
uiDataBufSize = getDataBufSize();
if( uiNewLength + uiEncNewLength + uiAlignment +
uiHeader + m_uiDataBufOffset + uiPicketFenceSize > uiDataBufSize)
{
// Re-allocate the buffer.
uiNewSize = m_uiBufferSize +
uiHeader +
uiAlignment +
uiNewLength +
uiEncNewLength +
uiPicketFenceSize +
32;
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, m_uiBufferSize, uiNewSize, &pThis,
sizeof( FlmRecord *), &m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
m_uiBufferSize = uiNewSize;
pField = &(getFieldTable()[ uiSlot - 1]);
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
}
pucDataPtr = getDataBufPtr() + uiAllocStart + uiHeader +
uiAlignment + (uiPicketFenceSize / 2);
pucEncDataPtr = pucDataPtr + uiNewLength + (uiPicketFenceSize / 2);
setEncHeader( getDataBufPtr() + uiAllocStart, uiFlags, uiEncId,
uiNewLength, uiEncNewLength);
pField->ui8DataLen = 0xFF;
pField->ui32DataOffset = (FLMUINT32)uiAllocStart;
m_uiDataBufOffset += uiHeader + uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize;
}
}
else
{
// If this is a binary field, it must start on an aligned byte.
if( uiDataType == FLM_BINARY_TYPE &&
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN);
}
uiAllocStart = m_uiDataBufOffset;
uiDataBufSize = getDataBufSize();
if( uiNewLength + uiAlignment + uiHeader + uiEncNewLength +
m_uiDataBufOffset + uiPicketFenceSize > uiDataBufSize)
{
// Re-allocate the buffer.
uiNewSize = m_uiBufferSize +
uiHeader +
uiAlignment +
uiNewLength +
uiEncNewLength +
uiPicketFenceSize +
32;
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, m_uiBufferSize, uiNewSize,
&pThis, sizeof( FlmRecord *), &m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
m_uiBufferSize = uiNewSize;
pField = &(getFieldTable()[ uiSlot - 1]);
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
}
pucDataPtr = getDataBufPtr() + uiAllocStart + uiHeader +
uiAlignment + (uiPicketFenceSize / 2);
if (bNewEncrypted)
{
pucEncDataPtr = pucDataPtr + uiNewLength + (uiPicketFenceSize / 2);
}
// The presence of uiHeader indicates that the length of the new field
// is greater than 255 bytes, or it is encrypted, therefore we will
// store the actual length in the buffer.
if( uiHeader)
{
if (!bNewEncrypted)
{
pucTmp = getDataBufPtr() + uiAllocStart;
*pucTmp = 0; // Flags
pucTmp++;
UD2FBA( (FLMUINT32)uiNewLength, pucTmp);
pField->ui8DataLen = 0xFF;
pField->ui32DataOffset = (FLMUINT32)uiAllocStart;
}
else // Encrypted
{
setEncHeader( getDataBufPtr() + uiAllocStart, uiFlags,
uiEncId, uiNewLength, uiEncNewLength);
pField->ui8DataLen = 0xFF;
pField->ui32DataOffset = (FLMUINT32)uiAllocStart;
}
}
else
{
flmAssert( !bNewEncrypted);
pField->ui8DataLen = (FLMUINT8)uiNewLength;
pField->ui32DataOffset = (FLMUINT32)(uiAllocStart + uiAlignment);
}
m_uiDataBufOffset += uiHeader + uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize;
}
}
else // uiOldLength > sizeof(FLMUINT32)
{
if( uiNewLength > sizeof(FLMUINT32))
{
if( uiDataType == FLM_BINARY_TYPE &&
((pField->ui32DataOffset + uiHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((pField->ui32DataOffset + uiHeader) & FLM_ALLOC_ALIGN);
}
// Smaller or same size?
if( (uiTmp = (uiHeader + uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize)) <= uiOldLength)
{
if( uiTmp != uiOldLength)
{
m_bHolesInData = TRUE;
}
if( uiHeader)
{
if (!bNewEncrypted)
{
pucTmp = getDataBufPtr() + pField->ui32DataOffset;
*pucTmp = 0; // Flags
pucTmp++;
UD2FBA( (FLMUINT32)uiNewLength, pucTmp);
pField->ui8DataLen = 0xFF;
}
else
{
// It's encrypted...
setEncHeader( getDataBufPtr() + pField->ui32DataOffset,
uiFlags,
uiEncId,
uiNewLength,
uiEncNewLength);
pField->ui8DataLen = 0xFF;
// No need to set the offset because we are re-using the same
// buffer space.
}
}
else
{
flmAssert( uiNewLength < 0xFF);
flmAssert( !bNewEncrypted);
pField->ui8DataLen = (FLMUINT8)uiNewLength;
}
pucDataPtr = getDataBufPtr() +
pField->ui32DataOffset + uiHeader + uiAlignment + (uiPicketFenceSize / 2);
if (bNewEncrypted)
{
pucEncDataPtr = pucDataPtr + uiNewLength + (uiPicketFenceSize / 2);
}
}
else
{
// The new value is larger than the original value.
// If this is a binary field it must start on an aligned byte.
uiAlignment = 0;
if( uiDataType == FLM_BINARY_TYPE &&
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN);
}
uiAllocStart = m_uiDataBufOffset;
uiDataBufSize = getDataBufSize();
if( (m_uiDataBufOffset + uiHeader +
uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize) > uiDataBufSize)
{
// Re-allocate the buffer.
uiNewSize = m_uiBufferSize +
uiHeader +
uiAlignment +
uiNewLength +
uiEncNewLength +
uiPicketFenceSize +
32;
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, m_uiBufferSize, uiNewSize,
&pThis, sizeof( FlmRecord *), &m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
m_uiBufferSize = uiNewSize;
pField = &(getFieldTable()[ uiSlot - 1]);
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
}
pucDataPtr = getDataBufPtr() + m_uiDataBufOffset + uiHeader +
uiAlignment + (uiPicketFenceSize / 2);
if (bNewEncrypted)
{
pucEncDataPtr = pucDataPtr + uiNewLength + (uiPicketFenceSize / 2);
}
if( uiHeader)
{
if (!bNewEncrypted)
{
pucTmp = getDataBufPtr() + uiAllocStart;
*pucTmp = 0; // Flags
pucTmp++;
UD2FBA( (FLMUINT32)uiNewLength, pucTmp);
pField->ui8DataLen = 0xFF;
pField->ui32DataOffset = (FLMUINT32)uiAllocStart;
}
else
{
setEncHeader( getDataBufPtr() + uiAllocStart, uiFlags,
uiEncId, uiNewLength, uiEncNewLength);
pField->ui8DataLen = 0xFF;
pField->ui32DataOffset = (FLMUINT32)uiAllocStart;
}
}
else
{
flmAssert( uiNewLength < 0xFF);
flmAssert( !bNewEncrypted);
pField->ui32DataOffset = (FLMUINT32)(uiAllocStart + uiAlignment);
pField->ui8DataLen = (FLMUINT8)uiNewLength;
}
m_uiDataBufOffset += uiHeader + uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize;
m_bHolesInData = TRUE;
}
}
else // uiNewLength <= sizeof(FLMUINT32)
{
if (!bNewEncrypted)
{
pField->ui32DataOffset = 0;
pucDataPtr = (FLMBYTE *)&(pField->ui32DataOffset);
pField->ui8DataLen = (FLMUINT8)uiNewLength;
m_bHolesInData = TRUE;
}
else
{
// The new field is encrypted.
// If this is a binary field, it must start on an aligned byte.
if( uiDataType == FLM_BINARY_TYPE &&
((pField->ui32DataOffset + uiHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((pField->ui32DataOffset + uiHeader) & FLM_ALLOC_ALIGN);
}
// New field will be stored in a buffer.
// See if we can re-use the old buffer.
if( (uiTmp = (uiHeader + uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize)) <= uiOldLength)
{
if( uiTmp != uiOldLength)
{
m_bHolesInData = TRUE;
}
setEncHeader( getDataBufPtr() + pField->ui32DataOffset,
uiFlags,
uiEncId,
uiNewLength,
uiEncNewLength);
pField->ui8DataLen = 0xFF;
uiAllocStart = pField->ui32DataOffset;
}
else
{
// The field position is changing, so we need to recalculate the alignment
// (if needed).
// If this is a binary field, it must start on an aligned byte.
if( uiDataType == FLM_BINARY_TYPE &&
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((m_uiDataBufOffset + uiHeader) & FLM_ALLOC_ALIGN);
}
uiAllocStart = m_uiDataBufOffset;
uiDataBufSize = getDataBufSize();
if( uiNewLength + uiEncNewLength + uiAlignment +
uiHeader + m_uiDataBufOffset + uiPicketFenceSize > uiDataBufSize)
{
// Re-allocate the buffer.
uiNewSize = m_uiBufferSize +
uiHeader +
uiAlignment +
uiNewLength +
uiEncNewLength +
uiPicketFenceSize +
32;
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, m_uiBufferSize, uiNewSize,
&pThis, sizeof( FlmRecord *), &m_pucBuffer, &bHeapAlloc)))
{
goto Exit;
}
m_uiBufferSize = uiNewSize;
pField = &(getFieldTable()[ uiSlot - 1]);
if( bHeapAlloc)
{
m_uiFlags |= RCA_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_HEAP_BUFFER;
}
}
setEncHeader( getDataBufPtr() + uiAllocStart,
uiFlags,
uiEncId,
uiNewLength,
uiEncNewLength);
pField->ui8DataLen = 0xFF;
pField->ui32DataOffset = (FLMUINT32)uiAllocStart;
m_uiDataBufOffset += uiHeader + uiAlignment + uiNewLength +
uiEncNewLength + uiPicketFenceSize;
}
pucDataPtr = getDataBufPtr() + uiAllocStart + uiHeader +
uiAlignment + (uiPicketFenceSize / 2);
pucEncDataPtr = pucDataPtr + uiNewLength + (uiPicketFenceSize / 2);
}
}
}
setFieldDataType( pField, uiDataType);
#ifdef FLM_DEBUG
f_memset( pucDataPtr, 0, uiNewLength);
f_memset( pucEncDataPtr, 0, uiEncNewLength);
#endif
if (ppDataPtr)
{
*ppDataPtr = pucDataPtr;
}
if (ppEncDataPtr)
{
*ppEncDataPtr = pucEncDataPtr;
}
#ifdef FLM_PICKET_FENCE
// Set the picket fences
if (bNewEncrypted)
{
pucDataPtr -= 4;
f_memcpy( pucDataPtr, (FLMBYTE *)FLD_RAW_FENCE,
FLD_PICKET_FENCE_SIZE / 2);
pucEncDataPtr -= 4;
f_memcpy( pucEncDataPtr, (FLMBYTE *)FLD_ENC_FENCE,
FLD_PICKET_FENCE_SIZE / 2);
}
#endif
Exit:
if( RC_BAD( rc))
{
if (ppDataPtr)
{
*ppDataPtr = NULL;
}
if (ppEncDataPtr)
{
*ppEncDataPtr = NULL;
}
}
return( rc);
}
/*****************************************************************************
Desc: Add a globally shared reference to this object.
*****************************************************************************/
FLMINT FLMAPI FlmRecord::AddRef( void)
{
FLMINT iRefCnt = 0;
iRefCnt = f_atomicInc( &m_refCnt);
flmAssert( iRefCnt > 1);
return( iRefCnt);
}
/*****************************************************************************
Desc: Removes a globally shared reference to this object.
*****************************************************************************/
FLMINT FlmRecord::Release(
FLMBOOL bMutexLocked)
{
FLMINT iRefCnt = 0;
FLMBOOL bUnlockMutex = FALSE;
if( isCached() && getRefCount() == 2)
{
if( !bMutexLocked)
{
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
bMutexLocked = TRUE;
bUnlockMutex = TRUE;
}
}
iRefCnt = f_atomicDec( &m_refCnt);
if( !iRefCnt)
{
flmAssert( !isCached());
m_uiFlags |= RCA_OK_TO_DELETE;
delete this;
}
else if( bMutexLocked && iRefCnt == 1 && isCached())
{
// If the record is still cached, and the reference count
// is 1, it is safe to compact the record's buffer
if( m_bHolesInData || getFreeMemory())
{
#ifdef FLM_CHECK_RECORD
if (RC_BAD( checkRecord()))
{
flmAssert( 0);
}
#endif
compactMemory();
#ifdef FLM_CHECK_RECORD
if (RC_BAD( checkRecord()))
{
flmAssert( 0);
}
#endif
flmAssert( !getFreeMemory());
}
}
if( bUnlockMutex)
{
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
}
return( iRefCnt);
}
/*****************************************************************************
Desc: Return the nth occurance of the field with a matching field id.
*****************************************************************************/
void * FlmRecord::find(
void * pvField,
FLMUINT uiFieldID,
FLMUINT uiOccur,
FLMUINT uiFindOption)
{
FLMUINT uiStartLevel;
FlmField * pField = getFieldPointer( pvField);
if( !uiOccur)
{
uiOccur = 1;
}
if( !pField)
{
goto Exit;
}
uiStartLevel = getFieldLevel( pField);
do
{
if( (uiFieldID == pField->ui16FieldID) && (--uiOccur < 1))
{
return( getFieldVoid( pField));
}
} while( (pField = nextField( pField)) != NULL &&
((getFieldLevel( pField) > uiStartLevel) ||
(uiFindOption == SEARCH_FOREST &&
getFieldLevel( pField) == uiStartLevel)));
Exit:
return( NULL);
}
/*****************************************************************************
Desc: Given a null terminated field path, return the matching occurance.
*****************************************************************************/
void * FlmRecord::find(
void * pvField,
FLMUINT * puiPathArray,
FLMUINT uiOccur,
FLMUINT uiFindOption)
{
FlmField * pSaveField;
FLMUINT * path;
FLMUINT uiLevel;
FlmField * pField = getFieldPointer( pvField);
// Handle empty record
if( !pvField)
{
return( NULL);
}
if( !uiOccur)
{
uiOccur = 1;
}
uiLevel = getFieldLevel( pField);
for(;;)
{
path = puiPathArray + ( getFieldLevel( pField) - uiLevel);
pSaveField = pField;
if( *path == pField->ui16FieldID)
{
if( *(path + 1) == 0 && (--uiOccur < 1))
{
return( getFieldVoid( pField));
}
if( (pField = firstChildField( pField)) != NULL)
{
continue;
}
pField = pSaveField;
}
do
{
pField = nextField( pField);
}
while( pField != NULL && getFieldLevel( pField) > getFieldLevel( pSaveField));
if( !pField || getFieldLevel( pField) < uiLevel ||
(uiFindOption != SEARCH_FOREST && getFieldLevel( pField) == uiLevel))
{
break;
}
}
return( NULL);
}
/*****************************************************************************
Desc: Import a record from a file
*****************************************************************************/
RCODE FlmRecord::importRecord(
IF_FileHdl * pFileHdl,
F_NameTable * pNameTable)
{
char ucBuffer[ 1024];
char * pucData = &ucBuffer[ 0];
flmAssert( !isCached());
flmAssert( !isReadOnly());
return( importTree( pFileHdl, &pucData,
sizeof( ucBuffer), pNameTable, this));
}
/*****************************************************************************
Desc: Import a record from a buffer
*****************************************************************************/
RCODE FlmRecord::importRecord(
const char ** ppBuffer,
FLMUINT uiBufSize,
F_NameTable * pNameTable)
{
flmAssert( !isCached());
flmAssert( !isReadOnly());
return( importTree( NULL, (char **)ppBuffer, uiBufSize, pNameTable, this));
}
/*****************************************************************************
Desc : This function parses and builds one complete GEDCOM tree from a GEDCOM
character buffer or file. The beginning level number is used only to
delimit the tree (possiblly in forest); the generated tree's level
always start at zero.
*****************************************************************************/
FSTATIC RCODE importTree(
IF_FileHdl * pFileHdl,
char ** ppBuf,
FLMUINT uiBufSize,
F_NameTable * pNameTable,
FlmRecord * pRec)
{
GED_STREAM gedStream;
FLMUINT level;
FLMUINT levelBase = 0;
FLMUINT levelPrior = 0;
FLMBYTE nextChar;
FLMBOOL bFirstFieldProcessed = FALSE;
RCODE rc;
FLMUINT64 ui64StartPos;
gedStream.pFileHdl = pFileHdl;
gedStream.pThis = *ppBuf;
gedStream.pBuf = *ppBuf;
gedStream.uiBufSize = uiBufSize;
pRec->clear();
if( pFileHdl)
{
// Find 1st starting file position
if( RC_OK( pFileHdl->seek( 0, FLM_IO_SEEK_CUR, &gedStream.ui64FilePos)))
{
gedStream.pLast = gedStream.pBuf;
gedReadChar( &gedStream, gedStream.ui64FilePos);
}
else
return( RC_SET( FERR_FILE_ER));
}
else
{
gedStream.errorIO = 0;
gedStream.ui64FilePos = 0;
gedStream.pLast = gedStream.pBuf + (uiBufSize - 1);
gedStream.thisC = f_toascii( *gedStream.pBuf);
}
for(;;)
{
gedSkipBlankLines( &gedStream);
ui64StartPos = gedStream.ui64FilePos;
if( f_isdigit( gedStream.thisC))
{
level = 0;
do
{
level = gedStream.thisC - ASCII_ZERO + (level * 10);
nextChar = (FLMBYTE)(gedNextChar( &gedStream));
} while( f_isdigit( nextChar));
if( ! f_iswhitespace( gedStream.thisC))
{
rc = RC_SET( FERR_BAD_FIELD_LEVEL);
break;
}
if( level > GED_MAXLVLNUM)
{
rc = RC_SET( FERR_GED_MAXLVLNUM);
break;
}
if( bFirstFieldProcessed)
{
if( levelBase >= level)
{
goto successful;
}
else if( (levelPrior < level) && ((levelPrior + 1) != level))
{
rc = RC_SET( FERR_GED_SKIP_LEVEL);
break;
}
}
else
{
levelBase = level;
}
levelPrior = level;
// Process import tag and value
rc = importField( (level - levelBase), &gedStream,
pNameTable, pRec);
if( RC_OK( rc))
{
bFirstFieldProcessed = TRUE;
continue;
}
}
else if( gedStream.thisC == '\0' || gedStream.thisC == ASCII_CTRLZ)
{
if( gedStream.errorIO)
{
rc = RC_SET( FERR_FILE_ER);
}
else if( bFirstFieldProcessed)
{
successful:
if( pFileHdl == NULL)
{
*ppBuf = gedStream.pThis +
(FLMINT32)(ui64StartPos - gedStream.ui64FilePos);
}
gedStream.ui64FilePos = ui64StartPos;
rc = FERR_OK;
}
else
{
rc = RC_SET( FERR_END);
}
}
else
{
rc = RC_SET( FERR_BAD_FIELD_LEVEL);
}
break;
}
if( rc != FERR_OK)
{
pRec->clear();
if( pFileHdl == NULL)
{
*ppBuf = gedStream.pThis;
}
}
if( pFileHdl)
{
pFileHdl->seek( gedStream.ui64FilePos,
FLM_IO_SEEK_SET, &gedStream.ui64FilePos);
}
return( rc);
}
/*****************************************************************************
Desc: Parse the tag, value, and length from a GEDCOM buffer, and insert it into
a FlmRecord. Continuation lines and embedded comments are also handled.
*****************************************************************************/
FSTATIC RCODE importField(
FLMUINT uiLevel,
GED_STREAM * pGedStream,
F_NameTable * pNameTable,
FlmRecord * pRec)
{
FLMUINT64 ui64StartPos;
RCODE rc = FERR_OK;
FLMUINT drn = 0;
FLMUINT uiTagNum;
char tagBuf[ GED_MAXTAGLEN + 1];
void * pvField;
gedSkipWhiteSpaces( pGedStream);
// Process optional xref-id
ui64StartPos = pGedStream->ui64FilePos;
if( pGedStream->thisC == ASCII_AT)
{
int badDRN;
for( badDRN = 0, gedNextChar( pGedStream);
pGedStream->thisC != ASCII_AT;
gedNextChar( pGedStream))
{
FLMUINT priorDrn = drn;
if( ! badDRN)
{
if( f_isdigit( pGedStream->thisC))
{
drn = (drn * 10) + pGedStream->thisC - ASCII_ZERO;
badDRN = priorDrn != (drn / 10);
}
else
{
badDRN = 1;
}
}
}
if( badDRN)
{
drn = 0;
}
gedNextChar( pGedStream);
if( f_iswhitespace( pGedStream->thisC))
{
gedSkipWhiteSpaces( pGedStream);
}
else
{
rc = RC_SET( FERR_GED_BAD_RECID);
goto Exit;
}
}
if( drn)
{
// Record can only have one ID (DRN)
flmAssert( pRec->getID() == 0);
pRec->setID( drn);
}
// Determine the Tag Number and insert a new field
ui64StartPos = pGedStream->ui64FilePos;
if( !gedCopyTag( pGedStream, tagBuf))
{
return( RC_SET( FERR_INVALID_TAG));
}
if( !pNameTable->getFromTagTypeAndName( NULL, tagBuf,
FLM_FIELD_TAG, &uiTagNum))
{
// See if tag is the reserved tag with the number following
if( tagBuf[0] == f_toascii( 'T') &&
tagBuf[1] == f_toascii( 'A') &&
tagBuf[2] == f_toascii( 'G') &&
tagBuf[3] == f_toascii( '_'))
{
uiTagNum = f_atoi( &tagBuf[ 4]);
}
else
{
return( RC_SET( FERR_NOT_FOUND));
}
}
if( RC_BAD( rc = pRec->insertLast( uiLevel, uiTagNum,
FLM_TEXT_TYPE, &pvField)))
{
goto Exit;
}
gedSkipWhiteSpaces( pGedStream);
// Alternate xref_ptr used instead of "value"
ui64StartPos = pGedStream->ui64FilePos;
if( pGedStream->thisC == ASCII_AT)
{
for( drn = 0; gedNextChar( pGedStream) != ASCII_AT;)
{
FLMUINT priorDrn = drn;
if( f_isdigit( pGedStream->thisC))
{
drn = (drn * 10) + pGedStream->thisC - ASCII_ZERO;
if( priorDrn == (drn / 10))
{
continue;
}
}
rc = RC_SET( FERR_GED_BAD_VALUE);
goto Exit;
}
gedNextChar( pGedStream);
if( RC_BAD( rc = pRec->setUINT( pvField, drn)))
{
goto Exit;
}
if( gedCopyValue( pGedStream, NULL))
{
rc = RC_SET( FERR_GED_BAD_VALUE);
goto Exit;
}
}
else
{
FLMINT valLength;
FLMUINT64 ui64TempPos = pGedStream->ui64FilePos;
if( (valLength = gedCopyValue( pGedStream, NULL)) > 0)
{
FLMBYTE * vp;
if (RC_BAD( rc = pRec->allocStorageSpace(
pvField, FLM_TEXT_TYPE, valLength,
0, 0, 0, &vp, NULL)))
{
goto Exit;
}
gedReadChar( pGedStream, ui64TempPos);
gedCopyValue( pGedStream, (char *)vp);
}
}
ui64StartPos = pGedStream->ui64FilePos;
Exit:
gedReadChar( pGedStream, ui64StartPos);
return( rc);
}
/*****************************************************************************
Desc: Parse the tag and, if `dest' is not NULL, copy and terminating w/null
*****************************************************************************/
FLMUINT gedCopyTag(
GED_STREAM * pGedStream,
char * dest)
{
if( f_isalpha( pGedStream->thisC) || pGedStream->thisC == ASCII_UNDERSCORE )
{
int tagLen, maxTag;
for( tagLen = 0, maxTag = GED_MAXTAGLEN;
f_isalnum( pGedStream->thisC) || pGedStream->thisC == ASCII_UNDERSCORE;
gedNextChar( pGedStream))
{
tagLen++;
if( dest && maxTag)
{
*dest++ = (FLMBYTE)(pGedStream->thisC);
maxTag--;
}
}
if( dest)
{
*dest = '\0';
}
switch( pGedStream->thisC)
{
case '\0':
if( pGedStream->errorIO)
{
break;
}
case ASCII_SPACE:
case ASCII_NEWLINE:
case ASCII_CR:
case ASCII_TAB:
case ASCII_CTRLZ:
{
return( (FLMUINT) tagLen);
}
}
}
return( 0);
}
/*****************************************************************************
Desc: Parse the value and, if `dest' is not NULL, copy and terminating w/null
*****************************************************************************/
FLMINT gedCopyValue(
GED_STREAM * pGedStream,
char * dest)
{
FLMUINT OEMcount = 0;
FLMUINT valLength = 0;
FLMUINT wsCount;
FLMUINT lineLength = 0;
FLMUINT64 ui64FirstPos = 0;
FLMBYTE c;
for(;;)
{
gedSkipWhiteSpaces( pGedStream);
if( pGedStream->thisC == ASCII_DQUOTE)
{
gedNextChar( pGedStream);
ui64FirstPos = pGedStream->ui64FilePos;
}
else
{
ui64FirstPos = 0;
}
for( lineLength = 0;;)
{
switch( pGedStream->thisC)
{
case ASCII_DQUOTE:
case ASCII_SPACE:
case ASCII_TAB:
for( wsCount = 0;;)
{
switch( gedNextChar( pGedStream))
{
case '\0':
if( pGedStream->errorIO)
{
return( 0);
}
case ASCII_NEWLINE:
case ASCII_CR:
case ASCII_CTRLZ:
goto eol;
case ASCII_SPACE:
case ASCII_TAB:
wsCount++;
continue;
default:
gedReadChar( pGedStream, pGedStream->ui64FilePos - 1);
lineLength += wsCount;
goto valid;
}
}
case '\0':
if( pGedStream->errorIO)
{
return( 0);
}
case ASCII_NEWLINE:
case ASCII_CR:
case ASCII_CTRLZ:
eol: if( lineLength)
{
valLength += lineLength;
if( dest)
{
gedReadChar( pGedStream, ui64FirstPos);
while( lineLength--)
{
c = (FLMBYTE)pGedStream->thisC;
if ((c >= ASCII_SPACE) && (c < 127))
{
*dest++ = c;
}
else if (c == ASCII_TAB)
{
*dest++ = WHITE_SPACE_CODE | NATIVE_TAB;
}
else if (c == ASCII_NEWLINE)
{
*dest++ = WHITE_SPACE_CODE | NATIVE_LINEFEED;
}
else if (c == ASCII_CR)
{
*dest++ = WHITE_SPACE_CODE | HARD_RETURN;
}
else
{
*dest++ = (c >= 127)
? OEM_CODE
: UNK_EQ_1_CODE | NATIVE_TYPE;
*dest++ = c;
}
gedNextChar( pGedStream);
}
if( pGedStream->thisC == ASCII_DQUOTE)
{
gedNextChar( pGedStream);
}
}
}
break;
default:
valid:
c = (FLMBYTE)pGedStream->thisC;
if (((c < ASCII_SPACE) &&
(c != ASCII_TAB) &&
(c != ASCII_CR) &&
(c != ASCII_NEWLINE)) ||
(c >= 127))
{
OEMcount += 1;
}
if( !ui64FirstPos)
{
ui64FirstPos = pGedStream->ui64FilePos;
}
lineLength++;
gedNextChar( pGedStream);
continue;
}
break;
}
gedSkipBlankLines( pGedStream);
if( pGedStream->thisC != ASCII_PLUS)
{
break;
}
gedNextChar( pGedStream);
}
return( valLength + OEMcount);
}
/*****************************************************************************
Desc: strip blank lines from buffer by repositioning pointer
*****************************************************************************/
void gedSkipBlankLines(
GED_STREAM * pGedStream)
{
for(;;)
{
gedSkipWhiteSpaces( pGedStream);
switch( pGedStream->thisC)
{
case ASCII_POUND:
for(;;)
{
switch( gedNextChar( pGedStream))
{
case ASCII_NEWLINE:
case ASCII_CR:
goto outerFor;
case '\0':
case ASCII_CTRLZ:
return;
}
}
case ASCII_NEWLINE:
case ASCII_CR:
outerFor:
gedNextChar( pGedStream);
continue;
}
return;
}
}
/*****************************************************************************
Desc: get current character then increment pointer.
buffer may be refilled if file available
*****************************************************************************/
FLMINT gedNextChar(
GED_STREAM * pGedStream)
{
pGedStream->errorIO = 0;
if( pGedStream->pThis < pGedStream->pLast)
{
pGedStream->pThis++;
goto returnC;
}
if( pGedStream->pFileHdl)
{
FLMUINT bytesRead;
RCODE rc;
rc = pGedStream->pFileHdl->read( FLM_IO_CURRENT_POS,
pGedStream->uiBufSize, pGedStream->pBuf, &bytesRead);
if( rc == FERR_OK || (rc == FERR_IO_END_OF_FILE && bytesRead))
{
pGedStream->pThis = pGedStream->pBuf;
pGedStream->pLast = pGedStream->pBuf + (bytesRead - 1);
returnC:
pGedStream->ui64FilePos++;
return( pGedStream->thisC = f_toascii( *pGedStream->pThis));
}
pGedStream->errorIO = rc != FERR_IO_END_OF_FILE;
}
return( pGedStream->thisC = '\0');
}
/*****************************************************************************
Desc: get an ASCII character at absolute file position and reset pointers
*****************************************************************************/
FLMINT gedReadChar(
GED_STREAM * pGedStream,
FLMUINT64 ui64FilePos)
{
RCODE rc = FERR_OK;
pGedStream->errorIO = 0;
if( pGedStream->pFileHdl)
{
FLMUINT bytesRead;
char * pszTemp;
pszTemp = pGedStream->pThis +
(FLMINT32)(ui64FilePos - pGedStream->ui64FilePos);
if( pGedStream->pBuf == pGedStream->pLast ||
pszTemp > pGedStream->pLast || pszTemp < pGedStream->pBuf)
{
if( RC_OK(rc = pGedStream->pFileHdl->seek( ui64FilePos,
FLM_IO_SEEK_SET, &pGedStream->ui64FilePos)) &&
(RC_OK( rc = pGedStream->pFileHdl->read( FLM_IO_CURRENT_POS,
pGedStream->uiBufSize, pGedStream->pBuf, &bytesRead)) ||
(rc == FERR_IO_END_OF_FILE && bytesRead)))
{
pGedStream->pThis = pGedStream->pBuf;
pGedStream->pLast = pGedStream->pBuf + (bytesRead - 1);
return( pGedStream->thisC = f_toascii( *pGedStream->pThis));
}
else
{
pGedStream->errorIO = 1;
return( pGedStream->thisC = '\0');
}
}
else
{
pGedStream->ui64FilePos = ui64FilePos;
pGedStream->pThis = pszTemp;
return( pGedStream->thisC = f_toascii( *pszTemp));
}
}
if( (pGedStream->pBuf + ui64FilePos) > pGedStream->pLast)
{
return( pGedStream->thisC = '\0');
}
pGedStream->pThis = pGedStream->pBuf + ui64FilePos;
pGedStream->ui64FilePos = ui64FilePos;
return( pGedStream->thisC = f_toascii( *pGedStream->pThis));
}
/*****************************************************************************
Desc: Import a record from GEDCOM
*****************************************************************************/
RCODE FlmRecord::importRecord(
NODE * pNode)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMBYTE * pucEncData;
HFDB hDb;
FLMUINT uiContainerID = 0;
FLMUINT uiRecordID = 0;
void * pvField;
FlmField * pField;
FLMUINT uiDataType;
FLMUINT uiFlags;
flmAssert( pNode);
flmAssert( !isCached());
flmAssert( !isReadOnly());
clear();
// Copy any source info that is within the root node.
if( RC_OK( GedGetRecSource( pNode, &hDb, &uiContainerID, &uiRecordID)))
{
setContainerID( uiContainerID);
setID( uiRecordID);
}
while( pNode)
{
uiDataType = GedValType( pNode);
if( RC_BAD( rc = insertLast( GedNodeLevel( pNode),
GedTagNum( pNode), uiDataType, &pvField)))
{
goto Exit;
}
pField = getFieldPointer( pvField);
if( GedValLen( pNode) || GedEncLen( pNode))
{
uiFlags = pNode->ui32EncFlags;
if( RC_BAD( rc = getNewDataPtr( pField, uiDataType,
pNode->ui32Length, pNode->ui32EncLength, pNode->ui32EncId, uiFlags,
(!uiFlags || uiFlags & FLD_HAVE_DECRYPTED_DATA)
? &pucData
: NULL,
(uiFlags & FLD_HAVE_ENCRYPTED_DATA)
? &pucEncData
: NULL)))
{
goto Exit;
}
if (!uiFlags || uiFlags & FLD_HAVE_DECRYPTED_DATA)
{
f_memcpy( pucData, GedValPtr( pNode), GedValLen( pNode));
}
if ( uiFlags & FLD_HAVE_ENCRYPTED_DATA)
{
f_memcpy( pucEncData, GedEncPtr( pNode), GedEncLen( pNode));
}
}
// Copy any of the value flags that this node may have
if( GedIsRightTruncated( pNode))
{
setRightTruncated( pField, TRUE);
}
if( GedIsLeftTruncated( pNode))
{
setLeftTruncated( pField, TRUE);
}
pNode = GedNodeNext( pNode);
if( pNode && GedNodeLevel( pNode) == 0)
{
break;
}
}
Exit:
return( rc);
}
/*****************************************************************************
Desc: Export the record to GEDCOM
*****************************************************************************/
RCODE FlmRecord::exportRecord(
HFDB hDb,
F_Pool * pPool,
NODE ** ppRoot)
{
RCODE rc = FERR_OK;
FLMBYTE * pucData;
FLMBYTE * pucEncData = NULL;
FLMBYTE * exPtr;
FlmField * pField = getFieldPointer(root());
NODE * pPrevNode = NULL;
NODE * pNode = NULL;
FLMUINT uiLevelBase = getFieldLevel( pField);
FLMUINT uiEncLength = 0;
flmAssert( ppRoot != NULL);
flmAssert( pPool != NULL);
*ppRoot = NULL;
while( pField)
{
if( *ppRoot)
{
if( (pNode = GedNodeMake( pPool,
pField->ui16FieldID, &rc)) == NULL)
{
goto Exit;
}
}
else
{
// Create root source node
if( RC_BAD( rc = gedCreateSourceNode( pPool, pField->ui16FieldID,
hDb, getContainerID(), getID(), &pNode)))
{
goto Exit;
}
}
// Link in new node
if( pPrevNode)
{
pPrevNode->next = pNode;
}
else
{
*ppRoot = pNode;
}
pNode->prior = pPrevNode;
GedNodeLevelSet( pNode, (getFieldLevel( pField) - uiLevelBase));
pPrevNode = pNode;
if (isEncryptedField(pField))
{
uiEncLength = getEncryptedDataLength( pField);
pucData = (FLMBYTE *)GedAllocSpace( pPool,
pNode,
getFieldDataType( pField),
getFieldDataLength( pField),
getEncryptionID( pField),
uiEncLength);
pucEncData = (FLMBYTE *)GedEncPtr( pNode);
if( !pucData && uiEncLength)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
else
{
pucData = (FLMBYTE *)GedAllocSpace( pPool, pNode,
getFieldDataType( pField), getFieldDataLength( pField));
if( !pucData && getFieldDataLength( pField))
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
// Directly copy over the NODE value pucData.
if( getFieldDataLength( pField))
{
if( (exPtr = getDataPtr( pField)) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
f_memcpy( pucData, exPtr, getFieldDataLength( pField));
if (pNode->ui32EncId)
{
flmAssert( pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA);
}
}
if (pNode->ui32EncId)
{
if ((exPtr = getEncryptionDataPtr( pField)) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
flmAssert( uiEncLength);
flmAssert( pNode->ui32EncFlags & FLD_HAVE_ENCRYPTED_DATA);
f_memcpy( pucEncData, exPtr, uiEncLength);
}
// Copy any of the value flags that this node may have
if( isRightTruncated( pField))
{
GedSetRightTruncated( pNode);
}
if( isLeftTruncated( pField))
{
GedSetLeftTruncated( pNode);
}
pField = nextField( pField);
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
void * FlmRecord::getFieldVoid(
FlmField * pField)
{
FLMUINT uiSlot;
if( !pField)
{
return( NULL);
}
if( !m_uiFldTblOffset ||
pField > &(getFieldTable()[ m_uiFldTblOffset - 1]))
{
flmAssert( 0);
return( NULL);
}
uiSlot = (pField - getFieldTable()) + 1;
return( (void *)uiSlot);
}
/*****************************************************************************
Desc:
*****************************************************************************/
FlmField * FlmRecord::getFieldPointer(
void * pvField)
{
FLMUINT uiSlot = (FLMUINT)pvField;
FlmField * pField = NULL;
if( !uiSlot)
{
goto Exit;
}
if( uiSlot > m_uiFldTblOffset)
{
flmAssert( 0);
goto Exit;
}
pField = &(getFieldTable()[ uiSlot - 1]);
flmAssert( pField->ui16FieldID);
Exit:
return( pField);
}
/*****************************************************************************
Desc:
*****************************************************************************/
FLMBYTE * FlmRecord::getDataPtr(
FlmField * pField)
{
if (!isEncryptedField(pField))
{
if( !pField->ui8DataLen)
{
return( NULL);
}
else if( pField->ui8DataLen <= 4)
{
return( (FLMBYTE *)&(pField->ui32DataOffset));
}
else if( pField->ui8DataLen < 0xFF)
{
return( getDataBufPtr() + pField->ui32DataOffset);
}
else
{
FLMUINT uiOffset = pField->ui32DataOffset + sizeof( FLMUINT32) + 1;
if( getFieldDataType( pField) == FLM_BINARY_TYPE)
{
if( (uiOffset & FLM_ALLOC_ALIGN) != 0)
{
uiOffset += (FLM_ALLOC_ALIGN + 1) - (uiOffset & FLM_ALLOC_ALIGN);
}
}
return( getDataBufPtr() + uiOffset);
}
}
else
{
FLMUINT uiOffset = pField->ui32DataOffset + FLM_ENC_FLD_OVERHEAD;
FLMUINT uiPicketFenceSize = 0;
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = FLD_PICKET_FENCE_SIZE / 2;
#endif
if( getFieldDataType( pField) == FLM_BINARY_TYPE)
{
if( (uiOffset & FLM_ALLOC_ALIGN) != 0)
{
uiOffset += (FLM_ALLOC_ALIGN + 1) - (uiOffset & FLM_ALLOC_ALIGN);
}
}
return ( getDataBufPtr() + uiOffset + uiPicketFenceSize);
}
}
/*****************************************************************************
Desc: Get the pointer to the encrypted data.
*****************************************************************************/
FLMBYTE * FlmRecord::getEncryptionDataPtr(
FlmField * pField)
{
FLMBYTE * pucTmp = NULL;
FLMUINT uiOffset;
FLMUINT uiPicketFenceSize = 0;
// We assume that this field is encrypted. If it isn't,
// then we will return NULL.
if (!isEncryptedField(pField))
{
flmAssert( 0);
goto Exit;
}
#ifdef FLM_PICKET_FENCE
uiPicketFenceSize = FLD_PICKET_FENCE_SIZE;
#endif
uiOffset = pField->ui32DataOffset + FLM_ENC_FLD_OVERHEAD;
if( getFieldDataType( pField) == FLM_BINARY_TYPE)
{
if( (uiOffset & FLM_ALLOC_ALIGN) != 0)
{
uiOffset += (FLM_ALLOC_ALIGN + 1) - (uiOffset & FLM_ALLOC_ALIGN);
}
}
pucTmp = getDataBufPtr() + uiOffset + getFieldDataLength( pField) +
uiPicketFenceSize;
Exit:
return pucTmp;
}
/*****************************************************************************
Desc:
*****************************************************************************/
FlmField * FlmRecord::getFirstField( void)
{
if( !m_uiFldTblOffset)
{
return( NULL);
}
flmAssert( (getFieldTable()[ 0]).ui16FieldID);
return( &(getFieldTable()[ 0]));
}
/*****************************************************************************
Desc:
*****************************************************************************/
FlmField * FlmRecord::getLastField( void)
{
FLMUINT uiLoop;
FlmField * pField = NULL;
if( !m_uiFldTblOffset)
{
goto Exit;
}
for( uiLoop = m_uiFldTblOffset; uiLoop; uiLoop--)
{
pField = &(getFieldTable()[ uiLoop - 1]);
if( pField->ui16FieldID)
{
break;
}
}
Exit:
return( pField);
}
/*****************************************************************************
Desc:
*****************************************************************************/
FlmField * FlmRecord::prevField(
FlmField * pField)
{
if( !pField)
{
return( NULL);
}
return( getFieldPointer( (void *)((FLMUINT)(pField->uiPrev))));
}
/*****************************************************************************
Desc:
*****************************************************************************/
FlmField * FlmRecord::nextField(
FlmField * pField)
{
if( !pField)
{
return( NULL);
}
return( getFieldPointer( (void *)((FLMUINT)(pField->uiNext))));
}
/*****************************************************************************
Desc:
*****************************************************************************/
FLMUINT FlmRecord::getTotalMemory( void)
{
FLMUINT uiSize = sizeof( FlmRecord) +
gv_FlmSysData.RCacheMgr.pRecBufAlloc->getTrueSize(
m_uiBufferSize, m_pucBuffer);
if (m_pucFieldIdTable)
{
uiSize += gv_FlmSysData.RCacheMgr.pRecBufAlloc->getTrueSize(
fieldIdTableByteSize(), m_pucFieldIdTable);
}
return( uiSize);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::setFieldLevel(
FlmField * pField,
FLMUINT uiLevel)
{
flmAssert( !isCached());
flmAssert( !isReadOnly());
if( uiLevel > 7)
{
flmAssert( 0);
return( RC_SET( FERR_BAD_FIELD_LEVEL));
}
pField->ui8TypeAndLevel &= 0x1F;
pField->ui8TypeAndLevel |= (((FLMUINT8)uiLevel) << 5);
return( FERR_OK);
}
/*****************************************************************************
Desc:
*****************************************************************************/
FLMUINT FlmRecord::getFieldDataLength(
FlmField * pField)
{
FLMBYTE * pucLength;
FLMUINT uiDataLen = 0;
flmAssert( pField);
if( pField->ui8DataLen != 0xFF)
{
uiDataLen = pField->ui8DataLen;
goto Exit;
}
pucLength = getDataBufPtr() + pField->ui32DataOffset + 1;
if (isEncryptedField( pField))
{
pucLength += 2;
}
uiDataLen = FB2UD( pucLength);
Exit:
return( uiDataLen);
}
/*****************************************************************************
Desc:
*****************************************************************************/
void FlmRecord::setFieldDataType(
FlmField * pField,
FLMUINT uiDataType)
{
flmAssert( !isCached());
flmAssert( !isReadOnly());
pField->ui8TypeAndLevel &= 0xF8;
if( uiDataType <= 3)
{
pField->ui8TypeAndLevel |= (FLMUINT8)uiDataType;
}
else
{
flmAssert( uiDataType == FLM_BLOB_TYPE);
pField->ui8TypeAndLevel |= (FLMUINT8)FLD_BLOB_TYPE;
}
}
/*****************************************************************************
Desc: Function to determine if the field is encrypted.
*****************************************************************************/
FLMBOOL FlmRecord::isEncryptedField(
FlmField * pField
)
{
FLMBOOL bResult = FALSE;
FLMUINT uiFlags;
FLMBYTE * pucBuffer;
if (!pField)
{
goto Exit;
}
if (pField->ui8DataLen == 0xFF)
{
pucBuffer = getDataBufPtr() + pField->ui32DataOffset;
uiFlags = pucBuffer[ FLD_ENC_FLAGS_OFFSET];
if (uiFlags && uiFlags <= 0x03)
{
bResult = TRUE;
}
}
Exit:
return bResult;
}
/*****************************************************************************
Desc: Function to get the length of the encrypted data.
*****************************************************************************/
FLMUINT FlmRecord::getEncryptedDataLength(
FlmField * pField)
{
FLMBYTE * pucBuffer = NULL;
FLMUINT uiEncDataLength = 0;
// Make sure this is an encrypted field.
if (!isEncryptedField(pField))
{
flmAssert( 0);
goto Exit;
}
pucBuffer = getDataBufPtr() + pField->ui32DataOffset;
uiEncDataLength = FB2UD( &pucBuffer[ FLD_ENC_ENCRYPTED_DATA_LEN_OFFSET]);
Exit:
return uiEncDataLength;
}
/*****************************************************************************
Desc: Function to get the DRN of the encryption definition record in the
dictionary.
*****************************************************************************/
FLMUINT FlmRecord::getEncryptionID(
FlmField * pField
)
{
FLMBYTE * pucBuffer = NULL;
FLMUINT uiEncId = 0;
if (!isEncryptedField( pField))
{
flmAssert( 0);
goto Exit;
}
pucBuffer = getDataBufPtr() + pField->ui32DataOffset;
uiEncId = FB2UW( &pucBuffer[ FLD_ENC_ENCID_OFFSET]);
Exit:
return uiEncId;
}
/*****************************************************************************
Desc:
*****************************************************************************/
FLMUINT FlmRecord::getEncFlags(
FlmField * pField)
{
FLMBYTE * pucBuffer;
if (!isEncryptedField( pField))
{
flmAssert( 0);
return( 0);
}
pucBuffer = getDataBufPtr() + pField->ui32DataOffset;
return( (FLMUINT)pucBuffer[ FLD_ENC_FLAGS_OFFSET]);
}
/*****************************************************************************
Desc:
*****************************************************************************/
void FlmRecord::setEncFlags(
FlmField * pField,
FLMUINT uiFlags)
{
FLMBYTE * pucBuffer;
if (!isEncryptedField( pField))
{
flmAssert( 0);
return;
}
pucBuffer = getDataBufPtr() + pField->ui32DataOffset;
pucBuffer[ FLD_ENC_FLAGS_OFFSET] = (FLMBYTE)uiFlags;
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::getINT(
void * pvField,
FLMINT * piNumber)
{
RCODE rc = FERR_OK;
FLMBOOL bEncrypted;
FlmField * pField = getFieldPointer( pvField);
if (!pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
bEncrypted = isEncryptedField( pField);
if (!bEncrypted ||
(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = FlmStorage2INT( getFieldDataType( pField),
getFieldDataLength( pField),
(const FLMBYTE *)getDataPtr( pField),
piNumber);
}
else if (bEncrypted &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
else
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::getUINT(
void * pvField,
FLMUINT * puiNumber)
{
RCODE rc = FERR_OK;
FLMBOOL bEncrypted;
FlmField * pField = getFieldPointer( pvField);
if (!pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
bEncrypted = isEncryptedField( pField);
if (!bEncrypted ||
(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = FlmStorage2UINT( getFieldDataType( pField),
getFieldDataLength( pField),
(const FLMBYTE *)getDataPtr( pField),
puiNumber);
}
else if (bEncrypted &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
else
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::getUINT32(
void * pvField,
FLMUINT32 * pui32Number)
{
RCODE rc = FERR_OK;
FLMBOOL bEncrypted;
FlmField * pField = getFieldPointer( pvField);
if (!pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
bEncrypted = isEncryptedField( pField);
if (!bEncrypted ||
(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = FlmStorage2UINT32( getFieldDataType( pField),
getFieldDataLength( pField),
(const FLMBYTE *)getDataPtr( pField),
pui32Number);
}
else if (bEncrypted &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
else
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::getUnicode(
void * pvField,
FLMUNICODE * pUnicode,
FLMUINT * puiBufLen)
{
RCODE rc = FERR_OK;
FLMBOOL bEncrypted;
FlmField * pField = getFieldPointer( pvField);
if (!pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
bEncrypted = isEncryptedField( pField);
if (!bEncrypted ||
(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = FlmStorage2Unicode( getFieldDataType( pField),
getFieldDataLength( pField),
(const FLMBYTE *)getDataPtr( pField),
puiBufLen,
pUnicode);
}
else if (bEncrypted &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
else
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc:
*****************************************************************************/
RCODE FlmRecord::getNative(
void * pvField,
char * pszString,
FLMUINT * puiBufLen)
{
RCODE rc = FERR_OK;
FLMBOOL bEncrypted;
FlmField * pField = getFieldPointer( pvField);
if (!pField)
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
bEncrypted = isEncryptedField( pField);
if (!bEncrypted ||
(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = FlmStorage2Native( getFieldDataType( pField),
getFieldDataLength( pField),
(const FLMBYTE *)getDataPtr( pField),
puiBufLen,
pszString);
}
else if (bEncrypted &&
!(getEncFlags( pField) & FLD_HAVE_DECRYPTED_DATA))
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
else
{
rc = RC_SET( FERR_NOT_FOUND);
goto Exit;
}
Exit:
return( rc);
}
/*****************************************************************************
Desc: Set the header fields in an encrypted field.
*****************************************************************************/
void FlmRecord::setEncHeader(
FLMBYTE * pBuffer,
FLMUINT uiFlags,
FLMUINT uiEncId,
FLMUINT uiNewLength,
FLMUINT uiEncNewLength)
{
pBuffer [FLD_ENC_FLAGS_OFFSET] = (FLMBYTE)uiFlags;
UW2FBA( (FLMUINT16)uiEncId, &pBuffer [FLD_ENC_ENCID_OFFSET]);
UD2FBA( (FLMUINT32)uiNewLength, &pBuffer [FLD_ENC_DATA_LEN_OFFSET]);
UD2FBA( (FLMUINT32)uiEncNewLength, &pBuffer [FLD_ENC_ENCRYPTED_DATA_LEN_OFFSET]);
}
/*****************************************************************************
Desc:
*****************************************************************************/
void * FlmRecord::locateFieldByPosition(
FLMUINT uiPosition)
{
FlmField * pField = getFieldPointer( root());
FLMUINT uiLoop;
flmAssert( pField);
for (uiLoop = 1; pField && uiLoop < uiPosition; uiLoop++)
{
pField = nextField( pField);
}
return (getFieldVoid( pField));
}
/****************************************************************************
Desc:
****************************************************************************/
void FlmRecord::objectAllocInit(
void * pvAlloc)
{
// Need to make sure that m_refCnt and m_uiFlags are initialized to zero
// prior to unlocking the mutex. This is so the FLAIM allocator
// doesn't see garbage values that may cause it to relocate the object
// before the constructor has been called.
((FlmRecord *)pvAlloc)->m_uiFlags = 0;
((FlmRecord *)pvAlloc)->m_refCnt = 0;
}
#undef new
#undef delete
/****************************************************************************
Desc:
****************************************************************************/
void * FlmRecord::operator new(
FLMSIZET uiSize)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
F_UNREFERENCED_PARM( uiSize);
flmAssert( gv_FlmSysData.RCacheMgr.pRecAlloc->getCellSize() >= uiSize);
return( gv_FlmSysData.RCacheMgr.pRecAlloc->allocCell( NULL, objectAllocInit));
}
/****************************************************************************
Desc:
****************************************************************************/
void * FlmRecord::operator new[]( FLMSIZET)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
flmAssert( 0);
return( NULL);
}
/****************************************************************************
Desc:
****************************************************************************/
void * FlmRecord::operator new(
FLMSIZET uiSize,
const char *,
int)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
F_UNREFERENCED_PARM( uiSize);
flmAssert( gv_FlmSysData.RCacheMgr.pRecAlloc->getCellSize() >= uiSize);
return( gv_FlmSysData.RCacheMgr.pRecAlloc->allocCell( NULL, objectAllocInit));
}
/****************************************************************************
Desc:
****************************************************************************/
void * FlmRecord::operator new[](
FLMSIZET, // uiSize,
const char *, // pszFile,
int) // iLine)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
flmAssert( 0);
return( NULL);
}
/****************************************************************************
Desc:
****************************************************************************/
void FlmRecord::operator delete(
void * ptr)
{
if( !ptr)
{
return;
}
flmAssert( ((FlmRecord *)ptr)->m_refCnt == 0);
gv_FlmSysData.RCacheMgr.pRecAlloc->freeCell( ptr);
}
/****************************************************************************
Desc:
****************************************************************************/
void FlmRecord::operator delete[](
void * // ptr)
)
{
flmAssert( 0);
}
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_DEBUG) && !defined( __WATCOMC__)
void FlmRecord::operator delete(
void * ptr,
const char *, // file
int // line
)
{
if( !ptr)
{
return;
}
gv_FlmSysData.RCacheMgr.pRecAlloc->freeCell( ptr);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_DEBUG) && !defined( __WATCOMC__)
void FlmRecord::operator delete[](
void *, // ptr,
const char *, // file
int // line
)
{
flmAssert( 0);
}
#endif
/*****************************************************************************
Desc: Verify the structural integrity of the FlmRecord
*****************************************************************************/
RCODE FlmRecord::checkRecord()
{
RCODE rc = FERR_OK;
FlmField * pFld;
// Check each field to make sure it is acceptable.
pFld = getFieldPointer( root());
while( pFld)
{
if (RC_BAD( rc = checkField( pFld)))
{
flmAssert( 0);
goto Exit;
}
pFld = nextField( pFld);
}
Exit:
return( rc);
}
/******************************************************************************
Desc: Verify the structure and content of the FlmField
******************************************************************************/
RCODE FlmRecord::checkField(
FlmField * pFld)
{
RCODE rc = FERR_OK;
FLMBYTE * pucFldBuffer;
FLMUINT32 ui32DataLen;
FLMUINT32 ui32EncDataLen;
FLMUINT uiAlignment = 0;
FLMBYTE * pucDataFence = NULL;
FLMBYTE * pucEncFence = NULL;
FLMUNICODE * pUnicode = NULL;
FLMUINT uiBufLen;
FLMUINT uiEncID;
FLMUINT uiSlot = (FLMUINT)getFieldVoid( pFld);
// Determine if the field may be encrypted. Fields >= 255 and
// encrypted fields have their actual length stored in the buffer.
if ( pFld->ui8DataLen < 0xFF)
{
goto Exit;
}
// Get the buffer pointer and test it to make sure the structure is okay.
pucFldBuffer = getDataBufPtr() + pFld->ui32DataOffset;
// Test for encryption. If the field is not encrypted, we won't do any check
// right now.
if (*pucFldBuffer == 0)
{
goto Exit;
}
// If we have any other value now besides 1, 2 or 3, we have some corruption!
flmAssert( *pucFldBuffer <= 3);
// Make sure we have an encryption ID and that it appears to be within range
// at least.
uiEncID = FB2UW( &pucFldBuffer[ FLD_ENC_ENCID_OFFSET]);
if (!uiEncID)
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if (uiEncID > FLM_RESERVED_TAG_NUMS)
{
flmAssert( 0);
rc = RC_SET( FERR_BAD_ENCDEF_ID);
goto Exit;
}
ui32DataLen = FB2UD( &pucFldBuffer[ FLD_ENC_DATA_LEN_OFFSET]);
if (!ui32DataLen)
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
ui32EncDataLen = FB2UD( &pucFldBuffer[ FLD_ENC_ENCRYPTED_DATA_LEN_OFFSET]);
if (!ui32EncDataLen)
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
// Adjust for alignment issues of binary fields.
if( getFieldDataType( pFld) == FLM_BINARY_TYPE)
{
// Since the value is binary, need to account for any
// alignment bytes
if( ((pFld->ui32DataOffset + FLM_ENC_FLD_OVERHEAD) & FLM_ALLOC_ALIGN) != 0)
{
uiAlignment = (FLM_ALLOC_ALIGN + 1) -
((pFld->ui32DataOffset + FLM_ENC_FLD_OVERHEAD) & FLM_ALLOC_ALIGN);
}
}
#ifdef FLM_PICKET_FENCE
// Verify the picket fences.
pucDataFence = pucFldBuffer + FLM_ENC_FLD_OVERHEAD + uiAlignment;
pucEncFence = pucDataFence + ui32DataLen + 4;
if ( f_memcmp( pucDataFence, FLD_RAW_FENCE, FLD_PICKET_FENCE_SIZE / 2))
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if ( f_memcmp( pucEncFence, FLD_ENC_FENCE, FLD_PICKET_FENCE_SIZE / 2))
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
#else
F_UNREFERENCED_PARM( pucDataFence);
F_UNREFERENCED_PARM( pucEncFence);
#endif
// Verify the raw data if it is a text type.
if( getFieldDataType( pFld) == FLM_TEXT_TYPE)
{
// Let's try to get this as unicode.
// Allocate a temporary buffer.
uiBufLen = (ui32DataLen * 2) + 2;
if (RC_BAD( rc = f_alloc( uiBufLen, &pUnicode)))
{
goto Exit;
}
if (RC_BAD( rc = getUnicode( (void *)uiSlot,
pUnicode,
&uiBufLen)))
{
if (rc == FERR_ENCRYPTION_UNAVAILABLE)
{
rc = FERR_OK;
}
else
{
goto Exit;
}
}
f_free( &pUnicode);
pUnicode = NULL;
}
else if ( getFieldDataType( pFld) == FLM_NUMBER_TYPE)
{
FLMUINT uiTemp;
if ( RC_BAD( rc = getUINT( (void *)uiSlot, &uiTemp)))
{
if ( rc == FERR_ENCRYPTION_UNAVAILABLE)
{
rc = FERR_OK;
}
else
{
flmAssert(0);
goto Exit;
}
}
}
Exit:
if (pUnicode)
{
f_free( &pUnicode);
}
return( rc);
}
/******************************************************************************
Desc: Swap FIELD_ID structures in the array.
******************************************************************************/
FINLINE void fieldIdSwap(
FIELD_ID * pFieldIdArray,
FLMUINT uiLeft,
FLMUINT uiRight)
{
FIELD_ID tempId;
tempId.ui16FieldId = pFieldIdArray [uiLeft].ui16FieldId;
tempId.ui32FieldOffset = pFieldIdArray [uiLeft].ui32FieldOffset;
pFieldIdArray [uiLeft].ui16FieldId = pFieldIdArray [uiRight].ui16FieldId;
pFieldIdArray [uiLeft].ui32FieldOffset = pFieldIdArray [uiRight].ui32FieldOffset;
pFieldIdArray [uiRight].ui16FieldId = tempId.ui16FieldId;
pFieldIdArray [uiRight].ui32FieldOffset = tempId.ui32FieldOffset;
}
/******************************************************************************
Desc: Quick-sort a field ID table.
******************************************************************************/
FSTATIC void sortFieldIdArray(
FIELD_ID * pFieldIdArray,
FLMUINT uiLowerBounds,
FLMUINT uiUpperBounds)
{
FLMUINT uiLBPos;
FLMUINT uiUBPos;
FLMUINT uiMIDPos;
FLMUINT uiLeftItems;
FLMUINT uiRightItems;
FIELD_ID * pCurEntry;
FLMINT iCompare;
Iterate_Larger_Half:
uiUBPos = uiUpperBounds;
uiLBPos = uiLowerBounds;
uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2;
pCurEntry = &pFieldIdArray[ uiMIDPos ];
for( ;;)
{
while( (uiLBPos == uiMIDPos) // Don't compare with target
|| ((iCompare =
fieldIdCompare( &pFieldIdArray[ uiLBPos], pCurEntry)) < 0))
{
if( uiLBPos >= uiUpperBounds) break;
uiLBPos++;
}
while( (uiUBPos == uiMIDPos) // Don't compare with target
|| (((iCompare =
fieldIdCompare( pCurEntry, &pFieldIdArray[ uiUBPos])) < 0)))
{
if( !uiUBPos) break;
uiUBPos--;
}
if( uiLBPos < uiUBPos ) // Interchange and continue loop.
{
// Interchange [uiLBPos] with [uiUBPos].
fieldIdSwap( pFieldIdArray, uiLBPos, uiUBPos );
uiLBPos++; // Scan from left to right.
uiUBPos--; // Scan from right to left.
}
else // Past each other - done
{
break;
}
}
// Check for swap( LB, MID ) - cases 3 and 4
if( uiLBPos < uiMIDPos )
{
// Interchange [uiLBPos] with [uiMIDPos]
fieldIdSwap( pFieldIdArray, uiMIDPos, uiLBPos );
uiMIDPos = uiLBPos;
}
else if( uiMIDPos < uiUBPos )
{
// Interchange [uUBPos] with [uiMIDPos]
fieldIdSwap( pFieldIdArray, uiMIDPos, uiUBPos );
uiMIDPos = uiUBPos;
}
// Check the left piece.
uiLeftItems = (uiLowerBounds + 1 < uiMIDPos )
? uiMIDPos - uiLowerBounds // 2 or more
: 0;
uiRightItems = (uiMIDPos + 1 < uiUpperBounds )
? uiUpperBounds - uiMIDPos // 2 or more
: 0;
if (uiLeftItems < uiRightItems)
{
// Recurse on the LEFT side and goto the top on the RIGHT side.
if( uiLeftItems )
{
sortFieldIdArray( pFieldIdArray, uiLowerBounds, uiMIDPos - 1 );
}
uiLowerBounds = uiMIDPos + 1;
goto Iterate_Larger_Half;
}
else if (uiLeftItems) // Compute a truth table to figure out this check.
{
// Recurse on the RIGHT side and goto the top for the LEFT side.
if (uiRightItems)
{
sortFieldIdArray( pFieldIdArray, uiMIDPos + 1, uiUpperBounds);
}
uiUpperBounds = uiMIDPos - 1;
goto Iterate_Larger_Half;
}
}
/******************************************************************************
Desc: Sort the field ID table.
******************************************************************************/
void FlmRecord::sortFieldIdTable( void)
{
if (m_pucFieldIdTable)
{
if (getFieldIdTableItemCount( m_pucFieldIdTable) > 1)
{
sortFieldIdArray( getFieldIdTable( m_pucFieldIdTable), 0,
getFieldIdTableItemCount( m_pucFieldIdTable) - 1);
}
}
m_uiFlags &= (~(RCA_NEED_TO_SORT_FIELD_ID_TABLE));
}
/******************************************************************************
Desc: Find a field ID to the field ID table.
******************************************************************************/
FIELD_ID * FlmRecord::findFieldId(
FLMUINT16 ui16FieldId,
FIELDLINK ui32FieldOffset,
FLMUINT * puiInsertPos)
{
FIELD_ID * pFieldId = NULL;
FIELD_ID * pFieldIdTable = getFieldIdTable( m_pucFieldIdTable);
FLMUINT uiTblSize;
FLMUINT uiLow;
FLMUINT uiMid;
FLMUINT uiHigh;
FLMINT iCmp;
if (m_uiFlags & RCA_NEED_TO_SORT_FIELD_ID_TABLE)
{
sortFieldIdTable();
}
// Do binary search in the table
if ((uiTblSize = getFieldIdTableItemCount( m_pucFieldIdTable)) == 0)
{
if (puiInsertPos)
{
*puiInsertPos = 0;
}
goto Exit;
}
uiHigh = --uiTblSize;
uiLow = 0;
for (;;)
{
uiMid = (uiLow + uiHigh) / 2;
if (ui16FieldId < pFieldIdTable [uiMid].ui16FieldId)
{
iCmp = -1;
}
else if (ui16FieldId > pFieldIdTable [uiMid].ui16FieldId)
{
iCmp = 1;
}
else if (!ui32FieldOffset)
{
iCmp = 0;
}
else if (ui32FieldOffset < pFieldIdTable [uiMid].ui32FieldOffset)
{
iCmp = -1;
}
else if (ui32FieldOffset > pFieldIdTable [uiMid].ui32FieldOffset)
{
iCmp = 1;
}
else
{
iCmp = 0;
}
if (iCmp == 0)
{
// Found Match
// If ui32FieldOffset was not set, we need to
// backtrack to find the lowest one that matches
if (!ui32FieldOffset)
{
while (uiMid && pFieldIdTable [uiMid-1].ui16FieldId == ui16FieldId)
{
uiMid--;
}
}
pFieldId = &pFieldIdTable [uiMid];
if (puiInsertPos)
{
*puiInsertPos = uiMid;
}
goto Exit;
}
// Check if we are done
if (uiLow >= uiHigh)
{
// Done, item not found
if (puiInsertPos)
{
*puiInsertPos = (iCmp < 0)
? uiMid
: uiMid + 1;
}
goto Exit;
}
if (iCmp < 0)
{
if (uiMid == 0)
{
if (puiInsertPos)
{
*puiInsertPos = 0;
}
goto Exit;
}
uiHigh = uiMid - 1;
}
else
{
if (uiMid == uiTblSize)
{
if (puiInsertPos)
{
*puiInsertPos = uiMid + 1;
}
goto Exit;
}
uiLow = uiMid + 1;
}
}
Exit:
return( pFieldId);
}
/******************************************************************************
Desc: Add a field ID to the field ID table.
******************************************************************************/
RCODE FlmRecord::addToFieldIdTable(
FLMUINT16 ui16FieldId,
FIELDLINK ui32FieldOffset)
{
RCODE rc = FERR_OK;
FIELD_ID * pFieldId;
FLMUINT uiItemCount = getFieldIdTableItemCount( m_pucFieldIdTable);
FLMUINT uiTableSize = getFieldIdTableArraySize( m_pucFieldIdTable);
FLMBOOL bHeapAlloc;
FlmRecord * pThis = this;
if (uiItemCount == uiTableSize)
{
FLMUINT uiNewByteSize;
uiTableSize += 32;
uiNewByteSize = calcFieldIdTableByteSize( uiTableSize);
// Reallocate the table.
if (!uiItemCount)
{
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->allocBuf(
NULL, uiNewByteSize, &pThis, sizeof( FlmRecord *),
&m_pucFieldIdTable, &bHeapAlloc)))
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, fieldIdTableByteSize(), uiNewByteSize, &pThis,
sizeof( FlmRecord *), &m_pucFieldIdTable, &bHeapAlloc)))
{
goto Exit;
}
}
if( bHeapAlloc)
{
m_uiFlags |= RCA_ID_TABLE_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_ID_TABLE_HEAP_BUFFER;
}
setFieldIdTableArraySize( m_pucFieldIdTable, uiTableSize);
}
pFieldId = getFieldIdTable( m_pucFieldIdTable) + uiItemCount;
pFieldId->ui32FieldOffset = ui32FieldOffset;
pFieldId->ui16FieldId = ui16FieldId;
uiItemCount++;
setFieldIdTableItemCount( m_pucFieldIdTable, uiItemCount);
// Table may no longer be sorted, in which case we need to set a flag
// indicating that it needs to be sorted.
if (uiItemCount > 1 && !(m_uiFlags & RCA_NEED_TO_SORT_FIELD_ID_TABLE) &&
fieldIdCompare( pFieldId - 1, pFieldId) > 0)
{
m_uiFlags |= RCA_NEED_TO_SORT_FIELD_ID_TABLE;
}
Exit:
return( rc);
}
/******************************************************************************
Desc: Remove a field ID from the field ID table.
******************************************************************************/
RCODE FlmRecord::removeFromFieldIdTable(
FLMUINT16 ui16FieldId,
FIELDLINK ui32FieldOffset)
{
RCODE rc = FERR_OK;
FLMUINT uiInsertPos;
FIELD_ID * pFieldId;
if ((pFieldId = findFieldId( ui16FieldId, ui32FieldOffset, &uiInsertPos)) != NULL)
{
FLMUINT uiItemCount = getFieldIdTableItemCount( m_pucFieldIdTable);
FLMUINT uiTableSize = getFieldIdTableArraySize( m_pucFieldIdTable);
FIELD_ID * pFieldIdTable = getFieldIdTable( m_pucFieldIdTable);
if (uiInsertPos < uiItemCount - 1)
{
f_memmove( pFieldId, &pFieldIdTable [uiInsertPos + 1],
sizeof( FIELD_ID) * (uiItemCount - uiInsertPos - 1));
}
uiItemCount--;
if (!uiItemCount)
{
gv_FlmSysData.RCacheMgr.pRecBufAlloc->freeBuf(
fieldIdTableByteSize(), &m_pucFieldIdTable);
}
else
{
FLMBOOL bHeapAlloc;
FlmRecord * pThis = this;
setFieldIdTableItemCount( m_pucFieldIdTable, uiItemCount);
if (uiTableSize > uiItemCount + 32)
{
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, fieldIdTableByteSize(), calcFieldIdTableByteSize( uiItemCount),
&pThis, sizeof( FlmRecord *), &m_pucFieldIdTable, &bHeapAlloc)))
{
goto Exit;
}
setFieldIdTableArraySize( m_pucFieldIdTable, uiItemCount);
if( bHeapAlloc)
{
m_uiFlags |= RCA_ID_TABLE_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_ID_TABLE_HEAP_BUFFER;
}
}
}
}
Exit:
return( rc);
}
/******************************************************************************
Desc: Find a level one field in the record.
******************************************************************************/
void * FlmRecord::findLevelOneField(
FLMUINT uiFieldId,
FLMBOOL bFindInclusive)
{
FLMUINT uiInsertPos;
FIELD_ID * pFieldId;
void * pvField = NULL;
if (m_pucFieldIdTable)
{
if ((pFieldId = findFieldId( (FLMUINT16)uiFieldId, 0, &uiInsertPos)) != NULL)
{
pvField = (void *)((FLMUINT)pFieldId->ui32FieldOffset);
}
else if (bFindInclusive &&
uiInsertPos < getFieldIdTableItemCount( m_pucFieldIdTable))
{
pFieldId = getFieldIdTable( m_pucFieldIdTable) + uiInsertPos;
pvField = (void *)((FLMUINT)pFieldId->ui32FieldOffset);
}
}
else
{
flmAssert( m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED);
}
return( pvField);
}
/******************************************************************************
Desc: Find a level one field in the record.
******************************************************************************/
void * FlmRecord::nextLevelOneField(
void * pvLastLevelOneField)
{
FLMUINT16 ui16FieldId = (FLMUINT16)getFieldID( pvLastLevelOneField);
FIELDLINK ui32FieldOffset = (FIELDLINK)((FLMUINT)pvLastLevelOneField);
FLMUINT uiInsertPos;
FIELD_ID * pFieldId;
void * pvField = NULL;
if (m_pucFieldIdTable)
{
if ((pFieldId = findFieldId( ui16FieldId, ui32FieldOffset,
&uiInsertPos)) != NULL)
{
// See if there is a next field in the array.
if (uiInsertPos + 1 < getFieldIdTableItemCount( m_pucFieldIdTable))
{
// See if the next field in the array has the same field ID as
// the one we're looking for.
pFieldId = getFieldIdTable( m_pucFieldIdTable) + uiInsertPos + 1;
if (pFieldId->ui16FieldId == ui16FieldId)
{
pvField = (void *)((FLMUINT)pFieldId->ui32FieldOffset);
}
}
}
}
else
{
flmAssert( m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED);
}
return( pvField);
}
/******************************************************************************
Desc: Create the field ID table, if not already created.
******************************************************************************/
RCODE FlmRecord::createFieldIdTable(
FLMBOOL bTruncateTable)
{
RCODE rc = FERR_OK;
void * pvField;
if (!(m_uiFlags & RCA_FIELD_ID_TABLE_ENABLED))
{
m_uiFlags |= RCA_FIELD_ID_TABLE_ENABLED;
pvField = firstChild( root());
while (pvField)
{
if (RC_BAD( rc = addToFieldIdTable( (FLMUINT16)getFieldID( pvField),
(FIELDLINK)((FLMUINT)pvField))))
{
goto Exit;
}
pvField = nextSibling( pvField);
}
}
if (m_uiFlags & RCA_NEED_TO_SORT_FIELD_ID_TABLE)
{
sortFieldIdTable();
}
if (bTruncateTable)
{
if (RC_BAD( rc = truncateFieldIdTable()))
{
goto Exit;
}
}
Exit:
return( rc);
}
/******************************************************************************
Desc: Truncate the field ID table, if not already truncated.
******************************************************************************/
RCODE FlmRecord::truncateFieldIdTable( void)
{
RCODE rc = FERR_OK;
FLMUINT uiItemCount = getFieldIdTableItemCount( m_pucFieldIdTable);
if (uiItemCount != getFieldIdTableArraySize( m_pucFieldIdTable))
{
FLMBOOL bHeapAlloc;
FlmRecord * pThis = this;
if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->reallocBuf(
NULL, fieldIdTableByteSize(), calcFieldIdTableByteSize( uiItemCount),
&pThis, sizeof( FlmRecord *), &m_pucFieldIdTable, &bHeapAlloc)))
{
goto Exit;
}
setFieldIdTableArraySize( m_pucFieldIdTable, uiItemCount);
if( bHeapAlloc)
{
m_uiFlags |= RCA_ID_TABLE_HEAP_BUFFER;
}
else
{
m_uiFlags &= ~RCA_ID_TABLE_HEAP_BUFFER;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
class RecCursor : public F_Object
{
public:
RecCursor(
FlmRecord * pRecord,
GRD_CallBackFunction pCallBackFunction,
void * pvCallBackData)
{
m_pRecord = pRecord;
m_pvField = pRecord ? pRecord->root() : NULL;
m_uiRootLevel = pRecord ? pRecord->getLevel( m_pvField) : 0;
m_uiAbsoluteCursorPosition = 1;
m_pCallBack = pCallBackFunction;
m_pvCallBackData = pvCallBackData;
m_bStillAtTheRoot = TRUE;
}
RecCursor(
RecCursor * pCursor)
{
m_pRecord = pCursor->m_pRecord;
m_pvField = pCursor->m_pvField;
m_uiRootLevel = pCursor->m_uiRootLevel;
m_uiAbsoluteCursorPosition = pCursor->m_uiAbsoluteCursorPosition;
m_pCallBack = pCursor->m_pCallBack;
m_pvCallBackData = pCursor->m_pvCallBackData;
m_bStillAtTheRoot = pCursor->m_bStillAtTheRoot;
}
virtual ~RecCursor( void)
{
if (m_pRecord)
{
m_pRecord = NULL;
}
}
FINLINE FLMBOOL EndOfRecord( void)
{
return( m_pvField == NULL
? TRUE
: (m_pRecord->getLevel( m_pvField) <= m_uiRootLevel &&
!m_bStillAtTheRoot)
? TRUE
: FALSE);
}
FINLINE void Advance( void)
{
m_bStillAtTheRoot = FALSE;
if (m_pvField)
{
m_pvField = m_pRecord->next( m_pvField);
m_uiAbsoluteCursorPosition++;
}
}
FLMBOOL FieldValueIsEqualTo(
RecCursor * pSomeField);
FINLINE FLMBOOL FieldIdIsEqualTo(
RecCursor * pSomeField)
{
return( Level() == pSomeField->Level() &&
m_pRecord->getFieldID( m_pvField) ==
pSomeField->m_pRecord->getFieldID( pSomeField->m_pvField) &&
m_pRecord->getDataType( m_pvField) ==
pSomeField->m_pRecord->getDataType( pSomeField->m_pvField)
? TRUE
: FALSE);
}
enum RecFieldMatchTypes
{
GRD_NoMatch,
GRD_ExactMatch,
GRD_IDMatch
};
void * Scan(
RecCursor * pTargetCursor,
RecFieldMatchTypes * peMatchType);
FINLINE FLMUINT AbsolutePosition( void)
{
return (m_uiAbsoluteCursorPosition);
}
FINLINE void * Field( void)
{
return ( m_pvField);
}
FINLINE FlmRecord * Record( void)
{
return (m_pRecord);
}
FINLINE FLMUINT Level( void)
{
return (m_pvField ? Normalize( m_pRecord->getLevel( m_pvField)) : 0);
}
FINLINE FLMUINT RawLevel( void)
{
return( m_pvField ? m_pRecord->getLevel( m_pvField) : 0);
}
FINLINE void CallBack(
GRD_DifferenceData & difference)
{
(*m_pCallBack) (difference, m_pvCallBackData);
}
static void MarkBranchDeleted(
RecCursor * pBeforeCursor,
RecCursor * pAfterCursor);
static void MarkModified(
RecCursor * pBeforeCursor,
RecCursor * pAfterCursor);
static void MarkInserted(
RecCursor * pCursor);
static void MarkRangeInserted(
RecCursor * pAfterCursor,
void * pEndOfRange);
private:
FLMUINT m_uiAbsoluteCursorPosition;
FlmRecord * m_pRecord;
void * m_pvField;
FLMUINT m_uiRootLevel;
GRD_CallBackFunction m_pCallBack;
void * m_pvCallBackData;
FLMBOOL m_bStillAtTheRoot;
RecCursor( void)
{
}
FINLINE FLMUINT Normalize(
FLMUINT uiLevel)
{
return( uiLevel - m_uiRootLevel);
}
FINLINE FLMBOOL isLeafField(void)
{
void * pvNext = m_pRecord->next( m_pvField);
// It is valid to compare raw node levels of nodes within the same
// record
return( (pvNext &&
m_pRecord->getLevel( pvNext) > m_pRecord->getLevel( m_pvField))
? FALSE
: TRUE);
}
};
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL RecCursor::FieldValueIsEqualTo(
RecCursor* pSomeField)
{
FLMBOOL bEqual = FALSE;
FLMUINT uiFieldLen = m_pRecord->getDataLength( m_pvField);
FLMUINT uiSomeLen = pSomeField->m_pRecord->getDataLength( pSomeField->m_pvField);
FLMUINT uiEncFieldLen = 0;
FLMUINT uiEncSomeLen = 0;
const FLMBYTE* pValue1;
const FLMBYTE* pValue2;
// If the data lengths are not equal, we can exit.
if (uiFieldLen != uiSomeLen)
{
goto Exit;
}
// If one field is encrypted and the other is not, then we can exit.
if ((m_pRecord->isEncryptedField( m_pvField) &&
!pSomeField->m_pRecord->isEncryptedField( pSomeField->m_pvField)) ||
(!m_pRecord->isEncryptedField( m_pvField) &&
pSomeField->m_pRecord->isEncryptedField( pSomeField->m_pvField)))
{
goto Exit;
}
// If the fields are encrypted, are they using the same encryption
// scheme?
if (m_pRecord->isEncryptedField( m_pvField))
{
if (m_pRecord->getEncryptionID( m_pvField) !=
pSomeField->m_pRecord->getEncryptionID( pSomeField->m_pvField))
{
goto Exit;
}
}
// If the field is not encrypted, and we have a value length
if (uiFieldLen && !m_pRecord->isEncryptedField( m_pvField))
{
pValue1 = m_pRecord->getDataPtr( m_pvField);
pValue2 = pSomeField->m_pRecord->getDataPtr( pSomeField->m_pvField);
// If the values are not equal, we can exit.
if (f_memcmp( pValue1, pValue2, uiFieldLen) != 0)
{
goto Exit;
}
}
// Otherwise, if the field is encrypted, we need to check the
// encrypted value.
else if (m_pRecord->isEncryptedField( m_pvField))
{
uiEncFieldLen = m_pRecord->getEncryptedDataLength( m_pvField);
uiEncSomeLen = pSomeField->m_pRecord->getEncryptedDataLength( pSomeField->m_pvField);
// If the encrypted lengths are not equal, we can exit.
if (uiEncFieldLen != uiEncSomeLen)
{
goto Exit;
}
if (uiEncFieldLen)
{
pValue1 = m_pRecord->getEncryptionDataPtr( m_pvField);
pValue2 = pSomeField->m_pRecord->getEncryptionDataPtr( pSomeField->m_pvField);
// If the encrypted values are not equal, we can exit.
if (f_memcmp( pValue1, pValue2, uiFieldLen) != 0)
{
goto Exit;
}
}
}
// If we get this far, the fields are identical.
bEqual = TRUE;
Exit:
return (bEqual);
}
/****************************************************************************
Desc:
****************************************************************************/
void * RecCursor::Scan(
RecCursor * pTargetCursor,
RecFieldMatchTypes * peMatchType)
{
void * pvIDMatch = NULL;
FLMUINT uiTargetLevel = pTargetCursor->Level();
FLMBOOL bAdvanced = FALSE;
*peMatchType = GRD_NoMatch;
for (RecCursor candidate = this;
candidate.Level() >= uiTargetLevel && !candidate.EndOfRecord();
candidate.Advance(), bAdvanced = TRUE)
{
if (pTargetCursor->FieldIdIsEqualTo( &candidate))
{
if (pTargetCursor->FieldValueIsEqualTo( &candidate))
{
*peMatchType = GRD_ExactMatch;
return (candidate.Field());
}
else if (*peMatchType == GRD_NoMatch)
{
if (!bAdvanced && isLeafField())
{
// Only allow ID matches on leaf fields, when cursor hasn't
// advanced
*peMatchType = GRD_IDMatch;
pvIDMatch = candidate.Field();
}
}
}
}
return (pvIDMatch);
}
/****************************************************************************
Desc:
****************************************************************************/
void RecCursor::MarkBranchDeleted(
RecCursor * pBeforeCursor,
RecCursor * pAfterCursor)
{
GRD_DifferenceData difference;
FLMUINT uiStartLevel = pBeforeCursor->RawLevel();
difference.type = GRD_DeletedSubtree;
difference.uiAbsolutePosition = pAfterCursor->AbsolutePosition();
difference.pBeforeRecord = pBeforeCursor->Record();
difference.pvBeforeField = pBeforeCursor->Field();
difference.pAfterRecord = NULL;
difference.pvAfterField = NULL;
pBeforeCursor->CallBack( difference);
difference.type = GRD_Deleted;
do
{
pBeforeCursor->CallBack( difference);
pBeforeCursor->Advance();
} while( !pBeforeCursor->EndOfRecord() &&
pBeforeCursor->RawLevel() > uiStartLevel);
}
/****************************************************************************
Desc:
****************************************************************************/
void RecCursor::MarkModified(
RecCursor * pBeforeCursor,
RecCursor * pAfterCursor)
{
GRD_DifferenceData difference;
difference.type = GRD_Modified;
difference.uiAbsolutePosition = pAfterCursor->AbsolutePosition();
difference.pBeforeRecord = pBeforeCursor->Record();
difference.pvBeforeField = pBeforeCursor->Field();
difference.pAfterRecord = pAfterCursor->Record();
difference.pvAfterField = pAfterCursor->Field();
pBeforeCursor->CallBack( difference);
}
/****************************************************************************
Desc:
****************************************************************************/
void RecCursor::MarkInserted(
RecCursor * pAfterCursor)
{
GRD_DifferenceData difference;
difference.type = GRD_Inserted;
difference.uiAbsolutePosition = pAfterCursor->AbsolutePosition();
difference.pBeforeRecord = NULL;
difference.pvBeforeField = NULL;
difference.pAfterRecord = pAfterCursor->Record();
difference.pvAfterField = pAfterCursor->Field();
pAfterCursor->CallBack( difference);
}
/****************************************************************************
Desc:
****************************************************************************/
void RecCursor::MarkRangeInserted(
RecCursor * pAfterCursor,
void * pEndOfRange)
{
void * pvField;
for (pvField = pAfterCursor->Field();
pvField != pEndOfRange;
pvField = pAfterCursor->Field())
{
// Note that MarkInserted will advance the field pointer
RecCursor::MarkInserted( pAfterCursor);
pAfterCursor->Advance();
}
}
/****************************************************************************
Desc:
****************************************************************************/
void flmRecordDifference(
FlmRecord * pBefore,
FlmRecord * pAfter,
GRD_CallBackFunction pCallBackFunction,
void * pvCallBackData)
{
RecCursor beforeCursor( pBefore, pCallBackFunction, pvCallBackData);
RecCursor afterCursor( pAfter, pCallBackFunction, pvCallBackData);
// Iterate through all of the fields in the 'before record'
while (!beforeCursor.EndOfRecord())
{
void * pvFound;
RecCursor::RecFieldMatchTypes eMatchType;
if (afterCursor.EndOfRecord())
{
RecCursor::MarkBranchDeleted( &beforeCursor, &afterCursor);
continue;
}
pvFound = afterCursor.Scan( &beforeCursor, &eMatchType);
if (pvFound)
{
// 'before field' found in 'after record' Mark all intervening
// 'after fields' as inserted
RecCursor::MarkRangeInserted( &afterCursor, pvFound);
if (eMatchType == RecCursor::GRD_IDMatch)
{
// 'before field' was modified in 'after record'
RecCursor::MarkModified( &beforeCursor, &afterCursor);
}
afterCursor.Advance();
beforeCursor.Advance();
}
else
{
// 'before field' has been deleted from 'after record'
RecCursor::MarkBranchDeleted( &beforeCursor, &afterCursor);
}
}
// The end of the 'before record' has been reached, all remaining
// 'after fields' must have been inserted
RecCursor::MarkRangeInserted( &afterCursor, NULL);
}
/****************************************************************************
Desc: This routine adds a field to a record.
****************************************************************************/
RCODE flmAddField(
FlmRecord * pRecord,
FLMUINT uiTagNum,
const void * pvData,
FLMUINT uiDataLen,
FLMUINT uiDataType)
{
RCODE rc = FERR_OK;
void * pvField;
// Insert new field.
if( RC_BAD( rc = pRecord->insertLast( 1, uiTagNum, uiDataType, &pvField)))
{
goto Exit;
}
switch( uiDataType)
{
case FLM_TEXT_TYPE:
{
rc = pRecord->setNative( pvField, (const char *)pvData);
break;
}
case FLM_NUMBER_TYPE:
{
FLMUINT uiNum;
switch (uiDataLen)
{
case 0:
uiNum = (FLMUINT)(*((FLMUINT *)(pvData)));
break;
case 1:
uiNum = (FLMUINT)(*((FLMBYTE *)(pvData)));
break;
case 2:
uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData)));
break;
case 4:
uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData)));
break;
default:
flmAssert( 0);
rc = RC_SET( FERR_INVALID_PARM);
goto Exit;
}
rc = pRecord->setUINT( pvField, uiNum);
break;
}
case FLM_BINARY_TYPE:
{
rc = pRecord->setBinary( pvField, pvData, uiDataLen);
break;
}
default :
{
flmAssert( 0);
break;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine modifies the first matching field in a record.
If the field is not found, a new field will be created.
****************************************************************************/
RCODE flmModField(
FlmRecord * pRecord,
FLMUINT uiTagNum,
const void * pvData,
FLMUINT uiDataLen,
FLMUINT uiDataType)
{
RCODE rc = FERR_OK;
void * pvField;
if( (pvField = pRecord->find( pRecord->root(), uiTagNum)) == NULL)
{
// Create the field.
if( RC_BAD( rc = pRecord->insertLast( 1, uiTagNum, uiDataType, &pvField)))
{
goto Exit;
}
}
switch( uiDataType)
{
case FLM_TEXT_TYPE:
{
rc = pRecord->setNative( pvField, (const char *)pvData);
break;
}
case FLM_NUMBER_TYPE:
{
FLMUINT uiNum;
switch (uiDataLen)
{
case 0:
uiNum = (FLMUINT)(*((FLMUINT *)(pvData)));
case 1:
uiNum = (FLMUINT)(*((FLMBYTE *)(pvData)));
break;
case 2:
uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData)));
break;
case 4:
uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData)));
break;
default:
flmAssert( 0);
rc = RC_SET( FERR_INVALID_PARM);
goto Exit;
}
rc = pRecord->setUINT( pvField, uiNum);
break;
}
case FLM_BINARY_TYPE:
{
rc = pRecord->setBinary( pvField, pvData, uiDataLen);
break;
}
default :
{
flmAssert( 0);
break;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine searches for a specific numeric field and deletes
that field from the record.
****************************************************************************/
RCODE flmDelField(
FlmRecord * pRecord,
FLMUINT uiTagNum,
FLMUINT uiValue)
{
RCODE rc = FERR_OK;
FLMUINT uiNum;
void * pvField;
if( (pvField = pRecord->find( pRecord->root(), uiTagNum, 1)) != NULL)
{
for(;;)
{
if( pRecord->getFieldID( pvField) == uiTagNum)
{
if( RC_BAD( rc = pRecord->getUINT( pvField, &uiNum)))
{
goto Exit;
}
if( uiNum == uiValue)
{
pRecord->remove( pvField);
break;
}
}
pvField = pRecord->nextSibling( pvField);
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine finds a field in a record and increments its value.
The value of 1 will be assigned if the field is not present.
****************************************************************************/
RCODE flmIncrField(
FlmRecord * pRecord,
FLMUINT uiTagNum)
{
RCODE rc = FERR_OK;
void * pvField;
if( (pvField = pRecord->find( pRecord->root(), uiTagNum, 1)) != NULL)
{
FLMUINT uiNum;
if( RC_OK( rc = pRecord->getUINT( pvField, &uiNum)))
{
uiNum++;
rc = pRecord->setUINT( pvField, uiNum);
}
}
else
{
// Create the field and set the value to one.
if( RC_OK( rc = pRecord->insertLast( 1, uiTagNum,
FLM_NUMBER_TYPE, &pvField)))
{
rc = pRecord->setUINT( pvField, 1);
}
}
return( rc);
}
/****************************************************************************
Desc: This routine finds a field in a record and decrements its value.
****************************************************************************/
RCODE flmDecrField(
FlmRecord * pRecord,
FLMUINT uiTagNum)
{
RCODE rc = FERR_OK;
void * pvField;
if( (pvField = pRecord->find( pRecord->root(), uiTagNum, 1)) != NULL)
{
FLMUINT uiNum;
if( RC_OK( rc = pRecord->getUINT( pvField, &uiNum)))
{
uiNum--;
rc = pRecord->setUINT( pvField, uiNum);
}
}
return( rc);
}