1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package blob
18
19 import (
20 "context"
21 "errors"
22 "fmt"
23 "io"
24 "math"
25 "os"
26 )
27
28 var (
29 ErrNegativeSubFetch = errors.New("invalid negative subfetch parameters")
30 ErrOutOfRangeOffsetSubFetch = errors.New("subfetch offset greater than blob size")
31 )
32
33
34
35 type Fetcher interface {
36
37
38
39
40
41
42
43
44
45
46
47
48
49 Fetch(context.Context, Ref) (blob io.ReadCloser, size uint32, err error)
50 }
51
52
53
54 var ErrUnimplemented = errors.New("optional method not implemented")
55
56
57 type SubFetcher interface {
58
59
60
61
62
63
64
65
66 SubFetch(ctx context.Context, ref Ref, offset, length int64) (io.ReadCloser, error)
67 }
68
69 func NewSerialFetcher(fetchers ...Fetcher) Fetcher {
70 return &serialFetcher{fetchers}
71 }
72
73 func NewSimpleDirectoryFetcher(dir string) *DirFetcher {
74 return &DirFetcher{dir, "camli"}
75 }
76
77 type serialFetcher struct {
78 fetchers []Fetcher
79 }
80
81 func (sf *serialFetcher) Fetch(ctx context.Context, r Ref) (file io.ReadCloser, size uint32, err error) {
82 for _, fetcher := range sf.fetchers {
83 file, size, err = fetcher.Fetch(ctx, r)
84 if err == nil {
85 return
86 }
87 }
88 return
89 }
90
91 type DirFetcher struct {
92 directory, extension string
93 }
94
95 func (df *DirFetcher) Fetch(ctx context.Context, r Ref) (file io.ReadCloser, size uint32, err error) {
96 fileName := fmt.Sprintf("%s/%s.%s", df.directory, r.String(), df.extension)
97 var stat os.FileInfo
98 stat, err = os.Stat(fileName)
99 if err != nil {
100 return
101 }
102 if stat.Size() > math.MaxUint32 {
103 err = errors.New("file size too big")
104 return
105 }
106 file, err = os.Open(fileName)
107 if err != nil {
108 return
109 }
110 size = uint32(stat.Size())
111 return
112 }
113
114
115
116 func ReaderAt(ctx context.Context, sf SubFetcher, br Ref) io.ReaderAt {
117 return readerAt{ctx, sf, br}
118 }
119
120 type readerAt struct {
121 ctx context.Context
122 sf SubFetcher
123 br Ref
124 }
125
126 func (ra readerAt) ReadAt(p []byte, off int64) (n int, err error) {
127 rc, err := ra.sf.SubFetch(ra.ctx, ra.br, off, int64(len(p)))
128 if err != nil {
129 return 0, err
130 }
131 defer rc.Close()
132 return io.ReadFull(rc, p)
133 }