在go中有一个非常重要的接口io.Reader
可以实现这个接口,使得用户可以使用obj.Read(buf)函数操作对象。但通常,需要更进一步,将这个接口进行封装,实现自己的逻辑,使得其拥有较好的扩展性。屏蔽底层的io操作。
这里以实现一个RC4加密器为例,主要展示的是Reader的封装用法,而不是加密。
package rc4
import (
"io"
)
type RC4Reader struct {
baseReader io.Reader // 特别要注意这个baseReader,这里屏蔽了底层的操作,专注于加密这个用户逻辑
password string
sbox []byte
i, j int
}
func NewReader(password string, reader io.Reader) *RC4Reader {
key := []byte(password)
keylength := len(key)
r := new(RC4Reader)
r.password = password
r.baseReader = reader
sbox := make([]byte, 256)
for i := 0; i <= 255; i++ {
sbox[i] = byte(i)
}
j := 0
for i := 0; i < 256; i++ {
j = (j + int(sbox[i]) + int(key[i%keylength])) % 256
sbox[i], sbox[j] = sbox[j], sbox[i]
}
r.sbox = sbox
return r
}
func (r *RC4Reader) Read(p []byte) (n int, err error) {
tp := make([]byte, len(p), len(p))
n, err = r.baseReader.Read(tp) // 从baseReader读取数据,基于对接口的信任,这一定能取到需要加密的数据,而不需要管这个数据是从string还是文件或者网络流中来。这就是接口的妙用。
// 同时,处理baseReader的错误,进行封装即可
if err != nil {
return n, err
}
tp = tp[:n]
// 下面是加密编码操作,不需要理解
ret := make([]byte, n, n)
for i := 0; i < n; i++ {
r.i = (r.i + 1) % 256 //a
r.j = (r.j + int(r.sbox[r.i])) % 256 //b
r.sbox[r.i], r.sbox[r.j] = r.sbox[r.j], r.sbox[r.i]
ret[i] = tp[i] ^ r.sbox[(int(r.sbox[r.i])+int(r.sbox[r.j]))%256]
}
copy(p, ret)
return n, nil
}
因为封装了baseReader,这个加密器就能兼容所有的io.Reader的实现。而不需要去管这个reader是来源于何方。
调用:
func main() {
// 使用strings.Reader作为数据源,直接加密string的数据
r := rc4.NewReader("yzh", strings.NewReader("yzhyzhyzhyzhyzhyzhyzhyzhyzhyzhyzhyzh"))
var enc bytes.Buffer
for {
ret := make([]byte, 4, 4)
n, err := r.Read(ret)
if err != nil {
break
}
enc.Write(ret[:n])
}
fmt.Println("encrypt is", enc.Bytes())
// 使用bytes.Reader作为数据源,解码bytes数据
rdec := rc4.NewReader("yzh", bytes.NewReader(enc.Bytes()))
var dec bytes.Buffer
for {
ret := make([]byte, 4, 4)
n, err := rdec.Read(ret)
if err != nil {
break
}
dec.Write(ret[:n])
}
fmt.Println("decrypt is", dec.Bytes(), dec.String())
}
因为baseReader的存在,加/解密的数据源可以是任何实现了io.Reader的结构。包括strings,bytes甚至网络流,这对用户来说是极其灵活方便的。也统一了go的代码规范。岂不美哉。