UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

230 lines (229 loc) 8.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.codegen = exports.generate = exports.or = exports.ref = exports.map = exports.obj = exports.tup = exports.arr = exports.const_ = exports.bin = exports.str = exports.num = exports.bool = exports.any = void 0; const JsExpression_1 = require("@jsonjoy.com/util/lib/codegen/util/JsExpression"); const json_size_1 = require("@jsonjoy.com/util/lib/json-size"); const CapacityEstimatorCodegenContext_1 = require("./CapacityEstimatorCodegenContext"); const normalizeAccessor = (key) => { // Simple property access for valid identifiers, bracket notation otherwise if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) { return `.${key}`; } return `[${JSON.stringify(key)}]`; }; const any = (ctx, value, type) => { const codegen = ctx.codegen; codegen.link('Value'); const r = codegen.var(value.use()); codegen.if(`${r} instanceof Value`, () => { codegen.if(`${r}.type`, () => { ctx.codegen.js(`size += ${r}.type.capacityEstimator()(${r}.data);`); }, () => { ctx.codegen.js(`size += maxEncodingCapacity(${r}.data);`); }); }, () => { ctx.codegen.js(`size += maxEncodingCapacity(${r});`); }); }; exports.any = any; const bool = (ctx, value) => { ctx.inc(5 /* MaxEncodingOverhead.Boolean */); }; exports.bool = bool; const num = (ctx, value) => { ctx.inc(22 /* MaxEncodingOverhead.Number */); }; exports.num = num; const str = (ctx, value) => { ctx.inc(5 /* MaxEncodingOverhead.String */); ctx.codegen.js(`size += ${5 /* MaxEncodingOverhead.StringLengthMultiplier */} * ${value.use()}.length;`); }; exports.str = str; const bin = (ctx, value) => { ctx.inc(41 /* MaxEncodingOverhead.Binary */); ctx.codegen.js(`size += ${2 /* MaxEncodingOverhead.BinaryLengthMultiplier */} * ${value.use()}.length;`); }; exports.bin = bin; const const_ = (ctx, value, type) => { const constType = type; ctx.inc((0, json_size_1.maxEncodingCapacity)(constType.value())); }; exports.const_ = const_; const arr = (ctx, value, type) => { const codegen = ctx.codegen; ctx.inc(5 /* MaxEncodingOverhead.Array */); const rLen = codegen.var(`${value.use()}.length`); const arrayType = type; // ArrType const elementType = arrayType.type; codegen.js(`size += ${1 /* MaxEncodingOverhead.ArrayElement */} * ${rLen}`); const fn = elementType.compileCapacityEstimator({ system: ctx.options.system, name: ctx.options.name, }); const isConstantSizeType = ['con', 'bool', 'num'].includes(elementType.getTypeName()); if (isConstantSizeType) { const rFn = codegen.linkDependency(fn); codegen.js(`size += ${rLen} * ${rFn}(${elementType.random()});`); } else { const rFn = codegen.linkDependency(fn); const ri = codegen.var('0'); codegen.js(`for (; ${ri} < ${rLen}; ${ri}++) {`); codegen.js(`size += ${rFn}(${value.use()}[${ri}]);`); codegen.js(`}`); } }; exports.arr = arr; const tup = (ctx, value, type) => { const codegen = ctx.codegen; const r = codegen.var(value.use()); const tupleType = type; // TupType const types = tupleType.types; const overhead = 5 /* MaxEncodingOverhead.Array */ + 1 /* MaxEncodingOverhead.ArrayElement */ * types.length; ctx.inc(overhead); for (let i = 0; i < types.length; i++) { const elementType = types[i]; const fn = elementType.compileCapacityEstimator({ system: ctx.options.system, name: ctx.options.name, }); const rFn = codegen.linkDependency(fn); codegen.js(`size += ${rFn}(${r}[${i}]);`); } }; exports.tup = tup; const obj = (ctx, value, type, estimateCapacityFn) => { const codegen = ctx.codegen; const r = codegen.var(value.use()); const objectType = type; // ObjType const encodeUnknownFields = !!objectType.schema.encodeUnknownFields; if (encodeUnknownFields) { codegen.js(`size += maxEncodingCapacity(${r});`); return; } const fields = objectType.fields; const overhead = 5 /* MaxEncodingOverhead.Object */ + fields.length * 2 /* MaxEncodingOverhead.ObjectElement */; ctx.inc(overhead); for (const field of fields) { ctx.inc((0, json_size_1.maxEncodingCapacity)(field.key)); const accessor = normalizeAccessor(field.key); const isOptional = field.optional || field.constructor?.name === 'ObjectOptionalFieldType'; const block = () => estimateCapacityFn(ctx, new JsExpression_1.JsExpression(() => `${r}${accessor}`), field.value); if (isOptional) { codegen.if(`${r}${accessor} !== undefined`, block); } else block(); } }; exports.obj = obj; const map = (ctx, value, type) => { const codegen = ctx.codegen; ctx.inc(5 /* MaxEncodingOverhead.Object */); const r = codegen.var(value.use()); const rKeys = codegen.var(`Object.keys(${r})`); const rKey = codegen.var(); const rLen = codegen.var(`${rKeys}.length`); codegen.js(`size += ${2 /* MaxEncodingOverhead.ObjectElement */} * ${rLen}`); const mapType = type; // MapType const valueType = mapType.valueType; const fn = valueType.compileCapacityEstimator({ system: ctx.options.system, name: ctx.options.name, }); const rFn = codegen.linkDependency(fn); const ri = codegen.var('0'); codegen.js(`for (; ${ri} < ${rLen}; ${ri}++) {`); codegen.js(`${rKey} = ${rKeys}[${ri}];`); codegen.js(`size += maxEncodingCapacity(${rKey}) + ${rFn}(${r}[${rKey}]);`); codegen.js(`}`); }; exports.map = map; const ref = (ctx, value, type) => { const refType = type; // RefType const system = ctx.options.system || refType.system; if (!system) throw new Error('NO_SYSTEM'); const estimator = system.resolve(refType.schema.ref).type.capacityEstimator(); const d = ctx.codegen.linkDependency(estimator); ctx.codegen.js(`size += ${d}(${value.use()});`); }; exports.ref = ref; const or = (ctx, value, type, estimateCapacityFn) => { const codegen = ctx.codegen; const orType = type; // OrType const discriminator = orType.discriminator(); const d = codegen.linkDependency(discriminator); const types = orType.types; codegen.switch(`${d}(${value.use()})`, types.map((childType, index) => [ index, () => { estimateCapacityFn(ctx, value, childType); }, ])); }; exports.or = or; /** * Main router function that dispatches capacity estimation to the appropriate * estimator function based on the type's kind. */ const generate = (ctx, value, type) => { const kind = type.getTypeName(); switch (kind) { case 'any': (0, exports.any)(ctx, value, type); break; case 'bool': (0, exports.bool)(ctx, value); break; case 'num': (0, exports.num)(ctx, value); break; case 'str': (0, exports.str)(ctx, value); break; case 'bin': (0, exports.bin)(ctx, value); break; case 'con': (0, exports.const_)(ctx, value, type); break; case 'arr': (0, exports.arr)(ctx, value, type); break; case 'tup': (0, exports.tup)(ctx, value, type); break; case 'obj': (0, exports.obj)(ctx, value, type, exports.generate); break; case 'map': (0, exports.map)(ctx, value, type); break; case 'ref': (0, exports.ref)(ctx, value, type); break; case 'or': (0, exports.or)(ctx, value, type, exports.generate); break; default: throw new Error(`${kind} type capacity estimation not implemented`); } }; exports.generate = generate; /** * Standalone function to generate a capacity estimator for a given type. */ const codegen = (type, options) => { const ctx = new CapacityEstimatorCodegenContext_1.CapacityEstimatorCodegenContext({ system: type.system, ...options, type: type, }); const r = ctx.codegen.options.args[0]; const value = new JsExpression_1.JsExpression(() => r); // Use the centralized router instead of the abstract method (0, exports.generate)(ctx, value, type); return ctx.compile(); }; exports.codegen = codegen;