github.com/spi-ca/misc@v1.0.1/types/uuid.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"crypto/subtle"
     7  	"database/sql/driver"
     8  	"encoding/hex"
     9  	"errors"
    10  	"fmt"
    11  )
    12  
    13  type UUID [16]byte
    14  
    15  var (
    16  	zeroUUID UUID
    17  )
    18  
    19  func (u UUID) Marshal() ([]byte, error) {
    20  	return u[:], nil
    21  }
    22  
    23  func (u UUID) MarshalTo(buf []byte) (n int, err error) {
    24  	if len(u) == 0 {
    25  		return 0, nil
    26  	}
    27  	copy(buf, u[:])
    28  	return len(u), nil
    29  }
    30  func (u *UUID) Unmarshal(buf []byte) error {
    31  	if len(buf) != 16 {
    32  		return fmt.Errorf("invalid UUID (got %d bytes)", len(buf))
    33  	}
    34  	copy(u[:], buf)
    35  	return nil
    36  }
    37  
    38  func (u UUID) Compare(other UUID) int {
    39  	return bytes.Compare(u[:], other[:])
    40  }
    41  
    42  func (u UUID) Equal(other UUID) bool {
    43  	return subtle.ConstantTimeCompare(u[:], other[:]) == 1
    44  }
    45  func (u *UUID) UnmarshalJSON(from []byte) error {
    46  	quote := []byte("\"")
    47  	quoteSize := len(quote)
    48  
    49  	if len(from) < quoteSize*2 {
    50  		return errors.New("invalid quote notation")
    51  	}
    52  
    53  	if !bytes.HasPrefix(from, quote) || !bytes.HasSuffix(from, quote) {
    54  		return errors.New("invalid quote notation")
    55  	} else if _, err := hex.Decode(u[:], from[quoteSize:len(from)-quoteSize]); err != nil {
    56  		return err
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  func (u UUID) MarshalJSON() ([]byte, error) {
    63  	var buffer bytes.Buffer
    64  	buffer.WriteRune('"')
    65  	buffer.WriteString(hex.EncodeToString(u[:]))
    66  	buffer.WriteRune('"')
    67  	return buffer.Bytes(), nil
    68  }
    69  
    70  func (u *UUID) Size() int {
    71  	if u == nil {
    72  		return 0
    73  	}
    74  	if len(*u) == 0 {
    75  		return 0
    76  	}
    77  	return 16
    78  }
    79  
    80  func NewUUID() (u UUID) {
    81  	newObj := UUID{}
    82  	newObj.Random()
    83  	return newObj
    84  }
    85  
    86  func (u *UUID) UUIDFromHexString(buf []byte) error {
    87  	hexBuf := make([]byte, hex.DecodedLen(len(buf)))
    88  	if n, err := hex.Decode(hexBuf, buf); err != nil {
    89  		return err
    90  	} else {
    91  		hexBuf = hexBuf[:n]
    92  	}
    93  	if err := u.Unmarshal(hexBuf); err != nil {
    94  		return err
    95  	}
    96  	return nil
    97  }
    98  
    99  func (u UUID) ToHexString() string {
   100  	return hex.EncodeToString(u[:])
   101  }
   102  
   103  // Scan implements the Scanner interface.
   104  func (u *UUID) Scan(src any) error {
   105  	if src == nil {
   106  		return nil
   107  	}
   108  
   109  	b, ok := src.([]byte)
   110  	if !ok {
   111  		return errors.New("Scan source was not []bytes")
   112  	}
   113  
   114  	return u.UUIDFromHexString(b)
   115  }
   116  
   117  // Value implements the driver Valuer interface.
   118  func (u UUID) Value() (driver.Value, error) {
   119  	return u.ToHexString(), nil
   120  }
   121  
   122  func (u *UUID) Random() *UUID {
   123  	_, _ = rand.Read(u[:])
   124  	u[6] = (u[6] & 0x0f) | 0x40 // Version 4
   125  	u[8] = (u[8] & 0x3f) | 0x80 // Variant is 10
   126  	return u
   127  }
   128  
   129  func (u *UUID) Clear() *UUID {
   130  	copy(u[:], zeroUUID[:])
   131  	return u
   132  }
   133  
   134  func (u UUID) IsZero() bool {
   135  	return subtle.ConstantTimeCompare(zeroUUID[:], u[:]) == 1
   136  }