@cloudpss/ubjson
Version:
Opinionated UBJSON encoder/decoder for CloudPSS.
482 lines (457 loc) • 18.5 kB
JavaScript
/** @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 };