package net.i2p.util; //import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.zip.Deflater; import java.util.concurrent.LinkedBlockingQueue; import net.i2p.data.DataHelper; /** * Provide a cache of reusable GZIP streams, each handling up to 40 KB output without * expansion. * * This compresses to memory only. Retrieve the compressed data with getData(). * There is no facility to compress to an output stream. * * Do NOT use this for compression of unlimited-size data, as it will * expand, but never release, the BAOS memory buffer. */ public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream { // Apache Harmony 5.0M13 Deflater doesn't work after reset() // Neither does Android // attempt to fix #1915 //private static final boolean ENABLE_CACHING = !(SystemVersion.isApache() || // SystemVersion.isAndroid()); private static final boolean ENABLE_CACHING = false; private static final LinkedBlockingQueue<ReusableGZIPOutputStream> _available; static { if (ENABLE_CACHING) _available = new LinkedBlockingQueue<ReusableGZIPOutputStream>(16); else _available = null; } /** * Pull a cached instance */ public static ReusableGZIPOutputStream acquire() { ReusableGZIPOutputStream rv = null; if (ENABLE_CACHING) rv = _available.poll(); if (rv == null) { rv = new ReusableGZIPOutputStream(); } return rv; } /** * Release an instance back into the cache (this will discard any * state) */ public static void release(ReusableGZIPOutputStream out) { out.reset(); if (ENABLE_CACHING) _available.offer(out); } private final ByteArrayOutputStream _buffer; private ReusableGZIPOutputStream() { super(new ByteArrayOutputStream(DataHelper.MAX_UNCOMPRESSED)); _buffer = (ByteArrayOutputStream)out; } /** clear the data so we can start again afresh */ @Override public void reset() { super.reset(); _buffer.reset(); def.setLevel(Deflater.BEST_COMPRESSION); } public void setLevel(int level) { def.setLevel(level); } /** pull the contents of the stream written */ public byte[] getData() { return _buffer.toByteArray(); } /** * Clear the cache. * @since 0.9.21 */ public static void clearCache() { if (_available != null) _available.clear(); } /****** public static void main(String args[]) { try { for (int i = 0; i < 2; i++) test(); for (int i = 0; i < 64*1024; i++) { if (!test(i)) break; } } catch (Exception e) { e.printStackTrace(); } try { Thread.sleep(10*1000); } catch (InterruptedException ie){} System.out.println("After all tests are complete..."); } private static void test() { byte b[] = "hi, how are you today?".getBytes(); try { ReusableGZIPOutputStream o = ReusableGZIPOutputStream.acquire(); o.write(b); o.finish(); o.flush(); byte compressed[] = o.getData(); ReusableGZIPOutputStream.release(o); ResettableGZIPInputStream in = new ResettableGZIPInputStream(new java.io.ByteArrayInputStream(compressed)); byte rv[] = new byte[128]; int read = in.read(rv); if (!DataHelper.eq(rv, 0, b, 0, b.length)) throw new RuntimeException("foo, read=" + read); else System.out.println("match, w00t"); } catch (Exception e) { e.printStackTrace(); } } private static boolean test(int size) { byte b[] = new byte[size]; RandomSource.getInstance().nextBytes(b); try { ReusableGZIPOutputStream o = ReusableGZIPOutputStream.acquire(); o.write(b); o.finish(); o.flush(); byte compressed[] = o.getData(); ReusableGZIPOutputStream.release(o); ResettableGZIPInputStream in = new ResettableGZIPInputStream(new java.io.ByteArrayInputStream(compressed)); ByteArrayOutputStream baos2 = new ByteArrayOutputStream(size); byte rbuf[] = new byte[128]; while (true) { int read = in.read(rbuf); if (read == -1) break; baos2.write(rbuf, 0, read); } byte rv[] = baos2.toByteArray(); if (!DataHelper.eq(rv, 0, b, 0, b.length)) { throw new RuntimeException("foo, read=" + rv.length); } else { System.out.println("match, w00t @ " + size); return true; } } catch (Exception e) { System.out.println("Error on size=" + size + ": " + e.getMessage()); e.printStackTrace(); return false; } } *****/ }