id.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package span
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "crypto/rand"
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "strconv"
  10. "sync"
  11. "unsafe"
  12. )
  13. // An ID is a unique, uniformly distributed 64-bit ID.
  14. type ID uint64
  15. // String returns the ID as a hex string.
  16. func (id ID) String() string {
  17. return fmt.Sprintf("%016x", uint64(id))
  18. }
  19. // MarshalJSON encodes the ID as a hex string.
  20. func (id ID) MarshalJSON() ([]byte, error) {
  21. return json.Marshal(id.String())
  22. }
  23. // UnmarshalJSON decodes the given data as either a hexadecimal string or JSON
  24. // integer.
  25. func (id *ID) UnmarshalJSON(data []byte) error {
  26. i, err := parseJSONString(data)
  27. if err == nil {
  28. *id = i
  29. return nil
  30. }
  31. i, err = parseJSONInt(data)
  32. if err == nil {
  33. *id = i
  34. return nil
  35. }
  36. return fmt.Errorf("%s is not a valid ID", data)
  37. }
  38. // ParseID parses the given string as a hexadecimal string.
  39. func ParseID(s string) (ID, error) {
  40. i, err := strconv.ParseUint(s, 16, 64)
  41. if err != nil {
  42. return 0, err
  43. }
  44. return ID(i), nil
  45. }
  46. // generateID returns a randomly-generated 64-bit ID. This function is
  47. // thread-safe. IDs are produced by consuming an AES-CTR-128 keystream in
  48. // 64-bit chunks. The AES key is randomly generated on initialization, as is the
  49. // counter's initial state. On machines with AES-NI support, ID generation takes
  50. // ~30ns and generates no garbage.
  51. func generateID() ID {
  52. m.Lock()
  53. if n == aes.BlockSize {
  54. c.Encrypt(b, ctr)
  55. for i := aes.BlockSize - 1; i >= 0; i-- { // increment ctr
  56. ctr[i]++
  57. if ctr[i] != 0 {
  58. break
  59. }
  60. }
  61. n = 0
  62. }
  63. id := *(*ID)(unsafe.Pointer(&b[n])) // zero-copy b/c we're arch-neutral
  64. n += idSize
  65. m.Unlock()
  66. return id
  67. }
  68. const (
  69. idSize = aes.BlockSize / 2 // 64 bits
  70. keySize = aes.BlockSize // 128 bits
  71. )
  72. var (
  73. ctr []byte
  74. n int
  75. b []byte
  76. c cipher.Block
  77. m sync.Mutex
  78. )
  79. func init() {
  80. buf := make([]byte, keySize+aes.BlockSize)
  81. _, err := io.ReadFull(rand.Reader, buf)
  82. if err != nil {
  83. panic(err) // /dev/urandom had better work
  84. }
  85. c, err = aes.NewCipher(buf[:keySize])
  86. if err != nil {
  87. panic(err) // AES had better work
  88. }
  89. n = aes.BlockSize
  90. ctr = buf[keySize:]
  91. b = make([]byte, aes.BlockSize)
  92. }
  93. func parseJSONString(data []byte) (ID, error) {
  94. var s string
  95. if err := json.Unmarshal(data, &s); err != nil {
  96. return 0, err
  97. }
  98. i, err := ParseID(s)
  99. if err != nil {
  100. return 0, err
  101. }
  102. return i, nil
  103. }
  104. func parseJSONInt(data []byte) (ID, error) {
  105. var i uint64
  106. if err := json.Unmarshal(data, &i); err != nil {
  107. return 0, err
  108. }
  109. return ID(i), nil
  110. }