diffusion
Version:
Diffusion JavaScript client
256 lines (189 loc) • 6.77 kB
JavaScript
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;