update and fix the metainfo

This commit is contained in:
xgfone
2020-12-26 23:19:42 +08:00
parent 1d811ccdc2
commit 9fdf2b2a6b
9 changed files with 225 additions and 27 deletions

View File

@ -6,6 +6,7 @@ go:
- 1.12.x
- 1.13.x
- 1.14.x
- 1.15.x
env:
- GO111MODULE=on
script:

View File

@ -72,7 +72,11 @@ func NewAddressesFromString(s string) (addrs []Address, err error) {
addrs = make([]Address, len(ips))
for i, ip := range ips {
addrs[i] = Address{IP: ip, Port: port}
if ipv4 := ip.To4(); len(ipv4) != 0 {
addrs[i] = Address{IP: ipv4, Port: port}
} else {
addrs[i] = Address{IP: ip, Port: port}
}
}
return
@ -112,7 +116,7 @@ func (a *Address) FromString(addr string) (err error) {
func (a *Address) FromUDPAddr(ua *net.UDPAddr) {
a.Port = uint16(ua.Port)
a.IP = ua.IP
if ipv4 := a.IP.To4(); len(ipv4) > 0 {
if ipv4 := a.IP.To4(); len(ipv4) != 0 {
a.IP = ipv4
}
}
@ -181,15 +185,19 @@ func (a Address) MarshalBinary() (data []byte, err error) {
func (a *Address) decode(vs []interface{}) (err error) {
defer func() {
if e := recover(); e != nil {
err = e.(error)
switch e := recover().(type) {
case nil:
case error:
err = e
default:
err = fmt.Errorf("%v", e)
}
}()
host := vs[0].(string)
if a.IP = net.ParseIP(host); len(a.IP) == 0 {
return ErrInvalidAddr
} else if ip := a.IP.To4(); len(ip) > 0 {
} else if ip := a.IP.To4(); len(ip) != 0 {
a.IP = ip
}
@ -277,7 +285,7 @@ func (a HostAddress) String() string {
// Addresses parses the host address to a list of Addresses.
func (a HostAddress) Addresses() (addrs []Address, err error) {
if ip := net.ParseIP(a.Host); len(ip) > 0 {
if ip := net.ParseIP(a.Host); len(ip) != 0 {
return []Address{NewAddress(ip, a.Port)}, nil
}
@ -301,8 +309,12 @@ func (a HostAddress) Equal(o HostAddress) bool {
func (a *HostAddress) decode(vs []interface{}) (err error) {
defer func() {
if e := recover(); e != nil {
err = e.(error)
switch e := recover().(type) {
case nil:
case error:
err = e
default:
err = fmt.Errorf("%v", e)
}
}()

View File

@ -101,7 +101,7 @@ func (f File) FilePieces(info Info) (fps FilePieces) {
}}
}
fps = make(FilePieces, 0, endPieceIndex-startPieceIndex)
fps = make(FilePieces, 0, endPieceIndex-startPieceIndex+1)
fps = append(fps, FilePiece{
Index: uint32(startPieceIndex),
Offset: uint32(startPieceOffset),

View File

@ -127,8 +127,14 @@ func (info Info) PieceOffset(index, offset uint32) int64 {
}
// GetFileByOffset returns the file and its offset by the total offset.
//
// If fileOffset is eqaul to file.Length, it means to reach the end.
func (info Info) GetFileByOffset(offset int64) (file File, fileOffset int64) {
if !info.IsDir() {
if offset > info.Length {
panic(fmt.Errorf("offset '%d' exceeds the maximum length '%d'",
offset, info.Length))
}
return File{Length: info.Length}, offset
}
@ -140,6 +146,11 @@ func (info Info) GetFileByOffset(offset int64) (file File, fileOffset int64) {
fileOffset -= file.Length
}
if fileOffset > file.Length {
panic(fmt.Errorf("offset '%d' exceeds the maximum length '%d'",
offset, info.TotalLength()))
}
return
}

View File

@ -20,7 +20,9 @@ import (
"crypto/sha1"
"encoding/base32"
"encoding/hex"
"errors"
"fmt"
"io"
"github.com/xgfone/bt/bencode"
)
@ -96,6 +98,26 @@ func (h Hash) IsZero() bool {
return h == zeroHash
}
// WriteBinary is the same as MarshalBinary, but writes the result into w
// instead of returning.
func (h Hash) WriteBinary(w io.Writer) (m int, err error) {
return w.Write(h[:])
}
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
func (h *Hash) UnmarshalBinary(b []byte) (err error) {
if len(b) < HashSize {
return errors.New("Hash.UnmarshalBinary: too few bytes")
}
copy((*h)[:], b[:HashSize])
return
}
// MarshalBinary implements the interface binary.BinaryMarshaler.
func (h Hash) MarshalBinary() (data []byte, err error) {
return h[:], nil
}
// MarshalBencode implements the interface bencode.Marshaler.
func (h Hash) MarshalBencode() (b []byte, err error) {
return bencode.EncodeBytes(h[:])

80
metainfo/infohash_test.go Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2020 xgfone
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metainfo
import "testing"
func TestHash(t *testing.T) {
hexHash := "0001020304050607080909080706050403020100"
b, err := NewHashFromHexString(hexHash).MarshalBencode()
if err != nil {
t.Fatal(err)
}
var h Hash
if err = h.UnmarshalBencode(b); err != nil {
t.Fatal(err)
}
if hexs := h.String(); hexs != hexHash {
t.Errorf("expect '%s', but got '%s'\n", hexHash, hexs)
}
h = Hash{}
hexHash = "0001020304050607080900010203040506070809"
err = h.UnmarshalBinary([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
if err != nil {
t.Error(err)
} else if hexs := h.HexString(); hexs != hexHash {
t.Errorf("expect '%s', but got '%s'\n", hexHash, hexs)
}
}
func TestHashes(t *testing.T) {
hexHash1 := "0101010101010101010101010101010101010101"
hexHash2 := "0202020202020202020202020202020202020202"
hashes := Hashes{
NewHashFromHexString(hexHash1),
NewHashFromHexString(hexHash2),
}
b, err := hashes.MarshalBencode()
if err != nil {
t.Fatal(err)
}
hashes = Hashes{}
if err = hashes.UnmarshalBencode(b); err != nil {
t.Fatal(err)
}
if _len := len(hashes); _len != 2 {
t.Fatalf("expect the len(hashes)==2, but got '%d'", _len)
}
for i, h := range hashes {
if i == 0 {
if hexs := h.HexString(); hexs != hexHash1 {
t.Errorf("index %d: expect '%s', but got '%s'\n", i, hexHash1, hexs)
}
} else {
if hexs := h.HexString(); hexs != hexHash2 {
t.Errorf("index %d: expect '%s', but got '%s'\n", i, hexHash2, hexs)
}
}
}
}

View File

@ -69,16 +69,14 @@ func (r *reader) ReadAt(p []byte, offset int64) (n int, err error) {
for _len := len(p); n < _len; {
file, fileOffset := r.info.GetFileByOffset(offset)
if file.Length == 0 {
if file.Length == 0 || file.Length == fileOffset {
err = io.EOF
break
}
length := int(file.Length-fileOffset) + n
if _len < length {
length = _len
} else if length <= n {
err = io.EOF
break
pend := n + int(file.Length-fileOffset)
if pend > _len {
pend = _len
}
filename := file.PathWithPrefix(r.root, r.info)
@ -86,7 +84,7 @@ func (r *reader) ReadAt(p []byte, offset int64) (n int, err error) {
break
}
m, err = f.ReadAt(p[n:length], fileOffset)
m, err = f.ReadAt(p[n:pend], fileOffset)
n += m
offset += int64(m)
if err != nil {

View File

@ -0,0 +1,78 @@
// Copyright 2020 xgfone
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metainfo
import (
"bytes"
"os"
"testing"
)
func generateTestPieceData(len int, numData byte) []byte {
b := make([]byte, len)
for i := 0; i < len; i++ {
b[i] = numData
}
return b
}
func TestWriterAndReader(t *testing.T) {
info := Info{
Name: "test_rw",
PieceLength: 64,
Files: []File{
{Length: 100, Paths: []string{"file1"}},
{Length: 200, Paths: []string{"file2"}},
{Length: 300, Paths: []string{"file3"}},
},
}
r := NewReader("", info)
w := NewWriter("", info, 0600)
defer func() {
w.Close()
r.Close()
os.RemoveAll("test_rw")
}()
datalen := 600
wdata := make([]byte, datalen)
for i := 0; i < datalen; i++ {
wdata[i] = byte(i%26 + 97)
}
n, err := w.WriteAt(wdata, 0)
if err != nil {
t.Error(err)
return
} else if n != len(wdata) {
t.Errorf("expect wrote '%d', but got '%d'\n", len(wdata), n)
return
}
rdata := make([]byte, datalen)
n, err = r.ReadAt(rdata, 0)
if err != nil {
t.Error(err)
return
} else if n != len(rdata) {
t.Errorf("expect read '%d', but got '%d'\n", len(rdata), n)
return
}
if bytes.Compare(rdata, wdata) != 0 {
t.Errorf("expect read '%x', but got '%x'\n", wdata, rdata)
}
}

View File

@ -40,12 +40,12 @@ type writer struct {
// NewWriter returns a new Writer.
//
// If fileMode is equal to 0, it is 0700 by default.
// If fileMode is equal to 0, it is 0600 by default.
//
// Notice: fileMode is only used when writing the data.
func NewWriter(rootDir string, info Info, fileMode os.FileMode) Writer {
if fileMode == 0 {
fileMode = 0700
fileMode = 0600
}
return &writer{
@ -63,13 +63,8 @@ func (w *writer) open(filename string) (f *os.File, err error) {
f, ok := w.files[filename]
if !ok {
mode := w.mode
if mode == 0 {
mode = 0700
}
if err = os.MkdirAll(filepath.Dir(filename), mode); err == nil {
if f, err = os.OpenFile(filename, wflag, 0700); err == nil {
if err = os.MkdirAll(filepath.Dir(filename), 0700); err == nil {
if f, err = os.OpenFile(filename, wflag, w.mode); err == nil {
w.files[filename] = f
}
}
@ -103,7 +98,8 @@ func (w *writer) WriteAt(p []byte, offset int64) (n int, err error) {
for _len := len(p); n < _len; {
file, fileOffset := w.info.GetFileByOffset(offset)
if file.Length == 0 {
if file.Length == 0 || file.Length == fileOffset {
err = io.ErrShortWrite
break
}