@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
261 lines • 10.3 kB
JavaScript
;
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