You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
10 KiB
274 lines
10 KiB
/*++
|
|
|
|
Copyright (C) 2000-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
VarObjHeap.H
|
|
|
|
Abstract:
|
|
|
|
Implements the storage of variable length objects over the top of of a fixed
|
|
length page system. It keeps a set of admin pages for holding the pages active
|
|
by this subsystem, along with how much space is used on each. When a page becomes
|
|
empty it frees up the page to the page system. It also deals with blocks that span
|
|
multiple pages
|
|
|
|
History:
|
|
paulall 02-Feb-2001 Created
|
|
|
|
--*/
|
|
|
|
#include <unk.h>
|
|
#include <arrtempl.h>
|
|
#include <statsync.h>
|
|
|
|
class CPageFile;
|
|
class CPageSource;
|
|
|
|
#define VAROBJ_VERSION 1
|
|
|
|
//**************************************************************************************
|
|
//VarObjAdminPageEntry - This is a structure that is stored within the
|
|
//m_aAdminPages cache. It has an entry for each of the admin pages
|
|
//that we cache. It stores the PageId (0 for the first one!),
|
|
//pointer to the actual page, and a flag to determine if we need to
|
|
//flush it next time around.
|
|
//**************************************************************************************
|
|
typedef struct _VarObjAdminPageEntry
|
|
{
|
|
DWORD dwPageId;
|
|
BYTE *pbPage;
|
|
bool bDirty;
|
|
} VarObjAdminPageEntry;
|
|
|
|
//**************************************************************************************
|
|
//VarObjObjOffsetEntry: There is an array of these objects stored at the
|
|
//start of the object page to point out where each object is stored.
|
|
//If this is a continuation block we do not have one of these, however
|
|
//continuation blocks have consecutive pageIds so it should be fairly easy
|
|
//to conclude
|
|
//**************************************************************************************
|
|
typedef struct _VarObjObjOffsetEntry
|
|
{
|
|
DWORD dwOffsetId;
|
|
DWORD dwPhysicalStartOffset;
|
|
DWORD dwBlockLength;
|
|
DWORD dwCRC;
|
|
} VarObjObjOffsetEntry;
|
|
|
|
//**************************************************************************************
|
|
//VarObjHeapAdminPage - This is the header of each of the admin pages
|
|
//that are stored in the object file. The version is only relevant
|
|
//in the first page (page 0). The last entry is a buffer to make
|
|
//it 4-DWORD structure rather than 3. May use it at a later date.
|
|
//Should always set it to 0 for now.
|
|
//**************************************************************************************
|
|
typedef struct _VarObjHeapAdminPage
|
|
{
|
|
DWORD dwVersion;
|
|
DWORD dwNextAdminPage;
|
|
DWORD dwNumberEntriesOnPage;
|
|
|
|
//VarObjHeapFreeList aFreeListEntries[dwNumberEntriesOnPage];
|
|
} VarObjHeapAdminPage;
|
|
|
|
|
|
//**************************************************************************************
|
|
//VarObjHeapFreeList - This structure follows the admin page header
|
|
//and there is an entry for each page we use to store objects. The
|
|
//page may not be full, so we do not shuffle items on a second page
|
|
//to this page when we delete an entry.
|
|
//**************************************************************************************
|
|
typedef struct _VarObjHeapFreeList
|
|
{
|
|
DWORD dwPageId;
|
|
DWORD dwFreeSpace;
|
|
DWORD dwCRC32;
|
|
DWORD dwReserved;
|
|
} VarObjHeapFreeList;
|
|
|
|
//**************************************************************************************
|
|
//CVarObjHeap - This is the implementation of the variable sized object store
|
|
//over the top of the transacted fixed page manager. It tracks the admin pages
|
|
//that hold all pages we use to store objects in (it caches these pages), and
|
|
//also manages cases when an object is too big to fit on a single page.
|
|
//**************************************************************************************
|
|
class CVarObjHeap
|
|
{
|
|
private:
|
|
//Current status of admin page
|
|
enum
|
|
{
|
|
NoError = 0,
|
|
AdminPageReadFailure = 1,
|
|
RootAdminPageCreationFailure = 2,
|
|
AdminPagesNeedReading = 3
|
|
} m_dwStatus;
|
|
|
|
//Pointer to the transacted file for the object storage
|
|
CPageFile *m_pObjectFile;
|
|
|
|
//Page size used within the object storage file.
|
|
DWORD m_dwPageSize;
|
|
|
|
|
|
//Admin page structure
|
|
CLockableFlexArray<CStaticCritSec> m_aAdminPages;
|
|
|
|
protected:
|
|
//Adds an allocation to the end of the existing allocations
|
|
DWORD AllocateFromPage(/* in */ DWORD dwPageId,
|
|
/* in */ BYTE *pbPage,
|
|
/* in */ ULONG ulBlockSize,
|
|
/* in */ const BYTE *pBlock,
|
|
/* out*/ ULONG *pdwNewOffset);
|
|
|
|
//Allocates a multi-page entry in the object file. This requires
|
|
//different algorithms to work things out so is a special case
|
|
DWORD AllocateMultiPageBuffer(/* in */ ULONG ulBlockSize,
|
|
/* in */ const BYTE *pBlock,
|
|
/* out */ ULONG *pulPageId,
|
|
/* out */ ULONG *pulOffsetId);
|
|
|
|
//Given and offsetId and a page, calculate the physical pointer to the object and also
|
|
//return the size of the block
|
|
DWORD OffsetToPointer(/* in */ ULONG ulOffsetId,
|
|
/* in */ BYTE *pbPage,
|
|
/* out*/ BYTE **pOffsetPointer,
|
|
/* out*/ ULONG *pdwBlockSize,
|
|
/* out*/ DWORD *pdwCRC32);
|
|
|
|
//Reads the admin pages into memory and marks them as clean (no changes)
|
|
//setting bReReadPages to false has an affect of clearing the pages out
|
|
DWORD ReadAdminPages(CPageSource *pTransactionManager, bool bReReadPages);
|
|
|
|
//Writes each of the changed admin pages back to the object file
|
|
DWORD FlushAdminPages();
|
|
|
|
//Find a page form the admin pages that can accomodate a particular buffer size
|
|
DWORD FindPageWithSpace(/* in */ DWORD dwRequiredSize,
|
|
/* out*/ DWORD *pdwPageId);
|
|
|
|
//Allocate a new page for use with objects. A buffer for the new page is passed
|
|
//in, however the PageId of this page is passed out
|
|
DWORD AllocateNewPage(/* in */ DWORD ulBlockSize,
|
|
/* out*/ DWORD *dwPageId,
|
|
/* in */ BYTE *pbNewObjectPage);
|
|
|
|
//Deletes a page, and updates the admin pages as appropriage
|
|
DWORD DeletePage(/* in */ DWORD ulPageId);
|
|
|
|
//DeleteFromPage - removes an object from a specific object page
|
|
DWORD RemoveFromPage(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulOffsetId,
|
|
/* in */ BYTE *pbPage,
|
|
/* out*/ DWORD *pdwSize);
|
|
|
|
//MultiPageObject - returns true if the provided page is the first page
|
|
//of a multi-page object
|
|
bool MultiPageObject(/* in */ BYTE *pbPage) { return ((VarObjObjOffsetEntry*) pbPage)->dwBlockLength > (m_dwPageSize - (sizeof(VarObjObjOffsetEntry) * 2)); }
|
|
|
|
//DeleteMultiPageBuffer - handles the deletion of an object when it spans
|
|
//multiple pages
|
|
DWORD DeleteMultiPageBuffer(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulOffsetId,
|
|
/* in */ BYTE *pbPage);
|
|
|
|
//UpdateAdminPageForAllocate - Updates the admin page to decrement the amount
|
|
//of free space on a page by this amount ( + sizeof(VarObjObjOffsetEntry))
|
|
DWORD UpdateAdminPageForAllocate(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulBlockSize,
|
|
/* in */ DWORD dwCRC32);
|
|
DWORD UpdateAdminPageForAllocate2(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulFreeSpaceOnPage,
|
|
/* in */ DWORD dwCRC32);
|
|
|
|
//UpdateAdminPageForDelete - Updates the admin page for giving space back. If
|
|
//the page is totally empty we should delete the page altogether
|
|
DWORD UpdateAdminPageForDelete(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulBlockSize,
|
|
/* in */ DWORD dwCRC32,
|
|
/* out */ bool *pbPageDeleted);
|
|
|
|
//Removes an object page entry from an admin page, removing the
|
|
//admin page if it is no longer needed
|
|
DWORD RemoveEntryFromAdminPage(/* in */ DWORD dwAdminPageIndex,
|
|
/* in */ DWORD dwAdminPageEntry);
|
|
|
|
//Returns a CRC based on a given block of memory
|
|
#define FINALIZE_CRC32(x) (x=~x)
|
|
DWORD CreateCRC32(/* in */ const BYTE *pBlock,
|
|
/* in */ DWORD dwSize,
|
|
/* in */ DWORD dwPreviousCRC = (DWORD) -1); // Must be 0xFFFFFFFF if no previous CRC
|
|
|
|
//Given a page we validate that there is in fact enough space
|
|
//for this block. If there is not it asserts. This implies
|
|
//that the admin page is not in sync with the actual pages.
|
|
DWORD ValidatePageFreeSpace(/* in */ const BYTE *pbPage,
|
|
/* in */ DWORD ulBlockSize,
|
|
/* out */ DWORD *pulFreeSpaceLeftOnPage);
|
|
|
|
#ifdef DBG
|
|
//Given a page and a page ID, it validates the amount of free space
|
|
//on the page is equal to the amount the admin page thinks is on
|
|
//there.
|
|
DWORD ValidatePageFreeSpaceWithAdminPage(/* in */ const BYTE *pbPage,
|
|
/* in */ DWORD ulPageId);
|
|
|
|
//Dumps the offset table of a page to the debugger
|
|
DWORD DumpPageOffsetTable(/* in */ DWORD dwPageId,
|
|
/* in */ const BYTE *pbPage);
|
|
|
|
//Checks the CRCs of all objects on a page (cannot do this
|
|
//for a multi-page object though as we only have the first
|
|
//page!)
|
|
DWORD ValidateAllCRC32OnPage(/* in */ const BYTE *pbPage);
|
|
|
|
//Validates the page check-sum with the admin page
|
|
DWORD ValidatePageCRCWithAdminPage(/* in */ const BYTE *pbPage,
|
|
/* in */ DWORD dwPageId);
|
|
#endif /* DBG */
|
|
|
|
public:
|
|
CVarObjHeap();
|
|
~CVarObjHeap();
|
|
|
|
DWORD Initialize(CPageSource *pPageManager);
|
|
DWORD Shutdown(DWORD dwShutdownType);
|
|
|
|
//Re-read admin pages
|
|
DWORD InvalidateCache();
|
|
|
|
//Discard admin pages
|
|
DWORD FlushCaches();
|
|
|
|
//ReadBuffer pages the virtual page and offset of the block and returns a new[]-ed block
|
|
DWORD ReadBuffer(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulOffsetId,
|
|
/* out */ BYTE **ppReturnedBlock,
|
|
/* out */ DWORD *pdwBlockSize);
|
|
|
|
//WriteNewBuffer will write a new page based on size of BYTE *, and return the
|
|
//new virtual pageId and offsetId of the block.
|
|
DWORD WriteNewBuffer(/* in */ ULONG ulBlockSize,
|
|
/* in */ const BYTE *pBlock,
|
|
/* out */ ULONG *pulPageId,
|
|
/* out */ ULONG *pulOffsetId);
|
|
|
|
//WriteExistingBuffer will update an existing block with new data. The old virtual page
|
|
//and offset are passed in, and new ones are returned. They may or may not be the same
|
|
//depending on if it still fits in the page or not.
|
|
DWORD WriteExistingBuffer(/* in */ ULONG ulBlockSize,
|
|
/* in */ const BYTE *pBlock,
|
|
/* in */ ULONG ulOldPageId,
|
|
/* in */ ULONG ulOldOffsetId,
|
|
/* out */ ULONG *pulNewPageId,
|
|
/* out */ ULONG *pulNewOffsetId);
|
|
|
|
//DeleteBuffer is called to delete the item in the store given the virtual pageId and
|
|
//offsetId.
|
|
DWORD DeleteBuffer(/* in */ ULONG ulPageId,
|
|
/* in */ ULONG ulOffsetId);
|
|
};
|
|
|