1
16
17 package base64x
18
19 import (
20 `crypto/rand`
21 `encoding/base64`
22 `io`
23 `reflect`
24 `strings`
25 `testing`
26 `unsafe`
27 )
28
29 type TestPair struct {
30 decoded string
31 encoded string
32 }
33
34 type EncodingTest struct {
35 enc Encoding
36 conv func(string) string
37 }
38
39 var pairs = []TestPair{
40
41 {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"},
42 {"\x14\xfb\x9c\x03\xd9", "FPucA9k="},
43 {"\x14\xfb\x9c\x03", "FPucAw=="},
44
45
46 {"", ""},
47 {"f", "Zg=="},
48 {"fo", "Zm8="},
49 {"foo", "Zm9v"},
50 {"foob", "Zm9vYg=="},
51 {"fooba", "Zm9vYmE="},
52 {"foobar", "Zm9vYmFy"},
53
54
55 {"sure.", "c3VyZS4="},
56 {"sure", "c3VyZQ=="},
57 {"sur", "c3Vy"},
58 {"su", "c3U="},
59 {"leasure.", "bGVhc3VyZS4="},
60 {"easure.", "ZWFzdXJlLg=="},
61 {"asure.", "YXN1cmUu"},
62 {"sure.", "c3VyZS4="},
63
64
65 {
66 "Twas brillig, and the slithy toves",
67 "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==",
68 }, {
69 "\x9dyH\xd2Y\x9e^e\x9e\xb1\x9a\x9a\x12\xfe\x8a\x07\xc7\x07\xcc\xe8l\x81" +
70 "\xf2\xd9\xe3\x89\xb5\x98\xee\xbd\x8etQ`2>\\t:_\xd7w\xe6\xb5\x96\xc7\xff\x9c",
71 "nXlI0lmeXmWesZqaEv6KB8cHzOhsgfLZ44m1mO69jnRRYDI+XHQ6X9d35rWWx/+c",
72 },
73 }
74
75 var crlf_pairs = []TestPair{
76
77 {"\x14\xfb\x9c\x03\xd9\x7e", "FPuc\r\nA9l+"},
78 {"\x14\xfb\x9c\x03\xd9", "FP\r\r\r\rucA9k="},
79 {"\x14\xfb\x9c\x03", "\r\nFPucAw=\r=\n"},
80
81
82 {"", "\r"},
83 {"f", "Zg\r\n=="},
84 {"fo", "Zm\r\n8="},
85 {"fooba", "Zm\r\n9vY\r\nmE="},
86
87
88 {"su", "c3U\r="},
89 {"leasure.", "bGVhc3VyZ\nS4="},
90 {"easure.", "ZW\r\nFzdXJlLg=\r=\r\n"},
91 {"asure.", "YXN1cmUu"},
92 {"sure.", "c3VyZ\r\nS4="},
93
94
95 {
96 "Twas brillig, and the slithy toves",
97 "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw\r\n==\r\n",
98 }, {
99 "\x9dyH\xd2Y\x9e^e\x9e\xb1\x9a\x9a\x12\xfe\x8a\x07\xc7\x07\xcc\xe8l\x81" +
100 "\xf2\xd9\xe3\x89\xb5\x98\xee\xbd\x8etQ`2>\\t:_\xd7w\xe6\xb5\x96\xc7\xff\x9c",
101 "nXlI0lmeXmWesZqaEv6KB8cHzOhsg\r\nfLZ44m1mO69jnRRYDI+XH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nQ6X9d35rWWx/\r\n+c",
102 },
103 }
104
105 var json_pairs = []TestPair{
106
107 {"\x14\xfb\x9c\x03\xd9\x7e", `FPu\rcA9l+\n`},
108 {"\x14\xfb\x9c\x03\xd9\x7e", `FPuc\u00419l+`},
109 {"\x14\xfb\x9c\x03\xd9", `FPucA9k\u003d`},
110 {"\x14\xfb\x9c\x03\xd9", `FPucA\u0039k\u003d`},
111 {"\x14\xfb\x9c\x03", `FPucAw\u003d\u003d`},
112
113
114 {"", ""},
115 {"f", "Zg=="},
116 {"fo", "Zm8="},
117 {"foo", "Zm9v"},
118 {"foob", "Zm9vYg=="},
119 {"fooba", "Zm9vYmE="},
120 {"foobar", "Zm9vYmFy"},
121
122
123 {"sure.", "c3VyZS4="},
124 {"sure", "c3VyZQ=="},
125 {"sur", "c3Vy"},
126 {"su", "c3U="},
127 {"leasure.", "bGVhc3VyZS4="},
128 {"easure.", "ZWFzdXJlLg=="},
129 {"asure.", "YXN1cmUu"},
130 {"sure.", "c3VyZS4="},
131
132
133 {
134 "Twas brillig, and the slithy toves",
135 "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==",
136 }, {
137 "\x9dyH\xd2Y\x9e^e\x9e\xb1\x9a\x9a\x12\xfe\x8a\x07\xc7\x07\xcc\xe8l\x81" +
138 "\xf2\xd9\xe3\x89\xb5\x98\xee\xbd\x8etQ`2>\\t:_\xd7w\xe6\xb5\x96\xc7\xff\x9c",
139 `nXlI0lmeXmWesZqaEv6KB8cHzOhsgfLZ44m1mO\u0036\u0039jnRRYDI+XHQ6X9d35rWWx\/+c`,
140 },
141 }
142
143
144 func stdRef(ref string) string {
145 return ref
146 }
147
148
149 func urlRef(ref string) string {
150 ref = strings.ReplaceAll(ref, "+", "-")
151 ref = strings.ReplaceAll(ref, "/", "_")
152 return ref
153 }
154
155
156 func rawRef(ref string) string {
157 return strings.ReplaceAll(ref, "=", "")
158 }
159
160
161 func rawURLRef(ref string) string {
162 return rawRef(urlRef(ref))
163 }
164
165 var encodingTests = []EncodingTest{
166 {StdEncoding, stdRef},
167 {URLEncoding, urlRef},
168 {RawStdEncoding, rawRef},
169 {RawURLEncoding, rawURLRef},
170 }
171
172 func testEqual(t *testing.T, msg string, args ...interface{}) bool {
173 t.Helper()
174 if args[len(args) - 2] != args[len(args) - 1] {
175 t.Errorf(msg, args...)
176 return false
177 }
178 return true
179 }
180
181 func TestEncoderRecover(t *testing.T) {
182 t.Run("nil dst", func(t *testing.T) {
183 in := []byte("abc")
184 defer func(){
185 if v := recover(); v != nil {
186 println("recover:", v)
187 } else {
188 t.Fatal("not recover")
189 }
190 }()
191 b64encode(nil, &in, int(StdEncoding))
192 })
193 t.Run("nil src", func(t *testing.T) {
194 in := []byte("abc")
195 (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data = uintptr(0)
196 out := make([]byte, 0, 10)
197 defer func(){
198 if v := recover(); v != nil {
199 println("recover:", v)
200 } else {
201 t.Fatal("not recover")
202 }
203 }()
204 b64encode(&out, &in, int(StdEncoding))
205 })
206 }
207
208 func TestEncoder(t *testing.T) {
209 for _, p := range pairs {
210 for _, tt := range encodingTests {
211 got := tt.enc.EncodeToString([]byte(p.decoded))
212 testEqual(t, "Encode(%q) = %q, want %q", p.decoded, got, tt.conv(p.encoded))
213 }
214 }
215 }
216
217 func benchmarkStdlibWithSize(b *testing.B, nb int) {
218 buf := make([]byte, nb)
219 dst := make([]byte, base64.StdEncoding.EncodedLen(nb))
220 _, _ = io.ReadFull(rand.Reader, buf)
221 b.SetBytes(int64(nb))
222 b.ResetTimer()
223 b.RunParallel(func(pb *testing.PB) {
224 for pb.Next() {
225 base64.StdEncoding.Encode(dst, buf)
226 }
227 })
228 }
229
230 func benchmarkBase64xWithSize(b *testing.B, nb int) {
231 buf := make([]byte, nb)
232 dst := make([]byte, StdEncoding.EncodedLen(nb))
233 _, _ = io.ReadFull(rand.Reader, buf)
234 b.SetBytes(int64(nb))
235 b.ResetTimer()
236 b.RunParallel(func(pb *testing.PB) {
237 for pb.Next() {
238 StdEncoding.Encode(dst, buf)
239 }
240 })
241 }
242
243 func BenchmarkEncoderStdlib_16B (b *testing.B) { benchmarkStdlibWithSize(b, 16) }
244 func BenchmarkEncoderStdlib_56B (b *testing.B) { benchmarkStdlibWithSize(b, 56) }
245 func BenchmarkEncoderStdlib_128B (b *testing.B) { benchmarkStdlibWithSize(b, 128) }
246 func BenchmarkEncoderStdlib_4kB (b *testing.B) { benchmarkStdlibWithSize(b, 4 * 1024) }
247 func BenchmarkEncoderStdlib_256kB (b *testing.B) { benchmarkStdlibWithSize(b, 256 * 1024) }
248 func BenchmarkEncoderStdlib_1MB (b *testing.B) { benchmarkStdlibWithSize(b, 1024 * 1024) }
249
250 func BenchmarkEncoderBase64x_16B (b *testing.B) { benchmarkBase64xWithSize(b, 16) }
251 func BenchmarkEncoderBase64x_56B (b *testing.B) { benchmarkBase64xWithSize(b, 56) }
252 func BenchmarkEncoderBase64x_128B (b *testing.B) { benchmarkBase64xWithSize(b, 128) }
253 func BenchmarkEncoderBase64x_4kB (b *testing.B) { benchmarkBase64xWithSize(b, 4 * 1024) }
254 func BenchmarkEncoderBase64x_256kB (b *testing.B) { benchmarkBase64xWithSize(b, 256 * 1024) }
255 func BenchmarkEncoderBase64x_1MB (b *testing.B) { benchmarkBase64xWithSize(b, 1024 * 1024) }
256
257 func TestDecoder(t *testing.T) {
258 for _, p := range pairs {
259 for _, tt := range encodingTests {
260 encoded := tt.conv(p.encoded)
261 dbuf := make([]byte, tt.enc.DecodedLen(len(encoded)))
262 count, err := tt.enc.Decode(dbuf, []byte(encoded))
263 testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil))
264 testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded))
265 testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded)
266
267 dbuf, err = tt.enc.DecodeString(encoded)
268 testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil))
269 testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded)
270 }
271 }
272 }
273
274 func TestDecoderRecover(t *testing.T) {
275 t.Run("nil dst", func(t *testing.T) {
276 in := []byte("abc")
277 defer func(){
278 if v := recover(); v != nil {
279 println("recover:", v)
280 } else {
281 t.Fatal("not recover")
282 }
283 }()
284 b64decode(nil, unsafe.Pointer(&in[0]), len(in), int(StdEncoding))
285 })
286 t.Run("nil src", func(t *testing.T) {
287 out := make([]byte, 0, 10)
288 defer func(){
289 if v := recover(); v != nil {
290 println("recover:", v)
291 } else {
292 t.Fatal("not recover")
293 }
294 }()
295 b64decode(&out, nil, 5, int(StdEncoding))
296 })
297 }
298
299 func TestDecoderCRLF(t *testing.T) {
300 for _, p := range crlf_pairs {
301 for _, tt := range encodingTests {
302 encoded := tt.conv(p.encoded)
303 dbuf := make([]byte, tt.enc.DecodedLen(len(encoded)))
304 count, err := tt.enc.Decode(dbuf, []byte(encoded))
305 testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil))
306 testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded))
307 testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded)
308
309 dbuf, err = tt.enc.DecodeString(encoded)
310 testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil))
311 testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded)
312 }
313 }
314 }
315
316 func TestDecoderJSON(t *testing.T) {
317 for _, p := range json_pairs {
318 encoded := p.encoded
319 dbuf := make([]byte, JSONStdEncoding.DecodedLen(len(encoded)))
320 count, err := JSONStdEncoding.Decode(dbuf, []byte(encoded))
321 testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil))
322 testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded))
323 testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded)
324
325 dbuf, err = JSONStdEncoding.DecodeString(encoded)
326 testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil))
327 testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded)
328 }
329 }
330
331 func TestDecoderError(t *testing.T) {
332 _, err := StdEncoding.DecodeString("!aGVsbG8sIHdvcmxk")
333 if err != base64.CorruptInputError(0) {
334 panic(err)
335 }
336 _, err = StdEncoding.DecodeString("aGVsbG8!sIHdvcmxk")
337 if err != base64.CorruptInputError(7) {
338 panic(err)
339 }
340 _, err = StdEncoding.DecodeString("123456")
341 if err != base64.CorruptInputError(6) {
342 panic(err)
343 }
344 _, err = StdEncoding.DecodeString("1234;6")
345 if err != base64.CorruptInputError(4) {
346 panic(err)
347 }
348 _, err = StdEncoding.DecodeString("F\xaa\xaa\xaa\xaaDDDDDDDDDDDDD//z")
349 if err != base64.CorruptInputError(1) {
350 panic(err)
351 }
352 }
353
354 func benchmarkStdlibDecoder(b *testing.B, v string) {
355 src := []byte(v)
356 dst := make([]byte, base64.StdEncoding.DecodedLen(len(v)))
357 b.SetBytes(int64(len(v)))
358 b.ResetTimer()
359 b.RunParallel(func(pb *testing.PB) {
360 for pb.Next() {
361 _, _ = base64.StdEncoding.Decode(dst, src)
362 }
363 })
364 }
365
366 func benchmarkBase64xDecoder(b *testing.B, v string) {
367 src := []byte(v)
368 dst := make([]byte, StdEncoding.DecodedLen(len(v)))
369 b.SetBytes(int64(len(v)))
370 b.ResetTimer()
371 b.RunParallel(func(pb *testing.PB) {
372 for pb.Next() {
373 _, _ = StdEncoding.Decode(dst, src)
374 }
375 })
376 }
377
378 var data = `////////////////////////////////////////////////////////////////`
379 func BenchmarkDecoderStdLib (b *testing.B) { benchmarkStdlibDecoder(b, data) }
380 func BenchmarkDecoderBase64x (b *testing.B) { benchmarkBase64xDecoder(b, data) }
381
View as plain text