@jsonjoy.com/json-pack
Version:
High-performance JSON serialization library
287 lines • 10.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.XdrSchemaEncoder = void 0;
const XdrEncoder_1 = require("./XdrEncoder");
const XdrUnion_1 = require("./XdrUnion");
class XdrSchemaEncoder {
constructor(writer) {
this.writer = writer;
this.encoder = new XdrEncoder_1.XdrEncoder(writer);
}
encode(value, schema) {
this.writer.reset();
this.writeValue(value, schema);
return this.writer.flush();
}
writeVoid(schema) {
this.validateSchemaType(schema, 'void');
this.encoder.writeVoid();
}
writeInt(value, schema) {
this.validateSchemaType(schema, 'int');
if (!Number.isInteger(value) || value < -2147483648 || value > 2147483647) {
throw new Error('Value is not a valid 32-bit signed integer');
}
this.encoder.writeInt(value);
}
writeUnsignedInt(value, schema) {
this.validateSchemaType(schema, 'unsigned_int');
if (!Number.isInteger(value) || value < 0 || value > 4294967295) {
throw new Error('Value is not a valid 32-bit unsigned integer');
}
this.encoder.writeUnsignedInt(value);
}
writeBoolean(value, schema) {
this.validateSchemaType(schema, 'boolean');
this.encoder.writeBoolean(value);
}
writeHyper(value, schema) {
this.validateSchemaType(schema, 'hyper');
this.encoder.writeHyper(value);
}
writeUnsignedHyper(value, schema) {
this.validateSchemaType(schema, 'unsigned_hyper');
if ((typeof value === 'number' && value < 0) || (typeof value === 'bigint' && value < BigInt(0))) {
throw new Error('Value is not a valid unsigned integer');
}
this.encoder.writeUnsignedHyper(value);
}
writeFloat(value, schema) {
this.validateSchemaType(schema, 'float');
this.encoder.writeFloat(value);
}
writeDouble(value, schema) {
this.validateSchemaType(schema, 'double');
this.encoder.writeDouble(value);
}
writeQuadruple(value, schema) {
this.validateSchemaType(schema, 'quadruple');
this.encoder.writeQuadruple(value);
}
writeEnum(value, schema) {
if (schema.type !== 'enum') {
throw new Error('Schema is not an enum schema');
}
if (!(value in schema.values)) {
throw new Error(`Invalid enum value: ${value}. Valid values are: ${Object.keys(schema.values).join(', ')}`);
}
const enumValue = schema.values[value];
// Per RFC 4506 Section 4.3: "It is an error to encode as an enum any integer other than those that have been given assignments"
if (!Number.isInteger(enumValue)) {
throw new Error(`Enum value ${value} has non-integer assignment: ${enumValue}`);
}
this.encoder.writeInt(enumValue);
}
writeOpaque(value, schema) {
if (schema.type !== 'opaque') {
throw new Error('Schema is not an opaque schema');
}
if (value.length !== schema.size) {
throw new Error(`Opaque data length ${value.length} does not match schema size ${schema.size}`);
}
this.encoder.writeOpaque(value);
}
writeVarlenOpaque(value, schema) {
if (schema.type !== 'vopaque') {
throw new Error('Schema is not a variable-length opaque schema');
}
if (schema.size !== undefined && value.length > schema.size) {
throw new Error(`Opaque data length ${value.length} exceeds maximum size ${schema.size}`);
}
this.encoder.writeVarlenOpaque(value);
}
writeString(value, schema) {
if (schema.type !== 'string') {
throw new Error('Schema is not a string schema');
}
if (schema.size !== undefined && value.length > schema.size) {
throw new Error(`String length ${value.length} exceeds maximum size ${schema.size}`);
}
this.encoder.writeStr(value);
}
writeArray(value, schema) {
if (schema.type !== 'array') {
throw new Error('Schema is not an array schema');
}
if (value.length !== schema.size) {
throw new Error(`Array length ${value.length} does not match schema size ${schema.size}`);
}
for (const item of value) {
this.writeValue(item, schema.elements);
}
}
writeVarlenArray(value, schema) {
if (schema.type !== 'varray') {
throw new Error('Schema is not a variable-length array schema');
}
if (schema.size !== undefined && value.length > schema.size) {
throw new Error(`Array length ${value.length} exceeds maximum size ${schema.size}`);
}
this.encoder.writeUnsignedInt(value.length);
for (const item of value) {
this.writeValue(item, schema.elements);
}
}
writeStruct(value, schema) {
if (schema.type !== 'struct') {
throw new Error('Schema is not a struct schema');
}
for (const [fieldSchema, fieldName] of schema.fields) {
if (!(fieldName in value)) {
throw new Error(`Missing required field: ${fieldName}`);
}
this.writeValue(value[fieldName], fieldSchema);
}
}
writeUnion(value, schema, discriminant) {
if (schema.type !== 'union') {
throw new Error('Schema is not a union schema');
}
const arm = schema.arms.find(([armDiscriminant]) => armDiscriminant === discriminant);
if (!arm) {
if (schema.default) {
this.writeDiscriminant(discriminant);
this.writeValue(value, schema.default);
}
else {
throw new Error(`No matching arm found for discriminant: ${discriminant}`);
}
}
else {
this.writeDiscriminant(discriminant);
this.writeValue(value, arm[1]);
}
}
/**
* Writes optional-data value (RFC 1832 Section 3.19).
* Optional-data is syntactic sugar for a union with boolean discriminant.
* If value is null/undefined, writes FALSE; otherwise writes TRUE and the value.
*/
writeOptional(value, schema) {
if (schema.type !== 'optional') {
throw new Error('Schema is not an optional schema');
}
if (value === null || value === undefined) {
this.encoder.writeBoolean(false);
}
else {
this.encoder.writeBoolean(true);
this.writeValue(value, schema.element);
}
}
writeNumber(value, schema) {
switch (schema.type) {
case 'int':
this.writeInt(value, schema);
break;
case 'unsigned_int':
this.writeUnsignedInt(value, schema);
break;
case 'hyper':
this.writeHyper(value, schema);
break;
case 'unsigned_hyper':
this.writeUnsignedHyper(value, schema);
break;
case 'float':
this.writeFloat(value, schema);
break;
case 'double':
this.writeDouble(value, schema);
break;
case 'quadruple':
this.writeQuadruple(value, schema);
break;
default:
throw new Error(`Schema type ${schema.type} is not a numeric type`);
}
}
writeValue(value, schema) {
switch (schema.type) {
case 'void':
this.encoder.writeVoid();
break;
case 'int':
this.encoder.writeInt(value);
break;
case 'unsigned_int':
this.encoder.writeUnsignedInt(value);
break;
case 'boolean':
this.encoder.writeBoolean(value);
break;
case 'hyper':
this.encoder.writeHyper(value);
break;
case 'unsigned_hyper':
this.encoder.writeUnsignedHyper(value);
break;
case 'float':
this.encoder.writeFloat(value);
break;
case 'double':
this.encoder.writeDouble(value);
break;
case 'quadruple':
this.encoder.writeQuadruple(value);
break;
case 'enum':
this.writeEnum(value, schema);
break;
case 'opaque':
this.writeOpaque(value, schema);
break;
case 'vopaque':
this.writeVarlenOpaque(value, schema);
break;
case 'string':
this.writeString(value, schema);
break;
case 'array':
this.writeArray(value, schema);
break;
case 'varray':
this.writeVarlenArray(value, schema);
break;
case 'struct':
this.writeStruct(value, schema);
break;
case 'union':
if (value instanceof XdrUnion_1.XdrUnion) {
this.writeUnion(value.value, schema, value.discriminant);
}
else {
throw new Error('Union values must be wrapped in XdrUnion class');
}
break;
case 'optional':
this.writeOptional(value, schema);
break;
case 'const':
// Constants are not encoded; they are compile-time values
break;
default:
throw new Error(`Unknown schema type: ${schema.type}`);
}
}
validateSchemaType(schema, expectedType) {
if (schema.type !== expectedType) {
throw new Error(`Expected schema type ${expectedType}, got ${schema.type}`);
}
}
writeDiscriminant(discriminant) {
if (typeof discriminant === 'number') {
this.encoder.writeInt(discriminant);
}
else if (typeof discriminant === 'boolean') {
this.encoder.writeBoolean(discriminant);
}
else {
// For string discriminants, we need to know the enum mapping
// This is a simplified implementation
throw new Error('String discriminants require enum schema context');
}
}
}
exports.XdrSchemaEncoder = XdrSchemaEncoder;
//# sourceMappingURL=XdrSchemaEncoder.js.map