@soundscript/soundscript 0.1.2 → 0.1.4
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/README.md +1 -1
- package/async.d.ts +3 -3
- package/async.js +2 -2
- package/async.js.map +1 -1
- package/codec.d.ts +3 -2
- package/codec.js +2 -2
- package/codec.js.map +1 -1
- package/compare.js +5 -10
- package/compare.js.map +1 -1
- package/decode.d.ts +6 -4
- package/decode.js +5 -5
- package/decode.js.map +1 -1
- package/derive.d.ts +6 -0
- package/derive.js +8 -0
- package/derive.js.map +1 -0
- package/encode.d.ts +11 -9
- package/encode.js +5 -5
- package/encode.js.map +1 -1
- package/experimental/thunk.js.map +1 -0
- package/fetch.d.ts +1 -1
- package/hash.js +9 -14
- package/hash.js.map +1 -1
- package/json.d.ts +29 -2
- package/json.js +124 -1
- package/json.js.map +1 -1
- package/numerics.d.ts +523 -0
- package/numerics.js +1357 -0
- package/numerics.js.map +1 -0
- package/package.json +94 -35
- package/result.d.ts +21 -5
- package/result.js +55 -19
- package/result.js.map +1 -1
- package/soundscript/async.sts +7 -7
- package/soundscript/codec.sts +8 -7
- package/soundscript/compare.sts +5 -13
- package/soundscript/decode.sts +15 -13
- package/soundscript/derive.sts +7 -0
- package/soundscript/encode.sts +18 -16
- package/soundscript/hash.sts +9 -17
- package/soundscript/json.sts +209 -3
- package/soundscript/numerics.sts +1937 -0
- package/soundscript/result.sts +93 -24
- package/soundscript/typeclasses.sts +22 -16
- package/soundscript/value.sts +133 -0
- package/typeclasses.d.ts +2 -2
- package/typeclasses.js +10 -9
- package/typeclasses.js.map +1 -1
- package/value.d.ts +9 -0
- package/value.js +105 -0
- package/value.js.map +1 -0
- package/experimental/component.d.ts +0 -40
- package/experimental/component.js +0 -46
- package/experimental/component.js.map +0 -1
- package/soundscript/experimental/component.sts +0 -69
- package/thunk.js.map +0 -1
- /package/{thunk.d.ts → experimental/thunk.d.ts} +0 -0
- /package/{thunk.js → experimental/thunk.js} +0 -0
- /package/soundscript/{thunk.sts → experimental/thunk.sts} +0 -0
|
@@ -0,0 +1,1937 @@
|
|
|
1
|
+
import { fromCompare, type Eq, type Order } from './compare.js';
|
|
2
|
+
import { Failure } from './failures.js';
|
|
3
|
+
import { fromHashEq, stringHash, type HashEq } from './hash.js';
|
|
4
|
+
import { err, ok, type Result } from './result.js';
|
|
5
|
+
|
|
6
|
+
type MachineNumericKind =
|
|
7
|
+
| 'f64'
|
|
8
|
+
| 'f32'
|
|
9
|
+
| 'i8'
|
|
10
|
+
| 'i16'
|
|
11
|
+
| 'i32'
|
|
12
|
+
| 'i64'
|
|
13
|
+
| 'u8'
|
|
14
|
+
| 'u16'
|
|
15
|
+
| 'u32'
|
|
16
|
+
| 'u64';
|
|
17
|
+
|
|
18
|
+
type HostKind =
|
|
19
|
+
| 'bigint'
|
|
20
|
+
| 'boolean'
|
|
21
|
+
| 'function'
|
|
22
|
+
| 'number'
|
|
23
|
+
| 'object'
|
|
24
|
+
| 'string'
|
|
25
|
+
| 'symbol'
|
|
26
|
+
| 'undefined';
|
|
27
|
+
|
|
28
|
+
type NumericLikeInput = Numeric | number | bigint;
|
|
29
|
+
type NumericPayload = number | bigint;
|
|
30
|
+
type IntegerLeafKind = 'i8' | 'i16' | 'i32' | 'i64' | 'u8' | 'u16' | 'u32' | 'u64';
|
|
31
|
+
type FloatLeafKind = 'f32' | 'f64';
|
|
32
|
+
type NumberLeafKind = Exclude<MachineNumericKind, 'i64' | 'u64'>;
|
|
33
|
+
type BigIntLeafKind = Extract<MachineNumericKind, 'i64' | 'u64'>;
|
|
34
|
+
type BinaryNumericOperator =
|
|
35
|
+
| '+'
|
|
36
|
+
| '-'
|
|
37
|
+
| '*'
|
|
38
|
+
| '/'
|
|
39
|
+
| '%'
|
|
40
|
+
| '**'
|
|
41
|
+
| '&'
|
|
42
|
+
| '|'
|
|
43
|
+
| '^'
|
|
44
|
+
| '<<'
|
|
45
|
+
| '>>'
|
|
46
|
+
| '>>>';
|
|
47
|
+
type UnaryNumericOperator = '+' | '-' | '~';
|
|
48
|
+
|
|
49
|
+
export interface MachineNumericValue<Leaf extends MachineNumericKind> {
|
|
50
|
+
readonly __soundscript_numeric_kind: Leaf;
|
|
51
|
+
toJSON(): { $numeric: Leaf; value: string };
|
|
52
|
+
toString(): string;
|
|
53
|
+
valueOf(): never;
|
|
54
|
+
[Symbol.toPrimitive](hint: string): string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type f64 = MachineNumericValue<'f64'>;
|
|
58
|
+
export type f32 = MachineNumericValue<'f32'>;
|
|
59
|
+
export type i8 = MachineNumericValue<'i8'>;
|
|
60
|
+
export type i16 = MachineNumericValue<'i16'>;
|
|
61
|
+
export type i32 = MachineNumericValue<'i32'>;
|
|
62
|
+
export type i64 = MachineNumericValue<'i64'>;
|
|
63
|
+
export type u8 = MachineNumericValue<'u8'>;
|
|
64
|
+
export type u16 = MachineNumericValue<'u16'>;
|
|
65
|
+
export type u32 = MachineNumericValue<'u32'>;
|
|
66
|
+
export type u64 = MachineNumericValue<'u64'>;
|
|
67
|
+
|
|
68
|
+
export type Numeric = f64 | f32 | i8 | i16 | i32 | i64 | u8 | u16 | u32 | u64;
|
|
69
|
+
export type Int = i8 | i16 | i32 | i64 | u8 | u16 | u32 | u64;
|
|
70
|
+
export type Float = f32 | f64;
|
|
71
|
+
type NumericByKind<Leaf extends MachineNumericKind> = Extract<
|
|
72
|
+
Numeric,
|
|
73
|
+
MachineNumericValue<Leaf>
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
export type MachineNumericOrHostKind = MachineNumericKind | HostKind;
|
|
77
|
+
|
|
78
|
+
export interface NumericFactory<T extends Numeric> {
|
|
79
|
+
(value: NumericLikeInput): T;
|
|
80
|
+
readonly MAX_VALUE: T;
|
|
81
|
+
readonly MIN_VALUE: T;
|
|
82
|
+
compare(left: T, right: T): number;
|
|
83
|
+
format(value: T): string;
|
|
84
|
+
parse(text: string): T;
|
|
85
|
+
tryParse(text: string): T | undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface FloatFactory<T extends Float> extends NumericFactory<T> {
|
|
89
|
+
isFinite(value: T): boolean;
|
|
90
|
+
isNaN(value: T): boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface IntegerFactory<T extends Int> extends NumericFactory<T> {
|
|
94
|
+
checkedAdd(left: T, right: T): Result<T, NumericOverflowFailure>;
|
|
95
|
+
checkedSub(left: T, right: T): Result<T, NumericOverflowFailure>;
|
|
96
|
+
checkedMul(left: T, right: T): Result<T, NumericOverflowFailure>;
|
|
97
|
+
checkedDiv(left: T, right: T): Result<T, NumericOverflowFailure | NumericDivisionByZeroFailure>;
|
|
98
|
+
checkedRem(left: T, right: T): Result<T, NumericDivisionByZeroFailure>;
|
|
99
|
+
checkedNeg(value: T): Result<T, NumericOverflowFailure>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class NumericOverflowFailure extends Failure {
|
|
103
|
+
readonly leaf: IntegerLeafKind;
|
|
104
|
+
readonly operation: 'add' | 'sub' | 'mul' | 'div' | 'rem' | 'neg';
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
leaf: IntegerLeafKind,
|
|
108
|
+
operation: 'add' | 'sub' | 'mul' | 'div' | 'rem' | 'neg',
|
|
109
|
+
) {
|
|
110
|
+
super(`Checked ${operation} overflowed for ${leaf}.`);
|
|
111
|
+
this.leaf = leaf;
|
|
112
|
+
this.operation = operation;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export class NumericDivisionByZeroFailure extends Failure {
|
|
117
|
+
readonly leaf: IntegerLeafKind;
|
|
118
|
+
readonly operation: 'div' | 'rem';
|
|
119
|
+
|
|
120
|
+
constructor(leaf: IntegerLeafKind, operation: 'div' | 'rem') {
|
|
121
|
+
super(`Checked ${operation} divided ${leaf} by zero.`);
|
|
122
|
+
this.leaf = leaf;
|
|
123
|
+
this.operation = operation;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const MACHINE_KIND_PROPERTY = '__soundscript_numeric_kind';
|
|
128
|
+
|
|
129
|
+
class MachineNumericBox<Leaf extends MachineNumericKind>
|
|
130
|
+
implements MachineNumericValue<Leaf> {
|
|
131
|
+
readonly __soundscript_numeric_kind: Leaf;
|
|
132
|
+
readonly #key: string;
|
|
133
|
+
readonly #payload: NumericPayload;
|
|
134
|
+
|
|
135
|
+
constructor(kind: Leaf, payload: NumericPayload, key: string) {
|
|
136
|
+
this.__soundscript_numeric_kind = kind;
|
|
137
|
+
this.#key = key;
|
|
138
|
+
this.#payload = payload;
|
|
139
|
+
Object.freeze(this);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
payload(): NumericPayload {
|
|
143
|
+
return this.#payload;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
toJSON(): { $numeric: Leaf; value: string } {
|
|
147
|
+
return {
|
|
148
|
+
$numeric: this.__soundscript_numeric_kind,
|
|
149
|
+
value: formatPayload(this.#payload),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
toString(): string {
|
|
154
|
+
return this.#key;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
valueOf(): never {
|
|
158
|
+
throw new TypeError('Machine numerics do not support host numeric coercion.');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
[Symbol.toPrimitive](hint: string): string {
|
|
162
|
+
if (hint === 'string') {
|
|
163
|
+
return this.#key;
|
|
164
|
+
}
|
|
165
|
+
throw new TypeError('Machine numerics do not support host numeric coercion.');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const MACHINE_NUMERIC_KINDS = new Set<MachineNumericKind>([
|
|
170
|
+
'f64',
|
|
171
|
+
'f32',
|
|
172
|
+
'i8',
|
|
173
|
+
'i16',
|
|
174
|
+
'i32',
|
|
175
|
+
'i64',
|
|
176
|
+
'u8',
|
|
177
|
+
'u16',
|
|
178
|
+
'u32',
|
|
179
|
+
'u64',
|
|
180
|
+
]);
|
|
181
|
+
const INTEGER_LEAF_KINDS = new Set<IntegerLeafKind>([
|
|
182
|
+
'i8',
|
|
183
|
+
'i16',
|
|
184
|
+
'i32',
|
|
185
|
+
'i64',
|
|
186
|
+
'u8',
|
|
187
|
+
'u16',
|
|
188
|
+
'u32',
|
|
189
|
+
'u64',
|
|
190
|
+
]);
|
|
191
|
+
const FLOAT_LEAF_KINDS = new Set<FloatLeafKind>(['f32', 'f64']);
|
|
192
|
+
const BIGINT_LEAF_KINDS = new Set<BigIntLeafKind>(['i64', 'u64']);
|
|
193
|
+
const NUMBER_LEAF_KINDS = new Set<NumberLeafKind>([
|
|
194
|
+
'f64',
|
|
195
|
+
'f32',
|
|
196
|
+
'i8',
|
|
197
|
+
'i16',
|
|
198
|
+
'i32',
|
|
199
|
+
'u8',
|
|
200
|
+
'u16',
|
|
201
|
+
'u32',
|
|
202
|
+
]);
|
|
203
|
+
|
|
204
|
+
const MACHINE_NUMERIC_CACHES: Record<MachineNumericKind, Map<string, Numeric>> = {
|
|
205
|
+
f64: new Map(),
|
|
206
|
+
f32: new Map(),
|
|
207
|
+
i8: new Map(),
|
|
208
|
+
i16: new Map(),
|
|
209
|
+
i32: new Map(),
|
|
210
|
+
i64: new Map(),
|
|
211
|
+
u8: new Map(),
|
|
212
|
+
u16: new Map(),
|
|
213
|
+
u32: new Map(),
|
|
214
|
+
u64: new Map(),
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
function isMachineNumericBox(
|
|
218
|
+
value: unknown,
|
|
219
|
+
): value is MachineNumericBox<MachineNumericKind> {
|
|
220
|
+
return value instanceof MachineNumericBox;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function assertMachineNumeric(
|
|
224
|
+
value: unknown,
|
|
225
|
+
): MachineNumericBox<MachineNumericKind> {
|
|
226
|
+
if (!isMachineNumericBox(value)) {
|
|
227
|
+
throw new TypeError('Expected a machine numeric value.');
|
|
228
|
+
}
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function formatNumberPayload(value: number): string {
|
|
233
|
+
if (Number.isNaN(value)) {
|
|
234
|
+
return 'NaN';
|
|
235
|
+
}
|
|
236
|
+
if (value === Infinity) {
|
|
237
|
+
return 'Infinity';
|
|
238
|
+
}
|
|
239
|
+
if (value === -Infinity) {
|
|
240
|
+
return '-Infinity';
|
|
241
|
+
}
|
|
242
|
+
return String(value);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function formatPayload(value: NumericPayload): string {
|
|
246
|
+
return typeof value === 'bigint' ? value.toString() : formatNumberPayload(value);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function keyForLeaf(kind: MachineNumericKind, value: NumericPayload): string {
|
|
250
|
+
return `${kind}:${formatPayload(value)}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function canonicalizeFloat(value: number): number {
|
|
254
|
+
if (Number.isNaN(value)) {
|
|
255
|
+
return Number.NaN;
|
|
256
|
+
}
|
|
257
|
+
if (Object.is(value, -0)) {
|
|
258
|
+
return 0;
|
|
259
|
+
}
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function truncatedNumber(value: number): number {
|
|
264
|
+
return Number.isFinite(value) ? Math.trunc(value) : 0;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function payloadFromInput(value: NumericLikeInput): NumericPayload {
|
|
268
|
+
return isMachineNumericBox(value) ? value.payload() : value as number | bigint;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function numberFromInput(value: NumericLikeInput): number {
|
|
272
|
+
const payload = payloadFromInput(value);
|
|
273
|
+
return typeof payload === 'bigint' ? Number(payload) : payload;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function bigintFromInput(value: NumericLikeInput): bigint {
|
|
277
|
+
const payload = payloadFromInput(value);
|
|
278
|
+
if (typeof payload === 'bigint') {
|
|
279
|
+
return payload;
|
|
280
|
+
}
|
|
281
|
+
return BigInt(truncatedNumber(payload));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function exactIntegralBigIntFromInput(value: unknown): bigint | undefined {
|
|
285
|
+
if (isMachineNumericBox(value)) {
|
|
286
|
+
const payload = value.payload();
|
|
287
|
+
if (typeof payload === 'bigint') {
|
|
288
|
+
return payload;
|
|
289
|
+
}
|
|
290
|
+
if (Number.isInteger(payload)) {
|
|
291
|
+
return BigInt(payload);
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (typeof value === 'bigint') {
|
|
297
|
+
return value;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
301
|
+
return BigInt(value);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function canonicalizePayload(
|
|
308
|
+
kind: MachineNumericKind,
|
|
309
|
+
value: NumericLikeInput,
|
|
310
|
+
): NumericPayload {
|
|
311
|
+
switch (kind) {
|
|
312
|
+
case 'f64':
|
|
313
|
+
return canonicalizeFloat(numberFromInput(value));
|
|
314
|
+
case 'f32':
|
|
315
|
+
return canonicalizeFloat(Math.fround(numberFromInput(value)));
|
|
316
|
+
case 'i8':
|
|
317
|
+
return (truncatedNumber(numberFromInput(value)) << 24) >> 24;
|
|
318
|
+
case 'i16':
|
|
319
|
+
return (truncatedNumber(numberFromInput(value)) << 16) >> 16;
|
|
320
|
+
case 'i32':
|
|
321
|
+
return truncatedNumber(numberFromInput(value)) | 0;
|
|
322
|
+
case 'i64':
|
|
323
|
+
return BigInt.asIntN(64, bigintFromInput(value));
|
|
324
|
+
case 'u8':
|
|
325
|
+
return truncatedNumber(numberFromInput(value)) & 0xff;
|
|
326
|
+
case 'u16':
|
|
327
|
+
return truncatedNumber(numberFromInput(value)) & 0xffff;
|
|
328
|
+
case 'u32':
|
|
329
|
+
return truncatedNumber(numberFromInput(value)) >>> 0;
|
|
330
|
+
case 'u64':
|
|
331
|
+
return BigInt.asUintN(64, bigintFromInput(value));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function internMachineNumeric<Leaf extends MachineNumericKind>(
|
|
336
|
+
kind: Leaf,
|
|
337
|
+
value: NumericLikeInput,
|
|
338
|
+
): MachineNumericValue<Leaf> {
|
|
339
|
+
const payload = canonicalizePayload(kind, value);
|
|
340
|
+
const key = keyForLeaf(kind, payload);
|
|
341
|
+
const existing = MACHINE_NUMERIC_CACHES[kind].get(key);
|
|
342
|
+
if (existing) {
|
|
343
|
+
return existing as MachineNumericValue<Leaf>;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const created = new MachineNumericBox(kind, payload, key) as Numeric;
|
|
347
|
+
MACHINE_NUMERIC_CACHES[kind].set(key, created);
|
|
348
|
+
return created as MachineNumericValue<Leaf>;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function numberLeafCompare<Leaf extends NumberLeafKind>(
|
|
352
|
+
left: MachineNumericValue<Leaf>,
|
|
353
|
+
right: MachineNumericValue<Leaf>,
|
|
354
|
+
): number {
|
|
355
|
+
const leftValue = (left as MachineNumericBox<Leaf>).payload() as number;
|
|
356
|
+
const rightValue = (right as MachineNumericBox<Leaf>).payload() as number;
|
|
357
|
+
|
|
358
|
+
const leftNaN = Number.isNaN(leftValue);
|
|
359
|
+
const rightNaN = Number.isNaN(rightValue);
|
|
360
|
+
if (leftNaN || rightNaN) {
|
|
361
|
+
return leftNaN === rightNaN ? 0 : (leftNaN ? 1 : -1);
|
|
362
|
+
}
|
|
363
|
+
if (leftValue === rightValue) {
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
if (leftValue === -Infinity) {
|
|
367
|
+
return -1;
|
|
368
|
+
}
|
|
369
|
+
if (rightValue === -Infinity) {
|
|
370
|
+
return 1;
|
|
371
|
+
}
|
|
372
|
+
if (leftValue === Infinity) {
|
|
373
|
+
return 1;
|
|
374
|
+
}
|
|
375
|
+
if (rightValue === Infinity) {
|
|
376
|
+
return -1;
|
|
377
|
+
}
|
|
378
|
+
return leftValue < rightValue ? -1 : 1;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function bigintLeafCompare<Leaf extends BigIntLeafKind>(
|
|
382
|
+
left: MachineNumericValue<Leaf>,
|
|
383
|
+
right: MachineNumericValue<Leaf>,
|
|
384
|
+
): number {
|
|
385
|
+
const leftValue = (left as MachineNumericBox<Leaf>).payload() as bigint;
|
|
386
|
+
const rightValue = (right as MachineNumericBox<Leaf>).payload() as bigint;
|
|
387
|
+
if (leftValue === rightValue) {
|
|
388
|
+
return 0;
|
|
389
|
+
}
|
|
390
|
+
return leftValue < rightValue ? -1 : 1;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function parseNumberText(text: string): number | undefined {
|
|
394
|
+
const value = Number(text);
|
|
395
|
+
return Number.isNaN(value) && text.trim() !== 'NaN' ? undefined : value;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function parseBigIntText(text: string): bigint | undefined {
|
|
399
|
+
try {
|
|
400
|
+
return BigInt(text);
|
|
401
|
+
} catch {
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function createIntegerFactory<Leaf extends IntegerLeafKind>(
|
|
407
|
+
kind: Leaf,
|
|
408
|
+
minValue: NumericLikeInput,
|
|
409
|
+
maxValue: NumericLikeInput,
|
|
410
|
+
): IntegerFactory<NumericByKind<Leaf>> {
|
|
411
|
+
const factory = ((value: NumericLikeInput) =>
|
|
412
|
+
internMachineNumeric(kind, value)) as IntegerFactory<NumericByKind<Leaf>>;
|
|
413
|
+
Object.assign(factory, {
|
|
414
|
+
MAX_VALUE: internMachineNumeric(kind, maxValue),
|
|
415
|
+
MIN_VALUE: internMachineNumeric(kind, minValue),
|
|
416
|
+
compare(left: NumericByKind<Leaf>, right: NumericByKind<Leaf>): number {
|
|
417
|
+
return BIGINT_LEAF_KINDS.has(kind as BigIntLeafKind)
|
|
418
|
+
? bigintLeafCompare(left as MachineNumericValue<BigIntLeafKind>, right as MachineNumericValue<BigIntLeafKind>)
|
|
419
|
+
: numberLeafCompare(left as MachineNumericValue<NumberLeafKind>, right as MachineNumericValue<NumberLeafKind>);
|
|
420
|
+
},
|
|
421
|
+
format(value: NumericByKind<Leaf>): string {
|
|
422
|
+
return formatPayload((value as MachineNumericBox<Leaf>).payload());
|
|
423
|
+
},
|
|
424
|
+
parse(text: string): NumericByKind<Leaf> {
|
|
425
|
+
const parsed = BIGINT_LEAF_KINDS.has(kind as BigIntLeafKind)
|
|
426
|
+
? parseBigIntText(text)
|
|
427
|
+
: parseNumberText(text);
|
|
428
|
+
if (parsed === undefined) {
|
|
429
|
+
throw new TypeError(`Could not parse ${kind} from ${JSON.stringify(text)}.`);
|
|
430
|
+
}
|
|
431
|
+
return internMachineNumeric(kind, parsed as NumericLikeInput) as NumericByKind<Leaf>;
|
|
432
|
+
},
|
|
433
|
+
tryParse(text: string): NumericByKind<Leaf> | undefined {
|
|
434
|
+
const parsed = BIGINT_LEAF_KINDS.has(kind as BigIntLeafKind)
|
|
435
|
+
? parseBigIntText(text)
|
|
436
|
+
: parseNumberText(text);
|
|
437
|
+
return parsed === undefined
|
|
438
|
+
? undefined
|
|
439
|
+
: internMachineNumeric(kind, parsed as NumericLikeInput) as NumericByKind<Leaf>;
|
|
440
|
+
},
|
|
441
|
+
checkedAdd(
|
|
442
|
+
left: NumericByKind<Leaf>,
|
|
443
|
+
right: NumericByKind<Leaf>,
|
|
444
|
+
): Result<NumericByKind<Leaf>, NumericOverflowFailure> {
|
|
445
|
+
return checkedIntegerBinary(kind, 'add', left, right);
|
|
446
|
+
},
|
|
447
|
+
checkedSub(
|
|
448
|
+
left: NumericByKind<Leaf>,
|
|
449
|
+
right: NumericByKind<Leaf>,
|
|
450
|
+
): Result<NumericByKind<Leaf>, NumericOverflowFailure> {
|
|
451
|
+
return checkedIntegerBinary(kind, 'sub', left, right);
|
|
452
|
+
},
|
|
453
|
+
checkedMul(
|
|
454
|
+
left: NumericByKind<Leaf>,
|
|
455
|
+
right: NumericByKind<Leaf>,
|
|
456
|
+
): Result<NumericByKind<Leaf>, NumericOverflowFailure> {
|
|
457
|
+
return checkedIntegerBinary(kind, 'mul', left, right);
|
|
458
|
+
},
|
|
459
|
+
checkedDiv(
|
|
460
|
+
left: NumericByKind<Leaf>,
|
|
461
|
+
right: NumericByKind<Leaf>,
|
|
462
|
+
): Result<NumericByKind<Leaf>, NumericOverflowFailure | NumericDivisionByZeroFailure> {
|
|
463
|
+
return checkedIntegerBinary(kind, 'div', left, right);
|
|
464
|
+
},
|
|
465
|
+
checkedRem(
|
|
466
|
+
left: NumericByKind<Leaf>,
|
|
467
|
+
right: NumericByKind<Leaf>,
|
|
468
|
+
): Result<NumericByKind<Leaf>, NumericDivisionByZeroFailure> {
|
|
469
|
+
return checkedIntegerRemainder(kind, left, right);
|
|
470
|
+
},
|
|
471
|
+
checkedNeg(value: NumericByKind<Leaf>): Result<NumericByKind<Leaf>, NumericOverflowFailure> {
|
|
472
|
+
return checkedIntegerNegation(kind, value);
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
return factory;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function createFloatFactory<Leaf extends FloatLeafKind>(
|
|
479
|
+
kind: Leaf,
|
|
480
|
+
minValue: number,
|
|
481
|
+
maxValue: number,
|
|
482
|
+
): FloatFactory<NumericByKind<Leaf>> {
|
|
483
|
+
const factory = ((value: NumericLikeInput) =>
|
|
484
|
+
internMachineNumeric(kind, value)) as FloatFactory<NumericByKind<Leaf>>;
|
|
485
|
+
Object.assign(factory, {
|
|
486
|
+
MAX_VALUE: internMachineNumeric(kind, maxValue),
|
|
487
|
+
MIN_VALUE: internMachineNumeric(kind, minValue),
|
|
488
|
+
compare(left: NumericByKind<Leaf>, right: NumericByKind<Leaf>): number {
|
|
489
|
+
return numberLeafCompare(
|
|
490
|
+
left as MachineNumericValue<NumberLeafKind>,
|
|
491
|
+
right as MachineNumericValue<NumberLeafKind>,
|
|
492
|
+
);
|
|
493
|
+
},
|
|
494
|
+
format(value: NumericByKind<Leaf>): string {
|
|
495
|
+
return formatPayload((value as MachineNumericBox<Leaf>).payload());
|
|
496
|
+
},
|
|
497
|
+
isFinite(value: NumericByKind<Leaf>): boolean {
|
|
498
|
+
return Number.isFinite((value as MachineNumericBox<Leaf>).payload() as number);
|
|
499
|
+
},
|
|
500
|
+
isNaN(value: NumericByKind<Leaf>): boolean {
|
|
501
|
+
return Number.isNaN((value as MachineNumericBox<Leaf>).payload() as number);
|
|
502
|
+
},
|
|
503
|
+
parse(text: string): NumericByKind<Leaf> {
|
|
504
|
+
const parsed = parseNumberText(text);
|
|
505
|
+
if (parsed === undefined) {
|
|
506
|
+
throw new TypeError(`Could not parse ${kind} from ${JSON.stringify(text)}.`);
|
|
507
|
+
}
|
|
508
|
+
return internMachineNumeric(kind, parsed) as NumericByKind<Leaf>;
|
|
509
|
+
},
|
|
510
|
+
tryParse(text: string): NumericByKind<Leaf> | undefined {
|
|
511
|
+
const parsed = parseNumberText(text);
|
|
512
|
+
return parsed === undefined
|
|
513
|
+
? undefined
|
|
514
|
+
: internMachineNumeric(kind, parsed) as NumericByKind<Leaf>;
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
return factory;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function exactKindGuard<Leaf extends MachineNumericKind>(kind: Leaf) {
|
|
521
|
+
return (value: unknown): value is MachineNumericValue<Leaf> =>
|
|
522
|
+
isMachineNumericBox(value) && value.__soundscript_numeric_kind === kind;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function fitsIntegerKind(kind: IntegerLeafKind, value: unknown): boolean {
|
|
526
|
+
const integral = exactIntegralBigIntFromInput(value);
|
|
527
|
+
if (integral === undefined) {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
switch (kind) {
|
|
532
|
+
case 'i8':
|
|
533
|
+
return integral >= -128n && integral <= 127n;
|
|
534
|
+
case 'i16':
|
|
535
|
+
return integral >= -32768n && integral <= 32767n;
|
|
536
|
+
case 'i32':
|
|
537
|
+
return integral >= -2147483648n && integral <= 2147483647n;
|
|
538
|
+
case 'i64':
|
|
539
|
+
return BigInt.asIntN(64, integral) === integral;
|
|
540
|
+
case 'u8':
|
|
541
|
+
return integral >= 0n && integral <= 255n;
|
|
542
|
+
case 'u16':
|
|
543
|
+
return integral >= 0n && integral <= 65535n;
|
|
544
|
+
case 'u32':
|
|
545
|
+
return integral >= 0n && integral <= 4294967295n;
|
|
546
|
+
case 'u64':
|
|
547
|
+
return integral >= 0n && BigInt.asUintN(64, integral) === integral;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function fitsFloat32(value: unknown): boolean {
|
|
552
|
+
const payload = isMachineNumericBox(value) ? value.payload() : value;
|
|
553
|
+
return typeof payload === 'number' && Number.isFinite(payload) && Math.fround(payload) === payload;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function assertIntegerLeafValue<Leaf extends IntegerLeafKind>(
|
|
557
|
+
kind: Leaf,
|
|
558
|
+
value: NumericByKind<Leaf>,
|
|
559
|
+
): MachineNumericBox<Leaf> {
|
|
560
|
+
const numeric = assertMachineNumeric(value);
|
|
561
|
+
if (numeric.__soundscript_numeric_kind !== kind) {
|
|
562
|
+
throw new TypeError(`Expected ${kind} machine numeric value.`);
|
|
563
|
+
}
|
|
564
|
+
return numeric as MachineNumericBox<Leaf>;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function integerPayloadToBigInt<Leaf extends IntegerLeafKind>(numeric: MachineNumericBox<Leaf>): bigint {
|
|
568
|
+
const payload = numeric.payload();
|
|
569
|
+
return typeof payload === 'bigint' ? payload : BigInt(payload);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function checkedIntegerResult<Leaf extends IntegerLeafKind>(
|
|
573
|
+
kind: Leaf,
|
|
574
|
+
operation: 'add' | 'sub' | 'mul' | 'div' | 'neg',
|
|
575
|
+
value: bigint,
|
|
576
|
+
): Result<NumericByKind<Leaf>, NumericOverflowFailure> {
|
|
577
|
+
if (!fitsIntegerKind(kind, value)) {
|
|
578
|
+
return err(new NumericOverflowFailure(kind, operation));
|
|
579
|
+
}
|
|
580
|
+
return ok(internMachineNumeric(kind, value) as NumericByKind<Leaf>);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function checkedIntegerBinary<Leaf extends IntegerLeafKind>(
|
|
584
|
+
kind: Leaf,
|
|
585
|
+
operation: 'add' | 'sub' | 'mul' | 'div',
|
|
586
|
+
left: NumericByKind<Leaf>,
|
|
587
|
+
right: NumericByKind<Leaf>,
|
|
588
|
+
): Result<
|
|
589
|
+
NumericByKind<Leaf>,
|
|
590
|
+
NumericOverflowFailure | NumericDivisionByZeroFailure
|
|
591
|
+
> {
|
|
592
|
+
const leftValue = integerPayloadToBigInt(assertIntegerLeafValue(kind, left));
|
|
593
|
+
const rightValue = integerPayloadToBigInt(assertIntegerLeafValue(kind, right));
|
|
594
|
+
|
|
595
|
+
if (operation === 'div' && rightValue === 0n) {
|
|
596
|
+
return err(new NumericDivisionByZeroFailure(kind, operation));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
switch (operation) {
|
|
600
|
+
case 'add':
|
|
601
|
+
return checkedIntegerResult(kind, operation, leftValue + rightValue);
|
|
602
|
+
case 'sub':
|
|
603
|
+
return checkedIntegerResult(kind, operation, leftValue - rightValue);
|
|
604
|
+
case 'mul':
|
|
605
|
+
return checkedIntegerResult(kind, operation, leftValue * rightValue);
|
|
606
|
+
case 'div':
|
|
607
|
+
return checkedIntegerResult(kind, operation, leftValue / rightValue);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function checkedIntegerRemainder<Leaf extends IntegerLeafKind>(
|
|
612
|
+
kind: Leaf,
|
|
613
|
+
left: NumericByKind<Leaf>,
|
|
614
|
+
right: NumericByKind<Leaf>,
|
|
615
|
+
): Result<NumericByKind<Leaf>, NumericDivisionByZeroFailure> {
|
|
616
|
+
const leftValue = integerPayloadToBigInt(assertIntegerLeafValue(kind, left));
|
|
617
|
+
const rightValue = integerPayloadToBigInt(assertIntegerLeafValue(kind, right));
|
|
618
|
+
|
|
619
|
+
if (rightValue === 0n) {
|
|
620
|
+
return err(new NumericDivisionByZeroFailure(kind, 'rem'));
|
|
621
|
+
}
|
|
622
|
+
return ok(internMachineNumeric(kind, leftValue % rightValue) as NumericByKind<Leaf>);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function checkedIntegerNegation<Leaf extends IntegerLeafKind>(
|
|
626
|
+
kind: Leaf,
|
|
627
|
+
value: NumericByKind<Leaf>,
|
|
628
|
+
): Result<NumericByKind<Leaf>, NumericOverflowFailure> {
|
|
629
|
+
const payload = integerPayloadToBigInt(assertIntegerLeafValue(kind, value));
|
|
630
|
+
return checkedIntegerResult(kind, 'neg', -payload);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function exactShiftCount(value: NumericLikeInput): bigint {
|
|
634
|
+
return BigInt.asUintN(6, bigintFromInput(value));
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function applyBigIntBinaryOperator(
|
|
638
|
+
kind: BigIntLeafKind,
|
|
639
|
+
operator: BinaryNumericOperator,
|
|
640
|
+
left: bigint,
|
|
641
|
+
right: bigint,
|
|
642
|
+
): bigint {
|
|
643
|
+
switch (operator) {
|
|
644
|
+
case '+':
|
|
645
|
+
return left + right;
|
|
646
|
+
case '-':
|
|
647
|
+
return left - right;
|
|
648
|
+
case '*':
|
|
649
|
+
return left * right;
|
|
650
|
+
case '/':
|
|
651
|
+
return left / right;
|
|
652
|
+
case '%':
|
|
653
|
+
return left % right;
|
|
654
|
+
case '**':
|
|
655
|
+
return left ** right;
|
|
656
|
+
case '&':
|
|
657
|
+
return left & right;
|
|
658
|
+
case '|':
|
|
659
|
+
return left | right;
|
|
660
|
+
case '^':
|
|
661
|
+
return left ^ right;
|
|
662
|
+
case '<<':
|
|
663
|
+
return left << exactShiftCount(right);
|
|
664
|
+
case '>>':
|
|
665
|
+
return left >> exactShiftCount(right);
|
|
666
|
+
case '>>>': {
|
|
667
|
+
const shifted = BigInt.asUintN(64, left) >> exactShiftCount(right);
|
|
668
|
+
return kind === 'u64' ? shifted : BigInt.asIntN(64, shifted);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function applyNumberBinaryOperator(
|
|
674
|
+
operator: BinaryNumericOperator,
|
|
675
|
+
left: number,
|
|
676
|
+
right: number,
|
|
677
|
+
): number {
|
|
678
|
+
switch (operator) {
|
|
679
|
+
case '+':
|
|
680
|
+
return left + right;
|
|
681
|
+
case '-':
|
|
682
|
+
return left - right;
|
|
683
|
+
case '*':
|
|
684
|
+
return left * right;
|
|
685
|
+
case '/':
|
|
686
|
+
return left / right;
|
|
687
|
+
case '%':
|
|
688
|
+
return left % right;
|
|
689
|
+
case '**':
|
|
690
|
+
return left ** right;
|
|
691
|
+
case '&':
|
|
692
|
+
return left & right;
|
|
693
|
+
case '|':
|
|
694
|
+
return left | right;
|
|
695
|
+
case '^':
|
|
696
|
+
return left ^ right;
|
|
697
|
+
case '<<':
|
|
698
|
+
return left << right;
|
|
699
|
+
case '>>':
|
|
700
|
+
return left >> right;
|
|
701
|
+
case '>>>':
|
|
702
|
+
return left >>> right;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function applyNumberUnaryOperator(operator: UnaryNumericOperator, value: number): number {
|
|
707
|
+
switch (operator) {
|
|
708
|
+
case '+':
|
|
709
|
+
return +value;
|
|
710
|
+
case '-':
|
|
711
|
+
return -value;
|
|
712
|
+
case '~':
|
|
713
|
+
return ~value;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function applyBigIntUnaryOperator(operator: Exclude<UnaryNumericOperator, '+'>, value: bigint): bigint {
|
|
718
|
+
switch (operator) {
|
|
719
|
+
case '-':
|
|
720
|
+
return -value;
|
|
721
|
+
case '~':
|
|
722
|
+
return ~value;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export const F64 = createFloatFactory('f64', -Number.MAX_VALUE, Number.MAX_VALUE);
|
|
727
|
+
export const F32 = createFloatFactory('f32', -3.4028234663852886e38, 3.4028234663852886e38);
|
|
728
|
+
export const I8 = createIntegerFactory('i8', -128, 127);
|
|
729
|
+
export const I16 = createIntegerFactory('i16', -32768, 32767);
|
|
730
|
+
export const I32 = createIntegerFactory('i32', -2147483648, 2147483647);
|
|
731
|
+
export const I64 = createIntegerFactory('i64', -9223372036854775808n, 9223372036854775807n);
|
|
732
|
+
export const U8 = createIntegerFactory('u8', 0, 255);
|
|
733
|
+
export const U16 = createIntegerFactory('u16', 0, 65535);
|
|
734
|
+
export const U32 = createIntegerFactory('u32', 0, 4294967295);
|
|
735
|
+
export const U64 = createIntegerFactory('u64', 0n, 18446744073709551615n);
|
|
736
|
+
|
|
737
|
+
const FACTORY_BY_KIND: Record<MachineNumericKind, NumericFactory<Numeric>> = {
|
|
738
|
+
f64: F64 as NumericFactory<Numeric>,
|
|
739
|
+
f32: F32 as NumericFactory<Numeric>,
|
|
740
|
+
i8: I8 as NumericFactory<Numeric>,
|
|
741
|
+
i16: I16 as NumericFactory<Numeric>,
|
|
742
|
+
i32: I32 as NumericFactory<Numeric>,
|
|
743
|
+
i64: I64 as NumericFactory<Numeric>,
|
|
744
|
+
u8: U8 as NumericFactory<Numeric>,
|
|
745
|
+
u16: U16 as NumericFactory<Numeric>,
|
|
746
|
+
u32: U32 as NumericFactory<Numeric>,
|
|
747
|
+
u64: U64 as NumericFactory<Numeric>,
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
export function kindOf(value: unknown): MachineNumericOrHostKind {
|
|
751
|
+
if (isMachineNumericBox(value)) {
|
|
752
|
+
return value.__soundscript_numeric_kind;
|
|
753
|
+
}
|
|
754
|
+
return (typeof value) as HostKind;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export function isNumeric(value: unknown): value is Numeric {
|
|
758
|
+
return isMachineNumericBox(value);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
export function isInt(value: unknown): value is Int {
|
|
762
|
+
return isMachineNumericBox(value) &&
|
|
763
|
+
INTEGER_LEAF_KINDS.has(value.__soundscript_numeric_kind as IntegerLeafKind);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export function isFloat(value: unknown): value is Float {
|
|
767
|
+
return isMachineNumericBox(value) &&
|
|
768
|
+
FLOAT_LEAF_KINDS.has(value.__soundscript_numeric_kind as FloatLeafKind);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
export const isF64 = exactKindGuard('f64');
|
|
772
|
+
export const isF32 = exactKindGuard('f32');
|
|
773
|
+
export const isI8 = exactKindGuard('i8');
|
|
774
|
+
export const isI16 = exactKindGuard('i16');
|
|
775
|
+
export const isI32 = exactKindGuard('i32');
|
|
776
|
+
export const isI64 = exactKindGuard('i64');
|
|
777
|
+
export const isU8 = exactKindGuard('u8');
|
|
778
|
+
export const isU16 = exactKindGuard('u16');
|
|
779
|
+
export const isU32 = exactKindGuard('u32');
|
|
780
|
+
export const isU64 = exactKindGuard('u64');
|
|
781
|
+
|
|
782
|
+
export function fitsF32(value: unknown): boolean {
|
|
783
|
+
return fitsFloat32(value);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export function fitsI8(value: unknown): boolean {
|
|
787
|
+
return fitsIntegerKind('i8', value);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export function fitsI16(value: unknown): boolean {
|
|
791
|
+
return fitsIntegerKind('i16', value);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export function fitsI32(value: unknown): boolean {
|
|
795
|
+
return fitsIntegerKind('i32', value);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function fitsI64(value: unknown): boolean {
|
|
799
|
+
return fitsIntegerKind('i64', value);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
export function fitsU8(value: unknown): boolean {
|
|
803
|
+
return fitsIntegerKind('u8', value);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export function fitsU16(value: unknown): boolean {
|
|
807
|
+
return fitsIntegerKind('u16', value);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export function fitsU32(value: unknown): boolean {
|
|
811
|
+
return fitsIntegerKind('u32', value);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export function fitsU64(value: unknown): boolean {
|
|
815
|
+
return fitsIntegerKind('u64', value);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
export function toHostNumber(value: Numeric): number {
|
|
819
|
+
const payload = assertMachineNumeric(value).payload();
|
|
820
|
+
return typeof payload === 'bigint' ? Number(payload) : payload;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export function toHostBigInt(value: Int): bigint {
|
|
824
|
+
const payload = assertMachineNumeric(value).payload();
|
|
825
|
+
return typeof payload === 'bigint' ? payload : BigInt(payload);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
export function format(value: Numeric | number | bigint): string {
|
|
829
|
+
return isMachineNumericBox(value)
|
|
830
|
+
? formatPayload(value.payload())
|
|
831
|
+
: formatPayload(value as number | bigint);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export function keyOf(value: Numeric): string {
|
|
835
|
+
return assertMachineNumeric(value).toString();
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function readNumericFromDataView<Leaf extends MachineNumericKind>(
|
|
839
|
+
kind: Leaf,
|
|
840
|
+
view: DataView,
|
|
841
|
+
byteOffset: number,
|
|
842
|
+
littleEndian?: boolean,
|
|
843
|
+
): NumericByKind<Leaf> {
|
|
844
|
+
switch (kind) {
|
|
845
|
+
case 'f64':
|
|
846
|
+
return F64(view.getFloat64(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
847
|
+
case 'f32':
|
|
848
|
+
return F32(view.getFloat32(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
849
|
+
case 'i8':
|
|
850
|
+
return I8(view.getInt8(byteOffset)) as NumericByKind<Leaf>;
|
|
851
|
+
case 'i16':
|
|
852
|
+
return I16(view.getInt16(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
853
|
+
case 'i32':
|
|
854
|
+
return I32(view.getInt32(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
855
|
+
case 'i64':
|
|
856
|
+
return I64(view.getBigInt64(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
857
|
+
case 'u8':
|
|
858
|
+
return U8(view.getUint8(byteOffset)) as NumericByKind<Leaf>;
|
|
859
|
+
case 'u16':
|
|
860
|
+
return U16(view.getUint16(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
861
|
+
case 'u32':
|
|
862
|
+
return U32(view.getUint32(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
863
|
+
case 'u64':
|
|
864
|
+
return U64(view.getBigUint64(byteOffset, littleEndian)) as NumericByKind<Leaf>;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function writeNumericToDataView<Leaf extends MachineNumericKind>(
|
|
869
|
+
kind: Leaf,
|
|
870
|
+
view: DataView,
|
|
871
|
+
byteOffset: number,
|
|
872
|
+
value: NumericLikeInput,
|
|
873
|
+
littleEndian?: boolean,
|
|
874
|
+
): void {
|
|
875
|
+
const numeric = FACTORY_BY_KIND[kind](value) as NumericByKind<Leaf>;
|
|
876
|
+
const payload = assertMachineNumeric(numeric).payload();
|
|
877
|
+
|
|
878
|
+
switch (kind) {
|
|
879
|
+
case 'f64':
|
|
880
|
+
view.setFloat64(byteOffset, payload as number, littleEndian);
|
|
881
|
+
return;
|
|
882
|
+
case 'f32':
|
|
883
|
+
view.setFloat32(byteOffset, payload as number, littleEndian);
|
|
884
|
+
return;
|
|
885
|
+
case 'i8':
|
|
886
|
+
view.setInt8(byteOffset, payload as number);
|
|
887
|
+
return;
|
|
888
|
+
case 'i16':
|
|
889
|
+
view.setInt16(byteOffset, payload as number, littleEndian);
|
|
890
|
+
return;
|
|
891
|
+
case 'i32':
|
|
892
|
+
view.setInt32(byteOffset, payload as number, littleEndian);
|
|
893
|
+
return;
|
|
894
|
+
case 'i64':
|
|
895
|
+
view.setBigInt64(byteOffset, payload as bigint, littleEndian);
|
|
896
|
+
return;
|
|
897
|
+
case 'u8':
|
|
898
|
+
view.setUint8(byteOffset, payload as number);
|
|
899
|
+
return;
|
|
900
|
+
case 'u16':
|
|
901
|
+
view.setUint16(byteOffset, payload as number, littleEndian);
|
|
902
|
+
return;
|
|
903
|
+
case 'u32':
|
|
904
|
+
view.setUint32(byteOffset, payload as number, littleEndian);
|
|
905
|
+
return;
|
|
906
|
+
case 'u64':
|
|
907
|
+
view.setBigUint64(byteOffset, payload as bigint, littleEndian);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
export function readF64(view: DataView, byteOffset: number, littleEndian?: boolean): f64 {
|
|
913
|
+
return readNumericFromDataView('f64', view, byteOffset, littleEndian);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
export function readF32(view: DataView, byteOffset: number, littleEndian?: boolean): f32 {
|
|
917
|
+
return readNumericFromDataView('f32', view, byteOffset, littleEndian);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
export function readI8(view: DataView, byteOffset: number): i8 {
|
|
921
|
+
return readNumericFromDataView('i8', view, byteOffset);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
export function readI16(view: DataView, byteOffset: number, littleEndian?: boolean): i16 {
|
|
925
|
+
return readNumericFromDataView('i16', view, byteOffset, littleEndian);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export function readI32(view: DataView, byteOffset: number, littleEndian?: boolean): i32 {
|
|
929
|
+
return readNumericFromDataView('i32', view, byteOffset, littleEndian);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
export function readI64(view: DataView, byteOffset: number, littleEndian?: boolean): i64 {
|
|
933
|
+
return readNumericFromDataView('i64', view, byteOffset, littleEndian);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
export function readU8(view: DataView, byteOffset: number): u8 {
|
|
937
|
+
return readNumericFromDataView('u8', view, byteOffset);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
export function readU16(view: DataView, byteOffset: number, littleEndian?: boolean): u16 {
|
|
941
|
+
return readNumericFromDataView('u16', view, byteOffset, littleEndian);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
export function readU32(view: DataView, byteOffset: number, littleEndian?: boolean): u32 {
|
|
945
|
+
return readNumericFromDataView('u32', view, byteOffset, littleEndian);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
export function readU64(view: DataView, byteOffset: number, littleEndian?: boolean): u64 {
|
|
949
|
+
return readNumericFromDataView('u64', view, byteOffset, littleEndian);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
export function writeF64(
|
|
953
|
+
view: DataView,
|
|
954
|
+
byteOffset: number,
|
|
955
|
+
value: NumericLikeInput,
|
|
956
|
+
littleEndian?: boolean,
|
|
957
|
+
): void {
|
|
958
|
+
writeNumericToDataView('f64', view, byteOffset, value, littleEndian);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
export function writeF32(
|
|
962
|
+
view: DataView,
|
|
963
|
+
byteOffset: number,
|
|
964
|
+
value: NumericLikeInput,
|
|
965
|
+
littleEndian?: boolean,
|
|
966
|
+
): void {
|
|
967
|
+
writeNumericToDataView('f32', view, byteOffset, value, littleEndian);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
export function writeI8(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
971
|
+
writeNumericToDataView('i8', view, byteOffset, value);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
export function writeI16(
|
|
975
|
+
view: DataView,
|
|
976
|
+
byteOffset: number,
|
|
977
|
+
value: NumericLikeInput,
|
|
978
|
+
littleEndian?: boolean,
|
|
979
|
+
): void {
|
|
980
|
+
writeNumericToDataView('i16', view, byteOffset, value, littleEndian);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
export function writeI32(
|
|
984
|
+
view: DataView,
|
|
985
|
+
byteOffset: number,
|
|
986
|
+
value: NumericLikeInput,
|
|
987
|
+
littleEndian?: boolean,
|
|
988
|
+
): void {
|
|
989
|
+
writeNumericToDataView('i32', view, byteOffset, value, littleEndian);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
export function writeI64(
|
|
993
|
+
view: DataView,
|
|
994
|
+
byteOffset: number,
|
|
995
|
+
value: NumericLikeInput,
|
|
996
|
+
littleEndian?: boolean,
|
|
997
|
+
): void {
|
|
998
|
+
writeNumericToDataView('i64', view, byteOffset, value, littleEndian);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
export function writeU8(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1002
|
+
writeNumericToDataView('u8', view, byteOffset, value);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
export function writeU16(
|
|
1006
|
+
view: DataView,
|
|
1007
|
+
byteOffset: number,
|
|
1008
|
+
value: NumericLikeInput,
|
|
1009
|
+
littleEndian?: boolean,
|
|
1010
|
+
): void {
|
|
1011
|
+
writeNumericToDataView('u16', view, byteOffset, value, littleEndian);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
export function writeU32(
|
|
1015
|
+
view: DataView,
|
|
1016
|
+
byteOffset: number,
|
|
1017
|
+
value: NumericLikeInput,
|
|
1018
|
+
littleEndian?: boolean,
|
|
1019
|
+
): void {
|
|
1020
|
+
writeNumericToDataView('u32', view, byteOffset, value, littleEndian);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
export function writeU64(
|
|
1024
|
+
view: DataView,
|
|
1025
|
+
byteOffset: number,
|
|
1026
|
+
value: NumericLikeInput,
|
|
1027
|
+
littleEndian?: boolean,
|
|
1028
|
+
): void {
|
|
1029
|
+
writeNumericToDataView('u64', view, byteOffset, value, littleEndian);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function isCanonicalArrayIndexProperty(property: PropertyKey): number | undefined {
|
|
1033
|
+
if (typeof property !== 'string' || property === '') {
|
|
1034
|
+
return undefined;
|
|
1035
|
+
}
|
|
1036
|
+
const numeric = Number(property);
|
|
1037
|
+
if (!Number.isInteger(numeric) || numeric < 0 || String(numeric) !== property) {
|
|
1038
|
+
return undefined;
|
|
1039
|
+
}
|
|
1040
|
+
return numeric;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function normalizeViewIndex(index: number, length: number): number | undefined {
|
|
1044
|
+
return Number.isInteger(index) && index >= 0 && index < length ? index : undefined;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function resolveSubarrayIndex(index: number | undefined, length: number): number {
|
|
1048
|
+
if (index === undefined) {
|
|
1049
|
+
return length;
|
|
1050
|
+
}
|
|
1051
|
+
if (index < 0) {
|
|
1052
|
+
return Math.max(length + index, 0);
|
|
1053
|
+
}
|
|
1054
|
+
return Math.min(index, length);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
function resolveSliceIndex(index: number | undefined, length: number): number {
|
|
1058
|
+
if (index === undefined) {
|
|
1059
|
+
return 0;
|
|
1060
|
+
}
|
|
1061
|
+
if (index < 0) {
|
|
1062
|
+
return Math.max(length + index, 0);
|
|
1063
|
+
}
|
|
1064
|
+
return Math.min(index, length);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
function resolveLastSearchIndex(index: number | undefined, length: number): number {
|
|
1068
|
+
if (length === 0) {
|
|
1069
|
+
return -1;
|
|
1070
|
+
}
|
|
1071
|
+
if (index === undefined) {
|
|
1072
|
+
return length - 1;
|
|
1073
|
+
}
|
|
1074
|
+
if (index < 0) {
|
|
1075
|
+
return length + index;
|
|
1076
|
+
}
|
|
1077
|
+
return Math.min(index, length - 1);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function materializeNumericLikeSource(
|
|
1081
|
+
source: ArrayLike<NumericLikeInput> | Iterable<NumericLikeInput>,
|
|
1082
|
+
): NumericLikeInput[] {
|
|
1083
|
+
if (Symbol.iterator in Object(source)) {
|
|
1084
|
+
return Array.from(source as Iterable<NumericLikeInput>);
|
|
1085
|
+
}
|
|
1086
|
+
const arrayLike = source as ArrayLike<NumericLikeInput>;
|
|
1087
|
+
const values: NumericLikeInput[] = [];
|
|
1088
|
+
for (let index = 0; index < arrayLike.length; index += 1) {
|
|
1089
|
+
values.push(arrayLike[index]);
|
|
1090
|
+
}
|
|
1091
|
+
return values;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
abstract class MachineNumericArrayView<Leaf extends MachineNumericKind>
|
|
1095
|
+
implements Iterable<NumericByKind<Leaf>> {
|
|
1096
|
+
[index: number]: NumericByKind<Leaf>;
|
|
1097
|
+
readonly BYTES_PER_ELEMENT: number;
|
|
1098
|
+
readonly buffer: ArrayBufferLike;
|
|
1099
|
+
readonly byteLength: number;
|
|
1100
|
+
readonly byteOffset: number;
|
|
1101
|
+
readonly length: number;
|
|
1102
|
+
protected readonly _view: DataView;
|
|
1103
|
+
|
|
1104
|
+
protected constructor(
|
|
1105
|
+
buffer: ArrayBufferLike,
|
|
1106
|
+
bytesPerElement: number,
|
|
1107
|
+
byteOffset = 0,
|
|
1108
|
+
length?: number,
|
|
1109
|
+
) {
|
|
1110
|
+
if (byteOffset < 0 || !Number.isInteger(byteOffset)) {
|
|
1111
|
+
throw new RangeError('byteOffset must be a non-negative integer.');
|
|
1112
|
+
}
|
|
1113
|
+
if (byteOffset % bytesPerElement !== 0) {
|
|
1114
|
+
throw new RangeError('byteOffset must align to BYTES_PER_ELEMENT.');
|
|
1115
|
+
}
|
|
1116
|
+
if (byteOffset > buffer.byteLength) {
|
|
1117
|
+
throw new RangeError('byteOffset is out of range for the provided buffer.');
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
const remaining = buffer.byteLength - byteOffset;
|
|
1121
|
+
const computedLength = length ?? remaining / bytesPerElement;
|
|
1122
|
+
if (!Number.isInteger(computedLength) || computedLength < 0) {
|
|
1123
|
+
throw new RangeError('length must describe a whole number of elements.');
|
|
1124
|
+
}
|
|
1125
|
+
if (computedLength * bytesPerElement > remaining) {
|
|
1126
|
+
throw new RangeError('length is out of range for the provided buffer.');
|
|
1127
|
+
}
|
|
1128
|
+
if (length === undefined && remaining % bytesPerElement !== 0) {
|
|
1129
|
+
throw new RangeError('buffer byte length must align to BYTES_PER_ELEMENT.');
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
this.BYTES_PER_ELEMENT = bytesPerElement;
|
|
1133
|
+
this.buffer = buffer;
|
|
1134
|
+
this.byteOffset = byteOffset;
|
|
1135
|
+
this.length = computedLength;
|
|
1136
|
+
this.byteLength = computedLength * bytesPerElement;
|
|
1137
|
+
this._view = new DataView(buffer, byteOffset, this.byteLength);
|
|
1138
|
+
|
|
1139
|
+
return new Proxy(this, {
|
|
1140
|
+
get: (target, property, receiver) => {
|
|
1141
|
+
const index = isCanonicalArrayIndexProperty(property);
|
|
1142
|
+
if (index !== undefined) {
|
|
1143
|
+
return target.at(index);
|
|
1144
|
+
}
|
|
1145
|
+
const value = Reflect.get(target, property, receiver);
|
|
1146
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
1147
|
+
},
|
|
1148
|
+
set: (target, property, value, receiver) => {
|
|
1149
|
+
const index = isCanonicalArrayIndexProperty(property);
|
|
1150
|
+
if (index !== undefined) {
|
|
1151
|
+
target.setAt(index, value as NumericLikeInput);
|
|
1152
|
+
return true;
|
|
1153
|
+
}
|
|
1154
|
+
return Reflect.set(target, property, value, receiver);
|
|
1155
|
+
},
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
protected dataView(): DataView {
|
|
1160
|
+
return this._view;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
protected abstract leafKind(): Leaf;
|
|
1164
|
+
protected abstract readFromView(view: DataView, byteOffset: number): NumericByKind<Leaf>;
|
|
1165
|
+
protected abstract writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void;
|
|
1166
|
+
protected abstract constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): MachineNumericArrayView<Leaf>;
|
|
1167
|
+
|
|
1168
|
+
protected coerceToLeaf(value: NumericLikeInput): NumericByKind<Leaf> {
|
|
1169
|
+
return FACTORY_BY_KIND[this.leafKind()](value) as NumericByKind<Leaf>;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
at(index: number): NumericByKind<Leaf> | undefined {
|
|
1173
|
+
const normalized = normalizeViewIndex(index, this.length);
|
|
1174
|
+
if (normalized === undefined) {
|
|
1175
|
+
return undefined;
|
|
1176
|
+
}
|
|
1177
|
+
return this.readFromView(this._view, normalized * this.BYTES_PER_ELEMENT);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
setAt(index: number, value: NumericLikeInput): void {
|
|
1181
|
+
const normalized = normalizeViewIndex(index, this.length);
|
|
1182
|
+
if (normalized === undefined) {
|
|
1183
|
+
throw new RangeError('Index out of range for machine numeric view.');
|
|
1184
|
+
}
|
|
1185
|
+
this.writeToView(this._view, normalized * this.BYTES_PER_ELEMENT, value);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
fill(value: NumericLikeInput, start = 0, end = this.length): this {
|
|
1189
|
+
const startIndex = resolveSliceIndex(start, this.length);
|
|
1190
|
+
const endIndex = resolveSubarrayIndex(end, this.length);
|
|
1191
|
+
const finalEnd = Math.max(endIndex, startIndex);
|
|
1192
|
+
for (let index = startIndex; index < finalEnd; index += 1) {
|
|
1193
|
+
this.writeToView(this._view, index * this.BYTES_PER_ELEMENT, value);
|
|
1194
|
+
}
|
|
1195
|
+
return this;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
set(
|
|
1199
|
+
source: ArrayLike<NumericLikeInput> | Iterable<NumericLikeInput>,
|
|
1200
|
+
offset = 0,
|
|
1201
|
+
): void {
|
|
1202
|
+
if (!Number.isInteger(offset) || offset < 0 || offset > this.length) {
|
|
1203
|
+
throw new RangeError('Offset out of range for machine numeric view.');
|
|
1204
|
+
}
|
|
1205
|
+
const snapshot = materializeNumericLikeSource(source);
|
|
1206
|
+
if (offset + snapshot.length > this.length) {
|
|
1207
|
+
throw new RangeError('Source is too large for machine numeric view.');
|
|
1208
|
+
}
|
|
1209
|
+
for (let index = 0; index < snapshot.length; index += 1) {
|
|
1210
|
+
this.writeToView(
|
|
1211
|
+
this._view,
|
|
1212
|
+
(offset + index) * this.BYTES_PER_ELEMENT,
|
|
1213
|
+
snapshot[index],
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
copyWithin(target: number, start: number, end?: number): this {
|
|
1219
|
+
const targetIndex = resolveSliceIndex(target, this.length);
|
|
1220
|
+
const startIndex = resolveSliceIndex(start, this.length);
|
|
1221
|
+
const endIndex = resolveSubarrayIndex(end, this.length);
|
|
1222
|
+
const available = Math.max(endIndex - startIndex, 0);
|
|
1223
|
+
const count = Math.min(available, this.length - targetIndex);
|
|
1224
|
+
const snapshot: NumericLikeInput[] = [];
|
|
1225
|
+
for (let index = 0; index < count; index += 1) {
|
|
1226
|
+
snapshot.push(
|
|
1227
|
+
this.readFromView(
|
|
1228
|
+
this._view,
|
|
1229
|
+
(startIndex + index) * this.BYTES_PER_ELEMENT,
|
|
1230
|
+
),
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
for (let index = 0; index < snapshot.length; index += 1) {
|
|
1234
|
+
this.writeToView(
|
|
1235
|
+
this._view,
|
|
1236
|
+
(targetIndex + index) * this.BYTES_PER_ELEMENT,
|
|
1237
|
+
snapshot[index],
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
return this;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
*keys(): IterableIterator<number> {
|
|
1244
|
+
for (let index = 0; index < this.length; index += 1) {
|
|
1245
|
+
yield index;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
values(): IterableIterator<NumericByKind<Leaf>> {
|
|
1250
|
+
return this[Symbol.iterator]();
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
*entries(): IterableIterator<[number, NumericByKind<Leaf>]> {
|
|
1254
|
+
for (let index = 0; index < this.length; index += 1) {
|
|
1255
|
+
yield [index, this.readFromView(this._view, index * this.BYTES_PER_ELEMENT)];
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
includes(searchElement: NumericLikeInput, fromIndex = 0): boolean {
|
|
1260
|
+
return this.indexOf(searchElement, fromIndex) !== -1;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
indexOf(searchElement: NumericLikeInput, fromIndex = 0): number {
|
|
1264
|
+
const startIndex = resolveSliceIndex(fromIndex, this.length);
|
|
1265
|
+
const searchValue = this.coerceToLeaf(searchElement);
|
|
1266
|
+
for (let index = startIndex; index < this.length; index += 1) {
|
|
1267
|
+
if (this.readFromView(this._view, index * this.BYTES_PER_ELEMENT) === searchValue) {
|
|
1268
|
+
return index;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
return -1;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
lastIndexOf(searchElement: NumericLikeInput, fromIndex?: number): number {
|
|
1275
|
+
const startIndex = resolveLastSearchIndex(fromIndex, this.length);
|
|
1276
|
+
if (startIndex < 0) {
|
|
1277
|
+
return -1;
|
|
1278
|
+
}
|
|
1279
|
+
const searchValue = this.coerceToLeaf(searchElement);
|
|
1280
|
+
for (let index = startIndex; index >= 0; index -= 1) {
|
|
1281
|
+
if (this.readFromView(this._view, index * this.BYTES_PER_ELEMENT) === searchValue) {
|
|
1282
|
+
return index;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return -1;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
slice(begin?: number, end?: number): MachineNumericArrayView<Leaf> {
|
|
1289
|
+
const startIndex = resolveSliceIndex(begin, this.length);
|
|
1290
|
+
const endIndex = resolveSubarrayIndex(end, this.length);
|
|
1291
|
+
const finalLength = Math.max(endIndex - startIndex, 0);
|
|
1292
|
+
const sliced = this.constructSubarray(
|
|
1293
|
+
new ArrayBuffer(finalLength * this.BYTES_PER_ELEMENT),
|
|
1294
|
+
0,
|
|
1295
|
+
finalLength,
|
|
1296
|
+
);
|
|
1297
|
+
for (let index = 0; index < finalLength; index += 1) {
|
|
1298
|
+
const value = this.readFromView(
|
|
1299
|
+
this._view,
|
|
1300
|
+
(startIndex + index) * this.BYTES_PER_ELEMENT,
|
|
1301
|
+
);
|
|
1302
|
+
sliced.setAt(index, value);
|
|
1303
|
+
}
|
|
1304
|
+
return sliced;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
subarray(begin?: number, end?: number): MachineNumericArrayView<Leaf> {
|
|
1308
|
+
const startIndex = resolveSubarrayIndex(begin ?? 0, this.length);
|
|
1309
|
+
const endIndex = resolveSubarrayIndex(end, this.length);
|
|
1310
|
+
const finalLength = Math.max(endIndex - startIndex, 0);
|
|
1311
|
+
return this.constructSubarray(
|
|
1312
|
+
this.buffer,
|
|
1313
|
+
this.byteOffset + startIndex * this.BYTES_PER_ELEMENT,
|
|
1314
|
+
finalLength,
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
*[Symbol.iterator](): IterableIterator<NumericByKind<Leaf>> {
|
|
1319
|
+
for (let index = 0; index < this.length; index += 1) {
|
|
1320
|
+
yield this.readFromView(this._view, index * this.BYTES_PER_ELEMENT);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function assertLittleEndianNativeViewBridge(
|
|
1326
|
+
littleEndian: boolean,
|
|
1327
|
+
hostViewName: string,
|
|
1328
|
+
): void {
|
|
1329
|
+
if (!littleEndian) {
|
|
1330
|
+
throw new TypeError(
|
|
1331
|
+
`Native ${hostViewName} views require little-endian machine storage.`,
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
export class I8Array extends MachineNumericArrayView<'i8'> {
|
|
1337
|
+
static readonly BYTES_PER_ELEMENT = 1;
|
|
1338
|
+
|
|
1339
|
+
static fromHostView(view: Int8Array): I8Array {
|
|
1340
|
+
return new I8Array(view.buffer, view.byteOffset, view.length);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number) {
|
|
1344
|
+
super(buffer, I8Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
protected leafKind(): 'i8' {
|
|
1348
|
+
return 'i8';
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
protected readFromView(view: DataView, byteOffset: number): i8 {
|
|
1352
|
+
return readI8(view, byteOffset);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1356
|
+
writeI8(view, byteOffset, value);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): I8Array {
|
|
1360
|
+
return new I8Array(buffer, byteOffset, length);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
override subarray(begin?: number, end?: number): I8Array {
|
|
1364
|
+
return super.subarray(begin, end) as I8Array;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
override slice(begin?: number, end?: number): I8Array {
|
|
1368
|
+
return super.slice(begin, end) as I8Array;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
toHostView(): Int8Array {
|
|
1372
|
+
return new Int8Array(this.buffer, this.byteOffset, this.length);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
export class U8Array extends MachineNumericArrayView<'u8'> {
|
|
1377
|
+
static readonly BYTES_PER_ELEMENT = 1;
|
|
1378
|
+
|
|
1379
|
+
static fromHostView(view: Uint8Array): U8Array {
|
|
1380
|
+
return new U8Array(view.buffer, view.byteOffset, view.length);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number) {
|
|
1384
|
+
super(buffer, U8Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
protected leafKind(): 'u8' {
|
|
1388
|
+
return 'u8';
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
protected readFromView(view: DataView, byteOffset: number): u8 {
|
|
1392
|
+
return readU8(view, byteOffset);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1396
|
+
writeU8(view, byteOffset, value);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): U8Array {
|
|
1400
|
+
return new U8Array(buffer, byteOffset, length);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
override subarray(begin?: number, end?: number): U8Array {
|
|
1404
|
+
return super.subarray(begin, end) as U8Array;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
override slice(begin?: number, end?: number): U8Array {
|
|
1408
|
+
return super.slice(begin, end) as U8Array;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
toHostView(): Uint8Array {
|
|
1412
|
+
return new Uint8Array(this.buffer, this.byteOffset, this.length);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
export class I16Array extends MachineNumericArrayView<'i16'> {
|
|
1417
|
+
static readonly BYTES_PER_ELEMENT = 2;
|
|
1418
|
+
protected readonly _littleEndian: boolean;
|
|
1419
|
+
|
|
1420
|
+
static fromHostView(view: Int16Array): I16Array {
|
|
1421
|
+
return new I16Array(view.buffer, view.byteOffset, view.length, true);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1425
|
+
super(buffer, I16Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1426
|
+
this._littleEndian = littleEndian;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
protected leafKind(): 'i16' {
|
|
1430
|
+
return 'i16';
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
protected readFromView(view: DataView, byteOffset: number): i16 {
|
|
1434
|
+
return readI16(view, byteOffset, this._littleEndian);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1438
|
+
writeI16(view, byteOffset, value, this._littleEndian);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): I16Array {
|
|
1442
|
+
return new I16Array(buffer, byteOffset, length, this._littleEndian);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
override subarray(begin?: number, end?: number): I16Array {
|
|
1446
|
+
return super.subarray(begin, end) as I16Array;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
override slice(begin?: number, end?: number): I16Array {
|
|
1450
|
+
return super.slice(begin, end) as I16Array;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
toHostView(): Int16Array {
|
|
1454
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'Int16Array');
|
|
1455
|
+
return new Int16Array(this.buffer, this.byteOffset, this.length);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
export class U16Array extends MachineNumericArrayView<'u16'> {
|
|
1460
|
+
static readonly BYTES_PER_ELEMENT = 2;
|
|
1461
|
+
protected readonly _littleEndian: boolean;
|
|
1462
|
+
|
|
1463
|
+
static fromHostView(view: Uint16Array): U16Array {
|
|
1464
|
+
return new U16Array(view.buffer, view.byteOffset, view.length, true);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1468
|
+
super(buffer, U16Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1469
|
+
this._littleEndian = littleEndian;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
protected leafKind(): 'u16' {
|
|
1473
|
+
return 'u16';
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
protected readFromView(view: DataView, byteOffset: number): u16 {
|
|
1477
|
+
return readU16(view, byteOffset, this._littleEndian);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1481
|
+
writeU16(view, byteOffset, value, this._littleEndian);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): U16Array {
|
|
1485
|
+
return new U16Array(buffer, byteOffset, length, this._littleEndian);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
override subarray(begin?: number, end?: number): U16Array {
|
|
1489
|
+
return super.subarray(begin, end) as U16Array;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
override slice(begin?: number, end?: number): U16Array {
|
|
1493
|
+
return super.slice(begin, end) as U16Array;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
toHostView(): Uint16Array {
|
|
1497
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'Uint16Array');
|
|
1498
|
+
return new Uint16Array(this.buffer, this.byteOffset, this.length);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
export class I32Array extends MachineNumericArrayView<'i32'> {
|
|
1503
|
+
static readonly BYTES_PER_ELEMENT = 4;
|
|
1504
|
+
protected readonly _littleEndian: boolean;
|
|
1505
|
+
|
|
1506
|
+
static fromHostView(view: Int32Array): I32Array {
|
|
1507
|
+
return new I32Array(view.buffer, view.byteOffset, view.length, true);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1511
|
+
super(buffer, I32Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1512
|
+
this._littleEndian = littleEndian;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
protected leafKind(): 'i32' {
|
|
1516
|
+
return 'i32';
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
protected readFromView(view: DataView, byteOffset: number): i32 {
|
|
1520
|
+
return readI32(view, byteOffset, this._littleEndian);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1524
|
+
writeI32(view, byteOffset, value, this._littleEndian);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): I32Array {
|
|
1528
|
+
return new I32Array(buffer, byteOffset, length, this._littleEndian);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
override subarray(begin?: number, end?: number): I32Array {
|
|
1532
|
+
return super.subarray(begin, end) as I32Array;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
override slice(begin?: number, end?: number): I32Array {
|
|
1536
|
+
return super.slice(begin, end) as I32Array;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
toHostView(): Int32Array {
|
|
1540
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'Int32Array');
|
|
1541
|
+
return new Int32Array(this.buffer, this.byteOffset, this.length);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
export class U32Array extends MachineNumericArrayView<'u32'> {
|
|
1546
|
+
static readonly BYTES_PER_ELEMENT = 4;
|
|
1547
|
+
protected readonly _littleEndian: boolean;
|
|
1548
|
+
|
|
1549
|
+
static fromHostView(view: Uint32Array): U32Array {
|
|
1550
|
+
return new U32Array(view.buffer, view.byteOffset, view.length, true);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1554
|
+
super(buffer, U32Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1555
|
+
this._littleEndian = littleEndian;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
protected leafKind(): 'u32' {
|
|
1559
|
+
return 'u32';
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
protected readFromView(view: DataView, byteOffset: number): u32 {
|
|
1563
|
+
return readU32(view, byteOffset, this._littleEndian);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1567
|
+
writeU32(view, byteOffset, value, this._littleEndian);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): U32Array {
|
|
1571
|
+
return new U32Array(buffer, byteOffset, length, this._littleEndian);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
override subarray(begin?: number, end?: number): U32Array {
|
|
1575
|
+
return super.subarray(begin, end) as U32Array;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
override slice(begin?: number, end?: number): U32Array {
|
|
1579
|
+
return super.slice(begin, end) as U32Array;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
toHostView(): Uint32Array {
|
|
1583
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'Uint32Array');
|
|
1584
|
+
return new Uint32Array(this.buffer, this.byteOffset, this.length);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
export class I64Array extends MachineNumericArrayView<'i64'> {
|
|
1589
|
+
static readonly BYTES_PER_ELEMENT = 8;
|
|
1590
|
+
|
|
1591
|
+
static fromHostView(view: BigInt64Array): I64Array {
|
|
1592
|
+
return new I64Array(view.buffer, view.byteOffset, view.length, true);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
protected readonly _littleEndian: boolean;
|
|
1596
|
+
|
|
1597
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1598
|
+
super(buffer, I64Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1599
|
+
this._littleEndian = littleEndian;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
protected leafKind(): 'i64' {
|
|
1603
|
+
return 'i64';
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
protected readFromView(view: DataView, byteOffset: number): i64 {
|
|
1607
|
+
return readI64(view, byteOffset, this._littleEndian);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1611
|
+
writeI64(view, byteOffset, value, this._littleEndian);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): I64Array {
|
|
1615
|
+
return new I64Array(buffer, byteOffset, length, this._littleEndian);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
override subarray(begin?: number, end?: number): I64Array {
|
|
1619
|
+
return super.subarray(begin, end) as I64Array;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
override slice(begin?: number, end?: number): I64Array {
|
|
1623
|
+
return super.slice(begin, end) as I64Array;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
toHostView(): BigInt64Array {
|
|
1627
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'BigInt64Array');
|
|
1628
|
+
return new BigInt64Array(this.buffer, this.byteOffset, this.length);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
export class U64Array extends MachineNumericArrayView<'u64'> {
|
|
1633
|
+
static readonly BYTES_PER_ELEMENT = 8;
|
|
1634
|
+
protected readonly _littleEndian: boolean;
|
|
1635
|
+
|
|
1636
|
+
static fromHostView(view: BigUint64Array): U64Array {
|
|
1637
|
+
return new U64Array(view.buffer, view.byteOffset, view.length, true);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1641
|
+
super(buffer, U64Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1642
|
+
this._littleEndian = littleEndian;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
protected leafKind(): 'u64' {
|
|
1646
|
+
return 'u64';
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
protected readFromView(view: DataView, byteOffset: number): u64 {
|
|
1650
|
+
return readU64(view, byteOffset, this._littleEndian);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1654
|
+
writeU64(view, byteOffset, value, this._littleEndian);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): U64Array {
|
|
1658
|
+
return new U64Array(buffer, byteOffset, length, this._littleEndian);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
override subarray(begin?: number, end?: number): U64Array {
|
|
1662
|
+
return super.subarray(begin, end) as U64Array;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
override slice(begin?: number, end?: number): U64Array {
|
|
1666
|
+
return super.slice(begin, end) as U64Array;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
toHostView(): BigUint64Array {
|
|
1670
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'BigUint64Array');
|
|
1671
|
+
return new BigUint64Array(this.buffer, this.byteOffset, this.length);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
export class F32Array extends MachineNumericArrayView<'f32'> {
|
|
1676
|
+
static readonly BYTES_PER_ELEMENT = 4;
|
|
1677
|
+
|
|
1678
|
+
static fromHostView(view: Float32Array): F32Array {
|
|
1679
|
+
return new F32Array(view.buffer, view.byteOffset, view.length, true);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
protected readonly _littleEndian: boolean;
|
|
1683
|
+
|
|
1684
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1685
|
+
super(buffer, F32Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1686
|
+
this._littleEndian = littleEndian;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
protected leafKind(): 'f32' {
|
|
1690
|
+
return 'f32';
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
protected readFromView(view: DataView, byteOffset: number): f32 {
|
|
1694
|
+
return readF32(view, byteOffset, this._littleEndian);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1698
|
+
writeF32(view, byteOffset, value, this._littleEndian);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): F32Array {
|
|
1702
|
+
return new F32Array(buffer, byteOffset, length, this._littleEndian);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
override subarray(begin?: number, end?: number): F32Array {
|
|
1706
|
+
return super.subarray(begin, end) as F32Array;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
override slice(begin?: number, end?: number): F32Array {
|
|
1710
|
+
return super.slice(begin, end) as F32Array;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
toHostView(): Float32Array {
|
|
1714
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'Float32Array');
|
|
1715
|
+
return new Float32Array(this.buffer, this.byteOffset, this.length);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
export class F64Array extends MachineNumericArrayView<'f64'> {
|
|
1720
|
+
static readonly BYTES_PER_ELEMENT = 8;
|
|
1721
|
+
protected readonly _littleEndian: boolean;
|
|
1722
|
+
|
|
1723
|
+
static fromHostView(view: Float64Array): F64Array {
|
|
1724
|
+
return new F64Array(view.buffer, view.byteOffset, view.length, true);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
constructor(buffer: ArrayBufferLike, byteOffset = 0, length?: number, littleEndian = true) {
|
|
1728
|
+
super(buffer, F64Array.BYTES_PER_ELEMENT, byteOffset, length);
|
|
1729
|
+
this._littleEndian = littleEndian;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
protected leafKind(): 'f64' {
|
|
1733
|
+
return 'f64';
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
protected readFromView(view: DataView, byteOffset: number): f64 {
|
|
1737
|
+
return readF64(view, byteOffset, this._littleEndian);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
protected writeToView(view: DataView, byteOffset: number, value: NumericLikeInput): void {
|
|
1741
|
+
writeF64(view, byteOffset, value, this._littleEndian);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
protected constructSubarray(buffer: ArrayBufferLike, byteOffset: number, length: number): F64Array {
|
|
1745
|
+
return new F64Array(buffer, byteOffset, length, this._littleEndian);
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
override subarray(begin?: number, end?: number): F64Array {
|
|
1749
|
+
return super.subarray(begin, end) as F64Array;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
override slice(begin?: number, end?: number): F64Array {
|
|
1753
|
+
return super.slice(begin, end) as F64Array;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
toHostView(): Float64Array {
|
|
1757
|
+
assertLittleEndianNativeViewBridge(this._littleEndian, 'Float64Array');
|
|
1758
|
+
return new Float64Array(this.buffer, this.byteOffset, this.length);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
export function equalAs<T extends Numeric>(
|
|
1763
|
+
leaf: NumericFactory<T>,
|
|
1764
|
+
left: NumericLikeInput,
|
|
1765
|
+
right: NumericLikeInput,
|
|
1766
|
+
): boolean {
|
|
1767
|
+
return leaf(left) === leaf(right);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
export function compareAs<T extends Numeric>(
|
|
1771
|
+
leaf: NumericFactory<T>,
|
|
1772
|
+
left: NumericLikeInput,
|
|
1773
|
+
right: NumericLikeInput,
|
|
1774
|
+
): number {
|
|
1775
|
+
return leaf.compare(leaf(left), leaf(right));
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
export function eqAs<T extends Numeric>(
|
|
1779
|
+
leaf: NumericFactory<T>,
|
|
1780
|
+
): Eq<NumericLikeInput> {
|
|
1781
|
+
return {
|
|
1782
|
+
equals(left, right) {
|
|
1783
|
+
return equalAs(leaf, left, right);
|
|
1784
|
+
},
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
export function orderAs<T extends Numeric>(
|
|
1789
|
+
leaf: NumericFactory<T>,
|
|
1790
|
+
): Order<NumericLikeInput> {
|
|
1791
|
+
return fromCompare((left, right) => compareAs(leaf, left, right));
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
export function hashEqAs<T extends Numeric>(
|
|
1795
|
+
leaf: NumericFactory<T>,
|
|
1796
|
+
): HashEq<NumericLikeInput> {
|
|
1797
|
+
return fromHashEq(
|
|
1798
|
+
(value) => stringHash.hash(keyOf(leaf(value))),
|
|
1799
|
+
(left, right) => equalAs(leaf, left, right),
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
export function minAs<T extends Numeric>(
|
|
1804
|
+
leaf: NumericFactory<T>,
|
|
1805
|
+
values: Iterable<NumericLikeInput>,
|
|
1806
|
+
): T | undefined {
|
|
1807
|
+
let smallest: T | undefined;
|
|
1808
|
+
for (const value of values) {
|
|
1809
|
+
const coerced = leaf(value);
|
|
1810
|
+
if (!smallest || leaf.compare(coerced, smallest) < 0) {
|
|
1811
|
+
smallest = coerced;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
return smallest;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
export function maxAs<T extends Numeric>(
|
|
1818
|
+
leaf: NumericFactory<T>,
|
|
1819
|
+
values: Iterable<NumericLikeInput>,
|
|
1820
|
+
): T | undefined {
|
|
1821
|
+
let largest: T | undefined;
|
|
1822
|
+
for (const value of values) {
|
|
1823
|
+
const coerced = leaf(value);
|
|
1824
|
+
if (!largest || leaf.compare(coerced, largest) > 0) {
|
|
1825
|
+
largest = coerced;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
return largest;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
export function clampAs<T extends Numeric>(
|
|
1832
|
+
leaf: NumericFactory<T>,
|
|
1833
|
+
value: NumericLikeInput,
|
|
1834
|
+
minimum: NumericLikeInput,
|
|
1835
|
+
maximum: NumericLikeInput,
|
|
1836
|
+
): T {
|
|
1837
|
+
const minValue = leaf(minimum);
|
|
1838
|
+
const maxValue = leaf(maximum);
|
|
1839
|
+
if (leaf.compare(minValue, maxValue) > 0) {
|
|
1840
|
+
throw new RangeError('Expected clamp minimum to be less than or equal to clamp maximum.');
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
const coerced = leaf(value);
|
|
1844
|
+
if (leaf.compare(coerced, minValue) < 0) {
|
|
1845
|
+
return minValue;
|
|
1846
|
+
}
|
|
1847
|
+
if (leaf.compare(coerced, maxValue) > 0) {
|
|
1848
|
+
return maxValue;
|
|
1849
|
+
}
|
|
1850
|
+
return coerced;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
export function binarySearchAs<T extends Numeric>(
|
|
1854
|
+
leaf: NumericFactory<T>,
|
|
1855
|
+
values: ArrayLike<NumericLikeInput>,
|
|
1856
|
+
target: NumericLikeInput,
|
|
1857
|
+
): number {
|
|
1858
|
+
const targetValue = leaf(target);
|
|
1859
|
+
let low = 0;
|
|
1860
|
+
let high = values.length - 1;
|
|
1861
|
+
let foundIndex = -1;
|
|
1862
|
+
|
|
1863
|
+
while (low <= high) {
|
|
1864
|
+
const mid = (low + high) >> 1;
|
|
1865
|
+
const midValue = leaf(values[mid]!);
|
|
1866
|
+
const ordering = leaf.compare(midValue, targetValue);
|
|
1867
|
+
if (ordering < 0) {
|
|
1868
|
+
low = mid + 1;
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
if (ordering > 0) {
|
|
1872
|
+
high = mid - 1;
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1875
|
+
foundIndex = mid;
|
|
1876
|
+
high = mid - 1;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
return foundIndex;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
export function __numericBinary<T extends Numeric>(
|
|
1883
|
+
operator: BinaryNumericOperator,
|
|
1884
|
+
left: T,
|
|
1885
|
+
right: T,
|
|
1886
|
+
): T {
|
|
1887
|
+
const leftValue = assertMachineNumeric(left);
|
|
1888
|
+
const rightValue = assertMachineNumeric(right);
|
|
1889
|
+
const kind = leftValue.__soundscript_numeric_kind;
|
|
1890
|
+
if (kind !== rightValue.__soundscript_numeric_kind) {
|
|
1891
|
+
throw new TypeError('Mixed machine numeric operators require explicit coercion.');
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
if (BIGINT_LEAF_KINDS.has(kind as BigIntLeafKind)) {
|
|
1895
|
+
const result = applyBigIntBinaryOperator(
|
|
1896
|
+
kind as BigIntLeafKind,
|
|
1897
|
+
operator,
|
|
1898
|
+
leftValue.payload() as bigint,
|
|
1899
|
+
rightValue.payload() as bigint,
|
|
1900
|
+
);
|
|
1901
|
+
return internMachineNumeric(kind, result) as T;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
const result = applyNumberBinaryOperator(
|
|
1905
|
+
operator,
|
|
1906
|
+
leftValue.payload() as number,
|
|
1907
|
+
rightValue.payload() as number,
|
|
1908
|
+
);
|
|
1909
|
+
return internMachineNumeric(kind, result) as T;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
export function __numericUnary<T extends Numeric>(
|
|
1913
|
+
operator: UnaryNumericOperator,
|
|
1914
|
+
value: T,
|
|
1915
|
+
): T {
|
|
1916
|
+
const numeric = assertMachineNumeric(value);
|
|
1917
|
+
const kind = numeric.__soundscript_numeric_kind;
|
|
1918
|
+
|
|
1919
|
+
if (BIGINT_LEAF_KINDS.has(kind as BigIntLeafKind)) {
|
|
1920
|
+
if (operator === '+') {
|
|
1921
|
+
throw new TypeError('Unary plus is not supported on bigint-backed machine numerics.');
|
|
1922
|
+
}
|
|
1923
|
+
return internMachineNumeric(
|
|
1924
|
+
kind,
|
|
1925
|
+
applyBigIntUnaryOperator(operator, numeric.payload() as bigint),
|
|
1926
|
+
) as T;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
return internMachineNumeric(
|
|
1930
|
+
kind,
|
|
1931
|
+
applyNumberUnaryOperator(operator, numeric.payload() as number),
|
|
1932
|
+
) as T;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
export function __numericWasmLeaf<T>(value: unknown): T {
|
|
1936
|
+
return value as T;
|
|
1937
|
+
}
|