Skip to content

Commit 0d7b703

Browse files
author
LoneWandererProductions
committed
small fixes
1 parent 871f6ba commit 0d7b703

2 files changed

Lines changed: 370 additions & 13 deletions

File tree

ExtendedSystemObjects/Helper/UnmanagedMemoryHelper.cs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,8 @@ internal static unsafe IntPtr Clone<T>(T* source, int count) where T : unmanaged
151151
[MethodImpl(MethodImplOptions.AggressiveInlining)]
152152
internal static unsafe void Fill<T>(T* ptr, T value, int count) where T : unmanaged
153153
{
154-
for (var i = 0; i < count; i++)
155-
{
156-
ptr[i] = value;
157-
}
154+
// Generates highly optimized, vectorized machine code natively
155+
new Span<T>(ptr, count).Fill(value);
158156
}
159157

160158
/// <summary>
@@ -163,15 +161,8 @@ internal static unsafe void Fill<T>(T* ptr, T value, int count) where T : unmana
163161
[MethodImpl(MethodImplOptions.AggressiveInlining)]
164162
internal static unsafe int IndexOf<T>(T* ptr, T value, int length) where T : unmanaged, IEquatable<T>
165163
{
166-
for (var i = 0; i < length; i++)
167-
{
168-
if (ptr[i].Equals(value))
169-
{
170-
return i;
171-
}
172-
}
173-
174-
return -1;
164+
// Bypasses the manual loop and uses native vectorization
165+
return new ReadOnlySpan<T>(ptr, length).IndexOf(value);
175166
}
176167

