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