1// Copyright 2017 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package spanner
16
17import (
18	"reflect"
19	"strconv"
20	"testing"
21
22	"cloud.google.com/go/civil"
23	proto3 "github.com/golang/protobuf/ptypes/struct"
24	sppb "google.golang.org/genproto/googleapis/spanner/v1"
25)
26
27func BenchmarkEncodeIntArray(b *testing.B) {
28	for _, s := range []struct {
29		name string
30		f    func(a []int) (*proto3.Value, *sppb.Type, error)
31	}{
32		{"Orig", encodeIntArrayOrig},
33		{"Func", encodeIntArrayFunc},
34		{"Reflect", encodeIntArrayReflect},
35	} {
36		b.Run(s.name, func(b *testing.B) {
37			for _, size := range []int{1, 10, 100, 1000} {
38				a := make([]int, size)
39				b.Run(strconv.Itoa(size), func(b *testing.B) {
40					for i := 0; i < b.N; i++ {
41						s.f(a)
42					}
43				})
44			}
45		})
46	}
47}
48
49func encodeIntArrayOrig(a []int) (*proto3.Value, *sppb.Type, error) {
50	vs := make([]*proto3.Value, len(a))
51	var err error
52	for i := range a {
53		vs[i], _, err = encodeValue(a[i])
54		if err != nil {
55			return nil, nil, err
56		}
57	}
58	return listProto(vs...), listType(intType()), nil
59}
60
61func encodeIntArrayFunc(a []int) (*proto3.Value, *sppb.Type, error) {
62	v, err := encodeArray(len(a), func(i int) interface{} { return a[i] })
63	if err != nil {
64		return nil, nil, err
65	}
66	return v, listType(intType()), nil
67}
68
69func encodeIntArrayReflect(a []int) (*proto3.Value, *sppb.Type, error) {
70	v, err := encodeArrayReflect(a)
71	if err != nil {
72		return nil, nil, err
73	}
74	return v, listType(intType()), nil
75}
76
77func encodeArrayReflect(a interface{}) (*proto3.Value, error) {
78	va := reflect.ValueOf(a)
79	len := va.Len()
80	vs := make([]*proto3.Value, len)
81	var err error
82	for i := 0; i < len; i++ {
83		vs[i], _, err = encodeValue(va.Index(i).Interface())
84		if err != nil {
85			return nil, err
86		}
87	}
88	return listProto(vs...), nil
89}
90
91func BenchmarkDecodeGeneric(b *testing.B) {
92	v := stringProto("test")
93	t := stringType()
94	var g GenericColumnValue
95	b.ResetTimer()
96	for i := 0; i < b.N; i++ {
97		decodeValue(v, t, &g)
98	}
99}
100
101func BenchmarkDecodeString(b *testing.B) {
102	v := stringProto("test")
103	t := stringType()
104	var s string
105	b.ResetTimer()
106	for i := 0; i < b.N; i++ {
107		decodeValue(v, t, &s)
108	}
109}
110
111func BenchmarkDecodeCustomString(b *testing.B) {
112	v := stringProto("test")
113	t := stringType()
114	type CustomString string
115	var s CustomString
116	b.ResetTimer()
117	for i := 0; i < b.N; i++ {
118		decodeValue(v, t, &s)
119	}
120}
121
122func BenchmarkDecodeArray(b *testing.B) {
123	for _, size := range []int{1, 10, 100, 1000} {
124		vals := make([]*proto3.Value, size)
125		for i := 0; i < size; i++ {
126			vals[i] = dateProto(d1)
127		}
128		lv := &proto3.ListValue{Values: vals}
129		b.Run(strconv.Itoa(size), func(b *testing.B) {
130			for _, s := range []struct {
131				name   string
132				decode func(*proto3.ListValue)
133			}{
134				{"DateDirect", decodeArrayDateDirect},
135				{"DateFunc", decodeArrayDateFunc},
136				{"DateReflect", decodeArrayDateReflect},
137				{"StringDecodeStringArray", decodeStringArrayWrap},
138				{"StringDirect", decodeArrayStringDirect},
139				{"StringFunc", decodeArrayStringFunc},
140				{"StringReflect", decodeArrayStringReflect},
141			} {
142				b.Run(s.name, func(b *testing.B) {
143					for i := 0; i < b.N; i++ {
144						s.decode(lv)
145					}
146				})
147			}
148		})
149
150	}
151}
152
153func decodeArrayDateDirect(pb *proto3.ListValue) {
154	a := make([]civil.Date, len(pb.Values))
155	t := dateType()
156	for i, v := range pb.Values {
157		if err := decodeValue(v, t, &a[i]); err != nil {
158			panic(err)
159		}
160	}
161}
162
163func decodeArrayDateFunc(pb *proto3.ListValue) {
164	a := make([]civil.Date, len(pb.Values))
165	if err := decodeArrayFunc(pb, "DATE", dateType(), func(i int) interface{} { return &a[i] }); err != nil {
166		panic(err)
167	}
168}
169
170func decodeArrayDateReflect(pb *proto3.ListValue) {
171	var a []civil.Date
172	if err := decodeArrayReflect(pb, "DATE", dateType(), &a); err != nil {
173		panic(err)
174	}
175}
176
177func decodeStringArrayWrap(pb *proto3.ListValue) {
178	if _, err := decodeStringArray(pb); err != nil {
179		panic(err)
180	}
181}
182
183func decodeArrayStringDirect(pb *proto3.ListValue) {
184	a := make([]string, len(pb.Values))
185	t := stringType()
186	for i, v := range pb.Values {
187		if err := decodeValue(v, t, &a[i]); err != nil {
188			panic(err)
189		}
190	}
191}
192
193func decodeArrayStringFunc(pb *proto3.ListValue) {
194	a := make([]string, len(pb.Values))
195	if err := decodeArrayFunc(pb, "STRING", stringType(), func(i int) interface{} { return &a[i] }); err != nil {
196		panic(err)
197	}
198}
199
200func decodeArrayStringReflect(pb *proto3.ListValue) {
201	var a []string
202	if err := decodeArrayReflect(pb, "STRING", stringType(), &a); err != nil {
203		panic(err)
204	}
205}
206
207func decodeArrayFunc(pb *proto3.ListValue, name string, typ *sppb.Type, elptr func(int) interface{}) error {
208	if pb == nil {
209		return errNilListValue(name)
210	}
211	for i, v := range pb.Values {
212		if err := decodeValue(v, typ, elptr(i)); err != nil {
213			return errDecodeArrayElement(i, v, name, err)
214		}
215	}
216	return nil
217}
218
219func decodeArrayReflect(pb *proto3.ListValue, name string, typ *sppb.Type, aptr interface{}) error {
220	if pb == nil {
221		return errNilListValue(name)
222	}
223	av := reflect.ValueOf(aptr).Elem()
224	av.Set(reflect.MakeSlice(av.Type(), len(pb.Values), len(pb.Values)))
225	for i, v := range pb.Values {
226		if err := decodeValue(v, typ, av.Index(i).Addr().Interface()); err != nil {
227			av.Set(reflect.Zero(av.Type())) // reset slice to nil
228			return errDecodeArrayElement(i, v, name, err)
229		}
230	}
231	return nil
232}
233