github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/collection/zset/skiplist.go (about)

     1  // Copyright 2021 ByteDance 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  package zset
    15  
    16  import (
    17  	"math"
    18  	"unsafe"
    19  
    20  	"github.com/bytedance/gopkg/lang/fastrand"
    21  )
    22  
    23  //
    24  // Skip list implementation.
    25  //
    26  
    27  const (
    28  	maxLevel    = 32   // same to ZSKIPLIST_MAXLEVEL, should be enough for 2^64 elements
    29  	probability = 0.25 // same to ZSKIPLIST_P, 1/4
    30  )
    31  
    32  // float64ListNode is node of float64List.
    33  type float64ListNode struct {
    34  	score float64 // key for sorting, which is allowed to be repeated
    35  	value string
    36  	prev  *float64ListNode // back pointer that only available at level 1
    37  	level int              // the length of optionalArray
    38  	oparr optionalArray
    39  }
    40  
    41  func newFloat64ListNode(score float64, value string, level int) *float64ListNode {
    42  	node := &float64ListNode{
    43  		score: score,
    44  		value: value,
    45  		level: level,
    46  	}
    47  	node.oparr.init(level)
    48  	return node
    49  }
    50  
    51  func (n *float64ListNode) loadNext(i int) *float64ListNode {
    52  	return (*float64ListNode)(n.oparr.loadNext(i))
    53  }
    54  
    55  func (n *float64ListNode) storeNext(i int, node *float64ListNode) {
    56  	n.oparr.storeNext(i, unsafe.Pointer(node))
    57  }
    58  
    59  func (n *float64ListNode) loadSpan(i int) int {
    60  	return n.oparr.loadSpan(i)
    61  }
    62  
    63  func (n *float64ListNode) storeSpan(i int, span int) {
    64  	n.oparr.storeSpan(i, span)
    65  }
    66  
    67  func (n *float64ListNode) loadNextAndSpan(i int) (*float64ListNode, int) {
    68  	return n.loadNext(i), n.loadSpan(i)
    69  }
    70  
    71  func (n *float64ListNode) storeNextAndSpan(i int, next *float64ListNode, span int) {
    72  	n.storeNext(i, next)
    73  	n.storeSpan(i, span)
    74  }
    75  
    76  func (n *float64ListNode) lessThan(score float64, value string) bool {
    77  	if n.score < score {
    78  		return true
    79  	} else if n.score == score {
    80  		return n.value < value
    81  	}
    82  	return false
    83  }
    84  
    85  func (n *float64ListNode) lessEqual(score float64, value string) bool {
    86  	if n.score < score {
    87  		return true
    88  	} else if n.score == score {
    89  		return n.value <= value
    90  	}
    91  	return false
    92  }
    93  
    94  func (n *float64ListNode) equal(score float64, value string) bool {
    95  	return n.value == value && n.score == score
    96  }
    97  
    98  // float64List is a specialized skip list implementation for sorted set.
    99  //
   100  // It is almost implement the original
   101  // algorithm described by William Pugh in " Lists: A Probabilistic
   102  // Alternative to Balanced Trees", modified in three ways:
   103  // a) this implementation allows for repeated scores.
   104  // b) the comparison is not just by key (our 'score') but by satellite data(?).
   105  // c) there is a back pointer, so it's a doubly linked list with the back
   106  // pointers being only at "level 1". This allows to traverse the list
   107  // from tail to head, useful for RevRange.
   108  type float64List struct {
   109  	header       *float64ListNode
   110  	tail         *float64ListNode
   111  	length       int
   112  	highestLevel int // highest level for now
   113  }
   114  
   115  func newFloat64List() *float64List {
   116  	l := &float64List{
   117  		header:       newFloat64ListNode(-math.MaxFloat64, "__HEADER", maxLevel), // FIXME:
   118  		highestLevel: 1,
   119  	}
   120  	return l
   121  }
   122  
   123  // Insert inserts a new node in the skiplist. Assumes the element does not already
   124  // exist (up to the caller to enforce that).
   125  func (l *float64List) Insert(score float64, value string) *float64ListNode {
   126  	var (
   127  		update [maxLevel]*float64ListNode
   128  		rank   [maxLevel + 1]int // +1 for eliminating a boundary judgment
   129  	)
   130  
   131  	x := l.header
   132  	for i := l.highestLevel - 1; i >= 0; i-- {
   133  		rank[i] = rank[i+1] // also fine when i == maxLevel - 1
   134  		next := x.loadNext(i)
   135  		for next != nil && next.lessThan(score, value) {
   136  			rank[i] += x.loadSpan(i)
   137  			x = next
   138  			next = x.loadNext(i)
   139  		}
   140  		update[i] = x
   141  	}
   142  
   143  	// We assume the element is not already inside, since we allow duplicated
   144  	// scores, reinserting the same element should never happen since the
   145  	// caller of Add() should test in the hash table if the element is
   146  	// already inside or not.
   147  	level := l.randomLevel()
   148  	if level > l.highestLevel {
   149  		// Create higher levels.
   150  		for i := l.highestLevel; i < level; i++ {
   151  			rank[i] = 0
   152  			update[i] = l.header
   153  			update[i].storeSpan(i, l.length)
   154  		}
   155  		l.highestLevel = level
   156  	}
   157  	x = newFloat64ListNode(score, value, level)
   158  	for i := 0; i < level; i++ {
   159  		// update --> x --> update.next
   160  		x.storeNext(i, update[i].loadNext(i))
   161  		update[i].storeNext(i, x)
   162  		// update[i].span is splitted to: new update[i].span and x.span
   163  		x.storeSpan(i, update[i].loadSpan(i)-(rank[0]-rank[i]))
   164  		update[i].storeSpan(i, (rank[0]-rank[i])+1)
   165  	}
   166  	// Increment span for untouched levels.
   167  	for i := level; i < l.highestLevel; i++ {
   168  		update[i].storeSpan(i, update[i].loadSpan(i)+1)
   169  	}
   170  
   171  	// Update back pointer.
   172  	if update[0] != l.header {
   173  		x.prev = update[0]
   174  	}
   175  
   176  	if next := x.loadNext(0); next != nil { // not tail of skiplist
   177  		next.prev = x
   178  	} else {
   179  		l.tail = x
   180  	}
   181  	l.length++
   182  
   183  	return x
   184  }
   185  
   186  // randomLevel returns a level between [1, maxLevel] for insertion.
   187  func (l *float64List) randomLevel() int {
   188  	level := 1
   189  	for fastrand.Uint32n(1/probability) == 0 {
   190  		level++
   191  	}
   192  	if level > maxLevel {
   193  		return maxLevel
   194  	}
   195  	return level
   196  }
   197  
   198  // Rank finds the rank for an element by both score and value.
   199  // Returns 0 when the element cannot be found, rank otherwise.
   200  //
   201  // NOTE: the rank is 1-based due to the span of l->header to the
   202  // first element.
   203  func (l *float64List) Rank(score float64, value string) int {
   204  	rank := 0
   205  	x := l.header
   206  	for i := l.highestLevel - 1; i >= 0; i-- {
   207  		next := x.loadNext(i)
   208  		for next != nil && next.lessEqual(score, value) {
   209  			rank += x.loadSpan(i)
   210  			x = next
   211  			next = x.loadNext(i)
   212  		}
   213  
   214  		// x might be equal to l->header, so test if obj is non-nil
   215  		// TODO: Why not use if x != l.header?
   216  		if x.equal(score, value) {
   217  			return rank
   218  		}
   219  	}
   220  	return 0
   221  }
   222  
   223  // deleteNode is a internal function for deleting node x in O(1) time by giving a
   224  // update position matrix.
   225  func (l *float64List) deleteNode(x *float64ListNode, update *[maxLevel]*float64ListNode) {
   226  	for i := 0; i < l.highestLevel; i++ {
   227  		if update[i].loadNext(i) == x {
   228  			// Remove x, updaet[i].span = updaet[i].span + x.span - 1 (x removed).
   229  			next, span := x.loadNextAndSpan(i)
   230  			span += update[i].loadSpan(i) - 1
   231  			update[i].storeNextAndSpan(i, next, span)
   232  		} else {
   233  			// x does not appear on this level, just update span.
   234  			update[i].storeSpan(i, update[i].loadSpan(i)-1)
   235  		}
   236  	}
   237  	if next := x.loadNext(0); next != nil { // not tail of skiplist
   238  		next.prev = x.prev
   239  	} else {
   240  		l.tail = x.prev
   241  	}
   242  	for l.highestLevel > 1 && l.header.loadNext(l.highestLevel-1) != nil {
   243  		// Clear the pointer and span for safety.
   244  		l.header.storeNextAndSpan(l.highestLevel-1, nil, 0)
   245  		l.highestLevel--
   246  	}
   247  	l.length--
   248  }
   249  
   250  // Delete deletes an element with matching score/element from the skiplist.
   251  // The deleted node is returned if the node was found, otherwise 0 is returned.
   252  func (l *float64List) Delete(score float64, value string) *float64ListNode {
   253  	var update [maxLevel]*float64ListNode
   254  
   255  	x := l.header
   256  	for i := l.highestLevel - 1; i >= 0; i-- {
   257  		next := x.loadNext(i)
   258  		for next != nil && next.lessThan(score, value) {
   259  			x = next
   260  			next = x.loadNext(i)
   261  		}
   262  		update[i] = x
   263  	}
   264  	x = x.loadNext(0)
   265  	if x != nil && x.equal(score, value) {
   266  		l.deleteNode(x, &update)
   267  		return x
   268  	}
   269  	return nil // not found
   270  }
   271  
   272  // UpdateScore updates the score of an element inside the sorted set skiplist.
   273  //
   274  // NOTE: the element must exist and must match 'score'.
   275  // This function does not update the score in the hash table side, the
   276  // caller should take care of it.
   277  //
   278  // NOTE: this function attempts to just update the node, in case after
   279  // the score update, the node would be exactly at the same position.
   280  // Otherwise the skiplist is modified by removing and re-adding a new
   281  // element, which is more costly.
   282  //
   283  // The function returns the updated element skiplist node pointer.
   284  func (l *float64List) UpdateScore(oldScore float64, value string, newScore float64) *float64ListNode {
   285  	var update [maxLevel]*float64ListNode
   286  
   287  	x := l.header
   288  	for i := l.highestLevel - 1; i >= 0; i-- {
   289  		next := x.loadNext(i)
   290  		for next != nil && next.lessThan(oldScore, value) {
   291  			x = next
   292  			next = x.loadNext(i)
   293  		}
   294  		update[i] = x
   295  	}
   296  
   297  	// Jump to our element: note that this function assumes that the
   298  	// element with the matching score exists.
   299  	x = x.loadNext(0)
   300  
   301  	// Fastpath: If the node, after the score update, would be still exactly
   302  	// at the same position, we can just update the score without
   303  	// actually removing and re-inserting the element in the skiplist.
   304  	if next := x.loadNext(0); (x.prev == nil || x.prev.score < newScore) &&
   305  		(next == nil || next.score > newScore) {
   306  		x.score = newScore
   307  		return x
   308  	}
   309  
   310  	// No way to reuse the old node: we need to remove and insert a new
   311  	// one at a different place.
   312  	v := x.value
   313  	l.deleteNode(x, &update)
   314  	newNode := l.Insert(newScore, v)
   315  	return newNode
   316  }
   317  
   318  func greaterThanMin(value float64, min float64, ex bool) bool {
   319  	if ex {
   320  		return value > min
   321  	} else {
   322  		return value >= min
   323  	}
   324  }
   325  
   326  func lessThanMax(value float64, max float64, ex bool) bool {
   327  	if ex {
   328  		return value < max
   329  	} else {
   330  		return value <= max
   331  	}
   332  }
   333  
   334  // DeleteRangeByScore deletes all the elements with score between min and max
   335  // from the skiplist.
   336  // Both min and max can be inclusive or exclusive (see RangeOpt).
   337  // When inclusive a score >= min && score <= max is deleted.
   338  //
   339  // This function returns count of deleted elements.
   340  func (l *float64List) DeleteRangeByScore(min, max float64, opt RangeOpt, dict map[string]float64) []Float64Node {
   341  	var (
   342  		update  [maxLevel]*float64ListNode
   343  		removed []Float64Node
   344  	)
   345  
   346  	x := l.header
   347  	for i := l.highestLevel - 1; i >= 0; i-- {
   348  		next := x.loadNext(i)
   349  		for next != nil && !greaterThanMin(next.score, min, opt.ExcludeMin) {
   350  			x = next
   351  			next = x.loadNext(i)
   352  		}
   353  		update[i] = x
   354  	}
   355  
   356  	// Current node is the last with score not greater than min.
   357  	x = x.loadNext(0)
   358  
   359  	// Delete nodes in range.
   360  	for x != nil && lessThanMax(x.score, max, opt.ExcludeMax) {
   361  		next := x.loadNext(0)
   362  		l.deleteNode(x, &update)
   363  		delete(dict, x.value)
   364  		removed = append(removed, Float64Node{
   365  			Value: x.value,
   366  			Score: x.score,
   367  		})
   368  		x = next
   369  	}
   370  
   371  	return removed
   372  }
   373  
   374  // Delete all the elements with rank between start and end from the skiplist.
   375  // Start and end are inclusive.
   376  //
   377  // NOTE: start and end need to be 1-based
   378  func (l *float64List) DeleteRangeByRank(start, end int, dict map[string]float64) []Float64Node {
   379  	var (
   380  		update    [maxLevel]*float64ListNode
   381  		removed   []Float64Node
   382  		traversed int
   383  	)
   384  
   385  	x := l.header
   386  	for i := l.highestLevel - 1; i >= 0; i-- {
   387  		next, span := x.loadNextAndSpan(i)
   388  		for next != nil && traversed+span < start {
   389  			traversed += span
   390  			x = next
   391  			next, span = x.loadNextAndSpan(i)
   392  		}
   393  		update[i] = x
   394  	}
   395  
   396  	traversed++
   397  	x = x.loadNext(0)
   398  	// Delete nodes in range.
   399  	for x != nil && traversed <= end {
   400  		next := x.loadNext(0)
   401  		l.deleteNode(x, &update)
   402  		delete(dict, x.value)
   403  		removed = append(removed, Float64Node{
   404  			Value: x.value,
   405  			Score: x.score,
   406  		})
   407  		traversed++
   408  		x = next
   409  	}
   410  	return removed
   411  }
   412  
   413  // GetNodeByRank finds an element by its rank. The rank argument needs to be 1-based.
   414  func (l *float64List) GetNodeByRank(rank int) *float64ListNode {
   415  	var traversed int
   416  
   417  	x := l.header
   418  	for i := l.highestLevel - 1; i >= 0; i-- {
   419  		next, span := x.loadNextAndSpan(i)
   420  		for next != nil && traversed+span <= rank {
   421  			traversed += span
   422  			x = next
   423  			next, span = x.loadNextAndSpan(i)
   424  		}
   425  		if traversed == rank {
   426  			return x
   427  		}
   428  	}
   429  	return nil
   430  }
   431  
   432  // FirstInRange finds the first node that is contained in the specified range.
   433  func (l *float64List) FirstInRange(min, max float64, opt RangeOpt) *float64ListNode {
   434  	if !l.IsInRange(min, max, opt) {
   435  		return nil
   436  	}
   437  
   438  	x := l.header
   439  	for i := l.highestLevel - 1; i >= 0; i-- {
   440  		next := x.loadNext(i)
   441  		for next != nil && !greaterThanMin(next.score, min, opt.ExcludeMin) {
   442  			x = next
   443  			next = x.loadNext(i)
   444  		}
   445  	}
   446  
   447  	// The next node MUST not be NULL (excluded by IsInRange).
   448  	x = x.loadNext(0)
   449  	if !lessThanMax(x.score, max, opt.ExcludeMax) {
   450  		return nil
   451  	}
   452  	return x
   453  }
   454  
   455  // LastInRange finds the last node that is contained in the specified range.
   456  func (l *float64List) LastInRange(min, max float64, opt RangeOpt) *float64ListNode {
   457  	if !l.IsInRange(min, max, opt) {
   458  		return nil
   459  	}
   460  
   461  	x := l.header
   462  	for i := l.highestLevel - 1; i >= 0; i-- {
   463  		next := x.loadNext(i)
   464  		for next != nil && lessThanMax(next.score, max, opt.ExcludeMax) {
   465  			x = next
   466  			next = x.loadNext(i)
   467  		}
   468  	}
   469  
   470  	// The node x must not be NULL (excluded by IsInRange).
   471  	if !greaterThanMin(x.score, min, opt.ExcludeMin) {
   472  		return nil
   473  	}
   474  	return x
   475  }
   476  
   477  // IsInRange returns whether there is a port of sorted set in given range.
   478  func (l *float64List) IsInRange(min, max float64, opt RangeOpt) bool {
   479  	// Test empty range.
   480  	if min > max || (min == max && (opt.ExcludeMin || opt.ExcludeMax)) {
   481  		return false
   482  	}
   483  	if l.tail == nil || !greaterThanMin(l.tail.score, min, opt.ExcludeMin) {
   484  		return false
   485  	}
   486  	if next := l.header.loadNext(0); next == nil || !lessThanMax(next.score, max, opt.ExcludeMax) {
   487  		return false
   488  	}
   489  	return true
   490  }