json-joy
Version:
Collection of libraries for building collaborative editing apps.
185 lines (184 loc) • 6.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Encoder = void 0;
const tslib_1 = require("tslib");
const nodes = tslib_1.__importStar(require("../../../nodes"));
const ClockEncoder_1 = require("../../../../json-crdt-patch/codec/clock/ClockEncoder");
const CrdtWriter_1 = require("../../../../json-crdt-patch/util/binary/CrdtWriter");
const clock_1 = require("../../../../json-crdt-patch/clock");
const CborEncoder_1 = require("@jsonjoy.com/json-pack/lib/cbor/CborEncoder");
const constants_1 = require("../../../../json-crdt-patch/constants");
const constants_2 = require("../../structural/binary/constants");
const insertion_1 = require("@jsonjoy.com/util/lib/sort/insertion");
const Model_1 = require("../../../model/Model");
class Encoder {
constructor() {
this.clockEncoder = new ClockEncoder_1.ClockEncoder();
this.time = 0;
this.viewEncoder = new CborEncoder_1.CborEncoder(new CrdtWriter_1.CrdtWriter());
this.metaEncoder = new CborEncoder_1.CborEncoder(new CrdtWriter_1.CrdtWriter());
this.index = 0;
this.cTableEntry = (entry) => {
const clock = entry.clock;
const writer = this.metaEncoder.writer;
writer.vu57(clock.sid);
writer.vu57(clock.time);
};
this.ts = (ts) => {
const relativeId = this.clockEncoder.append(ts);
this.metaEncoder.writer.id(relativeId.sessionIndex, relativeId.timeDiff);
};
}
encode(doc) {
this.doc = doc;
this.index = 0;
this.metaEncoder.writer.reset();
this.encodeLogical(doc);
return [this.viewEncoder.writer.flush(), this.metaEncoder.writer.flush()];
}
encodeLogical(model) {
const writer = this.metaEncoder.writer;
this.clockEncoder.reset(model.clock);
writer.ensureCapacity(4);
const x0 = writer.x0;
const x = writer.x;
writer.x += 4;
this.cRoot(model.root);
this.encodeClockTable(x0, x);
}
encodeClockTable(x0, x) {
const writer = this.metaEncoder.writer;
const shift = writer.x0 - x0;
writer.view.setUint32(writer.x0 + (x - x0), writer.x - x - shift - 4);
const clockEncoder = this.clockEncoder;
const table = clockEncoder.table;
const length = table.size;
writer.vu57(length);
table.forEach(this.cTableEntry);
}
cRoot(root) {
const val = root.val;
if (val.sid === constants_1.SESSION.SYSTEM)
this.metaEncoder.writer.u8(0);
else
this.cNode(root.node());
}
writeTL(majorOverlay, length) {
const writer = this.metaEncoder.writer;
if (length < 24)
writer.u8(majorOverlay + length);
else if (length <= 0xff)
writer.u16(((majorOverlay + 24) << 8) + length);
else if (length <= 0xffff)
writer.u8u16(majorOverlay + 25, length);
else
writer.u8u32(majorOverlay + 26, length);
}
cNode(node) {
// TODO: PERF: use a switch?
if (node instanceof nodes.ConNode)
this.cCon(node);
else if (node instanceof nodes.ValNode)
this.cVal(node);
else if (node instanceof nodes.StrNode)
this.cStr(node);
else if (node instanceof nodes.ObjNode)
this.cObj(node);
else if (node instanceof nodes.VecNode)
this.cVec(node);
else if (node instanceof nodes.ArrNode)
this.cArr(node);
else if (node instanceof nodes.BinNode)
this.cBin(node);
}
cCon(node) {
const val = node.val;
this.ts(node.id);
const metaEncoder = this.metaEncoder;
if (val instanceof clock_1.Timestamp) {
this.viewEncoder.writeNull();
metaEncoder.writer.u8(1); // this.writeTL(CRDT_MAJOR_OVERLAY.CON, 1);
this.ts(val);
}
else {
this.viewEncoder.writeAny(val);
metaEncoder.writer.u8(0); // this.writeTL(CRDT_MAJOR_OVERLAY.CON, 0);
}
}
cVal(node) {
this.ts(node.id);
this.metaEncoder.writer.u8(0b00100000); // this.writeTL(CRDT_MAJOR_OVERLAY.VAL, 0);
this.cNode(node.node());
}
cObj(node) {
this.ts(node.id);
const keys = (0, insertion_1.sort)([...node.keys.keys()]);
const size = keys.length;
this.writeTL(constants_2.CRDT_MAJOR_OVERLAY.OBJ, size);
const viewEncoder = this.viewEncoder;
viewEncoder.writeObjHdr(size);
for (let i = 0; i < size; i++) {
const key = keys[i];
viewEncoder.writeStr(key);
this.cNode(node.get(key));
}
}
cVec(node) {
this.ts(node.id);
const elements = node.elements;
const length = elements.length;
this.writeTL(constants_2.CRDT_MAJOR_OVERLAY.VEC, length);
this.viewEncoder.writeArrHdr(length);
const index = this.doc.index;
for (let i = 0; i < length; i++) {
const elementId = elements[i];
if (!elementId)
this.cCon(Model_1.UNDEFINED);
else
this.cNode(index.get(elementId));
}
}
cStr(node) {
const ts = this.ts;
ts(node.id);
this.writeTL(constants_2.CRDT_MAJOR_OVERLAY.STR, node.count);
this.viewEncoder.writeStr(node.view());
const writer = this.metaEncoder.writer;
for (let chunk = node.first(); chunk; chunk = node.next(chunk)) {
ts(chunk.id);
writer.b1vu56(~~chunk.del, chunk.span);
}
}
cBin(node) {
const ts = this.ts;
ts(node.id);
this.writeTL(constants_2.CRDT_MAJOR_OVERLAY.BIN, node.count);
this.viewEncoder.writeBin(node.view());
const writer = this.metaEncoder.writer;
for (let chunk = node.first(); chunk; chunk = node.next(chunk)) {
ts(chunk.id);
writer.b1vu56(~~chunk.del, chunk.span);
}
}
cArr(node) {
const ts = this.ts;
ts(node.id);
this.writeTL(constants_2.CRDT_MAJOR_OVERLAY.ARR, node.count);
this.viewEncoder.writeArrHdr(node.length());
const writer = this.metaEncoder.writer;
const index = this.doc.index;
for (let chunk = node.first(); chunk; chunk = node.next(chunk)) {
ts(chunk.id);
const deleted = chunk.del;
const span = chunk.span;
writer.b1vu56(~~deleted, span);
if (!deleted) {
const elements = chunk.data;
const elementsLength = elements.length;
for (let i = 0; i < elementsLength; i++)
this.cNode(index.get(elements[i]));
}
}
}
}
exports.Encoder = Encoder;