📄 tabindnode.cpp
字号:
// tabindnode.cpp: implementation of the TABINDNode class.////////////////////////////////////////////////////////////////////////#include "tabindnode.h"#include "ugk_errhandle.h"/********************************************************************** * TABINDNode::TABINDNode() * * Constructor. **********************************************************************/TABINDNode::TABINDNode(TABAccess eAccessMode /*=TABRead*/){ m_fp = NULL; m_poCurChildNode = NULL; m_nSubTreeDepth = 0; m_nKeyLength = 0; m_eFieldType = TABFUnknown; m_poDataBlock = NULL; m_numEntriesInNode = 0; m_nCurIndexEntry = 0; m_nPrevNodePtr = 0; m_nNextNodePtr = 0; m_poBlockManagerRef = NULL; m_poParentNodeRef = NULL; m_bUnique = FALSE; m_eAccessMode = eAccessMode;}/********************************************************************** * TABINDNode::~TABINDNode() * * Destructor. **********************************************************************/TABINDNode::~TABINDNode(){ if (m_poCurChildNode) delete m_poCurChildNode; if (m_poDataBlock) delete m_poDataBlock;}/********************************************************************** * TABINDNode::InitNode() * * Init a node... this function can be used either to initialize a new * node, or to make it point to a new data block in the file. * * By default, this call will read the data from the file at the * specified location if necessary, and leave the object ready to be searched. * * In write access, if the block does not exist (i.e. nBlockPtr=0) then a * new one is created and initialized. * * poParentNode is used in write access in order to update the parent node * when this node becomes full and has to be split. * * Returns 0 on success, -1 on error. **********************************************************************/int TABINDNode::InitNode(FILE *fp, int nBlockPtr, int nKeyLength, int nSubTreeDepth, UGKBool bUnique, TABBinBlockManager *poBlockMgr /*=NULL*/, TABINDNode *poParentNode /*=NULL*/, int nPrevNodePtr /*=0*/, int nNextNodePtr /*=0*/){ /*----------------------------------------------------------------- * If the block already points to the right block, then don't do * anything here. *----------------------------------------------------------------*/ if (m_fp == fp && nBlockPtr> 0 && m_nCurDataBlockPtr == nBlockPtr) return 0; // Keep track of some info m_fp = fp; m_nKeyLength = nKeyLength; m_nSubTreeDepth = nSubTreeDepth; m_nCurDataBlockPtr = nBlockPtr; m_bUnique = bUnique; // Do not overwrite the following values if we receive NULL (the defaults) if (poBlockMgr) m_poBlockManagerRef = poBlockMgr; if (poParentNode) m_poParentNodeRef = poParentNode; // Set some defaults m_numEntriesInNode = 0; m_nPrevNodePtr = nPrevNodePtr; m_nNextNodePtr = nNextNodePtr; m_nCurIndexEntry = 0; /*----------------------------------------------------------------- * Init RawBinBlock * The node's buffer has to be created with read/write access since * the index is a very dynamic structure! *----------------------------------------------------------------*/ if (m_poDataBlock == NULL) m_poDataBlock = new TABRawBinBlock(TABReadWrite, TRUE); if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) && nBlockPtr == 0 && m_poBlockManagerRef) { /*------------------------------------------------------------- * Write access: Create and init a new block *------------------------------------------------------------*/ m_nCurDataBlockPtr = m_poBlockManagerRef->AllocNewBlock(); m_poDataBlock->InitNewBlock(m_fp, 512, m_nCurDataBlockPtr); m_poDataBlock->WriteInt32( m_numEntriesInNode ); m_poDataBlock->WriteInt32( m_nPrevNodePtr ); m_poDataBlock->WriteInt32( m_nNextNodePtr ); } else { assert(m_nCurDataBlockPtr > 0); /*------------------------------------------------------------- * Read the data block from the file, applies to read access, or * to write access (to modify an existing block) *------------------------------------------------------------*/ if (m_poDataBlock->ReadFromFile(m_fp, m_nCurDataBlockPtr, 512) != 0) { // CPLError() has already been called. return -1; } m_poDataBlock->GotoByteInBlock(0); m_numEntriesInNode = m_poDataBlock->ReadInt32(); m_nPrevNodePtr = m_poDataBlock->ReadInt32(); m_nNextNodePtr = m_poDataBlock->ReadInt32(); } // m_poDataBlock is now positioned at the beginning of the key entries return 0;}/********************************************************************** * TABINDNode::GotoNodePtr() * * Move to the specified node ptr, and read the new node data from the file. * * This is just a cover funtion on top of InitNode() **********************************************************************/int TABINDNode::GotoNodePtr(UGKInt32 nNewNodePtr){ // First flush current changes if any. if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) && m_poDataBlock && m_poDataBlock->CommitToFile() != 0) return -1; assert(nNewNodePtr % 512 == 0); // Then move to the requested location. return InitNode(m_fp, nNewNodePtr, m_nKeyLength, m_nSubTreeDepth, m_bUnique);}/********************************************************************** * TABINDNode::ReadIndexEntry() * * Read the key value and record/node ptr for the specified index entry * inside the current node data. * * nEntryNo is the 0-based index of the index entry that we are interested * in inside the current node. * * Returns the record/node ptr, and copies the key value inside the * buffer pointed to by *pKeyValue... this assumes that *pKeyValue points * to a buffer big enough to hold the key value (m_nKeyLength bytes). * If pKeyValue == NULL, then this parameter is ignored and the key value * is not copied. **********************************************************************/UGKInt32 TABINDNode::ReadIndexEntry(int nEntryNo, UGKByte *pKeyValue){ UGKInt32 nRecordPtr = 0; if (nEntryNo >= 0 && nEntryNo < m_numEntriesInNode) { if (pKeyValue) { m_poDataBlock->GotoByteInBlock(12 + nEntryNo*(m_nKeyLength+4)); m_poDataBlock->ReadBytes(m_nKeyLength, pKeyValue); } else { m_poDataBlock->GotoByteInBlock(12 + nEntryNo*(m_nKeyLength+4)+ m_nKeyLength); } nRecordPtr = m_poDataBlock->ReadInt32(); } return nRecordPtr;}/********************************************************************** * TABINDNode::IndexKeyCmp() * * Compare the specified index entry with the key value, and * return 0 if equal, an integer less than 0 if key is smaller than * index entry, and an integer greater than 0 if key is bigger than * index entry. * * nEntryNo is the 0-based index of the index entry that we are interested * in inside the current node. **********************************************************************/int TABINDNode::IndexKeyCmp(UGKByte *pKeyValue, int nEntryNo){ assert(pKeyValue); assert(nEntryNo >= 0 && nEntryNo < m_numEntriesInNode); m_poDataBlock->GotoByteInBlock(12 + nEntryNo*(m_nKeyLength+4)); return memcmp(pKeyValue, m_poDataBlock->GetCurDataPtr(), m_nKeyLength);}/********************************************************************** * TABINDNode::SetFieldType() * * Sets the field type for the current index and recursively set all * children as well. * This information will then be used in building the key values, etc. * * Returns 0 on success, -1 on error. **********************************************************************/int TABINDNode::SetFieldType(TABFieldType eType){ if (m_fp == NULL) { UGKError(ET_Failure, UGKErr_AssertionFailed, "TABINDNode::SetFieldType(): File has not been opened yet!"); return -1; } /*----------------------------------------------------------------- * Validate field type with key length *----------------------------------------------------------------*/ if ((eType == TABFInteger && m_nKeyLength != 4) || (eType == TABFSmallInt && m_nKeyLength != 2) || (eType == TABFFloat && m_nKeyLength != 8) || (eType == TABFDecimal && m_nKeyLength != 8) || (eType == TABFDate && m_nKeyLength != 4) || (eType == TABFLogical && m_nKeyLength != 4) ) { UGKError(ET_Failure, UGKErr_IllegalArg, "Index key length (%d) does not match field type (%s).", m_nKeyLength, TABFIELDTYPE_2_STRING(eType) ); return -1; } m_eFieldType = eType; /*----------------------------------------------------------------- * Pass the field type info to child nodes *----------------------------------------------------------------*/ if (m_poCurChildNode) return m_poCurChildNode->SetFieldType(eType); return 0;}/********************************************************************** * TABINDNode::FindFirst() * * Start a new search in this node and its children for a key value. * If the index is not unique, then FindNext() can be used to return * the other values that correspond to the key. * * Return value: * - the key's corresponding record number in the .DAT file (greater than 0) * - 0 if the key was not found * - or -1 if an error happened **********************************************************************/UGKInt32 TABINDNode::FindFirst(UGKByte *pKeyValue){ if (m_poDataBlock == NULL) { UGKError(ET_Failure, UGKErr_AssertionFailed, "TABINDNode::Search(): Node has not been initialized yet!"); return -1; } /*----------------------------------------------------------------- * Unless something has been broken, this method will be called by our * parent node after it has established that we are the best candidate * to contain the first instance of the key value. So there is no * need to look in the previous or next nodes in the chain... if the * value is not found in the current node block then it is not present * in the index at all. * * m_nCurIndexEntry will be used to keep track of the search pointer * when FindNext() will be used. *----------------------------------------------------------------*/ m_nCurIndexEntry = 0; if (m_nSubTreeDepth == 1) { /*------------------------------------------------------------- * Leaf node level... we look for an exact match *------------------------------------------------------------*/ while(m_nCurIndexEntry < m_numEntriesInNode) { int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry); if (nCmpStatus > 0) { /* Not there yet... (pKey > IndexEntry) */ m_nCurIndexEntry++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -