-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathes6Compress.js
163 lines (152 loc) · 4.49 KB
/
es6Compress.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
const compress = input => _compress(input, 15, a => f(a + 32)) + ' '
const f = String.fromCharCode
const _compress = (uncompressed, bitsPerChar, getCharFromInt) => {
if (uncompressed === null) return ''
let counter, marker
let dictionary = {}
let dictionaryToCreate = {}
let characterPair = ''
let prevCharacter = ''
let enlargeIn = 2 // Compensate for the first entry which should not count
let dictSize = 3
let numBits = 2
let data = []
let value = 0
let position = 0
// 1. Initialize the dictionary to contain all strings of length one.
// 2. Find the longest string W in the dictionary that matches the current input.
// 3. Emit the dictionary index for W to output and remove W from the input.
// 4. Add W followed by the next symbol in the input to the dictionary.
// 5. Go to Step 2.
const doTheThingAndShiftMarker = () => {
updateAndPush(mergeBits(shiftBitRight(value), isOdd(marker)))
marker = shiftBitLeft(marker)
}
const doTheThingAndShiftCounter = () => {
updateAndPush(mergeBits(shiftBitRight(value), isOdd(counter)))
counter = shiftBitLeft(counter)
}
const doTheThingAndResetCounter = () => {
updateAndPush(mergeBits(shiftBitRight(value), counter))
counter = 0
}
const doTheThingAndResetMarker = () => {
updateAndPush(mergeBits(shiftBitRight(value), isOdd(marker)))
marker = 0
}
const do8BitStuff = p => {
Array(numBits)
.fill()
.forEach(() => updateAndPush(shiftBitRight(value)))
counter = p.charCodeAt(0)
Array(8)
.fill()
.forEach(() => doTheThingAndShiftCounter())
}
const updateAndPush = v => {
value = v
if (position === bitsPerChar - 1) {
position = 0
data.push(getCharFromInt(value))
value = 0
} else {
position++
}
}
const engourge = () => {
enlargeIn--
if (enlargeIn === 0) {
enlargeIn = Math.pow(2, numBits)
numBits++
}
}
const setMarker = m => {
marker = m
Array(numBits)
.fill()
.forEach(() => doTheThingAndShiftMarker())
}
const setCounter = m => {
counter = m
Array(numBits)
.fill()
.forEach(() => doTheThingAndShiftCounter())
}
const flush = () => {
while (true) {
value = shiftBitRight(value)
if (position === bitsPerChar - 1) {
data.push(getCharFromInt(value))
break
} else position++
}
}
const eachCharacter = [...uncompressed]
eachCharacter.forEach(nextCharacter => {
// when nextCharacter isnt in dictionary prepare two dictionary elements
if (!has(dictionary, nextCharacter)) {
dictionary[nextCharacter] = dictSize++
dictionaryToCreate[nextCharacter] = true
}
characterPair = prevCharacter + nextCharacter
if (has(dictionary, characterPair)) {
prevCharacter = characterPair
return
}
// when prevCharacter isnt in dictionaryToCreate set counter to dictionary element
if (!has(dictionaryToCreate, prevCharacter)) setCounter(dictionary[prevCharacter])
else {
if (is8Bit(prevCharacter)) do8BitStuff(prevCharacter)
else {
counter = 1
Array(numBits)
.fill()
.forEach(() => doTheThingAndResetCounter())
counter = prevCharacter.charCodeAt(0)
Array(16)
.fill()
.forEach(() => doTheThingAndShiftCounter())
}
engourge()
delete dictionaryToCreate[prevCharacter]
}
engourge()
// Add characterPair to the dictionary.
dictionary[characterPair] = dictSize++
prevCharacter = nextCharacter
})
// Output the code for prevCharacter.
if (prevCharacter) {
if (!has(dictionaryToCreate, prevCharacter)) {
setMarker(dictionary[prevCharacter])
} else {
if (is8Bit(prevCharacter)) do8BitStuff(prevCharacter)
else {
// test not hitting here
counter = 1
Array(numBits)
.fill()
.forEach(() => doTheThingAndResetMarker())
marker = prevCharacter.charCodeAt(0)
Array(16)
.fill()
.forEach(() => doTheThingAndShiftMarker())
}
engourge()
delete dictionaryToCreate[prevCharacter]
}
engourge()
}
// Mark the end of the stream
setMarker(2)
// Flush the last char
flush()
return data.join``
}
const has = (x, y) => Object.prototype.hasOwnProperty.call(x, y)
const isOdd = n => n & 1
const shiftBitRight = n => n << 1
const shiftBitLeft = n => n >> 1
const mergeBits = (n, m) => n | m
const is8Bit = c => c.charCodeAt(0) < 256
module.exports.compressUTF16 = compress