#Span<bool>.Fill writes outside of the span when compiled with Burst

1 messages · Page 1 of 1 (latest)

willow temple
#

Using Burst, the Fill method of Span<bool> somehow thinks a bool has a 4 byte size 😮
If doing something such as nativeBoolArray.AsSpan().Fill(false) which seems very friendly, it will result in writing outside of the array leading to nasty bugs.

Example:

Debug.Log($"sizeof(bool)={sizeof(bool)}, UnsafeUtility.SizeOf<bool>()={UnsafeUtility.SizeOf<bool>()}");

const int size   = 16;
var       memory = CollectionHelper.CreateNativeArray<byte>(size, Allocator.Temp);

for (byte i = 0; i < size; i++) {
    memory[i] = i;
}

var boolSpan = new Span<bool>((bool*)memory.GetUnsafePtr() + 3, 2);
Debug.Log($"boolSpan.Length={boolSpan.Length}");
boolSpan.Fill(false);

for (int i = 0; i < size; i++) {
    Debug.Log($"{i}\t{memory[i]:X}");
}

Output:

Output: 
sizeof(bool)=1, UnsafeUtility.SizeOf<bool>()=1
boolSpan.Length=2
0    0
1    1
2    2
3    0
4    0
5    0
6    0
7    0
8    0
9    0
10    0
11    B
12    C
13    D
14    E
15    F
willow temple
#

somehow it however works with a generic type T being bool 🤔

[BurstCompile(CompileSynchronously = true)]
private static void TestSpanBurst() {
    TestUtility.AssertBurst();
    
    Debug.Log($"bool non-generic: {SpanSizeOfBool()}");
    Debug.Log($"byte non-generic: {SpanSizeOfByte()}");
    Debug.Log($"bool: {SpanSizeOf<bool>()}");
    Debug.Log($"byte: {SpanSizeOf<byte>()}");
    Debug.Log($"short: {SpanSizeOf<short>()}");
    Debug.Log($"int: {SpanSizeOf<int>()}");
    Debug.Log($"long: {SpanSizeOf<long>()}");
}

private static unsafe int SpanSizeOf<T>() where T : unmanaged {
    const int size   = 32;
    var       memory = CollectionHelper.CreateNativeArray<byte>(size, Allocator.Temp);

    for (byte i = 0; i < size; i++) {
        memory[i] = i;
    }

    var span = new Span<T>((T*)memory.GetUnsafePtr(), 1);
    span.Fill(default(T));

    int sizeOf = 0;
    while (sizeOf < size && memory[sizeOf] == 0)
        sizeOf++;
    
    return sizeOf;
}

private static unsafe int SpanSizeOfBool() {
    const int size   = 32;
    var       memory = CollectionHelper.CreateNativeArray<byte>(size, Allocator.Temp);

    for (byte i = 0; i < size; i++) {
        memory[i] = i;
    }

    var span = new Span<bool>((bool*)memory.GetUnsafePtr(), 1);
    span.Fill(default(bool));

    int sizeOf = 0;
    while (sizeOf < size && memory[sizeOf] == 0)
        sizeOf++;
    
    return sizeOf;
}

private static unsafe int SpanSizeOfByte() {
    const int size   = 32;
    var       memory = CollectionHelper.CreateNativeArray<byte>(size, Allocator.Temp);

    for (byte i = 0; i < size; i++) {
        memory[i] = i;
    }

    var span = new Span<byte>((byte*)memory.GetUnsafePtr(), 1);
    span.Fill(default(byte));

    int sizeOf = 0;
    while (sizeOf < size && memory[sizeOf] == 0)
        sizeOf++;
    
    return sizeOf;
}

Output:

bool non-generic: 4
byte non-generic: 4
bool: 1
byte: 1
short: 2
int: 4
long: 8

Definitely a Burst bug I'd say

midnight nacelle
#

That does indeed look like a Burst bug. It's weird that the behavior differs between the generic / non-generic versions.

Anyway, I've created an internal ticket for it and we'll look to get it fixed. Thanks for the heads up 🙂