1 |
2 |
habicht |
/**************************************************************************************************************
|
2 |
|
|
*
|
3 |
|
|
* L Z R W 1 E N C O D E R C O R E
|
4 |
|
|
*
|
5 |
|
|
* A high throughput loss less data compression core.
|
6 |
|
|
*
|
7 |
|
|
* Copyright 2012-2013 Lukas Schrittwieser (LS)
|
8 |
|
|
*
|
9 |
|
|
* This program is free software: you can redistribute it and/or modify
|
10 |
|
|
* it under the terms of the GNU General Public License as published by
|
11 |
|
|
* the Free Software Foundation, either version 2 of the License, or
|
12 |
|
|
* (at your option) any later version.
|
13 |
|
|
*
|
14 |
|
|
* This program is distributed in the hope that it will be useful,
|
15 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17 |
|
|
* GNU General Public License for more details.
|
18 |
|
|
*
|
19 |
|
|
* You should have received a copy of the GNU General Public License
|
20 |
|
|
* along with this program; if not, write to the Free Software
|
21 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
22 |
|
|
* Or see <http://www.gnu.org/licenses/>
|
23 |
|
|
*
|
24 |
|
|
***************************************************************************************************************
|
25 |
|
|
*
|
26 |
|
|
* Change Log:
|
27 |
|
|
*
|
28 |
|
|
* Version 1.0 - 2013/04/05 - LS
|
29 |
|
|
* released
|
30 |
|
|
*
|
31 |
|
|
***************************************************************************************************************
|
32 |
|
|
*
|
33 |
|
|
* This is an example decoder (decompressor) for which can be used to decompress data encoded by the core.
|
34 |
|
|
*
|
35 |
|
|
***************************************************************************************************************/
|
36 |
|
|
|
37 |
|
|
package org.sump.analyzer;
|
38 |
|
|
import java.io.IOException;
|
39 |
|
|
|
40 |
|
|
class LZRW1Decoder
|
41 |
|
|
{
|
42 |
|
|
final static boolean DEBUG = false; // set this to true for detailed debug output to the console
|
43 |
|
|
|
44 |
|
|
final static int HISTORY_LEN = 4096; // buffer length in bytes
|
45 |
|
|
final static int LOOK_AHEAD_LEN = 16; // look ahead buffer length in bytes
|
46 |
|
|
final static int HASH_BIT_LEN = 11; // number of bits for the index in the hash table
|
47 |
|
|
|
48 |
|
|
/* configer the number of items (offset-length-tuples or literals) per frame */
|
49 |
|
|
public final int ITEMS_PER_FRAME = 8;
|
50 |
|
|
|
51 |
|
|
public LZRW1Decoder ()
|
52 |
|
|
{
|
53 |
|
|
// allocate the history buffer
|
54 |
|
|
buffer = new int[HISTORY_LEN];
|
55 |
|
|
validBufferLen = 0;
|
56 |
|
|
histWrInd = 0;
|
57 |
|
|
flags = 0;
|
58 |
|
|
frameItemCnt = 0;
|
59 |
|
|
offset = -1;
|
60 |
|
|
copyCnt = 0;
|
61 |
|
|
readIndex = 0;
|
62 |
|
|
endOfData = false;
|
63 |
|
|
if (DEBUG)
|
64 |
|
|
System.out.println("LZRW1Decoder constructed");
|
65 |
|
|
}
|
66 |
|
|
|
67 |
|
|
/**
|
68 |
|
|
* Loads a block of compressed data into the module. Decoding will start at a given offset.
|
69 |
|
|
* @param cd The data to be decompressed
|
70 |
|
|
* @param o Index of the first byte of encoded data in cd
|
71 |
|
|
*/
|
72 |
|
|
void loadCompressedData (byte [] cd, int o)
|
73 |
|
|
{
|
74 |
|
|
compressed = cd;
|
75 |
|
|
// reset all internal states for a new stream
|
76 |
|
|
validBufferLen = 0;
|
77 |
|
|
histWrInd = 0;
|
78 |
|
|
flags = 0;
|
79 |
|
|
frameItemCnt = 0;
|
80 |
|
|
offset = -1;
|
81 |
|
|
copyCnt = 0;
|
82 |
|
|
readIndex = o;
|
83 |
|
|
endOfData = false;
|
84 |
|
|
if (DEBUG)
|
85 |
|
|
System.out.println("compressed data loaded");
|
86 |
|
|
}
|
87 |
|
|
|
88 |
|
|
/**
|
89 |
|
|
* Decompresses one byte of data stored in source block and returns it. If no source data is available an
|
90 |
|
|
* exception will be created. If the end-of-data symbol is encountered -1 will be returned. If the compressed
|
91 |
|
|
* data ends without an end-of-data symbol an exception will be created as well. Any data after an end-of-data
|
92 |
|
|
* symbol will be ignored and can not be decoded. However the method may still be called, it will keep returning
|
93 |
|
|
* -1 to indicated the end of the decoded data.
|
94 |
|
|
* @return The next byte in the stream of decoded data. -1 if end-of-data was reached
|
95 |
|
|
* @throws IOException if an error is found in the encoded data
|
96 |
|
|
*/
|
97 |
|
|
int decodeNextByte () throws IOException
|
98 |
|
|
{
|
99 |
|
|
int ch = 0; // decoded char (to be returned)
|
100 |
|
|
|
101 |
|
|
// make sure that we have data
|
102 |
|
|
if (compressed == null)
|
103 |
|
|
throw new IOException("no compressed data loaded");
|
104 |
|
|
|
105 |
|
|
if (endOfData)
|
106 |
|
|
return -1;
|
107 |
|
|
|
108 |
|
|
// check wether we can copy data from the stream history (due to a offset/length pair found earlier)
|
109 |
|
|
if (copyCnt > 0)
|
110 |
|
|
{
|
111 |
|
|
ch = readFromHistory();
|
112 |
|
|
copyCnt--;
|
113 |
|
|
// the byte is decoded, it becomes part of the stream history
|
114 |
|
|
insertIntoHistBuf(ch);
|
115 |
|
|
return ch;
|
116 |
|
|
}
|
117 |
|
|
|
118 |
|
|
// get next byte from compressed data stream
|
119 |
|
|
if (readIndex >= compressed.length)
|
120 |
|
|
throw new IOException("encountered end of compressed data without end-of-data symbol (data corrupt)");
|
121 |
|
|
ch = compressed[readIndex++];
|
122 |
|
|
|
123 |
|
|
// load a new header if necessary
|
124 |
|
|
if (frameItemCnt==0)
|
125 |
|
|
{
|
126 |
|
|
flags = ch&0xff;
|
127 |
|
|
if (DEBUG)
|
128 |
|
|
System.out.printf(" found header: %02x\n",flags);
|
129 |
|
|
frameItemCnt = 8;
|
130 |
|
|
// load the next byte
|
131 |
|
|
if (readIndex >= compressed.length)
|
132 |
|
|
throw new IOException("encountered end of compressed data without end-of-data symbol (data corrupt)");
|
133 |
|
|
ch = compressed[readIndex++];
|
134 |
|
|
}
|
135 |
|
|
|
136 |
|
|
// check whether we have a pair or not
|
137 |
|
|
if ((flags&0x01) == 1)
|
138 |
|
|
{
|
139 |
|
|
// this is a length-offset pair, load the second byte
|
140 |
|
|
if (readIndex >= compressed.length)
|
141 |
|
|
throw new IOException("encountered end of compressed data without end-of-data symbol (data corrupt)");
|
142 |
|
|
int lowByte = compressed[readIndex++];
|
143 |
|
|
int highByte = ch;
|
144 |
|
|
// decode the pair into offset and length
|
145 |
|
|
copyCnt = (highByte>>4) & 0x0f;
|
146 |
|
|
offset = ((highByte<<8)&0x0f00) + (lowByte&0xff);
|
147 |
|
|
// check for the end-of-data symbol
|
148 |
|
|
if ((offset==0) && (copyCnt==0))
|
149 |
|
|
{
|
150 |
|
|
if (DEBUG)
|
151 |
|
|
System.out.println(" found end of data symbol");
|
152 |
|
|
endOfData = true;
|
153 |
|
|
// check that there is no data left in the encoded input
|
154 |
|
|
/*if (readIndex < compressed.length)
|
155 |
|
|
throw new IOException("compressed data after end-of-data symbol (data corrupt) "+readIndex+" "+compressed.length);*/
|
156 |
|
|
return -1;
|
157 |
|
|
}
|
158 |
|
|
copyCnt++; // Note: the encoder saves the number of byte to be copied minus 1 to save some space (2 means copy 3 bytes, and so on)
|
159 |
|
|
if (DEBUG)
|
160 |
|
|
System.out.println("len:"+copyCnt+" off:"+offset);
|
161 |
|
|
// load the first copy byte from the history buffer
|
162 |
|
|
ch = readFromHistory();
|
163 |
|
|
copyCnt--; // one byte copied
|
164 |
|
|
// this byte is decoded, it becomes part of the stream history
|
165 |
|
|
insertIntoHistBuf(ch);
|
166 |
|
|
}
|
167 |
|
|
else
|
168 |
|
|
{
|
169 |
|
|
// convert the signed char (-128..127) to an unsigned value
|
170 |
|
|
//if (ch < 0)
|
171 |
|
|
// ch += 256;
|
172 |
|
|
ch &= 0xff;
|
173 |
|
|
|
174 |
|
|
// this is a literal
|
175 |
|
|
if (DEBUG)
|
176 |
|
|
System.out.printf("lit: '%c'=%02x\n",ch,ch);
|
177 |
|
|
// save this byte in the history buffer
|
178 |
|
|
insertIntoHistBuf(ch);
|
179 |
|
|
}
|
180 |
|
|
|
181 |
|
|
// shift header for next byte
|
182 |
|
|
frameItemCnt--;
|
183 |
|
|
flags >>= 1;
|
184 |
|
|
return ch;
|
185 |
|
|
}
|
186 |
|
|
|
187 |
|
|
/**
|
188 |
|
|
* read one byte from the history buffer from the position defined by offset.
|
189 |
|
|
*/
|
190 |
|
|
private int readFromHistory() throws IOException
|
191 |
|
|
{
|
192 |
|
|
if (offset >= validBufferLen)
|
193 |
|
|
throw new IOException("offset is pointing to a location before the beginning of the data (data corrupt)");
|
194 |
|
|
// calculate index (move back offset bytes) from last one (-1 because histWrInd point to next free location)
|
195 |
|
|
int ind = histWrInd-offset-1;
|
196 |
|
|
if (ind < 0) // wrap around (ring buffer)
|
197 |
|
|
ind += HISTORY_LEN;
|
198 |
|
|
if (ind < 0)
|
199 |
|
|
// index is still negative, this is an error in the encoded data
|
200 |
|
|
throw new IOException("Offset wraps more than once in history buffer (data corrupt)");
|
201 |
|
|
// load the byte from the histroy buffer
|
202 |
|
|
return buffer[ind];
|
203 |
|
|
}
|
204 |
|
|
|
205 |
|
|
private void insertIntoHistBuf (int ch)
|
206 |
|
|
{
|
207 |
|
|
buffer[histWrInd++] = ch;
|
208 |
|
|
if (histWrInd >= buffer.length)
|
209 |
|
|
histWrInd = 0;
|
210 |
|
|
// count the number of valid bytes in the histroy buffer
|
211 |
|
|
if (validBufferLen < HISTORY_LEN)
|
212 |
|
|
validBufferLen++;
|
213 |
|
|
}
|
214 |
|
|
|
215 |
|
|
int [] buffer;
|
216 |
|
|
int validBufferLen;
|
217 |
|
|
int histWrInd;
|
218 |
|
|
int flags;
|
219 |
|
|
int frameItemCnt;
|
220 |
|
|
int offset;
|
221 |
|
|
int copyCnt;
|
222 |
|
|
byte [] compressed; // holds the compressed data
|
223 |
|
|
int readIndex;
|
224 |
|
|
boolean endOfData;
|
225 |
|
|
}
|
226 |
|
|
|
227 |
|
|
|