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 BenchmarkDecodeArray(b *testing.B) {
102	for _, size := range []int{1, 10, 100, 1000} {
103		vals := make([]*proto3.Value, size)
104		for i := 0; i < size; i++ {
105			vals[i] = dateProto(d1)
106		}
107		lv := &proto3.ListValue{Values: vals}
108		b.Run(strconv.Itoa(size), func(b *testing.B) {
109			for _, s := range []struct {
110				name   string
111				decode func(*proto3.ListValue)
112			}{
113				{"DateDirect", decodeArrayDateDirect},
114				{"DateFunc", decodeArrayDateFunc},
115				{"DateReflect", decodeArrayDateReflect},
116				{"StringDecodeStringArray", decodeStringArrayWrap},
117				{"StringDirect", decodeArrayStringDirect},
118				{"StringFunc", decodeArrayStringFunc},
119				{"StringReflect", decodeArrayStringReflect},
120			} {
121				b.Run(s.name, func(b *testing.B) {
122					for i := 0; i < b.N; i++ {
123						s.decode(lv)
124					}
125				})
126			}
127		})
128
129	}
130}
131
132func decodeArrayDateDirect(pb *proto3.ListValue) {
133	a := make([]civil.Date, len(pb.Values))
134	t := dateType()
135	for i, v := range pb.Values {
136		if err := decodeValue(v, t, &a[i]); err != nil {
137			panic(err)
138		}
139	}
140}
141
142func decodeArrayDateFunc(pb *proto3.ListValue) {
143	a := make([]civil.Date, len(pb.Values))
144	if err := decodeArrayFunc(pb, "DATE", dateType(), func(i int) interface{} { return &a[i] }); err != nil {
145		panic(err)
146	}
147}
148
149func decodeArrayDateReflect(pb *proto3.ListValue) {
150	var a []civil.Date
151	if err := decodeArrayReflect(pb, "DATE", dateType(), &a); err != nil {
152		panic(err)
153	}
154}
155
156func decodeStringArrayWrap(pb *proto3.ListValue) {
157	if _, err := decodeStringArray(pb); err != nil {
158		panic(err)
159	}
160}
161
162func decodeArrayStringDirect(pb *proto3.ListValue) {
163	a := make([]string, len(pb.Values))
164	t := stringType()
165	for i, v := range pb.Values {
166		if err := decodeValue(v, t, &a[i]); err != nil {
167			panic(err)
168		}
169	}
170}
171
172func decodeArrayStringFunc(pb *proto3.ListValue) {
173	a := make([]string, len(pb.Values))
174	if err := decodeArrayFunc(pb, "STRING", stringType(), func(i int) interface{} { return &a[i] }); err != nil {
175		panic(err)
176	}
177}
178
179func decodeArrayStringReflect(pb *proto3.ListValue) {
180	var a []string
181	if err := decodeArrayReflect(pb, "STRING", stringType(), &a); err != nil {
182		panic(err)
183	}
184}
185
186func decodeArrayFunc(pb *proto3.ListValue, name string, typ *sppb.Type, elptr func(int) interface{}) error {
187	if pb == nil {
188		return errNilListValue(name)
189	}
190	for i, v := range pb.Values {
191		if err := decodeValue(v, typ, elptr(i)); err != nil {
192			return errDecodeArrayElement(i, v, name, err)
193		}
194	}
195	return nil
196}
197
198func decodeArrayReflect(pb *proto3.ListValue, name string, typ *sppb.Type, aptr interface{}) error {
199	if pb == nil {
200		return errNilListValue(name)
201	}
202	av := reflect.ValueOf(aptr).Elem()
203	av.Set(reflect.MakeSlice(av.Type(), len(pb.Values), len(pb.Values)))
204	for i, v := range pb.Values {
205		if err := decodeValue(v, typ, av.Index(i).Addr().Interface()); err != nil {
206			av.Set(reflect.Zero(av.Type())) // reset slice to nil
207			return errDecodeArrayElement(i, v, name, err)
208		}
209	}
210	return nil
211}
212