6 Commits

Author SHA1 Message Date
83bad54113 chore(release): 0.2.0 2021-03-16 02:27:39 +00:00
09883ed0d2 docs(readme): add UUID() example to readme 2021-03-16 02:25:50 +00:00
d7f439e1d9 Merge pull request #2 from jimeh/add-uuid
feat(strings): add UUID function to generate random RFC 4122 UUID v4 strings
2021-03-16 02:23:18 +00:00
825a3c18fb feat(strings): add UUID function to generate random RFC 4122 UUID v4 strings 2021-03-16 02:14:56 +00:00
a755fe957a fix(strings): add missing benchmark for DNSLabel 2021-03-16 02:13:20 +00:00
ef9cb3a01d chore(docs): remove redundant RFC URLs
pkg.go.dev parses "RFC 1035, section 2.3.1" style text and links it to
the relevant place. So there's no need to also include a full URL for
documentation purposes.
2021-03-16 02:10:59 +00:00
5 changed files with 134 additions and 51 deletions

View File

@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [0.2.0](https://github.com/jimeh/rands/compare/v0.1.1...v0.2.0) (2021-03-16)
### Features
* **strings:** add UUID function to generate random RFC 4122 UUID v4 strings ([825a3c1](https://github.com/jimeh/rands/commit/825a3c18fbc8e0497eafea9254baadb2951f23c6))
### Bug Fixes
* **strings:** add missing benchmark for DNSLabel ([a755fe9](https://github.com/jimeh/rands/commit/a755fe957a485e4c29b8ade474878b265785bc66))
### [0.1.1](https://github.com/jimeh/rands/compare/v0.1.0...v0.1.1) (2021-01-20)
## 0.1.0 (2021-01-20)

View File

@@ -52,6 +52,7 @@ s, _ := rands.String(16, "abcdefABCDEF") // => ad
s, _ := rands.UnicodeString(16, []rune("九七二人入八力十下三千上口土夕大")) // => 下下口九力下土夕下土八上二夕大三
s, _ := rands.DNSLabel(16) // => z0ij9o8qkbs0ru-h
s, _ := rands.UUID() // => a62b8712-f238-43ba-a47e-333f5fffe785
n, _ := rands.Int(2147483647) // => 1334400235
n, _ := rands.Int64(int64(9223372036854775807)) // => 8256935979116161233

View File

@@ -18,6 +18,7 @@ const (
alphabeticChars = upperChars + lowerChars
alphanumericChars = alphabeticChars + numericChars
dnsLabelChars = lowerNumericChars + "-"
uuidHyphen = byte('-')
)
var (
@@ -164,11 +165,9 @@ func UnicodeString(n int, alphabet []rune) (string, error) {
}
// DNSLabel returns a random string of n length in a DNS label compliant format
// as defined in RFC 1035, section 2.3.1:
// https://www.rfc-editor.org/rfc/rfc1035.html#section-2.3.1
// as defined in RFC 1035, section 2.3.1.
//
// It also adheres to RFC 5891, section 4.2.3.1:
// https://www.rfc-editor.org/rfc/rfc5891.html#section-4.2.3.1
// It also adheres to RFC 5891, section 4.2.3.1.
//
// In summary, the generated random string will:
//
@@ -235,6 +234,32 @@ func DNSLabel(n int) (string, error) {
}
}
// UUID returns a random UUID v4 in string format as defined by RFC 4122,
// section 4.4.
func UUID() (string, error) {
b, err := Bytes(16)
if err != nil {
return "", err
}
b[6] = (b[6] & 0x0f) | 0x40 // Version: 4 (random)
b[8] = (b[8] & 0x3f) | 0x80 // Variant: RFC 4122
// Construct a UUID v4 string according to RFC 4122 specifications.
dst := make([]byte, 36)
hex.Encode(dst[0:8], b[0:4]) // time-low
dst[8] = uuidHyphen
hex.Encode(dst[9:13], b[4:6]) // time-mid
dst[13] = uuidHyphen
hex.Encode(dst[14:18], b[6:8]) // time-high-and-version
dst[18] = uuidHyphen
hex.Encode(dst[19:23], b[8:10]) // clock-seq-and-reserved, clock-seq-low
dst[23] = uuidHyphen
hex.Encode(dst[24:], b[10:]) // node
return string(dst), nil
}
func isASCII(s string) bool {
for _, c := range s {
if c > unicode.MaxASCII {

View File

@@ -70,3 +70,8 @@ func ExampleDNSLabel() {
s, _ := rands.DNSLabel(16)
fmt.Println(s) // => z0ij9o8qkbs0ru-h
}
func ExampleUUID() {
s, _ := rands.UUID()
fmt.Println(s) // => a62b8712-f238-43ba-a47e-333f5fffe785
}

View File

@@ -2,6 +2,7 @@ package rands
import (
"encoding/base64"
"encoding/hex"
"errors"
"regexp"
"strings"
@@ -497,54 +498,55 @@ func BenchmarkUnicodeString(b *testing.B) {
}
}
var dnsLabelTestCases = []struct {
name string
n int
errIs error
errStr string
}{
{
name: "n=-128",
n: -128,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
{
name: "n=0",
n: 0,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
{name: "n=1", n: 1},
{name: "n=2", n: 2},
{name: "n=3", n: 3},
{name: "n=4", n: 4},
{name: "n=5", n: 5},
{name: "n=6", n: 6},
{name: "n=7", n: 7},
{name: "n=8", n: 8},
{name: "n=16", n: 16},
{name: "n=32", n: 32},
{name: "n=63", n: 63},
{
name: "n=64",
n: 64,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
{
name: "n=128",
n: 128,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
}
func TestDNSLabel(t *testing.T) {
tests := []struct {
name string
n int
errIs error
errStr string
}{
{
name: "n=-128",
n: -128,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
{
name: "n=0",
n: 0,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
{name: "n=1", n: 1},
{name: "n=2", n: 2},
{name: "n=3", n: 3},
{name: "n=4", n: 4},
{name: "n=5", n: 5},
{name: "n=6", n: 6},
{name: "n=7", n: 7},
{name: "n=8", n: 8},
{name: "n=16", n: 16},
{name: "n=32", n: 32},
{name: "n=63", n: 63},
{
name: "n=64",
n: 64,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
{
name: "n=128",
n: 128,
errIs: errDNSLabelLength,
errStr: "rands: DNS labels must be between 1 and 63 characters " +
"in length",
},
}
for _, tt := range tests {
for _, tt := range dnsLabelTestCases {
t.Run(tt.name, func(t *testing.T) {
// generate lots of labels to increase the chances of catching any
// obscure bugs
@@ -568,6 +570,44 @@ func TestDNSLabel(t *testing.T) {
}
}
func BenchmarkDNSLabel(b *testing.B) {
for _, tt := range dnsLabelTestCases {
b.Run(tt.name, func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, _ = DNSLabel(tt.n)
}
})
}
}
func TestUUID(t *testing.T) {
m := regexp.MustCompile(
`^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$`,
)
for i := 0; i < 10000; i++ {
got, err := UUID()
require.NoError(t, err)
require.Regexp(t, m, got)
raw := strings.ReplaceAll(got, "-", "")
b := make([]byte, 16)
_, err = hex.Decode(b, []byte(raw))
require.NoError(t, err)
require.Equal(t, 4, int(b[6]>>4), "version is not 4")
require.Equal(t, byte(0x80), b[8]&0xc0,
"variant is not RFC 4122",
)
}
}
func BenchmarkUUID(b *testing.B) {
for n := 0; n < b.N; n++ {
_, _ = UUID()
}
}
//
// Helpers
//