// k ordered id generator package ids import ( "errors" "sync" "time" ) const ( // 2024-07-03T00:00:00Z customEpochMs int64 = 1719964800000 seqBits = 8 workerBits = 16 timeBits = 40 seqMask = (1 << seqBits) - 1 // 0xFF workerMask = (1 << workerBits) - 1 // 0xFFFF timeMask = (int64(1) << timeBits) - 1 // low 40 bits workerShift = seqBits timeShift = workerBits + seqBits ) // Generator is safe for concurrent use. type Generator struct { workerID uint64 mu sync.Mutex lastMs int64 sequence uint64 } func NewGenerator(workerID uint32) (*Generator, error) { if workerID > workerMask { return nil, errors.New("workerID too large for 16 bits") } return &Generator{workerID: uint64(workerID)}, nil } func (g *Generator) Next() (uint64, error) { nowMs := time.Now().UTC().UnixMilli() delta := nowMs - customEpochMs if delta < 0 { return 0, errors.New("time is before custom epoch") } if delta > timeMask { return 0, errors.New("timestamp overflow (40-bit ms range exceeded)") } g.mu.Lock() defer g.mu.Unlock() if delta == g.lastMs { g.sequence = (g.sequence + 1) & seqMask if g.sequence == 0 { // sequence wrapped; wait for next millisecond for delta == g.lastMs { nowMs = time.Now().UTC().UnixMilli() delta = nowMs - customEpochMs } } } else { g.sequence = 0 g.lastMs = delta } id := (uint64(delta) << timeShift) | ((g.workerID & workerMask) << workerShift) | (g.sequence & seqMask) return id, nil } // ---- Base62 ---- const base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" func Base62Encode(x uint64) string { if x == 0 { return "0" } var buf [11]byte // 62^11 > 2^64, so max 11 chars i := len(buf) for x > 0 { r := x % 62 x /= 62 i-- buf[i] = base62Alphabet[r] } return string(buf[i:]) }