github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/datas/ref_closure.go (about) 1 // Copyright 2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package datas 16 17 import ( 18 "context" 19 20 "github.com/dolthub/dolt/go/store/hash" 21 "github.com/dolthub/dolt/go/store/types" 22 ) 23 24 // CommitClosure is a transitive closure of commit parents. 25 type CommitClosure interface { 26 // Contains returns true if |commit| is contained in the closure. 27 Contains(ctx context.Context, commit *Commit) (bool, error) 28 } 29 30 // NewSetCommitClosure computes the entire transitive closure of |commit|. 31 func NewSetCommitClosure(ctx context.Context, vr types.ValueReader, commit *Commit) (CommitClosure, error) { 32 s, err := transitiveClosure(ctx, vr, commit) 33 if err != nil { 34 return setCommitClosure{}, err 35 } 36 37 return setCommitClosure{HashSet: s}, nil 38 } 39 40 type setCommitClosure struct { 41 hash.HashSet 42 } 43 44 var _ CommitClosure = setCommitClosure{} 45 46 // Contains returns true if |commit| is contained in the closure. 47 func (s setCommitClosure) Contains(ctx context.Context, commit *Commit) (ok bool, err error) { 48 ok = s.HashSet.Has(commit.Addr()) 49 return 50 } 51 52 func transitiveClosure(ctx context.Context, vr types.ValueReader, commit *Commit) (s hash.HashSet, err error) { 53 h := &CommitByHeightHeap{commit} 54 s = hash.NewHashSet() 55 56 var curr []*Commit 57 for !h.Empty() { 58 curr = h.PopCommitsOfHeight(h.MaxHeight()) 59 for _, c := range curr { 60 s.Insert(c.Addr()) 61 } 62 63 err = parentsToQueue(ctx, curr, h, vr) 64 if err != nil { 65 return nil, err 66 } 67 } 68 69 return s, nil 70 } 71 72 // NewLazyCommitClosure makes a lazy CommitClosure, which computes the 73 // transitive closure of |commit| on demand to answer Contains() queries. 74 func NewLazyCommitClosure(commit *Commit, vr types.ValueReader) CommitClosure { 75 return lazyCommitClosure{ 76 seen: hash.NewHashSet(commit.Addr()), 77 heap: &CommitByHeightHeap{commit}, 78 vr: vr, 79 } 80 } 81 82 type lazyCommitClosure struct { 83 seen hash.HashSet 84 heap *CommitByHeightHeap 85 vr types.ValueReader 86 } 87 88 var _ CommitClosure = lazyCommitClosure{} 89 90 // Contains returns true if |commit| is contained in the closure. 91 func (l lazyCommitClosure) Contains(ctx context.Context, commit *Commit) (ok bool, err error) { 92 err = l.traverseBelowDepth(ctx, commit.Height()) 93 if err != nil { 94 return false, err 95 } 96 return l.seen.Has(commit.Addr()), nil 97 } 98 99 // traverseBelowDepth traverses through all of the refs of height |depth| or higher, 100 // adding them to the set |l.seen|. 101 func (l lazyCommitClosure) traverseBelowDepth(ctx context.Context, depth uint64) (err error) { 102 var curr []*Commit 103 for !l.heap.Empty() && depth <= l.heap.MaxHeight() { 104 curr = l.heap.PopCommitsOfHeight(l.heap.MaxHeight()) 105 for _, r := range curr { 106 l.seen.Insert(r.Addr()) 107 } 108 err = parentsToQueue(ctx, curr, l.heap, l.vr) 109 if err != nil { 110 return err 111 } 112 } 113 return nil 114 }