@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.
Files changed (100) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +674 -0
  3. package/README.md +345 -0
  4. package/dist/chunk-2FUTHZQQ.cjs +755 -0
  5. package/dist/chunk-2FUTHZQQ.cjs.map +1 -0
  6. package/dist/chunk-2HBCILJS.cjs +2034 -0
  7. package/dist/chunk-2HBCILJS.cjs.map +1 -0
  8. package/dist/chunk-7CFYWHS6.js +742 -0
  9. package/dist/chunk-7CFYWHS6.js.map +1 -0
  10. package/dist/chunk-PD72MVTX.cjs +160 -0
  11. package/dist/chunk-PD72MVTX.cjs.map +1 -0
  12. package/dist/chunk-ZDZ2B5PE.js +149 -0
  13. package/dist/chunk-ZDZ2B5PE.js.map +1 -0
  14. package/dist/chunk-ZRPJUEIZ.js +2020 -0
  15. package/dist/chunk-ZRPJUEIZ.js.map +1 -0
  16. package/dist/encoder/index.cjs +57 -0
  17. package/dist/encoder/index.cjs.map +1 -0
  18. package/dist/encoder/index.d.cts +72 -0
  19. package/dist/encoder/index.d.ts +72 -0
  20. package/dist/encoder/index.js +4 -0
  21. package/dist/encoder/index.js.map +1 -0
  22. package/dist/index.cjs +606 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +494 -0
  25. package/dist/index.d.ts +494 -0
  26. package/dist/index.js +523 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/metafile-cjs.json +1 -0
  29. package/dist/metafile-esm.json +1 -0
  30. package/dist/parser/index.cjs +85 -0
  31. package/dist/parser/index.cjs.map +1 -0
  32. package/dist/parser/index.d.cts +72 -0
  33. package/dist/parser/index.d.ts +72 -0
  34. package/dist/parser/index.js +4 -0
  35. package/dist/parser/index.js.map +1 -0
  36. package/dist/types-DvNlfbKB.d.cts +301 -0
  37. package/dist/types-DvNlfbKB.d.ts +301 -0
  38. package/dist/useCborSimpleEncoder-ButVU988.d.cts +268 -0
  39. package/dist/useCborSimpleEncoder-TVxzNJ_9.d.ts +268 -0
  40. package/dist/useCborTag-B_iaShG6.d.ts +142 -0
  41. package/dist/useCborTag-BfTIV8HM.d.cts +142 -0
  42. package/package.json +102 -0
  43. package/src/__tests__/public-api.test.ts +326 -0
  44. package/src/encoder/__tests__/cbor-collection-encoder.test.ts +331 -0
  45. package/src/encoder/__tests__/cbor-integer-encoder.test.ts +283 -0
  46. package/src/encoder/__tests__/cbor-simple-encoder.test.ts +224 -0
  47. package/src/encoder/__tests__/cbor-string-encoder.test.ts +345 -0
  48. package/src/encoder/__tests__/cbor-tag-encoder.test.ts +565 -0
  49. package/src/encoder/composables/#useCborTagEncoder.ts# +158 -0
  50. package/src/encoder/composables/useCborCollectionEncoder.ts +424 -0
  51. package/src/encoder/composables/useCborEncoder.ts +203 -0
  52. package/src/encoder/composables/useCborIntegerEncoder.ts +188 -0
  53. package/src/encoder/composables/useCborSimpleEncoder.ts +266 -0
  54. package/src/encoder/composables/useCborStringEncoder.ts +266 -0
  55. package/src/encoder/composables/useCborTagEncoder.ts +158 -0
  56. package/src/encoder/index.ts +35 -0
  57. package/src/encoder/types.ts +88 -0
  58. package/src/encoder/utils.ts +80 -0
  59. package/src/index.ts +434 -0
  60. package/src/parser/__tests__/ast-tree-structure.test.ts +311 -0
  61. package/src/parser/__tests__/cbor-collection-errors.test.ts +296 -0
  62. package/src/parser/__tests__/cbor-collection.test.ts +369 -0
  63. package/src/parser/__tests__/cbor-deterministic-encoding.test.ts +432 -0
  64. package/src/parser/__tests__/cbor-diagnostic.test.ts +333 -0
  65. package/src/parser/__tests__/cbor-duplicate-keys.test.ts +235 -0
  66. package/src/parser/__tests__/cbor-float-errors.test.ts +222 -0
  67. package/src/parser/__tests__/cbor-float.test.ts +502 -0
  68. package/src/parser/__tests__/cbor-integer-errors.test.ts +139 -0
  69. package/src/parser/__tests__/cbor-integer.test.ts +200 -0
  70. package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +403 -0
  71. package/src/parser/__tests__/cbor-parser-errors.test.ts +126 -0
  72. package/src/parser/__tests__/cbor-security-dos-protection.test.ts +503 -0
  73. package/src/parser/__tests__/cbor-sequences.test.ts +150 -0
  74. package/src/parser/__tests__/cbor-source-map.test.ts +321 -0
  75. package/src/parser/__tests__/cbor-standard-tags.test.ts +340 -0
  76. package/src/parser/__tests__/cbor-string-errors.test.ts +227 -0
  77. package/src/parser/__tests__/cbor-string.test.ts +224 -0
  78. package/src/parser/__tests__/cbor-tag-advanced.test.ts +500 -0
  79. package/src/parser/__tests__/cbor-tag-errors.test.ts +447 -0
  80. package/src/parser/__tests__/cbor-tag-source-map.test.ts +360 -0
  81. package/src/parser/__tests__/cbor-tag.test.ts +684 -0
  82. package/src/parser/__tests__/extreme-edge-cases.test.ts +146 -0
  83. package/src/parser/__tests__/pathBuilder.test.ts +256 -0
  84. package/src/parser/__tests__/rfc-test-vectors.test.ts +607 -0
  85. package/src/parser/__tests__/security-limits.test.ts +248 -0
  86. package/src/parser/__tests__/utils-errors.test.ts +127 -0
  87. package/src/parser/composables/useCborCollection.ts +509 -0
  88. package/src/parser/composables/useCborDiagnostic.ts +381 -0
  89. package/src/parser/composables/useCborFloat.ts +256 -0
  90. package/src/parser/composables/useCborInteger.ts +114 -0
  91. package/src/parser/composables/useCborParser.ts +951 -0
  92. package/src/parser/composables/useCborString.ts +330 -0
  93. package/src/parser/composables/useCborStringTypes.ts +129 -0
  94. package/src/parser/composables/useCborTag.ts +739 -0
  95. package/src/parser/index.ts +56 -0
  96. package/src/parser/types.ts +371 -0
  97. package/src/parser/utils/pathBuilder.ts +259 -0
  98. package/src/parser/utils.ts +398 -0
  99. package/src/utils/__tests__/logger.test.ts +186 -0
  100. package/src/utils/logger.ts +96 -0
