mirror of
https://github.com/go-i2p/go-i2p-bt.git
synced 2025-07-13 11:54:35 -04:00
update and fix the metainfo
This commit is contained in:
@ -6,6 +6,7 @@ go:
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- 1.15.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
script:
|
||||
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
80
metainfo/infohash_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
78
metainfo/reader_writer_test.go
Normal file
78
metainfo/reader_writer_test.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user