NativeArray created with NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray throws exceptions

This simple test code:

[Test] public static unsafe void does_ConvertExistingDataToNativeArray_work()
{
    var array = new int[]{ 1 , 2 , 3 , 4 };
    void* ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out ulong gcHandle);
    var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(ptr, array.Length, Allocator.None);
    for (int i = 0; i < array.Length; i++)
        Assert.AreEqual(array[i], actual:nativeArray[i]);
    UnsafeUtility.ReleaseGCObject(gcHandle);
}

Results in a NullReferenceException message:

But why?

It’s safety system complains. You need to properly initialize safety handle (how to get second argument depends on data source you converts to native array from):

var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(ptr, array.Length, Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.GetTempMemoryHandle());
#endif

Otherwise here you’ll have null m_Safety

8409792--1111218--upload_2022-9-2_11-32-6.png

1 Like

Thank you @eizenhorn for this very helpful answer! That was exactly what I was missing here

This unit test still fails, but for a baffling reason:

[Test] public static unsafe void does_ConvertExistingDataToNativeArray_work()
{
    var array = new int[]{ 1 , 2 , 3 , 4 };
 
    void* ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out ulong gcHandle);
    var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(ptr, array.Length, Allocator.None);
 
    #if ENABLE_UNITY_COLLECTIONS_CHECKS
    NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.GetTempMemoryHandle());
    #endif

    //Debug.Log($"array:\t\t{array.ToReadableString()}");
    //Debug.Log($"nativeArray:\t{nativeArray.ToReadableString()}");
 
    Assert.AreEqual(expected:array.Length, actual:nativeArray.Length);
    for (int i = 0; i < array.Length; i++)
        Assert.AreEqual(expected:array[i], actual:nativeArray[i]);
 
    UnsafeUtility.ReleaseGCObject(gcHandle);
}

Message:

and the logs tell me that this is what those 2 arrays looks like:

Why on earth does the first dword matches but the others does not?

That’s completely expected. You “reinterpret” int array to byte array. Types size not match as result these arrays memory layout cannot be aliased. As result in your case you see “invalid” values for your reinterpreted array. If you look at NativeArrayExtensions.Reinterpret you’ll see they have size check, which prevents you from doing that. If you change ConvertExistingDataToNativeArray<byte> to ConvertExistingDataToNativeArray<int> everything will match.

1 Like

Of course! I’m such a fool today, didn’t catch that. Thank you again! :slight_smile: