UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

263 lines (262 loc) 10.2 kB
"use strict"; 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;