utilium 1.10.0 → 2.0.0-pre.1

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.
@@ -1,38 +1,26 @@
1
1
  import type { ClassLike } from '../types.js';
2
2
  import type * as primitive from './primitives.js';
3
- declare global {
4
- interface SymbolConstructor {
5
- readonly size: unique symbol;
6
- readonly serialize: unique symbol;
7
- readonly deserialize: unique symbol;
8
- }
9
- }
10
- export type TypeLike = Custom | Like | primitive.Valid | undefined | null;
11
- export type Type = Custom | Static | primitive.Typename;
12
- /**
13
- * Member initialization data
14
- * This is needed since class decorators are called *after* member decorators
15
- */
16
- export interface MemberInit {
17
- name: string;
18
- type: string | ClassLike;
19
- length?: number | string;
20
- }
3
+ export type TypeLike = primitive.Type | Like | primitive.Valid | undefined | null;
4
+ export type Type = Static | primitive.Type;
21
5
  /**
22
6
  * Options for struct initialization
23
7
  */
24
8
  export interface Options {
9
+ packed: boolean;
25
10
  align: number;
26
- bigEndian: boolean;
27
11
  isUnion: boolean;
28
12
  }
29
13
  export interface Member {
30
14
  name: string;
31
15
  type: Type;
32
- staticOffset: number;
16
+ offset: number;
17
+ /** The size of the member, 0 for dynamically sized arrays */
18
+ size: number;
33
19
  length?: number | string;
34
20
  /** A C-style type/name declaration string, used for diagnostics */
35
21
  decl: string;
22
+ /** Whether the member is little endian */
23
+ littleEndian: boolean;
36
24
  }
