Skip to content

Commit 8d966ba

Browse files
pinadg
authored andcommitted
math/rand: fix io.Reader implementation
Do not throw away the rest of Int63 value used for generation random bytes. Save it in Rand struct and re-use during the next Read call. Fixes #16124 Change-Id: Ic70bd80c3c3a6590e60ac615e8b3c2324589bea3 Reviewed-on: https://go-review.googlesource.com/24251 Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Joe Tsai <[email protected]> Run-TryBot: Joe Tsai <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 0ce100d commit 8d966ba

File tree

3 files changed

+84
-28
lines changed

3 files changed

+84
-28
lines changed

src/math/rand/rand.go

+26-8
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,26 @@ func NewSource(seed int64) Source {
3333
// A Rand is a source of random numbers.
3434
type Rand struct {
3535
src Source
36+
37+
// readVal contains remainder of 63-bit integer used for bytes
38+
// generation during most recent Read call.
39+
// It is saved so next Read call can start where the previous
40+
// one finished.
41+
readVal int64
42+
// readPos indicates the number of low-order bytes of readVal
43+
// that are still valid.
44+
readPos int8
3645
}
3746

3847
// New returns a new Rand that uses random values from src
3948
// to generate other random values.
40-
func New(src Source) *Rand { return &Rand{src} }
49+
func New(src Source) *Rand { return &Rand{src: src} }
4150

4251
// Seed uses the provided seed value to initialize the generator to a deterministic state.
43-
func (r *Rand) Seed(seed int64) { r.src.Seed(seed) }
52+
func (r *Rand) Seed(seed int64) {
53+
r.src.Seed(seed)
54+
r.readPos = 0
55+
}
4456

4557
// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
4658
func (r *Rand) Int63() int64 { return r.src.Int63() }
@@ -161,14 +173,20 @@ func (r *Rand) Perm(n int) []int {
161173
// Read generates len(p) random bytes and writes them into p. It
162174
// always returns len(p) and a nil error.
163175
func (r *Rand) Read(p []byte) (n int, err error) {
164-
for i := 0; i < len(p); i += 7 {
165-
val := r.src.Int63()
166-
for j := 0; i+j < len(p) && j < 7; j++ {
167-
p[i+j] = byte(val)
168-
val >>= 8
176+
pos := r.readPos
177+
val := r.readVal
178+
for n = 0; n < len(p); n++ {
179+
if pos == 0 {
180+
val = r.Int63()
181+
pos = 7
169182
}
183+
p[n] = byte(val)
184+
val >>= 8
185+
pos--
170186
}
171-
return len(p), nil
187+
r.readPos = pos
188+
r.readVal = val
189+
return
172190
}
173191

174192
/*

src/math/rand/rand_test.go

+39-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
package rand
66

77
import (
8+
"bytes"
89
"errors"
910
"fmt"
1011
"internal/testenv"
12+
"io"
1113
"math"
1214
"os"
1315
"runtime"
1416
"testing"
17+
"testing/iotest"
1518
)
1619

1720
const (
@@ -373,7 +376,7 @@ func testReadUniformity(t *testing.T, n int, seed int64) {
373376
checkSampleDistribution(t, samples, expected)
374377
}
375378

376-
func TestRead(t *testing.T) {
379+
func TestReadUniformity(t *testing.T) {
377380
testBufferSizes := []int{
378381
2, 4, 7, 64, 1024, 1 << 16, 1 << 20,
379382
}
@@ -394,7 +397,42 @@ func TestReadEmpty(t *testing.T) {
394397
if n != 0 {
395398
t.Errorf("Read into empty buffer returned unexpected n of %d", n)
396399
}
400+
}
401+
402+
func TestReadByOneByte(t *testing.T) {
403+
r := New(NewSource(1))
404+
b1 := make([]byte, 100)
405+
_, err := io.ReadFull(iotest.OneByteReader(r), b1)
406+
if err != nil {
407+
t.Errorf("read by one byte: %v", err)
408+
}
409+
r = New(NewSource(1))
410+
b2 := make([]byte, 100)
411+
_, err = r.Read(b2)
412+
if err != nil {
413+
t.Errorf("read: %v", err)
414+
}
415+
if !bytes.Equal(b1, b2) {
416+
t.Errorf("read by one byte vs single read:\n%x\n%x", b1, b2)
417+
}
418+
}
397419

420+
func TestReadSeedReset(t *testing.T) {
421+
r := New(NewSource(42))
422+
b1 := make([]byte, 128)
423+
_, err := r.Read(b1)
424+
if err != nil {
425+
t.Errorf("read: %v", err)
426+
}
427+
r.Seed(42)
428+
b2 := make([]byte, 128)
429+
_, err = r.Read(b2)
430+
if err != nil {
431+
t.Errorf("read: %v", err)
432+
}
433+
if !bytes.Equal(b1, b2) {
434+
t.Errorf("mismatch after re-seed:\n%x\n%x", b1, b2)
435+
}
398436
}
399437

400438
// Benchmarks

src/math/rand/regress_test.go

+19-19
Original file line numberDiff line numberDiff line change
@@ -342,25 +342,25 @@ var regressGolden = []interface{}{
342342
[]int{8, 7, 5, 3, 4, 6, 0, 1, 2}, // Perm(9)
343343
[]int{1, 0, 2, 5, 7, 6, 9, 8, 3, 4}, // Perm(10)
344344
[]byte{0x1}, // Read([0])
345-
[]byte{0xc0, 0x41, 0xd3, 0xff, 0x12, 0x4, 0x5b}, // Read([0 0 0 0 0 0 0])
346-
[]byte{0x73, 0xc8, 0x6e, 0x4f, 0xf9, 0x5f, 0xf6, 0x62}, // Read([0 0 0 0 0 0 0 0])
347-
[]byte{0x4a, 0x2d, 0xb, 0x75, 0xfb, 0x18, 0xd, 0xaf, 0x48}, // Read([0 0 0 0 0 0 0 0 0])
348-
[]byte{0x39, 0x46, 0x51, 0x85, 0xf, 0xd4, 0xa1, 0x78, 0x89, 0x2e}, // Read([0 0 0 0 0 0 0 0 0 0])
349-
[]byte{0x51}, // Read([0])
350-
[]byte{0x4e, 0xe2, 0xd3, 0xd0, 0xd0, 0xde, 0x6b}, // Read([0 0 0 0 0 0 0])
351-
[]byte{0xf8, 0xf9, 0xb4, 0x4c, 0xe8, 0x5f, 0xf0, 0x44}, // Read([0 0 0 0 0 0 0 0])
352-
[]byte{0x3b, 0xbf, 0x85, 0x7a, 0xab, 0x99, 0xc5, 0xb2, 0x52}, // Read([0 0 0 0 0 0 0 0 0])
353-
[]byte{0xa8, 0xae, 0xb7, 0x9e, 0xf8, 0x56, 0xf6, 0x59, 0xc1, 0x8f}, // Read([0 0 0 0 0 0 0 0 0 0])
354-
[]byte{0xc7}, // Read([0])
355-
[]byte{0x5f, 0x67, 0xcf, 0xe2, 0x42, 0xcf, 0x3c}, // Read([0 0 0 0 0 0 0])
356-
[]byte{0xc3, 0x54, 0xf3, 0xed, 0xe2, 0xd6, 0xbe, 0xcc}, // Read([0 0 0 0 0 0 0 0])
357-
[]byte{0x6a, 0x9f, 0x4a, 0x57, 0x8b, 0xcb, 0x9e, 0xf2, 0xd4}, // Read([0 0 0 0 0 0 0 0 0])
358-
[]byte{0x6d, 0x29, 0x97, 0x61, 0xea, 0x9e, 0x4f, 0x5a, 0xa6, 0xae}, // Read([0 0 0 0 0 0 0 0 0 0])
359-
[]byte{0xaa}, // Read([0])
360-
[]byte{0x20, 0xef, 0xcd, 0x6c, 0xea, 0x84, 0xb6}, // Read([0 0 0 0 0 0 0])
361-
[]byte{0x92, 0x5e, 0x60, 0x7b, 0xe0, 0x63, 0x71, 0x6f}, // Read([0 0 0 0 0 0 0 0])
362-
[]byte{0x4, 0x5c, 0x3f, 0x0, 0xf, 0x8a, 0x79, 0x6b, 0xce}, // Read([0 0 0 0 0 0 0 0 0])
363-
[]byte{0xaa, 0xca, 0xee, 0xdf, 0xad, 0x5b, 0x50, 0x66, 0x64, 0xe8}, // Read([0 0 0 0 0 0 0 0 0 0])
345+
[]byte{0x94, 0xfd, 0xc2, 0xfa, 0x2f, 0xfc, 0xc0}, // Read([0 0 0 0 0 0 0])
346+
[]byte{0x41, 0xd3, 0xff, 0x12, 0x4, 0x5b, 0x73, 0xc8}, // Read([0 0 0 0 0 0 0 0])
347+
[]byte{0x6e, 0x4f, 0xf9, 0x5f, 0xf6, 0x62, 0xa5, 0xee, 0xe8}, // Read([0 0 0 0 0 0 0 0 0])
348+
[]byte{0x2a, 0xbd, 0xf4, 0x4a, 0x2d, 0xb, 0x75, 0xfb, 0x18, 0xd}, // Read([0 0 0 0 0 0 0 0 0 0])
349+
[]byte{0xaf}, // Read([0])
350+
[]byte{0x48, 0xa7, 0x9e, 0xe0, 0xb1, 0xd, 0x39}, // Read([0 0 0 0 0 0 0])
351+
[]byte{0x46, 0x51, 0x85, 0xf, 0xd4, 0xa1, 0x78, 0x89}, // Read([0 0 0 0 0 0 0 0])
352+
[]byte{0x2e, 0xe2, 0x85, 0xec, 0xe1, 0x51, 0x14, 0x55, 0x78}, // Read([0 0 0 0 0 0 0 0 0])
353+
[]byte{0x8, 0x75, 0xd6, 0x4e, 0xe2, 0xd3, 0xd0, 0xd0, 0xde, 0x6b}, // Read([0 0 0 0 0 0 0 0 0 0])
354+
[]byte{0xf8}, // Read([0])
355+
[]byte{0xf9, 0xb4, 0x4c, 0xe8, 0x5f, 0xf0, 0x44}, // Read([0 0 0 0 0 0 0])
356+
[]byte{0xc6, 0xb1, 0xf8, 0x3b, 0x8e, 0x88, 0x3b, 0xbf}, // Read([0 0 0 0 0 0 0 0])
357+
[]byte{0x85, 0x7a, 0xab, 0x99, 0xc5, 0xb2, 0x52, 0xc7, 0x42}, // Read([0 0 0 0 0 0 0 0 0])
358+
[]byte{0x9c, 0x32, 0xf3, 0xa8, 0xae, 0xb7, 0x9e, 0xf8, 0x56, 0xf6}, // Read([0 0 0 0 0 0 0 0 0 0])
359+
[]byte{0x59}, // Read([0])
360+
[]byte{0xc1, 0x8f, 0xd, 0xce, 0xcc, 0x77, 0xc7}, // Read([0 0 0 0 0 0 0])
361+
[]byte{0x5e, 0x7a, 0x81, 0xbf, 0xde, 0x27, 0x5f, 0x67}, // Read([0 0 0 0 0 0 0 0])
362+
[]byte{0xcf, 0xe2, 0x42, 0xcf, 0x3c, 0xc3, 0x54, 0xf3, 0xed}, // Read([0 0 0 0 0 0 0 0 0])
363+
[]byte{0xe2, 0xd6, 0xbe, 0xcc, 0x4e, 0xa3, 0xae, 0x5e, 0x88, 0x52}, // Read([0 0 0 0 0 0 0 0 0 0])
364364
uint32(4059586549), // Uint32()
365365
uint32(1052117029), // Uint32()
366366
uint32(2817310706), // Uint32()

0 commit comments

Comments
 (0)