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 }