|
|
//***************************************************************************
//
// BTR.CPP
//
// WMI disk-based B-tree implementation for repository index
//
// raymcc 15-Oct-00 Prepared for Whistler Beta 2 to reduce file count
//
//***************************************************************************
#include <windows.h>
#include <stdio.h>
#include <wbemcomn.h>
#include <reposit.h>
#include <stdlib.h>
#include <arena.h>
#include <math.h>
#include <btr.h>
#define MAX_WORD_VALUE 0xFFFF
#define MAX_TOKENS_PER_KEY 32
#define MAX_FLUSH_INTERVAL 4000
//#define MAX_PAGE_HISTORY 1024
/*
Notes:
(a) Modify allocators to special case for page-size (b) Modify WriteIdxPage to not rewrite if no deltas (c) ERROR_PATH_NOT_FOUND if starting enum has no presence; GPF presently (d) Do a history of page hits and see if caching would be helpful
*/
//static WORD History[MAX_PAGE_HISTORY] = {0};
LONG g_lAllocs = 0;
//***************************************************************************
//
// _BtrMemAlloc
//
//***************************************************************************
// ok
LPVOID WINAPI _BtrMemAlloc( SIZE_T dwBytes // number of bytes to allocate
) { // Lookaside for items of page size, default array size, default
// string pool size
g_lAllocs++; return HeapAlloc(CWin32DefaultArena::GetArenaHeap(), HEAP_ZERO_MEMORY, dwBytes); }
//***************************************************************************
//
// _BtrMemReAlloc
//
//***************************************************************************
// ok
LPVOID WINAPI _BtrMemReAlloc( LPVOID pOriginal, DWORD dwNewBytes ) { return HeapReAlloc(CWin32DefaultArena::GetArenaHeap(), HEAP_ZERO_MEMORY, pOriginal, dwNewBytes); }
//***************************************************************************
//
// _BtrMemFree
//
//***************************************************************************
// ok
BOOL WINAPI _BtrMemFree(LPVOID pMem) { if (pMem == 0) return TRUE; g_lAllocs--; return HeapFree(CWin32DefaultArena::GetArenaHeap(), 0, pMem); }
//***************************************************************************
//
// CPageSource::CPageSource
//
//***************************************************************************
// ok
CPageSource::CPageSource() { m_dwPageSize = 0; m_hFile = INVALID_HANDLE_VALUE; m_dwTotalPages = 0; m_dwLogicalRoot = 0; m_dwNextFreePage = 0; m_dwLastFlushTime = GetCurrentTime(); }
//***************************************************************************
//
// CPageSource::CPageSource
//
//***************************************************************************
// ok
CPageSource::~CPageSource() { }
//***************************************************************************
//
// CPageSource::Shutdown
//
//***************************************************************************
// ok
DWORD CPageSource::Shutdown(DWORD dwShutDownFlags) { #ifdef A51_STAGE_BELOW_INDEX
#else
CloseHandle(m_hFile); #endif
m_dwPageSize = 0; m_hFile = INVALID_HANDLE_VALUE; m_dwTotalPages = 0; m_dwLogicalRoot = 0; m_dwNextFreePage = 0;
return NO_ERROR; }
//***************************************************************************
//
// CPageSource::WriteAdminPage
//
// Rewrites the admin page. There is no need to update the pagesize,
// version, etc.
//
//***************************************************************************
// ok
DWORD CPageSource::WriteAdminPage() { LPDWORD pPageZero = 0; DWORD dwRes = GetPage(0, (LPVOID *) &pPageZero); if (dwRes) return dwRes;
pPageZero[OFFSET_LOGICAL_ROOT] = m_dwLogicalRoot; pPageZero[OFFSET_TOTAL_PAGES] = m_dwTotalPages; pPageZero[OFFSET_FREE_LIST_ROOT] = m_dwNextFreePage;
dwRes = PutPage(pPageZero, PAGE_TYPE_ADMIN); _BtrMemFree(pPageZero); return dwRes; }
//***************************************************************************
//
// CPageSource::SetRootPage
//
//***************************************************************************
//
DWORD CPageSource::SetRootPage(DWORD dwNewRoot) { m_dwLogicalRoot = dwNewRoot; return WriteAdminPage(); }
//***************************************************************************
//
// CPageSource::Init
//
// The real "constructor" which opens the file
//
//***************************************************************************
// ok
DWORD CPageSource::Init( DWORD dwPageSize, LPWSTR pszFilename #ifdef A51_STAGE_BELOW_INDEX
, CAbstractFileSource* pSource #endif
) { DWORD dwLastError = 0;
m_dwPageSize = dwPageSize; DWORD dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
m_hFile = CreateFileW(pszFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, dwFileFlags, 0); dwLastError = GetLastError();
if (m_hFile == INVALID_HANDLE_VALUE) { if (dwLastError != ERROR_FILE_NOT_FOUND) return dwLastError;
// If here, we have to create a new file.
m_hFile = CreateFileW(pszFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, dwFileFlags, 0);
if (m_hFile == INVALID_HANDLE_VALUE) return GetLastError();
#ifdef A51_STAGE_BELOW_INDEX
long lRes = pSource->Register(m_hFile, A51_INDEX_FILE_ID, true, &m_pFile); if(lRes != ERROR_SUCCESS) { CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; return lRes; } #endif
// Set up initial two pages
Setup(); } else { // File exists. Verify that the file is an integral number of pages based
// on the specified page size.
DWORD dwLen = SetFilePointer(m_hFile, 0, 0, FILE_END); if (dwLen % dwPageSize != 0) { CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; return ERROR_BAD_FORMAT; }
#ifdef A51_STAGE_BELOW_INDEX
long lRes = pSource->Register(m_hFile, A51_INDEX_FILE_ID, true, &m_pFile); if(lRes != ERROR_SUCCESS) { CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; return lRes; } #endif
}
return ReadAdminPage(); }
//***************************************************************************
//
// CPageSource::ReadAdminPage
//
//***************************************************************************
// ok
DWORD CPageSource::ReadAdminPage() { LPDWORD pPageZero = 0; DWORD dwRes = 0;
dwRes = GetPage(0, (LPVOID *) &pPageZero); if (dwRes) return dwRes;
// Grab useful info from these amazing admin pages...
m_dwLogicalRoot = pPageZero[OFFSET_LOGICAL_ROOT]; m_dwTotalPages = pPageZero[OFFSET_TOTAL_PAGES]; m_dwNextFreePage = pPageZero[OFFSET_FREE_LIST_ROOT];
if (m_dwPageSize != pPageZero[OFFSET_PAGE_SIZE]) dwRes = ERROR_BAD_FORMAT; else if (pPageZero[OFFSET_IMPL_VERSION] != const_CurrentVersion) dwRes = ERROR_BAD_FORMAT; else dwRes = NO_ERROR;
_BtrMemFree(pPageZero);
return dwRes; }
//***************************************************************************
//
// CPageSource::Setup
//
// Sets up the 0th page (Admin page)
//
//***************************************************************************
// ok
DWORD CPageSource::Setup() { DWORD dwRes;
// First two pages, admin & free list root
LPDWORD pPageZero = (LPDWORD) _BtrMemAlloc(m_dwPageSize);
if (pPageZero == 0) { dwRes = ERROR_NOT_ENOUGH_MEMORY; goto Exit; }
memset(pPageZero, 0, m_dwPageSize);
// Map the page
pPageZero[OFFSET_PAGE_TYPE] = PAGE_TYPE_ADMIN; pPageZero[OFFSET_PAGE_ID] = 0; pPageZero[OFFSET_NEXT_PAGE] = 0;
pPageZero[OFFSET_LOGICAL_ROOT] = 0; pPageZero[OFFSET_FREE_LIST_ROOT] = 0; pPageZero[OFFSET_TOTAL_PAGES] = 1; pPageZero[OFFSET_PAGE_SIZE] = m_dwPageSize; pPageZero[OFFSET_IMPL_VERSION] = const_CurrentVersion;
// Write it out
dwRes = PutPage(pPageZero, PAGE_TYPE_ADMIN); if (dwRes) goto Exit;
m_dwTotalPages = 1;
Exit: _BtrMemFree(pPageZero);
return dwRes; }
//***************************************************************************
//
// CPageSource::Dump
//
// Debug helper
//
//***************************************************************************
// ok
void CPageSource::Dump(FILE *f) { SetFilePointer(m_hFile, 0, 0, FILE_BEGIN); LPDWORD pPage = (LPDWORD) new BYTE[m_dwPageSize]; DWORD dwPage = 0; DWORD dwTotalKeys = 0;
fprintf(f, "---BEGIN PAGE SOURCE DUMP---\n"); fprintf(f, "In memory part:\n"); fprintf(f, " m_dwPageSize = %d (0x%X)\n", m_dwPageSize, m_dwPageSize); fprintf(f, " m_hFile = 0x%p\n", m_hFile); fprintf(f, " m_dwNextFreePage = %d\n", m_dwNextFreePage); fprintf(f, " m_dwTotalPages = %d\n", m_dwTotalPages); fprintf(f, " m_dwLogicalRoot = %d\n", m_dwLogicalRoot); fprintf(f, "---\n");
DWORD dwTotalFree = 0; DWORD dwOffs = 0;
while (1) { DWORD dwRead = 0; BOOL bRes = ReadFile(m_hFile, pPage, m_dwPageSize, &dwRead, 0); if (dwRead != m_dwPageSize) break;
fprintf(f, "Dump of page %d:\n", dwPage++); fprintf(f, " Page type = 0x%X", pPage[OFFSET_PAGE_TYPE]);
if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_IMPOSSIBLE) fprintf(f, " PAGE_TYPE_IMPOSSIBLE\n");
if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_DELETED) { fprintf(f, " PAGE_TYPE_DELETED\n"); fprintf(f, " <page num check = %d>\n", pPage[1]); fprintf(f, " <next free page = %d>\n", pPage[2]); dwTotalFree++; }
if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_ACTIVE) { fprintf(f, " PAGE_TYPE_ACTIVE\n"); fprintf(f, " <page num check = %d>\n", pPage[1]);
SIdxKeyTable *pKT = 0; DWORD dwKeys = 0; DWORD dwRes = SIdxKeyTable::Create(pPage, &pKT); if (dwRes == 0) { pKT->Dump(f, &dwKeys); pKT->Release(); dwTotalKeys += dwKeys; } else { fprintf(f, "<INVALID Page Decode>\n"); } }
if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_ADMIN) { fprintf(f, " PAGE_TYPE_ADMIN\n"); fprintf(f, " Page Num = %d\n", pPage[1]); fprintf(f, " Next Page = %d\n", pPage[2]); fprintf(f, " Logical Root = %d\n", pPage[3]); fprintf(f, " Free List Root = %d\n", pPage[4]); fprintf(f, " Total Pages = %d\n", pPage[5]); fprintf(f, " Page Size = %d (0x%X)\n", pPage[6], pPage[6]); fprintf(f, " Impl Version = 0x%X\n", pPage[7]); } }
delete [] pPage;
fprintf(f, "Total free pages detected by scan = %d\n", dwTotalFree); fprintf(f, "Total active keys = %d\n", dwTotalKeys); fprintf(f, "---END PAGE DUMP---\n"); }
//***************************************************************************
//
// CPageSource::GetPage
//
// Reads an existing page; does not support seeking beyond end-of-file
//
//***************************************************************************
// ok
DWORD CPageSource::GetPage( DWORD dwPage, LPVOID *pPage ) { DWORD dwRes;
if (pPage == 0) return ERROR_INVALID_PARAMETER;
// Allocate some memory
LPVOID pMem = _BtrMemAlloc(m_dwPageSize); if (!pMem) return ERROR_NOT_ENOUGH_MEMORY;
// Where is this page hiding?
DWORD dwOffs = dwPage * m_dwPageSize; DWORD dwRead = 0;
#ifdef A51_STAGE_BELOW_INDEX
long lRes = m_pFile->Read(dwOffs, pMem, m_dwPageSize, &dwRead); if (lRes != ERROR_SUCCESS) { _BtrMemFree(pMem); return lRes; }
#else
dwRes = SetFilePointer(m_hFile, dwOffs, 0, FILE_BEGIN); if (dwRes != dwOffs) { _BtrMemFree(pMem); return GetLastError(); }
// Too late; the page cannot escape us
BOOL bRes = ReadFile(m_hFile, pMem, m_dwPageSize, &dwRead, 0);
if (!bRes || dwRead != m_dwPageSize) { _BtrMemFree(pMem); return GetLastError(); }
#endif
// Maybe this is a bogus page trying to pretend it is real
// Check the signature
*pPage = pMem; return NO_ERROR; }
//***************************************************************************
//
// CPageSource::PutPage
//
// Always rewrites; the file extent was grown when the page was allocated
// with NewPage, so the page already exists and the write should not fail
//
//***************************************************************************
// ok
DWORD CPageSource::PutPage( LPVOID pPage, DWORD dwType ) { // Force the page to confess its identity
DWORD *pdwHeader = LPDWORD(pPage); DWORD dwPageId = pdwHeader[OFFSET_PAGE_ID]; pdwHeader[OFFSET_PAGE_TYPE] = dwType;
// Where is it on the disk, we ask ourselves
DWORD dwOffset = m_dwPageSize * dwPageId; DWORD dwWritten = 0;
#ifdef A51_STAGE_BELOW_INDEX
long lRes = m_pFile->Write(dwOffset, pPage, m_dwPageSize, &dwWritten); if(lRes != ERROR_SUCCESS) return lRes; #else
DWORD dwRes = SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN); if (dwRes != dwOffset) return GetLastError();
// Commit the page to the disk
BOOL bRes = WriteFile(m_hFile, pPage, m_dwPageSize, &dwWritten, 0);
if (bRes == FALSE) return GetLastError(); #endif
// We're happy. But wait! Have we flushed this thing lately?
// It would be a shame if the file buffers were much more enlightened
// about the future than the disk itself.
// ===================================================================
DWORD dwNow = GetCurrentTime(); if (dwNow - m_dwLastFlushTime > MAX_FLUSH_INTERVAL) { Flush(); m_dwLastFlushTime = dwNow; }
return NO_ERROR; }
//***************************************************************************
//
// CPageSource::NewPage
//
// Allocates a new page, preferring the free list
//
//***************************************************************************
// ok
DWORD CPageSource::NewPage(LPVOID *pRetPage) { DWORD dwRes;
if (pRetPage == 0) return ERROR_INVALID_PARAMETER; *pRetPage = 0;
// Try to reuse a page from the "free list". Fast, fun, efficient.
// ================================================================
if (m_dwNextFreePage) { LPDWORD pPage = 0; DWORD dwPage = m_dwNextFreePage; dwRes = GetPage(m_dwNextFreePage, (LPVOID *) &pPage); if (dwRes) return dwRes; m_dwNextFreePage = pPage[OFFSET_NEXT_PAGE]; dwRes = WriteAdminPage(); // Update free list
if (dwRes) return dwRes; memset(pPage, 0, m_dwPageSize); pPage[OFFSET_PAGE_ID] = dwPage; *pRetPage = pPage; return NO_ERROR; }
// Well, I guess there weren't any free pages left. Oh, well. Make a new one.
// Move to the end of the file
// ============================================================================
DWORD dwCurrentExtent = m_dwTotalPages * m_dwPageSize; DWORD dwNewExtent = dwCurrentExtent + m_dwPageSize;
dwRes = SetFilePointer(m_hFile, dwNewExtent, 0, FILE_BEGIN); if (dwRes != dwNewExtent) { // Must be out of disk space. We're unhappy.
return GetLastError(); }
// Ahh... How are little file has grown. Now manufacture some
// nonsensical memory with nothing in it and assign the page number & signatures.
// ==============================================================================
LPDWORD pNewPage = (LPDWORD) _BtrMemAlloc(m_dwPageSize); if (pNewPage == 0) return ERROR_NOT_ENOUGH_MEMORY;
memset(pNewPage, 0, m_dwPageSize); pNewPage[OFFSET_PAGE_ID] = m_dwTotalPages++; *pRetPage = pNewPage;
return WriteAdminPage(); }
//***************************************************************************
//
// CPageSource::FreePage
//
// Called to delete or free a page. If the last page is the one
// being freed, then the file is truncated.
//
//***************************************************************************
// ok
DWORD CPageSource::FreePage( LPVOID pPage ) { BOOL bRes; DWORD dwRes;
LPDWORD pCast = LPDWORD(pPage); DWORD dwPageId = pCast[OFFSET_PAGE_ID];
if (dwPageId == 0 || m_dwLogicalRoot == dwPageId) { DebugBreak(); // Oops. Tried to delete the admin page or active root
return ERROR_INVALID_PARAMETER; }
// See if page is the last one. If so, simply truncate the
// file and don't add to the free list.
// ========================================================
#ifdef DONT_DO_THIS_ANY_MORE
if (dwPageId == m_dwTotalPages - 1) { m_dwTotalPages--; DWORD dwTarget = dwPageId * m_dwPageSize; dwRes = SetFilePointer(m_hFile, dwTarget, 0, FILE_BEGIN); if (dwRes != dwTarget) return GetLastError(); bRes = SetEndOfFile(m_hFile); if (!bRes) return GetLastError(); return WriteAdminPage(); } #endif
// Otherwise, it goes onto the good ol' free list.
// ===============================================
pCast[OFFSET_NEXT_PAGE] = m_dwNextFreePage; m_dwNextFreePage = dwPageId;
dwRes = PutPage(pPage, PAGE_TYPE_DELETED); if (dwRes) return dwRes;
dwRes = WriteAdminPage();
return dwRes; }
//***************************************************************************
//
// CPageSource::FreePage
//
//***************************************************************************
//
DWORD CPageSource::FreePage( DWORD dwId ) { LPDWORD pFakePage = (LPDWORD) _BtrMemAlloc(m_dwPageSize); if (pFakePage == 0) return ERROR_NOT_ENOUGH_MEMORY;
pFakePage[OFFSET_PAGE_TYPE] = PAGE_TYPE_ACTIVE; pFakePage[OFFSET_PAGE_ID] = dwId;
DWORD dwRes = FreePage(pFakePage); _BtrMemFree(pFakePage); return dwRes; }
//***************************************************************************
//
// SIdxKeyTable::GetRequiredPageMemory
//
// Returns the amount of memory required to store this object in a
// linear page
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::GetRequiredPageMemory() { DWORD dwTotal = m_pStrPool->GetRequiredPageMemory();
// Size of the key lookup table & its sizing DWORD, and
// add in the child page & user data
dwTotal += sizeof(DWORD) + sizeof(WORD) * m_dwNumKeys; dwTotal += sizeof(DWORD) + sizeof(DWORD) * m_dwNumKeys; // User data
dwTotal += sizeof(DWORD) + sizeof(DWORD) * (m_dwNumKeys+1); // Child pages
// Add in the key encoding table
dwTotal += sizeof(WORD) + sizeof(WORD) * m_dwKeyCodesUsed;
// Add in per page overhead
//
// Signature, Page Id, Next Page, Parent Page
dwTotal += sizeof(DWORD) * 4;
// (NOTE A): Add some safety margin...
dwTotal += sizeof(DWORD) * 2;
return dwTotal; }
//***************************************************************************
//
// SIdxKeyTable::StealKeyFromSibling
//
// Transfers a key from the sibling via the parent in a sort of rotation:
//
// 10
// 1 2 12 13 14
//
// Where <this> is node (1,2) and sibling is (12,13). A single rotation
// moves 10 into (1,2) and grabs 12 from the sibling to replace it,
//
// 12
// 1 2 10 13 14
//
// We repeat this until minimum load of <this> is above const_MinimumLoad.
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::StealKeyFromSibling( SIdxKeyTable *pParent, SIdxKeyTable *pSibling ) { DWORD dwData, dwChild; WORD wID; LPSTR pszKey = 0;
DWORD dwSiblingId = pSibling->GetPageId(); DWORD dwThisId = GetPageId();
for (WORD i = 0; i < WORD(pParent->GetNumKeys()); i++) { DWORD dwChildA = pParent->GetChildPage(i); DWORD dwChildB = pParent->GetChildPage(i+1);
if (dwChildA == dwThisId && dwChildB == dwSiblingId) { pParent->GetKeyAt(i, &pszKey); dwData = pParent->GetUserData(i);
FindKey(pszKey, &wID); AddKey(pszKey, wID, dwData);
pParent->RemoveKey(i); _BtrMemFree(pszKey);
pSibling->GetKeyAt(0, &pszKey); dwData = pSibling->GetUserData(0); dwChild = pSibling->GetChildPage(0); pSibling->RemoveKey(0);
SetChildPage(wID+1, dwChild);
pParent->AddKey(pszKey, i, dwData); pParent->SetChildPage(i, dwThisId); pParent->SetChildPage(i+1, dwSiblingId); _BtrMemFree(pszKey); break; } else if (dwChildA == dwSiblingId && dwChildB == dwThisId) { pParent->GetKeyAt(i, &pszKey); dwData = pParent->GetUserData(i);
FindKey(pszKey, &wID); AddKey(pszKey, wID, dwData);
pParent->RemoveKey(i); _BtrMemFree(pszKey);
WORD wSibId = (WORD) pSibling->GetNumKeys() - 1; pSibling->GetKeyAt(wSibId, &pszKey); dwData = pSibling->GetUserData(wSibId); dwChild = pSibling->GetChildPage(wSibId+1); pSibling->RemoveKey(wSibId);
SetChildPage(wID, dwChild);
pParent->AddKey(pszKey, i, dwData); pParent->SetChildPage(i, dwSiblingId); pParent->SetChildPage(i+1, dwThisId); _BtrMemFree(pszKey); break; } }
return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::Collapse
//
// Collapses the contents of a node and its sibling into just one
// node and adjusts the parent.
//
// Precondition: The two siblings can be successfully collapsed
// into a single node, accomodate a key migrated from the parent
// and still safely fit into a single node. Page sizes are not
// checked here.
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::Collapse( SIdxKeyTable *pParent, SIdxKeyTable *pDoomedSibling ) { WORD wId; DWORD dwRes; LPSTR pszKey = 0; DWORD dwData; DWORD dwChild; BOOL bExtra;
DWORD dwSiblingId = pDoomedSibling->GetPageId(); DWORD dwThisId = GetPageId();
// Locate the node in the parent which points to the two
// siblings. Since we don't know which sibling this is,
// we have to take into account the two possibilites.
// Is <this> the right side or the left?
//
// 10 20 30 40
// | | | | |
// x Sib This x x
//
// vs.
// 10 20 30 40
// | | | | |
// x This Sib x x
//
// We then migrate the key down into the current node
// and remove it from the parent. We steal the first
//
// ======================================================
for (WORD i = 0; i < WORD(pParent->GetNumKeys()); i++) { DWORD dwChildA = pParent->GetChildPage(i); DWORD dwChildB = pParent->GetChildPage(i+1);
if (dwChildA == dwSiblingId && dwChildB == dwThisId) { pParent->GetKeyAt(i, &pszKey); dwData = pParent->GetUserData(i); pParent->RemoveKey(i); pParent->SetChildPage(i, dwThisId); dwChild = pDoomedSibling->GetLastChildPage(); AddKey(pszKey, 0, dwData); SetChildPage(0, dwChild); _BtrMemFree(pszKey); bExtra = FALSE; break; } else if (dwChildA == dwThisId && dwChildB == dwSiblingId) { pParent->GetKeyAt(i, &pszKey); dwData = pParent->GetUserData(i); pParent->RemoveKey(i); pParent->SetChildPage(i, dwThisId); FindKey(pszKey, &wId); AddKey(pszKey, wId, dwData); _BtrMemFree(pszKey); bExtra = TRUE; break; } }
// Move all info from sibling into the current node.
// ==================================================
DWORD dwNumSibKeys = pDoomedSibling->GetNumKeys();
for (WORD i = 0; i < WORD(dwNumSibKeys); i++) { LPSTR pKeyStr = 0; dwRes = pDoomedSibling->GetKeyAt(i, &pKeyStr); if (dwRes) return dwRes;
DWORD dwUserData = pDoomedSibling->GetUserData(i);
dwRes = FindKey(pKeyStr, &wId); if (dwRes != ERROR_NOT_FOUND) { _BtrMemFree(pKeyStr); return ERROR_BAD_FORMAT; }
dwRes = AddKey(pKeyStr, wId, dwUserData);
dwChild = pDoomedSibling->GetChildPage(i); SetChildPage(wId, dwChild); _BtrMemFree(pKeyStr); }
if (bExtra) SetChildPage(WORD(GetNumKeys()), pDoomedSibling->GetLastChildPage());
pDoomedSibling->ZapPage();
return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::GetRightSiblingOf
// SIdxKeyTable::GetRightSiblingOf
//
// Searches the child page pointers and returns the sibling of the
// specified page. A return value of zero indicates there was not
// sibling of the specified value in the direction requested.
//
//***************************************************************************
//
DWORD SIdxKeyTable::GetRightSiblingOf( DWORD dwId ) { for (DWORD i = 0; i < m_dwNumKeys; i++) { if (m_pdwChildPageMap[i] == dwId) return m_pdwChildPageMap[i+1]; }
return 0; }
DWORD SIdxKeyTable::GetLeftSiblingOf( DWORD dwId ) { for (DWORD i = 1; i < m_dwNumKeys+1; i++) { if (m_pdwChildPageMap[i] == dwId) return m_pdwChildPageMap[i-1]; }
return 0;
}
//***************************************************************************
//
// SIdxKeyTable::Redist
//
// Used when inserting and performing a node split.
// Precondition:
// (a) The current node is oversized
// (b) <pParent> is ready to receive the new median key
// (c) <pNewSibling> is completely empty and refers to the lesser node (left)
// (d) All pages have assigned numbers
//
// We move the nodes from <this> into the <pNewSibling> until both
// are approximately half full. The median key is moved into the parent.
// May fail if <pNewSibling> cannot allocate memory for the new stuff.
//
// If any errors occur, the entire sequence should be considered as failed
// and the pages invalid.
//
//***************************************************************************
//
DWORD SIdxKeyTable::Redist( SIdxKeyTable *pParent, SIdxKeyTable *pNewSibling ) { DWORD dwRes; WORD wID;
if (pParent == 0 || pNewSibling == 0) return ERROR_INVALID_PARAMETER;
if (m_dwNumKeys < 3) { return ERROR_INVALID_DATA; }
// Find median key info and put it into parent.
DWORD dwToTransfer = m_dwNumKeys / 2;
while (dwToTransfer--) { // Get 0th key
LPSTR pStr = 0; dwRes = GetKeyAt(0, &pStr); if (dwRes) return dwRes;
DWORD dwUserData = GetUserData(0);
// Move stuff into younger sibling
dwRes = pNewSibling->FindKey(pStr, &wID); if (dwRes != ERROR_NOT_FOUND) { _BtrMemFree(pStr); return dwRes; }
dwRes = pNewSibling->AddKey(pStr, wID, dwUserData); _BtrMemFree(pStr);
if (dwRes) return dwRes;
DWORD dwChildPage = GetChildPage(0); pNewSibling->SetChildPage(wID, dwChildPage); RemoveKey(0); }
pNewSibling->SetChildPage(WORD(pNewSibling->GetNumKeys()), GetChildPage(0));
// Next key is the median key, which migrates to the parent.
LPSTR pStr = 0; dwRes = GetKeyAt(0, &pStr); if (dwRes) return dwRes; DWORD dwUserData = GetUserData(0);
dwRes = pParent->FindKey(pStr, &wID); if (dwRes != ERROR_NOT_FOUND) { _BtrMemFree(pStr); return dwRes; }
dwRes = pParent->AddKey(pStr, wID, dwUserData); _BtrMemFree(pStr);
if (dwRes) return dwRes;
RemoveKey(0);
// Patch in the various page pointers
pParent->SetChildPage(wID, pNewSibling->GetPageId()); pParent->SetChildPage(wID+1, GetPageId());
// Everything else is already okay
return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::SIdxKeyTable
//
//***************************************************************************
// ok
SIdxKeyTable::SIdxKeyTable() { m_dwRefCount = 0; m_dwPageId = 0; m_dwParentPageId = 0;
m_dwNumKeys = 0; // Num keys
m_pwKeyLookup = 0; // Offset of key into key-lookup-table
m_dwKeyLookupTotalSize = 0; // Elements in array
m_pwKeyCodes = 0; // Key encoding table
m_dwKeyCodesTotalSize = 0; // Total elements in array
m_dwKeyCodesUsed = 0; // Elements used
m_pStrPool = 0; // The pool associated with this key table
m_pdwUserData = 0; // Stores user DWORDs for each key
m_pdwChildPageMap = 0; // Stores the child page map (num keys + 1)
}
//***************************************************************************
//
//***************************************************************************
//
DWORD SIdxKeyTable::Clone( OUT SIdxKeyTable **pRetCopy ) { SIdxKeyTable *pCopy = new SIdxKeyTable; if (!pCopy) return ERROR_NOT_ENOUGH_MEMORY;
pCopy->m_dwRefCount = 1; pCopy->m_dwPageId = m_dwPageId; pCopy->m_dwParentPageId = m_dwParentPageId; pCopy->m_dwNumKeys = m_dwNumKeys;
pCopy->m_pwKeyLookup = (WORD *)_BtrMemAlloc(sizeof(WORD) * m_dwKeyLookupTotalSize); if (pCopy->m_pwKeyLookup == 0) return ERROR_NOT_ENOUGH_MEMORY;
memcpy(pCopy->m_pwKeyLookup, m_pwKeyLookup, sizeof(WORD) * m_dwKeyLookupTotalSize); pCopy->m_dwKeyLookupTotalSize = m_dwKeyLookupTotalSize;
pCopy->m_pdwUserData = (DWORD *)_BtrMemAlloc(sizeof(DWORD) * m_dwKeyLookupTotalSize); if (pCopy->m_pdwUserData == 0) return ERROR_NOT_ENOUGH_MEMORY;
memcpy(pCopy->m_pdwUserData, m_pdwUserData, sizeof(DWORD) * m_dwKeyLookupTotalSize);
pCopy->m_pdwChildPageMap = (DWORD *) _BtrMemAlloc(sizeof(DWORD) * (m_dwKeyLookupTotalSize+1)); if (pCopy->m_pdwChildPageMap == 0) return ERROR_NOT_ENOUGH_MEMORY;
memcpy(pCopy->m_pdwChildPageMap, m_pdwChildPageMap, sizeof(DWORD) * (m_dwKeyLookupTotalSize+1));
pCopy->m_dwKeyCodesTotalSize = m_dwKeyCodesTotalSize; pCopy->m_pwKeyCodes = (WORD *) _BtrMemAlloc(sizeof(WORD) * m_dwKeyCodesTotalSize); if (pCopy->m_pwKeyCodes == 0) return ERROR_NOT_ENOUGH_MEMORY;
memcpy(pCopy->m_pwKeyCodes, m_pwKeyCodes, sizeof(WORD)* m_dwKeyCodesTotalSize); pCopy->m_dwKeyCodesUsed = m_dwKeyCodesUsed;
m_pStrPool->Clone(&pCopy->m_pStrPool);
*pRetCopy = pCopy; return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::~SIdxKeyTable
//
//***************************************************************************
//
SIdxKeyTable::~SIdxKeyTable() { if (m_pwKeyCodes) _BtrMemFree(m_pwKeyCodes); if (m_pwKeyLookup) _BtrMemFree(m_pwKeyLookup); if (m_pdwUserData) _BtrMemFree(m_pdwUserData); if (m_pdwChildPageMap) _BtrMemFree(m_pdwChildPageMap); if (m_pStrPool) delete m_pStrPool; }
//***************************************************************************
//
// SIdxKeyTable::GetKeyAt
//
// Precondition: <wID> is correct
// The only real case of failure is that the return string cannot be allocated.
//
// Return values:
// NO_ERROR
// ERROR_NOT_ENOUGH_MEMORY
// ERROR_INVALID_PARAMETER
//
//***************************************************************************
// tested
DWORD SIdxKeyTable::GetKeyAt( WORD wID, LPSTR *pszKey ) { if (wID >= m_dwNumKeys || pszKey == 0) return ERROR_INVALID_PARAMETER;
WORD wStartOffs = m_pwKeyLookup[wID]; WORD wNumTokens = m_pwKeyCodes[wStartOffs];
LPSTR Strings[MAX_TOKENS_PER_KEY]; DWORD dwTotalLengths = 0;
for (DWORD i = 0; i < DWORD(wNumTokens); i++) { Strings[i] = m_pStrPool->GetStrById(m_pwKeyCodes[wStartOffs+1+i]); dwTotalLengths += strlen(Strings[i]); }
LPSTR pszFinalStr = (LPSTR) _BtrMemAlloc(dwTotalLengths + 1 + wNumTokens); if (!pszFinalStr) return ERROR_NOT_ENOUGH_MEMORY; *pszFinalStr = 0;
for (DWORD i = 0; i < DWORD(wNumTokens); i++) { if (i > 0) strcat(pszFinalStr, "\\"); strcat(pszFinalStr, Strings[i]); }
*pszKey = pszFinalStr; return NO_ERROR; }
//***************************************************************************
//
// SIdxStringPool::FindStr
//
// Finds a string in the pool, if present and returns the assigned
// offset. Uses a binary search.
//
// Return codes:
// NO_ERROR The string was found
// ERROR_NOT_FOND
//
//***************************************************************************
// tested
DWORD SIdxStringPool::FindStr( IN LPSTR pszSearchKey, OUT WORD *pwStringNumber, OUT WORD *pwPoolOffset ) { if (m_dwNumStrings == 0) { *pwStringNumber = 0; return ERROR_NOT_FOUND; }
// Binary search current node for key match.
// =========================================
int nPosition = 0; int l = 0, u = int(m_dwNumStrings) - 1;
while (l <= u) { int m = (l + u) / 2;
// m is the current key to consider 0...n-1
LPSTR pszCandidateKeyStr = m_pStringPool+m_pwOffsets[m]; int nRes = strcmp(pszSearchKey, pszCandidateKeyStr);
// Decide which way to cut the array in half.
// ==========================================
if (nRes < 0) { u = m - 1; nPosition = u + 1; } else if (nRes > 0) { l = m + 1; nPosition = l; } else { // If here, we found the darn thing. Life is good.
// Populate the key unit.
// ================================================
if (pwStringNumber) *pwStringNumber = WORD(m); if (pwPoolOffset) *pwPoolOffset = m_pwOffsets[m]; return NO_ERROR; } }
// Not found, if here. We record where the key should have been
// and tell the user the unhappy news.
// ==============================================================
*pwStringNumber = WORD(short(nPosition)); // The key would have been 'here'
return ERROR_NOT_FOUND; }
//***************************************************************************
//
//***************************************************************************
//
DWORD SIdxStringPool::Clone( SIdxStringPool **pRetCopy ) { SIdxStringPool *pCopy = new SIdxStringPool; if (pCopy == 0) return ERROR_NOT_ENOUGH_MEMORY;
pCopy->m_dwNumStrings = m_dwNumStrings; pCopy->m_pwOffsets = (WORD *) _BtrMemAlloc(sizeof(WORD)*m_dwOffsetsSize); if (pCopy->m_pwOffsets == 0) return ERROR_NOT_ENOUGH_MEMORY; memcpy(pCopy->m_pwOffsets, m_pwOffsets, sizeof(WORD)*m_dwOffsetsSize);
pCopy->m_dwOffsetsSize = m_dwOffsetsSize;
pCopy->m_pStringPool = (LPSTR) _BtrMemAlloc(m_dwPoolTotalSize); if (pCopy->m_pStringPool == 0) return ERROR_NOT_ENOUGH_MEMORY;
memcpy(pCopy->m_pStringPool, m_pStringPool, m_dwPoolTotalSize); pCopy->m_dwPoolTotalSize = m_dwPoolTotalSize;
pCopy->m_dwPoolUsed = m_dwPoolUsed;
*pRetCopy = pCopy; return NO_ERROR; }
//***************************************************************************
//
// SIdxStringPool::DeleteStr
//
// Removes a string from the pool and pool index.
// Precondition: <wStringNum> is known to be valid by virtue of a prior
// call to <FindStr>.
//
// Return values:
// NO_ERROR <Cannot fail if precondition is met>.
//
//***************************************************************************
//
DWORD SIdxStringPool::DeleteStr( WORD wStringNum, int *pnAdjuster ) { if (pnAdjuster) *pnAdjuster = 0;
// Find the address of the string to be removed.
// =============================================
DWORD dwTargetOffs = m_pwOffsets[wStringNum]; LPSTR pszDoomed = m_pStringPool+dwTargetOffs; DWORD dwDoomedStrLen = strlen(pszDoomed) + 1;
// Copy all subsequent strings over the top and shorten the heap.
// Special case if this already the last string
// ==============================================================
DWORD dwStrBytesToMove = DWORD(m_pStringPool+m_dwPoolUsed - pszDoomed - dwDoomedStrLen);
if (dwStrBytesToMove) memmove(pszDoomed, pszDoomed+dwDoomedStrLen, dwStrBytesToMove);
m_dwPoolUsed -= dwDoomedStrLen;
// Remove this entry from the array.
// =================================
DWORD dwArrayElsToMove = m_dwNumStrings - wStringNum - 1; if (dwArrayElsToMove) { memmove(m_pwOffsets+wStringNum, m_pwOffsets+wStringNum+1, dwArrayElsToMove * sizeof(WORD)); if (pnAdjuster) *pnAdjuster = -1; } m_dwNumStrings--;
// For all remaining elements, adjust offsets that were affected.
// ==============================================================
for (DWORD dwTrace = 0; dwTrace < m_dwNumStrings; dwTrace++) { if (m_pwOffsets[dwTrace] > dwTargetOffs) m_pwOffsets[dwTrace] -= WORD(dwDoomedStrLen); }
// Adjust sizes.
// =============
return NO_ERROR; }
//***************************************************************************
//
// SIdxStringPool::AddStr
//
// Adds a string to the pool. Assumes it is known prior to the call that
// the string isn't present.
//
// Parameters:
// pszString The string to add
// pwAssignedOffset Returns the offset code assigned to the string
// Return values:
// NO_ERROR
// ERROR_NOT_ENOUGH_MEMORY
//
//***************************************************************************
// ok
DWORD SIdxStringPool::AddStr( LPSTR pszString, WORD wInsertPos, int *pnAdjuster ) { if (pnAdjuster) *pnAdjuster = 0;
// Precondition: String doesn't exist in the table
// Determine if the pool is too small for another string.
// If so, extend it.
// ======================================================
DWORD dwRequired = strlen(pszString)+1; DWORD dwPoolFree = m_dwPoolTotalSize - m_dwPoolUsed;
if (m_dwPoolUsed + dwRequired - 1 > MAX_WORD_VALUE) { return ERROR_INSUFFICIENT_BUFFER; }
if (dwRequired > dwPoolFree) { // Try to grow the pool
// ====================
LPVOID pTemp = _BtrMemReAlloc(m_pStringPool, m_dwPoolTotalSize * 2); if (!pTemp) { return ERROR_NOT_ENOUGH_MEMORY; } m_dwPoolTotalSize *= 2; m_pStringPool = (LPSTR) pTemp; }
// If array too small, reallocate to larger one
// ============================================
if (m_dwNumStrings == m_dwOffsetsSize) { // Realloc; double current size
LPVOID pTemp = _BtrMemReAlloc(m_pwOffsets, m_dwOffsetsSize * sizeof(WORD) * 2); if (!pTemp) return ERROR_NOT_ENOUGH_MEMORY; m_dwOffsetsSize *= 2; m_pwOffsets = PWORD(pTemp); }
// If here, no problem. We have enough space for everything.
// =========================================================
LPSTR pszInsertAddr = m_pStringPool+m_dwPoolUsed; DWORD dwInsertOffs = m_dwPoolUsed; strcpy(pszInsertAddr, pszString); m_dwPoolUsed += dwRequired;
// If here, there is enough room.
// ==============================
DWORD dwToBeMoved = m_dwNumStrings - wInsertPos;
if (dwToBeMoved) { memmove(&m_pwOffsets[wInsertPos+1], &m_pwOffsets[wInsertPos], sizeof(WORD)*dwToBeMoved); if (pnAdjuster) *pnAdjuster = 1; }
m_pwOffsets[wInsertPos] = WORD(dwInsertOffs); m_dwNumStrings++;
return NO_ERROR; }
//***************************************************************************
//
// ParseIntoTokens
//
// Parses a slash separated string into separate tokens in preparation
// for encoding into the string pool. Call FreeStringArray on the output
// when no longer needed.
//
// No more than MAX_TOKEN_PER_KEY are supported. This means that
// if backslashes are used, no more than MAX_TOKEN_PER_KEY units can
// be parsed out.
//
// Returns:
// ERROR_INVALID_PARAMETER
// ERROR_NOT_ENOUGH_MEMORY
// NO_ERROR
//
//***************************************************************************
// ok
DWORD ParseIntoTokens( IN LPSTR pszSource, OUT DWORD *pdwTokenCount, OUT LPSTR **pszTokens ) { LPSTR Strings[MAX_TOKENS_PER_KEY]; DWORD dwParseCount = 0, i = 0; DWORD dwSourceLen = strlen(pszSource); LPSTR *pszRetStr = 0; DWORD dwRet;
if (pszSource == 0 || *pszSource == 0) return ERROR_INVALID_PARAMETER;
LPSTR pszTempBuf = (LPSTR) _BtrMemAlloc(dwSourceLen+1); if (!pszTempBuf) return ERROR_NOT_ENOUGH_MEMORY;
LPSTR pszTracer = pszTempBuf;
for (;;) { *pszTracer = *pszSource; if (*pszTracer == '\\' || *pszTracer == 0) { *pszTracer = 0; // Replace with null terminator
LPSTR pszTemp2 = (LPSTR) _BtrMemAlloc(strlen(pszTempBuf)+1); if (pszTemp2 == 0) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Error; }
if (dwParseCount == MAX_TOKENS_PER_KEY) { _BtrMemFree(pszTemp2); dwRet = ERROR_INVALID_DATA; goto Error; }
strcpy(pszTemp2, pszTempBuf); Strings[dwParseCount++] = pszTemp2; pszTracer = pszTempBuf; pszTracer--; }
if (*pszSource == 0) break;
pszTracer++; pszSource++; }
// If here, we at least parsed one string.
// =======================================
pszRetStr = (LPSTR *) _BtrMemAlloc(sizeof(LPSTR) * dwParseCount); if (pszRetStr == 0) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Error; }
memcpy(pszRetStr, Strings, sizeof(LPSTR) * dwParseCount); *pdwTokenCount = dwParseCount; *pszTokens = pszRetStr;
_BtrMemFree(pszTempBuf);
return NO_ERROR;
Error: for (i = 0; i < dwParseCount; i++) _BtrMemFree(Strings[i]); *pdwTokenCount = 0;
_BtrMemFree(pszTempBuf);
return dwRet; }
//***************************************************************************
//
// FreeTokenArray
//
// Cleans up the array returned by ParseIntoTokens.
//
//***************************************************************************
// ok
void FreeTokenArray( DWORD dwCount, LPSTR *pszStrings ) { for (DWORD i = 0; i < dwCount; i++) _BtrMemFree(pszStrings[i]); _BtrMemFree(pszStrings); }
//***************************************************************************
//
// SIdxKeyTable::ZapPage
//
// Empties the page completely of all keys, codes, strings
//
//***************************************************************************
// ok
void SIdxKeyTable::ZapPage() { m_pStrPool->Empty(); m_dwKeyCodesUsed = 0; m_dwNumKeys = 0; }
//***************************************************************************
//
// SIdxKeyTable::MapFromPage
//
// CAUTION!!!
// The placement of DWORDs and WORDs is arranged to avoid 64-bit
// alignment faults.
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::MapFromPage(LPVOID pSrc) { if (pSrc == 0) return ERROR_INVALID_PARAMETER;
// Header
//
// DWORD[0] Signature
// DWORD[1] Page number
// DWORD[2] Next Page (always zero)
// ==================================\
LPDWORD pDWCast = (LPDWORD) pSrc;
if (*pDWCast++ != CPageSource::PAGE_TYPE_ACTIVE) { return ERROR_BAD_FORMAT; } m_dwPageId = *pDWCast++; pDWCast++; // Skip the 'next page' field
// Key lookup table info
//
// DWORD[0] Parent Page
// DWORD[1] Num Keys = n
// DWORD[n] User Data
// DWORD[n+1] Child Page Map
// WORD[n] Key encoding offsets array
// ======================================
m_dwParentPageId = *pDWCast++; m_dwNumKeys = *pDWCast++;
// Decide the allocation sizes and build the arrays
// ================================================
if (m_dwNumKeys <= const_DefaultArray) m_dwKeyLookupTotalSize = const_DefaultArray; else m_dwKeyLookupTotalSize = m_dwNumKeys;
m_pdwUserData = (DWORD*) _BtrMemAlloc(m_dwKeyLookupTotalSize * sizeof(DWORD)); m_pdwChildPageMap = (DWORD*) _BtrMemAlloc((m_dwKeyLookupTotalSize+1) * sizeof(DWORD)); m_pwKeyLookup = (WORD*) _BtrMemAlloc(m_dwKeyLookupTotalSize * sizeof(WORD));
if (m_pdwUserData == 0 || m_pdwChildPageMap == 0 || m_pwKeyLookup == 0) { return ERROR_NOT_ENOUGH_MEMORY; }
// Copy the page info into the arrays
// ==================================
memcpy(m_pdwUserData, pDWCast, sizeof(DWORD) * m_dwNumKeys); pDWCast += m_dwNumKeys; memcpy(m_pdwChildPageMap, pDWCast, sizeof(DWORD) * (m_dwNumKeys+1)); pDWCast += m_dwNumKeys + 1; memcpy(m_pwKeyLookup, pDWCast, sizeof(WORD) * m_dwNumKeys); LPWORD pWCast = LPWORD(pDWCast); pWCast += m_dwNumKeys;
// Key encoding table info
//
// WORD[0] Num key codes = n
// WORD[n] Key codes
// ===========================
m_dwKeyCodesUsed = (DWORD) *pWCast++;
if (m_dwKeyCodesUsed <= const_DefaultKeyCodeArray) m_dwKeyCodesTotalSize = const_DefaultKeyCodeArray; else m_dwKeyCodesTotalSize = m_dwKeyCodesUsed;
m_pwKeyCodes = (WORD*) _BtrMemAlloc(m_dwKeyCodesTotalSize * sizeof(WORD)); if (!m_pwKeyCodes) { return ERROR_NOT_ENOUGH_MEMORY; } memcpy(m_pwKeyCodes, pWCast, sizeof(WORD) * m_dwKeyCodesUsed); pWCast += m_dwKeyCodesUsed;
// String pool
//
// WORD[0] Num strings = n
// WORD[n] Offsets
//
// WORD[0] String pool size = n
// BYTE[n] String pool
// =============================
m_pStrPool = new SIdxStringPool; if (!m_pStrPool) return ERROR_NOT_ENOUGH_MEMORY;
m_pStrPool->m_dwNumStrings = (DWORD) *pWCast++; if (m_pStrPool->m_dwNumStrings <= const_DefaultArray) m_pStrPool->m_dwOffsetsSize = const_DefaultArray; else m_pStrPool->m_dwOffsetsSize = m_pStrPool->m_dwNumStrings;
m_pStrPool->m_pwOffsets = (WORD *) _BtrMemAlloc(sizeof(WORD)* m_pStrPool->m_dwOffsetsSize); if (m_pStrPool->m_pwOffsets == 0) return ERROR_NOT_ENOUGH_MEMORY;
memcpy(m_pStrPool->m_pwOffsets, pWCast, sizeof(WORD)*m_pStrPool->m_dwNumStrings); pWCast += m_pStrPool->m_dwNumStrings;
// String pool setup
// =================
m_pStrPool->m_dwPoolUsed = *pWCast++; LPSTR pszCast = LPSTR(pWCast);
if (m_pStrPool->m_dwPoolUsed <= SIdxStringPool::const_DefaultPoolSize) m_pStrPool->m_dwPoolTotalSize = SIdxStringPool::const_DefaultPoolSize; else m_pStrPool->m_dwPoolTotalSize = m_pStrPool->m_dwPoolUsed;
m_pStrPool->m_pStringPool = (LPSTR) _BtrMemAlloc(m_pStrPool->m_dwPoolTotalSize); if (m_pStrPool->m_pStringPool == 0) { return ERROR_NOT_ENOUGH_MEMORY; }
memcpy(m_pStrPool->m_pStringPool, pszCast, m_pStrPool->m_dwPoolUsed);
return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::MapToPage
//
// Copies the info to a linear page. Precondition: the page must
// be large enough by validating using a prior test to GetRequiredPageMemory.
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::MapToPage(LPVOID pDest) { if (pDest == 0) return ERROR_INVALID_PARAMETER;
// Header
//
// DWORD[0] Signature
// DWORD[1] Page number
// DWORD[2] Next Page (always zero)
// ==================================\
LPDWORD pDWCast = (LPDWORD) pDest; *pDWCast++ = CPageSource::PAGE_TYPE_ACTIVE; *pDWCast++ = m_dwPageId; *pDWCast++ = 0; // Unused 'next page' field
// Key lookup table info
//
// DWORD[0] Parent Page
// DWORD[1] Num Keys = n
// DWORD[n] User Data
// DWORD[n+1] Child Page Map
// WORD[n] Key encoding offsets array
// ======================================
*pDWCast++ = m_dwParentPageId; *pDWCast++ = m_dwNumKeys;
// Decide the allocation sizes and build the arrays
// ================================================
memcpy(pDWCast, m_pdwUserData, sizeof(DWORD) * m_dwNumKeys); pDWCast += m_dwNumKeys; memcpy(pDWCast, m_pdwChildPageMap, sizeof(DWORD) * (m_dwNumKeys+1)); pDWCast += m_dwNumKeys + 1; memcpy(pDWCast, m_pwKeyLookup, sizeof(WORD) * m_dwNumKeys); LPWORD pWCast = LPWORD(pDWCast); pWCast += m_dwNumKeys;
// Key encoding table info
//
// WORD[0] Num key codes = n
// WORD[n] Key codes
// ===========================
*pWCast++ = WORD(m_dwKeyCodesUsed); memcpy(pWCast, m_pwKeyCodes, sizeof(WORD) * m_dwKeyCodesUsed); pWCast += m_dwKeyCodesUsed;
// String pool
//
// WORD[0] Num strings = n
// WORD[n] Offsets
//
// WORD[0] String pool size = n
// BYTE[n] String pool
// =============================
*pWCast++ = WORD(m_pStrPool->m_dwNumStrings); memcpy(pWCast, m_pStrPool->m_pwOffsets, sizeof(WORD)*m_pStrPool->m_dwNumStrings); pWCast += m_pStrPool->m_dwNumStrings;
*pWCast++ = WORD(m_pStrPool->m_dwPoolUsed); LPSTR pszCast = LPSTR(pWCast); memcpy(pszCast, m_pStrPool->m_pStringPool, m_pStrPool->m_dwPoolUsed);
return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::Create
//
// Does a default create
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::Create( DWORD dwPageId, OUT SIdxKeyTable **pNewInst ) { SIdxKeyTable *p = new SIdxKeyTable; if (!p) return ERROR_NOT_ENOUGH_MEMORY;
// Set up default string pool, arrays, etc.
// ========================================
p->m_dwPageId = dwPageId; p->m_dwNumKeys = 0; p->m_pwKeyLookup = (WORD*) _BtrMemAlloc(const_DefaultArray * sizeof(WORD)); p->m_dwKeyLookupTotalSize = const_DefaultArray; p->m_pwKeyCodes = (WORD*) _BtrMemAlloc(const_DefaultArray * sizeof(WORD)); p->m_dwKeyCodesTotalSize = const_DefaultArray; p->m_dwKeyCodesUsed = 0;
p->m_pdwUserData = (DWORD*) _BtrMemAlloc(const_DefaultArray * sizeof(DWORD)); p->m_pdwChildPageMap = (DWORD*) _BtrMemAlloc((const_DefaultArray+1) * sizeof(DWORD));
// Set up string pool.
// ===================
p->m_pStrPool = new SIdxStringPool;
p->m_pStrPool->m_pwOffsets = (WORD*) _BtrMemAlloc(const_DefaultArray * sizeof(WORD)); p->m_pStrPool->m_dwOffsetsSize = const_DefaultArray;
p->m_pStrPool->m_pStringPool = (LPSTR) _BtrMemAlloc(SIdxStringPool::const_DefaultPoolSize); p->m_pStrPool->m_dwPoolTotalSize = SIdxStringPool::const_DefaultPoolSize;
// Check all pointers. If any are null, error out.
// ================================================
if ( p->m_pwKeyLookup == NULL || p->m_pwKeyCodes == NULL || p->m_pdwUserData == NULL || p->m_pdwChildPageMap == NULL || p->m_pStrPool == NULL || p->m_pStrPool->m_pwOffsets == NULL || p->m_pStrPool->m_pStringPool == NULL ) { delete p; *pNewInst = 0; return ERROR_NOT_ENOUGH_MEMORY; }
// Return good object to caller.
// =============================
p->AddRef(); *pNewInst = p; return NO_ERROR; }
//***************************************************************************
//
// SIdxStringPool::~SIdxStringPool
//
//***************************************************************************
// ok
SIdxStringPool::~SIdxStringPool() { if (m_pwOffsets) _BtrMemFree(m_pwOffsets); m_pwOffsets = 0; if (m_pStringPool) _BtrMemFree(m_pStringPool); // Pointer to string pool
m_pStringPool = 0; }
//***************************************************************************
//
// SIdxKeyTable::Create
//
// Does a default create
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::Create( IN LPVOID pPage, OUT SIdxKeyTable **pNewInst ) { SIdxKeyTable *p = new SIdxKeyTable; if (!p) return ERROR_NOT_ENOUGH_MEMORY; DWORD dwRes = p->MapFromPage(pPage); if (dwRes) { *pNewInst = 0; return dwRes; } p->AddRef(); *pNewInst = p; return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::AddRef
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::AddRef() { InterlockedIncrement((LONG *) &m_dwRefCount); return m_dwRefCount; }
//***************************************************************************
//
// SIdxKeyTable::Release
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::Release() { DWORD dwNewCount = InterlockedDecrement((LONG *) &m_dwRefCount); if (0 != dwNewCount) return dwNewCount; delete this; return 0; }
//***************************************************************************
//
// SIdxKeyTable::Cleanup
//
// Does a consistency check of the key encoding table and cleans up the
// string pool if any strings aren't being referenced.
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::Cleanup() { // See if all string pool codes are used in key code table.
// If not, remove the string pool code.
// =======================================================
DWORD dwLastId = m_pStrPool->GetLastId(); BOOL *pCheck = (BOOL*) _BtrMemAlloc(sizeof(BOOL) * dwLastId); if (!pCheck) return ERROR_NOT_ENOUGH_MEMORY;
while (1) { if (m_pStrPool->GetNumStrings() == 0 || m_dwKeyCodesUsed == 0 || m_dwNumKeys == 0) { ZapPage(); break; }
dwLastId = m_pStrPool->GetLastId(); memset(pCheck, 0, sizeof(BOOL)*dwLastId); // Mark all codes as 'unused'
// Move through all key codes. If we delete a key encoding, there
// may be a code in the string pool not used by the encoding.
// What we have to do is set the pCheck array to TRUE for each
// code encountered. If any have FALSE when we are done, we have
// an unused code.
WORD wCurrentSequence = 0; for (DWORD i = 0; i < m_dwKeyCodesUsed; i++) { if (wCurrentSequence == 0) // Skip the length WORD
{ wCurrentSequence = m_pwKeyCodes[i]; continue; } else // A string pool code
pCheck[m_pwKeyCodes[i]] = TRUE; wCurrentSequence--; }
// Now the pCheck array contains deep within its psyche
// the knowledge of whether or not all string pool codes
// were used TRUE for referenced ones, FALSE for those
// not referenced. Let's look through it and see!
DWORD dwUsed = 0, dwUnused = 0;
for (i = 0; i < dwLastId; i++) { if (pCheck[i] == FALSE) { dwUnused++; // Yikes! A lonely, unused string code. Let's be merciful
// and zap it before it knows the difference.
// =======================================================
int nAdj = 0; m_pStrPool->DeleteStr(WORD(i), &nAdj); AdjustKeyCodes(WORD(i), nAdj); break; } else dwUsed++; }
if (dwUnused == 0) break; }
_BtrMemFree(pCheck);
return NO_ERROR; }
//***************************************************************************
//
// SIdxKeyTable::AdjustKeyCodes
//
//***************************************************************************
// ok
void SIdxKeyTable::AdjustKeyCodes( WORD wID, int nAdjustment ) { // Adjust all key codes starting with wID by the amount of the
// adjustment, skipping length bytes.
// =============================================================
WORD wCurrentSequence = 0; for (DWORD i = 0; i < m_dwKeyCodesUsed; i++) { if (wCurrentSequence == 0) { wCurrentSequence = m_pwKeyCodes[i]; continue; } else { if (m_pwKeyCodes[i] >= wID) m_pwKeyCodes[i] = m_pwKeyCodes[i] + nAdjustment; } wCurrentSequence--; } }
//***************************************************************************
//
// SIdxKeyTable::AddKey
//
// Adds a string to the table at position <wID>. Assumes FindString
// was called first to get the correct location.
//
// Precondition: <pszStr> is valid, and <wID> is correct.
//
// Return codes:
//
// ERROR_OUT_OF_MEMORY
// NO_ERROR
// ERROR_INVALID_PARAMETER // Too many slashes in key
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::AddKey( LPSTR pszStr, WORD wKeyID, DWORD dwUserData ) { DWORD dwRes, dwRet; LPVOID pTemp = 0; LPSTR pszTemp = 0; DWORD dwLen, i; DWORD dwTokenCount = 0; WORD *pwTokenIDs = 0; DWORD dwNumNewTokens = 0; LPSTR *pszStrings = 0; DWORD dwToBeMoved; DWORD dwStartingOffset;
// Set up some temp working arrays.
// ================================
if (!pszStr) return ERROR_INVALID_PARAMETER; dwLen = strlen(pszStr); if (dwLen == 0) return ERROR_INVALID_PARAMETER;
pszTemp = (LPSTR) _BtrMemAlloc(dwLen+1); if (!pszTemp) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; }
// Ensure there is enough room.
// ============================
if (m_dwKeyLookupTotalSize == m_dwNumKeys) { // Expand the array.
DWORD dwNewSize = m_dwKeyLookupTotalSize * 2; pTemp = _BtrMemReAlloc(m_pwKeyLookup, dwNewSize * sizeof(WORD)); if (!pTemp) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } m_dwKeyLookupTotalSize = dwNewSize; m_pwKeyLookup = PWORD(pTemp);
// Expand user data.
pTemp = _BtrMemReAlloc(m_pdwUserData, dwNewSize * sizeof(DWORD)); if (!pTemp) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } m_pdwUserData = (DWORD *) pTemp;
// Expand child page map.
pTemp = _BtrMemReAlloc(m_pdwChildPageMap, (dwNewSize + 1) * sizeof(DWORD)); if (!pTemp) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } m_pdwChildPageMap = (DWORD *) pTemp; }
// Parse the string into backslash separated tokens.
// =================================================
dwRes = ParseIntoTokens(pszStr, &dwTokenCount, &pszStrings); if (dwRes) { dwRet = dwRes; goto Exit; }
// Allocate an array to hold the IDs of the tokens in the string.
// ==============================================================
pwTokenIDs = (WORD *) _BtrMemAlloc(sizeof(WORD) * dwTokenCount); if (pwTokenIDs == 0) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; }
// Work through the tokens and add them to the pool & key encoding table.
// =============================================================
for (i = 0; i < dwTokenCount; i++) { LPSTR pszTok = pszStrings[i];
// See if token exists, if not add it.
// ===================================
WORD wID = 0; dwRes = m_pStrPool->FindStr(pszTok, &wID, 0);
if (dwRes == NO_ERROR) { // Found it
pwTokenIDs[dwNumNewTokens++] = wID; } else if (dwRes == ERROR_NOT_FOUND) { int nAdjustment = 0; dwRes = m_pStrPool->AddStr(pszTok, wID, &nAdjustment); if (dwRes) { dwRet = dwRes; goto Exit; } // Adjust string IDs because of the addition.
// All existing ones with the same ID or higher
// must be adjusted upwards.
if (nAdjustment) { AdjustKeyCodes(wID, nAdjustment); for (DWORD i2 = 0; i2 < dwNumNewTokens; i2++) { if (pwTokenIDs[i2] >= wID) pwTokenIDs[i2] = pwTokenIDs[i2] + nAdjustment; } }
// Adjust current tokens to accomodate new
pwTokenIDs[dwNumNewTokens++] = wID; } else { dwRet = dwRes; goto Exit; } }
// Now we know the encodings. Add them to the key encoding table.
// First make sure that there is enough room in the table.
// ===============================================================
if (m_dwKeyCodesTotalSize - m_dwKeyCodesUsed < dwNumNewTokens + 1) { DWORD dwNewSize = m_dwKeyCodesTotalSize * 2 + dwNumNewTokens + 1; PWORD pTemp = (PWORD) _BtrMemReAlloc(m_pwKeyCodes, dwNewSize * sizeof(WORD)); if (!pTemp) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } m_pwKeyCodes = pTemp; m_dwKeyCodesTotalSize = dwNewSize; }
dwStartingOffset = m_dwKeyCodesUsed;
m_pwKeyCodes[m_dwKeyCodesUsed++] = (WORD) dwNumNewTokens; // First WORD is count of tokens
for (i = 0; i < dwNumNewTokens; i++) // Encoded tokens
m_pwKeyCodes[m_dwKeyCodesUsed++] = pwTokenIDs[i];
// Now, add in the new key lookup by inserting it into the array.
// ==============================================================
dwToBeMoved = m_dwNumKeys - wKeyID;
if (dwToBeMoved) { memmove(&m_pwKeyLookup[wKeyID+1], &m_pwKeyLookup[wKeyID], sizeof(WORD)*dwToBeMoved); memmove(&m_pdwUserData[wKeyID+1], &m_pdwUserData[wKeyID], sizeof(DWORD)*dwToBeMoved); memmove(&m_pdwChildPageMap[wKeyID+1], &m_pdwChildPageMap[wKeyID], (sizeof(DWORD))*(dwToBeMoved+1)); }
m_pwKeyLookup[wKeyID] = (WORD) dwStartingOffset; m_pdwUserData[wKeyID] = dwUserData; m_dwNumKeys++;
dwRet = NO_ERROR;
// Cleanup code.
// =============
Exit: if (pszTemp) _BtrMemFree(pszTemp); FreeTokenArray(dwTokenCount, pszStrings); if (pwTokenIDs) _BtrMemFree(pwTokenIDs);
return dwRet; }
//***************************************************************************
//
// SIdxKeyTable::RemoveKey
//
// Precondition: <wID> is the valid target
//
//***************************************************************************
// ok
DWORD SIdxKeyTable::RemoveKey( WORD wID ) { // Find the key code sequence and remove it.
// =========================================
WORD wKeyCodeStart = m_pwKeyLookup[wID]; DWORD dwToBeMoved = m_dwNumKeys - DWORD(wID) - 1; if (dwToBeMoved) { memmove(&m_pwKeyLookup[wID], &m_pwKeyLookup[wID+1], sizeof(WORD)*dwToBeMoved); memmove(&m_pdwUserData[wID], &m_pdwUserData[wID+1], sizeof(DWORD)*dwToBeMoved); memmove(&m_pdwChildPageMap[wID], &m_pdwChildPageMap[wID+1], sizeof(DWORD)*(dwToBeMoved+1)); } m_dwNumKeys--;
// Zap the key encoding table to remove references to this key.
// ============================================================
WORD wCount = m_pwKeyCodes[wKeyCodeStart]+1; dwToBeMoved = m_dwKeyCodesUsed - (wKeyCodeStart + wCount); if (dwToBeMoved) memmove(&m_pwKeyCodes[wKeyCodeStart], &m_pwKeyCodes[wKeyCodeStart + wCount], sizeof(WORD)*dwToBeMoved); m_dwKeyCodesUsed -= wCount;
// Adjust all zapped key codes referenced by key lookup table.
// ===========================================================
for (DWORD i = 0; i < m_dwNumKeys; i++) { if (m_pwKeyLookup[i] >= wKeyCodeStart) m_pwKeyLookup[i] -= wCount; }
// Now check the string pool & key encoding table for
// unreferenced strings thanks to the above tricks
// and clean up the mess left behind!!
// ==================================================
return Cleanup(); }
//***************************************************************************
//
// Compares the literal string in <pszSearchKey> against the encoded
// string at <nID>. Returns the same value as strcmp().
//
// This is done by decoding the compressed string, token by token, and
// comparing each character to that in the search string.
//
//***************************************************************************
// ok
int SIdxKeyTable::KeyStrCompare( LPSTR pszSearchKey, WORD wID ) { LPSTR pszTrace = pszSearchKey; WORD dwEncodingOffs = m_pwKeyLookup[wID]; WORD wNumTokens = m_pwKeyCodes[dwEncodingOffs]; WORD wStrId = m_pwKeyCodes[++dwEncodingOffs]; LPSTR pszDecoded = m_pStrPool->GetStrById(wStrId); wNumTokens--; int nRes;
while (1) { int nTraceChar = *pszTrace++; int nCodedChar = *pszDecoded++; if (nCodedChar == 0 && wNumTokens) { pszDecoded = m_pStrPool->GetStrById(m_pwKeyCodes[++dwEncodingOffs]); wNumTokens--; nCodedChar = '\\'; } nRes = nTraceChar - nCodedChar; if (nRes || (nTraceChar == 0 && nCodedChar == 0)) return nRes; }
// Identical strings
return 0; }
//***************************************************************************
//
// SIdxKeyTable::FindKey
//
// Finds the key in the key table, if present. If not, returns
// ERROR_NOT_FOUND and <pID> set to the point where it would be if
// later inserted.
//
// Return values:
// ERROR_NOT_FOUND
// NO_ERROR
//
//***************************************************************************
// ready for test
DWORD SIdxKeyTable::FindKey( LPSTR pszSearchKey, WORD *pID ) { if (pszSearchKey == 0 || *pszSearchKey == 0 || pID == 0) return ERROR_INVALID_PARAMETER;
// Binary search the key table.
// ============================
if (m_dwNumKeys == 0) { *pID = 0; return ERROR_NOT_FOUND; }
int nPosition = 0; int l = 0, u = int(m_dwNumKeys) - 1;
while (l <= u) { int m = (l + u) / 2; int nRes;
// m is the current key to consider 0...n-1
try { nRes = KeyStrCompare(pszSearchKey, WORD(m)); } catch(...) { DebugBreak(); nRes = KeyStrCompare(pszSearchKey, WORD(m)); }
// Decide which way to cut the array in half.
// ==========================================
if (nRes < 0) { u = m - 1; nPosition = u + 1; } else if (nRes > 0) { l = m + 1; nPosition = l; } else { // If here, we found the darn thing. Life is good.
// Populate the key unit.
// ================================================
*pID = WORD(m); return NO_ERROR; } }
// Not found, if here. We record where the key should have been
// and tell the user the unhappy news.
// ==============================================================
*pID = WORD(nPosition); // The key would have been 'here'
return ERROR_NOT_FOUND; }
//***************************************************************************
//
//***************************************************************************
// untested
DWORD SIdxKeyTable::Dump(FILE *f, DWORD *pdwKeys) { fprintf(f, "\t|---Begin Key Table Dump---\n");
fprintf(f, "\t| m_dwPageId = %d (0x%X)\n", m_dwPageId, m_dwPageId); fprintf(f, "\t| m_dwParentPageId = %d\n", m_dwParentPageId); fprintf(f, "\t| m_dwNumKeys = %d\n", m_dwNumKeys); fprintf(f, "\t| m_pwKeyLookup = 0x%p\n", m_pwKeyLookup); fprintf(f, "\t| m_dwKeyLookupTotalSize = %d\n", m_dwKeyLookupTotalSize); fprintf(f, "\t| m_pwKeyCodes = 0x%p\n", m_pwKeyCodes); fprintf(f, "\t| m_dwKeyCodesTotalSize = %d\n", m_dwKeyCodesTotalSize); fprintf(f, "\t| m_dwKeyCodesUsed = %d\n", m_dwKeyCodesUsed); fprintf(f, "\t| Required Page Memory = %d\n", GetRequiredPageMemory());
fprintf(f, "\t| --Key Lookup Table\n");
if (pdwKeys) *pdwKeys = m_dwNumKeys;
for (DWORD i = 0; i < m_dwNumKeys; i++) { fprintf(f, "\t| * Left Child Page ------------------------> %d\n", m_pdwChildPageMap[i]); fprintf(f, "\t| KeyID[%d] = offset %d\n", i, m_pwKeyLookup[i]); fprintf(f, "\t| User Data=%d\n", m_pdwUserData[i]);
WORD wKeyEncodingOffset = m_pwKeyLookup[i]; WORD wEncodingUnits = m_pwKeyCodes[wKeyEncodingOffset];
int nPass = 0; fprintf(f, "\t | Key=");
for (DWORD i2 = 0; i2 < DWORD(wEncodingUnits); i2++) { WORD wCode = m_pwKeyCodes[wKeyEncodingOffset + 1 + i2]; if (nPass) fprintf(f, "\\"); fprintf(f, "%s", m_pStrPool->GetStrById(wCode)); nPass++; }
fprintf(f, "\n"); fprintf(f, "\t| Num encoding units = %d\n", wEncodingUnits);
for (DWORD i2 = 0; i2 < DWORD(wEncodingUnits); i2++) { WORD wCode = m_pwKeyCodes[wKeyEncodingOffset + 1 + i2]; fprintf(f, "\t | KeyCode = %d\n", wCode); } }
fprintf(f, "\t| * Rightmost child page -------------------> %d\n", m_pdwChildPageMap[i]); fprintf(f, "\t|---\n");
#ifdef EXTENDED_STRING_TABLE_DUMP
fprintf(f, "\t|---Key Encoding Table\n");
WORD wCurrentSequence = 0; for (i = 0; i < m_dwKeyCodesUsed; i++) { if (wCurrentSequence == 0) { wCurrentSequence = m_pwKeyCodes[i]; fprintf(f, "\t| KeyCode[%d] = %d <count>\n", i, m_pwKeyCodes[i]); continue; } else fprintf(f, "\t| KeyCode[%d] = %d <%s>\n", i, m_pwKeyCodes[i], m_pStrPool->GetStrById(m_pwKeyCodes[i])); wCurrentSequence--; }
fprintf(f, "\t|---End Key Encoding Table---\n"); m_pStrPool->Dump(f); #endif
return 0; }
//***************************************************************************
//
// SIdxStringPool::Dump
//
// Dumps the string pool
//
//***************************************************************************
// tested
DWORD SIdxStringPool::Dump(FILE *f) { try { fprintf(f, "\t\t|| ---String Pool Dump---\n"); fprintf(f, "\t\t|| m_dwNumStrings = %d\n", m_dwNumStrings); fprintf(f, "\t\t|| m_pwOffsets = 0x%p\n", m_pwOffsets); fprintf(f, "\t\t|| m_dwOffsetsSize = %d\n", m_dwOffsetsSize); fprintf(f, "\t\t|| m_pStringPool = 0x%p\n", m_pStringPool); fprintf(f, "\t\t|| m_dwPoolTotalSize = %d\n", m_dwPoolTotalSize); fprintf(f, "\t\t|| m_dwPoolUsed = %d\n", m_dwPoolUsed);
fprintf(f, "\t\t|| --Contents of offsets array--\n");
for (DWORD ix = 0; ix < m_dwNumStrings; ix++) { fprintf(f, "\t\t|| String[%d] = offset %d Value=<%s>\n", ix, m_pwOffsets[ix], m_pStringPool+m_pwOffsets[ix]); }
#ifdef EXTENDED_STRING_TABLE_DUMP
fprintf(f, "\t\t|| --String table--\n");
for (ix = 0; ix < m_dwPoolTotalSize; ix += 20) { fprintf(f, "\t\t || %4d ", ix);
for (int nSubcount = 0; nSubcount < 20; nSubcount++) { if (nSubcount + ix >= m_dwPoolTotalSize) continue;
char c = m_pStringPool[ix+nSubcount]; fprintf(f, "%02x ", c); }
for (int nSubcount = 0; nSubcount < 20; nSubcount++) { if (nSubcount + ix >= m_dwPoolTotalSize) continue;
char c = m_pStringPool[ix+nSubcount]; if (c < 32) { c = '.'; } fprintf(f, "%c ", c); }
fprintf(f, "\n"); } #endif
fprintf(f, "\t\t|| ---End of String Pool Dump\n"); } catch(...) { printf("Exception during dump\n"); } return 0; }
//***************************************************************************
//
// CBTree::Init
//
//***************************************************************************
//
DWORD CBTree::Init( CPageSource *pSrc ) { DWORD dwRes;
if (pSrc == 0) return ERROR_INVALID_PARAMETER;
// Read the logical root page, if any. If the index is just
// being created, create the root index page.
m_pSrc = pSrc; m_pRoot = 0;
DWORD dwRoot = m_pSrc->GetRootPage(); if (dwRoot == 0) { LPDWORD pNewPage = 0;
dwRes = m_pSrc->NewPage((LPVOID *) &pNewPage); if (dwRes) return dwRes;
DWORD dwPageNum = pNewPage[CPageSource::OFFSET_PAGE_ID]; _BtrMemFree(pNewPage); dwRes = SIdxKeyTable::Create(dwPageNum, &m_pRoot); if (dwRes) return dwRes;
m_pSrc->SetRootPage(dwPageNum); dwRes = WriteIdxPage(m_pRoot); } else { // Retrieve existing root
LPVOID pPage = 0; dwRes = m_pSrc->GetPage(dwRoot, &pPage); if (dwRes) return dwRes;
dwRes = SIdxKeyTable::Create(pPage, &m_pRoot); _BtrMemFree(pPage); if (dwRes) return dwRes; }
return dwRes; }
//***************************************************************************
//
// CBTree::CBTree
//
//***************************************************************************
//
CBTree::CBTree() { m_pSrc = 0; m_pRoot = 0; m_lGeneration = 0; InitializeCriticalSection(&m_cs);
CBTreeIterator *m_pIterators; DWORD m_dwActiveIterators; DWORD m_dwIteratorsArraySize;
m_pIterators = 0; m_dwActiveIterators = 0; m_dwIteratorsArraySize = 0; }
//***************************************************************************
//
// CBTree::~CBTree
//
//***************************************************************************
//
CBTree::~CBTree() { if (m_pSrc || m_pRoot) { Shutdown(WMIDB_SHUTDOWN_NET_STOP); }
DeleteCriticalSection(&m_cs); }
//***************************************************************************
//
// CBTree::Shutdown
//
//***************************************************************************
//
DWORD CBTree::Shutdown(DWORD dwShutDownFlags) { DWORD dwRes; if (m_pRoot) { m_pRoot->Release(); m_pRoot = 0; } if (m_pSrc) { dwRes = m_pSrc->Shutdown(dwShutDownFlags); m_pSrc = 0; return dwRes; }
return ERROR_INVALID_FUNCTION; }
//***************************************************************************
//
// CBTree::InsertKey
//
// Inserts the key+data into the tree. Most of the work is done
// in InsertPhase2().
//
//***************************************************************************
// ok
DWORD CBTree::InsertKey( IN LPSTR pszKey, DWORD dwValue ) { WORD wID; DWORD dwRes; SIdxKeyTable *pIdx = 0; LONG StackPtr = -1; DWORD Stack[CBTreeIterator::const_MaxStack];
if (pszKey == 0 || *pszKey == 0) return ERROR_INVALID_PARAMETER;
dwRes = Search(pszKey, &pIdx, &wID, Stack, StackPtr); if (dwRes == 0) { // Ooops. Aleady exists. We can't insert it.
// ===========================================
pIdx->Release(); return ERROR_ALREADY_EXISTS; }
if (dwRes != ERROR_NOT_FOUND) return dwRes;
// If here, we can indeed add it.
// ==============================
dwRes = InsertPhase2(pIdx, wID, pszKey, dwValue, Stack, StackPtr); pIdx->Release();
return dwRes; }
//***************************************************************************
//
// CBTree::ComputeLoad
//
//***************************************************************************
//
DWORD CBTree::ComputeLoad( SIdxKeyTable *pKT ) { DWORD dwMem = pKT->GetRequiredPageMemory(); DWORD dwLoad = dwMem * 100 / m_pSrc->GetPageSize(); return dwLoad; }
//***************************************************************************
//
// CBTree::Search
//
// The actual search occurs here. Descends through the page mechanism.
//
// Returns:
// NO_ERROR <pPage> is assigned, and <pwID> points to the key.
//
// ERROR_NOT_FOUND <pPage> is assigned to where the insert should occur,
// at <pwID> in that page.
//
// Other errors don't assign the OUT parameters.
//
// Note: caller must release <pRetIdx> using Release() when it is returned
// whether with an error code or not.
//
//***************************************************************************
// ok
DWORD CBTree::Search( IN LPSTR pszKey, OUT SIdxKeyTable **pRetIdx, OUT WORD *pwID, DWORD Stack[], LONG &StackPtr ) { DWORD dwRes, dwChildPage, dwPage;
if (pszKey == 0 || *pszKey == 0 || pwID == 0 || pRetIdx == 0) return ERROR_INVALID_PARAMETER; *pRetIdx = 0;
SIdxKeyTable *pIdx = m_pRoot; pIdx->AddRef(); Stack[++StackPtr] = 0;
while (1) { dwRes = pIdx->FindKey(pszKey, pwID); if (dwRes == 0) { // Found it
*pRetIdx = pIdx; return NO_ERROR; }
// Otherwise, we have to try to descend to a child page.
// =====================================================
dwPage = pIdx->GetPageId(); dwChildPage = pIdx->GetChildPage(*pwID); if (dwChildPage == 0) break;
pIdx->Release(); pIdx = 0; Stack[++StackPtr] = dwPage;
dwRes = ReadIdxPage(dwChildPage, &pIdx); if (dwRes) return dwRes; }
*pRetIdx = pIdx;
return ERROR_NOT_FOUND; }
//***************************************************************************
//
// CBTree::InsertPhase2
//
// On entry, assumes that we have identified the page into which
// the insert must physically occur. This does the split + migrate
// logical to keep the tree in balance.
//
// Algorithm: Add key to page. If it does not overflow, we are done.
// If overflow occurs, allocate a new sibling page which will acquire
// half the keys from the current page. This sibling will be treated
// as lexically smaller in all cases. The median key is migrated
// up to the parent with pointers to both the new sibing page and
// the current page.
// The parent may also overflow. If so, the algorithm repeats.
// If an overflow occurs and there is no parent node (we are at the root)
// a new root node is allocated and the median key migrated into it.
//
//***************************************************************************
// ok
DWORD CBTree::InsertPhase2( SIdxKeyTable *pCurrent, WORD wID, LPSTR pszKey, DWORD dwValue, DWORD Stack[], LONG &StackPtr ) { DWORD dwRes;
// If non-NULL, used for a primary insert.
// If NULL, skip this, under the assumption the
// node is already up-to-date and merely requires
// the up-recursive split & migrate treatment.
// ==============================================
if (pszKey) { dwRes = pCurrent->AddKey(pszKey, wID, dwValue); if (dwRes) return dwRes; // Failed
}
pCurrent->AddRef(); // Makes following loop consistent
SIdxKeyTable *pSibling = 0; SIdxKeyTable *pParent = 0;
// The class B-tree split+migration loop.
// ======================================
for (;;) { // Check the current node where we added the key.
// If it isn't too big, we're done.
// ==============================================
dwRes = pCurrent->GetRequiredPageMemory(); if (dwRes <= m_pSrc->GetPageSize()) { dwRes = WriteIdxPage(pCurrent); break; }
// If here, it ain't gonna fit. We have to split the page.
// Allocate a new page (Sibling) and get the parent page, which
// will receive the median key.
// ============================================================
DWORD dwParent = Stack[StackPtr--]; if (dwParent == 0) { // Allocate a new page to become the parent.
LPDWORD pParentPg = 0; dwRes = m_pSrc->NewPage((LPVOID *) &pParentPg); if (dwRes) break;
DWORD dwNewParent = pParentPg[CPageSource::OFFSET_PAGE_ID]; _BtrMemFree(pParentPg);
dwRes = SIdxKeyTable::Create(dwNewParent, &pParent); if (dwRes) break; dwRes = m_pSrc->SetRootPage(dwNewParent); if (dwRes) break;
m_pRoot->Release(); // Replace old root
m_pRoot = pParent; m_pRoot->AddRef(); } else { if (dwParent == m_pRoot->GetPageId()) { pParent = m_pRoot; pParent->AddRef(); } else { dwRes = ReadIdxPage(dwParent, &pParent); if (dwRes) break; } }
// Allocate a new sibling in any case to hold half the keys
// ========================================================
LPDWORD pSibPg = 0; dwRes = m_pSrc->NewPage((LPVOID *) &pSibPg); if (dwRes) break;
DWORD dwNewSib = pSibPg[CPageSource::OFFSET_PAGE_ID]; _BtrMemFree(pSibPg);
dwRes = SIdxKeyTable::Create(dwNewSib, &pSibling); if (dwRes) break;
dwRes = pCurrent->Redist(pParent, pSibling); if (dwRes) break;
dwRes = WriteIdxPage(pCurrent); dwRes |= WriteIdxPage(pSibling);
pCurrent->Release(); pCurrent = 0; pSibling->Release(); pSibling = 0;
if (dwRes) break;
pCurrent = pParent; pParent = 0; }
ReleaseIfNotNULL(pParent); ReleaseIfNotNULL(pCurrent); ReleaseIfNotNULL(pSibling);
return dwRes; }
//***************************************************************************
//
// CBTree::WriteIdxPage
//
// Writes the object to the physical page it is assigned to.
// If the page ID is zero, then it is considered invalid. Further,
// while is it correct to precheck the page size, this function does
// validate with regard to sizes, etc.
//
//***************************************************************************
//
DWORD CBTree::WriteIdxPage( SIdxKeyTable *pIdx ) { DWORD dwRes; DWORD dwPageSize = m_pSrc->GetPageSize(); DWORD dwMem = pIdx->GetRequiredPageMemory(); if (dwMem > dwPageSize) return ERROR_INVALID_PARAMETER;
LPVOID pMem = _BtrMemAlloc(dwPageSize); if (pMem == 0) return ERROR_NOT_ENOUGH_MEMORY;
dwRes = pIdx->MapToPage(pMem); if (dwRes) { _BtrMemFree(pMem); return dwRes; }
dwRes = m_pSrc->PutPage(pMem, CPageSource::PAGE_TYPE_ACTIVE); _BtrMemFree(pMem);
InterlockedIncrement(&m_lGeneration);
// Check for a root update.
// ========================
if (m_pRoot != pIdx && m_pRoot->GetPageId() == pIdx->GetPageId()) { m_pRoot->Release(); m_pRoot = pIdx; m_pRoot->AddRef();
if (m_pSrc->GetRootPage() != m_pRoot->GetPageId()) dwRes = m_pSrc->SetRootPage(m_pRoot->GetPageId()); }
return dwRes; }
//***************************************************************************
//
// CBTree::ReadIdxPage
//
//***************************************************************************
//
DWORD CBTree::ReadIdxPage( DWORD dwPage, SIdxKeyTable **pIdx ) { DWORD dwRes; LPVOID pPage = 0; SIdxKeyTable *p = 0; if (pIdx == 0) return ERROR_INVALID_PARAMETER; *pIdx = 0;
// if (dwPage < MAX_PAGE_HISTORY) // May remove if studies show no caching possible
// ++History[dwPage];
dwRes = m_pSrc->GetPage(dwPage, &pPage); if (dwRes) return dwRes;
dwRes = SIdxKeyTable::Create(pPage, &p); if (dwRes) { _BtrMemFree(pPage); return dwRes; }
_BtrMemFree(pPage); if (dwRes) return dwRes;
*pIdx = p; return dwRes; }
//***************************************************************************
//
// CBTree::FindKey
//
// Does a simple search of a key, returning the user data, if requested.
//
// Typical Return values
// NO_ERROR
// ERROR_NOT_FOUND
//
//***************************************************************************
// ok
DWORD CBTree::FindKey( IN LPSTR pszKey, DWORD *pdwData ) { WORD wID; DWORD dwRes; SIdxKeyTable *pIdx = 0; LONG StackPtr = -1; DWORD Stack[CBTreeIterator::const_MaxStack];
if (pszKey == 0 || *pszKey == 0) return ERROR_INVALID_PARAMETER;
// Search high and low, hoping against hope...
// ===========================================
dwRes = Search(pszKey, &pIdx, &wID, Stack, StackPtr); if (dwRes == 0 && pdwData) { *pdwData = pIdx->GetUserData(wID); }
// If here, we can indeed add it.
// ==============================
ReleaseIfNotNULL(pIdx); return dwRes; }
//***************************************************************************
//
// CBTree::DeleteKey
//
//***************************************************************************
//
DWORD CBTree::DeleteKey( IN LPSTR pszKey ) { DWORD dwRes; LONG StackPtr = -1; DWORD Stack[CBTreeIterator::const_MaxStack]; SIdxKeyTable *pIdx = 0; WORD wId; DWORD dwLoad;
// Find it
// =======
dwRes = Search(pszKey, &pIdx, &wId, Stack, StackPtr); if (dwRes) return dwRes;
// Delete key from from page
// ==========================
if (pIdx->IsLeaf()) { // A leaf node. Remove the key.
// =============================
dwRes = pIdx->RemoveKey(wId);
// Now, check the load and see if it has dropped below 30%.
// Of course, if we are at the root node and it is a leaf,
// we have to pretty much let it go as is...
// ========================================================
dwLoad = ComputeLoad(pIdx); if (dwLoad > const_MinimumLoad || pIdx->GetPageId() == m_pRoot->GetPageId()) { dwRes = WriteIdxPage(pIdx); pIdx->Release(); return dwRes; } } else { // An internal node, so we have to find the successor.
// Since this call may alter the shape of the tree quite
// a bit (the successor may overflow the affected node),
// we have to relocate the successor.
// ====================================================
LPSTR pszSuccessor = 0; BOOL bUnderflow = FALSE; dwRes = ReplaceBySuccessor(pIdx, wId, &pszSuccessor, &bUnderflow, Stack, StackPtr); if (dwRes) return dwRes;
dwRes = InsertPhase2(pIdx, 0, 0, 0, Stack, StackPtr); if (dwRes) return dwRes;
pIdx->Release(); pIdx = 0; StackPtr = -1;
if (bUnderflow == FALSE) { _BtrMemFree(pszSuccessor); return NO_ERROR; }
// If here, the node we extracted the successor from was reduced
// to poverty and underflowed. We have to find it again and
// execute the underflow repair loop.
// =============================================================
dwRes = Search(pszSuccessor, &pIdx, &wId, Stack, StackPtr); _BtrMemFree(pszSuccessor); if (dwRes) return dwRes;
SIdxKeyTable *pSuccessor = 0; dwRes = FindSuccessorNode(pIdx, wId, &pSuccessor, 0, Stack, StackPtr); if (dwRes) return dwRes;
pIdx->Release(); pIdx = pSuccessor; }
// UNDERFLOW REPAIR Loop.
// At this point <pIdx> points to the deepest affected node.
// We need to start working back up the tree and repairing
// the damage. Nodes which have reached zero in size are
// quite a pain. But they aren't half as bad as nodes which claim
// they can recombine with a sibling but really can't. So,
// we either do nothing (the node has enough stuff to be useful),
// collapse with a sibling node or borrow some keys from a sibling
// to ensure all nodes meet the minimum load requirement.
// ===============================================================
SIdxKeyTable *pSibling = 0; SIdxKeyTable *pParent = 0;
for (;;) { DWORD dwParentId = Stack[StackPtr--]; DWORD dwThisId = pIdx->GetPageId();
dwLoad = ComputeLoad(pIdx); if (dwLoad > const_MinimumLoad || dwParentId == 0) { dwRes = WriteIdxPage(pIdx); pIdx->Release(); break; }
// If here the node is getting small. We must collapsed this
// node with a sibling.
// collapse this node and a sibling
dwRes = ReadIdxPage(dwParentId, &pParent);
// Locate a sibling and see if the sibling and the current node
// can be collapsed with leftover space.
// =============================================================
DWORD dwLeftSibling = pParent->GetLeftSiblingOf(pIdx->GetPageId()); DWORD dwRightSibling = pParent->GetRightSiblingOf(pIdx->GetPageId()); DWORD dwSiblingId = 0;
if (dwLeftSibling) { dwRes = ReadIdxPage(dwLeftSibling, &pSibling); dwSiblingId = pSibling->GetPageId(); } else { dwRes = ReadIdxPage(dwRightSibling, &pSibling); dwSiblingId = pSibling->GetPageId(); }
// If here, the node is 'underloaded'. Now we have to
// get the parent and the sibling and collapsed them.
// ===================================================
SIdxKeyTable *pCopy = 0; pIdx->Clone(&pCopy); dwRes = pIdx->Collapse(pParent, pSibling);
// Now we have a different sort of problem, possibly.
// If the collapsed node is too big, we have to try
// a different strategy.
// ===================================================
if (pIdx->GetRequiredPageMemory() > m_pSrc->GetPageSize()) { pIdx->Release(); pParent->Release(); pSibling->Release(); pIdx = pParent = pSibling = 0;
// Reread the pages.
// =================
pIdx = pCopy; dwRes = ReadIdxPage(dwParentId, &pParent); dwRes = ReadIdxPage(dwSiblingId, &pSibling);
// Transfer a key or two from sibling via parent.
// This doesn't change the tree shape, but the
// parent may overflow.
// ==============================================
do { dwRes = pIdx->StealKeyFromSibling(pParent, pSibling); dwLoad = ComputeLoad(pIdx); } while (dwLoad < const_MinimumLoad);
dwRes = WriteIdxPage(pIdx); pIdx->Release(); dwRes = WriteIdxPage(pSibling); pSibling->Release(); dwRes = InsertPhase2(pParent, 0, 0, 0, Stack, StackPtr); pParent->Release(); break; } else // The collapse worked; we can free the sibling page
{ pCopy->Release(); dwRes = m_pSrc->FreePage(pSibling->GetPageId()); pSibling->Release(); }
// If here, the collapse worked.
// =============================
dwRes = WriteIdxPage(pIdx); if (dwRes) { pIdx->Release(); break; }
if (pParent->GetNumKeys() == 0) { // We have replaced the root. Note
// that we transfer the ref count of pIdx to m_pRoot.
DWORD dwOldRootId = m_pRoot->GetPageId(); m_pRoot->Release(); m_pRoot = pIdx;
// Even though we wrote <pIdx> a few lines back,
// a rewrite is required to update internal stuff
// because this has become the new root.
// ==============================================
m_pSrc->SetRootPage(m_pRoot->GetPageId()); dwRes = WriteIdxPage(m_pRoot); m_pSrc->FreePage(dwOldRootId); pParent->Release(); break; }
pIdx->Release(); pIdx = pParent; }
return dwRes; }
//***************************************************************************
//
// CBTree::ReplaceBySuccessor
//
// Removes the wId key in the <pIdx> node, and replaces it with the
// successor.
//
// Precondition: <pIdx> is an internal (non-leaf) node.
//
// Side-effects: <pIdx> may be overflowed and require the InsertPhase2
// treatment. The node from which the successor is extracted is
// written, but may have been reduced to zero keys.
//
//***************************************************************************
//
DWORD CBTree::ReplaceBySuccessor( IN SIdxKeyTable *pIdx, IN WORD wId, OUT LPSTR *pszSuccessorKey, OUT BOOL *pbUnderflowDetected, DWORD Stack[], LONG &StackPtr ) { SIdxKeyTable *pTemp = 0; DWORD dwRes; DWORD dwPredecessorChild;
dwRes = FindSuccessorNode(pIdx, wId, &pTemp, &dwPredecessorChild, Stack, StackPtr); if (dwRes || pTemp == 0) return dwRes;
LPSTR pszKey = 0; pTemp->GetKeyAt(0, &pszKey); DWORD dwUserData = pTemp->GetUserData(0); pTemp->RemoveKey(0); if (ComputeLoad(pTemp) < const_MinimumLoad) *pbUnderflowDetected = TRUE; dwRes = WriteIdxPage(pTemp); pTemp->Release();
pIdx->RemoveKey(wId); pIdx->AddKey(pszKey, wId, dwUserData); pIdx->SetChildPage(wId, dwPredecessorChild);
*pszSuccessorKey = pszKey; StackPtr--; return dwRes; }
//***************************************************************************
//
// CBTree::FindSuccessorNode
//
// Read-only. Finds the node containing the successor to the specified key.
//
//***************************************************************************
//
DWORD CBTree::FindSuccessorNode( IN SIdxKeyTable *pIdx, IN WORD wId, OUT SIdxKeyTable **pSuccessor, OUT DWORD *pdwPredecessorChild, DWORD Stack[], LONG &StackPtr ) { SIdxKeyTable *pTemp = 0; DWORD dwRes; DWORD dwSuccessorChild, dwPredecessorChild;
dwPredecessorChild = pIdx->GetChildPage(wId); dwSuccessorChild = pIdx->GetChildPage(wId+1);
Stack[++StackPtr] = pIdx->GetPageId();
// From this point on, take leftmost children until
// we reach a leaf node. The leftmost key in the
// leftmost node is always the successor, thanks to the
// astonishing properties of the BTree. Nice and easy, huh?
// =========================================================
while (dwSuccessorChild) { Stack[++StackPtr] = dwSuccessorChild; if (pTemp) pTemp->Release(); dwRes = ReadIdxPage(dwSuccessorChild, &pTemp); dwSuccessorChild = pTemp->GetChildPage(0); }
StackPtr--; // Pop the element we are returning in <*pSuccessor>
*pSuccessor = pTemp; if (pdwPredecessorChild) *pdwPredecessorChild = dwPredecessorChild;
return dwRes; }
//***************************************************************************
//
// CBTree::BeginEnum
//
//***************************************************************************
//
DWORD CBTree::BeginEnum( LPSTR pszStartKey, OUT CBTreeIterator **pIterator ) { CBTreeIterator *pIt = new CBTreeIterator; if (pIt == 0) return ERROR_NOT_ENOUGH_MEMORY;
DWORD dwRes = pIt->Init(this, pszStartKey); if (dwRes) { pIt->Release(); return dwRes; }
*pIterator = pIt; return NO_ERROR; }
//***************************************************************************
//
// CBTree::Dump
//
//***************************************************************************
//
void CBTree::Dump(FILE *f) { m_pSrc->Dump(f); }
//***************************************************************************
//
//***************************************************************************
//
DWORD CBTree::InvalidateCache() { m_pRoot->Release(); DWORD dwRootPage = m_pSrc->GetRootPage(); DWORD dwRes = ReadIdxPage(dwRootPage, &m_pRoot); return dwRes; }
//***************************************************************************
//
// CBTreeIterator::Init
//
//***************************************************************************
//
DWORD CBTreeIterator::Init( IN CBTree *pTree, IN LPSTR pszStartKey ) { if (pTree == 0) return ERROR_INVALID_PARAMETER; m_pTree = pTree;
// Special case of enumerating everything. Probably not useful
// for WMI, but great for testing & debugging (I think).
// ============================================================
if (pszStartKey == 0) { Push(0, 0); // Sentinel value in stack
SIdxKeyTable *pRoot = pTree->m_pRoot; pRoot->AddRef(); Push(pRoot, 0);
DWORD dwChildPage = Peek()->GetChildPage(0);
while (dwChildPage) { SIdxKeyTable *pIdx = 0; DWORD dwRes = m_pTree->ReadIdxPage(dwChildPage, &pIdx); if (dwRes) return dwRes; if (StackFull()) { pIdx->Release(); return ERROR_INSUFFICIENT_BUFFER; } Push(pIdx, 0); dwChildPage = pIdx->GetChildPage(0); } return NO_ERROR; }
// If here, a matching string was specified.
// This is the typical case.
// =========================================
Push(0, 0); // Sentinel value in stack
WORD wId; DWORD dwRes, dwChildPage; SIdxKeyTable *pIdx = pTree->m_pRoot; pIdx->AddRef();
while (1) { dwRes = pIdx->FindKey(pszStartKey, &wId); if (dwRes == 0) { // Found it
Push(pIdx, wId); return NO_ERROR; }
// Otherwise, we have to try to descend to a child page.
// =====================================================
dwChildPage = pIdx->GetChildPage(wId); if (dwChildPage == 0) break;
Push(pIdx, wId); pIdx = 0; dwRes = pTree->ReadIdxPage(dwChildPage, &pIdx); if (dwRes) return dwRes; }
Push(pIdx, wId);
while (Peek() && PeekId() == WORD(Peek()->GetNumKeys())) Pop();
return NO_ERROR; }
//***************************************************************************
//
// CBTreeIterator::Next
//
// On entry:
// <wID> is the key to visit in the current node (top-of-stack).
// The call sets up the successor before leaving. If there is no successor,
// the top of stack is left at NULL and ERROR_NO_MORE_ITEMS is returned.
//
// Returns ERROR_NO_MORE_ITEMS when the iteration is complete.
//
//***************************************************************************
//
DWORD CBTreeIterator::Next( LPSTR *ppszStr, DWORD *pdwData ) { DWORD dwRes;
if (ppszStr == 0) return ERROR_INVALID_PARAMETER; *ppszStr = 0;
if (Peek() == 0) return ERROR_NO_MORE_ITEMS;
// Get the item for the caller.
// ============================
dwRes = Peek()->GetKeyAt(PeekId(), ppszStr); if (dwRes) return dwRes; if (pdwData) *pdwData = Peek()->GetUserData(PeekId()); IncStackId();
// Now find the successor.
// =======================
DWORD dwChildPage = Peek()->GetChildPage(PeekId());
while (dwChildPage) { SIdxKeyTable *pIdx = 0; dwRes = m_pTree->ReadIdxPage(dwChildPage, &pIdx); if (dwRes) return dwRes; if (StackFull()) { pIdx->Release(); return ERROR_INSUFFICIENT_BUFFER; } Push(pIdx, 0); dwChildPage = pIdx->GetChildPage(0); }
// If here, we are at a leaf node.
// ===============================
while (Peek() && PeekId() == WORD(Peek()->GetNumKeys())) Pop();
return NO_ERROR; }
//***************************************************************************
//
// CBTreeIterator::Release
//
//***************************************************************************
//
DWORD CBTreeIterator::Release() { delete this; return 0; }
//***************************************************************************
//
// CBTreeIterator::~CBTreeIterator
//
//***************************************************************************
//
CBTreeIterator::~CBTreeIterator() { // Cleanup any leftover stack
while (m_lStackPointer > -1) Pop(); }
|