Skip to content

Commit 238fe4e

Browse files
authored
fix: reduce required number of blockstore methods (#298)
The importer only needs `.put`, the exporter only needs `.get`
1 parent f3d8ba7 commit 238fe4e

File tree

8 files changed

+84
-74
lines changed

8 files changed

+84
-74
lines changed

packages/ipfs-unixfs-exporter/src/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,7 @@ export interface ShardTraversalContext {
7979
lastBucket: Bucket<boolean>
8080
}
8181

82-
export interface BlockstoreOptions {
83-
signal?: AbortSignal
84-
}
85-
86-
export type Blockstore = Pick<InterfaceBlockstore, 'has' | 'put' | 'get'>
82+
export type Blockstore = Pick<InterfaceBlockstore, 'get'>
8783

8884
const toPathComponents = (path: string = ''): string[] => {
8985
// split on / unless escaped with \

packages/ipfs-unixfs-importer/src/dag-builder/buffer-importer.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export interface BufferImporterOptions extends ProgressOptions<BufferImportProgr
3232
}
3333

3434
export function defaultBufferImporter (options: BufferImporterOptions): BufferImporter {
35-
return async function * bufferImporter (file, block) {
36-
for await (let buffer of file.content) {
35+
return async function * bufferImporter (file, blockstore) {
36+
for await (let block of file.content) {
3737
yield async () => {
38-
options.onProgress?.(new CustomProgressEvent<ImportProgressData>('unixfs:importer:progress', { bytes: buffer.length, path: file.path }))
38+
options.onProgress?.(new CustomProgressEvent<ImportProgressData>('unixfs:importer:progress', { bytes: block.byteLength, path: file.path }))
3939
let unixfs
4040

4141
const opts: PersistOptions = {
@@ -50,19 +50,20 @@ export function defaultBufferImporter (options: BufferImporterOptions): BufferIm
5050
} else {
5151
unixfs = new UnixFS({
5252
type: options.leafType,
53-
data: buffer
53+
data: block
5454
})
5555

56-
buffer = dagPb.encode({
56+
block = dagPb.encode({
5757
Data: unixfs.marshal(),
5858
Links: []
5959
})
6060
}
6161

6262
return {
63-
cid: await persist(buffer, block, opts),
63+
cid: await persist(block, blockstore, opts),
6464
unixfs,
65-
size: BigInt(buffer.length)
65+
size: BigInt(block.length),
66+
block
6667
}
6768
}
6869
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ export const dirBuilder = async (dir: Directory, blockstore: Blockstore, options
1717
mode: dir.mode
1818
})
1919

20-
const buffer = encode(prepare({ Data: unixfs.marshal() }))
21-
const cid = await persist(buffer, blockstore, options)
20+
const block = encode(prepare({ Data: unixfs.marshal() }))
21+
const cid = await persist(block, blockstore, options)
2222
const path = dir.path
2323

2424
return {
2525
cid,
2626
path,
2727
unixfs,
28-
size: BigInt(buffer.length),
29-
originalPath: dir.originalPath
28+
size: BigInt(block.length),
29+
originalPath: dir.originalPath,
30+
block
3031
}
3132
}

packages/ipfs-unixfs-importer/src/dag-builder/file.ts

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { persist } from '../utils/persist.js'
33
import { encode, PBLink, prepare } from '@ipld/dag-pb'
44
import parallelBatch from 'it-parallel-batch'
55
import * as rawCodec from 'multiformats/codecs/raw'
6-
import type { BufferImporter, File, InProgressImportResult, Blockstore } from '../index.js'
6+
import type { BufferImporter, File, InProgressImportResult, Blockstore, SingleBlockImportResult } from '../index.js'
77
import type { FileLayout, Reducer } from '../layout/index.js'
88
import type { Version } from 'multiformats/cid'
99
import type { ProgressOptions } from 'progress-events'
@@ -15,24 +15,37 @@ interface BuildFileBatchOptions {
1515

1616
async function * buildFileBatch (file: File, blockstore: Blockstore, options: BuildFileBatchOptions): AsyncGenerator<InProgressImportResult> {
1717
let count = -1
18-
let previous: InProgressImportResult | undefined
18+
let previous: SingleBlockImportResult | undefined
1919

2020
for await (const entry of parallelBatch(options.bufferImporter(file, blockstore), options.blockWriteConcurrency)) {
2121
count++
2222

2323
if (count === 0) {
24-
previous = entry
24+
// cache the first entry if case there aren't any more
25+
previous = {
26+
...entry,
27+
single: true
28+
}
29+
2530
continue
2631
} else if (count === 1 && (previous != null)) {
27-
yield previous
32+
// we have the second block of a multiple block import so yield the first
33+
yield {
34+
...previous,
35+
block: undefined,
36+
single: undefined
37+
}
2838
previous = undefined
2939
}
3040

31-
yield entry
41+
// yield the second or later block of a multiple block import
42+
yield {
43+
...entry,
44+
block: undefined
45+
}
3246
}
3347

3448
if (previous != null) {
35-
previous.single = true
3649
yield previous
3750
}
3851
}
@@ -43,49 +56,32 @@ interface ReduceOptions extends ProgressOptions {
4356
signal?: AbortSignal
4457
}
4558

59+
function isSingleBlockImport (result: any): result is SingleBlockImportResult {
60+
return result.single === true
61+
}
62+
4663
const reduce = (file: File, blockstore: Blockstore, options: ReduceOptions): Reducer => {
4764
const reducer: Reducer = async function (leaves) {
48-
if (leaves.length === 1 && leaves[0]?.single === true && options.reduceSingleLeafToSelf) {
65+
if (leaves.length === 1 && isSingleBlockImport(leaves[0]) && options.reduceSingleLeafToSelf) {
4966
const leaf = leaves[0]
5067

51-
if (file.mtime !== undefined || file.mode !== undefined) {
68+
if (isSingleBlockImport(leaf) && (file.mtime !== undefined || file.mode !== undefined)) {
5269
// only one leaf node which is a raw leaf - we have metadata so convert it into a
5370
// UnixFS entry otherwise we'll have nowhere to store the metadata
54-
let buffer = await blockstore.get(leaf.cid, options)
55-
5671
leaf.unixfs = new UnixFS({
5772
type: 'file',
5873
mtime: file.mtime,
5974
mode: file.mode,
60-
data: buffer
75+
data: leaf.block
6176
})
6277

63-
buffer = encode(prepare({ Data: leaf.unixfs.marshal() }))
64-
65-
// // TODO vmx 2021-03-26: This is what the original code does, it checks
66-
// // the multihash of the original leaf node and uses then the same
67-
// // hasher. i wonder if that's really needed or if we could just use
68-
// // the hasher from `options.hasher` instead.
69-
// const multihash = mh.decode(leaf.cid.multihash.bytes)
70-
// let hasher
71-
// switch multihash {
72-
// case sha256.code {
73-
// hasher = sha256
74-
// break;
75-
// }
76-
// //case identity.code {
77-
// // hasher = identity
78-
// // break;
79-
// //}
80-
// default: {
81-
// throw new Error(`Unsupported hasher "${multihash}"`)
82-
// }
83-
// }
84-
leaf.cid = await persist(buffer, blockstore, {
78+
leaf.block = encode(prepare({ Data: leaf.unixfs.marshal() }))
79+
80+
leaf.cid = await persist(leaf.block, blockstore, {
8581
...options,
8682
cidVersion: options.cidVersion
8783
})
88-
leaf.size = BigInt(buffer.length)
84+
leaf.size = BigInt(leaf.block.length)
8985
}
9086

9187
return {
@@ -147,15 +143,16 @@ const reduce = (file: File, blockstore: Blockstore, options: ReduceOptions): Red
147143
Data: f.marshal(),
148144
Links: links
149145
}
150-
const buffer = encode(prepare(node))
151-
const cid = await persist(buffer, blockstore, options)
146+
const block = encode(prepare(node))
147+
const cid = await persist(block, blockstore, options)
152148

153149
return {
154150
cid,
155151
path: file.path,
156152
unixfs: f,
157-
size: BigInt(buffer.length + node.Links.reduce((acc, curr) => acc + (curr.Tsize ?? 0), 0)),
158-
originalPath: file.originalPath
153+
size: BigInt(block.length + node.Links.reduce((acc, curr) => acc + (curr.Tsize ?? 0), 0)),
154+
originalPath: file.originalPath,
155+
block
159156
}
160157
}
161158

packages/ipfs-unixfs-importer/src/dir-flat.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { encode, PBNode, prepare } from '@ipld/dag-pb'
22
import type { Blockstore } from 'interface-blockstore'
33
import { UnixFS } from 'ipfs-unixfs'
4+
import type { CID } from 'multiformats/cid'
45
import { Dir, CID_V0, CID_V1, DirProps } from './dir.js'
56
import type { ImportResult, InProgressImportResult } from './index.js'
67
import { persist, PersistOptions } from './utils/persist.js'
@@ -68,20 +69,22 @@ export class DirFlat extends Dir {
6869
async * flush (block: Blockstore): AsyncGenerator<ImportResult> {
6970
const links = []
7071

71-
for (let [name, child] of this._children.entries()) {
72+
for (const [name, child] of this._children.entries()) {
73+
let result: { size?: bigint | number, cid?: CID } = child
74+
7275
if (child instanceof Dir) {
7376
for await (const entry of child.flush(block)) {
74-
child = entry
77+
result = entry
7578

76-
yield child
79+
yield entry
7780
}
7881
}
7982

80-
if (child.size != null && (child.cid != null)) {
83+
if (result.size != null && (result.cid != null)) {
8184
links.push({
8285
Name: name,
83-
Tsize: Number(child.size),
84-
Hash: child.cid
86+
Tsize: Number(result.size),
87+
Hash: result.cid
8588
})
8689
}
8790
}

packages/ipfs-unixfs-importer/src/index.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ import type { ProgressOptions } from 'progress-events'
1717
export type ByteStream = AwaitIterable<Uint8Array>
1818
export type ImportContent = ByteStream | Uint8Array
1919

20-
export interface BlockstoreOptions {
21-
signal?: AbortSignal
22-
}
23-
24-
export type Blockstore = Pick<InterfaceBlockstore, 'has' | 'put' | 'get'>
20+
export type Blockstore = Pick<InterfaceBlockstore, 'put'>
2521

2622
export interface FileCandidate {
2723
path?: string
@@ -60,14 +56,25 @@ export interface ImportResult {
6056
unixfs?: UnixFS
6157
}
6258

63-
export interface InProgressImportResult extends ImportResult {
64-
single?: boolean
59+
export interface MultipleBlockImportResult extends ImportResult {
60+
originalPath?: string
61+
}
62+
63+
export interface SingleBlockImportResult extends ImportResult {
64+
single: true
6565
originalPath?: string
66+
block: Uint8Array
67+
}
68+
69+
export type InProgressImportResult = SingleBlockImportResult | MultipleBlockImportResult
70+
71+
export interface BufferImporterResult extends ImportResult {
72+
block: Uint8Array
6673
}
6774

6875
export interface HamtHashFn { (value: Uint8Array): Promise<Uint8Array> }
6976
export interface TreeBuilder { (source: AsyncIterable<InProgressImportResult>, blockstore: Blockstore): AsyncIterable<ImportResult> }
70-
export interface BufferImporter { (file: File, blockstore: Blockstore): AsyncIterable<() => Promise<InProgressImportResult>> }
77+
export interface BufferImporter { (file: File, blockstore: Blockstore): AsyncIterable<() => Promise<BufferImporterResult>> }
7178

7279
export type ImportProgressEvents =
7380
BufferImportProgressEvents

packages/ipfs-unixfs-importer/test/builder-balanced.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ describe('builder: balanced', () => {
2424
it('reduces one value into itself', async () => {
2525
const source = [{
2626
cid: CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'),
27-
size: 0n
27+
size: 0n,
28+
block: Uint8Array.from([])
2829
}]
2930

3031
const result = await balanced(options)((async function * () {
@@ -37,13 +38,16 @@ describe('builder: balanced', () => {
3738
it('reduces 3 values into parent', async () => {
3839
const source = [{
3940
cid: CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'),
40-
size: 0n
41+
size: 0n,
42+
block: Uint8Array.from([])
4143
}, {
4244
cid: CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'),
43-
size: 0n
45+
size: 0n,
46+
block: Uint8Array.from([])
4447
}, {
4548
cid: CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'),
46-
size: 0n
49+
size: 0n,
50+
block: Uint8Array.from([])
4751
}]
4852

4953
const result = await balanced(options)((async function * () {

packages/ipfs-unixfs-importer/test/chunker-custom.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('custom chunker', function () {
1919
const block = new MemoryBlockstore()
2020

2121
const fromPartsTest = (content: AsyncIterable<Uint8Array>, size: bigint) => async () => {
22-
const put = async (buf: Uint8Array): Promise<{ cid: CID, size: bigint, unixfs: UnixFS }> => {
22+
const put = async (buf: Uint8Array): Promise<{ cid: CID, size: bigint, unixfs: UnixFS, block: Uint8Array }> => {
2323
const encodedBlock = await Block.encode({
2424
value: buf,
2525
codec: rawCodec,
@@ -29,7 +29,8 @@ describe('custom chunker', function () {
2929
return {
3030
cid: encodedBlock.cid,
3131
size: BigInt(buf.length),
32-
unixfs: new UnixFS()
32+
unixfs: new UnixFS(),
33+
block: buf
3334
}
3435
}
3536

0 commit comments

Comments
 (0)