Search Unity

Resolved Convert int array to byte array all at once

Discussion in 'Scripting' started by seejayjames, Mar 17, 2021.

  1. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Am sending texture data over UDP for an LED project. Am wondering if there's a way to convert the whole integer list (texture RGB information) to the byte array that's needed by the UDP method. Currently I loop through it and cast each int to a byte, which works, but if there's a faster way that would be great to know.

    Code (CSharp):
    1. void sendNumberList(int[] message)
    2.     {
    3.         byte[] byteMessage = new byte[message.Length];
    4.  
    5.         // Is there a way to convert the whole array at once instead of looping through it?
    6.         for (int i = 0; i < message.Length; i++)
    7.         {
    8.             byteMessage[i] = ((byte)message[i]);
    9.         }
    10.  
    11.         try
    12.         {
    13.             client.Send(byteMessage, byteMessage.Length, remoteEndPoint);
    14.             print("message " + message + " sent to " + remoteEndPoint);
    15.         }
    16.         catch (Exception err)
    17.         {
    18.             print(err.ToString());
    19.         }
    20.     }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,666
    There might be something in the Interop marshal classes, I dunno.

    Are you having a problem or is this just speculative optimization?
     
    seejayjames likes this.
  3. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    No problem as of yet, and probably won't have any if the lists aren't super-long. But I did see there's a built-in method for doing this for string messages, so was wondering if there's an equivalent one. That said, not sure how much faster any of these actually are under the hood, especially if they iterate through the lists anyway.

    byte[] data = Encoding.UTF8.GetBytes(message);
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
  5. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,401
    seejayjames likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,666
  7. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
  8. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,666
    I'm thinking pixels I know are made of Color32, which is RGBA, 8 bits for each channel, so one System.Int32.
     
  10. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Casting int to byte will limit the value of each int to the max value of a byte, which I believe is a range from -127 to +127, or similar (edit: sorry it is 0-255 :p ). I'm not sure what happens to values outside of that range, but they won't be correct. What you should do is serialize/deserialize, so you preserve all values of the int array.

    Quick example off the top of my head below. Writing it directly into the forums, so might be a typo or two. But this way preserves the entire range of values of the integers.

    Code (csharp):
    1. using system;
    2.  
    3. //convert an integer array into a byte array, with the integer array length as a 4 byte header
    4. public byte[] IntArrayToByteArray(int[] intArray)
    5. {
    6.     int totalBytes = (intArray.Length * 4) + 4;  //Integers are 4 bytes long, plus we're going to add another integer at the beginning with the total array length
    7.     byte[] serialized = new byte[totalBytes];  //Byte array we are going to return
    8.  
    9.     List<byte[]> listOfBytes = new List<byte[]>();  //A temporary list of byte arrays of converted integers
    10.     foreach (int i in intArray)
    11.     {
    12.         byte[] converted = BitConverter.GetBytes(i);  //convert an integer into a 4 length byte array
    13.         listOfBytes.Add(converted);
    14.     }
    15.  
    16.     //Now lets build the final byte array
    17.     int location = 0;  //track our current location within the byte array we are going to return with this
    18.  
    19.     Array.Copy(BitConverter.GetBytes(intArray.Length), 0, serialized, location, 4);  //include the length of the integer array as a header in front of the actual data
    20.     location += 4;
    21.     foreach (byte[] ba in listOfBytes)  //now add the contents of the list to the byte array
    22.     {
    23.         Array.Copy(ba, 0, serialized, location, 4);
    24.         location +=4;
    25.     }
    26.  
    27.     return serialized;
    28. }
    29.  
    30. //deserialize byte array back to an integer array
    31. //the provided byte array is a 4 byte header of an integer which represents the integer array length, followed by the serialized contents of the entire integer array
    32. public int[] byteArrayToIntArray(byte[] serialized)
    33. {
    34.     int location = 0;
    35.     //first we get the int array length that we serialized to the start of the byte array
    36.     int length = BitConverter.ToInt32(serialized, location);
    37.     location += 4;
    38.  
    39.     int[] intArray = new int[length];
    40.     int index = 0;
    41.     while (index < length)  //Now lets reconstruct the original integer array
    42.     {
    43.         intArray[index] = BitConverter.ToInt32(serialized, location);
    44.         location += 4;
    45.         index++;
    46.     }
    47.  
    48.     return intArray;
    49. }
     
    Last edited: Mar 17, 2021
    seejayjames likes this.
  11. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Thanks everyone! I've got a version that seems to work fine. I don't think any bottlenecks will be at this end anyways, it's more about UDP packet sizes and rate of transfer. Will test it on the LED hardware soon and see.
     
  12. TheZombieKiller

    TheZombieKiller

    Joined:
    Feb 8, 2013
    Posts:
    265
    Since UdpClient.Send can only accept byte arrays (and has no pointer overloads), there's unfortunately no way to safely avoid copying the data. With that in mind, the most efficient way to convert from int[] to byte[] should be the following:
    Code (CSharp):
    1. var result = new byte[input.Length * sizeof(int)];
    2. Buffer.BlockCopy(input, 0, result, 0, result.Length);
    I'd suggest allocating a single byte[] array with your largest expected size, and reusing it for future Send calls to keep your allocations down (since Send can accept a data size).

    Also keep in mind that none of these solutions (mine included) account for endianness, which requires additional code to handle properly.
     
    Last edited: Mar 18, 2021
    Suddoha and Joe-Censored like this.
  13. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Hmm, didn't work, it's inserting 3 0's after each value in the list. Without the *sizeof(int) it does the same, but only 8 values total are printed.
    { 0, 10, 50, 100, 150, 200, 250, 255 } // just for testing
    List out is
    { 0, 0, 0, 0, 10, 0 0 0 }

    Also, curious, any reason you used
    var result
    instead of
    byte[] result?

    I've tried both, seems to be no difference.
     
  14. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Well, if your test array is still an int array, that'd be correct (little endianness in your case).
    int32 split into bytes for your test array:
    0 => 0, 0, 0, 0
    10 => 10, 0, 0, 0
    50 => 50, 0, 0, 0

    and so on

    If your test array contained values greater than 255, it'd continue like this:
    256 => 0, 1, 0, 0
    257=> 1, 1, 0, 0
    ...
    300 => 44, 1, 0, 0

    The last argument tells the method how many bytes to copy. Since an int in C# (System.Int32) consists of 4 bytes, you want to copy the number of integers * size of an integer. Your test array contains 8 values, hence you want to copy 8 * 4 bytes = 32 bytes.
    If you remove the sizeof(int), it won't magically copy only one byte of each of those integers, instead it'll start at the supplied start index and copy 8 contiguous bytes, which is the length of 2 ints. So yes, the result looks the same (at least the 8 bytes it copied), but now you've only copied a quarter of the bytes contained in the int array.

    There's no difference. As long as the compiler can infer the type from the context (e.g. when you do an assignment), you'll be able to use it. But you don't have to. Note that this is usually turning into an endless debate about pros and cons of var versus using explicit types - better don't start it. :p
     
    TheZombieKiller and seejayjames like this.
  15. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Ok cool, that makes sense, thanks for the clarification. There are a few holes in my understanding of the basic data types and endianness. In my case I only need 8-bit ints, so now I'm just using a byte array to start with. Should be all good.