1// comment this out // + build testing
2
3// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
4// Use of this source code is governed by a MIT license found in the LICENSE file.
5
6package codec
7
8// This file contains values used by tests and benchmarks.
9// The benchmarks will test performance against other libraries
10// (encoding/json, json-iterator, bson, gob, etc).
11// Consequently, we only use values that will parse well in all engines,
12// and only leverage features that work across multiple libraries for a truer comparison.
13// For example,
14// - JSON/BSON do not like maps with keys that are not strings,
15//   so we only use maps with string keys here.
16// - _struct options are not honored by other libraries,
17//   so we don't use them in this file.
18
19import (
20	"math"
21	"strconv"
22	"strings"
23)
24
25// func init() {
26// 	rt := reflect.TypeOf((*TestStruc)(nil)).Elem()
27// 	defTypeInfos.get(rt2id(rt), rt)
28// }
29
30const numStrUi64T = 32 // use 8, prefer 32, test with 1024
31
32type wrapSliceUint64 []uint64
33type wrapSliceString []string
34type wrapUint64 uint64
35type wrapString string
36type wrapUint64Slice []wrapUint64
37type wrapStringSlice []wrapString
38
39// some other types
40
41type stringUint64T struct {
42	S string
43	U uint64
44}
45
46type AnonInTestStruc struct {
47	AS         string
48	AI64       int64
49	AI16       int16
50	AUi64      uint64
51	ASslice    []string
52	AI64slice  []int64
53	AUi64slice []uint64
54	AF64slice  []float64
55	AF32slice  []float32
56
57	// AMI32U32  map[int32]uint32
58	// AMU32F64 map[uint32]float64 // json/bson do not like it
59	AMSU16 map[string]uint16
60
61	// use these to test 0-len or nil slices/maps/arrays
62	AI64arr0    [0]int64
63	AI64slice0  []int64
64	AUi64sliceN []uint64
65	AMSU16N     map[string]uint16
66	AMSU16E     map[string]uint16
67}
68
69// testSimpleFields is a sub-set of TestStrucCommon
70type testSimpleFields struct {
71	S string
72
73	I64 int64
74	I8  int8
75
76	Ui64 uint64
77	Ui8  uint8
78
79	F64 float64
80	F32 float32
81
82	B bool
83
84	Sslice    []string
85	I16slice  []int16
86	Ui64slice []uint64
87	Ui8slice  []uint8
88	Bslice    []bool
89
90	Iptrslice []*int64
91
92	WrapSliceInt64  wrapSliceUint64
93	WrapSliceString wrapSliceString
94
95	Msi64 map[string]int64
96}
97
98type TestStrucCommon struct {
99	S string
100
101	I64 int64
102	I32 int32
103	I16 int16
104	I8  int8
105
106	I64n int64
107	I32n int32
108	I16n int16
109	I8n  int8
110
111	Ui64 uint64
112	Ui32 uint32
113	Ui16 uint16
114	Ui8  uint8
115
116	F64 float64
117	F32 float32
118
119	B  bool
120	By uint8 // byte: msgp doesn't like byte
121
122	Sslice    []string
123	I64slice  []int64
124	I16slice  []int16
125	Ui64slice []uint64
126	Ui8slice  []uint8
127	Bslice    []bool
128	Byslice   []byte
129
130	BytesSlice [][]byte
131
132	Iptrslice []*int64
133
134	WrapSliceInt64  wrapSliceUint64
135	WrapSliceString wrapSliceString
136
137	Msi64 map[string]int64
138
139	Msbytes map[string][]byte
140
141	Simplef testSimpleFields
142
143	SstrUi64T []stringUint64T
144	MstrUi64T map[string]*stringUint64T
145
146	AnonInTestStruc
147
148	NotAnon AnonInTestStruc
149
150	// R          Raw // Testing Raw must be explicitly turned on, so use standalone test
151	// Rext RawExt // Testing RawExt is tricky, so use standalone test
152
153	Nmap   map[string]bool //don't set this, so we can test for nil
154	Nslice []byte          //don't set this, so we can test for nil
155	Nint64 *int64          //don't set this, so we can test for nil
156}
157
158type TestStruc struct {
159	// _struct struct{} `json:",omitempty"` //set omitempty for every field
160
161	TestStrucCommon
162
163	Mtsptr     map[string]*TestStruc
164	Mts        map[string]TestStruc
165	Its        []*TestStruc
166	Nteststruc *TestStruc
167}
168
169func populateTestStrucCommon(ts *TestStrucCommon, n int, bench, useInterface, useStringKeyOnly bool) {
170	var i64a, i64b, i64c, i64d int64 = 64, 6464, 646464, 64646464
171
172	// if bench, do not use uint64 values > math.MaxInt64, as bson, etc cannot decode them
173
174	var a = AnonInTestStruc{
175		// There's more leeway in altering this.
176		AS:    strRpt(n, "A-String"),
177		AI64:  -64646464,
178		AI16:  1616,
179		AUi64: 64646464,
180		// (U+1D11E)G-clef character may be represented in json as "\uD834\uDD1E".
181		// single reverse solidus character may be represented in json as "\u005C".
182		// include these in ASslice below.
183		ASslice: []string{
184			strRpt(n, "Aone"),
185			strRpt(n, "Atwo"),
186			strRpt(n, "Athree"),
187			strRpt(n, "Afour.reverse_solidus.\u005c"),
188			strRpt(n, "Afive.Gclef.\U0001d11E\"ugorji\"done.")},
189		AI64slice: []int64{
190			0, 1, -1, -22, 333, -4444, 55555, -666666,
191			// msgpack ones
192			-48, -32, -24, -8, 32, 127, 192, 255,
193			// standard ones
194			0, -1, 1,
195			math.MaxInt8, math.MaxInt8 + 4, math.MaxInt8 - 4,
196			math.MaxInt16, math.MaxInt16 + 4, math.MaxInt16 - 4,
197			math.MaxInt32, math.MaxInt32 + 4, math.MaxInt32 - 4,
198			math.MaxInt64, math.MaxInt64 - 4,
199			math.MinInt8, math.MinInt8 + 4, math.MinInt8 - 4,
200			math.MinInt16, math.MinInt16 + 4, math.MinInt16 - 4,
201			math.MinInt32, math.MinInt32 + 4, math.MinInt32 - 4,
202			math.MinInt64, math.MinInt64 + 4,
203		},
204		AUi64slice: []uint64{
205			0, 1, 22, 333, 4444, 55555, 666666,
206			// standard ones
207			math.MaxUint8, math.MaxUint8 + 4, math.MaxUint8 - 4,
208			math.MaxUint16, math.MaxUint16 + 4, math.MaxUint16 - 4,
209			math.MaxUint32, math.MaxUint32 + 4, math.MaxUint32 - 4,
210		},
211		AMSU16: map[string]uint16{strRpt(n, "1"): 1, strRpt(n, "22"): 2, strRpt(n, "333"): 3, strRpt(n, "4444"): 4},
212
213		// Note: +/- inf, NaN, and other non-representable numbers should not be explicitly tested here
214
215		AF64slice: []float64{
216			11.11e-11, -11.11e+11,
217			2.222E+12, -2.222E-12,
218			-555.55E-5, 555.55E+5,
219			666.66E-6, -666.66E+6,
220			7777.7777E-7, -7777.7777E-7,
221			-8888.8888E+8, 8888.8888E+8,
222			-99999.9999E+9, 99999.9999E+9,
223			// these below are hairy enough to need strconv.ParseFloat
224			33.33E-33, -33.33E+33,
225			44.44e+44, -44.44e-44,
226			// standard ones
227			0, -1, 1,
228			// math.Inf(1), math.Inf(-1),
229			math.Pi, math.Phi, math.E,
230			math.MaxFloat64, math.SmallestNonzeroFloat64,
231		},
232		AF32slice: []float32{
233			11.11e-1, -11.11e+1,
234			2.222E+2, -2.222E-2,
235			-55.55E-5, 55.55E+5,
236			66.66E-6, -66.66E+6,
237			777.777E-7, -777.777E-7,
238			-8.88E+8, 8.88E-8,
239			-99999.9999E+9, 99999.9999E+9,
240			// these below are hairy enough to need strconv.ParseFloat
241			33.33E-33, -33.33E+33,
242			// standard ones
243			0, -1, 1,
244			// math.Float32frombits(0x7FF00000), math.Float32frombits(0xFFF00000), //+inf and -inf
245			math.MaxFloat32, math.SmallestNonzeroFloat32,
246		},
247
248		AI64slice0:  []int64{},
249		AUi64sliceN: nil,
250		AMSU16N:     nil,
251		AMSU16E:     map[string]uint16{},
252	}
253
254	if !bench {
255		a.AUi64slice = append(a.AUi64slice, math.MaxUint64, math.MaxUint64-4)
256	}
257	*ts = TestStrucCommon{
258		S: strRpt(n, `some really really cool names that are nigerian and american like "ugorji melody nwoke" - get it? `),
259
260		// set the numbers close to the limits
261		I8:   math.MaxInt8 * 2 / 3,  // 8,
262		I8n:  math.MinInt8 * 2 / 3,  // 8,
263		I16:  math.MaxInt16 * 2 / 3, // 16,
264		I16n: math.MinInt16 * 2 / 3, // 16,
265		I32:  math.MaxInt32 * 2 / 3, // 32,
266		I32n: math.MinInt32 * 2 / 3, // 32,
267		I64:  math.MaxInt64 * 2 / 3, // 64,
268		I64n: math.MinInt64 * 2 / 3, // 64,
269
270		Ui64: math.MaxUint64 * 2 / 3, // 64
271		Ui32: math.MaxUint32 * 2 / 3, // 32
272		Ui16: math.MaxUint16 * 2 / 3, // 16
273		Ui8:  math.MaxUint8 * 2 / 3,  // 8
274
275		F32: 3.402823e+38, // max representable float32 without losing precision
276		F64: 3.40281991833838838338e+53,
277
278		B:  true,
279		By: 5,
280
281		Sslice:    []string{strRpt(n, "one"), strRpt(n, "two"), strRpt(n, "three")},
282		I64slice:  []int64{1111, 2222, 3333},
283		I16slice:  []int16{44, 55, 66},
284		Ui64slice: []uint64{12121212, 34343434, 56565656},
285		Ui8slice:  []uint8{210, 211, 212},
286		Bslice:    []bool{true, false, true, false},
287		Byslice:   []byte{13, 14, 15},
288		BytesSlice: [][]byte{
289			[]byte(strRpt(n, "one")),
290			[]byte(strRpt(n, "two")),
291			[]byte(strRpt(n, "\"three\"")),
292		},
293		Msi64: map[string]int64{
294			strRpt(n, "one"):       1,
295			strRpt(n, "two"):       2,
296			strRpt(n, "\"three\""): 3,
297		},
298		Msbytes: map[string][]byte{
299			strRpt(n, "one"):       []byte(strRpt(n, "one")),
300			strRpt(n, "two"):       []byte(strRpt(n, "two")),
301			strRpt(n, "\"three\""): []byte(strRpt(n, "\"three\"")),
302		},
303		WrapSliceInt64:  []uint64{4, 16, 64, 256},
304		WrapSliceString: []string{strRpt(n, "4"), strRpt(n, "16"), strRpt(n, "64"), strRpt(n, "256")},
305
306		// R: Raw([]byte("goodbye")),
307		// Rext: RawExt{ 120, []byte("hello"), }, // TODO: don't set this - it's hard to test
308
309		// make Simplef same as top-level
310		// TODO: should this have slightly different values???
311		Simplef: testSimpleFields{
312			S: strRpt(n, `some really really cool names that are nigerian and american like "ugorji melody nwoke" - get it? `),
313
314			// set the numbers close to the limits
315			I8:  math.MaxInt8 * 2 / 3,  // 8,
316			I64: math.MaxInt64 * 2 / 3, // 64,
317
318			Ui64: math.MaxUint64 * 2 / 3, // 64
319			Ui8:  math.MaxUint8 * 2 / 3,  // 8
320
321			F32: 3.402823e+38, // max representable float32 without losing precision
322			F64: 3.40281991833838838338e+53,
323
324			B: true,
325
326			Sslice:    []string{strRpt(n, "one"), strRpt(n, "two"), strRpt(n, "three")},
327			I16slice:  []int16{44, 55, 66},
328			Ui64slice: []uint64{12121212, 34343434, 56565656},
329			Ui8slice:  []uint8{210, 211, 212},
330			Bslice:    []bool{true, false, true, false},
331
332			Msi64: map[string]int64{
333				strRpt(n, "one"):       1,
334				strRpt(n, "two"):       2,
335				strRpt(n, "\"three\""): 3,
336			},
337
338			WrapSliceInt64:  []uint64{4, 16, 64, 256},
339			WrapSliceString: []string{strRpt(n, "4"), strRpt(n, "16"), strRpt(n, "64"), strRpt(n, "256")},
340		},
341
342		SstrUi64T:       make([]stringUint64T, numStrUi64T), // {{"1", 1}, {"2", 2}, {"3", 3}, {"4", 4}},
343		MstrUi64T:       make(map[string]*stringUint64T, numStrUi64T),
344		AnonInTestStruc: a,
345		NotAnon:         a,
346	}
347
348	for i := uint64(0); i < numStrUi64T; i++ {
349		ss := strings.Repeat(strconv.FormatUint(i, 10), int(i)) // 4)
350		ts.SstrUi64T[i] = stringUint64T{S: ss, U: i}
351		ts.MstrUi64T[ss] = &ts.SstrUi64T[i]
352	}
353
354	if bench {
355		ts.Ui64 = math.MaxInt64 * 2 / 3
356		ts.Simplef.Ui64 = ts.Ui64
357	}
358
359	//For benchmarks, some things will not work.
360	if !bench {
361		//json and bson require string keys in maps
362		//ts.M = map[interface{}]interface{}{
363		//	true: "true",
364		//	int8(9): false,
365		//}
366		//gob cannot encode nil in element in array (encodeArray: nil element)
367		ts.Iptrslice = []*int64{nil, &i64a, nil, &i64b, nil, &i64c, nil, &i64d, nil}
368		// ts.Iptrslice = nil
369	}
370	if !useStringKeyOnly {
371		var _ byte = 0 // so this empty branch doesn't flag a warning
372		// ts.AnonInTestStruc.AMU32F64 = map[uint32]float64{1: 1, 2: 2, 3: 3} // Json/Bson barf
373	}
374}
375
376func newTestStruc(depth, n int, bench, useInterface, useStringKeyOnly bool) (ts *TestStruc) {
377	ts = &TestStruc{}
378	populateTestStrucCommon(&ts.TestStrucCommon, n, bench, useInterface, useStringKeyOnly)
379	if depth > 0 {
380		depth--
381		if ts.Mtsptr == nil {
382			ts.Mtsptr = make(map[string]*TestStruc)
383		}
384		if ts.Mts == nil {
385			ts.Mts = make(map[string]TestStruc)
386		}
387		ts.Mtsptr[strRpt(n, "0")] = newTestStruc(depth, n, bench, useInterface, useStringKeyOnly)
388		ts.Mts[strRpt(n, "0")] = *(ts.Mtsptr[strRpt(n, "0")])
389		ts.Its = append(ts.Its, ts.Mtsptr[strRpt(n, "0")])
390	}
391	return
392}
393
394var testStrRptMap = make(map[int]map[string]string)
395
396func strRpt(n int, s string) string {
397	if false {
398		// fmt.Printf(">>>> calling strings.Repeat on n: %d, key: %s\n", n, s)
399		return strings.Repeat(s, n)
400	}
401	m1, ok := testStrRptMap[n]
402	if !ok {
403		// fmt.Printf(">>>> making new map for n: %v\n", n)
404		m1 = make(map[string]string)
405		testStrRptMap[n] = m1
406	}
407	v1, ok := m1[s]
408	if !ok {
409		// fmt.Printf(">>>> creating new entry for key: %s\n", s)
410		v1 = strings.Repeat(s, n)
411		m1[s] = v1
412	}
413	return v1
414}
415
416// func wstrRpt(n int, s string) wrapBytes {
417// 	 return wrapBytes(bytes.Repeat([]byte(s), n))
418// }
419