github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libgit/on_demand_delta_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 onDemandDeltaObject struct {
    17  	s           storer.DeltaObjectStorer
    18  	hash        plumbing.Hash
    19  	typeSet     bool
    20  	objType     plumbing.ObjectType
    21  	sizeSet     bool
    22  	size        int64
    23  	baseHash    plumbing.Hash
    24  	actualHash  plumbing.Hash
    25  	actualSize  int64
    26  	recentCache *lru.Cache
    27  }
    28  
    29  var _ plumbing.DeltaObject = (*onDemandDeltaObject)(nil)
    30  
    31  type notDeltaError struct {
    32  }
    33  
    34  func (nde notDeltaError) Error() string {
    35  	return "Not a delta object"
    36  }
    37  
    38  func (oddo *onDemandDeltaObject) cache() (o plumbing.DeltaObject, err error) {
    39  	if tmp, ok := oddo.recentCache.Get(oddo.hash); ok {
    40  		o, ok = tmp.(plumbing.DeltaObject)
    41  		if !ok {
    42  			return nil, notDeltaError{}
    43  		}
    44  	} else {
    45  		eo, err := oddo.s.DeltaObject(oddo.objType, oddo.hash)
    46  		if err != nil {
    47  			return nil, err
    48  		}
    49  		oddo.recentCache.Add(oddo.hash, eo)
    50  		o, ok = eo.(plumbing.DeltaObject)
    51  		if !ok {
    52  			return nil, notDeltaError{}
    53  		}
    54  	}
    55  
    56  	if !oddo.sizeSet {
    57  		oddo.size = o.Size()
    58  	}
    59  	if !oddo.typeSet {
    60  		oddo.objType = o.Type()
    61  	}
    62  	oddo.baseHash = o.BaseHash()
    63  	oddo.actualHash = o.ActualHash()
    64  	oddo.actualSize = o.ActualSize()
    65  	return o, nil
    66  }
    67  
    68  func (oddo *onDemandDeltaObject) cacheIfNeeded() error {
    69  	if oddo.size >= 0 {
    70  		return nil
    71  	}
    72  	// TODDO: We should be able to read the type and size from the
    73  	// object's header, without loading the entire object itself.
    74  	_, err := oddo.cache()
    75  	return err
    76  }
    77  
    78  func (oddo *onDemandDeltaObject) Hash() plumbing.Hash {
    79  	return oddo.hash
    80  }
    81  
    82  func (oddo *onDemandDeltaObject) Type() plumbing.ObjectType {
    83  	_ = oddo.cacheIfNeeded()
    84  	return oddo.objType
    85  }
    86  
    87  func (oddo *onDemandDeltaObject) SetType(ot plumbing.ObjectType) {
    88  	oddo.typeSet = true
    89  	oddo.objType = ot
    90  }
    91  
    92  func (oddo *onDemandDeltaObject) Size() int64 {
    93  	_ = oddo.cacheIfNeeded()
    94  	return oddo.size
    95  }
    96  
    97  func (oddo *onDemandDeltaObject) SetSize(s int64) {
    98  	oddo.sizeSet = true
    99  	oddo.size = s
   100  }
   101  
   102  func (oddo *onDemandDeltaObject) Reader() (io.ReadCloser, error) {
   103  	// Create a new object to stream data from the storer on-demand,
   104  	// without caching the bytes to long-lived memory.  Let the block
   105  	// cache do its job to keep recent data in memory and control
   106  	// total memory usage.
   107  	o, err := oddo.cache()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	return o.Reader()
   112  }
   113  
   114  func (oddo *onDemandDeltaObject) Writer() (io.WriteCloser, error) {
   115  	return nil, errors.New("onDemandDeltaObject shouldn't be used for writes")
   116  }
   117  
   118  func (oddo *onDemandDeltaObject) BaseHash() plumbing.Hash {
   119  	_ = oddo.cacheIfNeeded()
   120  	return oddo.baseHash
   121  }
   122  
   123  func (oddo *onDemandDeltaObject) ActualHash() plumbing.Hash {
   124  	_ = oddo.cacheIfNeeded()
   125  	return oddo.actualHash
   126  }
   127  
   128  func (oddo *onDemandDeltaObject) ActualSize() int64 {
   129  	_ = oddo.cacheIfNeeded()
   130  	return oddo.actualSize
   131  }