guid_string.go 3.8 KB

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