Skip to content

Commit cfecf39

Browse files
authored
refactor: use the block API from ipfs instead of ipld internals (#51)
This improves resusability of the module as it can be used by passing part of an `ipfs` or `ipfs-http-client` instance in. It also means we no longer double-serialize blocks before adding them which delivers a small but almost unperceptible performance increase. Finally also documents the `pin` and `preload` arguments. BREAKING CHANGE: The importer takes a pin argument (previously undocumented) - it used to default to true but since this switches to use the block API the default has changed to false, as the typical usage pattern is to pin the root block of a DAG recursively instead of every block that makes up the DAG.
1 parent bca2cdf commit cfecf39

24 files changed

+271
-160
lines changed

packages/ipfs-unixfs-exporter/test/exporter-sharded.spec.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ const {
1818
DAGLink,
1919
DAGNode
2020
} = require('ipld-dag-pb')
21+
const blockApi = require('./helpers/block')
2122

2223
const SHARD_SPLIT_THRESHOLD = 10
2324

2425
describe('exporter sharded', function () {
2526
this.timeout(30000)
2627

2728
let ipld
29+
let block
2830

2931
const createShard = (numFiles) => {
3032
return createShardWithFileNames(numFiles, (index) => `file-${index}`)
@@ -40,14 +42,15 @@ describe('exporter sharded', function () {
4042
}
4143

4244
const createShardWithFiles = async (files) => {
43-
return (await last(importer(files, ipld, {
45+
return (await last(importer(files, block, {
4446
shardSplitThreshold: SHARD_SPLIT_THRESHOLD,
4547
wrapWithDirectory: true
4648
}))).cid
4749
}
4850

4951
before(async () => {
5052
ipld = await inMemory(IPLD)
53+
block = blockApi(ipld)
5154
})
5255

5356
it('exports a sharded directory', async () => {
@@ -62,7 +65,7 @@ describe('exporter sharded', function () {
6265
const imported = await all(importer(Object.keys(files).map(path => ({
6366
path,
6467
content: files[path].content
65-
})), ipld, {
68+
})), block, {
6669
wrapWithDirectory: true,
6770
shardSplitThreshold: SHARD_SPLIT_THRESHOLD
6871
}))

packages/ipfs-unixfs-exporter/test/exporter-subtree.spec.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@ const mc = require('multicodec')
1111
const all = require('async-iterator-all')
1212
const last = require('it-last')
1313
const randomBytes = require('async-iterator-buffer-stream')
14+
const blockApi = require('./helpers/block')
1415

1516
const ONE_MEG = Math.pow(1024, 2)
1617

1718
const exporter = require('./../src')
1819

1920
describe('exporter subtree', () => {
2021
let ipld
22+
let block
2123

2224
before(async () => {
2325
ipld = await inMemory(IPLD)
26+
block = blockApi(ipld)
2427
})
2528

2629
it('exports a file 2 levels down', async () => {
@@ -32,7 +35,7 @@ describe('exporter subtree', () => {
3235
}, {
3336
path: './level-1/200Bytes.txt',
3437
content
35-
}], ipld))
38+
}], block))
3639

3740
const exported = await exporter(`${imported.cid.toBaseEncodedString()}/level-1/200Bytes.txt`, ipld)
3841

@@ -54,7 +57,7 @@ describe('exporter subtree', () => {
5457
content
5558
}, {
5659
path: './level-1/level-2'
57-
}], ipld))
60+
}], block))
5861

5962
const exported = await exporter(`${imported.cid.toBaseEncodedString()}/level-1`, ipld)
6063
const files = await all(exported.content())
@@ -74,7 +77,7 @@ describe('exporter subtree', () => {
7477
const imported = await last(importer([{
7578
path: '/derp/200Bytes.txt',
7679
content: randomBytes(ONE_MEG)
77-
}], ipld))
80+
}], block))
7881

7982
try {
8083
await exporter(`${imported.cid.toBaseEncodedString()}/doesnotexist`, ipld)
@@ -89,7 +92,7 @@ describe('exporter subtree', () => {
8992
const imported = await last(importer([{
9093
path: './level-1/200Bytes.txt',
9194
content
92-
}], ipld, {
95+
}], block, {
9396
wrapWithDirectory: true
9497
}))
9598

@@ -122,7 +125,7 @@ describe('exporter subtree', () => {
122125
}, {
123126
path: './level-1/level-2/200Bytes.txt',
124127
content
125-
}], ipld))
128+
}], block))
126129

127130
const exported = await all(exporter.path(`${imported.cid.toBaseEncodedString()}/level-1/level-2/200Bytes.txt`, ipld))
128131

