utilium 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/buffer.d.ts +1 -0
- package/dist/buffer.js +7 -0
- package/dist/internal/primitives.d.ts +5 -5
- package/dist/internal/primitives.js +2 -2
- package/dist/internal/struct.d.ts +30 -15
- package/dist/internal/struct.js +22 -22
- package/dist/objects.d.ts +1 -2
- package/dist/struct.d.ts +2 -3
- package/dist/struct.js +24 -16
- package/package.json +1 -1
- package/src/buffer.ts +6 -0
- package/src/internal/primitives.ts +5 -5
- package/src/internal/struct.ts +63 -45
- package/src/objects.ts +1 -1
- package/src/struct.ts +30 -20
package/dist/buffer.d.ts
CHANGED
@@ -13,3 +13,4 @@ export interface ArrayBufferViewConstructor {
|
|
13
13
|
* @returns The original buffer if resized successfully, or a newly created buffer
|
14
14
|
*/
|
15
15
|
export declare function extendBuffer<T extends ArrayBufferLike | ArrayBufferView>(buffer: T, newByteLength: number): T;
|
16
|
+
export declare function toUint8Array(buffer: ArrayBufferLike | ArrayBufferView): Uint8Array;
|
package/dist/buffer.js
CHANGED
@@ -30,3 +30,10 @@ export function extendBuffer(buffer, newByteLength) {
|
|
30
30
|
return newBuffer;
|
31
31
|
}
|
32
32
|
}
|
33
|
+
export function toUint8Array(buffer) {
|
34
|
+
if (buffer instanceof Uint8Array)
|
35
|
+
return buffer;
|
36
|
+
if (!ArrayBuffer.isView(buffer))
|
37
|
+
return new Uint8Array(buffer);
|
38
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
39
|
+
}
|
@@ -6,16 +6,16 @@ type BitsToBytes = {
|
|
6
6
|
'128': 16;
|
7
7
|
};
|
8
8
|
export type Size<T extends string> = T extends `${'int' | 'uint' | 'float'}${infer bits}` ? bits extends keyof BitsToBytes ? BitsToBytes[bits] : never : never;
|
9
|
-
export declare const
|
10
|
-
export type
|
11
|
-
export type Valid =
|
12
|
-
export declare const
|
9
|
+
export declare const typeNames: readonly ["int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "int128", "uint128", "float32", "float64", "float128"];
|
10
|
+
export type Typename = (typeof typeNames)[number];
|
11
|
+
export type Valid = Typename | Capitalize<Typename> | 'char';
|
12
|
+
export declare const validNames: ("int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "int128" | "uint128" | "float32" | "float64" | "float128" | "Int8" | "Uint8" | "Int16" | "Uint16" | "Int32" | "Uint32" | "Int64" | "Uint64" | "Int128" | "Uint128" | "Float32" | "Float64" | "Float128" | "char")[];
|
13
13
|
export declare const regex: RegExp;
|
14
14
|
export type Normalize<T extends Valid> = T extends 'char' ? 'uint8' : Uncapitalize<T>;
|
15
15
|
export declare function normalize<T extends Valid>(type: T): Normalize<T>;
|
16
16
|
export declare function isType(type: {
|
17
17
|
toString(): string;
|
18
|
-
}): type is
|
18
|
+
}): type is Typename;
|
19
19
|
export declare function isValid(type: {
|
20
20
|
toString(): string;
|
21
21
|
}): type is Valid;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { capitalize } from '../string.js';
|
2
|
-
export const
|
2
|
+
export const typeNames = [
|
3
3
|
'int8',
|
4
4
|
'uint8',
|
5
5
|
'int16',
|
@@ -14,7 +14,7 @@ export const types = [
|
|
14
14
|
'float64',
|
15
15
|
'float128',
|
16
16
|
];
|
17
|
-
export const
|
17
|
+
export const validNames = [...typeNames, ...typeNames.map(t => capitalize(t)), 'char'];
|
18
18
|
export const regex = /^(u?int|float)(8|16|32|64|128)$/i;
|
19
19
|
export function normalize(type) {
|
20
20
|
return (type == 'char' ? 'uint8' : type.toLowerCase());
|
@@ -2,17 +2,25 @@ import type { ClassLike } from '../types.js';
|
|
2
2
|
import type * as primitive from './primitives.js';
|
3
3
|
declare global {
|
4
4
|
interface SymbolConstructor {
|
5
|
-
|
6
|
-
readonly
|
5
|
+
/** User-defined size */
|
6
|
+
readonly size: unique symbol;
|
7
|
+
/** User-defined serialization */
|
8
|
+
readonly serialize: unique symbol;
|
9
|
+
/** User-defined deserialization */
|
10
|
+
readonly deserialize: unique symbol;
|
7
11
|
}
|
8
12
|
}
|
13
|
+
export type TypeLike = UserDefined | Like | primitive.Valid;
|
14
|
+
export type Type = UserDefined | Static | primitive.Typename;
|
15
|
+
/**
|
16
|
+
* Member initialization data
|
17
|
+
* This is needed since class decorators are called *after* member decorators
|
18
|
+
*/
|
9
19
|
export interface MemberInit {
|
10
20
|
name: string;
|
11
21
|
type: string | ClassLike;
|
12
22
|
length?: number;
|
13
23
|
}
|
14
|
-
/** @deprecated */
|
15
|
-
export declare const init: typeof Symbol.struct_init;
|
16
24
|
/**
|
17
25
|
* Options for struct initialization
|
18
26
|
*/
|
@@ -22,7 +30,7 @@ export interface Options {
|
|
22
30
|
isUnion: boolean;
|
23
31
|
}
|
24
32
|
export interface Member {
|
25
|
-
type:
|
33
|
+
type: Type;
|
26
34
|
offset: number;
|
27
35
|
length?: number;
|
28
36
|
}
|
@@ -30,20 +38,18 @@ export interface Metadata {
|
|
30
38
|
options: Partial<Options>;
|
31
39
|
members: Map<string, Member>;
|
32
40
|
size: number;
|
41
|
+
init?: MemberInit[];
|
33
42
|
}
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
[Symbol.struct_metadata]?: T;
|
38
|
-
[Symbol.struct_init]?: MemberInit[];
|
39
|
-
}
|
43
|
+
type _DecoratorMetadata<T extends Metadata = Metadata> = DecoratorMetadata & {
|
44
|
+
struct?: Partial<T>;
|
45
|
+
};
|
40
46
|
export interface DecoratorContext<T extends Metadata = Metadata> {
|
41
47
|
metadata: _DecoratorMetadata<T>;
|
42
48
|
}
|
43
49
|
export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
|
44
50
|
export interface Static<T extends Metadata = Metadata> {
|
45
|
-
[Symbol.metadata]:
|
46
|
-
|
51
|
+
[Symbol.metadata]: {
|
52
|
+
struct: T;
|
47
53
|
};
|
48
54
|
new (): Instance<T>;
|
49
55
|
prototype: Instance<T>;
|
@@ -52,7 +58,7 @@ export interface StaticLike<T extends Metadata = Metadata> extends ClassLike {
|
|
52
58
|
[Symbol.metadata]?: _DecoratorMetadata<T> | null;
|
53
59
|
}
|
54
60
|
export declare function isValidMetadata<T extends Metadata = Metadata>(arg: unknown): arg is DecoratorMetadata & {
|
55
|
-
|
61
|
+
struct: T;
|
56
62
|
};
|
57
63
|
/**
|
58
64
|
* Polyfill context.metadata
|
@@ -74,5 +80,14 @@ export declare function isInstance<T extends Metadata = Metadata>(arg: unknown):
|
|
74
80
|
export declare function checkInstance<T extends Metadata = Metadata>(arg: unknown): asserts arg is Instance<T> & Record<keyof any, any>;
|
75
81
|
export declare function isStruct<T extends Metadata = Metadata>(arg: unknown): arg is Instance<T> | Static<T>;
|
76
82
|
export declare function checkStruct<T extends Metadata = Metadata>(arg: unknown): asserts arg is Instance<T> | Static<T>;
|
83
|
+
export interface UserDefined {
|
84
|
+
readonly [Symbol.size]: number;
|
85
|
+
[Symbol.serialize](): Uint8Array;
|
86
|
+
[Symbol.deserialize](value: Uint8Array): void;
|
87
|
+
}
|
88
|
+
export declare function isUserDefined(arg: unknown): arg is UserDefined;
|
77
89
|
export type Like<T extends Metadata = Metadata> = InstanceLike<T> | StaticLike<T>;
|
78
|
-
export type Size<T extends
|
90
|
+
export type Size<T extends TypeLike> = T extends {
|
91
|
+
readonly [Symbol.size]: infer S;
|
92
|
+
} ? S : T extends primitive.Valid ? primitive.Size<T> : T extends Like<infer M> ? M['size'] : number;
|
93
|
+
export {};
|
package/dist/internal/struct.js
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
export const init = Symbol.struct_init;
|
7
|
-
/** @deprecated */
|
8
|
-
export const metadata = Symbol.struct_metadata;
|
1
|
+
Object.assign(Symbol, {
|
2
|
+
size: Symbol('uSize'),
|
3
|
+
serialize: Symbol('uSerialize'),
|
4
|
+
deserialize: Symbol('uDeserialize'),
|
5
|
+
});
|
9
6
|
export function isValidMetadata(arg) {
|
10
|
-
return arg != null && typeof arg == 'object' &&
|
7
|
+
return arg != null && typeof arg == 'object' && 'struct' in arg;
|
11
8
|
}
|
12
9
|
/**
|
13
10
|
* Polyfill Symbol.metadata
|
@@ -19,12 +16,8 @@ Symbol.metadata ??= Symbol.for('Symbol.metadata');
|
|
19
16
|
* @see https://github.com/microsoft/TypeScript/issues/53461
|
20
17
|
*/
|
21
18
|
export function _polyfill_contextMetadata(target) {
|
22
|
-
if (!Symbol?.metadata)
|
19
|
+
if (!Symbol?.metadata || Symbol.metadata in target)
|
23
20
|
return;
|
24
|
-
}
|
25
|
-
if (Symbol.metadata in target) {
|
26
|
-
return;
|
27
|
-
}
|
28
21
|
Object.defineProperty(target, Symbol.metadata, {
|
29
22
|
enumerable: true,
|
30
23
|
configurable: true,
|
@@ -52,17 +45,24 @@ export function isInstance(arg) {
|
|
52
45
|
return arg != null && typeof arg == 'object' && isStatic(arg.constructor);
|
53
46
|
}
|
54
47
|
export function checkInstance(arg) {
|
55
|
-
if (
|
56
|
-
|
57
|
-
|
58
|
-
|
48
|
+
if (isInstance(arg))
|
49
|
+
return;
|
50
|
+
throw new TypeError((typeof arg == 'function' ? arg.name : typeof arg == 'object' && arg ? arg.constructor.name : arg)
|
51
|
+
+ ' is not a struct instance');
|
59
52
|
}
|
60
53
|
export function isStruct(arg) {
|
61
54
|
return isInstance(arg) || isStatic(arg);
|
62
55
|
}
|
63
56
|
export function checkStruct(arg) {
|
64
|
-
if (
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
if (isStruct(arg))
|
58
|
+
return;
|
59
|
+
throw new TypeError((typeof arg == 'function' ? arg.name : typeof arg == 'object' && arg ? arg.constructor.name : arg)
|
60
|
+
+ ' is not a struct');
|
61
|
+
}
|
62
|
+
export function isUserDefined(arg) {
|
63
|
+
return (typeof arg == 'object'
|
64
|
+
&& arg != null
|
65
|
+
&& Symbol.size in arg
|
66
|
+
&& Symbol.serialize in arg
|
67
|
+
&& Symbol.deserialize in arg);
|
68
68
|
}
|
package/dist/objects.d.ts
CHANGED
@@ -41,11 +41,10 @@ export type JSONValue = JSONPrimitive | JSONObject | JSONValue[];
|
|
41
41
|
/**
|
42
42
|
* An object `T` with all of its functions bound to a `This` value
|
43
43
|
*/
|
44
|
-
type Bound<T extends object, This = any> = T & {
|
44
|
+
export type Bound<T extends object, This = any> = T & {
|
45
45
|
[k in keyof T]: T[k] extends (...args: any[]) => any ? (this: This, ...args: Parameters<T[k]>) => ReturnType<T[k]> : T[k];
|
46
46
|
};
|
47
47
|
/**
|
48
48
|
* Binds a this value for all of the functions in an object (not recursive)
|
49
49
|
*/
|
50
50
|
export declare function bindFunctions<T extends object, This = any>(fns: T, thisValue: This): Bound<T, This>;
|
51
|
-
export {};
|
package/dist/struct.d.ts
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
import * as primitive from './internal/primitives.js';
|
2
|
-
import type { DecoratorContext, InstanceLike, Options, Size, StaticLike } from './internal/struct.js';
|
3
|
-
import { type MemberContext } from './internal/struct.js';
|
2
|
+
import type { DecoratorContext, InstanceLike, Options, Size, StaticLike, TypeLike, MemberContext } from './internal/struct.js';
|
4
3
|
import type { ClassLike } from './types.js';
|
5
4
|
export * as Struct from './internal/struct.js';
|
6
5
|
/**
|
7
6
|
* Gets the size in bytes of a type
|
8
7
|
*/
|
9
|
-
export declare function sizeof<T extends
|
8
|
+
export declare function sizeof<T extends TypeLike>(type: T): Size<T>;
|
10
9
|
/**
|
11
10
|
* Returns the offset (in bytes) of a member in a struct.
|
12
11
|
*/
|
package/dist/struct.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
import { toUint8Array } from './buffer.js';
|
1
2
|
import * as primitive from './internal/primitives.js';
|
2
|
-
import { checkInstance, checkStruct, isStatic, symbol_metadata } from './internal/struct.js';
|
3
|
+
import { checkInstance, checkStruct, isStatic, isUserDefined, symbol_metadata } from './internal/struct.js';
|
3
4
|
import { capitalize } from './string.js';
|
4
5
|
export * as Struct from './internal/struct.js';
|
5
6
|
/**
|
@@ -11,9 +12,12 @@ export function sizeof(type) {
|
|
11
12
|
primitive.checkValid(type);
|
12
13
|
return (+primitive.normalize(type).match(primitive.regex)[2] / 8);
|
13
14
|
}
|
15
|
+
if (isUserDefined(type)) {
|
16
|
+
return type[Symbol.size];
|
17
|
+
}
|
14
18
|
checkStruct(type);
|
15
19
|
const struct = isStatic(type) ? type : type.constructor;
|
16
|
-
return struct[symbol_metadata(struct)]
|
20
|
+
return struct[symbol_metadata(struct)].struct.size;
|
17
21
|
}
|
18
22
|
/**
|
19
23
|
* Returns the offset (in bytes) of a member in a struct.
|
@@ -21,7 +25,7 @@ export function sizeof(type) {
|
|
21
25
|
export function offsetof(type, memberName) {
|
22
26
|
checkStruct(type);
|
23
27
|
const struct = isStatic(type) ? type : type.constructor;
|
24
|
-
const metadata = struct[symbol_metadata(struct)]
|
28
|
+
const metadata = struct[symbol_metadata(struct)].struct;
|
25
29
|
const member = metadata.members.get(memberName);
|
26
30
|
if (!member)
|
27
31
|
throw new Error('Struct does not have member: ' + memberName);
|
@@ -39,11 +43,11 @@ export function align(value, alignment) {
|
|
39
43
|
export function struct(options = {}) {
|
40
44
|
return function _decorateStruct(target, context) {
|
41
45
|
context.metadata ??= {};
|
42
|
-
context.metadata
|
46
|
+
context.metadata.struct ??= {};
|
47
|
+
context.metadata.struct.init ??= [];
|
43
48
|
let size = 0;
|
44
49
|
const members = new Map();
|
45
|
-
for (const
|
46
|
-
const { name, type, length } = _;
|
50
|
+
for (const { name, type, length } of context.metadata.struct.init) {
|
47
51
|
if (!primitive.isValid(type) && !isStatic(type)) {
|
48
52
|
throw new TypeError('Not a valid type: ' + type);
|
49
53
|
}
|
@@ -56,7 +60,7 @@ export function struct(options = {}) {
|
|
56
60
|
size = options.isUnion ? Math.max(size, memberSize) : size + memberSize;
|
57
61
|
size = align(size, options.align || 1);
|
58
62
|
}
|
59
|
-
context.metadata
|
63
|
+
context.metadata.struct = { options, members, size };
|
60
64
|
return target;
|
61
65
|
};
|
62
66
|
}
|
@@ -70,12 +74,12 @@ export function member(type, length) {
|
|
70
74
|
console.warn('Symbol used for struct member name will be coerced to string: ' + name.toString());
|
71
75
|
name = name.toString();
|
72
76
|
}
|
73
|
-
if (!name)
|
77
|
+
if (!name)
|
74
78
|
throw new ReferenceError('Invalid name for struct member');
|
75
|
-
}
|
76
79
|
context.metadata ??= {};
|
77
|
-
context.metadata
|
78
|
-
context.metadata
|
80
|
+
context.metadata.struct ??= {};
|
81
|
+
context.metadata.struct.init ??= [];
|
82
|
+
context.metadata.struct.init.push({ name, type, length });
|
79
83
|
return value;
|
80
84
|
};
|
81
85
|
}
|
@@ -83,8 +87,10 @@ export function member(type, length) {
|
|
83
87
|
* Serializes a struct into a Uint8Array
|
84
88
|
*/
|
85
89
|
export function serialize(instance) {
|
90
|
+
if (isUserDefined(instance))
|
91
|
+
return instance[Symbol.serialize]();
|
86
92
|
checkInstance(instance);
|
87
|
-
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)]
|
93
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)].struct;
|
88
94
|
const buffer = new Uint8Array(sizeof(instance));
|
89
95
|
const view = new DataView(buffer.buffer);
|
90
96
|
// for unions we should write members in ascending last modified order, but we don't have that info.
|
@@ -132,9 +138,11 @@ export function serialize(instance) {
|
|
132
138
|
* Deserializes a struct from a Uint8Array
|
133
139
|
*/
|
134
140
|
export function deserialize(instance, _buffer) {
|
141
|
+
const buffer = toUint8Array(_buffer);
|
142
|
+
if (isUserDefined(instance))
|
143
|
+
return instance[Symbol.deserialize](buffer);
|
135
144
|
checkInstance(instance);
|
136
|
-
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)]
|
137
|
-
const buffer = _buffer instanceof Uint8Array ? _buffer : new Uint8Array('buffer' in _buffer ? _buffer.buffer : _buffer);
|
145
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)].struct;
|
138
146
|
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
139
147
|
for (const [name, { type, offset, length }] of members) {
|
140
148
|
for (let i = 0; i < (length || 1); i++) {
|
@@ -149,7 +157,7 @@ export function deserialize(instance, _buffer) {
|
|
149
157
|
if (object[key] === null || object[key] === undefined) {
|
150
158
|
continue;
|
151
159
|
}
|
152
|
-
deserialize(object[key], new Uint8Array(buffer.
|
160
|
+
deserialize(object[key], new Uint8Array(buffer.subarray(iOff, iOff + sizeof(type))));
|
153
161
|
continue;
|
154
162
|
}
|
155
163
|
if (length > 0) {
|
@@ -198,4 +206,4 @@ function _member(type) {
|
|
198
206
|
*
|
199
207
|
* Instead of writing `@member(type)` you can write `@types.type`, or `@types.type(length)` for arrays
|
200
208
|
*/
|
201
|
-
export const types = Object.fromEntries(primitive.
|
209
|
+
export const types = Object.fromEntries(primitive.validNames.map(t => [t, _member(t)]));
|
package/package.json
CHANGED
package/src/buffer.ts
CHANGED
@@ -49,3 +49,9 @@ export function extendBuffer<T extends ArrayBufferLike | ArrayBufferView>(buffer
|
|
49
49
|
return newBuffer;
|
50
50
|
}
|
51
51
|
}
|
52
|
+
|
53
|
+
export function toUint8Array(buffer: ArrayBufferLike | ArrayBufferView): Uint8Array {
|
54
|
+
if (buffer instanceof Uint8Array) return buffer;
|
55
|
+
if (!ArrayBuffer.isView(buffer)) return new Uint8Array(buffer);
|
56
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
57
|
+
}
|
@@ -14,7 +14,7 @@ export type Size<T extends string> = T extends `${'int' | 'uint' | 'float'}${inf
|
|
14
14
|
: never
|
15
15
|
: never;
|
16
16
|
|
17
|
-
export const
|
17
|
+
export const typeNames = [
|
18
18
|
'int8',
|
19
19
|
'uint8',
|
20
20
|
'int16',
|
@@ -30,11 +30,11 @@ export const types = [
|
|
30
30
|
'float128',
|
31
31
|
] as const;
|
32
32
|
|
33
|
-
export type
|
33
|
+
export type Typename = (typeof typeNames)[number];
|
34
34
|
|
35
|
-
export type Valid =
|
35
|
+
export type Valid = Typename | Capitalize<Typename> | 'char';
|
36
36
|
|
37
|
-
export const
|
37
|
+
export const validNames = [...typeNames, ...typeNames.map(t => capitalize(t)), 'char'] satisfies Valid[];
|
38
38
|
|
39
39
|
export const regex = /^(u?int|float)(8|16|32|64|128)$/i;
|
40
40
|
|
@@ -44,7 +44,7 @@ export function normalize<T extends Valid>(type: T): Normalize<T> {
|
|
44
44
|
return (type == 'char' ? 'uint8' : type.toLowerCase()) as Normalize<T>;
|
45
45
|
}
|
46
46
|
|
47
|
-
export function isType(type: { toString(): string }): type is
|
47
|
+
export function isType(type: { toString(): string }): type is Typename {
|
48
48
|
return regex.test(type.toString());
|
49
49
|
}
|
50
50
|
|
package/src/internal/struct.ts
CHANGED
@@ -3,26 +3,37 @@ import type * as primitive from './primitives.js';
|
|
3
3
|
|
4
4
|
declare global {
|
5
5
|
interface SymbolConstructor {
|
6
|
-
|
7
|
-
readonly
|
6
|
+
/** User-defined size */
|
7
|
+
readonly size: unique symbol;
|
8
|
+
|
9
|
+
/** User-defined serialization */
|
10
|
+
readonly serialize: unique symbol;
|
11
|
+
|
12
|
+
/** User-defined deserialization */
|
13
|
+
readonly deserialize: unique symbol;
|
8
14
|
}
|
9
15
|
}
|
10
16
|
|
11
|
-
|
12
|
-
|
17
|
+
Object.assign(Symbol, {
|
18
|
+
size: Symbol('uSize'),
|
19
|
+
serialize: Symbol('uSerialize'),
|
20
|
+
deserialize: Symbol('uDeserialize'),
|
21
|
+
});
|
22
|
+
|
23
|
+
export type TypeLike = UserDefined | Like | primitive.Valid;
|
13
24
|
|
14
|
-
|
15
|
-
Symbol.struct_metadata ||= Symbol('struct_metadata');
|
25
|
+
export type Type = UserDefined | Static | primitive.Typename;
|
16
26
|
|
27
|
+
/**
|
28
|
+
* Member initialization data
|
29
|
+
* This is needed since class decorators are called *after* member decorators
|
30
|
+
*/
|
17
31
|
export interface MemberInit {
|
18
32
|
name: string;
|
19
33
|
type: string | ClassLike;
|
20
34
|
length?: number;
|
21
35
|
}
|
22
36
|
|
23
|
-
/** @deprecated */
|
24
|
-
export const init: typeof Symbol.struct_init = Symbol.struct_init;
|
25
|
-
|
26
37
|
/**
|
27
38
|
* Options for struct initialization
|
28
39
|
*/
|
@@ -33,7 +44,7 @@ export interface Options {
|
|
33
44
|
}
|
34
45
|
|
35
46
|
export interface Member {
|
36
|
-
type:
|
47
|
+
type: Type;
|
37
48
|
offset: number;
|
38
49
|
length?: number;
|
39
50
|
}
|
@@ -42,15 +53,12 @@ export interface Metadata {
|
|
42
53
|
options: Partial<Options>;
|
43
54
|
members: Map<string, Member>;
|
44
55
|
size: number;
|
56
|
+
init?: MemberInit[];
|
45
57
|
}
|
46
58
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
export interface _DecoratorMetadata<T extends Metadata = Metadata> extends DecoratorMetadata {
|
51
|
-
[Symbol.struct_metadata]?: T;
|
52
|
-
[Symbol.struct_init]?: MemberInit[];
|
53
|
-
}
|
59
|
+
type _DecoratorMetadata<T extends Metadata = Metadata> = DecoratorMetadata & {
|
60
|
+
struct?: Partial<T>;
|
61
|
+
};
|
54
62
|
|
55
63
|
export interface DecoratorContext<T extends Metadata = Metadata> {
|
56
64
|
metadata: _DecoratorMetadata<T>;
|
@@ -59,9 +67,7 @@ export interface DecoratorContext<T extends Metadata = Metadata> {
|
|
59
67
|
export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
|
60
68
|
|
61
69
|
export interface Static<T extends Metadata = Metadata> {
|
62
|
-
[Symbol.metadata]:
|
63
|
-
[Symbol.struct_metadata]: T;
|
64
|
-
};
|
70
|
+
[Symbol.metadata]: { struct: T };
|
65
71
|
new (): Instance<T>;
|
66
72
|
prototype: Instance<T>;
|
67
73
|
}
|
@@ -73,9 +79,9 @@ export interface StaticLike<T extends Metadata = Metadata> extends ClassLike {
|
|
73
79
|
export function isValidMetadata<T extends Metadata = Metadata>(
|
74
80
|
arg: unknown
|
75
81
|
): arg is DecoratorMetadata & {
|
76
|
-
|
82
|
+
struct: T;
|
77
83
|
} {
|
78
|
-
return arg != null && typeof arg == 'object' &&
|
84
|
+
return arg != null && typeof arg == 'object' && 'struct' in arg;
|
79
85
|
}
|
80
86
|
|
81
87
|
/**
|
@@ -89,12 +95,8 @@ export function isValidMetadata<T extends Metadata = Metadata>(
|
|
89
95
|
* @see https://github.com/microsoft/TypeScript/issues/53461
|
90
96
|
*/
|
91
97
|
export function _polyfill_contextMetadata(target: object): void {
|
92
|
-
if (!Symbol?.metadata)
|
93
|
-
|
94
|
-
}
|
95
|
-
if (Symbol.metadata in target) {
|
96
|
-
return;
|
97
|
-
}
|
98
|
+
if (!Symbol?.metadata || Symbol.metadata in target) return;
|
99
|
+
|
98
100
|
Object.defineProperty(target, Symbol.metadata, {
|
99
101
|
enumerable: true,
|
100
102
|
configurable: true,
|
@@ -140,12 +142,11 @@ export function isInstance<T extends Metadata = Metadata>(arg: unknown): arg is
|
|
140
142
|
export function checkInstance<T extends Metadata = Metadata>(
|
141
143
|
arg: unknown
|
142
144
|
): asserts arg is Instance<T> & Record<keyof any, any> {
|
143
|
-
if (
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
}
|
145
|
+
if (isInstance(arg)) return;
|
146
|
+
throw new TypeError(
|
147
|
+
(typeof arg == 'function' ? arg.name : typeof arg == 'object' && arg ? arg.constructor.name : arg)
|
148
|
+
+ ' is not a struct instance'
|
149
|
+
);
|
149
150
|
}
|
150
151
|
|
151
152
|
export function isStruct<T extends Metadata = Metadata>(arg: unknown): arg is Instance<T> | Static<T> {
|
@@ -153,18 +154,35 @@ export function isStruct<T extends Metadata = Metadata>(arg: unknown): arg is In
|
|
153
154
|
}
|
154
155
|
|
155
156
|
export function checkStruct<T extends Metadata = Metadata>(arg: unknown): asserts arg is Instance<T> | Static<T> {
|
156
|
-
if (
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
157
|
+
if (isStruct(arg)) return;
|
158
|
+
throw new TypeError(
|
159
|
+
(typeof arg == 'function' ? arg.name : typeof arg == 'object' && arg ? arg.constructor.name : arg)
|
160
|
+
+ ' is not a struct'
|
161
|
+
);
|
162
|
+
}
|
163
|
+
|
164
|
+
export interface UserDefined {
|
165
|
+
readonly [Symbol.size]: number;
|
166
|
+
[Symbol.serialize](): Uint8Array;
|
167
|
+
[Symbol.deserialize](value: Uint8Array): void;
|
168
|
+
}
|
169
|
+
|
170
|
+
export function isUserDefined(arg: unknown): arg is UserDefined {
|
171
|
+
return (
|
172
|
+
typeof arg == 'object'
|
173
|
+
&& arg != null
|
174
|
+
&& Symbol.size in arg
|
175
|
+
&& Symbol.serialize in arg
|
176
|
+
&& Symbol.deserialize in arg
|
177
|
+
);
|
162
178
|
}
|
163
179
|
|
164
180
|
export type Like<T extends Metadata = Metadata> = InstanceLike<T> | StaticLike<T>;
|
165
181
|
|
166
|
-
export type Size<T extends
|
167
|
-
?
|
168
|
-
: T extends
|
169
|
-
?
|
170
|
-
:
|
182
|
+
export type Size<T extends TypeLike> = T extends { readonly [Symbol.size]: infer S }
|
183
|
+
? S
|
184
|
+
: T extends primitive.Valid
|
185
|
+
? primitive.Size<T>
|
186
|
+
: T extends Like<infer M>
|
187
|
+
? M['size']
|
188
|
+
: number;
|
package/src/objects.ts
CHANGED
@@ -122,7 +122,7 @@ export type JSONValue = JSONPrimitive | JSONObject | JSONValue[];
|
|
122
122
|
/**
|
123
123
|
* An object `T` with all of its functions bound to a `This` value
|
124
124
|
*/
|
125
|
-
type Bound<T extends object, This = any> = T & {
|
125
|
+
export type Bound<T extends object, This = any> = T & {
|
126
126
|
[k in keyof T]: T[k] extends (...args: any[]) => any
|
127
127
|
? (this: This, ...args: Parameters<T[k]>) => ReturnType<T[k]>
|
128
128
|
: T[k];
|
package/src/struct.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { toUint8Array } from './buffer.js';
|
1
2
|
import * as primitive from './internal/primitives.js';
|
2
3
|
import type {
|
3
4
|
DecoratorContext,
|
@@ -8,8 +9,10 @@ import type {
|
|
8
9
|
Options,
|
9
10
|
Size,
|
10
11
|
StaticLike,
|
12
|
+
TypeLike,
|
13
|
+
MemberContext,
|
11
14
|
} from './internal/struct.js';
|
12
|
-
import { checkInstance, checkStruct, isStatic,
|
15
|
+
import { checkInstance, checkStruct, isStatic, isUserDefined, symbol_metadata } from './internal/struct.js';
|
13
16
|
import { capitalize } from './string.js';
|
14
17
|
import type { ClassLike } from './types.js';
|
15
18
|
export * as Struct from './internal/struct.js';
|
@@ -17,7 +20,7 @@ export * as Struct from './internal/struct.js';
|
|
17
20
|
/**
|
18
21
|
* Gets the size in bytes of a type
|
19
22
|
*/
|
20
|
-
export function sizeof<T extends
|
23
|
+
export function sizeof<T extends TypeLike>(type: T): Size<T> {
|
21
24
|
// primitive
|
22
25
|
if (typeof type == 'string') {
|
23
26
|
primitive.checkValid(type);
|
@@ -25,11 +28,15 @@ export function sizeof<T extends primitive.Valid | StaticLike | InstanceLike>(ty
|
|
25
28
|
return (+primitive.normalize(type).match(primitive.regex)![2] / 8) as Size<T>;
|
26
29
|
}
|
27
30
|
|
31
|
+
if (isUserDefined(type)) {
|
32
|
+
return type[Symbol.size] as Size<T>;
|
33
|
+
}
|
34
|
+
|
28
35
|
checkStruct(type);
|
29
36
|
|
30
37
|
const struct = isStatic(type) ? type : type.constructor;
|
31
38
|
|
32
|
-
return struct[symbol_metadata(struct)]
|
39
|
+
return struct[symbol_metadata(struct)].struct.size as Size<T>;
|
33
40
|
}
|
34
41
|
|
35
42
|
/**
|
@@ -39,7 +46,7 @@ export function offsetof(type: StaticLike | InstanceLike, memberName: string): n
|
|
39
46
|
checkStruct(type);
|
40
47
|
|
41
48
|
const struct = isStatic(type) ? type : type.constructor;
|
42
|
-
const metadata = struct[symbol_metadata(struct)]
|
49
|
+
const metadata = struct[symbol_metadata(struct)].struct;
|
43
50
|
|
44
51
|
const member = metadata.members.get(memberName);
|
45
52
|
if (!member) throw new Error('Struct does not have member: ' + memberName);
|
@@ -62,11 +69,12 @@ export function struct(options: Partial<Options> = {}) {
|
|
62
69
|
context: ClassDecoratorContext & DecoratorContext
|
63
70
|
): T {
|
64
71
|
context.metadata ??= {};
|
65
|
-
context.metadata
|
72
|
+
context.metadata.struct ??= {};
|
73
|
+
context.metadata.struct.init ??= [];
|
74
|
+
|
66
75
|
let size = 0;
|
67
76
|
const members = new Map<string, Member>();
|
68
|
-
for (const
|
69
|
-
const { name, type, length } = _;
|
77
|
+
for (const { name, type, length } of context.metadata.struct.init) {
|
70
78
|
if (!primitive.isValid(type) && !isStatic(type)) {
|
71
79
|
throw new TypeError('Not a valid type: ' + type);
|
72
80
|
}
|
@@ -80,7 +88,7 @@ export function struct(options: Partial<Options> = {}) {
|
|
80
88
|
size = align(size, options.align || 1);
|
81
89
|
}
|
82
90
|
|
83
|
-
context.metadata
|
91
|
+
context.metadata.struct = { options, members, size } satisfies Metadata;
|
84
92
|
return target;
|
85
93
|
};
|
86
94
|
}
|
@@ -96,13 +104,12 @@ export function member(type: primitive.Valid | ClassLike, length?: number) {
|
|
96
104
|
name = name.toString();
|
97
105
|
}
|
98
106
|
|
99
|
-
if (!name)
|
100
|
-
throw new ReferenceError('Invalid name for struct member');
|
101
|
-
}
|
107
|
+
if (!name) throw new ReferenceError('Invalid name for struct member');
|
102
108
|
|
103
109
|
context.metadata ??= {};
|
104
|
-
context.metadata
|
105
|
-
context.metadata
|
110
|
+
context.metadata.struct ??= {};
|
111
|
+
context.metadata.struct.init ??= [];
|
112
|
+
context.metadata.struct.init.push({ name, type, length } satisfies MemberInit);
|
106
113
|
return value;
|
107
114
|
};
|
108
115
|
}
|
@@ -111,8 +118,10 @@ export function member(type: primitive.Valid | ClassLike, length?: number) {
|
|
111
118
|
* Serializes a struct into a Uint8Array
|
112
119
|
*/
|
113
120
|
export function serialize(instance: unknown): Uint8Array {
|
121
|
+
if (isUserDefined(instance)) return instance[Symbol.serialize]();
|
122
|
+
|
114
123
|
checkInstance(instance);
|
115
|
-
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)]
|
124
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)].struct;
|
116
125
|
|
117
126
|
const buffer = new Uint8Array(sizeof(instance));
|
118
127
|
const view = new DataView(buffer.buffer);
|
@@ -173,11 +182,12 @@ export function serialize(instance: unknown): Uint8Array {
|
|
173
182
|
* Deserializes a struct from a Uint8Array
|
174
183
|
*/
|
175
184
|
export function deserialize(instance: unknown, _buffer: ArrayBufferLike | ArrayBufferView) {
|
176
|
-
|
177
|
-
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)][Symbol.struct_metadata];
|
185
|
+
const buffer = toUint8Array(_buffer);
|
178
186
|
|
179
|
-
|
180
|
-
|
187
|
+
if (isUserDefined(instance)) return instance[Symbol.deserialize](buffer);
|
188
|
+
|
189
|
+
checkInstance(instance);
|
190
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)].struct;
|
181
191
|
|
182
192
|
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
183
193
|
|
@@ -197,7 +207,7 @@ export function deserialize(instance: unknown, _buffer: ArrayBufferLike | ArrayB
|
|
197
207
|
if (object[key] === null || object[key] === undefined) {
|
198
208
|
continue;
|
199
209
|
}
|
200
|
-
deserialize(object[key], new Uint8Array(buffer.
|
210
|
+
deserialize(object[key], new Uint8Array(buffer.subarray(iOff, iOff + sizeof(type))));
|
201
211
|
continue;
|
202
212
|
}
|
203
213
|
|
@@ -261,6 +271,6 @@ function _member<T extends primitive.Valid>(type: T) {
|
|
261
271
|
*
|
262
272
|
* Instead of writing `@member(type)` you can write `@types.type`, or `@types.type(length)` for arrays
|
263
273
|
*/
|
264
|
-
export const types = Object.fromEntries(primitive.
|
274
|
+
export const types = Object.fromEntries(primitive.validNames.map(t => [t, _member(t)])) as {
|
265
275
|
[K in primitive.Valid]: ReturnType<typeof _member<K>>;
|
266
276
|
};
|