UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

197 lines (196 loc) 7.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ArrayType = void 0; const tslib_1 = require("tslib"); const JsExpression_1 = require("@jsonjoy.com/util/lib/codegen/util/JsExpression"); const printTree_1 = require("tree-dump/lib/printTree"); const schema = tslib_1.__importStar(require("../../schema")); const validate_1 = require("../../schema/validate"); const constants_1 = require("../../constants"); const CborEncoderCodegenContext_1 = require("../../codegen/binary/CborEncoderCodegenContext"); const MessagePackEncoderCodegenContext_1 = require("../../codegen/binary/MessagePackEncoderCodegenContext"); const AbstractType_1 = require("./AbstractType"); const ConstType_1 = require("./ConstType"); const BooleanType_1 = require("./BooleanType"); const NumberType_1 = require("./NumberType"); class ArrayType extends AbstractType_1.AbstractType { constructor(type, options) { super(); this.type = type; this.schema = schema.s.Array(schema.s.any, options); } getSchema(ctx) { return { ...this.schema, type: this.type.getSchema(ctx), }; } toJsonSchema() { const schema = this.getSchema(); const jsonSchema = { type: 'array', items: this.type.toJsonSchema(), ...super.toJsonSchema(), }; if (schema.min !== undefined) jsonSchema.minItems = schema.min; if (schema.max !== undefined) jsonSchema.maxItems = schema.max; return jsonSchema; } getOptions() { const { kind, type, ...options } = this.schema; return options; } validateSchema() { const schema = this.getSchema(); (0, validate_1.validateTType)(schema, 'arr'); const { min, max } = schema; (0, validate_1.validateMinMax)(min, max); this.type.validateSchema(); } codegenValidator(ctx, path, r) { const rl = ctx.codegen.getRegister(); const ri = ctx.codegen.getRegister(); const rv = ctx.codegen.getRegister(); const err = ctx.err(constants_1.ValidationError.ARR, path); const errLen = ctx.err(constants_1.ValidationError.ARR_LEN, path); const { min, max } = this.schema; ctx.js(/* js */ `if (!Array.isArray(${r})) return ${err};`); ctx.js(`var ${rl} = ${r}.length;`); if (min !== undefined) ctx.js(`if (${rl} < ${min}) return ${errLen};`); if (max !== undefined) ctx.js(`if (${rl} > ${max}) return ${errLen};`); ctx.js(`for (var ${rv}, ${ri} = ${r}.length; ${ri}-- !== 0;) {`); ctx.js(`${rv} = ${r}[${ri}];`); this.type.codegenValidator(ctx, [...path, { r: ri }], rv); ctx.js(`}`); ctx.emitCustomValidators(this, path, r); } codegenJsonTextEncoder(ctx, value) { ctx.writeText('['); const codegen = ctx.codegen; const r = codegen.getRegister(); // array const rl = codegen.getRegister(); // array.length const rll = codegen.getRegister(); // last const ri = codegen.getRegister(); // index ctx.js(/* js */ `var ${r} = ${value.use()}, ${rl} = ${r}.length, ${rll} = ${rl} - 1, ${ri} = 0;`); ctx.js(/* js */ `for(; ${ri} < ${rll}; ${ri}++) ` + '{'); this.type.codegenJsonTextEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${ri}]`)); ctx.js(/* js */ `s += ',';`); ctx.js(`}`); ctx.js(`if (${rl}) {`); this.type.codegenJsonTextEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${rll}]`)); ctx.js(`}`); ctx.writeText(']'); } codegenBinaryEncoder(ctx, value) { const type = this.type; const codegen = ctx.codegen; const r = codegen.getRegister(); // array const rl = codegen.getRegister(); // array.length const ri = codegen.getRegister(); // index const rItem = codegen.getRegister(); // item const expr = new JsExpression_1.JsExpression(() => `${rItem}`); ctx.js(/* js */ `var ${r} = ${value.use()}, ${rl} = ${r}.length, ${ri} = 0, ${rItem};`); ctx.js(/* js */ `encoder.writeArrHdr(${rl});`); ctx.js(/* js */ `for(; ${ri} < ${rl}; ${ri}++) ` + '{'); ctx.js(/* js */ `${rItem} = ${r}[${ri}];`); if (ctx instanceof CborEncoderCodegenContext_1.CborEncoderCodegenContext) type.codegenCborEncoder(ctx, expr); else if (ctx instanceof MessagePackEncoderCodegenContext_1.MessagePackEncoderCodegenContext) type.codegenMessagePackEncoder(ctx, expr); else throw new Error('Unknown encoder'); ctx.js(`}`); } codegenCborEncoder(ctx, value) { this.codegenBinaryEncoder(ctx, value); } codegenMessagePackEncoder(ctx, value) { this.codegenBinaryEncoder(ctx, value); } codegenJsonEncoder(ctx, value) { const type = this.type; const codegen = ctx.codegen; const expr = new JsExpression_1.JsExpression(() => `${rItem}`); const r = codegen.var(value.use()); const rLen = codegen.var(`${r}.length`); const rLast = codegen.var(`${rLen} - 1`); const ri = codegen.var('0'); const rItem = codegen.var(); ctx.blob(ctx.gen((encoder) => { encoder.writeStartArr(); })); codegen.js(`for(; ${ri} < ${rLast}; ${ri}++) {`); codegen.js(`${rItem} = ${r}[${ri}];`); type.codegenJsonEncoder(ctx, expr); ctx.blob(ctx.gen((encoder) => { encoder.writeArrSeparator(); })); ctx.js(`}`); ctx.js(`if (${rLen}) {`); codegen.js(`${rItem} = ${r}[${rLast}];`); type.codegenJsonEncoder(ctx, expr); ctx.js(`}`); ctx.blob(ctx.gen((encoder) => { encoder.writeEndArr(); })); } codegenCapacityEstimator(ctx, value) { const codegen = ctx.codegen; ctx.inc(5 /* MaxEncodingOverhead.Array */); const rLen = codegen.var(`${value.use()}.length`); const type = this.type; codegen.js(`size += ${1 /* MaxEncodingOverhead.ArrayElement */} * ${rLen}`); const fn = type.compileCapacityEstimator({ system: ctx.options.system, name: ctx.options.name, }); const isConstantSizeType = type instanceof ConstType_1.ConstType || type instanceof BooleanType_1.BooleanType || type instanceof NumberType_1.NumberType; if (isConstantSizeType) { codegen.js(`size += ${rLen} * ${fn(null)};`); } else { const r = codegen.var(value.use()); const rFn = codegen.linkDependency(fn); const ri = codegen.getRegister(); codegen.js(`for(var ${ri} = ${rLen}; ${ri}-- !== 0;) size += ${rFn}(${r}[${ri}]);`); } } random() { let length = Math.round(Math.random() * 10); const { min, max } = this.schema; if (min !== undefined && length < min) length = min + length; if (max !== undefined && length > max) length = max; const arr = []; for (let i = 0; i < length; i++) arr.push(this.type.random()); return arr; } toTypeScriptAst() { return { node: 'ArrayType', elementType: this.type.toTypeScriptAst(), }; } toJson(value, system = this.system) { const length = value.length; if (!length) return '[]'; const last = length - 1; const type = this.type; let str = '['; for (let i = 0; i < last; i++) str += type.toJson(value[i], system) + ','; str += type.toJson(value[last], system); return (str + ']'); } toString(tab = '') { return super.toString(tab) + (0, printTree_1.printTree)(tab, [(tab) => this.type.toString(tab)]); } } exports.ArrayType = ArrayType;