/
base64.go
111 lines (92 loc) · 2.6 KB
/
base64.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package gophpass
import (
"encoding/base64"
)
// MyEncoding is Alias/Composition of struct Encoding
type MyEncoding struct {
base64.Encoding // extend base64.Encoding because
encode [64]byte
decodeMap [256]byte
padChar rune
}
const (
StdPadding rune = '=' // Standard padding character
NoPadding rune = -1 // No padding
)
// a string for mapping an int to the corresponding base 64 character.
const alphabet = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var bcEncoding = base64.NewEncoding(alphabet)
func base64Encode(src []byte) []byte {
n := bcEncoding.EncodedLen(len(src))
dst := make([]byte, n)
MyNewEncoding(alphabet).MyEncode(dst, src)
return dst
}
// MyEncode is overridden methode base64.Encoding.Encode() (polymorphism)
// MyEncoding is a Composition class of Encoding
func (enc *MyEncoding) MyEncode(dst, src []byte) {
if len(src) == 0 {
return
}
// enc is a pointer receiver, so the use of enc.encode within the hot
// loop below means a nil check at every operation. Lift that nil check
// outside of the loop to speed up the encoder.
_ = enc.encode
di, si := 0, 0
n := (len(src) / 3) * 3
for si < n {
// Convert 3x 8bit source bytes into 4 bytes
// val := uint(src[si+0])<<16 | uint(src[si+1])<<8 | uint(src[si+2])
val := uint(src[si+0]) | uint(src[si+1])<<8 | uint(src[si+2])<<16
dst[di+0] = enc.encode[val&0x3F]
dst[di+1] = enc.encode[val>>6&0x3F]
dst[di+2] = enc.encode[val>>12&0x3F]
dst[di+3] = enc.encode[val>>18&0x3F]
si += 3
di += 4
}
remain := len(src) - si
if remain == 0 {
return
}
// Add the remaining small block
val := uint(src[si+0]) << 16
if remain == 2 {
val |= uint(src[si+1]) << 8
}
dst[di+0] = enc.encode[val>>18&0x3F]
dst[di+1] = enc.encode[val>>12&0x3F]
switch remain {
case 2:
dst[di+2] = enc.encode[val>>6&0x3F]
if enc.padChar != NoPadding {
dst[di+3] = byte(enc.padChar)
}
case 1:
if enc.padChar != NoPadding {
dst[di+2] = byte(enc.padChar)
dst[di+3] = byte(enc.padChar)
}
}
}
// MyNewEncoding overrides base64.Encoding.NewEncoding()
func MyNewEncoding(encoder string) *MyEncoding {
if len(encoder) != 64 {
panic("encoding alphabet is not 64-bytes long")
}
for i := 0; i < len(encoder); i++ {
if encoder[i] == '\n' || encoder[i] == '\r' {
panic("encoding alphabet contains newline character")
}
}
e := new(MyEncoding)
e.padChar = StdPadding
copy(e.encode[:], encoder)
for i := 0; i < len(e.decodeMap); i++ {
e.decodeMap[i] = 0xFF
}
for i := 0; i < len(encoder); i++ {
e.decodeMap[encoder[i]] = byte(i)
}
return e
}