Skip to content

Instantly share code, notes, and snippets.

@AndyBowes
Created February 15, 2016 10:58
Show Gist options
  • Save AndyBowes/9a7bb672aedb797c562f to your computer and use it in GitHub Desktop.
Save AndyBowes/9a7bb672aedb797c562f to your computer and use it in GitHub Desktop.
Read LZW Compressed Stream to get 12-bit numbers as Code Points.
package org.nhs.mesh.util.lzw;
import java.io.IOException;
import java.io.InputStream;
/**
* Wrapper around an Input Stream which allows the Code Points to be read sequentially from
* an LZW compressed file.
*
* @author Andy Bowes
*/
public class LZWCodePointStream {
private final int CODE_POINT_SIZE = 12;
private final InputStream wrappedStream;
private int readBitCount = 0;
private int bitBuffer = 0;
public LZWCodePointStream(InputStream wrappedStream) {
super();
this.wrappedStream = wrappedStream;
}
public void close() throws IOException {
if (wrappedStream != null){
wrappedStream.close();
}
}
public int nextCodePoint() throws IOException{
while (readBitCount < CODE_POINT_SIZE){
int nextByte = wrappedStream.read();
if (nextByte < 0){
return -1;
}
readBitCount += 8;
bitBuffer <<= 8;
bitBuffer += nextByte;
}
int returnValue = (bitBuffer >> (readBitCount-CODE_POINT_SIZE)) & 0xFFF;
readBitCount -= CODE_POINT_SIZE;
bitBuffer = (readBitCount == 0) ? 0 : bitBuffer & 0xF;
return returnValue;
}
}
package org.nhs.mesh.util.lzw;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of Input Stream that performs LZW Decompression
*
*
* @author Andy Bowes
*/
public class LZWInputStream extends InputStream {
private final int INITIAL_DICT_SIZE = 256;
private final int LAST_CODE_POINT = 4095;
private final LZWCodePointStream codePointStream;
private final Map<Integer, String> dictionary = new HashMap<Integer, String>();
private int dictSize = INITIAL_DICT_SIZE;
private final StringBuffer outputBuffer = new StringBuffer();
private String w;
public LZWInputStream(InputStream inputStream) throws IOException{
codePointStream = new LZWCodePointStream(inputStream);
for (int i=0; i < INITIAL_DICT_SIZE; i++){
dictionary.put(i, "" + (char)i);
}
dictSize = INITIAL_DICT_SIZE;
w = "" + dictionary.get(codePointStream.nextCodePoint());
outputBuffer.append(w);
}
@Override
/**
* Return the next byte in the decompressed output stream.
*
*/
public int read() throws IOException {
if (outputBuffer.length() == 0){
String entry;
int nextCodePoint = codePointStream.nextCodePoint();
if (nextCodePoint <= 0){
return -1;
}
if (nextCodePoint == LAST_CODE_POINT){
entry = w;
w = "";
} else if (dictionary.containsKey(nextCodePoint)){
entry = dictionary.get(nextCodePoint);
} else if (nextCodePoint == dictSize ){
entry = w + w.charAt(0);
} else {
throw new IllegalArgumentException("Bad compressed CodePoint: " + nextCodePoint);
}
outputBuffer.append(entry);
dictionary.put(dictSize++, w + entry.charAt(0));
w = entry;
}
char firstByte = outputBuffer.charAt(0);
outputBuffer.deleteCharAt(0); // TBD - Is this the best method to truncate the buffer;
return firstByte;
}
@Override
public synchronized void reset() throws IOException {
throw new java.lang.UnsupportedOperationException("LZWInputStream: reset() is not supported");
}
@Override
public long skip(long arg0) throws IOException {
throw new java.lang.UnsupportedOperationException("LZWInputStream: skip() is not supported");
}
@Override
public void close() throws IOException {
this.codePointStream.close();
super.close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment