UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

258 lines (257 loc) 9.94 kB
import { OpAdd, OpAnd, OpContains, OpCopy, OpDefined, OpEnds, OpExtend, OpFlip, OpIn, OpInc, OpLess, OpMatches, OpMerge, OpMore, OpMove, OpNot, OpOr, OpRemove, OpReplace, OpSplit, OpStarts, OpStrDel, OpStrIns, OpTest, OpTestString, OpTestStringLen, OpTestType, OpType, OpUndefined, } from '../../op'; import { MsgPackDecoderFast } from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoderFast'; import { OPCODE } from '../../constants'; import { createMatcherDefault } from '../../util'; export class Decoder extends MsgPackDecoderFast { options; constructor(options) { super(); this.options = options; } decode(uint8) { this.reader.reset(uint8); return this.decodePatch(); } decodePatch() { const len = this.decodeArrayHeader(); const ops = []; for (let i = 0; i < len; i++) ops.push(this.decodeOp(undefined)); return ops; } decodeOp(parent) { const length = this.decodeArrayHeader(); const opcode = this.reader.u8(); switch (opcode) { case OPCODE.add: { const path = this.decodeArray(); const value = this.val(); return new OpAdd(path, value); } case OPCODE.and: { const path = this.decodePath(parent); const length = this.decodeArrayHeader(); const ops = []; const op = new OpAnd(path, ops); for (let i = 0; i < length; i++) ops.push(this.decodeOp(op)); return op; } case OPCODE.contains: { const path = this.decodePath(parent); const value = this.decodeString(); const ignoreCase = length > 3; return new OpContains(path, value, ignoreCase); } case OPCODE.copy: { const path = this.decodeArray(); const from = this.decodeArray(); return new OpCopy(path, from); } case OPCODE.defined: { const path = this.decodePath(parent); return new OpDefined(path); } case OPCODE.ends: { const path = this.decodePath(parent); const value = this.decodeString(); const ignoreCase = length > 3; return new OpEnds(path, value, ignoreCase); } case OPCODE.extend: { const path = this.decodeArray(); const props = this.decodeObject(); const deleteNull = length > 3; return new OpExtend(path, props, deleteNull); } case OPCODE.flip: { const path = this.decodeArray(); return new OpFlip(path); } case OPCODE.in: { const path = this.decodePath(parent); const value = this.decodeArray(); return new OpIn(path, value); } case OPCODE.inc: { const path = this.decodePath(parent); const inc = this.val(); return new OpInc(path, inc); } case OPCODE.less: { const path = this.decodePath(parent); const value = this.val(); return new OpLess(path, value); } case OPCODE.matches: { const path = this.decodePath(parent); const value = this.decodeString(); const ignoreCase = length > 3; return new OpMatches(path, value, ignoreCase, this.options.createMatcher || createMatcherDefault); } case OPCODE.merge: { const hasProps = length > 3; const path = this.decodeArray(); const pos = this.val(); const props = hasProps ? this.decodeObject() : null; return new OpMerge(path, pos, props); } case OPCODE.more: { const path = this.decodePath(parent); const value = this.val(); return new OpMore(path, value); } case OPCODE.move: { const path = this.decodeArray(); const from = this.decodeArray(); return new OpMove(path, from); } case OPCODE.not: { const path = this.decodePath(parent); const length = this.decodeArrayHeader(); const ops = []; const op = new OpNot(path, ops); for (let i = 0; i < length; i++) ops.push(this.decodeOp(op)); return op; } case OPCODE.or: { const path = this.decodePath(parent); const length = this.decodeArrayHeader(); const ops = []; const op = new OpOr(path, ops); for (let i = 0; i < length; i++) ops.push(this.decodeOp(op)); return op; } case OPCODE.remove: { const path = this.decodeArray(); const hasOldValue = length > 2; const oldValue = hasOldValue ? this.val() : undefined; return new OpRemove(path, oldValue); } case OPCODE.replace: { const path = this.decodeArray(); const value = this.val(); const hasOldValue = length > 3; const oldValue = hasOldValue ? this.val() : undefined; return new OpReplace(path, value, oldValue); } case OPCODE.split: { const path = this.decodeArray(); const pos = this.val(); const hasProps = length > 3; const props = hasProps ? this.decodeObject() : null; return new OpSplit(path, pos, props); } case OPCODE.starts: { const ignoreCase = length > 3; const path = this.decodePath(parent); const value = this.decodeString(); return new OpStarts(path, value, ignoreCase); } case OPCODE.str_del: { const hasStr = length < 5; const path = this.decodeArray(); const pos = this.val(); if (hasStr) { const str = this.decodeString(); return new OpStrDel(path, pos, str, undefined); } else { this.reader.u8(); const len = this.val(); return new OpStrDel(path, pos, undefined, len); } } case OPCODE.str_ins: { const path = this.decodeArray(); const pos = this.val(); const str = this.decodeString(); return new OpStrIns(path, pos, str); } case OPCODE.test: { const not = length > 3; const path = this.decodePath(parent); const value = this.val(); return new OpTest(path, value, not); } case OPCODE.test_string: { const not = length > 4; const path = this.decodePath(parent); const pos = this.val(); const str = this.decodeString(); return new OpTestString(path, pos, str, not); } case OPCODE.test_string_len: { const not = length > 3; const path = this.decodePath(parent); const len = this.val(); return new OpTestStringLen(path, len, not); } case OPCODE.test_type: { const path = this.decodePath(parent); const type = this.decodeArray(); return new OpTestType(path, type); } case OPCODE.type: { const path = this.decodePath(parent); const value = this.decodeString(); return new OpType(path, value); } case OPCODE.undefined: { const path = this.decodePath(parent); return new OpUndefined(path); } } throw new Error('OP_UNKNOWN'); } decodePath(parent) { const path = this.decodeArray(); if (!parent) return path; return [...parent.path, ...path]; } decodeObject() { const reader = this.reader; const byte = reader.u8(); if (byte <= 0xbf) return this.obj(byte & 0b1111); else if (byte === 0xde) return this.obj(reader.u16()); /* 0xdf */ else return this.obj(reader.u32()); } decodeArray() { const reader = this.reader; const byte = reader.u8(); if (byte < 0b10011111) return this.arr(byte & 0b1111); else if (byte === 0xdc) return this.arr(reader.u16()); else return this.arr(reader.u32()); } decodeArrayHeader() { const reader = this.reader; const byte = reader.u8(); if (byte < 0b10011111) return byte & 0b1111; else if (byte === 0xdc) return reader.u16(); else return reader.u32(); } decodeString() { const reader = this.reader; const byte = reader.u8(); if (byte <= 0xbf) return reader.utf8(byte & 0b11111); else if (byte === 0xd9) return reader.utf8(reader.u8()); else if (byte === 0xda) return reader.utf8(reader.u16()); /* 0xDB */ else return reader.utf8(reader.u32()); } }