Skip to content

Commit 9cf85a7

Browse files
committed
Fixed bug in queue
1 parent 909fb10 commit 9cf85a7

File tree

3 files changed

+118
-53
lines changed

3 files changed

+118
-53
lines changed

Hexa.NET.Utilities/Hexa.NET.Utilities.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<PropertyGroup>
2020
<PackageId>Hexa.NET.Utilities</PackageId>
2121
<AssemblyVersion>1.0.0</AssemblyVersion>
22-
<PackageVersion>2.1.0</PackageVersion>
22+
<PackageVersion>2.1.1</PackageVersion>
2323
<Authors>Juna</Authors>
2424
<AssemblyName>Hexa.NET.Utilities</AssemblyName>
2525
<PackageProjectUrl>https://github.com/HexaEngine/Hexa.NET.Utilities</PackageProjectUrl>

Hexa.NET.Utilities/UnsafeQueue.cs

Lines changed: 92 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ public unsafe struct UnsafeQueue<T> : IFreeable where T : unmanaged
1212
private const int DefaultCapacity = 4;
1313

1414
private T* items;
15-
private T* head;
16-
private T* tail;
17-
private int size;
18-
private int capacity;
15+
private nint front;
16+
private nint rear;
17+
private nint size;
18+
private nint capacity;
1919

2020
/// <summary>
2121
/// Initializes a new instance of the <see cref="UnsafeQueue{T}"/> struct with the default capacity.
@@ -37,24 +37,48 @@ public UnsafeQueue(int capacity)
3737
/// <summary>
3838
/// Gets the number of elements in the queue.
3939
/// </summary>
40-
public readonly int Size => size;
40+
public readonly nint Size => size;
4141

4242
/// <summary>
4343
/// Gets or sets the capacity of the queue.
4444
/// </summary>
45-
public int Capacity
45+
public nint Capacity
4646
{
4747
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4848
readonly get => capacity;
4949
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5050
set
5151
{
52+
if (value == capacity)
53+
{
54+
return;
55+
}
56+
57+
if (items == null)
58+
{
59+
items = AllocT<T>(value);
60+
return;
61+
}
62+
5263
var tmp = AllocT<T>(value);
53-
var oldsize = size * sizeof(T);
54-
var newsize = value * sizeof(T);
55-
Buffer.MemoryCopy(items, tmp, newsize, oldsize > newsize ? newsize : oldsize);
64+
65+
if (size > 0)
66+
{
67+
if (front <= rear)
68+
{
69+
MemcpyT(items + front, tmp, value, size);
70+
}
71+
else
72+
{
73+
MemcpyT(items + front, tmp, value, capacity - front);
74+
MemcpyT(items, tmp + (capacity - front), value, rear);
75+
}
76+
}
77+
5678
Free(items);
57-
head = tail = items = tmp;
79+
items = tmp;
80+
front = 0;
81+
rear = size - 1;
5882
capacity = value;
5983
size = capacity < size ? capacity : size;
6084
}
@@ -71,6 +95,21 @@ public T this[int index]
7195
set => items[index] = value;
7296
}
7397

98+
/// <summary>
99+
/// Gets the pointer to the items of the queue.
100+
/// </summary>
101+
public readonly T* Items => items;
102+
103+
/// <summary>
104+
/// Gets the pointer to the front element of the queue.
105+
/// </summary>
106+
public readonly T* Front => items + front;
107+
108+
/// <summary>
109+
/// Gets the pointer to the rear element of the queue.
110+
/// </summary>
111+
public readonly T* Rear => items + rear;
112+
74113
/// <summary>
75114
/// Initializes the queue with the default capacity.
76115
/// </summary>
@@ -91,9 +130,9 @@ public void Init(int capacity)
91130
}
92131