177168
/// <summary>
Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
/*
2+
* COPYRIGHT: See COPYING in the top level directory
3+
* PROJECT: ExtendedSystemObjects
4+
* FILE: ExtendedSystemObjects/UnmanagedList.cs
5+
* PURPOSE:
6+
* PROGRAMMER: Peter Geinitz (Wayfarer)
7+
*/
8+
9+
// ReSharper disable MemberCanBeInternal
10+
// ReSharper disable UnusedMember.Global
11+
// ReSharper disable MemberCanBePrivate.Global
12+
13+
using System;
14+
using System.Collections;
15+
using System.Collections.Generic;
16+
using System.Runtime.CompilerServices;
17+
using System.Runtime.InteropServices;
18+
using ExtendedSystemObjects.Helper;
19+
using ExtendedSystemObjects.Interfaces;
20+
21+
namespace ExtendedSystemObjects
22+
{
23+
/// <inheritdoc cref="IDisposable" />
24+
/// <summary>
25+
/// Unsafe array
26+
/// </summary>
27+
/// <typeparam name="T">Generic Type, must be unmanaged</typeparam>
28+
/// <seealso cref="T:System.IDisposable" />
29+
public sealed unsafe class UnmanagedList<T> : IUnmanagedArray<T>, IEnumerable<T> where T : unmanaged
30+
{
31+
/// <summary>
32+
/// The buffer
33+
/// </summary>
34+
private IntPtr _buffer;
35+
36+
/// <summary>
37+
/// Check if we disposed the object
38+
/// </summary>
39+
private bool _disposed;
40+
41+
/// <summary>
42+
/// The pointer
43+
/// </summary>
44+
private T* _ptr;
45+
46+
/// <summary>
47+
/// Provides direct access to the underlying unmanaged pointer.
48+
/// Use with caution.
49+
/// </summary>
50+
public T* Pointer => _ptr;
51+
52+
/// <summary>
53+
/// Initializes a new instance of the <see cref="UnmanagedList{T}" /> class.
54+
/// </summary>
55+
/// <param name="initialCapacity">The size.</param>
56+
public UnmanagedList(int initialCapacity)
57+
{
58+
Capacity = initialCapacity;
59+
Length = 0;
60+
if (initialCapacity > 0)
61+
{
62+
_buffer = UnmanagedMemoryHelper.Allocate<T>(initialCapacity);
63+
_ptr = (T*)_buffer;
64+
}
65+
else
66+
{
67+
_buffer = IntPtr.Zero;
68+
_ptr = null;
69+
}
70+
}
71+
72+
/// <summary>
73+
/// The capacity of the current Array.
74+
/// </summary>
75+
/// <value>
76+
/// The capacity.
77+
/// </value>
78+
public int Capacity { get; private set; }
79+
80+
81+
/// <inheritdoc />
82+
/// <summary>
83+
/// Returns an enumerator that iterates through the collection.
84+
/// </summary>
85+
/// <returns>
86+
/// An enumerator that can be used to iterate through the collection.
87+
/// </returns>
88+
public IEnumerator<T> GetEnumerator()
89+
{
90+
return new Enumerator<T>(_ptr, Length);
91+
}
92+
93+
/// <inheritdoc />
94+
/// <summary>
95+
/// Returns an enumerator that iterates through a collection.
96+
/// </summary>
97+
/// <returns>
98+
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
99+
/// </returns>
100+
IEnumerator IEnumerable.GetEnumerator()
101+
{
102+
return GetEnumerator();
103+
}
104+
105+
/// <inheritdoc />
106+
/// <inheritdoc />
107+
/// <summary>
108+
/// Gets the length.
109+
/// </summary>
110+
/// <value>
111+
/// The length.
112+
/// </value>
113+
public int Length { get; private set; }
114+
115+
/// <inheritdoc />
116+
/// <summary>
117+
/// Gets or sets the <see cref="!:T" /> at the specified index.
118+
/// </summary>
119+
/// <value>
120+
/// The <see cref="!:T" />.
121+
/// </value>
122+
/// <param name="index">The index.</param>
123+
/// <returns>The value at the specified index.</returns>
124+
/// <exception cref="T:System.IndexOutOfRangeException"></exception>
125+
public T this[int index]
126+
{
127+
get
128+
{
129+
#if DEBUG
130+
if (index < 0 || index >= Length)
131+
{
132+
throw new IndexOutOfRangeException();
133+
}
134+
#endif
135+
return _ptr[index];
136+
}
137+
set
138+
{
139+
#if DEBUG
140+
if (index < 0 || index >= Length)
141+
{
142+
throw new IndexOutOfRangeException();
143+
}
144+
#endif
145+
_ptr[index] = value;
146+
}
147+
}
148+
149+
/// <summary>
150+
/// Adds the specified item.
151+
/// </summary>
152+
/// <param name="item">The item.</param>
153+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
154+
public void Add(T item)
155+
{
156+
// If we hit capacity, double it
157+
if (Length == Capacity)
158+
{
159+
EnsureCapacity(Capacity == 0 ? 4 : Capacity * 2);
160+
}
161+
162+
_ptr[Length++] = item; // Drop it in and increment length
163+
}
164+
165+
/// <summary>
166+
/// Attempts to get the value at the specified index without throwing an exception.
167+
/// </summary>
168+
/// <param name="index">The zero-based index.</param>
169+
/// <param name="value">When this method returns, contains the value at the index, if found.</param>
170+
/// <returns>True if the index was valid and the list is not disposed; otherwise, false.</returns>
171+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
172+
public bool TryGet(int index, out T value)
173+
{
174+
// Check disposed and bounds in one go for speed
175+
if (_disposed || (uint)index >= (uint)Length)
176+
{
177+
value = default;
178+
return false;
179+
}
180+
181+
value = _ptr[index];
182+
return true;
183+
}
184+
185+
/// <inheritdoc />
186+
/// <summary>
187+
/// Removes elements starting at the given index.
188+
/// </summary>
189+
/// <param name="index">The start index.</param>
190+
/// <param name="count">Number of elements to remove.</param>
191+
/// <exception cref="ArgumentOutOfRangeException">index or count is invalid.</exception>
192+
public void RemoveAt(int index, int count = 1)
193+
{
194+
if (index < 0 || index >= Length) throw new ArgumentOutOfRangeException(nameof(index));
195+
if (count < 1 || index + count > Length) throw new ArgumentOutOfRangeException(nameof(count));
196+
197+
int moveCount = Length - (index + count);
198+
if (moveCount > 0)
199+
{
200+
// Use Spans to perform a high-speed memmove
201+
var source = new ReadOnlySpan<T>(_ptr + index + count, moveCount);
202+
var destination = new Span<T>(_ptr + index, moveCount);
203+
source.CopyTo(destination);
204+
}
205+
206+
Length -= count;
207+
}
208+
209+
/// <inheritdoc />
210+
/// <summary>
211+
/// Resizes the internal array to the specified new size.
212+
/// Contents will be preserved up to the minimum of old and new size.
213+
/// </summary>
214+
/// <param name="newSize">The new size of the array.</param>
215+
public void Resize(int newSize)
216+
{
217+
EnsureNotDisposed();
218+
ArgumentOutOfRangeException.ThrowIfNegative(newSize);
219+
220+
if (newSize == Capacity)
221+
{
222+
return;
223+
}
224+
225+
var newBuffer = UnmanagedMemoryHelper.Reallocate<T>(_buffer, newSize);
226+
var newPtr = (T*)newBuffer;
227+
228+
if (newSize > Capacity)
229+
{
230+
UnmanagedMemoryHelper.Clear<T>(new IntPtr(newPtr + Capacity), newSize - Capacity);
231+
}
232+
233+
_buffer = newBuffer;
234+
_ptr = newPtr;
235+
Capacity = newSize;
236+
237+
if (Length > newSize)
238+
{
239+
Length = newSize;
240+
}
241+
}
242+
243+
/// <inheritdoc />
244+
/// <summary>
245+
/// Clears the array by setting all elements to zero.
246+
/// </summary>
247+
public void Clear()
248+
{
249+
// We don't actually need to zero out the memory with Span.Clear.
250+
// Setting Length to 0 is instantly fast and means the next Add() will just overwrite the old garbage memory.
251+
Length = 0;
252+
}
253+
254+
/// <inheritdoc />
255+
/// <summary>
256+
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
257+
/// </summary>
258+
public void Dispose()
259+
{
260+
Dispose(true);
261+
GC.SuppressFinalize(this);
262+
}
263+
264+
/// <summary>
265+
/// Inserts at.
266+
/// </summary>
267+
/// <param name="index">The index.</param>
268+
/// <param name="value">The value.</param>
269+
/// <param name="count">The count.</param>
270+
/// <exception cref="System.ArgumentOutOfRangeException">
271+
/// index
272+
/// or
273+
/// count
274+
/// </exception>
275+
public void InsertAt(int index, T value, int count = 1)
276+
{
277+
if (index < 0 || index > Length) throw new ArgumentOutOfRangeException(nameof(index));
278+
279+
if (count <= 0) return;
280+
281+
EnsureCapacity(Length + count);
282+
283+
int moveCount = Length - index;
284+
if (moveCount > 0)
285+
{
286+
var source = new ReadOnlySpan<T>(_ptr + index, moveCount);
287+
var destination = new Span<T>(_ptr + index + count, moveCount);
288+
source.CopyTo(destination);
289+
}
290+
291+
// Optimization: Use Span.Fill instead of a for-loop
292+
new Span<T>(_ptr + index, count).Fill(value);
293+
294+
Length += count;
295+
}
296+
297+
/// <summary>
298+
/// Ensures the capacity.
299+
/// </summary>
300+
/// <param name="minCapacity">The minimum capacity.</param>
301+
public void EnsureCapacity(int minCapacity)
302+
{
303+
if (minCapacity <= Capacity)
304+
{
305+
return;
306+
}
307+
308+
var newCapacity = Capacity == 0 ? 4 : Capacity;
309+
while (newCapacity < minCapacity)
310+
{
311+
newCapacity *= 2;
312+
}
313+
314+
Resize(newCapacity);
315+
}
316+
317+
/// <summary>
318+
/// Access the span.
319+
/// </summary>
320+
/// <returns>Return all Values as Span</returns>
321+
public Span<T> AsSpan()
322+
{
323+
EnsureNotDisposed();
324+
return new Span<T>(_ptr, Length);
325+
}
326+
327+
/// <summary>
328+
/// Ensures the not disposed.
329+
/// </summary>
330+
/// <exception cref="System.ObjectDisposedException">T</exception>
331+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
332+
private void EnsureNotDisposed()
333+
{
334+
if (_disposed) throw new ObjectDisposedException(nameof(UnmanagedArray<T>));
335+
}
336+
337+
/// <summary>
338+
/// Finalizes an instance of the <see cref="UnmanagedArray{T}" /> class.
339+
/// </summary>
340+
~UnmanagedList()
341+
{
342+
Dispose(false);
343+
}
344+
345+
/// <summary>
346+
/// Releases unmanaged and - optionally - managed resources.
347+
/// </summary>
348+
/// <param name="disposing">
349+
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
350+
/// unmanaged resources.
351+
/// </param>
352+
private void Dispose(bool disposing)
353+
{
354+
if (_disposed) return;
355+
356+
if (_buffer != IntPtr.Zero)
357+
{
358+
Marshal.FreeHGlobal(_buffer);
359+
_buffer = IntPtr.Zero;
360+
_ptr = null; // EnsureNotDisposed checks often rely on this being null
361+
}
362+
363+
_disposed = true;
364+
}
365+
}
366+
}

0 commit comments

Comments
 (0)