@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
263 lines (262 loc) • 10.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
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 normalizeAccessor_1 = require("@jsonjoy.com/util/lib/codegen/util/normalizeAccessor");
const codegenBinaryEncoder = (ctx, value, type) => {
const kind = type.getTypeName();
const v = value.use();
switch (kind) {
case 'str': {
const strType = type; // StrType
const { ascii, format } = strType.schema;
// Use ASCII encoding if format is 'ascii' or ascii=true (backward compatibility)
const useAscii = format === 'ascii' || ascii;
if (useAscii)
ctx.js(/* js */ `encoder.writeAsciiStr(${v});`);
else
ctx.js(/* js */ `encoder.writeStr(${v});`);
break;
}
case 'bin': {
ctx.js(/* js */ `encoder.writeBin(${v});`);
break;
}
case 'num': {
const numType = type; // NumType
const { format, int } = numType.schema;
if (format === 'u8')
ctx.js(/* js */ `encoder.writeU8(${v});`);
else if (format === 'u16')
ctx.js(/* js */ `encoder.writeU16(${v});`);
else if (format === 'u32')
ctx.js(/* js */ `encoder.writeU32(${v});`);
else if (format === 'i8')
ctx.js(/* js */ `encoder.writeI8(${v});`);
else if (format === 'i16')
ctx.js(/* js */ `encoder.writeI16(${v});`);
else if (format === 'i32')
ctx.js(/* js */ `encoder.writeI32(${v});`);
else if (format === 'f32')
ctx.js(/* js */ `encoder.writeF32(${v});`);
else if (format === 'f64')
ctx.js(/* js */ `encoder.writeF64(${v});`);
else if (int)
ctx.js(/* js */ `encoder.writeUInt(${v});`);
else
ctx.js(/* js */ `encoder.writeF64(${v});`);
break;
}
case 'bool': {
ctx.js(/* js */ `encoder.writeBoolean(${v});`);
break;
}
default: {
ctx.js(/* js */ `encoder.writeAny(${v});`);
break;
}
}
};
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`, () => {
codegen.js(`${r}.type.encoder(encoder.format)(${r}.data, encoder);`);
}, () => {
codegen.js(`encoder.writeAny(${r}.data);`);
});
}, () => {
codegen.js(`encoder.writeAny(${r});`);
});
};
exports.any = any;
const bool = (ctx, value) => {
codegenBinaryEncoder(ctx, value, { getTypeName: () => 'bool' });
};
exports.bool = bool;
const num = (ctx, value, type) => {
codegenBinaryEncoder(ctx, value, type);
};
exports.num = num;
const str = (ctx, value, type) => {
codegenBinaryEncoder(ctx, value, type);
};
exports.str = str;
const bin = (ctx, value, type) => {
codegenBinaryEncoder(ctx, value, type);
};
exports.bin = bin;
const const_ = (ctx, value, type) => {
const constType = type; // ConType
const constValue = constType.value();
ctx.js(/* js */ `encoder.writeAny(${JSON.stringify(constValue)});`);
};
exports.const_ = const_;
const arr = (ctx, value, type, encodeFn) => {
const arrType = type; // ArrType
const codegen = ctx.codegen;
const r = codegen.getRegister(); // array
const rl = codegen.getRegister(); // array.length
const ri = codegen.getRegister(); // index
ctx.js(/* js */ `var ${r} = ${value.use()}, ${rl} = ${r}.length, ${ri} = 0;`);
ctx.js(/* js */ `encoder.writeArrHdr(${rl});`);
ctx.js(/* js */ `for(; ${ri} < ${rl}; ${ri}++) ` + '{');
encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${ri}]`), arrType.type);
ctx.js(`}`);
};
exports.arr = arr;
const tup = (ctx, value, type, encodeFn) => {
const tupType = type; // TupType
const codegen = ctx.codegen;
const r = codegen.var(value.use());
const types = tupType.types;
ctx.js(/* js */ `encoder.writeArrHdr(${types.length});`);
for (let i = 0; i < types.length; i++) {
encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${i}]`), types[i]);
}
};
exports.tup = tup;
const obj = (ctx, value, type, encodeFn) => {
const objType = type; // ObjType
const codegen = ctx.codegen;
const r = codegen.var(value.use());
const encodeUnknownFields = !!objType.schema.encodeUnknownFields;
if (encodeUnknownFields) {
ctx.js(/* js */ `encoder.writeAny(${r});`);
return;
}
const fields = objType.fields;
const requiredFields = fields.filter((f) => !f.optional && f.constructor?.name !== 'ObjectOptionalFieldType');
const optionalFields = fields.filter((f) => f.optional || f.constructor?.name === 'ObjectOptionalFieldType');
if (optionalFields.length === 0) {
// All fields are required
ctx.js(/* js */ `encoder.writeObjHdr(${fields.length});`);
for (const field of fields) {
const key = field.key;
const accessor = (0, normalizeAccessor_1.normalizeAccessor)(key);
ctx.js(/* js */ `encoder.writeStr(${JSON.stringify(key)});`);
encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}${accessor}`), field.value);
}
}
else {
// Mixed fields - need to count optional ones dynamically
const rSize = codegen.getRegister();
ctx.js(/* js */ `var ${rSize} = ${requiredFields.length};`);
// Count optional fields that exist
for (const field of optionalFields) {
const key = field.key;
const accessor = (0, normalizeAccessor_1.normalizeAccessor)(key);
ctx.js(/* js */ `if (${r}${accessor} !== undefined) ${rSize}++;`);
}
ctx.js(/* js */ `encoder.writeObjHdr(${rSize});`);
// Encode required fields
for (const field of requiredFields) {
const key = field.key;
const accessor = (0, normalizeAccessor_1.normalizeAccessor)(key);
ctx.js(/* js */ `encoder.writeStr(${JSON.stringify(key)});`);
encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}${accessor}`), field.value);
}
// Encode optional fields
for (const field of optionalFields) {
const key = field.key;
const accessor = (0, normalizeAccessor_1.normalizeAccessor)(key);
ctx.js(/* js */ `if (${r}${accessor} !== undefined) {`);
ctx.js(/* js */ `encoder.writeStr(${JSON.stringify(key)});`);
encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}${accessor}`), field.value);
ctx.js(/* js */ `}`);
}
}
};
exports.obj = obj;
const map = (ctx, value, type, encodeFn) => {
const mapType = type; // MapType
const codegen = ctx.codegen;
const r = codegen.var(value.use());
const rKeys = codegen.var(`Object.keys(${r})`);
const rKey = codegen.var();
const rLen = codegen.var(`${rKeys}.length`);
const ri = codegen.var('0');
ctx.js(/* js */ `var ${rKeys} = Object.keys(${r}), ${rLen} = ${rKeys}.length, ${rKey}, ${ri} = 0;`);
ctx.js(/* js */ `encoder.writeObjHdr(${rLen});`);
ctx.js(/* js */ `for (; ${ri} < ${rLen}; ${ri}++) {`);
ctx.js(/* js */ `${rKey} = ${rKeys}[${ri}];`);
ctx.js(/* js */ `encoder.writeStr(${rKey});`);
encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`), mapType.valueType);
ctx.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 format = 0 /* EncodingFormat.Cbor */;
const encoder = system.resolve(refType.schema.ref).type.encoder(format);
const d = ctx.codegen.linkDependency(encoder);
ctx.js(`${d}(${value.use()}, encoder);`);
};
exports.ref = ref;
const or = (ctx, value, type, encodeFn) => {
const orType = type; // OrType
const codegen = ctx.codegen;
const discriminator = orType.discriminator();
const d = codegen.linkDependency(discriminator);
const types = orType.types;
codegen.switch(`${d}(${value.use()})`, types.map((childType, index) => [
index,
() => {
encodeFn(ctx, value, childType);
},
]));
};
exports.or = or;
/**
* Main router function that dispatches CBOR encoding to the appropriate
* encoder 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, type);
break;
case 'str':
(0, exports.str)(ctx, value, type);
break;
case 'bin':
(0, exports.bin)(ctx, value, type);
break;
case 'con':
(0, exports.const_)(ctx, value, type);
break;
case 'arr':
(0, exports.arr)(ctx, value, type, exports.generate);
break;
case 'tup':
(0, exports.tup)(ctx, value, type, exports.generate);
break;
case 'obj':
(0, exports.obj)(ctx, value, type, exports.generate);
break;
case 'map':
(0, exports.map)(ctx, value, type, exports.generate);
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 CBOR encoding not implemented`);
}
};
exports.generate = generate;