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  }