guid.go 3.9 KB

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