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 }