2

I have an array of Int32 represented by Byte array (every 4 bytes are 1 Int32) and i want to convert them to Int32 array (with length of Byte.length/4). here is example of what i want:

//byte[] buffer;
for (int i=0; i<buffer.Length; i+=4)
{
    Int32 temp0 = BitConverter.ToInt32(buffer, i);
    temp0 += 10;
    byte[] temp1 = BitConverter.GetBytes(temp0);
    for (int j=0;j<4;j++)
    {
        buffer[i + j] = temp1[j];
    }
}

But I don't want to copy them, I just want to be able to tell the compiler that it is Int32 array and not a byte array (in order to later manipulation).

I looked at this How to Convert a byte array into an int array but it convert every byte into Int32 and I want to convert every 4 bytes into Int32. I also would like to this without copy it into another array for performance.

(we can assume that hardware is the endianness native, little endian system for little endian representation).

Community
  • 1
  • 1
pio
  • 500
  • 5
  • 12
  • 2
    sounds like a job for unsafe and pointers – pm100 Dec 19 '16 at 16:53
  • [Here](https://referencesource.microsoft.com/#mscorlib/system/bitconverter.cs,1618fc20415532f2) is the source code for [`BitConverter.ToInt32(byte[], Int32)`](https://msdn.microsoft.com/en-us/library/system.bitconverter.toint32(v=vs.110).aspx); I *think* this code: `fixed( byte * pbyte = &value[startIndex]) {if( startIndex % 4 == 0) { // data is aligned return *((int *) pbyte); }` means that it simply casts 4 bytes to int32 when aligned, it does *not* convert and thus should be fast. Can someone verify? – Quantic Dec 19 '16 at 16:53
  • @pm100 can you please write the code of the unsafe? i tried but i got all sort of errors :( – pio Dec 20 '16 at 17:18

2 Answers2

3

There is no direct way to convert them without copying them. You could write a linq query to return the bytes as integers, but that won't let you manipulate them.

One way to achieve what you want could be to wrap this in an own class:

public class IntArrayOverBytes
{
    private readonly byte[] bytes;
    public IntArrayOverBytes(byte[] bytes)
    {
        this.bytes = bytes;
    }

    public int this[int index]
    {
        get { return BitConverter.ToInt32(bytes, index * 4); }
        set { Array.Copy(BitConverter.GetBytes(value), 0, bytes, index * 4, 4); }
    }
}

With this class you can read int values from your byte array and write them back:

IntArrayOverBytes intArray = new IntArrayOverBytes(bytes);
intArray[5] = 2016;
Console.WriteLine(intArray[5]);

For full Array like functionality you would need to add some more code. For example implementing IEnumerable<int> could be useful:

public int Length => bytes.Length/4;
public IEnumerator<int> GetEnumerator()
{
    for(int i=0; i<Length; i++) yield return this[i];
}
René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • your code is by no doudbt nicer and more readable than mine, but still I think that there is a way to cast without copy, after all the memory already set to Int32 and we just need the compiler to know about it. I really care about performance in this application. – pio Dec 19 '16 at 19:00
  • @pio so I guess you'll need one of the `unsafe` ways mentioned in the comments, but I'm not experienced with that. – René Vogt Dec 19 '16 at 21:50
  • @pio but please note that my code does not copy the array, only the reference to the array. of course it "copies" the values you access. – René Vogt Dec 19 '16 at 21:55
  • your'e right, but I need fast casting, continue with this wrapping class seems to hurt performance. – pio Dec 21 '16 at 08:11
1

Here is one of the unsafe versions (needs Allow unsafe code checked in the Project Build Properties):

byte[] buffer = { 255,255,255,255, 255,255,255,127 }; // little endian { -1, int.MaxValue }

unsafe
{
    fixed (byte* bytePtr = buffer) // or = &buffer[0] 
    {
        for (int* intPtr = (int*)bytePtr; intPtr < bytePtr + buffer.Length; intPtr++)
        {
            *intPtr += 10; //intPtr is the address, and *intPtr is the value at that address
        }
    }
}

Debug.Print(string.Join(", ", buffer)); //"9, 0, 0, 0, 9, 0, 0, 128" { 9, int.MinValue + 9 }

fixed is needed to get the address of the array, and to prevent the garbage collector from relocating the array to different memory location. bytePtr + buffer.Length is the memory address after the last element in the buffer array. Adding 1 to the intPtr address moves it by 4 bytes.

My guess is that the unsafe version would be less than 2 times faster than the safe BitConverter version, so I don't think the risks are worth it. I think you can get much better performance from the suggestions in my previous answer.

Community
  • 1
  • 1
Slai
  • 22,144
  • 5
  • 45
  • 53
  • Thx for your help! But I need to pass the array to further processing (to a function for example) and if I would just pass the int* I cant treat it as int[] (got error for convertion). I'm starting to give up on C#, the Ints array is already in the memory in the right position as int32, the only thing needed is to tell the compiler to treat them as Int array and not as byte array.. – pio Dec 21 '16 at 08:19