UNPKG

@cloudpss/ubjson

Version:

Opinionated UBJSON encoder/decoder for CloudPSS.

482 lines (457 loc) 18.5 kB
/** @type {Record<string, unknown>} */ export const INPUTS = {}; /** @type {Record<string, unknown>} */ export const EXPECTED = {}; INPUTS['number 0'] = 0; INPUTS['number 1'] = 1; INPUTS['number nan'] = Number.NaN; INPUTS['number infinity'] = Number.POSITIVE_INFINITY; INPUTS['number negative infinity'] = -Number.NEGATIVE_INFINITY; INPUTS['number epsilon'] = Number.EPSILON; INPUTS['number int8 max'] = 127; INPUTS['number int8 min'] = -128; INPUTS['number int16 max'] = 32767; INPUTS['number int16 min'] = -32768; INPUTS['number int32 max'] = 2_147_483_647; INPUTS['number int32 min'] = -2_147_483_648; INPUTS['number uint8 max'] = 255; INPUTS['number 0.1'] = 0.1; INPUTS['number 0.01'] = 0.01; INPUTS['number max safe integer'] = Number.MAX_SAFE_INTEGER; INPUTS['number min safe integer'] = Number.MIN_SAFE_INTEGER; INPUTS['number max value'] = Number.MAX_VALUE; INPUTS['number min value'] = Number.MIN_VALUE; INPUTS['has function'] = { value: 1, func: () => null, }; EXPECTED['has function'] = { value: 1 }; INPUTS['function'] = () => null; EXPECTED['function'] = new Error('Unsupported type Function'); INPUTS['symbol key'] = { value: 1, [Symbol('key')]: 2, }; EXPECTED['symbol key'] = { value: 1 }; INPUTS['symbol value'] = { value: Symbol('value'), }; EXPECTED['symbol value'] = new Error('Unsupported type Symbol'); INPUTS['has null'] = { value: 1, null: null, }; INPUTS['has undefined'] = { value: 1, undefined: undefined, }; EXPECTED['has undefined'] = { value: 1 }; INPUTS['with toJSON'] = new Date(0); EXPECTED['with toJSON'] = '1970-01-01T00:00:00.000Z'; INPUTS['int8 array'] = new Int8Array([-128, 0, 1, 2, 3, 4, 5, 127]); INPUTS['uint8 array'] = new Uint8Array([0, 1, 2, 3, 4, 5, 255]); INPUTS['int16 array'] = new Int16Array([-32768, 0, 1, 2, 3, 4, 5, 32767]); INPUTS['int32 array'] = new Int32Array([-2_147_483_648, 0, 1, 2, 3, 4, 5, 2_147_483_647]); INPUTS['bigint64 array'] = new BigInt64Array([ -9_223_372_036_854_775_808n, 0n, 1n, 2n, 3n, 4n, 5n, 9_223_372_036_854_775_807n, ]); INPUTS['float32 array'] = new Float32Array([0, Number.NaN, 2, 3, 4, 5, Number.MAX_VALUE, Number.MIN_VALUE]); INPUTS['float64 array'] = new Float64Array([0, Number.NaN, 2, 3, 4, 5, Number.MAX_VALUE, Number.MIN_VALUE]); INPUTS['big int8 array'] = new Int8Array(128 * 1024); INPUTS['big uint8 array'] = new Uint8Array(128 * 1024); INPUTS['big int16 array'] = new Int16Array(64 * 1024); INPUTS['big int32 array'] = new Int32Array(32 * 1024); INPUTS['big bigint64 array'] = new BigInt64Array(16 * 1024); INPUTS['big float32 array'] = new Float32Array(32 * 1024); INPUTS['big float64 array'] = new Float64Array(16 * 1024); INPUTS['uint8clamp array'] = new Uint8ClampedArray([0, 1, 2, 3, 4, 5, 255]); EXPECTED['uint8clamp array'] = new Error('Unsupported array buffer view of type Uint8ClampedArray'); INPUTS['typed arrays'] = { int8: new Int8Array([-128, 0, 1, 2, 3, 4, 5, 127]), uint8: new Uint8Array([0, 1, 2, 3, 4, 5, 255]), int16: new Int16Array([-32768, 0, 1, 2, 3, 4, 5, 32767]), int32: new Int32Array([-2_147_483_648, 0, 1, 2, 3, 4, 5, 2_147_483_647]), bigint64: new BigInt64Array([-9_223_372_036_854_775_808n, 0n, 1n, 2n, 3n, 4n, 5n, 9_223_372_036_854_775_807n]), float32: new Float32Array([0, Number.NaN, 2, 3, 4, 5, Number.MAX_VALUE, Number.MIN_VALUE]), float64: new Float64Array([0, Number.NaN, 2, 3, 4, 5, Number.MAX_VALUE, Number.MIN_VALUE]), int8_big: new Int8Array(128 * 1024), uint8_big: new Uint8Array(128 * 1024), int16_big: new Int16Array(64 * 1024), int32_big: new Int32Array(32 * 1024), bigint64_big: new BigInt64Array(16 * 1024), float32_big: new Float32Array(32 * 1024), float64_big: new Float64Array(16 * 1024), }; INPUTS['string empty'] = ''; INPUTS['string short ascii'] = 'hello world'; INPUTS['string short 2-byte utf8'] = 'ӒӓӔӕӖӗӘәӚӛӜӝ'; INPUTS['string short 3-byte utf8'] = '你好世界'; INPUTS['string short 4-byte utf8'] = '😀😀😀'; INPUTS['string short 4-byte utf8 unaligned'] = 'a😀😀😀'; INPUTS['string bad surrogate pair'] = 'a\uD800b\uDC00c'; EXPECTED['string bad surrogate pair'] = 'a\uFFFDb\uFFFDc'; INPUTS['string bad high surrogate'] = '\uD800'.repeat(8); EXPECTED['string bad high surrogate'] = '\uFFFD'.repeat(8); INPUTS['string bad low surrogate'] = '\uDC00'.repeat(8); EXPECTED['string bad low surrogate'] = '\uFFFD'.repeat(8); INPUTS['long string with surrogate pairs'] = '😀'.repeat(128 * 1024); INPUTS['long string with surrogate pairs unaligned'] = 'a' + '😀'.repeat(128 * 1024) + 'b'; INPUTS['long string bad high surrogate'] = '\uD800'.repeat(128 * 1024); EXPECTED['long string bad high surrogate'] = '\uFFFD'.repeat(128 * 1024); INPUTS['long string bad low surrogate'] = '\uDC00'.repeat(128 * 1024); EXPECTED['long string bad low surrogate'] = '\uFFFD'.repeat(128 * 1024); INPUTS['huge object'] = Object.fromEntries( Array.from({ length: 100_000 }, (_, i) => [ `key${i}`, { number: i, boolean: i % 2 === 0, string: `value${i}`, null: null, }, ]), ); INPUTS['huge array'] = Array.from({ length: 100_000 }, (_, i) => { switch (i % 7) { case 0: return i; case 1: return i % 2 === 0; case 2: return `value${i}`; case 3: return null; } return undefined; }); EXPECTED['huge array'] = Array.from({ length: 100_000 }, (_, i) => { switch (i % 7) { case 0: return i; case 1: return i % 2 === 0; case 2: return `value${i}`; } return null; }); INPUTS['complex object'] = { hello: 'world', from: ['UBJSON'], colors: [[255, 255, 255], [0, 0, 0], [64, 64, 96], new Uint8Array([255, 0, 1, 2, 3, 127])], domains: { '': '', com: 'commercial', org: 'organization', net: 'network', }, entires: [ { id: 1, name: 'test', content: null, timestamp: 1_532_432_408.008, published: false, }, { id: 2, name: 'lorem', content: 'Lorem ipsum...', timestamp: 1_532_432_416.346, published: true, }, ], plots: { traces: [ { name: 'test', x: new Float64Array([ 1, 2, 3, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.NaN, Number.MAX_VALUE, Number.MIN_VALUE, ]), y: new Int8Array([0, 4, 5, 6, -128, -1, 127]), }, { name: 'test2', x: new Float32Array([ 1, 2, 3, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.NaN, Number.MAX_VALUE, Number.MIN_VALUE, ]), y: new Int16Array([4, 5, 6, -32768, 32767]), }, { name: 'test233', x: new Int32Array([1, 2, 3, 0, -1]), y: new Int32Array([4, 5, 6, -(2 ** 31), 2 ** 31 - 1]), }, ], }, name1: 'short text'.repeat(10), name2: 'short text'.repeat(20), name3: 'short text'.repeat(30), name4: 'short text'.repeat(40), name5: 'short text'.repeat(50), name6: 'short text'.repeat(60), name7: 'short text'.repeat(70), name8: 'short text'.repeat(80), name9: 'short text'.repeat(90), description1: 'medium text'.repeat(100), description2: 'medium text'.repeat(200), description3: 'medium text'.repeat(500), description4: 'medium text'.repeat(1000), description5: 'medium text'.repeat(2000), description6: 'medium text'.repeat(3000), document1: 'long text'.repeat(10000), document2: 'long text'.repeat(20000), document3: 'long text'.repeat(50000), document4: 'long text'.repeat(100_000), }; INPUTS['cloudpss model'] = { rid: 'project/CloudPSS/_SubSysPort', name: '模块端口', description: '', tags: [ 'project:component', 'type:30000', 'project-group:模块-基础:101', 'project-category:component', 'project-support:job-definition/cloudpss/emtp', 'project-support:job-definition/cloudpss/sfemt', 'project-support:job-definition/cloudpss/power-flow', ], revision: { graphic: { pins: { input: { position: { x: 0.999_999_9, y: 0.5, }, }, output: { position: { x: 0, y: 0.5, }, }, electrical: { position: { x: 0.999_999_9, y: 0.5, }, }, }, attrs: { p0: { fill: 'var(--fill)', stroke: 'var(--stroke)', refPath: { d: 'm90000 0h-60000a10000 10000 0 0 0 0 20000h60000zm0 10000h20000', h: 20000, w: 110_000, }, fillOpacity: 'var(--fill-opacity)', strokeWidth: 'var(--stroke-width)', strokeOpacity: 'var(--stroke-opacity)', }, p1: { fill: 'var(--fill)', stroke: 'var(--stroke)', refPath: { d: 'm90000 0h-60000a10000 10000 0 0 0 0 20000h60000zm0 10000h20000', h: 20000, w: 110_000, }, fillOpacity: 'var(--fill-opacity)', strokeWidth: 'var(--stroke-width)', strokeOpacity: 'var(--stroke-opacity)', }, p2: { fill: 'transparent', stroke: 'var(--stroke)', refPath: { d: 'm20000 10000m-5000 -5000l5000 5000l-5000 5000', h: 20000, w: 110_000, }, fillOpacity: 'var(--fill-opacity)', strokeWidth: 'var(--stroke-width)', strokeOpacity: 'var(--stroke-opacity)', }, p3: { fill: 'var(--fill)', stroke: 'var(--stroke)', refPath: { d: 'm20000 0h60000a10000 10000 0 0 1 0 20000h-60000zm0 10000h-20000', h: 20000, w: 110_000, }, fillOpacity: 'var(--fill-opacity)', strokeWidth: 'var(--stroke-width)', strokeOpacity: 'var(--stroke-opacity)', }, p4: { fill: 'transparent', stroke: 'var(--stroke)', refPath: { d: 'm110000 10000m-5000 -5000l5000 5000l-5000 5000', h: 20000, w: 110_000, }, fillOpacity: 'var(--fill-opacity)', strokeWidth: 'var(--stroke-width)', strokeOpacity: 'var(--stroke-opacity)', }, t0: { fill: 'var(--spectrum-global-color-orange-600)', refX: 0.5, refY: 0.5, stroke: 'transparent', fontSize: 12, textAnchor: 'middle', }, }, width: 110, height: 20, markup: [ { tagName: 'path', selector: 'p0', condition: "=finder(p, index, array)= equalText(p.key, Key);\n p = $$.revision.pins.find(finder);\n p == undefined ? true : (not equalText(p.connection, 'input') and not equalText(p.connection, 'output'))", }, { tagName: 'path', selector: 'p1', condition: "=finder(p, index, array) = equalText(p.key, Key);\n p = $$.revision.pins.find(finder);\n p != undefined ? equalText(p.connection, 'input') :false", }, { tagName: 'path', selector: 'p2', condition: "=finder(p, index, array) = equalText(p.key, Key);\n p = $$.revision.pins.find(finder);\n p != undefined ? equalText(p.connection, 'output') : false", }, { tagName: 'path', selector: 'p3', condition: "=finder(p, index, array) = equalText(p.key, Key);\n p = $$.revision.pins.find(finder);\n p != undefined ? equalText(p.connection, 'output') : false", }, { tagName: 'path', selector: 'p4', condition: "=finder(p, index, array) = equalText(p.key, Key);\n p = $$.revision.pins.find(finder);\n p != undefined ? equalText(p.connection, 'input') :false", }, { attrs: { y: '0.35em', }, tagName: 'text', selector: 't0', condition: '', textContent: 'Port: $Key', }, ], generator: 'x6', }, parameters: [ { name: 'Configuration', items: [ { key: 'Key', name: 'Pin Key', type: 'choice', value: '', choices: '=mapper(p,index,array) = {key: p.key, name: p.key, description: p.description};\n$$.revision.pins.map(mapper)', condition: 'true', description: '绑定端口', }, ], condition: 'true', description: 'Configuration', }, ], pins: [ { dim: [ "=finder(p, index, array) = equalText(p.key, 'a');\np = $$.revision.pins.find(finder);\np!=undefined?p.dim[1]:''", "=finder(p, index, array) = equalText(p.key, 'a');\np = $$.revision.pins.find(finder);\np!=undefined?p.dim[2]:''", ], key: 'input', data: 'real', name: 'Port', visible: true, condition: "=finder(p, index, array) = equalText(p.key, Key);\np = $$.revision.pins.find(finder);\np != undefined ? equalText(p.connection, 'input') :false", connection: 'output', }, { dim: [ "=finder(p, index, array) = equalText(p.key, 'a');\np = $$.revision.pins.find(finder);\np!=undefined?p.dim[2]:''", "=finder(p, index, array) = equalText(p.key, 'a');\np = $$.revision.pins.find(finder);\np!=undefined?p.dim[2]:''", ], key: 'output', data: 'real', name: 'Port', visible: true, condition: "=finder(p, index, array) = equalText(p.key, Key);\np = $$.revision.pins.find(finder);\np != undefined ? equalText(p.connection, 'output') :false", connection: 'input', }, { dim: [ "=finder(p, index, array) = equalText(p.key, 'a');\np = $$.revision.pins.find(finder);\np!=undefined?p.dim[2]:''", "=finder(p, index, array) = equalText(p.key, 'a');\np = $$.revision.pins.find(finder);\np!=undefined?p.dim[2]:''", ], key: 'electrical', data: 'real', name: 'Port', visible: true, condition: "=finder(p, index, array)= equalText(p.key, Key);\np = $$.revision.pins.find(finder);\np == undefined ? true : (not equalText(p.connection, 'input') and not equalText(p.connection, 'output'))", connection: 'electrical', }, ], documentation: '@[docs](http://docs.cloudpss.net/components/comp_PSS/comp_PSSSystem/BasicComp/SystemPort)', }, }; INPUTS['large array'] = Array.from({ length: 100_000 }).map((_, i) => (i === 12345 ? 'item' : undefined)); EXPECTED['large array'] = Array.from({ length: 100_000 }).map((_, i) => (i === 12345 ? 'item' : null)); INPUTS['object with undefined values'] = { a: 1, b: undefined, c: { d: 2, e: undefined, f: null } }; EXPECTED['object with undefined values'] = { a: 1, c: { d: 2, f: null } }; INPUTS['array with undefined values'] = [1, undefined, 2, undefined, 3]; EXPECTED['array with undefined values'] = [1, null, 2, null, 3]; INPUTS['inject __proto__'] = JSON.parse('{"__proto__": {"a":1}}'); EXPECTED['inject __proto__'] = {}; INPUTS['invalid __proto__'] = JSON.parse('{"__proto__":"xxx"}'); EXPECTED['invalid __proto__'] = {}; INPUTS['null __proto__'] = JSON.parse('{"__proto__":null}'); EXPECTED['null __proto__'] = {}; INPUTS['inject constructor'] = { constructor: { prototype: { a: 1 } } }; INPUTS['invalid constructor prototype'] = { constructor: { prototype: 'xxx' } }; INPUTS['null constructor prototype'] = { constructor: { prototype: null } }; INPUTS['invalid constructor'] = { constructor: 'xxx' }; INPUTS['null constructor'] = { constructor: null };