diff --git a/.gitignore b/.gitignore index c6127b3..5de59ff 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ modules.order Module.symvers Mkfile.old dkms.conf + +# Visual Studio +.vs/ \ No newline at end of file diff --git a/minimal_heap.h b/minimal_heap.h index 20a5dab..a89c758 100644 --- a/minimal_heap.h +++ b/minimal_heap.h @@ -1,110 +1,153 @@ -#pragma once +/* Version 1.2 */ + +#ifndef __MINIMAL_HEAP_H__ +#define __MINIMAL_HEAP_H__ #include #include #include -#define USE_HEAP_STRUCTURE_CHECKSUM 1 -#define USE_CANARY_VALIDATION 1 - -#define HEAP_CORRUPTION_TYPE_MEMORY_AREA_CHECKSUM_MISMATCH 0 -#define HEAP_CORRUPTION_TYPE_SEGMENT_CHECKSUM_MISMATCH 1 -#define HEAP_CORRUPTION_TYPE_FRONT_CANARY_MISMATCH 2 -#define HEAP_CORRUPTION_TYPE_REAR_CANARY_MISMATCH 3 - -struct heap_stats -{ - size_t num_memory_areas; - size_t num_total_segments; - size_t num_total_occupied_segments; - size_t total_size; -} typedef heap_stats; - -struct heap_memory_area -{ - size_t size; - size_t num_segments; - size_t num_occupied_segments; - heap_memory_area* next_area; - heap_memory_area* previous_area; - -#if USE_HEAP_STRUCTURE_CHECKSUM - uint32_t checksum; +#ifndef MIHP_IMPLEMENTATION + #define MIHP_IMPLEMENTATION 0 #endif -} typedef heap_memory_area; -struct heap_segment -{ - size_t size; - bool is_occupied; +#define MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_NONE 0 +#define MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_MAGIC_NUMBER 1 +#define MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_CHECKSUM 2 - heap_segment* next_segment; - heap_segment* previous_segment; - heap_memory_area* encapsulating_memory_area; - -#if USE_HEAP_STRUCTURE_CHECKSUM - uint32_t checksum; +/* Defines to be configured by the library user */ +#ifndef MIHP_HEAP_STRUCTURE_VALIDATION_TYPE + #define MIHP_HEAP_STRUCTURE_VALIDATION_TYPE MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_CHECKSUM #endif -} typedef heap_segment; -struct heap_corruption_info -{ - uint8_t type; - void* ptr; - uint32_t expected_value; - uint32_t actual_value; -} typedef heap_corruption_info; +#ifndef MIHP_ASSERT + #define MIHP_ASSERT(Condition) while(1) { if (Condition) break; *(volatile char*)(0x0) = 0; } +#endif + +#ifndef MIHP_HEAP_LOCK_TYPE + #define MIHP_HEAP_LOCK_TYPE bool // Does NOT have to be reentred safe + #define MIHP_LOCK_HEAP(LockVarPtr) { while (*(LockVarPtr)) {}; *(LockVarPtr) = true; } + #define MIHP_UNLOCK_HEAP(LockVarPtr) { *(LockVarPtr) = false; } +#endif +/* */ /* Hooks to be implemented by the library user */ -void* platform_request_memory_area(size_t size); -bool platform_free_memory_area(void* ptr, size_t size); -uint8_t platform_get_required_memory_alingment(); -void platform_heap_corruption_detected(heap_corruption_info info); +void* MIHP_PlatformRequestMemory(size_t size); // Alignment must at least be alignment of size_t +bool MIHP_PlatformFreeMemory(void* ptr, size_t size); +void MIHP_PlatformOnHeapCorruptionDetected(struct _MIHP_HeapCorruptionInfo info); +/* */ -size_t config_minimal_memory_area_size(); -size_t config_minimal_amount_unoccupied_bytes(); -size_t config_minimal_amount_unoccupied_continuous_bytes(); -/* === */ +#define MIHP_HEAP_CORRUPTION_TYPE_MEMORY_AREA_CHECKSUM_MISMATCH 0 +#define MIHP_HEAP_CORRUPTION_TYPE_SEGMENT_CHECKSUM_MISMATCH 1 +#define MIHP_HEAP_CORRUPTION_TYPE_FRONT_CANARY_MISMATCH 2 +#define MIHP_HEAP_CORRUPTION_TYPE_REAR_CANARY_MISMATCH 3 -uint32_t fast_memory_hasher(const void* data, size_t size) +typedef struct _MIHP_HeapConfig { - uint32_t hash = 2166136261U; - const unsigned char* bytes = (const unsigned char*)data; - for (size_t i = 0; i < size; i++) - { - hash ^= bytes[i]; - hash *= 16777619U; - } + size_t AllocationAlignment; // Must be power of two and at least sizeof(size_t) - return hash; -} + size_t MinimalMemoryAreaSize; // Must be a multiple of AllocationAlingment + size_t MaximalMemoryAreaSize; // Must be a multiple of AllocationAlingment + + char AllocationInitialValue; +} MIHP_HeapConfig; -uint32_t generate_heap_memory_area_checksum(const heap_memory_area* area) +typedef struct _MIHP_HeapStats { - size_t checksum_size; + size_t NumMemoryAreas; + size_t NumTotalSegments; + size_t NumTotalOccupiedSegments; + size_t TotalSize; +} MIHP_HeapStats; + +// This structs size must be multiple of 8 +typedef struct _MIHP_HeapMemoryAreaHeader +{ + size_t AreaSize; + size_t NumSegments; + size_t NumOccupiedSegments; + + struct _MIHP_HeapMemoryAreaHeader* NextArea; + struct _MIHP_HeapMemoryAreaHeader* PreviousArea; + + struct _MIHP_Heap* OwningHeap; + struct _MIHP_HeapSegmentHeader* FirstSegment; + + + uint32_t Checksum; // Checksum must be the last member ! +} MIHP_HeapMemoryAreaHeader; + +// This structs size must be multiple of 8 +typedef struct _MIHP_HeapSegmentHeader +{ + size_t SegmentSize; + size_t OccupiedSize; + + struct _MIHP_HeapSegmentHeader* NextSegment; + struct _MIHP_HeapSegmentHeader* PreviousSegment; + struct _MIHP_HeapMemoryAreaHeader* OwningMemoryArea; + + char _padding0[16]; + + uint32_t Checksum; // Checksum must be the last member ! + +} MIHP_HeapSegmentHeader; + +typedef struct _MIHP_HeapCorruptionInfo +{ + const struct _MIHP_Heap* Heap; + uint8_t Type; + const void* Ptr; + uint32_t ExpectedValue; + uint32_t ActualValue; +} MIHP_HeapCorruptionInfo; + +typedef struct _MIHP_Heap +{ + MIHP_HeapConfig Config; + MIHP_HeapStats Stats; + MIHP_HeapMemoryAreaHeader* FirstArea; + MIHP_HeapMemoryAreaHeader* LastSuccessfulAllocationArea; + + MIHP_HEAP_LOCK_TYPE HeapLock; +} MIHP_Heap; + +/* API Interface */ +MIHP_Heap* MIHP_CreateHeap(MIHP_HeapConfig config); +bool MIHP_DestroyHeap(MIHP_Heap* heap, bool force); +void* MIHP_Allocate(MIHP_Heap* heap, size_t size); +void* MIHP_Realloc(MIHP_Heap* heap, void* ptr, size_t newSize); +bool MIHP_Free(MIHP_Heap* heap, void* ptr); + +bool MIHP_IsPointerInHeap(MIHP_Heap* heap, void* ptr); +size_t MIHP_GetPtrAllocationSize(MIHP_Heap* heap, void* ptr); +void MIHP_ValidateHeap(MIHP_Heap* heap); +/* */ + +/* Internals */ +MIHP_HeapMemoryAreaHeader* MIHP_CreateHeapMemoryArea(MIHP_Heap* heap, size_t requestedSize); +bool MIHP_DestroyHeapMemoryArea(MIHP_Heap* heap, MIHP_HeapMemoryAreaHeader* area); + +MIHP_HeapSegmentHeader* MIHP_InitializeHeapSegment(MIHP_HeapMemoryAreaHeader* area, void* segmentStart, size_t size); +bool MIHP_UninitializeHeapSegment(MIHP_HeapSegmentHeader* segment); + +bool MIHP_SplitHeapSegment(MIHP_HeapSegmentHeader* sourceSegment, size_t newSegmentSize, MIHP_HeapSegmentHeader** outNewSegment); +bool MIHP_MergeHeapSegments(MIHP_HeapSegmentHeader* sourceSegment, MIHP_HeapSegmentHeader* segmentToAbsorb); + +size_t MIHP_GetHeapAlignedSize(const MIHP_Heap* heap, size_t size); + +uint32_t MIHP_HashMemoryRegion(const void* data, size_t size); +uint32_t MIHP_GenerateHeapMemoryAreaChecksum(const MIHP_HeapMemoryAreaHeader* area); +uint32_t MIHP_GenerateHeapSegmentChecksum(const MIHP_HeapSegmentHeader* segment); + +void MIHP_ValidateHeapMemoryAreaHeader(const MIHP_Heap* heap, const MIHP_HeapMemoryAreaHeader* area); +void MIHP_ValidateHeapSegmentHeader(const MIHP_Heap* heap, const MIHP_HeapSegmentHeader* segment); + +size_t MIHP_GetMinimalPayloadSize(const MIHP_Heap* heap); +/* */ -#if USE_HEAP_STRUCTURE_CHECKSUM - checksum_size = sizeof(area->checksum); -#else - checksum_size = 0; #endif - // Do not include the checksum itself into the new checksum - size_t size = sizeof(*area) - checksum_size; - return fast_memory_hasher(area, size); -} - -uint32_t generate_heap_segment_checksum(const heap_segment* segment) -{ - size_t checksum_size; - -#if USE_HEAP_STRUCTURE_CHECKSUM - checksum_size = sizeof(segment->checksum); -#else - checksum_size = 0; +#if MIHP_IMPLEMENTATION + #include "minimal_heap_implementation.inl" #endif - - // Do not include the checksum itself into the new checksum - size_t size = sizeof(*segment) - checksum_size; - return fast_memory_hasher(segment, size); -} \ No newline at end of file diff --git a/minimal_heap_implementation.inl b/minimal_heap_implementation.inl new file mode 100644 index 0000000..592935e --- /dev/null +++ b/minimal_heap_implementation.inl @@ -0,0 +1,637 @@ +#define MIHP_MIN(A, B) (A) < (B) ? (A) : (B) +#define MIHP_MAX(A, B) (A) > (B) ? (A) : (B) + +#define MIHP_MEMSET(Ptr, Size, Val) for(size_t i = 0; i < Size; ++i) *((char*)Ptr + i) = Val; + +#define MIHP_IS_PO2(Val) (Val != 0 && (Val & (Val - 1)) == 0) + +MIHP_Heap* MIHP_CreateHeap(MIHP_HeapConfig config) +{ + if (config.AllocationAlignment < sizeof(size_t)) + return NULL; + + if (!MIHP_IS_PO2(config.AllocationAlignment)) + return NULL; + + if (config.MinimalMemoryAreaSize % config.AllocationAlignment != 0) + return NULL; + + if (config.MaximalMemoryAreaSize % config.AllocationAlignment != 0) + return NULL; + + MIHP_Heap* heap = (MIHP_Heap*)MIHP_PlatformRequestMemory(sizeof(MIHP_Heap)); + if (heap == NULL) + return NULL; + + MIHP_MEMSET(heap, sizeof(MIHP_Heap), 0); + + heap->Config = config; + + heap->FirstArea = MIHP_CreateHeapMemoryArea(heap, config.MinimalMemoryAreaSize); + heap->LastSuccessfulAllocationArea = heap->FirstArea; + if (heap->FirstArea == NULL) + { + MIHP_PlatformFreeMemory(heap, sizeof(MIHP_Heap)); + return NULL; + } + + heap->FirstArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(heap->FirstArea); + + return heap; +} + +bool MIHP_DestroyHeap(MIHP_Heap* heap, bool force) +{ + if (heap == NULL) + return false; + + if (!force && heap->Stats.NumTotalOccupiedSegments > 0) + return false; + + MIHP_HeapMemoryAreaHeader* nextArea = heap->FirstArea; + while (nextArea) + { + MIHP_HeapMemoryAreaHeader* currentArea = nextArea; + MIHP_ValidateHeapMemoryAreaHeader(heap, currentArea); + nextArea = currentArea->NextArea; + + MIHP_DestroyHeapMemoryArea(heap, currentArea); + } + + MIHP_PlatformFreeMemory(heap, sizeof(MIHP_Heap)); + return true; +} + +void* MIHP_Allocate(MIHP_Heap* heap, size_t size) +{ + if (heap == NULL) + return NULL; + + if (size == 0) + return NULL; + + if (size > heap->Config.MaximalMemoryAreaSize) + return NULL; + + MIHP_LOCK_HEAP(&heap->HeapLock); + + bool triedLastAllocationArea = false; + MIHP_HeapMemoryAreaHeader* nextArea = heap->FirstArea; + while (nextArea) + { + MIHP_HeapMemoryAreaHeader* currentArea = NULL; + + if (!triedLastAllocationArea) + { + currentArea = heap->LastSuccessfulAllocationArea; + triedLastAllocationArea = true; + } + else + { + currentArea = nextArea; + nextArea = currentArea->NextArea; + } + + MIHP_ValidateHeapMemoryAreaHeader(heap, currentArea); + + if (currentArea->NumOccupiedSegments == currentArea->NumSegments) + continue; + + MIHP_HeapSegmentHeader* nextSegment = currentArea->FirstSegment; + while (nextSegment) + { + MIHP_HeapSegmentHeader* currentSegment = nextSegment; + MIHP_ValidateHeapSegmentHeader(heap, currentSegment); + nextSegment = currentSegment->NextSegment; + + if (currentSegment->OccupiedSize != 0) + continue; + + size_t segmentHeaderSize = MIHP_GetHeapAlignedSize(heap, sizeof(MIHP_HeapSegmentHeader)); + MIHP_HeapSegmentHeader* targetSegment = NULL; + + if (currentSegment->SegmentSize >= 2 * segmentHeaderSize + size + MIHP_GetMinimalPayloadSize(heap)) + MIHP_SplitHeapSegment(currentSegment, segmentHeaderSize + size, &targetSegment); + else if (currentSegment->SegmentSize >= segmentHeaderSize + size) + targetSegment = currentSegment; + else + continue; + + targetSegment->OccupiedSize = size; + targetSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(targetSegment); + + currentArea->NumOccupiedSegments++; + currentArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(currentArea); + + heap->Stats.NumTotalOccupiedSegments++; + heap->LastSuccessfulAllocationArea = currentArea; + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + + void* data = (char*)targetSegment + segmentHeaderSize; + MIHP_MEMSET(data, size, heap->Config.AllocationInitialValue); + + return data; + } + } + + size_t newAreaSize = heap->Stats.TotalSize; + + while (newAreaSize < size) + newAreaSize = MIHP_MIN(heap->Config.MaximalMemoryAreaSize, newAreaSize * 2); + + MIHP_HeapMemoryAreaHeader* newArea = MIHP_CreateHeapMemoryArea(heap, newAreaSize); + if (newArea == NULL) + return NULL; + + newArea->NextArea = heap->FirstArea; + heap->FirstArea->PreviousArea = newArea; + heap->FirstArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(heap->FirstArea); + + heap->FirstArea = newArea; + heap->LastSuccessfulAllocationArea = newArea; + newArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(newArea); + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return MIHP_Allocate(heap, size); +} + +void* MIHP_Realloc(MIHP_Heap* heap, void* ptr, size_t newSize) +{ + if (newSize == 0) + { + MIHP_Free(heap, ptr); + return NULL; + } + + if (heap == NULL) + return NULL; + + if (ptr == NULL) + return MIHP_Allocate(heap, newSize); + + if (!MIHP_IsPointerInHeap(heap, ptr)) + return NULL; + + MIHP_LOCK_HEAP(&heap->HeapLock); + + size_t segmentHeaderSize = MIHP_GetHeapAlignedSize(heap, sizeof(MIHP_HeapSegmentHeader)); + MIHP_HeapSegmentHeader* segment = (MIHP_HeapSegmentHeader*)((char*)ptr - segmentHeaderSize); + MIHP_ValidateHeapSegmentHeader(heap, segment); + + if (newSize > segment->OccupiedSize) + { + size_t maxPayloadSize = segment->SegmentSize - segmentHeaderSize; + if (newSize <= maxPayloadSize) + { + MIHP_MEMSET((char*)ptr + segment->OccupiedSize, newSize - segment->OccupiedSize, heap->Config.AllocationInitialValue); + segment->OccupiedSize = newSize; + segment->Checksum = MIHP_GenerateHeapSegmentChecksum(segment); + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return ptr; + } + + if (segment->NextSegment && MIHP_MergeHeapSegments(segment, segment->NextSegment)) + { + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return MIHP_Realloc(heap, ptr, newSize); + } + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + + void* newPtr = MIHP_Allocate(heap, newSize); + if (!newPtr) + return NULL; + + for (size_t i = 0; i < segment->OccupiedSize; i++) + ((char*)newPtr)[i] = ((char*)ptr)[i]; + + MIHP_Free(heap, ptr); + return newPtr; + } + else if (newSize < segment->OccupiedSize) + { + segment->OccupiedSize = newSize; + segment->Checksum = MIHP_GenerateHeapSegmentChecksum(segment); + + size_t alignedNewSize = MIHP_GetHeapAlignedSize(heap, newSize); + if (segment->SegmentSize > 2 * segmentHeaderSize + alignedNewSize + MIHP_GetMinimalPayloadSize(heap)) + { + size_t newSplitSegmentSize = segment->SegmentSize - segmentHeaderSize - alignedNewSize; + + MIHP_HeapSegmentHeader* newSpittedSegment = NULL; + MIHP_SplitHeapSegment(segment, newSplitSegmentSize, &newSpittedSegment); + if (newSpittedSegment && newSpittedSegment->NextSegment) + MIHP_MergeHeapSegments(newSpittedSegment, newSpittedSegment->NextSegment); + } + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return ptr; + } + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return ptr; +} + +bool MIHP_Free(MIHP_Heap* heap, void* ptr) +{ + if (heap == NULL) + return false; + + if (ptr == NULL) + return false; + + if (!MIHP_IsPointerInHeap(heap, ptr)) + return false; + + MIHP_LOCK_HEAP(&heap->HeapLock); + + size_t segmentHeaderSize = MIHP_GetHeapAlignedSize(heap, sizeof(MIHP_HeapSegmentHeader)); + MIHP_HeapSegmentHeader* segment = (MIHP_HeapSegmentHeader*)((char*)ptr - segmentHeaderSize); + MIHP_ValidateHeapSegmentHeader(heap, segment); + + MIHP_HeapMemoryAreaHeader* area = segment->OwningMemoryArea; + MIHP_ValidateHeapMemoryAreaHeader(heap, area); + + segment->OccupiedSize = 0; + segment->Checksum = MIHP_GenerateHeapSegmentChecksum(segment); + + area->NumOccupiedSegments--; + area->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(area); + + heap->Stats.NumTotalOccupiedSegments--; + + if (area->NumOccupiedSegments == 0 && heap->Stats.NumMemoryAreas > 1) + { + MIHP_HeapMemoryAreaHeader* prevArea = area->PreviousArea; + MIHP_HeapMemoryAreaHeader* nextArea = area->NextArea; + + if (MIHP_DestroyHeapMemoryArea(heap, area)) + { + if (prevArea) + { + MIHP_ValidateHeapMemoryAreaHeader(heap, prevArea); + prevArea->NextArea = nextArea; + prevArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(prevArea); + } + else + heap->FirstArea = nextArea; + + if (nextArea) + { + MIHP_ValidateHeapMemoryAreaHeader(heap, nextArea); + nextArea->PreviousArea = prevArea; + nextArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(nextArea); + } + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return true; + } + } + + if (segment->NextSegment) + { + MIHP_ValidateHeapSegmentHeader(heap, segment->NextSegment); + if (segment->NextSegment->OccupiedSize == 0) + MIHP_MergeHeapSegments(segment, segment->NextSegment); + } + + if (segment->PreviousSegment) + { + MIHP_ValidateHeapSegmentHeader(heap, segment->PreviousSegment); + if (segment->PreviousSegment->OccupiedSize == 0) + MIHP_MergeHeapSegments(segment->PreviousSegment, segment); + } + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return true; +} + +bool MIHP_IsPointerInHeap(MIHP_Heap* heap, void* ptr) +{ + if (heap == NULL) + return false; + + MIHP_LOCK_HEAP(&heap->HeapLock); + + size_t searchingPtr = (size_t)ptr; + + MIHP_HeapMemoryAreaHeader* nextArea = heap->FirstArea; + while (nextArea) + { + MIHP_HeapMemoryAreaHeader* currentArea = nextArea; + nextArea = currentArea->NextArea; + + size_t ptrMin = (size_t)currentArea; + size_t ptrMax = (size_t)currentArea + currentArea->AreaSize; + + if (searchingPtr >= ptrMin && searchingPtr <= ptrMax) + { + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return true; + } + } + + MIHP_UNLOCK_HEAP(&heap->HeapLock); + return false; +} + +size_t MIHP_GetPtrAllocationSize(MIHP_Heap* heap, void* ptr) +{ + if (!MIHP_IsPointerInHeap(heap, ptr)) + return 0; + + MIHP_LOCK_HEAP(&heap->HeapLock); + + size_t segmentHeaderSize = MIHP_GetHeapAlignedSize(heap, sizeof(MIHP_HeapSegmentHeader)); + MIHP_HeapSegmentHeader* segment = (MIHP_HeapSegmentHeader*)((char*)ptr - segmentHeaderSize); + MIHP_ValidateHeapSegmentHeader(heap, segment); + + return segment->OccupiedSize; +} + +void MIHP_ValidateHeap(MIHP_Heap* heap) +{ + MIHP_HeapMemoryAreaHeader* nextArea = heap->FirstArea; + while (nextArea) + { + MIHP_HeapMemoryAreaHeader* currentArea = nextArea; + MIHP_ValidateHeapMemoryAreaHeader(heap, currentArea); + nextArea = currentArea->NextArea; + + MIHP_HeapSegmentHeader* nextSegment = currentArea->FirstSegment; + while (nextSegment) + { + MIHP_HeapSegmentHeader* currentSegment = nextSegment; + MIHP_ValidateHeapSegmentHeader(heap, currentSegment); + nextSegment = currentSegment->NextSegment; + } + } +} + +MIHP_HeapMemoryAreaHeader* MIHP_CreateHeapMemoryArea(MIHP_Heap* heap, size_t requestedSize) +{ + MIHP_ASSERT(heap); + + size_t effectiveSize = ((requestedSize + heap->Config.MinimalMemoryAreaSize - 1) / heap->Config.MinimalMemoryAreaSize) * heap->Config.MinimalMemoryAreaSize; + effectiveSize = MIHP_MAX(effectiveSize, heap->Config.MinimalMemoryAreaSize); + effectiveSize = MIHP_MIN(effectiveSize, heap->Config.MaximalMemoryAreaSize); + + MIHP_HeapMemoryAreaHeader* area = (MIHP_HeapMemoryAreaHeader*)MIHP_PlatformRequestMemory(effectiveSize); + if (area == NULL) + return NULL; + + MIHP_MEMSET(area, sizeof(MIHP_HeapMemoryAreaHeader), 0); + + area->AreaSize = effectiveSize; + area->OwningHeap = heap; + area->Checksum = ~0; + + size_t headerSize = MIHP_GetHeapAlignedSize(heap, sizeof(MIHP_HeapMemoryAreaHeader)); + area->FirstSegment = MIHP_InitializeHeapSegment(area, (char*)area + headerSize, area->AreaSize - headerSize); + area->FirstSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(area->FirstSegment); + + heap->Stats.NumMemoryAreas++; + heap->Stats.TotalSize += effectiveSize; + + return area; +} + +bool MIHP_DestroyHeapMemoryArea(MIHP_Heap* heap, MIHP_HeapMemoryAreaHeader* memoryArea) +{ + MIHP_ASSERT(heap); + MIHP_ASSERT(memoryArea); + + size_t areaNumSegments = memoryArea->NumSegments; + size_t areaNumOccupiedSegments = memoryArea->NumOccupiedSegments; + size_t areaSize = memoryArea->AreaSize; + + if (!MIHP_PlatformFreeMemory(memoryArea, memoryArea->AreaSize)) + return false; + + heap->Stats.NumTotalSegments -= areaNumSegments; + heap->Stats.NumTotalOccupiedSegments -= areaNumOccupiedSegments; + heap->Stats.NumMemoryAreas--; + heap->Stats.TotalSize -= areaSize; + + return true; +} + +MIHP_HeapSegmentHeader* MIHP_InitializeHeapSegment(MIHP_HeapMemoryAreaHeader* area, void* segmentStart, size_t segmentSize) +{ + MIHP_ASSERT(area); + MIHP_ASSERT(segmentStart); + + MIHP_HeapSegmentHeader* segment = (MIHP_HeapSegmentHeader*)segmentStart; + + MIHP_MEMSET(segment, sizeof(MIHP_HeapSegmentHeader), 0); + + segment->SegmentSize = segmentSize; + segment->OwningMemoryArea = area; + segment->Checksum = ~0; + + area->NumSegments++; + area->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(area); + + area->OwningHeap->Stats.NumTotalSegments++; + + return segment; +} + +bool MIHP_UninitializeHeapSegment(MIHP_HeapSegmentHeader* segment) +{ + MIHP_ASSERT(segment); + + if (segment->OccupiedSize > 0) + return false; + + segment->OwningMemoryArea->NumSegments--; + segment->OwningMemoryArea->Checksum = MIHP_GenerateHeapMemoryAreaChecksum(segment->OwningMemoryArea); + segment->OwningMemoryArea->OwningHeap->Stats.NumTotalSegments--; + + MIHP_MEMSET(segment, sizeof(MIHP_HeapSegmentHeader), 0); + + return true; +} + +bool MIHP_SplitHeapSegment(MIHP_HeapSegmentHeader* sourceSegment, size_t newSegmentMinSize, MIHP_HeapSegmentHeader** outNewSegment) +{ + MIHP_ASSERT(sourceSegment); + + size_t headerSize = MIHP_GetHeapAlignedSize(sourceSegment->OwningMemoryArea->OwningHeap, sizeof(MIHP_HeapSegmentHeader)); + size_t effectiveNewSegmentSize = MIHP_GetHeapAlignedSize(sourceSegment->OwningMemoryArea->OwningHeap, newSegmentMinSize); + + if (sourceSegment->SegmentSize < effectiveNewSegmentSize) + return false; + + size_t sourceResultingSegmentSize = sourceSegment->SegmentSize - effectiveNewSegmentSize; + + if (sourceResultingSegmentSize < sourceSegment->OccupiedSize + headerSize) + return false; + + sourceSegment->SegmentSize = sourceResultingSegmentSize; + + MIHP_HeapSegmentHeader* newSegment = MIHP_InitializeHeapSegment(sourceSegment->OwningMemoryArea, (char*)sourceSegment + sourceResultingSegmentSize, effectiveNewSegmentSize); + newSegment->NextSegment = sourceSegment->NextSegment; + newSegment->PreviousSegment = sourceSegment; + + if (sourceSegment->NextSegment) + { + MIHP_ValidateHeapSegmentHeader(sourceSegment->OwningMemoryArea->OwningHeap, sourceSegment->NextSegment); + sourceSegment->NextSegment->PreviousSegment = newSegment; + sourceSegment->NextSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(sourceSegment->NextSegment); + } + + sourceSegment->NextSegment = newSegment; + + if (outNewSegment) + *outNewSegment = newSegment; + + newSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(newSegment); + sourceSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(sourceSegment); + + return true; +} + +bool MIHP_MergeHeapSegments(MIHP_HeapSegmentHeader* sourceSegment, MIHP_HeapSegmentHeader* segmentToAbsorb) +{ + MIHP_ASSERT(sourceSegment); + MIHP_ASSERT(segmentToAbsorb); + MIHP_ASSERT(sourceSegment->NextSegment == segmentToAbsorb); + + if (segmentToAbsorb->OccupiedSize > 0) + return false; + + sourceSegment->NextSegment = segmentToAbsorb->NextSegment; + + if (sourceSegment->NextSegment) + { + MIHP_ValidateHeapSegmentHeader(sourceSegment->OwningMemoryArea->OwningHeap, sourceSegment->NextSegment); + sourceSegment->NextSegment->PreviousSegment = sourceSegment; + sourceSegment->NextSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(sourceSegment->NextSegment); + } + + sourceSegment->SegmentSize += segmentToAbsorb->SegmentSize; + MIHP_UninitializeHeapSegment(segmentToAbsorb); + + sourceSegment->Checksum = MIHP_GenerateHeapSegmentChecksum(sourceSegment); + + return true; +} + +size_t MIHP_GetHeapAlignedSize(const MIHP_Heap* heap, size_t size) +{ + return (size + (heap->Config.AllocationAlignment - 1)) & ~(heap->Config.AllocationAlignment - 1); +} + +uint32_t MIHP_HashMemoryRegion(const void* data, size_t size) +{ + uint32_t hash = 2166136261U; + const unsigned char* bytes = (const unsigned char*)data; + for (size_t i = 0; i < size; i++) + { + hash ^= bytes[i]; + hash *= 16777619U; + } + + return hash; +} + +uint32_t MIHP_GenerateHeapMemoryAreaChecksum(const MIHP_HeapMemoryAreaHeader* area) +{ + +#if MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_NONE + return 0; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_MAGIC_NUMBER + return 0xAADEADAA; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_CHECKSUM + + MIHP_ASSERT(area); + + // Do not include the checksum itself into the new checksum + size_t size = offsetof(MIHP_HeapMemoryAreaHeader, Checksum); + return MIHP_HashMemoryRegion(area, size); + +#else + #error "Invalid heap structure validation type" +#endif +} + +uint32_t MIHP_GenerateHeapSegmentChecksum(const MIHP_HeapSegmentHeader* segment) +{ +#if MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_NONE + return 0; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_MAGIC_NUMBER + return 0x55DEAD55; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_CHECKSUM + + MIHP_ASSERT(segment); + + // Do not include the checksum itself into the new checksum + size_t size = offsetof(MIHP_HeapSegmentHeader, Checksum); + return MIHP_HashMemoryRegion(segment, size); + +#else + #error "Invalid heap structure validation type" +#endif +} + +void MIHP_ValidateHeapMemoryAreaHeader(const MIHP_Heap* heap, const MIHP_HeapMemoryAreaHeader* area) +{ + uint32_t newChecksum; + +#if MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_NONE + return; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_MAGIC_NUMBER + newChecksum = 0xAADEADAA; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_CHECKSUM + newChecksum = MIHP_GenerateHeapMemoryAreaChecksum(area); +#else + #error "Invalid heap structure validation type" +#endif + + if (area->Checksum != newChecksum) + { + MIHP_HeapCorruptionInfo corruptionInfo; + corruptionInfo.Heap = heap; + corruptionInfo.Type = MIHP_HEAP_CORRUPTION_TYPE_MEMORY_AREA_CHECKSUM_MISMATCH; + corruptionInfo.Ptr = area; + corruptionInfo.ExpectedValue = area->Checksum; + corruptionInfo.ActualValue = newChecksum; + + MIHP_PlatformOnHeapCorruptionDetected(corruptionInfo); + } +} + +void MIHP_ValidateHeapSegmentHeader(const MIHP_Heap* heap, const MIHP_HeapSegmentHeader* segment) +{ + uint32_t newChecksum; + +#if MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_NONE + return; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_MAGIC_NUMBER + newChecksum = 0x55DEAD55; +#elif MIHP_HEAP_STRUCTURE_VALIDATION_TYPE == MIHP_HEAP_STRUCTURE_VALIDATION_TYPE_CHECKSUM + newChecksum = MIHP_GenerateHeapSegmentChecksum(segment); +#else + #error "Invalid heap structure validation type" +#endif + + if (segment->Checksum != newChecksum) + { + MIHP_HeapCorruptionInfo corruptionInfo; + corruptionInfo.Heap = heap; + corruptionInfo.Type = MIHP_HEAP_CORRUPTION_TYPE_SEGMENT_CHECKSUM_MISMATCH; + corruptionInfo.Ptr = segment; + corruptionInfo.ExpectedValue = segment->Checksum; + corruptionInfo.ActualValue = newChecksum; + + MIHP_PlatformOnHeapCorruptionDetected(corruptionInfo); + } +} + +size_t MIHP_GetMinimalPayloadSize(const MIHP_Heap* heap) +{ + return heap->Config.AllocationAlignment; +} diff --git a/minimal_heap_test.c b/minimal_heap_test.c new file mode 100644 index 0000000..4c194d0 --- /dev/null +++ b/minimal_heap_test.c @@ -0,0 +1,108 @@ +#define MIHP_IMPLEMENTATION 1 +#include "minimal_heap.h" + +#include +#include +#include + +#define USE_MALLOC 0 + +//#define MIHP_ValidateHeap() + +void* MIHP_PlatformRequestMemory(size_t size) +{ + return malloc(size); +} + +bool MIHP_PlatformFreeMemory(void* ptr, size_t size) +{ + free(ptr); + return true; +} + +void MIHP_PlatformOnHeapCorruptionDetected(MIHP_HeapCorruptionInfo info) +{ + __debugbreak(); +} + +#define NUM_ALLOCATIONS (4096 * 4) + +void* myptrs[NUM_ALLOCATIONS] = { 0 }; + +int main() +{ + MIHP_HeapConfig config = {0}; + config.AllocationInitialValue = 0xbe; + config.AllocationAlignment = 16; + config.MinimalMemoryAreaSize = 4096 * 32; + config.MaximalMemoryAreaSize = 4096ull * 4096ull * 4096ull; + MIHP_Heap* myHeap = MIHP_CreateHeap(config); + + for (int i = 0; i < NUM_ALLOCATIONS; i++) + { + size_t size = 473 + (i % 5 == 0 ? 854 : 0); +#if USE_MALLOC + myptrs[i] = malloc(size); +#else + myptrs[i] = MIHP_Allocate(myHeap, size); + MIHP_ValidateHeap(myHeap); +#endif + } + + __debugbreak(); + + for (int i = 0; i < NUM_ALLOCATIONS; i++) + { + if (rand() % 3 == 0) + { +#if USE_MALLOC + free(myptrs[i]); +#else + MIHP_Free(myHeap, myptrs[i]); + MIHP_ValidateHeap(myHeap); +#endif + + myptrs[i] = NULL; + } + } + + __debugbreak(); + + for (int i = 0; i < NUM_ALLOCATIONS; i++) + { + volatile int n = i; + if (myptrs[i]) + { + size_t size = 300 + rand() % 500; + +#if USE_MALLOC + myptrs[i] = realloc(myptrs[i], size); +#else + myptrs[i] = MIHP_Realloc(myHeap, myptrs[i], size); + MIHP_ValidateHeap(myHeap); + + +#endif + } + } + + __debugbreak(); + + for (int z = 15; z >= 0; z--) + for (int y = 0; y < NUM_ALLOCATIONS / 16; y++) + { + int i = z + y * 16; +#if USE_MALLOC + free(myptrs[i]); +#else + MIHP_Free(myHeap, myptrs[i]); + MIHP_ValidateHeap(myHeap); +#endif + + myptrs[i] = NULL; + } + + + __debugbreak(); +} + diff --git a/minimal_heap_test.sln b/minimal_heap_test.sln new file mode 100644 index 0000000..806e01c --- /dev/null +++ b/minimal_heap_test.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36717.8 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minimal_heap_test", "minimal_heap_test.vcxproj", "{58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Debug|x64.ActiveCfg = Debug|x64 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Debug|x64.Build.0 = Debug|x64 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Debug|x86.ActiveCfg = Debug|Win32 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Debug|x86.Build.0 = Debug|Win32 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Release|x64.ActiveCfg = Release|x64 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Release|x64.Build.0 = Release|x64 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Release|x86.ActiveCfg = Release|Win32 + {58F662F6-EAAC-4D4B-A2F5-C25ED48A05F7}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4273B178-AE5C-4FCB-99D3-ACA9EE4C7E80} + EndGlobalSection +EndGlobal diff --git a/minimal_heap_test.vcxproj b/minimal_heap_test.vcxproj new file mode 100644 index 0000000..06693a7 --- /dev/null +++ b/minimal_heap_test.vcxproj @@ -0,0 +1,138 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {58f662f6-eaac-4d4b-a2f5-c25ed48a05f7} + minimalheaptest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdc17 + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/minimal_heap_test.vcxproj.filters b/minimal_heap_test.vcxproj.filters new file mode 100644 index 0000000..aad3cae --- /dev/null +++ b/minimal_heap_test.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Quelldateien + + + + + Headerdateien + + + + + Headerdateien + + + \ No newline at end of file diff --git a/minimal_heap_test.vcxproj.user b/minimal_heap_test.vcxproj.user new file mode 100644 index 0000000..966b4ff --- /dev/null +++ b/minimal_heap_test.vcxproj.user @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file