@bcts/dcbor 1.0.0-alpha.10

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.
Files changed (45) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +13 -0
  3. package/dist/index.cjs +9151 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +3107 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +3107 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +9155 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +9027 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +80 -0
  14. package/src/.claude-flow/metrics/agent-metrics.json +1 -0
  15. package/src/.claude-flow/metrics/performance.json +87 -0
  16. package/src/.claude-flow/metrics/task-metrics.json +10 -0
  17. package/src/byte-string.ts +300 -0
  18. package/src/cbor-codable.ts +170 -0
  19. package/src/cbor-tagged-codable.ts +72 -0
  20. package/src/cbor-tagged-decodable.ts +184 -0
  21. package/src/cbor-tagged-encodable.ts +138 -0
  22. package/src/cbor-tagged.ts +104 -0
  23. package/src/cbor.ts +869 -0
  24. package/src/conveniences.ts +840 -0
  25. package/src/date.ts +553 -0
  26. package/src/decode.ts +276 -0
  27. package/src/diag.ts +462 -0
  28. package/src/dump.ts +277 -0
  29. package/src/error.ts +259 -0
  30. package/src/exact.ts +714 -0
  31. package/src/float.ts +279 -0
  32. package/src/global.d.ts +34 -0
  33. package/src/globals.d.ts +0 -0
  34. package/src/index.ts +180 -0
  35. package/src/map.ts +308 -0
  36. package/src/prelude.ts +70 -0
  37. package/src/set.ts +515 -0
  38. package/src/simple.ts +153 -0
  39. package/src/stdlib.ts +55 -0
  40. package/src/string-util.ts +55 -0
  41. package/src/tag.ts +53 -0
  42. package/src/tags-store.ts +294 -0
  43. package/src/tags.ts +231 -0
  44. package/src/varint.ts +124 -0
  45. package/src/walk.ts +516 -0
