1
1
import { decode , type PBLink , type PBNode } from '@ipld/dag-pb'
2
2
import { murmur3128 } from '@multiformats/murmur3'
3
3
import { Bucket , type BucketPosition , createHAMT } from 'hamt-sharding'
4
+ import errCode from 'err-code'
5
+ import { UnixFS } from 'ipfs-unixfs'
4
6
import type { ExporterOptions , ShardTraversalContext , ReadableStorage } from '../index.js'
5
7
import type { CID } from 'multiformats/cid'
6
8
@@ -16,13 +18,14 @@ const hashFn = async function (buf: Uint8Array): Promise<Uint8Array> {
16
18
}
17
19
18
20
const addLinksToHamtBucket = async ( links : PBLink [ ] , bucket : Bucket < boolean > , rootBucket : Bucket < boolean > ) : Promise < void > => {
21
+ const padLength = ( bucket . tableSize ( ) - 1 ) . toString ( 16 ) . length
19
22
await Promise . all (
20
23
links . map ( async link => {
21
24
if ( link . Name == null ) {
22
25
// TODO(@rvagg): what do? this is technically possible
23
26
throw new Error ( 'Unexpected Link without a Name' )
24
27
}
25
- if ( link . Name . length === 2 ) {
28
+ if ( link . Name . length === padLength ) {
26
29
const pos = parseInt ( link . Name , 16 )
27
30
28
31
bucket . _putObjectAt ( pos , new Bucket ( {
@@ -37,12 +40,12 @@ const addLinksToHamtBucket = async (links: PBLink[], bucket: Bucket<boolean>, ro
37
40
)
38
41
}
39
42
40
- const toPrefix = ( position : number ) : string => {
43
+ const toPrefix = ( position : number , padLength : number ) : string => {
41
44
return position
42
45
. toString ( 16 )
43
46
. toUpperCase ( )
44
- . padStart ( 2 , '0' )
45
- . substring ( 0 , 2 )
47
+ . padStart ( padLength , '0' )
48
+ . substring ( 0 , padLength )
46
49
}
47
50
48
51
const toBucketPath = ( position : BucketPosition < boolean > ) : Array < Bucket < boolean > > => {
@@ -62,8 +65,27 @@ const toBucketPath = (position: BucketPosition<boolean>): Array<Bucket<boolean>>
62
65
63
66
const findShardCid = async ( node : PBNode , name : string , blockstore : ReadableStorage , context ?: ShardTraversalContext , options ?: ExporterOptions ) : Promise < CID | undefined > => {
64
67
if ( context == null ) {
68
+ if ( node . Data == null ) {
69
+ throw errCode ( new Error ( 'no data in PBNode' ) , 'ERR_NOT_UNIXFS' )
70
+ }
71
+
72
+ let dir : UnixFS
73
+ try {
74
+ dir = UnixFS . unmarshal ( node . Data )
75
+ } catch ( err : any ) {
76
+ throw errCode ( err , 'ERR_NOT_UNIXFS' )
77
+ }
78
+
79
+ if ( dir . type !== 'hamt-sharded-directory' ) {
80
+ throw errCode ( new Error ( 'not a HAMT' ) , 'ERR_NOT_UNIXFS' )
81
+ }
82
+ if ( dir . fanout == null ) {
83
+ throw errCode ( new Error ( 'missing fanout' ) , 'ERR_NOT_UNIXFS' )
84
+ }
85
+
65
86
const rootBucket = createHAMT < boolean > ( {
66
- hashFn
87
+ hashFn,
88
+ bits : Math . log2 ( Number ( dir . fanout ) )
67
89
} )
68
90
69
91
context = {
@@ -73,25 +95,27 @@ const findShardCid = async (node: PBNode, name: string, blockstore: ReadableStor
73
95
}
74
96
}
75
97
98
+ const padLength = ( context . lastBucket . tableSize ( ) - 1 ) . toString ( 16 ) . length
99
+
76
100
await addLinksToHamtBucket ( node . Links , context . lastBucket , context . rootBucket )
77
101
78
102
const position = await context . rootBucket . _findNewBucketAndPos ( name )
79
- let prefix = toPrefix ( position . pos )
103
+ let prefix = toPrefix ( position . pos , padLength )
80
104
const bucketPath = toBucketPath ( position )
81
105
82
106
if ( bucketPath . length > context . hamtDepth ) {
83
107
context . lastBucket = bucketPath [ context . hamtDepth ]
84
108
85
- prefix = toPrefix ( context . lastBucket . _posAtParent )
109
+ prefix = toPrefix ( context . lastBucket . _posAtParent , padLength )
86
110
}
87
111
88
112
const link = node . Links . find ( link => {
89
113
if ( link . Name == null ) {
90
114
return false
91
115
}
92
116
93
- const entryPrefix = link . Name . substring ( 0 , 2 )
94
- const entryName = link . Name . substring ( 2 )
117
+ const entryPrefix = link . Name . substring ( 0 , padLength )
118
+ const entryName = link . Name . substring ( padLength )
95
119
96
120
if ( entryPrefix !== prefix ) {
97
121
// not the entry or subshard we're looking for
@@ -110,7 +134,7 @@ const findShardCid = async (node: PBNode, name: string, blockstore: ReadableStor
110
134
return
111
135
}
112
136
113
- if ( link . Name != null && link . Name . substring ( 2 ) === name ) {
137
+ if ( link . Name != null && link . Name . substring ( padLength ) === name ) {
114
138
return link . Hash
115
139
}
116
140
0 commit comments