commit 8b0a7fd89a3055115d7904fa39885f1d266b5e5a Author: upstream Date: Fri Sep 4 04:36:21 2026 +0200 Upstream oklog/ulid state of the touched files before d52271c696 diff --git a/ulid.go b/ulid.go new file mode 100644 index 0000000..e07e1a6 --- /dev/null +++ b/ulid.go @@ -0,0 +1,722 @@ +// Copyright 2016 The Oklog Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ulid + +import ( + "bufio" + "bytes" + "database/sql/driver" + "encoding/binary" + "errors" + "io" + "math" + "math/bits" + "math/rand" + "sync" + "time" +) + +/* +A ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier + + The components are encoded as 16 octets. + Each component is encoded with the MSB first (network byte order). + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 32_bit_uint_time_high | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 16_bit_uint_time_low | 16_bit_uint_random | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 32_bit_uint_random | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 32_bit_uint_random | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +type ULID [16]byte + +var ( + // ErrDataSize is returned when parsing or unmarshaling ULIDs with the wrong + // data size. + ErrDataSize = errors.New("ulid: bad data size when unmarshaling") + + // ErrInvalidCharacters is returned when parsing or unmarshaling ULIDs with + // invalid Base32 encodings. + ErrInvalidCharacters = errors.New("ulid: bad data characters when unmarshaling") + + // ErrBufferSize is returned when marshalling ULIDs to a buffer of insufficient + // size. + ErrBufferSize = errors.New("ulid: bad buffer size when marshaling") + + // ErrBigTime is returned when constructing a ULID with a time that is larger + // than MaxTime. + ErrBigTime = errors.New("ulid: time too big") + + // ErrOverflow is returned when unmarshaling a ULID whose first character is + // larger than 7, thereby exceeding the valid bit depth of 128. + ErrOverflow = errors.New("ulid: overflow when unmarshaling") + + // ErrMonotonicOverflow is returned by a Monotonic entropy source when + // incrementing the previous ULID's entropy bytes would result in overflow. + ErrMonotonicOverflow = errors.New("ulid: monotonic entropy overflow") + + // ErrScanValue is returned when the value passed to scan cannot be unmarshaled + // into the ULID. + ErrScanValue = errors.New("ulid: source value must be a string or byte slice") + + // Zero is a zero-value ULID. + Zero ULID +) + +// MonotonicReader is an interface that should yield monotonically increasing +// entropy into the provided slice for all calls with the same ms parameter. If +// a MonotonicReader is provided to the New constructor, its MonotonicRead +// method will be used instead of Read. +type MonotonicReader interface { + io.Reader + MonotonicRead(ms uint64, p []byte) error +} + +// New returns a ULID with the given Unix milliseconds timestamp and an +// optional entropy source. Use the Timestamp function to convert +// a time.Time to Unix milliseconds. +// +// ErrBigTime is returned when passing a timestamp bigger than MaxTime. +// Reading from the entropy source may also return an error. +// +// Safety for concurrent use is only dependent on the safety of the +// entropy source. +func New(ms uint64, entropy io.Reader) (id ULID, err error) { + if err = id.SetTime(ms); err != nil { + return id, err + } + + switch e := entropy.(type) { + case nil: + return id, err + case MonotonicReader: + err = e.MonotonicRead(ms, id[6:]) + default: + _, err = io.ReadFull(e, id[6:]) + } + + return id, err +} + +// MustNew is a convenience function equivalent to New that panics on failure +// instead of returning an error. +func MustNew(ms uint64, entropy io.Reader) ULID { + id, err := New(ms, entropy) + if err != nil { + panic(err) + } + return id +} + +// MustNewDefault is a convenience function equivalent to MustNew with +// DefaultEntropy as the entropy. It may panic if the given time.Time is too +// large or too small. +func MustNewDefault(t time.Time) ULID { + return MustNew(Timestamp(t), defaultEntropy) +} + +var defaultEntropy = func() io.Reader { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + return &LockedMonotonicReader{MonotonicReader: Monotonic(rng, 0)} +}() + +// DefaultEntropy returns a thread-safe per process monotonically increasing +// entropy source. +func DefaultEntropy() io.Reader { + return defaultEntropy +} + +// Make returns a ULID with the current time in Unix milliseconds and +// monotonically increasing entropy for the same millisecond. +// It is safe for concurrent use, leveraging a sync.Pool underneath for minimal +// contention. +func Make() (id ULID) { + // NOTE: MustNew can't panic since DefaultEntropy never returns an error. + return MustNew(Now(), defaultEntropy) +} + +// Parse parses an encoded ULID, returning an error in case of failure. +// +// ErrDataSize is returned if the len(ulid) is different from an encoded +// ULID's length. Invalid encodings produce undefined ULIDs. For a version that +// returns an error instead, see ParseStrict. +func Parse(ulid string) (id ULID, err error) { + return id, parse([]byte(ulid), false, &id) +} + +// ParseStrict parses an encoded ULID, returning an error in case of failure. +// +// It is like Parse, but additionally validates that the parsed ULID consists +// only of valid base32 characters. It is slightly slower than Parse. +// +// ErrDataSize is returned if the len(ulid) is different from an encoded +// ULID's length. Invalid encodings return ErrInvalidCharacters. +func ParseStrict(ulid string) (id ULID, err error) { + return id, parse([]byte(ulid), true, &id) +} + +func parse(v []byte, strict bool, id *ULID) error { + // Check if a base32 encoded ULID is the right length. + if len(v) != EncodedSize { + return ErrDataSize + } + + // Check if all the characters in a base32 encoded ULID are part of the + // expected base32 character set. + if strict && + (dec[v[0]] == 0xFF || + dec[v[1]] == 0xFF || + dec[v[2]] == 0xFF || + dec[v[3]] == 0xFF || + dec[v[4]] == 0xFF || + dec[v[5]] == 0xFF || + dec[v[6]] == 0xFF || + dec[v[7]] == 0xFF || + dec[v[8]] == 0xFF || + dec[v[9]] == 0xFF || + dec[v[10]] == 0xFF || + dec[v[11]] == 0xFF || + dec[v[12]] == 0xFF || + dec[v[13]] == 0xFF || + dec[v[14]] == 0xFF || + dec[v[15]] == 0xFF || + dec[v[16]] == 0xFF || + dec[v[17]] == 0xFF || + dec[v[18]] == 0xFF || + dec[v[19]] == 0xFF || + dec[v[20]] == 0xFF || + dec[v[21]] == 0xFF || + dec[v[22]] == 0xFF || + dec[v[23]] == 0xFF || + dec[v[24]] == 0xFF || + dec[v[25]] == 0xFF) { + return ErrInvalidCharacters + } + + // Check if the first character in a base32 encoded ULID will overflow. This + // happens because the base32 representation encodes 130 bits, while the + // ULID is only 128 bits. + // + // See https://github.com/oklog/ulid/issues/9 for details. + if v[0] > '7' { + return ErrOverflow + } + + // Use an optimized unrolled loop (from https://github.com/RobThree/NUlid) + // to decode a base32 ULID. + + // 6 bytes timestamp (48 bits) + (*id)[0] = (dec[v[0]] << 5) | dec[v[1]] + (*id)[1] = (dec[v[2]] << 3) | (dec[v[3]] >> 2) + (*id)[2] = (dec[v[3]] << 6) | (dec[v[4]] << 1) | (dec[v[5]] >> 4) + (*id)[3] = (dec[v[5]] << 4) | (dec[v[6]] >> 1) + (*id)[4] = (dec[v[6]] << 7) | (dec[v[7]] << 2) | (dec[v[8]] >> 3) + (*id)[5] = (dec[v[8]] << 5) | dec[v[9]] + + // 10 bytes of entropy (80 bits) + (*id)[6] = (dec[v[10]] << 3) | (dec[v[11]] >> 2) + (*id)[7] = (dec[v[11]] << 6) | (dec[v[12]] << 1) | (dec[v[13]] >> 4) + (*id)[8] = (dec[v[13]] << 4) | (dec[v[14]] >> 1) + (*id)[9] = (dec[v[14]] << 7) | (dec[v[15]] << 2) | (dec[v[16]] >> 3) + (*id)[10] = (dec[v[16]] << 5) | dec[v[17]] + (*id)[11] = (dec[v[18]] << 3) | dec[v[19]]>>2 + (*id)[12] = (dec[v[19]] << 6) | (dec[v[20]] << 1) | (dec[v[21]] >> 4) + (*id)[13] = (dec[v[21]] << 4) | (dec[v[22]] >> 1) + (*id)[14] = (dec[v[22]] << 7) | (dec[v[23]] << 2) | (dec[v[24]] >> 3) + (*id)[15] = (dec[v[24]] << 5) | dec[v[25]] + + return nil +} + +// MustParse is a convenience function equivalent to Parse that panics on failure +// instead of returning an error. +func MustParse(ulid string) ULID { + id, err := Parse(ulid) + if err != nil { + panic(err) + } + return id +} + +// MustParseStrict is a convenience function equivalent to ParseStrict that +// panics on failure instead of returning an error. +func MustParseStrict(ulid string) ULID { + id, err := ParseStrict(ulid) + if err != nil { + panic(err) + } + return id +} + +// Bytes returns bytes slice representation of ULID. +func (id ULID) Bytes() []byte { + return id[:] +} + +// String returns a lexicographically sortable string encoded ULID +// (26 characters, non-standard base 32) e.g. 01AN4Z07BY79KA1307SR9X4MV3. +// Format: tttttttttteeeeeeeeeeeeeeee where t is time and e is entropy. +func (id ULID) String() string { + ulid := make([]byte, EncodedSize) + _ = id.MarshalTextTo(ulid) + return string(ulid) +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface by +// returning the ULID as a byte slice. +func (id ULID) MarshalBinary() ([]byte, error) { + ulid := make([]byte, len(id)) + return ulid, id.MarshalBinaryTo(ulid) +} + +// MarshalBinaryTo writes the binary encoding of the ULID to the given buffer. +// ErrBufferSize is returned when the len(dst) != 16. +func (id ULID) MarshalBinaryTo(dst []byte) error { + if len(dst) != len(id) { + return ErrBufferSize + } + + copy(dst, id[:]) + return nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface by +// copying the passed data and converting it to a ULID. ErrDataSize is +// returned if the data length is different from ULID length. +func (id *ULID) UnmarshalBinary(data []byte) error { + if len(data) != len(*id) { + return ErrDataSize + } + + copy((*id)[:], data) + return nil +} + +// Encoding is the base 32 encoding alphabet used in ULID strings. +const Encoding = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" + +// MarshalText implements the encoding.TextMarshaler interface by +// returning the string encoded ULID. +func (id ULID) MarshalText() ([]byte, error) { + ulid := make([]byte, EncodedSize) + return ulid, id.MarshalTextTo(ulid) +} + +// MarshalTextTo writes the ULID as a string to the given buffer. +// ErrBufferSize is returned when the len(dst) != 26. +func (id ULID) MarshalTextTo(dst []byte) error { + // Optimized unrolled loop ahead. + // From https://github.com/RobThree/NUlid + + if len(dst) != EncodedSize { + return ErrBufferSize + } + + // 10 byte timestamp + dst[0] = Encoding[(id[0]&224)>>5] + dst[1] = Encoding[id[0]&31] + dst[2] = Encoding[(id[1]&248)>>3] + dst[3] = Encoding[((id[1]&7)<<2)|((id[2]&192)>>6)] + dst[4] = Encoding[(id[2]&62)>>1] + dst[5] = Encoding[((id[2]&1)<<4)|((id[3]&240)>>4)] + dst[6] = Encoding[((id[3]&15)<<1)|((id[4]&128)>>7)] + dst[7] = Encoding[(id[4]&124)>>2] + dst[8] = Encoding[((id[4]&3)<<3)|((id[5]&224)>>5)] + dst[9] = Encoding[id[5]&31] + + // 16 bytes of entropy + dst[10] = Encoding[(id[6]&248)>>3] + dst[11] = Encoding[((id[6]&7)<<2)|((id[7]&192)>>6)] + dst[12] = Encoding[(id[7]&62)>>1] + dst[13] = Encoding[((id[7]&1)<<4)|((id[8]&240)>>4)] + dst[14] = Encoding[((id[8]&15)<<1)|((id[9]&128)>>7)] + dst[15] = Encoding[(id[9]&124)>>2] + dst[16] = Encoding[((id[9]&3)<<3)|((id[10]&224)>>5)] + dst[17] = Encoding[id[10]&31] + dst[18] = Encoding[(id[11]&248)>>3] + dst[19] = Encoding[((id[11]&7)<<2)|((id[12]&192)>>6)] + dst[20] = Encoding[(id[12]&62)>>1] + dst[21] = Encoding[((id[12]&1)<<4)|((id[13]&240)>>4)] + dst[22] = Encoding[((id[13]&15)<<1)|((id[14]&128)>>7)] + dst[23] = Encoding[(id[14]&124)>>2] + dst[24] = Encoding[((id[14]&3)<<3)|((id[15]&224)>>5)] + dst[25] = Encoding[id[15]&31] + + return nil +} + +// Byte to index table for O(1) lookups when unmarshaling. +// We use 0xFF as sentinel value for invalid indexes. +var dec = [...]byte{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, + 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, + 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, + 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, + 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +} + +// EncodedSize is the length of a text encoded ULID. +const EncodedSize = 26 + +// UnmarshalText implements the encoding.TextUnmarshaler interface by +// parsing the data as string encoded ULID. +// +// ErrDataSize is returned if the len(v) is different from an encoded +// ULID's length. Invalid encodings produce undefined ULIDs. +func (id *ULID) UnmarshalText(v []byte) error { + return parse(v, false, id) +} + +// Time returns the Unix time in milliseconds encoded in the ULID. +// Use the top level Time function to convert the returned value to +// a time.Time. +func (id ULID) Time() uint64 { + return uint64(id[5]) | uint64(id[4])<<8 | + uint64(id[3])<<16 | uint64(id[2])<<24 | + uint64(id[1])<<32 | uint64(id[0])<<40 +} + +// Timestamp returns the time encoded in the ULID as a time.Time. +func (id ULID) Timestamp() time.Time { + return Time(id.Time()) +} + +// IsZero returns true if the ULID is a zero-value ULID, i.e. ulid.Zero. +func (id ULID) IsZero() bool { + return id.Compare(Zero) == 0 +} + +// maxTime is the maximum Unix time in milliseconds that can be +// represented in a ULID. +var maxTime = ULID{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.Time() + +// MaxTime returns the maximum Unix time in milliseconds that +// can be encoded in a ULID. +func MaxTime() uint64 { return maxTime } + +// Now is a convenience function that returns the current +// UTC time in Unix milliseconds. Equivalent to: +// +// Timestamp(time.Now().UTC()) +func Now() uint64 { return Timestamp(time.Now().UTC()) } + +// Timestamp converts a time.Time to Unix milliseconds. +// +// Because of the way ULID stores time, times from the year +// 10889 produces undefined results. +func Timestamp(t time.Time) uint64 { + return uint64(t.Unix())*1000 + + uint64(t.Nanosecond()/int(time.Millisecond)) +} + +// Time converts Unix milliseconds in the format +// returned by the Timestamp function to a time.Time. +func Time(ms uint64) time.Time { + s := int64(ms / 1e3) + ns := int64((ms % 1e3) * 1e6) + return time.Unix(s, ns) +} + +// SetTime sets the time component of the ULID to the given Unix time +// in milliseconds. +func (id *ULID) SetTime(ms uint64) error { + if ms > maxTime { + return ErrBigTime + } + + (*id)[0] = byte(ms >> 40) + (*id)[1] = byte(ms >> 32) + (*id)[2] = byte(ms >> 24) + (*id)[3] = byte(ms >> 16) + (*id)[4] = byte(ms >> 8) + (*id)[5] = byte(ms) + + return nil +} + +// Entropy returns the entropy from the ULID. +func (id ULID) Entropy() []byte { + e := make([]byte, 10) + copy(e, id[6:]) + return e +} + +// SetEntropy sets the ULID entropy to the passed byte slice. +// ErrDataSize is returned if len(e) != 10. +func (id *ULID) SetEntropy(e []byte) error { + if len(e) != 10 { + return ErrDataSize + } + + copy((*id)[6:], e) + return nil +} + +// Compare returns an integer comparing id and other lexicographically. +// The result will be 0 if id==other, -1 if id < other, and +1 if id > other. +func (id ULID) Compare(other ULID) int { + return bytes.Compare(id[:], other[:]) +} + +// Scan implements the sql.Scanner interface. It supports scanning +// a string or byte slice. +func (id *ULID) Scan(src interface{}) error { + switch x := src.(type) { + case nil: + return nil + case string: + return id.UnmarshalText([]byte(x)) + case []byte: + // Drivers often return text/varchar columns as []byte. Accept both + // the 16-byte binary form and the 26-character text encoding. + switch len(x) { + case len(*id): + return id.UnmarshalBinary(x) + case EncodedSize: + return id.UnmarshalText(x) + default: + return ErrDataSize + } + } + + return ErrScanValue +} + +// Value implements the sql/driver.Valuer interface, returning the ULID as a +// slice of bytes, by invoking MarshalBinary. If your use case requires a string +// representation instead, you can create a wrapper type that calls String() +// instead. +// +// type stringValuer ulid.ULID +// +// func (v stringValuer) Value() (driver.Value, error) { +// return ulid.ULID(v).String(), nil +// } +// +// // Example usage. +// db.Exec("...", stringValuer(id)) +// +// All valid ULIDs, including zero-value ULIDs, return a valid Value with a nil +// error. If your use case requires zero-value ULIDs to return a non-nil error, +// you can create a wrapper type that special-cases this behavior. +// +// var zeroValueULID ulid.ULID +// +// type invalidZeroValuer ulid.ULID +// +// func (v invalidZeroValuer) Value() (driver.Value, error) { +// if ulid.ULID(v).Compare(zeroValueULID) == 0 { +// return nil, fmt.Errorf("zero value") +// } +// return ulid.ULID(v).Value() +// } +// +// // Example usage. +// db.Exec("...", invalidZeroValuer(id)) +func (id ULID) Value() (driver.Value, error) { + return id.MarshalBinary() +} + +// Monotonic returns a source of entropy that yields strictly increasing entropy +// bytes, to a limit governeed by the `inc` parameter. +// +// Specifically, calls to MonotonicRead within the same ULID timestamp return +// entropy incremented by a random number between 1 and `inc` inclusive. If an +// increment results in entropy that would overflow available space, +// MonotonicRead returns ErrMonotonicOverflow. +// +// Passing `inc == 0` results in the reasonable default `math.MaxUint32`. Lower +// values of `inc` provide more monotonic entropy in a single millisecond, at +// the cost of easier "guessability" of generated ULIDs. If your code depends on +// ULIDs having secure entropy bytes, then it's recommended to use the secure +// default value of `inc == 0`, unless you know what you're doing. +// +// The provided entropy source must actually yield random bytes. Otherwise, +// monotonic reads are not guaranteed to terminate, since there isn't enough +// randomness to compute an increment number. +// +// The returned type isn't safe for concurrent use. +func Monotonic(entropy io.Reader, inc uint64) *MonotonicEntropy { + m := MonotonicEntropy{ + Reader: bufio.NewReader(entropy), + inc: inc, + } + + if m.inc == 0 { + m.inc = math.MaxUint32 + } + + if rng, ok := entropy.(rng); ok { + m.rng = rng + } + + return &m +} + +type rng interface{ Int63n(n int64) int64 } + +// LockedMonotonicReader wraps a MonotonicReader with a sync.Mutex for safe +// concurrent use. +type LockedMonotonicReader struct { + mu sync.Mutex + MonotonicReader +} + +// MonotonicRead synchronizes calls to the wrapped MonotonicReader. +func (r *LockedMonotonicReader) MonotonicRead(ms uint64, p []byte) (err error) { + r.mu.Lock() + err = r.MonotonicReader.MonotonicRead(ms, p) + r.mu.Unlock() + return err +} + +// MonotonicEntropy is an opaque type that provides monotonic entropy. +type MonotonicEntropy struct { + io.Reader + ms uint64 + inc uint64 + entropy uint80 + rand [8]byte + rng rng +} + +// MonotonicRead implements the MonotonicReader interface. +func (m *MonotonicEntropy) MonotonicRead(ms uint64, entropy []byte) (err error) { + if !m.entropy.IsZero() && m.ms == ms { + err = m.increment() + m.entropy.AppendTo(entropy) + } else if _, err = io.ReadFull(m.Reader, entropy); err == nil { + m.ms = ms + m.entropy.SetBytes(entropy) + } + return err +} + +// increment the previous entropy number with a random number +// of up to m.inc (inclusive). +func (m *MonotonicEntropy) increment() error { + if inc, err := m.random(); err != nil { + return err + } else if m.entropy.Add(inc) { + return ErrMonotonicOverflow + } + return nil +} + +// random returns a uniform random value in [1, m.inc), reading entropy +// from m.Reader. When m.inc == 0 || m.inc == 1, it returns 1. +// Adapted from: https://golang.org/pkg/crypto/rand/#Int +func (m *MonotonicEntropy) random() (inc uint64, err error) { + if m.inc <= 1 { + return 1, nil + } + + // Fast path for using a underlying rand.Rand directly. + if m.rng != nil { + // Range: [1, m.inc) + return 1 + uint64(m.rng.Int63n(int64(m.inc))), nil + } + + // bitLen is the maximum bit length needed to encode a value < m.inc. + bitLen := bits.Len64(m.inc) + + // byteLen is the maximum byte length needed to encode a value < m.inc. + byteLen := uint(bitLen+7) / 8 + + // msbitLen is the number of bits in the most significant byte of m.inc-1. + msbitLen := uint(bitLen % 8) + if msbitLen == 0 { + msbitLen = 8 + } + + for inc == 0 || inc >= m.inc { + if _, err = io.ReadFull(m.Reader, m.rand[:byteLen]); err != nil { + return 0, err + } + + // Clear bits in the first byte to increase the probability + // that the candidate is < m.inc. + m.rand[0] &= uint8(int(1< t2 && s1 > s2 && ord == +1) || + (t1 < t2 && s1 < s2 && ord == -1) + } + + top := ulid.MustNew(ulid.MaxTime(), nil) + for i := 0; i < 10; i++ { // test upper boundary state space + next := ulid.MustNew(top.Time()-1, nil) + if !prop(top, next) { + t.Fatalf("bad lexicographical order: (%v, %q) > (%v, %q) == false", + top.Time(), top, + next.Time(), next, + ) + } + top = next + } + + if err := quick.Check(prop, &quick.Config{MaxCount: 1e6}); err != nil { + t.Fatal(err) + } +} + +func TestCaseInsensitivity(t *testing.T) { + t.Parallel() + + upper := func(id ulid.ULID) (out ulid.ULID) { + return ulid.MustParse(strings.ToUpper(id.String())) + } + + lower := func(id ulid.ULID) (out ulid.ULID) { + return ulid.MustParse(strings.ToLower(id.String())) + } + + err := quick.CheckEqual(upper, lower, nil) + if err != nil { + t.Fatal(err) + } +} + +func TestParseRobustness(t *testing.T) { + t.Parallel() + + cases := [][]byte{ + {0x1, 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 0x4e, 0xf8, 0x44, 0x3b, + 0xb2, 0xa8, 0x59, 0xc7, 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 0x20}, + } + + for _, tc := range cases { + if _, err := ulid.Parse(string(tc)); err != nil { + t.Error(err) + } + } + + prop := func(s [26]byte) (ok bool) { + defer func() { + if err := recover(); err != nil { + t.Error(err) + ok = false + } + }() + + // quick.Check doesn't constrain input, + // so we need to do so artificially. + if s[0] > '7' { + s[0] %= '7' + } + + var err error + if _, err = ulid.Parse(string(s[:])); err != nil { + t.Error(err) + } + + return err == nil + } + + err := quick.Check(prop, &quick.Config{MaxCount: 1e4}) + if err != nil { + t.Fatal(err) + } +} + +func TestNow(t *testing.T) { + t.Parallel() + + before := ulid.Now() + after := ulid.Timestamp(time.Now().UTC().Add(time.Millisecond)) + + if before >= after { + t.Fatalf("clock went mad: before %v, after %v", before, after) + } +} + +func TestTimestamp(t *testing.T) { + t.Parallel() + + tm := time.Unix(1, 1000) // will be truncated + if got, want := ulid.Timestamp(tm), uint64(1000); got != want { + t.Errorf("for %v, got %v, want %v", tm, got, want) + } + + mt := ulid.MaxTime() + dt := time.Unix(int64(mt/1000), int64((mt%1000)*1000000)).Truncate(time.Millisecond) + ts := ulid.Timestamp(dt) + if got, want := ts, mt; got != want { + t.Errorf("got timestamp %d, want %d", got, want) + } +} + +func TestTime(t *testing.T) { + t.Parallel() + + original := time.Now() + diff := original.Sub(ulid.Time(ulid.Timestamp(original))) + if diff >= time.Millisecond { + t.Errorf("difference between original and recovered time (%d) greater"+ + "than a millisecond", diff) + } +} + +func TestTimestampRoundTrips(t *testing.T) { + t.Parallel() + + prop := func(ts uint64) bool { + return ts == ulid.Timestamp(ulid.Time(ts)) + } + + err := quick.Check(prop, &quick.Config{MaxCount: 1e5}) + if err != nil { + t.Fatal(err) + } +} + +func TestULIDTime(t *testing.T) { + t.Parallel() + + maxTime := ulid.MaxTime() + + var id ulid.ULID + if got, want := id.SetTime(maxTime+1), ulid.ErrBigTime; got != want { + t.Errorf("got err %v, want %v", got, want) + } + + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < 1e6; i++ { + ms := uint64(rng.Int63n(int64(maxTime))) + + var id ulid.ULID + if err := id.SetTime(ms); err != nil { + t.Fatal(err) + } + + if got, want := id.Time(), ms; got != want { + t.Fatalf("\nfor %v:\ngot %v\nwant %v", id, got, want) + } + } +} + +func TestULIDTimestamp(t *testing.T) { + t.Parallel() + + { + id := ulid.Make() + ts := id.Timestamp() + tt := ulid.Time(id.Time()) + if ts != tt { + t.Errorf("id.Timestamp() %s != ulid.Time(id.Time()) %s", ts, tt) + } + } + + { + now := time.Now() + id := ulid.MustNew(ulid.Timestamp(now), ulid.DefaultEntropy()) + if want, have := now.Truncate(time.Millisecond), id.Timestamp(); want != have { + t.Errorf("Timestamp: want %v, have %v", want, have) + } + } +} + +func TestZero(t *testing.T) { + t.Parallel() + + var id ulid.ULID + if ok := id.IsZero(); !ok { + t.Error(".IsZero: must return true for zero-value ULIDs, have false") + } + + id = ulid.MustNew(ulid.Now(), ulid.DefaultEntropy()) + if ok := id.IsZero(); ok { + t.Error(".IsZero: must return false for non-zero-value ULIDs, have true") + } +} + +func TestEntropy(t *testing.T) { + t.Parallel() + + var id ulid.ULID + if got, want := id.SetEntropy([]byte{}), ulid.ErrDataSize; got != want { + t.Errorf("got err %v, want %v", got, want) + } + + prop := func(e [10]byte) bool { + var id ulid.ULID + if err := id.SetEntropy(e[:]); err != nil { + t.Fatalf("got err %v", err) + } + + got, want := id.Entropy(), e[:] + eq := bytes.Equal(got, want) + if !eq { + t.Errorf("\n(!= %v\n %v)", got, want) + } + + return eq + } + + if err := quick.Check(prop, nil); err != nil { + t.Fatal(err) + } +} + +func TestEntropyRead(t *testing.T) { + t.Parallel() + + prop := func(e [10]byte) bool { + flakyReader := iotest.HalfReader(bytes.NewReader(e[:])) + + id, err := ulid.New(ulid.Now(), flakyReader) + if err != nil { + t.Fatalf("got err %v", err) + } + + got, want := id.Entropy(), e[:] + eq := bytes.Equal(got, want) + if !eq { + t.Errorf("\n(!= %v\n %v)", got, want) + } + + return eq + } + + if err := quick.Check(prop, &quick.Config{MaxCount: 1e4}); err != nil { + t.Fatal(err) + } +} + +func TestCompare(t *testing.T) { + t.Parallel() + + a := func(a, b ulid.ULID) int { + return strings.Compare(a.String(), b.String()) + } + + b := func(a, b ulid.ULID) int { + return a.Compare(b) + } + + err := quick.CheckEqual(a, b, &quick.Config{MaxCount: 1e5}) + if err != nil { + t.Error(err) + } +} + +func TestOverflowHandling(t *testing.T) { + t.Parallel() + + for s, want := range map[string]error{ + "00000000000000000000000000": nil, + "70000000000000000000000000": nil, + "7ZZZZZZZZZZZZZZZZZZZZZZZZZ": nil, + "80000000000000000000000000": ulid.ErrOverflow, + "80000000000000000000000001": ulid.ErrOverflow, + "ZZZZZZZZZZZZZZZZZZZZZZZZZZ": ulid.ErrOverflow, + } { + if _, have := ulid.Parse(s); want != have { + t.Errorf("%s: want error %v, have %v", s, want, have) + } + } +} + +func TestScan(t *testing.T) { + id := ulid.MustNew(123, crand.Reader) + + for _, tc := range []struct { + name string + in interface{} + out ulid.ULID + err error + }{ + {"string", id.String(), id, nil}, + {"bytes", id[:], id, nil}, + {"text-as-bytes", []byte(id.String()), id, nil}, + {"nil", nil, ulid.ULID{}, nil}, + {"other", 44, ulid.ULID{}, ulid.ErrScanValue}, + } { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + var out ulid.ULID + err := out.Scan(tc.in) + if got, want := out, tc.out; got.Compare(want) != 0 { + t.Errorf("got ULID %s, want %s", got, want) + } + + if got, want := fmt.Sprint(err), fmt.Sprint(tc.err); got != want { + t.Errorf("got err %q, want %q", got, want) + } + }) + } +} + +func TestMonotonic(t *testing.T) { + now := ulid.Now() + for _, e := range []struct { + name string + mk func() io.Reader + }{ + {"cryptorand", func() io.Reader { return crand.Reader }}, + {"mathrand", func() io.Reader { return rand.New(rand.NewSource(int64(now))) }}, + } { + for _, inc := range []uint64{ + 0, + 1, + 2, + math.MaxUint8 + 1, + math.MaxUint16 + 1, + math.MaxUint32 + 1, + } { + inc := inc + entropy := ulid.Monotonic(e.mk(), uint64(inc)) + + t.Run(fmt.Sprintf("entropy=%s/inc=%d", e.name, inc), func(t *testing.T) { + t.Parallel() + + var prev ulid.ULID + for i := 0; i < 10000; i++ { + next, err := ulid.New(123, entropy) + if err != nil { + t.Fatal(err) + } + + if prev.Compare(next) >= 0 { + t.Fatalf("prev: %v %v > next: %v %v", + prev.Time(), prev.Entropy(), next.Time(), next.Entropy()) + } + + prev = next + } + }) + } + } +} + +func TestMonotonicOverflow(t *testing.T) { + t.Parallel() + + entropy := ulid.Monotonic( + io.MultiReader( + bytes.NewReader(bytes.Repeat([]byte{0xFF}, 10)), // Entropy for first ULID + crand.Reader, // Following random entropy + ), + 0, + ) + + prev, err := ulid.New(0, entropy) + if err != nil { + t.Fatal(err) + } + + next, err := ulid.New(prev.Time(), entropy) + if have, want := err, ulid.ErrMonotonicOverflow; have != want { + t.Errorf("have ulid: %v %v err: %v, want err: %v", + next.Time(), next.Entropy(), have, want) + } +} + +func TestMonotonicSafe(t *testing.T) { + t.Parallel() + + var ( + rng = rand.New(rand.NewSource(time.Now().UnixNano())) + safe = &ulid.LockedMonotonicReader{MonotonicReader: ulid.Monotonic(rng, 0)} + t0 = ulid.Timestamp(time.Now()) + ) + + errs := make(chan error, 100) + for i := 0; i < cap(errs); i++ { + go func() { + u0 := ulid.MustNew(t0, safe) + u1 := u0 + for j := 0; j < 1024; j++ { + u0, u1 = u1, ulid.MustNew(t0, safe) + if u0.String() >= u1.String() { + errs <- fmt.Errorf( + "%s (%d %x) >= %s (%d %x)", + u0.String(), u0.Time(), u0.Entropy(), + u1.String(), u1.Time(), u1.Entropy(), + ) + return + } + } + errs <- nil + }() + } + + for i := 0; i < cap(errs); i++ { + if err := <-errs; err != nil { + t.Fatal(err) + } + } +} + +func TestULID_Bytes(t *testing.T) { + tt := time.Unix(1000000, 0) + entropy := ulid.Monotonic(rand.New(rand.NewSource(tt.UnixNano())), 0) + id := ulid.MustNew(ulid.Timestamp(tt), entropy) + bid := id.Bytes() + bid[len(bid)-1]++ + if bytes.Equal(id.Bytes(), bid) { + t.Error("Bytes() returned a reference to ulid underlying array!") + } +} + +func BenchmarkNew(b *testing.B) { + benchmarkMakeULID(b, func(timestamp uint64, entropy io.Reader) { + _, _ = ulid.New(timestamp, entropy) + }) +} + +func BenchmarkMustNew(b *testing.B) { + benchmarkMakeULID(b, func(timestamp uint64, entropy io.Reader) { + _ = ulid.MustNew(timestamp, entropy) + }) +} + +func benchmarkMakeULID(b *testing.B, f func(uint64, io.Reader)) { + b.ReportAllocs() + b.SetBytes(int64(len(ulid.ULID{}))) + + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + + for _, tc := range []struct { + name string + timestamps []uint64 + entropy io.Reader + }{ + {"WithCrypoEntropy", []uint64{123}, crand.Reader}, + {"WithEntropy", []uint64{123}, rng}, + {"WithMonotonicEntropy_SameTimestamp_Inc0", []uint64{123}, ulid.Monotonic(rng, 0)}, + {"WithMonotonicEntropy_DifferentTimestamp_Inc0", []uint64{122, 123}, ulid.Monotonic(rng, 0)}, + {"WithMonotonicEntropy_SameTimestamp_Inc1", []uint64{123}, ulid.Monotonic(rng, 1)}, + {"WithMonotonicEntropy_DifferentTimestamp_Inc1", []uint64{122, 123}, ulid.Monotonic(rng, 1)}, + {"WithCryptoMonotonicEntropy_SameTimestamp_Inc1", []uint64{123}, ulid.Monotonic(crand.Reader, 1)}, + {"WithCryptoMonotonicEntropy_DifferentTimestamp_Inc1", []uint64{122, 123}, ulid.Monotonic(crand.Reader, 1)}, + {"WithoutEntropy", []uint64{123}, nil}, + } { + tc := tc + b.Run(tc.name, func(b *testing.B) { + b.StopTimer() + b.ResetTimer() + b.StartTimer() + for i := 0; i < b.N; i++ { + f(tc.timestamps[i%len(tc.timestamps)], tc.entropy) + } + }) + } +} + +func BenchmarkParse(b *testing.B) { + const s = "0000XSNJG0MQJHBF4QX1EFD6Y3" + b.SetBytes(int64(len(s))) + for i := 0; i < b.N; i++ { + _, _ = ulid.Parse(s) + } +} + +func BenchmarkParseStrict(b *testing.B) { + const s = "0000XSNJG0MQJHBF4QX1EFD6Y3" + b.SetBytes(int64(len(s))) + for i := 0; i < b.N; i++ { + _, _ = ulid.ParseStrict(s) + } +} + +func BenchmarkMustParse(b *testing.B) { + const s = "0000XSNJG0MQJHBF4QX1EFD6Y3" + b.SetBytes(int64(len(s))) + for i := 0; i < b.N; i++ { + _ = ulid.MustParse(s) + } +} + +func BenchmarkString(b *testing.B) { + entropy := rand.New(rand.NewSource(time.Now().UnixNano())) + id := ulid.MustNew(123456, entropy) + b.SetBytes(int64(len(id))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.String() + } +} + +func BenchmarkMarshal(b *testing.B) { + entropy := rand.New(rand.NewSource(time.Now().UnixNano())) + buf := make([]byte, ulid.EncodedSize) + id := ulid.MustNew(123456, entropy) + + b.Run("Text", func(b *testing.B) { + b.SetBytes(int64(len(id))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = id.MarshalText() + } + }) + + b.Run("TextTo", func(b *testing.B) { + b.SetBytes(int64(len(id))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.MarshalTextTo(buf) + } + }) + + b.Run("Binary", func(b *testing.B) { + b.SetBytes(int64(len(id))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = id.MarshalBinary() + } + }) + + b.Run("BinaryTo", func(b *testing.B) { + b.SetBytes(int64(len(id))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.MarshalBinaryTo(buf) + } + }) +} + +func BenchmarkUnmarshal(b *testing.B) { + var id ulid.ULID + s := "0000XSNJG0MQJHBF4QX1EFD6Y3" + txt := []byte(s) + bin, _ := ulid.MustParse(s).MarshalBinary() + + b.Run("Text", func(b *testing.B) { + b.SetBytes(int64(len(txt))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.UnmarshalText(txt) + } + }) + + b.Run("Binary", func(b *testing.B) { + b.SetBytes(int64(len(bin))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.UnmarshalBinary(bin) + } + }) +} + +func BenchmarkNow(b *testing.B) { + b.SetBytes(8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = ulid.Now() + } +} + +func BenchmarkTimestamp(b *testing.B) { + now := time.Now() + b.SetBytes(8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = ulid.Timestamp(now) + } +} + +func BenchmarkTime(b *testing.B) { + id := ulid.MustNew(123456789, nil) + b.SetBytes(8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.Time() + } +} + +func BenchmarkSetTime(b *testing.B) { + var id ulid.ULID + b.SetBytes(8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.SetTime(123456789) + } +} + +func BenchmarkEntropy(b *testing.B) { + id := ulid.MustNew(0, strings.NewReader("ABCDEFGHIJKLMNOP")) + b.SetBytes(10) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.Entropy() + } +} + +func BenchmarkSetEntropy(b *testing.B) { + var id ulid.ULID + e := []byte("ABCDEFGHIJKLMNOP") + b.SetBytes(10) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.SetEntropy(e) + } +} + +func BenchmarkCompare(b *testing.B) { + id, other := ulid.MustNew(12345, nil), ulid.MustNew(54321, nil) + b.SetBytes(int64(len(id) * 2)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = id.Compare(other) + } +}