fix(Array, {HeadlessType})

This commit is contained in:
2025-08-06 16:20:14 +03:00
parent 35a98456ef
commit 16789a942e
12 changed files with 246 additions and 74 deletions

5
.prettierignore Normal file
View File

@ -0,0 +1,5 @@
# Ignore artifacts:
package-lock.json
dist
build
coverage

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true
}

17
package-lock.json generated
View File

@ -11,6 +11,7 @@
"devDependencies": { "devDependencies": {
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"esbuild": "^0.25.8", "esbuild": "^0.25.8",
"prettier": "^3.6.2",
"rollup": "^4.46.2", "rollup": "^4.46.2",
"rollup-plugin-dts": "^6.2.1", "rollup-plugin-dts": "^6.2.1",
"vitest": "^3.2.4" "vitest": "^3.2.4"
@ -1718,6 +1719,22 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/prettier": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.46.2", "version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",

View File

@ -16,11 +16,13 @@
"scripts": { "scripts": {
"build": "npx esbuild src/index.js --bundle --minify --outfile=dist/index.js --format=esm && npx rollup -c", "build": "npx esbuild src/index.js --bundle --minify --outfile=dist/index.js --format=esm && npx rollup -c",
"test": "vitest", "test": "vitest",
"cover": "vitest --coverage" "cover": "vitest --coverage",
"format": "prettier . --write"
}, },
"devDependencies": { "devDependencies": {
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"esbuild": "^0.25.8", "esbuild": "^0.25.8",
"prettier": "^3.6.2",
"rollup": "^4.46.2", "rollup": "^4.46.2",
"rollup-plugin-dts": "^6.2.1", "rollup-plugin-dts": "^6.2.1",
"vitest": "^3.2.4" "vitest": "^3.2.4"

View File

@ -1,4 +1,4 @@
import { parse, serialize, sizeofHead, Type } from "." import { isHeadless, isSerializableType, parse, serialize, sizeof, sizeofHead, Type } from "."
export function ConstArray(size) { export function ConstArray(size) {
const obj = { _size: size } const obj = { _size: size }
@ -7,39 +7,82 @@ export function ConstArray(size) {
return obj return obj
} }
ConstArray.prototype.serialize = function(dv, src, ...inner_types) { ConstArray.prototype.serialize = function(dv, src, ...inner_types) {
const item_size = sizeofHead(src[0]) const item_headless = isHeadless(...inner_types)
const item_head_size = sizeofHead(src[0], ...inner_types)
const size = this._size const size = this._size
if (dv.byteLength < size * item_size) { if (dv.byteLength < this.sizeof(src, ...inner_types)) {
throw new Error('too small buffer') throw new Error(this.name_+ ', ' + inner_types.join(', ') + ' too small buffer')
} }
if (src.length != size) { if (src.length != size) {
throw new Error('array should be ' + size + ' elements length') throw new Error(this.name_+ ', ' + inner_types.join(', ') + ' should be ' + size + ' elements length')
} }
let offset = item_head_size * size
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const item_frame = new DataView(dv.buffer, dv.byteOffset + item_size * i) const item_head_frame = new DataView(dv.buffer, dv.byteOffset + item_head_size * i)
serialize(item_frame, src[i], ...inner_types) if (item_headless) {
item_head_frame.setUint32(0, offset)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
serialize(item_frame, src[i], ...inner_types)
offset += sizeof(src[i], ...inner_types)
} else {
serialize(item_head_frame, src[i], ...inner_types)
}
} }
return return
} }
ConstArray.prototype.parse = function(dv, ...inner_types) { ConstArray.prototype.parse = function(dv, ...inner_types) {
const item_headless = isHeadless(...inner_types)
const item_head_size = sizeofHead(...inner_types)
const size = this._size const size = this._size
const item_size = sizeofHead(...inner_types)
const array = Array(size) const array = Array(size)
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const item_frame = new DataView(dv.buffer, dv.byteOffset + item_size * i) const item_head_frame = new DataView(dv.buffer, dv.byteOffset + item_head_size * i)
array[i] = parse(item_frame, ...inner_types) if (item_headless) {
const offset = item_head_frame.getUint32(0)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
array[i] = parse(item_frame, ...inner_types)
} else {
array[i] = parse(item_head_frame, ...inner_types)
}
} }
return array return array
} }
ConstArray.prototype.isHeadless = function() { ConstArray.prototype.isHeadless = function(...inner_types) {
return false return isHeadless(...inner_types)
} }
ConstArray.prototype.sizeof = function(...inner_types) { ConstArray.prototype.sizeof = function(arg, ...args) {
return sizeofHead(...inner_types) * this._size let inner_types
let src
if (isSerializableType(arg)) {
src = undefined
inner_types = [arg, ...args]
} else {
src = arg
inner_types = args
}
const fixed_size = sizeofHead(...inner_types) * this._size
if (isHeadless(...inner_types)) {
if (src == undefined) {
throw new Error('unknown sizeof ' + this._name + ', ' + inner_types.join(','))
}
let variable_size = 0
for (const item of src) {
variable_size += sizeof(item, ...inner_types)
}
return fixed_size + variable_size
} else {
return fixed_size
}
} }
Object.setPrototypeOf(ConstArray.prototype, Type.prototype) Object.setPrototypeOf(ConstArray.prototype, Type.prototype)
Object.freeze(ConstArray.prototype) Object.freeze(ConstArray.prototype)

View File

@ -8,10 +8,10 @@ export function ConstDataView(size) {
} }
ConstDataView.prototype.serialize = function(dv, src) { ConstDataView.prototype.serialize = function(dv, src) {
if (dv.byteLength < this._size) { if (dv.byteLength < this._size) {
throw new Error('too small buffer') throw new Error(this._name + ' too small buffer')
} }
if (src.byteLength != this._size) { if (src.byteLength != this._size) {
throw new Error('buffer should be ' + this._size + ' bytes length') throw new Error(this._name + ' should be ' + this._size + ' bytes length')
} }
memcpy(dv, src) memcpy(dv, src)
return return

View File

@ -12,10 +12,10 @@ ConstString.prototype.serialize = function(dv, src) {
const encoded = new DataView(encoder.encode(src).buffer, 0, this._size) const encoded = new DataView(encoder.encode(src).buffer, 0, this._size)
if (dv.byteLength < encoded.byteLength) { if (dv.byteLength < encoded.byteLength) {
throw new Error('too small buffer') throw new Error(this._name + ' too small buffer')
} }
if (src.length != this._size) { if (src.length != this._size) {
throw new Error('string should be ' + this._size + ' symbols length') throw new Error(this._name + ' should be ' + this._size + ' symbols length')
} }
memcpy(dv, encoded) memcpy(dv, encoded)

View File

@ -27,7 +27,7 @@ export function Int(bits, sign) {
break break
default: default:
throw new Error('incorrect bits ' + bits) throw new Error(obj._name + ' incorrect bits ' + bits)
} }
break break
@ -52,25 +52,25 @@ export function Int(bits, sign) {
break break
default: default:
throw new Error('incorrect bits ' + bits) throw new Error(obj._name + ' incorrect bits ' + bits)
} }
break break
default: default:
throw new Error('incorrect sign ' + sign) throw new Error(obj._name + ' incorrect sign ' + sign)
} }
return obj return obj
} }
Int.prototype.serialize = function (dv, src) { Int.prototype.serialize = function (dv, src) {
if (dv.byteLength < this._size) { if (dv.byteLength < this._size) {
throw new Error('buffer is too small') throw new Error(this._name + ' buffer is too small')
} }
if (src > this._limits.MAX_VALUE) { if (src > this._limits.MAX_VALUE) {
throw new Error(`number should be less or equal than ` + this._limits.MAX_VALUE) throw new Error(this._name + ` should be less or equal than ` + this._limits.MAX_VALUE)
} }
if (src < this._limits.MIN_VALUE) { if (src < this._limits.MIN_VALUE) {
throw new Error(`number should be more or equal than ` + this._limits.MIN_VALUE) throw new Error(this._name + ` should be more or equal than ` + this._limits.MIN_VALUE)
} }
this._dv_set.call(dv, 0, src) this._dv_set.call(dv, 0, src)
} }

