@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,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBOR String Parser Composable
|
|
3
|
+
* Handles Major Types 2 (Byte Strings) and 3 (Text Strings)
|
|
4
|
+
* Supports definite and indefinite length encoding
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ParseResult, ParseOptions } from '../types'
|
|
8
|
+
import { hexToBytes, readByte, readUint, readBigUint, extractCborHeader, validateUtf8Strict } from '../utils'
|
|
9
|
+
import { useCborByteString, useCborTextString } from './useCborStringTypes'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Composable for parsing CBOR strings (Major Types 2 and 3)
|
|
13
|
+
*
|
|
14
|
+
* @returns Object with string parsing functions
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const { parseString } = useCborString()
|
|
19
|
+
* const result = parseString('6449455446') // "IETF"
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function useCborString() {
|
|
23
|
+
const { create: createByteString } = useCborByteString()
|
|
24
|
+
const { create: createTextString } = useCborTextString()
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parses the length from CBOR additional info field
|
|
28
|
+
*
|
|
29
|
+
* @param buffer - Data buffer
|
|
30
|
+
* @param offset - Current offset (after initial byte)
|
|
31
|
+
* @param ai - Additional info field (0-31)
|
|
32
|
+
* @returns Object with length and bytes consumed
|
|
33
|
+
*/
|
|
34
|
+
const parseLength = (buffer: Uint8Array, offset: number, ai: number, options?: ParseOptions): { length: number; bytesConsumed: number } => {
|
|
35
|
+
if (ai < 24) {
|
|
36
|
+
// Direct encoding (0-23)
|
|
37
|
+
return { length: ai, bytesConsumed: 0 }
|
|
38
|
+
} else if (ai === 24) {
|
|
39
|
+
// 1 byte follows
|
|
40
|
+
const length = readByte(buffer, offset)
|
|
41
|
+
|
|
42
|
+
// RFC 8949 Section 4.2.1: Canonical encoding requires shortest form
|
|
43
|
+
if (options?.validateCanonical && length < 24) {
|
|
44
|
+
throw new Error(`Non-canonical length encoding: ${length} should use direct encoding (AI < 24), not AI=24`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { length, bytesConsumed: 1 }
|
|
48
|
+
} else if (ai === 25) {
|
|
49
|
+
// 2 bytes follow
|
|
50
|
+
const length = readUint(buffer, offset, 2)
|
|
51
|
+
|
|
52
|
+
// RFC 8949 Section 4.2.1: Value must be >= 256 to justify 2-byte encoding
|
|
53
|
+
if (options?.validateCanonical && length < 256) {
|
|
54
|
+
throw new Error(`Non-canonical length encoding: ${length} should use ${length < 24 ? 'direct encoding' : '1-byte encoding (AI=24)'}, not AI=25`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { length, bytesConsumed: 2 }
|
|
58
|
+
} else if (ai === 26) {
|
|
59
|
+
// 4 bytes follow
|
|
60
|
+
const length = readUint(buffer, offset, 4)
|
|
61
|
+
|
|
62
|
+
// RFC 8949 Section 4.2.1: Value must be >= 65536 to justify 4-byte encoding
|
|
63
|
+
if (options?.validateCanonical && length < 65536) {
|
|
64
|
+
throw new Error(`Non-canonical length encoding: ${length} should use shorter encoding, not AI=26`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { length, bytesConsumed: 4 }
|
|
68
|
+
} else if (ai === 27) {
|
|
69
|
+
// 8 bytes follow - rare for strings, but supported
|
|
70
|
+
const lengthBigInt = readBigUint(buffer, offset, 8)
|
|
71
|
+
|
|
72
|
+
// Check if value fits in safe integer range
|
|
73
|
+
if (lengthBigInt > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
74
|
+
throw new Error(`String length ${lengthBigInt} exceeds maximum safe integer`)
|
|
75
|
+
}
|
|
76
|
+
const length = Number(lengthBigInt)
|
|
77
|
+
|
|
78
|
+
// RFC 8949 Section 4.2.1: Value must be >= 2^32 to justify 8-byte encoding
|
|
79
|
+
if (options?.validateCanonical && length < 4294967296) {
|
|
80
|
+
throw new Error(`Non-canonical length encoding: ${length} should use shorter encoding, not AI=27`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { length, bytesConsumed: 8 }
|
|
84
|
+
} else if (ai === 31) {
|
|
85
|
+
// Indefinite length (break-terminated) - special marker
|
|
86
|
+
return { length: -1, bytesConsumed: 0 }
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error(`Invalid additional info: ${ai}`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Parses CBOR byte string (Major Type 2)
|
|
94
|
+
*
|
|
95
|
+
* @param buffer - Data buffer
|
|
96
|
+
* @param offset - Current offset
|
|
97
|
+
* @param options - Parser options (optional)
|
|
98
|
+
* @returns Parsed byte array and bytes read
|
|
99
|
+
*/
|
|
100
|
+
const parseByteString = (buffer: Uint8Array, offset: number = 0, options?: ParseOptions): ParseResult => {
|
|
101
|
+
const initialByte = readByte(buffer, offset)
|
|
102
|
+
const { majorType, additionalInfo } = extractCborHeader(initialByte)
|
|
103
|
+
|
|
104
|
+
if (majorType !== 2) {
|
|
105
|
+
throw new Error(`Expected major type 2 (byte string), got ${majorType}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check if indefinite length is allowed
|
|
109
|
+
if (additionalInfo === 31 && options?.allowIndefinite === false) {
|
|
110
|
+
throw new Error('Indefinite-length encoding is not allowed in strict mode')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Parse the length
|
|
114
|
+
const { length, bytesConsumed } = parseLength(buffer, offset + 1, additionalInfo, options)
|
|
115
|
+
|
|
116
|
+
// Handle indefinite-length byte string
|
|
117
|
+
if (length === -1) {
|
|
118
|
+
const chunks: Uint8Array[] = []
|
|
119
|
+
let currentOffset = offset + 1 + bytesConsumed
|
|
120
|
+
let totalLength = 0
|
|
121
|
+
|
|
122
|
+
while (currentOffset < buffer.length) {
|
|
123
|
+
// Peek at next byte to check for break marker
|
|
124
|
+
if (currentOffset >= buffer.length) {
|
|
125
|
+
throw new Error(`Incomplete indefinite byte string: missing break marker`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const nextByte = buffer[currentOffset]
|
|
129
|
+
|
|
130
|
+
// Check for break marker (0xff)
|
|
131
|
+
if (nextByte === 0xff) {
|
|
132
|
+
currentOffset++ // Consume break byte
|
|
133
|
+
break
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Parse chunk (must be definite-length byte string)
|
|
137
|
+
// Use try-catch to provide better error context for indefinite strings
|
|
138
|
+
let chunkResult
|
|
139
|
+
try {
|
|
140
|
+
chunkResult = parseByteString(buffer, currentOffset, options)
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw new Error(`Error parsing indefinite byte string chunk at offset ${currentOffset}: ${(error as Error).message}`)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Validate chunk is a byte string
|
|
146
|
+
if (!(chunkResult.value instanceof Uint8Array)) {
|
|
147
|
+
throw new Error('Indefinite byte string must contain only byte string chunks')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
chunks.push(chunkResult.value)
|
|
151
|
+
totalLength += chunkResult.value.length
|
|
152
|
+
currentOffset += chunkResult.bytesRead
|
|
153
|
+
|
|
154
|
+
// Check length limit during accumulation
|
|
155
|
+
if (options?.limits?.maxStringLength && totalLength > options.limits.maxStringLength) {
|
|
156
|
+
throw new Error(`Byte string length ${totalLength} exceeds limit of ${options.limits.maxStringLength} bytes`)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Concatenate all chunks
|
|
161
|
+
const bytes = new Uint8Array(totalLength)
|
|
162
|
+
let destOffset = 0
|
|
163
|
+
for (const chunk of chunks) {
|
|
164
|
+
bytes.set(chunk, destOffset)
|
|
165
|
+
destOffset += chunk.length
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Return CborByteString with indefinite marker and original chunks
|
|
169
|
+
return {
|
|
170
|
+
value: createByteString(bytes, true, chunks),
|
|
171
|
+
bytesRead: currentOffset - offset
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Definite-length byte string
|
|
176
|
+
// Check length limit before allocating
|
|
177
|
+
if (options?.limits?.maxStringLength && length > options.limits.maxStringLength) {
|
|
178
|
+
throw new Error(`Byte string length ${length} exceeds limit of ${options.limits.maxStringLength} bytes`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const payloadOffset = offset + 1 + bytesConsumed
|
|
182
|
+
|
|
183
|
+
// Validate buffer has enough bytes
|
|
184
|
+
if (payloadOffset + length > buffer.length) {
|
|
185
|
+
throw new Error(`Insufficient data: expected ${length} bytes at offset ${payloadOffset}, but only ${buffer.length - payloadOffset} bytes available`)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const payload = buffer.slice(payloadOffset, payloadOffset + length)
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
value: payload,
|
|
192
|
+
bytesRead: 1 + bytesConsumed + length
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Parses CBOR text string (Major Type 3)
|
|
198
|
+
*
|
|
199
|
+
* @param buffer - Data buffer
|
|
200
|
+
* @param offset - Current offset
|
|
201
|
+
* @param options - Parser options (optional)
|
|
202
|
+
* @returns Parsed text string and bytes read
|
|
203
|
+
*/
|
|
204
|
+
const parseTextString = (buffer: Uint8Array, offset: number = 0, options?: ParseOptions): ParseResult => {
|
|
205
|
+
const initialByte = readByte(buffer, offset)
|
|
206
|
+
const { majorType, additionalInfo } = extractCborHeader(initialByte)
|
|
207
|
+
|
|
208
|
+
if (majorType !== 3) {
|
|
209
|
+
throw new Error(`Expected major type 3 (text string), got ${majorType}`)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check if indefinite length is allowed
|
|
213
|
+
if (additionalInfo === 31 && options?.allowIndefinite === false) {
|
|
214
|
+
throw new Error('Indefinite-length encoding is not allowed in strict mode')
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Parse the length
|
|
218
|
+
const { length, bytesConsumed } = parseLength(buffer, offset + 1, additionalInfo, options)
|
|
219
|
+
|
|
220
|
+
// Handle indefinite-length text string
|
|
221
|
+
if (length === -1) {
|
|
222
|
+
const chunks: string[] = []
|
|
223
|
+
let currentOffset = offset + 1 + bytesConsumed
|
|
224
|
+
let totalLength = 0
|
|
225
|
+
|
|
226
|
+
while (currentOffset < buffer.length) {
|
|
227
|
+
// Peek at next byte to check for break marker
|
|
228
|
+
if (currentOffset >= buffer.length) {
|
|
229
|
+
throw new Error(`Incomplete indefinite text string: missing break marker`)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const nextByte = buffer[currentOffset]
|
|
233
|
+
|
|
234
|
+
// Check for break marker (0xff)
|
|
235
|
+
if (nextByte === 0xff) {
|
|
236
|
+
currentOffset++ // Consume break byte
|
|
237
|
+
break
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Parse chunk (must be definite-length text string)
|
|
241
|
+
// Use try-catch to provide better error context for indefinite strings
|
|
242
|
+
let chunkResult
|
|
243
|
+
try {
|
|
244
|
+
chunkResult = parseTextString(buffer, currentOffset, options)
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw new Error(`Error parsing indefinite text string chunk at offset ${currentOffset}: ${(error as Error).message}`)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Validate chunk is a text string
|
|
250
|
+
if (typeof chunkResult.value !== 'string') {
|
|
251
|
+
throw new Error('Indefinite text string must contain only text string chunks')
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
chunks.push(chunkResult.value)
|
|
255
|
+
totalLength += chunkResult.value.length
|
|
256
|
+
currentOffset += chunkResult.bytesRead
|
|
257
|
+
|
|
258
|
+
// Check length limit during accumulation
|
|
259
|
+
if (options?.limits?.maxStringLength && totalLength > options.limits.maxStringLength) {
|
|
260
|
+
throw new Error(`Text string length ${totalLength} exceeds limit of ${options.limits.maxStringLength} characters`)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Concatenate all chunks
|
|
265
|
+
const text = chunks.join('')
|
|
266
|
+
|
|
267
|
+
// Return CborTextString with indefinite marker and original chunks
|
|
268
|
+
return {
|
|
269
|
+
value: createTextString(text, true, chunks),
|
|
270
|
+
bytesRead: currentOffset - offset
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Definite-length text string
|
|
275
|
+
// Check length limit before allocating
|
|
276
|
+
if (options?.limits?.maxStringLength && length > options.limits.maxStringLength) {
|
|
277
|
+
throw new Error(`Text string length ${length} bytes exceeds limit of ${options.limits.maxStringLength} bytes`)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const payloadOffset = offset + 1 + bytesConsumed
|
|
281
|
+
|
|
282
|
+
// Validate buffer has enough bytes
|
|
283
|
+
if (payloadOffset + length > buffer.length) {
|
|
284
|
+
throw new Error(`Insufficient data: expected ${length} bytes at offset ${payloadOffset}, but only ${buffer.length - payloadOffset} bytes available`)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const payload = buffer.slice(payloadOffset, payloadOffset + length)
|
|
288
|
+
|
|
289
|
+
// Validate UTF-8 if strict validation is enabled
|
|
290
|
+
if (options?.validateUtf8Strict) {
|
|
291
|
+
validateUtf8Strict(payload)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Decode UTF-8 bytes to string
|
|
295
|
+
const decoder = new TextDecoder('utf-8')
|
|
296
|
+
const text = decoder.decode(payload)
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
value: text,
|
|
300
|
+
bytesRead: 1 + bytesConsumed + length
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Parses CBOR string (auto-detects byte or text string)
|
|
306
|
+
*
|
|
307
|
+
* @param hexString - CBOR hex string
|
|
308
|
+
* @param options - Parser options (optional)
|
|
309
|
+
* @returns Parsed string (Uint8Array for MT 2, string for MT 3) and bytes read
|
|
310
|
+
*/
|
|
311
|
+
const parseString = (hexString: string, options?: ParseOptions): ParseResult => {
|
|
312
|
+
const buffer = hexToBytes(hexString)
|
|
313
|
+
const initialByte = readByte(buffer, 0)
|
|
314
|
+
const { majorType } = extractCborHeader(initialByte)
|
|
315
|
+
|
|
316
|
+
if (majorType === 2) {
|
|
317
|
+
return parseByteString(buffer, 0, options)
|
|
318
|
+
} else if (majorType === 3) {
|
|
319
|
+
return parseTextString(buffer, 0, options)
|
|
320
|
+
} else {
|
|
321
|
+
throw new Error(`Expected major type 2 or 3 (string), got ${majorType}`)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
parseString,
|
|
327
|
+
parseByteString,
|
|
328
|
+
parseTextString
|
|
329
|
+
}
|
|
330
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBOR String Type Helpers
|
|
3
|
+
* Composables for working with CborByteString and CborTextString
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CborByteString, CborTextString } from '../types'
|
|
7
|
+
import { INDEFINITE_SYMBOL } from '../types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Composable for creating and working with CBOR byte strings
|
|
11
|
+
*/
|
|
12
|
+
export function useCborByteString() {
|
|
13
|
+
/**
|
|
14
|
+
* Create a CBOR byte string
|
|
15
|
+
*
|
|
16
|
+
* @param bytes - The byte data
|
|
17
|
+
* @param indefinite - Whether this was encoded with indefinite length
|
|
18
|
+
* @param chunks - Original chunks for indefinite byte strings
|
|
19
|
+
* @returns CborByteString object
|
|
20
|
+
*/
|
|
21
|
+
const create = (bytes: Uint8Array, indefinite = false, chunks?: Uint8Array[]): CborByteString => {
|
|
22
|
+
const result: CborByteString = {
|
|
23
|
+
type: 'cbor-byte-string',
|
|
24
|
+
bytes,
|
|
25
|
+
...(chunks && { chunks })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (indefinite) {
|
|
29
|
+
(result as any)[INDEFINITE_SYMBOL] = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return result
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if a byte string was encoded with indefinite length
|
|
37
|
+
*/
|
|
38
|
+
const isIndefinite = (value: CborByteString | Uint8Array): boolean => {
|
|
39
|
+
return (value as any)[INDEFINITE_SYMBOL] === true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract raw bytes from a CborByteString or Uint8Array
|
|
44
|
+
*/
|
|
45
|
+
const toBytes = (value: CborByteString | Uint8Array): Uint8Array => {
|
|
46
|
+
if (value instanceof Uint8Array) {
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
return value.bytes
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a value is a CborByteString
|
|
54
|
+
*/
|
|
55
|
+
const isCborByteString = (value: any): value is CborByteString => {
|
|
56
|
+
return typeof value === 'object' &&
|
|
57
|
+
value !== null &&
|
|
58
|
+
value.type === 'cbor-byte-string' &&
|
|
59
|
+
value.bytes instanceof Uint8Array
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
create,
|
|
64
|
+
isIndefinite,
|
|
65
|
+
toBytes,
|
|
66
|
+
isCborByteString
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Composable for creating and working with CBOR text strings
|
|
72
|
+
*/
|
|
73
|
+
export function useCborTextString() {
|
|
74
|
+
/**
|
|
75
|
+
* Create a CBOR text string
|
|
76
|
+
*
|
|
77
|
+
* @param text - The text data
|
|
78
|
+
* @param indefinite - Whether this was encoded with indefinite length
|
|
79
|
+
* @param chunks - Original chunks for indefinite text strings
|
|
80
|
+
* @returns CborTextString object
|
|
81
|
+
*/
|
|
82
|
+
const create = (text: string, indefinite = false, chunks?: string[]): CborTextString => {
|
|
83
|
+
const result: CborTextString = {
|
|
84
|
+
type: 'cbor-text-string',
|
|
85
|
+
text,
|
|
86
|
+
...(chunks && { chunks })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (indefinite) {
|
|
90
|
+
(result as any)[INDEFINITE_SYMBOL] = true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if a text string was encoded with indefinite length
|
|
98
|
+
*/
|
|
99
|
+
const isIndefinite = (value: CborTextString | string): boolean => {
|
|
100
|
+
return (value as any)[INDEFINITE_SYMBOL] === true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Extract raw string from a CborTextString or string
|
|
105
|
+
*/
|
|
106
|
+
const toString = (value: CborTextString | string): string => {
|
|
107
|
+
if (typeof value === 'string') {
|
|
108
|
+
return value
|
|
109
|
+
}
|
|
110
|
+
return value.text
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if a value is a CborTextString
|
|
115
|
+
*/
|
|
116
|
+
const isCborTextString = (value: any): value is CborTextString => {
|
|
117
|
+
return typeof value === 'object' &&
|
|
118
|
+
value !== null &&
|
|
119
|
+
value.type === 'cbor-text-string' &&
|
|
120
|
+
typeof value.text === 'string'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
create,
|
|
125
|
+
isIndefinite,
|
|
126
|
+
toString,
|
|
127
|
+
isCborTextString
|
|
128
|
+
}
|
|
129
|
+
}
|