/* * JLibs: Common Utilities for Java * Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package jlibs.nio.filters; import jlibs.nio.Output; import jlibs.nio.OutputFilter; import jlibs.nio.Reactor; import jlibs.nio.util.NIOUtil; import java.io.IOException; import java.nio.ByteBuffer; import java.util.zip.Deflater; /** * @author Santhosh Kumar Tekuri */ public class DeflaterOutput extends OutputFilter{ protected Deflater deflater; private ByteBuffer buffer; private ByteBuffer tmpBuffer; public DeflaterOutput(Output peer){ this(new Deflater(), peer); } public DeflaterOutput(Deflater deflater, Output peer){ super(peer); this.deflater = deflater; buffer = Reactor.current().allocator.allocateHeap(); addHeader(buffer); buffer.flip(); } protected void addHeader(ByteBuffer buffer){} protected boolean trailerAdded; protected void addTrailer(ByteBuffer buffer){} protected void setInput(byte bytes[], int offset, int length){ deflater.setInput(bytes, offset, length); } @Override public int write(ByteBuffer src) throws IOException{ ensureOpen(); deflate(false); if(src.hasRemaining() && deflater.needsInput()){ int wrote; if(src.hasArray()){ wrote = src.remaining(); setInput(src.array(), src.arrayOffset()+src.position(), wrote); src.position(src.limit()); }else{ if(tmpBuffer==null) tmpBuffer = Reactor.current().allocator.allocateHeap(); else tmpBuffer.clear(); wrote = NIOUtil.copy(src, tmpBuffer); setInput(tmpBuffer.array(), 0, tmpBuffer.position()); } deflate(false); return wrote; }else return 0; } private boolean flushBuffer() throws IOException{ while(buffer.hasRemaining()){ if(peer.write(buffer)==0) return false; } buffer.position(0); buffer.limit(0); return true; } private void deflate(boolean flushCompletely) throws IOException{ if(buffer.position()!=0 || buffer.remaining()==buffer.capacity()){ if(!flushBuffer()) return; } while(isOpen() ? !deflater.needsInput() : !deflater.finished()){ int compressed = deflater.deflate(buffer.array(), buffer.limit(), buffer.capacity()-buffer.limit()); if(compressed>0){ buffer.limit(buffer.limit()+compressed); if(buffer.remaining()==buffer.capacity()){ if(!flushBuffer()) return; } } } if(!isOpen() && deflater.finished()){ if(!trailerAdded){ if(buffer.capacity()-buffer.limit()<8){ if(!flushBuffer()) return; } assert buffer.position()==0; buffer.position(buffer.limit()); buffer.limit(buffer.capacity()); addTrailer(buffer); buffer.limit(buffer.position()); buffer.position(0); trailerAdded = true; deflater.end(); deflater = null; } } if(flushCompletely) flushBuffer(); } protected boolean _flush() throws IOException{ deflate(true); return !buffer.hasRemaining(); } @Override protected void _close() throws IOException{ deflater.finish(); } @Override protected void detached(){ if(deflater!=null){ deflater.end(); } if(buffer!=null){ Reactor.current().allocator.free(buffer); buffer = null; } if(tmpBuffer!=null){ Reactor.current().allocator.free(tmpBuffer); tmpBuffer = null; } } }