kraken

Форк
0
/
metainfo.go 
175 строк · 4.5 Кб
1
// Copyright (c) 2016-2019 Uber Technologies, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
package core
15

16
import (
17
	"bytes"
18
	"encoding/json"
19
	"errors"
20
	"fmt"
21
	"io"
22

23
	"github.com/jackpal/bencode-go"
24
)
25

26
// info contains the "instructions" for how to download / seed a torrent,
27
// primarily describing how a blob is broken up into pieces and how to verify
28
// those pieces (i.e. the piece sums).
29
type info struct {
30
	// Exported for bencoding.
31
	PieceLength int64
32
	PieceSums   []uint32
33
	Name        string
34
	Length      int64
35
}
36

37
// Hash computes the InfoHash of info.
38
func (info *info) Hash() (InfoHash, error) {
39
	var b bytes.Buffer
40
	if err := bencode.Marshal(&b, *info); err != nil {
41
		return InfoHash{}, fmt.Errorf("bencode: %s", err)
42
	}
43
	return NewInfoHashFromBytes(b.Bytes()), nil
44
}
45

46
// MetaInfo contains torrent metadata.
47
type MetaInfo struct {
48
	info     info
49
	infoHash InfoHash
50
	digest   Digest
51
}
52

53
// NewMetaInfo creates a new MetaInfo. Assumes that d is the valid digest for
54
// blob (re-computing it is expensive).
55
func NewMetaInfo(d Digest, blob io.Reader, pieceLength int64) (*MetaInfo, error) {
56
	length, pieceSums, err := calcPieceSums(blob, pieceLength)
57
	if err != nil {
58
		return nil, err
59
	}
60
	info := info{
61
		PieceLength: pieceLength,
62
		PieceSums:   pieceSums,
63
		Name:        d.Hex(),
64
		Length:      length,
65
	}
66
	h, err := info.Hash()
67
	if err != nil {
68
		return nil, fmt.Errorf("compute info hash: %s", err)
69
	}
70
	return &MetaInfo{
71
		info:     info,
72
		infoHash: h,
73
		digest:   d,
74
	}, nil
75
}
76

77
// InfoHash returns the torrent InfoHash.
78
func (mi *MetaInfo) InfoHash() InfoHash {
79
	return mi.infoHash
80
}
81

82
// Digest returns the digest of the original blob.
83
func (mi *MetaInfo) Digest() Digest {
84
	return mi.digest
85
}
86

87
// Length returns the length of the original blob.
88
func (mi *MetaInfo) Length() int64 {
89
	return mi.info.Length
90
}
91

92
// NumPieces returns the number of pieces in the torrent.
93
func (mi *MetaInfo) NumPieces() int {
94
	return len(mi.info.PieceSums)
95
}
96

97
// PieceLength returns the piece length used to break up the original blob. Note,
98
// the final piece may be shorter than this. Use GetPieceLength for the true
99
// lengths of each piece.
100
func (mi *MetaInfo) PieceLength() int64 {
101
	return mi.info.PieceLength
102
}
103

104
// GetPieceLength returns the length of piece i.
105
func (mi *MetaInfo) GetPieceLength(i int) int64 {
106
	if i < 0 || i >= len(mi.info.PieceSums) {
107
		return 0
108
	}
109
	if i == len(mi.info.PieceSums)-1 {
110
		// Last piece.
111
		return mi.info.Length - mi.info.PieceLength*int64(i)
112
	}
113
	return mi.info.PieceLength
114
}
115

116
// GetPieceSum returns the checksum of piece i. Does not check bounds.
117
func (mi *MetaInfo) GetPieceSum(i int) uint32 {
118
	return mi.info.PieceSums[i]
119
}
120

121
// metaInfoJSON is used for serializing / deserializing MetaInfo.
122
type metaInfoJSON struct {
123
	// Only serialize info for backwards compatibility.
124
	Info info `json:"Info"`
125
}
126

127
// Serialize converts mi to a json blob.
128
func (mi *MetaInfo) Serialize() ([]byte, error) {
129
	return json.Marshal(&metaInfoJSON{mi.info})
130
}
131

132
// DeserializeMetaInfo reconstructs a MetaInfo from a json blob.
133
func DeserializeMetaInfo(data []byte) (*MetaInfo, error) {
134
	var j metaInfoJSON
135
	if err := json.Unmarshal(data, &j); err != nil {
136
		return nil, fmt.Errorf("json: %s", err)
137
	}
138
	h, err := j.Info.Hash()
139
	if err != nil {
140
		return nil, fmt.Errorf("compute info hash: %s", err)
141
	}
142
	d, err := NewSHA256DigestFromHex(j.Info.Name)
143
	if err != nil {
144
		return nil, fmt.Errorf("parse name: %s", err)
145
	}
146
	return &MetaInfo{
147
		info:     j.Info,
148
		infoHash: h,
149
		digest:   d,
150
	}, nil
151
}
152

153
// calcPieceSums hashes blob content in pieceLength chunks.
154
func calcPieceSums(blob io.Reader, pieceLength int64) (length int64, pieceSums []uint32, err error) {
155
	if pieceLength <= 0 {
156
		return 0, nil, errors.New("piece length must be positive")
157
	}
158
	for {
159
		h := PieceHash()
160
		n, err := io.CopyN(h, blob, pieceLength)
161
		if err != nil && err != io.EOF {
162
			return 0, nil, fmt.Errorf("read blob: %s", err)
163
		}
164
		length += n
165
		if n == 0 {
166
			break
167
		}
168
		sum := h.Sum32()
169
		pieceSums = append(pieceSums, sum)
170
		if n < pieceLength {
171
			break
172
		}
173
	}
174
	return length, pieceSums, nil
175
}
176

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.