-
Notifications
You must be signed in to change notification settings - Fork 0
/
id.go
147 lines (128 loc) · 3.34 KB
/
id.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package id
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"os"
"sync/atomic"
"time"
)
type Id struct {
stringValue string
byteSliceValue []byte
uint32Value uint32
}
var objectIDCounter = getRandomCounter()
func getRandomCounter() uint32 {
counter := make([]byte, 4)
rand.Read(counter)
return binary.LittleEndian.Uint32(counter)
}
// TODO: Change the pid to a hash of the object data.
var pid = os.Getpid()
var machineID = generateMachineID()
func generateMachineID() [3]byte {
var sum [3]byte // 3 byte Machine ID
id := sum[:]
hostname, err1 := os.Hostname()
if err1 != nil {
// if getting hostname failed
// get a crypto random id and return
_, err2 := io.ReadFull(rand.Reader, id)
if err2 != nil {
panic(fmt.Errorf("Cannot get hostname: %v, %v", err1, err2))
}
copy(sum[:], id)
return sum
}
// TODO Why md5? Lets use xxhash!
hw := NewXXHash32()
// append hostname to the running hash
hw.Write([]byte(hostname))
copy(sum[:], hw.Sum(nil))
return sum
}
// New returns a new unique ObjectId.
// 4 byte time,
// 3 byte Machine ID
// 2 byte pid
// 3 byte self increased id.
func NewFromSeed(c interface{}) Id {
var b [12]byte
// TimeStamp, 4 bytes, big endian.
binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
// Machine, first 3 bytes of md5(hostname)
for i := 0; i < len(machineID); i++ {
b[4+i] = machineID[i]
}
structHash := Hash(c).Bytes()
// Pid, 2 bytes, specs don't specify endianness, but we use big endian
b[7] = structHash[0]
b[8] = structHash[1]
// increment 3 bytes, big Endian
i := atomic.AddUint32(&objectIDCounter, 1)
b[9] = byte(i >> 16)
b[10] = byte(i >> 8)
b[11] = byte(i)
hw := NewXXHash32()
hw.Write(b[:])
return Id{
stringValue: hex.EncodeToString(b[:]),
uint32Value: hw.Sum32(),
byteSliceValue: b[:],
}
}
func New() Id {
var b [12]byte
// TimeStamp, 4 bytes, big endian.
binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
// Machine, first 3 bytes of xxh32(hostname)
for i := 0; i < len(machineID); i++ {
b[4+i] = machineID[i]
}
// Pid, 2 bytes, specs don't specify endianness, but we use big endian
b[7] = byte(pid >> 8)
b[8] = byte(pid)
// increment 3 bytes, big Endian
i := atomic.AddUint32(&objectIDCounter, 1)
b[9] = byte(i >> 16)
b[10] = byte(i >> 8)
b[11] = byte(i)
hw := NewXXHash32()
hw.Write(b[:])
return Id{
stringValue: hex.EncodeToString(b[:]),
uint32Value: hw.Sum32(),
byteSliceValue: b[:],
}
}
func NewShort() Id {
// TODO: Take out the pid , then nanoId could be take out the machine, then
// pico could be take out both
var b [8]byte
// TimeStamp, 4 bytes, big endian.
binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
// Machine, first 3 bytes of xxh32(hostname)
for i := 2; i < len(machineID); i++ {
b[2+i] = machineID[i]
}
// increment 3 bytes, big Endian
i := atomic.AddUint32(&objectIDCounter, 1)
b[5] = byte(i >> 16)
b[6] = byte(i >> 8)
b[7] = byte(i)
hw := NewXXHash32()
hw.Write(b[:])
return Id{
stringValue: hex.EncodeToString(b[:]),
uint32Value: hw.Sum32(),
byteSliceValue: b[:],
}
}
// TODO: Would be nice to be able to extract the timestamp, and possibly the
// value used as a seed.
func (self Id) Bytes() []byte { return self.byteSliceValue }
func (self Id) UInt32() uint32 { return self.uint32Value }
func (self Id) String() string { return self.stringValue }