blob: d0b717412ab6d7e7f4ab8149616fe31469af2e3e [file] [log] [blame]
package blkio
import (
"fmt"
"io"
"os"
)
type ReaderWithSize struct {
io.Reader
size int64
}
// SizedReader is an io.Reader with a known size
type SizedReader interface {
io.Reader
Size() int64
}
// NewSizedReader returns a SizedReader given a reader and a size.
// The returned SizedReader is a ReaderWithSize.
func NewSizedReader(r io.Reader, size int64) SizedReader {
return &ReaderWithSize{r, size}
}
func (r *ReaderWithSize) Size() int64 {
return r.size
}
// LazyFileReader implements a SizedReader which opens a file on first read
// and closes it again after the reader has reached EOF.
type LazyFileReader struct {
name string
size int64
f *os.File
done bool
}
func (r *LazyFileReader) init() error {
f, err := os.Open(r.name)
if err != nil {
return fmt.Errorf("failed to open file for reading: %w", err)
}
r.f = f
return nil
}
func (r *LazyFileReader) Size() int64 {
return r.size
}
func (r *LazyFileReader) Read(b []byte) (n int, err error) {
if r.done {
return 0, io.EOF
}
if r.f == nil {
if err = r.init(); err != nil {
return
}
}
n, err = r.f.Read(b)
if err == io.EOF {
r.done = true
r.f.Close()
}
return
}
func (r *LazyFileReader) Close() {
r.done = true
r.f.Close()
}
func NewFileReader(name string) (*LazyFileReader, error) {
info, err := os.Stat(name)
if err != nil {
return nil, fmt.Errorf("failed to stat: %w", err)
}
return &LazyFileReader{
size: info.Size(),
name: name,
}, nil
}