UNPKG

mudb

Version:

Real-time database for multiplayer games

725 lines 25.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tape = require("tape"); const stream_1 = require("../../stream"); const index_1 = require("../index"); const random_1 = require("../../util/random"); function createTest(t, schema) { return (base, target) => { const out = new stream_1.MuWriteStream(1); if (schema.diff(base, target, out)) { t.notDeepEqual(base, target, 'diff() implied values are not identical'); t.true(out.offset > 0, 'at least one byte should be written to stream'); const inp = new stream_1.MuReadStream(out.bytes()); t.deepEqual(schema.patch(base, inp), target, 'patched value should be identical to target'); t.equal(inp.offset, inp.length, 'patch() should consume all bytes on stream'); } else { t.deepEqual(base, target, 'diff() implied values are identical'); t.equal(out.offset, 0, 'no bytes should be written to stream'); } }; } function randArray() { const a = new Array(Math.random() * 10 | 0); for (let i = 0; i < a.length; ++i) { a[i] = random_1.randFloat32(); } return a; } function randDict() { const d = {}; let code = 97 + Math.random() * 6 | 0; for (let i = Math.random() * 6 | 0; i > 0; --i) { d[String.fromCharCode(code++)] = random_1.randFloat32(); } return d; } const compare = (a, b) => a - b; tape.onFailure(() => { process.exit(1); }); tape('de/serializing boolean', (t) => { const bool = new index_1.MuBoolean(); const test = createTest(t, bool); test(true, true); test(false, false); test(true, false); test(false, true); t.end(); }); tape('de/serializing string', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test('', a); test('', b); }; } t.test('ascii', (st) => { const ascii = new index_1.MuASCII(); const testPair = createTestPair(st, ascii); testPair('', ' '); testPair('a', 'b'); testPair('a', '<a href="https://github.com/mikolalysenko/mudb/">mudb</a>'); const codePoints = new Array(0x80); for (let i = 0; i < codePoints.length; ++i) { codePoints[i] = i; } testPair(String.fromCharCode.apply(null, codePoints), String.fromCharCode.apply(null, codePoints.reverse())); st.end(); }); t.test('fixed-ascii', (st) => { const fixedAscii = new index_1.MuFixedASCII(32); const test = createTest(st, fixedAscii); test('https://github.com/mikolalysenko', 'https://github.com/mikolalysenko'); test('e42dfecf821ebdfce692c7692b18d2b1', 'https://github.com/mikolalysenko'); test('https://github.com/mikolalysenko', 'e42dfecf821ebdfce692c7692b18d2b1'); st.end(); }); t.test('utf8', (st) => { const utf8 = new index_1.MuUTF8(); const testPair = createTestPair(st, utf8); testPair('', ' '); testPair('<a href="https://github.com/mikolalysenko/mudb/">mudb</a>', 'Iñtërnâtiônàlizætiøn☃💩'); testPair('<a href="https://github.com/mikolalysenko/mudb/">mudb</a>', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩'); let bigText = '啊啊啊'; for (let i = 0; i < 16; ++i) { bigText += bigText; } testPair('<a href="https://github.com/mikolalysenko/mudb/">mudb</a>', bigText); st.end(); }); t.end(); }); tape('de/serializing number', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); }; } const testFloat32 = createTestPair(t, new index_1.MuFloat32()); testFloat32(-3.4028234663852886e+38, 3.4028234663852886e+38); const testFloat64 = createTestPair(t, new index_1.MuFloat64()); testFloat64(-1.7976931348623157e+308, 1.7976931348623157e+308); const testInt8 = createTestPair(t, new index_1.MuInt8()); testInt8(-0x80, 0x7f); const testInt16 = createTestPair(t, new index_1.MuInt16()); testInt16(-0x8000, 0x7fff); const testInt32 = createTestPair(t, new index_1.MuInt32()); testInt32(-0x80000000, 0x7fffffff); const testUint8 = createTestPair(t, new index_1.MuUint8()); testUint8(0, 0xff); const testUint16 = createTestPair(t, new index_1.MuUint16()); testUint16(0, 0xffff); const testUint32 = createTestPair(t, new index_1.MuUint32()); testUint32(0, 0xffffffff); t.end(); }); tape('de/serializing varint', (t) => { const sample = [1, 64, 128, 256, 1 << 14, 1 << 21, 1 << 28, 1 << 31 >>> 0]; for (let i = sample.length - 1; i >= 0; --i) { const x = sample[i]; sample.push(x - 1); sample.push(x + 1); sample.push(x + (x * Math.random() | 0) >>> 0); } sample.push(0xffffffff); const testVarint = createTest(t, new index_1.MuVarint()); for (let i = 0; i < sample.length; ++i) { const x = sample[i]; testVarint(0, x); testVarint(x, x); } t.end(); }); tape('de/serializing rvarint', (t) => { const sample = []; for (let i = 0; i < 30; ++i) { sample.push(1 << i); sample.push(-1 << i); } for (let i = sample.length - 1; i >= 0; --i) { const x = sample[i]; sample.push(x - 1); sample.push(x + 1); sample.push(x + (x * Math.random() | 0) >> 0); } sample.push(1 << 31); const testRelativeVarint = createTest(t, new index_1.MuRelativeVarint()); for (let i = 0; i < sample.length; ++i) { const x = sample[i]; testRelativeVarint(0, x); testRelativeVarint(x, x); } testRelativeVarint(0x80000000, 0); testRelativeVarint(0x80000001, 1); testRelativeVarint(0x100000000, 0x80000000); t.end(); }); tape('de/serializing array', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test([], a); test([], b); }; } function randNestedArray() { const na = new Array(Math.random() * 10 | 0); for (let i = 0; i < na.length; ++i) { na[i] = randArray(); } return na; } t.test('simple array', (st) => { const array = new index_1.MuArray(new index_1.MuFloat32(), Infinity); const testPair = createTestPair(st, array); testPair([0], [1]); testPair([0, 1], [1, 1]); testPair([0, 1], [0, 2]); testPair([0, 1], [0.5, 1.5]); testPair([0], [0, 1]); testPair([0, 1], [1, 2, 3]); for (let i = 0; i < 1000; ++i) { testPair(randArray(), randArray()); } st.end(); }); t.test('nested array', (st) => { const array = new index_1.MuArray(new index_1.MuArray(new index_1.MuFloat32(), Infinity), Infinity); const testPair = createTestPair(st, array); testPair([[]], [[], []]); for (let i = 0; i < 1000; ++i) { testPair(randNestedArray(), randNestedArray()); } st.end(); }); t.end(); }); tape('de/serializing sorted array', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { a.sort(compare); b.sort(compare); test(a, a); test(b, b); test(a, b); test(b, a); test([], a); test([], b); test(a, []); test(b, []); }; } const sortedArray = new index_1.MuSortedArray(new index_1.MuFloat32(), Infinity); const testPair = createTestPair(t, sortedArray); testPair([], []); testPair([0], [1]); testPair([0, 1], [1, 1]); testPair([0, 1], [0, 2]); testPair([0, 1], [0.5, 1.5]); testPair([0], [0, 1]); testPair([0, 1], [1, 2, 3]); for (let i = 0; i < 1000; ++i) { testPair(randArray(), randArray()); } const structSchema = new index_1.MuStruct({ id: new index_1.MuUint32(0), garbage: new index_1.MuArray(new index_1.MuFloat64(10), Infinity), poop: new index_1.MuFloat32(0), }); function randomStruct() { const s = structSchema.alloc(); s.id = (Math.random() * 1000) | 0; s.garbage.length = 0; const ngarbage = Math.random() * 10; for (let i = 0; i < ngarbage; ++i) { s.garbage.push(Math.random()); } s.poop = (Math.random() * 10) | 0; return s; } const arraySchema = new index_1.MuSortedArray(structSchema, Infinity, (a, b) => a.id - b.id); function randomArray() { const x = arraySchema.alloc(); const n = (Math.random() * 100) | 0; for (let i = 0; i < n; ++i) { x.push(randomStruct()); } x.sort(arraySchema.compare); return x; } const testStruct = createTestPair(t, arraySchema); testStruct([], []); for (let i = 0; i < 100; ++i) { const x = randomArray(); const y = randomArray(); testStruct(x, y); arraySchema.assign(x, []); arraySchema.free(y); } t.end(); }); tape('de/serializing struct', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test(schema.alloc(), a); test(schema.alloc(), b); }; } const struct = new index_1.MuStruct({ struct: new index_1.MuStruct({ ascii: new index_1.MuASCII(), fixed: new index_1.MuFixedASCII(8), utf8: new index_1.MuUTF8(), bool: new index_1.MuBoolean(), float32: new index_1.MuFloat32(), float64: new index_1.MuFloat64(), int8: new index_1.MuInt8(), int16: new index_1.MuInt16(), int32: new index_1.MuInt32(), uint8: new index_1.MuUint8(), uint16: new index_1.MuUint16(), uint32: new index_1.MuUint32(), varint: new index_1.MuVarint(), rvarint: new index_1.MuRelativeVarint(), array: new index_1.MuArray(new index_1.MuFloat32(), Infinity), sorted: new index_1.MuSortedArray(new index_1.MuFloat32(), Infinity), dict: new index_1.MuDictionary(new index_1.MuFloat32(), Infinity), vector: new index_1.MuVector(new index_1.MuFloat32(), 3), }), }); const strings = [ ' ', '<a href="https://github.com/mikolalysenko/mudb/">mudb</a>', 'Iñtërnâtiônàlizætiøn☃💩', ]; function createStruct() { const s = struct.alloc(); s.struct.ascii = Math.random().toString(36); s.struct.fixed = Math.random().toString(36).substring(2, 10); s.struct.utf8 = strings[Math.random() * 3 | 0]; s.struct.bool = random_1.randBool(); s.struct.float32 = random_1.randFloat32(); s.struct.float64 = random_1.randFloat64(); s.struct.int8 = random_1.randInt8(); s.struct.int16 = random_1.randInt16(); s.struct.int32 = random_1.randInt32(); s.struct.uint8 = random_1.randUint8(); s.struct.uint16 = random_1.randUint16(); s.struct.uint32 = random_1.randUint32(); s.struct.varint = random_1.randUint32(); s.struct.rvarint = random_1.randInt16(); s.struct.array = randArray(); s.struct.sorted = randArray().sort(compare); s.struct.dict = randDict(); s.struct.vector = randVec(3); return s; } const testPair = createTestPair(t, struct); for (let i = 0; i < 2000; ++i) { testPair(createStruct(), createStruct()); } t.end(); }); tape('struct of quantized floats', (t) => { function createTest_(t_, schema) { return (base, target) => { const out = new stream_1.MuWriteStream(1); if (schema.diff(base, target, out)) { t_.notDeepEqual(base, target, 'diff() implied values are not identical'); t_.true(out.offset > 0, 'at least one byte should be written to stream'); const inp = new stream_1.MuReadStream(out.bytes()); t_.true(schema.equal(schema.patch(base, inp), target), 'patched value should be identical to target'); t_.equal(inp.offset, inp.length, 'patch() should consume all bytes on stream'); } else { t_.deepEqual(base, target, 'diff() implied values are identical'); t_.equal(out.offset, 0, 'no bytes should be written to stream'); } }; } function createTestPair(t_, schema) { const test = createTest_(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test(schema.alloc(), a); test(schema.alloc(), b); }; } function createStruct(schema) { const s = schema.alloc(); Object.keys(s).forEach((k) => { s[k] = schema.muData[k].clone(random_1.randFloat32()); }); return s; } const structSchema = new index_1.MuStruct({ lowPrecision: new index_1.MuQuantizedFloat(1 / 16), mediumPrecision: new index_1.MuQuantizedFloat(1 / 256), highPrecision: new index_1.MuQuantizedFloat(1 / 4096), }); const testPair = createTestPair(t, structSchema); for (let i = 0; i < 200; i++) { testPair(createStruct(structSchema), createStruct(structSchema)); } t.end(); }); tape('de/serializing struct of booleans', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test(schema.alloc(), a); test(schema.alloc(), b); }; } function createStruct(schema) { const s = schema.alloc(); Object.keys(s).forEach((key) => { s[key] = random_1.randBool(); }); return s; } const struct1 = new index_1.MuStruct({ a: new index_1.MuBoolean(), }); const struct2 = new index_1.MuStruct({ a: new index_1.MuBoolean(), b: new index_1.MuBoolean(), }); const struct8 = new index_1.MuStruct({ a: new index_1.MuBoolean(), b: new index_1.MuBoolean(), c: new index_1.MuBoolean(), d: new index_1.MuBoolean(), e: new index_1.MuBoolean(), f: new index_1.MuBoolean(), g: new index_1.MuBoolean(), h: new index_1.MuBoolean(), }); const struct9 = new index_1.MuStruct({ a: new index_1.MuBoolean(), b: new index_1.MuBoolean(), c: new index_1.MuBoolean(), d: new index_1.MuBoolean(), e: new index_1.MuBoolean(), f: new index_1.MuBoolean(), g: new index_1.MuBoolean(), h: new index_1.MuBoolean(), i: new index_1.MuBoolean(), }); const shape = {}; for (let i = 0; i < 1000; ++i) { shape[i] = new index_1.MuBoolean(); } const struct = new index_1.MuStruct(shape); const testPair1 = createTestPair(t, struct1); testPair1({ a: true }, { a: false }); const testPair2 = createTestPair(t, struct2); testPair2({ a: false, b: true }, { a: true, b: false }); testPair2({ a: false, b: true }, { a: true, b: true }); testPair2({ a: true, b: false }, { a: true, b: true }); const testPair8 = createTestPair(t, struct8); for (let i = 0; i < 1000; ++i) { testPair8(createStruct(struct8), createStruct(struct8)); } const testPair9 = createTestPair(t, struct9); for (let i = 0; i < 1000; ++i) { testPair9(createStruct(struct9), createStruct(struct9)); } const testPair = createTestPair(t, struct); for (let i = 0; i < 1000; ++i) { testPair(createStruct(struct), createStruct(struct)); } t.end(); }); tape('de/serializing union', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); }; } const spec = { b: new index_1.MuBoolean(), u: new index_1.MuUTF8(), f: new index_1.MuFloat32(), a: new index_1.MuArray(new index_1.MuFloat32(), Infinity), sa: new index_1.MuSortedArray(new index_1.MuFloat32(), Infinity), v: new index_1.MuVector(new index_1.MuFloat32(), 16), d: new index_1.MuDictionary(new index_1.MuFloat32(), Infinity), }; const tags = Object.keys(spec); const numTags = tags.length; const strings = [ '', '<a href="https://github.com/mikolalysenko/mudb/">mudb</a>', 'Iñtërnâtiônàlizætiøn☃💩', ]; function randUnionCase() { const type = tags[Math.random() * numTags | 0]; let data; switch (type) { case 'b': data = random_1.randBool(); break; case 'u': data = strings[Math.random() * strings.length | 0]; break; case 'f': data = random_1.randFloat32(); break; case 'a': data = randArray(); break; case 'sa': data = randArray().sort(compare); break; case 'v': data = randVec(16); break; case 'd': data = randDict(); break; } return { type, data, }; } const union = new index_1.MuUnion(spec); const testPair = createTestPair(t, union); for (let i = 0; i < 1000; ++i) { testPair(randUnionCase(), randUnionCase()); } t.end(); }); tape('de/serializing bytes', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, b); test(b, a); }; } function randUint8Array() { const a = new Uint8Array(Math.ceil(Math.random() * 100)); for (let i = 0; i < a.length; ++i) { a[i] = random_1.randUint8(); } return a; } const bytes = new index_1.MuBytes(); const testPair = createTestPair(t, bytes); testPair(new Uint8Array([]), randUint8Array()); for (let i = 0; i < 1000; ++i) { const a = randUint8Array(); const b = randUint8Array(); if (!bytes.equal(a, b)) { testPair(a, b); } } t.end(); }); tape('de/serializing dictionary', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test({}, a); test({}, b); }; } function randNestedDict() { const nd = {}; let code = 97 + Math.random() * 6 | 0; for (let i = Math.random() * 6 | 0; i > 0; --i) { nd[String.fromCharCode(code++)] = randDict(); } return nd; } t.test('simple dictionary', (st) => { const dictionary = new index_1.MuDictionary(new index_1.MuFloat32(), Infinity); const testPair = createTestPair(st, dictionary); testPair({ f: 0 }, { f: 0.5 }); testPair({ f: 0, g: 0.5 }, { f: 0, g: 1 }); testPair({ f: 0, g: 0.5 }, { f: 1, g: 0.5 }); testPair({ f: 0, g: 0.5 }, { f: 1, g: 1.5 }); testPair({ f: 0 }, { g: 0 }); testPair({ f: 0 }, { g: 0.5 }); testPair({ f: 0, g: 0.5 }, { g: 1, h: 1.5 }); testPair({ f: 0, g: 0.5 }, { h: 1, i: 1.5 }); testPair({ f: 0 }, { f: 0, g: 0.5 }); testPair({ f: 0 }, { f: 0.5, g: 1 }); for (let i = 0; i < 1000; ++i) { testPair(randDict(), randDict()); } st.end(); }); t.test('nested dictionary', (st) => { const dictionary = new index_1.MuDictionary(new index_1.MuDictionary(new index_1.MuFloat32(), Infinity), Infinity); const testPair = createTestPair(st, dictionary); testPair({ a: { a: 0 } }, { a: { b: 0.5 } }); testPair({ a: { a: 0 }, b: { a: 0 } }, { a: { a: 0 }, b: { b: 0.5 } }); testPair({ a: { a: 0 }, b: { a: 0 } }, { a: { b: 0.5 }, b: { a: 0 } }); testPair({ a: { a: 0 }, b: { a: 0 } }, { a: { b: 0.5 }, b: { b: 0.5 } }); testPair({ a: { a: 0 } }, { b: { a: 0 } }); testPair({ a: { a: 0 } }, { b: { b: 0 } }); testPair({ a: { a: 0 }, b: { a: 0 } }, { b: { b: 0.5 }, c: { a: 0 } }); testPair({ a: { a: 0 }, b: { a: 0 } }, { c: { a: 0 }, d: { a: 0 } }); testPair({ a: { a: 0 } }, { a: { b: 0.5 }, b: { a: 0 } }); testPair({ a: { a: 0 } }, { b: { a: 0.5 }, c: { a: 0.5 } }); for (let i = 0; i < 1000; ++i) { testPair(randNestedDict(), randNestedDict()); } st.end(); }); t.end(); }); function randVec(dimension) { const v = new index_1.MuVector(new index_1.MuFloat32(), dimension).alloc(); for (let i = 0; i < v.length; ++i) { v[i] = random_1.randFloat32(); } return v; } tape('de/serializing vector', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, a); test(b, b); test(a, b); test(b, a); test(schema.alloc(), a); test(schema.alloc(), b); }; } t.test('vec0', (st) => { const vector = new index_1.MuVector(new index_1.MuFloat32(), 0); const test = createTest(st, vector); const zeroA = vector.alloc(); const zeroB = vector.alloc(); test(zeroA, zeroB); st.end(); }); t.test('vec1', (st) => { const vector = new index_1.MuVector(new index_1.MuFloat32(), 1); const testPair = createTestPair(st, vector); for (let i = 0; i < 10; ++i) { testPair(randVec(1), randVec(1)); } st.end(); }); t.test('vec2', (st) => { const vector = new index_1.MuVector(new index_1.MuFloat32(), 2); const testPair = createTestPair(st, vector); for (let i = 0; i < 100; ++i) { testPair(randVec(2), randVec(2)); } st.end(); }); t.test('vec3', (st) => { const vector = new index_1.MuVector(new index_1.MuFloat32(), 3); const testPair = createTestPair(st, vector); for (let i = 0; i < 1000; ++i) { testPair(randVec(3), randVec(3)); } st.end(); }); t.test('vec10000', (st) => { const vector = new index_1.MuVector(new index_1.MuFloat32(), 10000); const testPair = createTestPair(st, vector); for (let i = 0; i < 10; ++i) { testPair(randVec(10000), randVec(10000)); } st.end(); }); t.end(); }); tape('de/serializing date', (t) => { const date = new index_1.MuDate(); const test = createTest(t, date); const d1 = date.alloc(); const d2 = date.alloc(); d2.setTime(0); test(d1, d1); test(d2, d2); test(d1, d2); test(d2, d1); t.end(); }); tape('de/serializing json', (t) => { function createTestPair(t_, schema) { const test = createTest(t_, schema); return (a, b) => { test(a, b); test(b, a); }; } const json = new index_1.MuJSON(); const testPair = createTestPair(t, json); testPair({}, { a: 0.5, b: false, c: '', d: [] }); testPair([], [1e9, true, 'Iñtërnâtiônàlizætiøn☃💩', {}]); t.end(); }); tape('de/serializing option', (t) => { const op = new index_1.MuOption(new index_1.MuFloat32()); const of = new index_1.MuOption(new index_1.MuStruct({ op: op })); const testPrimitive = createTest(t, op); testPrimitive(undefined, undefined); testPrimitive(undefined, 4); testPrimitive(4, undefined); testPrimitive(undefined, 0); testPrimitive(0, undefined); testPrimitive(4, 4); testPrimitive(3, 4); const testFunctor = createTest(t, of); testFunctor(undefined, undefined); testFunctor({ op: undefined }, undefined); testFunctor(undefined, { op: undefined }); testFunctor({ op: 4 }, undefined); testFunctor(undefined, { op: 4 }); testFunctor({ op: 4 }, { op: 3 }); testFunctor({ op: 4 }, { op: 4 }); testFunctor({ op: 0 }, { op: 4 }); testFunctor({ op: 4 }, { op: 0 }); testFunctor({ op: undefined }, { op: 0 }); testFunctor({ op: 0 }, { op: undefined }); testFunctor(undefined, { op: 0 }); testFunctor({ op: 0 }, undefined); t.end(); }); //# sourceMappingURL=serialization.js.map