Dany
Dany

Reputation: 2174

How to get little endian data from big endian in c# using bitConverter.ToInt32 method?

I am making application in C# which has a byte array containing hex values.

I am getting data as a big-endian but I want it as a little-endian and I am using Bitconverter.toInt32 method for converting that value to integer.

My problem is that before converting the value, I have to copy that 4 byte data into temporary array from source byte array and then reverse that temporary byte array.

I can't reverse source array because it also contains other data.

Because of that my application becomes slow.

In the code I have one source array of byte as waveData[] which contains a lot of data.

byte[] tempForTimestamp=new byte[4];
tempForTimestamp[0] = waveData[290];
tempForTimestamp[1] = waveData[289];
tempForTimestamp[2] = waveData[288];
tempForTimestamp[3] = waveData[287];
int number = BitConverter.ToInt32(tempForTimestamp, 0);

Is there any other method for that conversion?

Upvotes: 56

Views: 97676

Answers (13)

Moaaz Assali
Moaaz Assali

Reputation: 394

Similar to what @GeorgePolevoy answered but AsSpan(287) should be used instead:

BinaryPrimitives.ReadInt32BigEndian(waveData.AsSpan(287));

Using AsSpan() is around 2-2.5x faster from my tests than the one without. More details here from Microsoft documentation.

Here is a timing test to check on your machine:

Stopwatch stopwatch = new();

byte[] bytes = { 0x00, 0x00, 0x00, 0x00, 0x53, 0xb3, 0xd8 };

stopwatch.Start();
for (int i = 0; i < 1000000; i++)
    BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(3));
stopwatch.Stop();
Console.WriteLine($"Elapsed Time: {stopwatch.Elapsed}");

stopwatch.Restart();
for (int i = 0; i < 1000000; i++)
    BinaryPrimitives.ReadInt32BigEndian(bytes[3..]);
stopwatch.Stop();
Console.WriteLine($"Elapsed Time: {stopwatch.Elapsed}");

Upvotes: 3

This will reverse the data inline if unsafe code is allowed...

fixed (byte* wavepointer = waveData)
   new Span<byte>(wavepointer + offset, 4).Reverse();

Upvotes: 2

George Polevoy
George Polevoy

Reputation: 7671

The most straightforward way is to use the BinaryPrimitives.ReadInt32BigEndian(ReadOnlySpan) Method introduced in .NET Standard 2.1

var number = BinaryPrimitives.ReadInt32BigEndian(waveData[297..291]);

Upvotes: 18

&#193;kos Halmai
&#193;kos Halmai

Reputation: 21

public static unsafe int Reverse(int value)
{
    byte* p = (byte*)&value;
    return (*p << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}

If unsafe is allowed... Based on Marc Gravell's post

Upvotes: 2

Nyerguds
Nyerguds

Reputation: 5629

I dislike BitConverter, because (as Marc Gravell answered) it is specced to rely on system endianness, meaning you technically have to do a system endianness check every time you use BitConverter to ensure you don't have to reverse the array. And usually, with saved files, you generally know the endianness you're trying to read, and that might not be the same. You might just be handling file formats with big-endian values, too, like, for instance, PNG chunks.

Because of that, I just wrote my own methods for this, which take a byte array, the read offset and read length as arguments, as well as a boolean to specify the endianness handling, and which uses bit shifting for efficiency:

public static UInt64 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
    UInt64 value = 0;
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        value |= (((UInt64)data[offs]) << (8 * index));
    }
    return value;
}

This code can handle any value between 1 and 8 bytes, both little-endian and big-endian. The only small usage peculiarity is that you need to both give the amount of bytes to read, and need to specifically cast the result to the type you want.

Example from some code where I used it to read the header of some proprietary image type:

Int16 imageWidth = (Int16) ReadIntFromByteArray(fileData, hdrOffset, 2, true);
Int16 imageHeight = (Int16) ReadIntFromByteArray(fileData, hdrOffset + 2, 2, true);

This will read two consecutive 16-bit integers off an array, as signed little-endian values. You can of course just make a bunch of overload functions for all possibilities, like this:

public Int16 ReadInt16FromByteArrayLe(Byte[] data, Int32 startIndex)
{
    return (Int16) ReadIntFromByteArray(data, startIndex, 2, true);
}

But personally I didn't bother with that.

And, here's the same for writing bytes:

public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt64 value)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        data[offs] = (Byte) (value >> (8*index) & 0xFF);
    }
}

The only requirement here is that you have to cast the input arg to 64-bit unsigned integer when passing it to the function.

Upvotes: 2

Endel Dreyer
Endel Dreyer

Reputation: 1654

You can also use Jon Skeet "Misc Utils" library, available at https://jonskeet.uk/csharp/miscutil/

