github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/utils/uuid/uuid.go (about) 1 package uuid 2 3 import ( 4 "crypto/rand" 5 "encoding/base64" 6 "encoding/binary" 7 "encoding/json" 8 "fmt" 9 10 "github.com/Asutorufa/yuhaiin/pkg/utils/uuid/base58" 11 ) 12 13 const ( 14 invalid uint8 = iota 15 b58Hlf 16 b64Hlf 17 b58 18 b64 19 std 20 ) 21 22 const hexTable = "0123456789abcdef" 23 24 type UUID struct { 25 hi uint64 26 lo uint64 27 tp uint8 28 } 29 30 func Random() UUID { 31 return randomUUID(std) 32 } 33 34 func RandomB64() UUID { 35 return randomUUID(b64) 36 } 37 38 func RandomB64Hlf() UUID { 39 return randomUUID(b64Hlf) 40 } 41 42 func RandomB58() UUID { 43 return randomUUID(b58) 44 } 45 46 func RandomB58Hlf() UUID { 47 return randomUUID(b58Hlf) 48 } 49 50 func Parse(s string) (UUID, error) { 51 switch len(s) { 52 case 32, 36, 34, 38, 41, 45: 53 return ParseStd(s) 54 case 22: 55 return ParseB64(s) 56 case 11: 57 return ParseB64Hlf(s) 58 } 59 return UUID{}, fmt.Errorf("unable to parse UUID: %s", s) 60 } 61 62 func ParseStd(s string) (UUID, error) { 63 switch len(s) { 64 case 32: // hash 65 case 36: // canonical 66 case 34, 38: 67 if s[0] != '{' || s[len(s)-1] != '}' { 68 return UUID{}, fmt.Errorf("uuid: incorrect UUID format in string %s", s) 69 } 70 s = s[1 : len(s)-1] 71 case 41, 45: 72 if s[:9] != "urn:uuid:" { 73 return UUID{}, fmt.Errorf("uuid: incorrect UUID format in string %q", s[:9]) 74 } 75 s = s[9:] 76 default: 77 return UUID{}, fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(s), s) 78 } 79 80 u := make([]byte, 16) 81 82 // canonical 83 if len(s) == 36 { 84 if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { 85 return UUID{}, fmt.Errorf("uuid: incorrect UUID format in string %q", s) 86 } 87 for i, x := range [16]byte{ 88 0, 2, 4, 6, 89 9, 11, 90 14, 16, 91 19, 21, 92 24, 26, 28, 30, 32, 34, 93 } { 94 v1 := fromHexChar(s[x]) 95 v2 := fromHexChar(s[x+1]) 96 if v1|v2 == 255 { 97 return UUID{}, fmt.Errorf("invalid UUID format: %s", s) 98 } 99 u[i] = (v1 << 4) | v2 100 } 101 return FromStd(u), nil 102 } 103 // hash like 104 for i := 0; i < 32; i += 2 { 105 v1 := fromHexChar(s[i]) 106 v2 := fromHexChar(s[i+1]) 107 if v1|v2 == 255 { 108 return UUID{}, fmt.Errorf("invalid UUID format: %s", s) 109 } 110 u[i/2] = (v1 << 4) | v2 111 } 112 return FromStd(u), nil 113 } 114 115 func ParseB64(s string) (UUID, error) { 116 if len(s) != 22 { 117 return UUID{}, fmt.Errorf("uuid: incorrect UUID length %d in string %s", len(s), s) 118 } 119 dst := make([]byte, 16) 120 _, err := base64.RawURLEncoding.Decode(dst, []byte(s)) 121 if err != nil { 122 return UUID{}, err 123 } 124 return FromB64(dst), nil 125 } 126 127 func ParseB64Hlf(s string) (UUID, error) { 128 if len(s) != 11 { 129 return UUID{}, fmt.Errorf("uuid: incorrect UUID length %d in string %s", len(s), s) 130 } 131 dst := make([]byte, 8) 132 _, err := base64.RawURLEncoding.Decode(dst, []byte(s)) 133 if err != nil { 134 return UUID{}, err 135 } 136 return FromB64Hlf(dst), nil 137 } 138 139 func ParseB58(s string) (UUID, error) { 140 l := len(s) 141 if l != 22 && l != 21 { 142 return UUID{}, fmt.Errorf("uuid: incorrect UUID length %d in string %s", l, s) 143 } 144 dst := base58.Decode(s) 145 uuid, ok := fromSlice(dst, b58) 146 if !ok { 147 return UUID{}, fmt.Errorf("unable to parse UUID: %s", s) 148 } 149 return uuid, nil 150 } 151 152 func ParseB58Hlf(s string) (UUID, error) { 153 l := len(s) 154 if l != 11 && l != 10 { 155 return UUID{}, fmt.Errorf("uuid: incorrect UUID length %d in string %s", l, s) 156 } 157 dst := base58.Decode(s) 158 uuid, ok := fromSlice(dst, b58Hlf) 159 if !ok { 160 return UUID{}, fmt.Errorf("unable to parse UUID: %s", s) 161 } 162 return uuid, nil 163 } 164 165 func FromStd(src []byte) (uuid UUID) { 166 uuid, _ = fromSlice(src, std) 167 return 168 } 169 170 func FromB64(src []byte) (uuid UUID) { 171 uuid, _ = fromSlice(src, b64) 172 return 173 } 174 175 func FromB64Hlf(src []byte) (uuid UUID) { 176 uuid, _ = fromSlice(src, b64Hlf) 177 return 178 } 179 180 func FromB58(src []byte) (uuid UUID) { 181 uuid, _ = fromSlice(src, b58) 182 return 183 } 184 185 func FromB58Hlf(src []byte) (uuid UUID) { 186 uuid, _ = fromSlice(src, b58Hlf) 187 return 188 } 189 190 func fromSlice(slice []byte, tp uint8) (uuid UUID, ok bool) { 191 switch len(slice) { 192 case 8: 193 if tp != b58Hlf && tp != b64Hlf { 194 return UUID{}, false 195 } 196 return UUID{ 197 hi: 0, 198 lo: binary.NativeEndian.Uint64(slice), 199 tp: tp, 200 }, true 201 case 16: 202 return UUID{ 203 hi: binary.NativeEndian.Uint64(slice[:8]), 204 lo: binary.NativeEndian.Uint64(slice[8:]), 205 tp: tp, 206 }, true 207 } 208 return UUID{}, false 209 } 210 211 func randomUUID(t uint8) UUID { 212 l := 16 213 if t == b58Hlf || t == b64Hlf { 214 l = 8 215 } 216 u := make([]byte, l) 217 _, err := rand.Read(u) 218 if err != nil { 219 return UUID{} 220 } 221 if l == 16 { // v4 222 u[6] = (u[6] & 0x0f) | 0x40 223 u[8] = (u[8] & 0x3f) | 0x80 224 } 225 uuid, _ := fromSlice(u, t) 226 return uuid 227 } 228 229 func fromHexChar(c byte) byte { 230 switch { 231 case '0' <= c && c <= '9': 232 return c - '0' 233 case 'a' <= c && c <= 'f': 234 return c - 'a' + 10 235 case 'A' <= c && c <= 'F': 236 return c - 'A' + 10 237 } 238 return 255 239 } 240 241 func encodeCanonical(u []byte) string { 242 dst := make([]byte, 36) 243 dst[8] = '-' 244 dst[13] = '-' 245 dst[18] = '-' 246 dst[23] = '-' 247 for i, x := range [16]byte{ 248 0, 2, 4, 6, 249 9, 11, 250 14, 16, 251 19, 21, 252 24, 26, 28, 30, 32, 34, 253 } { 254 c := u[i] 255 dst[x] = hexTable[c>>4] 256 dst[x+1] = hexTable[c&0x0f] 257 } 258 return string(dst) 259 } 260 261 func formatUUID(id UUID, tp uint8) string { 262 switch tp { 263 case std: 264 u := make([]byte, 16) 265 binary.NativeEndian.PutUint64(u[:8], id.hi) 266 binary.NativeEndian.PutUint64(u[8:], id.lo) 267 return encodeCanonical(u) 268 case b64: 269 u := make([]byte, 16) 270 buf := make([]byte, 22) 271 binary.NativeEndian.PutUint64(u[:8], id.hi) 272 binary.NativeEndian.PutUint64(u[8:], id.lo) 273 base64.RawURLEncoding.Encode(buf, u) 274 return string(buf) 275 case b64Hlf: 276 u := make([]byte, 8) 277 buf := make([]byte, 11) 278 binary.NativeEndian.PutUint64(u, id.lo) 279 base64.RawURLEncoding.Encode(buf, u) 280 return string(buf) 281 case b58: 282 u := make([]byte, 16) 283 binary.NativeEndian.PutUint64(u[:8], id.hi) 284 binary.NativeEndian.PutUint64(u[8:], id.lo) 285 return base58.Encode(u) 286 case b58Hlf: 287 u := make([]byte, 8) 288 binary.NativeEndian.PutUint64(u, id.lo) 289 return base58.Encode(u) 290 default: 291 return "invalid UUID" 292 } 293 } 294 295 func (id UUID) IsValid() bool { 296 return id.tp != invalid 297 } 298 299 func (id UUID) IsFull() bool { 300 return id.hi != 0 && id.tp != invalid 301 } 302 303 func (id UUID) HighDigit() uint64 { 304 return id.hi 305 } 306 307 func (id UUID) LowDigit() uint64 { 308 return id.lo 309 } 310 311 func (id UUID) Bytes() []byte { 312 if !id.IsValid() { 313 return nil 314 } 315 if id.hi == 0 { 316 u := make([]byte, 8) 317 binary.NativeEndian.PutUint64(u, id.lo) 318 return u 319 } 320 u := make([]byte, 16) 321 binary.NativeEndian.PutUint64(u[:8], id.hi) 322 binary.NativeEndian.PutUint64(u[8:], id.lo) 323 return u 324 } 325 326 func (id UUID) String() string { 327 return formatUUID(id, id.tp) 328 } 329 330 func (id UUID) StringStd() string { 331 if !id.IsFull() { 332 return "invalid UUID" 333 } 334 return formatUUID(id, std) 335 } 336 337 func (id UUID) String64() string { 338 if !id.IsFull() { 339 return "invalid UUID64" 340 } 341 return formatUUID(id, b64) 342 } 343 344 func (id UUID) String64Hlf() string { 345 return formatUUID(id, b64Hlf) 346 } 347 348 func (id UUID) String58() string { 349 if !id.IsFull() { 350 return "invalid UUID58" 351 } 352 return formatUUID(id, b58) 353 } 354 355 func (id UUID) String58Hlf() string { 356 return formatUUID(id, b58Hlf) 357 } 358 359 func (id UUID) Compare(id2 UUID) int { 360 hi1, hi2 := id.hi, id2.hi 361 if hi1 < hi2 { 362 return -1 363 } 364 if hi1 > hi2 { 365 return 1 366 } 367 lo1, lo2 := id.lo, id2.lo 368 if lo1 < lo2 { 369 return -1 370 } 371 if lo1 > lo2 { 372 return 1 373 } 374 tp1, tp2 := id.tp, id2.tp 375 if tp1 < tp2 { 376 return -1 377 } 378 if tp1 > tp2 { 379 return 1 380 } 381 return 0 382 } 383 384 func (id UUID) MarshalText() ([]byte, error) { 385 if !id.IsValid() { 386 return nil, nil 387 } 388 return []byte(id.String()), nil 389 } 390 391 func (id *UUID) UnmarshalText(b []byte) error { 392 u, err := Parse(string(b)) 393 if err != nil { 394 return err 395 } 396 *id = u 397 return nil 398 } 399 400 func (id UUID) MarshalJSON() ([]byte, error) { 401 if !id.IsValid() { 402 return nil, nil 403 } 404 return json.Marshal(id.String()) 405 } 406 407 func (id *UUID) UnmarshalJSON(data []byte) error { 408 var s string 409 err := json.Unmarshal(data, &s) 410 if err != nil { 411 return err 412 } 413 u, err := Parse(s) 414 if err != nil { 415 return err 416 } 417 *id = u 418 return nil 419 } 420 421 func (id UUID) MarshalBinary() ([]byte, error) { 422 return id.Bytes(), nil 423 } 424 425 func (id *UUID) UnmarshalBinary(data []byte) error { 426 var ( 427 u UUID 428 ok bool 429 ) 430 switch len(data) { 431 case 16: 432 u, ok = fromSlice(data, std) 433 case 8: 434 u, ok = fromSlice(data, b64Hlf) 435 } 436 if ok { 437 *id = u 438 return nil 439 } 440 return fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) 441 }