add function B for package grand; improve package grand

This commit is contained in:
john 2020-05-16 16:05:31 +08:00
parent bd27258c46
commit 4164059211
5 changed files with 122 additions and 86 deletions

View File

@ -8,6 +8,7 @@
package grand
import (
"encoding/binary"
"unsafe"
)
@ -28,6 +29,23 @@ func MeetProb(prob float32) bool {
return Intn(1e7) < int(prob*1e7)
}
// B retrieves and returns random bytes of given length <n>.
func B(n int) []byte {
if n <= 0 {
return nil
}
i := 0
b := make([]byte, n)
for {
copy(b[i:], <-bufferChan)
i += 4
if i >= n {
break
}
}
return b
}
// N returns a random int between min and max: [min, max].
// The <min> and <max> also support negative numbers.
func N(min, max int) int {
@ -115,3 +133,20 @@ func Perm(n int) []int {
}
return m
}
// Intn returns a int number which is between 0 and max: [0, max).
//
// Note that:
// 1. The <max> can only be greater than 0, or else it returns <max> directly;
// 2. The result is greater than or equal to 0, but less than <max>;
// 3. The result number is 32bit and less than math.MaxUint32.
func Intn(max int) int {
if max <= 0 {
return max
}
n := int(binary.LittleEndian.Uint32(<-bufferChan)) % max
if (max > 0 && n < 0) || (max < 0 && n > 0) {
return -n
}
return n
}

View File

@ -0,0 +1,62 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package grand
import (
"crypto/rand"
)
const (
// Buffer size for uint32 random number.
gBUFFER_SIZE = 10000
)
var (
// bufferChan is the buffer for random bytes,
// every item storing 4 bytes.
bufferChan = make(chan []byte, gBUFFER_SIZE)
)
func init() {
go asyncProducingRandomBufferBytesLoop()
}
// asyncProducingRandomBufferBytes is a named goroutine, which uses a asynchronous goroutine
// to produce the random bytes, and a buffer chan to store the random bytes.
// So it has high performance to generate random numbers.
func asyncProducingRandomBufferBytesLoop() {
var (
step = 0
buffer = make([]byte, 1024)
)
for {
if n, err := rand.Read(buffer); err != nil {
panic(err)
} else {
for i := 0; i < n-4; {
bufferChan <- buffer[i : i+4]
i++
}
// Reuse the rand buffer.
for i := 0; i < n; i++ {
step = int(buffer[0]) % 10
if step != 0 {
break
}
}
// The step cannot be 0,
// as it will produce the same random number as previous.
if step == 0 {
step = 2
}
for i := 0; i < n-4; {
bufferChan <- buffer[i : i+4]
i += step
}
}
}
}

View File

@ -1,77 +0,0 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package grand
import (
"crypto/rand"
"encoding/binary"
)
const (
// Buffer size for uint32 random number.
gBUFFER_SIZE = 10000
)
var (
// Buffer chan.
bufferChan = make(chan uint32, gBUFFER_SIZE)
)
// It uses a asynchronous goroutine to produce the random number,
// and a buffer chan to store the random numbers.
// So it has high performance to generate random numbers.
func init() {
var (
step = 0
buffer = make([]byte, 1024)
)
go func() {
for {
if n, err := rand.Read(buffer); err != nil {
panic(err)
} else {
for i := 0; i < n-4; {
bufferChan <- binary.LittleEndian.Uint32(buffer[i : i+4])
i++
}
// Reuse the rand buffer.
for i := 0; i < n; i++ {
step = int(buffer[0]) % 10
if step != 0 {
break
}
}
// The step cannot be 0,
// as it will produce the same random number as previous.
if step == 0 {
step = 2
}
for i := 0; i < n-4; {
bufferChan <- binary.BigEndian.Uint32(buffer[i : i+4])
i += step
}
}
}
}()
}
// Intn returns a int number which is between 0 and max: [0, max).
//
// Note that:
// 1. The <max> can only be greater than 0, or else it returns <max> directly;
// 2. The result is greater than or equal to 0, but less than <max>;
// 3. The result number is 32bit and less than math.MaxUint32.
func Intn(max int) int {
if max <= 0 {
return max
}
n := int(<-bufferChan) % max
if (max > 0 && n < 0) || (max < 0 && n > 0) {
return -n
}
return n
}

View File

@ -60,6 +60,12 @@ func Benchmark_StrSymbols(b *testing.B) {
}
}
func Benchmark_Uint32Converting(b *testing.B) {
for i := 0; i < b.N; i++ {
binary.LittleEndian.Uint32([]byte{1, 1, 1, 1})
}
}
func Benchmark_Buffer(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := rand.Read(buffer); err == nil {

View File

@ -90,15 +90,7 @@ func Test_Rand(t *testing.T) {
})
}
func Test_Str(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
t.Assert(len(grand.S(5)), 5)
}
})
}
func Test_RandS(t *testing.T) {
func Test_S(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
t.Assert(len(grand.S(5)), 5)
@ -111,6 +103,24 @@ func Test_RandS(t *testing.T) {
})
}
func Test_B(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
b := grand.B(5)
t.Assert(len(b), 5)
t.AssertNE(b, make([]byte, 5))
}
})
}
func Test_Str(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
t.Assert(len(grand.S(5)), 5)
}
})
}
func Test_RandStr(t *testing.T) {
str := "我爱GoFrame"
gtest.C(t, func(t *gtest.T) {