93132
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94-
private void Grow(int capacity)
133+
private void Grow(nint capacity)
95134
{
96-
int newcapacity = size == 0 ? DefaultCapacity : 2 * size;
135+
nint newcapacity = size == 0 ? DefaultCapacity : 2 * size;
97136

98137
if (newcapacity < capacity)
99138
{
@@ -103,12 +142,21 @@ private void Grow(int capacity)
103142
Capacity = newcapacity;
104143
}
105144

145+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
146+
private void Shrink()
147+
{
148+
if (capacity > DefaultCapacity && capacity > size * 2)
149+
{
150+
Capacity = size;
151+
}
152+
}
153+
106154
/// <summary>
107155
/// Ensures that the queue has the specified capacity.
108156
/// </summary>
109157
/// <param name="capacity">The desired capacity.</param>
110158
[MethodImpl(MethodImplOptions.AggressiveInlining)]
111-
public void EnsureCapacity(int capacity)
159+
public void EnsureCapacity(nint capacity)
112160
{
113161
if (this.capacity < capacity)
114162
{
@@ -124,8 +172,8 @@ public void EnsureCapacity(int capacity)
124172
public void Enqueue(T item)
125173
{
126174
EnsureCapacity(size + 1);
127-
head++;
128-
*head = item;
175+
rear = (rear + 1) % capacity;
176+
items[rear] = item;
129177
size++;
130178
}
131179

@@ -136,15 +184,17 @@ public void Enqueue(T item)
136184
[MethodImpl(MethodImplOptions.AggressiveInlining)]
137185
public T Dequeue()
138186
{
139-
var tmp = *tail;
140-
*tail = default;
141-
tail++;
142-
if (head == tail)
187+
if (size == 0)
143188
{
144-
head = tail = items;
189+
throw new InvalidOperationException("Queue is empty.");
145190
}
191+
192+
T item = items[front];
193+
front = (front + 1) % capacity;
146194
size--;
147-
return tmp;
195+
Shrink();
196+
197+
return item;
148198
}
149199

150200
/// <summary>
@@ -160,14 +210,12 @@ public bool TryDequeue(out T item)
160210
item = default;
161211
return false;
162212
}
163-
item = *tail;
164-
*tail = default;
165-
tail++;
166-
if (head == tail)
167-
{
168-
head = tail = items;
169-
}
213+
214+
item = items[front];
215+
front = (front + 1) % capacity;
170216
size--;
217+
Shrink();
218+
171219
return true;
172220
}
173221

@@ -178,9 +226,9 @@ public bool TryDequeue(out T item)
178226
/// <param name="arrayIndex">The starting index in the destination array.</param>
179227
/// <param name="arraySize">The size of the destination array.</param>
180228
[MethodImpl(MethodImplOptions.AggressiveInlining)]
181-
public readonly void CopyTo(T* array, uint arrayIndex, uint arraySize)
229+
public readonly void CopyTo(T* array, int arrayIndex, int arraySize)
182230
{
183-
Buffer.MemoryCopy(items, &array[arrayIndex], (arraySize - arrayIndex) * sizeof(T), size * sizeof(T));
231+
MemcpyT(items, &array[arrayIndex], arraySize - arrayIndex, size);
184232
}
185233

186234
/// <summary>
@@ -192,9 +240,9 @@ public readonly void CopyTo(T* array, uint arrayIndex, uint arraySize)
192240
/// <param name="offset">The starting index in the queue.</param>
193241
/// <param name="count">The number of elements to copy.</param>
194242
[MethodImpl(MethodImplOptions.AggressiveInlining)]
195-
public void CopyTo(T* array, uint arrayIndex, uint arraySize, uint offset, uint count)
243+
public void CopyTo(T* array, int arrayIndex, int arraySize, int offset, int count)
196244
{
197-
Buffer.MemoryCopy(&items[offset], &array[arrayIndex], (arraySize - arrayIndex) * sizeof(T), (count - offset) * sizeof(T));
245+
MemcpyT(&items[offset], &array[arrayIndex], arraySize - arrayIndex, count - offset);
198246
}
199247

200248
/// <summary>
@@ -203,13 +251,10 @@ public void CopyTo(T* array, uint arrayIndex, uint arraySize, uint offset, uint
203251
[MethodImpl(MethodImplOptions.AggressiveInlining)]
204252
public void Clear()
205253
{
206-
var ptr = items;
207-
for (int i = 0; i < size; i++)
208-
{
209-
ptr[i] = default;
210-
}
254+
ZeroMemoryT(items, capacity);
211255
size = 0;
212-
head = tail = items;
256+
front = 0;
257+
rear = 0;
213258
}
214259

215260
/// <summary>
@@ -218,17 +263,13 @@ public void Clear()
218263
/// <param name="item">The item to search for.</param>
219264
/// <returns><c>true</c> if the item is found; otherwise, <c>false</c>.</returns>
220265
[MethodImpl(MethodImplOptions.AggressiveInlining)]
221-
public bool Contains(T* item)
266+
public bool Contains(T item)
222267
{
223-
for (int i = 0; i < size; i++)
268+
for (nint i = 0; i < size; i++)
224269
{
225-
var current = &items[i];
226-
if (current == null)
227-
{
228-
break;
229-
}
270+
var current = items[(front + i) % capacity];
230271

231-
if (current == item)
272+
if (current.Equals(item))
232273
{
233274
return true;
234275
}
@@ -243,12 +284,11 @@ public bool Contains(T* item)
243284
[MethodImpl(MethodImplOptions.AggressiveInlining)]
244285
public void Release()
245286
{
246-
Free(items);
247-
items = null;
248-
head = null;
249-
tail = null;
250-
capacity = 0;
251-
size = 0;
287+
if (items != null)
288+
{
289+
Free(items);
290+
this = default;
291+
}
252292
}
253293
}
254294
}

Hexa.NET.Utilities/Utils.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,19 @@ public static void MemcpyT<T>(T* src, T* dst, int dstLength, int srcLength) wher
380380
Buffer.MemoryCopy(src, dst, dstLength * sizeof(T), srcLength * sizeof(T));
381381
}
382382

383+
/// <summary>
384+
/// Copies memory from the source to the destination with the same length for both source and destination.
385+
/// </summary>
386+
/// <param name="src">Pointer to the source memory.</param>
387+
/// <param name="dst">Pointer to the destination memory.</param>
388+
/// <param name="dstLength">Length of the destination memory to copy to.</param>
389+
/// <param name="srcLength">Length of the source memory to copy from.</param>
390+
/// <typeparam name="T">Type of elements to copy.</typeparam>
391+
public static void MemcpyT<T>(T* src, T* dst, nint dstLength, nint srcLength) where T : unmanaged
392+
{
393+
Buffer.MemoryCopy(src, dst, dstLength * sizeof(T), srcLength * sizeof(T));
394+
}
395+
383396
/// <summary>
384397
/// Copies memory from the source to the destination with the same length for both source and destination.
385398
/// </summary>
@@ -441,6 +454,18 @@ public static void ZeroMemoryT<T>(T* pointer, uint length) where T : unmanaged
441454
ZeroMemory(pointer, sizeof(T) * (int)length);
442455
}
443456

457+
/// <summary>
458+
/// Sets all bytes in memory to zero for a span of type T.
459+
/// </summary>
460+
/// <typeparam name="T">The type of elements in the span.</typeparam>
461+
/// <param name="pointer">Pointer to the memory to clear.</param>
462+
/// <param name="length">Number of elements of type T to clear.</param>
463+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
464+
public static void ZeroMemoryT<T>(T* pointer, nint length) where T : unmanaged
465+
{
466+
ZeroMemory(pointer, sizeof(T) * length);
467+
}
468+
444469
/// <summary>
445470
/// Sets all bytes in memory to zero for a specified number of bytes.
446471
/// </summary>

0 commit comments

Comments
 (0)