json-joy
Version:
Collection of libraries for building collaborative editing apps.
184 lines (183 loc) • 7.02 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decoder = void 0;
const tslib_1 = require("tslib");
const ClockDecoder_1 = require("../../../../json-crdt-patch/codec/clock/ClockDecoder");
const CrdtReader_1 = require("../../../../json-crdt-patch/util/binary/CrdtReader");
const Model_1 = require("../../../model/Model");
const CborDecoderBase_1 = require("@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase");
const nodes = tslib_1.__importStar(require("../../../nodes"));
const constants_1 = require("../../structural/binary/constants");
const insertion_1 = require("@jsonjoy.com/util/lib/sort/insertion");
const constants_2 = require("../../../../json-crdt-patch/constants");
class Decoder {
constructor() {
this.clockDecoder = undefined;
this.time = -1;
this.decoder = new CborDecoderBase_1.CborDecoderBase(new CrdtReader_1.CrdtReader());
}
decode(view, meta) {
this.clockDecoder = undefined;
this.time = -1;
this.decoder.reader.reset(meta);
this.decodeClockTable();
const clock = this.clockDecoder.clock;
this.doc = Model_1.Model.withLogicalClock(clock);
this.doc.root = new nodes.RootNode(this.doc, this.cRoot(view).id);
this.clockDecoder = undefined;
return this.doc;
}
decodeClockTable() {
const reader = this.decoder.reader;
const clockTableOffset = reader.u32();
const offset = reader.x;
reader.x += clockTableOffset;
const length = reader.vu57();
const sessionId = reader.vu57();
const time = reader.vu57();
this.clockDecoder = new ClockDecoder_1.ClockDecoder(sessionId, time);
for (let i = 1; i < length; i++) {
const sid = reader.vu57();
const time = reader.vu57();
this.clockDecoder.pushTuple(sid, time);
}
reader.x = offset;
}
ts() {
const decoderTime = this.time;
const [sessionIndex, timeDiff] = this.decoder.reader.id();
return this.clockDecoder.decodeId(sessionIndex, timeDiff);
}
cRoot(view) {
const reader = this.decoder.reader;
const peek = reader.uint8[reader.x];
return !peek ? Model_1.UNDEFINED : this.cNode(view);
}
cNode(view) {
const reader = this.decoder.reader;
const id = this.ts();
const octet = reader.u8();
const major = octet >> 5;
const minor = octet & 0b11111;
const length = minor < 24 ? minor : minor === 24 ? reader.u8() : minor === 25 ? reader.u16() : reader.u32();
switch (major) {
case constants_1.CRDT_MAJOR.CON:
return this.cCon(view, id, length);
case constants_1.CRDT_MAJOR.VAL:
return this.cVal(view, id);
case constants_1.CRDT_MAJOR.OBJ:
return this.cObj(view, id, length);
case constants_1.CRDT_MAJOR.VEC:
return this.cVec(view, id, length);
case constants_1.CRDT_MAJOR.STR:
return this.cStr(view, id, length);
case constants_1.CRDT_MAJOR.BIN:
return this.cBin(view, id, length);
case constants_1.CRDT_MAJOR.ARR:
return this.cArr(view, id, length);
}
throw new Error('UNKNOWN_NODE');
}
cCon(view, id, length) {
const doc = this.doc;
const node = new nodes.ConNode(id, length ? this.ts() : view);
doc.index.set(id, node);
return node;
}
cVal(view, id) {
const child = this.cNode(view);
const doc = this.doc;
const node = new nodes.ValNode(doc, id, child.id);
doc.index.set(id, node);
return node;
}
cObj(view, id, length) {
const obj = new nodes.ObjNode(this.doc, id);
if (!view || typeof view !== 'object')
throw new Error('INVALID_OBJ');
const keys = (0, insertion_1.sort)(Object.keys(view));
if (keys.length !== length)
throw new Error('INVALID_OBJ');
const objKeys = obj.keys;
for (let i = 0; i < length; i++) {
const key = keys[i];
const childNode = this.cNode(view[key]);
objKeys.set(key, childNode.id);
}
this.doc.index.set(id, obj);
return obj;
}
cVec(view, id, length) {
const obj = new nodes.VecNode(this.doc, id);
if (!Array.isArray(view) || view.length !== length)
throw new Error('INVALID_VEC');
const elements = obj.elements;
const reader = this.decoder.reader;
for (let i = 0; i < length; i++) {
const child = this.cNode(view[i]);
const childId = child.id;
if (childId.sid === constants_2.SESSION.SYSTEM)
elements.push(undefined);
else
elements.push(childId);
}
this.doc.index.set(id, obj);
return obj;
}
cStr(view, id, length) {
if (typeof view !== 'string')
throw new Error('INVALID_STR');
const node = new nodes.StrNode(id);
const reader = this.decoder.reader;
let offset = 0;
node.ingest(length, () => {
const id = this.ts();
const [deleted, span] = reader.b1vu56();
if (deleted)
return new nodes.StrChunk(id, span, '');
const text = view.slice(offset, offset + span);
offset += span;
return new nodes.StrChunk(id, text.length, text);
});
this.doc.index.set(id, node);
return node;
}
cBin(view, id, length) {
if (!(view instanceof Uint8Array))
throw new Error('INVALID_BIN');
const node = new nodes.BinNode(id);
const reader = this.decoder.reader;
let offset = 0;
node.ingest(length, () => {
const id = this.ts();
const [deleted, span] = reader.b1vu56();
if (deleted)
return new nodes.BinChunk(id, span, undefined);
const slice = view.slice(offset, offset + span);
offset += span;
return new nodes.BinChunk(id, slice.length, slice);
});
this.doc.index.set(id, node);
return node;
}
cArr(view, id, length) {
if (!Array.isArray(view))
throw new Error('INVALID_ARR');
const obj = new nodes.ArrNode(this.doc, id);
const reader = this.decoder.reader;
let i = 0;
obj.ingest(length, () => {
const id = this.ts();
const [deleted, span] = reader.b1vu56();
if (deleted)
return new nodes.ArrChunk(id, span, undefined);
const ids = [];
for (let j = 0; j < span; j++)
ids.push(this.cNode(view[i++]).id);
return new nodes.ArrChunk(id, span, ids);
});
this.doc.index.set(id, obj);
return obj;
}
}
exports.Decoder = Decoder;