github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libgit/on_demand_object.go (about)

     1  // Copyright 2017 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  	lru "github.com/hashicorp/golang-lru"
    11  	"github.com/pkg/errors"
    12  	"gopkg.in/src-d/go-git.v4/plumbing"
    13  	"gopkg.in/src-d/go-git.v4/plumbing/storer"
    14  )
    15  
    16  type onDemandObject struct {
    17  	s           storer.EncodedObjectStorer
    18  	hash        plumbing.Hash
    19  	typeSet     bool
    20  	objType     plumbing.ObjectType
    21  	sizeSet     bool
    22  	size        int64
    23  	recentCache *lru.Cache
    24  }
    25  
    26  var _ plumbing.EncodedObject = (*onDemandObject)(nil)
    27  
    28  func (odo *onDemandObject) cache() (o plumbing.EncodedObject, err error) {
    29  	if tmp, ok := odo.recentCache.Get(odo.hash); ok {
    30  		o = tmp.(plumbing.EncodedObject)
    31  	} else {
    32  		o, err = odo.s.EncodedObject(odo.objType, odo.hash)
    33  		if err != nil {
    34  			return nil, err
    35  		}
    36  		odo.recentCache.Add(odo.hash, o)
    37  	}
    38  
    39  	if !odo.sizeSet {
    40  		odo.size = o.Size()
    41  	}
    42  	if !odo.typeSet {
    43  		odo.objType = o.Type()
    44  	}
    45  	return o, nil
    46  }
    47  
    48  func (odo *onDemandObject) cacheIfNeeded() error {
    49  	if odo.size >= 0 {
    50  		return nil
    51  	}
    52  	// TODO: We should be able to read the type and size from the
    53  	// object's header, without loading the entire object itself.
    54  	_, err := odo.cache()
    55  	return err
    56  }
    57  
    58  func (odo *onDemandObject) Hash() plumbing.Hash {
    59  	return odo.hash
    60  }
    61  
    62  func (odo *onDemandObject) Type() plumbing.ObjectType {
    63  	_ = odo.cacheIfNeeded()
    64  	return odo.objType
    65  }
    66  
    67  func (odo *onDemandObject) SetType(ot plumbing.ObjectType) {
    68  	odo.typeSet = true
    69  	odo.objType = ot
    70  }
    71  
    72  func (odo *onDemandObject) Size() int64 {
    73  	_ = odo.cacheIfNeeded()
    74  	return odo.size
    75  }
    76  
    77  func (odo *onDemandObject) SetSize(s int64) {
    78  	odo.sizeSet = true
    79  	odo.size = s
    80  }
    81  
    82  func (odo *onDemandObject) Reader() (io.ReadCloser, error) {
    83  	// Create a new object to stream data from the storer on-demand,
    84  	// without caching the bytes to long-lived memory.  Let the block
    85  	// cache do its job to keep recent data in memory and control
    86  	// total memory usage.
    87  	o, err := odo.cache()
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	return o.Reader()
    92  }
    93  
    94  func (odo *onDemandObject) Writer() (io.WriteCloser, error) {
    95  	return nil, errors.New("onDemandObject shouldn't be used for writes")
    96  }