github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/uuid/uuid.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
    12  // Use of this source code is governed by a MIT-style
    13  // license that can be found in licenses/MIT-gofrs.txt.
    14  
    15  // This code originated in github.com/gofrs/uuid.
    16  
    17  package uuid
    18  
    19  import (
    20  	"encoding/binary"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"math"
    24  	"time"
    25  
    26  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    27  	"github.com/cockroachdb/errors"
    28  )
    29  
    30  // Size of a UUID in bytes.
    31  const Size = 16
    32  
    33  // UUID is an array type to represent the value of a UUID, as defined in RFC-4122.
    34  type UUID [Size]byte
    35  
    36  // UUID versions.
    37  const (
    38  	_  byte = iota
    39  	V1      // Version 1 (date-time and MAC address)
    40  	_       // Version 2 (date-time and MAC address, DCE security version)
    41  	V3      // Version 3 (namespace name-based)
    42  	V4      // Version 4 (random)
    43  	V5      // Version 5 (namespace name-based)
    44  )
    45  
    46  // UUID layout variants.
    47  const (
    48  	VariantNCS byte = iota
    49  	VariantRFC4122
    50  	VariantMicrosoft
    51  	VariantFuture
    52  )
    53  
    54  // Timestamp is the count of 100-nanosecond intervals since 00:00:00.00,
    55  // 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5
    56  // UUIDs since they don't have an embedded timestamp.
    57  type Timestamp uint64
    58  
    59  const _100nsPerSecond = 10000000
    60  
    61  // Time returns the UTC time.Time representation of a Timestamp
    62  func (t Timestamp) Time() (time.Time, error) {
    63  	secs := uint64(t) / _100nsPerSecond
    64  	nsecs := 100 * (uint64(t) % _100nsPerSecond)
    65  	return timeutil.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil
    66  }
    67  
    68  // TimestampFromV1 returns the Timestamp embedded within a V1 UUID.
    69  // Returns an error if the UUID is any version other than 1.
    70  func TimestampFromV1(u UUID) (Timestamp, error) {
    71  	if u.Version() != 1 {
    72  		err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version())
    73  		return 0, err
    74  	}
    75  	low := binary.BigEndian.Uint32(u[0:4])
    76  	mid := binary.BigEndian.Uint16(u[4:6])
    77  	hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff
    78  	return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil
    79  }
    80  
    81  // String parse helpers.
    82  var (
    83  	urnPrefix  = []byte("urn:uuid:")
    84  	byteGroups = []int{8, 4, 4, 4, 12}
    85  )
    86  
    87  // Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to
    88  // zero.
    89  var Nil = UUID{}
    90  
    91  // Predefined namespace UUIDs.
    92  var (
    93  	NamespaceDNS  = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
    94  	NamespaceURL  = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
    95  	NamespaceOID  = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
    96  	NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
    97  )
    98  
    99  // Version returns the algorithm version used to generate the UUID.
   100  func (u UUID) Version() byte {
   101  	return u[6] >> 4
   102  }
   103  
   104  // Variant returns the UUID layout variant.
   105  func (u UUID) Variant() byte {
   106  	switch {
   107  	case (u[8] >> 7) == 0x00:
   108  		return VariantNCS
   109  	case (u[8] >> 6) == 0x02:
   110  		return VariantRFC4122
   111  	case (u[8] >> 5) == 0x06:
   112  		return VariantMicrosoft
   113  	case (u[8] >> 5) == 0x07:
   114  		fallthrough
   115  	default:
   116  		return VariantFuture
   117  	}
   118  }
   119  
   120  // bytes returns a byte slice representation of the UUID. It incurs an
   121  // allocation if the return value escapes.
   122  func (u UUID) bytes() []byte {
   123  	return u[:]
   124  }
   125  
   126  // bytesMut returns a mutable byte slice representation of the UUID. Unlike
   127  // bytes, it does not necessarily incur an allocation if the return value
   128  // escapes. Instead, the return value escaping will cause the method's receiver
   129  // (and any struct that it is a part of) to escape.
   130  func (u *UUID) bytesMut() []byte {
   131  	return u[:]
   132  }
   133  
   134  // String returns a canonical RFC-4122 string representation of the UUID:
   135  // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
   136  func (u UUID) String() string {
   137  	buf := make([]byte, 36)
   138  	u.StringBytes(buf)
   139  	return string(buf)
   140  }
   141  
   142  // StringBytes writes the result of String directly into a buffer, which must
   143  // have a length of at least 36.
   144  func (u UUID) StringBytes(buf []byte) {
   145  	_ = buf[:36]
   146  	hex.Encode(buf[0:8], u[0:4])
   147  	buf[8] = '-'
   148  	hex.Encode(buf[9:13], u[4:6])
   149  	buf[13] = '-'
   150  	hex.Encode(buf[14:18], u[6:8])
   151  	buf[18] = '-'
   152  	hex.Encode(buf[19:23], u[8:10])
   153  	buf[23] = '-'
   154  	hex.Encode(buf[24:], u[10:])
   155  }
   156  
   157  // SetVersion sets the version bits.
   158  func (u *UUID) SetVersion(v byte) {
   159  	u[6] = (u[6] & 0x0f) | (v << 4)
   160  }
   161  
   162  // SetVariant sets the variant bits.
   163  func (u *UUID) SetVariant(v byte) {
   164  	switch v {
   165  	case VariantNCS:
   166  		u[8] = (u[8]&(0xff>>1) | (0x00 << 7))
   167  	case VariantRFC4122:
   168  		u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
   169  	case VariantMicrosoft:
   170  		u[8] = (u[8]&(0xff>>3) | (0x06 << 5))
   171  	case VariantFuture:
   172  		fallthrough
   173  	default:
   174  		u[8] = (u[8]&(0xff>>3) | (0x07 << 5))
   175  	}
   176  }
   177  
   178  // Must is a helper that wraps a call to a function returning (UUID, error)
   179  // and panics if the error is non-nil. It is intended for use in variable
   180  // initializations such as
   181  //  var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000"))
   182  func Must(u UUID, err error) UUID {
   183  	if err != nil {
   184  		panic(err)
   185  	}
   186  	return u
   187  }
   188  
   189  // DeterministicV4 overwrites this UUID with one computed deterministically to
   190  // evenly fill the space of possible V4 UUIDs. `n` represents how many UUIDs
   191  // will fill the space and `i` is an index into these `n` (and thus must be in
   192  // the range `[0,n)`). The resulting UUIDs will be unique, evenly-spaced, and
   193  // sorted.
   194  func (u *UUID) DeterministicV4(i, n uint64) {
   195  	if i >= n {
   196  		panic(errors.Errorf(`i must be in [0,%d) was %d`, n, i))
   197  	}
   198  	// V4 uuids are generated by simply filling 16 bytes with random data (then
   199  	// setting the version and variant), so they're randomly distributed through
   200  	// the space of possible values. This also means they're roughly evenly
   201  	// distributed. We guarantee these values to be similarly distributed.
   202  	//
   203  	// So, space the row indexes out to fill the space of the integers
   204  	// representable with 8 bytes. Then, because this involves some floats (and
   205  	// who knows what kind of crazy rounding things can happen when floats are
   206  	// involved), make sure they're unique by sticking the index
   207  	// in the lower 8 bytes. Note that we need to use BigEndian encodings to keep
   208  	// the uuids sorted in the same order as the ints.
   209  	spacing := uint64(float64(i) * float64(math.MaxUint64) / float64(n))
   210  	binary.BigEndian.PutUint64(u[0:8], spacing)
   211  	binary.BigEndian.PutUint64(u[8:16], i)
   212  	u.SetVersion(V4)
   213  	u.SetVariant(VariantRFC4122)
   214  }