github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/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/wangyougui/gf/v2/container/gtype" 16 "github.com/wangyougui/gf/v2/encoding/ghash" 17 "github.com/wangyougui/gf/v2/errors/gcode" 18 "github.com/wangyougui/gf/v2/errors/gerror" 19 "github.com/wangyougui/gf/v2/net/gipv4" 20 "github.com/wangyougui/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 }