git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/uuid/uuid.go (about)

     1  // Copyright 2018 Google Inc.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package uuid
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/rand"
    10  	"encoding/hex"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"strings"
    15  	"sync"
    16  )
    17  
    18  const (
    19  	Size = 16
    20  )
    21  
    22  // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
    23  // 4122.
    24  type UUID [Size]byte
    25  
    26  // A Version represents a UUID's version.
    27  type Version byte
    28  
    29  // A Variant represents a UUID's variant.
    30  type Variant byte
    31  
    32  // Constants returned by Variant.
    33  const (
    34  	Invalid   = Variant(iota) // Invalid UUID
    35  	RFC4122                   // The variant specified in RFC4122
    36  	Reserved                  // Reserved, NCS backward compatibility.
    37  	Microsoft                 // Reserved, Microsoft Corporation backward compatibility.
    38  	Future                    // Reserved for future definition.
    39  )
    40  
    41  const randPoolSize = 16 * 16
    42  
    43  var (
    44  	rander      = rand.Reader // random function
    45  	poolEnabled = false
    46  	poolMu      sync.Mutex
    47  	poolPos     = randPoolSize     // protected with poolMu
    48  	pool        [randPoolSize]byte // protected with poolMu
    49  )
    50  
    51  type invalidLengthError struct{ len int }
    52  
    53  func (err invalidLengthError) Error() string {
    54  	return fmt.Sprintf("invalid UUID length: %d", err.len)
    55  }
    56  
    57  // IsInvalidLengthError is matcher function for custom error invalidLengthError
    58  func IsInvalidLengthError(err error) bool {
    59  	_, ok := err.(invalidLengthError)
    60  	return ok
    61  }
    62  
    63  // Parse decodes s into a UUID or returns an error if it cannot be parsed.  Both
    64  // the standard UUID forms defined in RFC 4122
    65  // (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
    66  // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded.  In addition,
    67  // Parse accepts non-standard strings such as the raw hex encoding
    68  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings,
    69  // e.g.  {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}.  Only the middle 36 bytes are
    70  // examined in the latter case.  Parse should not be used to validate strings as
    71  // it parses non-standard encodings as indicated above.
    72  func Parse(s string) (UUID, error) {
    73  	var uuid UUID
    74  	switch len(s) {
    75  	// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    76  	case 36:
    77  
    78  	// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    79  	case 36 + 9:
    80  		if !strings.EqualFold(s[:9], "urn:uuid:") {
    81  			return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
    82  		}
    83  		s = s[9:]
    84  
    85  	// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
    86  	case 36 + 2:
    87  		s = s[1:]
    88  
    89  	// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    90  	case 32:
    91  		var ok bool
    92  		for i := range uuid {
    93  			uuid[i], ok = xtob(s[i*2], s[i*2+1])
    94  			if !ok {
    95  				return uuid, errors.New("invalid UUID format")
    96  			}
    97  		}
    98  		return uuid, nil
    99  	default:
   100  		return uuid, invalidLengthError{len(s)}
   101  	}
   102  	// s is now at least 36 bytes long
   103  	// it must be of the form  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   104  	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
   105  		return uuid, errors.New("invalid UUID format")
   106  	}
   107  	for i, x := range [16]int{
   108  		0, 2, 4, 6,
   109  		9, 11,
   110  		14, 16,
   111  		19, 21,
   112  		24, 26, 28, 30, 32, 34,
   113  	} {
   114  		v, ok := xtob(s[x], s[x+1])
   115  		if !ok {
   116  			return uuid, errors.New("invalid UUID format")
   117  		}
   118  		uuid[i] = v
   119  	}
   120  	return uuid, nil
   121  }
   122  
   123  // ParseBytes is like Parse, except it parses a byte slice instead of a string.
   124  func ParseBytes(b []byte) (UUID, error) {
   125  	var uuid UUID
   126  	switch len(b) {
   127  	case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   128  	case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   129  		if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
   130  			return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
   131  		}
   132  		b = b[9:]
   133  	case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
   134  		b = b[1:]
   135  	case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
   136  		var ok bool
   137  		for i := 0; i < 32; i += 2 {
   138  			uuid[i/2], ok = xtob(b[i], b[i+1])
   139  			if !ok {
   140  				return uuid, errors.New("invalid UUID format")
   141  			}
   142  		}
   143  		return uuid, nil
   144  	default:
   145  		return uuid, invalidLengthError{len(b)}
   146  	}
   147  	// s is now at least 36 bytes long
   148  	// it must be of the form  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   149  	if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
   150  		return uuid, errors.New("invalid UUID format")
   151  	}
   152  	for i, x := range [16]int{
   153  		0, 2, 4, 6,
   154  		9, 11,
   155  		14, 16,
   156  		19, 21,
   157  		24, 26, 28, 30, 32, 34,
   158  	} {
   159  		v, ok := xtob(b[x], b[x+1])
   160  		if !ok {
   161  			return uuid, errors.New("invalid UUID format")
   162  		}
   163  		uuid[i] = v
   164  	}
   165  	return uuid, nil
   166  }
   167  
   168  // MustParse is like Parse but panics if the string cannot be parsed.
   169  // It simplifies safe initialization of global variables holding compiled UUIDs.
   170  func MustParse(s string) UUID {
   171  	uuid, err := Parse(s)
   172  	if err != nil {
   173  		panic(`uuid: Parse(` + s + `): ` + err.Error())
   174  	}
   175  	return uuid
   176  }
   177  
   178  // FromBytes creates a new UUID from a byte slice. Returns an error if the slice
   179  // does not have a length of 16. The bytes are copied from the slice.
   180  func FromBytes(b []byte) (uuid UUID, err error) {
   181  	err = uuid.UnmarshalBinary(b)
   182  	return uuid, err
   183  }
   184  
   185  // Must returns uuid if err is nil and panics otherwise.
   186  func Must(uuid UUID, err error) UUID {
   187  	if err != nil {
   188  		panic(err)
   189  	}
   190  	return uuid
   191  }
   192  
   193  // Validate returns an error if s is not a properly formatted UUID in one of the following formats:
   194  //
   195  //	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   196  //	urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   197  //	xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
   198  //	{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
   199  //
   200  // It returns an error if the format is invalid, otherwise nil.
   201  func Validate(s string) error {
   202  	switch len(s) {
   203  	// Standard UUID format
   204  	case 36:
   205  
   206  	// UUID with "urn:uuid:" prefix
   207  	case 36 + 9:
   208  		if !strings.EqualFold(s[:9], "urn:uuid:") {
   209  			return fmt.Errorf("invalid urn prefix: %q", s[:9])
   210  		}
   211  		s = s[9:]
   212  
   213  	// UUID enclosed in braces
   214  	case 36 + 2:
   215  		if s[0] != '{' || s[len(s)-1] != '}' {
   216  			return fmt.Errorf("invalid bracketed UUID format")
   217  		}
   218  		s = s[1 : len(s)-1]
   219  
   220  	// UUID without hyphens
   221  	case 32:
   222  		for i := 0; i < len(s); i += 2 {
   223  			_, ok := xtob(s[i], s[i+1])
   224  			if !ok {
   225  				return errors.New("invalid UUID format")
   226  			}
   227  		}
   228  
   229  	default:
   230  		return invalidLengthError{len(s)}
   231  	}
   232  
   233  	// Check for standard UUID format
   234  	if len(s) == 36 {
   235  		if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
   236  			return errors.New("invalid UUID format")
   237  		}
   238  		for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
   239  			if _, ok := xtob(s[x], s[x+1]); !ok {
   240  				return errors.New("invalid UUID format")
   241  			}
   242  		}
   243  	}
   244  
   245  	return nil
   246  }
   247  
   248  // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
   249  // , or "" if uuid is invalid.
   250  func (uuid UUID) String() string {
   251  	var buf [36]byte
   252  	encodeHex(buf[:], uuid)
   253  	return string(buf[:])
   254  }
   255  
   256  func (uuid UUID) Equal(other UUID) bool {
   257  	return bytes.Equal(uuid[:], other[:])
   258  }
   259  
   260  // URN returns the RFC 2141 URN form of uuid,
   261  // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,  or "" if uuid is invalid.
   262  func (uuid UUID) URN() string {
   263  	var buf [36 + 9]byte
   264  	copy(buf[:], "urn:uuid:")
   265  	encodeHex(buf[9:], uuid)
   266  	return string(buf[:])
   267  }
   268  
   269  func encodeHex(dst []byte, uuid UUID) {
   270  	hex.Encode(dst, uuid[:4])
   271  	dst[8] = '-'
   272  	hex.Encode(dst[9:13], uuid[4:6])
   273  	dst[13] = '-'
   274  	hex.Encode(dst[14:18], uuid[6:8])
   275  	dst[18] = '-'
   276  	hex.Encode(dst[19:23], uuid[8:10])
   277  	dst[23] = '-'
   278  	hex.Encode(dst[24:], uuid[10:])
   279  }
   280  
   281  // Variant returns the variant encoded in uuid.
   282  func (uuid UUID) Variant() Variant {
   283  	switch {
   284  	case (uuid[8] & 0xc0) == 0x80:
   285  		return RFC4122
   286  	case (uuid[8] & 0xe0) == 0xc0:
   287  		return Microsoft
   288  	case (uuid[8] & 0xe0) == 0xe0:
   289  		return Future
   290  	default:
   291  		return Reserved
   292  	}
   293  }
   294  
   295  // Version returns the version of uuid.
   296  func (uuid UUID) Version() Version {
   297  	return Version(uuid[6] >> 4)
   298  }
   299  
   300  func (v Version) String() string {
   301  	if v > 15 {
   302  		return fmt.Sprintf("BAD_VERSION_%d", v)
   303  	}
   304  	return fmt.Sprintf("VERSION_%d", v)
   305  }
   306  
   307  func (v Variant) String() string {
   308  	switch v {
   309  	case RFC4122:
   310  		return "RFC4122"
   311  	case Reserved:
   312  		return "Reserved"
   313  	case Microsoft:
   314  		return "Microsoft"
   315  	case Future:
   316  		return "Future"
   317  	case Invalid:
   318  		return "Invalid"
   319  	}
   320  	return fmt.Sprintf("BadVariant%d", int(v))
   321  }
   322  
   323  // SetRand sets the random number generator to r, which implements io.Reader.
   324  // If r.Read returns an error when the package requests random data then
   325  // a panic will be issued.
   326  //
   327  // Calling SetRand with nil sets the random number generator to the default
   328  // generator.
   329  func SetRand(r io.Reader) {
   330  	if r == nil {
   331  		rander = rand.Reader
   332  		return
   333  	}
   334  	rander = r
   335  }
   336  
   337  // EnableRandPool enables internal randomness pool used for Random
   338  // (Version 4) UUID generation. The pool contains random bytes read from
   339  // the random number generator on demand in batches. Enabling the pool
   340  // may improve the UUID generation throughput significantly.
   341  //
   342  // Since the pool is stored on the Go heap, this feature may be a bad fit
   343  // for security sensitive applications.
   344  //
   345  // Both EnableRandPool and DisableRandPool are not thread-safe and should
   346  // only be called when there is no possibility that New or any other
   347  // UUID Version 4 generation function will be called concurrently.
   348  func EnableRandPool() {
   349  	poolEnabled = true
   350  }
   351  
   352  // DisableRandPool disables the randomness pool if it was previously
   353  // enabled with EnableRandPool.
   354  //
   355  // Both EnableRandPool and DisableRandPool are not thread-safe and should
   356  // only be called when there is no possibility that New or any other
   357  // UUID Version 4 generation function will be called concurrently.
   358  func DisableRandPool() {
   359  	poolEnabled = false
   360  	defer poolMu.Unlock()
   361  	poolMu.Lock()
   362  	poolPos = randPoolSize
   363  }
   364  
   365  // UUIDs is a slice of UUID types.
   366  type UUIDs []UUID
   367  
   368  // Strings returns a string slice containing the string form of each UUID in uuids.
   369  func (uuids UUIDs) Strings() []string {
   370  	var uuidStrs = make([]string, len(uuids))
   371  	for i, uuid := range uuids {
   372  		uuidStrs[i] = uuid.String()
   373  	}
   374  	return uuidStrs
   375  }