json-joy
Version:
Collection of libraries for building collaborative editing apps.
184 lines (183 loc) • 6.25 kB
JavaScript
import * as nodes from '../../../nodes';
import { ClockDecoder } from '../../../../json-crdt-patch/codec/clock/ClockDecoder';
import { CrdtReader } from '../../../../json-crdt-patch/util/binary/CrdtReader';
import { Timestamp } from '../../../../json-crdt-patch/clock';
import { Model, UNDEFINED } from '../../../model/Model';
import { CborDecoderBase } from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase';
import { SESSION } from '../../../../json-crdt-patch/constants';
import { CRDT_MAJOR } from './constants';
export class Decoder extends CborDecoderBase {
doc;
clockDecoder = undefined;
time = -1;
constructor() {
super(new CrdtReader());
}
decode(data, model) {
this.clockDecoder = undefined;
this.time = -1;
const reader = this.reader;
reader.reset(data);
const isServerTime = reader.peak() & 0b10000000;
if (isServerTime) {
reader.x++;
const time = (this.time = reader.vu57());
if (!model)
model = Model.withServerClock(time);
}
else {
this.decodeClockTable();
if (!model) {
const clock = this.clockDecoder.clock;
model = Model.withLogicalClock(clock);
}
}
this.doc = model;
model.root = new nodes.RootNode(this.doc, this.cRoot().id);
this.clockDecoder = undefined;
return model;
}
decodeClockTable() {
const reader = this.reader;
const clockTableOffset = reader.u32();
const offset = reader.x;
reader.x += clockTableOffset;
const length = reader.vu57();
const sessionId = reader.vu57();
const time = reader.vu57();
this.clockDecoder = new ClockDecoder(sessionId, time);
for (let i = 1; i < length; i++) {
const sid = reader.vu57();
const time = reader.vu57();
this.clockDecoder.pushTuple(sid, time);
}
reader.x = offset;
}
ts() {
const decoderTime = this.time;
const isLogical = decoderTime < 0;
if (isLogical) {
const [sessionIndex, timeDiff] = this.reader.id();
return this.clockDecoder.decodeId(sessionIndex, timeDiff);
}
else {
return new Timestamp(SESSION.SERVER, this.reader.vu57());
}
}
cRoot() {
const reader = this.reader;
const peek = reader.uint8[reader.x];
return !peek ? UNDEFINED : this.cNode();
}
cNode() {
const reader = this.reader;
const id = this.ts();
const octet = reader.u8();
const major = octet >> 5;
const minor = octet & 0b11111;
switch (major) {
case CRDT_MAJOR.CON:
return this.cCon(id, minor);
case CRDT_MAJOR.VAL:
return this.cVal(id);
case CRDT_MAJOR.OBJ:
return this.cObj(id, minor !== 0b11111 ? minor : reader.vu57());
case CRDT_MAJOR.VEC:
return this.cVec(id, minor !== 0b11111 ? minor : reader.vu57());
case CRDT_MAJOR.STR:
return this.cStr(id, minor !== 0b11111 ? minor : reader.vu57());
case CRDT_MAJOR.BIN:
return this.cBin(id, minor !== 0b11111 ? minor : reader.vu57());
case CRDT_MAJOR.ARR:
return this.cArr(id, minor !== 0b11111 ? minor : reader.vu57());
}
throw new Error('UNKNOWN_NODE');
}
cCon(id, length) {
const doc = this.doc;
const data = !length ? this.val() : this.ts();
const node = new nodes.ConNode(id, data);
doc.index.set(id, node);
return node;
}
cVal(id) {
const child = this.cNode();
const doc = this.doc;
const node = new nodes.ValNode(doc, id, child.id);
doc.index.set(id, node);
return node;
}
cObj(id, length) {
const obj = new nodes.ObjNode(this.doc, id);
for (let i = 0; i < length; i++)
this.cObjChunk(obj);
this.doc.index.set(id, obj);
return obj;
}
cObjChunk(obj) {
const key = this.key();
obj.keys.set(key, this.cNode().id);
}
cVec(id, length) {
const reader = this.reader;
const obj = new nodes.VecNode(this.doc, id);
const elements = obj.elements;
for (let i = 0; i < length; i++) {
const octet = reader.peak();
if (!octet) {
reader.x++;
elements.push(undefined);
}
else
elements.push(this.cNode().id);
}
this.doc.index.set(id, obj);
return obj;
}
cStr(id, length) {
const node = new nodes.StrNode(id);
if (length)
node.ingest(length, this.cStrChunk);
this.doc.index.set(id, node);
return node;
}
cStrChunk = () => {
const id = this.ts();
const val = this.val();
if (typeof val === 'string')
return new nodes.StrChunk(id, val.length, val);
return new nodes.StrChunk(id, ~~val, '');
};
cBin(id, length) {
const node = new nodes.BinNode(id);
if (length)
node.ingest(length, this.cBinChunk);
this.doc.index.set(id, node);
return node;
}
cBinChunk = () => {
const id = this.ts();
const reader = this.reader;
const [deleted, length] = reader.b1vu56();
if (deleted)
return new nodes.BinChunk(id, length, undefined);
return new nodes.BinChunk(id, length, reader.buf(length));
};
cArr(id, length) {
const obj = new nodes.ArrNode(this.doc, id);
if (length)
obj.ingest(length, this.cArrChunk);
this.doc.index.set(id, obj);
return obj;
}
cArrChunk = () => {
const id = this.ts();
const [deleted, length] = this.reader.b1vu56();
if (deleted)
return new nodes.ArrChunk(id, length, undefined);
const ids = [];
for (let i = 0; i < length; i++)
ids.push(this.cNode().id);
return new nodes.ArrChunk(id, length, ids);
};
}