using System;
using System.Collections.Generic;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.Rendering
{
///
/// Static class with unsafe utility functions.
///
public static unsafe class CoreUnsafeUtils
{
///
/// Fixed Buffer String Queue class.
///
public struct FixedBufferStringQueue
{
byte* m_ReadCursor;
byte* m_WriteCursor;
readonly byte* m_BufferEnd;
readonly byte* m_BufferStart;
readonly int m_BufferLength;
///
/// Number of element in the queue.
///
public int Count { get; private set; }
///
/// Constructor.
///
/// Buffer pointer.
/// Length of the provided allocated buffer in byte.
public FixedBufferStringQueue(byte* ptr, int length)
{
m_BufferStart = ptr;
m_BufferLength = length;
m_BufferEnd = m_BufferStart + m_BufferLength;
m_ReadCursor = m_BufferStart;
m_WriteCursor = m_BufferStart;
Count = 0;
Clear();
}
///
/// Try to push a new element in the queue.
///
/// Element to push in the queue.
/// True if the new element could be pushed in the queue. False if reserved memory was not enough.
public bool TryPush(string v)
{
var size = v.Length * sizeof(char) + sizeof(int);
if (m_WriteCursor + size >= m_BufferEnd)
return false;
*(int*)m_WriteCursor = v.Length;
m_WriteCursor += sizeof(int);
var charPtr = (char*)m_WriteCursor;
for (int i = 0; i < v.Length; ++i, ++charPtr)
*charPtr = v[i];
m_WriteCursor += sizeof(char) * v.Length;
++Count;
return true;
}
///
/// Pop an element of the queue.
///
/// Output result string.
/// True if an element was succesfuly poped.
public bool TryPop(out string v)
{
var size = *(int*)m_ReadCursor;
if (size != 0)
{
m_ReadCursor += sizeof(int);
v = new string((char*)m_ReadCursor, 0, size);
m_ReadCursor += size * sizeof(char);
return true;
}
v = default;
return false;
}
///
/// Clear the queue.
///
public void Clear()
{
m_WriteCursor = m_BufferStart;
m_ReadCursor = m_BufferStart;
Count = 0;
UnsafeUtility.MemClear(m_BufferStart, m_BufferLength);
}
}
///
/// Key Getter interface.
///
/// Value
/// Key
public interface IKeyGetter
{
/// Getter
/// The value
/// The key
TKey Get(ref TValue v);
}
internal struct DefaultKeyGetter : IKeyGetter
{ public T Get(ref T v) { return v; } }
// Note: this is a workaround needed to circumvent some AOT issues when building for xbox
internal struct UintKeyGetter : IKeyGetter
{ public uint Get(ref uint v) { return v; } }
///
/// Extension method to copy elements of a list into a buffer.
///
/// Type of the provided List.
/// Input List.
/// Destination buffer.
/// Number of elements to copy.
public static void CopyTo(this List list, void* dest, int count)
where T : struct
{
var c = Mathf.Min(count, list.Count);
for (int i = 0; i < c; ++i)
UnsafeUtility.WriteArrayElement(dest, i, list[i]);
}
///
/// Extension method to copy elements of an array into a buffer.
///
/// Type of the provided array.
/// Input List.
/// Destination buffer.
/// Number of elements to copy.
public static void CopyTo(this T[] list, void* dest, int count)
where T : struct
{
var c = Mathf.Min(count, list.Length);
for (int i = 0; i < c; ++i)
UnsafeUtility.WriteArrayElement(dest, i, list[i]);
}
///
/// Quick Sort
///
/// uint array.
/// Left boundary.
/// Left boundary.
public static unsafe void QuickSort(uint[] arr, int left, int right)
{
fixed (uint* ptr = arr)
CoreUnsafeUtils.QuickSort(ptr, left, right);
}
///
/// Quick sort.
///
/// Type to compare.
/// Number of element.
/// Buffer to sort.
public static void QuickSort(int count, void* data)
where T : struct, IComparable
{
QuickSort>(data, 0, count - 1);
}
///
/// Quick sort.
///
/// Value type.
/// Key Type.
/// Getter type.
/// Number of element.
/// Data to sort.
public static void QuickSort(int count, void* data)
where TKey : struct, IComparable
where TValue : struct
where TGetter : struct, IKeyGetter
{
QuickSort(data, 0, count - 1);
}
///
/// Quick sort.
///
/// Value type.
/// Key Type.
/// Getter type.
/// Data to sort.
/// Left boundary.
/// Right boundary.
public static void QuickSort(void* data, int left, int right)
where TKey : struct, IComparable
where TValue : struct
where TGetter : struct, IKeyGetter
{
// For Recursion
if (left < right)
{
int pivot = Partition(data, left, right);
if (pivot >= 1)
QuickSort(data, left, pivot);
if (pivot + 1 < right)
QuickSort(data, pivot + 1, right);
}
}
///
/// Index of an element in a buffer.
///
/// Data type.
/// Data buffer.
/// Number of elements.
/// Element to test against.
/// The first index of the provided element.
public static int IndexOf(void* data, int count, T v)
where T : struct, IEquatable
{
for (int i = 0; i < count; ++i)
{
if (UnsafeUtility.ReadArrayElement(data, i).Equals(v))
return i;
}
return -1;
}
///
/// Compare hashes of two collections and provide
/// a list of indices to remove in
/// and a list of indices to add in .
///
/// Assumes that and are sorted.
///
/// Old value type.
/// Old getter type.
/// New value type.
/// New getter type.
/// Number of hashes in .
/// Previous hashes to compare.
/// Number of hashes in .
/// New hashes to compare.
/// Indices of element to add in will be written here.
/// Indices of element to remove in will be written here.
/// Number of elements to add will be written here.
/// Number of elements to remove will be written here.
/// The number of operation to perform ( +
)
public static int CompareHashes(
int oldHashCount, void* oldHashes,
int newHashCount, void* newHashes,
// assume that the capacity of indices is >= max(oldHashCount, newHashCount)
int* addIndices, int* removeIndices,
out int addCount, out int remCount
)
where TOldValue : struct
where TNewValue : struct
where TOldGetter : struct, IKeyGetter
where TNewGetter : struct, IKeyGetter
{
var oldGetter = new TOldGetter();
var newGetter = new TNewGetter();
addCount = 0;
remCount = 0;
// Check combined hashes
if (oldHashCount == newHashCount)
{
var oldHash = new Hash128();
var newHash = new Hash128();
CombineHashes(oldHashCount, oldHashes, &oldHash);
CombineHashes(newHashCount, newHashes, &newHash);
if (oldHash == newHash)
return 0;
}
var numOperations = 0;
var oldI = 0;
var newI = 0;
while (oldI < oldHashCount || newI < newHashCount)
{
// At the end of old array.
if (oldI == oldHashCount)
{
// No more hashes in old array. Add remaining entries from new array.
for (; newI < newHashCount; ++newI)
{
addIndices[addCount++] = newI;
++numOperations;
}
continue;
}
// At end of new array.
if (newI == newHashCount)
{
// No more hashes in old array. Remove remaining entries from old array.
for (; oldI < oldHashCount; ++oldI)
{
removeIndices[remCount++] = oldI;
++numOperations;
}
continue;
}
// Both arrays have data.
var newVal = UnsafeUtility.ReadArrayElement(newHashes, newI);
var oldVal = UnsafeUtility.ReadArrayElement(oldHashes, oldI);
var newKey = newGetter.Get(ref newVal);
var oldKey = oldGetter.Get(ref oldVal);
if (newKey == oldKey)
{
// Matching hash, skip.
++newI;
++oldI;
continue;
}
// Both arrays have data, but hashes do not match.
if (newKey < oldKey)
{
// oldIter is the greater hash. Push "add" jobs from the new array until reaching the oldIter hash.
while (newI < newHashCount && newKey < oldKey)
{
addIndices[addCount++] = newI;
++newI;
++numOperations;
newVal = UnsafeUtility.ReadArrayElement(newHashes, newI);
newKey = newGetter.Get(ref newVal);
}
}
else
{
// newIter is the greater hash. Push "remove" jobs from the old array until reaching the newIter hash.
while (oldI < oldHashCount && oldKey < newKey)
{
removeIndices[remCount++] = oldI;
++numOperations;
++oldI;
}
}
}
return numOperations;
}
///
/// Compare hashes.
///
/// Number of hashes in .
/// Previous hashes to compare.
/// Number of hashes in .
/// New hashes to compare.
/// Indices of element to add in will be written here.
/// Indices of element to remove in will be written here.
/// Number of elements to add will be written here.
/// Number of elements to remove will be written here.
/// The number of operation to perform ( +
)
public static int CompareHashes(
int oldHashCount, Hash128* oldHashes,
int newHashCount, Hash128* newHashes,
// assume that the capacity of indices is >= max(oldHashCount, newHashCount)
int* addIndices, int* removeIndices,
out int addCount, out int remCount
)
{
return CompareHashes, Hash128, DefaultKeyGetter>(
oldHashCount, oldHashes,
newHashCount, newHashes,
addIndices, removeIndices,
out addCount, out remCount
);
}
/// Combine all of the hashes of a collection of hashes.
/// Value type.
/// Getter type.
/// Number of hash to combine.
/// Hashes to combine.
/// Hash to update.
public static void CombineHashes(int count, void* hashes, Hash128* outHash)
where TValue : struct
where TGetter : struct, IKeyGetter
{
var getter = new TGetter();
for (int i = 0; i < count; ++i)
{
var v = UnsafeUtility.ReadArrayElement(hashes, i);
var h = getter.Get(ref v);
HashUtilities.AppendHash(ref h, ref *outHash);
}
}
///
/// Combine hashes.
///
/// Number of hash to combine.
/// Hashes to combine.
/// Hash to update.
public static void CombineHashes(int count, Hash128* hashes, Hash128* outHash)
{
CombineHashes>(count, hashes, outHash);
}
// Just a sort function that doesn't allocate memory
// Note: Should be replace by a radix sort for positive integer
static int Partition(void* data, int left, int right)
where TKey : struct, IComparable
where TValue : struct
where TGetter : struct, IKeyGetter
{
var getter = default(TGetter);
var pivotvalue = UnsafeUtility.ReadArrayElement(data, left);
var pivot = getter.Get(ref pivotvalue);
--left;
++right;
while (true)
{
var c = 0;
var lvalue = default(TValue);
var lkey = default(TKey);
do
{
++left;
lvalue = UnsafeUtility.ReadArrayElement(data, left);
lkey = getter.Get(ref lvalue);
c = lkey.CompareTo(pivot);
}
while (c < 0);
var rvalue = default(TValue);
var rkey = default(TKey);
do
{
--right;
rvalue = UnsafeUtility.ReadArrayElement(data, right);
rkey = getter.Get(ref rvalue);
c = rkey.CompareTo(pivot);
}
while (c > 0);
if (left < right)
{
UnsafeUtility.WriteArrayElement(data, right, lvalue);
UnsafeUtility.WriteArrayElement(data, left, rvalue);
}
else
{
return right;
}
}
}
///
/// Checks for duplicates in an array.
///
/// Input array.
/// True if there is any duplicate in the input array.
public static unsafe bool HaveDuplicates(int[] arr)
{
int* copy = stackalloc int[arr.Length];
arr.CopyTo(copy, arr.Length);
QuickSort(arr.Length, copy);
for (int i = arr.Length - 1; i > 0; --i)
{
if (UnsafeUtility.ReadArrayElement(copy, i).CompareTo(UnsafeUtility.ReadArrayElement(copy, i - 1)) == 0)
{
return true;
}
}
return false;
}
}
}