(Android)ArgumentException on Buffer.BlockCopy.

Version: Unity2022.1.12

I am using Buffer.BlockCopy to copy an sbyte array to a byte array, but when I check it with an apk created with IL2CPP, I get an ArgumentException is thrown.

The results were the same whether the sbyte type was changed to a byte type before execution or a short type before execution.

It works fine on the editor and works fine on Unity 2022.1.11.

In the release notes of Unity2022.1.12, there is a description about Buffer.BlockCopy in IL2CPP, and I believe that the bug is caused by that fix.

I am not good at English, so I am sorry if I am not explaining myself well.

1 Like

According to docs ArgumentException is thrown when the following occurs
src or dst is not an array of primitives.
-or-
The number of bytes in src is less than srcOffset plus count.
-or-
The number of bytes in dst is less than dstOffset plus count.

With this in mind, reverse check your assumptions by safeguarding the operation with try/catch while outputting the exact values to screen or file. Try to find out what the underlying reason might be and narrow down the possible causes.

edit:
In my opinion it’s one of the three things (from the most likely to least likely):

  1. Logical error: your environment is somehow different on Android (if it’s not an error in static architecture, maybe it’s some sort of race condition or memory/allocation issue).
  2. Incompatibility error: IL2CPP is compiled differently for some reason, maybe the libraries are different, or something else entirely triggers an unknown behavior.
  3. Specification error: somehow the method behaves more strictly on Android.
2 Likes

Thanks for the answer.

The number of bytes in src is less than srcOffset plus count.
The number of bytes in dst is less than dstOffset plus count.
This has been confirmed and both were fine.

The length of the src array is more than 50,000, and srcOffset is 0.
The dst array has a Length of 40, dstOffset is 0, and count is 40.

The size is correct. The value being passed is a primitive type. However, an ArgumentException is thrown, so I suspect this is a bug due to a fix implemented in Unity 2022.1.12.

For your information.

//src is an array of type sbyte
for (int i=0; i < count; ++i)
{
dst[i + dstOffset] = (byte)src[i + srcOffset];
}

If we replace the program in this way, it will work correctly, so we believe that there is no problem with the data itself.

1 Like

It would be nice if you could nail the exact nature of the problem. Other people will stumble on this topic if the problem remains persistent.

Btw, what was the code that didn’t work properly?
Was it just

BlockCopy(src, srcOffset, dst, dstOffset, count);

?

Can you test with try/catch whether that line is the one that’s crashing? Because BlockCopy seems to be blind to platform endianness. What is the platform you’re developing with?

Here’s the bug that was described as fixed. It doesn’t tell much. It is reproducible on macOS, however it’s little-endian just as Android. For a minute I thought maybe it’s because of macOS but nah they changed the architecture.

edit:
oh well, it’s probably an IL2CPP specific bug, Unity messed it up somehow.

The development environment is Windows 10, Platform in Build Settings is Android, Target API Level is 33. apk is created with IL2CPP.

The only line of code that did not work correctly is this one.

Buffer.BlockCopy ( src , 0 , dst , 0 , count ) ;.

The size of each data is as in the previous comment.
If this code is changed to cast in a for loop as in the previous comment, it works fine.

When I test the Buffer.BlockCopy process with try/catch, I catch the following error.
ArgumentException: Object must be an array of primitives.

The error log is output to Logcat as follows.

E/Unity: ArgumentException: Object must be an array of primitives.
ByteLength (System.Array array) [0x00000] in <00000000000000000000000000000000000000000000000000000000000000000000000000>:0
at System.Buffer.BlockCopy (System.Array src, System.Int32 srcOffset, System.Array dst, System.Int32 dstOffset, System.Int32 count) [0x00000] in <000000000000000000000000000000000000000000000000000000000000000000000000000000>:0

All of these were tested by installing the apk I created on an Android device.

Sorry if I did not answer well.

Have you tried to do something similar with two byte arrays (instead of sbyte → byte) for the sake of testing? What would happen, does it work at all? Depending on what you’re trying to achieve, if you’re truly blitting regions in memory, BlockCopy is vastly more performant than any other routine you can otherwise come up with. I wouldn’t let this go if I were you.

Do you really need sbytes at all? For some reason they end up being considered as non-primitive (which isn’t true, but something’s weird).

I could perhaps recreate some examples of fast memory ‘monkey-business’ but there is no way I could test any of it on Android so I can’t really help you much.

edit:
Maybe you can try declaring your arrays with proper type names, like System.Byte and System.SByte. Who knows what weird wrench-like optimization Unity introduced to IL2CPP.

I too suspected sbyte and did the test.

In the test, I cast the sbyte array to a byte array and tried to copy the data from the byte array to the byte array using BlockCopy, but it threw an ArgumentException.

I also tested with an array of sbytes cast to short, but the result was the same.

I think the process to determine if it is a primitive type is probably broken.

Unity seemed to be aware of the issue here, so please check the following issuetracker for a solution to the problem.

https:// Unity Issue Tracker - [Android] SByte type is considered to be not primitive when compiling IL2CPP code