Skip to content

Commit 9cf292e

Browse files
committed
chore: refactor and add tests with fixtures
1 parent 0721fae commit 9cf292e

File tree

3 files changed

+205
-90
lines changed

3 files changed

+205
-90
lines changed

data/builder/dir_test.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package builder
33
import (
44
"bytes"
55
"fmt"
6+
"io"
67
"os"
78
"path/filepath"
9+
"strconv"
810
"testing"
911

1012
"github.com/ipfs/go-cid"
13+
u "github.com/ipfs/go-ipfs-util"
1114
"github.com/ipfs/go-unixfsnode"
1215
dagpb "github.com/ipld/go-codec-dagpb"
1316
"github.com/ipld/go-ipld-prime"
@@ -19,11 +22,7 @@ func mkEntries(cnt int, ls *ipld.LinkSystem) ([]dagpb.PBLink, error) {
1922
entries := make([]dagpb.PBLink, 0, cnt)
2023
for i := 0; i < cnt; i++ {
2124
r := bytes.NewBufferString(fmt.Sprintf("%d", i))
22-
f, s, err := BuildUnixFSFile(r, "", ls)
23-
if err != nil {
24-
return nil, err
25-
}
26-
e, err := BuildUnixFSDirectoryEntry(fmt.Sprintf("file %d", i), int64(s), f)
25+
e, err := mkEntry(r, fmt.Sprintf("file %d", i), ls)
2726
if err != nil {
2827
return nil, err
2928
}
@@ -32,6 +31,42 @@ func mkEntries(cnt int, ls *ipld.LinkSystem) ([]dagpb.PBLink, error) {
3231
return entries, nil
3332
}
3433

34+
func mkEntry(r io.Reader, name string, ls *ipld.LinkSystem) (dagpb.PBLink, error) {
35+
f, s, err := BuildUnixFSFile(r, "", ls)
36+
if err != nil {
37+
return nil, err
38+
}
39+
return BuildUnixFSDirectoryEntry(name, int64(s), f)
40+
}
41+
42+
func TestBuildUnixFSFileWrappedInDirectory_Reference(t *testing.T) {
43+
for _, tc := range referenceTestCases {
44+
t.Run(strconv.Itoa(tc.size), func(t *testing.T) {
45+
buf := make([]byte, tc.size)
46+
u.NewSeededRand(0xdeadbeef).Read(buf)
47+
r := bytes.NewReader(buf)
48+
49+
ls := cidlink.DefaultLinkSystem()
50+
storage := cidlink.Memory{}
51+
ls.StorageReadOpener = storage.OpenRead
52+
ls.StorageWriteOpener = storage.OpenWrite
53+
54+
e, err := mkEntry(r, fmt.Sprintf("%d", tc.size), &ls)
55+
require.NoError(t, err)
56+
d, sz, err := BuildUnixFSDirectory([]dagpb.PBLink{e}, &ls)
57+
require.NoError(t, err)
58+
require.Equal(t, tc.wrappedExpected.String(), d.(cidlink.Link).Cid.String())
59+
60+
// check sz is the stored size of all blocks in the generated DAG
61+
var totStored int
62+
for _, blk := range storage.Bag {
63+
totStored += len(blk)
64+
}
65+
require.Equal(t, totStored, int(sz))
66+
})
67+
}
68+
}
69+
3570
func TestBuildUnixFSDirectory(t *testing.T) {
3671
ls := cidlink.DefaultLinkSystem()
3772
storage := cidlink.Memory{}

data/builder/file.go

Lines changed: 112 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
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
3063
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)
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,122 @@ 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, 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+
}
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+
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
102146
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)
104149
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
108152
break
109153
}
110-
totalSize += sz
111-
children = append(children, nxt)
112-
childLen = append(childLen, sz)
113-
blksizes = append(blksizes, sz)
154+
children = append(children, next)
114155
}
156+
115157
if len(children) == 0 {
116-
// empty case.
117-
return nil, 0, nil
158+
// empty case
159+
return fileShardMeta{}, nil
118160
} else if len(children) == 1 {
119161
// degenerate case
120-
return children[0], childLen[0], nil
162+
return children[0], nil
121163
}
122164

123-
// make the unixfs node.
165+
// make the unixfs node
124166
node, err := BuildUnixFS(func(b *Builder) {
125-
FileSize(b, totalSize)
126-
BlockSizes(b, blksizes)
167+
FileSize(b, children.totalByteSize())
168+
BlockSizes(b, children.byteSizes())
127169
})
128170
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
130176
}
131177

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) {
133190
dpbb := dagpb.Type.PBNode.NewBuilder()
134191
pbm, err := dpbb.BeginMap(2)
135192
if err != nil {
136-
return nil, 0, err
193+
return nil, err
137194
}
138195
pblb, err := pbm.AssembleEntry("Links")
139196
if err != nil {
140-
return nil, 0, err
197+
return nil, err
141198
}
142199
pbl, err := pblb.BeginList(int64(len(children)))
143200
if err != nil {
144-
return nil, 0, err
201+
return nil, err
145202
}
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)
148205
if err != nil {
149-
return nil, 0, err
206+
return nil, err
150207
}
151208
if err = pbl.AssembleValue().AssignNode(pbln); err != nil {
152-
return nil, 0, err
209+
return nil, err
153210
}
154211
}
155212
if err = pbl.Finish(); err != nil {
156-
return nil, 0, err
213+
return nil, err
157214
}
158215
if err = pbm.AssembleKey().AssignString("Data"); err != nil {
159-
return nil, 0, err
216+
return nil, err
160217
}
161218
if err = pbm.AssembleValue().AssignBytes(data.EncodeUnixFSData(node)); err != nil {
162-
return nil, 0, err
219+
return nil, err
163220
}
164221
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
172223
}
173-
return link, totalSize + sz, nil
224+
return dpbb.Build(), nil
174225
}
175226

176227
// BuildUnixFSDirectoryEntry creates the link to a file or directory as it appears within a unixfs directory.

0 commit comments

Comments
 (0)