Skip to content

Commit

Permalink
math/rand/v2: remove Read
Browse files Browse the repository at this point in the history
In math/rand, Read is deprecated. Remove in v2.
People should use crypto/rand if they need long strings.

For #61716.

Change-Id: Ib254b7e1844616e96db60a3a7abb572b0dcb1583
Reviewed-on: https://go-review.googlesource.com/c/go/+/502497
Reviewed-by: Rob Pike <r@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Russ Cox <rsc@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
  • Loading branch information
rsc authored and pull[bot] committed Apr 11, 2024
1 parent 80de45f commit 21e6c63
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 265 deletions.
3 changes: 0 additions & 3 deletions api/next/61716.txt
Expand Up @@ -12,8 +12,6 @@ pkg math/rand/v2, func NewSource(int64) Source #61716
pkg math/rand/v2, func NewZipf(*Rand, float64, float64, uint64) *Zipf #61716
pkg math/rand/v2, func NormFloat64() float64 #61716
pkg math/rand/v2, func Perm(int) []int #61716
pkg math/rand/v2, func Read //deprecated #61716
pkg math/rand/v2, func Read([]uint8) (int, error) #61716
pkg math/rand/v2, func Seed //deprecated #61716
pkg math/rand/v2, func Seed(int64) #61716
pkg math/rand/v2, func Shuffle(int, func(int, int)) #61716
Expand All @@ -30,7 +28,6 @@ pkg math/rand/v2, method (*Rand) Int64N(int64) int64 #61716
pkg math/rand/v2, method (*Rand) IntN(int) int #61716
pkg math/rand/v2, method (*Rand) NormFloat64() float64 #61716
pkg math/rand/v2, method (*Rand) Perm(int) []int #61716
pkg math/rand/v2, method (*Rand) Read([]uint8) (int, error) #61716
pkg math/rand/v2, method (*Rand) Seed(int64) #61716
pkg math/rand/v2, method (*Rand) Shuffle(int, func(int, int)) #61716
pkg math/rand/v2, method (*Rand) Uint32() uint32 #61716
Expand Down
1 change: 0 additions & 1 deletion src/math/rand/v2/race_test.go
Expand Up @@ -38,7 +38,6 @@ func TestConcurrent(t *testing.T) {
for _, p := range Perm(10) {
seed += int64(p)
}
Read(buf)
for _, b := range buf {
seed += int64(b)
}
Expand Down
86 changes: 3 additions & 83 deletions src/math/rand/v2/rand.go
Expand Up @@ -62,15 +62,6 @@ func newSource(seed int64) *rngSource {
type Rand struct {
src Source
s64 Source64 // non-nil if src is source64

// readVal contains remainder of 63-bit integer used for bytes
// generation during most recent Read call.
// It is saved so next Read call can start where the previous
// one finished.
readVal int64
// readPos indicates the number of low-order bytes of readVal
// that are still valid.
readPos int8
}

// New returns a new Rand that uses random values from src
Expand All @@ -84,12 +75,10 @@ func New(src Source) *Rand {
// Seed should not be called concurrently with any other Rand method.
func (r *Rand) Seed(seed int64) {
if lk, ok := r.src.(*lockedSource); ok {
lk.seedPos(seed, &r.readPos)
lk.Seed(seed)
return
}

r.src.Seed(seed)
r.readPos = 0
}

// Int64 returns a non-negative pseudo-random 63-bit integer as an int64.
Expand Down Expand Up @@ -266,41 +255,6 @@ func (r *Rand) Shuffle(n int, swap func(i, j int)) {
}
}

// Read generates len(p) random bytes and writes them into p. It
// always returns len(p) and a nil error.
// Read should not be called concurrently with any other Rand method.
func (r *Rand) Read(p []byte) (n int, err error) {
switch src := r.src.(type) {
case *lockedSource:
return src.read(p, &r.readVal, &r.readPos)
case *fastSource:
return src.read(p, &r.readVal, &r.readPos)
}
return read(p, r.src, &r.readVal, &r.readPos)
}

func read(p []byte, src Source, readVal *int64, readPos *int8) (n int, err error) {
pos := *readPos
val := *readVal
rng, _ := src.(*rngSource)
for n = 0; n < len(p); n++ {
if pos == 0 {
if rng != nil {
val = rng.Int64()
} else {
val = src.Int64()
}
pos = 7
}
p[n] = byte(val)
val >>= 8
pos--
}
*readPos = pos
*readVal = val
return
}

/*
* Top-level convenience functions
*/
Expand Down Expand Up @@ -349,12 +303,8 @@ func globalRand() *Rand {
//go:linkname fastrand64
func fastrand64() uint64

// fastSource is an implementation of Source64 that uses the runtime
// fastrand functions.
type fastSource struct {
// The mutex is used to avoid race conditions in Read.
mu sync.Mutex
}
// fastSource is a Source that uses the runtime fastrand functions.
type fastSource struct{}

func (*fastSource) Int64() int64 {
return int64(fastrand64() & rngMask)
Expand All @@ -368,13 +318,6 @@ func (*fastSource) Uint64() uint64 {
return fastrand64()
}

func (fs *fastSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) {
fs.mu.Lock()
n, err = read(p, fs, readVal, readPos)
fs.mu.Unlock()
return
}

// Seed uses the provided seed value to initialize the default Source to a
// deterministic state. Seed values that have the same remainder when
// divided by 2³¹-1 generate the same pseudo-random sequence.
Expand Down Expand Up @@ -469,13 +412,6 @@ func Perm(n int) []int { return globalRand().Perm(n) }
// swap swaps the elements with indexes i and j.
func Shuffle(n int, swap func(i, j int)) { globalRand().Shuffle(n, swap) }

// Read generates len(p) random bytes from the default Source and
// writes them into p. It always returns len(p) and a nil error.
// Read, unlike the Rand.Read method, is safe for concurrent use.
//
// Deprecated: For almost all use cases, crypto/rand.Read is more appropriate.
func Read(p []byte) (n int, err error) { return globalRand().Read(p) }

// NormFloat64 returns a normally distributed float64 in the range
// [-math.MaxFloat64, +math.MaxFloat64] with
// standard normal distribution (mean = 0, stddev = 1)
Expand Down Expand Up @@ -520,14 +456,6 @@ func (r *lockedSource) Seed(seed int64) {
r.lk.Unlock()
}

// seedPos implements Seed for a lockedSource without a race condition.
func (r *lockedSource) seedPos(seed int64, readPos *int8) {
r.lk.Lock()
r.seed(seed)
*readPos = 0
r.lk.Unlock()
}

// seed seeds the underlying source.
// The caller must have locked r.lk.
func (r *lockedSource) seed(seed int64) {
Expand All @@ -537,11 +465,3 @@ func (r *lockedSource) seed(seed int64) {
r.s.Seed(seed)
}
}

// read implements Read for a lockedSource without a race condition.
func (r *lockedSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) {
r.lk.Lock()
n, err = read(p, r.s, readVal, readPos)
r.lk.Unlock()
return
}
118 changes: 0 additions & 118 deletions src/math/rand/v2/rand_test.go
Expand Up @@ -5,18 +5,15 @@
package rand_test

import (
"bytes"
"errors"
"fmt"
"internal/testenv"
"io"
"math"
. "math/rand/v2"
"os"
"runtime"
"sync"
"testing"
"testing/iotest"
)

const (
Expand Down Expand Up @@ -367,94 +364,6 @@ func TestFloat32(t *testing.T) {
}
}

func testReadUniformity(t *testing.T, n int, seed int64) {
r := New(NewSource(seed))
buf := make([]byte, n)
nRead, err := r.Read(buf)
if err != nil {
t.Errorf("Read err %v", err)
}
if nRead != n {
t.Errorf("Read returned unexpected n; %d != %d", nRead, n)
}

// Expect a uniform distribution of byte values, which lie in [0, 255].
var (
mean = 255.0 / 2
stddev = 256.0 / math.Sqrt(12.0)
errorScale = stddev / math.Sqrt(float64(n))
)

expected := &statsResults{mean, stddev, 0.10 * errorScale, 0.08 * errorScale}

// Cast bytes as floats to use the common distribution-validity checks.
samples := make([]float64, n)
for i, val := range buf {
samples[i] = float64(val)
}
// Make sure that the entire set matches the expected distribution.
checkSampleDistribution(t, samples, expected)
}

func TestReadUniformity(t *testing.T) {
testBufferSizes := []int{
2, 4, 7, 64, 1024, 1 << 16, 1 << 20,
}
for _, seed := range testSeeds {
for _, n := range testBufferSizes {
testReadUniformity(t, n, seed)
}
}
}

func TestReadEmpty(t *testing.T) {
r := New(NewSource(1))
buf := make([]byte, 0)
n, err := r.Read(buf)
if err != nil {
t.Errorf("Read err into empty buffer; %v", err)
}
if n != 0 {
t.Errorf("Read into empty buffer returned unexpected n of %d", n)
}
}

func TestReadByOneByte(t *testing.T) {
r := New(NewSource(1))
b1 := make([]byte, 100)
_, err := io.ReadFull(iotest.OneByteReader(r), b1)
if err != nil {
t.Errorf("read by one byte: %v", err)
}
r = New(NewSource(1))
b2 := make([]byte, 100)
_, err = r.Read(b2)
if err != nil {
t.Errorf("read: %v", err)
}
if !bytes.Equal(b1, b2) {
t.Errorf("read by one byte vs single read:\n%x\n%x", b1, b2)
}
}

func TestReadSeedReset(t *testing.T) {
r := New(NewSource(42))
b1 := make([]byte, 128)
_, err := r.Read(b1)
if err != nil {
t.Errorf("read: %v", err)
}
r.Seed(42)
b2 := make([]byte, 128)
_, err = r.Read(b2)
if err != nil {
t.Errorf("read: %v", err)
}
if !bytes.Equal(b1, b2) {
t.Errorf("mismatch after re-seed:\n%x\n%x", b1, b2)
}
}

func TestShuffleSmall(t *testing.T) {
// Check that Shuffle allows n=0 and n=1, but that swap is never called for them.
r := New(NewSource(1))
Expand Down Expand Up @@ -658,33 +567,6 @@ func BenchmarkShuffleOverhead(b *testing.B) {
}
}

func BenchmarkRead3(b *testing.B) {
r := New(NewSource(1))
buf := make([]byte, 3)
b.ResetTimer()
for n := b.N; n > 0; n-- {
r.Read(buf)
}
}

func BenchmarkRead64(b *testing.B) {
r := New(NewSource(1))
buf := make([]byte, 64)
b.ResetTimer()
for n := b.N; n > 0; n-- {
r.Read(buf)
}
}

func BenchmarkRead1000(b *testing.B) {
r := New(NewSource(1))
buf := make([]byte, 1000)
b.ResetTimer()
for n := b.N; n > 0; n-- {
r.Read(buf)
}
}

func BenchmarkConcurrent(b *testing.B) {
const goroutines = 4
var wg sync.WaitGroup
Expand Down

0 comments on commit 21e6c63

Please sign in to comment.