UNPKG

beson

Version:

Yet an another binary representation of json format

356 lines (310 loc) 10.1 kB
import {HAS_NODE_BUFFER, DATA_TYPE, TYPE_HEADER, UTF8Encode, MergeArrayBuffers} from "src/helper.esm.js"; import { Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, UInt512, Int256, Int512, Float32, BinaryData } from "src/beson-types.esm.js"; //@export=serialize const SEQUENCE_END = (new Uint8Array([0x00]).buffer); function Serialize(data) { const chunks = []; SerializeData(data, (chunk)=>{ chunks.push(chunk); }); return new Uint8Array(MergeArrayBuffers(chunks)); } function SerializeData(data, data_cb) { const type = __serializeType("$", data, data_cb); __serializeTypeData("$", type, data, data_cb); } function __serializeType(path, data, data_cb) { if ( data === undefined ) return; if ( data === null ) { data_cb(Uint8Array.from([TYPE_HEADER.NULL]).buffer); return DATA_TYPE.NULL; } const data_type = typeof data; if ( data_type === 'boolean') { data_cb(Uint8Array.from([data ? TYPE_HEADER.TRUE : TYPE_HEADER.FALSE]).buffer); return data ? DATA_TYPE.TRUE : DATA_TYPE.FALSE; } if ( data_type === "number" ) { data_cb(Uint8Array.from([TYPE_HEADER.FLOAT64]).buffer); return DATA_TYPE.FLOAT64; } if ( data_type === "string" ) { data_cb(Uint8Array.from([ TYPE_HEADER.STRING ]).buffer); return DATA_TYPE.STRING; } if (Array.isArray(data)) { data_cb(Uint8Array.from([ TYPE_HEADER.ARRAY ]).buffer); return DATA_TYPE.ARRAY; } if ( data instanceof Int8 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT8 ]).buffer); return DATA_TYPE.INT8; } if ( data instanceof UInt8 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT8 ]).buffer); return DATA_TYPE.UINT8; } if ( data instanceof Int16 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT16 ]).buffer); return DATA_TYPE.INT16; } if ( data instanceof UInt16 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT16 ]).buffer); return DATA_TYPE.UINT16; } if ( data instanceof Int32 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT32 ]).buffer); return DATA_TYPE.INT32; } if ( data instanceof UInt32 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT32 ]).buffer); return DATA_TYPE.UINT32; } if ( data instanceof Int64 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT64 ]).buffer); return DATA_TYPE.INT64; } if ( data instanceof UInt64 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT64 ]).buffer); return DATA_TYPE.UINT64; } if ( data instanceof Int128 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT128 ]).buffer); return DATA_TYPE.INT128; } if ( data instanceof UInt128 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT128 ]).buffer); return DATA_TYPE.UINT128; } if ( data instanceof Int256 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT256 ]).buffer); return DATA_TYPE.INT256; } if ( data instanceof UInt256 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT256 ]).buffer); return DATA_TYPE.UINT256; } if ( data instanceof Int512 ) { data_cb(Uint8Array.from([ TYPE_HEADER.INT512 ]).buffer); return DATA_TYPE.INT512; } if ( data instanceof UInt512 ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT512 ]).buffer); return DATA_TYPE.UINT512; } if ( data instanceof Float32 ) { data_cb(Uint8Array.from([ TYPE_HEADER.FLOAT32 ]).buffer); return DATA_TYPE.FLOAT32; } if (data instanceof Date) { data_cb(Uint8Array.from([ TYPE_HEADER.DATE ]).buffer); return DATA_TYPE.DATE; } if (data instanceof RegExp) { data_cb(Uint8Array.from([ TYPE_HEADER.REGEX ]).buffer); return DATA_TYPE.REGEX; } if (data instanceof Map) { data_cb(Uint8Array.from([ TYPE_HEADER.MAP ]).buffer); return DATA_TYPE.MAP; } if (data instanceof Set) { data_cb(Uint8Array.from([ TYPE_HEADER.SET ]).buffer); return DATA_TYPE.SET; } if ( data instanceof ArrayBuffer) { data_cb(Uint8Array.from([ TYPE_HEADER.ARRAY_BUFFER ]).buffer); return DATA_TYPE.ARRAY_BUFFER; } if ( data instanceof DataView) { data_cb(Uint8Array.from([ TYPE_HEADER.DATA_VIEW ]).buffer); return DATA_TYPE.DATA_VIEW; } // NOTE: This line must be prior to Uint8Array since NodeJS Buffer is defined to be inheritance of Uint8Array if ( HAS_NODE_BUFFER && data instanceof Buffer ) { data_cb(Uint8Array.from([ TYPE_HEADER.SPECIAL_BUFFER ]).buffer); return DATA_TYPE.SPECIAL_BUFFER; } if ( data instanceof Uint8Array) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT8_ARRAY ]).buffer); return DATA_TYPE.UINT8_ARRAY; } if ( data instanceof Int8Array) { data_cb(Uint8Array.from([ TYPE_HEADER.INT8_ARRAY ]).buffer); return DATA_TYPE.INT8_ARRAY; } if ( data instanceof Uint16Array) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT16_ARRAY ]).buffer); return DATA_TYPE.UINT16_ARRAY; } if ( data instanceof Int16Array) { data_cb(Uint8Array.from([ TYPE_HEADER.INT16_ARRAY ]).buffer); return DATA_TYPE.INT16_ARRAY; } if ( data instanceof Uint32Array) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT32_ARRAY ]).buffer); return DATA_TYPE.UINT32_ARRAY; } if ( data instanceof Int32Array) { data_cb(Uint8Array.from([ TYPE_HEADER.INT32_ARRAY ]).buffer); return DATA_TYPE.INT32_ARRAY; } if ( data instanceof Float32Array) { data_cb(Uint8Array.from([ TYPE_HEADER.FLOAT32_ARRAY ]).buffer); return DATA_TYPE.FLOAT32_ARRAY; } if ( data instanceof Float64Array) { data_cb(Uint8Array.from([ TYPE_HEADER.FLOAT64_ARRAY ]).buffer); return DATA_TYPE.FLOAT64_ARRAY; } if (Object(data) === data) { if ( typeof data.toBytes === "function" ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT8_ARRAY ]).buffer); return DATA_TYPE.BINARIZABLE; } data_cb(Uint8Array.from([ TYPE_HEADER.OBJECT ]).buffer); return DATA_TYPE.OBJECT; } if (data_type === 'object') { if ( typeof data.toBytes === "function" ) { data_cb(Uint8Array.from([ TYPE_HEADER.UINT8_ARRAY ]).buffer); return DATA_TYPE.BINARIZABLE; } data_cb(Uint8Array.from([ TYPE_HEADER.OBJECT ]).buffer); return DATA_TYPE.OBJECT; } console.error(`Given path ${path} cannot be serialized as beson! Skipping...`); console.error(data); return; } function __serializeTypeData(path, type, data, data_cb) { if (type === undefined || type === DATA_TYPE.NULL || type === DATA_TYPE.FALSE || type === DATA_TYPE.TRUE) { // null and boolean has no data payload return; } if ( BinaryData.isBinaryData(data) ) { data_cb(data._ab); return; } if ( type === DATA_TYPE.FLOAT64 ) { const buff = new Float64Array([data]); data_cb(buff.buffer); return; } if ( type === DATA_TYPE.STRING ) { const dataBuffer = UTF8Encode(data); const lengthData = new Uint32Array([dataBuffer.byteLength]); data_cb(lengthData.buffer); data_cb(dataBuffer); return; } if ( type === DATA_TYPE.DATE ) { const dateData = new Float64Array([data.getTime()]); data_cb(dateData.buffer); return; } if ( type === DATA_TYPE.SPECIAL_BUFFER ) { const buff = new Uint8Array(data.length); buff.set(data); const lengthData = new Uint32Array([buff.length]); data_cb(lengthData.buffer); data_cb(buff.buffer); return; } if ( type === DATA_TYPE.BINARIZABLE ) { const raw_data = data.toBytes(); if ( !(raw_data instanceof Uint8Array) ) { throw new TypeError( "Beson binary interface `toBytes` must return Uint8Array instance!" ) } const lengthData = new Uint32Array([raw_data.length]); data_cb(lengthData.buffer); data_cb(raw_data.buffer); return; } if ( data instanceof ArrayBuffer ) { const lengthData = new Uint32Array([data.byteLength]); data_cb(lengthData.buffer); data_cb(data); return; } if ( ArrayBuffer.isView(data) ) { const buffer = data.buffer; const lengthData = new Uint32Array([buffer.byteLength]); data_cb(lengthData.buffer); data_cb(buffer); return; } if ( type === DATA_TYPE.REGEX ) { const sourceBuffer = UTF8Encode(data.source); if ( sourceBuffer.byteLength > 65535 ) { throw new RangeError( "Beson can only accept regex with source string not longer than 65535 bytes!" ); } const flagBuffer = UTF8Encode(data.flags); if ( flagBuffer.byteLength > 255 ) { throw new RangeError( "Beson can only accept regex with flag string not longer than 255 bytes!" ); } data_cb(Uint16Array.from([sourceBuffer.byteLength]).buffer); data_cb(sourceBuffer); data_cb(Uint8Array.from([flagBuffer.byteLength]).buffer); data_cb(flagBuffer); return; } if (type === DATA_TYPE.ARRAY || type === DATA_TYPE.SET) { return __serializeArrayAndSet(path, data, data_cb); } if (type === DATA_TYPE.OBJECT) { return __serializeObject(path, data, data_cb); } if (type === DATA_TYPE.MAP) { return __serializeMap(path, data, data_cb); } } function __serializeArrayAndSet(path, array, data_cb) { for ( let data of array ) { if ( data === undefined ) { data = null; } const type = __serializeType(path, data, data_cb); __serializeTypeData(path, type, data, data_cb); } data_cb(SEQUENCE_END); } function __serializeShortString(data, data_cb) { const buffer = UTF8Encode(data); if ( buffer.byteLength > 65535 ) { throw new RangeError("Given key cannot be larger than 65565 bytes!"); } const length_data = Uint16Array.from([buffer.byteLength]).buffer; data_cb(length_data); data_cb(buffer); } function __serializeObject(path, object, data_cb) { for ( let key in object ) { const data = object[key]; if ( data === undefined ) continue; path = `\`${path}\`.\`${key}\``; const type = __serializeType(path, data, data_cb); __serializeShortString(`${key}`, data_cb); __serializeTypeData(path, type, data, data_cb); } data_cb(SEQUENCE_END); } function __serializeMap(path, map, data_cb) { for ( let [key, data] of map ) { if ( data === undefined ) continue; if ( Object(key) === key ) { console.error( "You're serializing a Map that contains object key! Some object references will not be kept!" ); } path = `\`${path}\`.\`${key}\``; const key_type = __serializeType(path, key, data_cb); __serializeTypeData(path, key_type, key, data_cb); const data_type = __serializeType(path, data, data_cb); __serializeTypeData(path, data_type, data, data_cb); } data_cb(SEQUENCE_END); } //@endexport export {Serialize}; export {SerializeData};