From 02336d10e700c3824a7118f5dad5699a58610307 Mon Sep 17 00:00:00 2001 From: Leo Balduf Date: Sat, 17 Jun 2017 21:19:51 +0200 Subject: [PATCH] pkg/xorshift: rebuild to use stack only --- middleware/pkg/random/entropy.go | 17 ++++++ middleware/pkg/random/xorshift.go | 28 ++++++++++ middleware/pkg/random/xorshift_test.go | 38 ++++++++++++++ middleware/varinterval/varinterval.go | 24 ++++----- pkg/xorshift/xorshift.go | 71 -------------------------- pkg/xorshift/xorshift_test.go | 46 ----------------- 6 files changed, 92 insertions(+), 132 deletions(-) create mode 100644 middleware/pkg/random/entropy.go create mode 100644 middleware/pkg/random/xorshift.go create mode 100644 middleware/pkg/random/xorshift_test.go delete mode 100644 pkg/xorshift/xorshift.go delete mode 100644 pkg/xorshift/xorshift_test.go diff --git a/middleware/pkg/random/entropy.go b/middleware/pkg/random/entropy.go new file mode 100644 index 0000000..d62fed8 --- /dev/null +++ b/middleware/pkg/random/entropy.go @@ -0,0 +1,17 @@ +package random + +import ( + "encoding/binary" + + "github.com/chihaya/chihaya/bittorrent" +) + +// DeriveEntropyFromRequest generates 2*64 bits of pseudo random state from an +// AnnounceRequest. +// +// Calling DeriveEntropyFromRequest multiple times yields the same values. +func DeriveEntropyFromRequest(req *bittorrent.AnnounceRequest) (uint64, uint64) { + v0 := binary.BigEndian.Uint64([]byte(req.InfoHash[:8])) + binary.BigEndian.Uint64([]byte(req.InfoHash[8:16])) + v1 := binary.BigEndian.Uint64([]byte(req.Peer.ID[:8])) + binary.BigEndian.Uint64([]byte(req.Peer.ID[8:16])) + return v0, v1 +} diff --git a/middleware/pkg/random/xorshift.go b/middleware/pkg/random/xorshift.go new file mode 100644 index 0000000..e229f31 --- /dev/null +++ b/middleware/pkg/random/xorshift.go @@ -0,0 +1,28 @@ +// Package random implements the XORShift PRNG and a way to derive random state +// from an AnnounceRequest. +package random + +// GenerateAndAdvance applies XORShift128Plus on s0 and s1, returning +// the new states newS0, newS1 and a pseudo-random number v. +func GenerateAndAdvance(s0, s1 uint64) (v, newS0, newS1 uint64) { + v = s0 + s1 + newS0 = s1 + s0 ^= (s0 << 23) + newS1 = s0 ^ s1 ^ (s0 >> 18) ^ (s1 >> 5) + return +} + +// Intn generates an int k that satisfies k >= 0 && k < n. +// n must be > 0. +// It returns the generated k and the new state of the generator. +func Intn(s0, s1 uint64, n int) (int, uint64, uint64) { + if n <= 0 { + panic("invalid n <= 0") + } + v, newS0, newS1 := GenerateAndAdvance(s0, s1) + k := int(v) + if k < 0 { + k = -k + } + return k % n, newS0, newS1 +} diff --git a/middleware/pkg/random/xorshift_test.go b/middleware/pkg/random/xorshift_test.go new file mode 100644 index 0000000..553dec6 --- /dev/null +++ b/middleware/pkg/random/xorshift_test.go @@ -0,0 +1,38 @@ +package random + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestIntn(t *testing.T) { + rand.Seed(time.Now().UnixNano()) + s0, s1 := rand.Uint64(), rand.Uint64() + var k int + for i := 0; i < 10000; i++ { + k, s0, s1 = Intn(s0, s1, 10) + require.True(t, k >= 0, "Intn() must be >= 0") + require.True(t, k < 10, "Intn(k) must be < k") + } +} + +func BenchmarkAdvanceXORShift128Plus(b *testing.B) { + s0, s1 := rand.Uint64(), rand.Uint64() + var v uint64 + for i := 0; i < b.N; i++ { + v, s0, s1 = GenerateAndAdvance(s0, s1) + } + _, _, _ = v, s0, s1 +} + +func BenchmarkIntn(b *testing.B) { + s0, s1 := rand.Uint64(), rand.Uint64() + var v int + for i := 0; i < b.N; i++ { + v, s0, s1 = Intn(s0, s1, 1000) + } + _, _, _ = v, s0, s1 +} diff --git a/middleware/varinterval/varinterval.go b/middleware/varinterval/varinterval.go index 26f3d27..f531e4b 100644 --- a/middleware/varinterval/varinterval.go +++ b/middleware/varinterval/varinterval.go @@ -3,13 +3,12 @@ package varinterval import ( "context" "errors" - "math/rand" "sync" "time" "github.com/chihaya/chihaya/bittorrent" "github.com/chihaya/chihaya/middleware" - "github.com/chihaya/chihaya/pkg/xorshift" + "github.com/chihaya/chihaya/middleware/pkg/random" ) // ErrInvalidModifyResponseProbability is returned for a config with an invalid @@ -48,7 +47,6 @@ func checkConfig(cfg Config) error { type hook struct { cfg Config - pr [1024]*xorshift.LockedXORShift128Plus sync.Mutex } @@ -62,23 +60,19 @@ func New(cfg Config) (middleware.Hook, error) { h := &hook{ cfg: cfg, - pr: [1024]*xorshift.LockedXORShift128Plus{}, - } - for i := range h.pr { - h.pr[i] = xorshift.NewLockedXORShift128Plus(rand.Uint64(), rand.Uint64()) } return h, nil } -func (h *hook) getXORShiftByInfohash(ih *bittorrent.InfoHash) *xorshift.LockedXORShift128Plus { - return h.pr[(int(ih[1])|int(ih[0])<<8)%len(h.pr)] -} - func (h *hook) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest, resp *bittorrent.AnnounceResponse) (context.Context, error) { - r := h.getXORShiftByInfohash(&req.InfoHash) - - if h.cfg.ModifyResponseProbability == 1 || float32(xorshift.Intn(r, 1<<24))/(1<<24) < h.cfg.ModifyResponseProbability { - addSeconds := time.Duration(xorshift.Intn(r, h.cfg.MaxIncreaseDelta)+1) * time.Second + s0, s1 := random.DeriveEntropyFromRequest(req) + // Generate a probability p < 1.0. + v, s0, s1 := random.Intn(s0, s1, 1<<24) + p := float32(v) / (1 << 24) + if h.cfg.ModifyResponseProbability == 1 || p < h.cfg.ModifyResponseProbability { + // Generate the increase delta. + v, _, _ = random.Intn(s0, s1, h.cfg.MaxIncreaseDelta) + addSeconds := time.Duration(v+1) * time.Second resp.Interval += addSeconds diff --git a/pkg/xorshift/xorshift.go b/pkg/xorshift/xorshift.go deleted file mode 100644 index db5073a..0000000 --- a/pkg/xorshift/xorshift.go +++ /dev/null @@ -1,71 +0,0 @@ -// Package xorshift implements the XORShift PRNG. -package xorshift - -import "sync" - -// XORShift describes the functionality of an XORShift PRNG. -type XORShift interface { - Next() uint64 -} - -// XORShift128Plus holds the state of an XORShift128Plus PRNG. -type XORShift128Plus struct { - state [2]uint64 -} - -// Next generates a pseudorandom number and advances the state of s. -func (s *XORShift128Plus) Next() uint64 { - s1 := s.state[0] - s0 := s.state[1] - s1Tmp := s1 // need this for result computation - s.state[0] = s0 - s1 ^= (s1 << 23) // a - s.state[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5) // b, c - return s0 + s1Tmp -} - -// NewXORShift128Plus creates a new XORShift PRNG. -func NewXORShift128Plus(s0, s1 uint64) *XORShift128Plus { - return &XORShift128Plus{ - state: [2]uint64{s0, s1}, - } -} - -// LockedXORShift128Plus is a thread-safe XORShift128Plus. -type LockedXORShift128Plus struct { - sync.Mutex - state [2]uint64 -} - -// NewLockedXORShift128Plus creates a new LockedXORShift128Plus. -func NewLockedXORShift128Plus(s0, s1 uint64) *LockedXORShift128Plus { - return &LockedXORShift128Plus{ - state: [2]uint64{s0, s1}, - } -} - -// Next generates a pseudorandom number and advances the state of s. -func (s *LockedXORShift128Plus) Next() uint64 { - s.Lock() - s1 := s.state[0] - s0 := s.state[1] - s1Tmp := s1 // need this for result computation - s.state[0] = s0 - s1 ^= (s1 << 23) // a - s.state[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5) // b, c - s.Unlock() - return s0 + s1Tmp -} - -// Intn generates an int k that satisfies k >= 0 && k < n. -// n must be > 0. -func Intn(s XORShift, n int) int { - if n <= 0 { - panic("invalid n <= 0") - } - v := int(s.Next()) - if v < 0 { - v = -v - } - return v % n -} diff --git a/pkg/xorshift/xorshift_test.go b/pkg/xorshift/xorshift_test.go deleted file mode 100644 index 073f932..0000000 --- a/pkg/xorshift/xorshift_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package xorshift - -import ( - "math/rand" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestIntn(t *testing.T) { - rand.Seed(time.Now().UnixNano()) - s := NewXORShift128Plus(rand.Uint64(), rand.Uint64()) - for i := 0; i < 10000; i++ { - k := Intn(s, 10) - require.True(t, k >= 0, "Intn() must be >= 0") - require.True(t, k < 10, "Intn(k) must be < k") - } -} - -func BenchmarkXORShift128Plus_Next(b *testing.B) { - s := NewXORShift128Plus(rand.Uint64(), rand.Uint64()) - var k uint64 - for i := 0; i < b.N; i++ { - k = s.Next() - } - _ = k -} - -func BenchmarkIntnXORShift128Plus(b *testing.B) { - s := NewXORShift128Plus(rand.Uint64(), rand.Uint64()) - var k int - for i := 0; i < b.N; i++ { - k = Intn(s, 1000) - } - _ = k -} - -func BenchmarkLockedXORShift128Plus_Next(b *testing.B) { - s := NewLockedXORShift128Plus(rand.Uint64(), rand.Uint64()) - var k uint64 - for i := 0; i < b.N; i++ { - k = s.Next() - } - _ = k -}