Skip to content

Instantly share code, notes, and snippets.

@zrong
Created September 22, 2015 03:42
Show Gist options
  • Save zrong/6e8d6b733158b0539bf2 to your computer and use it in GitHub Desktop.
Save zrong/6e8d6b733158b0539bf2 to your computer and use it in GitHub Desktop.
Int64.ts and Buffer.ts for egret.
////////////////////////////////////////
// Buffer.ts
// extend egret.ByteArray, implement writeInt64
// @author [email protected]
// Creation 2015-09-14
////////////////////////////////////////
class Buffer extends egret.ByteArray
{
private static SIZE_OF_INT64:number = 8;
private static SIZE_OF_UINT64:number = 8;
constructor(buffer?:ArrayBuffer)
{
super(buffer);
}
public readUnsignedInt64(raw=true):any {
return this.readInt64(raw);
}
public writeUnsignedInt64(value:any):void {
this.writeInt64(value);
}
public readInt64(raw=true):any {
if (!this.validate(Buffer.SIZE_OF_INT64)) return null;
var buffer:Array<number> = [];
for(var i:number=0; i<Buffer.SIZE_OF_INT64; i++)
{
buffer[i] = this.readByte();
}
var intValue:Int64 = new Int64(buffer);
if(raw)
{
return intValue.toNumber();
}
return intValue;
}
public writeInt64(value:any):void {
var intValue:Int64;
if(typeof(value) == 'number')
{
intValue = new Int64(value);
}
else
{
intValue = value;
}
var buffer:Array<number> = intValue.toBuffer(true);
for(var i:number=0; i<buffer.length; i++)
{
this.writeByte(buffer[i]);
}
}
}
// Int64.js
//
// Copyright (c) 2012 Robert Kieffer
// MIT License - http://opensource.org/licenses/mit-license.php
/**
* Support for handling 64-bit int numbers in Javascript (node.js)
*
* JS Numbers are IEEE-754 binary double-precision floats, which limits the
* range of values that can be represented with integer precision to:
*
* 2^^53 <= N <= 2^53
*
* Int64 objects wrap a node Buffer that holds the 8-bytes of int64 data. These
* objects operate directly on the buffer which means that if they are created
* using an existing buffer then setting the value will modify the Buffer, and
* vice-versa.
*
* Internal Representation
*
* The internal buffer format is Big Endian. I.e. the most-significant byte is
* at buffer[0], the least-significant at buffer[7]. For the purposes of
* converting to/from JS native numbers, the value is assumed to be a signed
* integer stored in 2's complement form.
*
* For details about IEEE-754 see:
* http://en.wikipedia.org/wiki/Double_precision_floating-point_format
*/
//
// Int64
//
class Int64
{
// Useful masks and values for bit twiddling
public static MASK31:number = 0x7fffffff;
public static VAL31:number = 0x80000000;
public static MASK32:number = 0xffffffff;
public static VAL32:number = 0x100000000;
public static MAX_INT:number = Math.pow(2, 53);
public static MIN_INT:number = -Math.pow(2, 53);
private static _HEX:Array<any> = new Array<any>();
public buffer:Array<number>;
public offset:number;
/**
* Constructor accepts any of the following argument types:
*
* new Int64(buffer[, offset=0]) - Existing Buffer with byte offset
* new Int64(Uint8Array[, offset=0]) - Existing Uint8Array with a byte offset
* new Int64(string) - Hex string (throws if n is outside int64 range)
* new Int64(number) - Number (throws if n is outside int64 range)
* new Int64(hi, lo) - Raw bits as two 32-bit values
*/
public constructor(a1:any, a2?:any)
{
this._buildHex();
if (a1 instanceof Array)
{
this.buffer = a1;
this.offset = a2 || 0;
}
else if (Object.prototype.toString.call(a1) == '[object Uint8Array]')
{
// Under Browserify, Buffers can extend Uint8Arrays rather than an
// instance of Buffer. We could assume the passed in Uint8Array is actually
// a buffer but that won't handle the case where a raw Uint8Array is passed
// in. We construct a new Buffer just in case.
this.buffer = Array.apply([], a1);
this.offset = a2 || 0;
}
else
{
this.buffer = this.buffer || [];
this.offset = 0;
this.setValue.apply(this, arguments);
}
}
// Map for converting hex octets to strings
private _buildHex():void
{
//Int64._HEX = [];
for (var i = 0; i < 256; i++) {
Int64._HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);
}
}
/**
* Do in-place 2's compliment. See
* http://en.wikipedia.org/wiki/Two's_complement
*/
private _2scomp()
{
var b = this.buffer, o = this.offset, carry = 1;
for (var i = o + 7; i >= o; i--) {
var v = (b[i] ^ 0xff) + carry;
b[i] = v & 0xff;
carry = v >> 8;
}
}
/**
* Set the value. Takes any of the following arguments:
*
* setValue(string) - A hexidecimal string
* setValue(number) - Number (throws if n is outside int64 range)
* setValue(hi, lo) - Raw bits as two 32-bit values
*/
public setValue(hi:any, lo?:any):void {
var negate:boolean = false;
if (arguments.length == 1) {
if (typeof(hi) == 'number') {
// Simplify bitfield retrieval by using abs() value. We restore sign
// later
negate = hi < 0;
hi = Math.abs(hi);
lo = hi % Int64.VAL32;
hi = hi / Int64.VAL32;
if (hi > Int64.VAL32) throw new RangeError(hi + ' is outside Int64 range');
hi = hi | 0;
} else if (typeof(hi) == 'string') {
hi = (hi + '').replace(/^0x/, '');
lo = hi.substr(-8);
hi = hi.length > 8 ? hi.substr(0, hi.length - 8) : '';
hi = parseInt(hi, 16);
lo = parseInt(lo, 16);
} else {
throw new Error(hi + ' must be a Number or String');
}
}
// Technically we should throw if hi or lo is outside int32 range here, but
// it's not worth the effort. Anything past the 32'nd bit is ignored.
// Copy bytes to buffer
var b = this.buffer, o = this.offset;
for (var i = 7; i >= 0; i--) {
b[o+i] = lo & 0xff;
lo = i == 4 ? hi : lo >>> 8;
}
// Restore sign of passed argument
if (negate) this._2scomp();
}
/**
* Convert to a native JS number.
*
* WARNING: Do not expect this value to be accurate to integer precision for
* large (positive or negative) numbers!
*
* @param allowImprecise If true, no check is performed to verify the
* returned value is accurate to integer precision. If false, imprecise
* numbers (very large positive or negative numbers) will be forced to +/-
* Infinity.
*/
public toNumber(allowImprecise:boolean=false):number {
var b = this.buffer, o = this.offset;
// Running sum of octets, doing a 2's complement
var negate = b[o] & 0x80, x = 0, carry = 1;
for (var i = 7, m = 1; i >= 0; i--, m *= 256) {
var v = b[o+i];
// 2's complement for negative numbers
if (negate) {
v = (v ^ 0xff) + carry;
carry = v >> 8;
v = v & 0xff;
}
x += v * m;
}
// Return Infinity if we've lost integer precision
if (!allowImprecise && x >= Int64.MAX_INT) {
return negate ? -Infinity : Infinity;
}
return negate ? -x : x;
}
/**
* Convert to a JS Number. Returns +/-Infinity for values that can't be
* represented to integer precision.
*/
public valueOf():number {
return this.toNumber(false);
}
/**
* Return string value
*
* @param radix Just like Number#toString()'s radix
*/
public toString(radix:number=10):string {
return this.valueOf().toString(radix);
}
/**
* Return a string showing the buffer octets, with MSB on the left.
*
* @param sep separator string. default is '' (empty string)
*/
public toOctetString(sep:string=''):string {
var out = new Array(8);
var b = this.buffer, o = this.offset;
for (var i = 0; i < 8; i++) {
out[i] = Int64._HEX[b[o+i]];
}
return out.join(sep || '');
}
/**
* Returns the int64's 8 bytes in a buffer.
*
* @param {bool} [rawBuffer=false] If no offset and this is true, return the internal buffer. Should only be used if
* you're discarding the Int64 afterwards, as it breaks encapsulation.
*/
public toBuffer(rawBuffer:boolean=false):Array<number> {
if (rawBuffer && this.offset === 0) return this.buffer;
var out = Array.call([], this.buffer);
return out;
}
/**
* Returns a number indicating whether this comes before or after or is the
* same as the other in sort order.
*
* @param {Int64} other Other Int64 to compare.
*/
public compare(other:Int64):number {
// If sign bits differ ...
if ((this.buffer[this.offset] & 0x80) != (other.buffer[other.offset] & 0x80)) {
return other.buffer[other.offset] - this.buffer[this.offset];
}
// otherwise, compare bytes lexicographically
for (var i = 0; i < 8; i++) {
if (this.buffer[this.offset+i] !== other.buffer[other.offset+i]) {
return this.buffer[this.offset+i] - other.buffer[other.offset+i];
}
}
return 0;
}
/**
* Returns a boolean indicating if this integer is equal to other.
*
* @param {Int64} other Other Int64 to compare.
*/
public equals(other:Int64):boolean {
return this.compare(other) === 0;
}
/**
* Pretty output in console.log
*/
public inspect():string {
return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment