1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package blobpacked
18
19 import (
20 "context"
21 "io"
22
23 "perkeep.org/pkg/blob"
24 )
25
26
27 var _ blob.SubFetcher = (*storage)(nil)
28
29
30
31
32
33
34 func (s *storage) SubFetch(ctx context.Context, ref blob.Ref, offset, length int64) (io.ReadCloser, error) {
35
36 m, err := s.getMetaRow(ref)
37 if err != nil {
38 return nil, err
39 }
40 if m.isPacked() {
41 length, err = capOffsetLength(m.size, offset, length)
42 if err != nil {
43 return nil, err
44 }
45
46 return s.large.SubFetch(ctx, m.largeRef, int64(m.largeOff)+offset, length)
47 }
48 if sf, ok := s.small.(blob.SubFetcher); ok {
49 rc, err := sf.SubFetch(ctx, ref, offset, length)
50 if err != blob.ErrUnimplemented {
51 return rc, err
52 }
53 }
54 rc, size, err := s.small.Fetch(ctx, ref)
55 if err != nil {
56 return rc, err
57 }
58 length, err = capOffsetLength(size, offset, length)
59 if err != nil {
60 rc.Close()
61 return nil, err
62 }
63 if offset != 0 {
64 if _, err = io.CopyN(io.Discard, rc, offset); err != nil {
65 rc.Close()
66 return nil, err
67 }
68 }
69 return struct {
70 io.Reader
71 io.Closer
72 }{io.LimitReader(rc, length), rc}, nil
73 }
74
75 func capOffsetLength(size uint32, offset, length int64) (newLength int64, err error) {
76 if offset < 0 || length < 0 {
77 return 0, blob.ErrNegativeSubFetch
78 }
79 if offset > int64(size) {
80 return 0, blob.ErrOutOfRangeOffsetSubFetch
81 }
82 if over := (offset + length) - int64(size); over > 0 {
83 length -= over
84 }
85 return length, nil
86 }