1package mapstructure
2
3import (
4	"encoding/json"
5	"testing"
6)
7
8type Person struct {
9	Name   string
10	Age    int
11	Emails []string
12	Extra  map[string]string
13}
14
15func Benchmark_Decode(b *testing.B) {
16	input := map[string]interface{}{
17		"name":   "Mitchell",
18		"age":    91,
19		"emails": []string{"one", "two", "three"},
20		"extra": map[string]string{
21			"twitter": "mitchellh",
22		},
23	}
24
25	var result Person
26	for i := 0; i < b.N; i++ {
27		Decode(input, &result)
28	}
29}
30
31// decodeViaJSON takes the map data and passes it through encoding/json to convert it into the
32// given Go native structure pointed to by v. v must be a pointer to a struct.
33func decodeViaJSON(data interface{}, v interface{}) error {
34	// Perform the task by simply marshalling the input into JSON,
35	// then unmarshalling it into target native Go struct.
36	b, err := json.Marshal(data)
37	if err != nil {
38		return err
39	}
40	return json.Unmarshal(b, v)
41}
42
43func Benchmark_DecodeViaJSON(b *testing.B) {
44	input := map[string]interface{}{
45		"name":   "Mitchell",
46		"age":    91,
47		"emails": []string{"one", "two", "three"},
48		"extra": map[string]string{
49			"twitter": "mitchellh",
50		},
51	}
52
53	var result Person
54	for i := 0; i < b.N; i++ {
55		decodeViaJSON(input, &result)
56	}
57}
58
59func Benchmark_JSONUnmarshal(b *testing.B) {
60	input := map[string]interface{}{
61		"name":   "Mitchell",
62		"age":    91,
63		"emails": []string{"one", "two", "three"},
64		"extra": map[string]string{
65			"twitter": "mitchellh",
66		},
67	}
68
69	inputB, err := json.Marshal(input)
70	if err != nil {
71		b.Fatal("Failed to marshal test input:", err)
72	}
73
74	var result Person
75	for i := 0; i < b.N; i++ {
76		json.Unmarshal(inputB, &result)
77	}
78}
79
80func Benchmark_DecodeBasic(b *testing.B) {
81	input := map[string]interface{}{
82		"vstring":     "foo",
83		"vint":        42,
84		"Vuint":       42,
85		"vbool":       true,
86		"Vfloat":      42.42,
87		"vsilent":     true,
88		"vdata":       42,
89		"vjsonInt":    json.Number("1234"),
90		"vjsonFloat":  json.Number("1234.5"),
91		"vjsonNumber": json.Number("1234.5"),
92	}
93
94	for i := 0; i < b.N; i++ {
95		var result Basic
96		Decode(input, &result)
97	}
98}
99
100func Benchmark_DecodeEmbedded(b *testing.B) {
101	input := map[string]interface{}{
102		"vstring": "foo",
103		"Basic": map[string]interface{}{
104			"vstring": "innerfoo",
105		},
106		"vunique": "bar",
107	}
108
109	var result Embedded
110	for i := 0; i < b.N; i++ {
111		Decode(input, &result)
112	}
113}
114
115func Benchmark_DecodeTypeConversion(b *testing.B) {
116	input := map[string]interface{}{
117		"IntToFloat":    42,
118		"IntToUint":     42,
119		"IntToBool":     1,
120		"IntToString":   42,
121		"UintToInt":     42,
122		"UintToFloat":   42,
123		"UintToBool":    42,
124		"UintToString":  42,
125		"BoolToInt":     true,
126		"BoolToUint":    true,
127		"BoolToFloat":   true,
128		"BoolToString":  true,
129		"FloatToInt":    42.42,
130		"FloatToUint":   42.42,
131		"FloatToBool":   42.42,
132		"FloatToString": 42.42,
133		"StringToInt":   "42",
134		"StringToUint":  "42",
135		"StringToBool":  "1",
136		"StringToFloat": "42.42",
137		"SliceToMap":    []interface{}{},
138		"MapToSlice":    map[string]interface{}{},
139	}
140
141	var resultStrict TypeConversionResult
142	for i := 0; i < b.N; i++ {
143		Decode(input, &resultStrict)
144	}
145}
146
147func Benchmark_DecodeMap(b *testing.B) {
148	input := map[string]interface{}{
149		"vfoo": "foo",
150		"vother": map[interface{}]interface{}{
151			"foo": "foo",
152			"bar": "bar",
153		},
154	}
155
156	var result Map
157	for i := 0; i < b.N; i++ {
158		Decode(input, &result)
159	}
160}
161
162func Benchmark_DecodeMapOfStruct(b *testing.B) {
163	input := map[string]interface{}{
164		"value": map[string]interface{}{
165			"foo": map[string]string{"vstring": "one"},
166			"bar": map[string]string{"vstring": "two"},
167		},
168	}
169
170	var result MapOfStruct
171	for i := 0; i < b.N; i++ {
172		Decode(input, &result)
173	}
174}
175
176func Benchmark_DecodeSlice(b *testing.B) {
177	input := map[string]interface{}{
178		"vfoo": "foo",
179		"vbar": []string{"foo", "bar", "baz"},
180	}
181
182	var result Slice
183	for i := 0; i < b.N; i++ {
184		Decode(input, &result)
185	}
186}
187
188func Benchmark_DecodeSliceOfStruct(b *testing.B) {
189	input := map[string]interface{}{
190		"value": []map[string]interface{}{
191			{"vstring": "one"},
192			{"vstring": "two"},
193		},
194	}
195
196	var result SliceOfStruct
197	for i := 0; i < b.N; i++ {
198		Decode(input, &result)
199	}
200}
201
202func Benchmark_DecodeWeaklyTypedInput(b *testing.B) {
203	// This input can come from anywhere, but typically comes from
204	// something like decoding JSON, generated by a weakly typed language
205	// such as PHP.
206	input := map[string]interface{}{
207		"name":   123,                      // number => string
208		"age":    "42",                     // string => number
209		"emails": map[string]interface{}{}, // empty map => empty array
210	}
211
212	var result Person
213	config := &DecoderConfig{
214		WeaklyTypedInput: true,
215		Result:           &result,
216	}
217
218	decoder, err := NewDecoder(config)
219	if err != nil {
220		panic(err)
221	}
222
223	for i := 0; i < b.N; i++ {
224		decoder.Decode(input)
225	}
226}
227
228func Benchmark_DecodeMetadata(b *testing.B) {
229	input := map[string]interface{}{
230		"name":  "Mitchell",
231		"age":   91,
232		"email": "foo@bar.com",
233	}
234
235	var md Metadata
236	var result Person
237	config := &DecoderConfig{
238		Metadata: &md,
239		Result:   &result,
240	}
241
242	decoder, err := NewDecoder(config)
243	if err != nil {
244		panic(err)
245	}
246
247	for i := 0; i < b.N; i++ {
248		decoder.Decode(input)
249	}
250}
251
252func Benchmark_DecodeMetadataEmbedded(b *testing.B) {
253	input := map[string]interface{}{
254		"vstring": "foo",
255		"vunique": "bar",
256	}
257
258	var md Metadata
259	var result EmbeddedSquash
260	config := &DecoderConfig{
261		Metadata: &md,
262		Result:   &result,
263	}
264
265	decoder, err := NewDecoder(config)
266	if err != nil {
267		b.Fatalf("err: %s", err)
268	}
269
270	for i := 0; i < b.N; i++ {
271		decoder.Decode(input)
272	}
273}
274
275func Benchmark_DecodeTagged(b *testing.B) {
276	input := map[string]interface{}{
277		"foo": "bar",
278		"bar": "value",
279	}
280
281	var result Tagged
282	for i := 0; i < b.N; i++ {
283		Decode(input, &result)
284	}
285}
286