前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go:封装io.Reader 实现灵活可扩展的io程序

go:封装io.Reader 实现灵活可扩展的io程序

作者头像
超级大猪
发布2020-02-13 15:03:21
9270
发布2020-02-13 15:03:21
举报
文章被收录于专栏:大猪的笔记大猪的笔记

在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的代码规范。岂不美哉。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档