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.
264 lines
8.0 KiB
264 lines
8.0 KiB
#include "stdinc.h"
|
|
#include "lhport.h"
|
|
#include "windows.h"
|
|
#include "numberof.h"
|
|
#include "positionindependentblob.h"
|
|
#include "fusionhandle.h"
|
|
|
|
#undef ASSERT
|
|
#undef ASSERT_NTC /* NTC == no trace context */
|
|
|
|
#if DBG
|
|
void AssertFunction(PCSTR s) { ASSERT2_NTC(FALSE, s); }
|
|
#define ASSERT(expr) ((expr) ? TRUE : (AssertFunction(#expr),FALSE))
|
|
#define ASSERT_NTC(expr) ((expr) ? TRUE : (AssertFunction(#expr),FALSE))
|
|
#else
|
|
#define ASSERT_NTC(expr) ((expr) ? TRUE : FALSE)
|
|
#endif
|
|
|
|
void *
|
|
CPositionIndependentOperatorNew::operator new(
|
|
size_t n,
|
|
CPositionIndependentBlob * Container
|
|
)
|
|
{
|
|
return Container->OperatorNew(n);
|
|
}
|
|
|
|
void CPositionIndependentBlob::OutOfMemory()
|
|
{
|
|
OutputDebugStringA("SXS_PIBLOB: out of memory\n");
|
|
F::CErr::ThrowWin32(FUSION_WIN32_ALLOCFAILED_ERROR);
|
|
}
|
|
|
|
CPositionIndependentBlob::CPositionIndependentBlob()
|
|
{
|
|
ZeroMemory(this, sizeof(*this));
|
|
}
|
|
|
|
CPositionIndependentBlob::~CPositionIndependentBlob()
|
|
{
|
|
}
|
|
|
|
void
|
|
CPositionIndependentBlob::Alloc(
|
|
CPositionIndependentBlob * Container,
|
|
ULONG NumberOfBytes,
|
|
ULONG * Offset
|
|
)
|
|
{
|
|
if (Container != NULL)
|
|
{
|
|
return Container->Alloc(NumberOfBytes, Offset);
|
|
}
|
|
return this->Alloc(NumberOfBytes, Offset);
|
|
}
|
|
|
|
//
|
|
// CONSIDER: NtExtendSection for pagefile mappings.
|
|
//
|
|
|
|
BOOL CPositionIndependentBlob::IsPagefileMapping()
|
|
{
|
|
return (m_FileHandle == INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
void GetFirstError(DWORD & FirstError)
|
|
{
|
|
if (FirstError == NO_ERROR)
|
|
FirstError = F::GetLastWin32Error();
|
|
}
|
|
|
|
void CPositionIndependentBlob::Free(const BYTE * Pointer)
|
|
{
|
|
FN_PROLOG_VOID_THROW
|
|
|
|
CBlock InUseBlock;
|
|
InUseBlock.m_Offset = static_cast<ULONG>((Pointer - GetBasePointer()));
|
|
InUseBlock.m_Size = 0;
|
|
CBlockByOffsetSet::iterator InUseByOffsetIterator = m_InUseBlocksSortedByOffset.lower_bound(InUseBlock);
|
|
if (!ASSERT_NTC(InUseByOffsetIterator != m_InUseBlocksSortedByOffset.end()))
|
|
{
|
|
return;
|
|
}
|
|
InUseBlock = *InUseByOffsetIterator;
|
|
m_InUseBlocksSortedByOffset.erase(InUseByOffsetIterator);
|
|
CBlockByOffsetSet::iterator End = m_FreeBlocksSortedByOffset.end();
|
|
CBlockByOffsetSet::iterator PrevFreeBlockIterator = m_FreeBlocksSortedByOffset.lower_bound(InUseBlock);
|
|
CBlockByOffsetSet::iterator NextFreeBlockIterator = m_FreeBlocksSortedByOffset.upper_bound(InUseBlock);
|
|
CBlockByOffsetSet::iterator FreeByOffsetToErase = End;
|
|
|
|
CBlock BiggerFreeBlock;
|
|
BiggerFreeBlock.m_Size = InUseBlock.m_Size;
|
|
|
|
if (PrevFreeBlockIterator != End && !InUseBlock.Neighbors(*PrevFreeBlockIterator))
|
|
PrevFreeBlockIterator = End;
|
|
if (NextFreeBlockIterator != End && !InUseBlock.Neighbors(*NextFreeBlockIterator))
|
|
NextFreeBlockIterator = End;
|
|
switch (
|
|
(ULONG(PrevFreeBlockIterator != End) << 1) | ULONG(NextFreeBlockIterator != End)
|
|
)
|
|
{
|
|
case 3:
|
|
// freed between two free blocks, merge all three
|
|
const_cast<ULONG&>(PrevFreeBlockIterator->m_Size) += InUseBlock.m_Size;
|
|
const_cast<ULONG&>(PrevFreeBlockIterator->m_Size) += NextFreeBlockIterator->m_Size;
|
|
BiggerFreeBlock = *PrevFreeBlockIterator;
|
|
FreeByOffsetToErase = NextFreeBlockIterator;
|
|
break;
|
|
case 2:
|
|
// freed with a free block to the left, merge with left
|
|
const_cast<ULONG&>(PrevFreeBlockIterator->m_Size) += InUseBlock.m_Size;
|
|
BiggerFreeBlock = *PrevFreeBlockIterator;
|
|
FreeByOffsetToErase = NextFreeBlockIterator;
|
|
break;
|
|
case 1:
|
|
// freed with free block to the right, merge with right
|
|
const_cast<ULONG&>(NextFreeBlockIterator->m_Offset) -= InUseBlock.m_Size;
|
|
const_cast<ULONG&>(NextFreeBlockIterator->m_Size) += InUseBlock.m_Size;
|
|
BiggerFreeBlock = *NextFreeBlockIterator;
|
|
FreeByOffsetToErase = PrevFreeBlockIterator;
|
|
break;
|
|
case 0:
|
|
// freed between two inuse blocks, no merge
|
|
// BUG Free should not allocate memory. This is avoidable by keeping empty blocks at the ends.
|
|
m_FreeBlocksSortedByOffset.insert(BiggerFreeBlock);
|
|
break;
|
|
}
|
|
|
|
if (PrevFreeBlockIterator != End)
|
|
m_FreeBlocksSortedBySize.erase(*PrevFreeBlockIterator);
|
|
if (NextFreeBlockIterator != End)
|
|
m_FreeBlocksSortedBySize.erase(*NextFreeBlockIterator);
|
|
if (FreeByOffsetToErase != End)
|
|
m_FreeBlocksSortedByOffset.erase(FreeByOffsetToErase);
|
|
|
|
m_PendingFreeBySize += 1;
|
|
|
|
FN_EPILOG_THROW;
|
|
}
|
|
|
|
void CPositionIndependentBlob::RecalculateBlocksBySize()
|
|
{
|
|
if (m_PendingFreeBySize == 0)
|
|
return;
|
|
// UNDONE
|
|
m_PendingFreeBySize = 0;
|
|
}
|
|
|
|
void
|
|
CPositionIndependentBlob::GetBounds(
|
|
const BYTE * Bounds[2]
|
|
)
|
|
{
|
|
FN_PROLOG_VOID_THROW
|
|
|
|
Bounds[0] = GetBasePointer();
|
|
Bounds[1] = Bounds[0] + m_AllocatedSize;;
|
|
|
|
FN_EPILOG_THROW;
|
|
}
|
|
|
|
void
|
|
CPositionIndependentBlob::IsInBlob(
|
|
const BYTE * Data,
|
|
ULONG Size,
|
|
BOOL * IsInBlob
|
|
)
|
|
{
|
|
FN_PROLOG_VOID_THROW
|
|
|
|
const BYTE * Bounds[2];
|
|
|
|
GetBounds(Bounds);
|
|
*IsInBlob = (Data >= Bounds[0] && (Data + Size) < Bounds[1]);
|
|
|
|
FN_EPILOG_THROW;
|
|
}
|
|
|
|
void Reserve()
|
|
{
|
|
// UNDONE
|
|
}
|
|
|
|
void CPositionIndependentBlob::Alloc(ULONG NumberOfBytes, ULONG * Offset)
|
|
{
|
|
FN_PROLOG_VOID_THROW
|
|
|
|
CBlock Block;
|
|
Block.m_Offset = 0;
|
|
Block.m_Size = NumberOfBytes;
|
|
|
|
RecalculateBlocksBySize();
|
|
|
|
CBlockBySizeSet::iterator i = m_FreeBlocksSortedBySize.lower_bound(Block);
|
|
if (i == m_FreeBlocksSortedBySize.end() && m_pmfCompact != NULL)
|
|
{
|
|
RecalculateBlocksBySize();
|
|
i = m_FreeBlocksSortedBySize.lower_bound(Block);
|
|
}
|
|
if (i == m_FreeBlocksSortedBySize.end() && m_pmfCompact != NULL)
|
|
{
|
|
(this->*m_pmfCompact)();
|
|
i = m_FreeBlocksSortedBySize.lower_bound(Block);
|
|
}
|
|
if (i == m_FreeBlocksSortedBySize.end())
|
|
{
|
|
Grow(NumberOfBytes);
|
|
i = m_FreeBlocksSortedBySize.lower_bound(Block);
|
|
}
|
|
if (i == m_FreeBlocksSortedBySize.end())
|
|
{
|
|
OutOfMemory();
|
|
return;
|
|
}
|
|
CBlock Found = *i;
|
|
CBlock FitFound = Found;
|
|
ULONG RemainderSize = (Found.m_Size - NumberOfBytes);
|
|
FitFound.m_Size = NumberOfBytes;
|
|
|
|
//
|
|
// erase before we mess with the ordering (we would create ties otherwise)
|
|
//
|
|
m_FreeBlocksSortedByOffset.erase(*i);
|
|
m_FreeBlocksSortedBySize.erase(i);
|
|
|
|
if (RemainderSize != 0)
|
|
{
|
|
CBlockByOffsetSet::iterator End = m_FreeBlocksSortedByOffset.end();
|
|
CBlockByOffsetSet::iterator PrevFreeBlockIterator = m_FreeBlocksSortedByOffset.lower_bound(Found);
|
|
CBlockByOffsetSet::iterator NextFreeBlockIterator = m_FreeBlocksSortedByOffset.upper_bound(Found);
|
|
|
|
if (PrevFreeBlockIterator != End && !Found.Neighbors(*PrevFreeBlockIterator))
|
|
PrevFreeBlockIterator = End;
|
|
if (NextFreeBlockIterator != End && !Found.Neighbors(*NextFreeBlockIterator))
|
|
NextFreeBlockIterator = End;
|
|
switch (
|
|
(ULONG(PrevFreeBlockIterator != End) << 1) | ULONG(NextFreeBlockIterator != End)
|
|
)
|
|
{
|
|
case 3:
|
|
ASSERT_NTC(!"Should not be free on both sides of a free.");
|
|
break;
|
|
case 2:
|
|
m_FreeBlocksSortedBySize.erase(*PrevFreeBlockIterator);
|
|
const_cast<ULONG&>(PrevFreeBlockIterator->m_Size) += RemainderSize;
|
|
m_FreeBlocksSortedBySize.insert(*PrevFreeBlockIterator);
|
|
FitFound.m_Offset += RemainderSize;
|
|
break;
|
|
case 1:
|
|
m_FreeBlocksSortedBySize.erase(*NextFreeBlockIterator);
|
|
const_cast<ULONG&>(NextFreeBlockIterator->m_Offset) -= RemainderSize;
|
|
const_cast<ULONG&>(NextFreeBlockIterator->m_Size) += RemainderSize;
|
|
m_FreeBlocksSortedBySize.insert(*NextFreeBlockIterator);
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
}
|
|
m_InUseBlocksSortedByOffset.insert(FitFound);
|
|
*Offset = FitFound.m_Offset;
|
|
|
|
FN_EPILOG_THROW;
|
|
}
|
|
|