//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1994 - 2000. // // File: rowindex.cxx // // Contents: Implementation of CRowIndex // // Classes: CRowIndex // // History: 23 Aug 1994 dlee Created // 30 Nov 1996 dlee Converted to use dynarrayinplace // //-------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #include "rowindex.hxx" #include "tabledbg.hxx" inline BOOL isOdd(ULONG x) { return 0 != (x & 1); } //+------------------------------------------------------------------------- // // Member: CRowIndex::_FindInsertionPoint, private // // Synopsis: Binary search to find insertion point for a row. // Returns one past the last row or the first row >= // to the given row // // Arguments: [Value] -- value of the row -- internally an offset // // History: 23 Aug 1994 dlee Created // //-------------------------------------------------------------------------- ULONG CRowIndex::_FindInsertionPoint( TBL_OFF Value ) const { ULONG cRows = _aRows.Count(); ULONG iLo = 0; ULONG iHi = cRows - 1; do { ULONG cHalf = cRows / 2; if (0 != cHalf) { ULONG iMid = isOdd(cRows) ? cHalf : (cHalf - 1); iMid += iLo; int i = _pRowCompare->Compare( Value, _aRows[iMid] ); if (0 == i) { return iMid; } else if (i < 0) { iHi = iMid - 1; cRows = isOdd(cRows) ? cHalf : (cHalf - 1); } else { iLo = iMid + 1; cRows = cHalf; } } else if (0 != cRows) { int i = _pRowCompare->Compare( Value, _aRows[iLo] ); if (i <= 0) return iLo; else return iLo + 1; } else return iLo; } while (TRUE); Win4Assert(! "Invalid CRowIndex::_Find function exit point"); return 0; } //_FindInsertionPoint //+--------------------------------------------------------------------------- // // Function: _FindRowByLinearSearch, private // // Synopsis: Given the offset of a row in the table window, this method // searches for the entry in the row index which points to that // row. // // Arguments: [oTableRow] -- Offset of the row in the table window. // [iRowIndex] -- On output, will have the index of the entry // in the row index which points to oTableRow. // // Returns: TRUE if found; FALSE o/w // // History: 11-22-94 srikants Created // // Notes: The row index is searched linearly. This must be used only // if there is no row comparator. // //---------------------------------------------------------------------------- inline BOOL CRowIndex::_FindRowByLinearSearch( TBL_OFF oTableRow, ULONG & iRowIndex ) const { for ( ULONG iCurr = 0; iCurr < _aRows.Count(); iCurr++ ) { if ( _aRows[iCurr] == oTableRow ) { iRowIndex = iCurr; return TRUE; } } return FALSE; } // _FindRowByLinearSearch #if 0 //+--------------------------------------------------------------------------- // // Function: _FindRowByBinarySearch, private // // Synopsis: Using a binary search, this method locates the entry in the // row index which is same as the row indicated by oTableRow. // As duplicates are allowed, it is possible that there is more // than one entry in the row index, which will match the row in // the table window indicated by the oTableRow. // // Arguments: [oTableRow] -- Offset of the row in the table window. // [iRowIndex] -- On output, will contain the index of the entry // in the row index which has the same key as the "oTableRow" // // Returns: TRUE if found successfully. FALSE o/w // // History: 11-22-94 srikants Created // // Notes: NOT TESTED OR REVIEWED // //---------------------------------------------------------------------------- inline BOOL CRowIndex::_FindRowByBinarySearch( TBL_OFF oTableRow, ULONG & iRowIndex ) const { Win4Assert( 0 != _pRowCompare ); ULONG *pBase = _Base(); ULONG cRows = _aRows.Count(); ULONG iLo = 0; ULONG iHi = _aRows.Count() - 1; ULONG cHalf = 0; do { cHalf = cRows / 2; if (0 != cHalf) { ULONG iMid = isOdd(cRows) ? cHalf : (cHalf - 1); iMid += iLo; int i = _pRowCompare->Compare(oTableRow, pBase[iMid]); if (0 == i) { iRowIndex = iMid; return TRUE; } else if (i < 0) { iHi = iMid - 1; cRows = isOdd(cRows) ? cHalf : (cHalf - 1); } else { iLo = iMid + 1; cRows = cHalf; } } else if (0 != cRows) { Win4Assert( 1 == cRows ); int i = _pRowCompare->Compare(oTableRow, pBase[iLo]); if ( 0 == i ) { iRowIndex = iLo; return TRUE; } } } while ( 0 != cHalf ); Win4Assert( !_FindRowByLinearSearch( oTableRow, iRowIndex ) ); return FALSE; } // FindRowByBinarySearch #endif //+--------------------------------------------------------------------------- // // Function: FindRow, public // // Synopsis: Given an offset of a row in the table window, this method // locates the entry in the row index, which points to the // row in the table window. // // Arguments: [oTableRow] -- (IN) The offset of the row in the table window. // [iRowIndex] -- (OUT) The index of the entry in the row index // which points to the oTableRow. // // Returns: TRUE if search successful; FALSE O/W // // History: 11-22-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- BOOL CRowIndex::FindRow( TBL_OFF oTableRow, ULONG &iRowIndex ) const { return _FindRowByLinearSearch( oTableRow, iRowIndex ); } //+------------------------------------------------------------------------- // // Member: CRowIndex::AddRow, public // // Synopsis: Adds a row in sort order to the index // // Arguments: [Value] -- Value to insert in the index, represents a row // // Returns: Index of the newly added row // // History: 23 Aug 1994 dlee Created // //-------------------------------------------------------------------------- ULONG CRowIndex::AddRow(TBL_OFF Value) { Win4Assert( 0 != _pRowCompare ); ULONG cRows = _aRows.Count(); // Find the insertion point for the new row. if ( 0 == cRows ) { // No rows yet _aRows[0] = Value; return 0; } else if ( ( _pRowCompare->Compare( _aRows[cRows - 1], Value ) ) <= 0 ) { // append a row to the end _aRows[ cRows ] = Value; return cRows; } // insert a row. ULONG iInsertionPoint = _FindInsertionPoint( Value ); _aRows.Insert( Value, iInsertionPoint ); return iInsertionPoint; } //AddRow //+------------------------------------------------------------------------- // // Member: CRowIndex::DeleteRow, public // // Synopsis: Deletes a row from the index and moves the following rows // up a notch in the array. // // Arguments: [iRow] -- row to be deleted // // History: 29 Aug 1994 dlee Created // //-------------------------------------------------------------------------- void CRowIndex::DeleteRow(ULONG iRow) { Win4Assert( iRow < _aRows.Count() ); _aRows.Remove( iRow ); } //DeleteRow //+------------------------------------------------------------------------- // // Member: CRowIndex::ResortRow, public // // Synopsis: Bubbles the given row up or down based on the sort key. // Useful for when a file property is updated after being // added to the table. // // Arguments: [iRow] -- row to be resorted // // Returns: ULONG - new index of the row // // PERFFIX: should probably do a binary search, not a linear one // // History: 29 Aug 1994 dlee Created // //-------------------------------------------------------------------------- ULONG CRowIndex::ResortRow(ULONG iRow) { Win4Assert(iRow < _aRows.Count()); Win4Assert( 0 != _pRowCompare ); // Get the start of the array of offsets TBL_OFF *pBase = _Base(); Win4Assert(0 != pBase); // Bubble toward row 0 while ((iRow > 0) && (_pRowCompare->Compare(pBase[iRow], pBase[iRow - 1]) < 0)) { TBL_OFF iTmp = pBase[iRow]; pBase[iRow] = pBase[iRow - 1]; iRow--; pBase[iRow] = iTmp; } // Bubble toward the last row while ((iRow < (_aRows.Count() - 1)) && (_pRowCompare->Compare(pBase[iRow], pBase[iRow + 1]) > 0)) { TBL_OFF iTmp = pBase[iRow]; pBase[iRow] = pBase[iRow + 1]; iRow++; pBase[iRow] = iTmp; } return iRow; } //ResortRow //+--------------------------------------------------------------------------- // // Member: CRowIndex::ResizeAndInit // // Synopsis: Resizes the current row index to be the same size as // specified. // // Arguments: [cNewRows] - Number of rows in the new row index. // // History: 7-31-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRowIndex::ResizeAndInit( ULONG cNewRows ) { _aRows.Clear(); } //+--------------------------------------------------------------------------- // // Function: SyncUp // // Synopsis: Synchronizes the permutation (rowindex contents) with that // of the new index. // // Arguments: [newIndex] -- The newIndex whose permutation must be copied // to our permutation. // // History: 11-29-94 srikants Created // 11-30-96 dlee converted to dynarrayinplace // // Notes: The implementation is optimized by doing a block copy of the // contents of the source row index. That is much faster than // adding individual entries from the source row index. // //---------------------------------------------------------------------------- void CRowIndex::SyncUp( CRowIndex & newIndex ) { _aRows.Duplicate( newIndex._aRows ); } //+--------------------------------------------------------------------------- // // Function: FindSplitPoint // // Synopsis: Given a row whose offset in the table is "oTableRow", this // method finds out the highest row in the rowIndex which is <= // "oTableRow". This method is used during a window split to // determine the split-position of a rowIndex. // // Arguments: [oTableRow] - Offset of the row to compare with. // // Returns: The first row that belongs to the RHS. // // History: 1-08-95 srikants Created // // Notes: This method is used to find a split point in the client // row index during a window split. After determining the split // point in the query row index, we have to find a point in the // client row index which will split the client row index also // in the same manner as the query row index. // //---------------------------------------------------------------------------- LONG CRowIndex::FindSplitPoint( TBL_OFF oTableRow ) const { #if DBG==1 // CheckSortOrder(); #endif // DBG==1 // Get the start of the array of offsets LONG iSplitRow = LONG_MAX; int iComp = 0; if ( 0 == _pRowCompare || 0 == _aRows.Count() ) { iSplitRow = 0; } else if ( _pRowCompare->Compare( oTableRow, _aRows[0] ) < 0 ) { // // The given row is < the smallest row in the row index. // iSplitRow = 0; } else if ( (iComp = _pRowCompare->Compare( oTableRow, _aRows[_aRows.Count()-1] )) >= 0 ) { // // The given row is >= the biggest row in the row index. // iSplitRow = _aRows.Count(); } else { ULONG oSplitRow = _FindInsertionPoint( oTableRow ); Win4Assert( oSplitRow < _aRows.Count() ); iSplitRow = (LONG) _aRows.Count(); for ( unsigned i = oSplitRow; i < _aRows.Count(); i++ ) { int iComp = _pRowCompare->Compare( oTableRow, _aRows[i] ); Win4Assert( iComp <= 0 ); if ( iComp < 0 ) { iSplitRow = (LONG) i; break; } } } Win4Assert( LONG_MAX != iSplitRow ); return iSplitRow; } //+--------------------------------------------------------------------------- // // Function: FindMidSplitPoint // // Synopsis: Finds out a point in the row index which can serve as a split // point during a window split. The split point is such that // all rows in the rowindex from 0..SplitPoint are <= the row // in the SplitPoint and all rows SplitPoint+1.._aRows.Count()-1 are // > the row in the SplitPoint. // // Arguments: [riSplitPoint] - (output) The split point, if one exists. // // Returns: TRUE if a split point satisfying the above requirement is // found. FALSE o/w // // History: 1-25-95 srikants Created // // Notes: This method is used during a window split to determine a // split point in the query row index (if one exists). // //---------------------------------------------------------------------------- BOOL CRowIndex::FindMidSplitPoint( ULONG & riSplitPoint ) const { #if DBG==1 // CheckSortOrder(); #endif // DBG==1 BOOL fFound = FALSE; if ( 0 != _aRows.Count() && 0 != _pRowCompare ) { ULONG oMidPoint = _aRows.Count()/2; // // Find the first row to the RHS which is > the middle row. // for ( ULONG j = oMidPoint+1; j < _aRows.Count(); j++ ) { int iComp; if ( 0 != ( iComp = _pRowCompare->Compare( _aRows[oMidPoint], _aRows[j]) ) ) { Win4Assert( iComp < 0 ); fFound = TRUE; riSplitPoint = j-1; break; } } if ( !fFound ) { // // All rows to the right of oMidPoint are equal. We should now // try the LHS. // for ( int i = (int) oMidPoint-1; i >= 0; i-- ) { int iComp; if ( 0 != (iComp = _pRowCompare->Compare( _aRows[i], _aRows[oMidPoint] )) ) { Win4Assert( iComp < 0 ); fFound = TRUE; riSplitPoint = (ULONG) i; break; } } } // // PERFFIX - this algorithm can be modified to find the split point // which is as close to the mid point as possible. That would mean // looking the LHS even if we find a split point in the RHS and the // split point != the oMidPoint. That will involve more comparisons. // } return fFound; } #if CIDBG==1 || DBG==1 void CRowIndex::CheckSortOrder() const { if ( _aRows.Count() <= 1 || 0 == _pRowCompare ) { return; } for ( unsigned i = 0; i < _aRows.Count()-1 ; i++ ) { int iComp = _pRowCompare->Compare( _aRows[i], _aRows[i+1] ); Win4Assert( iComp <= 0 ); } } #endif // CIDBG==1 || DBG==1