-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLZW.ts
146 lines (118 loc) · 4.71 KB
/
LZW.ts
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
import LZWDictionary from './LZWDictionary';
export class LZW {
//Extracts a series of integers from the byte array
static parseByteStreamToIndexes(
binFile: { getUByte: () => number },
remainingCodedBytes: number,
uByteMode: number
) {
const parsedIndexes = []; //ArrayList<Integer> parsedIndexes = new ArrayList<Integer>();
let usableBits = 0,
usableBitCount = 0,
indicatorLength = 1 /* to increment with ++; rule is that 8+indicatorLength must be <= uByteMode, otherwise reset */,
indicatorFlag = 0x001 /* to increment with <<=1 followed by |= 1 */,
nextThreshold = 0x0100 /*256*/ /* to increment with <<=1, or *=2 */,
decodedCounter = 0,
index = 0;
while (remainingCodedBytes > 0) {
/* get enough coded bits to work with */
while (usableBitCount < 8 + indicatorLength) {
usableBits |= binFile.getUByte() << usableBitCount;
remainingCodedBytes--;
usableBitCount += 8;
}
/* decode bytes and indicators */
while (usableBitCount >= 8 + indicatorLength) {
//Builds out a bit screen that is of size 8 + the number of indicator bits and ors it with current set of usable bits
index = usableBits & (((indicatorFlag << 8) & 0xff00) | 0x00ff);
usableBits >>= 8 + indicatorLength; //Right-shift out the used bits
usableBitCount -= 8 + indicatorLength; //Decrement the usableBitCount by the number of bits used
decodedCounter++; //Keep track of how many things we have decoded to ensure threshold isn't reached
if (decodedCounter == nextThreshold) {
//If threshold is reached...
decodedCounter = 0;
indicatorLength += 1; // to increment with ++; rule is that 8+indicatorLength must be <= uByteMode, otherwise reset
indicatorFlag <<= 1;
indicatorFlag |= 1; // left-shift bitmask and add another 1 (e.g. 0001 > 0011 > 0111)
nextThreshold <<= 1; //Double the next threshold
if (8 + indicatorLength > uByteMode) {
decodedCounter = 0;
indicatorLength = 1;
indicatorFlag = 0x001;
nextThreshold = 0x0100 /*256*/;
}
}
parsedIndexes.push(index);
}
}
return parsedIndexes;
}
//Decodes the integers into byte-level data using a dictionary look-up
static decode(inputData: any, dicIndexMaxBits: number) {
dicIndexMaxBits = dicIndexMaxBits || 0x0b;
let codedData = inputData; //stringToNumArray(inputData); //Convert string to an array of numbers
const decodedData: number[] = [];
while (codedData.length > 0) {
const dic = new LZWDictionary(dicIndexMaxBits),
plainData = [codedData[0]];
let oldCode = codedData[0],
buffer = dic.getEntry(oldCode),
character = oldCode,
newCode;
let i = 0;
while (!dic.isFull() && i++ < codedData.length - 1) {
newCode = codedData[i];
let p: number[] | null = [];
if (newCode >= dic.getCurPos()) {
p = dic.getEntry(oldCode);
if (p === null) {
console.error(
`No dictionary entry in LZW special case: oldCode=${oldCode}; newCode=${newCode}; i=${i}; buffer=${buffer}`
);
}
p = [...(p ?? []), character]; //create a copy of the array p to not overwrite the dic entry and append character=
} else {
// Get entry p for coded index codedData[i]
p = dic.getEntry(codedData[i]);
}
plainData.push.apply(plainData, p as number[]); //Merge entry p onto the plainData output
character = (p as number[])[0]; // Get first char of p
dic.addEntry((buffer as number[]).concat(character)); // Add new entry to dictionary as buffer+c
oldCode = newCode;
buffer = dic.getEntry(oldCode);
}
decodedData.push.apply(decodedData, plainData); //First append plainData to decodedData
codedData = codedData.slice(i + 1); //Second truncate codedData to keep only undecoded stuff before continuing the loop
}
return decodedData;
}
//Run-length decodes around special character 0x90
static RLEDecode(byteArr: number[]) {
const opt = [];
let lastChr = 0,
isFlag = false;
for (let i = 0; i < byteArr.length; i++) {
const chr = byteArr[i];
if (isFlag) {
if (chr == 0x00) {
lastChr = 0x90;
opt.push(0x90);
} else {
for (let j = 0; j < chr - 1; j++) {
opt.push(lastChr);
}
}
isFlag = false;
} else {
if (chr == 0x90) {
isFlag = true;
} else {
opt.push(chr);
lastChr = chr;
}
}
}
return opt;
}
}
export default LZW;