UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

156 lines (155 loc) 5.6 kB
import * as nodes from '../../../nodes'; import { ClockTable } from '../../../../json-crdt-patch/codec/clock/ClockTable'; import { CrdtReader } from '../../../../json-crdt-patch/util/binary/CrdtReader'; import { Timestamp, ClockVector } from '../../../../json-crdt-patch/clock'; import { Model, UNDEFINED } from '../../../model/Model'; import { CborDecoderBase } from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase'; import { CRDT_MAJOR } from '../../structural/binary/constants'; export class Decoder { dec; doc; clockTable; constructor(reader) { this.dec = new CborDecoderBase(reader || new CrdtReader()); } decode(fields, ModelConstructor = Model) { const reader = this.dec.reader; reader.reset(fields.c); const clockTable = (this.clockTable = ClockTable.decode(reader)); return this.decodeFields(clockTable, fields, ModelConstructor); } decodeFields(clockTable, fields, ModelConstructor = Model) { const reader = this.dec.reader; const firstClock = clockTable.byIdx[0]; const vectorClock = new ClockVector(firstClock.sid, firstClock.time + 1); const doc = (this.doc = new ModelConstructor(vectorClock)); const root = fields.r; if (root && root.length) { reader.reset(root); const rootValue = this.ts(); doc.root.set(rootValue); } const index = doc.index; const keys = Object.keys(fields); const length = keys.length; for (let i = 0; i < length; i++) { const field = keys[i]; if (field.length < 3) continue; // Skip "c" and "r". const arr = fields[field]; const id = clockTable.parseField(field); reader.reset(arr); const node = this.decodeNode(id); index.set(id, node); } return doc; } ts() { const [sessionIndex, timeDiff] = this.dec.reader.id(); return new Timestamp(this.clockTable.byIdx[sessionIndex].sid, timeDiff); } decodeNode(id) { const reader = this.dec.reader; const octet = reader.u8(); const major = octet >> 5; const minor = octet & 0b11111; const length = minor < 24 ? minor : minor === 24 ? reader.u8() : minor === 25 ? reader.u16() : reader.u32(); switch (major) { case CRDT_MAJOR.CON: return this.decodeCon(id, length); case CRDT_MAJOR.VAL: return this.decodeVal(id); case CRDT_MAJOR.OBJ: return this.decodeObj(id, length); case CRDT_MAJOR.VEC: return this.decodeVec(id, length); case CRDT_MAJOR.STR: return this.decodeStr(id, length); case CRDT_MAJOR.BIN: return this.decodeBin(id, length); case CRDT_MAJOR.ARR: return this.decodeArr(id, length); } return UNDEFINED; } decodeCon(id, length) { const decoder = this.dec; const data = !length ? decoder.val() : this.ts(); const node = new nodes.ConNode(id, data); return node; } decodeVal(id) { const val = this.ts(); const node = new nodes.ValNode(this.doc, id, val); return node; } decodeObj(id, length) { const decoder = this.dec; const obj = new nodes.ObjNode(this.doc, id); const keys = obj.keys; for (let i = 0; i < length; i++) { const key = decoder.val() + ''; const val = this.ts(); keys.set(key, val); } return obj; } decodeVec(id, length) { const reader = this.dec.reader; const node = new nodes.VecNode(this.doc, id); const elements = node.elements; for (let i = 0; i < length; i++) { const octet = reader.u8(); if (!octet) elements.push(undefined); else elements.push(this.ts()); } return node; } decodeStr(id, length) { const node = new nodes.StrNode(id); node.ingest(length, this.decodeStrChunk); return node; } decodeStrChunk = () => { const decoder = this.dec; const reader = decoder.reader; const id = this.ts(); const val = decoder.val(); if (typeof val === 'string') return new nodes.StrChunk(id, val.length, val); return new nodes.StrChunk(id, ~~val, ''); }; decodeBin(id, length) { const node = new nodes.BinNode(id); node.ingest(length, this.decodeBinChunk); return node; } decodeBinChunk = () => { const id = this.ts(); const reader = this.dec.reader; const [deleted, length] = reader.b1vu56(); if (deleted) return new nodes.BinChunk(id, length, undefined); return new nodes.BinChunk(id, length, reader.buf(length)); }; decodeArr(id, length) { const node = new nodes.ArrNode(this.doc, id); node.ingest(length, this.decodeArrChunk); return node; } decodeArrChunk = () => { const id = this.ts(); const reader = this.dec.reader; const [deleted, length] = reader.b1vu56(); if (deleted) return new nodes.ArrChunk(id, length, undefined); else { const data = []; for (let i = 0; i < length; i++) data.push(this.ts()); return new nodes.ArrChunk(id, length, data); } }; }