packages/ipfs-unixfs-exporter/test/exporter.spec.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ const last = require('it-last')
2222
const first = require('async-iterator-first')
2323
const randomBytes = require('async-iterator-buffer-stream')
2424
const AbortController = require('abort-controller')
25+
const blockApi = require('./helpers/block')
2526

2627
const ONE_MEG = Math.pow(1024, 2)
2728

2829
describe('exporter', () => {
2930
let ipld
31+
let block
3032
let bigFile
3133
let smallFile
3234

@@ -58,7 +60,7 @@ describe('exporter', () => {
5860
const result = await all(importer([{
5961
path,
6062
content: file
61-
}], ipld, {
63+
}], block, {
6264
strategy,
6365
rawLeaves,
6466
chunkerOptions: {
@@ -123,6 +125,7 @@ describe('exporter', () => {
123125

124126
before(async () => {
125127
ipld = await inMemory(IPLD)
128+
block = blockApi(ipld)
126129
})
127130

128131
it('ensure hash inputs are sanitized', async () => {
@@ -148,7 +151,7 @@ describe('exporter', () => {
148151
const files = await all(importer([{
149152
path: filePath,
150153
content: smallFile
151-
}], ipld))
154+
}], block))
152155

153156
const path = `/ipfs/${files[1].cid.toBaseEncodedString()}/${fileName}`
154157
const file = await exporter(path, ipld)
@@ -164,7 +167,7 @@ describe('exporter', () => {
164167
const files = await all(importer([{
165168
path: filePath,
166169
content: smallFile
167-
}], ipld))
170+
}], block))
168171

169172
const path = `/ipfs/${files[1].cid.toBaseEncodedString()}/${fileName}`
170173
const file = await exporter(path, ipld)
@@ -333,7 +336,7 @@ describe('exporter', () => {
333336
content: randomBytes(ONE_MEG)
334337
}, {
335338
path: './level-1/level-2'
336-
}], ipld))
339+
}], block))
337340
const dir = await exporter(importedDir.cid, ipld)
338341
const files = await all(dir.content())
339342

@@ -371,7 +374,7 @@ describe('exporter', () => {
371374
path: './dir-another'
372375
}, {
373376
path: './level-1'
374-
}], ipld))
377+
}], block))
375378

376379
const dir = await exporter(importedDir.cid, ipld)
377380
const files = await all(dir.content())
@@ -516,7 +519,7 @@ describe('exporter', () => {
516519
const imported = await first(importer([{
517520
path: '1.2MiB.txt',
518521
content: bigFile
519-
}], ipld, {
522+
}], block, {
520523
rawLeaves: true
521524
}))
522525

@@ -529,7 +532,7 @@ describe('exporter', () => {
529532
it('returns an empty stream for dir', async () => {
530533
const imported = await first(importer([{
531534
path: 'empty'
532-
}], ipld))
535+
}], block))
533536
const dir = await exporter(imported.cid, ipld)
534537
const files = await all(dir.content())
535538
expect(files.length).to.equal(0)
@@ -755,7 +758,7 @@ describe('exporter', () => {
755758
const imported = await first(importer([{
756759
path: '200Bytes.txt',
757760
content: bigFile
758-
}], ipld, {
761+
}], block, {
759762
rawLeaves: true
760763
}))
761764

@@ -771,7 +774,7 @@ describe('exporter', () => {
771774
const imported = await first(importer([{
772775
path: '200Bytes.txt',
773776
content: smallFile
774-
}], ipld, {
777+
}], block, {
775778
rawLeaves: true
776779
}))
777780

@@ -862,7 +865,7 @@ describe('exporter', () => {
862865
const imported = await all(importer([{
863866
path: '/foo/bar/baz.txt',
864867
content: Buffer.from('hello world')
865-
}], ipld))
868+
}], block))
866869

867870
const exported = await exporter(imported[0].cid, ipld)
868871

@@ -879,7 +882,7 @@ describe('exporter', () => {
879882
}, {
880883
path: '/foo/bar/quux.txt',
881884
content: Buffer.from('hello world')
882-
}], ipld))
885+
}], block))
883886

