utilium 0.5.9 → 0.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/internal/struct.d.ts +21 -7
- package/dist/internal/struct.js +15 -2
- package/dist/list.d.ts +8 -0
- package/dist/list.js +27 -0
- package/dist/struct.d.ts +8 -12
- package/dist/struct.js +27 -31
- package/package.json +1 -1
- package/src/internal/struct.ts +40 -12
- package/src/list.ts +35 -0
- package/src/struct.ts +47 -44
- package/tsconfig.json +1 -2
@@ -6,7 +6,6 @@ export interface MemberInit {
|
|
6
6
|
length?: number;
|
7
7
|
}
|
8
8
|
export declare const init: unique symbol;
|
9
|
-
export type init = typeof init;
|
10
9
|
/**
|
11
10
|
* Options for struct initialization
|
12
11
|
*/
|
@@ -25,16 +24,31 @@ export interface Metadata {
|
|
25
24
|
size: number;
|
26
25
|
}
|
27
26
|
export declare const metadata: unique symbol;
|
28
|
-
export
|
27
|
+
export interface _DecoratorMetadata<T extends Metadata = Metadata> extends DecoratorMetadata {
|
28
|
+
[metadata]?: T;
|
29
|
+
[init]?: MemberInit[];
|
30
|
+
}
|
31
|
+
export interface DecoratorContext<T extends Metadata = Metadata> {
|
32
|
+
metadata: _DecoratorMetadata<T>;
|
33
|
+
}
|
34
|
+
export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
|
29
35
|
export interface Static<T extends Metadata = Metadata> {
|
30
|
-
[metadata]:
|
31
|
-
|
32
|
-
|
36
|
+
[Symbol.metadata]: DecoratorMetadata & {
|
37
|
+
[metadata]: T;
|
38
|
+
};
|
39
|
+
new (): Instance<T>;
|
40
|
+
prototype: Instance<T>;
|
33
41
|
}
|
34
42
|
export interface StaticLike<T extends Metadata = Metadata> extends ClassLike {
|
35
|
-
[metadata]?: T;
|
36
|
-
[init]?: MemberInit[];
|
43
|
+
[Symbol.metadata]?: _DecoratorMetadata<T> | null;
|
37
44
|
}
|
45
|
+
export declare function isValidMetadata<T extends Metadata = Metadata>(arg: unknown): arg is DecoratorMetadata & {
|
46
|
+
[metadata]: T;
|
47
|
+
};
|
48
|
+
/**
|
49
|
+
* Gets a reference to Symbol.metadata, even on platforms that do not expose it globally (like Node)
|
50
|
+
*/
|
51
|
+
export declare function symbol_metadata(arg: ClassLike): typeof Symbol.metadata;
|
38
52
|
export declare function isStatic<T extends Metadata = Metadata>(arg: unknown): arg is Static<T>;
|
39
53
|
export interface Instance<T extends Metadata = Metadata> {
|
40
54
|
constructor: Static<T>;
|
package/dist/internal/struct.js
CHANGED
@@ -1,10 +1,23 @@
|
|
1
1
|
export const init = Symbol('struct_init');
|
2
2
|
export const metadata = Symbol('struct');
|
3
|
+
export function isValidMetadata(arg) {
|
4
|
+
return arg != null && typeof arg == 'object' && metadata in arg;
|
5
|
+
}
|
6
|
+
/**
|
7
|
+
* Gets a reference to Symbol.metadata, even on platforms that do not expose it globally (like Node)
|
8
|
+
*/
|
9
|
+
export function symbol_metadata(arg) {
|
10
|
+
const symbol_metadata = Symbol.metadata || Object.getOwnPropertySymbols(arg).find(s => s.description == 'Symbol.metadata');
|
11
|
+
if (!symbol_metadata) {
|
12
|
+
throw new ReferenceError('Could not get a reference to Symbol.metadata');
|
13
|
+
}
|
14
|
+
return symbol_metadata;
|
15
|
+
}
|
3
16
|
export function isStatic(arg) {
|
4
|
-
return typeof arg == 'function' &&
|
17
|
+
return typeof arg == 'function' && symbol_metadata(arg) in arg && isValidMetadata(arg[symbol_metadata(arg)]);
|
5
18
|
}
|
6
19
|
export function isInstance(arg) {
|
7
|
-
return
|
20
|
+
return arg != null && typeof arg == 'object' && isStatic(arg.constructor);
|
8
21
|
}
|
9
22
|
export function isStruct(arg) {
|
10
23
|
return isInstance(arg) || isStatic(arg);
|
package/dist/list.d.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { EventEmitter } from 'eventemitter3';
|
2
2
|
export declare class List<T> extends EventEmitter<'update'> implements Set<T>, RelativeIndexable<T> {
|
3
3
|
readonly [Symbol.toStringTag] = "List";
|
4
|
+
constructor(values?: readonly T[] | Iterable<T> | null);
|
4
5
|
protected data: Set<T>;
|
5
6
|
array(): T[];
|
6
7
|
json(): string;
|
@@ -15,6 +16,13 @@ export declare class List<T> extends EventEmitter<'update'> implements Set<T>, R
|
|
15
16
|
add(value: T): this;
|
16
17
|
clear(): void;
|
17
18
|
delete(value: T): boolean;
|
19
|
+
union<U>(other: ReadonlySetLike<U>): List<T | U>;
|
20
|
+
intersection<U>(other: ReadonlySetLike<U>): List<T & U>;
|
21
|
+
difference<U>(other: ReadonlySetLike<U>): List<T>;
|
22
|
+
symmetricDifference<U>(other: ReadonlySetLike<U>): List<T | U>;
|
23
|
+
isSubsetOf(other: ReadonlySetLike<unknown>): boolean;
|
24
|
+
isSupersetOf(other: ReadonlySetLike<unknown>): boolean;
|
25
|
+
isDisjointFrom(other: ReadonlySetLike<unknown>): boolean;
|
18
26
|
forEach(callbackfn: (value: T, value2: T, list: List<T>) => void, thisArg?: any): void;
|
19
27
|
has(value: T): boolean;
|
20
28
|
get size(): number;
|
package/dist/list.js
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
import { EventEmitter } from 'eventemitter3';
|
2
2
|
export class List extends EventEmitter {
|
3
3
|
[Symbol.toStringTag] = 'List';
|
4
|
+
constructor(values) {
|
5
|
+
super();
|
6
|
+
if (values) {
|
7
|
+
this.push(...values);
|
8
|
+
}
|
9
|
+
}
|
4
10
|
data = new Set();
|
5
11
|
array() {
|
6
12
|
return [...this.data];
|
@@ -74,6 +80,27 @@ export class List extends EventEmitter {
|
|
74
80
|
this.emit('update');
|
75
81
|
return success;
|
76
82
|
}
|
83
|
+
union(other) {
|
84
|
+
return new List(this.data.union(other));
|
85
|
+
}
|
86
|
+
intersection(other) {
|
87
|
+
return new List(this.data.intersection(other));
|
88
|
+
}
|
89
|
+
difference(other) {
|
90
|
+
return new List(this.data.difference(other));
|
91
|
+
}
|
92
|
+
symmetricDifference(other) {
|
93
|
+
return new List(this.data.symmetricDifference(other));
|
94
|
+
}
|
95
|
+
isSubsetOf(other) {
|
96
|
+
return this.data.isSubsetOf(other);
|
97
|
+
}
|
98
|
+
isSupersetOf(other) {
|
99
|
+
return this.data.isSupersetOf(other);
|
100
|
+
}
|
101
|
+
isDisjointFrom(other) {
|
102
|
+
return this.data.isDisjointFrom(other);
|
103
|
+
}
|
77
104
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
78
105
|
forEach(callbackfn, thisArg) {
|
79
106
|
this.data.forEach((v1, v2) => callbackfn.call(thisArg, v1, v2, this));
|
package/dist/struct.d.ts
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
import * as Struct from './internal/struct.js';
|
2
|
-
import { ClassLike } from './types.js';
|
3
1
|
import * as primitive from './internal/primitives.js';
|
4
|
-
|
2
|
+
import { DecoratorContext, InstanceLike, Options, Size, StaticLike, type MemberContext } from './internal/struct.js';
|
3
|
+
import { ClassLike } from './types.js';
|
4
|
+
export * as Struct from './internal/struct.js';
|
5
5
|
/**
|
6
6
|
* Gets the size in bytes of a type
|
7
7
|
*/
|
8
|
-
export declare function sizeof<T extends primitive.Valid |
|
8
|
+
export declare function sizeof<T extends primitive.Valid | StaticLike | InstanceLike>(type: T): Size<T>;
|
9
9
|
/**
|
10
10
|
* Aligns a number
|
11
11
|
*/
|
@@ -13,11 +13,11 @@ export declare function align(value: number, alignment: number): number;
|
|
13
13
|
/**
|
14
14
|
* Decorates a class as a struct
|
15
15
|
*/
|
16
|
-
export declare function struct(options?: Partial<
|
16
|
+
export declare function struct(options?: Partial<Options>): <const T extends StaticLike>(target: T, context: ClassDecoratorContext & DecoratorContext) => T;
|
17
17
|
/**
|
18
18
|
* Decorates a class member to be serialized
|
19
19
|
*/
|
20
|
-
export declare function member(type: primitive.Valid | ClassLike, length?: number): (
|
20
|
+
export declare function member(type: primitive.Valid | ClassLike, length?: number): <V>(value: V, context: MemberContext) => V;
|
21
21
|
/**
|
22
22
|
* Serializes a struct into a Uint8Array
|
23
23
|
*/
|
@@ -26,13 +26,9 @@ export declare function serialize(instance: unknown): Uint8Array;
|
|
26
26
|
* Deserializes a struct from a Uint8Array
|
27
27
|
*/
|
28
28
|
export declare function deserialize(instance: unknown, _buffer: ArrayBuffer | ArrayBufferView): void;
|
29
|
-
/**
|
30
|
-
* Also can be a name when legacy decorators are used
|
31
|
-
*/
|
32
|
-
type Context = string | symbol | ClassMemberDecoratorContext;
|
33
29
|
declare function _member<T extends primitive.Valid>(type: T): {
|
34
|
-
(length: number): (
|
35
|
-
(
|
30
|
+
<const V>(length: number): (value: V, context: MemberContext) => V;
|
31
|
+
<const V>(value: V, context: MemberContext): V;
|
36
32
|
};
|
37
33
|
/**
|
38
34
|
* Shortcut types
|
package/dist/struct.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
import * as Struct from './internal/struct.js';
|
2
|
-
import { capitalize } from './string.js';
|
3
1
|
import * as primitive from './internal/primitives.js';
|
4
|
-
|
2
|
+
import { symbol_metadata, init, isInstance, isStatic, isStruct, metadata, } from './internal/struct.js';
|
3
|
+
import { capitalize } from './string.js';
|
4
|
+
export * as Struct from './internal/struct.js';
|
5
5
|
/**
|
6
6
|
* Gets the size in bytes of a type
|
7
7
|
*/
|
@@ -13,11 +13,11 @@ export function sizeof(type) {
|
|
13
13
|
}
|
14
14
|
return (+primitive.normalize(type).match(primitive.regex)[2] / 8);
|
15
15
|
}
|
16
|
-
if (!
|
16
|
+
if (!isStruct(type)) {
|
17
17
|
throw new TypeError('Not a struct');
|
18
18
|
}
|
19
|
-
const
|
20
|
-
return
|
19
|
+
const struct = isStatic(type) ? type : type.constructor;
|
20
|
+
return struct[symbol_metadata(struct)][metadata].size;
|
21
21
|
}
|
22
22
|
/**
|
23
23
|
* Aligns a number
|
@@ -30,12 +30,13 @@ export function align(value, alignment) {
|
|
30
30
|
*/
|
31
31
|
export function struct(options = {}) {
|
32
32
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
33
|
-
return function (target,
|
34
|
-
|
33
|
+
return function __decorateStruct(target, context) {
|
34
|
+
context.metadata[init] ||= [];
|
35
35
|
let size = 0;
|
36
36
|
const members = new Map();
|
37
|
-
for (const
|
38
|
-
|
37
|
+
for (const _ of context.metadata[init]) {
|
38
|
+
const { name, type, length } = _;
|
39
|
+
if (!primitive.isValid(type) && !isStatic(type)) {
|
39
40
|
throw new TypeError('Not a valid type: ' + type);
|
40
41
|
}
|
41
42
|
members.set(name, {
|
@@ -46,15 +47,16 @@ export function struct(options = {}) {
|
|
46
47
|
size += sizeof(type) * (length || 1);
|
47
48
|
size = align(size, options.align || 1);
|
48
49
|
}
|
49
|
-
|
50
|
+
context.metadata[metadata] = { options, members, size };
|
51
|
+
return target;
|
50
52
|
};
|
51
53
|
}
|
52
54
|
/**
|
53
55
|
* Decorates a class member to be serialized
|
54
56
|
*/
|
55
57
|
export function member(type, length) {
|
56
|
-
return function (
|
57
|
-
let name =
|
58
|
+
return function (value, context) {
|
59
|
+
let name = context.name;
|
58
60
|
if (typeof name == 'symbol') {
|
59
61
|
console.warn('Symbol used for struct member name will be coerced to string: ' + name.toString());
|
60
62
|
name = name.toString();
|
@@ -62,25 +64,19 @@ export function member(type, length) {
|
|
62
64
|
if (!name) {
|
63
65
|
throw new ReferenceError('Invalid name for struct member');
|
64
66
|
}
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
if (!('constructor' in target)) {
|
69
|
-
throw new TypeError('Invalid member for struct field');
|
70
|
-
}
|
71
|
-
const struct = target.constructor;
|
72
|
-
struct[Struct.init] ||= [];
|
73
|
-
struct[Struct.init].push({ name, type, length });
|
67
|
+
context.metadata[init] ||= [];
|
68
|
+
context.metadata[init].push({ name, type, length });
|
69
|
+
return value;
|
74
70
|
};
|
75
71
|
}
|
76
72
|
/**
|
77
73
|
* Serializes a struct into a Uint8Array
|
78
74
|
*/
|
79
75
|
export function serialize(instance) {
|
80
|
-
if (!
|
76
|
+
if (!isInstance(instance)) {
|
81
77
|
throw new TypeError('Can not serialize, not a struct instance');
|
82
78
|
}
|
83
|
-
const { options, members } = instance.constructor[
|
79
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)][metadata];
|
84
80
|
const buffer = new Uint8Array(sizeof(instance));
|
85
81
|
const view = new DataView(buffer.buffer);
|
86
82
|
for (const [name, { type, length, offset }] of members) {
|
@@ -114,10 +110,10 @@ export function serialize(instance) {
|
|
114
110
|
* Deserializes a struct from a Uint8Array
|
115
111
|
*/
|
116
112
|
export function deserialize(instance, _buffer) {
|
117
|
-
if (!
|
113
|
+
if (!isInstance(instance)) {
|
118
114
|
throw new TypeError('Can not deserialize, not a struct instance');
|
119
115
|
}
|
120
|
-
const { options, members } = instance.constructor[
|
116
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)][metadata];
|
121
117
|
const buffer = new Uint8Array('buffer' in _buffer ? _buffer.buffer : _buffer);
|
122
118
|
const view = new DataView(buffer.buffer);
|
123
119
|
for (const [name, { type, offset, length }] of members) {
|
@@ -156,13 +152,13 @@ export function deserialize(instance, _buffer) {
|
|
156
152
|
}
|
157
153
|
}
|
158
154
|
function _member(type) {
|
159
|
-
function
|
160
|
-
if (typeof
|
161
|
-
return member(type,
|
155
|
+
function _structMemberDecorator(valueOrLength, context) {
|
156
|
+
if (typeof valueOrLength == 'number') {
|
157
|
+
return member(type, valueOrLength);
|
162
158
|
}
|
163
|
-
return member(type)(
|
159
|
+
return member(type)(valueOrLength, context);
|
164
160
|
}
|
165
|
-
return
|
161
|
+
return _structMemberDecorator;
|
166
162
|
}
|
167
163
|
/**
|
168
164
|
* Shortcut types
|
package/package.json
CHANGED
package/src/internal/struct.ts
CHANGED
@@ -7,9 +7,7 @@ export interface MemberInit {
|
|
7
7
|
length?: number;
|
8
8
|
}
|
9
9
|
|
10
|
-
export const init = Symbol('struct_init');
|
11
|
-
|
12
|
-
export type init = typeof init;
|
10
|
+
export const init: unique symbol = Symbol('struct_init');
|
13
11
|
|
14
12
|
/**
|
15
13
|
* Options for struct initialization
|
@@ -31,23 +29,53 @@ export interface Metadata {
|
|
31
29
|
size: number;
|
32
30
|
}
|
33
31
|
|
34
|
-
export const metadata = Symbol('struct');
|
32
|
+
export const metadata: unique symbol = Symbol('struct');
|
35
33
|
|
36
|
-
export
|
34
|
+
export interface _DecoratorMetadata<T extends Metadata = Metadata> extends DecoratorMetadata {
|
35
|
+
[metadata]?: T;
|
36
|
+
[init]?: MemberInit[];
|
37
|
+
}
|
38
|
+
|
39
|
+
export interface DecoratorContext<T extends Metadata = Metadata> {
|
40
|
+
metadata: _DecoratorMetadata<T>;
|
41
|
+
}
|
42
|
+
|
43
|
+
export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
|
37
44
|
|
38
45
|
export interface Static<T extends Metadata = Metadata> {
|
39
|
-
[metadata]:
|
40
|
-
|
41
|
-
|
46
|
+
[Symbol.metadata]: DecoratorMetadata & {
|
47
|
+
[metadata]: T;
|
48
|
+
};
|
49
|
+
new (): Instance<T>;
|
50
|
+
prototype: Instance<T>;
|
42
51
|
}
|
43
52
|
|
44
53
|
export interface StaticLike<T extends Metadata = Metadata> extends ClassLike {
|
45
|
-
[metadata]?: T;
|
46
|
-
|
54
|
+
[Symbol.metadata]?: _DecoratorMetadata<T> | null;
|
55
|
+
}
|
56
|
+
|
57
|
+
export function isValidMetadata<T extends Metadata = Metadata>(
|
58
|
+
arg: unknown
|
59
|
+
): arg is DecoratorMetadata & {
|
60
|
+
[metadata]: T;
|
61
|
+
} {
|
62
|
+
return arg != null && typeof arg == 'object' && metadata in arg;
|
63
|
+
}
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Gets a reference to Symbol.metadata, even on platforms that do not expose it globally (like Node)
|
67
|
+
*/
|
68
|
+
export function symbol_metadata(arg: ClassLike): typeof Symbol.metadata {
|
69
|
+
const symbol_metadata = Symbol.metadata || Object.getOwnPropertySymbols(arg).find(s => s.description == 'Symbol.metadata');
|
70
|
+
if (!symbol_metadata) {
|
71
|
+
throw new ReferenceError('Could not get a reference to Symbol.metadata');
|
72
|
+
}
|
73
|
+
|
74
|
+
return symbol_metadata as typeof Symbol.metadata;
|
47
75
|
}
|
48
76
|
|
49
77
|
export function isStatic<T extends Metadata = Metadata>(arg: unknown): arg is Static<T> {
|
50
|
-
return typeof arg == 'function' &&
|
78
|
+
return typeof arg == 'function' && symbol_metadata(arg as ClassLike) in arg && isValidMetadata(arg[symbol_metadata(arg as ClassLike)]);
|
51
79
|
}
|
52
80
|
|
53
81
|
export interface Instance<T extends Metadata = Metadata> {
|
@@ -59,7 +87,7 @@ export interface InstanceLike<T extends Metadata = Metadata> {
|
|
59
87
|
}
|
60
88
|
|
61
89
|
export function isInstance<T extends Metadata = Metadata>(arg: unknown): arg is Instance<T> {
|
62
|
-
return
|
90
|
+
return arg != null && typeof arg == 'object' && isStatic(arg.constructor);
|
63
91
|
}
|
64
92
|
|
65
93
|
export function isStruct<T extends Metadata = Metadata>(arg: unknown): arg is Instance<T> | Static<T> {
|
package/src/list.ts
CHANGED
@@ -3,6 +3,13 @@ import { EventEmitter } from 'eventemitter3';
|
|
3
3
|
export class List<T> extends EventEmitter<'update'> implements Set<T>, RelativeIndexable<T> {
|
4
4
|
public readonly [Symbol.toStringTag] = 'List';
|
5
5
|
|
6
|
+
public constructor(values?: readonly T[] | Iterable<T> | null) {
|
7
|
+
super();
|
8
|
+
if (values) {
|
9
|
+
this.push(...values);
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
6
13
|
protected data = new Set<T>();
|
7
14
|
|
8
15
|
public array(): T[] {
|
@@ -96,6 +103,34 @@ export class List<T> extends EventEmitter<'update'> implements Set<T>, RelativeI
|
|
96
103
|
return success;
|
97
104
|
}
|
98
105
|
|
106
|
+
public union<U>(other: ReadonlySetLike<U>): List<T | U> {
|
107
|
+
return new List(this.data.union(other));
|
108
|
+
}
|
109
|
+
|
110
|
+
public intersection<U>(other: ReadonlySetLike<U>): List<T & U> {
|
111
|
+
return new List(this.data.intersection(other));
|
112
|
+
}
|
113
|
+
|
114
|
+
public difference<U>(other: ReadonlySetLike<U>): List<T> {
|
115
|
+
return new List(this.data.difference(other));
|
116
|
+
}
|
117
|
+
|
118
|
+
public symmetricDifference<U>(other: ReadonlySetLike<U>): List<T | U> {
|
119
|
+
return new List(this.data.symmetricDifference(other));
|
120
|
+
}
|
121
|
+
|
122
|
+
public isSubsetOf(other: ReadonlySetLike<unknown>): boolean {
|
123
|
+
return this.data.isSubsetOf(other);
|
124
|
+
}
|
125
|
+
|
126
|
+
public isSupersetOf(other: ReadonlySetLike<unknown>): boolean {
|
127
|
+
return this.data.isSupersetOf(other);
|
128
|
+
}
|
129
|
+
|
130
|
+
public isDisjointFrom(other: ReadonlySetLike<unknown>): boolean {
|
131
|
+
return this.data.isDisjointFrom(other);
|
132
|
+
}
|
133
|
+
|
99
134
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
100
135
|
public forEach(callbackfn: (value: T, value2: T, list: List<T>) => void, thisArg?: any): void {
|
101
136
|
this.data.forEach((v1, v2) => callbackfn.call(thisArg, v1, v2, this));
|
package/src/struct.ts
CHANGED
@@ -1,29 +1,44 @@
|
|
1
|
-
import * as
|
1
|
+
import * as primitive from './internal/primitives.js';
|
2
|
+
import {
|
3
|
+
DecoratorContext,
|
4
|
+
InstanceLike,
|
5
|
+
MemberInit,
|
6
|
+
Metadata,
|
7
|
+
Options,
|
8
|
+
Size,
|
9
|
+
StaticLike,
|
10
|
+
symbol_metadata,
|
11
|
+
init,
|
12
|
+
isInstance,
|
13
|
+
isStatic,
|
14
|
+
isStruct,
|
15
|
+
metadata,
|
16
|
+
type MemberContext,
|
17
|
+
} from './internal/struct.js';
|
2
18
|
import { capitalize } from './string.js';
|
3
19
|
import { ClassLike } from './types.js';
|
4
|
-
|
5
|
-
export { Struct };
|
20
|
+
export * as Struct from './internal/struct.js';
|
6
21
|
|
7
22
|
/**
|
8
23
|
* Gets the size in bytes of a type
|
9
24
|
*/
|
10
|
-
export function sizeof<T extends primitive.Valid |
|
25
|
+
export function sizeof<T extends primitive.Valid | StaticLike | InstanceLike>(type: T): Size<T> {
|
11
26
|
// primitive
|
12
27
|
if (typeof type == 'string') {
|
13
28
|
if (!primitive.isValid(type)) {
|
14
29
|
throw new TypeError('Invalid primitive type: ' + type);
|
15
30
|
}
|
16
31
|
|
17
|
-
return (+primitive.normalize(type).match(primitive.regex)![2] / 8) as
|
32
|
+
return (+primitive.normalize(type).match(primitive.regex)![2] / 8) as Size<T>;
|
18
33
|
}
|
19
34
|
|
20
|
-
if (!
|
35
|
+
if (!isStruct(type)) {
|
21
36
|
throw new TypeError('Not a struct');
|
22
37
|
}
|
23
38
|
|
24
|
-
const
|
39
|
+
const struct = isStatic(type) ? type : type.constructor;
|
25
40
|
|
26
|
-
return
|
41
|
+
return struct[symbol_metadata(struct)][metadata].size as Size<T>;
|
27
42
|
}
|
28
43
|
|
29
44
|
/**
|
@@ -36,14 +51,15 @@ export function align(value: number, alignment: number): number {
|
|
36
51
|
/**
|
37
52
|
* Decorates a class as a struct
|
38
53
|
*/
|
39
|
-
export function struct(options: Partial<
|
54
|
+
export function struct(options: Partial<Options> = {}) {
|
40
55
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
41
|
-
return function (target:
|
42
|
-
|
56
|
+
return function __decorateStruct<const T extends StaticLike>(target: T, context: ClassDecoratorContext & DecoratorContext): T {
|
57
|
+
context.metadata[init] ||= [];
|
43
58
|
let size = 0;
|
44
59
|
const members = new Map();
|
45
|
-
for (const
|
46
|
-
|
60
|
+
for (const _ of context.metadata[init]) {
|
61
|
+
const { name, type, length } = _;
|
62
|
+
if (!primitive.isValid(type) && !isStatic(type)) {
|
47
63
|
throw new TypeError('Not a valid type: ' + type);
|
48
64
|
}
|
49
65
|
members.set(name, {
|
@@ -55,7 +71,8 @@ export function struct(options: Partial<Struct.Options> = {}) {
|
|
55
71
|
size = align(size, options.align || 1);
|
56
72
|
}
|
57
73
|
|
58
|
-
|
74
|
+
context.metadata[metadata] = { options, members, size } satisfies Metadata;
|
75
|
+
return target;
|
59
76
|
};
|
60
77
|
}
|
61
78
|
|
@@ -63,8 +80,8 @@ export function struct(options: Partial<Struct.Options> = {}) {
|
|
63
80
|
* Decorates a class member to be serialized
|
64
81
|
*/
|
65
82
|
export function member(type: primitive.Valid | ClassLike, length?: number) {
|
66
|
-
return function (
|
67
|
-
let name =
|
83
|
+
return function <V>(value: V, context: MemberContext): V {
|
84
|
+
let name = context.name;
|
68
85
|
if (typeof name == 'symbol') {
|
69
86
|
console.warn('Symbol used for struct member name will be coerced to string: ' + name.toString());
|
70
87
|
name = name.toString();
|
@@ -74,18 +91,9 @@ export function member(type: primitive.Valid | ClassLike, length?: number) {
|
|
74
91
|
throw new ReferenceError('Invalid name for struct member');
|
75
92
|
}
|
76
93
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
if (!('constructor' in target)) {
|
82
|
-
throw new TypeError('Invalid member for struct field');
|
83
|
-
}
|
84
|
-
|
85
|
-
const struct = (target as Struct.InstanceLike).constructor;
|
86
|
-
|
87
|
-
struct[Struct.init] ||= [];
|
88
|
-
struct[Struct.init].push({ name, type, length } satisfies Struct.MemberInit);
|
94
|
+
context.metadata[init] ||= [];
|
95
|
+
context.metadata[init].push({ name, type, length } satisfies MemberInit);
|
96
|
+
return value;
|
89
97
|
};
|
90
98
|
}
|
91
99
|
|
@@ -93,10 +101,10 @@ export function member(type: primitive.Valid | ClassLike, length?: number) {
|
|
93
101
|
* Serializes a struct into a Uint8Array
|
94
102
|
*/
|
95
103
|
export function serialize(instance: unknown): Uint8Array {
|
96
|
-
if (!
|
104
|
+
if (!isInstance(instance)) {
|
97
105
|
throw new TypeError('Can not serialize, not a struct instance');
|
98
106
|
}
|
99
|
-
const { options, members } = instance.constructor[
|
107
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)][metadata];
|
100
108
|
|
101
109
|
const buffer = new Uint8Array(sizeof(instance));
|
102
110
|
const view = new DataView(buffer.buffer);
|
@@ -139,10 +147,10 @@ export function serialize(instance: unknown): Uint8Array {
|
|
139
147
|
* Deserializes a struct from a Uint8Array
|
140
148
|
*/
|
141
149
|
export function deserialize(instance: unknown, _buffer: ArrayBuffer | ArrayBufferView) {
|
142
|
-
if (!
|
150
|
+
if (!isInstance(instance)) {
|
143
151
|
throw new TypeError('Can not deserialize, not a struct instance');
|
144
152
|
}
|
145
|
-
const { options, members } = instance.constructor[
|
153
|
+
const { options, members } = instance.constructor[symbol_metadata(instance.constructor)][metadata];
|
146
154
|
|
147
155
|
const buffer = new Uint8Array('buffer' in _buffer ? _buffer.buffer : _buffer);
|
148
156
|
|
@@ -191,22 +199,17 @@ export function deserialize(instance: unknown, _buffer: ArrayBuffer | ArrayBuffe
|
|
191
199
|
}
|
192
200
|
}
|
193
201
|
|
194
|
-
/**
|
195
|
-
* Also can be a name when legacy decorators are used
|
196
|
-
*/
|
197
|
-
type Context = string | symbol | ClassMemberDecoratorContext;
|
198
|
-
|
199
202
|
function _member<T extends primitive.Valid>(type: T) {
|
200
|
-
function
|
201
|
-
function
|
202
|
-
function
|
203
|
-
if (typeof
|
204
|
-
return member(type,
|
203
|
+
function _structMemberDecorator<const V>(length: number): (value: V, context: MemberContext) => V;
|
204
|
+
function _structMemberDecorator<const V>(value: V, context: MemberContext): V;
|
205
|
+
function _structMemberDecorator<const V>(valueOrLength: V | number, context?: MemberContext): V | ((value: V, context: MemberContext) => V) {
|
206
|
+
if (typeof valueOrLength == 'number') {
|
207
|
+
return member(type, valueOrLength);
|
205
208
|
}
|
206
209
|
|
207
|
-
return member(type)(
|
210
|
+
return member(type)(valueOrLength, context!);
|
208
211
|
}
|
209
|
-
return
|
212
|
+
return _structMemberDecorator;
|
210
213
|
}
|
211
214
|
|
212
215
|
/**
|
package/tsconfig.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"compilerOptions": {
|
3
|
-
"lib": ["
|
3
|
+
"lib": ["ESNext"],
|
4
4
|
"module": "NodeNext",
|
5
5
|
"target": "ES2023",
|
6
6
|
"moduleResolution": "NodeNext",
|
@@ -10,7 +10,6 @@
|
|
10
10
|
"esModuleInterop": true,
|
11
11
|
"noImplicitThis": true,
|
12
12
|
"declaration": true,
|
13
|
-
"experimentalDecorators": true,
|
14
13
|
"strict": true
|
15
14
|
},
|
16
15
|
"typedocOptions": {
|