Endianness of the target

I’ve again run into a problem caused by the endianness of a ByteArray. To fix my problem, I have to manually set ByteArray.endian = Endian.LITTLE_ENDIAN;

This is not a generic solution though. Is there any way to read the endianness of the target?

Thank you!

You can google about endian for each target and write code like

ByteArray.endian = 
#if flash
THAT_ENDIAN;
#elseif android
ANDROID_ENDIAN;
#else
OTHER_ENDIAN;
#end

True. I was hoping that there was something already defined for each target.

One problem with this approach is that it doesn’t automatically handle any new targets that are added. Also, some targets can support different endianness. For instance, Linux runs on both big and little endian systems.

There are various C++ solutions, which could deal with C++ targets for you. You might even be able to get the compiler to inline the result by injecting C++ code. (Using a native extension might be easier, but I doubt anything would be inlined.)

Otherwise, well, you’ve already found a way to test it. You know that problem you mentioned, “caused by the endianness of a ByteArray”? Set that up intentionally, check whether it gives the same result as expected, and if not, toggle the ByteArray’s endianness.

Well, so far, the times I’ve run into it are while loading external data. But yes, I should be able to setup a test case for it. That will work fine for my own programs.

But what I’d really like is to be able to change the implementation of ByteArray to default to using the endianness of the target instead of defaulting to big endian. As it is now, the default behavior of ByteArray on little endian targets does work as the developer expects, and I don’t really want to put a kludge in the implementation of ByteArray.

Okay, I remembered the details of the first time I hit this problem. (I’ve got a 3 month old baby at home, so it takes me a little while to remember what I did last month…)

Search the web for how to use dynamically generate audio in flash and you’ll find a page from Adobe with an easy sample. The handler is trivially ported to haxe:

public function sineWaveGenerator(event:SampleDataEvent):Void
{
    var i: Int;
    for (i in 0...8192)
    {
        var n:Float = Math.sin((i + event.position) / Math.PI / 4);
        event.data.writeFloat(n);
        event.data.writeFloat(n);
    }
}

Compile that for a flash target and it works great! But now compile it for Windows or Mac and you get white noise. (Okay, that’s not true anymore, but I’ll get to that in a moment.) Turns out the problem is that ByteArray.writeFloat() writes using whatever endianness is set in ByteArray.endian, but the bytes are read back out to send to the driver in lime C++ code with essentially:

double value = *(double *)&array.data;

So it’s always read out in endianness of the target.

The simple fix for this is to set the endianness of the array in that handler:

public function sineWaveGenerator(event:SampleDataEvent):Void
{
    event.data.endian = Endian.LITTLE_ENDIAN;
    ...
}

But now this code no longer matches the flash original and it’s not obvious to a haxe developer looking at an examples from AS3 that they’d need to do this.

As mentioned earlier, if you have a recent version of openfl, you won’t be able to reproduce this because I already checked in a workaround for this specific case by defaulting SampleDataEvent to use little endian, as that’s far more common now. Of course, this workaround now means that if this is used on a big endian target, it will fail there.

I’d like to fix SampleDataEvent (and ByteArray in general) to default to the endianness of the target.

Although I thought at first that @player_03’s suggestion would work, I actually haven’t come up with a workable test in haxe. I can make a ByteArray and write some data into it in big endian order:

var endianTest = new ByteArray();
endianTest.writeByte(0xDE);
endianTest.writeByte(0xAD);
endianTest.writeByte(0xBE);
endianTest.writeByte(0xEF);

But then I can read it out both “correct” as a big endian value:

endianTest.endian = Endian.BIG_ENDIAN;
endianTest.position = 0;
var test = endianTest.readUnsignedInt();
trace('Reading as big endian returned 0x${StringTools.hex(test)}');

Produces:

Reading as big endian returned 0xDEADBEEF

and also “incorrect” as a little endian value:

endianTest.endian = Endian.LITTLE_ENDIAN;
endianTest.position = 0;
var test = endianTest.readUnsignedInt();
trace('Reading as little endian returned 0x${StringTools.hex(test)}');

produces:

Reading as little endian returned 0xEFBEADDE

As far as I can tell, that doesn’t give me any information about the endianness of the target.

I’ll look into the C++ solutions. It sounds like there is no existing solution for this, so perhaps I need to write a lime function to return the endianness of the target.

I found a test that seems to work. The trick is that in OpenFL, ByteArray extends Bytes, and the latter provides a function that uses the underlying platform’s endianness.

public function platformIsBigEndian():Bool {
    #if (neko || cpp)
        var byteArray:ByteArray = new ByteArray();
        byteArray.bigEndian = true;
        
        byteArray.writeFloat(1);
        return byteArray.getFloat(0) == 1;
    #elseif flash
        //Flash is always big endian.
        return true;
    #else
        //No way to check. :(
        return false;
    #end
}

Only tested on my computer, sorry.