syncthing/lib/rand/securesource.go
greatroar 198028d627
lib/rand: Optimizations (#7964)
rand.secureSource.Uint64 no longer allocates. rand.String uses a
strings.Builder. Benchmark results on linux/amd64:

name            old time/op    new time/op    delta
SecureSource-8    69.1ns ± 3%    51.7ns ± 3%   -25.21%  (p=0.000 n=20+10)
String-8          2.66µs ± 2%    1.95µs ± 1%   -26.61%  (p=0.000 n=10+10)

name            old alloc/op   new alloc/op   delta
SecureSource-8     8.00B ± 0%     0.00B       -100.00%  (p=0.000 n=20+10)
String-8            288B ± 0%       32B ± 0%   -88.89%  (p=0.000 n=10+10)

name            old allocs/op  new allocs/op  delta
SecureSource-8      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=20+10)
String-8            33.0 ± 0%       1.0 ± 0%   -96.97%  (p=0.000 n=10+10)
2021-09-26 12:15:39 +02:00

63 lines
1.5 KiB
Go

// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package rand
import (
"bufio"
"crypto/rand"
"encoding/binary"
"io"
"sync"
)
// The secureSource is a math/rand.Source + io.Reader that reads bytes from
// crypto/rand.Reader. It means we can use the convenience functions
// provided by math/rand.Rand on top of a secure source of numbers. It is
// concurrency safe for ease of use.
type secureSource struct {
rd io.Reader
mut sync.Mutex
buf [8]byte
}
func newSecureSource() *secureSource {
return &secureSource{
// Using buffering on top of the rand.Reader increases our
// performance by about 20%, even though it means we must use
// locking.
rd: bufio.NewReader(rand.Reader),
}
}
func (s *secureSource) Seed(int64) {
panic("SecureSource is not seedable")
}
func (s *secureSource) Int63() int64 {
return int64(s.Uint64() & (1<<63 - 1))
}
func (s *secureSource) Read(p []byte) (int, error) {
s.mut.Lock()
defer s.mut.Unlock()
return s.rd.Read(p)
}
func (s *secureSource) Uint64() uint64 {
// Read eight bytes of entropy from the buffered, secure random number
// generator. The buffered reader isn't concurrency safe, so we lock
// around that.
s.mut.Lock()
defer s.mut.Unlock()
_, err := io.ReadFull(s.rd, s.buf[:])
if err != nil {
panic("randomness failure: " + err.Error())
}
return binary.LittleEndian.Uint64(s.buf[:])
}