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  }