mudb
Version:
Real-time database for multiplayer games
445 lines (393 loc) • 12.7 kB
text/typescript
import * as test from 'tape';
import { MuWriteStream, MuReadStream } from '../';
test('new MuWriteStream()', (t) => {
t.equal(new MuWriteStream(0).buffer.uint8.byteLength, 2);
t.equal(new MuWriteStream(0.1).buffer.uint8.byteLength, 2);
t.equal(new MuWriteStream(1).buffer.uint8.byteLength, 2);
t.equal(new MuWriteStream(2).buffer.uint8.byteLength, 2);
t.equal(new MuWriteStream(3).buffer.uint8.byteLength, 4);
t.equal(new MuWriteStream(4).buffer.uint8.byteLength, 4);
t.equal(new MuWriteStream(5).buffer.uint8.byteLength, 8);
t.equal(new MuWriteStream(8).buffer.uint8.byteLength, 8);
t.equal(new MuWriteStream(9).buffer.uint8.byteLength, 16);
t.equal(new MuWriteStream(16).buffer.uint8.byteLength, 16);
t.equal(new MuWriteStream(2 ** 30).buffer.uint8.byteLength, 2 ** 30);
t.equal(new MuWriteStream(NaN).buffer.uint8.byteLength, 2);
t.throws(() => new MuWriteStream(Infinity));
t.throws(() => new MuWriteStream(-Infinity));
t.throws(() => new MuWriteStream(-1));
t.throws(() => new MuWriteStream(2 ** 30 + 1));
t.end();
});
test('growing buffer', (t) => {
const ws = new MuWriteStream(8);
const oldBuffer = ws.buffer;
ws.writeFloat64(1234.5678);
ws.grow(0);
t.equals(ws.buffer.buffer.byteLength, 8);
t.equals(oldBuffer, ws.buffer, 'still the same buffer');
ws.grow(1);
t.equals(ws.buffer.buffer.byteLength, 16);
t.notEquals(oldBuffer, ws.buffer, 'different buffer now');
t.equals(ws.buffer.dataView.getFloat64(0, true), oldBuffer.dataView.getFloat64(0, true), 'but same content');
t.end();
});
test('int', (t) => {
const loops = 64;
const ws = new MuWriteStream(0x1000);
for (let i = -0x80; i < 0x80; i += 0x100 / loops) {
ws.writeInt8(i);
ws.writeInt8(i + 0x100 / loops - 1);
}
for (let i = -0x8000; i < 0x8000; i += 0x10000 / loops) {
ws.writeInt16(i);
ws.writeInt16(i + 0x10000 / loops - 1);
}
for (let i = -0x80000000; i < 0x80000000; i += 0x100000000 / loops) {
ws.writeInt32(i);
ws.writeInt32(i + 0x100000000 / loops - 1);
}
for (let i = 0; i < 0x100000000; i += 0x100000000 / loops) {
ws.writeUint32(i);
ws.writeUint32(i + 0x100000000 / loops - 1);
}
for (let i = 0; i < 0x10000; i += 0x10000 / loops) {
ws.writeUint16(i);
ws.writeUint16(i + 0x10000 / loops - 1);
}
for (let i = 0; i < 0x100; i += 0x100 / loops) {
ws.writeUint8(i);
ws.writeUint8(i + 0x100 / loops - 1);
}
const rs = new MuReadStream(ws.buffer.uint8);
for (let i = -0x80; i < 0x80; i += 0x100 / loops) {
t.equals(rs.readInt8(), i);
t.equals(rs.readInt8(), i + 0x100 / loops - 1);
}
for (let i = -0x8000; i < 0x8000; i += 0x10000 / loops) {
t.equals(rs.readInt16(), i);
t.equals(rs.readInt16(), i + 0x10000 / loops - 1);
}
for (let i = -0x80000000; i < 0x80000000; i += 0x100000000 / loops) {
t.equals(rs.readInt32(), i);
t.equals(rs.readInt32(), i + 0x100000000 / loops - 1);
}
for (let i = 0; i < 0x100000000; i += 0x100000000 / loops) {
t.equals(rs.readUint32(), i);
t.equals(rs.readUint32(), i + 0x100000000 / loops - 1);
}
for (let i = 0; i < 0x10000; i += 0x10000 / loops) {
t.equals(rs.readUint16(), i);
t.equals(rs.readUint16(), i + 0x10000 / loops - 1);
}
for (let i = 0; i < 0x100; i += 0x100 / loops) {
t.equals(rs.readUint8(), i);
t.equals(rs.readUint8(), i + 0x100 / loops - 1);
}
t.end();
});
test('float', (t) => {
const FLOAT32_EPSILON = 1.401298464324817e-45;
const FLOAT32_MIN = 1.1754943508222875e-38;
const FLOAT32_MAX = 3.4028234663852886e+38;
const FLOAT64_EPSILON = 5e-324;
const FLOAT64_MIN = 2.2250738585072014e-308;
const FLOAT64_MAX = 1.7976931348623157e+308;
const ws = new MuWriteStream(0x100);
const floatNum = Math.random();
ws.writeFloat32(FLOAT32_MIN);
ws.writeFloat32(-FLOAT32_EPSILON);
ws.writeFloat32(0);
ws.writeFloat32(FLOAT32_EPSILON);
ws.writeFloat32(FLOAT32_MAX);
ws.writeFloat32(floatNum);
ws.writeFloat64(FLOAT64_MIN);
ws.writeFloat64(-FLOAT64_EPSILON);
ws.writeFloat64(0);
ws.writeFloat64(FLOAT64_EPSILON);
ws.writeFloat64(FLOAT64_MAX);
ws.writeFloat64(floatNum);
const fround = (function (a) {
return function (n:number) : number {
a[0] = n;
return a[0];
};
})(new Float32Array(1));
const rs = new MuReadStream(ws.buffer.uint8);
t.equals(rs.readFloat32(), FLOAT32_MIN);
t.equals(rs.readFloat32(), -FLOAT32_EPSILON);
t.equals(rs.readFloat32(), 0);
t.equals(rs.readFloat32(), FLOAT32_EPSILON);
t.equals(rs.readFloat32(), FLOAT32_MAX);
t.equals(rs.readFloat32(), fround(floatNum));
t.equals(rs.readFloat64(), FLOAT64_MIN);
t.equals(rs.readFloat64(), -FLOAT64_EPSILON);
t.equals(rs.readFloat64(), 0);
t.equals(rs.readFloat64(), FLOAT64_EPSILON);
t.equals(rs.readFloat64(), FLOAT64_MAX);
t.equals(rs.readFloat64(), floatNum);
t.end();
});
test('ascii string', (t) => {
const asciis = new Array(128);
for (let i = 0; i < 128; ++i) {
asciis[i] = String.fromCharCode(i);
}
let ws = new MuWriteStream(1024);
ws.writeUint32(0);
ws.writeASCII('');
for (let i = 0; i < 128; ++i) {
ws.writeUint32(1);
ws.writeASCII(asciis[i]);
}
let rs = new MuReadStream(ws.buffer.uint8);
let strLeng = rs.readUint32();
t.equals(rs.readASCII(strLeng), '');
for (let i = 0; i < 128; ++i) {
strLeng = rs.readUint32();
t.equals(rs.readASCII(strLeng), asciis[i]);
}
let longStr = '';
const str = asciis.join('');
for (let i = 0; i < 1e5; ++i) {
longStr += str;
}
ws = new MuWriteStream(2 ** 30);
ws.writeUint32(longStr.length);
ws.writeASCII(longStr);
rs = new MuReadStream(ws.buffer.uint8);
strLeng = rs.readUint32();
t.equals(rs.readASCII(strLeng), longStr);
t.end();
});
test('string', (t) => {
const emptyStr = '';
const strA = String.fromCharCode(0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00);
const strB = String.fromCharCode(0x12, 0x34, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00);
const strC = String.fromCharCode(0x12, 0x34, 0x56, 0x78,
0x87, 0x65, 0x43, 0x21);
const strD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF);
let ws = new MuWriteStream(128);
ws.writeString('');
ws.writeString(strA);
ws.writeString(strB);
ws.writeString(strC);
ws.writeString(strD);
let rs = new MuReadStream(ws.buffer.uint8);
t.equals(rs.readString(), '');
t.equals(rs.readString(), strA);
t.equals(rs.readString(), strB);
t.equals(rs.readString(), strC);
t.equals(rs.readString(), strD);
const ascii = 'I <3 you.';
const twoBytes = 'אני אוהבת אותך';
const threeBytes = '我♥你';
const fourBytes = '👩👨❤️👨👩';
ws = new MuWriteStream(256);
ws.writeString(ascii);
ws.writeString(twoBytes);
ws.writeString(threeBytes);
ws.writeString(fourBytes);
rs = new MuReadStream(ws.buffer.uint8);
t.equals(rs.readString(), ascii);
t.equals(rs.readString(), twoBytes);
t.equals(rs.readString(), threeBytes);
t.equals(rs.readString(), fourBytes);
const varBytes = fourBytes + twoBytes + ascii + threeBytes;
let longStr = '';
for (let i = 0; i < 100000; ++i) {
longStr += varBytes;
}
ws = new MuWriteStream(2 ** 30);
ws.writeString(longStr);
rs = new MuReadStream(ws.buffer.uint8);
t.equals(rs.readString(), longStr, 'able to write and read a long string of characters of various bytes');
t.end();
});
test('varints', (t) => {
const testVals = [
1,
64,
128,
129,
255,
256,
(1 << 7),
(1 << 14),
(1 << 21),
(1 << 28),
(1 << 31),
];
for (let i = testVals.length - 1; i >= 0; --i) {
const x = testVals[i];
testVals.push(
(x - 1) >>> 0,
(x + 1) >>> 0,
(x + (Math.random() * x) | 0) >>> 0);
}
testVals.push(0);
testVals.push(0xfffffff);
const ws = new MuWriteStream(5 * testVals.length);
for (let i = 0; i < testVals.length; ++i) {
ws.writeVarint(testVals[i]);
}
const rs = new MuReadStream(ws.bytes());
for (let i = 0; i < testVals.length; ++i) {
t.equals(rs.readVarint(), testVals[i] >>> 0, 'var int read ok: 0b' + (testVals[i] >>> 0).toString(2));
}
t.end();
});
test('stream offset & length', (t) => {
const ws = new MuWriteStream(8);
ws.offset += 8;
const rs0 = new MuReadStream(ws.bytes());
t.equals(rs0.offset, 0);
t.equals(rs0.length, 8);
rs0.readFloat32();
const rs1 = new MuReadStream(rs0.bytes());
t.equals(rs1.offset, 4);
t.equals(rs1.length, 8);
t.end();
});
test('checking bounds', (t) => {
{
const ws = new MuWriteStream(1);
ws.offset += 1;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readInt8());
t.throws(() => rs.readInt8());
}
{
const ws = new MuWriteStream(1);
ws.offset += 1;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readUint8());
t.throws(() => rs.readUint8());
}
{
const ws = new MuWriteStream(2);
ws.offset += 2;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readInt16());
}
{
const ws = new MuWriteStream(2);
ws.offset += 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readInt16());
}
{
const ws = new MuWriteStream(2);
ws.offset += 2;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readUint16());
}
{
const ws = new MuWriteStream(2);
ws.offset += 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readUint16());
}
{
const ws = new MuWriteStream(4);
ws.offset += 4;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readInt32());
}
{
const ws = new MuWriteStream(4);
ws.offset += 3;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readInt32());
}
{
const ws = new MuWriteStream(4);
ws.offset += 4;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readUint32());
}
{
const ws = new MuWriteStream(4);
ws.offset += 3;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readUint32());
}
{
const ws = new MuWriteStream(4);
ws.offset += 4;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readFloat32());
}
{
const ws = new MuWriteStream(4);
ws.offset += 3;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readFloat32());
}
{
const ws = new MuWriteStream(8);
ws.offset += 8;
const rs = new MuReadStream(ws.bytes());
t.doesNotThrow(() => rs.readFloat64());
}
{
const ws = new MuWriteStream(8);
ws.offset += 7;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readFloat64());
}
{
const ws = new MuWriteStream(5);
ws.writeVarint(0x7f);
ws.offset -= 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readVarint());
}
{
const ws = new MuWriteStream(5);
ws.writeVarint(0x3fff);
ws.offset -= 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readVarint());
}
{
const ws = new MuWriteStream(5);
ws.writeVarint(0x1fffff);
ws.offset -= 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readVarint());
}
{
const ws = new MuWriteStream(5);
ws.writeVarint(0xfffffff);
ws.offset -= 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readVarint());
}
{
const ws = new MuWriteStream(5);
ws.writeVarint(0xffffffff);
ws.offset -= 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readVarint());
}
{
const ws = new MuWriteStream(5);
const s = 'a';
ws.writeVarint(s.length + 1);
ws.writeASCII(s);
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readASCII(rs.readVarint()));
}
{
const ws = new MuWriteStream(128);
ws.writeString('Iñtërnâtiônàlizætiøn☃💩');
ws.offset -= 1;
const rs = new MuReadStream(ws.bytes());
t.throws(() => rs.readString());
}
t.end();
});