package/src/exact.ts ADDED
@@ -0,0 +1,714 @@
1
+ /**
2
+ * Exact numeric conversions.
3
+ *
4
+ * This module is based on the Swift `exactly` initializers.
5
+ * See https://github.com/apple/swift-evolution/blob/main/proposals/0080-failable-numeric-initializers.md
6
+ * See https://github.com/apple/swift/blob/main/stdlib/public/core/IntegerTypes.swift.gyb
7
+ *
8
+ * Provides exact conversions between numeric types (f16/f32/f64/integers).
9
+ * Returns undefined if the conversion cannot be represented exactly.
10
+ *
11
+ * @module exact
12
+ */
13
+
14
+ import { binary16ToNumber, binary32ToNumber, numberToBinary16, numberToBinary32 } from "./float";
15
+
16
+ // TypeScript doesn't have native integer types with overflow, so we use number for most operations
17
+ // and bigint for values that exceed Number.MAX_SAFE_INTEGER
18
+
19
+ // Helper to check if a number has a fractional part
20
+ const hasFract = (n: number): boolean => {
21
+ return n % 1 !== 0;
22
+ };
23
+
24
+ /**
25
+ * Exact conversions for i16 (-32768 to 32767).
26
+ */
27
+ export class ExactI16 {
28
+ static readonly MIN = -32768;
29
+ static readonly MAX = 32767;
30
+
31
+ static exactFromF16(source: number): number | undefined {
32
+ if (!Number.isFinite(source)) return undefined;
33
+ if (source <= -32768.0 || source >= 32768.0) return undefined;
34
+ if (hasFract(source)) return undefined;
35
+ return Math.trunc(source);
36
+ }
37
+
38
+ static exactFromF32(source: number): number | undefined {
39
+ if (!Number.isFinite(source)) return undefined;
40
+ if (source <= -32769.0 || source >= 32768.0) return undefined;
41
+ if (hasFract(source)) return undefined;
42
+ return Math.trunc(source);
43
+ }
44
+
45
+ static exactFromF64(source: number): number | undefined {
46
+ if (!Number.isFinite(source)) return undefined;
47
+ if (source <= -32769.0 || source >= 32768.0) return undefined;
48
+ if (hasFract(source)) return undefined;
49
+ return Math.trunc(source);
50
+ }
51
+
52
+ static exactFromU64(source: number | bigint): number | undefined {
53
+ const n = typeof source === "bigint" ? Number(source) : source;
54
+ if (n > 32767) return undefined;
55
+ return n;
56
+ }
57
+
58
+ static exactFromI64(source: number | bigint): number | undefined {
59
+ const n = typeof source === "bigint" ? Number(source) : source;
60
+ if (n < -32768 || n > 32767) return undefined;
61
+ return n;
62
+ }
63
+
64
+ static exactFromU128(source: bigint): number | undefined {
65
+ if (source > 32767n) return undefined;
66
+ return Number(source);
67
+ }
68
+
69
+ static exactFromI128(source: bigint): number | undefined {
70
+ if (source < -32768n || source > 32767n) return undefined;
71
+ return Number(source);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Exact conversions for i32 (-2147483648 to 2147483647).
77
+ */
78
+ export class ExactI32 {
79
+ static readonly MIN = -2147483648;
80
+ static readonly MAX = 2147483647;
81
+
82
+ static exactFromF16(source: number): number | undefined {
83
+ if (!Number.isFinite(source)) return undefined;
84
+ // A Float16 value, if finite, is always in-range for 32-bit signed integer types
85
+ if (hasFract(source)) return undefined;
86
+ return Math.trunc(source);
87
+ }
88
+
89
+ static exactFromF32(source: number): number | undefined {
90
+ if (!Number.isFinite(source)) return undefined;
91
+ if (source <= -2147483904.0 || source >= 2147483648.0) return undefined;
92
+ if (hasFract(source)) return undefined;
93
+ return Math.trunc(source);
94
+ }
95
+
96
+ static exactFromF64(source: number): number | undefined {
97
+ if (!Number.isFinite(source)) return undefined;
98
+ if (source <= -2147483649.0 || source >= 2147483648.0) return undefined;
99
+ if (hasFract(source)) return undefined;
100
+ return Math.trunc(source);
101
+ }
102
+
103
+ static exactFromU64(source: number | bigint): number | undefined {
104
+ const n = typeof source === "bigint" ? Number(source) : source;
105
+ if (n > 2147483647) return undefined;
106
+ return n;
107
+ }
108
+
109
+ static exactFromI64(source: number | bigint): number | undefined {
110
+ const n = typeof source === "bigint" ? Number(source) : source;
111
+ if (n < -2147483648 || n > 2147483647) return undefined;
112
+ return n;
113
+ }
114
+
115
+ static exactFromU128(source: bigint): number | undefined {
116
+ if (source > 2147483647n) return undefined;
117
+ return Number(source);
118
+ }
119
+
120
+ static exactFromI128(source: bigint): number | undefined {
121
+ if (source < -2147483648n || source > 2147483647n) return undefined;
122
+ return Number(source);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Exact conversions for i64 (-9223372036854775808 to 9223372036854775807).
128
+ */
129
+ export class ExactI64 {
130
+ static readonly MIN = -9223372036854775808n;
131
+ static readonly MAX = 9223372036854775807n;
132
+
133
+ static exactFromF16(source: number): number | bigint | undefined {
134
+ if (!Number.isFinite(source)) return undefined;
135
+ // A Float16 value, if finite, is always in-range for 64-bit signed integer types
136
+ if (hasFract(source)) return undefined;
137
+ return Math.trunc(source);
138
+ }
139
+
140
+ static exactFromF32(source: number): number | bigint | undefined {
141
+ if (!Number.isFinite(source)) return undefined;
142
+ if (source <= -9223373136366403584.0 || source >= 9223372036854775808.0) return undefined;
143
+ if (hasFract(source)) return undefined;
144
+ const result = Math.trunc(source);
145
+ return result > Number.MAX_SAFE_INTEGER || result < Number.MIN_SAFE_INTEGER
146
+ ? BigInt(result)
147
+ : result;
148
+ }
149
+
150
+ static exactFromF64(source: number): number | bigint | undefined {
151
+ if (!Number.isFinite(source)) return undefined;
152
+ if (source <= -9223372036854777856.0 || source >= 9223372036854775808.0) return undefined;
153
+ if (hasFract(source)) return undefined;
154
+ const result = Math.trunc(source);
155
+ return result > Number.MAX_SAFE_INTEGER || result < Number.MIN_SAFE_INTEGER
156
+ ? BigInt(result)
157
+ : result;
158
+ }
159
+
160
+ static exactFromU64(source: number | bigint): number | bigint | undefined {
161
+ const n = typeof source === "bigint" ? source : BigInt(source);
162
+ if (n > 9223372036854775807n) return undefined;
163
+ return n <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(n) : n;
164
+ }
165
+
166
+ static exactFromI64(source: number | bigint): number | bigint | undefined {
167
+ // Identity conversion
168
+ return source;
169
+ }
170
+
171
+ static exactFromU128(source: bigint): number | bigint | undefined {
172
+ if (source > 9223372036854775807n) return undefined;
173
+ return source <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(source) : source;
174
+ }
175
+
176
+ static exactFromI128(source: bigint): number | bigint | undefined {
177
+ if (source < -9223372036854775808n || source > 9223372036854775807n) return undefined;
178
+ return source <= BigInt(Number.MAX_SAFE_INTEGER) && source >= BigInt(Number.MIN_SAFE_INTEGER)
179
+ ? Number(source)
180
+ : source;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Exact conversions for i128 (JavaScript bigint).
186
+ */
187
+ export class ExactI128 {
188
+ static readonly MIN = -(2n ** 127n);
189
+ static readonly MAX = 2n ** 127n - 1n;
190
+
191
+ static exactFromF16(source: number): bigint | undefined {
192
+ if (!Number.isFinite(source)) return undefined;
193
+ // A Float16 value, if finite, is always in-range for 128-bit signed integer types
194
+ if (hasFract(source)) return undefined;
195
+ return BigInt(Math.trunc(source));
196
+ }
197
+
198
+ static exactFromF32(source: number): bigint | undefined {
199
+ if (!Number.isFinite(source)) return undefined;
200
+ // f32::MIN and f32::MAX are both in range of i128
201
+ if (hasFract(source)) return undefined;
202
+ return BigInt(Math.trunc(source));
203
+ }
204
+
205
+ static exactFromF64(source: number): bigint | undefined {
206
+ if (!Number.isFinite(source)) return undefined;
207
+ // f64::MIN and f64::MAX are both in range of i128
208
+ if (hasFract(source)) return undefined;
209
+ return BigInt(Math.trunc(source));
210
+ }
211
+
212
+ static exactFromU64(source: number | bigint): bigint | undefined {
213
+ return BigInt(source);
214
+ }
215
+
216
+ static exactFromI64(source: number | bigint): bigint | undefined {
217
+ return BigInt(source);
218
+ }
219
+
220
+ static exactFromU128(source: bigint): bigint | undefined {
221
+ const max = 2n ** 127n - 1n;
222
+ if (source > max) return undefined;
223
+ return source;
224
+ }
225
+
226
+ static exactFromI128(source: bigint): bigint | undefined {
227
+ // Identity conversion
228
+ return source;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Exact conversions for u16 (0 to 65535).
234
+ */
235
+ export class ExactU16 {
236
+ static readonly MIN = 0;
237
+ static readonly MAX = 65535;
238
+
239
+ static exactFromF16(source: number): number | undefined {
240
+ if (!Number.isFinite(source)) return undefined;
241
+ // A Float16 value, if greater than -1 and finite, is always in-range for 16-bit unsigned integer types
242
+ if (source <= -1.0) return undefined;
243
+ if (hasFract(source)) return undefined;
244
+ return Math.trunc(source);
245
+ }
246
+
247
+ static exactFromF32(source: number): number | undefined {
248
+ if (!Number.isFinite(source)) return undefined;
249
+ if (source <= -1.0 || source >= 65536.0) return undefined;
250
+ if (hasFract(source)) return undefined;
251
+ return Math.trunc(source);
252
+ }
253
+
254
+ static exactFromF64(source: number): number | undefined {
255
+ if (!Number.isFinite(source)) return undefined;
256
+ if (source <= -1.0 || source >= 65536.0) return undefined;
257
+ if (hasFract(source)) return undefined;
258
+ return Math.trunc(source);
259
+ }
260
+
261
+ static exactFromU64(source: number | bigint): number | undefined {
262
+ const n = typeof source === "bigint" ? Number(source) : source;
263
+ if (n > 65535) return undefined;
264
+ return n;
265
+ }
266
+
267
+ static exactFromI64(source: number | bigint): number | undefined {
268
+ const n = typeof source === "bigint" ? Number(source) : source;
269
+ if (n < 0 || n > 65535) return undefined;
270
+ return n;
271
+ }
272
+
273
+ static exactFromU128(source: bigint): number | undefined {
274
+ if (source > 65535n) return undefined;
275
+ return Number(source);
276
+ }
277
+
278
+ static exactFromI128(source: bigint): number | undefined {
279
+ if (source < 0n || source > 65535n) return undefined;
280
+ return Number(source);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Exact conversions for u32 (0 to 4294967295).
286
+ */
287
+ export class ExactU32 {
288
+ static readonly MIN = 0;
289
+ static readonly MAX = 4294967295;
290
+
291
+ static exactFromF16(source: number): number | undefined {
292
+ if (!Number.isFinite(source)) return undefined;
293
+ // A Float16 value, if greater than -1 and finite, is always in-range for 32-bit unsigned integer types
294
+ if (source <= -1.0) return undefined;
295
+ if (hasFract(source)) return undefined;
296
+ return Math.trunc(source);
297
+ }
298
+
299
+ static exactFromF32(source: number): number | undefined {
300
+ if (!Number.isFinite(source)) return undefined;
301
+ if (source <= -1.0 || source >= 4294967296.0) return undefined;
302
+ if (hasFract(source)) return undefined;
303
+ return Math.trunc(source);
304
+ }
305
+
306
+ static exactFromF64(source: number): number | undefined {
307
+ if (!Number.isFinite(source)) return undefined;
308
+ if (source <= -1.0 || source >= 4294967296.0) return undefined;
309
+ if (hasFract(source)) return undefined;
310
+ return Math.trunc(source);
311
+ }
312
+
313
+ static exactFromU64(source: number | bigint): number | undefined {
314
+ const n = typeof source === "bigint" ? Number(source) : source;
315
+ if (n > 4294967295) return undefined;
316
+ return n;
317
+ }
318
+
319
+ static exactFromI64(source: number | bigint): number | undefined {
320
+ const n = typeof source === "bigint" ? Number(source) : source;
321
+ if (n < 0 || n > 4294967295) return undefined;
322
+ return n;
323
+ }
324
+
325
+ static exactFromU128(source: bigint): number | undefined {
326
+ if (source > 4294967295n) return undefined;
327
+ return Number(source);
328
+ }
329
+
330
+ static exactFromI128(source: bigint): number | undefined {
331
+ if (source < 0n || source > 4294967295n) return undefined;
332
+ return Number(source);
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Exact conversions for u64 (0 to 18446744073709551615).
338
+ */
339
+ export class ExactU64 {
340
+ static readonly MIN = 0n;
341
+ static readonly MAX = 18446744073709551615n;
342
+
343
+ static exactFromF16(source: number): number | bigint | undefined {
344
+ if (!Number.isFinite(source)) return undefined;
345
+ // A Float16 value, if greater than -1 and finite, is always in-range for 64-bit unsigned integer types
346
+ if (source <= -1.0) return undefined;
347
+ if (hasFract(source)) return undefined;
348
+ return Math.trunc(source);
349
+ }
350
+
351
+ static exactFromF32(source: number): number | bigint | undefined {
352
+ if (!Number.isFinite(source)) return undefined;
353
+ if (source <= -1.0 || source >= 18446744073709551616.0) return undefined;
354
+ if (hasFract(source)) return undefined;
355
+ const result = Math.trunc(source);
356
+ return result > Number.MAX_SAFE_INTEGER ? BigInt(result) : result;
357
+ }
358
+
359
+ static exactFromF64(source: number): number | bigint | undefined {
360
+ if (!Number.isFinite(source)) return undefined;
361
+ if (source <= -1.0 || source >= 18446744073709551616.0) return undefined;
362
+ if (hasFract(source)) return undefined;
363
+ const result = Math.trunc(source);
364
+ return result > Number.MAX_SAFE_INTEGER ? BigInt(result) : result;
365
+ }
366
+
367
+ static exactFromU64(source: number | bigint): number | bigint | undefined {
368
+ // Identity conversion
369
+ return source;
370
+ }
371
+
372
+ static exactFromI64(source: number | bigint): number | bigint | undefined {
373
+ const n = typeof source === "bigint" ? source : BigInt(source);
374
+ if (n < 0n) return undefined;
375
+ return source;
376
+ }
377
+
378
+ static exactFromU128(source: bigint): number | bigint | undefined {
379
+ if (source > 18446744073709551615n) return undefined;
380
+ return source <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(source) : source;
381
+ }
382
+
383
+ static exactFromI128(source: bigint): number | bigint | undefined {
384
+ if (source < 0n || source > 18446744073709551615n) return undefined;
385
+ return source <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(source) : source;
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Exact conversions for u128 (JavaScript bigint, unsigned).
391
+ */
392
+ export class ExactU128 {
393
+ static readonly MIN = 0n;
394
+ static readonly MAX = 2n ** 128n - 1n;
395
+
396
+ static exactFromF16(source: number): bigint | undefined {
397
+ if (!Number.isFinite(source)) return undefined;
398
+ if (source <= -1.0) return undefined;
399
+ if (hasFract(source)) return undefined;
400
+ return BigInt(Math.trunc(source));
401
+ }
402
+
403
+ static exactFromF32(source: number): bigint | undefined {
404
+ if (!Number.isFinite(source)) return undefined;
405
+ if (source <= -1.0) return undefined;
406
+ if (hasFract(source)) return undefined;
407
+ return BigInt(Math.trunc(source));
408
+ }
409
+
410
+ static exactFromF64(source: number): bigint | undefined {
411
+ if (!Number.isFinite(source)) return undefined;
412
+ if (source <= -1.0) return undefined;
413
+ if (hasFract(source)) return undefined;
414
+ return BigInt(Math.trunc(source));
415
+ }
416
+
417
+ static exactFromU64(source: number | bigint): bigint | undefined {
418
+ return BigInt(source);
419
+ }
420
+
421
+ static exactFromI64(source: number | bigint): bigint | undefined {
422
+ const n = typeof source === "bigint" ? source : BigInt(source);
423
+ if (n < 0n) return undefined;
424
+ return n;
425
+ }
426
+
427
+ static exactFromU128(source: bigint): bigint | undefined {
428
+ // Identity conversion
429
+ return source;
430
+ }
431
+
432
+ static exactFromI128(source: bigint): bigint | undefined {
433
+ if (source < 0n) return undefined;
434
+ return source;
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Exact conversions for f16 (half precision float).
440
+ * In TypeScript, we work with f16 as raw bytes (Uint8Array of length 2).
441
+ */
442
+ export class ExactF16 {
443
+ static exactFromF16(source: number): number | undefined {
444
+ if (Number.isNaN(source)) return NaN;
445
+ return source;
446
+ }
447
+
448
+ static exactFromF32(source: number): number | undefined {
449
+ if (Number.isNaN(source)) return NaN;
450
+ if (!Number.isFinite(source)) return source; // Infinity
451
+
452
+ // Convert to f16 and back to check exactness
453
+ const f16Bytes = numberToBinary16(source);
454
+ const roundTrip = binary16ToNumber(f16Bytes);
455
+ return roundTrip === source ? roundTrip : undefined;
456
+ }
457
+
458
+ static exactFromF64(source: number): number | undefined {
459
+ if (Number.isNaN(source)) return NaN;
460
+ if (!Number.isFinite(source)) return source; // Infinity
461
+
462
+ // Convert to f16 and back to check exactness
463
+ const f16Bytes = numberToBinary16(source);
464
+ const roundTrip = binary16ToNumber(f16Bytes);
465
+ return roundTrip === source ? roundTrip : undefined;
466
+ }
467
+
468
+ static exactFromU64(source: number | bigint): number | undefined {
469
+ const n = typeof source === "bigint" ? Number(source) : source;
470
+ const f16Bytes = numberToBinary16(n);
471
+ const f = binary16ToNumber(f16Bytes);
472
+ if (!Number.isFinite(f)) return undefined;
473
+ const roundTrip = typeof source === "bigint" ? BigInt(Math.trunc(f)) : Math.trunc(f);
474
+ return roundTrip === source ? f : undefined;
475
+ }
476
+
477
+ static exactFromI64(source: number | bigint): number | undefined {
478
+ const n = typeof source === "bigint" ? Number(source) : source;
479
+ const f16Bytes = numberToBinary16(n);
480
+ const f = binary16ToNumber(f16Bytes);
481
+ if (!Number.isFinite(f)) return undefined;
482
+ const roundTrip = typeof source === "bigint" ? BigInt(Math.trunc(f)) : Math.trunc(f);
483
+ return roundTrip === source ? f : undefined;
484
+ }
485
+
486
+ static exactFromU128(source: bigint): number | undefined {
487
+ const n = Number(source);
488
+ const f16Bytes = numberToBinary16(n);
489
+ const f = binary16ToNumber(f16Bytes);
490
+ if (!Number.isFinite(f)) return undefined;
491
+ const roundTrip = BigInt(Math.trunc(f));
492
+ return roundTrip === source ? f : undefined;
493
+ }
494
+
495
+ static exactFromI128(source: bigint): number | undefined {
496
+ const n = Number(source);
497
+ const f16Bytes = numberToBinary16(n);
498
+ const f = binary16ToNumber(f16Bytes);
499
+ if (!Number.isFinite(f)) return undefined;
500
+ const roundTrip = BigInt(Math.trunc(f));
501
+ return roundTrip === source ? f : undefined;
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Exact conversions for f32 (single precision float).
507
+ */
508
+ export class ExactF32 {
509
+ static exactFromF16(source: number): number | undefined {
510
+ if (Number.isNaN(source)) return NaN;
511
+ return source; // f16 always fits in f32
512
+ }
513
+
514
+ static exactFromF32(source: number): number | undefined {
515
+ if (Number.isNaN(source)) return NaN;
516
+ return source;
517
+ }
518
+
519
+ static exactFromF64(source: number): number | undefined {
520
+ if (Number.isNaN(source)) return NaN;
521
+ // JavaScript numbers are f64, need to check if it fits in f32 exactly
522
+ const f32Bytes = numberToBinary32(source);
523
+ const roundTrip = binary32ToNumber(f32Bytes);
524
+ return roundTrip === source ? roundTrip : undefined;
525
+ }
526
+
527
+ static exactFromU64(source: number | bigint): number | undefined {
528
+ const n = typeof source === "bigint" ? Number(source) : source;
529
+ const f32Bytes = numberToBinary32(n);
530
+ const f = binary32ToNumber(f32Bytes);
531
+ const roundTrip = typeof source === "bigint" ? BigInt(Math.trunc(f)) : Math.trunc(f);
532
+ return roundTrip === source ? f : undefined;
533
+ }
534
+
535
+ static exactFromI64(source: number | bigint): number | undefined {
536
+ const n = typeof source === "bigint" ? Number(source) : source;
537
+ const f32Bytes = numberToBinary32(n);
538
+ const f = binary32ToNumber(f32Bytes);
539
+ const roundTrip = typeof source === "bigint" ? BigInt(Math.trunc(f)) : Math.trunc(f);
540
+ return roundTrip === source ? f : undefined;
541
+ }
542
+
543
+ static exactFromU128(source: bigint): number | undefined {
544
+ const n = Number(source);
545
+ const f32Bytes = numberToBinary32(n);
546
+ const f = binary32ToNumber(f32Bytes);
547
+ if (!Number.isFinite(f)) return undefined;
548
+ const roundTrip = BigInt(Math.trunc(f));
549
+ return roundTrip === source ? f : undefined;
550
+ }
551
+
552
+ static exactFromI128(source: bigint): number | undefined {
553
+ // Check the range
554
+ if (source < -0x8000_0000n || source > 0x7fff_ffffn) return undefined;
555
+
556
+ const absSource = source < 0n ? -source : source;
557
+
558
+ // Check the magnitude
559
+ if (absSource <= 0xff_ffffn) {
560
+ return Number(source);
561
+ }
562
+
563
+ // Check divisibility by powers of 2
564
+ const trailingZeros = countTrailingZeros(absSource);
565
+ if (trailingZeros >= 25 && trailingZeros <= 31) {
566
+ return Number(source);
567
+ }
568
+
569
+ return undefined;
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Exact conversions for f64 (double precision float).
575
+ */
576
+ export class ExactF64 {
577
+ static exactFromF16(source: number): number | undefined {
578
+ if (Number.isNaN(source)) return NaN;
579
+ return source; // f16 always fits in f64
580
+ }
581
+
582
+ static exactFromF32(source: number): number | undefined {
583
+ if (Number.isNaN(source)) return NaN;
584
+ return source; // f32 always fits in f64
585
+ }
586
+
587
+ static exactFromF64(source: number): number | undefined {
588
+ if (Number.isNaN(source)) return NaN;
589
+ return source;
590
+ }
591
+
592
+ static exactFromU64(source: number | bigint): number | undefined {
593
+ const n = typeof source === "bigint" ? Number(source) : source;
594
+ const roundTrip = typeof source === "bigint" ? BigInt(Math.trunc(n)) : Math.trunc(n);
595
+ return roundTrip === source ? n : undefined;
596
+ }
597
+
598
+ static exactFromI64(source: number | bigint): number | undefined {
599
+ const n = typeof source === "bigint" ? Number(source) : source;
600
+ const roundTrip = typeof source === "bigint" ? BigInt(Math.trunc(n)) : Math.trunc(n);
601
+ return roundTrip === source ? n : undefined;
602
+ }
603
+
604
+ static exactFromU128(source: bigint): number | undefined {
605
+ const n = Number(source);
606
+ const roundTrip = BigInt(Math.trunc(n));
607
+ return roundTrip === source ? n : undefined;
608
+ }
609
+
610
+ static exactFromI128(source: bigint): number | undefined {
611
+ // Check the range
612
+ if (source < -0x8000_0000_0000_0000n || source > 0x7fff_ffff_ffff_ffffn) {
613
+ return undefined;
614
+ }
615
+
616
+ const absSource = source < 0n ? -source : source;
617
+
618
+ // Check the magnitude
619
+ if (absSource <= 0xf_ffff_ffff_ffffn) {
620
+ return Number(source);
621
+ }
622
+
623
+ // Check divisibility by powers of 2
624
+ const trailingZeros = countTrailingZeros(absSource);
625
+ if (trailingZeros >= 53 && trailingZeros <= 63) {
626
+ return Number(source);
627
+ }
628
+
629
+ return undefined;
630
+ }
631
+ }
632
+
633
+ // Helper function to count trailing zeros in a bigint
634
+ const countTrailingZeros = (n: bigint): number => {
635
+ if (n === 0n) return 0;
636
+ let count = 0;
637
+ while ((n & 1n) === 0n) {
638
+ count++;
639
+ n = n >> 1n;
640
+ }
641
+ return count;
642
+ };
643
+
644
+ // ============================================================================
645
+ // CBOR Type Extraction Utilities
646
+ // ============================================================================
647
+
648
+ import { type Cbor, MajorType } from "./cbor";
649
+
650
+ /**
651
+ * Extract exact unsigned integer value.
652
+ * Returns undefined if not an unsigned integer.
653
+ */
654
+ export const exactUnsigned = (cbor: Cbor): number | bigint | undefined => {
655
+ if (cbor.type === MajorType.Unsigned) {
656
+ return cbor.value;
657
+ }
658
+ return undefined;
659
+ };
660
+
661
+ /**
662
+ * Extract exact negative integer value (as actual negative number).
663
+ * Returns undefined if not a negative integer.
664
+ */
665
+ export const exactNegative = (cbor: Cbor): number | bigint | undefined => {
666
+ if (cbor.type === MajorType.Negative) {
667
+ if (typeof cbor.value === "bigint") {
668
+ return -(cbor.value + 1n);
669
+ }
670
+ return -(cbor.value + 1);
671
+ }
672
+ return undefined;
673
+ };
674
+
675
+ /**
676
+ * Extract exact integer value (unsigned or negative).
677
+ * Returns undefined if not an integer.
678
+ */
679
+ export const exactInteger = (cbor: Cbor): number | bigint | undefined => {
680
+ return exactUnsigned(cbor) ?? exactNegative(cbor);
681
+ };
682
+
683
+ /**
684
+ * Extract exact string value.
685
+ * Returns undefined if not a text string.
686
+ */
687
+ export const exactString = (cbor: Cbor): string | undefined => {
688
+ if (cbor.type === MajorType.Text) {
689
+ return cbor.value;
690
+ }
691
+ return undefined;
692
+ };
693
+
694
+ /**
695
+ * Extract exact byte string value.
696
+ * Returns undefined if not a byte string.
697
+ */
698
+ export const exactBytes = (cbor: Cbor): Uint8Array | undefined => {
699
+ if (cbor.type === MajorType.ByteString) {
700
+ return cbor.value;
701
+ }
702
+ return undefined;
703
+ };
704
+
705
+ /**
706
+ * Extract exact array value.
707
+ * Returns undefined if not an array.
708
+ */
709
+ export const exactArray = (cbor: Cbor): readonly Cbor[] | undefined => {
710
+ if (cbor.type === MajorType.Array) {
711
+ return cbor.value;
712
+ }
713
+ return undefined;
714
+ };