-
Notifications
You must be signed in to change notification settings - Fork 1
/
LZstr.js
209 lines (183 loc) · 6.33 KB
/
LZstr.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
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
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.6
// prefer inline minified functions from LZstr.min.js
// compress string
Lc = function (uncompressed) {
// private property
var bitsPerChar = 16,
d256 = 256,
fromCharCode = String.fromCharCode,
StringStream_o = [],
StringStream_d = [],
StringStream_v = 0,
StringStream_p = 0,
StringStream_b = bitsPerChar,
j = 0, value = 0,
dictionary = {},
freshNode = true,
c = 0,
node, // first node will always be initialised like this.
nextNode,
dictSize = 2,
numBitsMask = 0b100,
forceBreak = true,
StringStream_s = (value, numBitsMask) => { //streamBits
StringStream_o.push([value, numBitsMask]);
for (var i = 0; numBitsMask >>= 1; i++) {
// shifting has precedence over bitmasking
StringStream_v = value >> i & 1 | StringStream_v << 1;
if (++StringStream_p === StringStream_b) {
StringStream_p = 0;
StringStream_d.push(fromCharCode(StringStream_v));
StringStream_v = 0;
}
}
},
newSymbol = () => {
// Prefix+charCode does not exist in trie yet.
// We write the prefix to the bitstream, and add
// the new charCode to the dictionary if it's new
// Then we set `node` to the root node matching
// the charCode.
if (freshNode) {
// Prefix is a freshly added character token,
// which was already written to the bitstream
freshNode = false;
} else {
// write out the current prefix token
StringStream_s(node.v, numBitsMask);
}
// Is the new charCode a new character
// that needs to be stored at the root?
if (dictionary[c] == undefined) {
// increase token bitlength if necessary
if (++dictSize >= numBitsMask) {
numBitsMask <<= 1;
}
// insert "new 8/16 bit charCode" token,
// see comments above for explanation
value = c < 256 ? 0 : 1;
StringStream_s(value, numBitsMask);
StringStream_s(c, value ? 0b10000000000000000 : 0b100000000);
dictionary[c] = { v: dictSize, d: {} };
// Note of that we already wrote
// the charCode token to the bitstream
freshNode = true;
}
// increase token bitlength if necessary
if (++dictSize >= numBitsMask) {
numBitsMask <<= 1;
}
};
// The first charCode is guaranteed to be new
c = uncompressed.charCodeAt(0);
newSymbol();
numBitsMask = 4;
--dictSize;
node = dictionary[c];
for (j = 1; j < uncompressed.length; j++) {
c = uncompressed.charCodeAt(j);
// does the new charCode match an existing prefix?
nextNode = node.d[c];
if (nextNode) {
// continue with next prefix
node = nextNode;
} else {
// Is the new charCode a new character
// that needs to be stored at the root?
newSymbol();
// splitting magic - separate on comma leading to big gain for JSON!
node.d[c] = { v: dictSize, d: {} };
// set node to first charCode of new prefix
node = dictionary[c];
}
}
// Is c a new character?
newSymbol();
//}
// Mark the end of the stream
StringStream_s(2, numBitsMask);
// Flush the last char
StringStream_v <<= StringStream_b - StringStream_p;
StringStream_d.push(fromCharCode(StringStream_v));
return StringStream_d.join('');
}
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.6
// decompress compressed string
Ld = function (compressed) {
var fromCharCode = String.fromCharCode,
length = compressed.length,
getNextValue = compressed.charCodeAt.bind(compressed),
resetBits = 16,
empty = '',
dictionary = [empty, empty, empty],
enlargeIn = 4,
dictSize = 4,
numBits = 3,
entry,
result = [],
bits = 0,
maxpower = 2,
power = 0,
c,
data_val = getNextValue(0),
data_position = resetBits,
data_index = 1;
// breakCode=44;
// slightly decreases decompression but strongly decreases size
var getBits = () => {
bits = power = 0;
while (power != maxpower) {
// shifting has precedence over bitmasking
bits += (data_val >> --data_position & 1) << power++;
if (data_position == 0) {
data_position = resetBits;
data_val = getNextValue(data_index++);
}
}
};
// Get first token, guaranteed to be either
// a new character token (8 or 16 bits)
// or end of stream token.
getBits();
// else, get character
maxpower = bits * 8 + 8;
getBits();
c = fromCharCode(bits);
dictionary[3] = c;
result.push(c);
// read rest of string
while (data_index <= length) {
// read out next token
maxpower = numBits;
getBits();
// 0 or 1 implies new character token
if (bits < 2) {
maxpower = (8 + 8 * bits);
getBits();
dictionary[dictSize] = fromCharCode(bits);
bits = dictSize++;
if (--enlargeIn == 0) {
enlargeIn = 1 << numBits++;
}
} else if (bits == 2) {
// end of stream token
return result.join(empty);
}
entry = bits < dictionary.length ? dictionary[bits] : c + c.charAt(0);
result.push(entry);
// if (breakCode!==c.charCodeAt(0) && breakCode===entry.charCodeAt(entry.length-1)) {
// enlargeIn++;
// } else {
// Add c+entry[0] to the dictionary.
dictionary[dictSize++] = c + entry.charAt(0);
// }
c = entry;
if (--enlargeIn == 0) {
enlargeIn = 1 << numBits++;
}
}
}