Skip to content

Commit

Permalink
make io.Seeker implementation optional
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Lanford <[email protected]>
  • Loading branch information
joelanford committed Aug 4, 2023
1 parent 4470b8a commit 2b152d0
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 56 deletions.
88 changes: 84 additions & 4 deletions benchmarks/benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tarfs

import (
"archive/tar"
"io"
"io/fs"
"math/rand"
"os"
Expand Down Expand Up @@ -33,6 +34,16 @@ func BenchmarkOpenTarThenReadFile_ManySmallFiles(b *testing.B) {
}
}

func BenchmarkOpenTarThenReadFile_ManySmallFiles_DisableSeek(b *testing.B) {
fileName := randomFileName["many-small-files.tar"]

b.ResetTimer()

for i := 0; i < b.N; i++ {
openTarThenReadFile("many-small-files.tar", fileName, tarfs.DisableSeek(true))
}
}

func BenchmarkOpenTarThenReadFile_FewLargeFiles(b *testing.B) {
fileName := randomFileName["few-large-files.tar"]

Expand All @@ -43,22 +54,56 @@ func BenchmarkOpenTarThenReadFile_FewLargeFiles(b *testing.B) {
}
}

func BenchmarkOpenTarThenReadFile_FewLargeFiles_DisableSeek(b *testing.B) {
fileName := randomFileName["few-large-files.tar"]

b.ResetTimer()

for i := 0; i < b.N; i++ {
openTarThenReadFile("few-large-files.tar", fileName, tarfs.DisableSeek(true))
}
}

func BenchmarkReadFile_ManySmallFiles(b *testing.B) {
benchmarkReadFile(b, "many-small-files.tar")
}

func BenchmarkReadFile_ManySmallFiles_DisableSeek(b *testing.B) {
benchmarkReadFile(b, "many-small-files.tar", tarfs.DisableSeek(true))
}

func BenchmarkReadFile_FewLargeFiles(b *testing.B) {
benchmarkReadFile(b, "few-large-files.tar")
}

