@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.
- package/LICENSE +48 -0
- package/README.md +13 -0
- package/dist/index.cjs +9151 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3107 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +3107 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +9155 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +9027 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +80 -0
- package/src/.claude-flow/metrics/agent-metrics.json +1 -0
- package/src/.claude-flow/metrics/performance.json +87 -0
- package/src/.claude-flow/metrics/task-metrics.json +10 -0
- package/src/byte-string.ts +300 -0
- package/src/cbor-codable.ts +170 -0
- package/src/cbor-tagged-codable.ts +72 -0
- package/src/cbor-tagged-decodable.ts +184 -0
- package/src/cbor-tagged-encodable.ts +138 -0
- package/src/cbor-tagged.ts +104 -0
- package/src/cbor.ts +869 -0
- package/src/conveniences.ts +840 -0
- package/src/date.ts +553 -0
- package/src/decode.ts +276 -0
- package/src/diag.ts +462 -0
- package/src/dump.ts +277 -0
- package/src/error.ts +259 -0
- package/src/exact.ts +714 -0
- package/src/float.ts +279 -0
- package/src/global.d.ts +34 -0
- package/src/globals.d.ts +0 -0
- package/src/index.ts +180 -0
- package/src/map.ts +308 -0
- package/src/prelude.ts +70 -0
- package/src/set.ts +515 -0
- package/src/simple.ts +153 -0
- package/src/stdlib.ts +55 -0
- package/src/string-util.ts +55 -0
- package/src/tag.ts +53 -0
- package/src/tags-store.ts +294 -0
- package/src/tags.ts +231 -0
- package/src/varint.ts +124 -0
- package/src/walk.ts +516 -0
package/src/decode.ts
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Cbor,
|
|
3
|
+
type CborNumber,
|
|
4
|
+
MajorType,
|
|
5
|
+
isCbor,
|
|
6
|
+
encodeCbor,
|
|
7
|
+
cborData,
|
|
8
|
+
attachMethods,
|
|
9
|
+
} from "./cbor";
|
|
10
|
+
import { areBytesEqual } from "./stdlib";
|
|
11
|
+
import { binary16ToNumber, binary32ToNumber, binary64ToNumber } from "./float";
|
|
12
|
+
import { CborMap } from "./map";
|
|
13
|
+
import { CborError } from "./error";
|
|
14
|
+
|
|
15
|
+
export function decodeCbor(data: Uint8Array): Cbor {
|
|
16
|
+
const { cbor, len } = decodeCborInternal(
|
|
17
|
+
new DataView(data.buffer, data.byteOffset, data.byteLength),
|
|
18
|
+
);
|
|
19
|
+
const remaining = data.length - len;
|
|
20
|
+
if (remaining !== 0) {
|
|
21
|
+
throw new CborError({ type: "UnusedData", count: remaining });
|
|
22
|
+
}
|
|
23
|
+
return cbor;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseHeader(header: number): { majorType: MajorType; headerValue: number } {
|
|
27
|
+
const majorType = (header >> 5) as MajorType;
|
|
28
|
+
const headerValue = header & 31;
|
|
29
|
+
return { majorType, headerValue };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function at(data: DataView, index: number): number {
|
|
33
|
+
return data.getUint8(index);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function from(data: DataView, index: number): DataView {
|
|
37
|
+
return new DataView(data.buffer, data.byteOffset + index);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function range(data: DataView, start: number, end: number): DataView {
|
|
41
|
+
return new DataView(data.buffer, data.byteOffset + start, end - start);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseBytes(data: DataView, len: number): DataView {
|
|
45
|
+
if (data.byteLength < len) {
|
|
46
|
+
throw new CborError({ type: "Underrun" });
|
|
47
|
+
}
|
|
48
|
+
return range(data, 0, len);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseHeaderVarint(data: DataView): {
|
|
52
|
+
majorType: MajorType;
|
|
53
|
+
value: CborNumber;
|
|
54
|
+
varIntLen: number;
|
|
55
|
+
} {
|
|
56
|
+
if (data.byteLength < 1) {
|
|
57
|
+
throw new CborError({ type: "Underrun" });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const header = at(data, 0);
|
|
61
|
+
const { majorType, headerValue } = parseHeader(header);
|
|
62
|
+
const dataRemaining = data.byteLength - 1;
|
|
63
|
+
let value: CborNumber;
|
|
64
|
+
let varIntLen: number;
|
|
65
|
+
if (headerValue <= 23) {
|
|
66
|
+
value = headerValue;
|
|
67
|
+
varIntLen = 1;
|
|
68
|
+
} else if (headerValue === 24) {
|
|
69
|
+
if (dataRemaining < 1) {
|
|
70
|
+
throw new CborError({ type: "Underrun" });
|
|
71
|
+
}
|
|
72
|
+
value = at(data, 1);
|
|
73
|
+
if (value < 24) {
|
|
74
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
75
|
+
}
|
|
76
|
+
varIntLen = 2;
|
|
77
|
+
} else if (headerValue === 25) {
|
|
78
|
+
if (dataRemaining < 2) {
|
|
79
|
+
throw new CborError({ type: "Underrun" });
|
|
80
|
+
}
|
|
81
|
+
value = ((at(data, 1) << 8) | at(data, 2)) >>> 0;
|
|
82
|
+
// Floats with header 0xf9 are allowed to have values <= 0xFF
|
|
83
|
+
if (value <= 0xff && header !== 0xf9) {
|
|
84
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
85
|
+
}
|
|
86
|
+
varIntLen = 3;
|
|
87
|
+
} else if (headerValue === 26) {
|
|
88
|
+
if (dataRemaining < 4) {
|
|
89
|
+
throw new CborError({ type: "Underrun" });
|
|
90
|
+
}
|
|
91
|
+
value = ((at(data, 1) << 24) | (at(data, 2) << 16) | (at(data, 3) << 8) | at(data, 4)) >>> 0;
|
|
92
|
+
// Floats with header 0xfa are allowed to have values <= 0xFFFF
|
|
93
|
+
if (value <= 0xffff && header !== 0xfa) {
|
|
94
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
95
|
+
}
|
|
96
|
+
varIntLen = 5;
|
|
97
|
+
} else if (headerValue === 27) {
|
|
98
|
+
if (dataRemaining < 8) {
|
|
99
|
+
throw new CborError({ type: "Underrun" });
|
|
100
|
+
}
|
|
101
|
+
const a = BigInt(at(data, 1)) << 56n;
|
|
102
|
+
const b = BigInt(at(data, 2)) << 48n;
|
|
103
|
+
const c = BigInt(at(data, 3)) << 40n;
|
|
104
|
+
const d = BigInt(at(data, 4)) << 32n;
|
|
105
|
+
const e = BigInt(at(data, 5)) << 24n;
|
|
106
|
+
const f = BigInt(at(data, 6)) << 16n;
|
|
107
|
+
const g = BigInt(at(data, 7)) << 8n;
|
|
108
|
+
const h = BigInt(at(data, 8));
|
|
109
|
+
value = a | b | c | d | e | f | g | h;
|
|
110
|
+
if (value <= Number.MAX_SAFE_INTEGER) {
|
|
111
|
+
value = Number(value);
|
|
112
|
+
}
|
|
113
|
+
// Floats with header 0xfb are allowed to have values <= 0xFFFFFFFF
|
|
114
|
+
if (value <= 0xffffffff && header !== 0xfb) {
|
|
115
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
116
|
+
}
|
|
117
|
+
varIntLen = 9;
|
|
118
|
+
} else {
|
|
119
|
+
throw new CborError({ type: "UnsupportedHeaderValue", value: headerValue });
|
|
120
|
+
}
|
|
121
|
+
return { majorType, value, varIntLen };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function decodeCborInternal(data: DataView): { cbor: Cbor; len: number } {
|
|
125
|
+
if (data.byteLength < 1) {
|
|
126
|
+
throw new CborError({ type: "Underrun" });
|
|
127
|
+
}
|
|
128
|
+
const { majorType, value, varIntLen } = parseHeaderVarint(data);
|
|
129
|
+
switch (majorType) {
|
|
130
|
+
case MajorType.Unsigned: {
|
|
131
|
+
const cborObj = { isCbor: true, type: MajorType.Unsigned, value: value } as const;
|
|
132
|
+
const cbor = attachMethods(cborObj);
|
|
133
|
+
const buf = new Uint8Array(data.buffer, data.byteOffset, varIntLen);
|
|
134
|
+
checkCanonicalEncoding(cbor, buf);
|
|
135
|
+
return { cbor, len: varIntLen };
|
|
136
|
+
}
|
|
137
|
+
case MajorType.Negative: {
|
|
138
|
+
// Store the magnitude as-is, matching Rust's representation
|
|
139
|
+
// The decoded value is what gets encoded (not the actual negative number)
|
|
140
|
+
const cborObj = { isCbor: true, type: MajorType.Negative, value: value } as const;
|
|
141
|
+
const cbor = attachMethods(cborObj);
|
|
142
|
+
const buf = new Uint8Array(data.buffer, data.byteOffset, varIntLen);
|
|
143
|
+
checkCanonicalEncoding(cbor, buf);
|
|
144
|
+
return { cbor, len: varIntLen };
|
|
145
|
+
}
|
|
146
|
+
case MajorType.ByteString: {
|
|
147
|
+
const dataLen = value;
|
|
148
|
+
if (typeof dataLen === "bigint") {
|
|
149
|
+
throw new CborError({ type: "OutOfRange" });
|
|
150
|
+
}
|
|
151
|
+
const buf = parseBytes(from(data, varIntLen), dataLen);
|
|
152
|
+
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
153
|
+
const cborObj = { isCbor: true, type: MajorType.ByteString, value: bytes } as const;
|
|
154
|
+
return { cbor: attachMethods(cborObj), len: varIntLen + dataLen };
|
|
155
|
+
}
|
|
156
|
+
case MajorType.Text: {
|
|
157
|
+
const textLen = value;
|
|
158
|
+
if (typeof textLen === "bigint") {
|
|
159
|
+
throw new CborError({ type: "OutOfRange" });
|
|
160
|
+
}
|
|
161
|
+
const textBuf = parseBytes(from(data, varIntLen), textLen);
|
|
162
|
+
const text = new TextDecoder().decode(textBuf);
|
|
163
|
+
// dCBOR requires all text strings to be in Unicode Normalization Form C (NFC)
|
|
164
|
+
// Reject any strings that are not already in NFC form
|
|
165
|
+
if (text.normalize("NFC") !== text) {
|
|
166
|
+
throw new CborError({ type: "NonCanonicalString" });
|
|
167
|
+
}
|
|
168
|
+
const cborObj = { isCbor: true, type: MajorType.Text, value: text } as const;
|
|
169
|
+
return { cbor: attachMethods(cborObj), len: varIntLen + textLen };
|
|
170
|
+
}
|
|
171
|
+
case MajorType.Array: {
|
|
172
|
+
let pos = varIntLen;
|
|
173
|
+
const items: Cbor[] = [];
|
|
174
|
+
for (let i = 0; i < value; i++) {
|
|
175
|
+
const { cbor: item, len: itemLen } = decodeCborInternal(from(data, pos));
|
|
176
|
+
items.push(item);
|
|
177
|
+
pos += itemLen;
|
|
178
|
+
}
|
|
179
|
+
const cborObj = { isCbor: true, type: MajorType.Array, value: items } as const;
|
|
180
|
+
return { cbor: attachMethods(cborObj), len: pos };
|
|
181
|
+
}
|
|
182
|
+
case MajorType.Map: {
|
|
183
|
+
let pos = varIntLen;
|
|
184
|
+
const map = new CborMap();
|
|
185
|
+
for (let i = 0; i < value; i++) {
|
|
186
|
+
const { cbor: key, len: keyLen } = decodeCborInternal(from(data, pos));
|
|
187
|
+
pos += keyLen;
|
|
188
|
+
const { cbor: value, len: valueLen } = decodeCborInternal(from(data, pos));
|
|
189
|
+
pos += valueLen;
|
|
190
|
+
map.setNext(key, value);
|
|
191
|
+
}
|
|
192
|
+
const cborObj = { isCbor: true, type: MajorType.Map, value: map } as const;
|
|
193
|
+
return { cbor: attachMethods(cborObj), len: pos };
|
|
194
|
+
}
|
|
195
|
+
case MajorType.Tagged: {
|
|
196
|
+
const { cbor: item, len: itemLen } = decodeCborInternal(from(data, varIntLen));
|
|
197
|
+
const cborObj = { isCbor: true, type: MajorType.Tagged, tag: value, value: item } as const;
|
|
198
|
+
return { cbor: attachMethods(cborObj), len: varIntLen + itemLen };
|
|
199
|
+
}
|
|
200
|
+
case MajorType.Simple:
|
|
201
|
+
switch (varIntLen) {
|
|
202
|
+
case 3: {
|
|
203
|
+
const f = binary16ToNumber(new Uint8Array(data.buffer, data.byteOffset + 1, 2));
|
|
204
|
+
checkCanonicalEncoding(f, new Uint8Array(data.buffer, data.byteOffset, varIntLen));
|
|
205
|
+
const cborObj = {
|
|
206
|
+
isCbor: true,
|
|
207
|
+
type: MajorType.Simple,
|
|
208
|
+
value: { type: "Float", value: f },
|
|
209
|
+
} as const;
|
|
210
|
+
return { cbor: attachMethods(cborObj), len: varIntLen };
|
|
211
|
+
}
|
|
212
|
+
case 5: {
|
|
213
|
+
const f = binary32ToNumber(new Uint8Array(data.buffer, data.byteOffset + 1, 4));
|
|
214
|
+
checkCanonicalEncoding(f, new Uint8Array(data.buffer, data.byteOffset, varIntLen));
|
|
215
|
+
const cborObj = {
|
|
216
|
+
isCbor: true,
|
|
217
|
+
type: MajorType.Simple,
|
|
218
|
+
value: { type: "Float", value: f },
|
|
219
|
+
} as const;
|
|
220
|
+
return { cbor: attachMethods(cborObj), len: varIntLen };
|
|
221
|
+
}
|
|
222
|
+
case 9: {
|
|
223
|
+
const f = binary64ToNumber(new Uint8Array(data.buffer, data.byteOffset + 1, 8));
|
|
224
|
+
checkCanonicalEncoding(f, new Uint8Array(data.buffer, data.byteOffset, varIntLen));
|
|
225
|
+
const cborObj = {
|
|
226
|
+
isCbor: true,
|
|
227
|
+
type: MajorType.Simple,
|
|
228
|
+
value: { type: "Float", value: f },
|
|
229
|
+
} as const;
|
|
230
|
+
return { cbor: attachMethods(cborObj), len: varIntLen };
|
|
231
|
+
}
|
|
232
|
+
default:
|
|
233
|
+
switch (value) {
|
|
234
|
+
case 20:
|
|
235
|
+
return {
|
|
236
|
+
cbor: attachMethods({
|
|
237
|
+
isCbor: true,
|
|
238
|
+
type: MajorType.Simple,
|
|
239
|
+
value: { type: "False" },
|
|
240
|
+
}),
|
|
241
|
+
len: varIntLen,
|
|
242
|
+
};
|
|
243
|
+
case 21:
|
|
244
|
+
return {
|
|
245
|
+
cbor: attachMethods({
|
|
246
|
+
isCbor: true,
|
|
247
|
+
type: MajorType.Simple,
|
|
248
|
+
value: { type: "True" },
|
|
249
|
+
}),
|
|
250
|
+
len: varIntLen,
|
|
251
|
+
};
|
|
252
|
+
case 22:
|
|
253
|
+
return {
|
|
254
|
+
cbor: attachMethods({
|
|
255
|
+
isCbor: true,
|
|
256
|
+
type: MajorType.Simple,
|
|
257
|
+
value: { type: "Null" },
|
|
258
|
+
}),
|
|
259
|
+
len: varIntLen,
|
|
260
|
+
};
|
|
261
|
+
default:
|
|
262
|
+
// Per dCBOR spec, only false/true/null/floats are valid
|
|
263
|
+
throw new CborError({ type: "InvalidSimpleValue" });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function checkCanonicalEncoding(cbor: Cbor | CborNumber, buf: Uint8Array): void {
|
|
270
|
+
// If it's already a CBOR object, encode it directly
|
|
271
|
+
// Otherwise treat it as a native value (for floats, etc.)
|
|
272
|
+
const buf2 = isCbor(cbor) ? cborData(cbor) : encodeCbor(cbor);
|
|
273
|
+
if (!areBytesEqual(buf, buf2)) {
|
|
274
|
+
throw new CborError({ type: "NonCanonicalNumeric" });
|
|
275
|
+
}
|
|
276
|
+
}
|