884887
const exported = await all(exporter.recursive(dir.cid, ipld))
885888
const dirCid = dir.cid.toBaseEncodedString()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict'
2+
3+
const DAG_PB = require('ipld-dag-pb')
4+
const multicodec = require('multicodec')
5+
const mh = require('multihashing-async').multihash
6+
7+
module.exports = (ipld) => {
8+
// make ipld behave like the block api, some tests need to pull
9+
// data from ipld so can't use use a simple hash
10+
return {
11+
put: async (buf, { cid }) => {
12+
const multihash = mh.decode(cid.multihash)
13+
14+
if (cid.codec === 'dag-pb') {
15+
buf = DAG_PB.util.deserialize(buf)
16+
}
17+
18+
await ipld.put(buf, cid.codec === 'dag-pb' ? multicodec.DAG_PB : multicodec.RAW, {
19+
cidVersion: cid.version,
20+
hashAlg: multihash.code
21+
})
22+
23+
return { cid, data: buf }
24+
},
25+
get: async (cid, options) => {
26+
const node = await ipld.get(cid, options)
27+
28+
if (cid.codec === 'dag-pb') {
29+
return node.serialize()
30+
}
31+
32+
return { cid, data: node }
33+
}
34+
}
35+
}

packages/ipfs-unixfs-importer/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ The input's file paths and directory structure will be preserved in the [`dag-pb
140140
- `leafType` (string, defaults to `'file'`) what type of UnixFS node leaves should be - can be `'file'` or `'raw'` (ignored when `rawLeaves` is `true`)
141141
- `blockWriteConcurrency` (positive integer, defaults to 10) How many blocks to hash and write to the block store concurrently. For small numbers of large files this should be high (e.g. 50).
142142
- `fileImportConcurrency` (number, defaults to 50) How many files to import concurrently. For large numbers of small files this should be high (e.g. 50).
143+
- `pin` (boolean, defaults to `false`) Whether to pin each block as it is created
144+
- `preload` (boolean, defaults to `false`) Whether to preload each block as it is created
143145

144146
## Overriding internals
145147

packages/ipfs-unixfs-importer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"ipld-in-memory": "^3.0.0",
4747
"it-buffer-stream": "^1.0.0",
4848
"it-last": "^1.0.1",
49+
"multicodec": "^1.0.0",
4950
"nyc": "^15.0.0",
5051
"sinon": "^9.0.1"
5152
},
@@ -60,7 +61,6 @@
6061
"it-first": "^1.0.1",
6162
"it-parallel-batch": "^1.0.3",
6263
"merge-options": "^2.0.0",
63-
"multicodec": "^1.0.0",
6464
"multihashing-async": "^0.8.0",
6565
"rabin-wasm": "^0.1.1"
6666
}

packages/ipfs-unixfs-importer/src/dag-builder/dir.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ const {
66
DAGNode
77
} = require('ipld-dag-pb')
88

9-
const dirBuilder = async (item, ipld, options) => {
9+
const dirBuilder = async (item, block, options) => {
1010
const unixfs = new UnixFS({
1111
type: 'directory',
1212
mtime: item.mtime,
1313
mode: item.mode
1414
})
1515

16-
const node = new DAGNode(unixfs.marshal(), [])
17-
const cid = await persist(node, ipld, options)
16+
const buffer = new DAGNode(unixfs.marshal()).serialize()
17+
const cid = await persist(buffer, block, options)
1818
const path = item.path
1919

2020
return {
2121
cid,
2222
path,
2323
unixfs,
24-
size: node.size
24+
size: buffer.length
2525
}
2626
}
2727

packages/ipfs-unixfs-importer/src/dag-builder/file/buffer-importer.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,17 @@ const {
66
DAGNode
77
} = require('ipld-dag-pb')
88

9-
async function * bufferImporter (file, source, ipld, options) {
10-
for await (const buffer of source) {
9+
async function * bufferImporter (file, source, block, options) {
10+
for await (let buffer of source) {
1111
yield async () => {
1212
options.progress(buffer.length)
13-
let node
1413
let unixfs
15-
let size
1614

1715
const opts = {
1816
...options
1917
}
2018

2119
if (options.rawLeaves) {
22-
node = buffer
23-
size = buffer.length
24-
2520
opts.codec = 'raw'
2621
opts.cidVersion = 1
2722
} else {
@@ -32,16 +27,13 @@ async function * bufferImporter (file, source, ipld, options) {
3227
mode: file.mode
3328
})
3429

35-
node = new DAGNode(unixfs.marshal())
36-
size = node.size
30+
buffer = new DAGNode(unixfs.marshal()).serialize()
3731
}
3832

39-
const cid = await persist(node, ipld, opts)
40-
4133
return {
42-
cid: cid,
34+
cid: await persist(buffer, block, opts),
4335
unixfs,
44-
size
36+
size: buffer.length
4537
}
4638
}
4739
}

0 commit comments

Comments
 (0)