|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: EntryBuf.Cxx
//
// Contents: Implementation of the CEntryBuffer and associated classes.
//
// Classes: CEntry, CKeyVector, CEntryBuffer, CEntryBufferHandler
//
// History: 18-Mar-93 AmyA Created from wordlist.cxx and
// sort.cxx
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <entrybuf.hxx>
#include <fdaemon.hxx>
#include <pidmap.hxx>
#include <norm.hxx>
// 12 was chosed after some testing on actual data
const unsigned int minPartSize = 12;
// the stack will be big enough to quicksort 2^cwStack entries
const unsigned int cwStack = 32;
//+---------------------------------------------------------------------------
//
// Function: Median, public
//
// Synopsis: Find the median of three entries
//
// Arguments: [apEntry] -- array of pointers to entries
// [i1], [i2], [i3] -- three indexes
//
// History: 07-Jun-91 BartoszM Created
//
//----------------------------------------------------------------------------
unsigned Median ( CEntry * * apEntry, unsigned i1, unsigned i2, unsigned i3 ) { const CEntry* p1 = apEntry[i1]; const CEntry* p2 = apEntry[i2]; const CEntry* p3 = apEntry[i3];
int c = p1->Compare( p2 ); if ( c > 0 ) // p1 > p2
{ c = p2->Compare( p3 ); if ( c > 0 ) return i2; else { c = p1->Compare( p3 ); return ( c > 0 )? i3: i1; } } else // p2 >= p1
{ c = p1->Compare( p3 ); if ( c > 0 ) return i1; else { c = p2->Compare( p3 ); return ( c > 0 )? i3: i2; } } }
//+---------------------------------------------------------------------------
//
// Function: InsertSort
//
// Synopsis: Last step of sort is to perform a straight insertion sort
// if the limit partition size is > 1.
//
// Arguments: [apEntry] -- array of pointers to sort keys
// [count] -- # of sort keys to be sorted
//
// Notes: First key is at offset 1, last at offset count
//
//---------------------------------------------------------------------------
void InsertSort( CEntry * apEntry[], unsigned count) { if (count <= 1) // nothing to sort
return;
// loop from 2 through nth element
for(unsigned j = 2; j <= count; j++) { CEntry * key = apEntry[j]; // go backwards from j-1 shifting up keys greater than 'key'
for ( unsigned i = j - 1; i > 0 && apEntry[i]->Compare( key ) > 0; i-- ) { apEntry[i+1] = apEntry[i]; } // found key less than 'key' or hit the beginning (i == 0)
// insert key in the hole
apEntry[i+1] = key; } }
//+---------------------------------------------------------------------------
//
// Class: CIStack
//
// Purpose: A stack of integers
//
// History: 12-Jun-91 BartoszM Created
//
//----------------------------------------------------------------------------
class CIStack { public: CIStack() { _top = _arr; } void Push ( unsigned i ) { *_top++ = i; ciAssert(_top-_arr < cwStack); } unsigned Pop () { return *--_top; } unsigned Top () const { return *_top; } BOOL Empty() const { return _top == _arr; } private: unsigned _arr[cwStack]; unsigned * _top; };
//+---------------------------------------------------------------------------
//
// Member: CEntryBuffer::CEntryBuffer, public
//
// Synopsis: Fills the buffer with two sentinels.
//
// Arguments: [cb] -- size of the buffer
// [buf] -- Pointer to the buffer
//
// History: 28-May-92 KyleP Added single wid detection
// 07-Jun-91 BartoszM Created
// 22-Feb-96 SrikantS Pass in a buffer instead of allocing
// everytime.
//
//----------------------------------------------------------------------------
CEntryBuffer::CEntryBuffer ( ULONG cb, BYTE * buf ) : _buf( buf ), _cb( cb ) { Win4Assert( 0 != buf );
// save space at the end of the buffer for the vector array offset.
_pWidSingle = (WORKID*)(_buf + cb - sizeof(WORKID) - sizeof(int)); _bufSize = (unsigned)((BYTE*)_pWidSingle - _buf); ciAssert ( _bufSize > 4*(sizeof(CEntry) + MAXKEYSIZE) );
// This is done once during construction
_curEntry = (CEntry*) _buf; _keyVec.Init ( _buf, _bufSize );
// insert sentinel keys
// first sort key is minimum key, i.e., 0 length key with 0 pid, 0
// workid and 0 offset
// this key will be added to the end of _keyVec
// just before sorting.
_curEntry->SetPid ( 0 ); _curEntry->SetWid ( widInvalid ); _curEntry->SetOcc ( 0 ); _curEntry->SetCount ( 0 );
_curEntry = _curEntry->Next();
// then the maximum key
// max key has max size filled with 0xff's
ciAssert ( widInvalid == 0xffffffff ); ciAssert ( OCC_INVALID == 0xffffffff ); ciAssert ( pidInvalid == 0xffffffff );
_curEntry->SetPid ( pidInvalid ); _curEntry->SetWid ( widInvalid ); _curEntry->SetOcc ( OCC_INVALID ); _curEntry->SetCount (MAXKEYSIZE ); memset(_curEntry->GetKeyBuf(), MAX_BYTE, MAXKEYSIZE); Advance();
//
// *_pWidSingle = 0 means we haven't seen a wid yet.
//
*_pWidSingle = 0; }
//+---------------------------------------------------------------------------
//
// Member: CEntryBuffer::ReInit, public
//
// Synopsis: Discard old data, reuse the sentinels
//
// History: 28-May-92 KyleP Added single wid detection
// 07-Jun-91 BartoszM Created
//
//----------------------------------------------------------------------------
void CEntryBuffer::ReInit() { _keyVec.Init ( _buf, _bufSize ); _curEntry = (CEntry*) _buf;
// skip the min key
_curEntry = _curEntry->Next(); // add the max key to the table
Advance();
//
// *_pWidSingle = 0 means we haven't seen a wid yet.
//
*_pWidSingle = 0; }
//+---------------------------------------------------------------------------
//
// Member: CEntryBuffer::Done, public
//
// Synopsis: Prepare buffer to be transported to kernel by calculating
// offsets and inserting offset to first key at the end of the
// buffer.
//
// History: 30-Jun-93 AmyA Created
//
//----------------------------------------------------------------------------
void CEntryBuffer::Done() { _keyVec.CalculateOffsets ( Count() + 2 ); // include sentinels
// the offset to the vector array is put in the space in the buffer that
// has been reserved after the WORKID pointed to by _pWidSingle.
*(int *)((BYTE *)_pWidSingle + sizeof(WORKID)) = (int)((BYTE *)(_keyVec.GetVector()) - _buf); }
//+---------------------------------------------------------------------------
//
// Member: CEntryBuffer::AddEntry, public
//
// Synopsis: Add entry to buffer
//
// Arguments: [cb] -- size of key
// [key] -- buffer with key
// [pid] -- property id
// [widFake] -- fake work id
// [occ] -- occurrence
//
// History: 28-May-92 KyleP Added single wid detection
// 07-Jun-91 BartoszM Created
//
//----------------------------------------------------------------------------
void CEntryBuffer::AddEntry ( unsigned cb, const BYTE * key, PROPID pid, WORKID widFake, OCCURRENCE occ) { ciAssert( widFake > 0 && widFake <= maxFakeWid ); ciAssert( widFake != widInvalid ); ciAssert( cb > 0 );
_curEntry->SetWid ( widFake ); _curEntry->SetPid ( pid ); _curEntry->SetOcc ( occ ); _curEntry->SetCount ( cb ); memcpy ( _curEntry->GetKeyBuf(), key, cb ); Advance();
if ( *_pWidSingle == 0 ) *_pWidSingle = widFake; else if ( *_pWidSingle != widFake ) *_pWidSingle = widInvalid; }
//+---------------------------------------------------------------------------
//
// Member: CEntryBuffer::Sort, private
//
// Synopsis: sorts the array of keys using a quicksort algorithm
//
// Effects: sorts buffer
//
// History: 22-May-91 Brianb Created
// 05-Jun-91 BartoszM Rewrote it
// 02-Jun-92 KyleP Added wid mapping.
//
//----------------------------------------------------------------------------
void CEntryBuffer::Sort() { //ciDebugOut (( DEB_ITRACE, "sorting\n" ));
unsigned count = Count(); if (count == 0) // nothing to sort
return;
CEntry ** apEntry = _keyVec.GetVector();
// compute length of pointer array; note low key and high key
// are at positions 0, count+1
// Use qucksort above threshold size
if (count > minPartSize) { CIStack stack; unsigned left = 1; unsigned right = count;
// outer loop for sorting partitions
while(TRUE) { // compute median of left, right, (left+right)/2
ciAssert(right >= left + minPartSize);
unsigned median = Median ( apEntry, left, right, (left+right)/2);
Win4Assert( median >= left && median <= right );
// exchange median with left
CEntry * pivot = apEntry[median]; apEntry [median] = apEntry[left]; apEntry [left] = pivot;
// point i at the pivot, j beyond the end of partition
// and sort in between
unsigned i = left; unsigned j = right + 1;
// partition chosen, now burn the candle from both ends
while(TRUE) { // increment i until key >= pivot
do i++; while ( apEntry[i]->Compare( pivot ) < 0);
// decrement j until key <= pivot
do j--; while ( apEntry[j]->Compare( pivot ) > 0);
if ( j <= i ) break;
// swap the elements that are out of order
CEntry* tmp = apEntry[i]; apEntry[i] = apEntry[j]; apEntry[j] = tmp;
} // continue burning
// finally, excange the pivot
apEntry[left] = apEntry[j]; apEntry[j] = pivot;
// entries to the left of j are smaller
// than entries to the right of j
unsigned rsize = right - j; unsigned lsize = j - left;
// Push the larger one on the stack for later sorting
// If any of the partitions is smaller than the
// minimum, skip it and proceed directly with the other one
// if both are too small, pop the stack
if ( rsize > minPartSize ) // right partition big enough
{ if ( lsize >= rsize ) // left even bigger
{ // sort left later
stack.Push ( j - 1 ); stack.Push ( left ); // sort right now
left = j + 1; } else if ( lsize > minPartSize ) // left big enough
{ // sort right later
stack.Push ( right ); stack.Push ( j + 1 ); // sort left now
right = j - 1; } else // left too small
{ // sort the right partition now
left = j + 1; } } // right partition too small
else if ( lsize > minPartSize ) // but left big enough
{ // sort the left partition now
right = j - 1; } else // both are too small
{ if ( stack.Empty() ) break; // we are done!!!
else { // sort the next partition
left = stack.Pop(); right = stack.Pop(); } }
} // keep sorting
} // Unsorted chunks are of the size <= minPartSize
if (minPartSize > 1) InsertSort( apEntry, count);
#if CIDBG == 1
// check to see that array is really sorted. j=0 is a sentinel.
for (unsigned j = 1; j <= count; j++) { Win4Assert( apEntry[j]->Pid() != pidAll );
if ( apEntry[j]->Compare( apEntry[j+1] ) >= 0 ) { CKeyBuf first( apEntry[j]->Pid(), apEntry[j]->GetKeyBuf(), apEntry[j]->Count() );
CKeyBuf second( apEntry[j+1]->Pid(), apEntry[j+1]->GetKeyBuf(), apEntry[j+1]->Count() );
ciDebugOut(( DEB_ERROR, "apEntry[%d]='%*ws' (%p) len=0x%x pid=0x%x wid=0x%x occ=0x%x\n", j, first.StrLen(), first.GetStr(), first.GetStr(), first.StrLen(), first.Pid(), apEntry[j]->Wid(), apEntry[j]->Occ() ));
ciDebugOut(( DEB_ERROR, "apEntry[%d]='%*ws' (%p) len=0x%x pid=0x%x wid=0x%x occ=0x%x\n", j+1, second.StrLen(), second.GetStr(), second.GetStr(), second.StrLen(), second.Pid(), apEntry[j+1]->Wid(), apEntry[j+1]->Occ() ));
Win4Assert( apEntry[j]->Compare( apEntry[j+1] ) != 0 && "Duplicate keys"); Win4Assert( apEntry[j]->Compare( apEntry[j+1] ) < 0 && "Out of order keys"); }
ciAssert( apEntry[j]->Wid() <= maxFakeWid ); } #endif //CIDBG == 1
}
|