UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

261 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractBinaryCodegen = void 0; const concat_1 = require("@jsonjoy.com/buffers/lib/concat"); const codegen_1 = require("@jsonjoy.com/codegen"); const JsExpression_1 = require("@jsonjoy.com/codegen/lib/util/JsExpression"); const util_1 = require("../../util"); const Value_1 = require("../../value/Value"); const AbstractCodege_1 = require("../AbstractCodege"); const capacity_1 = require("../capacity"); const discriminator_1 = require("../discriminator"); const WriteBlobStep_1 = require("./WriteBlobStep"); class AbstractBinaryCodegen extends AbstractCodege_1.AbstractCodegen { constructor(type, name) { super(); this.type = type; this.codegen = new codegen_1.Codegen({ name: 'toBinary' + (name ? '_' + name : ''), args: ['r0', 'encoder'], prologue: /* js */ ` var writer = encoder.writer; writer.ensureCapacity(capacityEstimator(r0)); var uint8 = writer.uint8, view = writer.view;`, epilogue: '', linkable: { Value: Value_1.Value, }, processSteps: (steps) => { const stepsJoined = []; for (let i = 0; i < steps.length; i++) { const step = steps[i]; if (step instanceof codegen_1.CodegenStepExecJs) stepsJoined.push(step); else if (step instanceof WriteBlobStep_1.WriteBlobStep) { const last = stepsJoined[stepsJoined.length - 1]; if (last instanceof WriteBlobStep_1.WriteBlobStep) last.arr = (0, concat_1.concat)(last.arr, step.arr); else stepsJoined.push(step); } } const execSteps = []; for (const step of stepsJoined) { if (step instanceof codegen_1.CodegenStepExecJs) { execSteps.push(step); } else if (step instanceof WriteBlobStep_1.WriteBlobStep) { execSteps.push(this.codegenBlob(step)); } } return execSteps; }, }); this.codegen.linkDependency(capacity_1.CapacityEstimatorCodegen.get(type), 'capacityEstimator'); } getBigIntStr(arr, offset) { const buf = new Uint8Array(8); for (let i = 0; i < 8; i++) buf[i] = arr[offset + i]; const view = new DataView(buf.buffer); const bigint = view.getBigUint64(0); return bigint.toString() + 'n'; } codegenBlob(step) { const lines = []; const ro = this.codegen.getRegister(); const length = step.arr.length; if (length === 1) { lines.push(/* js */ `uint8[writer.x++] = ${step.arr[0]};`); } else { lines.push(`var ${ro} = writer.x;`); lines.push(`writer.x += ${step.arr.length};`); let i = 0; while (i < length) { const remaining = length - i; if (remaining >= 8) { const value = this.getBigIntStr(step.arr, i); lines.push(/* js */ `view.setBigUint64(${ro}${i ? ` + ${i}` : ''}, ${value});`); i += 8; } else if (remaining >= 4) { const value = (step.arr[i] << 24) | (step.arr[i + 1] << 16) | (step.arr[i + 2] << 8) | step.arr[i + 3]; lines.push(/* js */ `view.setInt32(${ro}${i ? ` + ${i}` : ''}, ${value});`); i += 4; } else if (remaining >= 2) { const value = (step.arr[i] << 8) | step.arr[i + 1]; lines.push(/* js */ `view.setInt16(${ro}${i ? ` + ${i}` : ''}, ${value});`); i += 2; } else { lines.push(/* js */ `uint8[${ro}${i ? ` + ${i}` : ''}] = ${step.arr[i]};`); i++; } } } const js = lines.join('\n'); return new codegen_1.CodegenStepExecJs(js); } js(js) { this.codegen.js(js); } gen(callback) { const encoder = this.encoder; encoder.writer.reset(); callback(encoder); return encoder.writer.flush(); } blob(arr) { this.codegen.step(new WriteBlobStep_1.WriteBlobStep(arr)); } compile() { return this.codegen.compile(); } onAny(path, r, type) { const codegen = this.codegen; const rv = codegen.var(r.use()); codegen.link('Value'); this.linkGet(); codegen.if( /* js */ `${rv} instanceof Value`, () => { const rType = codegen.var(/* js */ `${rv}.type`); const rData = codegen.var(/* js */ `${rv}.data`); codegen.if( /* js */ `${rType}`, () => { codegen.js(/* js */ `get(${rType})(${rData},encoder);`); }, () => { this.codegen.js(`encoder.writeAny(${rData});`); }); }, () => { this.codegen.js(`encoder.writeAny(${rv});`); }); } onCon(path, r, type) { this.blob(this.gen((encoder) => { encoder.writeAny(type.literal()); })); } onBool(path, r, type) { this.codegen.if(`${r.use()}`, () => { this.blob(this.gen((encoder) => { encoder.writeBoolean(true); })); }, () => { this.blob(this.gen((encoder) => { encoder.writeBoolean(false); })); }); } onNum(path, r, type) { const { format } = type.schema; const v = r.use(); const codegen = this.codegen; if (util_1.uints.has(format)) codegen.js(/* js */ `encoder.writeUInteger(${v});`); else if (util_1.ints.has(format)) codegen.js(/* js */ `encoder.writeInteger(${v});`); else if (util_1.floats.has(format)) codegen.js(/* js */ `encoder.writeFloat(${v});`); else codegen.js(/* js */ `encoder.writeNumber(${v});`); } onStr(path, r, type) { const { ascii, format } = type.schema; const codegen = this.codegen; // Use ASCII encoding if format is 'ascii' or ascii=true (backward compatibility) const v = r.use(); const useAscii = format === 'ascii' || ascii; if (useAscii) codegen.js(/* js */ `encoder.writeAsciiStr(${v});`); else codegen.js(/* js */ `encoder.writeStr(${v});`); } onBin(path, r, type) { this.codegen.js(/* js */ `encoder.writeBin(${r.use()});`); } onArr(path, val, type) { const codegen = this.codegen; const r = codegen.getRegister(); // array const rl = codegen.getRegister(); // array.length const ri = codegen.getRegister(); // index const { _head = [], _type, _tail = [] } = type; const headLength = _head.length; const tailLength = _tail.length; const constLen = headLength + tailLength; codegen.js(/* js */ `var ${r} = ${val.use()}, ${rl} = ${r}.length, ${ri} = 0;`); if (constLen) { codegen.if(/* js */ `${rl} < ${constLen}`, () => { codegen.js(`throw new Error('ARR_LEN');`); }); } codegen.js(/* js */ `encoder.writeArrHdr(${rl});`); if (headLength > 0) { for (let i = 0; i < headLength; i++) { this.onNode([...path, { r: ri }], new JsExpression_1.JsExpression(() => /* js */ `${r}[${ri}]`), _head[i]); codegen.js(/* js */ `${ri}++`); } } if (_type) { codegen.js(/* js */ `for(; ${ri} < ${rl} - ${tailLength}; ${ri}++) {`); this.onNode([...path, { r: ri }], new JsExpression_1.JsExpression(() => /* js */ `${r}[${ri}]`), _type); codegen.js(/* js */ `}`); } if (tailLength > 0) { for (let i = 0; i < tailLength; i++) { this.onNode([...path, { r: ri }], new JsExpression_1.JsExpression(() => /* js */ `${r}[${ri}]`), _tail[i]); codegen.js(/* js */ `${ri}++`); } } } onMap(path, val, type) { const codegen = this.codegen; const r = codegen.var(val.use()); const rKeys = codegen.var(/* js */ `Object.keys(${r})`); const rKey = codegen.var(); const rLength = codegen.var(/* js */ `${rKeys}.length`); const ri = codegen.var('0'); codegen.js(`encoder.writeObjHdr(${rLength});`); codegen.js(`for(; ${ri} < ${rLength}; ${ri}++){`); codegen.js(`${rKey} = ${rKeys}[${ri}];`); codegen.js(`encoder.writeStr(${rKey});`); const expr = new JsExpression_1.JsExpression(() => `${r}[${rKey}]`); this.onNode([...path, { r: rKey }], expr, type._value); codegen.js(/* js */ `}`); } onOr(path, r, type) { const codegen = this.codegen; const discriminator = discriminator_1.DiscriminatorCodegen.get(type); const d = codegen.linkDependency(discriminator); const types = type.types; codegen.switch(`${d}(${r.use()})`, types.map((type, index) => [ index, () => { this.onNode(path, r, type); }, ])); } onRef(path, r, type) { const system = type.getSystem(); const alias = system.resolve(type.ref()); switch (alias.type.kind()) { case 'str': case 'bool': case 'num': case 'any': case 'tup': { this.onNode(path, r, alias.type); break; } default: { const encoder = this.genEncoder(alias.type); const codegen = this.codegen; const d = codegen.linkDependency(encoder); codegen.js(/* js */ `${d}(${r.use()}, encoder);`); } } } } exports.AbstractBinaryCodegen = AbstractBinaryCodegen; //# sourceMappingURL=AbstractBinaryCodegen.js.map