func benchmarkReadFile(b *testing.B, tarFileName string) {
func BenchmarkReadFile_FewLargeFiles_DisableSeek(b *testing.B) {
benchmarkReadFile(b, "few-large-files.tar", tarfs.DisableSeek(true))
}

func BenchmarkOpenAndReadFile_ManySmallFiles(b *testing.B) {
benchmarkOpenAndReadFile(b, "many-small-files.tar")
}

func BenchmarkOpenAndReadFile_ManySmallFiles_DisableSeek(b *testing.B) {
benchmarkOpenAndReadFile(b, "many-small-files.tar", tarfs.DisableSeek(true))
}

func BenchmarkOpenAndReadFile_FewLargeFiles(b *testing.B) {
benchmarkOpenAndReadFile(b, "few-large-files.tar")
}

func BenchmarkOpenAndReadFile_FewLargeFiles_DisableSeek(b *testing.B) {
benchmarkOpenAndReadFile(b, "few-large-files.tar", tarfs.DisableSeek(true))
}

func benchmarkReadFile(b *testing.B, tarFileName string, options ...tarfs.Option) {
tf, err := os.Open(tarFileName)
if err != nil {
panic(err)
}
defer tf.Close()

tfs, err := tarfs.New(tf)
tfs, err := tarfs.New(tf, options...)
if err != nil {
panic(err)
}
Expand All @@ -74,7 +119,42 @@ func benchmarkReadFile(b *testing.B, tarFileName string) {
}
}

func openTarThenReadFile(tarName, fileName string) {
func benchmarkOpenAndReadFile(b *testing.B, tarFileName string, options ...tarfs.Option) {
tf, err := os.Open(tarFileName)
if err != nil {
panic(err)
}
defer tf.Close()

tfs, err := tarfs.New(tf, options...)
if err != nil {
panic(err)
}

fileName := randomFileName[tarFileName]

b.ResetTimer()

for i := 0; i < b.N; i++ {
f, err := tfs.Open(fileName)
if err != nil {
panic(err)
}
st, err := f.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, st.Size())
if _, err := io.ReadFull(f, buf); err != nil {
panic(err)
}
if err := f.Close(); err != nil {
panic(err)
}
}
}

func openTarThenReadFile(tarName, fileName string, options ...tarfs.Option) {
tf, err := os.Open(tarName)
if err != nil {
panic(err)
Expand All @@ -83,7 +163,7 @@ func openTarThenReadFile(tarName, fileName string) {

var tfs fs.FS

tfs, err = tarfs.New(tf)
tfs, err = tarfs.New(tf, options...)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion example_stat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func Example_stat() {
}
defer tf.Close()

tfs, err := tarfs.New(tf)
tfs, err := tarfs.New(tf, tarfs.DisableSeek(true))
if err != nil {
panic(err)
}
Expand Down
39 changes: 22 additions & 17 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

type file struct {
entry
r io.ReadSeeker
r io.Reader
readDirPos int
closed bool
}
Expand Down Expand Up @@ -51,22 +51,6 @@ func (f *file) Close() error {
return nil
}

var _ io.Seeker = &file{}

func (f *file) Seek(offset int64, whence int) (int64, error) {
const op = "seek"

if f.closed {
return 0, newErrClosed(op, f.Name())
}

if f.IsDir() {
return 0, newErrDir(op, f.Name())
}

return f.r.Seek(offset, whence)
}

var _ fs.ReadDirFile = &file{}

func (f *file) ReadDir(n int) ([]fs.DirEntry, error) {
Expand Down Expand Up @@ -100,3 +84,24 @@ func (f *file) ReadDir(n int) ([]fs.DirEntry, error) {

return entries, nil
}

type fileSeeker struct {
file
seeker io.Seeker
}

var _ io.Seeker = &fileSeeker{}

func (f *fileSeeker) Seek(offset int64, whence int) (int64, error) {
const op = "seek"

if f.closed {
return 0, newErrClosed(op, f.Name())
}

if f.IsDir() {
return 0, newErrDir(op, f.Name())
}

return f.seeker.Seek(offset, whence)
}
71 changes: 53 additions & 18 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,25 @@ type tarfs struct {
entries map[string]fs.DirEntry
}

type options struct {
disableSeek bool
}

type Option func(*options)

func DisableSeek(disable bool) func(*options) {
return func(o *options) {
o.disableSeek = disable
}
}

// New creates a new tar fs.FS from r
func New(r io.Reader) (fs.FS, error) {
func New(r io.Reader, opts ...Option) (fs.FS, error) {
o := &options{}
for _, opt := range opts {
opt(o)
}

tfs := &tarfs{make(map[string]fs.DirEntry)}
tfs.entries["."] = newDirEntry(fs.FileInfoToDirEntry(fakeDirFileInfo(".")))

Expand Down Expand Up @@ -61,7 +78,7 @@ func New(r io.Reader) (fs.FS, error) {
if h.FileInfo().IsDir() {
tfs.append(name, newDirEntry(de))
} else {
tfs.append(name, &regEntry{de, name, ra, cr.Count() - blockSize})
tfs.append(name, &regEntry{de, o.disableSeek, name, ra, cr.Count() - blockSize})
}
}

Expand Down Expand Up @@ -240,14 +257,15 @@ type entry interface {
readdir(path string) ([]fs.DirEntry, error)
readfile(path string) ([]byte, error)
entries(op, path string) ([]fs.DirEntry, error)
open() (*file, error)
open() (fs.File, error)
}

type regEntry struct {
fs.DirEntry
name string
ra io.ReaderAt
offset int64
disableSeek bool
name string
ra io.ReaderAt
offset int64
}

var _ entry = &regEntry{}
Expand All @@ -262,32 +280,49 @@ func (e *regEntry) readdir(path string) ([]fs.DirEntry, error) {
}

func (e *regEntry) readfile(path string) ([]byte, error) {
tr := tar.NewReader(io.NewSectionReader(e.ra, e.offset, 1<<63-1))

if _, err := tr.Next(); err != nil {
f, err := e.openReader()
if err != nil {
return nil, err
}

b := bytes.NewBuffer(make([]byte, 0, e.size()))

if _, err := io.Copy(b, tr); err != nil {
buf := make([]byte, 0, e.size())
if _, err := io.ReadFull(f, buf); err != nil {
return nil, err
}

return b.Bytes(), nil
return buf, nil
}

func (e *regEntry) entries(op, path string) ([]fs.DirEntry, error) {
return nil, newErrNotDir(op, path)
}

func (e *regEntry) open() (*file, error) {
b, err := e.readfile("")
func (e *regEntry) openReader() (io.Reader, error) {
tr := tar.NewReader(io.NewSectionReader(e.ra, e.offset, 1<<63-1))

if _, err := tr.Next(); err != nil {
return nil, err
}
return tr, nil
}

func (e *regEntry) open() (fs.File, error) {
tr, err := e.openReader()
if err != nil {
return nil, err
}
if e.disableSeek {
return &file{e, tr, -1, false}, nil
}

return &file{e, bytes.NewReader(b), -1, false}, nil
b := bytes.NewBuffer(make([]byte, 0, e.size()))
if _, err := io.Copy(b, tr); err != nil {
return nil, err
}
r := bytes.NewReader(b.Bytes())
return &fileSeeker{
file: file{e, r, -1, false},
seeker: r,
}, nil
}

type dirEntry struct {
Expand Down Expand Up @@ -334,7 +369,7 @@ func (e *dirEntry) entries(op, path string) ([]fs.DirEntry, error) {
return e._entries, nil
}

func (e *dirEntry) open() (*file, error) {
func (e *dirEntry) open() (fs.File, error) {
return &file{e, nil, 0, false}, nil
}

Expand Down
Loading

0 comments on commit 2b152d0

Please sign in to comment.