mudb
Version:
Real-time database for multiplayer games
247 lines • 7.85 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const is_primitive_1 = require("./is-primitive");
function assignPrimitive(dst, src) {
const dKeys = Object.keys(dst);
const sKeys = Object.keys(src);
for (let i = 0; i < dKeys.length; ++i) {
const k = dKeys[i];
if (!(k in src)) {
delete dst[k];
}
}
for (let i = 0; i < sKeys.length; ++i) {
const k = sKeys[i];
dst[k] = src[k];
}
return dst;
}
function assignGeneric(schema) {
return (dst, src) => {
const dKeys = Object.keys(dst);
const sKeys = Object.keys(src);
for (let i = 0; i < dKeys.length; ++i) {
const k = dKeys[i];
if (!(k in src)) {
schema.free(dst[k]);
delete dst[k];
}
}
for (let i = 0; i < sKeys.length; ++i) {
const k = sKeys[i];
if (k in dst) {
dst[k] = schema.assign(dst[k], src[k]);
}
else {
dst[k] = schema.clone(src[k]);
}
}
return dst;
};
}
class MuDictionary {
constructor(schema, capacity, identity) {
this.muType = 'dictionary';
this.muData = schema;
this.capacity = capacity;
this.identity = {};
if (identity) {
const keys = Object.keys(identity);
for (let i = 0; i < keys.length; ++i) {
const k = keys[i];
this.identity[k] = schema.clone(identity[k]);
}
}
this.json = {
type: 'dictionary',
valueType: schema.json,
identity: JSON.stringify(this.identity),
};
if (is_primitive_1.isMuPrimitiveType(schema.muType)) {
this.assign = assignPrimitive;
}
else {
this.assign = assignGeneric(schema);
}
}
alloc() {
return {};
}
free(dict) {
const props = Object.keys(dict);
const schema = this.muData;
for (let i = 0; i < props.length; ++i) {
schema.free(dict[props[i]]);
}
}
equal(a, b) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) {
return false;
}
for (let i = aKeys.length - 1; i >= 0; --i) {
if (!(aKeys[i] in b)) {
return false;
}
}
const schema = this.muData;
for (let i = 0; i < aKeys.length; ++i) {
const k = aKeys[i];
if (!schema.equal(a[k], b[k])) {
return false;
}
}
return true;
}
clone(dict) {
const copy = {};
const keys = Object.keys(dict);
const schema = this.muData;
for (let i = 0; i < keys.length; ++i) {
const k = keys[i];
copy[k] = schema.clone(dict[k]);
}
return copy;
}
diff(base, target, out) {
let numDel = 0;
let numPatch = 0;
let numAdd = 0;
out.grow(12);
const head = out.offset;
out.offset += 12;
const bKeys = Object.keys(base).sort();
out.grow(5 * bKeys.length);
for (let i = 0; i < bKeys.length; ++i) {
if (!(bKeys[i] in target)) {
++numDel;
out.writeVarint(i);
}
}
const tKeys = Object.keys(target);
const schema = this.muData;
const newKeys = [];
for (let i = 0; i < tKeys.length; ++i) {
const key = tKeys[i];
if (key in base) {
const prefix = out.offset;
out.grow(5);
out.writeVarint(bKeys.indexOf(key));
if (schema.diff(base[key], target[key], out)) {
++numPatch;
}
else {
out.offset = prefix;
}
}
else {
newKeys.push(key);
}
}
numAdd = newKeys.length;
const numTrackers = Math.ceil(numAdd / 8);
out.grow(numTrackers);
let trackerOffset = out.offset;
out.offset += numTrackers;
let tracker = 0;
for (let i = 0; i < numAdd; ++i) {
const key = newKeys[i];
out.writeString(key);
if (schema.diff(schema.identity, target[key], out)) {
tracker |= 1 << (i & 7);
}
if ((i & 7) === 7) {
out.writeUint8At(trackerOffset++, tracker);
tracker = 0;
}
}
if (numAdd & 7) {
out.writeUint8At(trackerOffset, tracker);
}
if (numDel > 0 || numPatch > 0 || numAdd > 0) {
out.writeUint32At(head, numDel);
out.writeUint32At(head + 4, numPatch);
out.writeUint32At(head + 8, numAdd);
return true;
}
out.offset = head;
return false;
}
patch(base, inp) {
const numDel = inp.readUint32();
const numPatch = inp.readUint32();
const numAdd = inp.readUint32();
const bKeys = Object.keys(base).sort();
const numTargetProps = bKeys.length - numDel + numAdd;
if (numTargetProps > this.capacity) {
throw new Error(`number of target props ${numTargetProps} exceeds capacity ${this.capacity}`);
}
const result = {};
const schema = this.muData;
const keysToDel = {};
for (let i = 0; i < numDel; ++i) {
keysToDel[bKeys[inp.readVarint()]] = true;
}
for (let i = 0; i < bKeys.length; ++i) {
const key = bKeys[i];
if (!keysToDel[key]) {
result[key] = schema.clone(base[key]);
}
}
for (let i = 0; i < numPatch; ++i) {
const idx = inp.readVarint();
const key = bKeys[idx];
if (!key) {
throw new Error(`invalid index of key`);
}
result[key] = schema.patch(base[key], inp);
}
const numFullTrackers = numAdd / 8 | 0;
const numTrackers = Math.ceil(numAdd / 8);
let trackerOffset = inp.offset;
inp.offset += numTrackers;
for (let i = 0; i < numFullTrackers; ++i) {
const tracker = inp.readUint8At(trackerOffset++);
for (let j = 0; j < 8; ++j) {
result[inp.readString()] = tracker & (1 << j) ?
schema.patch(schema.identity, inp) :
schema.clone(schema.identity);
}
}
if (numAdd & 7) {
const tracker = inp.readUint8At(trackerOffset);
for (let i = 0; i < (numAdd & 7); ++i) {
result[inp.readString()] = tracker & (1 << i) ?
schema.patch(schema.identity, inp) :
schema.clone(schema.identity);
}
}
return result;
}
toJSON(dict) {
const json = {};
const keys = Object.keys(dict);
const schema = this.muData;
for (let i = 0; i < keys.length; ++i) {
const k = keys[i];
json[k] = schema.toJSON(dict[k]);
}
return json;
}
fromJSON(x) {
if (Object.prototype.toString.call(x) === '[object Object]') {
const dict = {};
const keys = Object.keys(x);
const schema = this.muData;
for (let i = 0; i < keys.length; ++i) {
const k = keys[i];
dict[k] = schema.fromJSON(x[k]);
}
return dict;
}
return this.clone(this.identity);
}
}
exports.MuDictionary = MuDictionary;
//# sourceMappingURL=dictionary.js.map