UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

187 lines (186 loc) 6.04 kB
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])); } } }