UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

178 lines (177 loc) 6.33 kB
import { CrdtReader } from '../../util/binary/CrdtReader'; import { interval, ClockVector, ServerClockVector, Timestamp, } from '../../clock'; import { PatchBuilder } from '../../PatchBuilder'; import { SESSION } from '../../constants'; import { CborDecoder } from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import { JsonCrdtPatchOpcode } from '../../constants'; /** * JSON CRDT Patch "binary" codec decoder. */ export class Decoder extends CborDecoder { builder; patchSid; /** * Creates a new JSON CRDT patch decoder. * * @param reader An optional custom implementation of a CRDT decoder. */ constructor(reader = new CrdtReader()) { super(reader); } /** * Decodes a JSON CRDT patch from a binary blob. * * @param data Binary data to decode. * @returns A JSON CRDT patch. */ decode(data) { this.reader.reset(data); return this.readPatch(); } readPatch() { const reader = this.reader; const sid = reader.vu57(); const time = reader.vu57(); const isServerClock = sid === SESSION.SERVER; const clock = isServerClock ? new ServerClockVector(SESSION.SERVER, time) : new ClockVector(sid, time); this.patchSid = clock.sid; const builder = (this.builder = new PatchBuilder(clock)); const map = this.val(); if (map instanceof Array) builder.patch.meta = map[0]; this.decodeOperations(); return builder.patch; } decodeId() { const reader = this.reader; const [isSessionDifferent, x] = reader.b1vu56(); return isSessionDifferent ? new Timestamp(reader.vu57(), x) : new Timestamp(this.patchSid, x); } decodeTss() { const id = this.decodeId(); const span = this.reader.vu57(); return interval(id, 0, span); } decodeOperations() { const reader = this.reader; const length = reader.vu57(); for (let i = 0; i < length; i++) this.decodeOperation(); } decodeOperation() { const builder = this.builder; const reader = this.reader; const octet = reader.u8(); const opcode = octet >> 3; switch (opcode) { case JsonCrdtPatchOpcode.new_con: { const length = octet & 0b111; builder.const(!length ? this.val() : this.decodeId()); break; } case JsonCrdtPatchOpcode.new_val: { builder.val(); break; } case JsonCrdtPatchOpcode.new_obj: { builder.obj(); break; } case JsonCrdtPatchOpcode.new_vec: { builder.vec(); break; } case JsonCrdtPatchOpcode.new_str: { builder.str(); break; } case JsonCrdtPatchOpcode.new_bin: { builder.bin(); break; } case JsonCrdtPatchOpcode.new_arr: { builder.arr(); break; } case JsonCrdtPatchOpcode.ins_val: { const obj = this.decodeId(); const val = this.decodeId(); builder.setVal(obj, val); break; } case JsonCrdtPatchOpcode.ins_obj: { const length = octet & 0b111 || reader.vu57(); const obj = this.decodeId(); const tuples = []; for (let i = 0; i < length; i++) { const key = this.val(); if (typeof key !== 'string') continue; const value = this.decodeId(); tuples.push([key, value]); } builder.insObj(obj, tuples); break; } case JsonCrdtPatchOpcode.ins_vec: { const length = octet & 0b111 || reader.vu57(); const obj = this.decodeId(); const tuples = []; for (let i = 0; i < length; i++) { const index = this.val(); if (typeof index !== 'number') continue; const value = this.decodeId(); tuples.push([index, value]); } builder.insVec(obj, tuples); break; } case JsonCrdtPatchOpcode.ins_str: { const length = octet & 0b111 || reader.vu57(); const obj = this.decodeId(); const after = this.decodeId(); const str = reader.utf8(length); builder.insStr(obj, after, str); break; } case JsonCrdtPatchOpcode.ins_bin: { const length = octet & 0b111 || reader.vu57(); const obj = this.decodeId(); const after = this.decodeId(); const buf = reader.buf(length); if (!(buf instanceof Uint8Array)) return; builder.insBin(obj, after, buf); break; } case JsonCrdtPatchOpcode.ins_arr: { const length = octet & 0b111 || reader.vu57(); const obj = this.decodeId(); const after = this.decodeId(); const elements = []; for (let i = 0; i < length; i++) elements.push(this.decodeId()); builder.insArr(obj, after, elements); break; } case JsonCrdtPatchOpcode.del: { const length = octet & 0b111 || reader.vu57(); const obj = this.decodeId(); const what = []; for (let i = 0; i < length; i++) what.push(this.decodeTss()); builder.del(obj, what); break; } case JsonCrdtPatchOpcode.nop: { const length = octet & 0b111 || reader.vu57(); builder.nop(length); break; } default: { throw new Error('UNKNOWN_OP'); } } } }