Files
lime/nmegl/utils/ByteArray.hx
underscorediscovery 7ce604c914 Adding initial commit
2013-06-23 00:24:09 -02:30

586 lines
14 KiB
Haxe

package nmegl.utils;
#if (cpp || neko)
import nmegl.core.Libs;
import haxe.io.Bytes;
import haxe.io.BytesData;
// import nme.errors.EOFError; // Ensure that the neko->haxe callbacks are initialized
import nmegl.utils.CompressionAlgorithm;
import nmegl.utils.IDataInput;
#if neko
import neko.Lib;
import neko.zip.Compress;
import neko.zip.Uncompress;
import neko.zip.Flush;
#else
import cpp.Lib;
import cpp.zip.Compress;
import cpp.zip.Uncompress;
import cpp.zip.Flush;
#end
class ByteArray extends Bytes #if !haxe3 , #end implements ArrayAccess<Int> #if !haxe3 , #end implements IDataInput #if !haxe3 , #end implements IMemoryRange
{
public var bigEndian:Bool;
public var bytesAvailable(get_bytesAvailable, null):Int;
public var endian(get_endian, set_endian):String;
public var position:Int;
public var byteLength(get_byteLength,null):Int;
#if neko
/** @private */ private var alloced:Int;
#end
public function new(inSize = 0)
{
bigEndian = true;
position = 0;
if (inSize >= 0)
{
#if neko
alloced = inSize < 16 ? 16 : inSize;
var bytes = untyped __dollar__smake(alloced);
super(inSize, bytes);
#else
var data = new BytesData();
if (inSize > 0)
untyped data[inSize - 1] = 0;
super(inSize, data);
#end
}
}
@:keep
inline public function __get(pos:Int):Int
{
// Neko/cpp pseudo array accessors...
// No bounds checking is done in the cpp case
#if cpp
return untyped b[pos];
#else
return get(pos);
#end
}
#if !no_nme_io
/** @private */ static function __init__() {
var factory = function(inLen:Int) { return new ByteArray(inLen); };
var resize = function(inArray:ByteArray, inLen:Int)
{
if (inLen > 0)
inArray.ensureElem(inLen - 1, true);
inArray.length = inLen;
};
var bytes = function(inArray:ByteArray) { return inArray==null ? null : inArray.b; }
var slen = function(inArray:ByteArray) { return inArray == null ? 0 : inArray.length; }
var init = Libs.load("nme", "nme_byte_array_init", 4);
init(factory, slen, resize, bytes);
}
#end
@:keep
inline public function __set(pos:Int, v:Int):Void
{
// No bounds checking is done in the cpp case
#if cpp
untyped b[pos] = v;
#else
set(pos, v);
#end
}
public function asString():String
{
return readUTFBytes(length);
}
public function checkData(inLength:Int)
{
if (inLength + position > length)
ThrowEOFi();
}
public function clear()
{
position = 0;
length = 0;
}
public function compress(algorithm:CompressionAlgorithm = null)
{
#if neko
var src = alloced == length ? this : sub(0, length);
#else
var src = this;
#end
var result:Bytes;
if (algorithm == CompressionAlgorithm.LZMA)
{
result = Bytes.ofData(nme_lzma_encode( cast src.getData()) );
} else
{
var windowBits = switch(algorithm)
{
case DEFLATE: -15;
case GZIP: 31;
default: 15;
}
#if enable_deflate
result = Compress.run(src, 8, windowBits);
#else
result = Compress.run(src, 8);
#end
}
b = result.b;
length = result.length;
position = length;
#if neko
alloced = length;
#end
}
public function deflate()
{
compress(CompressionAlgorithm.DEFLATE);
}
/** @private */ private function ensureElem(inSize:Int, inUpdateLenght:Bool) {
var len = inSize + 1;
#if neko
if (alloced < len)
{
alloced =((len+1) * 3) >> 1;
var new_b = untyped __dollar__smake(alloced);
untyped __dollar__sblit(new_b, 0, b, 0, length);
b = new_b;
}
#else
if (b.length < len)
untyped b.__SetSize(len);
#end
if (inUpdateLenght && length < len)
length = len;
}
static public function fromBytes(inBytes:Bytes)
{
var result = new ByteArray( -1);
result.nmeFromBytes(inBytes);
return result;
}
public function getLength():Int { return length; }
// IMemoryRange
public function getByteBuffer():ByteArray { return this; }
public function getStart():Int { return 0; }
public function inflate()
{
uncompress(CompressionAlgorithm.DEFLATE);
}
private inline function nmeFromBytes(inBytes:Bytes):Void
{
b = inBytes.b;
length = inBytes.length;
#if neko
alloced = length;
#end
}
public inline function readBoolean():Bool
{
return(position < length) ? __get(position++) != 0 : ThrowEOFi() != 0;
}
public inline function readByte():Int
{
var val:Int = readUnsignedByte();
return((val & 0x80) != 0) ?(val - 0x100) : val;
}
public function readBytes(outData:ByteArray, inOffset:Int = 0, inLen:Int = 0):Void
{
if (inLen == 0)
inLen = length - position;
if (position + inLen > length)
ThrowEOFi();
if (outData.length < inOffset + inLen)
outData.ensureElem(inOffset + inLen - 1, true);
#if neko
outData.blit(inOffset, this, position, inLen);
#else
var b1 = b;
var b2 = outData.b;
var p = position;
for(i in 0...inLen)
b2[inOffset + i] = b1[p + i];
#end
position += inLen;
}
public function readDouble():Float
{
if (position + 8 > length)
ThrowEOFi();
#if neko
var bytes = new Bytes(8, untyped __dollar__ssub(b, position, 8));
#elseif cpp
var bytes = new Bytes(8, b.slice(position, position + 8));
#end
position += 8;
return _double_of_bytes(bytes.b, bigEndian);
}
#if !no_nme_io
static public function readFile(inString:String):ByteArray
{
return nme_byte_array_read_file(inString);
}
#end
public function readFloat():Float
{
if (position + 4 > length)
ThrowEOFi();
#if neko
var bytes = new Bytes(4, untyped __dollar__ssub(b, position, 4));
#elseif cpp
var bytes = new Bytes(4, b.slice(position, position + 4));
#end
position += 4;
return _float_of_bytes(bytes.b, bigEndian);
}
public function readInt():Int
{
var ch1 = readUnsignedByte();
var ch2 = readUnsignedByte();
var ch3 = readUnsignedByte();
var ch4 = readUnsignedByte();
return bigEndian ?(ch1 << 24) |(ch2 << 16) |(ch3 << 8) | ch4 :(ch4 << 24) |(ch3 << 16) |(ch2 << 8) | ch1;
}
public inline function readMultiByte(inLen:Int, charSet:String):String
{
// TODO - use code page
return readUTFBytes(inLen);
}
public function readShort():Int
{
var ch1 = readUnsignedByte();
var ch2 = readUnsignedByte();
var val = bigEndian ?((ch1 << 8) | ch2) :((ch2 << 8) | ch1);
return((val & 0x8000) != 0) ?(val - 0x10000) : val;
}
inline public function readUnsignedByte():Int
{
return(position < length) ? __get(position++) : ThrowEOFi();
}
public function readUnsignedInt():Int
{
var ch1 = readUnsignedByte();
var ch2 = readUnsignedByte();
var ch3 = readUnsignedByte();
var ch4 = readUnsignedByte();
return bigEndian ?(ch1 << 24) |(ch2 << 16) |(ch3 << 8) | ch4 :(ch4 << 24) |(ch3 << 16) |(ch2 << 8) | ch1;
}
public function readUnsignedShort():Int
{
var ch1 = readUnsignedByte();
var ch2 = readUnsignedByte();
return bigEndian ?(ch1 << 8) | ch2 :(ch2 << 8) + ch1;
}
public function readUTF():String
{
var len = readUnsignedShort();
return readUTFBytes(len);
}
public function readUTFBytes(inLen:Int):String
{
if (position + inLen > length)
ThrowEOFi();
var p = position;
position += inLen;
#if neko
return new String(untyped __dollar__ssub(b, p, inLen));
#elseif cpp
var result:String="";
untyped __global__.__hxcpp_string_of_bytes(b, result, p, inLen);
return result;
#end
}
public function setLength(inLength:Int):Void
{
if (inLength > 0)
ensureElem(inLength - 1, false);
length = inLength;
}
// ArrayBuffer interface
public function slice(inBegin:Int, ?inEnd:Int):ByteArray
{
var begin = inBegin;
if (begin < 0)
{
begin += length;
if (begin < 0)
begin = 0;
}
var end:Int = inEnd == null ? length : inEnd;
if (end < 0)
{
end += length;
if (end < 0)
end = 0;
}
if (begin >= end)
return new ByteArray();
var result = new ByteArray(end - begin);
var opos = position;
result.blit(0, this, begin, end - begin);
return result;
}
/** @private */ private function ThrowEOFi():Int {
throw "new EOFError();"; //todo sven
return 0;
}
public function uncompress(algorithm:CompressionAlgorithm = null):Void
{
if (algorithm == null) algorithm = CompressionAlgorithm.GZIP;
#if neko
var src = alloced == length ? this : sub(0, length);
#else
var src = this;
#end
var result:Bytes;
if (algorithm == CompressionAlgorithm.LZMA)
{
result = Bytes.ofData(nme_lzma_decode(src.getData()));
} else
{
var windowBits = switch(algorithm)
{
case DEFLATE: -15;
case GZIP: 31;
default: 15;
}
#if enable_deflate
result = Uncompress.run(src, null, windowBits);
#else
result = Uncompress.run(src, null);
#end
}
b = result.b;
length = result.length;
position = 0;
#if neko
alloced = length;
#end
}
/** @private */ inline function write_uncheck(inByte:Int) {
#if cpp
untyped b.__unsafe_set(position++, inByte);
#else
untyped __dollar__sset(b, position++, inByte & 0xff);
#end
}
public function writeBoolean(value:Bool)
{
writeByte(value ? 1 : 0);
}
inline public function writeByte(value:Int)
{
ensureElem(position, true);
#if cpp
b[position++] = untyped value;
#else
untyped __dollar__sset(b, position++, value & 0xff);
#end
}
public function writeBytes(bytes:Bytes, inOffset:Int = 0, inLength:Int = 0)
{
if (inLength == 0) inLength = bytes.length - inOffset;
ensureElem(position + inLength - 1, true);
var opos = position;
position += inLength;
blit(opos, bytes, inOffset, inLength);
}
public function writeDouble(x:Float)
{
#if neko
var bytes = new Bytes(8, _double_bytes(x, bigEndian));
#elseif cpp
var bytes = Bytes.ofData(_double_bytes(x, bigEndian));
#end
writeBytes(bytes);
}
#if !no_nme_io
public function writeFile(inString:String):Void
{
nme_byte_array_overwrite_file(inString, this);
}
#end
public function writeFloat(x:Float)
{
#if neko
var bytes = new Bytes(4, _float_bytes(x, bigEndian));
#elseif cpp
var bytes = Bytes.ofData(_float_bytes(x, bigEndian));
#end
writeBytes(bytes);
}
public function writeInt(value:Int)
{
ensureElem(position + 3, true);
if (bigEndian)
{
write_uncheck(value >> 24);
write_uncheck(value >> 16);
write_uncheck(value >> 8);
write_uncheck(value);
} else
{
write_uncheck(value);
write_uncheck(value >> 8);
write_uncheck(value >> 16);
write_uncheck(value >> 24);
}
}
// public function writeMultiByte(value:String, charSet:String)
// public function writeObject(object:*)
public function writeShort(value:Int)
{
ensureElem(position + 1, true);
if (bigEndian)
{
write_uncheck(value >> 8);
write_uncheck(value);
} else
{
write_uncheck(value);
write_uncheck(value >> 8);
}
}
public function writeUnsignedInt(value:Int)
{
writeInt(value);
}
public function writeUTF(s:String)
{
#if neko
var bytes = new Bytes(s.length, untyped s.__s);
#else
var bytes = Bytes.ofString(s);
#end
writeShort(bytes.length);
writeBytes(bytes);
}
public function writeUTFBytes(s:String)
{
#if neko
var bytes = new Bytes(s.length, untyped s.__s);
#else
var bytes = Bytes.ofString(s);
#end
writeBytes(bytes);
}
// Getters & Setters
private function get_bytesAvailable():Int { return length - position; }
private function get_byteLength():Int { return length; }
private function get_endian():String { return bigEndian ? Endian.BIG_ENDIAN : Endian.LITTLE_ENDIAN; }
private function set_endian(s:String):String { bigEndian =(s == Endian.BIG_ENDIAN); return s; }
// Native Methods
/** @private */ private static var _double_bytes = Libs.load("std", "double_bytes", 2);
/** @private */ private static var _double_of_bytes = Libs.load("std", "double_of_bytes", 2);
/** @private */ private static var _float_bytes = Libs.load("std", "float_bytes", 2);
/** @private */ private static var _float_of_bytes = Libs.load("std", "float_of_bytes", 2);
#if !no_nme_io
private static var nme_byte_array_overwrite_file = Libs.load("nme","nme_byte_array_overwrite_file", 2);
private static var nme_byte_array_read_file = Libs.load("nme", "nme_byte_array_read_file", 1);
#end
private static var nme_lzma_encode = Libs.load("nme", "nme_lzma_encode", 1);
private static var nme_lzma_decode = Libs.load("nme", "nme_lzma_decode", 1);
}
#else
typedef ByteArray = flash.utils.ByteArray;
#end