@@ -0,0 +1,381 @@
1
+ /**
2
+ * useCborDiagnostic - RFC 8949 Appendix B Diagnostic Notation
3
+ *
4
+ * Converts CBOR values to human-readable diagnostic notation as defined
5
+ * in RFC 8949 (Concise Binary Object Representation).
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const { toDiagnostic } = useCborDiagnostic()
10
+ *
11
+ * toDiagnostic(100) // "100"
12
+ * toDiagnostic(new Uint8Array([1,2])) // "h'0102'"
13
+ * toDiagnostic([1, 2, 3]) // "[1, 2, 3]"
14
+ * toDiagnostic({a: 1}) // '{"a": 1}'
15
+ * ```
16
+ */
17
+
18
+ /**
19
+ * Options for diagnostic notation output
20
+ */
21
+ export interface DiagnosticOptions {
22
+ /** Pretty print with indentation (default: false) */
23
+ pretty?: boolean
24
+ /** Indentation string for pretty printing (default: ' ') */
25
+ indent?: string
26
+ /** Maximum depth for nested structures (default: 100) */
27
+ maxDepth?: number
28
+ /** Mark as indefinite-length (default: false) */
29
+ indefinite?: boolean
30
+ /** Show byte offsets as comments (default: false) */
31
+ showOffsets?: boolean
32
+ }
33
+
34
+ /**
35
+ * Tagged value interface (CBOR Major Type 6)
36
+ */
37
+ interface TaggedValue {
38
+ tag: number
39
+ value: unknown
40
+ }
41
+
42
+ /**
43
+ * Check if value is a tagged value
44
+ */
45
+ function isTaggedValue(value: unknown): value is TaggedValue {
46
+ return (
47
+ typeof value === 'object' &&
48
+ value !== null &&
49
+ 'tag' in value &&
50
+ 'value' in value &&
51
+ typeof (value as TaggedValue).tag === 'number'
52
+ )
53
+ }
54
+
55
+ /**
56
+ * Check if value is a plain object (not array, not special type)
57
+ */
58
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
59
+ return (
60
+ typeof value === 'object' &&
61
+ value !== null &&
62
+ !Array.isArray(value) &&
63
+ !(value instanceof Uint8Array) &&
64
+ !(value instanceof Map) &&
65
+ !(value instanceof Set) &&
66
+ !isTaggedValue(value) &&
67
+ value.constructor === Object
68
+ )
69
+ }
70
+
71
+ /**
72
+ * Escape string for diagnostic notation (JSON-style escaping)
73
+ */
74
+ function escapeString(str: string): string {
75
+ return str
76
+ .replace(/\\/g, '\\\\')
77
+ .replace(/"/g, '\\"')
78
+ .replace(/\n/g, '\\n')
79
+ .replace(/\r/g, '\\r')
80
+ .replace(/\t/g, '\\t')
81
+ .replace(/[\x00-\x1f\x7f-\x9f]/g, (char) => {
82
+ const code = char.charCodeAt(0)
83
+ return `\\u${code.toString(16).padStart(4, '0')}`
84
+ })
85
+ }
86
+
87
+ /**
88
+ * Convert Uint8Array to hex string
89
+ */
90
+ function bytesToHex(bytes: Uint8Array): string {
91
+ return Array.from(bytes)
92
+ .map(b => b.toString(16).padStart(2, '0'))
93
+ .join('')
94
+ }
95
+
96
+ /**
97
+ * Composable for CBOR diagnostic notation
98
+ */
99
+ export function useCborDiagnostic() {
100
+ /**
101
+ * Convert a CBOR value to RFC 8949 diagnostic notation
102
+ *
103
+ * @param value - The CBOR value to convert
104
+ * @param options - Formatting options
105
+ * @returns Diagnostic notation string
106
+ */
107
+ const toDiagnostic = (
108
+ value: unknown,
109
+ options: DiagnosticOptions = {}
110
+ ): string => {
111
+ const {
112
+ pretty = false,
113
+ indent = ' ',
114
+ maxDepth = 100,
115
+ indefinite = false
116
+ } = options
117
+
118
+ return formatValue(value, 0, pretty, indent, maxDepth, indefinite)
119
+ }
120
+
121
+ /**
122
+ * Internal recursive formatter
123
+ */
124
+ const formatValue = (
125
+ value: unknown,
126
+ depth: number,
127
+ pretty: boolean,
128
+ indent: string,
129
+ maxDepth: number,
130
+ indefinite: boolean
131
+ ): string => {
132
+ // Check depth limit
133
+ if (depth > maxDepth) {
134
+ return '...'
135
+ }
136
+
137
+ // Handle null/undefined
138
+ if (value === null) {
139
+ return 'null'
140
+ }
141
+ if (value === undefined) {
142
+ return 'undefined'
143
+ }
144
+
145
+ // Handle booleans
146
+ if (typeof value === 'boolean') {
147
+ return value ? 'true' : 'false'
148
+ }
149
+
150
+ // Handle numbers
151
+ if (typeof value === 'number') {
152
+ // Special float values
153
+ if (Number.isNaN(value)) {
154
+ return 'NaN'
155
+ }
156
+ if (value === Infinity) {
157
+ return 'Infinity'
158
+ }
159
+ if (value === -Infinity) {
160
+ return '-Infinity'
161
+ }
162
+ // Negative zero
163
+ if (Object.is(value, -0)) {
164
+ return '-0.0'
165
+ }
166
+ // Regular numbers
167
+ if (Number.isInteger(value)) {
168
+ return value.toString()
169
+ }
170
+ // Floats - preserve precision
171
+ return value.toString()
172
+ }
173
+
174
+ // Handle BigInt
175
+ if (typeof value === 'bigint') {
176
+ return value.toString()
177
+ }
178
+
179
+ // Handle strings
180
+ if (typeof value === 'string') {
181
+ return `"${escapeString(value)}"`
182
+ }
183
+
184
+ // Handle byte strings (Uint8Array)
185
+ if (value instanceof Uint8Array) {
186
+ return `h'${bytesToHex(value)}'`
187
+ }
188
+
189
+ // Handle tagged values
190
+ if (isTaggedValue(value)) {
191
+ const taggedContent = formatValue(
192
+ value.value,
193
+ depth + 1,
194
+ pretty,
195
+ indent,
196
+ maxDepth,
197
+ false
198
+ )
199
+ return `${value.tag}(${taggedContent})`
200
+ }
201
+
202
+ // Handle arrays
203
+ if (Array.isArray(value)) {
204
+ if (value.length === 0) {
205
+ return indefinite ? '[_ ]' : '[]'
206
+ }
207
+
208
+ const items = value.map(item =>
209
+ formatValue(item, depth + 1, pretty, indent, maxDepth, false)
210
+ )
211
+
212
+ if (pretty) {
213
+ const prefix = indefinite ? '[_ ' : '['
214
+ const lineIndent = indent.repeat(depth + 1)
215
+ const closeIndent = indent.repeat(depth)
216
+ return `${prefix}\n${lineIndent}${items.join(`,\n${lineIndent}`)}\n${closeIndent}]`
217
+ } else {
218
+ const prefix = indefinite ? '[_ ' : '['
219
+ return `${prefix}${items.join(', ')}]`
220
+ }
221
+ }
222
+
223
+ // Handle Maps
224
+ if (value instanceof Map) {
225
+ if (value.size === 0) {
226
+ return indefinite ? '{_ }' : '{}'
227
+ }
228
+
229
+ const entries: string[] = []
230
+ for (const [k, v] of value) {
231
+ const keyStr = formatValue(k, depth + 1, pretty, indent, maxDepth, false)
232
+ const valueStr = formatValue(v, depth + 1, pretty, indent, maxDepth, false)
233
+ entries.push(`${keyStr}: ${valueStr}`)
234
+ }
235
+
236
+ if (pretty) {
237
+ const prefix = indefinite ? '{_ ' : '{'
238
+ const lineIndent = indent.repeat(depth + 1)
239
+ const closeIndent = indent.repeat(depth)
240
+ return `${prefix}\n${lineIndent}${entries.join(`,\n${lineIndent}`)}\n${closeIndent}}`
241
+ } else {
242
+ const prefix = indefinite ? '{_ ' : '{'
243
+ return `${prefix}${entries.join(', ')}}`
244
+ }
245
+ }
246
+
247
+ // Handle Sets (convert to array)
248
+ if (value instanceof Set) {
249
+ const items = Array.from(value).map(item =>
250
+ formatValue(item, depth + 1, pretty, indent, maxDepth, false)
251
+ )
252
+
253
+ if (pretty) {
254
+ const lineIndent = indent.repeat(depth + 1)
255
+ const closeIndent = indent.repeat(depth)
256
+ return `[\n${lineIndent}${items.join(`,\n${lineIndent}`)}\n${closeIndent}]`
257
+ } else {
258
+ return `[${items.join(', ')}]`
259
+ }
260
+ }
261
+
262
+ // Handle plain objects (as CBOR maps with string keys)
263
+ if (isPlainObject(value)) {
264
+ const keys = Object.keys(value)
265
+ if (keys.length === 0) {
266
+ return indefinite ? '{_ }' : '{}'
267
+ }
268
+
269
+ const entries = keys.map(key => {
270
+ const keyStr = `"${escapeString(key)}"`
271
+ const valueStr = formatValue(
272
+ value[key],
273
+ depth + 1,
274
+ pretty,
275
+ indent,
276
+ maxDepth,
277
+ false
278
+ )
279
+ return `${keyStr}: ${valueStr}`
280
+ })
281
+
282
+ if (pretty) {
283
+ const prefix = indefinite ? '{_ ' : '{'
284
+ const lineIndent = indent.repeat(depth + 1)
285
+ const closeIndent = indent.repeat(depth)
286
+ return `${prefix}\n${lineIndent}${entries.join(`,\n${lineIndent}`)}\n${closeIndent}}`
287
+ } else {
288
+ const prefix = indefinite ? '{_ ' : '{'
289
+ return `${prefix}${entries.join(', ')}}`
290
+ }
291
+ }
292
+
293
+ // Fallback for unknown types
294
+ return String(value)
295
+ }
296
+
297
+ /**
298
+ * Parse diagnostic notation back to CBOR value
299
+ * (Not fully implemented - for future use)
300
+ */
301
+ const fromDiagnostic = (diag: string): unknown => {
302
+ // Basic parsing for common cases
303
+ const trimmed = diag.trim()
304
+
305
+ // Null/undefined
306
+ if (trimmed === 'null') return null
307
+ if (trimmed === 'undefined') return undefined
308
+
309
+ // Booleans
310
+ if (trimmed === 'true') return true
311
+ if (trimmed === 'false') return false
312
+
313
+ // Special floats
314
+ if (trimmed === 'NaN') return NaN
315
+ if (trimmed === 'Infinity') return Infinity
316
+ if (trimmed === '-Infinity') return -Infinity
317
+
318
+ // Numbers (including negative zero)
319
+ if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
320
+ const num = Number(trimmed)
321
+ if (trimmed === '-0.0' || trimmed === '-0') {
322
+ return -0
323
+ }
324
+ return num
325
+ }
326
+
327
+ // Byte strings h'...'
328
+ const hexMatch = trimmed.match(/^h'([0-9a-fA-F]*)'$/)
329
+ if (hexMatch && hexMatch[1] !== undefined) {
330
+ const hex = hexMatch[1]
331
+ const bytes = new Uint8Array(hex.length / 2)
332
+ for (let i = 0; i < hex.length; i += 2) {
333
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
334
+ }
335
+ return bytes
336
+ }
337
+
338
+ // Strings "..."
339
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
340
+ // Basic unescape - full implementation would handle all escapes
341
+ return trimmed.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\')
342
+ }
343
+
344
+ // For arrays, maps, tags - would need full parser
345
+ // Return as string for now
346
+ return trimmed
347
+ }
348
+
349
+ /**
350
+ * Format value with type annotation (extended notation)
351
+ */
352
+ const toDiagnosticWithType = (
353
+ value: unknown,
354
+ majorType: number,
355
+ options: DiagnosticOptions = {}
356
+ ): string => {
357
+ const typeNames: Record<number, string> = {
358
+ 0: 'uint',
359
+ 1: 'nint',
360
+ 2: 'bstr',
361
+ 3: 'tstr',
362
+ 4: 'array',
363
+ 5: 'map',
364
+ 6: 'tag',
365
+ 7: 'simple'
366
+ }
367
+
368
+ const typeName = typeNames[majorType] || 'unknown'
369
+ const diag = toDiagnostic(value, options)
370
+
371
+ return `${typeName}(${diag})`
372
+ }
373
+
374
+ return {
375
+ toDiagnostic,
376
+ fromDiagnostic,
377
+ toDiagnosticWithType
378
+ }
379
+ }
380
+
381
+ export default useCborDiagnostic
@@ -0,0 +1,256 @@
1
+ /**
2
+ * CBOR Float and Simple Values Parser Composable
3
+ * Handles Major Type 7 (Simple Values and Floats)
4
+ * Supports Float16, Float32, Float64, and simple values (true, false, null, undefined)
5
+ */
6
+
7
+ import type { ParseResult, ParseOptions } from '../types'
8
+ import { hexToBytes, readByte, extractCborHeader } from '../utils'
9
+
10
+ /**
11
+ * Composable for parsing CBOR floats and simple values (Major Type 7)
12
+ *
13
+ * @returns Object with parse, parseFloat, and parseSimple functions
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const { parse } = useCborFloat()
18
+ * const result = parse('f5') // true
19
+ * ```
20
+ */
21
+ export function useCborFloat() {
22
+ /**
23
+ * Converts IEEE 754 binary16 (Float16) to JavaScript number
24
+ * Manual conversion required as JavaScript doesn't have native Float16 support
25
+ *
26
+ * @param buffer - Data buffer
27
+ * @param offset - Starting offset
28
+ * @returns Float64 number representation
29
+ */
30
+ const float16ToFloat64 = (buffer: Uint8Array, offset: number): number => {
31
+ // Read 16-bit value in big-endian
32
+ const byte1 = readByte(buffer, offset)
33
+ const byte2 = readByte(buffer, offset + 1)
34
+ const value = (byte1 << 8) | byte2
35
+
36
+ // Extract components
37
+ const sign = (value & 0x8000) >> 15
38
+ const exponent = (value & 0x7c00) >> 10
39
+ const fraction = value & 0x03ff
40
+
41
+ // Handle special cases
42
+ if (exponent === 0) {
43
+ if (fraction === 0) {
44
+ // Zero (positive or negative)
45
+ return sign === 0 ? 0.0 : -0.0
46
+ }
47
+ // Subnormal number
48
+ return (sign === 0 ? 1 : -1) * Math.pow(2, -14) * (fraction / 1024)
49
+ }
50
+
51
+ if (exponent === 0x1f) {
52
+ if (fraction === 0) {
53
+ // Infinity (positive or negative)
54
+ return sign === 0 ? Infinity : -Infinity
55
+ }
56
+ // NaN
57
+ return NaN
58
+ }
59
+
60
+ // Normal number
61
+ // Formula: (-1)^sign * 2^(exponent - 15) * (1 + fraction/1024)
62
+ return (sign === 0 ? 1 : -1) * Math.pow(2, exponent - 15) * (1 + fraction / 1024)
63
+ }
64
+
65
+ /**
66
+ * Parses simple values (booleans, null, undefined, unassigned)
67
+ *
68
+ * @param buffer - Data buffer
69
+ * @param offset - Current offset
70
+ * @returns Parsed simple value and bytes read
71
+ */
72
+ const parseSimpleFromBuffer = (buffer: Uint8Array, offset: number): ParseResult => {
73
+ const initialByte = readByte(buffer, offset)
74
+ const { majorType, additionalInfo } = extractCborHeader(initialByte)
75
+
76
+ if (majorType !== 7) {
77
+ throw new Error(`Expected major type 7 (simple/float), got ${majorType}`)
78
+ }
79
+
80
+ // Simple values based on additional info
81
+ if (additionalInfo < 20) {
82
+ // Unassigned simple values (0-19)
83
+ return {
84
+ value: { simpleValue: additionalInfo },
85
+ bytesRead: 1
86
+ }
87
+ }
88
+
89
+ switch (additionalInfo) {
90
+ case 20: // false
91
+ return { value: false, bytesRead: 1 }
92
+
93
+ case 21: // true
94
+ return { value: true, bytesRead: 1 }
95
+
96
+ case 22: // null
97
+ return { value: null, bytesRead: 1 }
98
+
99
+ case 23: // undefined
100
+ return { value: undefined, bytesRead: 1 }
101
+
102
+ case 24: // 1-byte simple value
103
+ {
104
+ if (offset + 1 >= buffer.length) {
105
+ throw new Error('Unexpected end of buffer while reading simple value')
106
+ }
107
+ const simpleValue = readByte(buffer, offset + 1)
108
+ // Simple values 0-19 should not use 1-byte encoding
109
+ if (simpleValue < 32) {
110
+ throw new Error(`Invalid 1-byte encoding for simple value ${simpleValue}`)
111
+ }
112
+ return {
113
+ value: { simpleValue },
114
+ bytesRead: 2
115
+ }
116
+ }
117
+
118
+ case 25: // Float16
119
+ case 26: // Float32
120
+ case 27: // Float64
121
+ // These are floats, not simple values - should use parseFloatFromBuffer
122
+ throw new Error(`Additional info ${additionalInfo} is a float, use parseFloat instead`)
123
+
124
+ case 28:
125
+ case 29:
126
+ case 30:
127
+ // Reserved
128
+ throw new Error(`Reserved additional info value: ${additionalInfo}`)
129
+
130
+ case 31: // Break marker
131
+ throw new Error('Break marker (0xff) should only appear in indefinite-length items')
132
+
133
+ default:
134
+ throw new Error(`Invalid additional info: ${additionalInfo}`)
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Parses floating point numbers (Float16, Float32, Float64)
140
+ *
141
+ * @param buffer - Data buffer
142
+ * @param offset - Current offset
143
+ * @returns Parsed float and bytes read
144
+ */
145
+ const parseFloatFromBuffer = (buffer: Uint8Array, offset: number): ParseResult => {
146
+ const initialByte = readByte(buffer, offset)
147
+ const { majorType, additionalInfo } = extractCborHeader(initialByte)
148
+
149
+ if (majorType !== 7) {
150
+ throw new Error(`Expected major type 7 (simple/float), got ${majorType}`)
151
+ }
152
+
153
+ switch (additionalInfo) {
154
+ case 25: // Float16 (2 bytes)
155
+ {
156
+ if (offset + 2 >= buffer.length) {
157
+ throw new Error('Unexpected end of buffer while reading Float16')
158
+ }
159
+ const value = float16ToFloat64(buffer, offset + 1)
160
+ return { value, bytesRead: 3 }
161
+ }
162
+
163
+ case 26: // Float32 (4 bytes)
164
+ {
165
+ if (offset + 4 >= buffer.length) {
166
+ throw new Error('Unexpected end of buffer while reading Float32')
167
+ }
168
+ // Use DataView for proper IEEE 754 parsing
169
+ const dataView = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 4)
170
+ const value = dataView.getFloat32(0, false) // false = big-endian
171
+ return { value, bytesRead: 5 }
172
+ }
173
+
174
+ case 27: // Float64 (8 bytes)
175
+ {
176
+ if (offset + 8 >= buffer.length) {
177
+ throw new Error('Unexpected end of buffer while reading Float64')
178
+ }
179
+ // Use DataView for proper IEEE 754 parsing
180
+ const dataView = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 8)
181
+ const value = dataView.getFloat64(0, false) // false = big-endian
182
+ return { value, bytesRead: 9 }
183
+ }
184
+
185
+ default:
186
+ throw new Error(`Additional info ${additionalInfo} is not a float type`)
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Auto-detects and parses any Major Type 7 value (simple or float)
192
+ *
193
+ * @param buffer - Data buffer
194
+ * @param offset - Current offset
195
+ * @returns Parsed value and bytes read
196
+ */
197
+ const parseFromBuffer = (buffer: Uint8Array, offset: number): ParseResult => {
198
+ const initialByte = readByte(buffer, offset)
199
+ const { majorType, additionalInfo } = extractCborHeader(initialByte)
200
+
201
+ if (majorType !== 7) {
202
+ throw new Error(`Expected major type 7 (simple/float), got ${majorType}`)
203
+ }
204
+
205
+ // Determine if it's a float or simple value based on additional info
206
+ if (additionalInfo === 25 || additionalInfo === 26 || additionalInfo === 27) {
207
+ // Float16, Float32, or Float64
208
+ return parseFloatFromBuffer(buffer, offset)
209
+ } else {
210
+ // Simple value (including false, true, null, undefined)
211
+ return parseSimpleFromBuffer(buffer, offset)
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Parses CBOR simple value from hex string
217
+ *
218
+ * @param hexString - CBOR hex string
219
+ * @param _options - Parser options (optional, for future use)
220
+ * @returns Parsed simple value and bytes read
221
+ */
222
+ const parseSimple = (hexString: string, _options?: ParseOptions): ParseResult => {
223
+ const buffer = hexToBytes(hexString)
224
+ return parseSimpleFromBuffer(buffer, 0)
225
+ }
226
+
227
+ /**
228
+ * Parses CBOR float from hex string
229
+ *
230
+ * @param hexString - CBOR hex string
231
+ * @param _options - Parser options (optional, for future use)
232
+ * @returns Parsed float and bytes read
233
+ */
234
+ const parseFloat = (hexString: string, _options?: ParseOptions): ParseResult => {
235
+ const buffer = hexToBytes(hexString)
236
+ return parseFloatFromBuffer(buffer, 0)
237
+ }
238
+
239
+ /**
240
+ * Auto-detects and parses any Major Type 7 value from hex string
241
+ *
242
+ * @param hexString - CBOR hex string
243
+ * @param _options - Parser options (optional, for future use)
244
+ * @returns Parsed value and bytes read
245
+ */
246
+ const parse = (hexString: string, _options?: ParseOptions): ParseResult => {
247
+ const buffer = hexToBytes(hexString)
248
+ return parseFromBuffer(buffer, 0)
249
+ }
250
+
251
+ return {
252
+ parse,
253
+ parseFloat,
254
+ parseSimple
255
+ }
256
+ }