99 "github.com/ipfs/go-unixfsnode/data"
1010 dagpb "github.com/ipld/go-codec-dagpb"
1111 "github.com/ipld/go-ipld-prime"
12+ "github.com/ipld/go-ipld-prime/datamodel"
1213 cidlink "github.com/ipld/go-ipld-prime/linking/cid"
1314 basicnode "github.com/ipld/go-ipld-prime/node/basic"
1415 "github.com/multiformats/go-multicodec"
@@ -18,6 +19,38 @@ import (
1819 _ "github.com/ipld/go-ipld-prime/codec/raw"
1920)
2021
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+
2154// BuildUnixFSFile creates a dag of ipld Nodes representing file data.
2255// This recreates the functionality previously found in
2356// github.com/ipfs/go-unixfs/importer/balanced, but tailored to the
@@ -28,31 +61,29 @@ import (
2861// data nodes are stored as raw bytes.
2962// ref: https://github.com/ipfs/go-mfs/blob/1b1fd06cff048caabeddb02d4dbf22d2274c7971/file.go#L50
3063func 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 )
3265 if err != nil {
3366 return nil , 0 , err
3467 }
3568
36- var prev []ipld.Link
37- var prevLen []uint64
69+ var prev fileShards
3870 depth := 1
3971 for {
40- root , size , err := fileTreeRecursive (depth , prev , prevLen , s , ls )
72+ next , err := fileTreeRecursive (depth , prev , src , ls )
4173 if err != nil {
4274 return nil , 0 , err
4375 }
4476
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 {
4779 node := basicnode .NewBytes ([]byte {})
4880 link , err := ls .Store (ipld.LinkContext {}, leafLinkProto , node )
4981 return link , 0 , err
5082 }
51- return root , size , nil
83+ return next . link , next . storedSize , nil
5284 }
5385
54- prev = []ipld.Link {root }
55- prevLen = []uint64 {size }
86+ prev = []fileShardMeta {next }
5687 depth ++
5788 }
5889}
@@ -75,102 +106,119 @@ var leafLinkProto = cidlink.LinkPrototype{
75106 },
76107}
77108
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, first chunk, encode as raw bytes and store
120+ if len (children ) > 0 {
121+ return fileShardMeta {}, fmt .Errorf ("leaf nodes cannot have children" )
122+ }
82123 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
87129 }
88130 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
90136 }
91- // depth > 1.
92- totalSize := uint64 ( 0 )
93- blksizes := make ([] uint64 , 0 , DefaultLinksPerBlock )
137+
138+ // depth > 1
139+
94140 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 )
101142 }
143+
102144 for len (children ) < DefaultLinksPerBlock {
103- nxt , sz , err := fileTreeRecursive (depth - 1 , nil , nil , src , ls )
145+ next , err := fileTreeRecursive (depth - 1 , nil , src , ls )
104146 if err != nil {
105- return nil , 0 , err
106- } else if nxt == nil {
107- // eof
147+ return fileShardMeta {}, err
148+ } else if next .link == nil { // eof
108149 break
109150 }
110- totalSize += sz
111- children = append (children , nxt )
112- childLen = append (childLen , sz )
113- blksizes = append (blksizes , sz )
151+ children = append (children , next )
114152 }
153+
115154 if len (children ) == 0 {
116- // empty case.
117- return nil , 0 , nil
155+ // empty case
156+ return fileShardMeta {} , nil
118157 } else if len (children ) == 1 {
119158 // degenerate case
120- return children [0 ], childLen [ 0 ], nil
159+ return children [0 ], nil
121160 }
122161
123- // make the unixfs node.
162+ // make the unixfs node
124163 node , err := BuildUnixFS (func (b * Builder ) {
125- FileSize (b , totalSize )
126- BlockSizes (b , blksizes )
164+ FileSize (b , children . totalByteSize () )
165+ BlockSizes (b , children . byteSizes () )
127166 })
128167 if err != nil {
129- return nil , 0 , err
168+ return fileShardMeta {}, err
169+ }
170+ pbn , err := packFileChildren (node , children )
171+ if err != nil {
172+ return fileShardMeta {}, err
130173 }
131174
132- // Pack into the dagpb node.
175+ link , sz , err := sizedStore (ls , fileLinkProto , pbn )
176+ if err != nil {
177+ return fileShardMeta {}, err
178+ }
179+ return fileShardMeta {
180+ link : link ,
181+ byteSize : children .totalByteSize (),
182+ storedSize : children .totalStoredSize () + sz ,
183+ }, nil
184+ }
185+
186+ func packFileChildren (node data.UnixFSData , children fileShards ) (datamodel.Node , error ) {
133187 dpbb := dagpb .Type .PBNode .NewBuilder ()
134188 pbm , err := dpbb .BeginMap (2 )
135189 if err != nil {
136- return nil , 0 , err
190+ return nil , err
137191 }
138192 pblb , err := pbm .AssembleEntry ("Links" )
139193 if err != nil {
140- return nil , 0 , err
194+ return nil , err
141195 }
142196 pbl , err := pblb .BeginList (int64 (len (children )))
143197 if err != nil {
144- return nil , 0 , err
198+ return nil , err
145199 }
146- for i , c := range children {
147- pbln , err := BuildUnixFSDirectoryEntry ("" , int64 (blksizes [ i ] ), c )
200+ for _ , c := range children {
201+ pbln , err := BuildUnixFSDirectoryEntry ("" , int64 (c . storedSize ), c . link )
148202 if err != nil {
149- return nil , 0 , err
203+ return nil , err
150204 }
151205 if err = pbl .AssembleValue ().AssignNode (pbln ); err != nil {
152- return nil , 0 , err
206+ return nil , err
153207 }
154208 }
155209 if err = pbl .Finish (); err != nil {
156- return nil , 0 , err
210+ return nil , err
157211 }
158212 if err = pbm .AssembleKey ().AssignString ("Data" ); err != nil {
159- return nil , 0 , err
213+ return nil , err
160214 }
161215 if err = pbm .AssembleValue ().AssignBytes (data .EncodeUnixFSData (node )); err != nil {
162- return nil , 0 , err
216+ return nil , err
163217 }
164218 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
219+ return nil , err
172220 }
173- return link , totalSize + sz , nil
221+ return dpbb . Build () , nil
174222}
175223
176224// BuildUnixFSDirectoryEntry creates the link to a file or directory as it appears within a unixfs directory.
0 commit comments