github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/list/x_skl_rand.go (about)

     1  package list
     2  
     3  // References:
     4  // https://gitee.com/bombel/cdf_skiplist
     5  // http://snap.stanford.edu/data/index.html
     6  //
     7  // Goland math random (math.Float64()) contains global mutex lock
     8  // Ref
     9  // https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/math/rand/rand.go
    10  // https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/math/bits/bits.go
    11  // 1. Avoid using global mutex lock
    12  // 2. Avoid generating random number each time
    13  
    14  import (
    15  	crand "crypto/rand"
    16  	"encoding/binary"
    17  	"math"
    18  	"math/bits"
    19  	randv2 "math/rand/v2"
    20  )
    21  
    22  type SklRand func(maxLevel int, currentElements int64) int32
    23  
    24  func maxLevels(totalElements int64, P float64) int {
    25  	// Ref https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf
    26  	// maxLevels = log(1/P) * log(totalElements)
    27  	// P = 1/4, totalElements = 2^32 - 1
    28  	if totalElements <= 0 {
    29  		return 0
    30  	}
    31  	return int(math.Ceil(math.Log(1/P) * math.Log(float64(totalElements))))
    32  }
    33  
    34  func randomLevel(maxLevel int, currentElements int32) int32 {
    35  	level := 1
    36  	for float64(randv2.Int64()&0xFFFF) < sklProbability*0xFFFF {
    37  		level += 1
    38  	}
    39  	if level < sklMaxLevel {
    40  		return int32(level)
    41  	}
    42  	return sklMaxLevel
    43  }
    44  
    45  // randomLevelV2 is the skip list level element.
    46  // Dynamic level calculation.
    47  func randomLevelV2(maxLevel int, currentElements int64) int32 {
    48  	// Call function maxLevels to get total?
    49  	// maxLevel => n, 2^n-1, there will be 2^n-1 elements in the skip list
    50  	var total uint64
    51  	if maxLevel == sklMaxLevel {
    52  		total = uint64(math.MaxUint32)
    53  	} else {
    54  		total = uint64(1)<<maxLevel - 1
    55  	}
    56  	// Bits right shift equal to manipulate a high-level bit
    57  	// Calculate the minimum bits of the random number
    58  	// Lookup table.
    59  	level := maxLevel - bits.Len64(randv2.Uint64()&total) + 1
    60  	// Avoid the value of randomly generated level deviates
    61  	// far from the number of elements within the skip-list.
    62  	// Level should be greater than but approximate to log(currentElements)
    63  	for level > 3 && uint64(1)<<(level-3) > uint64(currentElements) {
    64  		level--
    65  	}
    66  	return int32(level)
    67  }
    68  
    69  // randomLevelV3 is the skip list level element.
    70  // Dynamic level calculation.
    71  // Concurrency safe.
    72  func randomLevelV3(maxLevel int, currentElements int64) int32 {
    73  	// Call function maxLevels to get total?
    74  	// maxLevel => n, 2^n-1, there will be 2^n-1 elements in the skip list
    75  	var total uint64
    76  	if maxLevel == sklMaxLevel {
    77  		total = uint64(math.MaxUint32)
    78  	} else {
    79  		total = uint64(1)<<maxLevel - 1
    80  	}
    81  
    82  	num := cryptoRandUint64()
    83  	rest := num & total
    84  	// Bits right shift equal to manipulate a high-level bit
    85  	// Calculate the minimum bits of the random number
    86  	tmp := bits.Len64(rest) // Lookup table.
    87  	level := maxLevel - tmp + 1
    88  	// Avoid the value of randomly generated level deviates
    89  	// far from the number of elements within the skip-list.
    90  	// level should be greater than but approximate to log(currentElements)
    91  	for level > 3 && uint64(1)<<(level-3) > uint64(currentElements) {
    92  		level--
    93  	}
    94  	return int32(level)
    95  }
    96  
    97  func cryptoRandUint64() uint64 {
    98  	randUint64 := [8]byte{}
    99  	if _, err := crand.Read(randUint64[:]); err != nil {
   100  		panic(err)
   101  	}
   102  	if randUint64[7]&0x8 == 0x0 {
   103  		return binary.LittleEndian.Uint64(randUint64[:])
   104  	}
   105  	return binary.BigEndian.Uint64(randUint64[:])
   106  }
   107  
   108  func cryptoRandUint32() uint32 {
   109  	randUint32 := [4]byte{}
   110  	if _, err := crand.Read(randUint32[:]); err != nil {
   111  		panic(err)
   112  	}
   113  	if randUint32[3]&0x8 == 0x0 {
   114  		return binary.LittleEndian.Uint32(randUint32[:])
   115  	}
   116  	return binary.BigEndian.Uint32(randUint32[:])
   117  }