659 lines
19 KiB
Haxe
659 lines
19 KiB
Haxe
package lime.utils;
|
|
|
|
#if (js && !display)
|
|
|
|
typedef ArrayBufferView = js.html.ArrayBufferView;
|
|
|
|
#else
|
|
|
|
class ArrayBufferView {
|
|
|
|
public var type = TypedArrayType.None;
|
|
public var buffer:ArrayBuffer;
|
|
public var byteOffset:Int;
|
|
public var byteLength:Int;
|
|
public var length:Int;
|
|
|
|
//internal for avoiding switching on types
|
|
var bytesPerElement (default,null) : Int = 0;
|
|
|
|
@:allow(lime.utils)
|
|
#if !no_typedarray_inline inline #end
|
|
function new( ?elements:Null<Int> = null, in_type:TypedArrayType) {
|
|
|
|
type = in_type;
|
|
bytesPerElement = bytesForType(type);
|
|
|
|
//other constructor types use
|
|
//the init calls below
|
|
if(elements != null && elements != 0) {
|
|
|
|
if(elements < 0) elements = 0;
|
|
//:note:spec: also has, platform specific max int?
|
|
//elements = min(elements,maxint);
|
|
|
|
byteOffset = 0;
|
|
byteLength = toByteLength(elements);
|
|
buffer = new ArrayBuffer( byteLength );
|
|
length = elements;
|
|
|
|
}
|
|
|
|
} //new
|
|
|
|
//Constructor helpers
|
|
|
|
@:allow(lime.utils)
|
|
#if !no_typedarray_inline inline #end
|
|
function initTypedArray( view:ArrayBufferView ) {
|
|
|
|
var srcData = view.buffer;
|
|
var srcLength = view.length;
|
|
var srcByteOffset = view.byteOffset;
|
|
var srcElementSize = view.bytesPerElement;
|
|
var elementSize = bytesPerElement;
|
|
|
|
//same species, so just blit the data
|
|
//in other words, it shares the same bytes per element etc
|
|
if(view.type == type) {
|
|
cloneBuffer(srcData, srcByteOffset);
|
|
} else {
|
|
//see :note:1: below use FPHelper!
|
|
throw ("unimplemented");
|
|
}
|
|
|
|
byteLength = bytesPerElement * srcLength;
|
|
byteOffset = 0;
|
|
length = srcLength;
|
|
|
|
return this;
|
|
|
|
} //(typedArray)
|
|
|
|
@:allow(lime.utils)
|
|
#if !no_typedarray_inline inline #end
|
|
function initBuffer( in_buffer:ArrayBuffer, ?in_byteOffset:Int = 0, len:Null<Int> = null ) {
|
|
|
|
if(in_byteOffset < 0) throw TAError.RangeError;
|
|
if(in_byteOffset % bytesPerElement != 0) throw TAError.RangeError;
|
|
|
|
var bufferByteLength = in_buffer.length;
|
|
var elementSize = bytesPerElement;
|
|
var newByteLength = bufferByteLength;
|
|
|
|
if( len == null ) {
|
|
|
|
newByteLength = bufferByteLength - in_byteOffset;
|
|
|
|
if(bufferByteLength % bytesPerElement != 0) throw TAError.RangeError;
|
|
if(newByteLength < 0) throw TAError.RangeError;
|
|
|
|
} else {
|
|
|
|
newByteLength = len * bytesPerElement;
|
|
|
|
var newRange = in_byteOffset + newByteLength;
|
|
if( newRange > bufferByteLength ) throw TAError.RangeError;
|
|
|
|
}
|
|
|
|
buffer = in_buffer;
|
|
byteOffset = in_byteOffset;
|
|
byteLength = newByteLength;
|
|
length = Std.int(newByteLength / bytesPerElement);
|
|
|
|
return this;
|
|
|
|
} //(buffer [, byteOffset [, length]])
|
|
|
|
|
|
@:allow(lime.utils)
|
|
#if !no_typedarray_inline inline #end
|
|
function initArray<T>( array:Array<T> ) {
|
|
|
|
byteOffset = 0;
|
|
length = array.length;
|
|
byteLength = toByteLength(length);
|
|
|
|
buffer = new ArrayBuffer( byteLength );
|
|
copyFromArray(cast array);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
//Public shared APIs
|
|
|
|
//T is required because it can translate [0,0] as Int array
|
|
#if !no_typedarray_inline inline #end
|
|
public function set<T>( ?view:ArrayBufferView, ?array:Array<T>, offset:Int = 0 ) : Void {
|
|
|
|
if(view != null && array == null) {
|
|
buffer.blit( toByteLength(offset), view.buffer, view.byteOffset, view.byteLength );
|
|
} else if(array != null && view == null) {
|
|
copyFromArray(cast array, offset);
|
|
} else {
|
|
throw "Invalid .set call. either view, or array must be not-null.";
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//Internal TypedArray api
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
function cloneBuffer(src:ArrayBuffer, srcByteOffset:Int = 0) {
|
|
|
|
var srcLength = src.length;
|
|
var cloneLength = srcLength - srcByteOffset;
|
|
|
|
buffer = new ArrayBuffer( cloneLength );
|
|
buffer.blit( 0, src, srcByteOffset, cloneLength );
|
|
|
|
}
|
|
|
|
|
|
@:generic
|
|
@:allow(lime.utils)
|
|
#if !no_typedarray_inline inline #end
|
|
function subarray<T_subarray>( begin:Int, end:Null<Int> = null ) : T_subarray {
|
|
|
|
if (end == null) end == length;
|
|
var len = end - begin;
|
|
var byte_offset = toByteLength(begin);
|
|
|
|
var view : ArrayBufferView =
|
|
switch(type) {
|
|
|
|
case Int8:
|
|
new Int8Array(buffer, byte_offset, len);
|
|
|
|
case Int16:
|
|
new Int16Array(buffer, byte_offset, len);
|
|
|
|
case Int32:
|
|
new Int32Array(buffer, byte_offset, len);
|
|
|
|
case Uint8:
|
|
new UInt8Array(buffer, byte_offset, len);
|
|
|
|
case Uint8Clamped:
|
|
new UInt8ClampedArray(buffer, byte_offset, len);
|
|
|
|
case Uint16:
|
|
new UInt16Array(buffer, byte_offset, len);
|
|
|
|
case Uint32:
|
|
new UInt32Array(buffer, byte_offset, len);
|
|
|
|
case Float32:
|
|
new Float32Array(buffer, byte_offset, len);
|
|
|
|
case Float64:
|
|
new Float64Array(buffer, byte_offset, len);
|
|
|
|
case None:
|
|
throw "subarray on a blank ArrayBufferView";
|
|
}
|
|
|
|
return cast view;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
function bytesForType( type:TypedArrayType ) : Int {
|
|
|
|
return
|
|
switch(type) {
|
|
|
|
case Int8:
|
|
Int8Array.BYTES_PER_ELEMENT;
|
|
|
|
case Uint8:
|
|
UInt8Array.BYTES_PER_ELEMENT;
|
|
|
|
case Uint8Clamped:
|
|
UInt8ClampedArray.BYTES_PER_ELEMENT;
|
|
|
|
case Int16:
|
|
Int16Array.BYTES_PER_ELEMENT;
|
|
|
|
case Uint16:
|
|
UInt16Array.BYTES_PER_ELEMENT;
|
|
|
|
case Int32:
|
|
Int32Array.BYTES_PER_ELEMENT;
|
|
|
|
case Uint32:
|
|
UInt32Array.BYTES_PER_ELEMENT;
|
|
|
|
case Float32:
|
|
Float32Array.BYTES_PER_ELEMENT;
|
|
|
|
case Float64:
|
|
Float64Array.BYTES_PER_ELEMENT;
|
|
|
|
case _: 1;
|
|
}
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
function toString() {
|
|
|
|
if (this == null) return null;
|
|
|
|
var name =
|
|
switch(type) {
|
|
case Int8: 'Int8Array';
|
|
case Uint8: 'UInt8Array';
|
|
case Uint8Clamped: 'UInt8ClampedArray';
|
|
case Int16: 'Int16Array';
|
|
case Uint16: 'UInt16Array';
|
|
case Int32: 'Int32Array';
|
|
case Uint32: 'UInt32Array';
|
|
case Float32: 'Float32Array';
|
|
case Float64: 'Float64Array';
|
|
case _: 'ArrayBufferView';
|
|
}
|
|
|
|
return name + ' [byteLength:${this.byteLength}, length:${this.length}]';
|
|
|
|
} //toString
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
function toByteLength( elemCount:Int ) : Int {
|
|
|
|
return elemCount * bytesPerElement;
|
|
|
|
}
|
|
|
|
//Non-spec
|
|
|
|
#if !no_typedarray_inline #end
|
|
function copyFromArray(array:Array<Float>, ?offset : Int = 0 ) {
|
|
|
|
//Ideally, native semantics could be used, like cpp.NativeArray.blit
|
|
var i = 0, len = array.length;
|
|
|
|
switch(type) {
|
|
case Int8:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setInt8(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Int16:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setInt16(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Int32:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setInt32(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Uint8:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setUint8(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Uint16:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setUint16(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Uint32:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setUint32(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Uint8Clamped:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setUint8Clamped(buffer,
|
|
pos, Std.int(array[i]));
|
|
++i;
|
|
}
|
|
case Float32:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setFloat32(buffer,
|
|
pos, array[i]);
|
|
++i;
|
|
}
|
|
case Float64:
|
|
while(i<len) {
|
|
var pos = (offset+i)*bytesPerElement;
|
|
ArrayBufferIO.setFloat64(buffer,
|
|
pos, array[i]);
|
|
++i;
|
|
}
|
|
|
|
case None:
|
|
throw "copyFromArray on a base type ArrayBuffer";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //ArrayBufferView
|
|
|
|
#end //!js
|
|
|
|
@:noCompletion @:dox(hide) private enum TAError {
|
|
RangeError;
|
|
}
|
|
|
|
@:noCompletion @:dox(hide) @:enum
|
|
private abstract TypedArrayType(Int) from Int to Int {
|
|
var None = 0;
|
|
var Int8 = 1;
|
|
var Int16 = 2;
|
|
var Int32 = 3;
|
|
var Uint8 = 4;
|
|
var Uint8Clamped = 5;
|
|
var Uint16 = 6;
|
|
var Uint32 = 7;
|
|
var Float32 = 8;
|
|
var Float64 = 9;
|
|
}
|
|
|
|
#if (!js || display)
|
|
|
|
@:noCompletion @:dox(hide) class ArrayBufferIO {
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getInt8( buffer:ArrayBuffer, byteOffset:Int ) : Int {
|
|
|
|
#if cpp
|
|
return untyped __global__.__hxcpp_memory_get_byte(buffer.getData(), byteOffset);
|
|
#else
|
|
var val:Int = buffer.get(byteOffset);
|
|
return ((val & 0x80) != 0) ?(val - 0x100) : val;
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setInt8( buffer:ArrayBuffer, byteOffset:Int, value:Int ) {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_byte(buffer.getData(), byteOffset, value);
|
|
#elseif neko
|
|
untyped __dollar__sset(buffer.b, byteOffset, value & 0xff);
|
|
#else
|
|
buffer.set(byteOffset, value);
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getUint8( buffer:ArrayBuffer, byteOffset:Int ) : Null<UInt> {
|
|
|
|
#if cpp
|
|
return untyped __global__.__hxcpp_memory_get_byte(buffer.getData(), byteOffset) & 0xff;
|
|
#else
|
|
return buffer.get( byteOffset );
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setUint8Clamped( buffer:ArrayBuffer, byteOffset:Int, value:UInt ) : UInt {
|
|
|
|
return setUint8( buffer, byteOffset, _clamp(value) );
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setUint8( buffer:ArrayBuffer, byteOffset:Int, value:UInt ) : UInt {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_byte(buffer.getData(), byteOffset, value);
|
|
#else
|
|
buffer.set( byteOffset, value );
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
public static function getInt16( buffer:ArrayBuffer, byteOffset:Int, littleEndian:Bool=true ) : Int {
|
|
|
|
#if cpp
|
|
untyped return __global__.__hxcpp_memory_get_i16(buffer.getData(), byteOffset);
|
|
#else
|
|
var ch1 = buffer.get(byteOffset );
|
|
var ch2 = buffer.get(byteOffset + 1);
|
|
|
|
var val =
|
|
if(littleEndian)
|
|
((ch2 << 8) | ch1) else
|
|
((ch1 << 8) | ch2);
|
|
|
|
return
|
|
((val & 0x8000) != 0) ?
|
|
( val - 0x10000 ) : ( val );
|
|
#end
|
|
|
|
}
|
|
|
|
public static function setInt16( buffer:ArrayBuffer, byteOffset:Int, value:Int, littleEndian:Bool=true ) {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_i16(buffer.getData(), byteOffset, value);
|
|
#elseif neko
|
|
untyped var b = buffer.b;
|
|
if(littleEndian) {
|
|
untyped __dollar__sset(b, byteOffset , (value ) & 0xff);
|
|
untyped __dollar__sset(b, byteOffset+1, (value >> 8) & 0xff);
|
|
} else {
|
|
untyped __dollar__sset(b, byteOffset , (value >> 8) & 0xff);
|
|
untyped __dollar__sset(b, byteOffset+1, (value ) & 0xff);
|
|
}
|
|
#else
|
|
if(littleEndian) {
|
|
buffer.set(byteOffset , (value ) & 0xff);
|
|
buffer.set(byteOffset+1, (value >> 8) & 0xff);
|
|
} else {
|
|
buffer.set(byteOffset , (value >> 8) & 0xff);
|
|
buffer.set(byteOffset+1, (value ) & 0xff);
|
|
}
|
|
#end
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getUint16( buffer:ArrayBuffer, byteOffset:Int, littleEndian:Bool=true ) : Null<UInt> {
|
|
|
|
#if cpp
|
|
untyped return __global__.__hxcpp_memory_get_ui16(buffer.getData(), byteOffset) & 0xffff;
|
|
#else
|
|
|
|
var ch1 = buffer.get(byteOffset );
|
|
var ch2 = buffer.get(byteOffset + 1);
|
|
|
|
return
|
|
if(littleEndian)
|
|
((ch2 << 8) | ch1) else
|
|
((ch1 << 8) | ch2);
|
|
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setUint16( buffer:ArrayBuffer, byteOffset:Int, value:UInt, littleEndian:Bool=true ) : UInt {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_ui16(buffer.getData(), byteOffset, value);
|
|
#else
|
|
setInt16(buffer, byteOffset, value, littleEndian);
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getInt32( buffer:ArrayBuffer, byteOffset:Int, littleEndian:Bool=true ) : Int {
|
|
|
|
#if cpp
|
|
untyped return __global__.__hxcpp_memory_get_i32(buffer.getData(), byteOffset);
|
|
#else
|
|
#if (haxe_ver > 3103)
|
|
return buffer.getI32(byteOffset);
|
|
#else
|
|
|
|
var ch1 = getInt8(buffer, byteOffset );
|
|
var ch2 = getInt8(buffer, byteOffset+1);
|
|
var ch3 = getInt8(buffer, byteOffset+2);
|
|
var ch4 = getInt8(buffer, byteOffset+3);
|
|
|
|
return
|
|
littleEndian ?
|
|
(ch4 << 24) |(ch3 << 16) |(ch2 << 8) | ch1 //littleEndian
|
|
: (ch1 << 24) |(ch2 << 16) |(ch3 << 8) | ch4; //bigEndian
|
|
|
|
#end //3.1.3
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setInt32( buffer:ArrayBuffer, byteOffset:Int, value:Int, littleEndian:Bool=true ) {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_i32(buffer.getData(), byteOffset, value);
|
|
#else
|
|
#if (haxe_ver > 3103)
|
|
buffer.setI32(byteOffset,value);
|
|
#else
|
|
if (littleEndian) {
|
|
setInt8(buffer, byteOffset , value );
|
|
setInt8(buffer, byteOffset+1, value >> 8);
|
|
setInt8(buffer, byteOffset+2, value >> 16);
|
|
setInt8(buffer, byteOffset+3, value >> 24);
|
|
} else {
|
|
setInt8(buffer, byteOffset , value >> 24);
|
|
setInt8(buffer, byteOffset+1, value >> 16);
|
|
setInt8(buffer, byteOffset+2, value >> 8);
|
|
setInt8(buffer, byteOffset+3, value );
|
|
}
|
|
#end //3.1.3.
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getUint32( buffer:ArrayBuffer, byteOffset:Int, littleEndian:Bool=true ) : Null<UInt> {
|
|
|
|
#if cpp
|
|
untyped return __global__.__hxcpp_memory_get_ui32(buffer.getData(), byteOffset);
|
|
#else
|
|
return getInt32( buffer, byteOffset, littleEndian );
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setUint32( buffer:ArrayBuffer, byteOffset:Int, value:UInt, littleEndian:Bool=true ) : UInt {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_ui32(buffer.getData(), byteOffset, value);
|
|
#else
|
|
setInt32( buffer, byteOffset, value );
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getFloat32( buffer:ArrayBuffer, byteOffset:Int, littleEndian:Bool=true ) : Float {
|
|
|
|
#if cpp
|
|
untyped return __global__.__hxcpp_memory_get_float(buffer.getData(), byteOffset);
|
|
#else
|
|
return buffer.getFloat( byteOffset );
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setFloat32( buffer:ArrayBuffer, byteOffset:Int, value:Float, littleEndian:Bool=true ) : Float {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_float(buffer.getData(), byteOffset, value);
|
|
#else
|
|
buffer.setFloat( byteOffset, value );
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function getFloat64( buffer:ArrayBuffer, byteOffset:Int, littleEndian:Bool=true ) : Float {
|
|
|
|
#if cpp
|
|
untyped return __global__.__hxcpp_memory_get_double(buffer.getData(), byteOffset);
|
|
#else
|
|
return buffer.getDouble( byteOffset );
|
|
#end
|
|
|
|
}
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
public static function setFloat64( buffer:ArrayBuffer, byteOffset:Int, value:Float, littleEndian:Bool=true ) : Float {
|
|
|
|
#if cpp
|
|
untyped __global__.__hxcpp_memory_set_double(buffer.getData(), byteOffset, value);
|
|
#else
|
|
buffer.setDouble( byteOffset, value );
|
|
#end
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
//Internal
|
|
|
|
#if !no_typedarray_inline inline #end
|
|
//clamp a Int to a 0-255 Uint8 (for Uint8Clamped array)
|
|
static function _clamp(_in:Float) : Int {
|
|
|
|
var _out = Std.int(_in);
|
|
_out = _out > 255 ? 255 : _out;
|
|
return _out < 0 ? 0 : _out;
|
|
|
|
} //_clamp
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
//#error "ArrayBufferIO is not used on js target, use DataView instead"
|
|
|
|
#end //!js
|