mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
add encoders for variable length ints
This commit is contained in:
parent
a979cf6b83
commit
79cd277fed
9 changed files with 148 additions and 24 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -229,9 +229,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"bit-buffer": {
|
"bit-buffer": {
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.2.0.tgz",
|
||||||
"integrity": "sha1-gWTBXb0hjup04IQ9pw76VVpEAsQ="
|
"integrity": "sha512-et8MIvMW4fqwgseIq0SLjyjbhmypVSBcb+4Zd/+2CkYTDsh/yWXEosXrHaCvrTdYzkeeBJraypggmE3XKPlUAw=="
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
|
"version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"jsnext:main": "build/es6/index.js",
|
"jsnext:main": "build/es6/index.js",
|
||||||
"module": "build/es6/index.js",
|
"module": "build/es6/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bit-buffer": "^0.1.0",
|
"bit-buffer": "^0.2.0",
|
||||||
"clone": "^2.1.0",
|
"clone": "^2.1.0",
|
||||||
"minimist": "1.1.x",
|
"minimist": "1.1.x",
|
||||||
"snappyjs": "^0.5.0"
|
"snappyjs": "^0.5.0"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export function logBase2(num: number): number {
|
export function logBase2(num: number): number {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
num >>= 1;
|
num >>= 1;
|
||||||
while (num !== 0) {
|
while (num !== 0 && result < 32) {
|
||||||
result++;
|
result++;
|
||||||
num >>= 1;
|
num >>= 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export function ParseClassInfo(stream: BitStream): ClassInfoPacket { // 10: clas
|
||||||
|
|
||||||
export function EncodeClassInfo(packet: ClassInfoPacket, stream: BitStream) {
|
export function EncodeClassInfo(packet: ClassInfoPacket, stream: BitStream) {
|
||||||
stream.writeUint16(packet.number);
|
stream.writeUint16(packet.number);
|
||||||
stream.writeBoolean(packet.create ? 1 : 0);
|
stream.writeBoolean(packet.create);
|
||||||
if (!packet.create) {
|
if (!packet.create) {
|
||||||
const bits = logBase2(packet.number) + 1;
|
const bits = logBase2(packet.number) + 1;
|
||||||
for (const entry of packet.entries) {
|
for (const entry of packet.entries) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import {Packet} from '../../Data/Packet';
|
import {Packet} from '../../Data/Packet';
|
||||||
import {PacketHandler} from './Parser';
|
import {PacketHandler} from './Parser';
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
export function make(name: string, definition: string): PacketHandler {
|
export function make(name: string, definition: string): PacketHandler {
|
||||||
const parts = definition.split('}');
|
const parts = definition.split('}');
|
||||||
|
|
@ -31,7 +32,7 @@ export function make(name: string, definition: string): PacketHandler {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function readItem(stream, description, data) {
|
function readItem(stream: BitStream, description: string, data) {
|
||||||
if (description[0] === 'b') {
|
if (description[0] === 'b') {
|
||||||
return stream.readBoolean();
|
return stream.readBoolean();
|
||||||
} else if (description[0] === 's') {
|
} else if (description[0] === 's') {
|
||||||
|
|
@ -54,25 +55,25 @@ function readItem(stream, description, data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeItem(stream, description, data, value) {
|
function writeItem(stream: BitStream, description: string, data, value: boolean | string | number) {
|
||||||
if (description[0] === 'b') {
|
if (description[0] === 'b') {
|
||||||
return stream.writeBoolean(value);
|
return stream.writeBoolean(value as boolean);
|
||||||
} else if (description[0] === 's') {
|
} else if (description[0] === 's') {
|
||||||
if (description.length === 1) {
|
if (description.length === 1) {
|
||||||
return stream.writeUTF8String(value);
|
return stream.writeUTF8String(value as string);
|
||||||
} else {
|
} else {
|
||||||
const length = parseInt(description.substr(1), 10);
|
const length = parseInt(description.substr(1), 10);
|
||||||
return stream.writeUTF8String(value, length);
|
return stream.writeUTF8String(value as string, length);
|
||||||
}
|
}
|
||||||
} else if (description === 'f32') {
|
} else if (description === 'f32') {
|
||||||
return stream.writeFloat32(value);
|
return stream.writeFloat32(value as number);
|
||||||
} else if (description[0] === 'u') {
|
} else if (description[0] === 'u') {
|
||||||
const length = parseInt(description.substr(1), 10);
|
const length = parseInt(description.substr(1), 10);
|
||||||
return stream.writeBits(value, length);
|
return stream.writeBits(value as number, length);
|
||||||
} else if (description[0] === '$') {
|
} else if (description[0] === '$') {
|
||||||
const variable = description.substr(1);
|
const variable = description.substr(1);
|
||||||
return stream.writeBits(value, data[variable]);
|
return stream.writeBits(value as number, data[variable]);
|
||||||
} else {
|
} else {
|
||||||
return stream.writeBits(value, parseInt(description, 10), true);
|
return stream.writeBits(value as number, parseInt(description, 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,15 @@
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {logBase2} from '../Math';
|
||||||
|
|
||||||
|
function makeUnsigned(value: number, signed?: boolean) {
|
||||||
|
if (signed) {
|
||||||
|
const signBit = value < 0 ? 1 : 0;
|
||||||
|
return ((value ^ -signBit) << 1) + signBit;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function readBitVar(stream: BitStream, signed?: boolean): number {
|
export function readBitVar(stream: BitStream, signed?: boolean): number {
|
||||||
const type = stream.readBits(2);
|
const type = stream.readBits(2);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -14,6 +25,28 @@ export function readBitVar(stream: BitStream, signed?: boolean): number {
|
||||||
throw new Error('Invalid var bit');
|
throw new Error('Invalid var bit');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function writeBitVar(value: number, stream: BitStream, signed?: boolean) {
|
||||||
|
const bitsNeeded = (signed) ? logBase2(Math.abs(value)) + 2 : logBase2(value) + 1;
|
||||||
|
if (signed) {
|
||||||
|
const signBit = value < 0 ? 1 : 0;
|
||||||
|
value = value ^ (-signBit << (bitsNeeded - 1)) + (signBit << (bitsNeeded - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitsNeeded > 12) {
|
||||||
|
stream.writeBits(3, 2);
|
||||||
|
stream.writeBits(value, 32);
|
||||||
|
} else if (bitsNeeded > 8) {
|
||||||
|
stream.writeBits(2, 2);
|
||||||
|
stream.writeBits(value, 12);
|
||||||
|
} else if (bitsNeeded > 4) {
|
||||||
|
stream.writeBits(1, 2);
|
||||||
|
stream.writeBits(value, 8);
|
||||||
|
} else {
|
||||||
|
stream.writeBits(0, 2);
|
||||||
|
stream.writeBits(value, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const readUBitVar = readBitVar;
|
export const readUBitVar = readBitVar;
|
||||||
|
|
||||||
export function readVarInt(stream: BitStream, signed: boolean = false) {
|
export function readVarInt(stream: BitStream, signed: boolean = false) {
|
||||||
|
|
@ -33,3 +66,14 @@ export function readVarInt(stream: BitStream, signed: boolean = false) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function writeVarInt(value: number, stream: BitStream, signed: boolean = false) {
|
||||||
|
value = makeUnsigned(value, signed);
|
||||||
|
|
||||||
|
do {
|
||||||
|
const byte = value & 0x7F;
|
||||||
|
const resumeBit = (value > 128) ? 0x80 : 0;
|
||||||
|
stream.writeUint8(byte | resumeBit);
|
||||||
|
value = value >> 7;
|
||||||
|
} while (value > 0);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {Packet} from '../../../../Data/Packet';
|
import {Packet} from '../../../../Data/Packet';
|
||||||
import {Encoder, Parser} from '../../../../Parser/Packet/Parser';
|
|
||||||
import {isArray} from 'util';
|
|
||||||
|
|
||||||
export function getStream(data: string | number[]) {
|
export function getStream(data: string | number[]) {
|
||||||
if (isArray(data)) {
|
if (typeof data === 'string') {
|
||||||
const array = new Uint8Array(data as number[]);
|
|
||||||
return new BitStream(array.buffer);
|
|
||||||
} else {
|
|
||||||
const buffer = new Buffer(data + '\0remaining dummy data');
|
const buffer = new Buffer(data + '\0remaining dummy data');
|
||||||
return new BitStream(buffer);
|
return new BitStream(buffer);
|
||||||
|
} else {
|
||||||
|
const array = new Uint8Array(data as number[]);
|
||||||
|
return new BitStream(array.buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Encoder = (data: any, stream: BitStream) => void;
|
||||||
|
|
||||||
export function assertEncoder(parser: Parser, encoder: Encoder, data: any, length: number = 0) {
|
export function assertEncoder(parser: Parser, encoder: Encoder, data: any, length: number = 0) {
|
||||||
const stream = new BitStream(new ArrayBuffer(64));
|
const stream = new BitStream(new ArrayBuffer(64));
|
||||||
|
|
||||||
|
|
@ -28,10 +28,12 @@ export function assertEncoder(parser: Parser, encoder: Encoder, data: any, lengt
|
||||||
stream.index = 0;
|
stream.index = 0;
|
||||||
|
|
||||||
const result = parser(stream);
|
const result = parser(stream);
|
||||||
assert.deepEqual(data, result);
|
assert.deepEqual(data, result, 'Re-decoded value not equal to original value');
|
||||||
assert.equal(pos, stream.index, 'Number of bits used for encoding and parsing not equal');
|
assert.equal(pos, stream.index, 'Number of bits used for encoding and parsing not equal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Parser = (stream: BitStream) => any;
|
||||||
|
|
||||||
export function assertParser(parser: Parser, stream: BitStream, expected: any, length: number) {
|
export function assertParser(parser: Parser, stream: BitStream, expected: any, length: number) {
|
||||||
const start = stream.index;
|
const start = stream.index;
|
||||||
assert.deepEqual(expected, parser(stream));
|
assert.deepEqual(expected, parser(stream));
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ suite('Parser generator', () => {
|
||||||
|
|
||||||
test('Boolean', () => {
|
test('Boolean', () => {
|
||||||
const stream = new BitStream(new ArrayBuffer(64));
|
const stream = new BitStream(new ArrayBuffer(64));
|
||||||
stream.writeBoolean(1);
|
stream.writeBoolean(true);
|
||||||
stream.writeBoolean(0);
|
stream.writeBoolean(false);
|
||||||
stream.writeASCIIString('remaining');
|
stream.writeASCIIString('remaining');
|
||||||
stream.index = 0;
|
stream.index = 0;
|
||||||
|
|
||||||
|
|
|
||||||
77
src/tests/unit/Parser/readBitVarTest.ts
Normal file
77
src/tests/unit/Parser/readBitVarTest.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {assertEncoder, assertParser, getStream} from './Packet/PacketTest';
|
||||||
|
import {readBitVar, readVarInt, writeBitVar, writeVarInt} from '../../../Parser/readBitVar';
|
||||||
|
|
||||||
|
function readVarIntSigned(stream: BitStream) {
|
||||||
|
return readVarInt(stream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeVarIntSigned(value: number, stream: BitStream) {
|
||||||
|
return writeVarInt(value, stream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBitVarSigned(stream: BitStream) {
|
||||||
|
return readBitVar(stream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeBitVarSigned(value: number, stream: BitStream) {
|
||||||
|
return writeBitVar(value, stream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
suite('readBitVar', () => {
|
||||||
|
test('readVarInt', () => {
|
||||||
|
assertParser(readVarInt, getStream([121, 25, 12, 14]), 121, 8);
|
||||||
|
assertParser(readVarInt, getStream([129, 25, 12, 14]), 3201, 16);
|
||||||
|
assertParser(readVarInt, getStream([129, 225, 12, 14]), 209025, 24);
|
||||||
|
assertParser(readVarInt, getStream([129, 225, 212, 14]), 30748801, 32);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('readVarInt signed', () => {
|
||||||
|
assertParser(readVarIntSigned, getStream([121, 25, 12, 14]), -61, 8);
|
||||||
|
assertParser(readVarIntSigned, getStream([129, 25, 12, 14]), -1601, 16);
|
||||||
|
assertParser(readVarIntSigned, getStream([129, 225, 12, 14]), -104513, 24);
|
||||||
|
assertParser(readVarIntSigned, getStream([130, 225, 212, 14]), 15374401, 32);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('writeVarInt', () => {
|
||||||
|
assertEncoder(readVarInt, writeVarInt, 121, 8);
|
||||||
|
assertEncoder(readVarInt, writeVarInt, 3201, 16);
|
||||||
|
assertEncoder(readVarInt, writeVarInt, 209025, 24);
|
||||||
|
assertEncoder(readVarInt, writeVarInt, 30748801, 32);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('writeVarInt signed', () => {
|
||||||
|
assertEncoder(readVarIntSigned, writeVarIntSigned, -61, 8);
|
||||||
|
assertEncoder(readVarIntSigned, writeVarIntSigned, -1254, 16);
|
||||||
|
assertEncoder(readVarIntSigned, writeVarIntSigned, -104513, 24);
|
||||||
|
assertEncoder(readVarIntSigned, writeVarIntSigned, 15374401, 32);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('readBitVar', () => {
|
||||||
|
assertParser(readBitVar, getStream([121, 25, 12, 14]), 94, 10);
|
||||||
|
assertParser(readBitVar, getStream([130, 25, 12, 14]), 1632, 14);
|
||||||
|
assertParser(readBitVar, getStream([8, 225, 12, 14]), 2, 6);
|
||||||
|
assertParser(readBitVar, getStream([131, 225, 212, 14, 123]), 3283433568, 34);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('readBitVar signed', () => {
|
||||||
|
assertParser(readBitVarSigned, getStream([120, 225, 12, 14]), -2, 6);
|
||||||
|
assertParser(readBitVarSigned, getStream([121, 25, 12, 14]), 94, 10);
|
||||||
|
assertParser(readBitVarSigned, getStream([130, 25, 12, 14]), 1632, 14);
|
||||||
|
assertParser(readBitVarSigned, getStream([131, 225, 212, 14, 123]), -1011533728, 34);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('writeVarInt', () => {
|
||||||
|
assertEncoder(readBitVar, writeBitVar, 2, 6);
|
||||||
|
assertEncoder(readBitVar, writeBitVar, 94, 10);
|
||||||
|
assertEncoder(readBitVar, writeBitVar, 1632, 14);
|
||||||
|
assertEncoder(readBitVar, writeBitVar, 3283433568, 34);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('writeVarInt signed', () => {
|
||||||
|
assertEncoder(readBitVarSigned, writeBitVarSigned, 2, 6);
|
||||||
|
assertEncoder(readBitVarSigned, writeBitVarSigned, -94, 10);
|
||||||
|
assertEncoder(readBitVarSigned, writeBitVarSigned, 1632, 14);
|
||||||
|
assertEncoder(readBitVarSigned, writeBitVarSigned, -283433565, 34);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue