Files
2025-01-25 04:38:09 +08:00

215 lines
7.4 KiB
C#

using System;
namespace Nordeus.DataStructures
{
#if !UNITY_WEBPLAYER
using System.Runtime.InteropServices;
using UnityEngine;
/// <summary>
/// This struct is used to access the hidden .Net fields of the Array type.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct ArrayHeader
{
/// <summary>
/// Array type.
/// </summary>
public UIntPtr type;
/// <summary>
/// Array length.
/// </summary>
public UIntPtr length;
}
#endif
/// <summary>
/// Highly specialized, unsafe-code-calling class which can alter the perceived length of the underlying buffer in runtime without
/// copying the array. See the <c>AsArrayOfLength</c> method docs for more details.
/// </summary>
/// <remarks>
/// Since this class is abstract, you need to use a subclass which implements the functionality of your desired buffer type.
/// Unfortunately, due to a limitation in C#'s unsafe code implementation, you cannot use arbitrary classes as type arguments, since
/// the buffer elements have to be of a blittable type, and there's no way to enforce blittability through type parameter constraints.
/// (And no, "where T: struct" doesn't cut it.)
/// </remarks>
/// <remarks>
/// Note: This class is NOT thread-safe!
/// example usage:
/// verts.AsArrayOfLength(verts.size, (buffer) =>
/// {
/// mesh.vertices = buffer;
/// });
/// </remarks>
/// <remarks>
/// </remarks>
public abstract class VaryingList<T> : BufferedList<T>
{
/// <summary>
/// The action delegate which will be invoked with the list's buffer (with its size modified) as a parameter.
/// </summary>
/// <param name="array">The underlying buffer with its size changed for the duration of this delegate's invokation.</param>
public delegate void ArrayAction(T[] array);
#if !UNITY_WEBPLAYER
/// <summary>
/// Changes the perceived length of the underlying integer buffer for the duration of the <paramref name="action"/> invokation. The
/// perceived buffer size is changed to <paramref name="length"/> and then the <paramref name="action"/> callback is invoked. During
/// that time, the code in the <paramref name="action"/> callback sees the buffer as having the given <paramref name="length"/>.
/// When the callback returns, the buffer's perceived size is returned to its normal value.
/// </summary>
/// <remarks>Note: This method is NOT thread safe! Do not expect delayed actions to perceive the buffer as having the given length,
/// since the length will be returned to normal as soon as the action delegate finishes.</remarks>
/// <param name="length">The length with which the underlying list buffer will be perceived. If it is less than zero, the action is
/// not performed and this method exits immediately. If it is larger than the actual buffer size, this method acts as if it's the
/// same as the buffer size.</param>
/// <param name="action">The action delegate during whose invokation the buffer will have a modified length. If it is null, this
/// method exits immediately.</param>
public unsafe void AsArrayOfLength(int length, ArrayAction action)
{
//if (action == null || length <= 0) return;
if (length <= 0) return;
if (length > size) length = size;
void* pBuffer = GetBufferPointer();
// Get the header
ArrayHeader* header = (ArrayHeader*)pBuffer - 1;
// Change the length
UIntPtr originalLength = header->length;
header->length = new UIntPtr((ulong)length);
// Do stuff with the changed array
if (action != null)
{
action(buffer);
}
// Revert back to old length
header->length = originalLength;
}
/// <summary>
/// Gets the buffer pointer. This is the only piece of information a subclass needs to provide to this class. The buffer has to
/// contain only blittable types, and the compiler prevents unsafe code from operating on generic type arguments, so this method is
/// a workaround for that.
/// </summary>
/// <returns>The buffer pointer.</returns>
public abstract unsafe void* GetBufferPointer();
#endif
/// <summary>
/// Set return array with trimed-out free space. For non-web build we use unsafe code to change buffer duration
/// in array header.
/// </summary>
public void TrimBuffer(ArrayAction action)
{
#if !UNITY_WEBPLAYER
AsArrayOfLength(size, action);
#else
T[] bufferCopy = new T[size];
Array.Copy(buffer, bufferCopy, size);
action(bufferCopy);
#endif
}
}
/// <summary>
/// The implementation of <see cref="VaryingList<T>"/> for the integer element type.
/// </summary>
public class VaryingIntList : VaryingList<int>
{
#if !UNITY_WEBPLAYER
public override unsafe void* GetBufferPointer()
{
fixed (void* pBuffer = buffer)
{
return pBuffer;
}
}
#endif
}
/// <summary>
/// The implementation of <see cref="VaryingList<T>"/> for the <see cref="Vector2"/> element type.
/// </summary>
public class VaryingVector2List : VaryingList<UnityEngine.Vector2>
{
#if !UNITY_WEBPLAYER
public override unsafe void* GetBufferPointer()
{
fixed (void* pBuffer = buffer)
{
return pBuffer;
}
}
#endif
}
/// <summary>
/// The implementation of <see cref="VaryingList<T>"/> for the <see cref="Vector3"/> element type.
/// </summary>
public class VaryingVector3List : VaryingList<UnityEngine.Vector3>
{
#if !UNITY_WEBPLAYER
public override unsafe void* GetBufferPointer()
{
fixed (void* pBuffer = buffer)
{
return pBuffer;
}
}
#endif
}
/// <summary>
/// The implementation of <see cref="VaryingList<T>"/> for the <see cref="Vector4"/> element type.
/// </summary>
public class VaryingVector4List : VaryingList<UnityEngine.Vector4>
{
#if !UNITY_WEBPLAYER
public override unsafe void* GetBufferPointer()
{
fixed (void* pBuffer = buffer)
{
return pBuffer;
}
}
#endif
}
/// <summary>
/// The implementation of <see cref="VaryingList<T>"/> for the <see cref="Color32"/> element type.
/// </summary>
public class VaryingColor32List : VaryingList<UnityEngine.Color32>
{
#if !UNITY_WEBPLAYER
public override unsafe void* GetBufferPointer()
{
fixed (void* pBuffer = buffer)
{
return pBuffer;
}
}
#endif
}
/// <summary>
/// The implementation of <see cref="VaryingList<T>"/> for the <see cref="Color"/> element type.
/// </summary>
public class VaryingColorList : VaryingList<UnityEngine.Color>
{
#if !UNITY_WEBPLAYER
public override unsafe void* GetBufferPointer()
{
fixed (void* pBuffer = buffer)
{
return pBuffer;
}
}
#endif
}
}