json-joy
Version:
Collection of libraries for building collaborative editing apps.
187 lines (186 loc) • 6.04 kB
JavaScript
import * as nodes from '../../../nodes';
import { ClockEncoder } from '../../../../json-crdt-patch/codec/clock/ClockEncoder';
import { CrdtWriter } from '../../../../json-crdt-patch/util/binary/CrdtWriter';
import { Timestamp } from '../../../../json-crdt-patch/clock';
import { CborEncoder } from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder';
import { SESSION } from '../../../../json-crdt-patch/constants';
import { CRDT_MAJOR_OVERLAY } from './constants';
export class Encoder extends CborEncoder {
clockEncoder = new ClockEncoder();
time = 0;
doc;
constructor(writer) {
super(writer || new CrdtWriter());
}
encode(doc) {
this.doc = doc;
const writer = this.writer;
writer.reset();
if (doc.clock.sid === SESSION.SERVER)
this.encodeServer(doc);
else
this.encodeLogical(doc);
return writer.flush();
}
encodeLogical(model) {
const writer = this.writer;
this.ts = this.tsLogical;
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);
}
encodeServer(model) {
this.ts = this.tsServer;
const writer = this.writer;
writer.u8(0b10000000);
writer.vu57((this.time = model.clock.time));
this.cRoot(model.root);
}
encodeClockTable(x0, x) {
const writer = this.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);
}
cTableEntry = (entry) => {
const clock = entry.clock;
const writer = this.writer;
writer.vu57(clock.sid);
writer.vu57(clock.time);
};
tsLogical = (ts) => {
const relativeId = this.clockEncoder.append(ts);
this.writer.id(relativeId.sessionIndex, relativeId.timeDiff);
};
tsServer = (ts) => {
this.writer.vu57(ts.time);
};
ts = this.tsLogical;
cRoot(root) {
const val = root.val;
if (val.sid === SESSION.SYSTEM)
this.writer.u8(0);
else
this.cNode(root.node());
}
writeTL(majorOverlay, length) {
const writer = this.writer;
if (length < 0b11111)
writer.u8(majorOverlay | length);
else {
writer.u8(majorOverlay | 0b11111);
writer.vu57(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);
if (val instanceof Timestamp) {
this.writer.u8(1); // this.writeTL(CRDT_MAJOR_OVERLAY.CON, 1);
this.ts(val);
}
else {
this.writer.u8(0); // this.writeTL(CRDT_MAJOR_OVERLAY.CON, 0);
this.writeAny(val);
}
}
cVal(node) {
this.ts(node.id);
this.writer.u8(0b00100000); // this.writeTL(CRDT_MAJOR_OVERLAY.VAL, 0);
this.cNode(node.node());
}
cObj(node) {
this.ts(node.id);
const keys = node.keys;
this.writeTL(CRDT_MAJOR_OVERLAY.OBJ, keys.size);
keys.forEach(this.cKey);
}
cKey = (val, key) => {
this.writeStr(key);
this.cNode(this.doc.index.get(val));
};
cVec(node) {
const elements = node.elements;
const length = elements.length;
this.ts(node.id);
this.writeTL(CRDT_MAJOR_OVERLAY.VEC, length);
const index = this.doc.index;
for (let i = 0; i < length; i++) {
const elementId = elements[i];
if (!elementId)
this.writer.u8(0);
else
this.cNode(index.get(elementId));
}
}
cStr(node) {
const ts = this.ts;
ts(node.id);
this.writeTL(CRDT_MAJOR_OVERLAY.STR, node.count);
for (let chunk = node.first(); chunk; chunk = node.next(chunk)) {
ts(chunk.id);
if (chunk.del)
this.writeUInteger(chunk.span);
else
this.writeStr(chunk.data);
}
}
cBin(node) {
const ts = this.ts;
const writer = this.writer;
ts(node.id);
this.writeTL(CRDT_MAJOR_OVERLAY.BIN, node.count);
for (let chunk = node.first(); chunk; chunk = node.next(chunk)) {
ts(chunk.id);
const length = chunk.span;
const deleted = chunk.del;
writer.b1vu56(~~deleted, length);
if (deleted)
continue;
writer.buf(chunk.data, length);
}
}
cArr(node) {
const ts = this.ts;
const writer = this.writer;
ts(node.id);
this.writeTL(CRDT_MAJOR_OVERLAY.ARR, node.count);
const index = this.doc.index;
for (let chunk = node.first(); chunk; chunk = node.next(chunk)) {
ts(chunk.id);
const span = chunk.span;
const deleted = chunk.del;
writer.b1vu56(~~deleted, span);
if (deleted)
continue;
const nodes = chunk.data;
for (let i = 0; i < span; i++)
this.cNode(index.get(nodes[i]));
}
}
}