Golang中gzip过滤器的源码分析与解释
2017-11-22 19:46
309 查看
Golang中gzip过滤器的源码分析与解释
与上次相同,这次gzip过滤器的源码,依旧是使用LiteIDE中自带的源码阅读功能来解读的。个人感觉在Go语言的学习中这款软件非常方便这次我们对gzip包的源码做个整体分析。下面就一部分一部分来看。
引用的包
可看到gzip包引用了如下包:package gzip import ( "compress/flate" "errors" "fmt" "hash/crc32" "io" "time" )
结构体与常量的定义
在gzip包的源码中,定义了一个Writer结构体和一些常量:type Writer struct { Header w io.Writer level int wroteHeader bool compressor *flate.Writer digest uint32 size uint32 closed bool buf [10]byte err error }
一个Writer是一个io.WriteCloser,写入到一个被压缩和被写入w的Writer。
const ( NoCompression = flate.NoCompression BestSpeed = flate.BestSpeed BestCompression = flate.BestCompression DefaultCompression = flate.DefaultCompression HuffmanOnly = flate.HuffmanOnly )
其中这些常量的定义是从compress/flate包中拷贝来的,所以在使用时,需要引用gzip包的时候就没必要再引用flate包了。
NewWriter函数
func NewWriter(w io.Writer) *Writer { z, _ := NewWriterLevel(w, DefaultCompression) return z }
该函数返回一个新的Writer。要在Writer.Header中设置字段的调用者必须先执行此操作。
Writer可能会被缓冲,直到调用Close才会被刷新。而调用Close需要调用者自己完成。
这是对于Write,Flush或Close的第一次调用。
NewWriterLevel及init函数
NewWriterLevel:func NewWriterLevel(w io.Writer, level int) (*Writer, error) { if level < HuffmanOnly || level > BestCompression { return nil, fmt.Errorf("gzip: invalid compression level: %d", level) } z := new(Writer) z.init(w, level) return z, nil }
init:
func (z *Writer) init(w io.Writer, level int) { compressor := z.compressor if compressor != nil { compressor.Reset(w) } *z = Writer{ Header: Header{ OS: 255, // unknown }, w: w, level: level, compressor: compressor, } }
NewWriterLevel函数与NewWriter函数很相似,区别在于,NewWriterLevel函数指定了压缩级别,而不是和NewWriter一样设为DefaultCompression。
这里的压缩级别可以是DefaultCompression,NoCompression,HuffmanOnly
,或者BestSpeed和BestCompression中的任何整数值。
如果级别有效,则error会返回nil。
init就是一个初始化函数,没什么特别的地方,之后来看看其他的函数。
Reset函数
func (z *Writer) Reset(w io.Writer) { z.init(w, z.level) }
Reset函数会舍弃掉Writer z的当前状态,并使其等于之前NewWriter或NewWriterLevel给它的原始状态,但是这些都会写入w。
这样做是为了可以重复利用一个Writer,而不是再新分配一个新的w。
writeBytes函数
func (z *Writer) writeBytes(b []byte) error { if len(b) > 0xffff { return errors.New("gzip.Write: Extra data is too large") } le.PutUint16(z.buf[:2], uint16(len(b))) _, err := z.w.Write(z.buf[:2]) if err != nil { return err } _, err = z.w.Write(b) return err }
writeBytes函数的作用是把一个长度为前缀的byte切片写入到z.w。
writeString函数
func (z *Writer) writeString(s string) (err error) { needconv := false for _, v := range s { if v == 0 || v > 0xff { return errors.New("gzip.Write: non-Latin-1 header string") } if v > 0x7f { needconv = true } } if needconv { b := make([]byte, 0, len(s)) for _, v := range s { b = append(b, byte(v)) } _, err = z.w.Write(b) } else { _, err = io.WriteString(z.w, s) } if err != nil { return err } // GZIP strings are NUL-terminated. z.buf[0] = 0 _, err = z.w.Write(z.buf[:1]) return err }
writeString函数的作用是将一个UTF-8字符串以GZIP的格式写入z.w。
GZIP格式: GZIP(RFC 1952)指定字符串是以NUL结尾的ISO 8859-1(Latin-1)。
如果是非Latin-1格式则返回error;如果是非ASCII格式,则会发生转换。
Write函数
func (z *Writer) Write(p []byte) (int, error) { if z.err != nil { return 0, z.err } var n int if !z.wroteHeader { z.wroteHeader = true z.buf = [10]byte{0: gzipID1, 1: gzipID2, 2: gzipDeflate} if z.Extra != nil { z.buf[3] |= 0x04 } if z.Name != "" { z.buf[3] |= 0x08 } if z.Comment != "" { z.buf[3] |= 0x10 } if z.ModTime.After(time.Unix(0, 0)) { le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix())) } if z.level == BestCompression { z.buf[8] = 2 } else if z.level == BestSpeed { z.buf[8] = 4 } z.buf[9] = z.OS n, z.err = z.w.Write(z.buf[:10]) if z.err != nil { return n, z.err } if z.Extra != nil { z.err = z.writeBytes(z.Extra) if z.err != nil { return n, z.err } } if z.Name != "" { z.err = z.writeString(z.Name) if z.err != nil { return n, z.err } } if z.Comment != "" { z.err = z.writeString(z.Comment) if z.err != nil { return n, z.err } } if z.compressor == nil { z.compressor, _ = flate.NewWriter(z.w, z.level) } } z.size += uint32(len(p)) z.digest = crc32.Update(z.digest, crc32.IEEETable, p) n, z.err = z.compressor.Write(p) return n, z.err }
Write函数把一个压缩形式的p写入到到底层io.Writer。在Writer函数结束之前,压缩的字节不一定会被刷新。
函数中,关于ModTime,MTIME取0意味着没有设置修改的时间。
Flush函数
func (z *Writer) Flush() error { if z.err != nil { return z.err } if z.closed { return nil } if !z.wroteHeader { z.Write(nil) if z.err != nil { return z.err } } z.err = z.compressor.Flush() return z.err }
Flush函数刷新所有正在挂起的被压缩的数据,去到底层writer。
Flush函数主要用于压缩网络协议,以确保远程读取器有足够的数据来重建数据包。 在数据写入之前,刷新不会返回。 如果底层写入程序返回错误,则Flush返回该错误。
Close函数
func (z *Writer) Close() error { if z.err != nil { return z.err } if z.closed { return nil } z.closed = true if !z.wroteHeader { z.Write(nil) if z.err != nil { return z.err } } z.err = z.compressor.Close() if z.err != nil { return z.err } le.PutUint32(z.buf[:4], z.digest) le.PutUint32(z.buf[4:8], z.size) _, z.err = z.w.Write(z.buf[:8]) return z.err }
Close函数通过将所有未写入的数据刷新到底层的io.Writer,并写入GZIP页脚来关闭Writer。
需要注意的是,Close函数不会关闭底层的io.Writer。
总结
以上就是gzip包的所有源码,可以看出gzip对于一个writer的操作有Write、Close、Flush三种,功能都很明确且很方便,同时也有writeBytes和writeString这样的函数,使得操作简化。使用gzip包进行压缩方面的操作可以很容易完成。要注意在使用writeString的时候明确字符串格式等问题。相关文章推荐
- gzip过滤器源码分析
- Golang中net/http包源码分析与解释
- Elasticsearch源码分析十二--过滤器
- golang之websocket 源码分析
- MINA源码分析---对客户端设置连接间隔时间的过滤器
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[一]
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[一]
- PostgreSQL源码分析(2)– 常用数据类型/SQL语句的解释和执行
- Lua源码分析 -- 虚拟机以及指令解释
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[二]
- golang 移动应用例子 example/basic 源码框架分析
- Struts2 源码分析——过滤器(Filter)
- netty源码分析(十)ChannelPipeline创建时机与高级拦截过滤器模式的运用
- MVC源码分析 - Authorize授权过滤器
- Golang1.7 Goroutine源码分析(转)
- Struts2 源码分析——过滤器(Filter)
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[一]
- golang sync.Pool 使用和源码分析
- okhttp源码分析(二)-RetryAndFollowUpInterceptor过滤器
- golang-net/http源码分析之http server