New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow concurrent, re-creatable usage #44
Changes from 5 commits
005951d
caad4a1
63b66dd
dc0f887
e23e7ef
f00b204
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package uuid | ||
|
||
import ( | ||
"io" | ||
"crypto/rand" | ||
) | ||
|
||
// A UuidSource holds a random number generator and generates UUIDs using it as its source. | ||
// | ||
// It is useful when a process need its own random number generator, | ||
// e.g. when running some processes concurrently. | ||
type UuidSource struct { | ||
rander io.Reader | ||
} | ||
|
||
// Creates a new UuidSource which holds its own random number generator. | ||
// | ||
// Calling NewSource with nil sets the random number generator to a default | ||
// generator. | ||
func NewSource(r io.Reader) UuidSource { | ||
var uuidSource UuidSource | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return Source{render: r} No reason to special case nil. They can pass in rand.Reader if they want. |
||
uuidSource.SetRand(r) | ||
return uuidSource | ||
|
||
} | ||
|
||
// SetRand sets the random number generator of the UuidSource to r, which implements io.Reader. | ||
// If r.Read returns an error when the package requests random data then | ||
// a panic will be issued. | ||
// | ||
// Calling SetRand with nil sets the random number generator to a default | ||
// generator. | ||
func (uuidSource *UuidSource) SetRand(r io.Reader) { | ||
if r == nil { | ||
uuidSource.rander = rand.Reader | ||
return | ||
} | ||
uuidSource.rander = r | ||
} | ||
|
||
// NewRandom returns a Random (Version 4) UUID based on the random number generator in the UuidSource. | ||
// | ||
// See more detailed explanation here: https://godoc.org/github.com/google/uuid#NewRandom | ||
func (uuidSource UuidSource) NewRandom() (UUID, error) { | ||
return newRandom(uuidSource.rander) | ||
} | ||
|
||
// New creates a new random UUID based on the random number generator in the UuidSource or panics. New is equivalent to | ||
// the expression | ||
// | ||
// uuid.Must(uuid.NewRandom()) | ||
func (uuidSource UuidSource) New() UUID { | ||
return Must(uuidSource.NewRandom()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package uuid | ||
|
||
import ( | ||
"testing" | ||
"strings" | ||
|
||
regen "github.com/zach-klippenstein/goregen" | ||
) | ||
|
||
func TestUuidSources(t *testing.T) { | ||
|
||
myString, _ := regen.Generate("[a-zA-Z]{1000}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than depend on another external package (creating another dependency) why not just make a very simple reader that returns a deterministic set based on some seed. For example:
|
||
|
||
// Two identical sources, should give same sequence | ||
uuidSourceA := NewSource(strings.NewReader(myString)) | ||
uuidSourceB := NewSource(strings.NewReader(myString)) | ||
|
||
for i := 0; i < 10; i++ { | ||
uuid1 := uuidSourceA.New() | ||
uuid2 := uuidSourceB.New() | ||
if uuid1 != uuid2 { | ||
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid2) | ||
} | ||
} | ||
|
||
// Set rander with nil, each source will be random | ||
uuidSourceA.SetRand(nil) | ||
uuidSourceB.SetRand(nil) | ||
|
||
for i := 0; i < 10; i++ { | ||
uuid1 := uuidSourceA.New() | ||
uuid2 := uuidSourceB.New() | ||
if uuid1 == uuid2 { | ||
t.Errorf("unexpected duplicates, got %q", uuid1) | ||
} | ||
} | ||
|
||
// Set rander to rand source with same seed, should give same sequence | ||
uuidSourceA.SetRand(strings.NewReader(myString)) | ||
uuidSourceB.SetRand(strings.NewReader(myString)) | ||
|
||
for i := 0; i < 10; i++ { | ||
uuid1 := uuidSourceA.New() | ||
uuid2 := uuidSourceB.New() | ||
if uuid1 != uuid2 { | ||
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid2) | ||
} | ||
} | ||
|
||
// Set rander to rand source with different seeds, should not give same sequence | ||
uuidSourceA.SetRand(strings.NewReader("456" + myString)) | ||
uuidSourceB.SetRand(strings.NewReader("myString" + myString)) | ||
|
||
for i := 0; i < 10; i++ { | ||
uuid1 := uuidSourceA.New() | ||
uuid2 := uuidSourceB.New() | ||
if uuid1 == uuid2 { | ||
t.Errorf("unexpected duplicates, got %q", uuid1) | ||
} | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be UUIDSource, but if you are mimicking math/rand then just call it Source.
One issue with this setup it you still can only have a single rander function. Rather than add all of this complexity, it might be easier to simply create one new function, say NewRandomFromReader (I don't really like that name, it is a bit of a mouthful, but I don't expect it to be used often at all).
Now an external package can create this type if they want.
That will keep the uuid package cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pborman , Thanks for looking into this PR!
Let me ask to clarify - are you suggesting here that instead of everything in the uuid_source file, we just add the
func NewRandomFromReadr(r io.Reader) (UUID, error)
function toversion4.go
and let the user build the structs around it if they need? (it is actually thefunc newRandom(r io.Reader) (UUID, error)
that I added there). I am not clear, as you are also commenting on the content of the uuid_source files.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is exactly correct. And yes, the NewRandomFromReader is essentially the newRandom function you added. The uuid_source files would no longer be needed, although a simple test showing that NewRandomFromReader did in fact use the supplied reader would probably be a good addition. This could be very simple where you have a bytes.NewReader that has 16 bytes of data and the first NewRandomFromReader gives you the UUID you expect and the second NewRandomFromReader returns an error.
You might want to consider putting a package in GitHub that has the functionality of your uuid_source file so others can also use it if they need that sort of functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, Got it.
A package for using it seems too much for just few lines of code. Maybe I'll just add an example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pborman