Instantly share code, notes, and snippets.

@sirisian /Packet.js
Last active May 30, 2018

Embed
What would you like to do?
Type Proposal Example
/**
* A binary bit-based writer and reader designed for packets.
* @class
*/
export class Packet
{
/**
* The byte buffer for the packet.
* @private
*/
buffer:uint32[];
/**
* The current bit index of the writer and reader.
* @private
*/
bitIndex:uint32;
/**
* The maximum bit index of the writer and reader.
* @private
*/
maximumBitIndex:uint32;
/**
* Error handler.
* @private
*/
error:(message:string);
/**
* Constructor for writing.
* @constructs
*/
constructor()
{
this.bitIndex = 0;
this.maximumBitIndex = 0;
// 1500 bytes - Websocket Header - TCP Header = Default Packet Size
this.buffer = new uint32[1400];
}
/**
* Constructor for reading.
* @constructs
* @param buffer The buffer to read from.
* @param error Error callback.
*/
constructor(buffer:uint8[], error:(message:string))
{
packet.buffer = uint32[](buffer.buffer);
packet.error = (message) =>
{
error(message);
this.error = null;
};
packet.ReadHeader();
}
/**
* Buffer
*/
get Buffer()
{
return UInt8[](this.buffer.buffer, 0, (this.bitIndex + 7) / 8);
}
/**
* BitIndex
*/
get BitIndex()
{
return this.bitIndex;
}
set BitIndex(value)
{
this.bitIndex = value;
}
/**
* MaximumBitIndex
*/
get MaximumBitIndex()
{
return this.maximumBitIndex;
}
set MaximumBitIndex(value)
{
this.maximumBitIndex = value;
}
/**
* headerSize
*/
get headerSize()
{
return 16;
}
/**
* Error
*/
get Error()
{
return this.error == null;
}
/**
* Computes the log2 of a value.
* @param value The value to compute the log2 of.
*/
Log2(value:uint32)
{
if (value == 0)
{
return 0;
}
else
{
var left:uint32 = 0;
var right:uint32 = 32;
for (var i = 0; i < 5; ++i)
{
if (value >> (32 - (left + right) / 2) != 0)
{
// Left
right = (left + right) / 2;
}
else
{
// Right
left = (left + right) / 2;
}
}
return 32 - left;
}
}
/**
* Moves the bit index after the header.
*/
ResetBitIndex()
{
this.bitIndex = this.headerSize;
}
/**
* Returns a string of 0s and 1s representing the bits in the this.buffer.
*/
Trace()
{
var s = '';
for (var copyBits = 0; copyBits < Math.max(this.bitIndex, this.maximumBitIndex); ++copyBits)
{
s += (this.buffer[copyBits / 32] << copyBits % 32 >> 31) == 0 ? '0' : '1';
}
return s;
}
/**
* Reads the length of the packet stored in the header.
*/
ReadHeader()
{
this.maximumBitIndex = this.buffer[0] >> 32 - this.headerSize;
this.bitIndex += this.headerSize;
}
/**
* Writes a 16 bit header.
*/
Begin()
{
this.bitIndex += this.headerSize;
}
/**
* Writes maxBitIndex to the packet header created with Begin.
*/
End()
{
this.maximumBitIndex = this.bitIndex;
this.buffer[0] |= this.maximumBitIndex << 32 - this.headerSize;
}
/**
* Writes an event to the packet.
* @param value The event value.
*/
WriteEvent(value:uint8)
{
this.WriteUInt8(value);
}
/**
* Writes a boolean to the packet.
* @param value The boolean value.
*/
WriteBoolean(value:boolean)
{
if (value)
{
this.buffer[this.bitIndex / 32] |= 1 << 31 - this.bitIndex % 32;
}
this.bitIndex++;
}
/**
* Writes an 8-bit unsigned integer to the packet.
* @param value The 8 bit unsigned integer value.
*/
WriteUInt8(value:uint8)
{
var offset = this.bitIndex % 32;
this.buffer[this.bitIndex / 32] |= value << 24 >> offset;
if (offset > 24)
{
this.buffer[this.bitIndex / 32 + 1] |= value << 56 - offset;
}
this.bitIndex += 8;
}
/**
* Writes an 8-bit signed integer to the packet.
* @param value The 8 bit signed integer value.
*/
WriteInt8(value:int8)
{
this.WriteUInt8(uint8(int8[]([value]).buffer)[0]);
}
/**
* Writes an 16-bit unsigned integer to the packet.
* @param value The 16 bit unsigned integer value.
*/
WriteUInt16(value:uint16)
{
var offset = this.bitIndex % 32;
this.buffer[this.bitIndex / 32] |= value << 16 >> offset;
if (offset > 16)
{
this.buffer[this.bitIndex / 32 + 1] |= value << 48 - offset;
}
this.bitIndex += 16;
}
/**
* Writes an 16-bit signed integer to the packet.
* @param value The 16 bit signed integer value.
*/
WriteInt16(value:int16)
{
this.WriteUInt16(uint16(int16[]([value]).buffer)[0]);
}
/**
* Writes an 32-bit unsigned integer to the packet.
* @param value The 32 bit unsigned integer value.
*/
WriteUInt32(value:uint32)
{
var offset = this.bitIndex % 32;
this.buffer[this.bitIndex / 32] |= value >> offset;
if (offset > 0)
{
this.buffer[this.bitIndex / 32 + 1] |= value << 32 - offset;
}
this.bitIndex += 32;
}
/**
* Writes an 32-bit signed integer to the packet.
* @param value The 32 bit signed integer value.
*/
WriteInt32(value:int32)
{
this.WriteUInt32(uint32(int32[]([value]).buffer)[0]);
}
/**
* Writes an n-bit unsigned integer to the packet.
* @param value The unsigned integer value.
* @param bits The number of bits to use.
*/
WriteUIntN(value:uint32, bits:uint32)
{
var offset = this.bitIndex % 32;
this.buffer[this.bitIndex / 32] |= value << 32 - bits >> offset;
if (offset > 32 - bits)
{
this.buffer[this.bitIndex / 32 + 1] |= value << 64 - bits - offset;
}
this.bitIndex += bits;
}
/**
* Writes an n-bit unsigned integer to the packet.
* @param value The signed integer value.
* @param bits The number of bits to use.
*/
WriteIntN(value:uint32, bits:uint32)
{
this.WriteUIntN(uint32(int32[]([value]).buffer)[0], bits);
}
/**
* Writes an unsigned integer to the packet.
* @param value The unsigned integer value.
* @param maximum The inclusive maximum for the range starting at 0.
*/
WriteUInt(value:uint32, maximum:uint32)
{
var bits = this.Log2(maximum);
value -= minimum;
this.WriteUIntN(value, bits);
}
/**
* Writes an unsigned integer to the packet.
* @param value The unsigned integer value.
* @param minimum The inclusive minimum for the range.
* @param maximum The inclusive maximum for the range.
*/
WriteUInt(value:uint32, minimum:uint32, maximum:uint32)
{
var bits = this.Log2(maximum - minimum);
value -= minimum;
this.WriteUIntN(value, bits);
}
/**
* Writes an signed integer to the packet.
* @param value The signed integer value.
* @param maximum The inclusive maximum for the range starting at 0.
*/
WriteInt(value:int32, maximum:int32)
{
var bits = this.Log2(maximum);
value -= minimum;
this.WriteUIntN(value, bits);
}
/**
* Writes an signed integer to the packet.
* @param value The signed integer value.
* @param minimum The inclusive minimum for the range.
* @param maximum The inclusive maximum for the range.
*/
WriteInt(value:int32, minimum:int32, maximum:int32)
{
var bits = this.Log2(maximum - minimum);
value -= minimum;
this.WriteUIntN(uint32(int32[]([value]).buffer)[0], bits);
}
/**
* Writes an unsigned integer to the packet using a variable width encoding.
* @param value The unsigned integer value.
* @param bits The number of bits to use for the sequence. Choose a bits value that represents the number of bits to hold the average value.
*/
WriteVariableWidthUInt(value:uint32, bits:uint32)
{
var shift = bits;
// Stop when our value can fit inside
for (; shift < 32 && value >= (0x1 << shift); shift += bits)
{
this.WriteBoolean(true); // Write a 1 for a continuation bit signifying one more interval is needed
}
if (shift < 32)
{
this.WriteBoolean(false); // Write a 0 for a continuation bit signifying the end
}
this.WriteUIntN(value, shift > 32 ? 32 : shift);
}
/**
* Writes an signed integer to the packet using a variable width encoding.
* @param {uint32} value The signed integer value.
* @param {uint32} bits The number of bits to use for the sequence. Choose a bits value that represents the number of bits to hold the average value.
*/
WriteVariableWidthInt(value:uint32, bits:uint32)
{
var shift = bits;
// Stop when our value can fit inside
for (; shift < 32 && (value < -(0x1 << (shift - 1)) || value >= 0x1 << (shift - 1)); shift += bits)
{
this.WriteBoolean(true); // Write a 1 for a continuation bit signifying one more interval is needed
}
if (shift < 32)
{
this.WriteBoolean(false); // Write a 0 for a continuation bit signifying the end
}
this.WriteIntN(value, shift > 32 ? 32 : shift);
}
/**
* Writes a 32 bit float to the packet.
* @param value The floating point value.
*/
WriteFloat32(value:float32)
{
this.WriteUInt32(uint32(float32[]([value]).buffer)[0]);
}
/**
* Writes a 32 bit float to the packet.
* @param value The floating point value.
* @param maximum The inclusive maximum for the range starting at 0.
* @param bits The number of bits to use.
*/
WriteFloat32(value:float32, maximum:float32, bits:uint32)
{
this.WriteUIntN(Math.round(value / maximum * ((0x1 << bits) - 1)), bits);
}
/**
* Writes a 32 bit float to the packet.
* @param value The floating point value.
* @param minimum The inclusive minimum for the range.
* @param maximum The inclusive maximum for the range.
* @param bits The number of bits to use.
*/
WriteFloat32(value:float32, minimum:float32, maximum:float32, bits:uint32)
{
if (minimum < 0 && maximum > 0)
{
this.WriteUIntN(value == 0 ? 0 : Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 2)) + 1, bits);
}
else
{
this.WriteUIntN(Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 1)), bits);
}
}
/**
* Writes a 64 bit float to the packet.
* @param value The floating point value.
*/
WriteFloat64(value:float32)
{
var float64UInt32 = uint32(float64[]([value]).buffer);
this.WriteUInt32(float64UInt32[0]);
this.WriteUInt32(float64UInt32[1]);
}
/**
* Writes a 64 bit float to the packet.
* @param value The floating point value.
* @param maximum The inclusive maximum for the range starting at 0.
* @param bits The number of bits to use.
*/
WriteFloat64(value:float32, maximum:float32, bits:uint32)
{
this.WriteUIntN(Math.round(value / maximum * ((0x1 << bits) - 1)), bits);
}
/**
* Writes a 64 bit float to the packet.
* @param value The floating point value.
* @param minimum The inclusive minimum for the range.
* @param maximum The inclusive maximum for the range.
* @param bits The number of bits to use.
*/
WriteFloat64(value:float32, minimum:float32, maximum:float32, bits:uint32)
{
if (minimum < 0 && maximum > 0)
{
this.WriteUIntN(value == 0 ? 0 : Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 2)) + 1, bits);
}
else
{
this.WriteUIntN(Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 1)), bits);
}
}
/**
* Writes a string to the packet.
* @param value The string value.
*/
WriteString(value:string)
{
this.WriteUInt16(value.length);
for (var index = 0; index < value.length; ++index)
{
this.WriteUIntN(value.charCodeAt(index), 7);
}
}
/**
* Writes a packet to the packet.
* @param value The packet.
*/
WritePacket(value:Packet)
{
value.BitIndex = 0;
for (var copyBits = 0; copyBits < value.MaximumBitIndex; copyBits += 32)
{
var bits = value.MaximumBitIndex - copyBits > 32 ? 32 : value.MaximumBitIndex - copyBits;
// Read n-bits from value.
var valueUIntN:uint32 = 0;
var offset = value.bitIndex % 32;
valueUIntN |= value.buffer[value.bitIndex / 32] << offset >> 32 - bits;
if (offset > 32 - bits)
{
valueUIntN |= value.buffer[value.bitIndex / 32 + 1] >> 64 - bits - offset;
}
value.bitIndex += bits;
// Write n-bits to the buffer.
offset = this.bitIndex % 32;
this.buffer[this.bitIndex / 32] |= valueUIntN << 32 - bits >> offset;
if (offset > 32 - bits)
{
this.buffer[this.bitIndex / 32 + 1] |= valueUIntN << 64 - bits - offset;
}
this.bitIndex += bits;
}
}
/**
* Reads an event from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadEvent(callback:(uint8, Packet):void)
{
if (this.error == null) return;
if (this.bitIndex + 8 > this.maximumBitIndex)
{
this.error('Event expected');
}
this.ReadUInt8(callback);
}
/**
* Reads a 1-bit boolean from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadBoolean(callback:(boolean, Packet))
{
if (this.error == null) return;
var value = false;
if (this.bitIndex + 1 > this.maximumBitIndex)
{
this.error('Boolean expected');
}
else
{
value = (this.buffer[this.bitIndex / 32] << this.bitIndex % 32 >> 31) == 1;
this.bitIndex++;
callback(value, this);
}
}
/**
* Reads an 8-bit unsigned integer from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadUInt8(callback:(uint8, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 8 > this.maximumBitIndex)
{
this.error('uint8 expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset >> 24;
if (offset > 24)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 56 - offset;
}
this.bitIndex += 8;
callback(value);
}
}
/**
* Reads an 8-bit signed integer from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadInt8(callback:(int8, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 8 > this.maximumBitIndex)
{
this.error('int8 expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset >> 24;
if (offset > 24)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 56 - offset;
}
this.bitIndex += 8;
callback(value, this);
}
}
/**
* Reads a 16-bit unsigned integer from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadUInt16(callback:(uint16, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 16 > this.maximumBitIndex)
{
this.error('uint16 expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset >> 16;
if (offset > 16)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 48 - offset;
}
this.bitIndex += 16;
callback(value, this);
}
}
/**
* Reads a 16-bit signed integer from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadInt16(callback:(int16, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 16 > this.maximumBitIndex)
{
this.error('int16 expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset >> 16;
if (offset > 16)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 48 - offset;
}
this.bitIndex += 16;
callback(value, this);
}
}
/**
* Reads a 32-bit unsigned integer from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadUInt32(callback:(uint32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 32 > this.maximumBitIndex)
{
this.error('uint32 expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset;
if (offset > 0)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 32 - offset;
}
this.bitIndex += 32;
callback(value, this);
}
}
/**
* Reads a 32-bit signed integer from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadInt32(callback:(int32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 32 > this.maximumBitIndex)
{
this.error('int32 expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset;
if (offset > 0)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 32 - offset;
}
this.bitIndex += 32;
callback(value, this);
}
}
/**
* Reads an n-bit unsigned integer from the packet.
* @param bits The number of bits used.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadUIntN(bits:uint32, callback:(uint32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('uint' + bits + ' expected');
}
else
{
var value = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset >> 32 - bits;
if (offset > 32 - bits)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 64 - bits - offset;
}
this.bitIndex += bits;
callback(value, this);
}
}
/**
* Reads an n-bit signed integer from the packet.
* @param bits The number of bits used.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadIntN(bits:uint32, callback:(int32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('int' + bits + ' expected');
}
else
{
var value:uint32 = 0;
var offset = this.bitIndex % 32;
value |= this.buffer[this.bitIndex / 32] << offset >> 32 - bits;
if (offset > 32 - bits)
{
value |= this.buffer[this.bitIndex / 32 + 1] >> 64 - bits - offset;
}
this.bitIndex += bits;
callback(value, this);
}
}
/**
* Reads an unsigned integer from the packet.
* @param maximum The inclusive maximum used for the range starting at 0.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadUInt(maximum:uint32, callback:(uint32, Packet))
{
if (this.error == null) return;
var bits = this.Log2(maximum);
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('uint' + bits + ' with range [' + minimum + ', ' + maximum + '] expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
callback(value + minimum, this);
});
}
}
/**
* Reads an unsigned integer from the packet.
* @param minimum The inclusive minimum used for the range.
* @param maximum The inclusive maximum used for the range.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadUInt(minimum:uint32, maximum:uint32, callback:(uint32, Packet))
{
if (this.error == null) return;
var bits = this.Log2(maximum - minimum);
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('uint' + bits + ' with range [' + minimum + ', ' + maximum + '] expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
callback(value + minimum, this);
});
}
}
/**
* Reads a signed integer from the packet.
* @param maximum The inclusive maximum used for the range starting at 0.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadInt(maximum:int32, callback:(int32, Packet))
{
if (this.error == null) return;
var bits = this.Log2(maximum);
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('int' + bits + ' with range [' + minimum + ', ' + maximum + '] expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
callback(value + minimum, this);
});
}
}
/**
* Reads a signed integer from the packet.
* @param minimum The inclusive minimum used for the range.
* @param maximum The inclusive maximum used for the range.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadInt(minimum:int32, maximum:int32, callback:(int32, Packet))
{
if (this.error == null) return;
var bits = this.Log2(maximum - minimum);
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('int' + bits + ' with range [' + minimum + ', ' + maximum + '] expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
callback(value + minimum, this);
});
}
}
/**
* Reads an unsigned integer from the packet that was written using an n-bit variable width encoding.
* @param bits The number of bits used for the sequence.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadVariableWidthUInt(bits:uint32, callback:(uint32, Packet))
{
if (this.error == null) return;
var bitCount = 0;
var continuationBitValue;
do
{
continuationBitValue = false;
this.ReadBoolean((continuationBit) =>
{
continuationBitValue = continuationBit;
bitCount += bits;
});
}
while (continuationBitValue && bitCount < 32);
if (bitCount > 32)
{
this.error('uint expected');
}
else
{
this.ReadUIntN(bitCount, callback);
}
}
/**
* Read a signed integer from the packet that was written using an n-bit variable width encoding.
* @param bits The number of bits used for the sequence.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadVariableWidthInt(bits, callback:(int32, Packet))
{
if (this.error == null) return;
var bitCount = 0;
var continuationBitValue;
do
{
continuationBitValue = false;
this.ReadBoolean((continuationBit) =>
{
continuationBitValue = continuationBit;
bitCount += bits;
});
}
while (continuationBitValue && bitCount < 32);
if (bitCount > 32)
{
this.error('int expected');
}
else
{
this.ReadIntN(bitCount, callback);
}
}
/**
* Reads a 32-bit floating point from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadFloat32(callback:(float32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 32 > this.maximumBitIndex)
{
this.error('float32 expected');
}
else
{
var arrayBuffer = new ArrayBuffer(4);
var float32 = new Float32Array(arrayBuffer);
var float32UInt32 = new Uint32Array(arrayBuffer);
this.ReadUInt32((value) =>
{
float32UInt32[0] = value;
});
callback(float32[0], this);
}
}
/**
* Reads a 32-bit floating point from the packet.
* @param maximum The inclusive maximum used for the range starting at 0.
* @param bits The number of bits used.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadFloat32(maximum:float32, bits:uint32, callback:(float32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('float32 expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
callback(value / ((0x1 << bits) - 1) * maximum, this);
});
}
}
/**
* Reads a 32-bit floating point from the packet.
* @param minimum The inclusive minimum used for the range.
* @param maximum The inclusive maximum used for the range.
* @param bits The number of bits used.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadFloat32(minimum:float32, maximum:float32, bits:uint32, callback:(float32, Packet))
{
if (this.error == null) return;
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('float32 expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
if (minimum < 0 && maximum > 0)
{
callback(value == 0 ? 0 : (value - 1) / ((0x1 << bits) - 2) * (maximum - minimum) + minimum, this);
}
else
{
callback(value / ((0x1 << bits) - 1) * (maximum - minimum) + minimum, this);
}
});
}
}
/**
* Reads a 64-bit floating point from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadFloat64(callback:(float64, Packet))
{
if (this.error == null) return;
if (this.bitIndex + 64 > this.maximumBitIndex)
{
this.error('float64 expected');
}
else
{
var arrayBuffer = new ArrayBuffer(8);
var float64 = new Float64Array(arrayBuffer);
var float64UInt32 = new Uint32Array(arrayBuffer);
this.ReadUInt32((value) =>
{
float64UInt32[0] = value;
});
this.ReadUInt32((value) =>
{
float64UInt32[1] = value;
});
callback(float64[0], this);
}
}
/**
* Reads a 64-bit floating point from the packet.
* @param maximum The inclusive maximum used for the range starting at 0.
* @param bits The number of bits used.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadFloat64(maximum:float64, bits:uint32, callback:(float64, Packet))
{
if (this.error == null) return;
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('float64 expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
callback(value / ((0x1 << bits) - 1) * maximum, this);
});
}
}
/**
* Reads a 64-bit floating point from the packet.
* @param minimum The inclusive minimum used for the range.
* @param maximum The inclusive maximum used for the range.
* @param bits The number of bits used.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadFloat64(minimum:float64, maximum:float64, bits:uint32 callback:(float64, Packet))
{
if (this.error == null) return;
if (this.bitIndex + bits > this.maximumBitIndex)
{
this.error('float64 expected');
}
else
{
this.ReadUIntN(bits, (value) =>
{
if (minimum < 0 && maximum > 0)
{
callback(value == 0 ? 0 : (value - 1) / ((0x1 << bits) - 2) * (maximum - minimum) + minimum, this);
}
else
{
callback(value / ((0x1 << bits) - 1) * (maximum - minimum) + minimum, this);
}
});
}
}
/**
* Reads a string from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadString(callback:(string, Packet))
{
var value = '';
var packet = this;
this.ReadUInt16((length) =>
{
for (var index = 0; index < length; ++index)
{
packet.ReadUIntN(7, (charCode) =>
{
value += string.fromCharCode(charCode);
});
}
callback(value, this);
});
}
/**
* Reads a packet from the packet.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadPacket(callback:(Packet, Packet))
{
if (this.error == null) return;
this.ReadUIntN(this.headerSize, (maximumBitIndex, packet) =>
{
if (packet.bitIndex + maximumBitIndex - packet.headerSize > packet.maximumBitIndex)
{
packet.error('Packet expected');
}
else
{
var bitIndex = packet.BitIndex;
var value = Packet.Create(packet.Buffer.buffer, packet.error);
value.BitIndex = packet.bitIndex;
value.MaximumBitIndex = packet.bitIndex + maximumBitIndex - packet.headerSize;
packet.bitIndex += maximumBitIndex - packet.headerSize;
callback(value, packet);
}
});
}
/**
* Reads a block of data with one callback.
* @param readOperations A datatype string or array starting with a datatype string followed by arguments. As an example to use ReadUInt8 you would use the datatype string 'UInt8'. For ReadUIntN
* @param callback A callback with the result. Only executes if the read succeeds.
*/
Read(...readOperations, callback:(..., Packet))
{
var values = [];
for (var i = 0; i < readOperations.length; ++i)
{
if (typeof readOperations[i] == 'string')
{
this['Read' + readOperations[i]]((value) =>
{
values.push(value);
});
}
else
{
readOperations[i].push((value) =>
{
values.push(value);
});
this['Read' + readOperations[i].shift()].apply(this, readOperations[i]);
}
}
values.push(this);
callback.apply(this, values);
}
/**
* Reads a boolean and executes read operations on true.
* @param trueReadOperations A datatype string or array starting with a datatype string followed by arguments. As an example to use ReadUInt8 you would use the datatype string 'UInt8'.
* @param callback A callback with the result. Only executes if the read succeeds.
*/
ReadConditional(...trueReadOperations, trueCallback:(..., Packet))
{
this.ReadBoolean((conditional, packet) =>
{
if (conditional)
{
var values = [];
for (var i = 0; i < trueReadOperations.length; ++i)
{
if (typeof trueReadOperations[i] == 'string')
{
packet['Read' + trueReadOperations[i]]((value) =>
{
values.push(value);
});
}
else
{
trueReadOperations[i].push((value) =>
{
values.push(value);
});
packet['Read' + trueReadOperations[i].shift()].apply(packet, trueReadOperations[i]);
}
}
values.push(packet);
trueCallback.apply(arguments.callee.caller, values);
}
});
}
/**
* Reads a boolean and executes read operations on true or false.
* @param trueReadOperations A datatype string or array starting with a datatype string followed by arguments. As an example to use ReadUInt8 you would use the datatype string 'UInt8'.
* @param trueCallback A callback with the result. Only executes if the read succeeds.
* @param falseReadOperations A datatype string or array starting with a datatype string followed by arguments. As an example to use ReadUInt8 you would use the datatype string 'UInt8'.
* @param falseCallback A callback with the result. Only executes if the read succeeds.
*/
ReadConditional(...trueReadOperations, trueCallback:(..., Packet), ...falseReadOperations, falseCallback:(..., Packet))
{
this.ReadBoolean((conditional, packet) =>
{
if (conditional)
{
var values = [];
for (var i = 0; i < trueReadOperations.length; ++i)
{
if (typeof trueReadOperations[i] == 'string')
{
packet['Read' + trueReadOperations[i]]((value) =>
{
values.push(value);
});
}
else
{
trueReadOperations[i].push((value) =>
{
values.push(value);
});
packet['Read' + trueReadOperations[i].shift()].apply(packet, trueReadOperations[i]);
}
}
values.push(packet);
trueCallback.apply(packet, values);
}
else
{
var values = [];
for (var i = 0; i < falseReadOperations.length; ++i)
{
if (typeof falseReadOperations[i] == 'string')
{
packet['Read' + falseReadOperations[i]]((value) =>
{
values.push(value);
});
}
else
{
falseReadOperations[i].push((value) =>
{
values.push(value);
});
packet['Read' + falseReadOperations[i].shift()].apply(packet, falseReadOperations[i]);
}
}
values.push(packet);
falseCallback.apply(packet, values);
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment