/******************************************************************************* * Copyright (c) 2005-2011, G. Weirich and Elexis * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * G. Weirich - initial implementation * *******************************************************************************/ package ch.rgw.compress; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import ch.rgw.io.BitInputStream; import ch.rgw.io.BitOutputStream; import ch.rgw.tools.ExHandler; /** * GLZ is a simple implementation of a variation of the lempel-ziv algorithm. It compresses much * less than zip, but is fast enough to be implemented in pure Java. (I did program it in assembler * originally in the early 90's though; this alorithm programmed in assembler and running on a 80386 * of those days was even slower than running on a computer of today in this java programm, which is * essentially a transscript of the original Assembler source) * * @author Gerry */ public class GLZ { public static String Version(){ return "1.0.0"; } private static final int LZBUFFSIZE = 0x8cf2; private static final short ofb = 4; private int[] buff1, buff2, buff3; int LZCode, MaxCode, bitcount; public GLZ(){ buff1 = new int[LZBUFFSIZE]; buff2 = new int[LZBUFFSIZE]; buff3 = new int[LZBUFFSIZE / 2]; } public int compress(InputStream in, OutputStream o) throws IOException{ BitOutputStream bos = new BitOutputStream(o); int size = 0; fillbuffer(); int Prev = in.read(); if (Prev == -1) { Prev = 0x100; } while (in.available() > 0) { int Act = in.read(); size++; int hash = (Act << 6) ^ Prev; int cnt1; if (hash != 0) { cnt1 = (LZBUFFSIZE / 2) - hash; } else { cnt1 = 1; } hash = findHash(hash, Prev, Act, cnt1); // find code or empty place in hashtable if (buff1[hash] != -1) // code found { Prev = buff1[hash]; // use it continue; } buff1[hash] = LZCode++; // empty place found buff2[hash] = Prev; buff3[hash] = (byte) Act; bos.pushbits(Prev, bitcount); // write new code Prev = Act; if (LZCode > 0x3ffe) // dictionary exhausted { bos.pushbits(0x102, bitcount); // new dictionary fillbuffer(); continue; } if (LZCode > MaxCode) // codespace exhausted { bos.pushbits(0x101, bitcount++); // extend code length MaxCode <<= 1; MaxCode |= 1; } } /* WHILE */ bos.pushbits(Prev, bitcount); // last code bos.pushbits(0x100, bitcount); // eof bos.pushbits(0, bitcount); // fill bos.flush(); // write rest return size + 1; } private int findHash(int hash, int Prev, int Act, int cnt1){ while (buff1[hash] != -1) { if ((buff2[hash] == Prev) && (buff3[hash] == (byte) Act)) { break; // found } hash -= cnt1; // rehash if (hash < 0) hash += (LZBUFFSIZE / 2); } return hash; } private void fillbuffer(){ for (int i = 0; i < buff1.length; i++) { buff1[i] = -1; } LZCode = 0x103; bitcount = 9; MaxCode = 0x1ff; } public void expand(InputStream i, OutputStream o) throws IOException{ int Prev, Prev1, Act, dx; BitInputStream bis = new BitInputStream(i); newdic: while (true) { LZCode = 0x103; bitcount = 9; MaxCode = 0x1ff; Prev1 = bis.pullBits(bitcount); if (Prev1 == 0x100) { o.flush(); return; } Act = Prev = Prev1; o.write(Act); while (Prev1 != 0x100) { dx = Prev1 = (short) bis.pullBits(bitcount); switch (Prev1) { case 0x101: bitcount++; // Extend codesize and fall thru case 0x100: continue; // EOF; will break at "while" case 0x102: continue newdic; // Begin new dictionary default: int si = ofb; if (Prev1 >= LZCode) { buff1[si++] = Act; dx = Prev; } while (dx > 0xff) { buff1[si++] = buff3[dx]; dx = buff2[dx]; } buff1[si] = Act = dx; do { o.write(buff1[si--]); } while (si >= ofb); buff2[LZCode] = Prev; buff3[LZCode++] = (byte) Act; Prev = Prev1; } // case } // while; break; // EOF (0x100) received } // while(true) o.flush(); } public byte[] encodeString(String input){ byte[] b = input.getBytes(); ByteArrayInputStream in = new ByteArrayInputStream(b); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { compress(in, out); return out.toByteArray(); } catch (Exception ex) { ExHandler.handle(ex); return null; } } public String decodeString(byte[] i){ ByteArrayInputStream in = new ByteArrayInputStream(i); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { expand(in, out); byte[] cp = out.toByteArray(); return new String(cp); } catch (Exception ex) { ExHandler.handle(ex); return null; } } }