base64.mx raw
1 package helpers
2
3 const b64encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
4
5 // Base64Encode encodes bytes to standard base64. The output size is known
6 // from the input size, so it is presized and written through a cursor: push
7 // does not grow, and the padding concat returned a full slice that the next
8 // push could not extend.
9 func Base64Encode(data []byte) (s string) {
10 n := len(data)
11 out := []byte{:(n + 2) / 3 * 4}
12 j := 0
13 for i := 0; i < n; i += 3 {
14 var b0, b1, b2 byte
15 b0 = data[i]
16 if i+1 < n {
17 b1 = data[i+1]
18 }
19 if i+2 < n {
20 b2 = data[i+2]
21 }
22 out[j] = b64encode[(b0>>2)&0x3f]
23 j++
24 out[j] = b64encode[((b0<<4)|(b1>>4))&0x3f]
25 j++
26 if i+1 < n {
27 out[j] = b64encode[((b1<<2)|(b2>>6))&0x3f]
28 } else {
29 out[j] = '='
30 }
31 j++
32 if i+2 < n {
33 out[j] = b64encode[b2&0x3f]
34 } else {
35 out[j] = '='
36 }
37 j++
38 }
39 return string(out)
40 }
41
42 // Base64Decode decodes standard base64. Returns nil on invalid input.
43 func Base64Decode(s string) (buf []byte) {
44 // Strip padding.
45 n := len(s)
46 pad := 0
47 for n > 0 && s[n-1] == '=' {
48 pad++
49 n--
50 }
51 out := []byte{:n * 3 / 4}
52 j := 0
53 var acc uint32
54 var bits int32
55 for i := 0; i < n; i++ {
56 v := b64val(s[i])
57 if v < 0 {
58 return nil
59 }
60 acc = (acc << 6) | uint32(v)
61 bits += 6
62 if bits >= 8 {
63 bits -= 8
64 out[j] = byte(acc >> uint32(bits))
65 j++
66 }
67 }
68 _ = pad
69 return out[:j]
70 }
71
72 func b64val(c byte) (n int32) {
73 switch {
74 case c >= 'A' && c <= 'Z':
75 return int32(c - 'A')
76 case c >= 'a' && c <= 'z':
77 return int32(c-'a') + 26
78 case c >= '0' && c <= '9':
79 return int32(c-'0') + 52
80 case c == '+':
81 return 62
82 case c == '/':
83 return 63
84 default:
85 return -1
86 }
87 }
88