id.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package id
  2. import (
  3. "crypto/rand"
  4. "encoding/hex"
  5. "fmt"
  6. "strings"
  7. "sync"
  8. "time"
  9. )
  10. const (
  11. PrefixSession = "ses"
  12. PrefixMessage = "msg"
  13. PrefixUser = "usr"
  14. PrefixPart = "prt"
  15. )
  16. const length = 26
  17. var (
  18. lastTimestamp int64
  19. counter int64
  20. mu sync.Mutex
  21. )
  22. type Prefix string
  23. const (
  24. Session Prefix = PrefixSession
  25. Message Prefix = PrefixMessage
  26. User Prefix = PrefixUser
  27. Part Prefix = PrefixPart
  28. )
  29. func ValidatePrefix(id string, prefix Prefix) bool {
  30. return strings.HasPrefix(id, string(prefix))
  31. }
  32. func Ascending(prefix Prefix, given ...string) string {
  33. return generateID(prefix, false, given...)
  34. }
  35. func Descending(prefix Prefix, given ...string) string {
  36. return generateID(prefix, true, given...)
  37. }
  38. func generateID(prefix Prefix, descending bool, given ...string) string {
  39. if len(given) > 0 && given[0] != "" {
  40. if !strings.HasPrefix(given[0], string(prefix)) {
  41. panic(fmt.Sprintf("ID %s does not start with %s", given[0], string(prefix)))
  42. }
  43. return given[0]
  44. }
  45. return generateNewID(prefix, descending)
  46. }
  47. func randomBase62(length int) string {
  48. const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  49. result := make([]byte, length)
  50. bytes := make([]byte, length)
  51. rand.Read(bytes)
  52. for i := 0; i < length; i++ {
  53. result[i] = chars[bytes[i]%62]
  54. }
  55. return string(result)
  56. }
  57. func generateNewID(prefix Prefix, descending bool) string {
  58. mu.Lock()
  59. defer mu.Unlock()
  60. currentTimestamp := time.Now().UnixMilli()
  61. if currentTimestamp != lastTimestamp {
  62. lastTimestamp = currentTimestamp
  63. counter = 0
  64. }
  65. counter++
  66. now := uint64(currentTimestamp)*0x1000 + uint64(counter)
  67. if descending {
  68. now = ^now
  69. }
  70. timeBytes := make([]byte, 6)
  71. for i := 0; i < 6; i++ {
  72. timeBytes[i] = byte((now >> (40 - 8*i)) & 0xff)
  73. }
  74. return string(prefix) + "_" + hex.EncodeToString(timeBytes) + randomBase62(length-12)
  75. }