github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/roachpb/merge_spans.go (about)

     1  // Copyright 2016 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package roachpb
    12  
    13  import "sort"
    14  
    15  type sortedSpans []Span
    16  
    17  func (s sortedSpans) Less(i, j int) bool {
    18  	// Sort first on the start key and second on the end key. Note that we're
    19  	// relying on EndKey = nil (and len(EndKey) == 0) sorting before other
    20  	// EndKeys.
    21  	c := s[i].Key.Compare(s[j].Key)
    22  	if c != 0 {
    23  		return c < 0
    24  	}
    25  	return s[i].EndKey.Compare(s[j].EndKey) < 0
    26  }
    27  
    28  func (s sortedSpans) Swap(i, j int) {
    29  	s[i], s[j] = s[j], s[i]
    30  }
    31  
    32  func (s sortedSpans) Len() int {
    33  	return len(s)
    34  }
    35  
    36  // MergeSpans sorts the incoming spans and merges overlapping spans. Returns
    37  // true iff all of the spans are distinct. Note that even if it returns true,
    38  // adjacent spans might have been merged (i.e. [a, b) is distinct from [b,c),
    39  // but the two are still merged.
    40  //
    41  // The input spans are not safe for re-use.
    42  func MergeSpans(spans []Span) ([]Span, bool) {
    43  	if len(spans) == 0 {
    44  		return spans, true
    45  	}
    46  
    47  	sort.Sort(sortedSpans(spans))
    48  
    49  	// We build up the resulting slice of merged spans in place. This is safe
    50  	// because "r" grows by at most 1 element on each iteration, staying abreast
    51  	// or behind the iteration over "spans".
    52  	r := spans[:1]
    53  	distinct := true
    54  
    55  	for _, cur := range spans[1:] {
    56  		prev := &r[len(r)-1]
    57  		if len(cur.EndKey) == 0 && len(prev.EndKey) == 0 {
    58  			if cur.Key.Compare(prev.Key) != 0 {
    59  				// [a, nil] merge [b, nil]
    60  				r = append(r, cur)
    61  			} else {
    62  				// [a, nil] merge [a, nil]
    63  				distinct = false
    64  			}
    65  			continue
    66  		}
    67  		if len(prev.EndKey) == 0 {
    68  			if cur.Key.Compare(prev.Key) == 0 {
    69  				// [a, nil] merge [a, b]
    70  				prev.EndKey = cur.EndKey
    71  				distinct = false
    72  			} else {
    73  				// [a, nil] merge [b, c]
    74  				r = append(r, cur)
    75  			}
    76  			continue
    77  		}
    78  		if c := prev.EndKey.Compare(cur.Key); c >= 0 {
    79  			if cur.EndKey != nil {
    80  				if prev.EndKey.Compare(cur.EndKey) < 0 {
    81  					// [a, c] merge [b, d]
    82  					prev.EndKey = cur.EndKey
    83  					if c > 0 {
    84  						distinct = false
    85  					}
    86  				} else {
    87  					// [a, c] merge [b, c]
    88  					distinct = false
    89  				}
    90  			} else if c == 0 {
    91  				// [a, b] merge [b, nil]
    92  				prev.EndKey = cur.Key.Next()
    93  			} else {
    94  				// [a, c] merge [b, nil]
    95  				distinct = false
    96  			}
    97  			continue
    98  		}
    99  		r = append(r, cur)
   100  	}
   101  	return r, distinct
   102  }
   103  
   104  // SubtractSpans subtracts the subspans covered by a set of non-overlapping
   105  // spans from another set of non-overlapping spans.
   106  //
   107  // Specifically, it returns a non-overlapping set of spans that cover the spans
   108  // covered by the minuend but not the subtrahend. For example, given a single
   109  // minuend span [0, 10) and a subspan subtrahend [4,5) yields: [0, 4), [5, 10).
   110  //
   111  // The todo input is mutated during execution and is not safe for reuse after.
   112  // The done input is left as-is and is safe for later reuse.
   113  //
   114  // Internally the minuend and subtrahend are labeled as "todo" and "done", i.e.
   115  // conceptually it discusses them as a set of spans "to do" with a subset that
   116  // has been "done" and need to be removed from the set "todo".
   117  func SubtractSpans(todo, done Spans) Spans {
   118  	if len(done) == 0 {
   119  		return todo
   120  	}
   121  	sort.Sort(todo)
   122  	sort.Sort(done)
   123  
   124  	remaining := make(Spans, 0, len(todo))
   125  	appendRemaining := func(s Span) {
   126  		if len(remaining) > 0 && remaining[len(remaining)-1].EndKey.Equal(s.Key) {
   127  			remaining[len(remaining)-1].EndKey = s.EndKey
   128  		} else {
   129  			remaining = append(remaining, s)
   130  		}
   131  	}
   132  
   133  	var d int
   134  	var t int
   135  	for t < len(todo) && d < len(done) {
   136  		tStart, tEnd := todo[t].Key, todo[t].EndKey
   137  		dStart, dEnd := done[d].Key, done[d].EndKey
   138  		if tStart.Equal(tEnd) {
   139  			// We've shrunk the todo span to nothing: pop it off and move on.
   140  			t++
   141  			continue
   142  		}
   143  		if dStart.Compare(tEnd) >= 0 {
   144  			// Done span starts after todo span: todo is kept in its entirety.
   145  			appendRemaining(todo[t])
   146  			t++
   147  			continue
   148  		}
   149  		if dEnd.Compare(tStart) <= 0 {
   150  			// Done span isn't in todo at all, so pop it off and move on.
   151  			d++
   152  			continue
   153  		}
   154  
   155  		// At this point, we know that the two spans overlap.
   156  		endCmp := dEnd.Compare(tEnd)
   157  		if dStart.Compare(tStart) <= 0 {
   158  			// The done span starts at or before the todo span starts.
   159  			if endCmp < 0 {
   160  				// Covers strict prefix of todo: pop done and shrink remaining todo.
   161  				todo[t].Key = dEnd
   162  				d++
   163  			} else if endCmp > 0 {
   164  				// Covers all of todo and more: pop todo, keep consuming done.
   165  				t++
   166  			} else {
   167  				// cmp == 0 means exactly matches: pop both.
   168  				t++
   169  				d++
   170  			}
   171  		} else {
   172  			// The beginning of todo is uncovered: split it to remaining.
   173  			appendRemaining(Span{Key: tStart, EndKey: dStart})
   174  
   175  			if endCmp < 0 {
   176  				// There is todo uncovered after done: Pop done, shrink and keep todo.
   177  				todo[t].Key = dEnd
   178  				d++
   179  			} else if endCmp > 0 {
   180  				// Done covers beyond todo: pop todo, keep consuming done.
   181  				t++
   182  			} else {
   183  				// cmp == 0: covers to end, uncovered prefix already copied: pop both.
   184  				t++
   185  				d++
   186  			}
   187  		}
   188  	}
   189  	// Just append anything that's left.
   190  	if t < len(todo) {
   191  		remaining = append(remaining, todo[t:]...)
   192  	}
   193  	return remaining
   194  }