2024-06-09 09:22:22 +02:00
|
|
|
#include "map.h"
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
VOID
|
|
|
|
RtlHashmapDelete(_In_ PRTL_HASHMAP Hashmap)
|
|
|
|
{
|
|
|
|
ExFreePoolWithTag(Hashmap->buckets, POOL_TAG_HASHMAP);
|
|
|
|
ExDeleteLookasideListEx(&Hashmap->pool);
|
|
|
|
}
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
NTSTATUS
|
2024-06-11 13:41:55 +02:00
|
|
|
RtlHashmapCreate(_In_ UINT32 BucketCount,
|
2024-06-09 09:22:22 +02:00
|
|
|
_In_ UINT32 EntryObjectSize,
|
|
|
|
_In_ HASH_FUNCTION HashFunction,
|
|
|
|
_In_ COMPARE_FUNCTION CompareFunction,
|
2024-06-11 13:41:55 +02:00
|
|
|
_In_opt_ PVOID Context,
|
2024-06-09 09:22:22 +02:00
|
|
|
_Out_ PRTL_HASHMAP Hashmap)
|
|
|
|
{
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
UINT32 entry_size = sizeof(RTL_HASHMAP_ENTRY) + EntryObjectSize;
|
|
|
|
PRTL_HASHMAP_ENTRY entry = NULL;
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
if (!CompareFunction || !HashFunction)
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
Hashmap->buckets = ExAllocatePool2(
|
|
|
|
POOL_FLAG_NON_PAGED, BucketCount * entry_size, POOL_TAG_HASHMAP);
|
|
|
|
|
|
|
|
if (!Hashmap->buckets)
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
for (UINT32 index = 0; index < BucketCount; index++) {
|
|
|
|
entry = &Hashmap->buckets[index];
|
|
|
|
entry->in_use = FALSE;
|
|
|
|
InitializeListHead(&entry->entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
KeInitializeGuardedMutex(&Hashmap->lock);
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
status = ExInitializeLookasideListEx(&Hashmap->pool,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NonPagedPoolNx,
|
|
|
|
0,
|
|
|
|
entry_size,
|
|
|
|
POOL_TAG_HASHMAP,
|
|
|
|
0);
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DEBUG_ERROR("ExInitializeLookasideListEx: %x", status);
|
|
|
|
ExFreePoolWithTag(Hashmap->buckets, POOL_TAG_HASHMAP);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
Hashmap->bucket_count = BucketCount;
|
|
|
|
Hashmap->hash_function = HashFunction;
|
|
|
|
Hashmap->compare_function = CompareFunction;
|
|
|
|
Hashmap->object_size = EntryObjectSize;
|
|
|
|
Hashmap->active = TRUE;
|
|
|
|
Hashmap->context = Context;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
STATIC
|
|
|
|
PRTL_HASHMAP_ENTRY
|
2024-06-11 13:51:47 +02:00
|
|
|
RtlpHashmapFindUnusedEntry(_In_ PLIST_ENTRY Head)
|
2024-06-09 09:22:22 +02:00
|
|
|
{
|
2024-06-11 13:41:55 +02:00
|
|
|
PRTL_HASHMAP_ENTRY entry = NULL;
|
|
|
|
PLIST_ENTRY list_entry = Head->Flink;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
while (list_entry != Head) {
|
|
|
|
entry = CONTAINING_RECORD(list_entry, RTL_HASHMAP_ENTRY, entry);
|
|
|
|
|
|
|
|
if (entry->in_use == FALSE) {
|
|
|
|
entry->in_use = TRUE;
|
2024-06-09 09:22:22 +02:00
|
|
|
return entry;
|
2024-06-11 13:41:55 +02:00
|
|
|
}
|
2024-06-09 09:22:22 +02:00
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
list_entry = list_entry->Flink;
|
2024-06-09 09:22:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
STATIC
|
|
|
|
PRTL_HASHMAP_ENTRY
|
2024-06-11 13:51:47 +02:00
|
|
|
RtlpHashmapAllocateBucketEntry(_In_ PRTL_HASHMAP Hashmap)
|
2024-06-09 09:22:22 +02:00
|
|
|
{
|
2024-06-11 13:41:55 +02:00
|
|
|
PRTL_HASHMAP_ENTRY entry = ExAllocateFromLookasideListEx(&Hashmap->pool);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
if (!entry)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
entry->in_use = TRUE;
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
STATIC
|
|
|
|
BOOLEAN
|
2024-06-11 13:51:47 +02:00
|
|
|
RtlpHashmapIsIndexInRange(_In_ PRTL_HASHMAP Hashmap, _In_ UINT32 Index)
|
2024-06-09 09:22:22 +02:00
|
|
|
{
|
|
|
|
return Index < Hashmap->bucket_count ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* assumes map lock is held */
|
|
|
|
PVOID
|
2024-06-11 13:41:55 +02:00
|
|
|
RtlHashmapEntryInsert(_In_ PRTL_HASHMAP Hashmap, _In_ UINT64 Key)
|
2024-06-09 09:22:22 +02:00
|
|
|
{
|
2024-06-11 13:41:55 +02:00
|
|
|
UINT32 index = 0;
|
|
|
|
PLIST_ENTRY list_head = NULL;
|
|
|
|
PRTL_HASHMAP_ENTRY entry = NULL;
|
|
|
|
PRTL_HASHMAP_ENTRY new_entry = NULL;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
index = Hashmap->hash_function(Key);
|
|
|
|
|
2024-06-11 13:51:47 +02:00
|
|
|
if (!RtlpHashmapIsIndexInRange(Hashmap, index)) {
|
2024-06-09 09:22:22 +02:00
|
|
|
DEBUG_ERROR("Key is not in range of buckets");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
list_head = &(&Hashmap->buckets[index])->entry;
|
2024-06-11 13:51:47 +02:00
|
|
|
entry = RtlpHashmapFindUnusedEntry(list_head);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
if (entry)
|
|
|
|
return entry;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
2024-06-11 13:51:47 +02:00
|
|
|
new_entry = RtlpHashmapAllocateBucketEntry(Hashmap);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
if (!new_entry) {
|
|
|
|
DEBUG_ERROR("Failed to allocate new entry");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
InsertHeadList(list_head, &new_entry->entry);
|
|
|
|
return new_entry->object;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns a pointer to the start of the entries caller defined data. i.e
|
|
|
|
* &PRTL_HASHMAP_ENTRY->Object
|
|
|
|
*
|
|
|
|
* Also assumes lock is held.
|
|
|
|
*/
|
|
|
|
PVOID
|
2024-06-11 13:41:55 +02:00
|
|
|
RtlHashmapEntryLookup(_In_ PRTL_HASHMAP Hashmap,
|
2024-06-09 09:22:22 +02:00
|
|
|
_In_ UINT64 Key,
|
|
|
|
_In_ PVOID Compare)
|
|
|
|
{
|
|
|
|
UINT32 index = 0;
|
|
|
|
PRTL_HASHMAP_ENTRY entry = NULL;
|
|
|
|
|
|
|
|
index = Hashmap->hash_function(Key);
|
|
|
|
|
2024-06-11 13:51:47 +02:00
|
|
|
if (!RtlpHashmapIsIndexInRange(Hashmap, index)) {
|
2024-06-09 09:22:22 +02:00
|
|
|
DEBUG_ERROR("Key is not in range of buckets");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = &Hashmap->buckets[index];
|
|
|
|
|
|
|
|
while (entry) {
|
|
|
|
if (entry->in_use == FALSE)
|
|
|
|
goto increment;
|
|
|
|
|
|
|
|
if (Hashmap->compare_function(entry->object, Compare))
|
|
|
|
return entry->object;
|
|
|
|
|
|
|
|
increment:
|
|
|
|
entry = CONTAINING_RECORD(entry->entry.Flink, RTL_HASHMAP_ENTRY, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_ERROR("Unable to find entry in hashmap.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assumes lock is held */
|
|
|
|
BOOLEAN
|
2024-06-11 13:41:55 +02:00
|
|
|
RtlHashmapEntryDelete(_Inout_ PRTL_HASHMAP Hashmap,
|
|
|
|
_In_ UINT64 Key,
|
|
|
|
_In_ PVOID Compare)
|
2024-06-09 09:22:22 +02:00
|
|
|
{
|
2024-06-11 13:41:55 +02:00
|
|
|
UINT32 index = 0;
|
|
|
|
PLIST_ENTRY list_head = NULL;
|
|
|
|
PLIST_ENTRY list_entry = NULL;
|
|
|
|
PRTL_HASHMAP_ENTRY entry = NULL;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
index = Hashmap->hash_function(Key);
|
|
|
|
|
2024-06-11 13:51:47 +02:00
|
|
|
if (!RtlpHashmapIsIndexInRange(Hashmap, index)) {
|
2024-06-09 09:22:22 +02:00
|
|
|
DEBUG_ERROR("Key is not in range of buckets");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
list_head = &(&Hashmap->buckets[index])->entry;
|
|
|
|
list_entry = list_head->Flink;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
while (list_entry != list_head) {
|
|
|
|
entry = CONTAINING_RECORD(list_entry, RTL_HASHMAP_ENTRY, entry);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
if (entry->in_use &&
|
|
|
|
Hashmap->compare_function(entry->object, Compare)) {
|
|
|
|
if (entry == list_head) {
|
2024-06-09 09:22:22 +02:00
|
|
|
entry->in_use = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
RemoveEntryList(&entry->entry);
|
2024-06-11 13:41:55 +02:00
|
|
|
ExFreeToLookasideListEx(&Hashmap->pool, entry);
|
2024-06-09 09:22:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
list_entry = list_entry->Flink;
|
2024-06-09 09:22:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2024-06-11 13:51:47 +02:00
|
|
|
/* assumes lock is held */
|
2024-06-09 09:22:22 +02:00
|
|
|
VOID
|
2024-06-11 13:41:55 +02:00
|
|
|
RtlHashmapEnumerate(_In_ PRTL_HASHMAP Hashmap,
|
2024-06-09 09:22:22 +02:00
|
|
|
_In_ ENUMERATE_HASHMAP EnumerationCallback,
|
|
|
|
_In_opt_ PVOID Context)
|
|
|
|
{
|
2024-06-11 13:41:55 +02:00
|
|
|
PLIST_ENTRY list_head = NULL;
|
|
|
|
PLIST_ENTRY list_entry = NULL;
|
|
|
|
PRTL_HASHMAP_ENTRY entry = NULL;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
for (UINT32 index = 0; index < Hashmap->bucket_count; index++) {
|
2024-06-11 13:41:55 +02:00
|
|
|
list_head = &Hashmap->buckets[index];
|
|
|
|
list_entry = list_head->Flink;
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
while (list_entry != list_head) {
|
|
|
|
entry = CONTAINING_RECORD(list_entry, RTL_HASHMAP_ENTRY, entry);
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
if (entry->in_use == TRUE)
|
2024-06-09 09:22:22 +02:00
|
|
|
EnumerationCallback(entry->object, Context);
|
|
|
|
|
|
|
|
list_entry = list_entry->Flink;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|