diff --git a/.example/util/guid/guid_length.go b/.example/util/guid/guid_length.go new file mode 100644 index 000000000..1e5c642d6 --- /dev/null +++ b/.example/util/guid/guid_length.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "math" + "strconv" +) + +func main() { + fmt.Println(strconv.FormatUint(4589634556, 36)) + fmt.Println(strconv.FormatUint(math.MaxUint64-2, 36)) + fmt.Println(strconv.FormatUint(math.MaxUint32-1, 36)) + fmt.Println(strconv.FormatUint(math.MaxUint32, 36)) + fmt.Println(strconv.FormatUint(2000000, 36)) +} diff --git a/.example/util/guid/guid_unique.go b/.example/util/guid/guid_unique.go index fc26b90be..4009e305b 100644 --- a/.example/util/guid/guid_unique.go +++ b/.example/util/guid/guid_unique.go @@ -7,17 +7,17 @@ import ( func main() { for i := 0; i < 10; i++ { - s := guid.New([]byte("123")) + s := guid.S([]byte("123")) fmt.Println(s, len(s)) } fmt.Println() for i := 0; i < 10; i++ { - s := guid.New([]byte("123"), []byte("456")) + s := guid.S([]byte("123"), []byte("456")) fmt.Println(s, len(s)) } fmt.Println() for i := 0; i < 10; i++ { - s := guid.New([]byte("123"), []byte("456"), []byte("789")) + s := guid.S([]byte("123"), []byte("456"), []byte("789")) fmt.Println(s, len(s)) } } diff --git a/os/gsession/gsession_unit_test.go b/os/gsession/gsession_unit_test.go index 57e7c9205..a15499fba 100644 --- a/os/gsession/gsession_unit_test.go +++ b/os/gsession/gsession_unit_test.go @@ -17,6 +17,6 @@ func Test_NewSessionId(t *testing.T) { id1 := NewSessionId() id2 := NewSessionId() t.AssertNE(id1, id2) - t.Assert(len(id1), 18) + t.Assert(len(id1), 36) }) } diff --git a/util/guid/guid.go b/util/guid/guid.go index 76db77b56..64959a4bd 100644 --- a/util/guid/guid.go +++ b/util/guid/guid.go @@ -25,109 +25,101 @@ import ( var ( sequence gtype.Uint32 // Sequence for unique purpose of current process. - macAddrStr string // MAC addresses hash result in 7 bytes. - processIdStr string // Process id in 4 bytes. + sequenceMax = uint32(1000000) // Sequence max. randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // 36 + macAddrStr = "0000000" // MAC addresses hash result in 7 bytes. + processIdStr = "0000" // Process id in 4 bytes. ) +// init initializes several fixed local variable. func init() { // MAC addresses hash result in 7 bytes. macs, _ := gipv4.MacArray() if len(macs) > 0 { - var b []byte + var macAddrBytes []byte for _, mac := range macs { - b = append(b, []byte(mac)...) - } - macAddrStr = strconv.FormatUint(uint64(ghash.DJBHash(b)), 36) - if n := 7 - len(macAddrStr); n > 0 { - for i := 0; i < n; i++ { - macAddrStr = "0" + macAddrStr - } - } - if n := len(macAddrStr) - 7; n > 0 { - macAddrStr = macAddrStr[n:] + macAddrBytes = append(macAddrBytes, []byte(mac)...) } + b := []byte{'0', '0', '0', '0', '0', '0', '0'} + s := strconv.FormatUint(uint64(ghash.DJBHash(macAddrBytes)), 36) + copy(b[7-len(s):], s) + macAddrStr = string(b) } // Process id in 4 bytes. - processIdStr = strconv.FormatInt(int64(os.Getpid()), 36) - if n := 4 - len(processIdStr); n > 0 { - for i := 0; i < n; i++ { - processIdStr = "0" + processIdStr - } - } - if n := len(processIdStr) - 4; n > 0 { - processIdStr = processIdStr[n:] + { + b := []byte{'0', '0', '0', '0'} + s := strconv.FormatInt(int64(os.Getpid()), 36) + copy(b[4-len(s):], s) + processIdStr = string(b) } } -// S creates and returns an unique string in 36 bytes that meets most -// common usages without strict UUID algorithm. -// The returned string is composed with: -// MAC(7) + PID(4) + Sequence(7) + TimestampNano(12) + RandomString(6) -func S() string { - b := make([]byte, 36) - copy(b, macAddrStr) - copy(b[7:], processIdStr) - copy(b[11:], getSequence()) - copy(b[18:], strconv.FormatInt(time.Now().UnixNano(), 36)) - copy(b[30:], randomStr(randomStrBase, 6)) - return gconv.UnsafeBytesToStr(b) -} - -// New creates and returns an unique string in 36 bytes using custom data. -// The returned string is composed with: -// Data...(7 - 21) + TimestampNano(12) + RandomString(3 - 17) +// S creates and returns an unique string in 36 bytes that meets most common usages +// without strict UUID algorithm. It returns an unique string using default unique +// algorithm if no is given. // -// The specified can be count of 1 to 3. No matter how long each of the +// The specified can be no more than 3 count. No matter how long each of the // size is, each of them will be hashed into 7 bytes as part of the result. If given // count is less than 3, the leftover size of the result bytes will be token by // random string. // -// Note that the can not be empty. -func New(data ...[]byte) string { +// The returned string is composed with: +// 1. Default: MAC(7) + PID(4) + Sequence(4) + TimestampNano(12) + RandomString(9) +// 2. CustomData: Data...(7 - 21) + TimestampNano(12) + RandomString(3 - 17) +// +// Note that the returned length is fixed to 36 bytes for performance purpose. +func S(data ...[]byte) string { + var ( + b = make([]byte, 36) + nanoStr = strconv.FormatInt(time.Now().UnixNano(), 36) + ) if len(data) == 0 { - panic("data cannot be empty") - } - if len(data) > 3 { + copy(b, macAddrStr) + copy(b[7:], processIdStr) + copy(b[11:], getSequence()) + copy(b[15:], nanoStr) + copy(b[27:], getRandomStr(9)) + } else if len(data) <= 3 { + n := 0 + for i, v := range data { + copy(b[i*7:], getDataHashStr(v)) + n += 7 + } + copy(b[n:], nanoStr) + copy(b[n+12:], getRandomStr(36-n-12)) + } else { panic("data count too long, no more than 3") } - b := make([]byte, 36) - n := 0 - for i, v := range data { - copy(b[i*7:], getDataHashStr(v)) - n += 7 - } - copy(b[n:], strconv.FormatInt(time.Now().UnixNano(), 36)) - copy(b[n+12:], randomStr(randomStrBase, 36-n-12)) return gconv.UnsafeBytesToStr(b) } -// getSequence increases and returns the sequence string in 7 bytes. +// getSequence increases and returns the sequence string in 4 bytes. +// The sequence is less than 1000000. func getSequence() string { - b := make([]byte, 7) - copy(b, []byte{'0', '0', '0', '0', '0', '0', '0'}) - s := strconv.FormatUint(uint64(sequence.Add(1)), 36) - copy(b[7-len(s):], s) + b := []byte{'0', '0', '0', '0'} + s := strconv.FormatUint(uint64(sequence.Add(1)%sequenceMax), 36) + copy(b[4-len(s):], s) return gconv.UnsafeBytesToStr(b) } -// Str randomly picks and returns count of chars from given string . -// It also supports unicode string like Chinese/Russian/Japanese, etc. -func randomStr(s string, n int) string { +// getRandomStr randomly picks and returns count of chars from randomStrBase. +func getRandomStr(n int) string { + if n <= 0 { + return "" + } var ( b = make([]byte, n) numberBytes = grand.B(n) ) for i := range b { - b[i] = s[numberBytes[i]%36] + b[i] = randomStrBase[numberBytes[i]%36] } return gconv.UnsafeBytesToStr(b) } // getDataHashStr creates and returns hash bytes in 7 bytes with given data bytes. func getDataHashStr(data []byte) string { - b := make([]byte, 7) - copy(b, []byte{'0', '0', '0', '0', '0', '0', '0'}) + b := []byte{'0', '0', '0', '0', '0', '0', '0'} s := strconv.FormatUint(uint64(ghash.DJBHash(data)), 36) copy(b[7-len(s):], s) return gconv.UnsafeBytesToStr(b) diff --git a/util/guid/guid_z_bench_test.go b/util/guid/guid_z_bench_test.go index 4a70f5cee..5384bfdee 100644 --- a/util/guid/guid_z_bench_test.go +++ b/util/guid/guid_z_bench_test.go @@ -19,20 +19,20 @@ func Benchmark_S(b *testing.B) { } } -func Benchmark_New1(b *testing.B) { +func Benchmark_S_Data_1(b *testing.B) { for i := 0; i < b.N; i++ { - guid.New([]byte("123")) + guid.S([]byte("123")) } } -func Benchmark_New2(b *testing.B) { +func Benchmark_S_Data_2(b *testing.B) { for i := 0; i < b.N; i++ { - guid.New([]byte("123"), []byte("456")) + guid.S([]byte("123"), []byte("456")) } } -func Benchmark_New3(b *testing.B) { +func Benchmark_S_Data_3(b *testing.B) { for i := 0; i < b.N; i++ { - guid.New([]byte("123"), []byte("456"), []byte("789")) + guid.S([]byte("123"), []byte("456"), []byte("789")) } } diff --git a/util/guid/guid_z_unit_test.go b/util/guid/guid_z_unit_test.go index a9669d8c6..0e8716385 100644 --- a/util/guid/guid_z_unit_test.go +++ b/util/guid/guid_z_unit_test.go @@ -27,8 +27,8 @@ func Test_S(t *testing.T) { }) } -func Test_New(t *testing.T) { +func Test_S_Data(t *testing.T) { gtest.C(t, func(t *gtest.T) { - t.Assert(len(guid.New([]byte("123"))), 36) + t.Assert(len(guid.S([]byte("123"))), 36) }) }