💻
crypto/rand を math/rand として使う
みなさん一度は思いませんでした? なんで math/rand と crypto/rand は内部構成が全く違うんだろう,と。まぁ,目的が異なるので構成が違っててもおかしくないけど,せめて crypto/rand を math/rand のソースとして使えればいいのに,と。
実は crypto/rand を math/rand のソースにするのはそんなに難しくない。 math/rand のソースの定義は
math/rand/rand.go
// A Source represents a source of uniformly-distributed
// pseudo-random int64 values in the range [0, 1<<63).
type Source interface {
Int63() int64
Seed(seed int64)
}
// A Source64 is a Source that can also generate
// uniformly-distributed pseudo-random uint64 values in
// the range [0, 1<<64) directly.
// If a Rand r's underlying Source s implements Source64,
// then r.Uint64 returns the result of one call to s.Uint64
// instead of making two calls to s.Int63.
type Source64 interface {
Source
Uint64() uint64
}
となっているので,これにマッチするラッパーを作ってやればいいわけ。たとえばこんな感じ。
sample.go
type Source struct{}
// Seed method is dummy function for rand.Source interface.
func (s Source) Seed(seed int64) {}
// Uint64 method generates a random number in the range [0, 1<<64).
func (s Source) Uint64() uint64 {
b := [8]byte{}
ct, _ := rand.Read(b[:])
return binary.BigEndian.Uint64(b[:ct])
}
// Int63 method generates a random number in the range [0, 1<<63).
func (s Source) Int63() int64 {
return (int64)(s.Uint64() >> 1)
}
こうすれば
sample.go
fmt.Println(rand.New(Source{}).Float64()) // 0.9581627789424901
という感じに rand.Rand 型が提供するメソッドを利用することができる[1]。コード全体ではこんな感じ。
sample.go
//go:build run
// +build run
package main
import (
"crypto/rand"
"encoding/binary"
"fmt"
mrand "math/rand"
)
type Source struct{}
// Seed method is dummy function for rand.Source interface.
func (s Source) Seed(seed int64) {}
// Uint64 method generates a random number in the range [0, 1<<64).
func (s Source) Uint64() uint64 {
b := [8]byte{}
ct, _ := rand.Read(b[:])
return binary.BigEndian.Uint64(b[:ct])
}
// Int63 method generates a random number in the range [0, 1<<63).
func (s Source) Int63() int64 {
return (int64)(s.Uint64() >> 1)
}
func main() {
fmt.Println(mrand.New(Source{}).Float64())
}
というわけで,これをパッケージ化することにした。といっても,たったこれだけの機能のためにリポジトリを作るのはもったいないので,拙作の疑似乱数パッケージ github.com/goark/mt のオマケ機能として組み込んでみた。こんな感じに使える。
//go:build run
// +build run
package main
import (
"fmt"
"math/rand"
"github.com/goark/mt/secure"
)
func main() {
fmt.Println(rand.New(secure.Source{}).Uint64())
}
よーし,うむうむ,よーし。では,作業の続きをするか。
Discussion