github.com/gogf/gf@v1.16.9/util/guid/guid_string.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package guid
     8  
     9  import (
    10  	"os"
    11  	"strconv"
    12  	"time"
    13  
    14  	"github.com/gogf/gf/container/gtype"
    15  	"github.com/gogf/gf/encoding/ghash"
    16  	"github.com/gogf/gf/net/gipv4"
    17  	"github.com/gogf/gf/util/grand"
    18  )
    19  
    20  const (
    21  	sequenceMax   = uint32(46655)                          // Sequence max("zzz").
    22  	randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // Random chars string(36 bytes).
    23  )
    24  
    25  var (
    26  	sequence     gtype.Uint32 // Sequence for unique purpose of current process.
    27  	macAddrStr   = "0000000"  // MAC addresses hash result in 7 bytes.
    28  	processIdStr = "0000"     // Process id in 4 bytes.
    29  )
    30  
    31  // init initializes several fixed local variable.
    32  func init() {
    33  	// MAC addresses hash result in 7 bytes.
    34  	macs, _ := gipv4.GetMacArray()
    35  	if len(macs) > 0 {
    36  		var macAddrBytes []byte
    37  		for _, mac := range macs {
    38  			macAddrBytes = append(macAddrBytes, []byte(mac)...)
    39  		}
    40  		b := []byte{'0', '0', '0', '0', '0', '0', '0'}
    41  		s := strconv.FormatUint(uint64(ghash.DJBHash(macAddrBytes)), 36)
    42  		copy(b, s)
    43  		macAddrStr = string(b)
    44  	}
    45  	// Process id in 4 bytes.
    46  	{
    47  		b := []byte{'0', '0', '0', '0'}
    48  		s := strconv.FormatInt(int64(os.Getpid()), 36)
    49  		copy(b, s)
    50  		processIdStr = string(b)
    51  	}
    52  }
    53  
    54  // S creates and returns a global unique string in 32 bytes that meets most common
    55  // usages without strict UUID algorithm. It returns an unique string using default
    56  // unique algorithm if no `data` is given.
    57  //
    58  // The specified `data` can be no more than 2 parts. No matter how long each of the
    59  // `data` size is, each of them will be hashed into 7 bytes as part of the result.
    60  // If given `data` parts is less than 2, the leftover size of the result bytes will
    61  // be token by random string.
    62  //
    63  // The returned string is composed with:
    64  // 1. Default:    MAC(7) + PID(4) + TimestampNano(12) + Sequence(3) + RandomString(6)
    65  // 2. CustomData: Data(7/14) + TimestampNano(12) + Sequence(3) + RandomString(3/10)
    66  //
    67  // Note that:
    68  // 1. The returned length is fixed to 32 bytes for performance purpose.
    69  // 2. The custom parameter `data` composed should have unique attribute in your
    70  //    business situation.
    71  func S(data ...[]byte) string {
    72  	var (
    73  		b       = make([]byte, 32)
    74  		nanoStr = strconv.FormatInt(time.Now().UnixNano(), 36)
    75  	)
    76  	if len(data) == 0 {
    77  		copy(b, macAddrStr)
    78  		copy(b[7:], processIdStr)
    79  		copy(b[11:], nanoStr)
    80  		copy(b[23:], getSequence())
    81  		copy(b[26:], getRandomStr(6))
    82  	} else if len(data) <= 2 {
    83  		n := 0
    84  		for i, v := range data {
    85  			// Ignore empty data item bytes.
    86  			if len(v) > 0 {
    87  				copy(b[i*7:], getDataHashStr(v))
    88  				n += 7
    89  			}
    90  		}
    91  		copy(b[n:], nanoStr)
    92  		copy(b[n+12:], getSequence())
    93  		copy(b[n+12+3:], getRandomStr(32-n-12-3))
    94  	} else {
    95  		panic("too many data parts, it should be no more than 2 parts")
    96  	}
    97  	return string(b)
    98  }
    99  
   100  // getSequence increases and returns the sequence string in 3 bytes.
   101  // The sequence is less than "zzz"(46655).
   102  func getSequence() []byte {
   103  	b := []byte{'0', '0', '0'}
   104  	s := strconv.FormatUint(uint64(sequence.Add(1)%sequenceMax), 36)
   105  	copy(b, s)
   106  	return b
   107  }
   108  
   109  // getRandomStr randomly picks and returns `n` count of chars from randomStrBase.
   110  func getRandomStr(n int) []byte {
   111  	if n <= 0 {
   112  		return []byte{}
   113  	}
   114  	var (
   115  		b           = make([]byte, n)
   116  		numberBytes = grand.B(n)
   117  	)
   118  	for i := range b {
   119  		b[i] = randomStrBase[numberBytes[i]%36]
   120  	}
   121  	return b
   122  }
   123  
   124  // getDataHashStr creates and returns hash bytes in 7 bytes with given data bytes.
   125  func getDataHashStr(data []byte) []byte {
   126  	b := []byte{'0', '0', '0', '0', '0', '0', '0'}
   127  	s := strconv.FormatUint(uint64(ghash.DJBHash(data)), 36)
   128  	copy(b, s)
   129  	return b
   130  }