View File

@ -14,14 +14,14 @@ export function serialize(dv, src, ...types) {
if (type == Boolean && typeof src == 'boolean') { if (type == Boolean && typeof src == 'boolean') {
if (dv.byteLength < 1) { if (dv.byteLength < 1) {
throw new Error('too small buffer') throw new Error('Boolean too small buffer')
} }
dv.setUint8(0, src ? 1 : 0) dv.setUint8(0, src ? 1 : 0)
return return
} }
if (type == Number && typeof src == 'number') { if (type == Number && typeof src == 'number') {
if (dv.byteLength < 8) { if (dv.byteLength < 8) {
throw new Error('too small buffer') throw new Error('Number too small buffer')
} }
dv.setFloat64(0, src) dv.setFloat64(0, src)
return return
@ -31,10 +31,10 @@ export function serialize(dv, src, ...types) {
let encoded = new DataView(encoder.encode(src).buffer) let encoded = new DataView(encoder.encode(src).buffer)
if (dv.byteLength < 4 + encoded.byteLength) { if (dv.byteLength < 4 + encoded.byteLength) {
throw new Error('too small buffer') throw new Error('String too small buffer')
} }
if (encoded.byteLength > limits.u32.MAX_VALUE) { if (encoded.byteLength > limits.u32.MAX_VALUE) {
throw new Error('string is too long') throw new Error('String is too long')
} }
dv.setUint32(0, encoded.byteLength) dv.setUint32(0, encoded.byteLength)
@ -44,31 +44,40 @@ export function serialize(dv, src, ...types) {
return return
} }
if (type == Array && Array.isArray(src)) { if (type == Array && Array.isArray(src)) {
const item_headless = isHeadless(...inner_types)
const item_size = sizeofHead(src[0]) const item_head_size = sizeofHead(...inner_types)
const size = src.length const size = src.length
if (dv.byteLength < 4 + size * item_size) { if (dv.byteLength < sizeof(src, ...types)) {
throw new Error('too small buffer') throw new Error('Array, ' + inner_types.join(', ') + ' too small buffer')
} }
if (size > limits.u32.MAX_VALUE) { if (size > limits.u32.MAX_VALUE) {
throw new Error('array is too long') throw new Error('Array, ' + inner_types.join(', ') + ' is too long')
} }
dv.setUint32(0, size) dv.setUint32(0, size)
let offset = 4 + item_head_size * size
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const item_frame = new DataView(dv.buffer, dv.byteOffset + 4 + item_size * i) const item_head_frame = new DataView(dv.buffer, dv.byteOffset + 4 + item_head_size * i)
serialize(item_frame, src[i], ...inner_types) if (item_headless) {
item_head_frame.setUint32(0, offset)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
serialize(item_frame, src[i], ...inner_types)
offset += sizeof(src[i], ...inner_types)
} else {
serialize(item_head_frame, src[i], ...inner_types)
}
} }
return return
} }
if (type == DataView && src instanceof DataView) { if (type == DataView && src instanceof DataView) {
if (dv.byteLength < 4 + src.byteLength) { if (dv.byteLength < 4 + src.byteLength) {
throw new Error('too small buffer') throw new Error('DataView too small buffer')
} }
if (src.byteLength > limits.u32.MAX_VALUE) { if (src.byteLength > limits.u32.MAX_VALUE) {
throw new Error('data view is too long') throw new Error('DataView data is too long')
} }
dv.setUint32(0, src.byteLength) dv.setUint32(0, src.byteLength)
@ -99,13 +108,20 @@ export function parse(dv, ...types) {
return decoder.decode(frame) return decoder.decode(frame)
} }
if (type == Array) { if (type == Array) {
const item_headless = isHeadless(...inner_types)
const item_head_size = sizeofHead(...inner_types)
const size = dv.getUint32(0) const size = dv.getUint32(0)
const item_size = sizeofHead(inner_types[0])
const array = Array(size) const array = Array(size)
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const item_frame = new DataView(dv.buffer, dv.byteOffset + 4 + item_size * i) const item_head_frame = new DataView(dv.buffer, dv.byteOffset + 4 + item_head_size * i)
array[i] = parse(item_frame, ...inner_types) if (item_headless) {
const offset = item_head_frame.getUint32(0)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
array[i] = parse(item_frame, ...inner_types)
} else {
array[i] = parse(item_head_frame, ...inner_types)
}
} }
return array return array
@ -148,29 +164,50 @@ export function sizeofHead(...args) {
export function sizeof(...args) { export function sizeof(...args) {
const [arg, ...inner_args] = args const [arg, ...inner_args] = args
const [arg2, ...inner_args2] = inner_args
if (arg == Boolean || typeof arg == 'boolean') { if (arg == Boolean || arg2 == Boolean && typeof arg == 'boolean') {
return 1 return 1
} }
if (arg == Number || typeof arg == 'number') { if (arg == Number || arg2 == Number && typeof arg == 'number') {
return 8 return 8
} }
if (typeof arg == 'string') { if (arg2 == String && typeof arg == 'string') {
const encoder = new TextEncoder('utf-8') const encoder = new TextEncoder('utf-8')
return 4 + encoder.encode(arg).byteLength return 4 + encoder.encode(arg).byteLength
} }
if (Array.isArray(arg)) { if (arg2 == Array && Array.isArray(arg)) {
return 4 + sizeofHead(arg[0]) * arg.length
const fixed_size = 4 + sizeofHead(...inner_args2) * arg.length
if (isHeadless(...inner_args2)) {
let variable_size = 0
for (const item of arg) {
variable_size += sizeof(item, ...inner_args2)
}
return fixed_size + variable_size
} else {
return fixed_size
}
} }
if (arg instanceof DataView) { if (arg2 == DataView && arg instanceof DataView) {
return 4 + arg.byteLength return 4 + arg.byteLength
} }
if (arg instanceof Type) { if (arg instanceof Type) {
return arg.sizeof(...inner_args) return arg.sizeof(...inner_args)
} }
const [arg2, ...inner_args2] = inner_args
if (arg2 instanceof Type) { if (arg2 instanceof Type) {
return arg2.sizeof(arg, ...inner_args2) return arg2.sizeof(arg, ...inner_args2)
} }
throw new Error('unknown size of ' + arg) throw new Error('unknown size of ' + args)
}
export function isSerializableType(type) {
return type == Boolean ||
type == Number ||
type == String ||
type == Array ||
type == DataView ||
type instanceof Type
} }

View File

@ -1,5 +1,5 @@
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
import { ConstArray, parse, serialize, sizeof, sizeofHead } from "../src"; import { ConstArray, isHeadless, parse, serialize, sizeof, sizeofHead } from "../src";
import { expectDataViewEqual, filledDataView, sizedDataView } from "."; import { expectDataViewEqual, filledDataView, sizedDataView } from ".";
describe(ConstArray.name, () => { describe(ConstArray.name, () => {
@ -33,4 +33,41 @@ describe(ConstArray.name, () => {
test('sizeof', () => { test('sizeof', () => {
expect(sizeofHead(ConstArray(2), Number)).toEqual(16) expect(sizeofHead(ConstArray(2), Number)).toEqual(16)
}) })
const array_dv = filledDataView([
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x11,
// hello
0x00, 0x00, 0x00, 0x05,
0x68, 0x65, 0x6C, 0x6C, 0x6F,
// sek1ro
0x00, 0x00, 0x00, 0x06,
0x73, 0x65, 0x6b, 0x31, 0x72, 0x6f,
])
const array = ['hello', 'sek1ro']
test('serialize, String', () => {
const actual = sizedDataView(sizeof(array, ConstArray(2), String))
expect(() => sizeof(ConstArray(2), String)).toThrow()
serialize(actual, array, ConstArray(2), String)
expectDataViewEqual(actual, array_dv)
})
test('parse, String', () => {
expect(parse(array_dv, ConstArray(2), String)).toEqual(array)
})
test('isHeadless', () => {
expect(isHeadless(ConstArray(2), Number)).toBeFalsy()
expect(isHeadless(ConstArray(2), String)).toBeTruthy()
})
test('sizeof', () => {
expect(sizeof(ConstArray(2), Number)).toBe(16)
expect(() => sizeof(ConstArray(2), String)).toThrow()
expect(sizeof(['hello', 'sek1ro'], ConstArray(2), String)).toBe(27)
})
}) })

View File

@ -1,6 +1,6 @@
import { describe, test, expect } from 'vitest' import { describe, test, expect } from 'vitest'
import { expectDataViewEqual, filledDataView, sizedDataView } from '.' import { expectDataViewEqual, filledDataView, sizedDataView } from '.'
import { parse, serialize, sizeof, sizeofHead } from '../src' import { isHeadless, parse, serialize, sizeof, sizeofHead } from '../src'
describe('serialize', () => { describe('serialize', () => {
test('Boolean', () => { test('Boolean', () => {
@ -32,7 +32,7 @@ describe('serialize', () => {
let dv = sizedDataView(8) let dv = sizedDataView(8)
expect(() => serialize(dv, 'hello', String)).toThrow() expect(() => serialize(dv, 'hello', String)).toThrow()
dv = sizedDataView(sizeof('hello')) dv = sizedDataView(sizeof('hello', String))
expect(9).toEqual(dv.byteLength) expect(9).toEqual(dv.byteLength)
serialize(dv, 'hello', String) serialize(dv, 'hello', String)
@ -47,12 +47,30 @@ describe('serialize', () => {
let dv = sizedDataView(19) let dv = sizedDataView(19)
expect(() => serialize(dv, [1, 2], Array, Number)).toThrow() expect(() => serialize(dv, [1, 2], Array, Number)).toThrow()
dv = sizedDataView(sizeof([1, 2])) dv = sizedDataView(sizeof([1, 2], Array, Number))
expect(20).toEqual(dv.byteLength) expect(20).toEqual(dv.byteLength)
serialize(dv, [1, 2], Array, Number) serialize(dv, [1, 2], Array, Number)
expectDataViewEqual(dv, expected) expectDataViewEqual(dv, expected)
}) })
test('Array, String', () => {
const expected = filledDataView([
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x0C,
0x00, 0x00, 0x00, 0x15,
// hello
0x00, 0x00, 0x00, 0x05,
0x68, 0x65, 0x6C, 0x6C, 0x6F,
// sek1ro
0x00, 0x00, 0x00, 0x06,
0x73, 0x65, 0x6b, 0x31, 0x72, 0x6f,
])
const array = ['hello', 'sek1ro']
const actual = sizedDataView(sizeof(array, Array, String))
serialize(actual, array, Array, String)
expectDataViewEqual(actual, expected)
})
test('DataView', () => { test('DataView', () => {
const expected = filledDataView([ const expected = filledDataView([
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04,
@ -100,6 +118,22 @@ describe('parse', () => {
const actual = parse(dv, Array, Number) const actual = parse(dv, Array, Number)
expect(actual).toEqual(expected) expect(actual).toEqual(expected)
}) })
test('Array, String', () => {
const expected = ['hello', 'sek1ro']
const dv = filledDataView([
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x0C,
0x00, 0x00, 0x00, 0x15,
// hello
0x00, 0x00, 0x00, 0x05,
0x68, 0x65, 0x6C, 0x6C, 0x6F,
// sek1ro
0x00, 0x00, 0x00, 0x06,
0x73, 0x65, 0x6b, 0x31, 0x72, 0x6f,
])
const actual = parse(dv, Array, String)
expect(actual).toEqual(expected)
})
test('DataView', () => { test('DataView', () => {
const expected = filledDataView([ const expected = filledDataView([
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03,
@ -113,22 +147,28 @@ describe('parse', () => {
}) })
}) })
describe('isHeadless', () => {
test('Array', () => {
expect(isHeadless(Array, Number)).toBeTruthy()
expect(isHeadless(Array, String)).toBeTruthy()
})
})
describe('sizeofHead', () => { describe('sizeofHead', () => {
test('Boolean', () => {
expect(sizeofHead(Boolean)).toEqual(1)
})
test('Number', () => { test('Number', () => {
expect(sizeofHead(Number)).toEqual(8) expect(sizeofHead(Number)).toEqual(8)
expect(sizeofHead(1, Number)).toEqual(8)
}) })
test('String', () => { test('String', () => {
expect(sizeofHead(String)).toEqual(4) expect(sizeofHead(String)).toEqual(4)
expect(sizeofHead('s', String)).toEqual(4)
}) })
test('Array', () => { test('Array', () => {
expect(sizeofHead(Array, Number)).toEqual(4) expect(sizeofHead(Array, Number)).toEqual(4)
expect(sizeofHead([1], Array, Number)).toEqual(4)
}) })
test('DataView', () => { test('DataView', () => {
expect(sizeofHead(DataView)).toEqual(4) expect(sizeofHead(DataView)).toEqual(4)
expect(sizeofHead(filledDataView([0x01]), DataView)).toEqual(4)
}) })
}) })

18
types/index.d.ts vendored
View File

@ -28,14 +28,6 @@ export function serialize(dv: DataView, src: Serializable, ...types: Serializabl
*/ */
export function parse(dv: DataView, ...types: SerializableType[]): Serializable; export function parse(dv: DataView, ...types: SerializableType[]): Serializable;
/**
* some types, like Array, String, has no fixed size. So in Structure they are stored as u32 offset, which points to their beginning
* @param {Serializable} obj to check
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {boolean}
*/
export function isHeadless(obj: Serializable, ...types: SerializableType[]): boolean;
/** /**
* some types, like Array, String, has no fixed size. So in Structure they are stored as u32 offset, which points to their beginning * some types, like Array, String, has no fixed size. So in Structure they are stored as u32 offset, which points to their beginning
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number * @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
@ -43,14 +35,6 @@ export function isHeadless(obj: Serializable, ...types: SerializableType[]): boo
*/ */
export function isHeadless(...types: SerializableType[]): boolean; export function isHeadless(...types: SerializableType[]): boolean;
/**
* if obj has no fixed size, return 4 (sizeof u32 offset)
* @param {Serializable} obj to check
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {number}
*/
export function sizeofHead(obj: Serializable, ...types: SerializableType[]): number;
/** /**
* if obj has no fixed size, return 4 (sizeof u32 offset) * if obj has no fixed size, return 4 (sizeof u32 offset)
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number * @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
@ -71,3 +55,5 @@ export function sizeof(obj: Serializable, ...types: SerializableType[]): number;
* @throws {Error} if passed Array, String, DataView type (unknown sizeof) * @throws {Error} if passed Array, String, DataView type (unknown sizeof)
*/ */
export function sizeof(...types: SerializableType[]): number; export function sizeof(...types: SerializableType[]): number;
export function isSerializableType(type: unknown): boolean;