-
Notifications
You must be signed in to change notification settings - Fork 124
/
Copy pathcompression.js
130 lines (108 loc) · 3.89 KB
/
compression.js
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
import ZStream from 'pako/lib/zlib/zstream'
import { deflateInit2, deflate } from 'pako/lib/zlib/deflate'
import { inflate, inflateInit2 } from 'pako/lib/zlib/inflate'
import messages from 'pako/lib/zlib/messages.js'
import {
Z_NO_FLUSH, Z_SYNC_FLUSH, Z_OK,
Z_STREAM_END, Z_DEFAULT_COMPRESSION,
Z_DEFAULT_STRATEGY, Z_DEFLATED
} from 'pako/lib/zlib/constants'
const CHUNK_SIZE = 16384
const WINDOW_BITS = 15
/**
* Handles de-/compression via #inflate() and #deflate(), calls you back via #deflatedReady() and #inflatedReady().
* The chunk we get from deflater is actually a view of a 16kB arraybuffer, so we need to copy the relevant parts
* memory to a new arraybuffer.
*/
export default function Compressor (inflatedReady, deflatedReady) {
this.inflatedReady = inflatedReady
this.deflatedReady = deflatedReady
this._inflate = inflater(chunk => this.inflatedReady(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.length)))
this._deflate = deflater(chunk => this.deflatedReady(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.length)))
}
Compressor.prototype.inflate = function (buffer) {
this._inflate(new Uint8Array(buffer))
}
Compressor.prototype.deflate = function (buffer) {
this._deflate(new Uint8Array(buffer))
}
function deflater (emit) {
const stream = new ZStream()
const status = deflateInit2(stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, WINDOW_BITS, 8, Z_DEFAULT_STRATEGY)
if (status !== Z_OK) {
throw new Error('Problem initializing deflate stream: ' + messages[status])
}
return function (data) {
if (data === undefined) return emit()
// Attach the input data
stream.input = data
stream.next_in = 0
stream.avail_in = stream.input.length
let status
let output
let start
let ret = true
do {
// When the stream gets full, we need to create new space.
if (stream.avail_out === 0) {
stream.output = new Uint8Array(CHUNK_SIZE)
start = stream.next_out = 0
stream.avail_out = CHUNK_SIZE
}
// Perform the deflate
status = deflate(stream, Z_SYNC_FLUSH)
if (status !== Z_STREAM_END && status !== Z_OK) {
throw new Error('Deflate problem: ' + messages[status])
}
// If the output buffer got full, flush the data.
if (stream.avail_out === 0 && stream.next_out > start) {
output = stream.output.subarray(start, start = stream.next_out)
ret = emit(output)
}
} while ((stream.avail_in > 0 || stream.avail_out === 0) && status !== Z_STREAM_END)
// Emit whatever is left in output.
if (stream.next_out > start) {
output = stream.output.subarray(start, start = stream.next_out)
ret = emit(output)
}
return ret
}
}
function inflater (emit) {
const stream = new ZStream()
const status = inflateInit2(stream, WINDOW_BITS)
if (status !== Z_OK) {
throw new Error('Problem initializing inflate stream: ' + messages[status])
}
return function (data) {
if (data === undefined) return emit()
let start
stream.input = data
stream.next_in = 0
stream.avail_in = stream.input.length
let status, output
let ret = true
do {
if (stream.avail_out === 0) {
stream.output = new Uint8Array(CHUNK_SIZE)
start = stream.next_out = 0
stream.avail_out = CHUNK_SIZE
}
status = inflate(stream, Z_NO_FLUSH)
if (status !== Z_STREAM_END && status !== Z_OK) {
throw new Error('inflate problem: ' + messages[status])
}
if (stream.next_out) {
if (stream.avail_out === 0 || status === Z_STREAM_END) {
output = stream.output.subarray(start, start = stream.next_out)
ret = emit(output)
}
}
} while ((stream.avail_in > 0) && status !== Z_STREAM_END)
if (stream.next_out > start) {
output = stream.output.subarray(start, start = stream.next_out)
ret = emit(output)
}
return ret
}
}