-
Notifications
You must be signed in to change notification settings - Fork 0
/
第 12.02.02、compress_flate 包
349 lines (242 loc) · 9.17 KB
/
第 12.02.02、compress_flate 包
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
总览
flate 包实现了 DEFLATE 压缩数据格式,在 RFC 1951 中描述
gzip 和 zlib 包实现了存取基于 DEFALTE 的文件格式
EG.【Dictionary】
一个预设的字典可以用于提升压缩率
使用字典的缺点就是压缩器和解压器必须提前了解使用了何种字典
// 字典是一个用 byte 表示的字符串
// 当压缩一些输入的数据时,压缩器将尝试将子字符串替换为字典中匹配上的
// 这样来说,字典中的子字符串,应该仅为,会出现在实际数据流中的子字符串
const dict = `<?xml version="1.0"?>` + `<book>` + `<data>` + `<meta name="` + `" content="`
// 要压缩的数据应该(但不要求)频繁包含一些字符串,它们能与字典中的匹配上
const data = `<?xml version="1.0"?>
<book>
<meta name="title" content="The Go Programming Language"/>
<meta name="authors" content="Alan Donovan and Brian Kernighan"/>
<meta name="published" content="2015-10-26"/>
<meta name="isbn" content="978-0134190440"/>
<data>...</data>
</book>
`
var b bytes.Buffer
// 使用特制的字典压缩数据
zw, err := flate.NewWriterDict(&b, flate.DefaultCompression, []byte(dict))
if err != nil {
log.Fatal(err)
}
if _, err := io.Copy(zw, strings.NewReader(data)); err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}
// 解压器必须使用与压缩器相同的字典
// 否则输入就会像毁坏了一样
fmt.Println("Decompressed output using the dictionary:")
zr := flate.NewReaderDict(bytes.NewReader(b.Bytes()), []byte(dict))
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
fmt.Println()
// 将字典中出现的字节全部用 '#' 来代替,来可视化展现使用预设字典的大致效果
fmt.Println("Substrings matched by the dictionary are marked with #:")
hashDict := []byte(dict)
for i := range hashDict {
hashDict[i] = '#'
}
zr = flate.NewReaderDict(&b, hashDict)
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
EG.【Reset】
在对性能有严格要求的应用中,Reset 可以用于取消当前的压缩/解压状态,并使用先前分配的内存来快速重新初始化它们
proverbs := []string{
"Don't communicate by sharing memory, share memory by communicating.\n",
"Concurrency is not parallelism.\n",
"The bigger the interface, the weaker the abstraction.\n",
"Documentation is for users.\n",
}
var r strings.Reader
var b bytes.Buffer
buf := make([]byte, 32<<10)
zw, err := flate.NewWriter(nil, flate.DefaultCompression)
if err != nil {
log.Fatal(err)
}
zr := flate.NewReader(nil)
for _, s := range proverbs {
r.Reset(s)
b.Reset()
// 重置压缩器,并从输入流中编码
zw.Reset(&b)
if _, err := io.CopyBuffer(zw, &r, buf); err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}
// 重置压缩器,并解码至输出流中
if err := zr.(flate.Resetter).Reset(&b, nil); err != nil {
log.Fatal(err)
}
if _, err := io.CopyBuffer(os.Stdout, zr, buf); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
}
EG.【同步】
DEFLATE 适用于通过网络传输压缩过的数据
var wg sync.WaitGroup
defer wg.Wait()
// 使用 io.Pipe 来模拟一个网络连接
// 现实中的网络应用应该小心而正确地关闭底层连接
rp, wp := io.Pipe()
// 启动一个 goroutine 来饰演发送端
wg.Add(1)
go func() {
defer wg.Done()
zw, err := flate.NewWriter(wp, flate.BestSpeed)
if err != nil {
log.Fatal(err)
}
b := make([]byte, 256)
for _, m := range strings.Fields("A long time ago in a galaxy far, far away...") {
// 我们使用简单的帧格式
// 第一个字节是信息的长度,接着是内容本身
b[0] = uint8(copy(b[1:], m))
if _, err := zw.Write(b[:1+len(m)]); err != nil {
log.Fatal(err)
}
// Flush 保证了接收端能接收到到目前为止所有的数据
if err := zw.Flush(); err != nil {
log.Fatal(err)
}
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}
}()
// 启动一个 goroutine 来饰演接收端
wg.Add(1)
go func() {
defer wg.Done()
zr := flate.NewReader(rp)
b := make([]byte, 256)
for {
// 读取信息长度
// 用于保证对发送端的每个 Flush 和 Close 都返回
if _, err := io.ReadFull(zr, b[:1]); err != nil {
if err == io.EOF {
break // 发送端关闭了流
}
log.Fatal(err)
}
// 读取信息内容
n := int(b[0])
if _, err := io.ReadFull(zr, b[:n]); err != nil {
log.Fatal(err)
}
fmt.Printf("Received %d bytes: %s\n", n, b[:n])
}
fmt.Println()
if err := zr.Close(); err != nil {
log.Fatal(err)
}
}()
常量
const (
NoCompression = 0
BestSpeed = 1
BestCompression = 9
DefaultCompression = -1
// HuffmanOnly 禁用了 Lempel-Ziv 匹配搜索,仅执行 Huffman 行军编码。
// 这个模式常用于编码一个已经被 LZ 样式算法(eg. Snappy 或 LZ4)编码,但缺少行军编码的数据。
// 若输入流中某些字节比其他字节出现频率更多,则能提高压缩率
//
// 注意 HuffmanOnly 产生的压缩输出将兼容 RFC 1951。
// 也就是说,任何有效的 DEFLATE 加压器都将能
// 继续解压这个输出
HuffmanOnly = -2
)
NewReader(r io.Reader) io.ReadCloser
返回一个新的 ReadClose,它可以用于读取未压缩版本的 r
若 r 没有同时实现 io.ByteReader,解压器可能会从 r 中读取比所需的还要多的数据
当读取结束时,调用者有责任对 ReadCloser 调用 Close
本函数返回的 ReadCloser 同样实现了 Resetter
NewReaderDict(r io.Reader, dict []byte) io.ReadClose
与 NewReader 类似,但是一预设的字典初始化 reader
返回的 Reader 的行为,就如同未压缩的,以给定的字典为开头的数据流,而字典本身已经被读取
本函数常用于读取被 NewWriteDict 压缩的数据
被 NewReaderDict 返回的 ReadCloser 同样实现了 Resetter
CorruptInputError int64 类型
一个 CorruptInputError 报告了输出中给定偏移上出现的错误
type CorruptInputError int64
(e CooruptInputError) Error() string
InternalError string 类型
一个 InternalError 报告了 flate 编码本身的一个错误
type InternalError string
(e InternalError) Error() string
ReadError struct 类型
一个 ReadError 报告了再读取输入时遇到的错误
弃用:不再被返回
type ReadError struct {
Offset int64 // 当错误发生时,偏移的字节数
Err error // 被底层 Read 返回的错误
}
(e *ReadError) Error() string
Reader interface 类型
NewReader 所需的真实的读取接口
若传入的 io.Reader 不具有 ReadByte,NewReader 将启用它自己的缓存
type Reader interface {
io.Reader
io.ByteReader
}
Resetter interface 类型
重置被 NewReader 或者 NewReaderDict 返回的 ReadCloser,并切换至新的底层 Reader
这允许重复使用一个 ReadCloser,而非重新分配一个
WriteError struct 类型
一个 WriteError 报告了当写入输出时所遇见的一个错误
type WriteError struct {
Offset int64 // 当错误发生时,偏移的字节数
Err error // 被底层 Write 返回的错误
}
(e *WriterError) Error() string
Writer struct 类型
一个 Writer 取出写入的它的数据,并将压缩形式的数据写入底层的 writer(参见 NewWriter)
type Writer struct {
// 包含过滤或未导出字段
}
NewWriter(w io.Writer, level int) (*Writer, error)
返回一个新的 Writer 在给定的等级上压缩数据
与 zlib 类似,等级从 1(BestSpeed)至 9(BestCompression);
等级越高,一般来说,运行地越慢,但压缩的越多
等级 0(NoCompression)并不会做任何压缩;它仅会加上必要的 DEFLATE 帧
等级 -1(DefaultCompression) 使用默认的压缩等级
等级 -2(HuffmanOnly)将仅使用 Huffman 压缩,对所有类型的输入都具有非常快的压缩速度,但牺牲了可考虑的压缩有效性
若等级在 [-2,9] 之间,返回的错误将为 nil
其它情况将为非 nil
NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error)
与 NewWriter 类似,但使用给定的字典初始化一个新的 Writer
返回的 Writer 的行为,就像字典已经被写入,但是没有产生任何压缩的输出
写入 w 的压缩数据,仅能被相同字典初始化的 Reader 解压缩
(w *Writer) Close() error
刷写并关闭 writer
(w *Writer) Flush() error
刷写任何等待中的数据至底层的 writer
它主要常用于压缩网络协议,来保证远端 reader 具有足够的数据重构一个包
本方法不会返回,直到数据被写入
当没有等待中的数据,却调用本方法时,依旧会导致 Writer 发送一个至少4 字节的同步标记
若底层的 writer 返回了一个错误,本方法返回那个错误
在 zlib 库的专业术语中,本函数等价于 Z_SYNC_FLUSH
(w *Writer) Reset(dst io.Writer)
取消一个 writer 的状态,将其等价于以 dst 和 w 的等级和字典所调用的 NewWriter 或 NewWriterDict
(w *Writer) Write(data []byte) (n int, err error)
将数据写入 w 中,其最终将把压缩心事的数据写入底层的 writer