1
2
3
4
5
6 package guid
7
8 import (
9 "crypto/rand"
10 "crypto/sha1"
11 "encoding"
12 "encoding/binary"
13 "fmt"
14 "strconv"
15 )
16
17
18
19
20
21 type Variant uint8
22
23
24 const (
25
26
27 VariantUnknown Variant = iota
28 VariantNCS
29 VariantRFC4122
30 VariantMicrosoft
31 VariantFuture
32 )
33
34
35
36
37 type Version uint8
38
39 func (v Version) String() string {
40 return strconv.FormatUint(uint64(v), 10)
41 }
42
43 var _ = (encoding.TextMarshaler)(GUID{})
44 var _ = (encoding.TextUnmarshaler)(&GUID{})
45
46
47 func NewV4() (GUID, error) {
48 var b [16]byte
49 if _, err := rand.Read(b[:]); err != nil {
50 return GUID{}, err
51 }
52
53 g := FromArray(b)
54 g.setVersion(4)
55 g.setVariant(VariantRFC4122)
56
57 return g, nil
58 }
59
60
61
62
63
64
65
66
67 func NewV5(namespace GUID, name []byte) (GUID, error) {
68 b := sha1.New()
69 namespaceBytes := namespace.ToArray()
70 b.Write(namespaceBytes[:])
71 b.Write(name)
72
73 a := [16]byte{}
74 copy(a[:], b.Sum(nil))
75
76 g := FromArray(a)
77 g.setVersion(5)
78 g.setVariant(VariantRFC4122)
79
80 return g, nil
81 }
82
83 func fromArray(b [16]byte, order binary.ByteOrder) GUID {
84 var g GUID
85 g.Data1 = order.Uint32(b[0:4])
86 g.Data2 = order.Uint16(b[4:6])
87 g.Data3 = order.Uint16(b[6:8])
88 copy(g.Data4[:], b[8:16])
89 return g
90 }
91
92 func (g GUID) toArray(order binary.ByteOrder) [16]byte {
93 b := [16]byte{}
94 order.PutUint32(b[0:4], g.Data1)
95 order.PutUint16(b[4:6], g.Data2)
96 order.PutUint16(b[6:8], g.Data3)
97 copy(b[8:16], g.Data4[:])
98 return b
99 }
100
101
102 func FromArray(b [16]byte) GUID {
103 return fromArray(b, binary.BigEndian)
104 }
105
106
107
108 func (g GUID) ToArray() [16]byte {
109 return g.toArray(binary.BigEndian)
110 }
111
112
113 func FromWindowsArray(b [16]byte) GUID {
114 return fromArray(b, binary.LittleEndian)
115 }
116
117
118
119 func (g GUID) ToWindowsArray() [16]byte {
120 return g.toArray(binary.LittleEndian)
121 }
122
123 func (g GUID) String() string {
124 return fmt.Sprintf(
125 "%08x-%04x-%04x-%04x-%012x",
126 g.Data1,
127 g.Data2,
128 g.Data3,
129 g.Data4[:2],
130 g.Data4[2:])
131 }
132
133
134
135
136 func FromString(s string) (GUID, error) {
137 if len(s) != 36 {
138 return GUID{}, fmt.Errorf("invalid GUID %q", s)
139 }
140 if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
141 return GUID{}, fmt.Errorf("invalid GUID %q", s)
142 }
143
144 var g GUID
145
146 data1, err := strconv.ParseUint(s[0:8], 16, 32)
147 if err != nil {
148 return GUID{}, fmt.Errorf("invalid GUID %q", s)
149 }
150 g.Data1 = uint32(data1)
151
152 data2, err := strconv.ParseUint(s[9:13], 16, 16)
153 if err != nil {
154 return GUID{}, fmt.Errorf("invalid GUID %q", s)
155 }
156 g.Data2 = uint16(data2)
157
158 data3, err := strconv.ParseUint(s[14:18], 16, 16)
159 if err != nil {
160 return GUID{}, fmt.Errorf("invalid GUID %q", s)
161 }
162 g.Data3 = uint16(data3)
163
164 for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
165 v, err := strconv.ParseUint(s[x:x+2], 16, 8)
166 if err != nil {
167 return GUID{}, fmt.Errorf("invalid GUID %q", s)
168 }
169 g.Data4[i] = uint8(v)
170 }
171
172 return g, nil
173 }
174
175 func (g *GUID) setVariant(v Variant) {
176 d := g.Data4[0]
177 switch v {
178 case VariantNCS:
179 d = (d & 0x7f)
180 case VariantRFC4122:
181 d = (d & 0x3f) | 0x80
182 case VariantMicrosoft:
183 d = (d & 0x1f) | 0xc0
184 case VariantFuture:
185 d = (d & 0x0f) | 0xe0
186 case VariantUnknown:
187 fallthrough
188 default:
189 panic(fmt.Sprintf("invalid variant: %d", v))
190 }
191 g.Data4[0] = d
192 }
193
194
195 func (g GUID) Variant() Variant {
196 b := g.Data4[0]
197 if b&0x80 == 0 {
198 return VariantNCS
199 } else if b&0xc0 == 0x80 {
200 return VariantRFC4122
201 } else if b&0xe0 == 0xc0 {
202 return VariantMicrosoft
203 } else if b&0xe0 == 0xe0 {
204 return VariantFuture
205 }
206 return VariantUnknown
207 }
208
209 func (g *GUID) setVersion(v Version) {
210 g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
211 }
212
213
214 func (g GUID) Version() Version {
215 return Version((g.Data3 & 0xF000) >> 12)
216 }
217
218
219 func (g GUID) MarshalText() ([]byte, error) {
220 return []byte(g.String()), nil
221 }
222
223
224
225 func (g *GUID) UnmarshalText(text []byte) error {
226 g2, err := FromString(string(text))
227 if err != nil {
228 return err
229 }
230 *g = g2
231 return nil
232 }
233
View as plain text