From 4bfaae0c47d074e93d1231ed31edef1adb2465d4 Mon Sep 17 00:00:00 2001 From: Dmitry Chestnykh Date: Tue, 5 Apr 2011 17:57:25 +0200 Subject: [PATCH] Initial commit. --- LICENSE | 19 +++++++++++++++++ Makefile | 8 ++++++++ README | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ uniuri.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ uniuri_test.go | 35 +++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 uniuri.go create mode 100644 uniuri_test.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7851c34 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Dmitry Chestnykh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..63a9b81 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +include $(GOROOT)/src/Make.inc + +TARG=github.com/dchest/uniuri +GOFILES=\ + uniuri.go + +include $(GOROOT)/src/Make.pkg + diff --git a/README b/README new file mode 100644 index 0000000..6eed9aa --- /dev/null +++ b/README @@ -0,0 +1,56 @@ +PACKAGE + +package uniuri +import "github.com/dchest/uniuri" + +Package uniuri generates random strings good for use in URIs to identify +unique objects. + +Example usage: + + s := uniuri.New() // s is now "apHCJBl7L1OmC57n" + +A standard string created by New() is 16 bytes in length and consists of +Latin upper and lowercase letters, and numbers (from the set of 62 allowed +characters), which means that it has ~95 bits of entropy. To get more +entropy, you can use NewLen(UUIDLen), which returns 20-byte string, giving +~119 bits of entropy, or any other desired length. + +Functions read from crypto/rand random source, and panic if they fail to +read from it. + + +CONSTANTS + +const ( + // Standard length of uniuri string to achive ~95 bits of entropy. + StdLen = 16 + // Length of uniurl string to achive ~119 bits of entropy, closest + // to what can be losslessly converted to UUIDv4 (122 bits). + UUIDLen = 20 +) + + +VARIABLES + +var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890") + +Standard characters allowed in uniuri string. + + +FUNCTIONS + +func New() string + +New returns a new random string with the standard length and standard +characters. + +func NewLen(length int) string + +NewLen returns a new random string with the provided length and standard +characters. + +func NewLenChars(length int, chars []byte) string + +NewLenChars returns a new random string with the provided length and byte +slice of allowed characters (maximum 256). diff --git a/uniuri.go b/uniuri.go new file mode 100644 index 0000000..f035178 --- /dev/null +++ b/uniuri.go @@ -0,0 +1,55 @@ +// Package uniuri generates random strings good for use in URIs to identify +// unique objects. +// +// Example usage: +// +// s := uniuri.New() // s is now "apHCJBl7L1OmC57n" +// +// A standard string created by New() is 16 bytes in length and consists of +// Latin upper and lowercase letters, and numbers (from the set of 62 allowed +// characters), which means that it has ~95 bits of entropy. To get more +// entropy, you can use NewLen(UUIDLen), which returns 20-byte string, giving +// ~119 bits of entropy, or any other desired length. +// +// Functions read from crypto/rand random source, and panic if they fail to +// read from it. +package uniuri + +import "crypto/rand" + +const ( + // Standard length of uniuri string to achive ~95 bits of entropy. + StdLen = 16 + // Length of uniurl string to achive ~119 bits of entropy, closest + // to what can be losslessly converted to UUIDv4 (122 bits). + UUIDLen = 20 +) + +// Standard characters allowed in uniuri string. +var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890") + +// New returns a new random string with the standard length and standard +// characters. +func New() string { + return NewLenChars(StdLen, StdChars) +} + +// NewLen returns a new random string with the provided length and standard +// characters. +func NewLen(length int) string { + return NewLenChars(length, StdChars) +} + +// NewLenChars returns a new random string with the provided length and byte +// slice of allowed characters (maximum 256). +func NewLenChars(length int, chars []byte) string { + b := make([]byte, length) + if _, err := rand.Reader.Read(b); err != nil { + panic("error reading from random source: " + err.String()) + } + alen := byte(len(chars)) + for i, c := range b { + b[i] = chars[c%alen] + } + return string(b) +} diff --git a/uniuri_test.go b/uniuri_test.go new file mode 100644 index 0000000..9899081 --- /dev/null +++ b/uniuri_test.go @@ -0,0 +1,35 @@ +package uniuri + +import "testing" + +func TestNew(t *testing.T) { + u := New() + // Check length + if len(u) != StdLen { + t.Fatalf("wrong length: expected %d, got %d", StdLen, len(u)) + } + // Check that only allowed characters are present + for _, c := range u { + var present bool + for _, a := range StdChars { + if int(a) == c { + present = true + } + } + if !present { + t.Fatalf("chars not allowed in %q", u) + } + } + // Generate 1000 uniuris and check that they are unique + uris := make([]string, 1000) + for i, _ := range uris { + uris[i] = New() + } + for i, u := range uris { + for j, u2 := range uris { + if i != j && u == u2 { + t.Fatalf("not unique: %d:%q and %d:%q", i, j, u, u2) + } + } + } +}