966 lines
27 KiB
C++
966 lines
27 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1991 - 1999.
|
||
|
//
|
||
|
// File: CIDIR.CXX
|
||
|
//
|
||
|
// Contents: Persistent directory
|
||
|
//
|
||
|
// Classes: CiDirectory
|
||
|
//
|
||
|
// History: 3-Apr-91 BartoszM Created stub.
|
||
|
// 3-May-96 dlee Don't read it in -- map on-disk data
|
||
|
// 4-Nov-97 dlee Support merge shrink from front
|
||
|
//
|
||
|
// Notes: File format is two initial ULONGs with the count of level 1
|
||
|
// and count of level 2 keys, then the actual level 1 and level
|
||
|
// 2 keys.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <pch.cxx>
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include <mmstrm.hxx>
|
||
|
#include <cidir.hxx>
|
||
|
#include <cistore.hxx>
|
||
|
#include <eventlog.hxx>
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::CiDirectory, public
|
||
|
//
|
||
|
// Synopsis: Open existing or create empty directory
|
||
|
//
|
||
|
// Arguments: [storage] -- Through which streams are opened
|
||
|
// [objectId] -- Wid to open
|
||
|
// [mode] -- Mode to open the stream
|
||
|
//
|
||
|
// History: 13-Jun-91 BartoszM Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
CiDirectory::CiDirectory(
|
||
|
CiStorage & storage,
|
||
|
WORKID objectId,
|
||
|
PStorage::EOpenMode mode ) :
|
||
|
_storage( storage ),
|
||
|
_objectId( objectId ),
|
||
|
_cKeys( 0 ),
|
||
|
_cLevel2Keys( 0 ),
|
||
|
_pbCurrent( 0 ),
|
||
|
_pcKeys( 0 ),
|
||
|
_fReadOnly( PStorage::eOpenForRead == mode )
|
||
|
{
|
||
|
// If opening for create, don't try reading in existing data if the file
|
||
|
// exists since we want to blow it all away later anyway.
|
||
|
|
||
|
if ( PStorage::eCreate != mode )
|
||
|
ReadIn( !_fReadOnly );
|
||
|
} //CiDirectory
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::DoSeekForKeyBuf
|
||
|
//
|
||
|
// Synopsis: Find the key in the array of keys.
|
||
|
//
|
||
|
// Arguments: [key] -- search key
|
||
|
// [aKeys] -- array of keys to search
|
||
|
// [low] -- the index of the lower bound of the search
|
||
|
// [cKeys] -- # of keys over which the search happens
|
||
|
//
|
||
|
// Returns: index of the greatest key <= the search key
|
||
|
//
|
||
|
// History: 5-May-96 dlee Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG CiDirectory::DoSeekForKeyBuf(
|
||
|
const CKeyBuf & key,
|
||
|
CDirectoryKey ** aKeys,
|
||
|
ULONG low,
|
||
|
ULONG cKeys )
|
||
|
{
|
||
|
#if CIDBG == 1
|
||
|
Win4Assert( 0 != cKeys );
|
||
|
ULONG cArray = cKeys;
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
ULONG iHi = low + cKeys - 1;
|
||
|
ULONG iLo = low;
|
||
|
|
||
|
// do a binary search looking for the key
|
||
|
|
||
|
do
|
||
|
{
|
||
|
ULONG cHalf = cKeys / 2;
|
||
|
|
||
|
if ( 0 != cHalf )
|
||
|
{
|
||
|
ULONG cTmp = cHalf - 1 + ( cKeys & 1 );
|
||
|
ULONG iMid = iLo + cTmp;
|
||
|
|
||
|
Win4Assert( iMid < ( low + cArray ) );
|
||
|
|
||
|
if ( aKeys[ iMid ]->IsGreaterThanKeyBuf( key ) )
|
||
|
{
|
||
|
iHi = iMid - 1;
|
||
|
cKeys = cTmp;
|
||
|
}
|
||
|
else if ( ! aKeys[ iMid + 1 ]->IsGreaterThanKeyBuf( key ) )
|
||
|
{
|
||
|
iLo = iMid + 1;
|
||
|
cKeys = cHalf;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return iMid;
|
||
|
}
|
||
|
}
|
||
|
else if ( cKeys > 1 )
|
||
|
{
|
||
|
Win4Assert( ( iLo + 1 ) < ( low + cArray ) );
|
||
|
|
||
|
if ( aKeys[ iLo + 1 ]->IsGreaterThanKeyBuf( key ) )
|
||
|
return iLo;
|
||
|
else
|
||
|
return iLo + 1;
|
||
|
}
|
||
|
else return iLo;
|
||
|
}
|
||
|
while ( TRUE );
|
||
|
|
||
|
Win4Assert(( ! "Invalid doseek function exit point" ));
|
||
|
return 0;
|
||
|
} //DoSeekForKeyBuf
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::Seek
|
||
|
//
|
||
|
// Synopsis: Find bit offset and key of the greatest key less than or
|
||
|
// equal to search key.
|
||
|
// If none is found <= the search key, return the first item.
|
||
|
//
|
||
|
// Arguments: [key] -- search key
|
||
|
// [pKeyInit] -- key found: less or equal to search key.
|
||
|
// only returned if non-zero
|
||
|
// [off] -- bit offset of keyInit
|
||
|
//
|
||
|
// History: 22-Apr-92 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::Seek(
|
||
|
const CKeyBuf & key,
|
||
|
CKeyBuf * pKeyInit,
|
||
|
BitOffset & off )
|
||
|
{
|
||
|
// If a master merge failed, the stream may not be mapped. Map it.
|
||
|
|
||
|
ReMapIfNeeded();
|
||
|
|
||
|
ULONG iKey;
|
||
|
|
||
|
if ( ! key.IsMinKey() )
|
||
|
{
|
||
|
//
|
||
|
// Check if there is no level 2 (ie the dir is being created)
|
||
|
// In the case where there is no level 2, there is no max key
|
||
|
// at the end of the array. That's ok because queries will go
|
||
|
// to the old index for keys greater than the last key in this
|
||
|
// directory.
|
||
|
//
|
||
|
|
||
|
if ( 0 == _cLevel2Keys )
|
||
|
{
|
||
|
iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
|
||
|
Win4Assert( iKey < _cKeys );
|
||
|
Win4Assert( ( iKey == ( _cKeys - 1 ) ) ||
|
||
|
( _aKeys[ iKey + 1 ]->IsGreaterThanKeyBuf( key ) ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iKey = DoSeekForKeyBuf( key, _aLevel2Keys.GetPointer(), 0, _cLevel2Keys );
|
||
|
iKey *= eDirectoryFanOut;
|
||
|
Win4Assert( iKey < _cKeys );
|
||
|
unsigned cKeys = __min( eDirectoryFanOut, _cKeys - iKey );
|
||
|
iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), iKey, cKeys );
|
||
|
Win4Assert( ( 1 == _cKeys ) ||
|
||
|
( iKey < ( _cKeys - 1 ) ) );
|
||
|
Win4Assert( ( 1 == _cKeys) ||
|
||
|
( _aKeys[ iKey + 1 ]->IsGreaterThanKeyBuf( key ) ) );
|
||
|
}
|
||
|
|
||
|
Win4Assert( 0 == iKey ||
|
||
|
!_aKeys[ iKey ]->IsGreaterThanKeyBuf( key ) );
|
||
|
|
||
|
// If the search key is <= the first key, return the min key
|
||
|
|
||
|
if ( ( 0 == iKey ) &&
|
||
|
( ( 0 == _cKeys ) ||
|
||
|
( _aKeys[ 0 ]->IsGreaterThanKeyBuf( key ) ) ) )
|
||
|
{
|
||
|
// the key is less than the first key, return minkey
|
||
|
|
||
|
if ( 0 != pKeyInit )
|
||
|
pKeyInit->FillMin();
|
||
|
|
||
|
off.Init( 0, 0 );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iKey = 0;
|
||
|
}
|
||
|
|
||
|
_aKeys[ iKey ]->Offset( off );
|
||
|
|
||
|
if ( 0 != pKeyInit )
|
||
|
_aKeys[ iKey ]->MakeKeyBuf( *pKeyInit );
|
||
|
} //Seek
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::Seek
|
||
|
//
|
||
|
// Synopsis: Find bit offset and key of the greatest key less than or
|
||
|
// equal to search key.
|
||
|
// If none is found <= the search key, return the first item.
|
||
|
//
|
||
|
// Arguments: [key] -- search key
|
||
|
// [pKeyInit] -- key found: less or equal to search key.
|
||
|
// only returned if non-zero
|
||
|
// [off] -- bit offset of keyInit
|
||
|
//
|
||
|
// History: 22-Apr-92 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::Seek(
|
||
|
const CKey & key,
|
||
|
CKeyBuf * pKeyInit,
|
||
|
BitOffset & off )
|
||
|
{
|
||
|
CKeyBuf keyBuf;
|
||
|
keyBuf = key;
|
||
|
|
||
|
Seek( keyBuf, pKeyInit, off );
|
||
|
} //Seek
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::SeekNext
|
||
|
//
|
||
|
// Synopsis: Find the first key in the next page
|
||
|
//
|
||
|
// Arguments: [key] -- the search key in this page
|
||
|
// [off] -- the offset of the first key
|
||
|
//
|
||
|
// History: 8-Mar-94 t-joshh Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::SeekNext (
|
||
|
const CKeyBuf & key,
|
||
|
CKeyBuf * pKeyInit,
|
||
|
BitOffset & off )
|
||
|
{
|
||
|
ULONG iKey;
|
||
|
|
||
|
if ( _cKeys > 0 && ! key.IsMinKey() )
|
||
|
{
|
||
|
if ( 0 == _cLevel2Keys )
|
||
|
{
|
||
|
iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iKey = DoSeekForKeyBuf( key, _aLevel2Keys.GetPointer(), 0, _cLevel2Keys );
|
||
|
iKey *= eDirectoryFanOut;
|
||
|
Win4Assert( iKey < _cKeys );
|
||
|
unsigned cKeys = __min( eDirectoryFanOut, _cKeys - iKey );
|
||
|
iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), iKey, cKeys );
|
||
|
}
|
||
|
|
||
|
// this IS a seek next
|
||
|
iKey++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iKey = 0;
|
||
|
}
|
||
|
|
||
|
Win4Assert ( iKey < _cKeys );
|
||
|
|
||
|
_aKeys[ iKey ]->Offset( off );
|
||
|
|
||
|
if ( 0 != pKeyInit )
|
||
|
_aKeys[ iKey ]->MakeKeyBuf( *pKeyInit );
|
||
|
|
||
|
#if CIDBG == 1
|
||
|
_aKeys[ iKey ]->MakeKeyBuf( _keyLast );
|
||
|
#endif // CIDBG == 1
|
||
|
} //SeekNext
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::Add
|
||
|
//
|
||
|
// Synopsis: Adds entry to directory
|
||
|
//
|
||
|
// Arguments: [posKey] -- offset of key in file
|
||
|
// [key] -- key to add
|
||
|
//
|
||
|
// History: 22-Apr-92 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::Add(
|
||
|
BitOffset & posKey,
|
||
|
const CKeyBuf & key )
|
||
|
{
|
||
|
Win4Assert( !_fReadOnly );
|
||
|
Win4Assert( &key != 0 );
|
||
|
|
||
|
ciDebugOut(( DEB_PDIR,
|
||
|
"PDir::Add %.*ws at %d:%d\n",
|
||
|
key.StrLen(),
|
||
|
key.GetStr(),
|
||
|
posKey.Page(),
|
||
|
posKey.Offset() ));
|
||
|
|
||
|
LokWriteKey( key, posKey );
|
||
|
|
||
|
#if CIDBG==1
|
||
|
_bitOffLastAdded = posKey;
|
||
|
#endif // CIDBG
|
||
|
} //Add
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::ReMapIfNeeded, private
|
||
|
//
|
||
|
// Synopsis: ReMaps the directory stream if it isn't mapped due to a
|
||
|
// failure to extend or map the directory stream during a
|
||
|
// shrink from front master merge.
|
||
|
//
|
||
|
// History: 6-Aug-98 dlee Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::ReMapIfNeeded()
|
||
|
{
|
||
|
// If it's already mapped, we're set
|
||
|
|
||
|
if ( 0 != _streamBuf.Get() )
|
||
|
return;
|
||
|
|
||
|
Win4Assert( !_fReadOnly );
|
||
|
|
||
|
// Map the file
|
||
|
|
||
|
_stream->MapAll( _streamBuf );
|
||
|
|
||
|
// Rebase pointers if the new stream pointer is different
|
||
|
|
||
|
if ( 0 == _cKeys )
|
||
|
{
|
||
|
_pcKeys = (ULONG *) _streamBuf.Get();
|
||
|
_pbCurrent = (BYTE *) ( _pcKeys + 2 );
|
||
|
}
|
||
|
else if ( _pcKeys != (ULONG *) _streamBuf.Get() )
|
||
|
{
|
||
|
BYTE *pbOldBase = (BYTE *) _pcKeys;
|
||
|
BYTE *pbNewBase = (BYTE *) _streamBuf.Get();
|
||
|
UINT_PTR cb = _pbCurrent - pbOldBase;
|
||
|
|
||
|
for ( unsigned i = 0; i < _cKeys; i++ )
|
||
|
{
|
||
|
BYTE * p = (BYTE *) _aKeys[ i ];
|
||
|
p = p - pbOldBase + pbNewBase;
|
||
|
_aKeys[ i ] = (CDirectoryKey *) p;
|
||
|
}
|
||
|
|
||
|
_pcKeys = (ULONG *) _streamBuf.Get();
|
||
|
_pbCurrent = pbNewBase + cb;
|
||
|
}
|
||
|
} //ReMapIfNeeded
|
||
|
|
||
|
#if CIDBG == 1 // for testing failure path
|
||
|
BOOL g_fFailDirectoryMergeExtend = FALSE;
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::GrowIfNeeded, private
|
||
|
//
|
||
|
// Synopsis: Grows the file if necessary and maps the new section
|
||
|
//
|
||
|
// Arguments: [cbToGrow] -- # of bytes to append to the file
|
||
|
//
|
||
|
// History: 8-May-96 dlee Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::GrowIfNeeded(
|
||
|
unsigned cbToGrow )
|
||
|
{
|
||
|
if ( ( _pbCurrent + cbToGrow ) >=
|
||
|
( (BYTE *) _streamBuf.Get() + _streamBuf.Size() ) )
|
||
|
{
|
||
|
BYTE * pbOldBase = (BYTE *) _streamBuf.Get();
|
||
|
unsigned cbOld = (unsigned)(_pbCurrent - (BYTE *) _streamBuf.Get());
|
||
|
|
||
|
_stream->Unmap( _streamBuf );
|
||
|
ULONG sizeNew = CommonPageRound( cbOld + cbToGrow );
|
||
|
_stream->SetSize( _storage, sizeNew );
|
||
|
|
||
|
#if CIDBG == 1
|
||
|
// For testing failure at this point...
|
||
|
|
||
|
if ( g_fFailDirectoryMergeExtend )
|
||
|
{
|
||
|
g_fFailDirectoryMergeExtend = FALSE;
|
||
|
THROW( CException( E_OUTOFMEMORY ) );
|
||
|
}
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
_stream->MapAll( _streamBuf );
|
||
|
_pcKeys = (ULONG *) _streamBuf.Get();
|
||
|
_pbCurrent = (BYTE*) _streamBuf.Get() + cbOld;
|
||
|
|
||
|
//
|
||
|
// Rebase all the pointers in the key array if the new base
|
||
|
// address of the mapping is different than the old one.
|
||
|
//
|
||
|
|
||
|
BYTE * pbNewBase = (BYTE *) _streamBuf.Get();
|
||
|
|
||
|
if ( pbOldBase != pbNewBase )
|
||
|
{
|
||
|
for ( unsigned i = 0; i < _cKeys; i++ )
|
||
|
{
|
||
|
BYTE * p = (BYTE *) _aKeys[ i ];
|
||
|
p = p - pbOldBase + pbNewBase;
|
||
|
_aKeys[ i ] = (CDirectoryKey *) p;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ciDebugOut(( DEB_ITRACE, "no cidir rebasing needed\n" ));
|
||
|
}
|
||
|
|
||
|
// Toss the .dir file pages out of the working set
|
||
|
|
||
|
_streamBuf.PurgeFromWorkingSet( -1, -1 );
|
||
|
}
|
||
|
} //GrowIfNeeded
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::LokWriteKey, private
|
||
|
//
|
||
|
// Synopsis: Adds entry to directory
|
||
|
//
|
||
|
// Arguments: [key] -- key to add
|
||
|
// [bitOffset] -- offset of key in file
|
||
|
//
|
||
|
// History: 02-May-94 DwightKr Created
|
||
|
//
|
||
|
// Notes: Writes a single key to the mapped buffer.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
inline void CiDirectory::LokWriteKey(
|
||
|
const CKeyBuf & key,
|
||
|
BitOffset & bitOffset )
|
||
|
{
|
||
|
// If this is the first key, open the stream for writing
|
||
|
|
||
|
if ( 0 == _cKeys )
|
||
|
{
|
||
|
// Close any existing stream from a failed merge
|
||
|
|
||
|
LokClose();
|
||
|
|
||
|
_stream.Set( _storage.QueryExistingDirStream ( _objectId, TRUE ) );
|
||
|
|
||
|
if ( 0 == _stream.GetPointer() || !_stream->Ok() )
|
||
|
{
|
||
|
_stream.Free();
|
||
|
_stream.Set( _storage.QueryNewDirStream ( _objectId ) );
|
||
|
}
|
||
|
|
||
|
if ( ( 0 == _stream.GetPointer() ) || !_stream->Ok() )
|
||
|
THROW( CException ( STATUS_NO_MEMORY ) );
|
||
|
|
||
|
_stream->SetSize( _storage, COMMON_PAGE_SIZE );
|
||
|
_stream->MapAll( _streamBuf );
|
||
|
|
||
|
_pcKeys = (ULONG *) _streamBuf.Get();
|
||
|
_pbCurrent = (BYTE*) ( _pcKeys + 2 );
|
||
|
|
||
|
// Make sure the stream is in good shape in case we stop and restart
|
||
|
// the merge.
|
||
|
|
||
|
_pcKeys[0] = 0;
|
||
|
_pcKeys[1] = 0;
|
||
|
_stream->Flush( _streamBuf, 2 * sizeof ULONG, TRUE );
|
||
|
}
|
||
|
|
||
|
Win4Assert( key.Count() <= 0xff );
|
||
|
BYTE count = (BYTE)key.Count();
|
||
|
|
||
|
unsigned size = CDirectoryKey::ComputeSize( count, key.Pid() );
|
||
|
GrowIfNeeded( size );
|
||
|
|
||
|
CDirectoryKey *pkey = new( _pbCurrent ) CDirectoryKey;
|
||
|
|
||
|
// write the key
|
||
|
|
||
|
pkey->Write( count, bitOffset, key.Pid(), key.GetBuf() );
|
||
|
|
||
|
if ( _cKeys >= _aKeys.Count() )
|
||
|
_aKeys.ReSize( __max( 32, _cKeys * 2 + 1 ) );
|
||
|
|
||
|
_aKeys[ _cKeys ] = pkey;
|
||
|
|
||
|
_pbCurrent += size;
|
||
|
|
||
|
_cKeys++;
|
||
|
} //LokWriteKey
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::LokBuildDir, public
|
||
|
//
|
||
|
// Synopsis: Builds the directory tree correspdong to the leaf pages
|
||
|
//
|
||
|
// Arguments: [maxKey] -- largest key to add to directory
|
||
|
//
|
||
|
// History: ??-???-?? ???????? Created
|
||
|
// 02-May-94 DwightKr Added maxKey argument
|
||
|
//
|
||
|
// Notes: Writes keys from the beginning of the array up to and including
|
||
|
// maxKey (which is usually the splitKey). This will result in
|
||
|
// a directory that contains entries upto and including maxKey.
|
||
|
// It is certainly possible that the key array has keys past
|
||
|
// maxKey, but we don't want to add them to the directory since
|
||
|
// maxKey represents the largest key that has persistently
|
||
|
// written to disk during a master merge.
|
||
|
//
|
||
|
// The above would matter if downlevel master merges were
|
||
|
// atomic throughout. If they ever become atomic, we
|
||
|
// need to truncate the file after this key.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
void CiDirectory::LokBuildDir(
|
||
|
const CKeyBuf & maxKey )
|
||
|
{
|
||
|
ciDebugOut(( DEB_PDIR, "PDir::LokBuildDir %d\n", _cKeys ));
|
||
|
|
||
|
CKeyBuf maxKeyValue;
|
||
|
maxKeyValue.FillMax();
|
||
|
|
||
|
//
|
||
|
// Write out a maximum key at the end.
|
||
|
// The offset for the maximum key is not actually used my anyone,
|
||
|
// since the directory is structured to find a key <= the desired
|
||
|
// key. If someone generates a query for maxKey then everything
|
||
|
// will be returned. The offset of maxKey is therefore arbitrary.
|
||
|
//
|
||
|
|
||
|
BitOffset maxBitOffset;
|
||
|
maxBitOffset.Init( 0, 0 );
|
||
|
LokWriteKey( maxKeyValue, maxBitOffset );
|
||
|
|
||
|
//
|
||
|
// Write the keys in the level 2 tree.
|
||
|
// Write every DirectoryFanOut'th, so we can do a search on this Level2
|
||
|
// first, and reduce the working set.
|
||
|
//
|
||
|
|
||
|
_cLevel2Keys = 0;
|
||
|
|
||
|
CDirectoryKey *pkey = _aKeys[ 0 ];
|
||
|
|
||
|
for ( unsigned x = 0; x < _cKeys; x++ )
|
||
|
{
|
||
|
if ( 0 == ( x & ( eDirectoryFanOut - 1 ) ) )
|
||
|
{
|
||
|
unsigned cb = pkey->Size();
|
||
|
unsigned oKey = (unsigned)((BYTE *) pkey - (BYTE *) _streamBuf.Get());
|
||
|
GrowIfNeeded( cb );
|
||
|
pkey = (CDirectoryKey *) ( (BYTE *) _streamBuf.Get() + oKey );
|
||
|
RtlCopyMemory( _pbCurrent, pkey, cb );
|
||
|
_pbCurrent += cb;
|
||
|
_cLevel2Keys++;
|
||
|
}
|
||
|
|
||
|
pkey = pkey->NextKey();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write another max-key at the end. Decrement the # of keys since it
|
||
|
// is incorrectly incremented in LokWriteKey()
|
||
|
//
|
||
|
|
||
|
LokWriteKey( maxKeyValue, maxBitOffset );
|
||
|
_cLevel2Keys++;
|
||
|
_cKeys--;
|
||
|
|
||
|
// write the # of keys in the file and the # of keys in level 2.
|
||
|
|
||
|
_pcKeys[0] = _cKeys;
|
||
|
_pcKeys[1] = _cLevel2Keys;
|
||
|
|
||
|
// remember the size of the file used and release the mapping
|
||
|
|
||
|
unsigned cbFile = (unsigned)(_pbCurrent - (BYTE *) _streamBuf.Get());
|
||
|
|
||
|
_stream->Flush( _streamBuf, cbFile, TRUE );
|
||
|
_stream->Unmap( _streamBuf );
|
||
|
|
||
|
// truncate and close the file
|
||
|
|
||
|
_stream->SetSize( _storage, cbFile );
|
||
|
_stream->Close();
|
||
|
_stream.Free();
|
||
|
|
||
|
_cKeys = 0;
|
||
|
|
||
|
// read the directory back in, in read-only mode
|
||
|
|
||
|
ReadIn( FALSE );
|
||
|
} //LokBuildDir
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CiDirectory::ReadIn
|
||
|
//
|
||
|
// Synopsis: Load directory from storage
|
||
|
//
|
||
|
// Arguments: [fWrite] -- If TRUE, we're recovering from a stopped master
|
||
|
// merge, either clean or dirty. This can be TRUE
|
||
|
// even on read only catalogs if a MM is paused.
|
||
|
//
|
||
|
// History: 17-Feb-1994 KyleP Added header
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::ReadIn( BOOL fWrite )
|
||
|
{
|
||
|
_stream.Set( _storage.QueryExistingDirStream( _objectId, fWrite ) );
|
||
|
|
||
|
if ( ( 0 == _stream.GetPointer() ) || !_stream->Ok() )
|
||
|
{
|
||
|
if ( fWrite )
|
||
|
{
|
||
|
// New index; it'll be created later
|
||
|
|
||
|
_stream.Free();
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Win4Assert( !"Corrupt directory" );
|
||
|
_storage.ReportCorruptComponent( L"IndexDirectory1" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_stream->MapAll( _streamBuf );
|
||
|
|
||
|
BYTE * pbBuf = (BYTE *) _streamBuf.Get();
|
||
|
BYTE * pbEnd = pbBuf + _streamBuf.Size();
|
||
|
|
||
|
if ( _streamBuf.Size() <= ( 2 * sizeof ULONG ) )
|
||
|
{
|
||
|
Win4Assert( !"Corrupt directory" );
|
||
|
_storage.ReportCorruptComponent( L"IndexDirectory2" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
|
||
|
// The count of keys is stored in the first 4 bytes
|
||
|
// The count of level 2 keys is stored in the next 4 bytes
|
||
|
// There can be 0 level 2 keys if we stopped during the middle
|
||
|
// of a master merge.
|
||
|
// An empty index has a directory with 1 level 1 max key and 2 level
|
||
|
// 2 max keys.
|
||
|
|
||
|
_pcKeys = (ULONG *) pbBuf;
|
||
|
_cKeys = _pcKeys[0];
|
||
|
_cLevel2Keys = _pcKeys[1];
|
||
|
|
||
|
if ( ( !fWrite && 0 == _cKeys ) ||
|
||
|
( _cLevel2Keys > ( _cKeys + 1 ) ) ||
|
||
|
( !fWrite && ( 0 == _cLevel2Keys ) ) )
|
||
|
{
|
||
|
Win4Assert( !"Corrupt directory" );
|
||
|
_storage.ReportCorruptComponent( L"IndexDirectory3" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
|
||
|
pbBuf += ( 2 * sizeof ULONG );
|
||
|
|
||
|
// Store pointers to each of the keys
|
||
|
|
||
|
_aKeys.Free();
|
||
|
_aKeys.Init( _cKeys );
|
||
|
|
||
|
CDirectoryKey *pkey = new( pbBuf ) CDirectoryKey;
|
||
|
|
||
|
for ( unsigned x = 0; x < _cKeys; x++ )
|
||
|
{
|
||
|
if ( (BYTE *) pkey >= pbEnd )
|
||
|
{
|
||
|
Win4Assert( !"Corrupt directory" );
|
||
|
_storage.ReportCorruptComponent( L"IndexDirectory5" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
|
||
|
_aKeys[ x ] = pkey;
|
||
|
pkey = pkey->NextKey();
|
||
|
}
|
||
|
|
||
|
_aLevel2Keys.Free();
|
||
|
|
||
|
//
|
||
|
// Level2 keys can exist when fWrite is TRUE if we failed a master
|
||
|
// merge after building the level2 directory. It's likely the failure
|
||
|
// is in recording the transaction that the merge completed.
|
||
|
//
|
||
|
|
||
|
if ( fWrite )
|
||
|
_cLevel2Keys = 0;
|
||
|
else if ( 0 != _cLevel2Keys )
|
||
|
{
|
||
|
_aLevel2Keys.Init( _cLevel2Keys );
|
||
|
|
||
|
for ( x = 0; x < _cLevel2Keys; x++ )
|
||
|
{
|
||
|
if ( (BYTE *) pkey >= pbEnd )
|
||
|
{
|
||
|
Win4Assert( !"Corrupt directory" );
|
||
|
_storage.ReportCorruptComponent( L"IndexDirectory6" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
|
||
|
_aLevel2Keys[ x ] = pkey;
|
||
|
pkey = pkey->NextKey();
|
||
|
}
|
||
|
|
||
|
// If we didn't read as many keys as expected, the file is corrupt.
|
||
|
// This is only true if the directory has level 2 keys, since the
|
||
|
// file may have bogus data at the end if we are in the middle of
|
||
|
// a master merge.
|
||
|
|
||
|
if ( (BYTE *) pkey != pbEnd )
|
||
|
{
|
||
|
ciDebugOut(( DEB_WARN, "pkey, pbEnd: 0x%x, 0x%x\n", pkey, pbEnd ));
|
||
|
Win4Assert( !"Corrupt directory" );
|
||
|
_storage.ReportCorruptComponent( L"IndexDirectory4" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Initialize _pbCurrent, so we can restart a failed master merge
|
||
|
|
||
|
if ( fWrite )
|
||
|
_pbCurrent = (BYTE *) pkey;
|
||
|
|
||
|
// Toss the .dir file pages out of the working set
|
||
|
|
||
|
_streamBuf.PurgeFromWorkingSet( -1, -1 );
|
||
|
|
||
|
// toss the level1 directory from the working set
|
||
|
|
||
|
VirtualUnlock( _aKeys.GetPointer(), _aKeys.SizeOf() );
|
||
|
|
||
|
_fReadOnly = !fWrite;
|
||
|
} //ReadIn
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::Close, public
|
||
|
//
|
||
|
// Effects: Closes the directory. Usually called when a merge fails.
|
||
|
//
|
||
|
// History: 21-Apr-92 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::Close()
|
||
|
{
|
||
|
LokClose();
|
||
|
} //Close
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::LokClose, public
|
||
|
//
|
||
|
// Effects: Closes the directory. Usually called when a merge fails.
|
||
|
//
|
||
|
// History: 18-Nov-97 dlee Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::LokClose()
|
||
|
{
|
||
|
if ( !_stream.IsNull() )
|
||
|
{
|
||
|
// The _stream object can exist but not have a file open at this
|
||
|
// point if a merge failed, so only unmap what is mapped.
|
||
|
|
||
|
if ( ( _stream->Ok() ) && ( 0 != _streamBuf.Get() ) )
|
||
|
_stream->Unmap( _streamBuf );
|
||
|
|
||
|
_stream.Free();
|
||
|
}
|
||
|
} //LokClose
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: DeleteKeysAfter
|
||
|
//
|
||
|
// Synopsis: Deletes all keys in the tree after the specified key.
|
||
|
//
|
||
|
// Arguments: [key] - The key after which all keys in the tree must be
|
||
|
// deleted.
|
||
|
//
|
||
|
// History: 13-Nov-97 dlee Created
|
||
|
//
|
||
|
// Notes: Note that "key" is NOT deleted - only keys after "key" are
|
||
|
// deleted.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::DeleteKeysAfter( const CKeyBuf & key )
|
||
|
{
|
||
|
//
|
||
|
// If we're restarting a failed master merge after LokBuildDir
|
||
|
// succeeded, we have to close re-open the stream for write.
|
||
|
//
|
||
|
|
||
|
if ( _fReadOnly )
|
||
|
{
|
||
|
LokClose();
|
||
|
ReadIn( TRUE );
|
||
|
}
|
||
|
else if ( 0 != _cKeys )
|
||
|
ReMapIfNeeded();
|
||
|
|
||
|
// Now, wipe the level 2, since we're still building level 1
|
||
|
|
||
|
_cLevel2Keys = 0;
|
||
|
_aLevel2Keys.Free();
|
||
|
|
||
|
// Truncate level 1 at keys <= key
|
||
|
|
||
|
if ( 0 != _cKeys )
|
||
|
{
|
||
|
// use the tree to find the greatest key <= key
|
||
|
|
||
|
ULONG iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
|
||
|
|
||
|
//
|
||
|
// Move to the next key which must be > the seek key, but may be
|
||
|
// beyond the # of keys in the directory.
|
||
|
//
|
||
|
|
||
|
iKey++;
|
||
|
|
||
|
if ( iKey < _cKeys )
|
||
|
{
|
||
|
Win4Assert( _aKeys[ iKey ]->IsGreaterThanKeyBuf( key ) );
|
||
|
_cKeys = iKey;
|
||
|
_pbCurrent = (BYTE *) _aKeys[iKey];
|
||
|
}
|
||
|
}
|
||
|
} //DeleteKeysAfter
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: LokFlushDir
|
||
|
//
|
||
|
// Synopsis: Flushes the current state of the directory, up to and
|
||
|
// including the key specified. Called at checkpoints during
|
||
|
// a master merge. There may be some keys after the key
|
||
|
// specified. Leave them in the file, but don't include them
|
||
|
// in the count of keys.
|
||
|
//
|
||
|
// Arguments: [key] - The key before which and including is flushed
|
||
|
//
|
||
|
// History: 10-Aug-98 dlee Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::LokFlushDir( const CKeyBuf & key )
|
||
|
{
|
||
|
Win4Assert( !_fReadOnly );
|
||
|
Win4Assert( 0 == _cLevel2Keys );
|
||
|
|
||
|
// Truncate level 1 at keys <= key
|
||
|
|
||
|
if ( 0 != _cKeys )
|
||
|
{
|
||
|
ReMapIfNeeded();
|
||
|
|
||
|
// use the tree to find the greatest key <= key
|
||
|
|
||
|
ULONG iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
|
||
|
|
||
|
_pcKeys[0] = iKey + 1;
|
||
|
_pcKeys[1] = 0;
|
||
|
_streamBuf.Flush( TRUE );
|
||
|
}
|
||
|
} //LokFlushDir
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CiDirectory::MakeBackupCopy
|
||
|
//
|
||
|
// Synopsis: Creates a backup of the persistent directory using the
|
||
|
// storage provided.
|
||
|
//
|
||
|
// Arguments: [storage] - Destination storage
|
||
|
// [progressTracker] - Track progress and aborts.
|
||
|
//
|
||
|
// History: 3-18-97 srikants Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CiDirectory::MakeBackupCopy( PStorage & storage,
|
||
|
PSaveProgressTracker & progressTracker )
|
||
|
{
|
||
|
CiStorage & dstStorage = *((CiStorage *)&storage);
|
||
|
|
||
|
XPtr<PMmStream> dstStream;
|
||
|
CMmStreamBuf dstStreamBuf;
|
||
|
|
||
|
dstStream.Set( dstStorage.QueryExistingDirStream( _objectId, TRUE ) );
|
||
|
|
||
|
if ( 0 == dstStream.GetPointer() || !dstStream->Ok() )
|
||
|
{
|
||
|
dstStream.Free();
|
||
|
dstStream.Set( dstStorage.QueryNewDirStream( _objectId ) );
|
||
|
}
|
||
|
|
||
|
Win4Assert( 0 != _stream.GetPointer() );
|
||
|
|
||
|
ULONG cb = _streamBuf.Size();
|
||
|
|
||
|
dstStream->SetSize( dstStorage, cb );
|
||
|
dstStream->MapAll( dstStreamBuf );
|
||
|
|
||
|
RtlCopyMemory( dstStreamBuf.Get(), _streamBuf.Get(), cb );
|
||
|
|
||
|
dstStream->Flush( dstStreamBuf, cb );
|
||
|
dstStream->Unmap( dstStreamBuf );
|
||
|
} //MakeBackupCopy
|
||
|
|