windows-nt/Source/XPSP1/NT/inetsrv/query/bigtable/rowindex.cxx
2020-09-26 16:20:57 +08:00

579 lines
16 KiB
C++

//+-------------------------------------------------------------------------
//
// 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