6 g "github.com/anacrolix/generics"
7 "golang.org/x/exp/maps"
9 "github.com/anacrolix/torrent/bencode"
12 const FileTreePropertiesKey = ""
14 type FileTreeFile struct {
15 Length int64 `bencode:"length"`
16 PiecesRoot string `bencode:"pieces root"`
19 // The fields here don't need bencode tags as the marshalling is done manually.
20 type FileTree struct {
22 Dir map[string]FileTree
25 func (ft *FileTree) UnmarshalBencode(bytes []byte) (err error) {
26 var dir map[string]bencode.Bytes
27 err = bencode.Unmarshal(bytes, &dir)
31 if propBytes, ok := dir[""]; ok {
32 err = bencode.Unmarshal(propBytes, &ft.File)
38 g.MakeMapWithCap(&ft.Dir, len(dir))
39 for key, bytes := range dir {
41 err = sub.UnmarshalBencode(bytes)
50 var _ bencode.Unmarshaler = (*FileTree)(nil)
52 func (ft *FileTree) NumEntries() (num int) {
54 if g.MapContains(ft.Dir, FileTreePropertiesKey) {
60 func (ft *FileTree) IsDir() bool {
61 return ft.NumEntries() != 0
64 func (ft *FileTree) orderedKeys() []string {
65 keys := maps.Keys(ft.Dir)
70 func (ft *FileTree) upvertedFiles(pieceLength int64, out func(fi FileInfo)) {
72 ft.upvertedFilesInner(pieceLength, nil, &offset, out)
75 func (ft *FileTree) upvertedFilesInner(
79 out func(fi FileInfo),
82 for _, key := range ft.orderedKeys() {
83 if key == FileTreePropertiesKey {
86 sub := g.MapMustGet(ft.Dir, key)
87 sub.upvertedFilesInner(pieceLength, append(path, key), offset, out)
91 Length: ft.File.Length,
92 Path: append([]string(nil), path...),
93 // BEP 52 requires paths be UTF-8 if possible.
94 PathUtf8: append([]string(nil), path...),
95 PiecesRoot: ft.PiecesRootAsByteArray(),
96 TorrentOffset: *offset,
98 *offset += (ft.File.Length + pieceLength - 1) / pieceLength * pieceLength
102 func (ft *FileTree) Walk(path []string, f func(path []string, ft *FileTree)) {
104 for key, sub := range ft.Dir {
105 if key == FileTreePropertiesKey {
108 sub.Walk(append(path, key), f)
112 func (ft *FileTree) PiecesRootAsByteArray() (ret g.Option[[32]byte]) {
113 if ft.File.PiecesRoot == "" {
116 n := copy(ret.Value[:], ft.File.PiecesRoot)
118 // Must be 32 bytes for meta version 2 and non-empty files. See BEP 52.