// Package snowflake provides a very simple Twitter snowflake generator and parser. package snowflake import ( "encoding/base64" "encoding/binary" "errors" "fmt" "strconv" "sync" "time" ) var ( // Epoch is set to the twitter snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds // You may customize this to set a different epoch for your application. Epoch int64 = 1288834974657 // NodeBits holds the number of bits to use for Node // Remember, you have a total 22 bits to share between Node/Step NodeBits uint8 = 10 // StepBits holds the number of bits to use for Step // Remember, you have a total 22 bits to share between Node/Step StepBits uint8 = 12 // DEPRECATED: the below four variables will be removed in a future release. mu sync.Mutex nodeMax int64 = -1 ^ (-1 << NodeBits) nodeMask = nodeMax << StepBits stepMask int64 = -1 ^ (-1 << StepBits) timeShift = NodeBits + StepBits nodeShift = StepBits ) const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769" var decodeBase32Map [256]byte const encodeBase58Map = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" var decodeBase58Map [256]byte // A JSONSyntaxError is returned from UnmarshalJSON if an invalid ID is provided. type JSONSyntaxError struct{ original []byte } func (j JSONSyntaxError) Error() string { return fmt.Sprintf("invalid snowflake ID %q", string(j.original)) } // ErrInvalidBase58 is returned by ParseBase58 when given an invalid []byte var ErrInvalidBase58 = errors.New("invalid base58") // ErrInvalidBase32 is returned by ParseBase32 when given an invalid []byte var ErrInvalidBase32 = errors.New("invalid base32") // Create maps for decoding Base58/Base32. // This speeds up the process tremendously. func init() { for i := 0; i < len(encodeBase58Map); i++ { decodeBase58Map[i] = 0xFF } for i := 0; i < len(encodeBase58Map); i++ { decodeBase58Map[encodeBase58Map[i]] = byte(i) } for i := 0; i < len(encodeBase32Map); i++ { decodeBase32Map[i] = 0xFF } for i := 0; i < len(encodeBase32Map); i++ { decodeBase32Map[encodeBase32Map[i]] = byte(i) } } // A Node struct holds the basic information needed for a snowflake generator // node type Node struct { mu sync.Mutex epoch time.Time time int64 node int64 step int64 nodeMax int64 nodeMask int64 stepMask int64 timeShift uint8 nodeShift uint8 } // An ID is a custom type used for a snowflake ID. This is used so we can // attach methods onto the ID. type ID int64 // NewNode returns a new snowflake node that can be used to generate snowflake // IDs func NewNode(node int64) (*Node, error) { // re-calc in case custom NodeBits or StepBits were set // DEPRECATED: the below block will be removed in a future release. mu.Lock() nodeMax = -1 ^ (-1 << NodeBits) nodeMask = nodeMax << StepBits stepMask = -1 ^ (-1 << StepBits) timeShift = NodeBits + StepBits nodeShift = StepBits mu.Unlock() n := Node{} n.node = node n.nodeMax = -1 ^ (-1 << NodeBits) n.nodeMask = n.nodeMax << StepBits n.stepMask = -1 ^ (-1 << StepBits) n.timeShift = NodeBits + StepBits n.nodeShift = StepBits if n.node < 0 || n.node > n.nodeMax { return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10)) } var curTime = time.Now() // add time.Duration to curTime to make sure we use the monotonic clock if available n.epoch = curTime.Add(time.Unix(Epoch/1000, (Epoch%1000)*1000000).Sub(curTime)) return &n, nil } // Generate creates and returns a unique snowflake ID // To help guarantee uniqueness // - Make sure your system is keeping accurate system time // - Make sure you never have multiple nodes running with the same node ID func (n *Node) Generate() ID { n.mu.Lock() now := time.Since(n.epoch).Nanoseconds() / 1000000 if now == n.time { n.step = (n.step + 1) & n.stepMask if n.step == 0 { for now <= n.time { now = time.Since(n.epoch).Nanoseconds() / 1000000 } } } else { n.step = 0 } n.time = now r := ID((now)<= 32 { b = append(b, encodeBase32Map[f%32]) f /= 32 } b = append(b, encodeBase32Map[f]) for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 { b[x], b[y] = b[y], b[x] } return string(b) } // ParseBase32 parses a base32 []byte into a snowflake ID // NOTE: There are many different base32 implementations so becareful when // doing any interoperation. func ParseBase32(b []byte) (ID, error) { var id int64 for i := range b { if decodeBase32Map[b[i]] == 0xFF { return -1, ErrInvalidBase32 } id = id*32 + int64(decodeBase32Map[b[i]]) } return ID(id), nil } // Base36 returns a base36 string of the snowflake ID func (f ID) Base36() string { return strconv.FormatInt(int64(f), 36) } // ParseBase36 converts a Base36 string into a snowflake ID func ParseBase36(id string) (ID, error) { i, err := strconv.ParseInt(id, 36, 64) return ID(i), err } // Base58 returns a base58 string of the snowflake ID func (f ID) Base58() string { if f < 58 { return string(encodeBase58Map[f]) } b := make([]byte, 0, 11) for f >= 58 { b = append(b, encodeBase58Map[f%58]) f /= 58 } b = append(b, encodeBase58Map[f]) for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 { b[x], b[y] = b[y], b[x] } return string(b) } // ParseBase58 parses a base58 []byte into a snowflake ID func ParseBase58(b []byte) (ID, error) { var id int64 for i := range b { if decodeBase58Map[b[i]] == 0xFF { return -1, ErrInvalidBase58 } id = id*58 + int64(decodeBase58Map[b[i]]) } return ID(id), nil } // Base64 returns a base64 string of the snowflake ID func (f ID) Base64() string { return base64.StdEncoding.EncodeToString(f.Bytes()) } // ParseBase64 converts a base64 string into a snowflake ID func ParseBase64(id string) (ID, error) { b, err := base64.StdEncoding.DecodeString(id) if err != nil { return -1, err } return ParseBytes(b) } // Bytes returns a byte slice of the snowflake ID func (f ID) Bytes() []byte { return []byte(f.String()) } // ParseBytes converts a byte slice into a snowflake ID func ParseBytes(id []byte) (ID, error) { i, err := strconv.ParseInt(string(id), 10, 64) return ID(i), err } // IntBytes returns an array of bytes of the snowflake ID, encoded as a // big endian integer. func (f ID) IntBytes() [8]byte { var b [8]byte binary.BigEndian.PutUint64(b[:], uint64(f)) return b } // ParseIntBytes converts an array of bytes encoded as big endian integer as // a snowflake ID func ParseIntBytes(id [8]byte) ID { return ID(int64(binary.BigEndian.Uint64(id[:]))) } // Time returns an int64 unix timestamp in milliseconds of the snowflake ID time // DEPRECATED: the below function will be removed in a future release. func (f ID) Time() int64 { return (int64(f) >> timeShift) + Epoch } // Node returns an int64 of the snowflake ID node number // DEPRECATED: the below function will be removed in a future release. func (f ID) Node() int64 { return int64(f) & nodeMask >> nodeShift } // Step returns an int64 of the snowflake step (or sequence) number // DEPRECATED: the below function will be removed in a future release. func (f ID) Step() int64 { return int64(f) & stepMask } // MarshalJSON returns a json byte array string of the snowflake ID. func (f ID) MarshalJSON() ([]byte, error) { buff := make([]byte, 0, 22) buff = append(buff, '"') buff = strconv.AppendInt(buff, int64(f), 10) buff = append(buff, '"') return buff, nil } // UnmarshalJSON converts a json byte array of a snowflake ID into an ID type. func (f *ID) UnmarshalJSON(b []byte) error { if len(b) < 3 || b[0] != '"' || b[len(b)-1] != '"' { return JSONSyntaxError{b} } i, err := strconv.ParseInt(string(b[1:len(b)-1]), 10, 64) if err != nil { return err } *f = ID(i) return nil }