@ipld/car 3.2.4 → 4.1.1
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/README.md +183 -2
- package/api.ts +22 -4
- package/buffer-writer +1 -0
- package/cjs/browser-test/common.js +78 -3
- package/cjs/browser-test/node-test-large.js +8 -8
- package/cjs/browser-test/test-buffer-writer.js +330 -0
- package/cjs/browser-test/test-errors.js +57 -34
- package/cjs/browser-test/test-indexer.js +12 -0
- package/cjs/browser-test/test-reader.js +83 -0
- package/cjs/browser-test/test-writer.js +3 -3
- package/cjs/lib/buffer-writer.js +161 -0
- package/cjs/lib/decoder.js +72 -15
- package/cjs/lib/encoder.js +2 -2
- package/cjs/lib/header-validator.js +29 -0
- package/cjs/lib/reader-browser.js +7 -7
- package/cjs/lib/writer-browser.js +1 -1
- package/cjs/node-test/common.js +78 -3
- package/cjs/node-test/node-test-large.js +8 -8
- package/cjs/node-test/test-buffer-writer.js +330 -0
- package/cjs/node-test/test-errors.js +57 -34
- package/cjs/node-test/test-indexer.js +12 -0
- package/cjs/node-test/test-reader.js +83 -0
- package/cjs/node-test/test-writer.js +3 -3
- package/esm/browser-test/common.js +76 -1
- package/esm/browser-test/test-buffer-writer.js +311 -0
- package/esm/browser-test/test-errors.js +57 -33
- package/esm/browser-test/test-indexer.js +15 -0
- package/esm/browser-test/test-reader.js +90 -1
- package/esm/browser-test/test-writer.js +3 -3
- package/esm/lib/buffer-writer.js +126 -0
- package/esm/lib/decoder.js +69 -13
- package/esm/lib/header-validator.js +23 -0
- package/esm/lib/reader-browser.js +7 -8
- package/esm/lib/writer-browser.js +1 -1
- package/esm/node-test/common.js +76 -1
- package/esm/node-test/test-buffer-writer.js +311 -0
- package/esm/node-test/test-errors.js +57 -33
- package/esm/node-test/test-indexer.js +15 -0
- package/esm/node-test/test-reader.js +90 -1
- package/esm/node-test/test-writer.js +3 -3
- package/examples/car-to-fixture.js +1 -4
- package/examples/dump-index.js +24 -0
- package/examples/test-examples.js +33 -0
- package/lib/buffer-writer.js +286 -0
- package/lib/coding.ts +17 -2
- package/lib/decoder.js +130 -14
- package/lib/header-validator.js +33 -0
- package/lib/header.ipldsch +6 -0
- package/lib/reader-browser.js +11 -11
- package/lib/writer-browser.js +1 -1
- package/package.json +16 -6
- package/test/_fixtures_to_js.mjs +24 -0
- package/test/common.js +49 -3
- package/test/go.carv2 +0 -0
- package/test/test-buffer-writer.js +256 -0
- package/test/test-errors.js +52 -30
- package/test/test-indexer.js +24 -1
- package/test/test-reader.js +94 -1
- package/test/test-writer.js +3 -3
- package/tsconfig.json +3 -1
- package/types/api.d.ts +16 -0
- package/types/api.d.ts.map +1 -1
- package/types/lib/buffer-writer.d.ts +86 -0
- package/types/lib/buffer-writer.d.ts.map +1 -0
- package/types/lib/coding.d.ts +14 -4
- package/types/lib/coding.d.ts.map +1 -1
- package/types/lib/decoder.d.ts +38 -2
- package/types/lib/decoder.d.ts.map +1 -1
- package/types/lib/header-validator.d.ts +2 -0
- package/types/lib/header-validator.d.ts.map +1 -0
- package/types/lib/reader-browser.d.ts +15 -7
- package/types/lib/reader-browser.d.ts.map +1 -1
- package/types/test/_fixtures_to_js.d.mts +3 -0
- package/types/test/_fixtures_to_js.d.mts.map +1 -0
- package/types/test/common.d.ts +13 -0
- package/types/test/common.d.ts.map +1 -1
- package/types/test/fixtures-expectations.d.ts +63 -0
- package/types/test/fixtures-expectations.d.ts.map +1 -0
- package/types/test/fixtures.d.ts +3 -0
- package/types/test/fixtures.d.ts.map +1 -0
- package/types/test/test-buffer-writer.d.ts +2 -0
- package/types/test/test-buffer-writer.d.ts.map +1 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var bufferWriter = require('../lib/buffer-writer.js');
|
|
4
|
+
var reader = require('../lib/reader.js');
|
|
5
|
+
var encoder = require('../lib/encoder.js');
|
|
6
|
+
var common = require('./common.js');
|
|
7
|
+
var multiformats = require('multiformats');
|
|
8
|
+
var CBOR = require('@ipld/dag-cbor');
|
|
9
|
+
var sha2 = require('multiformats/hashes/sha2');
|
|
10
|
+
var identity = require('multiformats/hashes/identity');
|
|
11
|
+
var raw = require('multiformats/codecs/raw');
|
|
12
|
+
var Block = require('multiformats/block');
|
|
13
|
+
|
|
14
|
+
function _interopNamespace(e) {
|
|
15
|
+
if (e && e.__esModule) return e;
|
|
16
|
+
var n = Object.create(null);
|
|
17
|
+
if (e) {
|
|
18
|
+
Object.keys(e).forEach(function (k) {
|
|
19
|
+
if (k !== 'default') {
|
|
20
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
21
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () { return e[k]; }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
n["default"] = e;
|
|
29
|
+
return Object.freeze(n);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var CBOR__namespace = /*#__PURE__*/_interopNamespace(CBOR);
|
|
33
|
+
var raw__namespace = /*#__PURE__*/_interopNamespace(raw);
|
|
34
|
+
var Block__namespace = /*#__PURE__*/_interopNamespace(Block);
|
|
35
|
+
|
|
36
|
+
describe('CarBufferWriter', () => {
|
|
37
|
+
const cid = multiformats.CID.parse('bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu');
|
|
38
|
+
describe('calculateHeaderLength', async () => {
|
|
39
|
+
for (const count of [
|
|
40
|
+
0,
|
|
41
|
+
1,
|
|
42
|
+
10,
|
|
43
|
+
18,
|
|
44
|
+
24,
|
|
45
|
+
48,
|
|
46
|
+
124,
|
|
47
|
+
255,
|
|
48
|
+
258,
|
|
49
|
+
65536 - 1,
|
|
50
|
+
65536
|
|
51
|
+
]) {
|
|
52
|
+
it(`calculateHeaderLength(new Array(${ count }).fill(36))`, () => {
|
|
53
|
+
const roots = new Array(count).fill(cid);
|
|
54
|
+
const sizes = new Array(count).fill(cid.bytes.byteLength);
|
|
55
|
+
common.assert.deepEqual(bufferWriter.calculateHeaderLength(sizes), encoder.createHeader(roots).byteLength);
|
|
56
|
+
});
|
|
57
|
+
it(`calculateHeaderLength(new Array(${ count }).fill(36))`, () => {
|
|
58
|
+
const roots = new Array(count).fill(cid);
|
|
59
|
+
const rootLengths = roots.map(c => c.bytes.byteLength);
|
|
60
|
+
common.assert.deepEqual(bufferWriter.calculateHeaderLength(rootLengths), encoder.createHeader(roots).byteLength);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
it('estimate on large CIDs', () => {
|
|
64
|
+
const largeCID = multiformats.CID.parse(`bafkqbbac${ 'a'.repeat(416) }`);
|
|
65
|
+
common.assert.equal(bufferWriter.calculateHeaderLength([
|
|
66
|
+
cid.bytes.byteLength,
|
|
67
|
+
largeCID.bytes.byteLength
|
|
68
|
+
]), encoder.createHeader([
|
|
69
|
+
cid,
|
|
70
|
+
largeCID
|
|
71
|
+
]).byteLength);
|
|
72
|
+
});
|
|
73
|
+
it('estimate on large CIDs 2', () => {
|
|
74
|
+
const largeCID = multiformats.CID.createV1(raw__namespace.code, identity.identity.digest(new Uint8Array(512).fill(1)));
|
|
75
|
+
common.assert.equal(bufferWriter.calculateHeaderLength([
|
|
76
|
+
cid.bytes.byteLength,
|
|
77
|
+
largeCID.bytes.byteLength
|
|
78
|
+
]), encoder.createHeader([
|
|
79
|
+
cid,
|
|
80
|
+
largeCID
|
|
81
|
+
]).byteLength);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('writer', () => {
|
|
85
|
+
it('estimate header and write blocks', async () => {
|
|
86
|
+
const headerSize = bufferWriter.estimateHeaderLength(1);
|
|
87
|
+
const dataSize = 256;
|
|
88
|
+
const buffer = new ArrayBuffer(headerSize + dataSize);
|
|
89
|
+
const writer = bufferWriter.createWriter(buffer, { headerSize });
|
|
90
|
+
const b1 = await Block__namespace.encode({
|
|
91
|
+
value: { hello: 'world' },
|
|
92
|
+
codec: CBOR__namespace,
|
|
93
|
+
hasher: sha2.sha256
|
|
94
|
+
});
|
|
95
|
+
writer.write(b1);
|
|
96
|
+
const b2 = await Block__namespace.encode({
|
|
97
|
+
value: { bye: 'world' },
|
|
98
|
+
codec: CBOR__namespace,
|
|
99
|
+
hasher: sha2.sha256
|
|
100
|
+
});
|
|
101
|
+
writer.write(b2);
|
|
102
|
+
writer.addRoot(b1.cid);
|
|
103
|
+
const bytes = writer.close();
|
|
104
|
+
const reader$1 = await reader.CarReader.fromBytes(bytes);
|
|
105
|
+
common.assert.deepEqual(await reader$1.getRoots(), [b1.cid]);
|
|
106
|
+
common.assert.deepEqual(reader$1._blocks, [
|
|
107
|
+
{
|
|
108
|
+
cid: b1.cid,
|
|
109
|
+
bytes: b1.bytes
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
cid: b2.cid,
|
|
113
|
+
bytes: b2.bytes
|
|
114
|
+
}
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
it('overestimate header', async () => {
|
|
118
|
+
const headerSize = bufferWriter.estimateHeaderLength(2);
|
|
119
|
+
const dataSize = 256;
|
|
120
|
+
const buffer = new ArrayBuffer(headerSize + dataSize);
|
|
121
|
+
const writer = bufferWriter.createWriter(buffer, { headerSize });
|
|
122
|
+
const b1 = await Block__namespace.encode({
|
|
123
|
+
value: { hello: 'world' },
|
|
124
|
+
codec: CBOR__namespace,
|
|
125
|
+
hasher: sha2.sha256
|
|
126
|
+
});
|
|
127
|
+
writer.write(b1);
|
|
128
|
+
const b2 = await Block__namespace.encode({
|
|
129
|
+
value: { bye: 'world' },
|
|
130
|
+
codec: CBOR__namespace,
|
|
131
|
+
hasher: sha2.sha256
|
|
132
|
+
});
|
|
133
|
+
writer.write(b2);
|
|
134
|
+
writer.addRoot(b1.cid);
|
|
135
|
+
common.assert.throws(() => writer.close(), /Header size was overestimate/);
|
|
136
|
+
const bytes = writer.close({ resize: true });
|
|
137
|
+
const reader$1 = await reader.CarReader.fromBytes(bytes);
|
|
138
|
+
common.assert.deepEqual(await reader$1.getRoots(), [b1.cid]);
|
|
139
|
+
common.assert.deepEqual(reader$1._blocks, [
|
|
140
|
+
{
|
|
141
|
+
cid: b1.cid,
|
|
142
|
+
bytes: b1.bytes
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
cid: b2.cid,
|
|
146
|
+
bytes: b2.bytes
|
|
147
|
+
}
|
|
148
|
+
]);
|
|
149
|
+
});
|
|
150
|
+
it('underestimate header', async () => {
|
|
151
|
+
const headerSize = bufferWriter.estimateHeaderLength(2);
|
|
152
|
+
const dataSize = 300;
|
|
153
|
+
const buffer = new ArrayBuffer(headerSize + dataSize);
|
|
154
|
+
const writer = bufferWriter.createWriter(buffer, { headerSize });
|
|
155
|
+
const b1 = await Block__namespace.encode({
|
|
156
|
+
value: { hello: 'world' },
|
|
157
|
+
codec: CBOR__namespace,
|
|
158
|
+
hasher: sha2.sha256
|
|
159
|
+
});
|
|
160
|
+
writer.write(b1);
|
|
161
|
+
writer.addRoot(b1.cid);
|
|
162
|
+
const b2 = await Block__namespace.encode({
|
|
163
|
+
value: { bye: 'world' },
|
|
164
|
+
codec: CBOR__namespace,
|
|
165
|
+
hasher: sha2.sha512
|
|
166
|
+
});
|
|
167
|
+
writer.write(b2);
|
|
168
|
+
common.assert.throws(() => writer.addRoot(b2.cid), /has no capacity/);
|
|
169
|
+
writer.addRoot(b2.cid, { resize: true });
|
|
170
|
+
const bytes = writer.close();
|
|
171
|
+
const reader$1 = await reader.CarReader.fromBytes(bytes);
|
|
172
|
+
common.assert.deepEqual(await reader$1.getRoots(), [
|
|
173
|
+
b1.cid,
|
|
174
|
+
b2.cid
|
|
175
|
+
]);
|
|
176
|
+
common.assert.deepEqual(reader$1._blocks, [
|
|
177
|
+
{
|
|
178
|
+
cid: b1.cid,
|
|
179
|
+
bytes: b1.bytes
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
cid: b2.cid,
|
|
183
|
+
bytes: b2.bytes
|
|
184
|
+
}
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
it('has no space for the root', async () => {
|
|
189
|
+
const headerSize = bufferWriter.estimateHeaderLength(1);
|
|
190
|
+
const dataSize = 100;
|
|
191
|
+
const buffer = new ArrayBuffer(headerSize + dataSize);
|
|
192
|
+
const writer = bufferWriter.createWriter(buffer, { headerSize });
|
|
193
|
+
const b1 = await Block__namespace.encode({
|
|
194
|
+
value: { hello: 'world' },
|
|
195
|
+
codec: CBOR__namespace,
|
|
196
|
+
hasher: sha2.sha256
|
|
197
|
+
});
|
|
198
|
+
writer.write(b1);
|
|
199
|
+
writer.addRoot(b1.cid);
|
|
200
|
+
const b2 = await Block__namespace.encode({
|
|
201
|
+
value: { bye: 'world' },
|
|
202
|
+
codec: CBOR__namespace,
|
|
203
|
+
hasher: sha2.sha256
|
|
204
|
+
});
|
|
205
|
+
writer.write(b2);
|
|
206
|
+
common.assert.throws(() => writer.addRoot(b2.cid), /Buffer has no capacity for a new root/);
|
|
207
|
+
common.assert.throws(() => writer.addRoot(b2.cid, { resize: true }), /Buffer has no capacity for a new root/);
|
|
208
|
+
const bytes = writer.close();
|
|
209
|
+
const reader$1 = await reader.CarReader.fromBytes(bytes);
|
|
210
|
+
common.assert.deepEqual(await reader$1.getRoots(), [b1.cid]);
|
|
211
|
+
common.assert.deepEqual(reader$1._blocks, [
|
|
212
|
+
{
|
|
213
|
+
cid: b1.cid,
|
|
214
|
+
bytes: b1.bytes
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
cid: b2.cid,
|
|
218
|
+
bytes: b2.bytes
|
|
219
|
+
}
|
|
220
|
+
]);
|
|
221
|
+
});
|
|
222
|
+
it('has no space for the block', async () => {
|
|
223
|
+
const headerSize = bufferWriter.estimateHeaderLength(1);
|
|
224
|
+
const dataSize = 58;
|
|
225
|
+
const buffer = new ArrayBuffer(headerSize + dataSize);
|
|
226
|
+
const writer = bufferWriter.createWriter(buffer, { headerSize });
|
|
227
|
+
const b1 = await Block__namespace.encode({
|
|
228
|
+
value: { hello: 'world' },
|
|
229
|
+
codec: CBOR__namespace,
|
|
230
|
+
hasher: sha2.sha256
|
|
231
|
+
});
|
|
232
|
+
writer.write(b1);
|
|
233
|
+
writer.addRoot(b1.cid);
|
|
234
|
+
const b2 = await Block__namespace.encode({
|
|
235
|
+
value: { bye: 'world' },
|
|
236
|
+
codec: CBOR__namespace,
|
|
237
|
+
hasher: sha2.sha256
|
|
238
|
+
});
|
|
239
|
+
common.assert.throws(() => writer.write(b2), /Buffer has no capacity for this block/);
|
|
240
|
+
const bytes = writer.close();
|
|
241
|
+
const reader$1 = await reader.CarReader.fromBytes(bytes);
|
|
242
|
+
common.assert.deepEqual(await reader$1.getRoots(), [b1.cid]);
|
|
243
|
+
common.assert.deepEqual(reader$1._blocks, [{
|
|
244
|
+
cid: b1.cid,
|
|
245
|
+
bytes: b1.bytes
|
|
246
|
+
}]);
|
|
247
|
+
});
|
|
248
|
+
it('provide roots', async () => {
|
|
249
|
+
const b1 = await Block__namespace.encode({
|
|
250
|
+
value: { hello: 'world' },
|
|
251
|
+
codec: CBOR__namespace,
|
|
252
|
+
hasher: sha2.sha256
|
|
253
|
+
});
|
|
254
|
+
const b2 = await Block__namespace.encode({
|
|
255
|
+
value: { bye: 'world' },
|
|
256
|
+
codec: CBOR__namespace,
|
|
257
|
+
hasher: sha2.sha512
|
|
258
|
+
});
|
|
259
|
+
const buffer = new ArrayBuffer(300);
|
|
260
|
+
const writer = bufferWriter.createWriter(buffer, {
|
|
261
|
+
roots: [
|
|
262
|
+
b1.cid,
|
|
263
|
+
b2.cid
|
|
264
|
+
]
|
|
265
|
+
});
|
|
266
|
+
writer.write(b1);
|
|
267
|
+
writer.write(b2);
|
|
268
|
+
const bytes = writer.close();
|
|
269
|
+
const reader$1 = await reader.CarReader.fromBytes(bytes);
|
|
270
|
+
common.assert.deepEqual(await reader$1.getRoots(), [
|
|
271
|
+
b1.cid,
|
|
272
|
+
b2.cid
|
|
273
|
+
]);
|
|
274
|
+
common.assert.deepEqual(reader$1._blocks, [
|
|
275
|
+
{
|
|
276
|
+
cid: b1.cid,
|
|
277
|
+
bytes: b1.bytes
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
cid: b2.cid,
|
|
281
|
+
bytes: b2.bytes
|
|
282
|
+
}
|
|
283
|
+
]);
|
|
284
|
+
});
|
|
285
|
+
it('provide large CID root', async () => {
|
|
286
|
+
const bytes = new Uint8Array(512).fill(1);
|
|
287
|
+
const b1 = await Block__namespace.encode({
|
|
288
|
+
value: { hello: 'world' },
|
|
289
|
+
codec: CBOR__namespace,
|
|
290
|
+
hasher: sha2.sha256
|
|
291
|
+
});
|
|
292
|
+
const b2 = {
|
|
293
|
+
cid: multiformats.CID.createV1(raw__namespace.code, identity.identity.digest(bytes)),
|
|
294
|
+
bytes
|
|
295
|
+
};
|
|
296
|
+
const headerSize = CBOR__namespace.encode({
|
|
297
|
+
version: 1,
|
|
298
|
+
roots: [
|
|
299
|
+
b1.cid,
|
|
300
|
+
b2.cid
|
|
301
|
+
]
|
|
302
|
+
}).byteLength;
|
|
303
|
+
const bodySize = bufferWriter.blockLength(b1) + bufferWriter.blockLength(b2);
|
|
304
|
+
const varintSize = multiformats.varint.encodingLength(headerSize);
|
|
305
|
+
const writer = bufferWriter.createWriter(new ArrayBuffer(varintSize + headerSize + bodySize), {
|
|
306
|
+
roots: [
|
|
307
|
+
b1.cid,
|
|
308
|
+
b2.cid
|
|
309
|
+
]
|
|
310
|
+
});
|
|
311
|
+
writer.write(b1);
|
|
312
|
+
writer.write(b2);
|
|
313
|
+
const car = writer.close();
|
|
314
|
+
const reader$1 = await reader.CarReader.fromBytes(car);
|
|
315
|
+
common.assert.deepEqual(await reader$1.getRoots(), [
|
|
316
|
+
b1.cid,
|
|
317
|
+
b2.cid
|
|
318
|
+
]);
|
|
319
|
+
common.assert.deepEqual(reader$1._blocks, [
|
|
320
|
+
{
|
|
321
|
+
cid: b1.cid,
|
|
322
|
+
bytes: b1.bytes
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
cid: b2.cid,
|
|
326
|
+
bytes: b2.bytes
|
|
327
|
+
}
|
|
328
|
+
]);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var multiformats = require('multiformats');
|
|
4
|
-
var
|
|
4
|
+
var CBOR = require('@ipld/dag-cbor');
|
|
5
5
|
var varint = require('varint');
|
|
6
6
|
var reader = require('../lib/reader.js');
|
|
7
7
|
var common = require('./common.js');
|
|
8
8
|
|
|
9
9
|
function makeHeader(block) {
|
|
10
|
-
const u =
|
|
10
|
+
const u = CBOR.encode(block);
|
|
11
11
|
const l = varint.encode(u.length);
|
|
12
12
|
const u2 = new Uint8Array(u.length + l.length);
|
|
13
13
|
u2.set(l, 0);
|
|
@@ -26,42 +26,65 @@ describe('Misc errors', () => {
|
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
28
|
it('bad version', async () => {
|
|
29
|
-
const buf2 = multiformats.bytes.fromHex('
|
|
30
|
-
common.assert.strictEqual(multiformats.bytes.toHex(makeHeader({ version:
|
|
31
|
-
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR version:
|
|
29
|
+
const buf2 = multiformats.bytes.fromHex('0aa16776657273696f6e03');
|
|
30
|
+
common.assert.strictEqual(multiformats.bytes.toHex(makeHeader({ version: 3 })), '0aa16776657273696f6e03');
|
|
31
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR version: 3');
|
|
32
32
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
describe('bad header', async () => {
|
|
34
|
+
it('sanity check', async () => {
|
|
35
|
+
const buf2 = makeHeader({
|
|
36
|
+
version: 1,
|
|
37
|
+
roots: []
|
|
38
|
+
});
|
|
39
|
+
await common.assert.isFulfilled(reader.CarReader.fromBytes(buf2));
|
|
37
40
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
buf2 = makeHeader({
|
|
42
|
-
version: '1',
|
|
43
|
-
roots: []
|
|
41
|
+
it('no \'version\' array', async () => {
|
|
42
|
+
const buf2 = makeHeader({ roots: [] });
|
|
43
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
44
44
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
it('bad \'version\' type', async () => {
|
|
46
|
+
const buf2 = makeHeader({
|
|
47
|
+
version: '1',
|
|
48
|
+
roots: []
|
|
49
|
+
});
|
|
50
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
51
51
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
it('no \'roots\' array', async () => {
|
|
53
|
+
const buf2 = makeHeader({ version: 1 });
|
|
54
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
55
|
+
});
|
|
56
|
+
it('bad \'roots\' type', async () => {
|
|
57
|
+
const buf2 = makeHeader({
|
|
58
|
+
version: 1,
|
|
59
|
+
roots: {}
|
|
60
|
+
});
|
|
61
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
62
|
+
});
|
|
63
|
+
it('extraneous properties', async () => {
|
|
64
|
+
const buf2 = makeHeader({
|
|
65
|
+
version: 1,
|
|
66
|
+
roots: [],
|
|
67
|
+
blip: true
|
|
68
|
+
});
|
|
69
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
70
|
+
});
|
|
71
|
+
it('not an object', async () => {
|
|
72
|
+
const buf2 = makeHeader([
|
|
73
|
+
1,
|
|
74
|
+
[]
|
|
75
|
+
]);
|
|
76
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
77
|
+
});
|
|
78
|
+
it('not an object', async () => {
|
|
79
|
+
const buf2 = makeHeader(null);
|
|
80
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
81
|
+
});
|
|
82
|
+
it('recursive v2 header', async () => {
|
|
83
|
+
const v2Header = common.goCarV2Bytes.slice(0, 51);
|
|
84
|
+
const buf2 = new Uint8Array(51 * 2);
|
|
85
|
+
buf2.set(v2Header, 0);
|
|
86
|
+
buf2.set(v2Header, 51);
|
|
87
|
+
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR version: 2 (expected 1)');
|
|
57
88
|
});
|
|
58
|
-
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
59
|
-
buf2 = makeHeader([
|
|
60
|
-
1,
|
|
61
|
-
[]
|
|
62
|
-
]);
|
|
63
|
-
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
64
|
-
buf2 = makeHeader(null);
|
|
65
|
-
await common.assert.isRejected(reader.CarReader.fromBytes(buf2), Error, 'Invalid CAR header format');
|
|
66
89
|
});
|
|
67
90
|
});
|
|
@@ -15,6 +15,18 @@ describe('CarIndexer fromBytes()', () => {
|
|
|
15
15
|
}
|
|
16
16
|
common.assert.deepStrictEqual(indexData, common.goCarIndex);
|
|
17
17
|
});
|
|
18
|
+
it('v2 complete', async () => {
|
|
19
|
+
const indexer$1 = await indexer.CarIndexer.fromBytes(common.goCarV2Bytes);
|
|
20
|
+
const roots = await indexer$1.getRoots();
|
|
21
|
+
common.assert.strictEqual(roots.length, 1);
|
|
22
|
+
common.assert.ok(common.goCarV2Roots[0].equals(roots[0]));
|
|
23
|
+
common.assert.strictEqual(indexer$1.version, 2);
|
|
24
|
+
const indexData = [];
|
|
25
|
+
for await (const index of indexer$1) {
|
|
26
|
+
indexData.push(index);
|
|
27
|
+
}
|
|
28
|
+
common.assert.deepStrictEqual(indexData, common.goCarV2Index);
|
|
29
|
+
});
|
|
18
30
|
it('bad argument', async () => {
|
|
19
31
|
for (const arg of [
|
|
20
32
|
true,
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
var reader = require('../lib/reader.js');
|
|
4
4
|
var writer = require('../lib/writer.js');
|
|
5
|
+
var decoder = require('../lib/decoder.js');
|
|
5
6
|
var Block = require('multiformats/block');
|
|
6
7
|
var sha2 = require('multiformats/hashes/sha2');
|
|
7
8
|
var raw = require('multiformats/codecs/raw');
|
|
9
|
+
var base64 = require('multiformats/bases/base64');
|
|
10
|
+
var dagPb = require('@ipld/dag-pb');
|
|
8
11
|
var common = require('./common.js');
|
|
9
12
|
var verifyStoreReader = require('./verify-store-reader.js');
|
|
13
|
+
var fixtures = require('./fixtures.js');
|
|
14
|
+
var fixturesExpectations = require('./fixtures-expectations.js');
|
|
10
15
|
|
|
11
16
|
function _interopNamespace(e) {
|
|
12
17
|
if (e && e.__esModule) return e;
|
|
@@ -28,6 +33,7 @@ function _interopNamespace(e) {
|
|
|
28
33
|
|
|
29
34
|
var Block__namespace = /*#__PURE__*/_interopNamespace(Block);
|
|
30
35
|
var raw__namespace = /*#__PURE__*/_interopNamespace(raw);
|
|
36
|
+
var dagPb__namespace = /*#__PURE__*/_interopNamespace(dagPb);
|
|
31
37
|
|
|
32
38
|
describe('CarReader fromBytes()', () => {
|
|
33
39
|
it('complete', async () => {
|
|
@@ -66,6 +72,29 @@ describe('CarReader fromBytes()', () => {
|
|
|
66
72
|
message: 'Unexpected end of data'
|
|
67
73
|
});
|
|
68
74
|
});
|
|
75
|
+
it('v2 complete', async () => {
|
|
76
|
+
const reader$1 = await reader.CarReader.fromBytes(common.goCarV2Bytes);
|
|
77
|
+
const roots = await reader$1.getRoots();
|
|
78
|
+
common.assert.strictEqual(roots.length, 1);
|
|
79
|
+
common.assert.ok(common.goCarV2Roots[0].equals(roots[0]));
|
|
80
|
+
common.assert.strictEqual(reader$1.version, 2);
|
|
81
|
+
for (const {cid} of common.goCarV2Index) {
|
|
82
|
+
const block = await reader$1.get(cid);
|
|
83
|
+
common.assert.isDefined(block);
|
|
84
|
+
if (block) {
|
|
85
|
+
common.assert.ok(cid.equals(block.cid));
|
|
86
|
+
let content;
|
|
87
|
+
if (cid.code === dagPb__namespace.code) {
|
|
88
|
+
content = dagPb__namespace.decode(block.bytes);
|
|
89
|
+
} else if (cid.code === 85) {
|
|
90
|
+
content = new TextDecoder().decode(block.bytes);
|
|
91
|
+
} else {
|
|
92
|
+
common.assert.fail('Unexpected codec');
|
|
93
|
+
}
|
|
94
|
+
common.assert.deepStrictEqual(content, common.goCarV2Contents[cid.toString()]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
69
98
|
it('decode error - trailing null bytes', async () => {
|
|
70
99
|
const bytes = new Uint8Array(common.carBytes.length + 5);
|
|
71
100
|
bytes.set(common.carBytes);
|
|
@@ -192,4 +221,58 @@ describe('CarReader fromIterable()', () => {
|
|
|
192
221
|
message: 'Unexpected end of data'
|
|
193
222
|
});
|
|
194
223
|
});
|
|
224
|
+
it('v2 decode error - truncated', async () => {
|
|
225
|
+
const bytes = common.goCarV2Bytes.slice();
|
|
226
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
227
|
+
dv.setBigUint64(35, BigInt(448 - 10), true);
|
|
228
|
+
await common.assert.isRejected(reader.CarReader.fromIterable(common.makeIterable(bytes, 64)), {
|
|
229
|
+
name: 'Error',
|
|
230
|
+
message: 'Unexpected end of data'
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe('Shared fixtures', () => {
|
|
235
|
+
describe('Header', () => {
|
|
236
|
+
for (const [name, {
|
|
237
|
+
version: expectedVersion,
|
|
238
|
+
err: expectedError
|
|
239
|
+
}] of Object.entries(fixturesExpectations.expectations)) {
|
|
240
|
+
it(name, async () => {
|
|
241
|
+
const data = base64.base64.baseDecode(fixtures.data[name]);
|
|
242
|
+
let header;
|
|
243
|
+
try {
|
|
244
|
+
header = await decoder.readHeader(decoder.bytesReader(data));
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (expectedError != null) {
|
|
247
|
+
common.assert.equal(err.message, expectedError);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
common.assert.ifError(err);
|
|
251
|
+
}
|
|
252
|
+
if (expectedError != null) {
|
|
253
|
+
common.assert.fail(`Expected error: ${ expectedError }`);
|
|
254
|
+
}
|
|
255
|
+
common.assert.isDefined(header, 'did not decode header');
|
|
256
|
+
if (expectedVersion != null && header != null) {
|
|
257
|
+
common.assert.strictEqual(header.version, expectedVersion);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
describe('Contents', () => {
|
|
263
|
+
for (const [name, {cids: expectedCids}] of Object.entries(fixturesExpectations.expectations)) {
|
|
264
|
+
if (expectedCids == null) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
it(name, async () => {
|
|
268
|
+
const data = base64.base64.baseDecode(fixtures.data[name]);
|
|
269
|
+
const reader$1 = await reader.CarReader.fromBytes(data);
|
|
270
|
+
let i = 0;
|
|
271
|
+
for await (const cid of reader$1.cids()) {
|
|
272
|
+
common.assert.strictEqual(cid.toString(), expectedCids[i++]);
|
|
273
|
+
}
|
|
274
|
+
common.assert.strictEqual(i, expectedCids.length);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
195
278
|
});
|
|
@@ -182,9 +182,9 @@ describe('CarWriter', () => {
|
|
|
182
182
|
const rawBytes = await append(0);
|
|
183
183
|
const pbBytes = await append(1);
|
|
184
184
|
const cborBytes = await append(2);
|
|
185
|
-
common.assert(rawBytes.length > 0);
|
|
186
|
-
common.assert(pbBytes.length > 0);
|
|
187
|
-
common.assert(cborBytes.length > 0);
|
|
185
|
+
common.assert.ok(rawBytes.length > 0);
|
|
186
|
+
common.assert.ok(pbBytes.length > 0);
|
|
187
|
+
common.assert.ok(cborBytes.length > 0);
|
|
188
188
|
const reassembled = concatBytes([
|
|
189
189
|
headerBytes,
|
|
190
190
|
rawBytes,
|
|
@@ -186,6 +186,77 @@ const goCarIndex = [
|
|
|
186
186
|
blockLength: 18
|
|
187
187
|
}
|
|
188
188
|
];
|
|
189
|
+
const goCarV2Bytes = bytes.fromHex('0aa16776657273696f6e02000000000000000000000000000000003300000000000000c001000000000000f30100000000000038a265726f6f747381d82a5823001220fb16f5083412ef1371d031ed4aa239903d84efdadf1ba3cd678e6475b1a232f86776657273696f6e01511220fb16f5083412ef1371d031ed4aa239903d84efdadf1ba3cd678e6475b1a232f8122d0a221220d9c0d5376d26f1931f7ad52d7acc00fc1090d2edb0808bf61eeb0a152826f6261204f09f8da418a40185011220d9c0d5376d26f1931f7ad52d7acc00fc1090d2edb0808bf61eeb0a152826f62612310a221220d745b7757f5b4593eeab7820306c7bc64eb496a7410a0d07df7a34ffec4b97f1120962617272656c657965183a122e0a2401551220a2e1c40da1ae335d4dffe729eb4d5ca23b74b9e51fc535f4a804a261080c294d1204f09f90a11807581220d745b7757f5b4593eeab7820306c7bc64eb496a7410a0d07df7a34ffec4b97f112340a2401551220b474a99a2705e23cf905a484ec6d14ef58b56bbe62e9292783466ec363b5072d120a666973686d6f6e67657218042801551220b474a99a2705e23cf905a484ec6d14ef58b56bbe62e9292783466ec363b5072d666973682b01551220a2e1c40da1ae335d4dffe729eb4d5ca23b74b9e51fc535f4a804a261080c294d6c6f62737465720100000028000000c800000000000000a2e1c40da1ae335d4dffe729eb4d5ca23b74b9e51fc535f4a804a261080c294d9401000000000000b474a99a2705e23cf905a484ec6d14ef58b56bbe62e9292783466ec363b5072d6b01000000000000d745b7757f5b4593eeab7820306c7bc64eb496a7410a0d07df7a34ffec4b97f11201000000000000d9c0d5376d26f1931f7ad52d7acc00fc1090d2edb0808bf61eeb0a152826f6268b00000000000000fb16f5083412ef1371d031ed4aa239903d84efdadf1ba3cd678e6475b1a232f83900000000000000');
|
|
190
|
+
const goCarV2Roots = [CID.parse('QmfEoLyB5NndqeKieExd1rtJzTduQUPEV8TwAYcUiy3H5Z')];
|
|
191
|
+
const goCarV2Index = [
|
|
192
|
+
{
|
|
193
|
+
blockLength: 47,
|
|
194
|
+
blockOffset: 143,
|
|
195
|
+
cid: CID.parse('QmfEoLyB5NndqeKieExd1rtJzTduQUPEV8TwAYcUiy3H5Z'),
|
|
196
|
+
length: 82,
|
|
197
|
+
offset: 108
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
blockLength: 99,
|
|
201
|
+
blockOffset: 226,
|
|
202
|
+
cid: CID.parse('QmczfirA7VEH7YVvKPTPoU69XM3qY4DC39nnTsWd4K3SkM'),
|
|
203
|
+
length: 135,
|
|
204
|
+
offset: 190
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
blockLength: 54,
|
|
208
|
+
blockOffset: 360,
|
|
209
|
+
cid: CID.parse('Qmcpz2FHJD7VAhg1fxFXdYJKePtkx1BsHuCrAgWVnaHMTE'),
|
|
210
|
+
length: 89,
|
|
211
|
+
offset: 325
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
blockLength: 4,
|
|
215
|
+
blockOffset: 451,
|
|
216
|
+
cid: CID.parse('bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu'),
|
|
217
|
+
length: 41,
|
|
218
|
+
offset: 414
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
blockLength: 7,
|
|
222
|
+
blockOffset: 492,
|
|
223
|
+
cid: CID.parse('bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju'),
|
|
224
|
+
length: 44,
|
|
225
|
+
offset: 455
|
|
226
|
+
}
|
|
227
|
+
];
|
|
228
|
+
const goCarV2Contents = {
|
|
229
|
+
QmfEoLyB5NndqeKieExd1rtJzTduQUPEV8TwAYcUiy3H5Z: {
|
|
230
|
+
Links: [{
|
|
231
|
+
Hash: CID.parse('QmczfirA7VEH7YVvKPTPoU69XM3qY4DC39nnTsWd4K3SkM'),
|
|
232
|
+
Name: '\uD83C\uDF64',
|
|
233
|
+
Tsize: 164
|
|
234
|
+
}]
|
|
235
|
+
},
|
|
236
|
+
QmczfirA7VEH7YVvKPTPoU69XM3qY4DC39nnTsWd4K3SkM: {
|
|
237
|
+
Links: [
|
|
238
|
+
{
|
|
239
|
+
Hash: CID.parse('Qmcpz2FHJD7VAhg1fxFXdYJKePtkx1BsHuCrAgWVnaHMTE'),
|
|
240
|
+
Name: 'barreleye',
|
|
241
|
+
Tsize: 58
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
Hash: CID.parse('bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju'),
|
|
245
|
+
Name: '\uD83D\uDC21',
|
|
246
|
+
Tsize: 7
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
Qmcpz2FHJD7VAhg1fxFXdYJKePtkx1BsHuCrAgWVnaHMTE: {
|
|
251
|
+
Links: [{
|
|
252
|
+
Hash: CID.parse('bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu'),
|
|
253
|
+
Name: 'fishmonger',
|
|
254
|
+
Tsize: 4
|
|
255
|
+
}]
|
|
256
|
+
},
|
|
257
|
+
bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu: 'fish',
|
|
258
|
+
bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju: 'lobster'
|
|
259
|
+
};
|
|
189
260
|
export {
|
|
190
261
|
toBlock,
|
|
191
262
|
assert,
|
|
@@ -195,5 +266,9 @@ export {
|
|
|
195
266
|
carBytes,
|
|
196
267
|
goCarBytes,
|
|
197
268
|
goCarRoots,
|
|
198
|
-
goCarIndex
|
|
269
|
+
goCarIndex,
|
|
270
|
+
goCarV2Bytes,
|
|
271
|
+
goCarV2Roots,
|
|
272
|
+
goCarV2Index,
|
|
273
|
+
goCarV2Contents
|
|
199
274
|
};
|