utilium 1.7.10 → 1.7.12
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/debugging.d.ts +12 -0
- package/dist/debugging.js +17 -0
- package/dist/internal/struct.d.ts +17 -11
- package/dist/internal/struct.js +18 -21
- package/dist/struct.d.ts +1 -1
- package/dist/struct.js +122 -95
- package/package.json +1 -1
- package/src/debugging.ts +19 -0
- package/src/internal/struct.ts +38 -36
- package/src/struct.ts +159 -110
package/dist/debugging.d.ts
CHANGED
@@ -27,4 +27,16 @@ type LoggableDecoratorContext = Exclude<DecoratorContext, ClassFieldDecoratorCon
|
|
27
27
|
* Create a function that can be used to decorate classes and non-field members.
|
28
28
|
*/
|
29
29
|
export declare function createLogDecorator(options: CreateLoggerOptions): <T extends (...args: any[]) => any>(value: T, context: LoggableDecoratorContext) => T;
|
30
|
+
/**
|
31
|
+
* @internal @hidden
|
32
|
+
*/
|
33
|
+
export declare let U_DEBUG: boolean;
|
34
|
+
/**
|
35
|
+
* @internal @hidden
|
36
|
+
*/
|
37
|
+
export declare function _setDebug(value: boolean): void;
|
38
|
+
/**
|
39
|
+
* @internal @hidden
|
40
|
+
*/
|
41
|
+
export declare function _debugLog(...text: any[]): void;
|
30
42
|
export {};
|
package/dist/debugging.js
CHANGED
@@ -53,3 +53,20 @@ export function createLogDecorator(options) {
|
|
53
53
|
};
|
54
54
|
};
|
55
55
|
}
|
56
|
+
/**
|
57
|
+
* @internal @hidden
|
58
|
+
*/
|
59
|
+
export let U_DEBUG = 'process' in globalThis && 'env' in globalThis.process && globalThis.process.env.U_DEBUG == 'true';
|
60
|
+
/**
|
61
|
+
* @internal @hidden
|
62
|
+
*/
|
63
|
+
export function _setDebug(value) {
|
64
|
+
U_DEBUG = value;
|
65
|
+
}
|
66
|
+
/**
|
67
|
+
* @internal @hidden
|
68
|
+
*/
|
69
|
+
export function _debugLog(...text) {
|
70
|
+
if (U_DEBUG)
|
71
|
+
console.debug('[U]', ...text);
|
72
|
+
}
|
@@ -10,7 +10,7 @@ declare global {
|
|
10
10
|
readonly deserialize: unique symbol;
|
11
11
|
}
|
12
12
|
}
|
13
|
-
export type TypeLike = Custom | Like | primitive.Valid;
|
13
|
+
export type TypeLike = Custom | Like | primitive.Valid | undefined | null;
|
14
14
|
export type Type = Custom | Static | primitive.Typename;
|
15
15
|
/**
|
16
16
|
* Member initialization data
|
@@ -30,22 +30,31 @@ export interface Options {
|
|
30
30
|
isUnion: boolean;
|
31
31
|
}
|
32
32
|
export interface Member {
|
33
|
+
name: string;
|
33
34
|
type: Type;
|
34
|
-
|
35
|
+
staticOffset: number;
|
35
36
|
length?: number | string;
|
37
|
+
/** A C-style type/name declaration string, used for diagnostics */
|
38
|
+
decl: string;
|
36
39
|
}
|
37
40
|
export interface Metadata {
|
38
41
|
options: Partial<Options>;
|
39
42
|
members: Map<string, Member>;
|
40
|
-
init?: MemberInit[];
|
41
43
|
staticSize: number;
|
44
|
+
isDynamic: boolean;
|
42
45
|
}
|
43
46
|
type _DecoratorMetadata<T extends Metadata = Metadata> = DecoratorMetadata & {
|
44
|
-
struct?:
|
47
|
+
struct?: T;
|
48
|
+
structInit?: MemberInit[];
|
45
49
|
};
|
46
50
|
export interface DecoratorContext<T extends Metadata = Metadata> {
|
47
51
|
metadata: _DecoratorMetadata<T>;
|
48
52
|
}
|
53
|
+
/**
|
54
|
+
* Initializes the struct metadata for a class
|
55
|
+
* This also handles copying metadata from parent classes
|
56
|
+
*/
|
57
|
+
export declare function initMetadata(context: DecoratorContext): MemberInit[];
|
49
58
|
export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
|
50
59
|
export interface Static<T extends Metadata = Metadata> {
|
51
60
|
[Symbol.metadata]: {
|
@@ -63,12 +72,9 @@ export declare function isValidMetadata<T extends Metadata = Metadata>(arg: unkn
|
|
63
72
|
/**
|
64
73
|
* Polyfill context.metadata
|
65
74
|
* @see https://github.com/microsoft/TypeScript/issues/53461
|
75
|
+
* @internal @hidden
|
66
76
|
*/
|
67
|
-
export declare function
|
68
|
-
/**
|
69
|
-
* Gets a reference to Symbol.metadata, even on platforms that do not expose it globally (like Node)
|
70
|
-
*/
|
71
|
-
export declare function symbol_metadata(arg: ClassLike): typeof Symbol.metadata;
|
77
|
+
export declare function _polyfill_metadata(target: object): void;
|
72
78
|
export declare function isStatic<T extends Metadata = Metadata>(arg: unknown): arg is Static<T>;
|
73
79
|
export interface Instance<T extends Metadata = Metadata> {
|
74
80
|
constructor: Static<T>;
|
@@ -90,7 +96,7 @@ export interface Custom {
|
|
90
96
|
}
|
91
97
|
export declare function isCustom(arg: unknown): arg is Custom;
|
92
98
|
export type Like<T extends Metadata = Metadata> = InstanceLike<T> | StaticLike<T>;
|
93
|
-
export type Size<T extends TypeLike> = T extends {
|
94
|
-
readonly [Symbol.size]: infer S;
|
99
|
+
export type Size<T extends TypeLike> = T extends undefined | null ? 0 : T extends {
|
100
|
+
readonly [Symbol.size]: infer S extends number;
|
95
101
|
} ? S : T extends primitive.Valid ? primitive.Size<T> : number;
|
96
102
|
export {};
|
package/dist/internal/struct.js
CHANGED
@@ -1,22 +1,32 @@
|
|
1
|
+
/**
|
2
|
+
* Polyfill Symbol.metadata
|
3
|
+
* @see https://github.com/microsoft/TypeScript/issues/53461
|
4
|
+
*/
|
5
|
+
Symbol.metadata ??= Symbol.for('Symbol.metadata');
|
1
6
|
Object.assign(Symbol, {
|
2
7
|
size: Symbol('uSize'),
|
3
8
|
serialize: Symbol('uSerialize'),
|
4
9
|
deserialize: Symbol('uDeserialize'),
|
5
10
|
});
|
11
|
+
/**
|
12
|
+
* Initializes the struct metadata for a class
|
13
|
+
* This also handles copying metadata from parent classes
|
14
|
+
*/
|
15
|
+
export function initMetadata(context) {
|
16
|
+
context.metadata ??= {};
|
17
|
+
context.metadata.structInit = [...(context.metadata.structInit ?? [])];
|
18
|
+
return context.metadata.structInit;
|
19
|
+
}
|
6
20
|
export function isValidMetadata(arg) {
|
7
21
|
return arg != null && typeof arg == 'object' && 'struct' in arg;
|
8
22
|
}
|
9
|
-
/**
|
10
|
-
* Polyfill Symbol.metadata
|
11
|
-
* @see https://github.com/microsoft/TypeScript/issues/53461
|
12
|
-
*/
|
13
|
-
Symbol.metadata ??= Symbol.for('Symbol.metadata');
|
14
23
|
/**
|
15
24
|
* Polyfill context.metadata
|
16
25
|
* @see https://github.com/microsoft/TypeScript/issues/53461
|
26
|
+
* @internal @hidden
|
17
27
|
*/
|
18
|
-
export function
|
19
|
-
if (
|
28
|
+
export function _polyfill_metadata(target) {
|
29
|
+
if (Symbol.metadata in target)
|
20
30
|
return;
|
21
31
|
Object.defineProperty(target, Symbol.metadata, {
|
22
32
|
enumerable: true,
|
@@ -25,21 +35,8 @@ export function _polyfill_contextMetadata(target) {
|
|
25
35
|
value: Object.create(null),
|
26
36
|
});
|
27
37
|
}
|
28
|
-
/**
|
29
|
-
* Gets a reference to Symbol.metadata, even on platforms that do not expose it globally (like Node)
|
30
|
-
*/
|
31
|
-
export function symbol_metadata(arg) {
|
32
|
-
const symbol_metadata = Symbol.metadata || Object.getOwnPropertySymbols(arg).find(s => s.description == 'Symbol.metadata');
|
33
|
-
_polyfill_contextMetadata(arg);
|
34
|
-
if (!symbol_metadata) {
|
35
|
-
throw new ReferenceError('Could not get a reference to Symbol.metadata');
|
36
|
-
}
|
37
|
-
return symbol_metadata;
|
38
|
-
}
|
39
38
|
export function isStatic(arg) {
|
40
|
-
return
|
41
|
-
&& symbol_metadata(arg) in arg
|
42
|
-
&& isValidMetadata(arg[symbol_metadata(arg)]));
|
39
|
+
return typeof arg == 'function' && Symbol.metadata in arg && isValidMetadata(arg[Symbol.metadata]);
|
43
40
|
}
|
44
41
|
export function isInstance(arg) {
|
45
42
|
return arg != null && typeof arg == 'object' && isStatic(arg.constructor);
|
package/dist/struct.d.ts
CHANGED
@@ -5,7 +5,7 @@ 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 TypeLike>(type: T): Size<T>;
|
8
|
+
export declare function sizeof<T extends TypeLike>(type: T | T[]): Size<T>;
|
9
9
|
/**
|
10
10
|
* Returns the offset (in bytes) of a member in a struct.
|
11
11
|
*/
|
package/dist/struct.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { toUint8Array } from './buffer.js';
|
2
|
+
import { _debugLog } from './debugging.js';
|
2
3
|
import * as primitive from './internal/primitives.js';
|
3
|
-
import { checkInstance, checkStruct, isCustom, isStatic,
|
4
|
+
import { _polyfill_metadata, checkInstance, checkStruct, initMetadata, isCustom, isInstance, isStatic, } from './internal/struct.js';
|
4
5
|
import { _throw } from './misc.js';
|
5
6
|
import { capitalize } from './string.js';
|
6
7
|
export * as Struct from './internal/struct.js';
|
@@ -8,6 +9,15 @@ export * as Struct from './internal/struct.js';
|
|
8
9
|
* Gets the size in bytes of a type
|
9
10
|
*/
|
10
11
|
export function sizeof(type) {
|
12
|
+
if (type === undefined || type === null)
|
13
|
+
return 0;
|
14
|
+
if (Array.isArray(type)) {
|
15
|
+
let size = 0;
|
16
|
+
for (let i = 0; i < type.length; i++) {
|
17
|
+
size += sizeof(type[i]);
|
18
|
+
}
|
19
|
+
return size;
|
20
|
+
}
|
11
21
|
// primitive
|
12
22
|
if (typeof type == 'string') {
|
13
23
|
primitive.checkValid(type);
|
@@ -16,20 +26,40 @@ export function sizeof(type) {
|
|
16
26
|
if (isCustom(type))
|
17
27
|
return type[Symbol.size];
|
18
28
|
checkStruct(type);
|
19
|
-
const
|
20
|
-
|
29
|
+
const constructor = isStatic(type) ? type : type.constructor;
|
30
|
+
_polyfill_metadata(constructor);
|
31
|
+
const { struct } = constructor[Symbol.metadata];
|
32
|
+
if (isStatic(type))
|
33
|
+
return struct.staticSize;
|
34
|
+
let size = struct.staticSize;
|
35
|
+
for (const member of struct.members.values()) {
|
36
|
+
if (typeof member.length != 'string')
|
37
|
+
continue;
|
38
|
+
for (let i = 0; i < type[member.length]; i++) {
|
39
|
+
size += sizeof(type[member.name][i]);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
return size;
|
21
43
|
}
|
22
44
|
/**
|
23
45
|
* Returns the offset (in bytes) of a member in a struct.
|
24
46
|
*/
|
25
47
|
export function offsetof(type, memberName) {
|
26
48
|
checkStruct(type);
|
27
|
-
const
|
28
|
-
|
29
|
-
const
|
30
|
-
if (!
|
31
|
-
|
32
|
-
|
49
|
+
const constructor = isStatic(type) ? type : type.constructor;
|
50
|
+
_polyfill_metadata(constructor);
|
51
|
+
const { struct } = constructor[Symbol.metadata];
|
52
|
+
if (isStatic(type) || !struct.isDynamic) {
|
53
|
+
return (struct.members.get(memberName)?.staticOffset
|
54
|
+
?? _throw(new Error('Struct does not have member: ' + memberName)));
|
55
|
+
}
|
56
|
+
let offset = 0;
|
57
|
+
for (const member of struct.members.values()) {
|
58
|
+
if (member.name == memberName)
|
59
|
+
return offset;
|
60
|
+
offset += sizeof(type[member.name]);
|
61
|
+
}
|
62
|
+
throw new Error('Struct does not have member: ' + memberName);
|
33
63
|
}
|
34
64
|
/**
|
35
65
|
* Aligns a number
|
@@ -37,65 +67,40 @@ export function offsetof(type, memberName) {
|
|
37
67
|
export function align(value, alignment) {
|
38
68
|
return Math.ceil(value / alignment) * alignment;
|
39
69
|
}
|
40
|
-
/**
|
41
|
-
* Gets the length of an array in a struct
|
42
|
-
* @param length The numeric length or the name of the field which has the array length (like __counted_by)
|
43
|
-
* @param name The name of the array field— only used for errors
|
44
|
-
*/
|
45
|
-
function _memberLength(struct, length, name) {
|
46
|
-
if (length === undefined)
|
47
|
-
return -1;
|
48
|
-
if (typeof length != 'string')
|
49
|
-
return Number.isSafeInteger(length) && length >= 0
|
50
|
-
? length
|
51
|
-
: _throw(new Error('Array lengths must be natural numbers'));
|
52
|
-
if (!(length in struct))
|
53
|
-
throw new Error(`Can not use non-existent member to count ${name}: ` + length);
|
54
|
-
const n = struct[length];
|
55
|
-
if (typeof n != 'number')
|
56
|
-
throw new Error(`Can not use "${name}" to count ${length}`);
|
57
|
-
return n;
|
58
|
-
}
|
59
|
-
/** Compute the size of a struct including dynamically sized members */
|
60
|
-
function _structSize() {
|
61
|
-
const { staticSize, members } = this.constructor[symbol_metadata(this.constructor)].struct;
|
62
|
-
let size = staticSize;
|
63
|
-
for (const [name, { type, length: key }] of members) {
|
64
|
-
if (typeof key != 'string')
|
65
|
-
continue;
|
66
|
-
size += sizeof(type) * _memberLength(this, key, name);
|
67
|
-
}
|
68
|
-
return size;
|
69
|
-
}
|
70
70
|
/**
|
71
71
|
* Decorates a class as a struct
|
72
72
|
*/
|
73
73
|
export function struct(options = {}) {
|
74
74
|
return function _decorateStruct(target, context) {
|
75
|
-
context.metadata ??= {};
|
76
|
-
context.metadata.struct ??= {};
|
77
|
-
context.metadata.struct.init ??= [];
|
78
|
-
let staticSize = 0;
|
79
75
|
const members = new Map();
|
80
|
-
|
76
|
+
let staticSize = 0, isDynamic = false;
|
77
|
+
for (const { name, type, length } of initMetadata(context)) {
|
81
78
|
if (!primitive.isValid(type) && !isStatic(type))
|
82
79
|
throw new TypeError('Not a valid type: ' + type);
|
80
|
+
if (typeof length == 'string') {
|
81
|
+
const countedBy = members.get(length);
|
82
|
+
if (!countedBy)
|
83
|
+
throw new Error(`"${length}" is undefined or declared after "${name}"`);
|
84
|
+
if (!primitive.isType(countedBy.type))
|
85
|
+
throw new Error(`"${length}" is not a number and cannot be used to count "${name}"`);
|
86
|
+
}
|
87
|
+
let decl = `${typeof type == 'string' ? type : type.name} ${name}`;
|
88
|
+
if (length !== undefined)
|
89
|
+
decl += `[${length}]`;
|
83
90
|
members.set(name, {
|
84
|
-
|
91
|
+
name,
|
92
|
+
staticOffset: options.isUnion ? 0 : staticSize,
|
85
93
|
type: primitive.isValid(type) ? primitive.normalize(type) : type,
|
86
94
|
length,
|
95
|
+
decl,
|
87
96
|
});
|
88
97
|
const memberSize = typeof length == 'string' ? 0 : sizeof(type) * (length || 1);
|
98
|
+
isDynamic ||= typeof length == 'string';
|
89
99
|
staticSize = options.isUnion ? Math.max(staticSize, memberSize) : staticSize + memberSize;
|
90
100
|
staticSize = align(staticSize, options.align || 1);
|
101
|
+
_debugLog('define', target.name + '.' + name);
|
91
102
|
}
|
92
|
-
context.metadata.struct = { options, members, staticSize };
|
93
|
-
context.addInitializer(function () {
|
94
|
-
Object.defineProperty(this.prototype, Symbol.size, {
|
95
|
-
get: _structSize.bind(this),
|
96
|
-
enumerable: false,
|
97
|
-
});
|
98
|
-
});
|
103
|
+
context.metadata.struct = { options, members, staticSize, isDynamic };
|
99
104
|
return target;
|
100
105
|
};
|
101
106
|
}
|
@@ -111,13 +116,20 @@ export function member(type, length) {
|
|
111
116
|
}
|
112
117
|
if (!name)
|
113
118
|
throw new ReferenceError('Invalid name for struct member');
|
114
|
-
context.
|
115
|
-
context.metadata.struct ??= {};
|
116
|
-
context.metadata.struct.init ??= [];
|
117
|
-
context.metadata.struct.init.push({ name, type, length });
|
119
|
+
initMetadata(context).push({ name, type, length });
|
118
120
|
return value;
|
119
121
|
};
|
120
122
|
}
|
123
|
+
/** Gets the length of a member */
|
124
|
+
function _memberLength(instance, member) {
|
125
|
+
if (member.length === undefined)
|
126
|
+
return -1;
|
127
|
+
if (typeof member.length == 'string')
|
128
|
+
return instance[member.length];
|
129
|
+
return Number.isSafeInteger(member.length) && member.length >= 0
|
130
|
+
? member.length
|
131
|
+
: _throw(new Error('Array lengths must be natural numbers'));
|
132
|
+
}
|
121
133
|
/**
|
122
134
|
* Serializes a struct into a Uint8Array
|
123
135
|
*/
|
@@ -125,47 +137,53 @@ export function serialize(instance) {
|
|
125
137
|
if (isCustom(instance) && typeof instance[Symbol.serialize] == 'function')
|
126
138
|
return instance[Symbol.serialize]();
|
127
139
|
checkInstance(instance);
|
128
|
-
|
129
|
-
const
|
140
|
+
_polyfill_metadata(instance.constructor);
|
141
|
+
const { options, members } = instance.constructor[Symbol.metadata].struct;
|
142
|
+
const size = sizeof(instance);
|
143
|
+
const buffer = new Uint8Array(size);
|
130
144
|
const view = new DataView(buffer.buffer);
|
145
|
+
_debugLog('serialize', instance.constructor.name);
|
146
|
+
let offset = 0, nextOffset = 0;
|
131
147
|
// for unions we should write members in ascending last modified order, but we don't have that info.
|
132
|
-
for (const
|
133
|
-
const length = _memberLength(instance,
|
148
|
+
for (const member of members.values()) {
|
149
|
+
const length = _memberLength(instance, member);
|
150
|
+
_debugLog('\t', member.decl);
|
134
151
|
for (let i = 0; i < Math.abs(length); i++) {
|
135
|
-
|
136
|
-
let value = length != -1 ? instance[name][i] : instance[name];
|
152
|
+
let value = length != -1 ? instance[member.name][i] : instance[member.name];
|
137
153
|
if (typeof value == 'string') {
|
138
154
|
value = value.charCodeAt(0);
|
139
155
|
}
|
140
|
-
|
141
|
-
|
156
|
+
offset = nextOffset;
|
157
|
+
nextOffset += isInstance(value) ? sizeof(value) : sizeof(member.type);
|
158
|
+
if (!primitive.isType(member.type)) {
|
159
|
+
buffer.set(value ? serialize(value) : new Uint8Array(sizeof(member.type)), offset);
|
142
160
|
continue;
|
143
161
|
}
|
144
|
-
const fn = `set${capitalize(type)}`;
|
162
|
+
const fn = `set${capitalize(member.type)}`;
|
145
163
|
if (fn == 'setInt64') {
|
146
|
-
view.setBigInt64(
|
164
|
+
view.setBigInt64(offset, BigInt(value), !options.bigEndian);
|
147
165
|
continue;
|
148
166
|
}
|
149
167
|
if (fn == 'setUint64') {
|
150
|
-
view.setBigUint64(
|
168
|
+
view.setBigUint64(offset, BigInt(value), !options.bigEndian);
|
151
169
|
continue;
|
152
170
|
}
|
153
171
|
if (fn == 'setInt128') {
|
154
|
-
view.setBigUint64(
|
155
|
-
view.setBigInt64(
|
172
|
+
view.setBigUint64(offset + (!options.bigEndian ? 0 : 8), value & primitive.mask64, !options.bigEndian);
|
173
|
+
view.setBigInt64(offset + (!options.bigEndian ? 8 : 0), value >> BigInt(64), !options.bigEndian);
|
156
174
|
continue;
|
157
175
|
}
|
158
176
|
if (fn == 'setUint128') {
|
159
|
-
view.setBigUint64(
|
160
|
-
view.setBigUint64(
|
177
|
+
view.setBigUint64(offset + (!options.bigEndian ? 0 : 8), value & primitive.mask64, !options.bigEndian);
|
178
|
+
view.setBigUint64(offset + (!options.bigEndian ? 8 : 0), value >> BigInt(64), !options.bigEndian);
|
161
179
|
continue;
|
162
180
|
}
|
163
181
|
if (fn == 'setFloat128') {
|
164
|
-
view.setFloat64(
|
165
|
-
view.setBigUint64(
|
182
|
+
view.setFloat64(offset + (!options.bigEndian ? 0 : 8), Number(value), !options.bigEndian);
|
183
|
+
view.setBigUint64(offset + (!options.bigEndian ? 8 : 0), BigInt(0), !options.bigEndian);
|
166
184
|
continue;
|
167
185
|
}
|
168
|
-
view[fn](
|
186
|
+
view[fn](offset, Number(value), !options.bigEndian);
|
169
187
|
}
|
170
188
|
}
|
171
189
|
return buffer;
|
@@ -178,53 +196,62 @@ export function deserialize(instance, _buffer) {
|
|
178
196
|
if (isCustom(instance) && typeof instance[Symbol.deserialize] == 'function')
|
179
197
|
return instance[Symbol.deserialize](buffer);
|
180
198
|
checkInstance(instance);
|
181
|
-
|
199
|
+
_polyfill_metadata(instance.constructor);
|
200
|
+
const { options, members } = instance.constructor[Symbol.metadata].struct;
|
182
201
|
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
183
|
-
|
184
|
-
|
202
|
+
_debugLog('deserialize', instance.constructor.name);
|
203
|
+
let offset = 0, nextOffset = 0;
|
204
|
+
for (const member of members.values()) {
|
205
|
+
const length = _memberLength(instance, member);
|
206
|
+
_debugLog('\t', member.decl);
|
185
207
|
for (let i = 0; i < Math.abs(length); i++) {
|
186
|
-
let object = length != -1 ? instance[name] : instance;
|
187
|
-
const key = length != -1 ? i : name
|
188
|
-
|
189
|
-
|
190
|
-
|
208
|
+
let object = length != -1 ? instance[member.name] : instance;
|
209
|
+
const key = length != -1 ? i : member.name;
|
210
|
+
offset = nextOffset;
|
211
|
+
if (!isInstance(object[key]))
|
212
|
+
nextOffset += sizeof(member.type);
|
213
|
+
if (typeof instance[member.name] == 'string') {
|
214
|
+
instance[member.name] =
|
215
|
+
instance[member.name].slice(0, i)
|
216
|
+
+ String.fromCharCode(view.getUint8(offset))
|
217
|
+
+ instance[member.name].slice(i + 1);
|
191
218
|
continue;
|
192
219
|
}
|
193
|
-
if (!primitive.isType(type)) {
|
194
|
-
if (object[key] === null || object[key] === undefined)
|
220
|
+
if (!primitive.isType(member.type)) {
|
221
|
+
if (object[key] === null || object[key] === undefined)
|
195
222
|
continue;
|
196
|
-
|
197
|
-
|
223
|
+
deserialize(object[key], new Uint8Array(buffer.subarray(offset)));
|
224
|
+
nextOffset += sizeof(object[key]);
|
198
225
|
continue;
|
199
226
|
}
|
200
227
|
if (length && length != -1)
|
201
228
|
object ||= [];
|
202
|
-
const fn = `get${capitalize(type)}`;
|
229
|
+
const fn = `get${capitalize(member.type)}`;
|
203
230
|
if (fn == 'getInt64') {
|
204
|
-
object[key] = view.getBigInt64(
|
231
|
+
object[key] = view.getBigInt64(offset, !options.bigEndian);
|
205
232
|
continue;
|
206
233
|
}
|
207
234
|
if (fn == 'getUint64') {
|
208
|
-
object[key] = view.getBigUint64(
|
235
|
+
object[key] = view.getBigUint64(offset, !options.bigEndian);
|
209
236
|
continue;
|
210
237
|
}
|
211
238
|
if (fn == 'getInt128') {
|
212
239
|
object[key] =
|
213
|
-
(view.getBigInt64(
|
214
|
-
| view.getBigUint64(
|
240
|
+
(view.getBigInt64(offset + (!options.bigEndian ? 8 : 0), !options.bigEndian) << BigInt(64))
|
241
|
+
| view.getBigUint64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
|
215
242
|
continue;
|
216
243
|
}
|
217
244
|
if (fn == 'getUint128') {
|
218
245
|
object[key] =
|
219
|
-
(view.getBigUint64(
|
220
|
-
| view.getBigUint64(
|
246
|
+
(view.getBigUint64(offset + (!options.bigEndian ? 8 : 0), !options.bigEndian) << BigInt(64))
|
247
|
+
| view.getBigUint64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
|
221
248
|
continue;
|
222
249
|
}
|
223
250
|
if (fn == 'getFloat128') {
|
224
|
-
object[key] = view.getFloat64(
|
251
|
+
object[key] = view.getFloat64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
|
225
252
|
continue;
|
226
253
|
}
|
227
|
-
object[key] = view[fn](
|
254
|
+
object[key] = view[fn](offset, !options.bigEndian);
|
228
255
|
}
|
229
256
|
}
|
230
257
|
}
|
package/package.json
CHANGED
package/src/debugging.ts
CHANGED
@@ -92,3 +92,22 @@ export function createLogDecorator(options: CreateLoggerOptions) {
|
|
92
92
|
} as T;
|
93
93
|
};
|
94
94
|
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* @internal @hidden
|
98
|
+
*/
|
99
|
+
export let U_DEBUG = 'process' in globalThis && 'env' in globalThis.process && globalThis.process.env.U_DEBUG == 'true';
|
100
|
+
|
101
|
+
/**
|
102
|
+
* @internal @hidden
|
103
|
+
*/
|
104
|
+
export function _setDebug(value: boolean) {
|
105
|
+
U_DEBUG = value;
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* @internal @hidden
|
110
|
+
*/
|
111
|
+
export function _debugLog(...text: any[]) {
|
112
|
+
if (U_DEBUG) console.debug('[U]', ...text);
|
113
|
+
}
|
package/src/internal/struct.ts
CHANGED
@@ -14,13 +14,19 @@ declare global {
|
|
14
14
|
}
|
15
15
|
}
|
16
16
|
|
17
|
+
/**
|
18
|
+
* Polyfill Symbol.metadata
|
19
|
+
* @see https://github.com/microsoft/TypeScript/issues/53461
|
20
|
+
*/
|
21
|
+
(Symbol as { metadata: symbol }).metadata ??= Symbol.for('Symbol.metadata');
|
22
|
+
|
17
23
|
Object.assign(Symbol, {
|
18
24
|
size: Symbol('uSize'),
|
19
25
|
serialize: Symbol('uSerialize'),
|
20
26
|
deserialize: Symbol('uDeserialize'),
|
21
27
|
});
|
22
28
|
|
23
|
-
export type TypeLike = Custom | Like | primitive.Valid;
|
29
|
+
export type TypeLike = Custom | Like | primitive.Valid | undefined | null;
|
24
30
|
|
25
31
|
export type Type = Custom | Static | primitive.Typename;
|
26
32
|
|
@@ -44,26 +50,43 @@ export interface Options {
|
|
44
50
|
}
|
45
51
|
|
46
52
|
export interface Member {
|
53
|
+
name: string;
|
47
54
|
type: Type;
|
48
|
-
|
55
|
+
staticOffset: number;
|
49
56
|
length?: number | string;
|
57
|
+
|
58
|
+
/** A C-style type/name declaration string, used for diagnostics */
|
59
|
+
decl: string;
|
50
60
|
}
|
51
61
|
|
52
62
|
export interface Metadata {
|
53
63
|
options: Partial<Options>;
|
54
64
|
members: Map<string, Member>;
|
55
|
-
init?: MemberInit[];
|
56
65
|
staticSize: number;
|
66
|
+
isDynamic: boolean;
|
57
67
|
}
|
58
68
|
|
59
69
|
type _DecoratorMetadata<T extends Metadata = Metadata> = DecoratorMetadata & {
|
60
|
-
struct?:
|
70
|
+
struct?: T;
|
71
|
+
structInit?: MemberInit[];
|
61
72
|
};
|
62
73
|
|
63
74
|
export interface DecoratorContext<T extends Metadata = Metadata> {
|
64
75
|
metadata: _DecoratorMetadata<T>;
|
65
76
|
}
|
66
77
|
|
78
|
+
/**
|
79
|
+
* Initializes the struct metadata for a class
|
80
|
+
* This also handles copying metadata from parent classes
|
81
|
+
*/
|
82
|
+
export function initMetadata(context: DecoratorContext): MemberInit[] {
|
83
|
+
context.metadata ??= {};
|
84
|
+
|
85
|
+
context.metadata.structInit = [...(context.metadata.structInit ?? [])];
|
86
|
+
|
87
|
+
return context.metadata.structInit;
|
88
|
+
}
|
89
|
+
|
67
90
|
export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
|
68
91
|
|
69
92
|
export interface Static<T extends Metadata = Metadata> {
|
@@ -84,18 +107,13 @@ export function isValidMetadata<T extends Metadata = Metadata>(
|
|
84
107
|
return arg != null && typeof arg == 'object' && 'struct' in arg;
|
85
108
|
}
|
86
109
|
|
87
|
-
/**
|
88
|
-
* Polyfill Symbol.metadata
|
89
|
-
* @see https://github.com/microsoft/TypeScript/issues/53461
|
90
|
-
*/
|
91
|
-
(Symbol as { metadata: symbol }).metadata ??= Symbol.for('Symbol.metadata');
|
92
|
-
|
93
110
|
/**
|
94
111
|
* Polyfill context.metadata
|
95
112
|
* @see https://github.com/microsoft/TypeScript/issues/53461
|
113
|
+
* @internal @hidden
|
96
114
|
*/
|
97
|
-
export function
|
98
|
-
if (
|
115
|
+
export function _polyfill_metadata(target: object): void {
|
116
|
+
if (Symbol.metadata in target) return;
|
99
117
|
|
100
118
|
Object.defineProperty(target, Symbol.metadata, {
|
101
119
|
enumerable: true,
|
@@ -105,26 +123,8 @@ export function _polyfill_contextMetadata(target: object): void {
|
|
105
123
|
});
|
106
124
|
}
|
107
125
|
|
108
|
-
/**
|
109
|
-
* Gets a reference to Symbol.metadata, even on platforms that do not expose it globally (like Node)
|
110
|
-
*/
|
111
|
-
export function symbol_metadata(arg: ClassLike): typeof Symbol.metadata {
|
112
|
-
const symbol_metadata =
|
113
|
-
Symbol.metadata || Object.getOwnPropertySymbols(arg).find(s => s.description == 'Symbol.metadata');
|
114
|
-
_polyfill_contextMetadata(arg);
|
115
|
-
if (!symbol_metadata) {
|
116
|
-
throw new ReferenceError('Could not get a reference to Symbol.metadata');
|
117
|
-
}
|
118
|
-
|
119
|
-
return symbol_metadata as typeof Symbol.metadata;
|
120
|
-
}
|
121
|
-
|
122
126
|
export function isStatic<T extends Metadata = Metadata>(arg: unknown): arg is Static<T> {
|
123
|
-
return (
|
124
|
-
typeof arg == 'function'
|
125
|
-
&& symbol_metadata(arg as ClassLike) in arg
|
126
|
-
&& isValidMetadata(arg[symbol_metadata(arg as ClassLike)])
|
127
|
-
);
|
127
|
+
return typeof arg == 'function' && Symbol.metadata in arg && isValidMetadata(arg[Symbol.metadata]);
|
128
128
|
}
|
129
129
|
|
130
130
|
export interface Instance<T extends Metadata = Metadata> {
|
@@ -176,8 +176,10 @@ export function isCustom(arg: unknown): arg is Custom {
|
|
176
176
|
|
177
177
|
export type Like<T extends Metadata = Metadata> = InstanceLike<T> | StaticLike<T>;
|
178
178
|
|
179
|
-
export type Size<T extends TypeLike> = T extends
|
180
|
-
?
|
181
|
-
: T extends
|
182
|
-
?
|
183
|
-
:
|
179
|
+
export type Size<T extends TypeLike> = T extends undefined | null
|
180
|
+
? 0
|
181
|
+
: T extends { readonly [Symbol.size]: infer S extends number }
|
182
|
+
? S
|
183
|
+
: T extends primitive.Valid
|
184
|
+
? primitive.Size<T>
|
185
|
+
: number;
|
package/src/struct.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { toUint8Array } from './buffer.js';
|
2
|
+
import { _debugLog } from './debugging.js';
|
2
3
|
import * as primitive from './internal/primitives.js';
|
3
4
|
import type {
|
4
5
|
DecoratorContext,
|
@@ -10,11 +11,18 @@ import type {
|
|
10
11
|
Metadata,
|
11
12
|
Options,
|
12
13
|
Size,
|
13
|
-
Static,
|
14
14
|
StaticLike,
|
15
15
|
TypeLike,
|
16
16
|
} from './internal/struct.js';
|
17
|
-
import {
|
17
|
+
import {
|
18
|
+
_polyfill_metadata,
|
19
|
+
checkInstance,
|
20
|
+
checkStruct,
|
21
|
+
initMetadata,
|
22
|
+
isCustom,
|
23
|
+
isInstance,
|
24
|
+
isStatic,
|
25
|
+
} from './internal/struct.js';
|
18
26
|
import { _throw } from './misc.js';
|
19
27
|
import { capitalize } from './string.js';
|
20
28
|
import type { ClassLike } from './types.js';
|
@@ -23,7 +31,19 @@ export * as Struct from './internal/struct.js';
|
|
23
31
|
/**
|
24
32
|
* Gets the size in bytes of a type
|
25
33
|
*/
|
26
|
-
export function sizeof<T extends TypeLike>(type: T): Size<T> {
|
34
|
+
export function sizeof<T extends TypeLike>(type: T | T[]): Size<T> {
|
35
|
+
if (type === undefined || type === null) return 0 as Size<T>;
|
36
|
+
|
37
|
+
if (Array.isArray(type)) {
|
38
|
+
let size = 0;
|
39
|
+
|
40
|
+
for (let i = 0; i < type.length; i++) {
|
41
|
+
size += sizeof(type[i]);
|
42
|
+
}
|
43
|
+
|
44
|
+
return size as Size<T>;
|
45
|
+
}
|
46
|
+
|
27
47
|
// primitive
|
28
48
|
if (typeof type == 'string') {
|
29
49
|
primitive.checkValid(type);
|
@@ -35,9 +55,22 @@ export function sizeof<T extends TypeLike>(type: T): Size<T> {
|
|
35
55
|
|
36
56
|
checkStruct(type);
|
37
57
|
|
38
|
-
const
|
58
|
+
const constructor = isStatic(type) ? type : type.constructor;
|
59
|
+
_polyfill_metadata(constructor);
|
60
|
+
const { struct } = constructor[Symbol.metadata];
|
61
|
+
|
62
|
+
if (isStatic(type)) return struct.staticSize as Size<T>;
|
39
63
|
|
40
|
-
|
64
|
+
let size = struct.staticSize;
|
65
|
+
|
66
|
+
for (const member of struct.members.values()) {
|
67
|
+
if (typeof member.length != 'string') continue;
|
68
|
+
for (let i = 0; i < (type as any)[member.length]; i++) {
|
69
|
+
size += sizeof((type as any)[member.name][i]);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
return size as Size<T>;
|
41
74
|
}
|
42
75
|
|
43
76
|
/**
|
@@ -46,12 +79,26 @@ export function sizeof<T extends TypeLike>(type: T): Size<T> {
|
|
46
79
|
export function offsetof(type: StaticLike | InstanceLike, memberName: string): number {
|
47
80
|
checkStruct(type);
|
48
81
|
|
49
|
-
const
|
50
|
-
|
82
|
+
const constructor = isStatic(type) ? type : type.constructor;
|
83
|
+
|
84
|
+
_polyfill_metadata(constructor);
|
85
|
+
const { struct } = constructor[Symbol.metadata];
|
86
|
+
|
87
|
+
if (isStatic(type) || !struct.isDynamic) {
|
88
|
+
return (
|
89
|
+
struct.members.get(memberName)?.staticOffset
|
90
|
+
?? _throw(new Error('Struct does not have member: ' + memberName))
|
91
|
+
);
|
92
|
+
}
|
93
|
+
|
94
|
+
let offset = 0;
|
95
|
+
|
96
|
+
for (const member of struct.members.values()) {
|
97
|
+
if (member.name == memberName) return offset;
|
98
|
+
offset += sizeof((type as any)[member.name]);
|
99
|
+
}
|
51
100
|
|
52
|
-
|
53
|
-
if (!member) throw new Error('Struct does not have member: ' + memberName);
|
54
|
-
return member.offset;
|
101
|
+
throw new Error('Struct does not have member: ' + memberName);
|
55
102
|
}
|
56
103
|
|
57
104
|
/**
|
@@ -61,45 +108,6 @@ export function align(value: number, alignment: number): number {
|
|
61
108
|
return Math.ceil(value / alignment) * alignment;
|
62
109
|
}
|
63
110
|
|
64
|
-
/**
|
65
|
-
* Gets the length of an array in a struct
|
66
|
-
* @param length The numeric length or the name of the field which has the array length (like __counted_by)
|
67
|
-
* @param name The name of the array field— only used for errors
|
68
|
-
*/
|
69
|
-
function _memberLength<T extends Metadata>(
|
70
|
-
struct: Instance<T>,
|
71
|
-
length: string | number | undefined,
|
72
|
-
name: string
|
73
|
-
): number {
|
74
|
-
if (length === undefined) return -1;
|
75
|
-
if (typeof length != 'string')
|
76
|
-
return Number.isSafeInteger(length) && length >= 0
|
77
|
-
? length
|
78
|
-
: _throw(new Error('Array lengths must be natural numbers'));
|
79
|
-
|
80
|
-
if (!(length in struct)) throw new Error(`Can not use non-existent member to count ${name}: ` + length);
|
81
|
-
|
82
|
-
const n = (struct as any)[length];
|
83
|
-
|
84
|
-
if (typeof n != 'number') throw new Error(`Can not use "${name}" to count ${length}`);
|
85
|
-
|
86
|
-
return n;
|
87
|
-
}
|
88
|
-
|
89
|
-
/** Compute the size of a struct including dynamically sized members */
|
90
|
-
function _structSize<T extends Metadata>(this: Instance<T>) {
|
91
|
-
const { staticSize, members } = this.constructor[symbol_metadata(this.constructor)].struct;
|
92
|
-
|
93
|
-
let size = staticSize;
|
94
|
-
|
95
|
-
for (const [name, { type, length: key }] of members) {
|
96
|
-
if (typeof key != 'string') continue;
|
97
|
-
size += sizeof(type) * _memberLength(this, key, name);
|
98
|
-
}
|
99
|
-
|
100
|
-
return size;
|
101
|
-
}
|
102
|
-
|
103
111
|
/**
|
104
112
|
* Decorates a class as a struct
|
105
113
|
*/
|
@@ -108,33 +116,44 @@ export function struct(options: Partial<Options> = {}) {
|
|
108
116
|
target: T,
|
109
117
|
context: ClassDecoratorContext & DecoratorContext
|
110
118
|
): T {
|
111
|
-
context.metadata ??= {};
|
112
|
-
context.metadata.struct ??= {};
|
113
|
-
context.metadata.struct.init ??= [];
|
114
|
-
|
115
|
-
let staticSize = 0;
|
116
119
|
const members = new Map<string, Member>();
|
117
|
-
|
120
|
+
|
121
|
+
let staticSize = 0,
|
122
|
+
isDynamic = false;
|
123
|
+
|
124
|
+
for (const { name, type, length } of initMetadata(context)) {
|
118
125
|
if (!primitive.isValid(type) && !isStatic(type)) throw new TypeError('Not a valid type: ' + type);
|
119
126
|
|
127
|
+
if (typeof length == 'string') {
|
128
|
+
const countedBy = members.get(length);
|
129
|
+
|
130
|
+
if (!countedBy) throw new Error(`"${length}" is undefined or declared after "${name}"`);
|
131
|
+
|
132
|
+
if (!primitive.isType(countedBy.type))
|
133
|
+
throw new Error(`"${length}" is not a number and cannot be used to count "${name}"`);
|
134
|
+
}
|
135
|
+
|
136
|
+
let decl = `${typeof type == 'string' ? type : type.name} ${name}`;
|
137
|
+
|
138
|
+
if (length !== undefined) decl += `[${length}]`;
|
139
|
+
|
120
140
|
members.set(name, {
|
121
|
-
|
141
|
+
name,
|
142
|
+
staticOffset: options.isUnion ? 0 : staticSize,
|
122
143
|
type: primitive.isValid(type) ? primitive.normalize(type) : type,
|
123
144
|
length,
|
145
|
+
decl,
|
124
146
|
});
|
147
|
+
|
125
148
|
const memberSize = typeof length == 'string' ? 0 : sizeof(type) * (length || 1);
|
149
|
+
isDynamic ||= typeof length == 'string';
|
126
150
|
staticSize = options.isUnion ? Math.max(staticSize, memberSize) : staticSize + memberSize;
|
127
151
|
staticSize = align(staticSize, options.align || 1);
|
128
|
-
}
|
129
152
|
|
130
|
-
|
153
|
+
_debugLog('define', target.name + '.' + name);
|
154
|
+
}
|
131
155
|
|
132
|
-
context.
|
133
|
-
Object.defineProperty(this.prototype, Symbol.size, {
|
134
|
-
get: _structSize.bind(this),
|
135
|
-
enumerable: false,
|
136
|
-
});
|
137
|
-
});
|
156
|
+
context.metadata.struct = { options, members, staticSize, isDynamic } satisfies Metadata;
|
138
157
|
|
139
158
|
return target;
|
140
159
|
};
|
@@ -153,14 +172,20 @@ export function member(type: primitive.Valid | ClassLike, length?: number | stri
|
|
153
172
|
|
154
173
|
if (!name) throw new ReferenceError('Invalid name for struct member');
|
155
174
|
|
156
|
-
context.
|
157
|
-
context.metadata.struct ??= {};
|
158
|
-
context.metadata.struct.init ??= [];
|
159
|
-
context.metadata.struct.init.push({ name, type, length } satisfies MemberInit);
|
175
|
+
initMetadata(context).push({ name, type, length } satisfies MemberInit);
|
160
176
|
return value;
|
161
177
|
};
|
162
178
|
}
|
163
179
|
|
180
|
+
/** Gets the length of a member */
|
181
|
+
function _memberLength<T extends Metadata>(instance: Instance<T>, member: Member): number {
|
182
|
+
if (member.length === undefined) return -1;
|
183
|
+
if (typeof member.length == 'string') return (instance as any)[member.length];
|
184
|
+
return Number.isSafeInteger(member.length) && member.length >= 0
|
185
|
+
? member.length
|
186
|
+
: _throw(new Error('Array lengths must be natural numbers'));
|
187
|
+
}
|
188
|
+
|
164
189
|
/**
|
165
190
|
* Serializes a struct into a Uint8Array
|
166
191
|
*/
|
@@ -168,58 +193,69 @@ export function serialize(instance: unknown): Uint8Array {
|
|
168
193
|
if (isCustom(instance) && typeof instance[Symbol.serialize] == 'function') return instance[Symbol.serialize]!();
|
169
194
|
|
170
195
|
checkInstance(instance);
|
171
|
-
|
196
|
+
_polyfill_metadata(instance.constructor);
|
197
|
+
const { options, members } = instance.constructor[Symbol.metadata].struct;
|
172
198
|
|
173
|
-
const
|
199
|
+
const size = sizeof(instance);
|
200
|
+
const buffer = new Uint8Array(size);
|
174
201
|
const view = new DataView(buffer.buffer);
|
175
202
|
|
203
|
+
_debugLog('serialize', instance.constructor.name);
|
204
|
+
|
205
|
+
let offset = 0,
|
206
|
+
nextOffset = 0;
|
207
|
+
|
176
208
|
// for unions we should write members in ascending last modified order, but we don't have that info.
|
177
|
-
for (const
|
178
|
-
const length = _memberLength(instance,
|
179
|
-
|
180
|
-
|
209
|
+
for (const member of members.values()) {
|
210
|
+
const length = _memberLength(instance, member);
|
211
|
+
|
212
|
+
_debugLog('\t', member.decl);
|
181
213
|
|
182
|
-
|
214
|
+
for (let i = 0; i < Math.abs(length); i++) {
|
215
|
+
let value = length != -1 ? instance[member.name][i] : instance[member.name];
|
183
216
|
if (typeof value == 'string') {
|
184
217
|
value = value.charCodeAt(0);
|
185
218
|
}
|
186
219
|
|
187
|
-
|
188
|
-
|
220
|
+
offset = nextOffset;
|
221
|
+
nextOffset += isInstance(value) ? sizeof(value) : sizeof(member.type);
|
222
|
+
|
223
|
+
if (!primitive.isType(member.type)) {
|
224
|
+
buffer.set(value ? serialize(value) : new Uint8Array(sizeof(member.type)), offset);
|
189
225
|
continue;
|
190
226
|
}
|
191
227
|
|
192
|
-
const fn = `set${capitalize(type)}` as const;
|
228
|
+
const fn = `set${capitalize(member.type)}` as const;
|
193
229
|
|
194
230
|
if (fn == 'setInt64') {
|
195
|
-
view.setBigInt64(
|
231
|
+
view.setBigInt64(offset, BigInt(value), !options.bigEndian);
|
196
232
|
continue;
|
197
233
|
}
|
198
234
|
|
199
235
|
if (fn == 'setUint64') {
|
200
|
-
view.setBigUint64(
|
236
|
+
view.setBigUint64(offset, BigInt(value), !options.bigEndian);
|
201
237
|
continue;
|
202
238
|
}
|
203
239
|
|
204
240
|
if (fn == 'setInt128') {
|
205
|
-
view.setBigUint64(
|
206
|
-
view.setBigInt64(
|
241
|
+
view.setBigUint64(offset + (!options.bigEndian ? 0 : 8), value & primitive.mask64, !options.bigEndian);
|
242
|
+
view.setBigInt64(offset + (!options.bigEndian ? 8 : 0), value >> BigInt(64), !options.bigEndian);
|
207
243
|
continue;
|
208
244
|
}
|
209
245
|
|
210
246
|
if (fn == 'setUint128') {
|
211
|
-
view.setBigUint64(
|
212
|
-
view.setBigUint64(
|
247
|
+
view.setBigUint64(offset + (!options.bigEndian ? 0 : 8), value & primitive.mask64, !options.bigEndian);
|
248
|
+
view.setBigUint64(offset + (!options.bigEndian ? 8 : 0), value >> BigInt(64), !options.bigEndian);
|
213
249
|
continue;
|
214
250
|
}
|
215
251
|
|
216
252
|
if (fn == 'setFloat128') {
|
217
|
-
view.setFloat64(
|
218
|
-
view.setBigUint64(
|
253
|
+
view.setFloat64(offset + (!options.bigEndian ? 0 : 8), Number(value), !options.bigEndian);
|
254
|
+
view.setBigUint64(offset + (!options.bigEndian ? 8 : 0), BigInt(0), !options.bigEndian);
|
219
255
|
continue;
|
220
256
|
}
|
221
257
|
|
222
|
-
view[fn](
|
258
|
+
view[fn](offset, Number(value), !options.bigEndian);
|
223
259
|
}
|
224
260
|
}
|
225
261
|
|
@@ -236,64 +272,77 @@ export function deserialize(instance: unknown, _buffer: ArrayBufferLike | ArrayB
|
|
236
272
|
return instance[Symbol.deserialize]!(buffer);
|
237
273
|
|
238
274
|
checkInstance(instance);
|
239
|
-
|
275
|
+
_polyfill_metadata(instance.constructor);
|
276
|
+
const { options, members } = instance.constructor[Symbol.metadata].struct;
|
240
277
|
|
241
278
|
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
242
279
|
|
243
|
-
|
244
|
-
|
280
|
+
_debugLog('deserialize', instance.constructor.name);
|
281
|
+
|
282
|
+
let offset = 0,
|
283
|
+
nextOffset = 0;
|
284
|
+
|
285
|
+
for (const member of members.values()) {
|
286
|
+
const length = _memberLength(instance, member);
|
287
|
+
|
288
|
+
_debugLog('\t', member.decl);
|
289
|
+
|
245
290
|
for (let i = 0; i < Math.abs(length); i++) {
|
246
|
-
let object = length != -1 ? instance[name] : instance;
|
247
|
-
const key = length != -1 ? i : name
|
248
|
-
|
291
|
+
let object = length != -1 ? instance[member.name] : instance;
|
292
|
+
const key = length != -1 ? i : member.name;
|
293
|
+
|
294
|
+
offset = nextOffset;
|
295
|
+
if (!isInstance(object[key])) nextOffset += sizeof(member.type);
|
249
296
|
|
250
|
-
if (typeof instance[name] == 'string') {
|
251
|
-
instance[name] =
|
252
|
-
instance[name].slice(0, i)
|
297
|
+
if (typeof instance[member.name] == 'string') {
|
298
|
+
instance[member.name] =
|
299
|
+
instance[member.name].slice(0, i)
|
300
|
+
+ String.fromCharCode(view.getUint8(offset))
|
301
|
+
+ instance[member.name].slice(i + 1);
|
253
302
|
continue;
|
254
303
|
}
|
255
304
|
|
256
|
-
if (!primitive.isType(type)) {
|
257
|
-
if (object[key] === null || object[key] === undefined)
|
258
|
-
|
259
|
-
|
260
|
-
|
305
|
+
if (!primitive.isType(member.type)) {
|
306
|
+
if (object[key] === null || object[key] === undefined) continue;
|
307
|
+
|
308
|
+
deserialize(object[key], new Uint8Array(buffer.subarray(offset)));
|
309
|
+
nextOffset += sizeof(object[key]);
|
261
310
|
continue;
|
262
311
|
}
|
263
312
|
|
264
313
|
if (length && length != -1) object ||= [];
|
265
314
|
|
266
|
-
const fn = `get${capitalize(type)}` as const;
|
315
|
+
const fn = `get${capitalize(member.type)}` as const;
|
267
316
|
if (fn == 'getInt64') {
|
268
|
-
object[key] = view.getBigInt64(
|
317
|
+
object[key] = view.getBigInt64(offset, !options.bigEndian);
|
269
318
|
continue;
|
270
319
|
}
|
271
320
|
|
272
321
|
if (fn == 'getUint64') {
|
273
|
-
object[key] = view.getBigUint64(
|
322
|
+
object[key] = view.getBigUint64(offset, !options.bigEndian);
|
274
323
|
continue;
|
275
324
|
}
|
276
325
|
|
277
326
|
if (fn == 'getInt128') {
|
278
327
|
object[key] =
|
279
|
-
(view.getBigInt64(
|
280
|
-
| view.getBigUint64(
|
328
|
+
(view.getBigInt64(offset + (!options.bigEndian ? 8 : 0), !options.bigEndian) << BigInt(64))
|
329
|
+
| view.getBigUint64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
|
281
330
|
continue;
|
282
331
|
}
|
283
332
|
|
284
333
|
if (fn == 'getUint128') {
|
285
334
|
object[key] =
|
286
|
-
(view.getBigUint64(
|
287
|
-
| view.getBigUint64(
|
335
|
+
(view.getBigUint64(offset + (!options.bigEndian ? 8 : 0), !options.bigEndian) << BigInt(64))
|
336
|
+
| view.getBigUint64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
|
288
337
|
continue;
|
289
338
|
}
|
290
339
|
|
291
340
|
if (fn == 'getFloat128') {
|
292
|
-
object[key] = view.getFloat64(
|
341
|
+
object[key] = view.getFloat64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
|
293
342
|
continue;
|
294
343
|
}
|
295
344
|
|
296
|
-
object[key] = view[fn](
|
345
|
+
object[key] = view[fn](offset, !options.bigEndian);
|
297
346
|
}
|
298
347
|
}
|
299
348
|
}
|