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 }