github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/cos/uuid.go (about) 1 // Package cos provides common low-level types and utilities for all aistore projects 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package cos 6 7 import ( 8 "fmt" 9 "strconv" 10 11 "github.com/NVIDIA/aistore/cmn/atomic" 12 "github.com/OneOfOne/xxhash" 13 "github.com/teris-io/shortid" 14 ) 15 16 const LenShortID = 9 // UUID length, as per https://github.com/teris-io/shortid#id-length 17 18 const ( 19 // Alphabet for generating UUIDs similar to the shortid.DEFAULT_ABC 20 // NOTE: len(uuidABC) > 0x3f - see GenTie() 21 uuidABC = "-5nZJDft6LuzsjGNpPwY7rQa39vehq4i1cV2FROo8yHSlC0BUEdWbIxMmTgKXAk_" 22 23 lenDaemonID = 8 // via cryptographic rand 24 lenTooLongID = 32 // suspiciously long 25 26 lenK8sProxyID = 13 27 ) 28 29 const ( 30 OnlyNice = "may only contain letters, numbers, dashes (-), underscores (_), and dots (.)" 31 OnlyPlus = OnlyNice + ", and dots (.)" 32 ) 33 34 var ( 35 sid *shortid.Shortid 36 rtie atomic.Uint32 37 ) 38 39 func InitShortID(seed uint64) { 40 sid = shortid.MustNew(4 /*worker*/, uuidABC, seed) 41 } 42 43 // 44 // UUID 45 // 46 47 // compare with xreg.GenBEID 48 func GenUUID() (uuid string) { 49 var h, t string 50 uuid = sid.MustGenerate() 51 if !isAlpha(uuid[0]) { 52 tie := int(rtie.Add(1)) 53 h = string(rune('A' + tie%26)) 54 } 55 c := uuid[len(uuid)-1] 56 if c == '-' || c == '_' { 57 tie := int(rtie.Add(1)) 58 t = string(rune('a' + tie%26)) 59 } 60 return h + uuid + t 61 } 62 63 // "best-effort ID" - to independently and locally generate globally unique ID 64 // called by xreg.GenBEID 65 func GenBEID(val uint64, l int) string { 66 b := make([]byte, l) 67 for i := range l { 68 if idx := int(val & letterIdxMask); idx < LenRunes { 69 b[i] = LetterRunes[idx] 70 } else { 71 b[i] = LetterRunes[idx-LenRunes] 72 } 73 val >>= letterIdxBits 74 } 75 return UnsafeS(b) 76 } 77 78 func IsValidUUID(uuid string) bool { 79 return len(uuid) >= LenShortID && IsAlphaNice(uuid) 80 } 81 82 func ValidateNiceID(id string, minlen int, tag string) (err error) { 83 if len(id) < minlen { 84 return fmt.Errorf("%s %q is too short", tag, id) 85 } 86 if len(id) >= lenTooLongID { 87 return fmt.Errorf("%s %q is too long", tag, id) 88 } 89 if !IsAlphaNice(id) { 90 err = fmt.Errorf("%s %q is invalid: must start with a letter and can only contain [A-Za-z0-9-_]", tag, id) 91 } 92 return 93 } 94 95 // 96 // Daemon ID 97 // 98 99 func GenDaemonID() string { return CryptoRandS(lenDaemonID) } 100 101 func HashK8sProxyID(nodeName string) (pid string) { 102 digest := xxhash.Checksum64S(UnsafeB(nodeName), MLCG32) 103 pid = strconv.FormatUint(digest, 36) 104 if pid[0] >= '0' && pid[0] <= '9' { 105 pid = pid[1:] 106 } 107 if l := lenK8sProxyID - len(pid); l > 0 { 108 return GenBEID(digest, l) + pid 109 } 110 return pid 111 } 112 113 func ValidateDaemonID(id string) error { return ValidateNiceID(id, lenDaemonID, "node ID") } 114 115 // (when config.TestingEnv) 116 func GenTestingDaemonID(suffix string) string { 117 l := Max(lenDaemonID-len(suffix), 3) 118 return CryptoRandS(l) + suffix 119 } 120 121 // 122 // utility functions 123 // 124 125 func isAlpha(c byte) bool { 126 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 127 } 128 129 // letters and numbers w/ '-' and '_' permitted with limitations (below) 130 // (see OnlyNice above) 131 func IsAlphaNice(s string) bool { 132 l := len(s) 133 for i, c := range s { 134 if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') { 135 continue 136 } 137 if c != '-' && c != '_' { 138 return false 139 } 140 if i == 0 || i == l-1 { 141 return false 142 } 143 } 144 return true 145 } 146 147 // alpha-numeric++ including letters, numbers, dashes (-), and underscores (_) 148 // period (.) is allowed except for '..' 149 // (see OnlyPlus above) 150 func IsAlphaPlus(s string) bool { 151 for i, c := range s { 152 if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' { 153 continue 154 } 155 if c != '.' { 156 return false 157 } 158 if i < len(s)-1 && s[i+1] == '.' { 159 return false 160 } 161 } 162 return true 163 } 164 165 // 3-letter tie breaker (fast) 166 func GenTie() string { 167 tie := rtie.Add(1) 168 b0 := uuidABC[tie&0x3f] 169 b1 := uuidABC[-tie&0x3f] 170 b2 := uuidABC[(tie>>2)&0x3f] 171 return string([]byte{b0, b1, b2}) 172 }