37
25
  export interface Metadata {
38
26
  options: Partial<Options>;
@@ -43,9 +31,15 @@ export interface Metadata {
43
31
  /** Whether the struct is a union */
44
32
  isUnion: boolean;
45
33
  }
34
+ export interface Init {
35
+ members: Member[];
36
+ size: number;
37
+ isDynamic: boolean;
38
+ isUnion: boolean;
39
+ }
46
40
  type _DecoratorMetadata<T extends Metadata = Metadata> = DecoratorMetadata & {
47
41
  struct?: T;
48
- structInit?: MemberInit[];
42
+ structInit?: Init;
49
43
  };
50
44
  export interface DecoratorContext<T extends Metadata = Metadata> {
51
45
  metadata: _DecoratorMetadata<T>;
@@ -54,30 +48,21 @@ export interface DecoratorContext<T extends Metadata = Metadata> {
54
48
  * Initializes the struct metadata for a class
55
49
  * This also handles copying metadata from parent classes
56
50
  */
57
- export declare function initMetadata(context: DecoratorContext): MemberInit[];
58
- export type MemberContext = ClassMemberDecoratorContext & DecoratorContext;
51
+ export declare function initMetadata(context: DecoratorContext): Init;
59
52
  export interface Static<T extends Metadata = Metadata> {
60
- [Symbol.metadata]: {
61
- struct: T;
62
- };
63
- new (): Instance<T>;
64
- prototype: Instance<T>;
53
+ [Symbol.metadata]: Required<_DecoratorMetadata<T>>;
54
+ readonly prototype: Instance<T>;
55
+ new <TArrayBuffer extends ArrayBufferLike = ArrayBuffer>(buffer: TArrayBuffer, byteOffset?: number, length?: number): Instance<T> & ArrayBufferView<TArrayBuffer>;
56
+ new (array?: ArrayLike<number> | ArrayBuffer): Instance<T>;
65
57
  }
66
58
  export interface StaticLike<T extends Metadata = Metadata> extends ClassLike {
67
59
  [Symbol.metadata]?: _DecoratorMetadata<T> | null;
68
- new (): unknown;
69
60
  }
70
61
  export declare function isValidMetadata<T extends Metadata = Metadata>(arg: unknown): arg is DecoratorMetadata & {
71
62
  struct: T;
72
63
  };
73
- /**
74
- * Polyfill context.metadata
75
- * @see https://github.com/microsoft/TypeScript/issues/53461
76
- * @internal @hidden
77
- */
78
- export declare function _polyfill_metadata(target: object): void;
79
64
  export declare function isStatic<T extends Metadata = Metadata>(arg: unknown): arg is Static<T>;
80
- export interface Instance<T extends Metadata = Metadata> {
65
+ export interface Instance<T extends Metadata = Metadata> extends ArrayBufferView, Record<PropertyKey, any> {
81
66
  constructor: Static<T>;
82
67
  }
83
68
  export interface InstanceLike<T extends Metadata = Metadata> {
@@ -87,17 +72,6 @@ export declare function isInstance<T extends Metadata = Metadata>(arg: unknown):
87
72
  export declare function checkInstance<T extends Metadata = Metadata>(arg: unknown): asserts arg is Instance<T> & Record<keyof any, any>;
88
73
  export declare function isStruct<T extends Metadata = Metadata>(arg: unknown): arg is Instance<T> | Static<T>;
89
74
  export declare function checkStruct<T extends Metadata = Metadata>(arg: unknown): asserts arg is Instance<T> | Static<T>;
90
- /**
91
- * A "custom" type, which can be used to implement non-builtin size, serialization, and deserialization
92
- */
93
- export interface Custom {
94
- readonly [Symbol.size]: number;
95
- [Symbol.serialize]?(): Uint8Array;
96
- [Symbol.deserialize]?(value: Uint8Array): void;
97
- }
98
- export declare function isCustom(arg: unknown): arg is Custom;
99
75
  export type Like<T extends Metadata = Metadata> = InstanceLike<T> | StaticLike<T>;
100
- export type Size<T extends TypeLike> = T extends undefined | null ? 0 : T extends {
101
- readonly [Symbol.size]: infer S extends number;
102
- } ? S : T extends primitive.Valid ? primitive.Size<T> : number;
76
+ export type Size<T extends TypeLike> = T extends undefined | null ? 0 : T extends primitive.Valid ? primitive.Size<T> : number;
103
77
  export {};
@@ -3,38 +3,24 @@
3
3
  * @see https://github.com/microsoft/TypeScript/issues/53461
4
4
  */
5
5
  Symbol.metadata ??= Symbol.for('Symbol.metadata');
6
- Object.assign(Symbol, {
7
- size: Symbol('uSize'),
8
- serialize: Symbol('uSerialize'),
9
- deserialize: Symbol('uDeserialize'),
10
- });
11
6
  /**
12
7
  * Initializes the struct metadata for a class
13
8
  * This also handles copying metadata from parent classes
14
9
  */
15
10
  export function initMetadata(context) {
16
11
  context.metadata ??= {};
17
- context.metadata.structInit = [...(context.metadata.structInit ?? [])];
12
+ const existing = context.metadata.structInit ?? {};
13
+ context.metadata.structInit = {
14
+ members: [...(existing.members ?? [])],
15
+ size: existing.size ?? 0,
16
+ isDynamic: existing.isDynamic ?? false,
17
+ isUnion: existing.isUnion ?? false,
18
+ };
18
19
  return context.metadata.structInit;
19
20
  }
20
21
  export function isValidMetadata(arg) {
21
22
  return arg != null && typeof arg == 'object' && 'struct' in arg;
22
23
  }
23
- /**
24
- * Polyfill context.metadata
25
- * @see https://github.com/microsoft/TypeScript/issues/53461
26
- * @internal @hidden
27
- */
28
- export function _polyfill_metadata(target) {
29
- if (Symbol.metadata in target)
30
- return;
31
- Object.defineProperty(target, Symbol.metadata, {
32
- enumerable: true,
33
- configurable: true,
34
- writable: true,
35
- value: Object.create(null),
36
- });
37
- }
38
24
  export function isStatic(arg) {
39
25
  return typeof arg == 'function' && Symbol.metadata in arg && isValidMetadata(arg[Symbol.metadata]);
40
26
  }
@@ -56,6 +42,3 @@ export function checkStruct(arg) {
56
42
  throw new TypeError((typeof arg == 'function' ? arg.name : typeof arg == 'object' && arg ? arg.constructor.name : arg)
57
43
  + ' is not a struct');
58
44
  }
59
- export function isCustom(arg) {
60
- return typeof arg == 'object' && arg != null && Symbol.size in arg;
61
- }
package/dist/objects.d.ts CHANGED
@@ -48,3 +48,9 @@ export type Bound<T extends object, This = any> = T & {
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
+ /**
52
+ * Makes all properties in T mutable
53
+ */
54
+ export type Mutable<T> = {
55
+ -readonly [P in keyof T]: T[P];
56
+ };
package/dist/string.js CHANGED
@@ -16,7 +16,13 @@ const decoder = new TextDecoder();
16
16
  * Decodes a UTF-8 string from a buffer
17
17
  */
18
18
  export function decodeUTF8(input) {
19
- return decoder.decode(input);
19
+ if (!input)
20
+ return '';
21
+ if (input.buffer instanceof ArrayBuffer && !input.buffer.resizable)
22
+ return decoder.decode(input);
23
+ const buffer = new Uint8Array(input.byteLength);
24
+ buffer.set(input);
25
+ return decoder.decode(buffer);
20
26
  }
21
27
  export function encodeASCII(input) {
22
28
  const data = new Uint8Array(input.length);
package/dist/struct.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import * as primitive from './internal/primitives.js';
2
- import type { DecoratorContext, InstanceLike, MemberContext, Options, Size, StaticLike, TypeLike } from './internal/struct.js';
3
- import type { ClassLike } from './types.js';
2
+ import type { DecoratorContext, InstanceLike, Options, Size, StaticLike, TypeLike } from './internal/struct.js';
4
3
  export * as Struct from './internal/struct.js';
5
4
  /**
6
5
  * Gets the size in bytes of a type
@@ -10,29 +9,29 @@ export declare function sizeof<T extends TypeLike>(type: T | T[]): Size<T>;
10
9
  * Returns the offset (in bytes) of a member in a struct.
11
10
  */
12
11
  export declare function offsetof(type: StaticLike | InstanceLike, memberName: string): number;
13
- /**
14
- * Aligns a number
15
- */
12
+ /** Aligns a number */
16
13
  export declare function align(value: number, alignment: number): number;
17
14
  /**
18
15
  * Decorates a class as a struct
19
16
  */
20
- export declare function struct(options?: Partial<Options>): <const T extends StaticLike>(target: T, context: ClassDecoratorContext & DecoratorContext) => T;
17
+ export declare function struct(options?: Partial<Options>): <T extends StaticLike>(target: T, context: ClassDecoratorContext<T> & DecoratorContext) => void;
18
+ export interface MemberOptions {
19
+ bigEndian?: boolean;
20
+ length?: number | string;
21
+ align?: number;
22
+ typeName?: string;
23
+ }
21
24
  /**
22
25
  * Decorates a class member to be serialized
23
26
  */
24
- export declare function member(type: primitive.Valid | ClassLike, length?: number | string): <V>(value: V, context: MemberContext) => V;
25
- /**
26
- * Serializes a struct into a Uint8Array
27
- */
28
- export declare function serialize(instance: unknown): Uint8Array;
29
- /**
30
- * Deserializes a struct from a Uint8Array
31
- */
32
- export declare function deserialize(instance: unknown, _buffer: ArrayBufferLike | ArrayBufferView): void;
33
- declare function _member<T extends primitive.Valid>(type: T): {
34
- <const V>(length: number | string): (value: V, context: MemberContext) => V;
35
- <const V>(value: V, context: MemberContext): V;
27
+ export declare function member<V>(type: primitive.Type | StaticLike, opt?: MemberOptions): (value: Target<V>, context: Context<V>) => Result<V>;
28
+ type Target<V> = ClassAccessorDecoratorTarget<any, V>;
29
+ type Result<V> = ClassAccessorDecoratorResult<any, V>;
30
+ type Context<V> = ClassAccessorDecoratorContext<any, V> & DecoratorContext;
31
+ type Decorator<V> = (value: Target<V>, context: Context<V>) => Result<V>;
32
+ declare function _member<T extends primitive.Valid>(typeName: T): {
33
+ <V>(length: number | string): Decorator<V>;
34
+ <V>(value: Target<V>, context: Context<V>): Result<V>;
36
35
  };
37
36
  /**
38
37
  * Shortcut types
package/dist/struct.js CHANGED
@@ -1,9 +1,9 @@
1
- import { toUint8Array } from './buffer.js';
1
+ import { BufferViewArray } from './buffer.js';
2
2
  import { _debugLog } from './debugging.js';
3
3
  import * as primitive from './internal/primitives.js';
4
- import { _polyfill_metadata, checkInstance, checkStruct, initMetadata, isCustom, isInstance, isStatic, isStruct, } from './internal/struct.js';
4
+ import { checkStruct, initMetadata, isInstance, isStatic, isStruct } from './internal/struct.js';
5
5
  import { _throw } from './misc.js';
6
- import { capitalize } from './string.js';
6
+ import { getAllPrototypes } from './objects.js';
7
7
  export * as Struct from './internal/struct.js';
8
8
  /**
9
9
  * Gets the size in bytes of a type
@@ -21,38 +21,28 @@ export function sizeof(type) {
21
21
  // primitive or character
22
22
  if (typeof type == 'string') {
23
23
  primitive.checkValid(type);
24
- return (+primitive.normalize(type).match(primitive.regex)[2] / 8);
24
+ return primitive.types[primitive.normalize(type)].size;
25
25
  }
26
- if (isCustom(type))
27
- return type[Symbol.size];
26
+ if (primitive.isType(type))
27
+ return type.size;
28
28
  checkStruct(type);
29
29
  const constructor = isStatic(type) ? type : type.constructor;
30
- _polyfill_metadata(constructor);
31
- const { struct } = constructor[Symbol.metadata];
32
- let size = struct.staticSize;
33
- if (isStatic(type))
34
- return size;
35
- for (const member of struct.members.values()) {
36
- const value = type[member.name];
37
- if (isInstance(value) && value.constructor[Symbol.metadata].struct.isDynamic) {
38
- if (struct.isUnion)
39
- size = Math.max(size, sizeof(value));
40
- else
41
- size += sizeof(value);
42
- continue;
43
- }
44
- if (typeof member.length != 'string')
45
- continue;
46
- let subSize = 0;
47
- for (let i = 0; i < type[member.length]; i++) {
48
- subSize += sizeof(isStruct(value[i]) ? value[i] : member.type);
30
+ const { struct, structInit } = constructor[Symbol.metadata];
31
+ if (isStatic(type) || !struct.isDynamic)
32
+ return struct.staticSize;
33
+ const last = structInit.members.at(-1);
34
+ const length = type[last.length];
35
+ let dynamicSize = 0;
36
+ if (primitive.isType(last.type)) {
37
+ dynamicSize = last.type.size * length;
38
+ }
39
+ else {
40
+ const value = type[last.name];
41
+ for (let i = 0; i < length; i++) {
42
+ dynamicSize += sizeof(isStruct(value[i]) ? value[i] : last.type);
49
43
  }
50
- if (struct.isUnion)
51
- size = Math.max(size, subSize);
52
- else
53
- size += subSize;
54
44
  }
55
- return size;
45
+ return (struct.isUnion ? Math.max(struct.staticSize, dynamicSize) : struct.staticSize + dynamicSize);
56
46
  }
57
47
  /**
58
48
  * Returns the offset (in bytes) of a member in a struct.
@@ -60,24 +50,12 @@ export function sizeof(type) {
60
50
  export function offsetof(type, memberName) {
61
51
  checkStruct(type);
62
52
  const constructor = isStatic(type) ? type : type.constructor;
63
- _polyfill_metadata(constructor);
64
- const { struct } = constructor[Symbol.metadata];
65
- if (isStatic(type) || !struct.isDynamic) {
66
- return (struct.members.get(memberName)?.staticOffset
67
- ?? _throw(new Error('Struct does not have member: ' + memberName)));
68
- }
69
- let offset = 0;
70
- for (const member of struct.members.values()) {
71
- if (member.name == memberName)
72
- return offset;
73
- const value = type[member.name];
74
- offset += sizeof(isStruct(value) ? value : member.type);
75
- }
76
- throw new Error('Struct does not have member: ' + memberName);
53
+ const member = constructor[Symbol.metadata].struct.members.get(memberName);
54
+ if (!member)
55
+ throw new Error('Struct does not have member: ' + memberName);
56
+ return member.offset;
77
57
  }
78
- /**
79
- * Aligns a number
80
- */
58
+ /** Aligns a number */
81
59
  export function align(value, alignment) {
82
60
  return Math.ceil(value / alignment) * alignment;
83
61
  }
@@ -86,51 +64,33 @@ export function align(value, alignment) {
86
64
  */
87
65
  export function struct(options = {}) {
88
66
  return function _decorateStruct(target, context) {
67
+ const init = initMetadata(context);
89
68
  const members = new Map();
90
- let staticSize = 0, isDynamic = false;
91
- for (const { name, type, length } of initMetadata(context)) {
92
- if (!primitive.isValid(type) && !isStatic(type))
93
- throw new TypeError('Not a valid type: ' + type);
94
- if (typeof length == 'string') {
95
- const countedBy = members.get(length);
96
- if (!countedBy)
97
- throw new Error(`"${length}" is undefined or declared after "${name}"`);
98
- if (!primitive.isType(countedBy.type))
99
- throw new Error(`"${length}" is not a number and cannot be used to count "${name}"`);
100
- }
101
- let decl = `${typeof type == 'string' ? type : type.name} ${name}`;
102
- if (length !== undefined)
103
- decl += `[${length}]`;
104
- members.set(name, {
105
- name,
106
- staticOffset: options.isUnion ? 0 : staticSize,
107
- type: primitive.isValid(type) ? primitive.normalize(type) : type,
108
- length,
109
- decl,
110
- });
111
- const memberSize = typeof length == 'string' || (isStatic(type) && type[Symbol.metadata].struct.isDynamic)
112
- ? 0
113
- : sizeof(type) * (length || 1);
114
- isDynamic ||= isStatic(type) ? type[Symbol.metadata].struct.isDynamic : typeof length == 'string';
115
- staticSize = options.isUnion ? Math.max(staticSize, memberSize) : staticSize + memberSize;
116
- staticSize = align(staticSize, options.align || 1);
117
- _debugLog('define', target.name + '.' + name);
69
+ for (const member of init.members) {
70
+ if (options.isUnion)
71
+ member.offset = 0;
72
+ _debugLog('define', target.name + '.' + member.name);
73
+ members.set(member.name, member);
118
74
  }
119
75
  context.metadata.struct = {
120
76
  options,
121
77
  members,
122
- staticSize,
123
- isDynamic,
78
+ staticSize: init.size,
79
+ isDynamic: init.isDynamic,
124
80
  isUnion: options.isUnion ?? false,
125
81
  };
126
- return target;
127
82
  };
128
83
  }
129
84
  /**
130
85
  * Decorates a class member to be serialized
131
86
  */
132
- export function member(type, length) {
133
- return function (value, context) {
87
+ export function member(type, opt = {}) {
88
+ return function __decorateMember(value, context) {
89
+ if (context.kind != 'accessor')
90
+ throw new Error('Member must be an accessor');
91
+ const init = initMetadata(context);
92
+ if (init.isDynamic)
93
+ throw new Error('Dynamic members must be declared at the end of the struct');
134
94
  let name = context.name;
135
95
  if (typeof name == 'symbol') {
136
96
  console.warn('Symbol used for struct member name will be coerced to string: ' + name.toString());
@@ -138,155 +98,100 @@ export function member(type, length) {
138
98
  }
139
99
  if (!name)
140
100
  throw new ReferenceError('Invalid name for struct member');
141
- initMetadata(context).push({ name, type, length });
142
- return value;
101
+ if (!primitive.isType(type) && !isStatic(type))
102
+ throw new TypeError('Not a valid type: ' + type.name);
103
+ if (typeof opt.length == 'string') {
104
+ const countedBy = init.members.find(m => m.name == opt.length);
105
+ if (!countedBy)
106
+ throw new Error(`"${opt.length}" is not declared and cannot be used to count "${name}"`);
107
+ if (!primitive.isType(countedBy.type))
108
+ throw new Error(`"${opt.length}" is not a number and cannot be used to count "${name}"`);
109
+ init.isDynamic = true;
110
+ }
111
+ const size = align(sizeof(type) * (typeof opt.length == 'string' ? 0 : (opt.length ?? 1)), opt.align ?? sizeof(type));
112
+ const member = {
113
+ name,
114
+ offset: init.size,
115
+ type,
116
+ length: opt.length,
117
+ size,
118
+ decl: `${opt.typeName ?? type.name} ${name}${opt.length !== undefined ? `[${JSON.stringify(opt.length)}]` : ''}`,
119
+ littleEndian: !opt.bigEndian,
120
+ };
121
+ init.members.push(member);
122
+ // Apply after setting `offset`
123
+ init.size += size;
124
+ return {
125
+ get() {
126
+ return _get(this, member);
127
+ },
128
+ set(value) {
129
+ _set(this, member, value);
130
+ },
131
+ };
143
132
  };
144
133
  }
145
134
  /** Gets the length of a member */
146
- function _memberLength(instance, member) {
147
- if (member.length === undefined)
135
+ function _memberLength(instance, length) {
136
+ if (length === undefined)
148
137
  return -1;
149
- if (typeof member.length == 'string')
150
- return instance[member.length];
151
- return Number.isSafeInteger(member.length) && member.length >= 0
152
- ? member.length
138
+ if (typeof length == 'string')
139
+ return instance[length];
140
+ return Number.isSafeInteger(length) && length >= 0
141
+ ? length
153
142
  : _throw(new Error('Array lengths must be natural numbers'));
154
143
  }
155
- /**
156
- * Serializes a struct into a Uint8Array
157
- */
158
- export function serialize(instance) {
159
- if (isCustom(instance) && typeof instance[Symbol.serialize] == 'function')
160
- return instance[Symbol.serialize]();
161
- checkInstance(instance);
162
- _polyfill_metadata(instance.constructor);
163
- const { options, members } = instance.constructor[Symbol.metadata].struct;
164
- const size = sizeof(instance);
165
- const buffer = new Uint8Array(size);
166
- const view = new DataView(buffer.buffer);
167
- _debugLog('serialize', instance.constructor.name);
168
- let offset = 0, nextOffset = 0;
169
- // for unions we should write members in ascending last modified order, but we don't have that info.
170
- for (const member of members.values()) {
171
- const length = _memberLength(instance, member);
172
- _debugLog('\t', member.decl);
173
- for (let i = 0; i < Math.abs(length); i++) {
174
- let value = length != -1 ? instance[member.name][i] : instance[member.name];
175
- if (typeof value == 'string') {
176
- value = value.charCodeAt(0);
177
- }
178
- offset = nextOffset;
179
- nextOffset += isInstance(value) ? sizeof(value) : sizeof(member.type);
180
- if (!primitive.isType(member.type)) {
181
- buffer.set(value ? serialize(value) : new Uint8Array(sizeof(member.type)), offset);
182
- continue;
183
- }
184
- const fn = `set${capitalize(member.type)}`;
185
- if (fn == 'setInt64') {
186
- view.setBigInt64(offset, BigInt(value), !options.bigEndian);
187
- continue;
188
- }
189
- if (fn == 'setUint64') {
190
- view.setBigUint64(offset, BigInt(value), !options.bigEndian);
191
- continue;
192
- }
193
- if (fn == 'setInt128') {
194
- view.setBigUint64(offset + (!options.bigEndian ? 0 : 8), value & primitive.mask64, !options.bigEndian);
195
- view.setBigInt64(offset + (!options.bigEndian ? 8 : 0), value >> BigInt(64), !options.bigEndian);
196
- continue;
197
- }
198
- if (fn == 'setUint128') {
199
- view.setBigUint64(offset + (!options.bigEndian ? 0 : 8), value & primitive.mask64, !options.bigEndian);
200
- view.setBigUint64(offset + (!options.bigEndian ? 8 : 0), value >> BigInt(64), !options.bigEndian);
201
- continue;
202
- }
203
- if (fn == 'setFloat128') {
204
- view.setFloat64(offset + (!options.bigEndian ? 0 : 8), Number(value), !options.bigEndian);
205
- view.setBigUint64(offset + (!options.bigEndian ? 8 : 0), BigInt(0), !options.bigEndian);
206
- continue;
207
- }
208
- view[fn](offset, Number(value), !options.bigEndian);
144
+ function _set(instance, member, value, index) {
145
+ const { name, type, length: rawLength } = member;
146
+ const length = _memberLength(instance, rawLength);
147
+ if (!primitive.isType(type)) {
148
+ if (!isInstance(value))
149
+ return _debugLog(`Tried to set "${name}" to a non-instance value`);
150
+ if (length > 0 && typeof index != 'number') {
151
+ for (let i = 0; i < length; i++)
152
+ _set(instance, member, value[i], i);
153
+ return;
209
154
  }
155
+ if (!Array.from(getAllPrototypes(value.constructor)).some(c => c === type))
156
+ throw new Error(`${value.constructor.name} is not a subtype of ${type.name}`);
157
+ const offset = instance.byteOffset + member.offset + (index ?? 0) * sizeof(type);
158
+ // It's already the same value
159
+ if (value.buffer === instance.buffer && value.byteOffset === offset)
160
+ return;
161
+ const current = new Uint8Array(instance.buffer, offset, sizeof(value));
162
+ current.set(new Uint8Array(value.buffer, value.byteOffset, sizeof(value)));
163
+ return;
210
164
  }
211
- return buffer;
212
- }
213
- /**
214
- * Deserializes a struct from a Uint8Array
215
- */
216
- export function deserialize(instance, _buffer) {
217
- const buffer = toUint8Array(_buffer);
218
- if (isCustom(instance) && typeof instance[Symbol.deserialize] == 'function')
219
- return instance[Symbol.deserialize](buffer);
220
- checkInstance(instance);
221
- _polyfill_metadata(instance.constructor);
222
- const { options, members } = instance.constructor[Symbol.metadata].struct;
223
- const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
224
- _debugLog('deserialize', instance.constructor.name);
225
- let offset = 0, nextOffset = 0;
226
- for (const member of members.values()) {
227
- const length = _memberLength(instance, member);
228
- _debugLog('\t', member.decl);
229
- for (let i = 0; i < Math.abs(length); i++) {
230
- let object = length != -1 ? instance[member.name] : instance;
231
- const key = length != -1 ? i : member.name;
232
- const isNullish = object[key] === null || object[key] === undefined;
233
- const needsAllocation = isNullish && isStatic(member.type) && member.type[Symbol.metadata].struct.isDynamic;
234
- offset = nextOffset;
235
- if (!isInstance(object[key]) && !needsAllocation)
236
- nextOffset += sizeof(member.type);
237
- if (typeof instance[member.name] == 'string') {
238
- instance[member.name] =
239
- instance[member.name].slice(0, i)
240
- + String.fromCharCode(view.getUint8(offset))
241
- + instance[member.name].slice(i + 1);
242
- continue;
243
- }
244
- if (!primitive.isType(member.type)) {
245
- if (needsAllocation && isStatic(member.type))
246
- object[key] ??= new member.type();
247
- else if (isNullish)
248
- continue;
249
- deserialize(object[key], new Uint8Array(buffer.subarray(offset)));
250
- nextOffset += sizeof(object[key]);
251
- continue;
252
- }
253
- if (length && length != -1)
254
- object ||= [];
255
- const fn = `get${capitalize(member.type)}`;
256
- if (fn == 'getInt64') {
257
- object[key] = view.getBigInt64(offset, !options.bigEndian);
258
- continue;
259
- }
260
- if (fn == 'getUint64') {
261
- object[key] = view.getBigUint64(offset, !options.bigEndian);
262
- continue;
263
- }
264
- if (fn == 'getInt128') {
265
- object[key] =
266
- (view.getBigInt64(offset + (!options.bigEndian ? 8 : 0), !options.bigEndian) << BigInt(64))
267
- | view.getBigUint64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
268
- continue;
269
- }
270
- if (fn == 'getUint128') {
271
- object[key] =
272
- (view.getBigUint64(offset + (!options.bigEndian ? 8 : 0), !options.bigEndian) << BigInt(64))
273
- | view.getBigUint64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
274
- continue;
275
- }
276
- if (fn == 'getFloat128') {
277
- object[key] = view.getFloat64(offset + (!options.bigEndian ? 0 : 8), !options.bigEndian);
278
- continue;
279
- }
280
- object[key] = view[fn](offset, !options.bigEndian);
165
+ const view = new DataView(instance.buffer, instance.byteOffset, instance.byteLength);
166
+ if (length > 0 && typeof index != 'number') {
167
+ for (let i = 0; i < length; i++) {
168
+ const offset = member.offset + i * type.size;
169
+ type.set(view, offset, member.littleEndian, value[i]);
281
170
  }
171
+ return;
282
172
  }
173
+ if (typeof value == 'string')
174
+ value = value.charCodeAt(0);
175
+ type.set(view, member.offset + (index ?? 0) * type.size, member.littleEndian, value);
283
176
  }
284
- function _member(type) {
177
+ function _get(instance, member, index) {
178
+ const { type, length: rawLength } = member;
179
+ const length = _memberLength(instance, rawLength);
180
+ if (length > 0 && typeof index != 'number') {
181
+ return new (primitive.isType(type) ? type.array : BufferViewArray(type, sizeof(type)))(instance.buffer, instance.byteOffset + member.offset, length * sizeof(type));
182
+ }
183
+ const offset = member.offset + (index ?? 0) * sizeof(type);
184
+ if (isStatic(type))
185
+ return new type(instance.buffer, offset, sizeof(type));
186
+ const view = new DataView(instance.buffer, instance.byteOffset, instance.byteLength);
187
+ return type.get(view, offset, member.littleEndian);
188
+ }
189
+ function _member(typeName) {
190
+ const type = primitive.types[primitive.normalize(typeName)];
285
191
  function _structMemberDecorator(valueOrLength, context) {
286
- if (typeof valueOrLength == 'number' || typeof valueOrLength == 'string') {
287
- return member(type, valueOrLength);
288
- }
289
- return member(type)(valueOrLength, context);
192
+ return typeof valueOrLength == 'number' || typeof valueOrLength == 'string'
193
+ ? member(type, { length: valueOrLength, typeName })
194
+ : member(type, { typeName })(valueOrLength, context);
290
195
  }
291
196
  return _structMemberDecorator;
292
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utilium",
3
- "version": "1.10.0",
3
+ "version": "2.0.0-pre.1",
4
4
  "description": "Typescript utilities",
5
5
  "funding": {
6
6
  "type": "individual",