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  }