@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
197 lines (196 loc) • 7.97 kB
JavaScript
;
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;