UNPKG

@cloudpss/ubjson

Version:

Opinionated UBJSON encoder/decoder for CloudPSS.

588 lines (515 loc) 19.8 kB
/** * Tests from https://bitbucket.org/shelacek/ubjson */ import { encode, decode } from '../dist/index.js'; import { getEncoder } from '../dist/encoder.js'; import { toArray } from './.utils.js'; // @ts-expect-error Access private property const poolInit = getEncoder().pool; test('encode function', () => { expect(() => encode(function () { //noop }), ).toThrow(); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode bigint', () => { expect(toArray(encode(1n))).toEqual(toArray(encode(1))); expect(toArray(encode(0x1234_5678_90ab_cdefn))).toEqual( toArray('L', 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef), ); expect(() => encode(0x8234_5678_90ab_cdefn)).toThrow(/BigInt value out of range:/); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode symbol', () => { expect(() => encode(Symbol('sym'))).toThrow(); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode undefined', () => { expect(toArray(encode(undefined))).toEqual(toArray('N')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode null', () => { expect(toArray(encode(null))).toEqual(toArray('Z')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode true', () => { expect(toArray(encode(true))).toEqual(toArray('T')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode false', () => { expect(toArray(encode(false))).toEqual(toArray('F')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode int8', () => { expect(toArray(encode(-1))).toEqual(toArray('i', 255)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode uint8', () => { expect(toArray(encode(200))).toEqual(toArray('U', 200)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode int16', () => { expect(toArray(encode(0x1234))).toEqual(toArray('I', 0x12, 0x34)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode int32', () => { expect(toArray(encode(0x1234_5678))).toEqual(toArray('l', 0x12, 0x34, 0x56, 0x78)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode float32', () => { expect(toArray(encode(1.003_906_25))).toEqual(toArray('d', 0x3f, 0x80, 0x80, 0x00)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode float32 (too large integer)', () => { expect(toArray(encode(2_147_483_648))).toEqual(toArray('d', 0x4f, 0x00, 0x00, 0x00)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode float64', () => { expect(toArray(encode(100_000.003_906_25))).toEqual(toArray('D', 0x40, 0xf8, 0x6a, 0x00, 0x10, 0x00, 0x00, 0x00)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode char', () => { expect(toArray(encode('a'))).toEqual(toArray('C', 'a')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode char 127', () => { expect(toArray(encode('\u007F'))).toEqual(toArray('C', '\u007F')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode char 257', () => { expect(toArray(encode('\u0123'))).toEqual(toArray('S', 'i', 2, 196, 163)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode char emoji', () => { expect(toArray(encode('💖'))).toEqual(toArray('S', 'i', 4, 240, 159, 146, 150)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode string', () => { expect(toArray(encode('ubjson'))).toEqual(toArray('S', 'i', 6, ...'ubjson')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode string (len = 60)', () => { // 不生成 u8 const str = 'ubjson'.repeat(10); expect(toArray(encode(str))).toEqual(toArray('S', 'i', str.length, ...str)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode string (len = 120)', () => { // 不生成 u8 const str = 'ubjson'.repeat(20); expect(toArray(encode(str))).toEqual(toArray('S', 'i', str.length, ...str)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode string (len = 180)', () => { // 不生成 u8 const str = 'ubjson'.repeat(30); expect(toArray(encode(str))).toEqual(toArray('S', 'I', 0, str.length, ...str)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode string (no encodeInto)', () => { // eslint-disable-next-line @typescript-eslint/unbound-method const { encodeInto } = TextEncoder.prototype; // @ts-expect-error 移除 encodeInto 以测试兼容性 TextEncoder.prototype.encodeInto = undefined; expect(toArray(encode('ubjson'))).toEqual(toArray('S', 'i', 6, ...'ubjson')); TextEncoder.prototype.encodeInto = encodeInto; // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array', () => { expect(toArray(encode([1, 2, 3, -1]))).toEqual(toArray('[', 'U', 1, 'U', 2, 'U', 3, 'i', 255, ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (empty)', () => { expect(toArray(encode([]))).toEqual(toArray('[', ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (undefined)', () => { expect(toArray(encode([undefined]))).toEqual(toArray('[', 'Z', ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (function)', () => { expect(toArray(encode([() => 1]))).toEqual(toArray('[', 'Z', ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (spares)', () => { const array = Array.from({ length: 3 }); array[1] = true; expect(toArray(encode(array))).toEqual(toArray('[', 'Z', 'T', 'Z', ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (mixed)', () => { expect(toArray(encode([1, 'a', true]))).toEqual(toArray('[', 'U', 1, 'C', 'a', 'T', ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (int8)', () => { expect(toArray(encode([-1, 2, 3]))).toEqual(toArray('[', 'i', 255, 'U', 2, 'U', 3, ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (int16)', () => { expect(toArray(encode([255, -1]))).toEqual(toArray('[', 'U', 0xff, 'i', 0xff, ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (only null values)', () => { expect(toArray(encode([null, null, null]))).toEqual(toArray('[', 'Z', 'Z', 'Z', ']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode N-D array', () => { expect( decode( encode([ [1, 2, 3], [4, 5, 6], ]), ), ).toEqual([ [1, 2, 3], [4, 5, 6], ]); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array of objects', () => { expect( decode( encode([ { a: 1, b: 2, c: 3 }, { d: 4, e: 5, f: 6 }, ]), ), ).toEqual([ { a: 1, b: 2, c: 3 }, { d: 4, e: 5, f: 6 }, ]); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array of objects of arrays', () => { expect( decode( encode([ { a: [1, 2], b: [3, 4] }, { c: [5, 6], d: [7, 8] }, ]), ), ).toEqual([ { a: [1, 2], b: [3, 4] }, { c: [5, 6], d: [7, 8] }, ]); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (int8 typed array)', () => { expect(toArray(encode(Int8Array.from([18, -2])))).toEqual(toArray('[', '$', 'i', '#', 'i', 2, 0x12, 0xfe)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (uint8 typed array)', () => { expect(toArray(encode(Uint8Array.from([18, 254])))).toEqual(toArray('[', '$', 'U', '#', 'i', 2, 0x12, 0xfe)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (buffer array)', () => { expect(toArray(encode(Buffer.from([18, -2])))).toEqual(toArray('[', '$', 'U', '#', 'i', 2, 0x12, 0xfe)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (int16 typed array)', () => { expect(toArray(encode(Int16Array.from([4660, -292])))).toEqual( toArray('[', '$', 'I', '#', 'i', 2, 0x12, 0x34, 0xfe, 0xdc), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (int32 typed array)', () => { expect(toArray(encode(Int32Array.from([305_419_896, -19_088_744])))).toEqual( toArray('[', '$', 'l', '#', 'i', 2, 0x12, 0x34, 0x56, 0x78, 0xfe, 0xdc, 0xba, 0x98), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (float32 typed array)', () => { expect(toArray(encode(Float32Array.from([0.25, 0.125])))).toEqual( toArray('[', '$', 'd', '#', 'i', 2, 0x3e, 0x80, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (float64 typed array)', () => { expect(toArray(encode(Float64Array.from([0.25, 0.125])))).toEqual( toArray( '[', '$', 'D', '#', 'i', 2, 0x3f, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (uint8clamped typed array)', () => { expect(() => encode(new Uint8ClampedArray())).toThrow(); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (uint16 typed array)', () => { expect(() => encode(new Uint16Array())).toThrow(); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (uint32 typed array)', () => { expect(() => encode(new Uint32Array())).toThrow(); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (uint64 typed array)', () => { expect(() => encode(new BigUint64Array())).toThrow(); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode array (int64 typed array)', () => { expect(toArray(encode(new BigInt64Array([1n, 0x1234_5678_1234_5678n])))).toEqual( toArray( '[', '$', 'L', '#', 'i', 2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, ), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object', () => { expect(toArray(encode({ a: 1, b: 2, c: 3 }))).toEqual( toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (keep order)', () => { expect(toArray(encode({ b: 2, a: 1, c: 3 }, { sortObjectKeys: true }))).toEqual( toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (empty)', () => { expect(toArray(encode({}))).toEqual(toArray('{', '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (empty key)', () => { expect(toArray(encode({ '': '' }))).toEqual(toArray('{', 'i', 0, 'S', 'i', 0, '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (mixed)', () => { expect(toArray(encode({ a: 1, b: 'a', c: true }))).toEqual( toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'C', 'a', 'i', 1, 'c', 'T', '}'), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (only null values)', () => { expect(toArray(encode({ a: null, b: null, c: null }))).toEqual( toArray('{', 'i', 1, 'a', 'Z', 'i', 1, 'b', 'Z', 'i', 1, 'c', 'Z', '}'), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (skip prototype)', () => { const obj = Object.create({ a: 2, x: 'xx' }); obj.a = 1; obj.b = 'a'; obj.c = true; expect(toArray(encode(obj))).toEqual( toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'C', 'a', 'i', 1, 'c', 'T', '}'), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (skip symbol)', () => { const obj = { [Symbol()]: true, a: 1 }; expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (skip non-enumerable)', () => { const obj = {}; Object.defineProperty(obj, 'a', { value: 1, configurable: true, writable: true }); expect(toArray(encode(obj))).toEqual(toArray('{', '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (include getter)', () => { const obj = {}; Object.defineProperty(obj, 'a', { get: () => 1, enumerable: true }); expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (skip undefined)', () => { const obj = { a: undefined }; expect(toArray(encode(obj))).toEqual(toArray('{', '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (skip function)', () => { const obj = { a: 1, b: () => 1 }; expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode object (include null)', () => { const obj = { a: null }; expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'Z', '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge typed array (16K)', () => { const obj = new Uint8Array(16 * 1024); expect(toArray(encode(obj).slice(0, 8))).toEqual(toArray('[', '$', 'U', '#', 'I', 0x40, 0x00, 0)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge typed array (~128M)', () => { const obj = new Uint8Array(128 * 1024 * 1024 - 10); obj[0] = 0x58; expect(toArray(encode(obj).slice(0, 10))).toEqual(toArray('[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf6, 0x58)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge typed array (128M)', () => { const obj = new Uint8Array(128 * 1024 * 1024); obj[0] = 0x58; expect(toArray(encode(obj).slice(0, 10))).toEqual(toArray('[', '$', 'U', '#', 'l', 0x8, 0x00, 0x00, 0x00, 0x58)); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge typed array (1G) [error]', () => { const obj = new Uint8Array(1 * 1024 * 1024 * 1024); expect(() => encode(obj)).toThrow(/Buffer has exceed max size/); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge data (~128M)', () => { const obj = [new Uint8Array(128 * 1024 * 1024 - 20)]; obj[0][0] = 0x12; obj[0][1] = 0x34; expect(toArray(encode(obj).slice(0, 12))).toEqual( toArray('[', '[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xec, 0x12, 0x34), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge data (128M)', () => { const obj = [new Uint8Array(128 * 1024 * 1024)]; obj[0][0] = 0x12; obj[0][1] = 0x34; expect(toArray(encode(obj).slice(0, 12))).toEqual( toArray('[', '[', '$', 'U', '#', 'l', 0x8, 0x00, 0x00, 0x00, 0x12, 0x34), ); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode huge data (256M)', () => { const obj = [new Uint8Array(128 * 1024 * 1024 - 9), new Uint8Array(128 * 1024 * 1024 - 9)]; obj[0][0] = 0x12; obj[0][1] = 0x34; obj[1][0] = 0x56; obj[1][1] = 0x78; const encoded = encode(obj); expect(encoded.length).toBe(128 * 1024 * 1024 * 2 + 2); expect(toArray(encoded.slice(0, 12))).toEqual( toArray('[', '[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x12, 0x34), ); expect(toArray(encoded.slice(128 * 1024 * 1024 + 1, 128 * 1024 * 1024 + 1 + 11))).toEqual( toArray('[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x56, 0x78), ); expect(toArray(encoded.slice(-1))).toEqual(toArray(']')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode custom toJSON', () => { const obj = { toJSON: () => ({ a: 1 }), }; expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); }); test('encode Date', () => { const obj = new Date(0); expect(toArray(encode(obj))).toEqual(toArray('S', 'i', 24, ...'1970-01-01T00:00:00.000Z')); // @ts-expect-error Access private property expect(getEncoder().pool).toBe(poolInit); });