This commit is contained in:
2025-08-06 16:22:19 +03:00
parent 16789a942e
commit 40dc6a2637
20 changed files with 250 additions and 183 deletions

View File

@ -3,3 +3,4 @@ package-lock.json
dist
build
coverage
test

View File

@ -2,4 +2,3 @@
"semi": false,
"singleQuote": true
}

View File

@ -1,4 +1,4 @@
import { dts } from "rollup-plugin-dts";
import { dts } from 'rollup-plugin-dts'
export default {
input: 'types/index.d.ts',

View File

@ -1,4 +1,12 @@
import { isHeadless, isSerializableType, parse, serialize, sizeof, sizeofHead, Type } from "."
import {
isHeadless,
isSerializableType,
parse,
serialize,
sizeof,
sizeofHead,
Type,
} from '.'
export function ConstArray(size) {
const obj = { _size: size }
@ -12,16 +20,28 @@ ConstArray.prototype.serialize = function(dv, src, ...inner_types) {
const size = this._size
if (dv.byteLength < this.sizeof(src, ...inner_types)) {
throw new Error(this.name_+ ', ' + inner_types.join(', ') + ' too small buffer')
throw new Error(
this.name_ + ', ' + inner_types.join(', ') + ' too small buffer',
)
}
if (src.length != size) {
throw new Error(this.name_+ ', ' + inner_types.join(', ') + ' 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++) {
const item_head_frame = new DataView(dv.buffer, dv.byteOffset + item_head_size * i)
const item_head_frame = new DataView(
dv.buffer,
dv.byteOffset + item_head_size * i,
)
if (item_headless) {
item_head_frame.setUint32(0, offset)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
@ -40,7 +60,10 @@ ConstArray.prototype.parse = function(dv, ...inner_types) {
const array = Array(size)
for (let i = 0; i < size; i++) {
const item_head_frame = new DataView(dv.buffer, dv.byteOffset + item_head_size * i)
const item_head_frame = new DataView(
dv.buffer,
dv.byteOffset + item_head_size * i,
)
if (item_headless) {
const offset = item_head_frame.getUint32(0)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
@ -71,7 +94,9 @@ ConstArray.prototype.sizeof = function(arg, ...args) {
if (isHeadless(...inner_types)) {
if (src == undefined) {
throw new Error('unknown sizeof ' + this._name + ', ' + inner_types.join(','))
throw new Error(
'unknown sizeof ' + this._name + ', ' + inner_types.join(','),
)
}
let variable_size = 0

View File

@ -1,4 +1,4 @@
import { memcpy, Type } from "."
import { memcpy, Type } from '.'
export function ConstDataView(size) {
const obj = { _size: size }

View File

@ -1,5 +1,5 @@
import { memcpy } from "./mem"
import { Type } from "./Type"
import { memcpy } from './mem'
import { Type } from './Type'
export function ConstString(size) {
const obj = { _size: size }

View File

@ -67,10 +67,14 @@ Int.prototype.serialize = function (dv, src) {
throw new Error(this._name + ' buffer is too small')
}
if (src > this._limits.MAX_VALUE) {
throw new Error(this._name + ` 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) {
throw new Error(this._name + ` 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)
}

View File

@ -1,10 +1,10 @@
import { isHeadless, parse, serialize, sizeof, sizeofHead, Type } from "."
import { isHeadless, parse, serialize, sizeof, sizeofHead, Type } from '.'
export function Struct(type_obj) {
const obj = {}
Object.setPrototypeOf(obj, Struct.prototype)
obj._info_by_key = new Map
obj._info_by_key = new Map()
obj._headless = false
const arg = {}
@ -18,7 +18,7 @@ export function Struct(type_obj) {
types = [value]
}
arg[key] = types.map(type => type.name)
arg[key] = types.map((type) => type.name)
const headless = isHeadless(...types)
obj._info_by_key.set(key, {

View File

@ -6,7 +6,7 @@ export function Type() {
Type.prototype.new = function (func, args) {
this._name = func.name
if (args !== undefined) {
const str_args = Array.from(args).map(arg => JSON.stringify(arg))
const str_args = Array.from(args).map((arg) => JSON.stringify(arg))
this._name += '(' + str_args.join(', ') + ')'
}
}

View File

@ -1,13 +1,22 @@
import { limits } from "./limits"
import { memcpy } from "./mem"
import { Type } from "./Type"
import { Int } from "./Int"
import { limits } from './limits'
import { memcpy } from './mem'
import { Type } from './Type'
import { Int } from './Int'
import { ConstString } from './ConstString'
import { ConstArray } from "./ConstArray"
import { ConstArray } from './ConstArray'
import { ConstDataView } from './ConstDataView'
import { Struct } from './Struct'
export { limits, memcpy, Type, Int, ConstString, ConstArray, ConstDataView, Struct }
export {
limits,
memcpy,
Type,
Int,
ConstString,
ConstArray,
ConstDataView,
Struct,
}
export function serialize(dv, src, ...types) {
const [type, ...inner_types] = types
@ -60,7 +69,10 @@ export function serialize(dv, src, ...types) {
let offset = 4 + item_head_size * size
for (let i = 0; i < size; i++) {
const item_head_frame = new DataView(dv.buffer, dv.byteOffset + 4 + item_head_size * i)
const item_head_frame = new DataView(
dv.buffer,
dv.byteOffset + 4 + item_head_size * i,
)
if (item_headless) {
item_head_frame.setUint32(0, offset)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
@ -114,7 +126,10 @@ export function parse(dv, ...types) {
const array = Array(size)
for (let i = 0; i < size; i++) {
const item_head_frame = new DataView(dv.buffer, dv.byteOffset + 4 + item_head_size * i)
const item_head_frame = new DataView(
dv.buffer,
dv.byteOffset + 4 + item_head_size * i,
)
if (item_headless) {
const offset = item_head_frame.getUint32(0)
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset)
@ -146,12 +161,14 @@ export function isHeadless(...args) {
if (arg instanceof Type) {
return arg.isHeadless(...inner_args)
}
return arg == Array ||
return (
arg == Array ||
arg == String ||
arg == DataView ||
Array.isArray(arg) ||
typeof arg == 'string' ||
arg instanceof DataView
)
}
export function sizeofHead(...args) {
@ -166,10 +183,10 @@ export function sizeof(...args) {
const [arg, ...inner_args] = args
const [arg2, ...inner_args2] = inner_args
if (arg == Boolean || arg2 == Boolean && typeof arg == 'boolean') {
if (arg == Boolean || (arg2 == Boolean && typeof arg == 'boolean')) {
return 1
}
if (arg == Number || arg2 == Number && typeof arg == 'number') {
if (arg == Number || (arg2 == Number && typeof arg == 'number')) {
return 8
}
if (arg2 == String && typeof arg == 'string') {
@ -177,10 +194,8 @@ export function sizeof(...args) {
return 4 + encoder.encode(arg).byteLength
}
if (arg2 == Array && Array.isArray(arg)) {
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)
@ -204,10 +219,12 @@ export function sizeof(...args) {
}
export function isSerializableType(type) {
return type == Boolean ||
return (
type == Boolean ||
type == Number ||
type == String ||
type == Array ||
type == DataView ||
type instanceof Type
)
}

View File

@ -35,16 +35,16 @@ const bool = {
MAX_VALUE: 1,
}
const f32 = {
MIN_VALUE: -3.40282347e+38,
MAX_VALUE: 3.40282347e+38,
MIN_VALUE: -3.40282347e38,
MAX_VALUE: 3.40282347e38,
MIN_NORMAL_VALUE: 1.17549435e-38,
MIN_SAFE_INTEGER: -16777215,
MAX_SAFE_INTEGER: 16777215,
EPSILON: 1.19209290e-07,
EPSILON: 1.1920929e-7,
}
const f64 = {
MIN_VALUE: -1.7976931348623157e+308,
MAX_VALUE: 1.7976931348623157e+308,
MIN_VALUE: -1.7976931348623157e308,
MAX_VALUE: 1.7976931348623157e308,
MIN_NORMAL_VALUE: 2.2250738585072014e-308,
MIN_SAFE_INTEGER: -9007199254740991,
MAX_SAFE_INTEGER: 9007199254740991,

View File

@ -1,12 +1,12 @@
export function memcpy(dest, src) {
const min_size = dest.byteLength > src.byteLength ?
src.byteLength : dest.byteLength
const min_size =
dest.byteLength > src.byteLength ? src.byteLength : dest.byteLength
for (let i = 0; i < Math.floor(min_size / 8); i++) {
dest.setFloat64(i * 8, src.getFloat64(i * 8))
}
for (let i = min_size - min_size % 8; i < min_size; i++) {
for (let i = min_size - (min_size % 8); i < min_size; i++) {
dest.setInt8(i, src.getInt8(i))
}
}

View File

@ -1,8 +1,8 @@
import { Type } from ".";
import { Type } from '.'
/**
* constructs type of array with constant byte size
* @param {number} size number of items
* @returns {Type}
*/
export function ConstArray(size: number): Type;
export function ConstArray(size: number): Type

View File

@ -1,8 +1,8 @@
import { Type } from ".";
import { Type } from '.'
/**
* constructs type of data view with constant byte size
* @param {number} byte_size max byte size
* @returns {Type}
*/
export function ConstDataView(byte_size: number): Type;
export function ConstDataView(byte_size: number): Type

View File

@ -1,8 +1,8 @@
import { Type } from ".";
import { Type } from '.'
/**
* constructs type of utf8-string with constant byte size
* @param {number} byte_size max utf8-encoded byte size
* @returns {Type}
*/
export function ConstString(byte_size: number): Type;
export function ConstString(byte_size: number): Type

4
types/Int.d.ts vendored
View File

@ -1,3 +1,3 @@
import { Type } from ".";
import { Type } from '.'
export function Int(bits: 8 | 16 | 32, sign: 'signed' | 'unsigned'): Type;
export function Int(bits: 8 | 16 | 32, sign: 'signed' | 'unsigned'): Type

6
types/Struct.d.ts vendored
View File

@ -1,7 +1,9 @@
import { SerializableType, Type } from "."
import { SerializableType, Type } from '.'
/**
* constructs type of c-like structure. if field is headless, inside of structure will be stored u32 offset, outside of structure will be stored value of field
* @returns {Type}
*/
export function Struct(type_obj: Record<string, SerializableType | SerializableType[]>): Type;
export function Struct(
type_obj: Record<string, SerializableType | SerializableType[]>,
): Type

53
types/index.d.ts vendored
View File

@ -1,15 +1,30 @@
import { memcpy } from "./mem";
import { limits } from "./limits";
import { Type } from "./Type"
import { Int } from "./Int"
import { ConstString } from "./ConstString"
import { ConstArray } from "./ConstArray";
import { ConstDataView } from "./ConstDataView";
import { Struct } from "./Struct";
import { memcpy } from './mem'
import { limits } from './limits'
import { Type } from './Type'
import { Int } from './Int'
import { ConstString } from './ConstString'
import { ConstArray } from './ConstArray'
import { ConstDataView } from './ConstDataView'
import { Struct } from './Struct'
export { memcpy, limits, Type, Int, ConstString, ConstArray, ConstDataView, Struct }
export {
memcpy,
limits,
Type,
Int,
ConstString,
ConstArray,
ConstDataView,
Struct,
}
export type SerializableType = BooleanConstructor | NumberConstructor | StringConstructor | ArrayConstructor | DataViewConstructor | Type
export type SerializableType =
| BooleanConstructor
| NumberConstructor
| StringConstructor
| ArrayConstructor
| DataViewConstructor
| Type
export type Serializable = unknown
/**
@ -19,41 +34,45 @@ export type Serializable = unknown
* @throws {Error} if too small buffer
* @throws {Error} if array|string|DataView size is higher than limits.u32.MAX_VALUE
*/
export function serialize(dv: DataView, src: Serializable, ...types: SerializableType[]): void;
export function serialize(
dv: DataView,
src: Serializable,
...types: SerializableType[]
): void
/**
* @param {DataView} dv source memory
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {Serializable} parsed object
*/
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 {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {boolean}
*/
export function isHeadless(...types: SerializableType[]): boolean;
export function isHeadless(...types: SerializableType[]): boolean
/**
* if obj has no fixed size, return 4 (sizeof u32 offset)
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {number}
*/
export function sizeofHead(...types: SerializableType[]): number;
export function sizeofHead(...types: SerializableType[]): number
/**
* @param {Serializable} obj to check
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {number}
*/
export function sizeof(obj: Serializable, ...types: SerializableType[]): number;
export function sizeof(obj: Serializable, ...types: SerializableType[]): number
/**
* @param {SerializableType[]} ...types primary and inner types. eg: Array, Number
* @returns {number}
* @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;
export function isSerializableType(type: unknown): boolean

2
types/limits.d.ts vendored
View File

@ -29,4 +29,4 @@ export const limits: {
bool: IntLimit
f32: FloatLimit
f64: FloatLimit
};
}

2
types/mem.d.ts vendored
View File

@ -1,4 +1,4 @@
/**
* copies max possible number of bytes from src to dest (min of both sizes)
*/
export function memcpy(dest: DataView, src: DataView): void;
export function memcpy(dest: DataView, src: DataView): void