Skip to content

Commit

Permalink
[AUTOPR] Automatic updates (Vonage#241)
Browse files Browse the repository at this point in the history
* Add RandString function

---------

Co-authored-by: nicolaasuni-vonage <nicola.asuni@vonage.com>
  • Loading branch information
github-actions[bot] and nicolaasuni-vonage authored May 3, 2024
1 parent 9c2e76b commit 134fa91
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 4 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.89.0
1.90.0
2 changes: 1 addition & 1 deletion examples/service/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.22.2
replace github.com/Vonage/gosrvlib => ../..

require (
github.com/Vonage/gosrvlib v1.89.0
github.com/Vonage/gosrvlib v1.90.0
github.com/golang/mock v1.6.0
github.com/jstemmer/go-junit-report v1.0.0
github.com/prometheus/client_golang v1.19.0
Expand Down
38 changes: 38 additions & 0 deletions pkg/random/example_random_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package random_test

import (
"fmt"
"log"

"github.com/Vonage/gosrvlib/pkg/random"
)

//nolint:testableexamples
func ExampleRnd_RandUint32() {
r := random.New(nil)

n := r.RandUint32()

fmt.Println(n)
}

//nolint:testableexamples
func ExampleRnd_RandUint64() {
r := random.New(nil)

n := r.RandUint64()

fmt.Println(n)
}

//nolint:testableexamples
func ExampleRnd_RandString() {
r := random.New(nil)

s, err := r.RandString(16)
if err != nil {
log.Fatal(err)
}

fmt.Println(s)
}
20 changes: 20 additions & 0 deletions pkg/random/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package random

// Option is the interface that allows to set client options.
type Option func(c *Rnd)

// WithByteToCharMap overwrites the default slice used to map random bytes to characters.
// If cm is empty, then the default character map will be used.
// The maximum cm length is 256, if it is greater than 256, it will be truncated.
func WithByteToCharMap(cm []byte) Option {
switch d := len(cm); {
case d == 0:
cm = []byte(chrMap)
case d > chrMapMaxLen:
cm = cm[:chrMapMaxLen]
}

return func(c *Rnd) {
c.chrMap = cm
}
}
31 changes: 31 additions & 0 deletions pkg/random/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package random

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestWithByteToCharMap(t *testing.T) {
t.Parallel()

want := []byte(chrMap)
c := &Rnd{}

WithByteToCharMap(want)(c)
require.Equal(t, want, c.chrMap)

WithByteToCharMap(nil)(c)
require.Equal(t, want, c.chrMap)

WithByteToCharMap([]byte{})(c)
require.Equal(t, want, c.chrMap)

WithByteToCharMap([]byte("0123456789abcdefx"))(c)
require.Len(t, c.chrMap, 17)

longMap := make([]byte, chrMapMaxLen+1)

WithByteToCharMap(longMap)(c)
require.Len(t, c.chrMap, chrMapMaxLen)
}
39 changes: 37 additions & 2 deletions pkg/random/random.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,39 @@ import (
mrand "math/rand/v2"
)

const (
chrDigits = "0123456789"
chrUppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
chrLowercase = "abcdefghijklmnopqrstuvwxyz"
chrSymbols = "!#$%&()*+,-./:;<=>?@[]^_{|}~" // (exclude "\"'\\`")
chrMap = chrDigits + chrUppercase + chrLowercase + chrSymbols
chrMapMaxLen = 256
)

// Rnd defines then random number generator.
type Rnd struct {
reader io.Reader
chrMap []byte
}

// New initialize the random reader.
// The r argument must be a cryptographically secure random number generator.
// The crypto/rand.Read is used as default if r == nil.
func New(r io.Reader) *Rnd {
func New(r io.Reader, opts ...Option) *Rnd {
if r == nil {
r = rand.Reader
}

return &Rnd{reader: r}
rnd := &Rnd{
reader: r,
chrMap: []byte(chrMap),
}

for _, applyOpt := range opts {
applyOpt(rnd)
}

return rnd
}

// RandomBytes generates a slice of random bytes with the specified length.
Expand Down Expand Up @@ -60,3 +79,19 @@ func (r *Rnd) RandUint64() uint64 {

return binary.LittleEndian.Uint64(b)
}

// RandString returns n-characters long random string that can be used as password.
// It generates n random bytes and maps them to characters using the default character set.
// The default character set can be overwritten by using the WithCharByteMap option.
func (r *Rnd) RandString(n int) (string, error) {
b, err := r.RandomBytes(n)
if err != nil {
return "", err
}

for i, v := range b {
b[i] = r.chrMap[(int(v)*len(chrMap))>>8]
}

return string(b), nil
}
35 changes: 35 additions & 0 deletions pkg/random/random_benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package random

import (
"testing"
)

func BenchmarkRnd_RandUint32(b *testing.B) {
b.ResetTimer()

r := New(nil)

for i := 0; i < b.N; i++ {
_ = r.RandUint32()
}
}

func BenchmarkRnd_RandUint64(b *testing.B) {
b.ResetTimer()

r := New(nil)

for i := 0; i < b.N; i++ {
_ = r.RandUint64()
}
}

func BenchmarkRnd_RandString(b *testing.B) {
b.ResetTimer()

r := New(nil)

for i := 0; i < b.N; i++ {
_, _ = r.RandString(16)
}
}
30 changes: 30 additions & 0 deletions pkg/random/random_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ func TestNew(t *testing.T) {
r := New(nil)

require.NotNil(t, r.reader)
require.NotNil(t, r.chrMap)

errReader := iotest.ErrReader(errors.New("test-rand-reader-error"))
re := New(
errReader,
WithByteToCharMap([]byte("0123456789abcdefx")),
)

require.NotNil(t, re.reader)
require.Equal(t, errReader, re.reader)
require.NotNil(t, re.chrMap)
require.Len(t, re.chrMap, 17)
}

func TestRandomBytes(t *testing.T) {
Expand Down Expand Up @@ -65,3 +77,21 @@ func TestRandUint64(t *testing.T) {

require.NotZero(t, u)
}

func TestRandString(t *testing.T) {
t.Parallel()

r := New(nil)

s, err := r.RandString(17)

require.NoError(t, err)
require.Len(t, s, 17)

re := New(iotest.ErrReader(errors.New("test-randstring-error")))

s, err = re.RandString(32)

require.Error(t, err)
require.Empty(t, s)
}

0 comments on commit 134fa91

Please sign in to comment.