His library has many utility functions. For Big/Little endian conversions you can check the MiscUtil/Conversion/EndianBitConverter.cs file.

var littleEndianBitConverter = new MiscUtil.Conversion.LittleEndianBitConverter();
littleEndianBitConverter.ToInt64(bytes, offset);

var bigEndianBitConverter = new MiscUtil.Conversion.BigEndianBitConverter();
bigEndianBitConverter.ToInt64(bytes, offset);

His software is from 2009 but I guess it's still relevant.

Upvotes: 3

Zapnologica
Zapnologica

Reputation: 22556

I use the following helper functions

public static Int16 ToInt16(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt16(BitConverter.IsLittleEndian ? data.Skip(offset).Take(2).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt16(data, offset);
}
public static Int32 ToInt32(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt32(BitConverter.IsLittleEndian ? data.Skip(offset).Take(4).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt32(data, offset);
}
public static Int64 ToInt64(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt64(BitConverter.IsLittleEndian ? data.Skip(offset).Take(8).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt64(data, offset);
}

Upvotes: 3

FRL
FRL

Reputation: 776

Declare this class:

using static System.Net.IPAddress;

namespace BigEndianExtension
{
    public static class BigEndian
    {
        public static short ToBigEndian(this short value)   => HostToNetworkOrder(value);
        public static int   ToBigEndian(this int value)     => HostToNetworkOrder(value);
        public static long  ToBigEndian(this long value)    => HostToNetworkOrder(value);
        public static short FromBigEndian(this short value) => NetworkToHostOrder(value);
        public static int   FromBigEndian(this int value)   => NetworkToHostOrder(value);
        public static long  FromBigEndian(this long value)  => NetworkToHostOrder(value);
    }
}

Example, create a form with a button and a multiline textbox:

using BigEndianExtension;

private void button1_Click(object sender, EventArgs e)
{
    short int16 = 0x1234;
    int int32   = 0x12345678;
    long int64  = 0x123456789abcdef0;
    string text = string.Format("LE:{0:X4}\r\nBE:{1:X4}\r\n", int16, int16.ToBigEndian());

    text += string.Format("LE:{0:X8}\r\nBE:{1:X8}\r\n", int32, int32.ToBigEndian());
    text += string.Format("LE:{0:X16}\r\nBE:{1:X16}\r\n", int64, int64.ToBigEndian());
    textBox1.Text = text;
}
//Some code...

Upvotes: 10

Ioannis Karadimas
Ioannis Karadimas

Reputation: 7896

Here you go

public static int SwapEndianness(int value)
{
    var b1 = (value >> 0) & 0xff;
    var b2 = (value >> 8) & 0xff;
    var b3 = (value >> 16) & 0xff;
    var b4 = (value >> 24) & 0xff;

    return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
} 

Upvotes: 17

Herman
Herman

Reputation: 2998

Add a reference to System.Memory nuget and use BinaryPrimitives.ReverseEndianness().

using System.Buffers.Binary;
number = BinaryPrimitives.ReverseEndianness(number);

It supports both signed and unsigned integers (byte/short/int/long).

Upvotes: 60

Allison A
Allison A

Reputation: 5903

In modern-day Linq the one-liner and easiest to understand version would be:

int number = BitConverter.ToInt32(waveData.Skip(286).Take(4).Reverse().ToArray(), 0);

You could also...

byte[] tempForTimestamp = new byte[4];
Array.Copy(waveData, 287, tempForTimestamp, 0, 4);
Array.Reverse(tempForTimestamp);
int number = BitConverter.ToInt32(tempForTimestamp);

:)

Upvotes: 37

M Granja
M Granja

Reputation: 865

If you won't ever again need that reversed, temporary array, you could just create it as you pass the parameter, instead of making four assignments. For example:

int i = 287;
int value = BitConverter.ToInt32({
    waveData(i + 3),
    waveData(i + 2),
    waveData(i + 1),
    waveData(i)
}, 0);

Upvotes: 3

Marc Gravell
Marc Gravell

Reputation: 1062510

If you know the data is big-endian, perhaps just do it manually:

int value = (buffer[i++] << 24) | (buffer[i++] << 16)
          | (buffer[i++] << 8) | buffer[i++];

this will work reliably on any CPU, too. Note i is your current offset into the buffer.

Another approach would be to shuffle the array:

byte tmp = buffer[i+3];
buffer[i+3] = buffer[i];
buffer[i] = tmp;
tmp = buffer[i+2];
buffer[i+2] = buffer[i+1];
buffer[i+1] = tmp;
int value = BitConverter.ToInt32(buffer, i);
i += 4;

I find the first immensely more readable, and there are no branches / complex code, so it should work pretty fast too. The second could also run into problems on some platforms (where the CPU is already running big-endian).

Upvotes: 37

Related Questions