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