@marcuspuchalla/nachos 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/LICENSE +674 -0
- package/README.md +345 -0
- package/dist/chunk-2FUTHZQQ.cjs +755 -0
- package/dist/chunk-2FUTHZQQ.cjs.map +1 -0
- package/dist/chunk-2HBCILJS.cjs +2034 -0
- package/dist/chunk-2HBCILJS.cjs.map +1 -0
- package/dist/chunk-7CFYWHS6.js +742 -0
- package/dist/chunk-7CFYWHS6.js.map +1 -0
- package/dist/chunk-PD72MVTX.cjs +160 -0
- package/dist/chunk-PD72MVTX.cjs.map +1 -0
- package/dist/chunk-ZDZ2B5PE.js +149 -0
- package/dist/chunk-ZDZ2B5PE.js.map +1 -0
- package/dist/chunk-ZRPJUEIZ.js +2020 -0
- package/dist/chunk-ZRPJUEIZ.js.map +1 -0
- package/dist/encoder/index.cjs +57 -0
- package/dist/encoder/index.cjs.map +1 -0
- package/dist/encoder/index.d.cts +72 -0
- package/dist/encoder/index.d.ts +72 -0
- package/dist/encoder/index.js +4 -0
- package/dist/encoder/index.js.map +1 -0
- package/dist/index.cjs +606 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +494 -0
- package/dist/index.d.ts +494 -0
- package/dist/index.js +523 -0
- package/dist/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/dist/parser/index.cjs +85 -0
- package/dist/parser/index.cjs.map +1 -0
- package/dist/parser/index.d.cts +72 -0
- package/dist/parser/index.d.ts +72 -0
- package/dist/parser/index.js +4 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/types-DvNlfbKB.d.cts +301 -0
- package/dist/types-DvNlfbKB.d.ts +301 -0
- package/dist/useCborSimpleEncoder-ButVU988.d.cts +268 -0
- package/dist/useCborSimpleEncoder-TVxzNJ_9.d.ts +268 -0
- package/dist/useCborTag-B_iaShG6.d.ts +142 -0
- package/dist/useCborTag-BfTIV8HM.d.cts +142 -0
- package/package.json +102 -0
- package/src/__tests__/public-api.test.ts +326 -0
- package/src/encoder/__tests__/cbor-collection-encoder.test.ts +331 -0
- package/src/encoder/__tests__/cbor-integer-encoder.test.ts +283 -0
- package/src/encoder/__tests__/cbor-simple-encoder.test.ts +224 -0
- package/src/encoder/__tests__/cbor-string-encoder.test.ts +345 -0
- package/src/encoder/__tests__/cbor-tag-encoder.test.ts +565 -0
- package/src/encoder/composables/#useCborTagEncoder.ts# +158 -0
- package/src/encoder/composables/useCborCollectionEncoder.ts +424 -0
- package/src/encoder/composables/useCborEncoder.ts +203 -0
- package/src/encoder/composables/useCborIntegerEncoder.ts +188 -0
- package/src/encoder/composables/useCborSimpleEncoder.ts +266 -0
- package/src/encoder/composables/useCborStringEncoder.ts +266 -0
- package/src/encoder/composables/useCborTagEncoder.ts +158 -0
- package/src/encoder/index.ts +35 -0
- package/src/encoder/types.ts +88 -0
- package/src/encoder/utils.ts +80 -0
- package/src/index.ts +434 -0
- package/src/parser/__tests__/ast-tree-structure.test.ts +311 -0
- package/src/parser/__tests__/cbor-collection-errors.test.ts +296 -0
- package/src/parser/__tests__/cbor-collection.test.ts +369 -0
- package/src/parser/__tests__/cbor-deterministic-encoding.test.ts +432 -0
- package/src/parser/__tests__/cbor-diagnostic.test.ts +333 -0
- package/src/parser/__tests__/cbor-duplicate-keys.test.ts +235 -0
- package/src/parser/__tests__/cbor-float-errors.test.ts +222 -0
- package/src/parser/__tests__/cbor-float.test.ts +502 -0
- package/src/parser/__tests__/cbor-integer-errors.test.ts +139 -0
- package/src/parser/__tests__/cbor-integer.test.ts +200 -0
- package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +403 -0
- package/src/parser/__tests__/cbor-parser-errors.test.ts +126 -0
- package/src/parser/__tests__/cbor-security-dos-protection.test.ts +503 -0
- package/src/parser/__tests__/cbor-sequences.test.ts +150 -0
- package/src/parser/__tests__/cbor-source-map.test.ts +321 -0
- package/src/parser/__tests__/cbor-standard-tags.test.ts +340 -0
- package/src/parser/__tests__/cbor-string-errors.test.ts +227 -0
- package/src/parser/__tests__/cbor-string.test.ts +224 -0
- package/src/parser/__tests__/cbor-tag-advanced.test.ts +500 -0
- package/src/parser/__tests__/cbor-tag-errors.test.ts +447 -0
- package/src/parser/__tests__/cbor-tag-source-map.test.ts +360 -0
- package/src/parser/__tests__/cbor-tag.test.ts +684 -0
- package/src/parser/__tests__/extreme-edge-cases.test.ts +146 -0
- package/src/parser/__tests__/pathBuilder.test.ts +256 -0
- package/src/parser/__tests__/rfc-test-vectors.test.ts +607 -0
- package/src/parser/__tests__/security-limits.test.ts +248 -0
- package/src/parser/__tests__/utils-errors.test.ts +127 -0
- package/src/parser/composables/useCborCollection.ts +509 -0
- package/src/parser/composables/useCborDiagnostic.ts +381 -0
- package/src/parser/composables/useCborFloat.ts +256 -0
- package/src/parser/composables/useCborInteger.ts +114 -0
- package/src/parser/composables/useCborParser.ts +951 -0
- package/src/parser/composables/useCborString.ts +330 -0
- package/src/parser/composables/useCborStringTypes.ts +129 -0
- package/src/parser/composables/useCborTag.ts +739 -0
- package/src/parser/index.ts +56 -0
- package/src/parser/types.ts +371 -0
- package/src/parser/utils/pathBuilder.ts +259 -0
- package/src/parser/utils.ts +398 -0
- package/src/utils/__tests__/logger.test.ts +186 -0
- package/src/utils/logger.ts +96 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBOR Advanced Tag Tests - Semantic Interpretation & Validation
|
|
3
|
+
* Tests for Tag 258 (Set), Tag 4 (Decimal Fraction), Tag 5 (Bigfloat)
|
|
4
|
+
* Following TDD principles with real-world CBOR examples
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest'
|
|
8
|
+
import { useCborTag } from '../composables/useCborTag'
|
|
9
|
+
|
|
10
|
+
describe('useCborTag - Advanced Semantic Tags', () => {
|
|
11
|
+
describe('Tag 258: Mathematical Finite Set (with uniqueness validation)', () => {
|
|
12
|
+
describe('Valid sets (no duplicates)', () => {
|
|
13
|
+
it('should parse set of unique integers', () => {
|
|
14
|
+
const { parseTag } = useCborTag()
|
|
15
|
+
|
|
16
|
+
// 258([1, 2, 3]) - unique integers
|
|
17
|
+
// d9 0102 = tag 258 (2-byte), 83 = array(3)
|
|
18
|
+
const result = parseTag('d9010283010203')
|
|
19
|
+
|
|
20
|
+
expect(result.value.tag).toBe(258)
|
|
21
|
+
expect(result.value.value).toEqual([1, 2, 3])
|
|
22
|
+
expect(result.bytesRead).toBe(7)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should parse set of unique strings', () => {
|
|
26
|
+
const { parseTag } = useCborTag()
|
|
27
|
+
|
|
28
|
+
// 258(["a", "b", "c"]) - unique strings
|
|
29
|
+
const result = parseTag('d9010283616161626163')
|
|
30
|
+
|
|
31
|
+
expect(result.value.tag).toBe(258)
|
|
32
|
+
expect(result.value.value).toEqual(['a', 'b', 'c'])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should parse empty set', () => {
|
|
36
|
+
const { parseTag } = useCborTag()
|
|
37
|
+
|
|
38
|
+
// 258([]) - empty set (always valid)
|
|
39
|
+
const result = parseTag('d9010280')
|
|
40
|
+
|
|
41
|
+
expect(result.value.tag).toBe(258)
|
|
42
|
+
expect(result.value.value).toEqual([])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should parse single-element set', () => {
|
|
46
|
+
const { parseTag } = useCborTag()
|
|
47
|
+
|
|
48
|
+
// 258([42]) - single element
|
|
49
|
+
// d9 0102 = tag 258, 81 = array(1), 18 2a = value 42
|
|
50
|
+
const result = parseTag('d901028118 2a'.replace(/\s/g, ''))
|
|
51
|
+
|
|
52
|
+
expect(result.value.tag).toBe(258)
|
|
53
|
+
expect(result.value.value).toEqual([42])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should parse set with mixed types (valid if unique)', () => {
|
|
57
|
+
const { parseTag } = useCborTag()
|
|
58
|
+
|
|
59
|
+
// 258([1, "a", true]) - mixed types, all unique
|
|
60
|
+
// d9 0102 = tag 258, 83 = array(3)
|
|
61
|
+
// 01 = 1, 6161 = "a", f5 = true
|
|
62
|
+
const result = parseTag('d9010283016161f5')
|
|
63
|
+
|
|
64
|
+
expect(result.value.tag).toBe(258)
|
|
65
|
+
expect(result.value.value).toEqual([1, 'a', true])
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('Invalid sets (with duplicates) - strict mode', () => {
|
|
70
|
+
it('should reject set with duplicate integers in strict mode', () => {
|
|
71
|
+
const { parseTag } = useCborTag()
|
|
72
|
+
|
|
73
|
+
// 258([1, 2, 1]) - duplicate value 1
|
|
74
|
+
const maliciousHex = 'd9010283010201'
|
|
75
|
+
|
|
76
|
+
expect(() => parseTag(maliciousHex, { strict: true }))
|
|
77
|
+
.toThrow(/duplicate.*set/i)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should reject set with duplicate strings in strict mode', () => {
|
|
81
|
+
const { parseTag } = useCborTag()
|
|
82
|
+
|
|
83
|
+
// 258(["a", "b", "a"]) - duplicate "a"
|
|
84
|
+
const maliciousHex = 'd901028361616162616161'
|
|
85
|
+
|
|
86
|
+
expect(() => parseTag(maliciousHex, { strict: true }))
|
|
87
|
+
.toThrow(/duplicate.*set/i)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should reject set with all identical elements in strict mode', () => {
|
|
91
|
+
const { parseTag } = useCborTag()
|
|
92
|
+
|
|
93
|
+
// 258([5, 5, 5, 5]) - all duplicates
|
|
94
|
+
const maliciousHex = 'd901028405050505'
|
|
95
|
+
|
|
96
|
+
expect(() => parseTag(maliciousHex, { strict: true }))
|
|
97
|
+
.toThrow(/duplicate.*set/i)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should reject set with duplicate boolean values in strict mode', () => {
|
|
101
|
+
const { parseTag } = useCborTag()
|
|
102
|
+
|
|
103
|
+
// 258([true, false, true]) - duplicate true
|
|
104
|
+
const maliciousHex = 'd9010283f5f4f5'
|
|
105
|
+
|
|
106
|
+
expect(() => parseTag(maliciousHex, { strict: true }))
|
|
107
|
+
.toThrow(/duplicate.*set/i)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should reject set with duplicate byte strings in strict mode', () => {
|
|
111
|
+
const { parseTag } = useCborTag()
|
|
112
|
+
|
|
113
|
+
// 258([h'0102', h'0304', h'0102']) - duplicate h'0102'
|
|
114
|
+
const maliciousHex = 'd901028342010242030442 0102'.replace(/\s/g, '')
|
|
115
|
+
|
|
116
|
+
expect(() => parseTag(maliciousHex, { strict: true }))
|
|
117
|
+
.toThrow(/duplicate.*set/i)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
describe('Duplicate detection (non-strict mode)', () => {
|
|
122
|
+
it('should allow duplicates in non-strict mode but still parse', () => {
|
|
123
|
+
const { parseTag } = useCborTag()
|
|
124
|
+
|
|
125
|
+
// 258([1, 2, 1]) - with duplicates
|
|
126
|
+
const result = parseTag('d9010283010201')
|
|
127
|
+
|
|
128
|
+
// Should parse successfully (not throw)
|
|
129
|
+
expect(result.value.tag).toBe(258)
|
|
130
|
+
expect(result.value.value).toEqual([1, 2, 1])
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should allow duplicates when validateSetUniqueness is explicitly false', () => {
|
|
134
|
+
const { parseTag } = useCborTag()
|
|
135
|
+
|
|
136
|
+
// 258(["x", "x"]) - duplicates allowed
|
|
137
|
+
const result = parseTag('d901028261786178', {
|
|
138
|
+
validateSetUniqueness: false
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
expect(result.value.tag).toBe(258)
|
|
142
|
+
expect(result.value.value).toEqual(['x', 'x'])
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('Edge cases', () => {
|
|
147
|
+
it('should handle set with nested arrays (compare by serialization)', () => {
|
|
148
|
+
const { parseTag } = useCborTag()
|
|
149
|
+
|
|
150
|
+
// 258([[1,2], [3,4]]) - nested arrays should be unique
|
|
151
|
+
const result = parseTag('d901028282010282 0304'.replace(/\s/g, ''))
|
|
152
|
+
|
|
153
|
+
expect(result.value.tag).toBe(258)
|
|
154
|
+
expect(result.value.value).toEqual([[1, 2], [3, 4]])
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should detect duplicate nested arrays in strict mode', () => {
|
|
158
|
+
const { parseTag } = useCborTag()
|
|
159
|
+
|
|
160
|
+
// 258([[1,2], [1,2]]) - duplicate arrays
|
|
161
|
+
const maliciousHex = 'd901028282010282 0102'.replace(/\s/g, '')
|
|
162
|
+
|
|
163
|
+
expect(() => parseTag(maliciousHex, { strict: true }))
|
|
164
|
+
.toThrow(/duplicate.*set/i)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should handle large sets efficiently', () => {
|
|
168
|
+
const { parseTag } = useCborTag()
|
|
169
|
+
|
|
170
|
+
// 258([0, 1, 2, ..., 9]) - 10 unique integers (simpler test)
|
|
171
|
+
// d9 0102 = tag 258, 8a = array(10), then 00-09
|
|
172
|
+
const hex = 'd901028a00010203040506070809'
|
|
173
|
+
|
|
174
|
+
const result = parseTag(hex)
|
|
175
|
+
|
|
176
|
+
expect(result.value.tag).toBe(258)
|
|
177
|
+
expect(Array.isArray(result.value.value)).toBe(true)
|
|
178
|
+
expect(result.value.value).toHaveLength(10)
|
|
179
|
+
expect(result.value.value[0]).toBe(0)
|
|
180
|
+
expect(result.value.value[9]).toBe(9)
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
describe('Cardano-specific set usage', () => {
|
|
185
|
+
it('should parse Cardano Plutus set of datums', () => {
|
|
186
|
+
const { parseTag } = useCborTag()
|
|
187
|
+
|
|
188
|
+
// 258([datum1, datum2]) - Set of Plutus datums
|
|
189
|
+
// Using simple integers as placeholder datums
|
|
190
|
+
const result = parseTag('d901028218641865')
|
|
191
|
+
|
|
192
|
+
expect(result.value.tag).toBe(258)
|
|
193
|
+
expect(result.value.value).toEqual([100, 101])
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('should validate uniqueness for Cardano asset names in set', () => {
|
|
197
|
+
const { parseTag } = useCborTag()
|
|
198
|
+
|
|
199
|
+
// 258(["TokenA", "TokenB", "TokenC"]) - unique asset names
|
|
200
|
+
const result = parseTag('d901028366546f6b656e4166546f6b656e4266546f6b656e43', {
|
|
201
|
+
strict: true
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
expect(result.value.tag).toBe(258)
|
|
205
|
+
expect(result.value.value).toEqual(['TokenA', 'TokenB', 'TokenC'])
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe('Tag 4: Decimal Fraction [exponent, mantissa]', () => {
|
|
211
|
+
describe('Basic decimal fraction parsing', () => {
|
|
212
|
+
it('should parse decimal fraction 273.15 as [exponent: -2, mantissa: 27315]', () => {
|
|
213
|
+
const { parseTag } = useCborTag()
|
|
214
|
+
|
|
215
|
+
// 4([-2, 27315]) represents 27315 * 10^(-2) = 273.15
|
|
216
|
+
// c4 = tag 4, 82 = array(2), 21 = -2, 196ab3 = 27315
|
|
217
|
+
const result = parseTag('c48221196ab3')
|
|
218
|
+
|
|
219
|
+
expect(result.value.tag).toBe(4)
|
|
220
|
+
expect(result.value.value).toEqual([-2, 27315])
|
|
221
|
+
|
|
222
|
+
// Semantic interpretation: mantissa * 10^exponent
|
|
223
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
224
|
+
const decimalValue = mantissa * Math.pow(10, exponent)
|
|
225
|
+
expect(decimalValue).toBeCloseTo(273.15, 10)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should parse decimal fraction 1.1 (not exactly representable in binary)', () => {
|
|
229
|
+
const { parseTag } = useCborTag()
|
|
230
|
+
|
|
231
|
+
// 4([-1, 11]) represents 11 * 10^(-1) = 1.1
|
|
232
|
+
// c4 = tag 4, 82 = array(2), 20 = -1, 0b = 11
|
|
233
|
+
const result = parseTag('c482200b')
|
|
234
|
+
|
|
235
|
+
expect(result.value.tag).toBe(4)
|
|
236
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
237
|
+
const decimalValue = mantissa * Math.pow(10, exponent)
|
|
238
|
+
expect(decimalValue).toBeCloseTo(1.1, 10)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('should parse positive exponent decimal', () => {
|
|
242
|
+
const { parseTag } = useCborTag()
|
|
243
|
+
|
|
244
|
+
// 4([2, 5]) represents 5 * 10^2 = 500
|
|
245
|
+
const result = parseTag('c4820205')
|
|
246
|
+
|
|
247
|
+
expect(result.value.tag).toBe(4)
|
|
248
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
249
|
+
expect(mantissa * Math.pow(10, exponent)).toBe(500)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('should parse zero exponent decimal', () => {
|
|
253
|
+
const { parseTag } = useCborTag()
|
|
254
|
+
|
|
255
|
+
// 4([0, 123]) represents 123 * 10^0 = 123
|
|
256
|
+
const result = parseTag('c48200187b')
|
|
257
|
+
|
|
258
|
+
expect(result.value.tag).toBe(4)
|
|
259
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
260
|
+
expect(mantissa * Math.pow(10, exponent)).toBe(123)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('should parse negative mantissa decimal', () => {
|
|
264
|
+
const { parseTag } = useCborTag()
|
|
265
|
+
|
|
266
|
+
// 4([-2, -273]) represents -273 * 10^(-2) = -2.73
|
|
267
|
+
// c4 = tag 4, 82 = array(2), 21 = -2, 39 0110 = -273
|
|
268
|
+
const result = parseTag('c4822139 0110'.replace(/\s/g, ''))
|
|
269
|
+
|
|
270
|
+
expect(result.value.tag).toBe(4)
|
|
271
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
272
|
+
expect(mantissa * Math.pow(10, exponent)).toBeCloseTo(-2.73, 10)
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
describe('Financial/monetary use cases', () => {
|
|
277
|
+
it('should parse price $19.99 as decimal fraction', () => {
|
|
278
|
+
const { parseTag } = useCborTag()
|
|
279
|
+
|
|
280
|
+
// 4([-2, 1999]) = 1999 * 10^(-2) = $19.99
|
|
281
|
+
const result = parseTag('c48221190 7cf'.replace(/\s/g, ''))
|
|
282
|
+
|
|
283
|
+
expect(result.value.tag).toBe(4)
|
|
284
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
285
|
+
expect(mantissa * Math.pow(10, exponent)).toBeCloseTo(19.99, 10)
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
it('should parse Cardano ADA amount 1000.50 as decimal fraction', () => {
|
|
289
|
+
const { parseTag } = useCborTag()
|
|
290
|
+
|
|
291
|
+
// 4([-2, 100050]) = 100050 * 10^(-2) = 1000.50 ADA
|
|
292
|
+
const result = parseTag('c482211a000186d2')
|
|
293
|
+
|
|
294
|
+
expect(result.value.tag).toBe(4)
|
|
295
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
296
|
+
expect(mantissa * Math.pow(10, exponent)).toBeCloseTo(1000.50, 10)
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
describe('Error cases', () => {
|
|
301
|
+
it('should reject decimal fraction with non-array value', () => {
|
|
302
|
+
const { parseTag } = useCborTag()
|
|
303
|
+
|
|
304
|
+
// 4(42) - invalid, must be array
|
|
305
|
+
expect(() => parseTag('c4182a', { validateTagSemantics: true }))
|
|
306
|
+
.toThrow(/decimal fraction.*array/i)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('should reject decimal fraction with wrong array length', () => {
|
|
310
|
+
const { parseTag } = useCborTag()
|
|
311
|
+
|
|
312
|
+
// 4([1, 2, 3]) - invalid, must be exactly 2 elements
|
|
313
|
+
expect(() => parseTag('c483010203', { validateTagSemantics: true }))
|
|
314
|
+
.toThrow(/decimal fraction.*exactly.*2/i)
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('should reject decimal fraction with non-integer exponent', () => {
|
|
318
|
+
const { parseTag } = useCborTag()
|
|
319
|
+
|
|
320
|
+
// 4(["x", 100]) - invalid, exponent must be integer
|
|
321
|
+
expect(() => parseTag('c4826178186 4'.replace(/\s/g, ''), { validateTagSemantics: true }))
|
|
322
|
+
.toThrow(/exponent.*integer/i)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('should reject decimal fraction with non-integer mantissa', () => {
|
|
326
|
+
const { parseTag } = useCborTag()
|
|
327
|
+
|
|
328
|
+
// 4([-2, "hello"]) - invalid, mantissa must be integer
|
|
329
|
+
expect(() => parseTag('c482216568656c6c6f', { validateTagSemantics: true }))
|
|
330
|
+
.toThrow(/mantissa.*integer/i)
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
describe('Tag 5: Bigfloat [exponent, mantissa]', () => {
|
|
336
|
+
describe('Basic bigfloat parsing', () => {
|
|
337
|
+
it('should parse bigfloat [exponent: -2, mantissa: 3]', () => {
|
|
338
|
+
const { parseTag } = useCborTag()
|
|
339
|
+
|
|
340
|
+
// 5([-2, 3]) represents 3 * 2^(-2) = 3 * 0.25 = 0.75
|
|
341
|
+
// c5 = tag 5, 82 = array(2), 21 = -2, 03 = 3
|
|
342
|
+
const result = parseTag('c5822103')
|
|
343
|
+
|
|
344
|
+
expect(result.value.tag).toBe(5)
|
|
345
|
+
expect(result.value.value).toEqual([-2, 3])
|
|
346
|
+
|
|
347
|
+
// Semantic interpretation: mantissa * 2^exponent
|
|
348
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
349
|
+
const floatValue = mantissa * Math.pow(2, exponent)
|
|
350
|
+
expect(floatValue).toBe(0.75)
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it('should parse bigfloat with positive exponent', () => {
|
|
354
|
+
const { parseTag } = useCborTag()
|
|
355
|
+
|
|
356
|
+
// 5([10, 1000000]) represents 1000000 * 2^10 = 1024000000
|
|
357
|
+
// c5 = tag 5, 82 = array(2), 0a = 10, 1a000f4240 = 1000000
|
|
358
|
+
const result = parseTag('c5820a1a000f4240')
|
|
359
|
+
|
|
360
|
+
expect(result.value.tag).toBe(5)
|
|
361
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
362
|
+
expect(mantissa * Math.pow(2, exponent)).toBe(1024000000)
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
it('should parse bigfloat with zero exponent', () => {
|
|
366
|
+
const { parseTag } = useCborTag()
|
|
367
|
+
|
|
368
|
+
// 5([0, 42]) represents 42 * 2^0 = 42
|
|
369
|
+
const result = parseTag('c5820018 2a'.replace(/\s/g, ''))
|
|
370
|
+
|
|
371
|
+
expect(result.value.tag).toBe(5)
|
|
372
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
373
|
+
expect(mantissa * Math.pow(2, exponent)).toBe(42)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('should parse bigfloat with negative mantissa', () => {
|
|
377
|
+
const { parseTag } = useCborTag()
|
|
378
|
+
|
|
379
|
+
// 5([-1, -8]) represents -8 * 2^(-1) = -4
|
|
380
|
+
const result = parseTag('c5822027')
|
|
381
|
+
|
|
382
|
+
expect(result.value.tag).toBe(5)
|
|
383
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
384
|
+
expect(mantissa * Math.pow(2, exponent)).toBe(-4)
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
it('should parse bigfloat with bigint mantissa (beyond Number.MAX_SAFE_INTEGER)', () => {
|
|
388
|
+
const { parseTag } = useCborTag()
|
|
389
|
+
|
|
390
|
+
// 5([0, 2^63]) - large mantissa
|
|
391
|
+
// c5 = tag 5, 82 = array(2), 00 = 0, 1b8000000000000000 = 2^63
|
|
392
|
+
const result = parseTag('c582001b8000000000000000')
|
|
393
|
+
|
|
394
|
+
expect(result.value.tag).toBe(5)
|
|
395
|
+
expect(result.value.value[0]).toBe(0)
|
|
396
|
+
expect(result.value.value[1]).toBe(BigInt('9223372036854775808'))
|
|
397
|
+
})
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
describe('High-precision scientific computing', () => {
|
|
401
|
+
it('should represent fractional values precisely', () => {
|
|
402
|
+
const { parseTag } = useCborTag()
|
|
403
|
+
|
|
404
|
+
// 5([-10, 1025]) = 1025 * 2^(-10) = 1025 / 1024 ≈ 1.0009765625
|
|
405
|
+
const result = parseTag('c5822919040 1'.replace(/\s/g, ''))
|
|
406
|
+
|
|
407
|
+
expect(result.value.tag).toBe(5)
|
|
408
|
+
const [exponent, mantissa] = result.value.value as [number, number]
|
|
409
|
+
expect(mantissa * Math.pow(2, exponent)).toBeCloseTo(1.0009765625, 15)
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it('should handle very large exponents', () => {
|
|
413
|
+
const { parseTag } = useCborTag()
|
|
414
|
+
|
|
415
|
+
// 5([1000, 1]) = 1 * 2^1000 (astronomically large)
|
|
416
|
+
const result = parseTag('c58219 03e801'.replace(/\s/g, ''))
|
|
417
|
+
|
|
418
|
+
expect(result.value.tag).toBe(5)
|
|
419
|
+
expect(result.value.value).toEqual([1000, 1])
|
|
420
|
+
// Don't compute the value - it's too large!
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
it('should handle very negative exponents (tiny values)', () => {
|
|
424
|
+
const { parseTag } = useCborTag()
|
|
425
|
+
|
|
426
|
+
// 5([-1000, 1]) = 1 * 2^(-1000) (infinitesimally small)
|
|
427
|
+
const result = parseTag('c5823903e701')
|
|
428
|
+
|
|
429
|
+
expect(result.value.tag).toBe(5)
|
|
430
|
+
expect(result.value.value).toEqual([-1000, 1])
|
|
431
|
+
// Don't compute - underflows to 0 in JavaScript
|
|
432
|
+
})
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
describe('Error cases', () => {
|
|
436
|
+
it('should reject bigfloat with non-array value', () => {
|
|
437
|
+
const { parseTag } = useCborTag()
|
|
438
|
+
|
|
439
|
+
// 5(100) - invalid, must be array
|
|
440
|
+
expect(() => parseTag('c51864', { validateTagSemantics: true }))
|
|
441
|
+
.toThrow(/bigfloat.*array/i)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('should reject bigfloat with wrong array length', () => {
|
|
445
|
+
const { parseTag } = useCborTag()
|
|
446
|
+
|
|
447
|
+
// 5([1]) - invalid, must be exactly 2 elements
|
|
448
|
+
expect(() => parseTag('c58101', { validateTagSemantics: true }))
|
|
449
|
+
.toThrow(/bigfloat.*exactly.*2/i)
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
it('should reject bigfloat with non-integer exponent', () => {
|
|
453
|
+
const { parseTag } = useCborTag()
|
|
454
|
+
|
|
455
|
+
// 5(["text", 100]) - invalid, exponent must be integer
|
|
456
|
+
expect(() => parseTag('c4826474657874186 4'.replace(/\s/g, ''), { validateTagSemantics: true }))
|
|
457
|
+
.toThrow(/exponent.*integer/i)
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
it('should reject bigfloat with non-integer mantissa', () => {
|
|
461
|
+
const { parseTag } = useCborTag()
|
|
462
|
+
|
|
463
|
+
// 5([0, true]) - invalid, mantissa must be integer
|
|
464
|
+
expect(() => parseTag('c58200f5', { validateTagSemantics: true }))
|
|
465
|
+
.toThrow(/mantissa.*integer/i)
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
describe('Integration: Complex nested structures with advanced tags', () => {
|
|
471
|
+
it('should parse set containing decimal fractions', () => {
|
|
472
|
+
const { parseTag } = useCborTag()
|
|
473
|
+
|
|
474
|
+
// 258([4([-2, 1000]), 4([-2, 2000]), 4([-2, 3000])])
|
|
475
|
+
// Set of prices: {$10.00, $20.00, $30.00}
|
|
476
|
+
const result = parseTag('d9010283c482211903e8c482211907d0c482211 90bb8'.replace(/\s/g, ''))
|
|
477
|
+
|
|
478
|
+
expect(result.value.tag).toBe(258)
|
|
479
|
+
expect(result.value.value).toHaveLength(3)
|
|
480
|
+
|
|
481
|
+
// Each element should be a tagged decimal fraction
|
|
482
|
+
const prices = result.value.value as Array<{ tag: number, value: [number, number] }>
|
|
483
|
+
prices.forEach(item => {
|
|
484
|
+
expect(item.tag).toBe(4)
|
|
485
|
+
expect(item.value).toHaveLength(2)
|
|
486
|
+
})
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
it('should parse set containing bigfloats', () => {
|
|
490
|
+
const { parseTag } = useCborTag()
|
|
491
|
+
|
|
492
|
+
// 258([5([-2, 1]), 5([-2, 3])])
|
|
493
|
+
// Set of binary fractions: {0.25, 0.75}
|
|
494
|
+
const result = parseTag('d9010282c5822101c5822103')
|
|
495
|
+
|
|
496
|
+
expect(result.value.tag).toBe(258)
|
|
497
|
+
expect(result.value.value).toHaveLength(2)
|
|
498
|
+
})
|
|
499
|
+
})
|
|
500
|
+
})
|