github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/pkg/uuid/uuid.go (about)

     1  // Copyright (c) 2017 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  // Package uuid can be used to generate 128 bit UUIDs compatible with
     7  // rfc4122.  Currently, only version 4 UUIDs, UUIDs generated from random
     8  // data, can be created.  The package includes functions for generating
     9  // UUIDs and for converting them to and from strings.
    10  package uuid
    11  
    12  import (
    13  	"crypto/rand"
    14  	"encoding/binary"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"strconv"
    19  	"strings"
    20  )
    21  
    22  // UUID represents a single 128 bit UUID as an array of 16 bytes.
    23  type UUID [16]byte
    24  
    25  // UUIDRegex defines a pattern for validating UUIDs
    26  const UUIDRegex = "[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[8|9|aA|bB][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}"
    27  
    28  var (
    29  	// ErrUUIDInvalid indicates that a UIID is invalid.  Currently,
    30  	// returned by uuid.Parse if the string passed to this function
    31  	// does not contain a valid UUID.
    32  	ErrUUIDInvalid = errors.New("invalid uuid")
    33  )
    34  
    35  func encode4bytes(n uint64, b []byte) {
    36  	binary.BigEndian.PutUint32(b, uint32(n))
    37  }
    38  
    39  func encode2bytes(n uint64, b []byte) {
    40  	binary.BigEndian.PutUint16(b, uint16(n))
    41  }
    42  
    43  func encode1byte(n uint64, b []byte) {
    44  	b[0] = uint8(n)
    45  }
    46  
    47  func encode6bytes(n uint64, b []byte) {
    48  	d := make([]byte, 8)
    49  	binary.BigEndian.PutUint64(d, n)
    50  	copy(b, d[2:])
    51  }
    52  
    53  func stringToBE(s string, b []byte, f func(uint64, []byte)) error {
    54  	num, err := strconv.ParseUint(s, 16, len(s)*4)
    55  	if err != nil {
    56  		return ErrUUIDInvalid
    57  	}
    58  	f(num, b)
    59  	return nil
    60  }
    61  
    62  // Parse returns the binary encoding of the UUID passed in the s parameter.
    63  // The error ErrUUIDInvalid will be returned if s does not represent a valid
    64  // UUID.
    65  func Parse(s string) (UUID, error) {
    66  	var uuid UUID
    67  	var segmentSizes = [...]int{8, 4, 4, 4, 12}
    68  
    69  	segments := strings.Split(s, "-")
    70  	if len(segments) != len(segmentSizes) {
    71  		return uuid, ErrUUIDInvalid
    72  	}
    73  
    74  	for i, l := range segmentSizes {
    75  		if len(segments[i]) != l {
    76  			return uuid, ErrUUIDInvalid
    77  		}
    78  	}
    79  
    80  	if err := stringToBE(segments[0], uuid[:4], encode4bytes); err != nil {
    81  		return uuid, err
    82  	}
    83  	if err := stringToBE(segments[1], uuid[4:6], encode2bytes); err != nil {
    84  		return uuid, err
    85  	}
    86  	if err := stringToBE(segments[2], uuid[6:8], encode2bytes); err != nil {
    87  		return uuid, err
    88  	}
    89  	if err := stringToBE(segments[3][:2], uuid[8:9], encode1byte); err != nil {
    90  		return uuid, err
    91  	}
    92  	if err := stringToBE(segments[3][2:], uuid[9:10], encode1byte); err != nil {
    93  		return uuid, err
    94  	}
    95  	if err := stringToBE(segments[4], uuid[10:], encode6bytes); err != nil {
    96  		return uuid, err
    97  	}
    98  
    99  	return uuid, nil
   100  }
   101  
   102  // Generate generates a new v4 UUID, i.e., a random UUID.
   103  func Generate() UUID {
   104  	var u UUID
   105  
   106  	_, err := io.ReadFull(rand.Reader, u[:])
   107  	if err != nil {
   108  		panic(fmt.Errorf("Unable to read random data : %v", err))
   109  	}
   110  
   111  	u[6] = (u[6] & 0x0f) | 0x40
   112  	u[8] = (u[8] & 0x3f) | 0x80
   113  
   114  	return u
   115  }
   116  
   117  func (u UUID) String() string {
   118  	timeLow := binary.BigEndian.Uint32(u[:4])
   119  	timeMid := binary.BigEndian.Uint16(u[4:6])
   120  	timeHi := binary.BigEndian.Uint16(u[6:8])
   121  	clkSeqHi := u[8]
   122  	clkSeqLow := u[9]
   123  	buf := make([]byte, 8)
   124  	copy(buf[2:], u[10:])
   125  	node := binary.BigEndian.Uint64(buf)
   126  
   127  	return fmt.Sprintf("%08x-%04x-%04x-%02x%02x-%012x",
   128  		timeLow, timeMid, timeHi, clkSeqHi, clkSeqLow, node)
   129  }