uuid.go raw

   1  package rand
   2  
   3  import (
   4  	"encoding/hex"
   5  	"io"
   6  )
   7  
   8  const dash byte = '-'
   9  
  10  // UUIDIdempotencyToken provides a utility to get idempotency tokens in the
  11  // UUID format.
  12  type UUIDIdempotencyToken struct {
  13  	uuid *UUID
  14  }
  15  
  16  // NewUUIDIdempotencyToken returns a idempotency token provider returning
  17  // tokens in the UUID random format using the reader provided.
  18  func NewUUIDIdempotencyToken(r io.Reader) *UUIDIdempotencyToken {
  19  	return &UUIDIdempotencyToken{uuid: NewUUID(r)}
  20  }
  21  
  22  // GetIdempotencyToken returns a random UUID value for Idempotency token.
  23  func (u UUIDIdempotencyToken) GetIdempotencyToken() (string, error) {
  24  	return u.uuid.GetUUID()
  25  }
  26  
  27  // UUID provides computing random UUID version 4 values from a random source
  28  // reader.
  29  type UUID struct {
  30  	randSrc io.Reader
  31  }
  32  
  33  // NewUUID returns an initialized UUID value that can be used to retrieve
  34  // random UUID version 4 values.
  35  func NewUUID(r io.Reader) *UUID {
  36  	return &UUID{randSrc: r}
  37  }
  38  
  39  // GetUUID returns a random UUID version 4 string representation sourced from the random reader the
  40  // UUID was created with. Returns an error if unable to compute the UUID.
  41  func (r *UUID) GetUUID() (string, error) {
  42  	var b [16]byte
  43  	if _, err := io.ReadFull(r.randSrc, b[:]); err != nil {
  44  		return "", err
  45  	}
  46  	r.makeUUIDv4(b[:])
  47  	return format(b), nil
  48  }
  49  
  50  // GetBytes returns a byte slice containing a random UUID version 4 sourced from the random reader the
  51  // UUID was created with. Returns an error if unable to compute the UUID.
  52  func (r *UUID) GetBytes() (u []byte, err error) {
  53  	u = make([]byte, 16)
  54  	if _, err = io.ReadFull(r.randSrc, u); err != nil {
  55  		return u, err
  56  	}
  57  	r.makeUUIDv4(u)
  58  	return u, nil
  59  }
  60  
  61  func (r *UUID) makeUUIDv4(u []byte) {
  62  	// 13th character is "4"
  63  	u[6] = (u[6] & 0x0f) | 0x40 // Version 4
  64  	// 17th character is "8", "9", "a", or "b"
  65  	u[8] = (u[8] & 0x3f) | 0x80 // Variant most significant bits are 10x where x can be either 1 or 0
  66  }
  67  
  68  // Format returns the canonical text representation of a UUID.
  69  // This implementation is optimized to not use fmt.
  70  // Example: 82e42f16-b6cc-4d5b-95f5-d403c4befd3d
  71  func format(u [16]byte) string {
  72  	// https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
  73  
  74  	var scratch [36]byte
  75  
  76  	hex.Encode(scratch[:8], u[0:4])
  77  	scratch[8] = dash
  78  	hex.Encode(scratch[9:13], u[4:6])
  79  	scratch[13] = dash
  80  	hex.Encode(scratch[14:18], u[6:8])
  81  	scratch[18] = dash
  82  	hex.Encode(scratch[19:23], u[8:10])
  83  	scratch[23] = dash
  84  	hex.Encode(scratch[24:], u[10:])
  85  
  86  	return string(scratch[:])
  87  }
  88