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 }