github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/merge/three_way_list.go (about) 1 // Copyright 2019 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 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package merge 23 24 import ( 25 "context" 26 "fmt" 27 28 "golang.org/x/sync/errgroup" 29 30 "github.com/dolthub/dolt/go/store/d" 31 "github.com/dolthub/dolt/go/store/types" 32 ) 33 34 func threeWayListMerge(ctx context.Context, a, b, parent types.List) (merged types.List, err error) { 35 aSpliceChan, bSpliceChan := make(chan types.Splice), make(chan types.Splice) 36 37 eg, ctx := errgroup.WithContext(ctx) 38 eg.Go(func() error { 39 defer close(aSpliceChan) 40 return a.Diff(ctx, parent, aSpliceChan) 41 }) 42 eg.Go(func() error { 43 defer close(bSpliceChan) 44 return b.Diff(ctx, parent, bSpliceChan) 45 }) 46 47 // The algorithm below relies on determining whether one splice "comes 48 // before" another, and whether the splices coming from the two diffs 49 // remove/add precisely the same elements. Unfortunately, the Golang 50 // zero-value for types.Splice (which is what gets read out of 51 // a/bSpliceChan when they're closed) is actaually a valid splice, 52 // albeit a meaningless one that indicates a no-op. It "comes before" 53 // any other splice, so having it in play really gums up the logic 54 // below. Rather than specifically checking for it all over the place, 55 // swap the zero-splice out for one full of SPLICE_UNASSIGNED, which is 56 // really the proper invalid splice value. That splice doesn't come 57 // before ANY valid splice, so the logic below can flow more clearly. 58 zeroSplice := types.Splice{} 59 zeroToInvalid := func(sp types.Splice) types.Splice { 60 if sp == zeroSplice { 61 return types.Splice{SpAt: types.SPLICE_UNASSIGNED, SpRemoved: types.SPLICE_UNASSIGNED, SpAdded: types.SPLICE_UNASSIGNED, SpFrom: types.SPLICE_UNASSIGNED} 62 } 63 return sp 64 } 65 invalidSplice := zeroToInvalid(types.Splice{}) 66 67 merged = parent 68 eg.Go(func() error { 69 offset := uint64(0) 70 aSplice, bSplice := invalidSplice, invalidSplice 71 for { 72 // Get the next splice from both a and b. If either diff(a, 73 // parent) or diff(b, parent) is complete, aSplice or bSplice 74 // will get an invalid types.Splice. Generally, though, this 75 // allows us to proceed through both diffs in (index) order, 76 // considering the "current" splice from both diffs at the same 77 // time. 78 if aSplice == invalidSplice { 79 select { 80 case a := <-aSpliceChan: 81 aSplice = zeroToInvalid(a) 82 case <-ctx.Done(): 83 return ctx.Err() 84 } 85 } 86 if bSplice == invalidSplice { 87 select { 88 case b := <-bSpliceChan: 89 bSplice = zeroToInvalid(b) 90 case <-ctx.Done(): 91 return ctx.Err() 92 } 93 } 94 // Both channels are producing zero values, so we're done. 95 if aSplice == invalidSplice && bSplice == invalidSplice { 96 break 97 } 98 if overlap(aSplice, bSplice) { 99 if mergeable, err := canMerge(ctx, a, b, aSplice, bSplice); err != nil { 100 return err 101 } else if mergeable { 102 splice := merge(aSplice, bSplice) 103 merged, err = apply(ctx, a, merged, offset, splice) 104 if err != nil { 105 return err 106 } 107 108 offset += splice.SpAdded - splice.SpRemoved 109 aSplice, bSplice = invalidSplice, invalidSplice 110 continue 111 } 112 return newMergeConflict("Overlapping splices: %s vs %s", describeSplice(aSplice), describeSplice(bSplice)) 113 } 114 if aSplice.SpAt < bSplice.SpAt { 115 merged, err = apply(ctx, a, merged, offset, aSplice) 116 if err != nil { 117 return err 118 } 119 120 offset += aSplice.SpAdded - aSplice.SpRemoved 121 aSplice = invalidSplice 122 continue 123 } 124 merged, err = apply(ctx, b, merged, offset, bSplice) 125 if err != nil { 126 return err 127 } 128 129 offset += bSplice.SpAdded - bSplice.SpRemoved 130 bSplice = invalidSplice 131 } 132 return nil 133 }) 134 135 if err := eg.Wait(); err != nil { 136 return types.EmptyList, err 137 } 138 139 return merged, nil 140 } 141 142 func overlap(s1, s2 types.Splice) bool { 143 earlier, later := s1, s2 144 if s2.SpAt < s1.SpAt { 145 earlier, later = s2, s1 146 } 147 return s1.SpAt == s2.SpAt || earlier.SpAt+earlier.SpRemoved > later.SpAt 148 } 149 150 // 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. 151 func canMerge(ctx context.Context, a, b types.List, aSplice, bSplice types.Splice) (bool, error) { 152 if aSplice != bSplice { 153 return false, nil 154 } 155 aIter, err := a.IteratorAt(ctx, aSplice.SpFrom) 156 157 if err != nil { 158 return false, err 159 } 160 161 bIter, err := b.IteratorAt(ctx, bSplice.SpFrom) 162 163 if err != nil { 164 return false, err 165 } 166 167 for count := uint64(0); count < aSplice.SpAdded; count++ { 168 aVal, err := aIter.Next(ctx) 169 170 if err != nil { 171 return false, err 172 } 173 174 bVal, err := bIter.Next(ctx) 175 176 if err != nil { 177 return false, err 178 } 179 180 if aVal == nil || bVal == nil || !aVal.Equals(bVal) { 181 return false, nil 182 } 183 } 184 return true, nil 185 } 186 187 // Since merge() is only called when canMerge() is true, we know s1 and s2 are exactly equal. 188 func merge(s1, s2 types.Splice) types.Splice { 189 return s1 190 } 191 192 func apply(ctx context.Context, source, target types.List, offset uint64, s types.Splice) (types.List, error) { 193 toAdd := make([]types.Valuable, s.SpAdded) 194 iter, err := source.IteratorAt(ctx, s.SpFrom) 195 196 if err != nil { 197 return types.EmptyList, err 198 } 199 200 for i := 0; uint64(i) < s.SpAdded; i++ { 201 v, err := iter.Next(ctx) 202 203 if err != nil { 204 return types.EmptyList, err 205 } 206 207 if v == nil { 208 d.Panic("List diff returned a splice that inserts a nonexistent element.") 209 } 210 toAdd[i] = v 211 } 212 return target.Edit().Splice(s.SpAt+offset, s.SpRemoved, toAdd...).List(ctx) 213 } 214 215 func describeSplice(s types.Splice) string { 216 return fmt.Sprintf("%d elements removed at %d; adding %d elements", s.SpRemoved, s.SpAt, s.SpAdded) 217 }