github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libgit/browser_file.go (about) 1 // Copyright 2018 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libgit 6 7 import ( 8 "io" 9 10 "github.com/pkg/errors" 11 billy "gopkg.in/src-d/go-billy.v4" 12 "gopkg.in/src-d/go-git.v4/plumbing/object" 13 ) 14 15 const ( 16 browserFileDefaultMaxBufSize = 4 * 1024 * 1024 // 4 MB 17 ) 18 19 type browserFile struct { 20 f *object.File 21 r io.ReadCloser 22 maxBufSize int64 23 } 24 25 var _ billy.File = (*browserFile)(nil) 26 27 func newBrowserFile(f *object.File) (*browserFile, error) { 28 r, err := f.Reader() 29 if err != nil { 30 return nil, err 31 } 32 return &browserFile{ 33 f: f, 34 r: r, 35 maxBufSize: browserFileDefaultMaxBufSize, 36 }, nil 37 } 38 39 func (bf *browserFile) Name() string { 40 return bf.f.Name 41 } 42 43 func (bf *browserFile) Write(_ []byte) (n int, err error) { 44 return 0, errors.New("browser files can't be written") 45 } 46 47 func (bf *browserFile) Read(p []byte) (n int, err error) { 48 return bf.r.Read(p) 49 } 50 51 func (bf *browserFile) ReadAt(p []byte, off int64) (n int, err error) { 52 // Sadly go-git doesn't expose a `ReadAt` or `Seek` interface for 53 // this, but we can probably implement it if needed. Instead, use 54 // a new Reader object and just scan starting from the beginning. 55 r, err := bf.f.Reader() 56 if err != nil { 57 return 0, err 58 } 59 defer func() { 60 _ = r.Close() 61 }() 62 63 dataToSkip := off 64 bufSize := dataToSkip 65 if bufSize > bf.maxBufSize { 66 bufSize = bf.maxBufSize 67 } 68 buf := make([]byte, bufSize) 69 70 // Skip past the data we don't care about, one chunk at a time. 71 for dataToSkip > 0 { 72 toRead := int64(len(buf)) 73 if dataToSkip < toRead { 74 toRead = dataToSkip 75 } 76 77 // Throwaway data. 78 n, err := r.Read(buf[:toRead]) 79 if err != nil { 80 return 0, err 81 } 82 dataToSkip -= int64(n) 83 } 84 85 return r.Read(p) 86 } 87 88 func (bf *browserFile) Seek(offset int64, whence int) (int64, error) { 89 // TODO if needed: we'd have to track the offset of `bf.r` 90 // manually, the same way we do in `libfs.File`. 91 return 0, errors.New("browser files can't seek") 92 } 93 94 func (bf *browserFile) Close() error { 95 return bf.r.Close() 96 } 97 98 func (bf *browserFile) Lock() error { 99 return errors.New("browser files can't be locked") 100 } 101 102 func (bf *browserFile) Unlock() error { 103 return errors.New("browser files can't be unlocked") 104 } 105 106 func (bf *browserFile) Truncate(size int64) error { 107 return errors.New("browser files can't be truncated") 108 }