github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/go/merge/three_way_list.go (about)

     1  // Copyright 2016 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  package merge
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/attic-labs/noms/go/d"
    11  	"github.com/attic-labs/noms/go/types"
    12  )
    13  
    14  func threeWayListMerge(a, b, parent types.List) (merged types.List, err error) {
    15  	aSpliceChan, bSpliceChan := make(chan types.Splice), make(chan types.Splice)
    16  	aStopChan, bStopChan := make(chan struct{}, 1), make(chan struct{}, 1)
    17  
    18  	go func() {
    19  		a.Diff(parent, aSpliceChan, aStopChan)
    20  		close(aSpliceChan)
    21  	}()
    22  	go func() {
    23  		b.Diff(parent, bSpliceChan, bStopChan)
    24  		close(bSpliceChan)
    25  	}()
    26  
    27  	stopAndDrain := func(stop chan<- struct{}, drain <-chan types.Splice) {
    28  		close(stop)
    29  		for range drain {
    30  		}
    31  	}
    32  
    33  	defer stopAndDrain(aStopChan, aSpliceChan)
    34  	defer stopAndDrain(bStopChan, bSpliceChan)
    35  
    36  	// The algorithm below relies on determining whether one splice "comes before" another, and whether the splices coming from the two diffs remove/add precisely the same elements. Unfortunately, the Golang zero-value for types.Splice (which is what gets read out of a/bSpliceChan when they're closed) is actaually a valid splice, albeit a meaningless one that indicates a no-op. It "comes before" any other splice, so having it in play really gums up the logic below. Rather than specifically checking for it all over the place, swap the zero-splice out for one full of SPLICE_UNASSIGNED, which is really the proper invalid splice value. That splice doesn't come before ANY valid splice, so the logic below can flow more clearly.
    37  	zeroSplice := types.Splice{}
    38  	zeroToInvalid := func(sp types.Splice) types.Splice {
    39  		if sp == zeroSplice {
    40  			return types.Splice{
    41  				SpAt:      types.SPLICE_UNASSIGNED,
    42  				SpRemoved: types.SPLICE_UNASSIGNED,
    43  				SpAdded:   types.SPLICE_UNASSIGNED,
    44  				SpFrom:    types.SPLICE_UNASSIGNED,
    45  			}
    46  		}
    47  		return sp
    48  	}
    49  	invalidSplice := zeroToInvalid(types.Splice{})
    50  
    51  	merged = parent
    52  	offset := uint64(0)
    53  	aSplice, bSplice := invalidSplice, invalidSplice
    54  	for {
    55  		// Get the next splice from both a and b. If either diff(a, parent) or diff(b, parent) is complete, aSplice or bSplice will get an invalid types.Splice. Generally, though, this allows us to proceed through both diffs in (index) order, considering the "current" splice from both diffs at the same time.
    56  		if aSplice == invalidSplice {
    57  			aSplice = zeroToInvalid(<-aSpliceChan)
    58  		}
    59  		if bSplice == invalidSplice {
    60  			bSplice = zeroToInvalid(<-bSpliceChan)
    61  		}
    62  		// Both channels are producing zero values, so we're done.
    63  		if aSplice == invalidSplice && bSplice == invalidSplice {
    64  			break
    65  		}
    66  		if overlap(aSplice, bSplice) {
    67  			if canMerge(a, b, aSplice, bSplice) {
    68  				splice := merge(aSplice, bSplice)
    69  				merged = apply(a, merged, offset, splice)
    70  				offset += splice.SpAdded - splice.SpRemoved
    71  				aSplice, bSplice = invalidSplice, invalidSplice
    72  				continue
    73  			}
    74  			return parent, newMergeConflict("Overlapping splices: %s vs %s", describeSplice(aSplice), describeSplice(bSplice))
    75  		}
    76  		if aSplice.SpAt < bSplice.SpAt {
    77  			merged = apply(a, merged, offset, aSplice)
    78  			offset += aSplice.SpAdded - aSplice.SpRemoved
    79  			aSplice = invalidSplice
    80  			continue
    81  		}
    82  		merged = apply(b, merged, offset, bSplice)
    83  		offset += bSplice.SpAdded - bSplice.SpRemoved
    84  		bSplice = invalidSplice
    85  	}
    86  
    87  	return merged, nil
    88  }
    89  
    90  func overlap(s1, s2 types.Splice) bool {
    91  	earlier, later := s1, s2
    92  	if s2.SpAt < s1.SpAt {
    93  		earlier, later = s2, s1
    94  	}
    95  	return s1.SpAt == s2.SpAt || earlier.SpAt+earlier.SpRemoved > later.SpAt
    96  }
    97  
    98  // canMerge returns whether aSplice and bSplice can be merged into a single splice that can be applied to parent. Currently, we're only willing to do this if the two splices do _precisely_ the same thing -- that is, remove the same number of elements from the same starting index and insert the exact same list of new elements.
    99  func canMerge(a, b types.List, aSplice, bSplice types.Splice) bool {
   100  	if aSplice != bSplice {
   101  		return false
   102  	}
   103  	aIter, bIter := a.IteratorAt(aSplice.SpFrom), b.IteratorAt(bSplice.SpFrom)
   104  	for count := uint64(0); count < aSplice.SpAdded; count++ {
   105  		aVal, bVal := aIter.Next(), bIter.Next()
   106  		if aVal == nil || bVal == nil || !aVal.Equals(bVal) {
   107  			return false
   108  		}
   109  	}
   110  	return true
   111  }
   112  
   113  // Since merge() is only called when canMerge() is true, we know s1 and s2 are exactly equal.
   114  func merge(s1, s2 types.Splice) types.Splice {
   115  	return s1
   116  }
   117  
   118  func apply(source, target types.List, offset uint64, s types.Splice) types.List {
   119  	toAdd := make([]types.Valuable, s.SpAdded)
   120  	iter := source.IteratorAt(s.SpFrom)
   121  	for i := 0; uint64(i) < s.SpAdded; i++ {
   122  		v := iter.Next()
   123  		if v == nil {
   124  			d.Panic("List diff returned a splice that inserts a nonexistent element.")
   125  		}
   126  		toAdd[i] = v
   127  	}
   128  	return target.Edit().Splice(s.SpAt+offset, s.SpRemoved, toAdd...).List()
   129  }
   130  
   131  func describeSplice(s types.Splice) string {
   132  	return fmt.Sprintf("%d elements removed at %d; adding %d elements", s.SpRemoved, s.SpAt, s.SpAdded)
   133  }