Merge pull request #339 from mrd0ll4r/xorshift
pkg/xorshift: rebuild to use stack only
This commit is contained in:
commit
d43cb719b9
6 changed files with 92 additions and 132 deletions
17
middleware/pkg/random/entropy.go
Normal file
17
middleware/pkg/random/entropy.go
Normal file
|
@ -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
|
||||
}
|
28
middleware/pkg/random/xorshift.go
Normal file
28
middleware/pkg/random/xorshift.go
Normal file
|
@ -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
|
||||
}
|
38
middleware/pkg/random/xorshift_test.go
Normal file
38
middleware/pkg/random/xorshift_test.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue