Skip to content

Instantly share code, notes, and snippets.

@klauspost
Created February 27, 2023 13:08
Show Gist options
  • Save klauspost/bc0bd8c7f6e81bd09f8d525d5d5910be to your computer and use it in GitHub Desktop.
Save klauspost/bc0bd8c7f6e81bd09f8d525d5d5910be to your computer and use it in GitHub Desktop.
Buffered Intn.
type rngBuffer struct {
bitsLeft int
buffer [32]byte
}
// Intn returns, as an int, a non-negative random number in the half-open interval [0,n).
// It panics if n <= 0.
func (r *rngBuffer) Intn(n int) int {
if n <= 0 {
panic("invalid argument to Intn")
}
need := bits.Len(uint(n))
if r.bitsLeft < need {
_, err := io.ReadFull(crand.Reader, r.buffer[:])
if err != nil {
panic(fmt.Sprintf("crypto/rand.Reader returned: %v", err))
}
r.bitsLeft = len(r.buffer) * 8
}
wIdx := 0
idx := len(r.buffer) - r.bitsLeft
r.bitsLeft -= need
var res uint64
for need > 0 {
readIdx := idx / 8
bitsUsed := idx & 7
val := r.buffer[readIdx]
if bitsUsed == 0 {
res = res | uint64(val)<<wIdx
need -= 8
wIdx += 8
idx += 8
} else {
val >>= bitsUsed
res = res | uint64(val)<<wIdx
wIdx += 8 - bitsUsed
idx += 8 - bitsUsed
need -= 8 - bitsUsed
}
}
return int(res) % n
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment