Skip to content

Commit 08aba48

Browse files
committed
Add allocators, string utilities, and hashing support
Implemented `BumpAllocator` and `ConcurrentBumpAllocator` for efficient memory management in single-threaded and multi-threaded environments. Added `StringPool` for string deduplication and `StringSpan` for span-based string manipulation. Introduced `MurmurHash3` for robust hashing. Enhanced `AlignmentHelper` with `AlignUp` and `AlignDown` methods for `nuint` and `nint` types. Corrected operator precedence in existing alignment methods. Updated `UnsafeHashSet` with public `EntryFlags` and `Entry`, added `ResultPair` struct, and introduced `AddIt` and `At` methods. Added extensive unit tests for allocators, string utilities, and hash functions. Updated `Hexa.NET.Utilities` package version to 2.2.5.
1 parent b9dd4f7 commit 08aba48

File tree

11 files changed

+2756
-7
lines changed

11 files changed

+2756
-7
lines changed

Hexa.NET.Utilities.Tests/BumpAllocatorSingleThreadedTests.cs

Lines changed: 528 additions & 0 deletions
Large diffs are not rendered by default.

Hexa.NET.Utilities.Tests/ConcurrentBumpAllocatorTests.cs

Lines changed: 512 additions & 0 deletions
Large diffs are not rendered by default.

Hexa.NET.Utilities.Tests/StringPoolTests.cs

Lines changed: 577 additions & 0 deletions
Large diffs are not rendered by default.

Hexa.NET.Utilities/AlignmentHelper.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,44 @@
22
{
33
public static class AlignmentHelper
44
{
5+
public static nuint AlignUp(nuint size, nuint alignment)
6+
{
7+
return (size + alignment - 1) & ~(alignment - 1);
8+
}
9+
10+
public static nint AlignUp(nint size, nint alignment)
11+
{
12+
return (size + alignment - 1) & ~(alignment - 1);
13+
}
14+
515
public static ulong AlignUp(ulong size, ulong alignment)
616
{
7-
return size + alignment - 1 & ~(alignment - 1);
17+
return (size + alignment - 1) & ~(alignment - 1);
818
}
919

1020
public static uint AlignUp(uint size, uint alignment)
1121
{
12-
return size + alignment - 1 & ~(alignment - 1);
22+
return (size + alignment - 1) & ~(alignment - 1);
1323
}
1424

1525
public static long AlignUp(long size, long alignment)
1626
{
17-
return size + alignment - 1 & ~(alignment - 1);
27+
return (size + alignment - 1) & ~(alignment - 1);
1828
}
1929

2030
public static int AlignUp(int size, int alignment)
2131
{
22-
return size + alignment - 1 & ~(alignment - 1);
32+
return (size + alignment - 1) & ~(alignment - 1);
33+
}
34+
35+
public static nuint AlignDown(nuint size, nuint alignment)
36+
{
37+
return size & ~(alignment - 1);
38+
}
39+
40+
public static nint AlignDown(nint size, nint alignment)
41+
{
42+
return size & ~(alignment - 1);
2343
}
2444

2545
public static ulong AlignDown(ulong size, ulong alignment)
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#if NET5_0_OR_GREATER
2+
namespace Hexa.NET.Utilities
3+
{
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
7+
public unsafe struct BumpAllocator : IDisposable, IFreeable
8+
{
9+
public const uint PageSize = 4096;
10+
11+
private struct MemoryBlock
12+
{
13+
public MemoryBlock* Next;
14+
public uint Used;
15+
public uint Size;
16+
17+
public MemoryBlock(MemoryBlock* next, uint size)
18+
{
19+
Next = next;
20+
Size = size;
21+
}
22+
23+
public static MemoryBlock* Create(uint size, MemoryBlock* next)
24+
{
25+
size = AlignmentHelper.AlignUp(size + (uint)sizeof(MemoryBlock), PageSize);
26+
var mem = (byte*)NativeMemory.AlignedAlloc(size, PageSize) + size;
27+
MemoryBlock* block = ((MemoryBlock*)mem) - 1;
28+
*block = new(next, size - (uint)sizeof(MemoryBlock));
29+
return block;
30+
}
31+
32+
public uint Alloc(uint size, uint alignment)
33+
{
34+
var offset = Used;
35+
var alignedOffset = AlignmentHelper.AlignUp(offset, alignment);
36+
var newUsed = alignedOffset + size;
37+
if (newUsed > Size)
38+
{
39+
return uint.MaxValue;
40+
}
41+
Used = newUsed;
42+
return alignedOffset;
43+
}
44+
45+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46+
public static byte* GetBasePointer(MemoryBlock* block)
47+
{
48+
return (byte*)block - block->Size;
49+
}
50+
51+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52+
public static void* GetPointer(MemoryBlock* block, uint offset)
53+
{
54+
return GetBasePointer(block) + offset;
55+
}
56+
57+
58+
public static void Destroy(MemoryBlock* block)
59+
{
60+
NativeMemory.AlignedFree(GetBasePointer(block));
61+
}
62+
}
63+
64+
private MemoryBlock* head;
65+
private MemoryBlock* tail;
66+
private MemoryBlock* freeList;
67+
68+
69+
private MemoryBlock* CreateBlock(uint sizeMin)
70+
{
71+
MemoryBlock* block;
72+
73+
if (freeList != null)
74+
{
75+
block = freeList;
76+
freeList = freeList->Next;
77+
block->Next = tail;
78+
tail = block;
79+
return block;
80+
}
81+
82+
block = MemoryBlock.Create(Math.Max(sizeMin, PageSize), tail);
83+
tail = block;
84+
if (head == null)
85+
{
86+
head = block;
87+
}
88+
89+
return block;
90+
91+
}
92+
93+
public void* Alloc(uint size, uint alignment = 8)
94+
{
95+
while (true)
96+
{
97+
var currentTail = tail;
98+
if (currentTail != null)
99+
{
100+
var offs = currentTail->Alloc(size, alignment);
101+
if (offs != uint.MaxValue)
102+
{
103+
return MemoryBlock.GetPointer(currentTail, offs);
104+
}
105+
}
106+
107+
CreateBlock(size);
108+
}
109+
}
110+
111+
public void Free(void* ptr, uint size)
112+
{
113+
var basePtr = MemoryBlock.GetBasePointer(tail);
114+
var endPtr = basePtr + tail->Used;
115+
if ((byte*)ptr + size == endPtr)
116+
{
117+
tail->Used -= size;
118+
}
119+
}
120+
121+
public void Reset()
122+
{
123+
var curr = tail;
124+
while (curr != null)
125+
{
126+
curr->Used = 0;
127+
curr = curr->Next;
128+
}
129+
freeList = tail;
130+
tail = null;
131+
head = null;
132+
}
133+
134+
public void ReleaseAll()
135+
{
136+
DestroyList(tail);
137+
DestroyList(freeList);
138+
tail = null;
139+
head = null;
140+
freeList = null;
141+
}
142+
143+
private static void DestroyList(MemoryBlock* curr)
144+
{
145+
while (curr != null)
146+
{
147+
var next = curr->Next;
148+
MemoryBlock.Destroy(curr);
149+
curr = next;
150+
}
151+
}
152+
153+
public void Dispose()
154+
{
155+
ReleaseAll();
156+
}
157+
158+
public void Release()
159+
{
160+
ReleaseAll();
161+
}
162+
}
163+
}
164+
165+
#endif

0 commit comments

Comments
 (0)