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