File: rng.go

package info (click to toggle)
golang-gvisor-gvisor 0.0~20240729.0-6
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 21,280 kB
  • sloc: asm: 3,361; ansic: 1,197; cpp: 348; makefile: 92; python: 89; sh: 83
file content (131 lines) | stat: -rw-r--r-- 4,070 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package rand implements a cryptographically secure pseudorandom number
// generator.
package rand

import (
	"encoding/binary"
	"fmt"
	"io"
)

// RNG exposes convenience functions based on a cryptographically secure
// io.Reader.
type RNG struct {
	Reader io.Reader
}

// RNGFrom returns a new RNG. r must be a cryptographically secure io.Reader.
func RNGFrom(r io.Reader) RNG {
	return RNG{Reader: r}
}

// Uint16 is analogous to the standard library's math/rand.Uint16.
func (rg *RNG) Uint16() uint16 {
	var data [2]byte
	if _, err := rg.Reader.Read(data[:]); err != nil {
		panic(fmt.Sprintf("Read() failed: %v", err))
	}
	return binary.NativeEndian.Uint16(data[:])
}

// Uint32 is analogous to the standard library's math/rand.Uint32.
func (rg *RNG) Uint32() uint32 {
	var data [4]byte
	if _, err := rg.Reader.Read(data[:]); err != nil {
		panic(fmt.Sprintf("Read() failed: %v", err))
	}
	return binary.NativeEndian.Uint32(data[:])
}

// Int63n is analogous to the standard library's math/rand.Int63n.
func (rg *RNG) Int63n(n int64) int64 {
	// Based on Go's rand package implementation, but using
	// cryptographically secure random numbers.
	if n <= 0 {
		panic(fmt.Sprintf("n must be positive, but got %d", n))
	}

	// This can be done quickly when n is a power of 2.
	if n&(n-1) == 0 {
		return int64(rg.Uint64()) & (n - 1)
	}

	// The naive approach would be to return rg.Int63()%n, but we need the
	// random number to be fair. It shouldn't be biased towards certain
	// results, but simple modular math can be very biased. For example, if
	// n is 40% of the maximum int64, then the output values of rg.Int63
	// map to return values as follows:
	//
	//  - The first 40% of values map to themselves.
	//  - The second 40% map to themselves - maximum int64.
	//  - The remaining 20% map to the themselves - 2 * (maximum int64),
	//    i.e. the first half of possible output values.
	//
	// And thus 60% of results map the first half of possible output
	// values, and 40% map the second half. Oops!
	//
	// We use the same trick as Go to deal with this: shave off the last
	// segment (the 20% in our example) to make the RNG more fair.
	//
	// In the worst case, n is just over half of maximum int64, meaning
	// that the upper half of rg.Int63 return values are bad. So each call
	// to rg.Int63 has, at worst, a 50% chance of needing a retry.
	maximum := int64((1 << 63) - 1 - (1<<63)%uint64(n))
	ret := rg.Int63()
	for ret > maximum {
		ret = rg.Int63()
	}
	return ret % n
}

// Int63 is analogous to the standard library's math/rand.Int63.
func (rg *RNG) Int63() int64 {
	return ((1 << 63) - 1) & int64(rg.Uint64())
}

// Uint64 is analogous to the standard library's math/rand.Uint64.
func (rg *RNG) Uint64() uint64 {
	var data [8]byte
	if _, err := rg.Reader.Read(data[:]); err != nil {
		panic(fmt.Sprintf("Read() failed: %v", err))
	}
	return binary.NativeEndian.Uint64(data[:])
}

// Uint32 is analogous to the standard library's math/rand.Uint32.
func Uint32() uint32 {
	rng := RNG{Reader: Reader}
	return rng.Uint32()
}

// Int63n is analogous to the standard library's math/rand.Int63n.
func Int63n(n int64) int64 {
	rng := RNG{Reader: Reader}
	return rng.Int63n(n)
}

// Int63 is analogous to the standard library's math/rand.Int63.
func Int63() int64 {
	rng := RNG{Reader: Reader}
	return rng.Int63()
}

// Uint64 is analogous to the standard library's math/rand.Uint64.
func Uint64() uint64 {
	rng := RNG{Reader: Reader}
	return rng.Uint64()
}