123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- package span
- import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "encoding/json"
- "fmt"
- "io"
- "strconv"
- "sync"
- "unsafe"
- )
- // An ID is a unique, uniformly distributed 64-bit ID.
- type ID uint64
- // String returns the ID as a hex string.
- func (id ID) String() string {
- return fmt.Sprintf("%016x", uint64(id))
- }
- // MarshalJSON encodes the ID as a hex string.
- func (id ID) MarshalJSON() ([]byte, error) {
- return json.Marshal(id.String())
- }
- // UnmarshalJSON decodes the given data as either a hexadecimal string or JSON
- // integer.
- func (id *ID) UnmarshalJSON(data []byte) error {
- i, err := parseJSONString(data)
- if err == nil {
- *id = i
- return nil
- }
- i, err = parseJSONInt(data)
- if err == nil {
- *id = i
- return nil
- }
- return fmt.Errorf("%s is not a valid ID", data)
- }
- // ParseID parses the given string as a hexadecimal string.
- func ParseID(s string) (ID, error) {
- i, err := strconv.ParseUint(s, 16, 64)
- if err != nil {
- return 0, err
- }
- return ID(i), nil
- }
- // generateID returns a randomly-generated 64-bit ID. This function is
- // thread-safe. IDs are produced by consuming an AES-CTR-128 keystream in
- // 64-bit chunks. The AES key is randomly generated on initialization, as is the
- // counter's initial state. On machines with AES-NI support, ID generation takes
- // ~30ns and generates no garbage.
- func generateID() ID {
- m.Lock()
- if n == aes.BlockSize {
- c.Encrypt(b, ctr)
- for i := aes.BlockSize - 1; i >= 0; i-- { // increment ctr
- ctr[i]++
- if ctr[i] != 0 {
- break
- }
- }
- n = 0
- }
- id := *(*ID)(unsafe.Pointer(&b[n])) // zero-copy b/c we're arch-neutral
- n += idSize
- m.Unlock()
- return id
- }
- const (
- idSize = aes.BlockSize / 2 // 64 bits
- keySize = aes.BlockSize // 128 bits
- )
- var (
- ctr []byte
- n int
- b []byte
- c cipher.Block
- m sync.Mutex
- )
- func init() {
- buf := make([]byte, keySize+aes.BlockSize)
- _, err := io.ReadFull(rand.Reader, buf)
- if err != nil {
- panic(err) // /dev/urandom had better work
- }
- c, err = aes.NewCipher(buf[:keySize])
- if err != nil {
- panic(err) // AES had better work
- }
- n = aes.BlockSize
- ctr = buf[keySize:]
- b = make([]byte, aes.BlockSize)
- }
- func parseJSONString(data []byte) (ID, error) {
- var s string
- if err := json.Unmarshal(data, &s); err != nil {
- return 0, err
- }
- i, err := ParseID(s)
- if err != nil {
- return 0, err
- }
- return i, nil
- }
- func parseJSONInt(data []byte) (ID, error) {
- var i uint64
- if err := json.Unmarshal(data, &i); err != nil {
- return 0, err
- }
- return ID(i), nil
- }
|