summaryrefslogtreecommitdiff
path: root/vendor/github.com/docker/distribution/uuid/uuid.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/docker/distribution/uuid/uuid.go')
-rw-r--r--vendor/github.com/docker/distribution/uuid/uuid.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/vendor/github.com/docker/distribution/uuid/uuid.go b/vendor/github.com/docker/distribution/uuid/uuid.go
new file mode 100644
index 000000000..d433ccaf5
--- /dev/null
+++ b/vendor/github.com/docker/distribution/uuid/uuid.go
@@ -0,0 +1,126 @@
+// Package uuid provides simple UUID generation. Only version 4 style UUIDs
+// can be generated.
+//
+// Please see http://tools.ietf.org/html/rfc4122 for details on UUIDs.
+package uuid
+
+import (
+ "crypto/rand"
+ "fmt"
+ "io"
+ "os"
+ "syscall"
+ "time"
+)
+
+const (
+ // Bits is the number of bits in a UUID
+ Bits = 128
+
+ // Size is the number of bytes in a UUID
+ Size = Bits / 8
+
+ format = "%08x-%04x-%04x-%04x-%012x"
+)
+
+var (
+ // ErrUUIDInvalid indicates a parsed string is not a valid uuid.
+ ErrUUIDInvalid = fmt.Errorf("invalid uuid")
+
+ // Loggerf can be used to override the default logging destination. Such
+ // log messages in this library should be logged at warning or higher.
+ Loggerf = func(format string, args ...interface{}) {}
+)
+
+// UUID represents a UUID value. UUIDs can be compared and set to other values
+// and accessed by byte.
+type UUID [Size]byte
+
+// Generate creates a new, version 4 uuid.
+func Generate() (u UUID) {
+ const (
+ // ensures we backoff for less than 450ms total. Use the following to
+ // select new value, in units of 10ms:
+ // n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2
+ maxretries = 9
+ backoff = time.Millisecond * 10
+ )
+
+ var (
+ totalBackoff time.Duration
+ count int
+ retries int
+ )
+
+ for {
+ // This should never block but the read may fail. Because of this,
+ // we just try to read the random number generator until we get
+ // something. This is a very rare condition but may happen.
+ b := time.Duration(retries) * backoff
+ time.Sleep(b)
+ totalBackoff += b
+
+ n, err := io.ReadFull(rand.Reader, u[count:])
+ if err != nil {
+ if retryOnError(err) && retries < maxretries {
+ count += n
+ retries++
+ Loggerf("error generating version 4 uuid, retrying: %v", err)
+ continue
+ }
+
+ // Any other errors represent a system problem. What did someone
+ // do to /dev/urandom?
+ panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err))
+ }
+
+ break
+ }
+
+ u[6] = (u[6] & 0x0f) | 0x40 // set version byte
+ u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b}
+
+ return u
+}
+
+// Parse attempts to extract a uuid from the string or returns an error.
+func Parse(s string) (u UUID, err error) {
+ if len(s) != 36 {
+ return UUID{}, ErrUUIDInvalid
+ }
+
+ // create stack addresses for each section of the uuid.
+ p := make([][]byte, 5)
+
+ if _, err := fmt.Sscanf(s, format, &p[0], &p[1], &p[2], &p[3], &p[4]); err != nil {
+ return u, err
+ }
+
+ copy(u[0:4], p[0])
+ copy(u[4:6], p[1])
+ copy(u[6:8], p[2])
+ copy(u[8:10], p[3])
+ copy(u[10:16], p[4])
+
+ return
+}
+
+func (u UUID) String() string {
+ return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:])
+}
+
+// retryOnError tries to detect whether or not retrying would be fruitful.
+func retryOnError(err error) bool {
+ switch err := err.(type) {
+ case *os.PathError:
+ return retryOnError(err.Err) // unpack the target error
+ case syscall.Errno:
+ if err == syscall.EPERM {
+ // EPERM represents an entropy pool exhaustion, a condition under
+ // which we backoff and retry.
+ return true
+ }
+ }
+
+ return false
+}