Better leverage of stream-json to speed things up a little.
This commit is contained in:
Родитель
93c39e581a
Коммит
672b831ff2
|
@ -7,7 +7,6 @@ import { defined, panic, assert } from "../support/Support";
|
|||
import { isDate, isTime, isDateTime } from "../DateTime";
|
||||
|
||||
const Combo = require("stream-json/Combo");
|
||||
const Source = require("stream-json/Source");
|
||||
|
||||
export enum Tag {
|
||||
Null,
|
||||
|
@ -50,6 +49,21 @@ type Context = {
|
|||
currentNumberIsDouble: boolean | undefined;
|
||||
};
|
||||
|
||||
const methodMap: any = {
|
||||
startObject: "handleStartObject",
|
||||
endObject: "handleEndObject",
|
||||
startArray: "handleStartArray",
|
||||
endArray: "handleEndArray",
|
||||
startNumber: "handleStartNumber",
|
||||
numberChunk: "handleNumberChunk",
|
||||
endNumber: "handleEndNumber",
|
||||
keyValue: "handleKeyValue",
|
||||
stringValue: "handleStringValue",
|
||||
nullValue: "handleNullValue",
|
||||
trueValue: "handleTrueValue",
|
||||
falseValue: "handleFalseValue"
|
||||
};
|
||||
|
||||
export class CompressedJSON {
|
||||
private _rootValue: Value | undefined;
|
||||
|
||||
|
@ -61,6 +75,8 @@ export class CompressedJSON {
|
|||
private _objects: Value[][] = [];
|
||||
private _arrays: Value[][] = [];
|
||||
|
||||
[key: string]: any;
|
||||
|
||||
constructor(
|
||||
private readonly _makeDate: boolean,
|
||||
private readonly _makeTime: boolean,
|
||||
|
@ -68,25 +84,14 @@ export class CompressedJSON {
|
|||
) {}
|
||||
|
||||
async readFromStream(readStream: stream.Readable): Promise<Value> {
|
||||
const combo = new Combo();
|
||||
const jsonSource = new Source([combo]);
|
||||
jsonSource.on("startObject", this.handleStartObject);
|
||||
jsonSource.on("endObject", this.handleEndObject);
|
||||
jsonSource.on("startArray", this.handleStartArray);
|
||||
jsonSource.on("endArray", this.handleEndArray);
|
||||
jsonSource.on("startKey", this.handleStartKey);
|
||||
jsonSource.on("endKey", this.handleEndKey);
|
||||
jsonSource.on("startString", this.handleStartString);
|
||||
jsonSource.on("stringChunk", this.handleStringChunk);
|
||||
jsonSource.on("endString", this.handleEndString);
|
||||
jsonSource.on("startNumber", this.handleStartNumber);
|
||||
jsonSource.on("numberChunk", this.handleNumberChunk);
|
||||
jsonSource.on("endNumber", this.handleEndNumber);
|
||||
jsonSource.on("nullValue", this.handleNullValue);
|
||||
jsonSource.on("trueValue", this.handleTrueValue);
|
||||
jsonSource.on("falseValue", this.handleFalseValue);
|
||||
const combo = new Combo({ packKeys: true, packStrings: true });
|
||||
combo.on("data", (item: any) => {
|
||||
if (typeof methodMap[item.name] === "string") {
|
||||
this[methodMap[item.name]](item.value);
|
||||
}
|
||||
});
|
||||
const promise = new Promise<Value>((resolve, reject) => {
|
||||
jsonSource.on("end", () => {
|
||||
combo.on("end", () => {
|
||||
resolve(this.finish());
|
||||
});
|
||||
combo.on("error", (err: any) => {
|
||||
|
@ -94,7 +99,7 @@ export class CompressedJSON {
|
|||
});
|
||||
});
|
||||
readStream.setEncoding("utf8");
|
||||
readStream.pipe(jsonSource.input);
|
||||
readStream.pipe(combo);
|
||||
readStream.resume();
|
||||
return promise;
|
||||
}
|
||||
|
@ -183,12 +188,12 @@ export class CompressedJSON {
|
|||
this._ctx = this._contextStack.pop();
|
||||
};
|
||||
|
||||
private handleStartObject = (): void => {
|
||||
protected handleStartObject = (): void => {
|
||||
this.pushContext();
|
||||
defined(this._ctx).currentObject = [];
|
||||
};
|
||||
|
||||
private handleEndObject = (): void => {
|
||||
protected handleEndObject = (): void => {
|
||||
const obj = defined(this._ctx).currentObject;
|
||||
if (obj === undefined) {
|
||||
return panic("Object ended but not started");
|
||||
|
@ -197,12 +202,12 @@ export class CompressedJSON {
|
|||
this.commitValue(this.internObject(obj));
|
||||
};
|
||||
|
||||
private handleStartArray = (): void => {
|
||||
protected handleStartArray = (): void => {
|
||||
this.pushContext();
|
||||
defined(this._ctx).currentArray = [];
|
||||
};
|
||||
|
||||
private handleEndArray = (): void => {
|
||||
protected handleEndArray = (): void => {
|
||||
const arr = defined(this._ctx).currentArray;
|
||||
if (arr === undefined) {
|
||||
return panic("Array ended but not started");
|
||||
|
@ -211,52 +216,24 @@ export class CompressedJSON {
|
|||
this.commitValue(this.internArray(arr));
|
||||
};
|
||||
|
||||
private handleStartKey = (): void => {
|
||||
defined(this._ctx).currentString = "";
|
||||
protected handleKeyValue = (s: string): void => {
|
||||
defined(this._ctx).currentKey = s;
|
||||
};
|
||||
|
||||
private handleEndKey = (): void => {
|
||||
const ctx = defined(this._ctx);
|
||||
const str = ctx.currentString;
|
||||
if (str === undefined) {
|
||||
return panic("Key ended but no string");
|
||||
}
|
||||
ctx.currentKey = str;
|
||||
ctx.currentString = undefined;
|
||||
};
|
||||
|
||||
private handleStartString = (): void => {
|
||||
this.pushContext();
|
||||
defined(this._ctx).currentString = "";
|
||||
};
|
||||
|
||||
private handleStringChunk = (s: string): void => {
|
||||
const ctx = defined(this._ctx);
|
||||
if (ctx.currentString === undefined) {
|
||||
return panic("String chunk but no string");
|
||||
}
|
||||
ctx.currentString += s;
|
||||
};
|
||||
|
||||
private handleEndString = (): void => {
|
||||
const str = defined(this._ctx).currentString;
|
||||
if (str === undefined) {
|
||||
return panic("String ended but not started");
|
||||
}
|
||||
this.popContext();
|
||||
protected handleStringValue = (s: string): void => {
|
||||
let value: Value | undefined = undefined;
|
||||
if (str.length <= 64) {
|
||||
if (str.length > 0 && "0123456789".indexOf(str[0]) >= 0) {
|
||||
if (this._makeDate && isDate(str)) {
|
||||
if (s.length <= 64) {
|
||||
if (s.length > 0 && "0123456789".indexOf(s[0]) >= 0) {
|
||||
if (this._makeDate && isDate(s)) {
|
||||
value = makeValue(Tag.Date, 0);
|
||||
} else if (this._makeTime && isTime(str)) {
|
||||
} else if (this._makeTime && isTime(s)) {
|
||||
value = makeValue(Tag.Time, 0);
|
||||
} else if (this._makeDateTime && isDateTime(str)) {
|
||||
} else if (this._makeDateTime && isDateTime(s)) {
|
||||
value = makeValue(Tag.DateTime, 0);
|
||||
}
|
||||
}
|
||||
if (value === undefined) {
|
||||
value = this.internString(str);
|
||||
value = this.internString(s);
|
||||
}
|
||||
} else {
|
||||
value = makeValue(Tag.UninternedString, 0);
|
||||
|
@ -264,36 +241,33 @@ export class CompressedJSON {
|
|||
this.commitValue(value);
|
||||
};
|
||||
|
||||
private handleStartNumber = (): void => {
|
||||
protected handleStartNumber = (): void => {
|
||||
this.pushContext();
|
||||
defined(this._ctx).currentNumberIsDouble = false;
|
||||
};
|
||||
|
||||
private handleNumberChunk = (s: string): void => {
|
||||
protected handleNumberChunk = (s: string): void => {
|
||||
if (s.includes(".") || s.includes("e") || s.includes("E")) {
|
||||
defined(this._ctx).currentNumberIsDouble = true;
|
||||
}
|
||||
};
|
||||
|
||||
private handleEndNumber = (): void => {
|
||||
protected handleEndNumber = (): void => {
|
||||
const isDouble = defined(this._ctx).currentNumberIsDouble;
|
||||
if (isDouble === undefined) {
|
||||
return panic("Number ended but not started");
|
||||
}
|
||||
const numberTag = isDouble ? Tag.Double : Tag.Integer;
|
||||
this.popContext();
|
||||
this.commitValue(makeValue(numberTag, 0));
|
||||
};
|
||||
|
||||
private handleNullValue = (): void => {
|
||||
protected handleNullValue = (): void => {
|
||||
this.commitValue(makeValue(Tag.Null, 0));
|
||||
};
|
||||
|
||||
private handleTrueValue = (): void => {
|
||||
protected handleTrueValue = (): void => {
|
||||
this.commitValue(makeValue(Tag.True, 0));
|
||||
};
|
||||
|
||||
private handleFalseValue = (): void => {
|
||||
protected handleFalseValue = (): void => {
|
||||
this.commitValue(makeValue(Tag.False, 0));
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче