打开浏览器浏览网页时,我们可以看到各种各样的文字、图片、视频等等各式各样的信息。那么浏览器是怎样和服务器交互这些信息的呢?通过分析不难发现,这些信息往往要经过统一编码之后,才进行传递。今天,我们来通过分析gzip.go文件,了解gzip压缩的实现,进而去实现自己编码网页信息。
数据结构定义
1. 常量定义
常量定义来自deflate定义,阅读deflate.go文件,可以获得相关值。这些常量用来标记数据的状态和压缩方式。
// These constants are copied from the flate package, so that code that imports
// "compress/gzip" does not also have to import "compress/flate".
const (
NoCompression = flate.NoCompression
BestSpeed = flate.BestSpeed
BestCompression = flate.BestCompression
DefaultCompression = flate.DefaultCompression
HuffmanOnly = flate.HuffmanOnly
)
deflate.go中对这些常量的定义如下:
const (
NoCompression = 0
BestSpeed = 1
BestCompression = 9
DefaultCompression = -1
HuffmanOnly = -2
)
2. Writer结构体的定义
Writer结构体定义了压缩常用的变量,为后续接口函数的实现提供了便利。
type Writer struct {
Header // written at first call to Write, Flush, or Close
w io.Writer
level int
wroteHeader bool
compressor *flate.Writer
digest uint32 // CRC-32, IEEE polynomial (section 8)
size uint32 // Uncompressed size (section 2.3.1)
closed bool
buf [10]byte
err error
}
其中,
- Header:定义来自gunzip.go,定义了gzip数据头部的一些信息,稍后我们会详细解释
- w:声明为io.Writer接口,调用相关函数完成数据向缓冲区的写入
- level:压缩的等级。范围为[-2, 9]
- wroteHeader:表示gzip头部信息是否已经填充
- compressor:压缩器
- digest: 32位的循环冗余校验位。不懂请点击这里
- size:未压缩数据的长度
- closed:该Writer是否关闭
- buf:存储gzip头部的信息
- err:处理Writer相关函数中的一切错误
函数定义
1. NewWriter
该方法创建并使用io.Writer初始化一个Writer。
func NewWriter(w io.Writer) *Writer {
z, _ := NewWriterLevel(w, DefaultCompression)
return z
}
2. init
该方法初始化一个Writer。
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,
}
}
3. NewWriterLevel
该方法创建一个Writer,并且定义了Writer中level的值。
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
}
4. Reset
该方法重置Writer的状态,从而可以复用一个Writer。
func (z *Writer) Reset(w io.Writer) {
z.init(w, z.level)
}
5. writeBytes
该方法把一段字节数据的长度写入z.w。
func (z *Writer) writeBytes(b []byte) error {
if len(b) > 0xffff {
return errors.New("gzip.Write: Extra data is too large")
}
//var le = binary.LittleEndian
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
}
- 注解1
le.PutUint16(z.buf[:2], uint16(len(b)))
是将16位无符号整数输出为小端模式。PutUint16的定义如下:
func (littleEndian) PutUint16(b []byte, v