UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

246 lines (245 loc) 8.55 kB
import * as operations from '../../operations'; import { JsonCrdtPatchOpcodeOverlay } from '../../constants'; import { CrdtWriter } from '../../util/binary/CrdtWriter'; import { Timestamp } from '../../clock'; import { CborEncoder } from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; /** * JSON CRDT Patch "binary" codec encoder. */ export class Encoder extends CborEncoder { writer; patchSid = 0; /** * Creates a new encoder instance. * * @param writer An optional custom implementation of CRDT writer. */ constructor(writer = new CrdtWriter()) { super(writer); this.writer = writer; } /** * Encodes a JSON CRDT Patch into a {@link Uint8Array} blob. * * @param patch A JSON CRDT Patch to encode. * @returns A {@link Uint8Array} blob containing the encoded JSON CRDT Patch. */ encode(patch) { this.writer.reset(); const id = patch.getId(); const sid = (this.patchSid = id.sid); const writer = this.writer; writer.vu57(sid); writer.vu57(id.time); const meta = patch.meta; if (meta === undefined) this.writeUndef(); else this.writeArr([meta]); this.encodeOperations(patch); return writer.flush(); } encodeOperations(patch) { const ops = patch.ops; const length = ops.length; this.writer.vu57(length); for (let i = 0; i < length; i++) this.encodeOperation(ops[i]); } encodeId(id) { const sessionId = id.sid; const time = id.time; const writer = this.writer; if (sessionId === this.patchSid) { writer.b1vu56(0, time); } else { writer.b1vu56(1, time); writer.vu57(sessionId); } } encodeTss(span) { this.encodeId(span); this.writer.vu57(span.span); } writeInsStr(length, obj, ref, str) { const writer = this.writer; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_str + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_str); writer.vu57(length); } this.encodeId(obj); this.encodeId(ref); return writer.utf8(str); } encodeOperation(op) { const writer = this.writer; const constr = op.constructor; switch (constr) { case operations.NewConOp: { const operation = op; const val = operation.val; if (val instanceof Timestamp) { writer.u8(JsonCrdtPatchOpcodeOverlay.new_con + 1); this.encodeId(val); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.new_con); this.writeAny(val); } break; } case operations.NewValOp: { writer.u8(JsonCrdtPatchOpcodeOverlay.new_val); break; } case operations.NewObjOp: { writer.u8(JsonCrdtPatchOpcodeOverlay.new_obj); break; } case operations.NewVecOp: { writer.u8(JsonCrdtPatchOpcodeOverlay.new_vec); break; } case operations.NewStrOp: { writer.u8(JsonCrdtPatchOpcodeOverlay.new_str); break; } case operations.NewBinOp: { writer.u8(JsonCrdtPatchOpcodeOverlay.new_bin); break; } case operations.NewArrOp: { writer.u8(JsonCrdtPatchOpcodeOverlay.new_arr); break; } case operations.InsValOp: { const operation = op; writer.u8(JsonCrdtPatchOpcodeOverlay.ins_val); this.encodeId(operation.obj); this.encodeId(operation.val); break; } case operations.InsObjOp: { const operation = op; const data = operation.data; const length = data.length; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_obj + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_obj); writer.vu57(length); } this.encodeId(operation.obj); for (let i = 0; i < length; i++) { const tuple = data[i]; this.writeStr(tuple[0]); this.encodeId(tuple[1]); } break; } case operations.InsVecOp: { const operation = op; const data = operation.data; const length = data.length; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_vec + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_vec); writer.vu57(length); } this.encodeId(operation.obj); for (let i = 0; i < length; i++) { const tuple = data[i]; writer.u8(tuple[0]); this.encodeId(tuple[1]); } break; } case operations.InsStrOp: { const operation = op; const obj = operation.obj; const ref = operation.ref; const str = operation.data; const len1 = str.length; writer.ensureCapacity(24 + len1 * 4); const x = writer.x; const len2 = this.writeInsStr(len1, obj, ref, str); if (len1 !== len2) { writer.x = x; this.writeInsStr(len2, obj, ref, str); } break; } case operations.InsBinOp: { const operation = op; const buf = operation.data; const length = buf.length; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_bin + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_bin); writer.vu57(length); } this.encodeId(operation.obj); this.encodeId(operation.ref); writer.buf(buf, length); break; } case operations.InsArrOp: { const operation = op; const elements = operation.data; const length = elements.length; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_arr + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.ins_arr); writer.vu57(length); } this.encodeId(operation.obj); this.encodeId(operation.ref); for (let i = 0; i < length; i++) this.encodeId(elements[i]); break; } case operations.DelOp: { const operation = op; const what = operation.what; const length = what.length; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.del + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.del); writer.vu57(length); } this.encodeId(operation.obj); for (let i = 0; i < length; i++) this.encodeTss(what[i]); break; } case operations.NopOp: { const operation = op; const length = operation.len; if (length <= 0b111) { writer.u8(JsonCrdtPatchOpcodeOverlay.nop + length); } else { writer.u8(JsonCrdtPatchOpcodeOverlay.nop); writer.vu57(length); } break; } default: { throw new Error('UNKNOWN_OP'); } } } }