github.com/haraldrudell/parl@v0.4.176/prand/fastrand.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  // Package prand provides a fast and thread-safe random number generation.
     7  //   - prand.Uint32: 2 ns ±0.5
     8  //   - math/rand.Uint32: 14 ns ±0.5
     9  //   - /crypto/rand.Read: 330 ns ±0.5
    10  //   - same methods as math/rand package
    11  //   - based on runtime.fastrand
    12  package prand
    13  
    14  import (
    15  	"encoding/binary"
    16  	"unsafe"
    17  )
    18  
    19  const (
    20  	bitsPerByte  = 8
    21  	sizeOfUint32 = int(unsafe.Sizeof(uint32(1)))
    22  )
    23  
    24  // Uint32 returns a 32-bit unsigned random number using runtime.fastrand. Thread-safe
    25  func Uint32() (random uint32) {
    26  	return fastrand()
    27  }
    28  
    29  // Uint32n returns a 32-bit unsigned random number using runtime.fastrand. Thread-safe
    30  func Uint32n(n uint32) (random uint32) {
    31  	return fastrandn(n)
    32  }
    33  
    34  // Uint64 returns a 64-bit unsigned random number using runtime.fastrand. Thread-safe
    35  func Uint64() (random uint64) {
    36  	return uint64(fastrand())<<32 | uint64(fastrand())
    37  }
    38  
    39  // Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
    40  // It panics if n <= 0.
    41  func Int31n(n int32) (i32 int32) {
    42  	if n <= 0 {
    43  		panic("invalid argument to Int31n")
    44  	}
    45  	i32 = int32(fastrandn(uint32(n)))
    46  	return
    47  }
    48  
    49  // Read reads n random bytes into p. Thread-Safe.
    50  //   - n always len(p), err always nil
    51  func Read(p []byte) (n int, err error) {
    52  	n = len(p)
    53  
    54  	// randomize using 32-bit integers
    55  	index := 0
    56  	lengthMod4 := n &^ (sizeOfUint32 - 1)
    57  	for index < lengthMod4 {
    58  		binary.LittleEndian.PutUint32(p[index:], Uint32())
    59  		index += sizeOfUint32
    60  	}
    61  	if index == n {
    62  		return
    63  	}
    64  
    65  	// odd bytes at end
    66  	v := Uint32()
    67  	for index < n {
    68  		p[index] = byte(v)
    69  		index++
    70  		v >>= bitsPerByte
    71  	}
    72  
    73  	return
    74  }
    75  
    76  // Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
    77  func Int63() (random int64) { return int64(Uint64() >> 1) }
    78  
    79  // Int31 returns a non-negative pseudo-random 31-bit integer as an int32.
    80  func Int31() int32 { return int32(Uint32() >> 1) }