mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-25 05:06:38 +00:00
98263a7de6
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
615 lines
19 KiB
Go
615 lines
19 KiB
Go
// 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"
|
|
"time"
|
|
)
|
|
|
|
/*
|
|
An 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 an 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")
|
|
)
|
|
|
|
// New returns an 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.
|
|
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 *monotonic:
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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 an 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
|
|
}
|
|
|
|
// maxTime is the maximum Unix time in milliseconds that can be
|
|
// represented in an ULID.
|
|
var maxTime = ULID{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.Time()
|
|
|
|
// MaxTime returns the maximum Unix time in milliseconds that
|
|
// can be encoded in an 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:
|
|
return id.UnmarshalBinary(x)
|
|
}
|
|
|
|
return ErrScanValue
|
|
}
|
|
|
|
// Value implements the sql/driver.Valuer interface. This returns the value
|
|
// represented as a byte slice. If instead a string is desirable, a wrapper
|
|
// type can be created that calls String().
|
|
//
|
|
// // stringValuer wraps a ULID as a string-based driver.Valuer.
|
|
// type stringValuer ULID
|
|
//
|
|
// func (id stringValuer) Value() (driver.Value, error) {
|
|
// return ULID(id).String(), nil
|
|
// }
|
|
//
|
|
// // Example usage.
|
|
// db.Exec("...", stringValuer(id))
|
|
func (id ULID) Value() (driver.Value, error) {
|
|
return id.MarshalBinary()
|
|
}
|
|
|
|
// Monotonic returns an entropy source that is guaranteed to yield
|
|
// strictly increasing entropy bytes for the same ULID timestamp.
|
|
// On conflicts, the previous ULID entropy is incremented with a
|
|
// random number between 1 and `inc` (inclusive).
|
|
//
|
|
// The provided entropy source must actually yield random bytes or else
|
|
// monotonic reads are not guaranteed to terminate, since there isn't
|
|
// enough randomness to compute an increment number.
|
|
//
|
|
// When `inc == 0`, it'll be set to a secure default of `math.MaxUint32`.
|
|
// The lower the value of `inc`, the easier the next ULID within the
|
|
// same millisecond is to guess. If your code depends on ULIDs having
|
|
// secure entropy bytes, then don't go under this default unless you know
|
|
// what you're doing.
|
|
//
|
|
// The returned io.Reader isn't safe for concurrent use.
|
|
func Monotonic(entropy io.Reader, inc uint64) io.Reader {
|
|
m := monotonic{
|
|
Reader: bufio.NewReader(entropy),
|
|
inc: inc,
|
|
}
|
|
|
|
if m.inc == 0 {
|
|
m.inc = math.MaxUint32
|
|
}
|
|
|
|
if rng, ok := entropy.(*rand.Rand); ok {
|
|
m.rng = rng
|
|
}
|
|
|
|
return &m
|
|
}
|
|
|
|
type monotonic struct {
|
|
io.Reader
|
|
ms uint64
|
|
inc uint64
|
|
entropy uint80
|
|
rand [8]byte
|
|
rng *rand.Rand
|
|
}
|
|
|
|
func (m *monotonic) 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 *monotonic) 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 *monotonic) 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<<msbitLen) - 1)
|
|
|
|
// Convert the read bytes into an uint64 with byteLen
|
|
// Optimized unrolled loop.
|
|
switch byteLen {
|
|
case 1:
|
|
inc = uint64(m.rand[0])
|
|
case 2:
|
|
inc = uint64(binary.LittleEndian.Uint16(m.rand[:2]))
|
|
case 3, 4:
|
|
inc = uint64(binary.LittleEndian.Uint32(m.rand[:4]))
|
|
case 5, 6, 7, 8:
|
|
inc = uint64(binary.LittleEndian.Uint64(m.rand[:8]))
|
|
}
|
|
}
|
|
|
|
// Range: [1, m.inc)
|
|
return 1 + inc, nil
|
|
}
|
|
|
|
type uint80 struct {
|
|
Hi uint16
|
|
Lo uint64
|
|
}
|
|
|
|
func (u *uint80) SetBytes(bs []byte) {
|
|
u.Hi = binary.BigEndian.Uint16(bs[:2])
|
|
u.Lo = binary.BigEndian.Uint64(bs[2:])
|
|
}
|
|
|
|
func (u *uint80) AppendTo(bs []byte) {
|
|
binary.BigEndian.PutUint16(bs[:2], u.Hi)
|
|
binary.BigEndian.PutUint64(bs[2:], u.Lo)
|
|
}
|
|
|
|
func (u *uint80) Add(n uint64) (overflow bool) {
|
|
lo, hi := u.Lo, u.Hi
|
|
if u.Lo += n; u.Lo < lo {
|
|
u.Hi++
|
|
}
|
|
return u.Hi < hi
|
|
}
|
|
|
|
func (u uint80) IsZero() bool {
|
|
return u.Hi == 0 && u.Lo == 0
|
|
}
|