github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/pkg/stringid/stringid.go (about) 1 // Package stringid provides helper functions for dealing with string identifiers 2 package stringid 3 4 import ( 5 cryptorand "crypto/rand" 6 "encoding/hex" 7 "fmt" 8 "io" 9 "math" 10 "math/big" 11 "math/rand" 12 "regexp" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 const shortLen = 12 19 20 var ( 21 validShortID = regexp.MustCompile("^[a-f0-9]{12}$") 22 validHex = regexp.MustCompile(`^[a-f0-9]{64}$`) 23 ) 24 25 // IsShortID determines if an arbitrary string *looks like* a short ID. 26 func IsShortID(id string) bool { 27 return validShortID.MatchString(id) 28 } 29 30 // TruncateID returns a shorthand version of a string identifier for convenience. 31 // A collision with other shorthands is very unlikely, but possible. 32 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller 33 // will need to use a longer prefix, or the full-length Id. 34 func TruncateID(id string) string { 35 if i := strings.IndexRune(id, ':'); i >= 0 { 36 id = id[i+1:] 37 } 38 if len(id) > shortLen { 39 id = id[:shortLen] 40 } 41 return id 42 } 43 44 func generateID(r io.Reader) string { 45 b := make([]byte, 32) 46 for { 47 if _, err := io.ReadFull(r, b); err != nil { 48 panic(err) // This shouldn't happen 49 } 50 id := hex.EncodeToString(b) 51 // if we try to parse the truncated for as an int and we don't have 52 // an error then the value is all numeric and causes issues when 53 // used as a hostname. ref #3869 54 if _, err := strconv.ParseInt(TruncateID(id), 10, 64); err == nil { 55 continue 56 } 57 return id 58 } 59 } 60 61 // GenerateRandomID returns a unique id. 62 func GenerateRandomID() string { 63 return generateID(cryptorand.Reader) 64 } 65 66 // GenerateNonCryptoID generates unique id without using cryptographically 67 // secure sources of random. 68 // It helps you to save entropy. 69 func GenerateNonCryptoID() string { 70 return generateID(readerFunc(rand.Read)) 71 } 72 73 // ValidateID checks whether an ID string is a valid image ID. 74 func ValidateID(id string) error { 75 if ok := validHex.MatchString(id); !ok { 76 return fmt.Errorf("image ID %q is invalid", id) 77 } 78 return nil 79 } 80 81 func init() { 82 // safely set the seed globally so we generate random ids. Tries to use a 83 // crypto seed before falling back to time. 84 var seed int64 85 if cryptoseed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64)); err != nil { 86 // This should not happen, but worst-case fallback to time-based seed. 87 seed = time.Now().UnixNano() 88 } else { 89 seed = cryptoseed.Int64() 90 } 91 92 rand.Seed(seed) 93 } 94 95 type readerFunc func(p []byte) (int, error) 96 97 func (fn readerFunc) Read(p []byte) (int, error) { 98 return fn(p) 99 }