github.com/richardwilkes/toolbox@v1.121.0/xmath/rand/random.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  // Package rand provides a Randomizer based upon the crypto/rand package.
    11  package rand
    12  
    13  import (
    14  	"crypto/rand"
    15  	mrnd "math/rand/v2"
    16  )
    17  
    18  var cryptoRandInstance = &cryptoRand{}
    19  
    20  // Randomizer defines a source of random integer values.
    21  type Randomizer interface {
    22  	// Intn returns a non-negative random number from 0 to n-1. If n <= 0, the implementation should return 0.
    23  	Intn(n int) int
    24  }
    25  
    26  // NewCryptoRand returns a Randomizer based on the crypto/rand package. This method returns a shared singleton instance
    27  // and does not allocate.
    28  func NewCryptoRand() Randomizer {
    29  	return cryptoRandInstance
    30  }
    31  
    32  type cryptoRand struct{}
    33  
    34  func (r *cryptoRand) Intn(n int) int {
    35  	if n <= 0 {
    36  		return 0
    37  	}
    38  	var buffer [8]byte
    39  	size := 8
    40  	n64 := int64(n)
    41  	for i := 1; i < 8; i++ {
    42  		if n64 < int64(1)<<uint(i*8) {
    43  			size = i
    44  			break
    45  		}
    46  	}
    47  	if _, err := rand.Read(buffer[:size]); err != nil {
    48  		return mrnd.IntN(n) //nolint:gosec // Yes, it is ok to use a weak prng here
    49  	}
    50  	var v int
    51  	for i := size - 1; i >= 0; i-- {
    52  		v |= int(buffer[i]) << uint(i*8)
    53  	}
    54  	if v < 0 {
    55  		v = -v
    56  	}
    57  	return v % n
    58  }