diff options
Diffstat (limited to 'src/eu/siacs/conversations/utils/zlib')
-rw-r--r-- | src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java | 52 | ||||
-rw-r--r-- | src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java | 89 |
2 files changed, 141 insertions, 0 deletions
diff --git a/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java b/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java new file mode 100644 index 00000000..2eebf6f4 --- /dev/null +++ b/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java @@ -0,0 +1,52 @@ +package eu.siacs.conversations.utils.zlib; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * ZLibInputStream is a zlib and input stream compatible version of an + * InflaterInputStream. This class solves the incompatibility between + * {@link InputStream#available()} and {@link InflaterInputStream#available()}. + */ +public class ZLibInputStream extends InflaterInputStream { + + /** + * Construct a ZLibInputStream, reading data from the underlying stream. + * + * @param is The {@code InputStream} to read data from. + * @throws IOException If an {@code IOException} occurs. + */ + public ZLibInputStream(InputStream is) throws IOException { + super(is, new Inflater(), 512); + } + + /** + * Provide a more InputStream compatible version of available. + * A return value of 1 means that it is likly to read one byte without + * blocking, 0 means that the system is known to block for more input. + * + * @return 0 if no data is available, 1 otherwise + * @throws IOException + */ + @Override + public int available() throws IOException { + /* This is one of the funny code blocks. + * InflaterInputStream.available violates the contract of + * InputStream.available, which breaks kXML2. + * + * I'm not sure who's to blame, oracle/sun for a broken api or the + * google guys for mixing a sun bug with a xml reader that can't handle + * it.... + * + * Anyway, this simple if breaks suns distorted reality, but helps + * to use the api as intended. + */ + if (inf.needsInput()) { + return 0; + } + return super.available(); + } + +} diff --git a/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java b/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java new file mode 100644 index 00000000..f2bff9fd --- /dev/null +++ b/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java @@ -0,0 +1,89 @@ +package eu.siacs.conversations.utils.zlib; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.NoSuchAlgorithmException; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + * <p>Android 2.2 includes Java7 FLUSH_SYNC option, which will be used by this + * Implementation, preferable via reflection. The @hide was remove in API level + * 19. This class might thus go away in the future.</p> + * <p>Please use {@link ZLibOutputStream#SUPPORTED} to check for flush + * compatibility.</p> + */ +public class ZLibOutputStream extends DeflaterOutputStream { + + /** + * The reflection based flush method. + */ + + private final static Method method; + /** + * SUPPORTED is true if a flush compatible method exists. + */ + public final static boolean SUPPORTED; + + /** + * Static block to initialize {@link #SUPPORTED} and {@link #method}. + */ + static { + Method m = null; + try { + m = Deflater.class.getMethod("deflate", byte[].class, int.class, int.class, int.class); + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + } + method = m; + SUPPORTED = (method != null); + } + + /** + * Create a new ZLib compatible output stream wrapping the given low level + * stream. ZLib compatiblity means we will send a zlib header. + * @param os OutputStream The underlying stream. + * @throws IOException In case of a lowlevel transfer problem. + * @throws NoSuchAlgorithmException In case of a {@link Deflater} error. + */ + public ZLibOutputStream(OutputStream os) throws IOException, + NoSuchAlgorithmException { + super(os, new Deflater(Deflater.BEST_COMPRESSION)); + } + + /** + * Flush the given stream, preferring Java7 FLUSH_SYNC if available. + * @throws IOException In case of a lowlevel exception. + */ + @Override + public void flush() throws IOException { + if (!SUPPORTED) { + super.flush(); + return; + } + int count = 0; + if (!def.needsInput()) { + do { + count = def.deflate(buf, 0, buf.length); + out.write(buf, 0, count); + } while (count > 0); + out.flush(); + } + try { + do { + count = (Integer) method.invoke(def, buf, 0, buf.length, 2); + out.write(buf, 0, count); + } while (count > 0); + } catch (IllegalArgumentException e) { + throw new IOException("Can't flush"); + } catch (IllegalAccessException e) { + throw new IOException("Can't flush"); + } catch (InvocationTargetException e) { + throw new IOException("Can't flush"); + } + super.flush(); + } + +} |