2018-06-19 15:30:26 +02:00
|
|
|
// Copyright 2017 Google LLC
|
|
|
|
//
|
|
|
|
// 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 spanner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"cloud.google.com/go/civil"
|
|
|
|
proto3 "github.com/golang/protobuf/ptypes/struct"
|
|
|
|
sppb "google.golang.org/genproto/googleapis/spanner/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
func BenchmarkEncodeIntArray(b *testing.B) {
|
|
|
|
for _, s := range []struct {
|
|
|
|
name string
|
|
|
|
f func(a []int) (*proto3.Value, *sppb.Type, error)
|
|
|
|
}{
|
|
|
|
{"Orig", encodeIntArrayOrig},
|
|
|
|
{"Func", encodeIntArrayFunc},
|
|
|
|
{"Reflect", encodeIntArrayReflect},
|
|
|
|
} {
|
|
|
|
b.Run(s.name, func(b *testing.B) {
|
|
|
|
for _, size := range []int{1, 10, 100, 1000} {
|
|
|
|
a := make([]int, size)
|
|
|
|
b.Run(strconv.Itoa(size), func(b *testing.B) {
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
s.f(a)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodeIntArrayOrig(a []int) (*proto3.Value, *sppb.Type, error) {
|
|
|
|
vs := make([]*proto3.Value, len(a))
|
|
|
|
var err error
|
|
|
|
for i := range a {
|
|
|
|
vs[i], _, err = encodeValue(a[i])
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return listProto(vs...), listType(intType()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodeIntArrayFunc(a []int) (*proto3.Value, *sppb.Type, error) {
|
|
|
|
v, err := encodeArray(len(a), func(i int) interface{} { return a[i] })
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return v, listType(intType()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodeIntArrayReflect(a []int) (*proto3.Value, *sppb.Type, error) {
|
|
|
|
v, err := encodeArrayReflect(a)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return v, listType(intType()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodeArrayReflect(a interface{}) (*proto3.Value, error) {
|
|
|
|
va := reflect.ValueOf(a)
|
|
|
|
len := va.Len()
|
|
|
|
vs := make([]*proto3.Value, len)
|
|
|
|
var err error
|
|
|
|
for i := 0; i < len; i++ {
|
|
|
|
vs[i], _, err = encodeValue(va.Index(i).Interface())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return listProto(vs...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkDecodeGeneric(b *testing.B) {
|
|
|
|
v := stringProto("test")
|
|
|
|
t := stringType()
|
|
|
|
var g GenericColumnValue
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
decodeValue(v, t, &g)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkDecodeArray(b *testing.B) {
|
|
|
|
for _, size := range []int{1, 10, 100, 1000} {
|
|
|
|
vals := make([]*proto3.Value, size)
|
|
|
|
for i := 0; i < size; i++ {
|
|
|
|
vals[i] = dateProto(d1)
|
|
|
|
}
|
|
|
|
lv := &proto3.ListValue{Values: vals}
|
|
|
|
b.Run(strconv.Itoa(size), func(b *testing.B) {
|
|
|
|
for _, s := range []struct {
|
|
|
|
name string
|
|
|
|
decode func(*proto3.ListValue)
|
|
|
|
}{
|
2019-03-17 20:19:56 +01:00
|
|
|
{"DateDirect", decodeArrayDateDirect},
|
|
|
|
{"DateFunc", decodeArrayDateFunc},
|
|
|
|
{"DateReflect", decodeArrayDateReflect},
|
2018-06-19 15:30:26 +02:00
|
|
|
{"StringDecodeStringArray", decodeStringArrayWrap},
|
2019-03-17 20:19:56 +01:00
|
|
|
{"StringDirect", decodeArrayStringDirect},
|
|
|
|
{"StringFunc", decodeArrayStringFunc},
|
|
|
|
{"StringReflect", decodeArrayStringReflect},
|
2018-06-19 15:30:26 +02:00
|
|
|
} {
|
|
|
|
b.Run(s.name, func(b *testing.B) {
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
s.decode(lv)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayDateDirect(pb *proto3.ListValue) {
|
2018-06-19 15:30:26 +02:00
|
|
|
a := make([]civil.Date, len(pb.Values))
|
|
|
|
t := dateType()
|
|
|
|
for i, v := range pb.Values {
|
|
|
|
if err := decodeValue(v, t, &a[i]); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayDateFunc(pb *proto3.ListValue) {
|
2018-06-19 15:30:26 +02:00
|
|
|
a := make([]civil.Date, len(pb.Values))
|
2019-03-17 20:19:56 +01:00
|
|
|
if err := decodeArrayFunc(pb, "DATE", dateType(), func(i int) interface{} { return &a[i] }); err != nil {
|
2018-06-19 15:30:26 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayDateReflect(pb *proto3.ListValue) {
|
2018-06-19 15:30:26 +02:00
|
|
|
var a []civil.Date
|
2019-03-17 20:19:56 +01:00
|
|
|
if err := decodeArrayReflect(pb, "DATE", dateType(), &a); err != nil {
|
2018-06-19 15:30:26 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeStringArrayWrap(pb *proto3.ListValue) {
|
|
|
|
if _, err := decodeStringArray(pb); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayStringDirect(pb *proto3.ListValue) {
|
2018-06-19 15:30:26 +02:00
|
|
|
a := make([]string, len(pb.Values))
|
|
|
|
t := stringType()
|
|
|
|
for i, v := range pb.Values {
|
|
|
|
if err := decodeValue(v, t, &a[i]); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayStringFunc(pb *proto3.ListValue) {
|
2018-06-19 15:30:26 +02:00
|
|
|
a := make([]string, len(pb.Values))
|
2019-03-17 20:19:56 +01:00
|
|
|
if err := decodeArrayFunc(pb, "STRING", stringType(), func(i int) interface{} { return &a[i] }); err != nil {
|
2018-06-19 15:30:26 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayStringReflect(pb *proto3.ListValue) {
|
2018-06-19 15:30:26 +02:00
|
|
|
var a []string
|
2019-03-17 20:19:56 +01:00
|
|
|
if err := decodeArrayReflect(pb, "STRING", stringType(), &a); err != nil {
|
2018-06-19 15:30:26 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayFunc(pb *proto3.ListValue, name string, typ *sppb.Type, elptr func(int) interface{}) error {
|
2018-06-19 15:30:26 +02:00
|
|
|
if pb == nil {
|
|
|
|
return errNilListValue(name)
|
|
|
|
}
|
|
|
|
for i, v := range pb.Values {
|
|
|
|
if err := decodeValue(v, typ, elptr(i)); err != nil {
|
|
|
|
return errDecodeArrayElement(i, v, name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
func decodeArrayReflect(pb *proto3.ListValue, name string, typ *sppb.Type, aptr interface{}) error {
|
2018-06-19 15:30:26 +02:00
|
|
|
if pb == nil {
|
|
|
|
return errNilListValue(name)
|
|
|
|
}
|
|
|
|
av := reflect.ValueOf(aptr).Elem()
|
|
|
|
av.Set(reflect.MakeSlice(av.Type(), len(pb.Values), len(pb.Values)))
|
|
|
|
for i, v := range pb.Values {
|
|
|
|
if err := decodeValue(v, typ, av.Index(i).Addr().Interface()); err != nil {
|
|
|
|
av.Set(reflect.Zero(av.Type())) // reset slice to nil
|
|
|
|
return errDecodeArrayElement(i, v, name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|