9
9
"github.com/ipfs/go-unixfsnode/data"
10
10
dagpb "github.com/ipld/go-codec-dagpb"
11
11
"github.com/ipld/go-ipld-prime"
12
+ "github.com/ipld/go-ipld-prime/datamodel"
12
13
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
13
14
basicnode "github.com/ipld/go-ipld-prime/node/basic"
14
15
"github.com/multiformats/go-multicodec"
@@ -18,6 +19,38 @@ import (
18
19
_ "github.com/ipld/go-ipld-prime/codec/raw"
19
20
)
20
21
22
+ type fileShardMeta struct {
23
+ link datamodel.Link
24
+ byteSize uint64
25
+ storedSize uint64
26
+ }
27
+
28
+ type fileShards []fileShardMeta
29
+
30
+ func (fs fileShards ) totalByteSize () uint64 {
31
+ var total uint64
32
+ for _ , f := range fs {
33
+ total += f .byteSize
34
+ }
35
+ return total
36
+ }
37
+
38
+ func (fs fileShards ) totalStoredSize () uint64 {
39
+ var total uint64
40
+ for _ , f := range fs {
41
+ total += f .storedSize
42
+ }
43
+ return total
44
+ }
45
+
46
+ func (fs fileShards ) byteSizes () []uint64 {
47
+ sizes := make ([]uint64 , len (fs ))
48
+ for i , f := range fs {
49
+ sizes [i ] = f .byteSize
50
+ }
51
+ return sizes
52
+ }
53
+
21
54
// BuildUnixFSFile creates a dag of ipld Nodes representing file data.
22
55
// This recreates the functionality previously found in
23
56
// github.com/ipfs/go-unixfs/importer/balanced, but tailored to the
@@ -28,31 +61,29 @@ import (
28
61
// data nodes are stored as raw bytes.
29
62
// ref: https://github.com/ipfs/go-mfs/blob/1b1fd06cff048caabeddb02d4dbf22d2274c7971/file.go#L50
30
63
func BuildUnixFSFile (r io.Reader , chunker string , ls * ipld.LinkSystem ) (ipld.Link , uint64 , error ) {
31
- s , err := chunk .FromString (r , chunker )
64
+ src , err := chunk .FromString (r , chunker )
32
65
if err != nil {
33
66
return nil , 0 , err
34
67
}
35
68
36
- var prev []ipld.Link
37
- var prevLen []uint64
69
+ var prev fileShards
38
70
depth := 1
39
71
for {
40
- root , size , err := fileTreeRecursive (depth , prev , prevLen , s , ls )
72
+ next , err := fileTreeRecursive (depth , prev , src , ls )
41
73
if err != nil {
42
74
return nil , 0 , err
43
75
}
44
76
45
- if prev != nil && prev [0 ] == root {
46
- if root == nil {
77
+ if prev != nil && prev [0 ]. link == next . link {
78
+ if next . link == nil {
47
79
node := basicnode .NewBytes ([]byte {})
48
80
link , err := ls .Store (ipld.LinkContext {}, leafLinkProto , node )
49
81
return link , 0 , err
50
82
}
51
- return root , size , nil
83
+ return next . link , next . storedSize , nil
52
84
}
53
85
54
- prev = []ipld.Link {root }
55
- prevLen = []uint64 {size }
86
+ prev = []fileShardMeta {next }
56
87
depth ++
57
88
}
58
89
}
@@ -75,102 +106,122 @@ var leafLinkProto = cidlink.LinkPrototype{
75
106
},
76
107
}
77
108
78
- func fileTreeRecursive (depth int , children []ipld.Link , childLen []uint64 , src chunk.Splitter , ls * ipld.LinkSystem ) (ipld.Link , uint64 , error ) {
79
- if depth == 1 && len (children ) > 0 {
80
- return nil , 0 , fmt .Errorf ("leaf nodes cannot have children" )
81
- } else if depth == 1 {
109
+ // fileTreeRecursive packs a file into chunks recursively, returning a root for
110
+ // this level of recursion, the number of file bytes consumed for this level of
111
+ // recursion and and the number of bytes used to store this level of recursion.
112
+ func fileTreeRecursive (
113
+ depth int ,
114
+ children fileShards ,
115
+ src chunk.Splitter ,
116
+ ls * ipld.LinkSystem ,
117
+ ) (fileShardMeta , error ) {
118
+ if depth == 1 {
119
+ // file leaf, next chunk, encode as raw bytes, store and retuen
120
+ if len (children ) > 0 {
121
+ return fileShardMeta {}, fmt .Errorf ("leaf nodes cannot have children" )
122
+ }
82
123
leaf , err := src .NextBytes ()
83
- if err == io .EOF {
84
- return nil , 0 , nil
85
- } else if err != nil {
86
- return nil , 0 , err
124
+ if err != nil {
125
+ if err == io .EOF {
126
+ return fileShardMeta {}, nil
127
+ }
128
+ return fileShardMeta {}, err
87
129
}
88
130
node := basicnode .NewBytes (leaf )
89
- return sizedStore (ls , leafLinkProto , node )
131
+ l , sz , err := sizedStore (ls , leafLinkProto , node )
132
+ if err != nil {
133
+ return fileShardMeta {}, err
134
+ }
135
+ return fileShardMeta {link : l , byteSize : uint64 (len (leaf )), storedSize : sz }, nil
90
136
}
91
- // depth > 1.
92
- totalSize := uint64 ( 0 )
93
- blksizes := make ([] uint64 , 0 , DefaultLinksPerBlock )
137
+
138
+ // depth > 1
139
+
94
140
if children == nil {
95
- children = make ([]ipld.Link , 0 )
96
- } else {
97
- for i := range children {
98
- blksizes = append (blksizes , childLen [i ])
99
- totalSize += childLen [i ]
100
- }
141
+ children = make (fileShards , 0 )
101
142
}
143
+
144
+ // fill up the links for this level, if we need to go beyond
145
+ // DefaultLinksPerBlock we'll end up back here making a parallel tree
102
146
for len (children ) < DefaultLinksPerBlock {
103
- nxt , sz , err := fileTreeRecursive (depth - 1 , nil , nil , src , ls )
147
+ // descend down toward the leaves
148
+ next , err := fileTreeRecursive (depth - 1 , nil , src , ls )
104
149
if err != nil {
105
- return nil , 0 , err
106
- } else if nxt == nil {
107
- // eof
150
+ return fileShardMeta {}, err
151
+ } else if next .link == nil { // eof
108
152
break
109
153
}
110
- totalSize += sz
111
- children = append (children , nxt )
112
- childLen = append (childLen , sz )
113
- blksizes = append (blksizes , sz )
154
+ children = append (children , next )
114
155
}
156
+
115
157
if len (children ) == 0 {
116
- // empty case.
117
- return nil , 0 , nil
158
+ // empty case
159
+ return fileShardMeta {} , nil
118
160
} else if len (children ) == 1 {
119
161
// degenerate case
120
- return children [0 ], childLen [ 0 ], nil
162
+ return children [0 ], nil
121
163
}
122
164
123
- // make the unixfs node.
165
+ // make the unixfs node
124
166
node , err := BuildUnixFS (func (b * Builder ) {
125
- FileSize (b , totalSize )
126
- BlockSizes (b , blksizes )
167
+ FileSize (b , children . totalByteSize () )
168
+ BlockSizes (b , children . byteSizes () )
127
169
})
128
170
if err != nil {
129
- return nil , 0 , err
171
+ return fileShardMeta {}, err
172
+ }
173
+ pbn , err := packFileChildren (node , children )
174
+ if err != nil {
175
+ return fileShardMeta {}, err
130
176
}
131
177
132
- // Pack into the dagpb node.
178
+ link , sz , err := sizedStore (ls , fileLinkProto , pbn )
179
+ if err != nil {
180
+ return fileShardMeta {}, err
181
+ }
182
+ return fileShardMeta {
183
+ link : link ,
184
+ byteSize : children .totalByteSize (),
185
+ storedSize : children .totalStoredSize () + sz ,
186
+ }, nil
187
+ }
188
+
189
+ func packFileChildren (node data.UnixFSData , children fileShards ) (datamodel.Node , error ) {
133
190
dpbb := dagpb .Type .PBNode .NewBuilder ()
134
191
pbm , err := dpbb .BeginMap (2 )
135
192
if err != nil {
136
- return nil , 0 , err
193
+ return nil , err
137
194
}
138
195
pblb , err := pbm .AssembleEntry ("Links" )
139
196
if err != nil {
140
- return nil , 0 , err
197
+ return nil , err
141
198
}
142
199
pbl , err := pblb .BeginList (int64 (len (children )))
143
200
if err != nil {
144
- return nil , 0 , err
201
+ return nil , err
145
202
}
146
- for i , c := range children {
147
- pbln , err := BuildUnixFSDirectoryEntry ("" , int64 (blksizes [ i ] ), c )
203
+ for _ , c := range children {
204
+ pbln , err := BuildUnixFSDirectoryEntry ("" , int64 (c . storedSize ), c . link )
148
205
if err != nil {
149
- return nil , 0 , err
206
+ return nil , err
150
207
}
151
208
if err = pbl .AssembleValue ().AssignNode (pbln ); err != nil {
152
- return nil , 0 , err
209
+ return nil , err
153
210
}
154
211
}
155
212
if err = pbl .Finish (); err != nil {
156
- return nil , 0 , err
213
+ return nil , err
157
214
}
158
215
if err = pbm .AssembleKey ().AssignString ("Data" ); err != nil {
159
- return nil , 0 , err
216
+ return nil , err
160
217
}
161
218
if err = pbm .AssembleValue ().AssignBytes (data .EncodeUnixFSData (node )); err != nil {
162
- return nil , 0 , err
219
+ return nil , err
163
220
}
164
221
if err = pbm .Finish (); err != nil {
165
- return nil , 0 , err
166
- }
167
- pbn := dpbb .Build ()
168
-
169
- link , sz , err := sizedStore (ls , fileLinkProto , pbn )
170
- if err != nil {
171
- return nil , 0 , err
222
+ return nil , err
172
223
}
173
- return link , totalSize + sz , nil
224
+ return dpbb . Build () , nil
174
225
}
175
226
176
227
// BuildUnixFSDirectoryEntry creates the link to a file or directory as it appears within a unixfs directory.
0 commit comments