mirror of https://github.com/tongzx/nt5src
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.
704 lines
17 KiB
704 lines
17 KiB
#include <wbemcomn.h>
|
|
#include "a51tools.h"
|
|
#include "heap.h"
|
|
|
|
#define ROSWELL_MAIN_FILE_SUFFIX L".hea"
|
|
#define ROSWELL_FREE_FILE_SUFFIX L".fre"
|
|
#define ROSWELL_INFINITE_BLOCK_SIZE 0x7FFFFFFF
|
|
|
|
#define A51_HEAP_MAIN_FILE_INDEX 0
|
|
#define A51_HEAP_FREE_FILE_INDEX 1
|
|
|
|
CFileHeap::~CFileHeap()
|
|
{
|
|
}
|
|
|
|
|
|
long CFileHeap::Initialize( CAbstractFileSource* pSource,
|
|
LPCWSTR wszFileNameBase)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (m_bInit)
|
|
return ERROR_SUCCESS;
|
|
|
|
long lRes;
|
|
|
|
//
|
|
// Open both files
|
|
//
|
|
|
|
WCHAR wszMainFilePath[MAX_PATH+1];
|
|
wcscpy(wszMainFilePath, wszFileNameBase);
|
|
wcscat(wszMainFilePath, ROSWELL_MAIN_FILE_SUFFIX);
|
|
|
|
HANDLE h;
|
|
h = CreateFileW(wszMainFilePath, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ, NULL, OPEN_ALWAYS,
|
|
FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED,
|
|
NULL);
|
|
if(h == INVALID_HANDLE_VALUE)
|
|
{
|
|
lRes = GetLastError();
|
|
_ASSERT(lRes != ERROR_SUCCESS, L"Success from failure");
|
|
return lRes;
|
|
}
|
|
|
|
lRes = pSource->Register(h, A51_HEAP_MAIN_FILE_INDEX, false, &m_pMainFile);
|
|
if (ERROR_SUCCESS != lRes)
|
|
{
|
|
return lRes;
|
|
}
|
|
|
|
WCHAR wszFreeFilePath[MAX_PATH+1];
|
|
wcscpy(wszFreeFilePath, wszFileNameBase);
|
|
wcscat(wszFreeFilePath, ROSWELL_FREE_FILE_SUFFIX);
|
|
|
|
h = CreateFileW(wszFreeFilePath, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ, NULL, OPEN_ALWAYS,
|
|
FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED,
|
|
NULL);
|
|
if(h == INVALID_HANDLE_VALUE)
|
|
{
|
|
lRes = GetLastError();
|
|
_ASSERT(lRes != ERROR_SUCCESS, L"Success from failure");
|
|
return lRes;
|
|
}
|
|
|
|
lRes = pSource->Register(h, A51_HEAP_FREE_FILE_INDEX, false, &m_pFreeFile);
|
|
if (ERROR_SUCCESS != lRes)
|
|
{
|
|
return lRes;
|
|
}
|
|
|
|
|
|
//
|
|
// Read in the free list
|
|
//
|
|
|
|
m_bInit = TRUE; // set the status here, otherwise it will fail
|
|
|
|
lRes = ReadFreeList();
|
|
if(lRes != ERROR_SUCCESS)
|
|
{
|
|
m_bInit = FALSE;
|
|
return lRes;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
long CFileHeap::Uninitialize()
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return ERROR_SUCCESS;
|
|
|
|
if (m_pMainFile)
|
|
{
|
|
delete m_pMainFile;
|
|
m_pMainFile = NULL;
|
|
}
|
|
if (m_pFreeFile)
|
|
{
|
|
delete m_pFreeFile;
|
|
m_pFreeFile = NULL;
|
|
}
|
|
|
|
m_mapFree.clear();
|
|
m_mapFreeOffset.clear();
|
|
|
|
|
|
m_bInit = FALSE;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// this is a private function that uses a critsec,
|
|
// but the public function invalidate cache does not use it
|
|
//
|
|
long CFileHeap::ReadFreeList()
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
long lRes;
|
|
|
|
//
|
|
// Clear it out
|
|
//
|
|
|
|
m_mapFree.clear();
|
|
m_mapFreeOffset.clear();
|
|
|
|
DWORD dwSize;
|
|
|
|
//
|
|
// Figure out the size of the free list file
|
|
//
|
|
|
|
lRes = m_pFreeFile->GetFileLength(&dwSize);
|
|
|
|
if(dwSize > 0)
|
|
{
|
|
//
|
|
// Figure out how many records that is
|
|
//
|
|
|
|
_ASSERT(dwSize % GetFreeListRecordSize() == 0,
|
|
L"Wrong length of free file");
|
|
|
|
int nNumRecords = dwSize / GetFreeListRecordSize();
|
|
|
|
//ERRORTRACE((LOG_WBEMCORE, "Rereading %d records\n", nNumRecords));
|
|
|
|
BYTE* aBuffer = (BYTE*)TempAlloc(dwSize);
|
|
if(aBuffer == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
CTempFreeMe tfm(aBuffer);
|
|
|
|
lRes = ReadFromFreeFile(0, aBuffer, dwSize);
|
|
if(lRes != ERROR_SUCCESS)
|
|
{
|
|
return lRes;
|
|
}
|
|
|
|
//
|
|
// Insert them all into the map
|
|
//
|
|
|
|
BYTE* pCurrent = aBuffer;
|
|
for(int i = 0; i < nNumRecords; i++)
|
|
{
|
|
DWORD dwSize;
|
|
TOffset nOffset;
|
|
|
|
memcpy(&dwSize, pCurrent, sizeof(DWORD));
|
|
memcpy(&nOffset, pCurrent + sizeof(DWORD), sizeof(TOffset));
|
|
|
|
//ERRORTRACE((LOG_WBEMCORE, "Offset %d, Size %d\n", nOffset, dwSize));
|
|
|
|
pCurrent += GetFreeListRecordSize();
|
|
|
|
try
|
|
{
|
|
m_mapFree.insert(TFreeValue(dwSize, CRecordInfo(i, nOffset)));
|
|
if(dwSize)
|
|
m_mapFreeOffset.insert(TFreeOffsetValue(nOffset, dwSize));
|
|
}
|
|
catch(...)
|
|
{
|
|
ERRORTRACE((LOG_WBEMCORE, "Crash1\n"));
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Populate with an inifinite block
|
|
//
|
|
|
|
lRes = InsertFreeBlock(0, ROSWELL_INFINITE_BLOCK_SIZE);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
long CFileHeap::InvalidateCache()
|
|
{
|
|
return ReadFreeList();
|
|
}
|
|
|
|
long CFileHeap::FindNextFree(TOffset nCurrentOffset, TOffset* pnNextFreeOffset,
|
|
DWORD* pdwNextFreeLength)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
TFreeOffsetIterator it = m_mapFreeOffset.lower_bound(nCurrentOffset);
|
|
_ASSERT(it != m_mapFreeOffset.end(), L"Missing end-block");
|
|
|
|
*pnNextFreeOffset = it->first;
|
|
*pdwNextFreeLength = it->second;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD CFileHeap::GetFreeListRecordSize()
|
|
{
|
|
return sizeof(DWORD) // size
|
|
+ sizeof(TOffset); // offset
|
|
}
|
|
|
|
long CFileHeap::Allocate(DWORD dwLength, TOffset* pnOffset)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
long lRes;
|
|
|
|
//
|
|
// Inform transaction manager that we are doing stuff in this transaction,
|
|
// so that real rollback needs to be performed in case of failure
|
|
//
|
|
|
|
m_pMainFile->Touch();
|
|
|
|
//
|
|
// Find the smallest number not smaller than dwLength in the map
|
|
//
|
|
|
|
TFreeIterator it = m_mapFree.lower_bound(dwLength);
|
|
|
|
_ASSERT(it != m_mapFree.end(), L"Missing infinite block");
|
|
|
|
//
|
|
// Cut it off
|
|
//
|
|
|
|
*pnOffset = it->second.m_nOffset;
|
|
DWORD dwBlockSize = it->first;
|
|
_ASSERT(dwBlockSize >= dwLength, L"Found a smaller block");
|
|
|
|
DWORD dwSig = 0;
|
|
lRes = ReadBytes(*pnOffset, (BYTE*)&dwSig, sizeof(DWORD));
|
|
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
|
|
/*
|
|
_ASSERT(dwSig == 0x51515151, L"Mismatched block");
|
|
|
|
|
|
ERRORTRACE((LOG_WBEMCORE, "Allocate %d at %d out of %d\n",
|
|
(int)dwLength, (int)*pnOffset, (int)dwBlockSize));
|
|
*/
|
|
|
|
|
|
//
|
|
// Real block --- remove it and insert a shorter one (unless exact)
|
|
//
|
|
|
|
if(dwBlockSize != dwLength)
|
|
{
|
|
lRes = ReplaceFreeBlockByFreeIt(it, *pnOffset + dwLength,
|
|
dwBlockSize - dwLength);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
else
|
|
{
|
|
lRes = EraseFreeBlockByFreeIt(it);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
long CFileHeap::FreeAllocation(TOffset nOffset, DWORD dwLength)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
long lRes;
|
|
|
|
if(dwLength == 0)
|
|
return ERROR_SUCCESS;
|
|
|
|
//
|
|
// Inform transaction manager that we are doing stuff in this transaction,
|
|
// so that real rollback needs to be performed in case of failure
|
|
//
|
|
|
|
m_pMainFile->Touch();
|
|
|
|
//
|
|
// First, see if there is a free block after us
|
|
//
|
|
|
|
int nExtraLengthAtEnd = 0;
|
|
TFreeOffsetIterator itAfter = m_mapFreeOffset.lower_bound(nOffset);
|
|
_ASSERT(itAfter != m_mapFreeOffset.end(), L"Missing end-block");
|
|
_ASSERT(itAfter->first != nOffset, L"Found block we are deallocating "
|
|
L"in free list");
|
|
|
|
// ERRORTRACE((LOG_WBEMCORE, "Free %d at %d\n", (int)dwLength, (int)nOffset));
|
|
|
|
//
|
|
// See if it starts at our end
|
|
//
|
|
|
|
if(itAfter->first == nOffset + dwLength)
|
|
{
|
|
nExtraLengthAtEnd = itAfter->second;
|
|
}
|
|
|
|
//
|
|
// Now, check the block before us
|
|
//
|
|
|
|
int nExtraLengthInFront = 0;
|
|
TFreeOffsetIterator itBefore = itAfter;
|
|
if(itBefore != m_mapFreeOffset.begin())
|
|
{
|
|
itBefore--;
|
|
|
|
//
|
|
// See if it ends at our start
|
|
//
|
|
|
|
if(itBefore->first + itBefore->second == nOffset)
|
|
{
|
|
nExtraLengthInFront = itBefore->second;
|
|
}
|
|
}
|
|
|
|
if(nExtraLengthInFront == 0 && nExtraLengthAtEnd == 0)
|
|
{
|
|
//
|
|
// No coalescing.
|
|
//
|
|
|
|
lRes = InsertFreeBlock(nOffset, dwLength);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We shall coalesce this block into our neighbor
|
|
//
|
|
|
|
if(nExtraLengthInFront && nExtraLengthAtEnd)
|
|
{
|
|
//
|
|
// Coalesce all three
|
|
//
|
|
|
|
TOffset nNewOffset = itBefore->first;
|
|
DWORD dwNewLength = itBefore->second + dwLength + itAfter->second;
|
|
|
|
lRes = EraseFreeBlockByOffsetIt(itBefore);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
|
|
//
|
|
// NOTE: it is very important that we Replace the block that
|
|
// follows us, since the very last block is special, and we never
|
|
// want to erase it.
|
|
//
|
|
|
|
lRes = ReplaceFreeBlockByOffsetIt(itAfter, nNewOffset, dwNewLength);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
else if(nExtraLengthInFront)
|
|
{
|
|
//
|
|
// Collapse into the front block
|
|
//
|
|
|
|
TOffset nNewOffset = itBefore->first;
|
|
DWORD dwNewLength = itBefore->second + dwLength;
|
|
|
|
lRes = ReplaceFreeBlockByOffsetIt(itBefore, nNewOffset,
|
|
dwNewLength);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Collapse into the back block
|
|
//
|
|
|
|
TOffset nNewOffset = nOffset;
|
|
DWORD dwNewLength = itAfter->second + dwLength;
|
|
|
|
lRes = ReplaceFreeBlockByOffsetIt(itAfter, nNewOffset, dwNewLength);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
long CFileHeap::ReadBytes(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
return m_pMainFile->Read(nOffset, pBuffer, dwLength, NULL);
|
|
}
|
|
|
|
long CFileHeap::WriteBytes(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
// ERRORTRACE((LOG_WBEMCORE, "Write %d bytes at %d\n", (int)dwLength, (int)nOffset));
|
|
return m_pMainFile->Write(nOffset, pBuffer, dwLength, NULL);
|
|
}
|
|
|
|
//
|
|
//
|
|
// here are the private functions
|
|
// the private finctions are supposed to be called by the public ones
|
|
// that are guarded by a Critical Section
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
long CFileHeap::InsertFreeBlock(TOffset nOffset, DWORD dwLength)
|
|
{
|
|
//
|
|
// We need to find an empty record --- they are the ones with 0 length
|
|
//
|
|
|
|
DWORD dwIndex;
|
|
|
|
TFreeIterator it = m_mapFree.find(0);
|
|
if(it == m_mapFree.end())
|
|
{
|
|
//
|
|
// No free records --- just add one.
|
|
//
|
|
|
|
dwIndex = m_mapFree.size();
|
|
}
|
|
else
|
|
{
|
|
dwIndex = it->second.m_dwIndex;
|
|
try
|
|
{
|
|
m_mapFree.erase(it);
|
|
}
|
|
catch(...)
|
|
{
|
|
ERRORTRACE((LOG_WBEMCORE, "Crash2\n"));
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
CRecordInfo Info(dwIndex, nOffset);
|
|
try
|
|
{
|
|
m_mapFree.insert(TFreeValue(dwLength, Info));
|
|
m_mapFreeOffset.insert(TFreeOffsetValue(nOffset, dwLength));
|
|
}
|
|
catch(...)
|
|
{
|
|
ERRORTRACE((LOG_WBEMCORE, "Crash3\n"));
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
return WriteAllocationRecordToDisk(Info, dwLength);
|
|
}
|
|
|
|
|
|
CFileHeap::TFreeOffsetIterator CFileHeap::GetOffsetIteratorFromFree(
|
|
TFreeIterator itFree)
|
|
{
|
|
return m_mapFreeOffset.find(itFree->second.m_nOffset);
|
|
}
|
|
|
|
CFileHeap::TFreeIterator CFileHeap::GetFreeIteratorFromOffset(
|
|
TFreeOffsetIterator itOffset)
|
|
{
|
|
//
|
|
// Can't just look it up --- walk all entries of this size looking for the
|
|
// right offset
|
|
//
|
|
|
|
TOffset nOffset = itOffset->first;
|
|
DWORD dwSize = itOffset->second;
|
|
TFreeIterator itFree = m_mapFree.lower_bound(dwSize);
|
|
while(itFree != m_mapFree.end() && itFree->first == dwSize &&
|
|
itFree->second.m_nOffset != nOffset)
|
|
{
|
|
itFree++;
|
|
}
|
|
|
|
return itFree;
|
|
}
|
|
|
|
long CFileHeap::EraseFreeBlockByFreeIt(CFileHeap::TFreeIterator itFree)
|
|
{
|
|
return EraseFreeBlock(itFree, GetOffsetIteratorFromFree(itFree));
|
|
}
|
|
|
|
long CFileHeap::EraseFreeBlockByOffsetIt(CFileHeap::TFreeOffsetIterator itFreeOffset)
|
|
{
|
|
return EraseFreeBlock(GetFreeIteratorFromOffset(itFreeOffset),
|
|
itFreeOffset);
|
|
}
|
|
|
|
long CFileHeap::EraseFreeBlock(CFileHeap::TFreeIterator itFree,
|
|
CFileHeap::TFreeOffsetIterator itOffset)
|
|
{
|
|
_ASSERT(itFree != m_mapFree.end() && itOffset != m_mapFreeOffset.end(),
|
|
L"Erasing a block that's not in one of the maps");
|
|
|
|
//
|
|
// Move the block to the free free-block list
|
|
//
|
|
|
|
CRecordInfo Info = itFree->second;
|
|
Info.m_nOffset = ROSWELL_INVALID_OFFSET;
|
|
try
|
|
{
|
|
m_mapFree.erase(itFree);
|
|
m_mapFree.insert(TFreeValue(0, Info));
|
|
m_mapFreeOffset.erase(itOffset);
|
|
}
|
|
catch(...)
|
|
{
|
|
ERRORTRACE((LOG_WBEMCORE, "Crash4\n"));
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
return WriteAllocationRecordToDisk(Info, 0);
|
|
}
|
|
|
|
long CFileHeap::ReplaceFreeBlockByFreeIt(CFileHeap::TFreeIterator itFree, TOffset nOffset,
|
|
DWORD dwSize)
|
|
{
|
|
return ReplaceFreeBlock(itFree, GetOffsetIteratorFromFree(itFree),
|
|
nOffset, dwSize);
|
|
}
|
|
|
|
long CFileHeap::ReplaceFreeBlockByOffsetIt(CFileHeap::TFreeOffsetIterator itFreeOffset,
|
|
TOffset nOffset, DWORD dwSize)
|
|
{
|
|
return ReplaceFreeBlock(GetFreeIteratorFromOffset(itFreeOffset),
|
|
itFreeOffset, nOffset, dwSize);
|
|
}
|
|
long CFileHeap::ReplaceFreeBlock(CFileHeap::TFreeIterator itFree,
|
|
CFileHeap::TFreeOffsetIterator itOffset,
|
|
TOffset nOffset, DWORD dwSize)
|
|
{
|
|
long lRes;
|
|
|
|
//
|
|
// Check if the offset we are replacing is the ending offset.
|
|
//
|
|
|
|
bool bTruncate = false;
|
|
TFreeOffsetIterator itNext = itOffset;
|
|
itNext++;
|
|
if(itNext == m_mapFreeOffset.end())
|
|
{
|
|
if(nOffset < itOffset->first)
|
|
bTruncate = true;
|
|
}
|
|
|
|
//
|
|
// Compute the info block --- old index, new offset
|
|
//
|
|
|
|
CRecordInfo Info = itFree->second;
|
|
Info.m_nOffset = nOffset;
|
|
|
|
try
|
|
{
|
|
//
|
|
// If the size changed, remove and insert an entry into the by-size
|
|
// map. Otherwise, just change the info there
|
|
//
|
|
|
|
if(itFree->first != dwSize)
|
|
{
|
|
m_mapFree.erase(itFree);
|
|
m_mapFree.insert(TFreeValue(dwSize, Info));
|
|
}
|
|
else
|
|
{
|
|
itFree->second = Info;
|
|
}
|
|
|
|
//
|
|
// If the offset changed, remove and insert an entry into the by-offset
|
|
// map. Otherwise, just change the info there
|
|
//
|
|
|
|
if(itOffset->first != nOffset)
|
|
{
|
|
m_mapFreeOffset.erase(itOffset);
|
|
m_mapFreeOffset.insert(TFreeOffsetValue(Info.m_nOffset, dwSize));
|
|
}
|
|
else
|
|
{
|
|
itOffset->second = dwSize;
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
ERRORTRACE((LOG_WBEMCORE, "Crash5\n"));
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
if(bTruncate && 0)
|
|
{
|
|
//
|
|
// Truncate the file
|
|
//
|
|
lRes = m_pMainFile->SetFileLength(nOffset);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
|
|
}
|
|
|
|
return WriteAllocationRecordToDisk(Info, dwSize);
|
|
}
|
|
|
|
|
|
|
|
|
|
long CFileHeap::WriteAllocationRecordToDisk(const CRecordInfo& Info,
|
|
DWORD dwSize)
|
|
{
|
|
BYTE* pBuffer = (BYTE*)TempAlloc(GetFreeListRecordSize());
|
|
if(pBuffer == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
CTempFreeMe tfm(pBuffer);
|
|
|
|
memcpy(pBuffer, &dwSize, sizeof(DWORD));
|
|
memcpy(pBuffer + sizeof(DWORD), &Info.m_nOffset, sizeof(TOffset));
|
|
|
|
if(Info.m_nOffset != ROSWELL_INVALID_OFFSET && dwSize >= sizeof(DWORD))
|
|
{
|
|
DWORD dwSig = 0x51515151;
|
|
long lRes = WriteBytes(Info.m_nOffset, (BYTE*)&dwSig, sizeof(DWORD));
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
|
|
//
|
|
// Position in the file is identified by the index
|
|
//
|
|
|
|
return WriteToFreeFile(GetFreeListRecordSize() * Info.m_dwIndex,
|
|
pBuffer, GetFreeListRecordSize());
|
|
}
|
|
|
|
long CFileHeap::ReadFromFreeFile(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
|
|
{
|
|
return m_pFreeFile->Read(nOffset, pBuffer, dwLength, NULL);
|
|
}
|
|
|
|
long CFileHeap::WriteToFreeFile(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
|
|
{
|
|
return m_pFreeFile->Write(nOffset, pBuffer, dwLength, NULL);
|
|
}
|
|
|