Codebase list golang-github-rs-xid / eb75aa1
New upstream release. Debian Janitor 2 years ago
15 changed file(s) with 679 addition(s) and 106 deletion(s). Raw diff Collapse all Expand all
0 version: 1.0.0.{build}
1
2 platform: x64
3
4 branches:
5 only:
6 - master
7
8 clone_folder: c:\gopath\src\github.com\rs\xid
9
10 environment:
11 GOPATH: c:\gopath
12
13 install:
14 - echo %PATH%
15 - echo %GOPATH%
16 - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
17 - go version
18 - go env
19 - go get -t .
20
21 build_script:
22 - go build
23
24 test_script:
25 - go test
26
00 language: go
11 go:
2 - 1.5
3 - tip
2 - "1.9"
3 - "1.10"
4 - "master"
45 matrix:
56 allow_failures:
6 - go: tip
7 - go: "master"
11
22 [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/xid) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/xid/master/LICENSE) [![Build Status](https://travis-ci.org/rs/xid.svg?branch=master)](https://travis-ci.org/rs/xid) [![Coverage](http://gocover.io/_badge/github.com/rs/xid)](http://gocover.io/github.com/rs/xid)
33
4 Package xid is a globally unique id generator library, ready to be used safely directly in your server code.
4 Package xid is a globally unique id generator library, ready to safely be used directly in your server code.
55
6 Xid is using Mongo Object ID algorithm to generate globally unique ids with a different serialization (bast64) to make it shorter when transported as a string:
6 Xid uses the Mongo Object ID algorithm to generate globally unique ids with a different serialization (base64) to make it shorter when transported as a string:
77 https://docs.mongodb.org/manual/reference/object-id/
88
99 - 4-byte value representing the seconds since the Unix epoch,
3232 |-------------|-------------|----------------|----------------
3333 | [UUID] | 16 bytes | 36 chars | configuration free, not sortable
3434 | [shortuuid] | 16 bytes | 22 chars | configuration free, not sortable
35 | [Snowflake] | 8 bytes | up to 20 chars | needs machin/DC configuration, needs central server, sortable
35 | [Snowflake] | 8 bytes | up to 20 chars | needs machine/DC configuration, needs central server, sortable
3636 | [MongoID] | 12 bytes | 24 chars | configuration free, sortable
3737 | xid | 12 bytes | 20 chars | configuration free, sortable
3838
5151 - Unicity guaranteed for 16,777,216 (24 bits) unique ids per second and per host/process
5252 - Lock-free (i.e.: unlike UUIDv1 and v2)
5353
54 Best used with [xlog](https://github.com/rs/xlog)'s
55 [RequestIDHandler](https://godoc.org/github.com/rs/xlog#RequestIDHandler).
54 Best used with [zerolog](https://github.com/rs/zerolog)'s
55 [RequestIDHandler](https://godoc.org/github.com/rs/zerolog/hlog#RequestIDHandler).
56
57 Notes:
58
59 - Xid is dependent on the system time, a monotonic counter and so is not cryptographically secure. If unpredictability of IDs is important, you should not use Xids. It is worth noting that most other UUID-like implementations are also not cryptographically secure. You should use libraries that rely on cryptographically secure sources (like /dev/urandom on unix, crypto/rand in golang), if you want a truly random ID generator.
5660
5761 References:
5862
6064 - https://en.wikipedia.org/wiki/Universally_unique_identifier
6165 - https://blog.twitter.com/2010/announcing-snowflake
6266 - Python port by [Graham Abbott](https://github.com/graham): https://github.com/graham/python_xid
67 - Scala port by [Egor Kolotaev](https://github.com/kolotaev): https://github.com/kolotaev/ride
68 - Rust port by [Jérôme Renard](https://github.com/jeromer/): https://github.com/jeromer/libxid
69 - Ruby port by [Valar](https://github.com/valarpirai/): https://github.com/valarpirai/ruby_xid
70 - Java port by [0xShamil](https://github.com/0xShamil/): https://github.com/0xShamil/java-xid
6371
6472 ## Install
6573
99107 BenchmarkUUIDv4-4 1000000 1452 ns/op 64 B/op 2 allocs/op
100108 ```
101109
102 Note: UUIDv1 requires a global lock, hence the performence degrading as we add more CPUs.
110 Note: UUIDv1 requires a global lock, hence the performance degradation as we add more CPUs.
103111
104112 ## Licenses
105113
0 # Bytes storage
1
2 This subpackage is there to allow storage of XIDs in a binary format in, for example, a database.
3 It allows some data size optimisation as the 12 bytes will be smaller to store than a string.
0 package xidb
1
2 import (
3 "database/sql/driver"
4 "fmt"
5
6 "github.com/rs/xid"
7 )
8
9 type ID struct {
10 xid.ID
11 }
12
13 // Value implements the driver.Valuer interface.
14 func (id ID) Value() (driver.Value, error) {
15 if id.ID.IsNil() {
16 return nil, nil
17 }
18 return id.ID[:], nil
19 }
20
21 // Scan implements the sql.Scanner interface.
22 func (id *ID) Scan(value interface{}) (err error) {
23 switch val := value.(type) {
24 case []byte:
25 i, err := xid.FromBytes(val)
26 if err != nil {
27 return err
28 }
29 *id = ID{ID: i}
30 return nil
31 case nil:
32 *id = ID{ID: xid.NilID()}
33 return nil
34 default:
35 return fmt.Errorf("xid: scanning unsupported type: %T", value)
36 }
37 }
0 package xidb
1
2 import (
3 "reflect"
4 "testing"
5
6 "github.com/rs/xid"
7 )
8
9 func TestIDValue(t *testing.T) {
10 i, _ := xid.FromString("9m4e2mr0ui3e8a215n4g")
11
12 tests := []struct {
13 name string
14 id ID
15 expectedVal interface{}
16 }{
17 {
18 name: "non nil id",
19 id: ID{ID: i},
20 expectedVal: i.Bytes(),
21 },
22 {
23 name: "nil id",
24 id: ID{ID: xid.NilID()},
25 expectedVal: nil,
26 },
27 }
28
29 for _, tt := range tests {
30 t.Run(tt.name, func(t *testing.T) {
31 got, _ := tt.id.Value()
32 if !reflect.DeepEqual(got, tt.expectedVal) {
33 t.Errorf("wanted %v, got %v", tt.expectedVal, got)
34 }
35 })
36 }
37 }
38
39 func TestIDScan(t *testing.T) {
40 i, _ := xid.FromString("9m4e2mr0ui3e8a215n4g")
41
42 tests := []struct {
43 name string
44 val interface{}
45 expectedID ID
46 expectedErr bool
47 }{
48 {
49 name: "bytes id",
50 val: i.Bytes(),
51 expectedID: ID{ID: i},
52 },
53 {
54 name: "nil id",
55 val: nil,
56 expectedID: ID{ID: xid.NilID()},
57 },
58 {
59 name: "wrong bytes",
60 val: []byte{0x01},
61 expectedErr: true,
62 },
63 {
64 name: "unknown type",
65 val: 1,
66 expectedErr: true,
67 },
68 }
69
70 for _, tt := range tests {
71 t.Run(tt.name, func(t *testing.T) {
72 id := &ID{}
73 err := id.Scan(tt.val)
74 if (err != nil) != tt.expectedErr {
75 t.Errorf("error expected: %t, got %t", tt.expectedErr, (err != nil))
76 }
77 if err == nil {
78 if !reflect.DeepEqual(id.ID, tt.expectedID.ID) {
79 t.Errorf("wanted %v, got %v", tt.expectedID, id)
80 }
81 }
82
83 })
84 }
85 }
0 golang-github-rs-xid (1.3.0-1) UNRELEASED; urgency=low
1
2 * New upstream release.
3
4 -- Debian Janitor <janitor@jelmer.uk> Tue, 25 Jan 2022 21:58:36 -0000
5
06 golang-github-rs-xid (1.1-5) unstable; urgency=medium
17
28 [ Michael Stapelberg ]
0 module github.com/rs/xid
1
2 go 1.12
0 // +build darwin
1
2 package xid
3
4 import "syscall"
5
6 func readPlatformMachineID() (string, error) {
7 return syscall.Sysctl("kern.uuid")
8 }
0 // +build !darwin,!linux,!freebsd,!windows
1
2 package xid
3
4 import "errors"
5
6 func readPlatformMachineID() (string, error) {
7 return "", errors.New("not implemented")
8 }
0 // +build freebsd
1
2 package xid
3
4 import "syscall"
5
6 func readPlatformMachineID() (string, error) {
7 return syscall.Sysctl("kern.hostuuid")
8 }
0 // +build linux
1
2 package xid
3
4 import "io/ioutil"
5
6 func readPlatformMachineID() (string, error) {
7 b, err := ioutil.ReadFile("/etc/machine-id")
8 if err != nil || len(b) == 0 {
9 b, err = ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
10 }
11 return string(b), err
12 }
0 // +build windows
1
2 package xid
3
4 import (
5 "fmt"
6 "syscall"
7 "unsafe"
8 )
9
10 func readPlatformMachineID() (string, error) {
11 // source: https://github.com/shirou/gopsutil/blob/master/host/host_syscall.go
12 var h syscall.Handle
13 err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, syscall.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, syscall.KEY_READ|syscall.KEY_WOW64_64KEY, &h)
14 if err != nil {
15 return "", err
16 }
17 defer syscall.RegCloseKey(h)
18
19 const syscallRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
20 const uuidLen = 36
21
22 var regBuf [syscallRegBufLen]uint16
23 bufLen := uint32(syscallRegBufLen)
24 var valType uint32
25 err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
26 if err != nil {
27 return "", err
28 }
29
30 hostID := syscall.UTF16ToString(regBuf[:])
31 hostIDLen := len(hostID)
32 if hostIDLen != uuidLen {
33 return "", fmt.Errorf("HostID incorrect: %q\n", hostID)
34 }
35
36 return hostID, nil
37 }
+170
-54
id.go less more
2929 // - Non configured, you don't need set a unique machine and/or data center id
3030 // - K-ordered
3131 // - Embedded time with 1 second precision
32 // - Unicity guaranted for 16,777,216 (24 bits) unique ids per second and per host/process
32 // - Unicity guaranteed for 16,777,216 (24 bits) unique ids per second and per host/process
3333 //
3434 // Best used with xlog's RequestIDHandler (https://godoc.org/github.com/rs/xlog#RequestIDHandler).
3535 //
4141 package xid
4242
4343 import (
44 "bytes"
4445 "crypto/md5"
4546 "crypto/rand"
4647 "database/sql/driver"
4748 "encoding/binary"
4849 "errors"
4950 "fmt"
51 "hash/crc32"
52 "io/ioutil"
5053 "os"
54 "sort"
5155 "sync/atomic"
5256 "time"
57 "unsafe"
5358 )
5459
5560 // Code inspired from mgo/bson ObjectId
5964
6065 const (
6166 encodedLen = 20 // string encoded len
62 decodedLen = 15 // len after base32 decoding with the padded data
6367 rawLen = 12 // binary raw len
6468
6569 // encoding stores a custom version of the base32 encoding with lower case
6771 encoding = "0123456789abcdefghijklmnopqrstuv"
6872 )
6973
70 // ErrInvalidID is returned when trying to unmarshal an invalid ID
71 var ErrInvalidID = errors.New("xid: invalid ID")
72
73 // objectIDCounter is atomically incremented when generating a new ObjectId
74 // using NewObjectId() function. It's used as a counter part of an id.
75 // This id is initialized with a random value.
76 var objectIDCounter = randInt()
77
78 // machineId stores machine id generated once and used in subsequent calls
79 // to NewObjectId function.
80 var machineID = readMachineID()
81
82 // pid stores the current process id
83 var pid = os.Getpid()
84
85 // dec is the decoding map for base32 encoding
86 var dec [256]byte
74 var (
75 // ErrInvalidID is returned when trying to unmarshal an invalid ID
76 ErrInvalidID = errors.New("xid: invalid ID")
77
78 // objectIDCounter is atomically incremented when generating a new ObjectId
79 // using NewObjectId() function. It's used as a counter part of an id.
80 // This id is initialized with a random value.
81 objectIDCounter = randInt()
82
83 // machineId stores machine id generated once and used in subsequent calls
84 // to NewObjectId function.
85 machineID = readMachineID()
86
87 // pid stores the current process id
88 pid = os.Getpid()
89
90 nilID ID
91
92 // dec is the decoding map for base32 encoding
93 dec [256]byte
94 )
8795
8896 func init() {
8997 for i := 0; i < len(dec); i++ {
9199 }
92100 for i := 0; i < len(encoding); i++ {
93101 dec[encoding[i]] = byte(i)
102 }
103
104 // If /proc/self/cpuset exists and is not /, we can assume that we are in a
105 // form of container and use the content of cpuset xor-ed with the PID in
106 // order get a reasonable machine global unique PID.
107 b, err := ioutil.ReadFile("/proc/self/cpuset")
108 if err == nil && len(b) > 1 {
109 pid ^= int(crc32.ChecksumIEEE(b))
94110 }
95111 }
96112
99115 // a runtime error.
100116 func readMachineID() []byte {
101117 id := make([]byte, 3)
102 if hostname, err := os.Hostname(); err == nil {
118 hid, err := readPlatformMachineID()
119 if err != nil || len(hid) == 0 {
120 hid, err = os.Hostname()
121 }
122 if err == nil && len(hid) != 0 {
103123 hw := md5.New()
104 hw.Write([]byte(hostname))
124 hw.Write([]byte(hid))
105125 copy(id, hw.Sum(nil))
106126 } else {
107127 // Fallback to rand number if machine id can't be gathered
121141 return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
122142 }
123143
124 // New generates a globaly unique ID
144 // New generates a globally unique ID
125145 func New() ID {
146 return NewWithTime(time.Now())
147 }
148
149 // NewWithTime generates a globally unique ID with the passed in time
150 func NewWithTime(t time.Time) ID {
126151 var id ID
127152 // Timestamp, 4 bytes, big endian
128 binary.BigEndian.PutUint32(id[:], uint32(time.Now().Unix()))
153 binary.BigEndian.PutUint32(id[:], uint32(t.Unix()))
129154 // Machine, first 3 bytes of md5(hostname)
130155 id[4] = machineID[0]
131156 id[5] = machineID[1]
152177 func (id ID) String() string {
153178 text := make([]byte, encodedLen)
154179 encode(text, id[:])
155 return string(text)
180 return *(*string)(unsafe.Pointer(&text))
181 }
182
183 // Encode encodes the id using base32 encoding, writing 20 bytes to dst and return it.
184 func (id ID) Encode(dst []byte) []byte {
185 encode(dst, id[:])
186 return dst
156187 }
157188
158189 // MarshalText implements encoding/text TextMarshaler interface
162193 return text, nil
163194 }
164195
196 // MarshalJSON implements encoding/json Marshaler interface
197 func (id ID) MarshalJSON() ([]byte, error) {
198 if id.IsNil() {
199 return []byte("null"), nil
200 }
201 text := make([]byte, encodedLen+2)
202 encode(text[1:encodedLen+1], id[:])
203 text[0], text[encodedLen+1] = '"', '"'
204 return text, nil
205 }
206
165207 // encode by unrolling the stdlib base32 algorithm + removing all safe checks
166208 func encode(dst, id []byte) {
209 _ = dst[19]
210 _ = id[11]
211
212 dst[19] = encoding[(id[11]<<4)&0x1F]
213 dst[18] = encoding[(id[11]>>1)&0x1F]
214 dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F]
215 dst[16] = encoding[id[10]>>3]
216 dst[15] = encoding[id[9]&0x1F]
217 dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F]
218 dst[13] = encoding[(id[8]>>2)&0x1F]
219 dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F]
220 dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F]
221 dst[10] = encoding[(id[6]>>1)&0x1F]
222 dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F]
223 dst[8] = encoding[id[5]>>3]
224 dst[7] = encoding[id[4]&0x1F]
225 dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F]
226 dst[5] = encoding[(id[3]>>2)&0x1F]
227 dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F]
228 dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F]
229 dst[2] = encoding[(id[1]>>1)&0x1F]
230 dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F]
167231 dst[0] = encoding[id[0]>>3]
168 dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F]
169 dst[2] = encoding[(id[1]>>1)&0x1F]
170 dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F]
171 dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F]
172 dst[5] = encoding[(id[3]>>2)&0x1F]
173 dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F]
174 dst[7] = encoding[id[4]&0x1F]
175 dst[8] = encoding[id[5]>>3]
176 dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F]
177 dst[10] = encoding[(id[6]>>1)&0x1F]
178 dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F]
179 dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F]
180 dst[13] = encoding[(id[8]>>2)&0x1F]
181 dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F]
182 dst[15] = encoding[id[9]&0x1F]
183 dst[16] = encoding[id[10]>>3]
184 dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F]
185 dst[18] = encoding[(id[11]>>1)&0x1F]
186 dst[19] = encoding[(id[11]<<4)&0x1F]
187232 }
188233
189234 // UnmarshalText implements encoding/text TextUnmarshaler interface
200245 return nil
201246 }
202247
248 // UnmarshalJSON implements encoding/json Unmarshaler interface
249 func (id *ID) UnmarshalJSON(b []byte) error {
250 s := string(b)
251 if s == "null" {
252 *id = nilID
253 return nil
254 }
255 return id.UnmarshalText(b[1 : len(b)-1])
256 }
257
203258 // decode by unrolling the stdlib base32 algorithm + removing all safe checks
204259 func decode(id *ID, src []byte) {
260 _ = src[19]
261 _ = id[11]
262
263 id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4
264 id[10] = dec[src[16]]<<3 | dec[src[17]]>>2
265 id[9] = dec[src[14]]<<5 | dec[src[15]]
266 id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3
267 id[7] = dec[src[11]]<<4 | dec[src[12]]>>1
268 id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4
269 id[5] = dec[src[8]]<<3 | dec[src[9]]>>2
270 id[4] = dec[src[6]]<<5 | dec[src[7]]
271 id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3
272 id[2] = dec[src[3]]<<4 | dec[src[4]]>>1
273 id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4
205274 id[0] = dec[src[0]]<<3 | dec[src[1]]>>2
206 id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4
207 id[2] = dec[src[3]]<<4 | dec[src[4]]>>1
208 id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3
209 id[4] = dec[src[6]]<<5 | dec[src[7]]
210 id[5] = dec[src[8]]<<3 | dec[src[9]]>>2
211 id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4
212 id[7] = dec[src[11]]<<4 | dec[src[12]]>>1
213 id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3
214 id[9] = dec[src[14]]<<5 | dec[src[15]]
215 id[10] = dec[src[16]]<<3 | dec[src[17]]>>2
216 id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4
217275 }
218276
219277 // Time returns the timestamp part of the id.
246304
247305 // Value implements the driver.Valuer interface.
248306 func (id ID) Value() (driver.Value, error) {
307 if id.IsNil() {
308 return nil, nil
309 }
249310 b, err := id.MarshalText()
250311 return string(b), err
251312 }
257318 return id.UnmarshalText([]byte(val))
258319 case []byte:
259320 return id.UnmarshalText(val)
321 case nil:
322 *id = nilID
323 return nil
260324 default:
261325 return fmt.Errorf("xid: scanning unsupported type: %T", value)
262326 }
263327 }
328
329 // IsNil Returns true if this is a "nil" ID
330 func (id ID) IsNil() bool {
331 return id == nilID
332 }
333
334 // NilID returns a zero value for `xid.ID`.
335 func NilID() ID {
336 return nilID
337 }
338
339 // Bytes returns the byte array representation of `ID`
340 func (id ID) Bytes() []byte {
341 return id[:]
342 }
343
344 // FromBytes convert the byte array representation of `ID` back to `ID`
345 func FromBytes(b []byte) (ID, error) {
346 var id ID
347 if len(b) != rawLen {
348 return id, ErrInvalidID
349 }
350 copy(id[:], b)
351 return id, nil
352 }
353
354 // Compare returns an integer comparing two IDs. It behaves just like `bytes.Compare`.
355 // The result will be 0 if two IDs are identical, -1 if current id is less than the other one,
356 // and 1 if current id is greater than the other.
357 func (id ID) Compare(other ID) int {
358 return bytes.Compare(id[:], other[:])
359 }
360
361 type sorter []ID
362
363 func (s sorter) Len() int {
364 return len(s)
365 }
366
367 func (s sorter) Less(i, j int) bool {
368 return s[i].Compare(s[j]) < 0
369 }
370
371 func (s sorter) Swap(i, j int) {
372 s[i], s[j] = s[j], s[i]
373 }
374
375 // Sort sorts an array of IDs inplace.
376 // It works by wrapping `[]ID` and use `sort.Sort`.
377 func Sort(ids []ID) {
378 sort.Sort(sorter(ids))
379 }
00 package xid
11
22 import (
3 "bytes"
34 "encoding/json"
5 "errors"
6 "fmt"
7 "reflect"
48 "testing"
59 "time"
6
7 "github.com/stretchr/testify/assert"
810 )
9
10 const strInvalidID = "xid: invalid ID"
1111
1212 type IDParts struct {
1313 id ID
4343
4444 func TestIDPartsExtraction(t *testing.T) {
4545 for i, v := range IDs {
46 assert.Equal(t, v.id.Time(), time.Unix(v.timestamp, 0), "#%d timestamp", i)
47 assert.Equal(t, v.id.Machine(), v.machine, "#%d machine", i)
48 assert.Equal(t, v.id.Pid(), v.pid, "#%d pid", i)
49 assert.Equal(t, v.id.Counter(), v.counter, "#%d counter", i)
46 t.Run(fmt.Sprintf("Test%d", i), func(t *testing.T) {
47 if got, want := v.id.Time(), time.Unix(v.timestamp, 0); got != want {
48 t.Errorf("Time() = %v, want %v", got, want)
49 }
50 if got, want := v.id.Machine(), v.machine; !bytes.Equal(got, want) {
51 t.Errorf("Machine() = %v, want %v", got, want)
52 }
53 if got, want := v.id.Pid(), v.pid; got != want {
54 t.Errorf("Pid() = %v, want %v", got, want)
55 }
56 if got, want := v.id.Counter(), v.counter; got != want {
57 t.Errorf("Counter() = %v, want %v", got, want)
58 }
59 })
5060 }
5161 }
5262
6272 // Test for uniqueness among all other 9 generated ids
6373 for j, tid := range ids {
6474 if j != i {
65 assert.NotEqual(t, id, tid, "Generated ID is not unique")
75 if id.Compare(tid) == 0 {
76 t.Errorf("generated ID is not unique (%d/%d)", i, j)
77 }
6678 }
6779 }
6880 // Check that timestamp was incremented and is within 30 seconds of the previous one
6981 secs := id.Time().Sub(prevID.Time()).Seconds()
70 assert.Equal(t, (secs >= 0 && secs <= 30), true, "Wrong timestamp in generated ID")
82 if secs < 0 || secs > 30 {
83 t.Error("wrong timestamp in generated ID")
84 }
7185 // Check that machine ids are the same
72 assert.Equal(t, id.Machine(), prevID.Machine())
86 if !bytes.Equal(id.Machine(), prevID.Machine()) {
87 t.Error("machine ID not equal")
88 }
7389 // Check that pids are the same
74 assert.Equal(t, id.Pid(), prevID.Pid())
90 if id.Pid() != prevID.Pid() {
91 t.Error("pid not equal")
92 }
7593 // Test for proper increment
76 delta := int(id.Counter() - prevID.Counter())
77 assert.Equal(t, delta, 1, "Wrong increment in generated ID")
94 if got, want := int(id.Counter()-prevID.Counter()), 1; got != want {
95 t.Errorf("wrong increment in generated ID, delta=%v, want %v", got, want)
96 }
7897 }
7998 }
8099
81100 func TestIDString(t *testing.T) {
82101 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
83 assert.Equal(t, "9m4e2mr0ui3e8a215n4g", id.String())
102 if got, want := id.String(), "9m4e2mr0ui3e8a215n4g"; got != want {
103 t.Errorf("String() = %v, want %v", got, want)
104 }
105 }
106
107 func TestIDEncode(t *testing.T) {
108 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
109 text := make([]byte, encodedLen)
110 if got, want := string(id.Encode(text)), "9m4e2mr0ui3e8a215n4g"; got != want {
111 t.Errorf("Encode() = %v, want %v", got, want)
112 }
84113 }
85114
86115 func TestFromString(t *testing.T) {
87 id, err := FromString("9m4e2mr0ui3e8a215n4g")
88 assert.NoError(t, err)
89 assert.Equal(t, ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}, id)
116 got, err := FromString("9m4e2mr0ui3e8a215n4g")
117 if err != nil {
118 t.Fatal(err)
119 }
120 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
121 if got != want {
122 t.Errorf("FromString() = %v, want %v", got, want)
123 }
90124 }
91125
92126 func TestFromStringInvalid(t *testing.T) {
93 id, err := FromString("invalid")
94 assert.EqualError(t, err, strInvalidID)
95 assert.Equal(t, ID{}, id)
127 _, err := FromString("invalid")
128 if err != ErrInvalidID {
129 t.Errorf("FromString(invalid) err=%v, want %v", err, ErrInvalidID)
130 }
96131 }
97132
98133 type jsonType struct {
104139 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
105140 v := jsonType{ID: &id, Str: "test"}
106141 data, err := json.Marshal(&v)
107 assert.NoError(t, err)
108 assert.Equal(t, `{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`, string(data))
142 if err != nil {
143 t.Fatal(err)
144 }
145 if got, want := string(data), `{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`; got != want {
146 t.Errorf("json.Marshal() = %v, want %v", got, want)
147 }
109148 }
110149
111150 func TestIDJSONUnmarshaling(t *testing.T) {
112151 data := []byte(`{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`)
113152 v := jsonType{}
114153 err := json.Unmarshal(data, &v)
115 assert.NoError(t, err)
116 assert.Equal(t, ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}, *v.ID)
154 if err != nil {
155 t.Fatal(err)
156 }
157 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
158 if got := *v.ID; got.Compare(want) != 0 {
159 t.Errorf("json.Unmarshal() = %v, want %v", got, want)
160 }
161
117162 }
118163
119164 func TestIDJSONUnmarshalingError(t *testing.T) {
120165 v := jsonType{}
121166 err := json.Unmarshal([]byte(`{"ID":"9M4E2MR0UI3E8A215N4G"}`), &v)
122 assert.EqualError(t, err, strInvalidID)
167 if err != ErrInvalidID {
168 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
169 }
123170 err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS"}`), &v)
124 assert.EqualError(t, err, strInvalidID)
171 if err != ErrInvalidID {
172 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
173 }
125174 err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS3kdk"}`), &v)
126 assert.EqualError(t, err, strInvalidID)
175 if err != ErrInvalidID {
176 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
177 }
127178 }
128179
129180 func TestIDDriverValue(t *testing.T) {
130181 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
131 data, err := id.Value()
132 assert.NoError(t, err)
133 assert.Equal(t, "9m4e2mr0ui3e8a215n4g", data)
182 got, err := id.Value()
183 if err != nil {
184 t.Fatal(err)
185 }
186 if want := "9m4e2mr0ui3e8a215n4g"; got != want {
187 t.Errorf("Value() = %v, want %v", got, want)
188 }
134189 }
135190
136191 func TestIDDriverScan(t *testing.T) {
137 id := ID{}
138 err := id.Scan("9m4e2mr0ui3e8a215n4g")
139 assert.NoError(t, err)
140 assert.Equal(t, ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}, id)
192 got := ID{}
193 err := got.Scan("9m4e2mr0ui3e8a215n4g")
194 if err != nil {
195 t.Fatal(err)
196 }
197 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
198 if got.Compare(want) != 0 {
199 t.Errorf("Scan() = %v, want %v", got, want)
200 }
141201 }
142202
143203 func TestIDDriverScanError(t *testing.T) {
144204 id := ID{}
145 err := id.Scan(0)
146 assert.EqualError(t, err, "xid: scanning unsupported type: int")
147 err = id.Scan("0")
148 assert.EqualError(t, err, strInvalidID)
205 if got, want := id.Scan(0), errors.New("xid: scanning unsupported type: int"); !reflect.DeepEqual(got, want) {
206 t.Errorf("Scan() err=%v, want %v", got, want)
207 }
208 if got, want := id.Scan("0"), ErrInvalidID; got != want {
209 t.Errorf("Scan() err=%v, want %v", got, want)
210 }
149211 }
150212
151213 func TestIDDriverScanByteFromDatabase(t *testing.T) {
152 id := ID{}
214 got := ID{}
153215 bs := []byte("9m4e2mr0ui3e8a215n4g")
154 err := id.Scan(bs)
155 assert.NoError(t, err)
156 assert.Equal(t, ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}, id)
216 err := got.Scan(bs)
217 if err != nil {
218 t.Fatal(err)
219 }
220 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
221 if got.Compare(want) != 0 {
222 t.Errorf("Scan() = %v, want %v", got, want)
223 }
157224 }
158225
159226 func BenchmarkNew(b *testing.B) {
195262 // }
196263 // })
197264 // }
265
266 func TestID_IsNil(t *testing.T) {
267 tests := []struct {
268 name string
269 id ID
270 want bool
271 }{
272 {
273 name: "ID not nil",
274 id: New(),
275 want: false,
276 },
277 {
278 name: "Nil ID",
279 id: ID{},
280 want: true,
281 },
282 }
283 for _, tt := range tests {
284 tt := tt
285 t.Run(tt.name, func(t *testing.T) {
286 if got, want := tt.id.IsNil(), tt.want; got != want {
287 t.Errorf("IsNil() = %v, want %v", got, want)
288 }
289 })
290 }
291 }
292
293 func TestNilID(t *testing.T) {
294 got := ID{}
295 if want := NilID(); !reflect.DeepEqual(got, want) {
296 t.Error("NilID() not equal ID{}")
297 }
298 }
299
300 func TestNilID_IsNil(t *testing.T) {
301 if !NilID().IsNil() {
302 t.Error("NilID().IsNil() is not true")
303 }
304 }
305
306 func TestFromBytes_Invariant(t *testing.T) {
307 want := New()
308 got, err := FromBytes(want.Bytes())
309 if err != nil {
310 t.Fatal(err)
311 }
312 if got.Compare(want) != 0 {
313 t.Error("FromBytes(id.Bytes()) != id")
314 }
315 }
316
317 func TestFromBytes_InvalidBytes(t *testing.T) {
318 cases := []struct {
319 length int
320 shouldFail bool
321 }{
322 {11, true},
323 {12, false},
324 {13, true},
325 }
326 for _, c := range cases {
327 b := make([]byte, c.length, c.length)
328 _, err := FromBytes(b)
329 if got, want := err != nil, c.shouldFail; got != want {
330 t.Errorf("FromBytes() error got %v, want %v", got, want)
331 }
332 }
333 }
334
335 func TestID_Compare(t *testing.T) {
336 pairs := []struct {
337 left ID
338 right ID
339 expected int
340 }{
341 {IDs[1].id, IDs[0].id, -1},
342 {ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, IDs[2].id, -1},
343 {IDs[0].id, IDs[0].id, 0},
344 }
345 for _, p := range pairs {
346 if p.expected != p.left.Compare(p.right) {
347 t.Errorf("%s Compare to %s should return %d", p.left, p.right, p.expected)
348 }
349 if -1*p.expected != p.right.Compare(p.left) {
350 t.Errorf("%s Compare to %s should return %d", p.right, p.left, -1*p.expected)
351 }
352 }
353 }
354
355 var IDList = []ID{IDs[0].id, IDs[1].id, IDs[2].id}
356
357 func TestSorter_Len(t *testing.T) {
358 if got, want := sorter([]ID{}).Len(), 0; got != want {
359 t.Errorf("Len() %v, want %v", got, want)
360 }
361 if got, want := sorter(IDList).Len(), 3; got != want {
362 t.Errorf("Len() %v, want %v", got, want)
363 }
364 }
365
366 func TestSorter_Less(t *testing.T) {
367 sorter := sorter(IDList)
368 if !sorter.Less(1, 0) {
369 t.Errorf("Less(1, 0) not true")
370 }
371 if sorter.Less(2, 1) {
372 t.Errorf("Less(2, 1) true")
373 }
374 if sorter.Less(0, 0) {
375 t.Errorf("Less(0, 0) true")
376 }
377 }
378
379 func TestSorter_Swap(t *testing.T) {
380 ids := make([]ID, 0)
381 ids = append(ids, IDList...)
382 sorter := sorter(ids)
383 sorter.Swap(0, 1)
384 if got, want := ids[0], IDList[1]; !reflect.DeepEqual(got, want) {
385 t.Error("ids[0] != IDList[1]")
386 }
387 if got, want := ids[1], IDList[0]; !reflect.DeepEqual(got, want) {
388 t.Error("ids[1] != IDList[0]")
389 }
390 sorter.Swap(2, 2)
391 if got, want := ids[2], IDList[2]; !reflect.DeepEqual(got, want) {
392 t.Error("ids[2], IDList[2]")
393 }
394 }
395
396 func TestSort(t *testing.T) {
397 ids := make([]ID, 0)
398 ids = append(ids, IDList...)
399 Sort(ids)
400 if got, want := ids, []ID{IDList[1], IDList[2], IDList[0]}; !reflect.DeepEqual(got, want) {
401 t.Fail()
402 }
403 }