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  }