pkg/ranger: FileRanger (#18)

This commit is contained in:
JT Olds 2018-04-23 20:16:34 -06:00 committed by GitHub
parent fe60ba02a3
commit 00854b9736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 7 deletions

View File

@ -65,16 +65,12 @@ func Main() error {
if err != nil {
return err
}
fh, err := os.Open(filepath.Join(flag.Arg(0), piece.Name()))
r, err := ranger.FileRanger(filepath.Join(flag.Arg(0), piece.Name()))
if err != nil {
return err
}
defer fh.Close()
fs, err := fh.Stat()
if err != nil {
return err
}
rrs[piecenum] = ranger.ReaderAtRanger(fh, fs.Size())
defer r.Close()
rrs[piecenum] = r
}
rr, err := eestream.Decode(rrs, es)
if err != nil {

40
pkg/ranger/file.go Normal file
View File

@ -0,0 +1,40 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package ranger
import (
"io"
"os"
)
// FileHandleRanger returns a RangerCloser from a file handle. The
// RangerCloser's Close method will call fh.Close().
// Footgun: If FileHandleRanger fails, fh.Close will not have been called.
func FileHandleRanger(fh *os.File) (RangerCloser, error) {
stat, err := fh.Stat()
if err != nil {
return nil, Error.Wrap(err)
}
return struct {
Ranger
io.Closer
}{
Ranger: ReaderAtRanger(fh, stat.Size()),
Closer: fh,
}, nil
}
// FileRanger returns a RangerCloser from a path.
func FileRanger(path string) (RangerCloser, error) {
fh, err := os.Open(path)
if err != nil {
return nil, err
}
r, err := FileHandleRanger(fh)
if err != nil {
fh.Close()
return nil, err
}
return r, nil
}

67
pkg/ranger/file_test.go Normal file
View File

@ -0,0 +1,67 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package ranger
import (
"bytes"
"io/ioutil"
"testing"
)
func TestFileRanger(t *testing.T) {
for _, example := range []struct {
data string
size, offset, length int64
substr string
fail bool
}{
{"", 0, 0, 0, "", false},
{"abcdef", 6, 0, 0, "", false},
{"abcdef", 6, 3, 0, "", false},
{"abcdef", 6, 0, 6, "abcdef", false},
{"abcdef", 6, 0, 5, "abcde", false},
{"abcdef", 6, 0, 4, "abcd", false},
{"abcdef", 6, 1, 4, "bcde", false},
{"abcdef", 6, 2, 4, "cdef", false},
{"abcdefg", 7, 1, 4, "bcde", false},
{"abcdef", 6, 0, 7, "", true},
{"abcdef", 6, -1, 7, "abcde", true},
{"abcdef", 6, 0, -1, "abcde", true},
} {
fh, err := ioutil.TempFile("", "test")
if err != nil {
t.Fatalf("failed making tempfile")
}
_, err = fh.Write([]byte(example.data))
if err != nil {
t.Fatalf("failed writing data")
}
name := fh.Name()
err = fh.Close()
if err != nil {
t.Fatalf("failed closing data")
}
r, err := FileRanger(name)
if err != nil {
t.Fatalf("failed opening tempfile")
}
defer r.Close()
if r.Size() != example.size {
t.Fatalf("invalid size: %v != %v", r.Size(), example.size)
}
data, err := ioutil.ReadAll(r.Range(example.offset, example.length))
if example.fail {
if err == nil {
t.Fatalf("expected error")
}
} else {
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if !bytes.Equal(data, []byte(example.substr)) {
t.Fatalf("invalid subrange: %#v != %#v", string(data), example.substr)
}
}
}
}

View File

@ -19,6 +19,24 @@ type Ranger interface {
Range(offset, length int64) io.ReadCloser
}
// A RangerCloser is a Ranger that must be closed when finished
type RangerCloser interface {
Ranger
io.Closer
}
// NopCloser makes an existing Ranger function as a RangerCloser
// with a no-op for Close()
func NopCloser(r Ranger) RangerCloser {
return struct {
Ranger
io.Closer
}{
Ranger: r,
Closer: ioutil.NopCloser(nil),
}
}
// ByteRanger turns a byte slice into a Ranger
type ByteRanger []byte