diff --git a/pkg/util/sys.go b/pkg/util/sys.go index 836189594..bc8fde207 100644 --- a/pkg/util/sys.go +++ b/pkg/util/sys.go @@ -16,8 +16,11 @@ package util import ( "net" "os" + "unsafe" ) +const INT_SIZE int = int(unsafe.Sizeof(0)) + func GetLocalHostname() string { hostName, _ := os.Hostname() return hostName @@ -38,3 +41,13 @@ func GetLocalIP() string { } return "" } + +func IsBigEndian() bool { + return !IsLittleEndian() +} + +func IsLittleEndian() bool { + i := 0x1 + bs := (*[INT_SIZE]byte)(unsafe.Pointer(&i)) + return bs[0] == 0 +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 61368b01a..42d0b4f01 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -253,3 +253,23 @@ func GetStartTimeFromContext(ctx context.Context) time.Time { } return v } + +func BytesToInt32(bs []byte) (in int32) { + l := len(bs) + if l > 4 || l == 0 { + return 0 + } + + pi := (*[4]byte)(unsafe.Pointer(&in)) + if IsBigEndian() { + for i := range bs { + pi[i] = bs[l-i-1] + } + return + } + + for i := range bs { + pi[3-i] = bs[l-i-1] + } + return +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go new file mode 100644 index 000000000..f196959bf --- /dev/null +++ b/pkg/util/util_test.go @@ -0,0 +1,50 @@ +//Copyright 2017 Huawei Technologies Co., Ltd +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +package util + +import ( + "testing" +) + +func TestBytesToInt32(t *testing.T) { + bs := []byte{0, 0, 0, 1} + i := BytesToInt32(bs) + if i != 1 { + t.FailNow() + } + + bs = []byte{1, 0, 0, 0} + i = BytesToInt32(bs) + if i != 1<<(3*8) { + t.FailNow() + } + + bs = []byte{0, 0, 0, 0, 1} + i = BytesToInt32(bs) + if i != 0 { + t.FailNow() + } + + bs = []byte{1} + i = BytesToInt32(bs) + if i != 1 { + t.FailNow() + } + + bs = []byte{1, 0} + i = BytesToInt32(bs) + if i != 1<<8 { + t.FailNow() + } +} diff --git a/server/service/util/versionrule.go b/server/service/util/versionrule.go index 990a1cb29..57886bd92 100644 --- a/server/service/util/versionrule.go +++ b/server/service/util/versionrule.go @@ -14,7 +14,6 @@ package util import ( - "bytes" "github.com/ServiceComb/service-center/pkg/util" "github.com/coreos/etcd/mvcc/mvccpb" "sort" @@ -27,13 +26,14 @@ type VersionRule func(sorted []string, kvs map[string]string, start, end string) func (vr VersionRule) Match(kvs []*mvccpb.KeyValue, ops ...string) []string { sorter := &serviceKeySorter{ sortArr: make([]string, len(kvs)), - kvs: make(map[string]string), + kvs: make(map[string]string, len(kvs)), cmp: Larger, } for i, kv := range kvs { key := util.BytesToStringWithNoCopy(kv.Key) - sorter.sortArr[i] = key[strings.LastIndex(key, "/")+1:] - sorter.kvs[sorter.sortArr[i]] = util.BytesToStringWithNoCopy(kv.Value) + ver := key[strings.LastIndex(key, "/")+1:] + sorter.sortArr[i] = ver + sorter.kvs[ver] = util.BytesToStringWithNoCopy(kv.Value) } sort.Sort(sorter) @@ -66,23 +66,27 @@ func (sks *serviceKeySorter) Less(i, j int) bool { return sks.cmp(sks.sortArr[i], sks.sortArr[j]) } -func stringToBytesVersion(versionStr string) []byte { - verSet := strings.Split(versionStr, ".") - verBytes := make([]byte, len(verSet)) - for i, v := range verSet { - integer, err := strconv.ParseInt(v, 10, 8) - if err != nil { - return []byte{} +func versionToInt(versionStr string) (ret int32) { + verBytes := [4]byte{} + idx := 0 + for i := 0; i < 4 && idx < len(versionStr); i++ { + f := strings.IndexRune(versionStr[idx:], '.') + if f < 0 { + f = len(versionStr) - idx + } + integer, err := strconv.ParseInt(versionStr[idx:idx+f], 10, 8) + if err != nil || integer < 0 { + return 0 } verBytes[i] = byte(integer) + idx += f + 1 } - return verBytes[:] + ret = util.BytesToInt32(verBytes[:]) + return } func Larger(start, end string) bool { - startVerBytes := stringToBytesVersion(start) - endVerBytes := stringToBytesVersion(end) - return bytes.Compare(startVerBytes, endVerBytes) > 0 + return versionToInt(start) > versionToInt(end) } func Latest(sorted []string, kvs map[string]string, start, end string) []string { diff --git a/server/service/util/versionrule_test.go b/server/service/util/versionrule_test.go index 015f6686e..78c89158d 100644 --- a/server/service/util/versionrule_test.go +++ b/server/service/util/versionrule_test.go @@ -18,7 +18,10 @@ import ( "github.com/coreos/etcd/mvcc/mvccpb" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "math" + "math/rand" "sort" + "testing" ) var _ = Describe("Version Rule sorter", func() { @@ -220,3 +223,22 @@ var _ = Describe("Version Rule sorter", func() { }) }) }) + +func BenchmarkVersionRule_Match(b *testing.B) { + var kvs = []*mvccpb.KeyValue{} + for i := 1; i <= b.N; i++ { + x := rand.Intn(math.MaxInt8) + y := rand.Intn(math.MaxInt8) + z := rand.Intn(math.MaxInt8) + kvs = append(kvs, &mvccpb.KeyValue{ + Key: []byte(fmt.Sprintf("/service/ver/%d.%d.%d", x, y, z)), + Value: []byte(fmt.Sprintf("%d.%d.%d", x, y, z)), + }) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + VersionRule(Latest).Match(kvs) + } + b.ReportAllocs() +}