/* xbndx.cpp Xbase64 project source code NDX indexing routines for X-Base Copyright (C) 1997,2003,2004 Gary A Kunkel This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Email: xdb-devel@lists.sourceforge.net xdb-users@lists.sourceforge.net Regular Mail: XBase Support 149C South Main St Keller Texas, 76248 USA */ #ifdef __GNU LesserG__ #pragma implementation "xbndx.h" #endif #ifdef __WIN32__ #include #else #include #endif #include #include #ifdef XB_INDEX_NDX #ifdef HAVE_IO_H #include #endif #include #include #include //#include /*! \file xbndx.cpp */ /***********************************************************************/ //! Short description /*! */ /* This routine dumps the node chain to stdout */ #ifdef XBASE_DEBUG void xbNdx::DumpNodeChain() { xbNdxNodeLink *n; std::cout << std::endl << "*************************" << std::endl; std::cout << "xbNodeLinkCtr = " << xbNodeLinkCtr << std::endl; std::cout << "Reused = " << ReusedxbNodeLinks << std::endl; n = NodeChain; while(n){ std::cout << "xbNodeLink Chain ->" << n->NodeNo << std::endl; std::cout << " CurKeyNo ->" << n->CurKeyNo << std::endl; n = n->NextNode; } n = FreeNodeChain; while(n){ std::cout << "FreexbNodeLink Chain " << n->NodeNo << std::endl; n = n->NextNode; } n = DeleteChain; while(n){ std::cout << "DeleteLink Chain " << n->NodeNo << std::endl; n = n->NextNode; } } #endif /***********************************************************************/ //! Short description /*! \param n */ /* This routine returns a chain of one or more index nodes back to the */ /* free node chain */ void xbNdx::ReleaseNodeMemory(xbNdxNodeLink *n, xbBool doFree) { xbNdxNodeLink *temp; if(doFree){ while(n){ temp = n->NextNode; free(n); n = temp; } } else { if( !FreeNodeChain ) FreeNodeChain = n; else { /* put this list at the end */ temp = FreeNodeChain; while( temp->NextNode ) temp = temp->NextNode; temp->NextNode = n; } } } /***********************************************************************/ //! Short description /*! */ /* This routine returns a node from the free chain if available, */ /* otherwise it allocates new memory for the requested node */ xbNdxNodeLink * xbNdx::GetNodeMemory() { xbNdxNodeLink * temp; if( FreeNodeChain ){ temp = FreeNodeChain; FreeNodeChain = temp->NextNode; ReusedxbNodeLinks++; } else { temp = (xbNdxNodeLink *) malloc( sizeof( xbNdxNodeLink )); xbNodeLinkCtr++; } memset( temp, 0x00, sizeof( xbNdxNodeLink )); return temp; } /***********************************************************************/ //! Short description /*! */ #ifdef XBASE_DEBUG void xbNdx::DumpHdrNode( xbShort opt ) { if( opt ){ FILE * log; if(( log = fopen( "xbase64.log", "a+t" )) == NULL ) return; fprintf( log, "Index Header Node for %s\n", GetFileName().getData()); fprintf( log, "--------------------------------\n" ); fprintf( log, "Start node = %ld\n", HeadNode.StartNode ); fprintf( log, "Total nodes = %ld\n", HeadNode.TotalNodes ); fprintf( log, "No of keys = %ld\n", HeadNode.NoOfKeys ); fprintf( log, "Key Length = %d\n", HeadNode.KeyLen ); fprintf( log, "Keys Per Node = %d\n", HeadNode.KeysPerNode ); fprintf( log, "Key type = %d\n", HeadNode.KeyType ); fprintf( log, "Key size = %ld\n", HeadNode.KeySize ); fprintf( log, "Unknown 2 = %d\n", HeadNode.Unknown2 ); fprintf( log, "Unique = %d\n", HeadNode.Unique ); fprintf( log, "KeyExpression = %s\n", HeadNode.KeyExpression ); fclose( log ); } else { std::cout << "Start node = " << HeadNode.StartNode << std::endl; std::cout << "Total nodes = " << HeadNode.TotalNodes << std::endl; std::cout << "No of keys = " << HeadNode.NoOfKeys << std::endl; std::cout << "Key Length = " << HeadNode.KeyLen << std::endl; std::cout << "Keys Per Node = " << HeadNode.KeysPerNode << std::endl; std::cout << "Key type = " << HeadNode.KeyType << std::endl; std::cout << "Key size = " << HeadNode.KeySize << std::endl; std::cout << "Unknown 2 = " << HeadNode.Unknown2 << std::endl; std::cout << "Unique = " << HeadNode.Unique << std::endl; std::cout << "KeyExpression = " << HeadNode.KeyExpression << std::endl; #ifdef XB_VAR_NODESIZE std::cout << "NodeSize = " << NodeSize << std::endl; #endif // XB_VAR_NODESIZE std::cout << std::endl; } } #endif /***********************************************************************/ //! Constructor /*! \param pdbf */ xbNdx::xbNdx() : xbIndex() { } /***********************************************************************/ //! Constructor /*! \param pdbf */ xbNdx::xbNdx(xbDbf *pdbf) : xbIndex(pdbf) { #ifndef XB_VAR_NODESIZE memset( Node, 0x00, XB_NDX_NODE_SIZE ); #else memset( Node, 0x00, XB_MAX_NDX_NODE_SIZE ); #endif memset( &HeadNode, 0x00, sizeof( xbNdxHeadNode )); NodeChain = NULL; FreeNodeChain = NULL; DeleteChain = NULL; CurNode = NULL; xbNodeLinkCtr = 0L; ReusedxbNodeLinks = 0L; #ifndef XB_VAR_NODESIZE NodeSize = XB_NDX_NODE_SIZE; #else NodeSize = XB_DEFAULT_NDX_NODE_SIZE; #endif // XB_VAR_NODESIZE } /***********************************************************************/ //! Destructor /*! */ xbNdx::~xbNdx() { CloseIndex(); } /***********************************************************************/ //! Short description /*! */ xbShort xbNdx::GetHeadNode( void ) { char *p, *q; xbShort i; if( !IsOpen() ) return XB_NOT_OPEN; if( _fseek( indexfp, 0, SEEK_SET )) return XB_SEEK_ERROR; if(( fread( Node, XB_NDX_NODE_SIZE, 1, indexfp )) != 1 ) return XB_READ_ERROR; /* load the head node structure */ p = Node; HeadNode.StartNode = dbf->xbase->GetLong ( p ); p+=4; HeadNode.TotalNodes = dbf->xbase->GetLong ( p ); p+=4; HeadNode.NoOfKeys = dbf->xbase->GetLong ( p ); p+=4; HeadNode.KeyLen = dbf->xbase->GetShort( p ); p+=2; HeadNode.KeysPerNode = dbf->xbase->GetShort( p ); p+=2; HeadNode.KeyType = dbf->xbase->GetShort( p ); p+=2; HeadNode.KeySize = dbf->xbase->GetLong ( p ); p+=4; HeadNode.Unknown2 = *p++; HeadNode.Unique = *p++; #ifdef XB_VAR_NODESIZE // // Automagically determine the node size. Note the (2 * sizeof(xbLong)) // is taken directly from CreateIndex(). I don't understand it exactly, // but this is the value used to calculate the number of keys per node. // DTB. // NodeSize = (2 * sizeof(xbLong)) + HeadNode.KeySize * HeadNode.KeysPerNode; if(NodeSize % XB_NDX_NODE_MULTIPLE) NodeSize = ((NodeSize + XB_NDX_NODE_MULTIPLE) / XB_NDX_NODE_MULTIPLE) * XB_NDX_NODE_MULTIPLE; #endif q = HeadNode.KeyExpression; for( i = XB_NDX_NODE_BASESIZE; i < XB_NDX_NODE_SIZE && *p; i++ ) *q++ = *p++; return 0; } /***********************************************************************/ //! Short description /*! \param NodeNo \param SetNodeChain */ /* This routine reads a leaf node from disk */ /* */ /* If SetNodeChain 2, then the node is not appended to the node chain */ /* but the CurNode pointer points to the node read */ /* If SetNodeChain 1, then the node is appended to the node chain */ /* If SetNodeChain 0, then record is only read to Node memory */ xbShort xbNdx::GetLeafNode( xbLong NodeNo, xbShort SetNodeChain ) { xbNdxNodeLink *n; if( !IsOpen() ) return XB_NOT_OPEN; if( _fseek( indexfp, (xbOffT)NodeNo * XB_NDX_NODE_SIZE, SEEK_SET )) return XB_SEEK_ERROR; if(( fread( Node, XB_NDX_NODE_SIZE, 1, indexfp )) != 1 ) return XB_READ_ERROR; if( !SetNodeChain ) return 0; if(( n = GetNodeMemory()) == NULL ) return XB_NO_MEMORY; n->NodeNo = NodeNo; n->CurKeyNo = 0L; n->NextNode = NULL; n->Leaf.NoOfKeysThisNode = dbf->xbase->GetLong( Node ); memcpy( n->Leaf.KeyRecs, Node+4, XB_NDX_NODE_SIZE - 4 ); /* put the node in the chain */ if( SetNodeChain == 1 ){ if( NodeChain == NULL ){ /* first one ? */ NodeChain = n; CurNode = n; CurNode->PrevNode = NULL; } else { n->PrevNode = CurNode; CurNode->NextNode = n; CurNode = n; } } else CurNode = n; return 0; } /***********************************************************************/ //! Short description /*! \param n */ #ifdef XBASE_DEBUG void xbNdx::DumpNodeRec( xbLong n ) { char *p; xbLong NoOfKeys, LeftBranch, RecNo, NodeType; xbShort i,j; FILE * log; if(( log = fopen( "xbase64.log", "a+t" )) == NULL ) return; GetLeafNode( n, 0 ); NoOfKeys = dbf->xbase->GetLong( Node ); p = Node + 4; /* go past no of keys */ fprintf( log, "----------------------------------------------------\n" ); fprintf( log, "Node # %ld\n", n ); fprintf( log, "Number of keys = %ld\n", NoOfKeys ); fprintf( log, " Key Left Dbf Rec Key\n" ); fprintf( log, "Number Branch Number Data\n" ); NodeType = 0; for( i = 0; i < (NoOfKeys+NodeType); i++ ){ LeftBranch = dbf->xbase->GetLong( p ); if( i == 0 && LeftBranch ){ NodeType = 1; /* print one extra entry for interior nodes */ fprintf( log, "Interior node\n" ); } p+=4; RecNo = dbf->xbase->GetLong( p ); p+=4; fprintf( log, " %3d %9ld %9ld ", i, LeftBranch, RecNo ); if( NodeType == 1 && i == NoOfKeys ) fprintf( log, "...\n" ); else if( !HeadNode.KeyType ){ for( j = 0; j < HeadNode.KeyLen; j++ ) fputc( *p++, log ); fputc( '\n', log ); } else { fprintf( log, "??????\n" /*, dbf->xbase->GetDouble( p )*/ ); p += 8; } } fclose( log ); } #endif /***********************************************************************/ #ifndef XB_INLINE_GETDBFNO xbLong xbNdx::GetDbfNo( xbShort RecNo, xbNdxNodeLink * n ) { xbNdxLeafNode *temp; char *p; if( !n ) return 0L; temp = &n->Leaf; if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L; p = temp->KeyRecs + 4; p += RecNo * ( 8 + HeadNode.KeyLen ); return( dbf->xbase->GetLong( p )); } #endif /***********************************************************************/ //! Short description /*! \param RecNo \param n */ xbLong xbNdx::GetLeftNodeNo( xbShort RecNo, xbNdxNodeLink * n ) { xbNdxLeafNode *temp; char *p; if( !n ) return 0L; temp = &n->Leaf; if( RecNo < 0 || RecNo > temp->NoOfKeysThisNode ) return 0L; p = temp->KeyRecs; p += RecNo * ( 8 + HeadNode.KeyLen ); return( dbf->xbase->GetLong( p )); } /***********************************************************************/ //! Short description /*! \param RecNo \param n */ char * xbNdx::GetKeyData( xbShort RecNo, xbNdxNodeLink * n ) { xbNdxLeafNode *temp; char *p; if( !n ) return 0L; temp = &n->Leaf; if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L; p = temp->KeyRecs + 8; p += RecNo * ( 8 + HeadNode.KeyLen ); return( p ); } /***********************************************************************/ //! Short description /*! */ xbLong xbNdx::GetTotalNodes( void ) { if( &HeadNode ) return HeadNode.TotalNodes; else return 0L; } /***********************************************************************/ //! Short description /*! */ xbUShort xbNdx::GetKeysPerNode( void ) { if( &HeadNode ) return HeadNode.KeysPerNode; else return 0L; } /***********************************************************************/ //! Short description /*! \param RetrieveSw */ xbShort xbNdx::GetFirstKey( xbShort RetrieveSw ) { /* This routine returns 0 on success and sets CurDbfRec to the record */ /* corresponding to the first index pointer */ xbLong TempNodeNo; xbShort rc; /* initialize the node chain */ if( NodeChain ){ ReleaseNodeMemory( NodeChain ); NodeChain = NULL; } if(( rc = GetHeadNode()) != 0 ){ CurDbfRec = 0L; return rc; } /* get a node and add it to the link */ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){ return rc; } /* traverse down the left side of the tree */ while( GetLeftNodeNo( 0, CurNode )){ TempNodeNo = GetLeftNodeNo( 0, CurNode ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } CurNode->CurKeyNo = 0; } CurDbfRec = GetDbfNo( 0, CurNode ); if( RetrieveSw ) return dbf->GetRecord( CurDbfRec ); else return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param RetrieveSw */ xbShort xbNdx::GetNextKey( xbShort RetrieveSw ) { /* This routine returns 0 on success and sets CurDbfRec to the record */ /* corresponding to the next index pointer */ xbNdxNodeLink * TempxbNodeLink; xbLong TempNodeNo; xbShort rc; if( !IsOpen() ){ CurDbfRec = 0L; return XB_NOT_OPEN; } if( !CurNode ){ rc = GetFirstKey( RetrieveSw ); return rc; } /* more keys on this node ? */ if(( CurNode->Leaf.NoOfKeysThisNode-1) > CurNode->CurKeyNo ){ CurNode->CurKeyNo++; CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode ); if( RetrieveSw ) return dbf->GetRecord( CurDbfRec ); else return XB_NO_ERROR; } /* if head node we are at eof */ if( CurNode->NodeNo == HeadNode.StartNode ) { return XB_EOF; } /* this logic assumes that interior nodes have n+1 left node no's where */ /* n is the number of keys in the node */ /* pop up one node to the interior node level & free the leaf node */ TempxbNodeLink = CurNode; CurNode = CurNode->PrevNode; CurNode->NextNode = NULL; ReleaseNodeMemory( TempxbNodeLink ); /* while no more right keys && not head node, pop up one node */ while(( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode ) && ( CurNode->NodeNo != HeadNode.StartNode )){ TempxbNodeLink = CurNode; CurNode = CurNode->PrevNode; CurNode->NextNode = NULL; ReleaseNodeMemory( TempxbNodeLink ); } /* if head node && right most key, return end-of-file */ if(( HeadNode.StartNode == CurNode->NodeNo ) && ( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode )) { return XB_EOF; } /* move one to the right */ CurNode->CurKeyNo++; TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ return rc; } /* traverse down the left side of the tree */ while( GetLeftNodeNo( 0, CurNode )){ TempNodeNo = GetLeftNodeNo( 0, CurNode ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } CurNode->CurKeyNo = 0; } CurDbfRec = GetDbfNo( 0, CurNode ); if( RetrieveSw ) return dbf->GetRecord( CurDbfRec ); else return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param NodeNo \param RetrieveSw */ xbShort xbNdx::GetLastKey( xbLong NodeNo, xbShort RetrieveSw ) { /* This routine returns 0 on success and sets CurDbfRec to the record */ /* corresponding to the last index pointer */ /* If NodeNo = 0, start at head node, otherwise start at NodeNo */ xbLong TempNodeNo; xbShort rc; if( NodeNo < 0 || NodeNo > HeadNode.TotalNodes ) return XB_INVALID_NODE_NO; /* initialize the node chain */ if( NodeChain ){ ReleaseNodeMemory( NodeChain ); NodeChain = NULL; } if( NodeNo == 0L ) if(( rc = GetHeadNode()) != 0 ){ CurDbfRec = 0L; return rc; } /* get a node and add it to the link */ if( NodeNo == 0L ){ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } } else { if(( rc = GetLeafNode( NodeNo, 1 )) != 0 ) { CurDbfRec = 0L; return rc; } } CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; /* traverse down the right side of the tree */ while( GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode )){ TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; } CurNode->CurKeyNo--; /* leaf node has one fewer ix recs */ CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode-1, CurNode ); if( RetrieveSw ) return dbf->GetRecord( CurDbfRec ); else return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param RetrieveSw */ xbShort xbNdx::GetPrevKey( xbShort RetrieveSw ) { /* This routine returns 0 on success and sets CurDbfRec to the record */ /* corresponding to the previous index pointer */ xbNdxNodeLink * TempxbNodeLink; xbLong TempNodeNo; xbShort rc; if( !IsOpen() ){ CurDbfRec = 0L; return XB_NOT_OPEN; } if( !CurNode ){ CurDbfRec = 0L; return GetFirstKey( RetrieveSw ); } /* more keys on this node ? */ if( CurNode->CurKeyNo > 0 ){ CurNode->CurKeyNo--; CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode ); if( RetrieveSw ) return dbf->GetRecord( CurDbfRec ); else return XB_NO_ERROR; } /* this logic assumes that interior nodes have n+1 left node no's where */ /* n is the number of keys in the node */ /* pop up one node to the interior node level & free the leaf node */ if( !CurNode->PrevNode ) { /* michael - make sure prev node exists */ return XB_EOF; } TempxbNodeLink = CurNode; CurNode = CurNode->PrevNode; CurNode->NextNode = NULL; ReleaseNodeMemory( TempxbNodeLink ); /* while no more left keys && not head node, pop up one node */ while(( CurNode->CurKeyNo == 0 ) && ( CurNode->NodeNo != HeadNode.StartNode )) { TempxbNodeLink = CurNode; CurNode = CurNode->PrevNode; CurNode->NextNode = NULL; ReleaseNodeMemory( TempxbNodeLink ); } /* if head node && left most key, return beginning-of-file */ if(( HeadNode.StartNode == CurNode->NodeNo ) && ( CurNode->CurKeyNo == 0 )) { return XB_BOF; } /* move one to the left */ CurNode->CurKeyNo--; TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ) { return rc; } if( GetLeftNodeNo( 0, CurNode )) /* if interior node */ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; else /* leaf node */ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode - 1; /* traverse down the right side of the tree */ while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */ TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } if( GetLeftNodeNo( 0, CurNode )) /* if interior node */ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; else /* leaf node */ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode - 1; } CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode - 1, CurNode ); if( RetrieveSw ) return dbf->GetRecord( CurDbfRec ); else return XB_NO_ERROR; } /**************************************************************************/ //! Short description /*! \param key \param klen \param node \param comp */ /* ** This is a pretty basic binary search with two exceptions: 1) it will ** find the first of duplicate key values and 2) will return the index ** and the value of the last comparision even if it doesn't find a ** match. */ xbShort xbNdx::BSearchNode(const char *key, xbShort klen, const xbNdxNodeLink *node, xbShort *comp) { xbShort c, p, start = 0, end = node->Leaf.NoOfKeysThisNode - 1; if(start > end){ *comp = 2; return 0; } do { p = (start + end) / 2; c = CompareKey(key, GetKeyData(p, (xbNdxNodeLink *)node), klen); switch(c){ case 1 : /* greater than */ start = p + 1; break; case 2 : /* less than */ end = p - 1; break; } } while(start <= end && c); if(c == 1) while(p < node->Leaf.NoOfKeysThisNode && (c = CompareKey(key, GetKeyData(p, (xbNdxNodeLink *)node), klen)) == 1) p++; *comp = c; if(!c) while(p > 0 && !CompareKey(key, GetKeyData(p - 1, (xbNdxNodeLink *)node), klen)) p--; return p; } /***********************************************************************/ //! Short description /*! \param Tkey \param Klen */ xbLong xbNdx::GetLeafFromInteriorNode( const char * Tkey, xbShort Klen ) { /* This function scans an interior node for a key and returns the */ /* correct interior leaf node no */ xbShort p, c; /* if Tkey > any keys in node, return right most key */ p = CurNode->Leaf.NoOfKeysThisNode - 1; if( CompareKey( Tkey, GetKeyData( p, CurNode ), Klen ) == 1 ) { CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; return GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode ); } p = BSearchNode(Tkey, Klen, CurNode, &c); CurNode->CurKeyNo = p; return GetLeftNodeNo( p, CurNode ); } /***********************************************************************/ //! Short description /*! \param d */ xbShort xbNdx::KeyExists( xbDouble d ) { char buf[9]; memset( buf, 0x00, 9 ); dbf->xbase->PutDouble( buf, d ); return FindKey( buf, 8, 0 ); } /***********************************************************************/ //! Short description /*! \param d */ xbShort xbNdx::FindKey( xbDouble d ) { char buf[9]; memset( buf, 0x00, 9 ); dbf->xbase->PutDouble( buf, d ); return FindKey( buf, 8, 1 ); } /***********************************************************************/ //! Short description /*! \param Key */ xbShort xbNdx::FindKey( const char * Key ) { return FindKey( Key, strlen( Key ), 1 ); } /***********************************************************************/ //! Short description /*! \param Tkey \param DbfRec */ xbShort xbNdx::FindKey( const char * Tkey, xbLong DbfRec ) { /* find a key with a specifc DBF record number */ xbShort rc; xbLong CurDbfRecNo; xbLong CurNdxDbfNo; /* if we are already on the correct key, return XB_FOUND */ if( CurNode ) { CurDbfRecNo = dbf->GetCurRecNo(); CurNdxDbfNo = GetDbfNo( CurNode->CurKeyNo, CurNode ); if( CurDbfRecNo == CurNdxDbfNo && (strncmp(Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen ) == 0 )) { return XB_FOUND; } } rc = FindKey( Tkey, HeadNode.KeyLen, 0 ); while( rc == 0 || rc == XB_FOUND ) { if( strncmp( Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen ) == 0 ){ if( DbfRec == GetDbfNo( CurNode->CurKeyNo, CurNode )) { return XB_FOUND; } else rc = GetNextKey( 0 ); } else { return XB_NOT_FOUND; } } return XB_NOT_FOUND; } /***********************************************************************/ //! Short description /*! */ xbShort xbNdx::FindKey( void ) { /* if no paramaters given, use KeyBuf */ return( FindKey( KeyBuf, HeadNode.KeyLen, 0 )); } /***********************************************************************/ //! Short description /*! \param Tkey \param Klen \param RetrieveSw */ xbShort xbNdx::FindKey( const char * Tkey, xbShort Klen, xbShort RetrieveSw ) { /* This routine sets the current key to the found key */ /* if RetrieveSw is true, the method positions the dbf record */ xbShort rc,i; xbLong TempNodeNo; if( NodeChain ) { ReleaseNodeMemory( NodeChain ); NodeChain = NULL; } if(( rc = GetHeadNode()) != 0 ){ CurDbfRec = 0L; return rc; } /* load first node */ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } /* traverse down the tree until it hits a leaf */ while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */ TempNodeNo = GetLeafFromInteriorNode( Tkey, Klen ); if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ CurDbfRec = 0L; return rc; } } i = BSearchNode(Tkey, Klen, CurNode, &rc); switch(rc) { case 0 : /* found! */ CurNode->CurKeyNo = i; CurDbfRec = GetDbfNo( i, CurNode ); if( RetrieveSw ) dbf->GetRecord(CurDbfRec); return XB_FOUND; case 1 : /* less than */ // if(i < CurNode->Leaf.NoOfKeysThisNode) break; // i++; case 2 : /* greater than */ CurNode->CurKeyNo = i; CurDbfRec = GetDbfNo( i, CurNode ); if( RetrieveSw ) dbf->GetRecord(CurDbfRec); return XB_NOT_FOUND; } CurNode->CurKeyNo = i; if(i >= CurNode->Leaf.NoOfKeysThisNode){ CurDbfRec = 0; return XB_EOF; } CurDbfRec = GetDbfNo( i, CurNode ); if((RetrieveSw) && (CurDbfRec > 0)) dbf->GetRecord( CurDbfRec ); return XB_NOT_FOUND; } /***********************************************************************/ //! Short description /*! */ xbShort xbNdx::CalcKeyLen() { xbShort rc; xbExpNode * TempNode; char FieldName[11]; char Type; TempNode = IxExp->GetFirstTreeNode(); if( !TempNode ) return 0; if( TempNode->Type == 'd' ) return -8; if( TempNode->Type == 'D' ){ memset( FieldName, 0x00, 11 ); memcpy( FieldName, TempNode->NodeText, TempNode->Len ); Type = dbf->GetFieldType( dbf->GetFieldNo( FieldName )); if( Type == 'N' || Type == 'F' ) return -8; } if(( rc = IxExp->ProcessExpression()) != XB_NO_ERROR ) return 0; TempNode = (xbExpNode *) IxExp->Pop(); if( !TempNode ) return 0; rc = TempNode->DataLen; if( !TempNode->InTree ) delete TempNode; return rc; } /***********************************************************************/ //! Short description /*! \param IxName \param Exp \param Unique \param Overlay */ xbShort xbNdx::CreateIndex(const char * IxName, const char * Exp, xbShort Unique, xbShort Overlay ) { xbShort i, KeyLen, rc; if( IsOpen()) CloseIndex(); if( strlen( Exp ) > 488 ) return XB_INVALID_KEY_EXPRESSION; if( dbf->GetDbfStatus() == 0 ) return XB_NOT_OPEN; /* Get the index file name and store it in the class */ SetFileName(IxName); /* check if the file already exists */ if (((indexfp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ) { fclose( indexfp ); return XB_FILE_EXISTS; } if (indexfp) fclose(indexfp); if(( indexfp = fopen( GetFileName(), "w+b" )) == NULL ) return XB_OPEN_ERROR; #ifdef XB_LOCKING_ON /* ** Must turn off buffering when multiple programs may be accessing ** index files. */ setbuf( indexfp, NULL ); #endif /* parse the expression */ IxExp = new xbExpn( dbf->xbase ); if(( rc = IxExp->BuildExpressionTree( Exp, strlen( Exp ), dbf )) != XB_NO_ERROR ) return rc; /* build the header record */ memset( &HeadNode, 0x00, sizeof( xbNdxHeadNode )); HeadNode.StartNode = 1L; HeadNode.TotalNodes = 2L; HeadNode.NoOfKeys = 1L; KeyLen = CalcKeyLen(); if( KeyLen == 0 || KeyLen > 100 ) /* 100 byte key length limit */ return XB_INVALID_KEY; else if( KeyLen == -8 ){ HeadNode.KeyType = 1; /* numeric key */ HeadNode.KeyLen = 8; } else { HeadNode.KeyType = 0; /* character key */ HeadNode.KeyLen = KeyLen; } // HeadNode.KeysPerNode = (xbUShort) ( XB_NDX_NODE_SIZE - (2*sizeof( xbLong ))) / // (HeadNode.KeyLen + 8 ); // HeadNode.KeySize = HeadNode.KeyLen + 8; // while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/ /* above code replaced with following by Paul Koufalis pkoufalis@cogicom.com */ // while(( HeadNode.KeyLen % 4 ) != 0 ) HeadNode.KeyLen++; /* multiple of 4*/ // HeadNode.KeySize = HeadNode.KeyLen + 8; /* above two lines commented out by gary 4/14/99 and replaced w/ following For compatibility with other Xbase tools KeyLen is the length of the key data KeySize = KeyLen+8, rounded up until divisible by 4 */ HeadNode.KeySize = HeadNode.KeyLen + 8; while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/ HeadNode.KeysPerNode = (xbUShort) (XB_NDX_NODE_SIZE - (2*sizeof( xbLong ))) / HeadNode.KeySize; HeadNode.Unique = Unique; strncpy( HeadNode.KeyExpression, Exp, 488 ); KeyBuf = (char *) malloc( HeadNode.KeyLen + 1 ); KeyBuf2 = (char *) malloc( HeadNode.KeyLen + 1 ); memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 ); memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 ); if(( rc = PutHeadNode( &HeadNode, indexfp, 0 )) != 0 ){ return rc; } /* write node #1 all 0x00 */ for( i = 0; i < XB_NDX_NODE_SIZE; i++ ){ if ((fwrite("\x00", 1, 1, indexfp)) != 1){ fclose( indexfp ); return XB_WRITE_ERROR; } } // IndexStatus = XB_OPEN; return dbf->AddIndexToIxList( index, GetFileName() ); } /***********************************************************************/ //! Short description /*! \param RecNo \param n \param NodeNo */ xbShort xbNdx::PutLeftNodeNo( xbShort RecNo, xbNdxNodeLink *n, xbLong NodeNo ) { /* This routine sets n node's leftnode number */ xbNdxLeafNode *temp; char *p; if( !n ) return XB_INVALID_NODELINK; temp = &n->Leaf; if( RecNo < 0 || RecNo > HeadNode.KeysPerNode) return XB_INVALID_KEY; p = temp->KeyRecs; p+= RecNo * ( 8 + HeadNode.KeyLen ); dbf->xbase->PutLong( p, NodeNo ); return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param RecNo \param n \param DbfNo */ xbShort xbNdx::PutDbfNo( xbShort RecNo, xbNdxNodeLink *n, xbLong DbfNo ) { /* This routine sets n node's dbf number */ xbNdxLeafNode *temp; char *p; if( !n ) return XB_INVALID_NODELINK; temp = &n->Leaf; if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode-1)) return XB_INVALID_KEY; p = temp->KeyRecs + 4; p+= RecNo * ( 8 + HeadNode.KeyLen ); dbf->xbase->PutLong( p, DbfNo ); return XB_NO_ERROR; } /************************************************************************/ //! Short description /*! \param l \param n */ xbShort xbNdx::PutLeafNode( xbLong l, xbNdxNodeLink *n ) { if ((_fseek(indexfp, (xbOffT)l * XB_NDX_NODE_SIZE , SEEK_SET)) != 0) { fclose( indexfp ); return XB_SEEK_ERROR; } dbf->xbase->PutLong( Node, n->Leaf.NoOfKeysThisNode ); if(( fwrite( Node, 4, 1, indexfp )) != 1 ){ fclose( indexfp ); return XB_WRITE_ERROR; } if(( fwrite( &n->Leaf.KeyRecs, XB_NDX_NODE_SIZE-4, 1, indexfp )) != 1 ){ fclose( indexfp ); return XB_WRITE_ERROR; } return 0; } /************************************************************************/ //! Short description /*! \param Head \param f \param UpdateOnly */ xbShort xbNdx::PutHeadNode( xbNdxHeadNode * Head, FILE * f, xbShort UpdateOnly ) { char buf[4]; if(( _fseek( f, 0L, SEEK_SET )) != 0 ){ fclose( f ); return XB_SEEK_ERROR; } memset( buf, 0x00, 4 ); dbf->xbase->PutLong( buf, Head->StartNode ); if(( fwrite( &buf, 4, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } memset( buf, 0x00, 4 ); dbf->xbase->PutLong( buf, Head->TotalNodes ); if(( fwrite( &buf, 4, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } memset( buf, 0x00, 4 ); dbf->xbase->PutLong( buf, Head->NoOfKeys ); if(( fwrite( &buf, 4, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } if( UpdateOnly ) return XB_NO_ERROR; memset( buf, 0x00, 2 ); dbf->xbase->PutLong( buf, Head->KeyLen ); if(( fwrite( &buf, 2, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } memset( buf, 0x00, 2 ); dbf->xbase->PutLong( buf, Head->KeysPerNode ); if(( fwrite( &buf, 2, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } memset( buf, 0x00, 2 ); dbf->xbase->PutLong( buf, Head->KeyType ); if(( fwrite( &buf, 2, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } memset( buf, 0x00, 4 ); dbf->xbase->PutLong( buf, Head->KeySize ); if(( fwrite( &buf, 4, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } if(( fwrite( &Head->Unknown2, XB_NDX_NODE_SIZE - 22, 1, f )) != 1 ){ fclose( f ); return XB_WRITE_ERROR; } return 0; } /************************************************************************/ //! Short description /*! \param RecNo \param n */ xbShort xbNdx::PutKeyData( xbShort RecNo, xbNdxNodeLink *n ) { /* This routine copies the KeyBuf data into xbNdxNodeLink n */ xbNdxLeafNode *temp; char *p; xbShort i; if( !n ) return XB_INVALID_NODELINK; temp = &n->Leaf; if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode-1)) return XB_INVALID_KEY; p = temp->KeyRecs + 8; p+= RecNo * ( 8 + HeadNode.KeyLen ); for( i = 0; i < HeadNode.KeyLen; i++ ) { *p = KeyBuf[i]; p++; } return XB_NO_ERROR; } /************************************************************************/ //! Short description /*! \param n \param pos \param d \param l \param w */ xbShort xbNdx::PutKeyInNode( xbNdxNodeLink * n, xbShort pos, xbLong d, xbLong l, xbShort w ) { xbShort i; /* check the node */ if (!n) return XB_INVALID_NODELINK; if(pos < 0 || pos > HeadNode.KeysPerNode) return XB_INVALID_RECORD; if(n->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode) return XB_NODE_FULL; /* if key movement, save the original key */ if( pos < n->Leaf.NoOfKeysThisNode ) memcpy( KeyBuf2, KeyBuf, HeadNode.KeyLen + 1); /* if interior node, handle the right most left node no */ if( GetLeftNodeNo( 0, n )) PutLeftNodeNo( n->Leaf.NoOfKeysThisNode+1, n, GetLeftNodeNo( n->Leaf.NoOfKeysThisNode, n )); for( i = n->Leaf.NoOfKeysThisNode; i > pos; i-- ){ memcpy( KeyBuf, GetKeyData(i-1,n), HeadNode.KeyLen ); PutKeyData( i, n ); PutDbfNo( i, n, GetDbfNo(i-1,n)); PutLeftNodeNo(i, n, GetLeftNodeNo(i-1,n)); } /* put new key in node */ if( pos < n->Leaf.NoOfKeysThisNode ) memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1); PutKeyData( pos, n ); PutDbfNo( pos, n, d ); PutLeftNodeNo( pos, n, l ); n->Leaf.NoOfKeysThisNode++; if( w ) return PutLeafNode( n->NodeNo, n ); else return 0; } /************************************************************************/ //! Short description /*! \param curNode Current Node \param newNode New Empty Node \param pos Position of new key in current node \param d dbf record number */ /* This function splits a full index leaf node into two parts as of 2/13/04, this split logic was modified to cause an even split in order to keep the index tree balanced */ xbShort xbNdx::SplitLeafNode( xbNdxNodeLink *curNode, xbNdxNodeLink *newNode, xbShort pos, xbLong d ) { xbShort curNodeNewSize; xbShort newNodeSize; xbShort i,j,rc,startPos; curNodeNewSize = (curNode->Leaf.NoOfKeysThisNode + 1) / 2; newNodeSize = curNode->Leaf.NoOfKeysThisNode + 1 - curNodeNewSize; /* save off the current key buffer */ memcpy( KeyBuf2, KeyBuf, HeadNode.KeyLen + 1 ); if( pos < curNodeNewSize ){ /* new key goes in current node */ /* copy second half of current node to beginning of new node */ /* memcpy( dst, src, len ); */ startPos = curNode->Leaf.NoOfKeysThisNode - newNodeSize; for( i = startPos, j = 0; i < CurNode->Leaf.NoOfKeysThisNode; i++, j++){ memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen ); PutKeyData( j, newNode ); PutDbfNo( j, newNode, GetDbfNo( i, curNode )); } /* make a hole for the new key */ for( i = curNodeNewSize - 1; i > pos; i-- ){ memcpy( KeyBuf, GetKeyData( i-1, curNode ), HeadNode.KeyLen ); PutKeyData( i, curNode ); PutDbfNo( i, curNode, GetDbfNo( i-1, curNode )); } /* insert key appropriately */ memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1 ); PutKeyData( pos, curNode ); PutDbfNo( pos, curNode, d ); } else { pos -= curNodeNewSize; /* do part one of the key migration */ if( pos ){ /* was originally startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + 1; then was changed to */ /* if( ((pos + curNodeNewSize) == HeadNode.KeysPerNode) && (pos == newNodeSize) ){ // off the right end startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize; } else { startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + 1; } */ /* and this didn't work startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize; */ startPos = curNodeNewSize; for( i = startPos, j = 0; j < pos && i < curNode->Leaf.NoOfKeysThisNode; i++, j++){ memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen ); PutKeyData( j, newNode ); PutDbfNo( j, newNode, GetDbfNo( i, curNode )); } } /* insert new key appropriately */ memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1 ); PutKeyData( pos, newNode ); PutDbfNo( pos, newNode, d ); /* Load the remainder of the keys on the new node past the new key */ if( pos < (newNodeSize-1) ){ // startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + pos + 1; startPos = curNodeNewSize + pos; for( i = startPos, j = pos+1; j < newNodeSize; i++, j++){ memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen ); PutKeyData( j, newNode ); PutDbfNo( j, newNode, GetDbfNo( i, curNode )); } } } curNode->Leaf.NoOfKeysThisNode = curNodeNewSize; newNode->Leaf.NoOfKeysThisNode = newNodeSize; /* write the new nodes to disk */ if(( rc = PutLeafNode( curNode->NodeNo, curNode )) != 0 ) return rc; if(( rc = PutLeafNode( newNode->NodeNo, newNode )) != 0 ) return rc; return 0; } /************************************************************************/ //! Short description /*! \param nodeToSplit Interior node to split \param newNode New empty node to use \param dscNodeNo Descendant node number */ /* This routine splits an interior node */ xbShort xbNdx::SplitINode( xbNdxNodeLink *nodeToSplit, xbNdxNodeLink *newNode, xbLong dscNodeNo ) { xbShort i,j,rc; xbNdxNodeLink * SaveNodeChain; xbNdxNodeLink * SaveCurNode; xbLong newNodeToSplitSize; xbLong newNodeSize; xbShort pos, startPos, offset; newNodeToSplitSize = (nodeToSplit->Leaf.NoOfKeysThisNode + 2 ) / 2; newNodeSize = nodeToSplit->Leaf.NoOfKeysThisNode + 2 - newNodeToSplitSize; pos = nodeToSplit->CurKeyNo; if( pos < (newNodeToSplitSize-1) ){ /* copy second half of nodeToSplit to newNode */ startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeSize +1; for(i=startPos, j=0; i <= nodeToSplit->Leaf.NoOfKeysThisNode; i++, j++ ){ if( i < nodeToSplit->Leaf.NoOfKeysThisNode ){ memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen ); PutKeyData( j, newNode ); } PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit )); } /* make a hole for the new key */ for( i = newNodeToSplitSize; i > pos; i-- ){ memcpy( KeyBuf, GetKeyData( i-1, nodeToSplit ), HeadNode.KeyLen ); PutKeyData( i, nodeToSplit ); PutLeftNodeNo( i, nodeToSplit, GetLeftNodeNo( i-1, nodeToSplit )); } /* load new high key value into current position on nodeToSplit */ if( pos < (newNodeToSplitSize - 1 )){ SaveNodeChain = NodeChain; NodeChain = NULL; SaveCurNode = CurNode; GetLastKey( GetLeftNodeNo( pos, nodeToSplit ), 0 ); memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen ); PutKeyData( pos, nodeToSplit ); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; } PutLeftNodeNo( pos+1, nodeToSplit, dscNodeNo ); } /*************/ /* part b */ /*************/ else { pos -= newNodeToSplitSize-1; /* do part one of the key migration */ if( pos ){ // startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + 2; // 5/29/04 gak changed the following line for index probs // startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + 1; if( HeadNode.KeysPerNode % 2 ) offset = 2; else offset = 1; startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + offset; for( i = startPos, j = 0; j < pos; i++, j++ ){ if( i < nodeToSplit->Leaf.NoOfKeysThisNode && j < (pos-1)){ memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen ); PutKeyData( j, newNode ); } else { SaveNodeChain = NodeChain; NodeChain = NULL; SaveCurNode = CurNode; GetLastKey( GetLeftNodeNo( i, nodeToSplit ), 0 ); memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen); PutKeyData( j, newNode ); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; } PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit )); } } /* insert new key appropriately */ if( pos < (newNodeSize - 1)){ SaveNodeChain = NodeChain; NodeChain = NULL; SaveCurNode = CurNode; GetLastKey( dscNodeNo, 0 ); memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen); PutKeyData( pos, newNode ); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; } PutLeftNodeNo( pos, newNode, dscNodeNo ); /* load remainder of the keys */ if( pos < (newNodeSize - 1)){ // startPos=nodeToSplit->Leaf.NoOfKeysThisNode-newNodeToSplitSize+pos+2; // 5/29/04 gak changed the following line for index probs startPos=nodeToSplit->Leaf.NoOfKeysThisNode-newNodeToSplitSize+pos+offset; for( i = startPos, j = pos+1; j < newNodeSize; i++, j++ ){ if( i < nodeToSplit->Leaf.NoOfKeysThisNode ){ memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen ); PutKeyData( j, newNode ); } PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit )); } } } nodeToSplit->Leaf.NoOfKeysThisNode = newNodeToSplitSize - 1; newNode->Leaf.NoOfKeysThisNode = newNodeSize - 1; if((rc = PutLeafNode( nodeToSplit->NodeNo, nodeToSplit )) != 0) return rc; if((rc = PutLeafNode( newNode->NodeNo, newNode )) != 0) return rc; return 0; } /************************************************************************/ //! Short description /*! \param RecBufSw \param KeyBufSw */ xbShort xbNdx::CreateKey( xbShort RecBufSw, xbShort KeyBufSw ) { /* RecBufSw 0 Use RecBuf */ /* 1 Use RecBuf2 */ /* KeyBufSw 0 Use KeyBuf */ /* 1 Use KeyBuf2 */ xbShort rc; xbExpNode * TempNode; if(( rc = IxExp->ProcessExpression( RecBufSw )) != XB_NO_ERROR ) return rc; TempNode = (xbExpNode *) IxExp->Pop(); if( !TempNode ) return XB_INVALID_KEY; if( KeyBufSw ){ if( HeadNode.KeyType == 1 ) /* numeric key */ dbf->xbase->PutDouble( KeyBuf2, TempNode->DoubResult ); else{ /* character key */ memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 ); memcpy( KeyBuf2, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) ); } } else { if( HeadNode.KeyType == 1 ) /* numeric key */ dbf->xbase->PutDouble( KeyBuf, TempNode->DoubResult ); else { /* character key */ memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 ); memcpy( KeyBuf, TempNode->StringResult.c_str(), XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) ); } } // if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode ); if( !TempNode->InTree ) delete TempNode; return 0; } /************************************************************************/ //! Short description /*! \param key */ xbShort xbNdx::GetCurrentKey(char *key) { CreateKey(0, 0); if(HeadNode.KeyType == 1) memcpy(key, KeyBuf, 8); else memcpy(key, KeyBuf, HeadNode.KeyLen + 1); return 0; } /************************************************************************/ //! Short description /*! \param DbfRec */ xbShort xbNdx::AddKey( xbLong DbfRec ) { /* This routine assumes KeyBuf contains the contents of the index to key */ char *p; xbShort i,rc; xbNdxNodeLink * TempNode; xbNdxNodeLink * Tparent; xbLong TempNodeNo; /* new, unattached leaf node no */ xbNdxNodeLink * SaveNodeChain; xbNdxNodeLink * SaveCurNode; /* find node key belongs in */ rc = FindKey( KeyBuf, HeadNode.KeyLen, 0 ); if( rc == XB_FOUND && HeadNode.Unique ) return XB_KEY_NOT_UNIQUE; if( CurNode->Leaf.NoOfKeysThisNode > 0 && rc == XB_FOUND ){ rc = 0; while( rc == 0 ){ if(( p = GetKeyData( CurNode->CurKeyNo, CurNode )) == NULL ) rc = -1; else { rc = CompareKey( KeyBuf, p, HeadNode.KeyLen ); if( rc == 0 && DbfRec >= GetDbfNo( CurNode->CurKeyNo, CurNode )){ if((rc = GetNextKey(0)) == XB_EOF) { if((rc = GetLastKey(0, 0)) != XB_NO_ERROR) return rc; CurNode->CurKeyNo++; } } else rc = -1; } } } /* update header node */ HeadNode.NoOfKeys++; /************************************************/ /* section A - if room in node, add key to node */ /************************************************/ if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){ if(( rc = PutKeyInNode( CurNode,CurNode->CurKeyNo,DbfRec,0L,1)) != 0) return rc; if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0) return rc; return XB_NO_ERROR; } /***********************************************************************/ /* section B - split leaf node if full and put key in correct position */ /***********************************************************************/ TempNode = GetNodeMemory(); TempNode->NodeNo = HeadNode.TotalNodes++; rc = SplitLeafNode( CurNode, TempNode, CurNode->CurKeyNo, DbfRec ); if( rc ) return rc; TempNodeNo = TempNode->NodeNo; ReleaseNodeMemory( TempNode ); /*****************************************************/ /* section C go up tree splitting nodes as necessary */ /*****************************************************/ Tparent = CurNode->PrevNode; while( Tparent && Tparent->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode-1) { TempNode = GetNodeMemory(); if( !TempNode ) return XB_NO_MEMORY; TempNode->NodeNo = HeadNode.TotalNodes++; rc = SplitINode( Tparent, TempNode, TempNodeNo ); if( rc ) return rc; TempNodeNo = TempNode->NodeNo; ReleaseNodeMemory( TempNode ); ReleaseNodeMemory( CurNode ); CurNode = Tparent; CurNode->NextNode = NULL; Tparent = CurNode->PrevNode; } /************************************************************/ /* Section D if CurNode is split root, create new root */ /************************************************************/ /* at this point CurNode = The node that was just split TempNodeNo = The new node split off from CurNode */ if(CurNode->NodeNo == HeadNode.StartNode ){ TempNode = GetNodeMemory(); if( !TempNode ) return XB_NO_MEMORY; SaveNodeChain = NodeChain; NodeChain = NULL; SaveCurNode = CurNode; GetLastKey( CurNode->NodeNo, 0 ); memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo,CurNode ),HeadNode.KeyLen ); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; PutKeyData( 0, TempNode ); PutLeftNodeNo( 0, TempNode, CurNode->NodeNo ); PutLeftNodeNo( 1, TempNode, TempNodeNo ); TempNode->NodeNo = HeadNode.TotalNodes++; TempNode->Leaf.NoOfKeysThisNode++; HeadNode.StartNode = TempNode->NodeNo; rc = PutLeafNode( TempNode->NodeNo, TempNode ); if( rc ) return rc; rc = PutHeadNode( &HeadNode, indexfp, 1 ); if( rc ) return rc; ReleaseNodeMemory( TempNode ); return XB_NO_ERROR; } /**********************************/ /* Section E make room in parent */ /**********************************/ for( i = Tparent->Leaf.NoOfKeysThisNode; i > Tparent->CurKeyNo; i-- ){ memcpy( KeyBuf, GetKeyData( i-1, Tparent ), HeadNode.KeyLen ); PutKeyData( i, Tparent ); PutLeftNodeNo( i+1, Tparent, GetLeftNodeNo( i, Tparent )); } /* put key in parent */ SaveNodeChain = NodeChain; NodeChain = NULL; SaveCurNode = CurNode; GetLastKey( CurNode->NodeNo, 0 ); memcpy( KeyBuf,GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen ); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; PutKeyData( i, Tparent ); PutLeftNodeNo( i+1, Tparent, TempNodeNo ); Tparent->Leaf.NoOfKeysThisNode++; rc = PutLeafNode( Tparent->NodeNo, Tparent ); if( rc ) return rc; rc = PutHeadNode( &HeadNode, indexfp, 1 ); if( rc ) return rc; return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param pos \param n */ xbShort xbNdx::RemoveKeyFromNode( xbShort pos, xbNdxNodeLink *n ) { xbShort i; /* check the node */ if( !n ) return XB_INVALID_NODELINK; if( pos < 0 || pos > HeadNode.KeysPerNode ) return XB_INVALID_KEY; for( i = pos; i < n->Leaf.NoOfKeysThisNode-1; i++ ){ memcpy( KeyBuf, GetKeyData( i+1, n), HeadNode.KeyLen ); PutKeyData( i, n ); PutDbfNo( i, n, GetDbfNo( i+1, n )); PutLeftNodeNo( i, n, GetLeftNodeNo( i+1, n )); } PutLeftNodeNo( i, n, GetLeftNodeNo( i+1, n )); n->Leaf.NoOfKeysThisNode--; /* if last key was deleted, decrement CurKeyNo */ if( n->CurKeyNo > n->Leaf.NoOfKeysThisNode ) n->CurKeyNo--; return PutLeafNode( n->NodeNo, n ); } /***********************************************************************/ //! Short description /*! \param n */ xbShort xbNdx::UpdateParentKey( xbNdxNodeLink * n ) { /* this routine goes backwards thru the node chain looking for a parent node to update */ xbNdxNodeLink * TempNode; if( !n ) return XB_INVALID_NODELINK; if( !GetDbfNo( 0, n )) return XB_NOT_LEAFNODE; TempNode = n->PrevNode; while( TempNode ){ if( TempNode->CurKeyNo < TempNode->Leaf.NoOfKeysThisNode ){ memcpy(KeyBuf,GetKeyData(n->Leaf.NoOfKeysThisNode-1,n),HeadNode.KeyLen); PutKeyData( TempNode->CurKeyNo, TempNode ); return PutLeafNode( TempNode->NodeNo, TempNode ); } TempNode = TempNode->PrevNode; } return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param n */ /* This routine queues up a list of nodes which have been emptied */ void xbNdx::UpdateDeleteList( xbNdxNodeLink *n ) { n->NextNode = DeleteChain; DeleteChain = n; } /***********************************************************************/ //! Short description /*! */ /* Delete nodes from the node list - for now we leave the empty nodes */ /* dangling in the file. Eventually we will remove nodes from the file */ void xbNdx::ProcessDeleteList( void ) { if( DeleteChain ){ ReleaseNodeMemory( DeleteChain ); DeleteChain = NULL; } } /***********************************************************************/ //! Short description /*! */ xbShort xbNdx::KeyWasChanged( void ) { CreateKey( 0, 0 ); /* use KeyBuf, RecBuf */ CreateKey( 1, 1 ); /* use KeyBuf2, RecBuf2 */ if( CompareKey( KeyBuf, KeyBuf2, HeadNode.KeyLen ) != 0 ) return 1; else return 0; } /***********************************************************************/ //! Short description /*! \param n */ xbNdxNodeLink * xbNdx::LeftSiblingHasSpace( xbNdxNodeLink * n ) { xbNdxNodeLink * TempNode; xbNdxNodeLink * SaveCurNode; /* returns a Nodelink to xbNdxNodeLink n's left sibling if it has space */ /* if left most node in parent return NULL */ if( n->PrevNode->CurKeyNo == 0 ) return NULL; SaveCurNode = CurNode; GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo-1, n->PrevNode ), 2 ); if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){ TempNode = CurNode; CurNode = SaveCurNode; TempNode->PrevNode = n->PrevNode; return TempNode; } else { /* node is already full */ ReleaseNodeMemory( CurNode ); CurNode = SaveCurNode; return NULL; } } /***********************************************************************/ //! Short description /*! \param n */ xbNdxNodeLink * xbNdx::RightSiblingHasSpace( xbNdxNodeLink * n ) { /* returns a Nodelink to xbNdxNodeLink n's right sibling if it has space */ xbNdxNodeLink * TempNode; xbNdxNodeLink * SaveCurNode; /* if left most node in parent return NULL */ if( n->PrevNode->CurKeyNo >= n->PrevNode->Leaf.NoOfKeysThisNode ) return NULL; SaveCurNode = CurNode; /* point curnode to right sib*/ GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo+1, n->PrevNode ), 2 ); if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){ TempNode = CurNode; CurNode = SaveCurNode; TempNode->PrevNode = n->PrevNode; return TempNode; } else { /* node is already full */ ReleaseNodeMemory( CurNode ); CurNode = SaveCurNode; return NULL; } } /*************************************************************************/ //! Short description /*! \param n \param Right */ xbShort xbNdx::MoveToRightNode( xbNdxNodeLink * n, xbNdxNodeLink * Right ) { xbShort j; xbNdxNodeLink * TempNode; xbNdxNodeLink * SaveCurNode; xbNdxNodeLink * SaveNodeChain; if( n->CurKeyNo == 0 ){ j = 1; SaveNodeChain = NodeChain; SaveCurNode = CurNode; NodeChain = NULL; GetLastKey( n->NodeNo, 0 ); memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode),HeadNode.KeyLen); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; } else { j = 0; memcpy( KeyBuf, GetKeyData( j, n ), HeadNode.KeyLen); } PutKeyInNode( Right, 0, 0L, GetLeftNodeNo( j, n ), 1 ); ReleaseNodeMemory( Right ); TempNode = n; CurNode = n->PrevNode; n = n->PrevNode; n->NextNode = NULL; UpdateDeleteList( TempNode ); DeleteSibling( n ); return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param n \param Left */ xbShort xbNdx::MoveToLeftNode( xbNdxNodeLink * n, xbNdxNodeLink * Left ) { xbShort j, rc; xbNdxNodeLink * SaveNodeChain; xbNdxNodeLink * TempNode; if( n->CurKeyNo == 0 ) j = 1; else j = 0; /* save the original node chain */ SaveNodeChain = NodeChain; NodeChain = NULL; /* determine new right most key for left node */ GetLastKey( Left->NodeNo, 0 ); memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen); ReleaseNodeMemory( NodeChain ); NodeChain = NULL; /* for next GetLastKey */ PutKeyData( Left->Leaf.NoOfKeysThisNode, Left); PutLeftNodeNo( Left->Leaf.NoOfKeysThisNode+1, Left, GetLeftNodeNo( j,n )); Left->Leaf.NoOfKeysThisNode++; Left->CurKeyNo = Left->Leaf.NoOfKeysThisNode; if(( rc = PutLeafNode( Left->NodeNo, Left )) != 0 ) return rc; n->PrevNode->NextNode = NULL; UpdateDeleteList( n ); /* get the new right most key for left to update parents */ GetLastKey( Left->NodeNo, 0 ); /* assemble the chain */ TempNode = Left->PrevNode; TempNode->CurKeyNo--; NodeChain->PrevNode = Left->PrevNode; UpdateParentKey( CurNode ); ReleaseNodeMemory( NodeChain ); ReleaseNodeMemory( Left ); CurNode = TempNode; NodeChain = SaveNodeChain; TempNode->CurKeyNo++; DeleteSibling( TempNode ); return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param n */ xbShort xbNdx::DeleteSibling( xbNdxNodeLink * n ) { xbNdxNodeLink * Left; xbNdxNodeLink * Right; xbNdxNodeLink * SaveCurNode; xbNdxNodeLink * SaveNodeChain; xbNdxNodeLink * TempNode; xbShort rc; /* this routine deletes sibling CurRecNo out of xbNodeLink n */ if( n->Leaf.NoOfKeysThisNode > 1 ){ RemoveKeyFromNode( n->CurKeyNo, n ); if( n->CurKeyNo == n->Leaf.NoOfKeysThisNode ){ SaveNodeChain = NodeChain; SaveCurNode = CurNode; NodeChain = NULL; GetLastKey( n->NodeNo, 0 ); /* assemble the node chain */ TempNode = NodeChain->NextNode; NodeChain->NextNode = NULL; ReleaseNodeMemory( NodeChain ); TempNode->PrevNode = n; UpdateParentKey( CurNode ); /* take it back apart */ ReleaseNodeMemory( TempNode ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; } } else if( n->NodeNo == HeadNode.StartNode ) { /* get here if root node and only one child remains */ /* make remaining node the new root */ if( n->CurKeyNo == 0 ) HeadNode.StartNode = GetLeftNodeNo( 1, n ); else HeadNode.StartNode = GetLeftNodeNo( 0, n ); UpdateDeleteList( n ); NodeChain = NULL; CurNode = NULL; } else if (( Left = LeftSiblingHasSpace( n )) != NULL ) return MoveToLeftNode( n, Left ); else if (( Right = RightSiblingHasSpace( n )) != NULL ) return MoveToRightNode( n, Right ); /* else if left sibling exists */ else if( n->PrevNode->CurKeyNo > 0 ) { /* move right branch from left sibling to this node */ SaveCurNode = CurNode; SaveNodeChain = NodeChain; NodeChain = NULL; GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo-1, n->PrevNode ), 2 ); Left = CurNode; Left->PrevNode = SaveCurNode->PrevNode; GetLastKey( Left->NodeNo, 0 ); strncpy( KeyBuf, GetKeyData( CurNode->CurKeyNo,CurNode),HeadNode.KeyLen ); if( n->CurKeyNo == 1 ) PutLeftNodeNo( 1, n, GetLeftNodeNo( 0, n )); PutKeyData( 0, n ); PutLeftNodeNo( 0, n, GetLeftNodeNo( Left->Leaf.NoOfKeysThisNode, Left )); if(( rc = PutLeafNode( n->NodeNo, n )) != XB_NO_ERROR ) return rc; SaveCurNode = n->PrevNode; SaveCurNode->NextNode = NULL; ReleaseNodeMemory( n ); Left->Leaf.NoOfKeysThisNode--; if(( rc = PutLeafNode( Left->NodeNo, Left )) != XB_NO_ERROR ) return rc; /* rebuild left side of tree */ GetLastKey( Left->NodeNo, 0 ); NodeChain->PrevNode = SaveCurNode; SaveCurNode->CurKeyNo--; UpdateParentKey( CurNode ); ReleaseNodeMemory( NodeChain ); ReleaseNodeMemory( Left ); CurNode = SaveCurNode; NodeChain = SaveNodeChain; } /* right sibling must exist */ else if( n->PrevNode->CurKeyNo <= n->PrevNode->Leaf.NoOfKeysThisNode ){ /* move left branch from left sibling to this node */ SaveCurNode = CurNode; SaveNodeChain = NodeChain; NodeChain = NULL; /* move the left node number one to the left if necessary */ if( n->CurKeyNo == 0 ){ PutLeftNodeNo( 0, n, GetLeftNodeNo( 1, n )); GetLastKey( GetLeftNodeNo( 0, n ), 0 ); memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen); PutKeyData( 0, n ); ReleaseNodeMemory( NodeChain ); NodeChain = NULL; } GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo+1, n->PrevNode ), 2 ); /* put leftmost node number from right node in this node */ PutLeftNodeNo( 1, n, GetLeftNodeNo( 0, CurNode )); if(( rc = PutLeafNode( n->NodeNo, n )) != XB_NO_ERROR ) return rc; /* remove the key from the right node */ RemoveKeyFromNode( 0, CurNode ); if(( rc = PutLeafNode( CurNode->NodeNo, CurNode )) != XB_NO_ERROR ) return rc; ReleaseNodeMemory( CurNode ); /* update new parent key value */ GetLastKey( n->NodeNo, 0 ); NodeChain->PrevNode = n->PrevNode; UpdateParentKey( CurNode ); ReleaseNodeMemory( NodeChain ); NodeChain = SaveNodeChain; CurNode = SaveCurNode; } else { /* this should never be true-but could be if 100 byte limit is ignored*/ std::cout << "Fatal index error" << std::endl; exit(0); } return XB_NO_ERROR; } /***********************************************************************/ //! Short description /*! \param DbfRec */ xbShort xbNdx::DeleteKey( xbLong DbfRec ) { /* this routine assumes the key to be deleted is in KeyBuf */ xbNdxNodeLink * TempNode; xbShort rc; #if 0 // Not sure why this check is here, but it prevents numeric keys // from being deleted (and thus index updates will also fail). // I have removed it for now. Derry Bryson if( HeadNode.KeyType != 0x00 ) xb_error(XB_INVALID_KEY_TYPE); #endif if(( rc = FindKey( KeyBuf, DbfRec )) != XB_FOUND ) return rc; /* found the record to delete at this point */ HeadNode.NoOfKeys--; /* delete the key from the node */ if(( rc = RemoveKeyFromNode( CurNode->CurKeyNo, CurNode )) != 0 ) return rc; /* if root node, we are done */ if( !( CurNode->NodeNo == HeadNode.StartNode )){ /* if leaf node now empty */ if( CurNode->Leaf.NoOfKeysThisNode == 0 ){ TempNode = CurNode->PrevNode; TempNode->NextNode = NULL; UpdateDeleteList( CurNode ); CurNode = TempNode; DeleteSibling( CurNode ); ProcessDeleteList(); } /* if last key of leaf updated, update key in parent node */ /* this logic updates the correct parent key */ else if( CurNode->CurKeyNo == CurNode->Leaf.NoOfKeysThisNode ) UpdateParentKey( CurNode ); } if(CurNode) CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode ); else CurDbfRec = 0; if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0 ) return rc; return XB_NO_ERROR; } /************************************************************************/ //! Short description /*! \param option */ #ifdef XBASE_DEBUG xbShort xbNdx::CheckIndexIntegrity( const xbShort option ) { /* if option = 1, print out some stats */ xbShort rc; xbLong ctr = 1L; while( ctr <= dbf->NoOfRecords()){ if( option ) std::cout << "Checking Record " << ctr << std::endl; if(( rc = dbf->GetRecord(ctr++)) != XB_NO_ERROR ) return rc; if(!dbf->RecordDeleted()){ CreateKey( 0, 0 ); rc = FindKey( KeyBuf, dbf->GetCurRecNo()); if( rc != XB_FOUND ){ if( option ){ std::cout << "Record number " << dbf->GetCurRecNo() << " Not Found" << std::endl; std::cout << "Key = " << KeyBuf << std::endl; } return rc; } } } if( option ) std::cout << std::endl << "Total records checked = " << ctr - 1 << std::endl; return XB_NO_ERROR; } #endif /***********************************************************************/ //! Short description /*! \param statusFunc */ xbShort xbNdx::ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems)) { /* this method assumes the index has been locked in exclusive mode */ xbLong l; xbShort rc, i, saveAutoLock; xbNdxHeadNode TempHead; FILE *t; xbString TempName; memcpy( &TempHead, &HeadNode, sizeof( struct xbNdxHeadNode )); TempHead.NoOfKeys = 1L; TempHead.TotalNodes = 2L; TempHead.StartNode = 1L; rc = dbf->xbase->DirectoryExistsInName( GetFileName() ); if( rc ){ TempName.assign(GetFileName(), 0, rc); TempName += "TEMPFILE.NDX"; } else TempName = "TEMPFILE.NDX"; if(( t = fopen( TempName, "w+b" )) == NULL ) return XB_OPEN_ERROR; if(( rc = PutHeadNode( &TempHead, t, 0 )) != 0 ){ fclose( t ); remove(TempName); return rc; } for( i = 0; i < XB_NDX_NODE_SIZE; i++ ){ if(( fwrite( "\x00", 1, 1, t )) != 1 ){ fclose( t ); remove(TempName); return XB_WRITE_ERROR; } } if( fclose( indexfp ) != 0 ) return XB_CLOSE_ERROR; if( fclose( t ) != 0 ) return XB_CLOSE_ERROR; if( remove( GetFileName() ) != 0 ) return XB_CLOSE_ERROR; if( rename(TempName, GetFileName() ) != 0 ) return XB_WRITE_ERROR; if(( indexfp = fopen( GetFileName(), "r+b" )) == NULL ) return XB_OPEN_ERROR; saveAutoLock = dbf->GetAutoLock(); dbf->AutoLockOff(); for( l = 1; l <= dbf->PhysicalNoOfRecords(); l++ ){ if(statusFunc && (l == 1 || !(l % 100) || l == dbf->PhysicalNoOfRecords())) statusFunc(l, dbf->PhysicalNoOfRecords()); if(( rc = dbf->GetRecord(l)) != XB_NO_ERROR ){ if(saveAutoLock) dbf->AutoLockOn(); return rc; } if(!dbf->GetRealDelete() || !dbf->RecordDeleted()){ /* Create the key */ CreateKey( 0, 0 ); /* add key to index */ if(( rc = AddKey( l )) != XB_NO_ERROR ){ if(saveAutoLock) dbf->AutoLockOn(); return rc; } } } return rc; } /***********************************************************************/ //! Short description /*! \param size */ void xbNdx::SetNodeSize(xbShort size) { #ifdef XB_VAR_NODESIZE if(size >= XB_DEFAULT_NDX_NODE_SIZE) { if(size % XB_NDX_NODE_MULTIPLE) NodeSize = ((size + XB_NDX_NODE_MULTIPLE) / XB_NDX_NODE_MULTIPLE) * XB_NDX_NODE_MULTIPLE; else NodeSize = size; } else NodeSize = XB_DEFAULT_NDX_NODE_SIZE; #endif } /***********************************************************************/ //! Short description /*! \param buf \param len */ void xbNdx::GetExpression(char *buf, int len) { memcpy(buf, HeadNode.KeyExpression, len < XB_NDX_NODE_SIZE ? len : XB_NDX_NODE_SIZE - XB_NDX_NODE_BASESIZE); } const char* xbNdx::GetExtWithDot(bool lower) { return lower? ".ndx": ".NDX"; } xbUShort xbNdx::GetKeyLen() { return HeadNode.KeyLen; } const char* xbNdx::GetKeyExpression() { return HeadNode.KeyExpression; } void xbNdx::FreeNodesMemory() { ReleaseNodeMemory(NodeChain, true); NodeChain = 0; // ReleaseNodeMemory(CloneChain, true); // CloneChain = 0; ReleaseNodeMemory(FreeNodeChain, true); FreeNodeChain = 0; ReleaseNodeMemory(DeleteChain, true); DeleteChain = 0; } #endif /* XB_INDEX_NDX */