Created
October 4, 2014 04:41
-
-
Save sirisian/7f4cb0faedee4d17d65b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <var>A binary bit-based writer and reader used for packets.</var> | |
var Packet = | |
{ | |
Create: function() | |
{ | |
/// <signature> | |
/// <summary>Constructor.</summary> | |
/// <returns type="Packet" /> | |
/// </signature> | |
/// <signature> | |
/// <summary>Constructor.</summary> | |
/// <param name="codecState" type="CodecState">The codec state to use for coding.</param> | |
/// <returns type="Packet" /> | |
/// </signature> | |
/// <signature> | |
/// <summary>Constructor.</summary> | |
/// <param name="buffer" type="ArrayBuffer">The buffer to read from.</param> | |
/// <param name="error" type="function">Error callback.</param> | |
/// <returns type="Packet" /> | |
/// </signature> | |
/// <signature> | |
/// <summary>Constructor.</summary> | |
/// <param name="buffer" type="ArrayBuffer">The buffer to read from.</param> | |
/// <param name="codecState" type="CodecState">The codec state to use for coding.</param> | |
/// <param name="error" type="function">Error callback.</param> | |
/// <returns type="Packet" /> | |
/// </signature> | |
var packet = Object.create(this, | |
{ | |
internalBuffer: | |
{ | |
value: null, | |
writable: true | |
}, | |
buffer: | |
{ | |
value: null, | |
writable: true | |
}, | |
/// <field type="UInt8Array">The byte buffer for the packet.</field> | |
Buffer: | |
{ | |
get: function() | |
{ | |
return new Uint8Array(this.internalBuffer, 0, (this.bitIndex + 7) / 8 >> 0); | |
} | |
}, | |
bitIndex: | |
{ | |
value: 0, | |
writable: true | |
}, | |
/// <field type="UInt">The current bit index of the writer and reader.</field> | |
BitIndex: | |
{ | |
get: function() | |
{ | |
return this.bitIndex; | |
}, | |
set: function(value) | |
{ | |
this.bitIndex = value; | |
} | |
}, | |
maximumBitIndex: | |
{ | |
value: 0, | |
writable: true | |
}, | |
/// <field type="UInt">The maximum bit index of the writer and reader.</field> | |
MaximumBitIndex: | |
{ | |
get: function() | |
{ | |
return this.maximumBitIndex; | |
}, | |
set: function(value) | |
{ | |
this.maximumBitIndex = value; | |
} | |
}, | |
headerSize: | |
{ | |
get: function() | |
{ | |
return 16; | |
} | |
}, | |
error: | |
{ | |
value: null, | |
writable: true | |
}, | |
Error: | |
{ | |
get: function() | |
{ | |
return this.error == null; | |
} | |
}, | |
CodecState: | |
{ | |
value: null, | |
writable: true | |
} | |
}); | |
if (arguments.length == 0) | |
{ | |
// 1500 bytes - Websocket Header - TCP Header = Default Packet Size | |
packet.internalBuffer = new ArrayBuffer(1400); | |
packet.buffer = new Uint32Array(packet.internalBuffer); | |
} | |
else if (arguments.length == 1) | |
{ | |
// 1500 bytes - Websocket Header - TCP Header = Default Packet Size | |
packet.internalBuffer = new ArrayBuffer(1400); | |
packet.buffer = new Uint32Array(packet.internalBuffer); | |
packet.CodecState = arguments[0]; | |
} | |
else if (arguments.length == 2) | |
{ | |
packet.internalBuffer = arguments[0]; | |
packet.buffer = new Uint32Array(packet.internalBuffer); | |
var callback = arguments[1]; | |
packet.error = function(message) | |
{ | |
callback(message); | |
this.error = null; | |
}; | |
packet.ReadHeader(); | |
} | |
else | |
{ | |
packet.internalBuffer = arguments[0]; | |
packet.buffer = new Uint32Array(packet.internalBuffer); | |
packet.CodecState = arguments[1]; | |
var callback = arguments[2]; | |
packet.error = function(message) | |
{ | |
callback(message); | |
this.error = null; | |
}; | |
packet.ReadHeader(); | |
} | |
return packet; | |
}, | |
Log2: function(value) | |
{ | |
/// <signature> | |
/// <summary>Computes the log2 of a value.</summary> | |
/// <param name="value" type="UInt">The value to compute the log2 of.</param> | |
/// <returns type="UInt" /> | |
/// </signature> | |
if (value == 0) | |
{ | |
return 0; | |
} | |
else | |
{ | |
var left = 0; | |
var right = 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; | |
} | |
}, | |
ResetBitIndex: function() | |
{ | |
/// <signature> | |
/// <summary>Moves the bit index after the header.</summary> | |
/// </signature> | |
this.bitIndex = this.headerSize; | |
}, | |
Trace: function() | |
{ | |
/// <signature> | |
/// <summary>Returns a string of 0s and 1s representing the bits in the this.buffer.</summary> | |
/// <returns type="string" /> | |
/// </signature> | |
var s = ""; | |
for (var copyBits = 0; copyBits < Math.max(this.bitIndex, this.maximumBitIndex); ++copyBits) | |
{ | |
s += (this.buffer[copyBits / 32 >> 0] << copyBits % 32 >>> 31) == 0 ? "0" : "1"; | |
} | |
return s; | |
}, | |
ReadHeader: function() | |
{ | |
/// <signature> | |
/// <summary>Reads the length of the packet stored in the header.</summary> | |
/// </signature> | |
this.maximumBitIndex = this.buffer[0] >>> 32 - this.headerSize; | |
this.bitIndex += this.headerSize; | |
}, | |
Begin: function() | |
{ | |
/// <signature> | |
/// <summary>Writes a 16 bit header.</summary> | |
/// </signature> | |
this.bitIndex += this.headerSize; | |
}, | |
End: function() | |
{ | |
/// <signature> | |
/// <summary>Writes maxBitIndex to the packet header created with BeginPacket.</summary> | |
/// </signature> | |
this.maximumBitIndex = this.bitIndex; | |
this.buffer[0] |= this.maximumBitIndex << 32 - this.headerSize; | |
}, | |
WriteEvent: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an event to the packet.</summary> | |
/// <param name="value" type="UInt8">The event value.</param> | |
/// </signature> | |
this.WriteUInt8(value); | |
}, | |
WriteBool: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes a bool to the packet.</summary> | |
/// <param name="value" type="bool">The bool value.</param> | |
/// </signature> | |
if (value) | |
{ | |
this.buffer[this.bitIndex / 32 >> 0] |= 1 << 31 - this.bitIndex % 32; | |
} | |
this.bitIndex++; | |
}, | |
WriteUInt8: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an 8-bit unsigned integer to the packet.</summary> | |
/// <param name="value" type="UInt8">The 8 bit unsigned integer value.</param> | |
/// </signature> | |
var offset = this.bitIndex % 32; | |
this.buffer[this.bitIndex / 32 >> 0] |= value << 24 >>> offset; | |
if (offset > 24) | |
{ | |
this.buffer[(this.bitIndex / 32 >> 0) + 1] |= value << 56 - offset; | |
} | |
this.bitIndex += 8; | |
}, | |
WriteInt8: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an 8-bit signed integer to the packet.</summary> | |
/// <param name="value" type="Int8">The 8 bit signed integer value.</param> | |
/// </signature> | |
this.WriteUInt8(value); | |
}, | |
WriteUInt16: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an 16-bit unsigned integer to the packet.</summary> | |
/// <param name="value" type="UInt16">The 16 bit unsigned integer value.</param> | |
/// </signature> | |
var offset = this.bitIndex % 32; | |
this.buffer[this.bitIndex / 32 >> 0] |= value << 16 >>> offset; | |
if (offset > 16) | |
{ | |
this.buffer[(this.bitIndex / 32 >> 0) + 1] |= value << 48 - offset; | |
} | |
this.bitIndex += 16; | |
}, | |
WriteInt16: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an 16-bit signed integer to the packet.</summary> | |
/// <param name="value" type="Int16">The 16 bit signed integer value.</param> | |
/// </signature> | |
this.WriteUInt16(value); | |
}, | |
WriteUInt32: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an 32-bit unsigned integer to the packet.</summary> | |
/// <param name="value" type="UInt32">The 32 bit unsigned integer value.</param> | |
/// </signature> | |
var offset = this.bitIndex % 32; | |
this.buffer[this.bitIndex / 32 >> 0] |= value >>> offset; | |
if (offset > 0) | |
{ | |
this.buffer[(this.bitIndex / 32 >> 0) + 1] |= value << 32 - offset; | |
} | |
this.bitIndex += 32; | |
}, | |
WriteInt32: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an 32-bit signed integer to the packet.</summary> | |
/// <param name="value" type="Int32">The 32 bit signed integer value.</param> | |
/// </signature> | |
this.WriteUInt32(value); | |
}, | |
WriteUIntN: function(value, bits) | |
{ | |
/// <signature> | |
/// <summary>Writes an n-bit unsigned integer to the packet.</summary> | |
/// <param name="value" type="UInt">The unsigned integer value.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// </signature> | |
var offset = this.bitIndex % 32; | |
this.buffer[this.bitIndex / 32 >> 0] |= value << 32 - bits >>> offset; | |
if (offset > 32 - bits) | |
{ | |
this.buffer[(this.bitIndex / 32 >> 0) + 1] |= value << 64 - bits - offset; | |
} | |
this.bitIndex += bits; | |
}, | |
WriteIntN: function(value, bits) | |
{ | |
/// <signature> | |
/// <summary>Writes an n-bit unsigned integer to the packet.</summary> | |
/// <param name="value" type="Int">The signed integer value.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// </signature> | |
this.WriteUIntN(value, bits); | |
}, | |
WriteUInt: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes an unsigned integer to the packet.</summary> | |
/// <param name="value" type="UInt">The unsigned integer value.</param> | |
/// <param name="maximum" type="UInt">The inclusive unsigned integer maximum.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes an unsigned integer to the packet.</summary> | |
/// <param name="value" type="UInt">The unsigned integer value.</param> | |
/// <param name="minimum" type="UInt">The inclusive unsigned integer minimum.</param> | |
/// <param name="maximum" type="UInt">The inclusive unsigned integer maximum.</param> | |
/// </signature> | |
var minimum, maximum; | |
if (arguments.length == 2) | |
{ | |
minimum = 0; | |
maximum = arguments[1]; | |
} | |
else | |
{ | |
minimum = arguments[1]; | |
maximum = arguments[2]; | |
} | |
var bits = this.Log2(maximum - minimum); | |
value -= minimum; | |
this.WriteUIntN(value, bits); | |
}, | |
WriteInt: function() | |
{ | |
/// <signature> | |
/// <summary>Writes a signed integer to the packet.</summary> | |
/// <param name="value" type="Int">The signed integer value.</param> | |
/// <param name="maximum" type="Int">The inclusive signed integer maximum.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a signed integer to the packet.</summary> | |
/// <param name="value" type="Int">The signed integer value.</param> | |
/// <param name="minimum" type="Int">The inclusive signed integer minimum.</param> | |
/// <param name="maximum" type="Int">The inclusive signed integer maximum.</param> | |
/// </signature> | |
this.WriteUInt.apply(this, arguments); | |
}, | |
WriteVariableWidthUInt: function(value, bits) | |
{ | |
/// <signature> | |
/// <summary>Writes an unsigned integer to the packet using a variable width encoding.</summary> | |
/// <param name="value" type="UInt">The unsigned integer value.</param> | |
/// <param name="bits" type="UInt">The number of bits to use for the sequence. Choose a bits value that represents the number of bits to hold the average value.</param> | |
/// </signature> | |
var shift = bits; | |
// Stop when our value can fit inside | |
for (; shift < 32 && value >= (0x1 << shift); shift += bits) | |
{ | |
this.WriteBool(true); // Write a 1 for a continuation bit signifying one more interval is needed | |
} | |
if (shift < 32) | |
{ | |
this.WriteBool(false); // Write a 0 for a continuation bit signifying the end | |
} | |
this.WriteUIntN(value, shift > 32 ? 32 : shift); | |
}, | |
WriteVariableWidthInt: function(value, bits) | |
{ | |
/// <signature> | |
/// <summary>Writes an signed integer to the packet using a variable width encoding.</summary> | |
/// <param name="value" type="Int">The signed integer value.</param> | |
/// <param name="bits" type="UInt">The number of bits to use for the sequence. Choose a bits value that represents the number of bits to hold the average value.</param> | |
/// </signature> | |
var shift = bits; | |
// Stop when our value can fit inside | |
for (; shift < 32 && (value < -(0x1 << (shift - 1)) || value >= 0x1 << (shift - 1)); shift += bits) | |
{ | |
this.WriteBool(true); // Write a 1 for a continuation bit signifying one more interval is needed | |
} | |
if (shift < 32) | |
{ | |
this.WriteBool(false); // Write a 0 for a continuation bit signifying the end | |
} | |
this.WriteIntN(value, shift > 32 ? 32 : shift); | |
}, | |
WriteFloat32: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes a 32 bit float to the packet.</summary> | |
/// <param name="value" type="Float32">The floating point value.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a 32 bit float to the packet.</summary> | |
/// <param name="value" type="Float32">The floating point value.</param> | |
/// <param name="maximum" type="Float32">The maximum value for the range starting at 0.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a 32 bit float to the packet.</summary> | |
/// <param name="value" type="Float32">The floating point value.</param> | |
/// <param name="minimum" type="Float32">The inclusive floating point minimum.</param> | |
/// <param name="maximum" type="Float32">The inclusive floating point maximum.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// </signature> | |
if (arguments.length == 1) | |
{ | |
var arrayBuffer = new ArrayBuffer(4); | |
var float32 = new Float32Array(arrayBuffer); | |
var float32UInt32 = new Uint32Array(arrayBuffer); | |
float32[0] = value; | |
this.WriteUInt32(float32UInt32[0]); | |
} | |
else | |
{ | |
var minimum, maximum, bits; | |
if (arguments.length == 3) | |
{ | |
minimum = 0; | |
maximum = arguments[1]; | |
bits = arguments[2]; | |
} | |
else | |
{ | |
minimum = arguments[1]; | |
maximum = arguments[2]; | |
bits = arguments[3]; | |
} | |
var uValue; | |
if (minimum < 0 && maximum > 0) | |
{ | |
uValue = value == 0 ? 0 : Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 2)) + 1; | |
} | |
else | |
{ | |
uValue = Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 1)); | |
} | |
this.WriteUIntN(uValue, bits); | |
} | |
}, | |
WriteFloat64: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes a 64 bit float to the packet.</summary> | |
/// <param name="value" type="Float64">The floating point value.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a 64 bit float to the packet.</summary> | |
/// <param name="value" type="Float64">The floating point value.</param> | |
/// <param name="maximum" type="Float64">The maximum value for the range starting at 0.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a 64 bit float to the packet.</summary> | |
/// <param name="value" type="Float64">The floating point value.</param> | |
/// <param name="minimum" type="Float64">The inclusive floating point minimum.</param> | |
/// <param name="maximum" type="Float64">The inclusive floating point maximum.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// </signature> | |
if (arguments.length == 1) | |
{ | |
var arrayBuffer = new ArrayBuffer(8); | |
var float64 = new Float64Array(arrayBuffer); | |
var float64UInt32 = new Uint32Array(arrayBuffer); | |
float64[0] = value; | |
this.WriteUInt32(float64UInt32[0]); | |
this.WriteUInt32(float64UInt32[1]); | |
} | |
else | |
{ | |
var minimum, maximum, bits; | |
if (arguments.length == 3) | |
{ | |
minimum = 0; | |
maximum = arguments[1]; | |
bits = arguments[2]; | |
} | |
else | |
{ | |
minimum = arguments[1]; | |
maximum = arguments[2]; | |
bits = arguments[3]; | |
} | |
var uValue; | |
if (minimum < 0 && maximum > 0) | |
{ | |
uValue = value == 0 ? 0 : Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 2)) + 1; | |
} | |
else | |
{ | |
uValue = Math.round((value - minimum) / (maximum - minimum) * ((0x1 << bits) - 1)); | |
} | |
this.WriteUIntN(uValue, bits); | |
} | |
}, | |
WriteString: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes a string to the packet.</summary> | |
/// <param name="value" type="string">The string value.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a string to the packet using a codec to encode the string.</summary> | |
/// <param name="value" type="string">The string value.</param> | |
/// <param name="codec" type="UInt">The codec identifier for the binded codec state.</param> | |
/// </signature> | |
var codec = null; | |
if (arguments.length == 2) | |
{ | |
codec = arguments[1]; | |
} | |
this.WriteUInt16(value.length); | |
if (this.CodecState != undefined && codec != null) | |
{ | |
var codecToUse = this.CodecState.Get(codec); | |
for (var index = 0; index < value.length; ++index) | |
{ | |
codecToUse.Encode(this, value.charAt(index)); | |
} | |
} | |
else | |
{ | |
for (var index = 0; index < value.length; ++index) | |
{ | |
this.WriteUIntN(value.charCodeAt(index), 7); | |
} | |
} | |
}, | |
WritePacket: function(value) | |
{ | |
/// <signature> | |
/// <summary>Writes a packet to the packet.</summary> | |
/// <param name="value" type="Packet">The packet.</param> | |
/// </signature> | |
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 = 0; | |
var offset = value.bitIndex % 32; | |
valueUIntN |= value.buffer[value.bitIndex / 32 >> 0] << offset >>> 32 - bits; | |
if (offset > 32 - bits) | |
{ | |
valueUIntN |= value.buffer[(value.bitIndex / 32 >> 0) + 1] >>> 64 - bits - offset; | |
} | |
value.bitIndex += bits; | |
valueUIntN >>>= 0; | |
// Write n-bits to the buffer. | |
offset = this.bitIndex % 32; | |
this.buffer[this.bitIndex / 32 >> 0] |= valueUIntN << 32 - bits >>> offset; | |
if (offset > 32 - bits) | |
{ | |
this.buffer[(this.bitIndex / 32 >> 0) + 1] |= valueUIntN << 64 - bits - offset; | |
} | |
this.bitIndex += bits; | |
} | |
}, | |
ReadEvent: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads an event from the packet.</summary> | |
/// <param name="callback" type="function(UInt8, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
if (this.bitIndex + 8 > this.maximumBitIndex) | |
{ | |
this.error("Event expected"); | |
} | |
this.ReadUInt8(callback); | |
}, | |
ReadBool: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a bool from the packet.</summary> | |
/// <param name="callback" type="function(bool, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
var value = false; | |
if (this.bitIndex + 1 > this.maximumBitIndex) | |
{ | |
this.error("Bool expected"); | |
} | |
else | |
{ | |
value = (this.buffer[this.bitIndex / 32 >> 0] << this.bitIndex % 32 >>> 31) == 1; | |
this.bitIndex++; | |
callback(value, this); | |
} | |
}, | |
ReadUInt8: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads an 8-bit unsigned integer from the packet.</summary> | |
/// <param name="callback" type="function(UInt8, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset >>> 24; | |
if (offset > 24) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 56 - offset; | |
} | |
this.bitIndex += 8; | |
callback(value); | |
} | |
}, | |
ReadInt8: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads an 8-bit signed integer from the packet.</summary> | |
/// <param name="callback" type="function(Int8, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset >> 24; | |
if (offset > 24) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 56 - offset; | |
} | |
this.bitIndex += 8; | |
callback(value, this); | |
} | |
}, | |
ReadUInt16: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a 16-bit unsigned integer from the packet.</summary> | |
/// <param name="callback" type="function(UInt16, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset >>> 16; | |
if (offset > 16) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 48 - offset; | |
} | |
this.bitIndex += 16; | |
callback(value, this); | |
} | |
}, | |
ReadInt16: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a 16-bit signed integer from the packet.</summary> | |
/// <param name="callback" type="function(Int16, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset >> 16; | |
if (offset > 16) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 48 - offset; | |
} | |
this.bitIndex += 16; | |
callback(value, this); | |
} | |
}, | |
ReadUInt32: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a 32-bit unsigned integer from the packet.</summary> | |
/// <param name="callback" type="function(UInt32, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset; | |
if (offset > 0) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 32 - offset; | |
} | |
this.bitIndex += 32; | |
callback(value >>> 0, this); | |
} | |
}, | |
ReadInt32: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a 32-bit signed integer from the packet.</summary> | |
/// <param name="callback" type="function(Int32, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset; | |
if (offset > 0) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 32 - offset; | |
} | |
this.bitIndex += 32; | |
callback(value, this); | |
} | |
}, | |
ReadUIntN: function(bits, callback) | |
{ | |
/// <signature> | |
/// <summary>Reads an n-bit unsigned integer from the packet.</summary> | |
/// <param name="bits" type="UInt">The number of bits used.</param> | |
/// <param name="callback" type="function(UInt, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
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 >> 0] << offset >>> 32 - bits; | |
if (offset > 32 - bits) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 64 - bits - offset; | |
} | |
this.bitIndex += bits; | |
callback(value >>> 0, this); | |
} | |
}, | |
ReadIntN: function(bits, callback) | |
{ | |
/// <signature> | |
/// <summary>Reads an n-bit signed integer from the packet.</summary> | |
/// <param name="bits" type="UInt">The number of bits used.</param> | |
/// <param name="callback" type="function(Int, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
if (this.bitIndex + bits > this.maximumBitIndex) | |
{ | |
this.error("Int" + bits + " expected."); | |
} | |
else | |
{ | |
var value = 0; | |
var offset = this.bitIndex % 32; | |
value |= this.buffer[this.bitIndex / 32 >> 0] << offset >> 32 - bits; | |
if (offset > 32 - bits) | |
{ | |
value |= this.buffer[(this.bitIndex / 32 >> 0) + 1] >>> 64 - bits - offset; | |
} | |
this.bitIndex += bits; | |
callback(value >> 0, this); | |
} | |
}, | |
ReadUInt: function() | |
{ | |
/// <signature> | |
/// <summary>Reads an unsigned integer from the packet.</summary> | |
/// <param name="maximum" type="UInt">The inclusive unsigned integer maximum used.</param> | |
/// <param name="callback" type="function(UInt, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Reads an unsigned integer from the packet.</summary> | |
/// <param name="minimum" type="UInt">The inclusive unsigned integer minimum used.</param> | |
/// <param name="maximum" type="UInt">The inclusive unsigned integer maximum used.</param> | |
/// <param name="callback" type="function(UInt, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
var minimum, maximum, callback; | |
if (arguments.length == 2) | |
{ | |
minimum = 0; | |
maximum = arguments[0]; | |
callback = arguments[1]; | |
} | |
else | |
{ | |
minimum = arguments[0]; | |
maximum = arguments[1]; | |
callback = arguments[2]; | |
} | |
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, function(value) | |
{ | |
callback(value + minimum, this); | |
}); | |
} | |
}, | |
ReadInt: function(minimum, maximum, callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a signed integer from the packet.</summary> | |
/// <param name="maximum" type="Int">The inclusive signed integer maximum.</param> | |
/// <param name="callback" type="function(Int, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Reads a signed integer from the packet.</summary> | |
/// <param name="minimum" type="Int">The inclusive signed integer minimum.</param> | |
/// <param name="maximum" type="Int">The inclusive signed integer maximum.</param> | |
/// <param name="callback" type="function(Int, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
var minimum, maximum, callback; | |
if (arguments.length == 2) | |
{ | |
minimum = 0; | |
maximum = arguments[0]; | |
callback = arguments[1]; | |
} | |
else | |
{ | |
minimum = arguments[0]; | |
maximum = arguments[1]; | |
callback = arguments[2]; | |
} | |
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, function(value) | |
{ | |
callback(value + minimum, this); | |
}); | |
} | |
}, | |
ReadVariableWidthUInt: function(bits, callback) | |
{ | |
/// <signature> | |
/// <summary>Reads an unsigned integer to the packet using a variable width encoding.</summary> | |
/// <param name="value" type="UInt">The unsigned integer value.</param> | |
/// <param name="bits" type="UInt">The number of bits to use for the sequence. Choose a bits value that represents the number of bits to hold the average value.</param> | |
/// <param name="callback" type="function(UInt, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
var bitCount = 0; | |
var continuationBitValue; | |
do | |
{ | |
continuationBitValue = false; | |
this.ReadBool(function(continuationBit) | |
{ | |
continuationBitValue = continuationBit; | |
bitCount += bits; | |
}); | |
} | |
while (continuationBitValue && bitCount < 32); | |
if (bitCount > 32) | |
{ | |
this.error("VariableWidthUInt Expected."); | |
} | |
else | |
{ | |
this.ReadUIntN(bitCount, callback); | |
} | |
}, | |
ReadVariableWidthInt: function(bits, callback) | |
{ | |
/// <signature> | |
/// <summary>Read a signed integer to the packet using a variable width encoding.</summary> | |
/// <param name="value" type="Int">The signed integer value.</param> | |
/// <param name="bits" type="UInt">The number of bits to use for the sequence. Choose a bits value that represents the number of bits to hold the average value.</param> | |
/// <param name="callback" type="function(Int, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
var bitCount = 0; | |
var continuationBitValue; | |
do | |
{ | |
continuationBitValue = false; | |
this.ReadBool(function(continuationBit) | |
{ | |
continuationBitValue = continuationBit; | |
bitCount += bits; | |
}); | |
} | |
while (continuationBitValue && bitCount < 32); | |
if (bitCount > 32) | |
{ | |
this.error("VariableWidthInt Expected."); | |
} | |
else | |
{ | |
this.ReadIntN(bitCount, callback); | |
} | |
}, | |
ReadFloat32: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a 32 floating point from the packet.</summary> | |
/// <param name="callback" type="function(Float32, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Reads a 32 floating point from the packet.</summary> | |
/// <param name="maximum" type="Float32">The inclusive floating point maximum.</param> | |
/// <param name="bits" type="UInt">The number of bits used.</param> | |
/// <param name="callback" type="function(Float32, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a 32 bit float to the packet.</summary> | |
/// <param name="minimum" type="Float32">The inclusive floating point minimum.</param> | |
/// <param name="maximum" type="Float32">The inclusive floating point maximum.</param> | |
/// <param name="bits" type="UInt">The number of bits used.</param> | |
/// <param name="callback" type="function(Float32, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
if (arguments.length == 1) | |
{ | |
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(function(value) | |
{ | |
float32UInt32[0] = value; | |
}); | |
callback(float32[0], this); | |
} | |
} | |
else | |
{ | |
var minimum, maximum, bits, callback; | |
if (arguments.length == 3) | |
{ | |
minimum = 0; | |
maximum = arguments[0]; | |
bits = arguments[1]; | |
callback = arguments[2]; | |
} | |
else | |
{ | |
minimum = arguments[0]; | |
maximum = arguments[1]; | |
bits = arguments[2]; | |
callback = arguments[3]; | |
} | |
if (this.bitIndex + bits > this.maximumBitIndex) | |
{ | |
this.error("Float32 expected."); | |
} | |
else | |
{ | |
this.ReadUIntN(bits, function(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); | |
} | |
}); | |
} | |
} | |
}, | |
ReadFloat64: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a 64 floating point from the packet.</summary> | |
/// <param name="callback" type="function(Float64, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Reads a 64 floating point from the packet.</summary> | |
/// <param name="maximum" type="Float64">The inclusive floating point maximum.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// <param name="callback" type="function(Float64, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Writes a 64 bit float to the packet.</summary> | |
/// <param name="minimum" type="Float64">The inclusive floating point minimum.</param> | |
/// <param name="maximum" type="Float64">The inclusive floating point maximum.</param> | |
/// <param name="bits" type="UInt">The number of bits to use.</param> | |
/// <param name="callback" type="function(Float64, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
if (arguments.length == 1) | |
{ | |
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(function(value) | |
{ | |
float64UInt32[0] = value; | |
}); | |
this.ReadUInt32(function(value) | |
{ | |
float64UInt32[1] = value; | |
}); | |
callback(float64[0], this); | |
} | |
} | |
else | |
{ | |
var minimum, maximum, bits, callback; | |
if (arguments.length == 3) | |
{ | |
minimum = 0; | |
maximum = arguments[0]; | |
bits = arguments[1]; | |
callback = arguments[2]; | |
} | |
else | |
{ | |
minimum = arguments[0]; | |
maximum = arguments[1]; | |
bits = arguments[2]; | |
callback = arguments[3]; | |
} | |
if (this.bitIndex + bits > this.maximumBitIndex) | |
{ | |
this.error("Float64 expected."); | |
} | |
else | |
{ | |
this.ReadUIntN(bits, function(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); | |
} | |
}); | |
} | |
} | |
}, | |
ReadString: function() | |
{ | |
/// <signature> | |
/// <summary>Reads a string from the packet.</summary> | |
/// <param name="callback" type="function(String, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Reads a string from the packet.</summary> | |
/// <param name="codec" type="UInt">The codec identifier for the binded codec state.</param> | |
/// <param name="callback" type="function(String, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
var codec = null; | |
var callback; | |
if (arguments.length == 1) | |
{ | |
callback = arguments[0]; | |
} | |
else | |
{ | |
codec = arguments[0]; | |
callback = arguments[1]; | |
} | |
var value = ""; | |
var packet = this; | |
this.ReadUInt16(function(length) | |
{ | |
if (packet.CodecState != undefined && codec != null) | |
{ | |
var codecToUse = packet.CodecState.Get(codec); | |
for (var index = 0; index < length; ++index) | |
{ | |
codecToUse.Decode(packet, function(character) | |
{ | |
value += character; | |
}, packet.error); | |
} | |
} | |
else | |
{ | |
for (var index = 0; index < length; ++index) | |
{ | |
packet.ReadUIntN(7, function(charCode) | |
{ | |
value += string.fromCharCode(charCode); | |
}); | |
} | |
} | |
callback(value, this); | |
}); | |
}, | |
ReadPacket: function(callback) | |
{ | |
/// <signature> | |
/// <summary>Reads a packet from the packet.</summary> | |
/// <param name="callback" type="function(Packet, [Packet])">A callback with the result. Only executes if the read succeeds.</param> | |
/// </signature> | |
if (this.error == null) return; | |
this.ReadUIntN(this.headerSize, function(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); | |
if (packet.codecState != null) | |
{ | |
value.CodecState = packet.codecState; | |
} | |
value.BitIndex = packet.bitIndex; | |
value.MaximumBitIndex = packet.bitIndex + maximumBitIndex - packet.headerSize; | |
packet.bitIndex += maximumBitIndex - packet.headerSize; | |
callback(value, packet); | |
} | |
}); | |
}, | |
// Read methods | |
Read: function() | |
{ | |
/// <signature> | |
/// <summary>Reads a block of data with one callback.</summary> | |
/// <param name="readOperations" type=""datatype", ["datatype", arguments...]...">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> | |
/// <param name="callback" type="function(datatype..., [Packet])">A callback with the results. Only executes if the read succeeds.</param> | |
/// </signature> | |
var values = []; | |
for (var i = 0; i < arguments.length - 1; ++i) | |
{ | |
if (typeof arguments[i] == "string") | |
{ | |
this["Read" + arguments[i]](function(value) | |
{ | |
values.push(value); | |
}); | |
} | |
else | |
{ | |
arguments[i].push(function(value) | |
{ | |
values.push(value); | |
}); | |
this["Read" + arguments[i].shift()].apply(this, arguments[i]); | |
} | |
} | |
values.push(this); | |
arguments[arguments.length - 1].apply(this, values); | |
}, | |
ReadConditional: function() | |
{ | |
/// <signature> | |
/// <summary>Reads a boolean and executes read operations on true or false.</summary> | |
/// <param name="trueReadOperations" type=""datatype", ["datatype", arguments...]...">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> | |
/// <param name="trueCallback" type="function(datatype..., [Packet])">A callback with the results. Only executes if the read succeeds.</param> | |
/// </signature> | |
/// <signature> | |
/// <summary>Reads a boolean and executes read operations on true or false.</summary> | |
/// <param name="trueReadOperations" type=""datatype", ["datatype", arguments...]...">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> | |
/// <param name="trueCallback" type="function(datatype..., [Packet])">A callback with the results. Only executes if the read succeeds.</param> | |
/// <param name="falseReadOperations" type=""datatype", ["datatype", arguments...]...">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> | |
/// <param name="falseCallback" type="function(datatype..., [Packet])">A callback with the results. Only executes if the read succeeds.</param> | |
/// </signature> | |
var conditionalArguments = arguments; | |
this.ReadBool(function(conditional, packet) | |
{ | |
if (conditional) | |
{ | |
var values = []; | |
for (var i = 0;; ++i) | |
{ | |
if (typeof conditionalArguments[i] == "string") | |
{ | |
packet["Read" + conditionalArguments[i]](function(value) | |
{ | |
values.push(value); | |
}); | |
} | |
else if (Array.isArray(conditionalArguments[i])) | |
{ | |
conditionalArguments[i].push(function(value) | |
{ | |
values.push(value); | |
}); | |
packet["Read" + conditionalArguments[i].shift()].apply(packet, conditionalArguments[i]); | |
} | |
else | |
{ | |
values.push(packet); | |
conditionalArguments[i].apply(packet, values); | |
break; | |
} | |
} | |
} | |
else | |
{ | |
var i = 0; | |
for (;; ++i) | |
{ | |
if (typeof conditionalArguments[i] == "function") | |
{ | |
i++; | |
break; | |
} | |
} | |
var values = []; | |
for (; i < conditionalArguments.length - 1; ++i) | |
{ | |
if (typeof conditionalArguments[i] == "string") | |
{ | |
packet["Read" + conditionalArguments[i]](function(value) | |
{ | |
values.push(value); | |
}); | |
} | |
else if (Array.isArray(conditionalArguments[i])) | |
{ | |
conditionalArguments[i].push(function(value) | |
{ | |
values.push(value); | |
}); | |
packet["Read" + conditionalArguments[i].shift()].apply(packet, conditionalArguments[i]); | |
} | |
} | |
values.push(packet); | |
conditionalArguments[conditionalArguments.length - 1].apply(packet, values); | |
} | |
}); | |
} | |
} | |
/// <var>A state manager for compression codecs. Each session requires one if codec compression is to be used.</var> | |
var CodecState = | |
{ | |
Create: function() | |
{ | |
/// <signature> | |
/// <summary>Constructor.</summary> | |
/// <returns type="CodecState" /> | |
/// </signature> | |
var codecState = Object.create(this, | |
{ | |
codecs: | |
{ | |
value: [], | |
writable: true | |
} | |
}); | |
return codecState; | |
}, | |
Add: function(identifier, codec) | |
{ | |
/// <signature> | |
/// <summary>Registers an identifier to a new codec.</summary> | |
/// <param name="identifier" type="UInt">An unsigned integer enumeration that signifies what the codec is used for. For example Codecs.Chat.</param> | |
/// <param name="codec" type="Codec">A codec that supports Encode and Decode operations for strings. AdaptiveCodec is an example.</param> | |
/// </signature> | |
this.codecs[identifier] = codec; | |
}, | |
Get: function(identifier) | |
{ | |
return this.codecs[identifier]; | |
} | |
} | |
/// <var>An implementation of the adaptive huffman algorithm for string compression. Supports ASCII only.</var> | |
var AdaptiveCodec = | |
{ | |
Create: function() | |
{ | |
var adaptiveCodec = Object.create(this, | |
{ | |
characterToNode: | |
{ | |
value: [], | |
writable: true | |
}, | |
root: | |
{ | |
value: null, | |
writable: true | |
}, | |
newCharacterNode: | |
{ | |
value: null, | |
writable: true | |
}, | |
tail: | |
{ | |
value: null, | |
writable: true | |
}, | |
dummy: | |
{ | |
value: | |
{ | |
character: 0, | |
weight: 0, | |
code: -1, | |
parent: null, | |
left: null, | |
right: null, | |
previous: null, | |
next: null | |
}, | |
writable: true | |
} | |
}); | |
adaptiveCodec.root = | |
{ | |
character: 0, | |
weight: 0, | |
code: -1, | |
parent: null, | |
left: adaptiveCodec.dummy, | |
right: adaptiveCodec.dummy, | |
previous: null, | |
next: null | |
} | |
adaptiveCodec.newCharacterNode = adaptiveCodec.tail = adaptiveCodec.root; | |
for (var i = 32; i <= 126 + 1; ++i) | |
{ | |
adaptiveCodec.characterToNode.push(null); | |
} | |
return adaptiveCodec; | |
}, | |
Encode: function(packet, character) | |
{ | |
// CharCode is between 32 and 126 | |
var currentParent; | |
var characterIndex = character.charCodeAt(0) - 31; | |
var newCharacter = this.characterToNode[characterIndex] == null; | |
if (newCharacter) | |
{ | |
// New character in stream | |
this.GetCode(this.newCharacterNode, function(codePart) | |
{ | |
packet.WriteBool(codePart); | |
}); | |
packet.WriteUIntN(characterIndex, 7); | |
this.AddNewCharacter(characterIndex); | |
} | |
else | |
{ | |
// Existing character in stream | |
this.GetCode(this.characterToNode[characterIndex], function(codePart) | |
{ | |
packet.WriteBool(codePart); | |
}); | |
this.AddExistingCharacter(characterIndex); | |
} | |
}, | |
Decode: function(packet, callback, error) | |
{ | |
// CharCode is between 32 and 126 | |
var characterIndex = 0; | |
var newCharacter = false; | |
var node = this.root; | |
while (!packet.Error) | |
{ | |
if (node == null) | |
{ | |
error(); | |
return; | |
} | |
newCharacter = node === this.newCharacterNode; | |
if (newCharacter) | |
{ | |
// New character in stream | |
characterIndex = 0; | |
packet.ReadUIntN(7, function(value) | |
{ | |
characterIndex = value; | |
}); | |
callback(String.fromCharCode(characterIndex + 31)); | |
this.AddNewCharacter(characterIndex); | |
break; | |
} | |
else if (node.character != 0) | |
{ | |
// Existing character in stream | |
characterIndex = node.character; | |
callback(String.fromCharCode(characterIndex + 31)); | |
this.AddExistingCharacter(characterIndex); | |
break; | |
} | |
else | |
{ | |
// Branch node | |
packet.ReadBool(function(value) | |
{ | |
if (value) | |
{ | |
// Right | |
node = node.right; | |
} | |
else | |
{ | |
// Left | |
node = node.left; | |
} | |
}); | |
} | |
} | |
}, | |
AddNewCharacter: function(characterIndex) | |
{ | |
var newLeftNode = | |
{ | |
character: 0, | |
weight: 0, | |
code: 0, | |
parent: this.newCharacterNode, | |
left: this.dummy, | |
right: this.dummy, | |
previous: null, | |
next: null | |
}; | |
var newRightNode = | |
{ | |
character: characterIndex, | |
weight: 1, | |
code: 1, | |
parent: this.newCharacterNode, | |
left: this.dummy, | |
right: this.dummy, | |
previous: this.tail, | |
next: newLeftNode | |
}; | |
this.tail.next = newLeftNode.previous = newRightNode; | |
this.tail = newLeftNode; | |
this.characterToNode[characterIndex] = newRightNode; | |
this.newCharacterNode.left = newLeftNode; | |
this.newCharacterNode.right = newRightNode; | |
var currentParent = this.newCharacterNode; | |
this.newCharacterNode = newLeftNode; | |
while (currentParent != null) | |
{ | |
currentParent.weight++; | |
var swap = null; | |
for (var node = currentParent.previous; node != null && node.weight == currentParent.weight; node = node.previous) | |
{ | |
if (node !== currentParent.parent) | |
{ | |
swap = node; | |
} | |
} | |
if (swap != null) | |
{ | |
// Swap the character | |
var tempCharacter = swap.character; | |
swap.character = currentParent.character; | |
currentParent.character = tempCharacter; | |
// Swap character to node references | |
this.characterToNode[currentParent.character] = currentParent; | |
this.characterToNode[swap.character] = swap; | |
// Swap left child | |
var tempLeftChild = swap.left; | |
swap.left = currentParent.left; | |
currentParent.left = tempLeftChild; | |
// Swap right child | |
var tempRightChild = swap.right; | |
swap.right = currentParent.right; | |
currentParent.right = tempRightChild; | |
// Update the left and right parents to point to their new parent node | |
currentParent.left.parent = currentParent; | |
currentParent.right.parent = currentParent; | |
swap.left.parent = swap; | |
swap.right.parent = swap; | |
} | |
for (var node = currentParent; node.previous != null && node.weight > node.previous.weight; node = node.previous) | |
{ | |
if (node.previous.previous != null) node.previous.previous.next = node; | |
var tempPrevious = node.previous.previous; | |
node.previous.previous = node; | |
node.previous.next = node.next; | |
if (node.next != null) node.next.previous = node.previous; | |
node.next = node.previous; | |
node.previous = tempPrevious; | |
} | |
currentParent = currentParent.parent; | |
} | |
}, | |
AddExistingCharacter: function(characterIndex) | |
{ | |
var currentParent = this.characterToNode[characterIndex]; | |
currentParent.weight--; | |
var lastCurrentParent = currentParent; | |
while (currentParent != null) | |
{ | |
currentParent.weight++; | |
var swap = null; | |
for (var node = currentParent.previous; node != null && node.weight == currentParent.weight; node = node.previous) | |
{ | |
if (node !== currentParent.parent) | |
{ | |
swap = node; | |
} | |
} | |
if (swap != null) | |
{ | |
// Swap the character | |
var tempCharacter = swap.character; | |
swap.character = currentParent.character; | |
currentParent.character = tempCharacter; | |
// Swap character to node references | |
this.characterToNode[currentParent.character] = currentParent; | |
this.characterToNode[swap.character] = swap; | |
// Swap left child | |
var tempLeftChild = swap.left; | |
swap.left = currentParent.left; | |
currentParent.left = tempLeftChild; | |
// Swap right child | |
var tempRightChild = swap.right; | |
swap.right = currentParent.right; | |
currentParent.right = tempRightChild; | |
// Update the left and right parents to point to their new parent node | |
currentParent.left.parent = currentParent; | |
currentParent.right.parent = currentParent; | |
swap.left.parent = swap; | |
swap.right.parent = swap; | |
for (var node = swap; node.previous != null && node.weight > node.previous.weight; node = node.previous) | |
{ | |
if (node.previous.previous != null) node.previous.previous.next = node; | |
var tempPrevious = node.previous.previous; | |
node.previous.previous = node; | |
node.previous.next = node.next; | |
if (node.next != null) node.next.previous = node.previous; | |
node.next = node.previous; | |
node.previous = tempPrevious; | |
} | |
lastCurrentParent = currentParent; | |
currentParent = swap.parent; | |
} | |
else | |
{ | |
for (var node = currentParent; node.previous != null && node.weight > node.previous.weight; node = node.previous) | |
{ | |
if (node.previous.previous != null) node.previous.previous.next = node; | |
var tempPrevious = node.previous.previous; | |
node.previous.previous = node; | |
node.previous.next = node.next; | |
if (node.next != null) node.next.previous = node.previous; | |
node.next = node.previous; | |
node.previous = tempPrevious; | |
} | |
lastCurrentParent = currentParent; | |
currentParent = currentParent.parent; | |
} | |
lastCurrentParent.weight++; | |
} | |
}, | |
GetCode: function(node, appendCodePart) | |
{ | |
if (node.parent != null) | |
{ | |
this.GetCode(node.parent, appendCodePart); | |
appendCodePart(node.code); | |
} | |
} | |
} | |
module.exports = { Packet: Packet, CodecState: CodecState, AdaptiveCodec: AdaptiveCodec }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment