Decision Point: ByteArray

I have been working on a cleaner ByteArray class – one that has consistent code between platforms and is closer to the original spec.

Since Haxe 3.2, the core haxe.io.Bytes type has improved significantly, so we’re moving to a new implementation that centers on it. There’s a decision, though, whether to make ByteArray abstract. There are some pros and cons:

Abstract ByteArray

If openfl.utils.ByteArray is an abstract then we get array access:

byteArray[0] = 1000;

It also adds some interesting support for casting:

var bytes:Bytes = byteArray;
var arrayBuffer:ArrayBuffer = byteArray;

An abstract type does not exist at runtime, so Std.is (byteArray, ByteArray) would fail (though Std.is (byteArray, Bytes) would work on non-Flash platforms), or you could use the IDataInput or IDataOutput interface to check it.

Another downside of an abstract, is extended classes. MyClass extends Bytes would work but MyClass extends ByteArray would not.

Class ByteArray

Keeping ByteArray a class will be closer to our current behavior. Std.is (byteArray, ByteArray) would work, as well as MyClass extends ByteArray, but converting to and from haxe.io.Bytes will require conditional code depending on the platform. An abstract solves that problem for us. We also lose array access, the best we could do is:

byteArray.set (100, 120);

If we extend haxe.io.Bytes (which is what we currently do), we cannot set length. Using byteArray.length = 0; (similar to Flash) is possible if we use an abstract (above), or possibly if we have a separate bytes property internally and do not extend Bytes, but without abstract auto-casting, you could not use a ByteArray as Bytes directly.

Poll

What do you think?

  • (EDIT: option removed)
  • I like the benefits of an abstract ByteArray
  • I prefer the benefits of a ByteArray class

0 voters

I would say use the fastes one. Today ByteArrays are mostly use for optimizations or loading external data, but it’s not that common to use it as a substitute of an Array. So I believe most users will not notice the difference.

3 Likes

Maybe if you can create both… Start with a non abstract that extends Bytes and go as far as permitted by that paradigm. Than go the extra mile and create the abstract one on top of that class. Eventually, this Will let the user make his choice whether to use abstract or not.

Just a suggestion…

I would say: stay as consitent across targets as possible, because one of the main features of OpenFL is “Write once, deploy everywhere”. And for newcomers any inconcistency may become that last thing which will make them say “F**k this!”. So i vote for class ByteArray.

I think it’s better to introduce some additional API if we can achieve some significant perfomance boost, but keep it consistent across all targets too.

I currently have class ByteArrayData that works like the “class” example above, and abstract ByteArray which handles array access, casts and getting/setting length.

For consistency purposes, I think the abstract helps, because using ByteArray as haxe.io.Bytes will work consistently, and will not require #if flash behavior.

It also works as an array, similar to Flash.

We mostly lose MyClass extends ByteArray, which maybe is rare? Maybe not?

2 Likes

Will there be any difference in performance at all?

Not at all, this is just a matter of how it is accessed :slight_smile:

Why is that a bad thing if you can work around that and do

MyClass extends ByteArrayBase

All you lose is the length assignment and the array access which would not exist in the non abstract version anyways…

Yeah, I guess so :smile:

(we should perhaps override the get_length so it returns the logical __length instead of the bytes length, though, to avoid some possible issues there)

Open to ideas about the “base” name, too. I have “ByteArrayData” at the moment

I think the small incosistency introduced here is of no importance to 95% of developers.
However, I would agree that some hint about this in the documentation would be important.

1 Like

Probably a bigger surprise that byteArray[100] doesn’t work to the average dev? (which an abstract solves)

1 Like

On a side note, why does lime.utils.ByteArray only implement IDataInput for js and not all platforms?

Also, I had some issues with readUTFBytes on javascript. The changes below ensures it doesn’t read over the end of the buffer, and if it ends early due to malformed data, the position is updated by the full value of len, not just how many bytes were read.

public function readUTFBytes (len:Int):String {

	#if js
	
	var value = "";
	var max = this.position + len;
	
	// utf8-encode
	while (this.position < max) {
		
		var data:Dynamic = data;
		var c = data.getUint8 (this.position++);
		
		if (c < 0x80 || this.position == max) {
			
			if (c == 0) break;
			value += String.fromCharCode (c);
			
		} else if (c < 0xE0 || this.position == max-1) {
			
			value += String.fromCharCode (((c & 0x3F) << 6) | (data.getUint8 (this.position++) & 0x7F));
			
		} else if (c < 0xF0 || this.position == max-2) {
			
			var c2 = data.getUint8 (this.position++);
			value += String.fromCharCode (((c & 0x1F) << 12) | ((c2 & 0x7F) << 6) | (data.getUint8 (this.position++) & 0x7F));
			
		} else {
			
			var c2 = data.getUint8 (this.position++);
			var c3 = data.getUint8 (this.position++);
			value += String.fromCharCode (((c & 0x0F) << 18) | ((c2 & 0x7F) << 12) | ((c3 << 6) & 0x7F) | (data.getUint8 (this.position++) & 0x7F));
			
		}
		
	}
	this.position = max;
	
	return value;
	
	#else

rest of method is unaltered…

Jake

Is there any reason why i cant extend ByteArrayData ?
im getting:

Type not found : ByteArrayData

You might have to use ByteArray.ByteArrayData or import openfl.utils.ByteArray first

Apologies for dragging this up, but I’m running into it now.

So what do I replace the Std.is(x, Bytearray) with?

Is it all haxe.io.Bytes now, or ByteArrayData…
I’m encountering a lot of special case conversion code like this:

var fout:FileOutput = File.write(path, false);
if (Std.is(data, String)) fout.writeString(data);
else if (Std.is(data, Bytes)) {
    fout.write(data);
} else if (Std.is(data, ByteArray)) {
    var byteArray:ByteArray = cast(data, ByteArray);
    byteArray.position = 0;
    var bytes:Bytes = Bytes.alloc(byteArray.length);
    while (byteArray.bytesAvailable > 0) {
	bytes.set(byteArray.position, byteArray.readByte());
    }
    fout.write(bytes);
}

Ideally it would just be fout.write(anything), but at the moment, I just need a quick fix please.

Try Std.is(data, ByteArrayData) (which should work if you import openfl.utils.ByteArray)

That seems to work thanks. I couldn’t find length but it was just hidden.

Oh you can do:

if (Std.is(data, ByteArrayData))
{
    var ba:ByteArray = cast data;
    // use data
}