You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

86 lines
1.6 KiB

1 year ago
  1. package generate
  2. import (
  3. "crypto/rand"
  4. "encoding/binary"
  5. mathRand "math/rand"
  6. "strconv"
  7. "strings"
  8. )
  9. // ID generates an ID using crypto-random, falling back to math random when that fails
  10. // to avoid disrupting operation because of a faulty RNG.
  11. func ID(prefix string, length int) string {
  12. var data [32]byte
  13. result := strings.Builder{}
  14. result.Grow(length + 32)
  15. result.WriteString(prefix)
  16. pos := 0
  17. for result.Len() < length {
  18. if pos == 0 {
  19. randRead(data[:])
  20. }
  21. result.WriteString(strconv.FormatUint(binary.BigEndian.Uint64(data[pos:pos+8]), 36))
  22. pos = (pos + 8) % 32
  23. }
  24. return result.String()[:length]
  25. }
  26. func randRead(data []byte) {
  27. n, err := rand.Read(data)
  28. if err != nil {
  29. mathRand.Read(data[n:])
  30. }
  31. }
  32. // InternalErrorID generates a long string
  33. func InternalErrorID() string {
  34. return ID("ISE", 32)
  35. }
  36. // TagID generates a location ID. len=8
  37. func TagID(longName string) string {
  38. return friendlyID('T', longName, 8)
  39. }
  40. // CharacterID generates a character ID. len=8
  41. func CharacterID(longName string) string {
  42. return friendlyID('C', longName, 8)
  43. }
  44. func friendlyID(prefix byte, name string, length int) string {
  45. b := strings.Builder{}
  46. b.Grow(4)
  47. b.WriteByte(prefix)
  48. for _, ch := range strings.ToLower(name) {
  49. if ch >= 'a' && ch <= 'z' {
  50. b.WriteRune(ch)
  51. if b.Len() > 3 {
  52. break
  53. }
  54. }
  55. }
  56. return ID(b.String(), length)
  57. }
  58. // StoryID generates a story ID: len=8
  59. func StoryID() string {
  60. return ID("S", 12)
  61. }
  62. // PostID generates a post ID. len=12
  63. func PostID() string {
  64. return ID("P", 16)
  65. }
  66. // AnnotationID generates an annotation.sql ID. len=12
  67. func AnnotationID() string {
  68. return ID("A", 16)
  69. }