I was reading the implementation of UnsafeList.RemoveRange
.
Here it is for reference:
public unsafe void RemoveRange(int index, int count)
{
CheckIndexCount(index, count);
index = CollectionHelper.AssumePositive(index);
count = CollectionHelper.AssumePositive(count);
if (count > 0)
{
int num = math.min(index + count, m_length);
int num2 = sizeof(T);
void* destination = (byte*)Ptr + index * num2;
void* source = (byte*)Ptr + num * num2;
UnsafeUtility.MemCpy(destination, source, (m_length - num) * num2);
m_length -= count;
}
}
I was surprised to see the use of MemCpy, because the documentation of MemCpy contains this paragraph:
When the
source
anddestination
arrays overlap, the result isn’t guaranteed to be correct and you should use UnsafeUtility.MemMove instead.
Source: Unity - Scripting API: Unity.Collections.LowLevel.Unsafe.UnsafeUtility.MemCpy(void*,void*,ulong)
Questions:
- Aren’t destination and source overlapping if
2 * count < m_length - index
? - Does this mean that
RemoveRange
can lead to subtle bugs and should be avoided or is the documentation of MemCpy incomplete and the result is guaranteed in this case? For example it could be that MemCpy guarantees the result if the source pointer is greater than the destination pointer. - Why does
RemoveAt
use a for loop instead of MemCpy/MemMove? Isn’t MemCpy/MemMove supposed to be faster?
For reference here is the implementation ofRemoveAt
:public unsafe void RemoveAt(int index) { CollectionHelper.CheckIndexInRange(index, m_length); index = CollectionHelper.AssumePositive(index); T* ptr = Ptr + index; T* ptr2 = ptr + 1; m_length--; for (int i = index; i < m_length; i++) { *(ptr++) = *(ptr2++); } }
- Is my understanding correct that capping
num
tom_length
is pretty useless because a) when collection checks are enabled this is already asserted byCheckIndexCount
and b) when collection checks are disabled you can go out of bounds anyways by supplying a negative index?