Skip to content

Instantly share code, notes, and snippets.

@sirisian
Last active December 2, 2022 05:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sirisian/dbc628dde19771b54dec to your computer and use it in GitHub Desktop.
Save sirisian/dbc628dde19771b54dec to your computer and use it in GitHub Desktop.
Type Proposal Example
/**
* A binary bit-based writer and reader designed for packets.
* @class
*/
export class Packet {
/**
* The byte buffer for the packet.
*/
#buffer:[]<uint32>
/**
* The current bit index of the writer and reader.
*/
#bitIndex:uint32
/**
* The maximum bit index of the writer and reader.
*/
#maximumBitIndex:uint32
/**
* Error handler.
*/
#error:(string)
/**
* Constructor for writing.
* @constructs
*/
constructor() {
this.#bitIndex = 0;
this.#maximumBitIndex = 0;
// 1500 bytes - Websocket Header - TCP Header = Default Packet Size
this.#buffer = new [1400]<uint32>;
}
/**
* Constructor for reading.
* @constructs
* @param buffer The buffer to read from.
* @param error Error callback.
*/
constructor(buffer:[]<uint8>, error:(string)) {
this.#buffer = []<uint32>(buffer.buffer);
this.#error = message => {
error(message);
this.#error = null;
};
this.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;
}
/**
* 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() {
let s = '';
for (let 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) {
const 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) {
const 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) {
const 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) {
const 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) {
const 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) {
const 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) {
const 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) {
const 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) {
let 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) {
let 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:float64) {
const 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:float64, maximum:float64, 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:float64, minimum:float64, maximum:float64, 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 (let 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 (let copyBits = 0; copyBits < value.MaximumBitIndex; copyBits += 32) {
const bits = value.MaximumBitIndex - copyBits > 32 ? 32 : value.MaximumBitIndex - copyBits;
// Read n-bits from value.
let valueUIntN:uint32 = 0;
let 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):undefined) {
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;
let 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 {
let value = 0;
const 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 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 {
let value = 0;
const 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 {
let value = 0;
const 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 {
let value = 0;
const 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 {
let value = 0;
const 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 {
let value = 0;
const 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 {
let value = 0;
const 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 {
let value:uint32 = 0;
const 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;
const 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;
const 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;
const 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;
const 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;
let bitCount = 0;
let 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;
let bitCount = 0;
let 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 {
const float32Value = new [1]<float32>;
const uint32View = []<uint32>(float32Value.buffer);
this.readUInt32(value => {
uint32View[0] = value;
});
callback(float32Value[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 {
const float64Value = new [1]<float64>;
const uint32View = []<uint32>(float64Value.buffer);
this.readUInt32(value => {
uint32View[0] = value;
});
this.readUInt32(value => {
uint32View[1] = value;
});
callback(float64Value[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)) {
let value = '';
this.readUInt16(length => {
for (let index = 0; index < length; ++index) {
this.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 => {
if (this.#bitIndex + maximumBitIndex - this.headerSize > this.#maximumBitIndex) {
this.#error('Packet expected');
} else {
const value = new Packet(this.#buffer.buffer, this.#error);
value.#bitIndex = this.#bitIndex;
value.#maximumBitIndex = this.#bitIndex + maximumBitIndex - this.headerSize;
this.#bitIndex += maximumBitIndex - this.headerSize;
callback(value, this);
}
});
}
/**
* 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)) {
callback.call(this, ...readOperations.map(readOperation => {
if (typeof readOperation == 'string') {
let returnValue;
this['read' + readOperation](value => {
returnValue = value;
});
return returnValue;
} else {
let returnValue;
this['read' + readOperation.shift()].call(call, ...readOperation, value => {
returnValue = value;
});
return returnValue;
}
}), this);
}
/**
* 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 => {
if (conditional) {
trueCallback(...trueReadOperations.map(readOperation => {
if (typeof readOperation == 'string') {
let returnValue;
this['read' + readOperation](value => {
returnValue = value;
});
return returnValue;
} else {
let returnValue;
this['read' + readOperation.shift()].call(this, ...readOperation, value => {
returnValue = value;
});
return returnValue;
}
}), this);
}
});
}
/**
* 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 => {
if (conditional) {
trueCallback(...trueReadOperations.map(readOperation => {
if (typeof readOperation == 'string') {
let returnValue;
this['read' + readOperation](value => {
returnValue = value;
});
return returnValue;
} else {
let returnValue;
this['read' + readOperation.shift()].call(this, ...readOperation, value => {
returnValue = value;
});
return returnValue;
}
}), this);
} else {
falseCallback(...falseReadOperations.map(readOperation => {
if (typeof readOperation == 'string') {
let returnValue;
this['read' + readOperation](value => {
returnValue = value;
});
return returnValue;
} else {
let returnValue;
this['read' + readOperation.shift()].call(this, ...readOperation, value => {
returnValue = value;
});
return returnValue;
}
}), this);
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment