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.
374 lines
10 KiB
374 lines
10 KiB
#include "stdinc.h"
|
|
#include "lhport.h"
|
|
#include "positionindependenthashtableaccessor.h"
|
|
|
|
typedef CPositionIndependentHashTable::CHashTable CHashTable;
|
|
typedef CPositionIndependentHashTable::CHashTableBucket CHashTableBucket;
|
|
|
|
ULONG
|
|
CPositionIndependentHashTableAccessor::PointerToOffset(const BYTE * p)
|
|
{
|
|
return m_PositionIndependentTable->PointerToOffset(p);
|
|
}
|
|
|
|
CPositionIndependentHashTableAccessor::CPositionIndependentHashTableAccessor(
|
|
CPositionIndependentHashTable * PositionIndependentTable,
|
|
ULONG HashTableIndex
|
|
)
|
|
: m_PositionIndependentTable(PositionIndependentTable),
|
|
m_HashTableIndex(HashTableIndex),
|
|
m_Key(NULL),
|
|
m_PointerToHashTables(NULL),
|
|
m_PointerToBucket(NULL),
|
|
m_KeyHash(0),
|
|
m_KeyIndex(~0UL),
|
|
m_KeyHashIsValid(FALSE)
|
|
{ }
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::GetPointerToHashTable(CHashTable ** Result)
|
|
{
|
|
return this->GetPointerToHashTable(m_HashTableIndex, Result);
|
|
}
|
|
|
|
CHashTable *
|
|
CPositionIndependentHashTableAccessor::GetPointerToHashTable()
|
|
{
|
|
return this->GetPointerToHashTable(m_HashTableIndex);
|
|
}
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::GetPointerToHashTable(ULONG HashTableIndex, CHashTable ** Result)
|
|
{
|
|
*Result = GetPointerToHashTable(HashTableIndex);
|
|
}
|
|
|
|
CHashTable *
|
|
CPositionIndependentHashTableAccessor::GetPointerToHashTable(ULONG HashTableIndex)
|
|
{
|
|
if (m_PointerToHashTables == NULL)
|
|
{
|
|
OffsetToPointer(m_PositionIndependentTable->m_OffsetToHashTables, &m_PointerToHashTables);
|
|
}
|
|
|
|
return &m_PointerToHashTables[HashTableIndex];
|
|
}
|
|
|
|
BOOL
|
|
CPositionIndependentHashTableAccessor::AreKeysEqual(const BYTE * Key1, const BYTE * Key2)
|
|
{
|
|
return (*GetPointerToHashTable()->m_Equal.pfn)(Key1, Key2);
|
|
}
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::GetKeyHashNoCache(const BYTE * Key, ULONG * KeyHash)
|
|
{
|
|
*KeyHash = (*GetPointerToHashTable()->m_Hash.pfn)(Key);
|
|
}
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::GetKeyHash(const BYTE * Key, ULONG * KeyHash)
|
|
{
|
|
if (Key == m_Key && m_KeyHashIsValid)
|
|
{
|
|
*KeyHash = m_KeyHash;
|
|
return;
|
|
}
|
|
|
|
GetKeyHashNoCache(Key, KeyHash);
|
|
m_PointerToBucket = NULL;
|
|
m_KeyHash = *KeyHash;
|
|
m_Key = Key;
|
|
m_KeyHashIsValid = TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CPositionIndependentHashTableAccessor::IsKeyPresent(const BYTE * Key)
|
|
{
|
|
BOOL Result = FALSE;
|
|
CHashTable * PointerToHashTable = 0;
|
|
CHashTableBucket * PointerToBucket = 0;
|
|
ULONG Hash = 0;
|
|
ULONG BucketIndex = 0;
|
|
ULONG AllocatedElementsInBucket = 0;
|
|
CHashTableElement * PointerToElements = 0;
|
|
|
|
if (IsEmpty())
|
|
{
|
|
Result = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
GetPointerToHashTable(&PointerToHashTable);
|
|
GetHashTableBucketForKey(Key, &PointerToBucket);
|
|
GetKeyHash(Key, &Hash);
|
|
BucketIndex = (m_KeyHash % PointerToHashTable->m_NumberOfBuckets);
|
|
|
|
OffsetToPointer(PointerToHashTable->m_OffsetToBuckets + BucketIndex * sizeof(*PointerToBucket), &PointerToBucket);
|
|
AllocatedElementsInBucket = PointerToBucket->m_AllocatedElementsInBucket;
|
|
if (AllocatedElementsInBucket == 0)
|
|
{
|
|
Result = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
OffsetToPointer(PointerToBucket->m_OffsetToElements, &PointerToElements);
|
|
|
|
for (ULONG IndexIntoBucket = 0 ; IndexIntoBucket != AllocatedElementsInBucket ; ++IndexIntoBucket )
|
|
{
|
|
CHashTableElement * Element = &PointerToElements[IndexIntoBucket];
|
|
if (Element->m_PseudoKey == Hash)
|
|
{
|
|
PBYTE PointerToKey;
|
|
OffsetToPointer(Element->m_OffsetToKey, &PointerToKey);
|
|
if ((*PointerToHashTable->m_Equal.pfn)(Key, PointerToKey))
|
|
{
|
|
Result = TRUE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::GetHashTableBucketForKey(
|
|
const BYTE * Key,
|
|
CHashTableBucket ** Result
|
|
)
|
|
{
|
|
ULONG Hash = 0;
|
|
CHashTable * PointerToHashTable = 0;
|
|
CHashTableBucket * PointerToBucket = 0;
|
|
ULONG BucketIndex = 0;
|
|
ULONG NumberOfElementsInTable = 0;
|
|
|
|
*Result = NULL;
|
|
|
|
if (Key == m_Key && m_KeyHashIsValid && m_PointerToBucket != NULL)
|
|
{
|
|
*Result = m_PointerToBucket;
|
|
goto Exit;
|
|
}
|
|
|
|
if (IsEmpty())
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
GetKeyHash(Key, &Hash);
|
|
GetPointerToHashTable(&PointerToHashTable);
|
|
NumberOfElementsInTable = PointerToHashTable->m_NumberOfElementsInTable;
|
|
if (NumberOfElementsInTable == 0)
|
|
{
|
|
goto Exit;
|
|
}
|
|
BucketIndex = (Hash % PointerToHashTable->m_NumberOfBuckets);
|
|
|
|
OffsetToPointer(PointerToHashTable->m_OffsetToBuckets + BucketIndex * sizeof(*PointerToBucket), &PointerToBucket);
|
|
|
|
*Result = PointerToBucket;
|
|
m_PointerToBucket = PointerToBucket;
|
|
Exit:
|
|
;
|
|
}
|
|
|
|
BOOL CPositionIndependentHashTableAccessor::IsEmpty()
|
|
{
|
|
if (m_PositionIndependentTable->m_NumberOfHashTables == 0)
|
|
return TRUE;
|
|
|
|
if (GetPointerToHashTable()->m_NumberOfElementsInTable == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::GetBounds(
|
|
const BYTE * Bounds[2]
|
|
)
|
|
{
|
|
return m_PositionIndependentTable->GetBounds(Bounds);
|
|
}
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::IsInBlob(
|
|
const BYTE * Pointer,
|
|
ULONG Size,
|
|
BOOL * IsInBlob
|
|
)
|
|
{
|
|
return m_PositionIndependentTable->IsInBlob(Pointer, Size, IsInBlob);
|
|
}
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::SetAt(
|
|
const BYTE * Key,
|
|
ULONG KeySize,
|
|
const BYTE * Value,
|
|
ULONG ValueSize
|
|
)
|
|
{
|
|
FN_PROLOG_VOID_THROW
|
|
|
|
CHashTableBucket * Result = 0;
|
|
ULONG Hash = 0;
|
|
CHashTable * PointerToHashTable = 0;
|
|
CHashTableBucket Bucket = { 0 };
|
|
CHashTableBucket * PointerToBucket = 0;
|
|
ULONG BucketIndex = 0;
|
|
ULONG NumberOfElementsInTable = 0;
|
|
ULONG NumberOfBuckets = 0;
|
|
ULONG KeyOffset = 0;
|
|
ULONG ValueOffset = 0;
|
|
BOOL KeyIsInBlob = 0;
|
|
BOOL ValueIsInBlob = 0;
|
|
BYTE * BasePointer = BasePointer = GetBasePointer();
|
|
GetKeyHash(Key, &Hash);
|
|
GetPointerToHashTable(&PointerToHashTable);
|
|
NumberOfElementsInTable = PointerToHashTable->m_NumberOfElementsInTable;
|
|
NumberOfBuckets = PointerToHashTable->m_NumberOfBuckets;
|
|
|
|
//
|
|
// Do this before we get buckets.
|
|
//
|
|
IsInBlob(Key, KeySize, &KeyIsInBlob);
|
|
IsInBlob(Value, ValueSize, &ValueIsInBlob);
|
|
if (KeyIsInBlob)
|
|
Key = BasePointer + KeyOffset;
|
|
if (ValueIsInBlob)
|
|
Value = BasePointer + ValueOffset;
|
|
|
|
if (NumberOfBuckets == 0)
|
|
{
|
|
GrowNumberOfBuckets(17);
|
|
NumberOfBuckets = PointerToHashTable->m_NumberOfBuckets;
|
|
|
|
if (KeyIsInBlob)
|
|
OffsetToPointer(KeyOffset, &Key);
|
|
if (ValueIsInBlob)
|
|
OffsetToPointer(ValueOffset, &Value);
|
|
}
|
|
BucketIndex = (Hash % NumberOfBuckets);
|
|
OffsetToPointer(PointerToHashTable->m_OffsetToBuckets + BucketIndex * sizeof(*PointerToBucket), &PointerToBucket);
|
|
Bucket = *PointerToBucket;
|
|
CHashTableElement * Elements;
|
|
OffsetToPointer(Bucket.m_OffsetToElements, &Elements);
|
|
|
|
ULONG FreeElementIndex = 0;
|
|
ULONG FoundElementIndex = 0;
|
|
BOOL DidFindFreeElement = FALSE;
|
|
BOOL DidFindElement = FALSE;
|
|
|
|
for (ULONG ElementIndex = 0 ; ElementIndex != Bucket.m_AllocatedElementsInBucket; ++ElementIndex)
|
|
{
|
|
if (Elements[ElementIndex].m_InUse)
|
|
{
|
|
if (Hash == Elements[ElementIndex].m_PseudoKey
|
|
&& AreKeysEqual(Key, BasePointer + Elements[ElementIndex].m_OffsetToKey))
|
|
{
|
|
FoundElementIndex = ElementIndex;
|
|
DidFindElement = TRUE;
|
|
}
|
|
}
|
|
else if (!DidFindFreeElement)
|
|
{
|
|
FreeElementIndex = ElementIndex;
|
|
DidFindFreeElement = TRUE;
|
|
}
|
|
if (DidFindFreeElement && DidFindElement)
|
|
break;
|
|
}
|
|
|
|
ULONG NeededBytes = 0;
|
|
ULONG NeededBlocks = 0;
|
|
|
|
ULONG NeededElementBytes = 0;
|
|
if (!DidFindElement && !DidFindFreeElement)
|
|
{
|
|
//
|
|
// REVIEW Should we double, or just add one?
|
|
//
|
|
NeededElementBytes = sizeof(CHashTableElement) * (Bucket.m_AllocatedElementsInBucket + 1);
|
|
NeededBytes += NeededElementBytes;
|
|
NeededBlocks += 1;
|
|
}
|
|
else
|
|
{
|
|
NeededElementBytes = 0;
|
|
}
|
|
|
|
ULONG NeededKeyBytes = 0;
|
|
if (KeySize <= sizeof(CHashTableElement().m_SmallKey)
|
|
|| KeyIsInBlob
|
|
|| DidFindElement
|
|
|| (DidFindFreeElement && Elements[FreeElementIndex].m_KeySize >= ValueSize)
|
|
)
|
|
{
|
|
NeededKeyBytes = 0;
|
|
}
|
|
else
|
|
{
|
|
NeededKeyBytes = KeySize;
|
|
NeededBytes += KeySize;
|
|
NeededBlocks += 1;
|
|
}
|
|
ULONG NeededValueBytes = 0;
|
|
if (ValueSize <= sizeof(CHashTableElement().m_SmallValue)
|
|
|| ValueIsInBlob
|
|
|| (DidFindElement && Elements[FoundElementIndex].m_ValueAllocatedSize >= ValueSize)
|
|
|| (DidFindFreeElement && Elements[FreeElementIndex].m_ValueAllocatedSize >= ValueSize)
|
|
)
|
|
{
|
|
NeededValueBytes = 0;
|
|
|
|
// NOTE: what about replacement with "equal" values?
|
|
}
|
|
else
|
|
{
|
|
NeededValueBytes = ValueSize;
|
|
NeededBytes += ValueSize;
|
|
NeededBlocks += 1;
|
|
}
|
|
|
|
Reserve(NeededBytes, NeededBlocks);
|
|
|
|
if (NeededElementBytes != 0)
|
|
{
|
|
ULONG NewElementsOffset;
|
|
|
|
Alloc(NeededElementBytes, &NewElementsOffset);
|
|
|
|
CopyMemory(BasePointer + NewElementsOffset, Elements, NeededElementBytes - sizeof(CHashTableElement));
|
|
|
|
Elements = reinterpret_cast<CHashTableElement*>(BasePointer + NewElementsOffset);
|
|
}
|
|
|
|
|
|
// undone right here
|
|
|
|
FN_EPILOG_THROW;
|
|
}
|
|
|
|
PBYTE
|
|
CPositionIndependentHashTableAccessor::GetBasePointer()
|
|
{
|
|
return m_PositionIndependentTable->GetBasePointer();
|
|
}
|
|
|
|
void
|
|
CPositionIndependentHashTableAccessor::Alloc(ULONG NumberOfBytes, ULONG * Offset)
|
|
{
|
|
PBYTE BaseBefore = GetBasePointer();
|
|
m_PositionIndependentTable->Alloc(NumberOfBytes, Offset);
|
|
if (GetBasePointer() != BaseBefore)
|
|
{
|
|
m_PointerToBucket = NULL;
|
|
m_PointerToHashTables = NULL;
|
|
m_PointerToBucket = NULL;
|
|
}
|
|
}
|