github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/spans.go (about)

     1  // Copyright 2018 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 constraint
    12  
    13  import (
    14  	"sort"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/errors"
    18  )
    19  
    20  // Spans is a collection of spans. There are no general requirements on the
    21  // contents of the spans in the structure; the caller has to make sure they make
    22  // sense in the respective context.
    23  type Spans struct {
    24  	// firstSpan holds the first span and otherSpans hold any spans beyond the
    25  	// first. These are separated in order to optimize for the common case of a
    26  	// single-span constraint.
    27  	firstSpan  Span
    28  	otherSpans []Span
    29  	numSpans   int32
    30  	immutable  bool
    31  }
    32  
    33  // Alloc allocates enough space to support the given amount of spans without
    34  // reallocation. Does nothing if the structure already contains spans.
    35  func (s *Spans) Alloc(capacity int) {
    36  	// We don't preallocate if the capacity is only 2: pre-allocating the slice to
    37  	// size 1 is no better than allocating it on the first Append, but it's worse
    38  	// if we end up not needing it.
    39  	if capacity > 2 && s.numSpans == 0 {
    40  		s.otherSpans = make([]Span, 0, capacity-1)
    41  	}
    42  }
    43  
    44  // InitSingleSpan initializes the structure with a single span.
    45  func (s *Spans) InitSingleSpan(sp *Span) {
    46  	s.firstSpan = *sp
    47  	s.otherSpans = nil
    48  	s.numSpans = 1
    49  	s.immutable = false
    50  }
    51  
    52  // Count returns the number of spans.
    53  func (s *Spans) Count() int {
    54  	return int(s.numSpans)
    55  }
    56  
    57  // Get returns the nth span.
    58  func (s *Spans) Get(nth int) *Span {
    59  	if nth == 0 && s.numSpans > 0 {
    60  		return &s.firstSpan
    61  	}
    62  	return &s.otherSpans[nth-1]
    63  }
    64  
    65  // Append adds another span (at the end).
    66  func (s *Spans) Append(sp *Span) {
    67  	if s.immutable {
    68  		panic(errors.AssertionFailedf("mutation disallowed"))
    69  	}
    70  	if s.numSpans == 0 {
    71  		s.firstSpan = *sp
    72  	} else {
    73  		s.otherSpans = append(s.otherSpans, *sp)
    74  	}
    75  	s.numSpans++
    76  }
    77  
    78  // Truncate removes all but the first newLength spans.
    79  func (s *Spans) Truncate(newLength int) {
    80  	if s.immutable {
    81  		panic(errors.AssertionFailedf("mutation disallowed"))
    82  	}
    83  	if int32(newLength) > s.numSpans {
    84  		panic(errors.AssertionFailedf("can't truncate to longer length"))
    85  	}
    86  	if newLength == 0 {
    87  		s.otherSpans = s.otherSpans[:0]
    88  	} else {
    89  		s.otherSpans = s.otherSpans[:newLength-1]
    90  	}
    91  	s.numSpans = int32(newLength)
    92  }
    93  
    94  func (s Spans) String() string {
    95  	var b strings.Builder
    96  	for i := 0; i < s.Count(); i++ {
    97  		if i > 0 {
    98  			b.WriteRune(' ')
    99  		}
   100  		b.WriteString(s.Get(i).String())
   101  	}
   102  	return b.String()
   103  }
   104  
   105  // makeImmutable causes panics in any future calls to methods that mutate either
   106  // the Spans structure or any Span returned by Get.
   107  func (s *Spans) makeImmutable() {
   108  	s.immutable = true
   109  }
   110  
   111  // sortedAndMerged returns true if the collection of spans is strictly
   112  // ordered and no spans overlap.
   113  func (s *Spans) sortedAndMerged(keyCtx *KeyContext) bool {
   114  	for i := 1; i < s.Count(); i++ {
   115  		if !s.Get(i).StartsStrictlyAfter(keyCtx, s.Get(i-1)) {
   116  			return false
   117  		}
   118  	}
   119  	return true
   120  }
   121  
   122  // SortAndMerge sorts the spans and merges any overlapping spans.
   123  func (s *Spans) SortAndMerge(keyCtx *KeyContext) {
   124  	if s.sortedAndMerged(keyCtx) {
   125  		return
   126  	}
   127  	sort.Sort(&spanSorter{keyCtx: *keyCtx, spans: s})
   128  
   129  	// Merge overlapping spans. We maintain the last span and extend it with
   130  	// whatever spans it overlaps with.
   131  	n := 0
   132  	currentSpan := *s.Get(0)
   133  	for i := 1; i < s.Count(); i++ {
   134  		sp := s.Get(i)
   135  		if sp.StartsStrictlyAfter(keyCtx, &currentSpan) {
   136  			// No overlap. "Output" the current span.
   137  			*s.Get(n) = currentSpan
   138  			n++
   139  			currentSpan = *sp
   140  		} else {
   141  			// There is overlap; extend the current span to the right if necessary.
   142  			if currentSpan.CompareEnds(keyCtx, sp) < 0 {
   143  				currentSpan.end = sp.end
   144  				currentSpan.endBoundary = sp.endBoundary
   145  			}
   146  		}
   147  	}
   148  	*s.Get(n) = currentSpan
   149  	s.Truncate(n + 1)
   150  }
   151  
   152  type spanSorter struct {
   153  	keyCtx KeyContext
   154  	spans  *Spans
   155  }
   156  
   157  var _ sort.Interface = &spanSorter{}
   158  
   159  // Len is part of sort.Interface.
   160  func (ss *spanSorter) Len() int {
   161  	return ss.spans.Count()
   162  }
   163  
   164  // Less is part of sort.Interface.
   165  func (ss *spanSorter) Less(i, j int) bool {
   166  	return ss.spans.Get(i).Compare(&ss.keyCtx, ss.spans.Get(j)) < 0
   167  }
   168  
   169  // Swap is part of sort.Interface.
   170  func (ss *spanSorter) Swap(i, j int) {
   171  	si := ss.spans.Get(i)
   172  	sj := ss.spans.Get(j)
   173  	*si, *sj = *sj, *si
   174  }