UNPKG

diffusion

Version:

Diffusion JavaScript client

256 lines (189 loc) 6.77 kB
var JSONPointer = require('data/json/json-pointer'); var Tokeniser = require('cbor/tokeniser'); var consts = require('cbor/consts'); /* function rangeToLong(start, end) { return Long.fromInt(end).shiftLeft(32).add(start); } */ function ArrayAccumulator(tokeniser, base, start, next) { AbstractAccumulator.call(this, tokeniser, base, start, next); this.take = function(consumer, base, firstAccumulatedIndex, index, start, length) { consumer.accept(base.withIndex(index), start, length); }; this.currentPointer = function(base, total) { return base.withIndex(total); }; } function ObjectAccumulator(tokeniser, base, start, next) { AbstractAccumulator.call(this, tokeniser, base, start, next); var super_add = this.add; var pendingKeyName = ""; var pendingKey = true; var keys = []; this.add = function(tokenEnd) { if (pendingKey) { this.setNextStart(tokenEnd); pendingKeyName = tokeniser.getToken().value; pendingKey = false; } else { keys[this.accumulated()] = pendingKeyName; pendingKey = true; super_add(tokenEnd); } return !pendingKey; }; this.take = function(consumer, base, firstAccumulatedIndex, index, start, length) { consumer.accept(base.withKey(keys[index - firstAccumulatedIndex]), start, length); }; this.currentPointer = function(base) { return base.withKey(pendingKeyName); }; } function RootAccumulator(tokeniser) { AbstractAccumulator.call(this, tokeniser, JSONPointer.ROOT, -1, 0); this.take = function(consumer, base, firstAccumulatedIndex, index, start, length) { if (this.total() > 1) { throw new Error("Invalid JSON: multiple values found"); } consumer.accept(base, start, length); }; this.currentPointer = function(base) { return base; }; } function AbstractAccumulator(tokeniser, base, start, next) { var tokenRange = []; var accumulated = 0; var total = 0; var startOffset = start; var nextOffset = next; var self = this; this.total = function() { return total; }; this.accumulated = function() { return accumulated; }; this.newArray = function(start, next) { return new ArrayAccumulator(tokeniser, self.currentPointer(base, total), start, next); }; this.newObject = function(start, next) { return new ObjectAccumulator(tokeniser, self.currentPointer(base, total), start, next); }; this.notEmptyAndSplitBy = function(offset) { return startOffset < offset && total !== 0; }; this.add = function(next) { tokenRange[accumulated] = [nextOffset, next]; nextOffset = next; ++accumulated; ++total; return false; }; this.setNextStart = function(offset) { nextOffset = offset; }; this.skip = function() { ++total; accumulated = 0; }; this.takeAll = function(consumer) { var next = total; var begin = next - accumulated; for (var i = 0; i < accumulated; ++i) { var range = tokenRange[i]; var start = range[0]; var end = range[1]; self.take(consumer, base, begin, begin + i, start, end - start); } accumulated = 0; }; this.splitStructureEnd = function(consumer) { consumer.splitStructureEnd(base, total, startOffset, nextOffset + 1 - startOffset); }; this.toString = function() { return base.toString(); }; } function SpanParser(json) { var tokeniser = new Tokeniser(json.$buffer, json.$offset, json.$length); var structure = new RootAccumulator(tokeniser); var parentStack = []; var startOffset = tokeniser.offset(); var self = this; this.spanToNext = function(offset, result) { return self.spanTo(offset, result, true); }; /*eslint complexity: ["error", 20]*/ this.spanTo = function(offset, result, atLeastOne) { var start = self.nextByte(); if (start >= offset) { return 0; } var lastHeight = parentStack.length; var consumeFirstValue = atLeastOne; var next = start; var t; do { t = tokeniser.nextToken(); var tokenStart = next; next = self.nextByte(); if (t === null) { if (parentStack.length !== 0) { throw new Error("Invalid structure"); } break; } else if (t.type === consts.tokens.VALUE) { consumeFirstValue = structure.add(next); } else if (t.type === consts.tokens.ARRAY_START) { parentStack.push(structure); structure = structure.newArray(tokenStart, next); } else if (t.type === consts.tokens.MAP_START) { parentStack.push(structure); structure = structure.newObject(tokenStart, next); } else if (consts.isStructEnd(t.type)) { var parent = parentStack.pop(); if (structure.notEmptyAndSplitBy(start)) { structure.takeAll(result); structure.splitStructureEnd(result); parent.skip(); parent.setNextStart(next); } else { parent.add(next); } structure = parent; consumeFirstValue = false; } } while (consumeFirstValue || next < offset || self.nextTokenIsStructEnd(t, next)); for (var i = 0; i < parentStack.length; ++i) { parentStack[i].takeAll(result); } structure.takeAll(result); return parentStack.length - lastHeight; }; this.nextTokenIsStructEnd = function(t, next) { var context = tokeniser.getContext(); if (consts.isStructStart(t.type)) { return context.expected() === 0; } return context.acceptsBreakMarker() && next < json.$length && (json.$buffer[json.$offset + next] & 0x1F) === consts.additional.BREAK || !context.hasRemaining(); }; this.nextByte = function() { return tokeniser.offset() - startOffset; }; this.toString = function() { var parts = ['SpanParser', ' next=', self.nextByte(), ' [']; parentStack.forEach(function(x) { parts.push(x); parts.push(', '); }); parts.push(structure); parts.push(']'); return parts.join(''); }; } module.exports = SpanParser;