blob: d0b717412ab6d7e7f4ab8149616fe31469af2e3e [file] [log] [blame]
Lorenz Brunad131882023-06-28 16:42:20 +02001package blkio
2
3import (
4 "fmt"
5 "io"
6 "os"
7)
8
9type ReaderWithSize struct {
10 io.Reader
11 size int64
12}
13
14// SizedReader is an io.Reader with a known size
15type SizedReader interface {
16 io.Reader
17 Size() int64
18}
19
20// NewSizedReader returns a SizedReader given a reader and a size.
21// The returned SizedReader is a ReaderWithSize.
22func NewSizedReader(r io.Reader, size int64) SizedReader {
23 return &ReaderWithSize{r, size}
24}
25
26func (r *ReaderWithSize) Size() int64 {
27 return r.size
28}
29
30// LazyFileReader implements a SizedReader which opens a file on first read
31// and closes it again after the reader has reached EOF.
32type LazyFileReader struct {
33 name string
34 size int64
35 f *os.File
36 done bool
37}
38
39func (r *LazyFileReader) init() error {
40 f, err := os.Open(r.name)
41 if err != nil {
42 return fmt.Errorf("failed to open file for reading: %w", err)
43 }
44 r.f = f
45 return nil
46}
47
48func (r *LazyFileReader) Size() int64 {
49 return r.size
50}
51
52func (r *LazyFileReader) Read(b []byte) (n int, err error) {
53 if r.done {
54 return 0, io.EOF
55 }
56 if r.f == nil {
57 if err = r.init(); err != nil {
58 return
59 }
60 }
61 n, err = r.f.Read(b)
62 if err == io.EOF {
63 r.done = true
64 r.f.Close()
65 }
66 return
67}
68
69func (r *LazyFileReader) Close() {
70 r.done = true
71 r.f.Close()
72}
73
74func NewFileReader(name string) (*LazyFileReader, error) {
75 info, err := os.Stat(name)
76 if err != nil {
77 return nil, fmt.Errorf("failed to stat: %w", err)
78 }
79 return &LazyFileReader{
80 size: info.Size(),
81 name: name,
82 }, nil
83}