From 720044511249c8ad85b986a0ea1c8a7cd8ab38ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20Szyman=CC=81ski?= <zbigniew.szymanski@wearezeta.com> Date: Sat, 29 Mar 2014 21:12:29 +0100 Subject: [PATCH 1/3] PushParser refactoring - simplified implementation, removed couple unused methods - type safe TapCallback - no need for reflection, no problems with proguard --- .../com/koushikdutta/async/PushParser.java | 347 +++++------------- .../com/koushikdutta/async/TapCallback.java | 3 +- .../async/http/filter/GZIPInputFilter.java | 53 +-- .../async/test/ByteUtilTests.java | 8 +- 4 files changed, 122 insertions(+), 289 deletions(-) diff --git a/AndroidAsync/src/com/koushikdutta/async/PushParser.java b/AndroidAsync/src/com/koushikdutta/async/PushParser.java index aa41754..784729f 100644 --- a/AndroidAsync/src/com/koushikdutta/async/PushParser.java +++ b/AndroidAsync/src/com/koushikdutta/async/PushParser.java @@ -1,282 +1,129 @@ package com.koushikdutta.async; -import android.util.Log; - import com.koushikdutta.async.callback.DataCallback; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Hashtable; import java.util.LinkedList; -public class PushParser { - private LinkedList<Object> mWaiting = new LinkedList<Object>(); +public class PushParser implements DataCallback { - static class BufferWaiter { + static abstract class Waiter { int length; + /** + * Consumes received data, and/or returns next waiter to continue reading instead of this waiter. + * @param bb received data, bb.remaining >= length + * @return - a waiter that should continue reading right away, or null if this waiter is finished + */ + public abstract Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb); + } + + static class IntWaiter extends Waiter { + TapCallback<Integer> callback; + public IntWaiter(TapCallback<Integer> callback) { + this.callback = callback; + this.length = 4; + } + + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + callback.tap(bb.getInt()); + return null; + } } - - static class StringWaiter extends BufferWaiter { + + static class BufferWaiter extends Waiter { + TapCallback<byte[]> callback; + public BufferWaiter(int length, TapCallback<byte[]> callback) { + if (length <= 0) throw new IllegalArgumentException("length should be > 0"); + this.length = length; + this.callback = callback; + } + + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + byte[] bytes = new byte[length]; + bb.get(bytes); + callback.tap(bytes); + return null; + } } - - static class UntilWaiter { + + static class UntilWaiter extends Waiter { + byte value; DataCallback callback; - } - - int mNeeded = 0; - public PushParser readInt() { - mNeeded += 4; - mWaiting.add(int.class); - return this; - } + public UntilWaiter(byte value, DataCallback callback) { + this.length = 1; + this.value = value; + this.callback = callback; + } - public PushParser readByte() { - mNeeded += 1; - mWaiting.add(byte.class); - return this; - } - - public PushParser readShort() { - mNeeded += 2; - mWaiting.add(short.class); - return this; - } - - public PushParser readLong() { - mNeeded += 8; - mWaiting.add(long.class); - return this; - } - - public PushParser readBuffer(int length) { - if (length != -1) - mNeeded += length; - BufferWaiter bw = new BufferWaiter(); - bw.length = length; - mWaiting.add(bw); - return this; - } + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + boolean found = true; + ByteBufferList cb = new ByteBufferList(); + while (bb.size() > 0) { + ByteBuffer b = bb.remove(); + b.mark(); + int index = 0; + while (b.remaining() > 0 && !(found = (b.get() == value))) { + index++; + } + b.reset(); + if (found) { + bb.addFirst(b); + bb.get(cb, index); + // eat the one we're waiting on + bb.get(); + break; + } else { + cb.add(b); + } + } + + callback.onDataAvailable(emitter, cb); + + if (found) { + return null; + } else { + return this; + } + } - public PushParser readLenBuffer() { - readInt(); - BufferWaiter bw = new BufferWaiter(); - bw.length = -1; - mWaiting.add(bw); - return this; - } - - public PushParser readString() { - readInt(); - StringWaiter bw = new StringWaiter(); - bw.length = -1; - mWaiting.add(bw); - return this; - } - - public PushParser until(byte b, DataCallback callback) { - UntilWaiter waiter = new UntilWaiter(); - waiter.value = b; - waiter.callback = callback; - mWaiting.add(waiter); - mNeeded++; - return this; - } - - public PushParser noop() { - mWaiting.add(Object.class); - return this; } - DataEmitterReader mReader; DataEmitter mEmitter; + private LinkedList<Waiter> mWaiting = new LinkedList<Waiter>(); + ByteOrder order = ByteOrder.BIG_ENDIAN; + public PushParser(DataEmitter s) { mEmitter = s; - mReader = new DataEmitterReader(); - mEmitter.setDataCallback(mReader); - } - - private ArrayList<Object> mArgs = new ArrayList<Object>(); - private TapCallback mCallback; - - Exception stack() { - try { - throw new Exception(); - } - catch (Exception e) { - return e; - } + mEmitter.setDataCallback(this); } - - ByteOrder order = ByteOrder.BIG_ENDIAN; - public ByteOrder order() { - return order; - } - public PushParser order(ByteOrder order) { - this.order = order; + + public PushParser readInt(TapCallback<Integer> callback) { + mWaiting.add(new IntWaiter(callback)); return this; } - - public void tap(TapCallback callback) { - assert mCallback == null; - assert mWaiting.size() > 0; - mCallback = callback; - - new DataCallback() { - { - onDataAvailable(mEmitter, null); - } - - @Override - public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - try { - if (bb != null) - bb.order(order); - while (mWaiting.size() > 0) { - Object waiting = mWaiting.peek(); - if (waiting == null) - break; -// System.out.println("Remaining: " + bb.remaining()); - if (waiting == int.class) { - mArgs.add(bb.getInt()); - mNeeded -= 4; - } - else if (waiting == short.class) { - mArgs.add(bb.getShort()); - mNeeded -= 2; - } - else if (waiting == byte.class) { - mArgs.add(bb.get()); - mNeeded -= 1; - } - else if (waiting == long.class) { - mArgs.add(bb.getLong()); - mNeeded -= 8; - } - else if (waiting == Object.class) { - mArgs.add(null); - } - else if (waiting instanceof UntilWaiter) { - UntilWaiter uw = (UntilWaiter)waiting; - boolean different = true; - ByteBufferList cb = new ByteBufferList(); - while (bb.size() > 0) { - ByteBuffer b = bb.remove(); - b.mark(); - int index = 0; - while (b.remaining() > 0 && (different = (b.get() != uw.value))) { - index++; - } - b.reset(); - if (!different) { - bb.addFirst(b); - bb.get(cb, index); - // eat the one we're waiting on - bb.get(); - break; - } - else { - cb.add(b); - } - } - - if (uw.callback != null) - uw.callback.onDataAvailable(emitter, cb); + public PushParser readBuffer(int length, TapCallback<byte[]> callback) { + mWaiting.add(new BufferWaiter(length, callback)); + return this; + } - if (!different) { - mNeeded--; - } - else { - throw new Exception(); - } - } - else if (waiting instanceof BufferWaiter || waiting instanceof StringWaiter) { - BufferWaiter bw = (BufferWaiter)waiting; - int length = bw.length; - if (length == -1) { - length = (Integer)mArgs.get(mArgs.size() - 1); - mArgs.remove(mArgs.size() - 1); - bw.length = length; - mNeeded += length; - } - if (bb.remaining() < length) { -// System.out.print("imminient feilure detected"); - throw new Exception(); - } - -// e.printStackTrace(); -// System.out.println("Buffer length: " + length); - byte[] bytes = null; - if (length > 0) { - bytes = new byte[length]; - bb.get(bytes); - } - mNeeded -= length; - if (waiting instanceof StringWaiter) - mArgs.add(new String(bytes)); - else - mArgs.add(bytes); - } - else { - assert false; - } -// System.out.println("Parsed: " + mArgs.get(0)); - mWaiting.remove(); - } - } - catch (Exception ex) { - assert mNeeded != 0; -// ex.printStackTrace(); - mReader.read(mNeeded, this); - return; - } - - try { - Object[] args = mArgs.toArray(); - mArgs.clear(); - TapCallback callback = mCallback; - mCallback = null; - Method method = getTap(callback); - method.setAccessible(true); - method.invoke(callback, args); - } - catch (Exception ex) { - assert false; - Log.e("PushParser", "error during parse", ex); - } - } - }; + public PushParser until(byte b, DataCallback callback) { + mWaiting.add(new UntilWaiter(b, callback)); + return this; } - static Hashtable<Class, Method> mTable = new Hashtable<Class, Method>(); - static Method getTap(TapCallback callback) { - Method found = mTable.get(callback.getClass()); - if (found != null) - return found; + @Override + public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - for (Method method : callback.getClass().getMethods()) { - if ("tap".equals(method.getName())) { - mTable.put(callback.getClass(), method); - return method; - } + while (mWaiting.size() > 0 && bb.remaining() >= mWaiting.peek().length) { + bb.order(order); + Waiter next = mWaiting.poll().onDataAvailable(emitter, bb); + if (next != null) mWaiting.addFirst(next); } - - // try the proguard friendly route, take the first/only method - // in case "tap" has been renamed - Method[] candidates = callback.getClass().getDeclaredMethods(); - if (candidates.length == 1) - return candidates[0]; - - String fail = - "-keep class * extends com.koushikdutta.async.TapCallback {\n" + - " *;\n" + - "}\n"; - - //null != "AndroidAsync: tap callback could not be found. Proguard? Use this in your proguard config:\n" + fail; - assert false; - return null; } } diff --git a/AndroidAsync/src/com/koushikdutta/async/TapCallback.java b/AndroidAsync/src/com/koushikdutta/async/TapCallback.java index b0cf451..d6252f2 100644 --- a/AndroidAsync/src/com/koushikdutta/async/TapCallback.java +++ b/AndroidAsync/src/com/koushikdutta/async/TapCallback.java @@ -1,5 +1,6 @@ package com.koushikdutta.async; -public class TapCallback { +public interface TapCallback<T> { + public void tap(T data); } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java b/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java index c604caf..1bbe5c4 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java @@ -1,5 +1,9 @@ package com.koushikdutta.async.http.filter; +import com.koushikdutta.async.*; +import com.koushikdutta.async.callback.DataCallback; +import com.koushikdutta.async.http.libcore.Memory; + import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -7,15 +11,6 @@ import java.util.zip.CRC32; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; -import com.koushikdutta.async.ByteBufferList; -import com.koushikdutta.async.DataEmitter; -import com.koushikdutta.async.DataEmitterReader; -import com.koushikdutta.async.NullDataCallback; -import com.koushikdutta.async.PushParser; -import com.koushikdutta.async.TapCallback; -import com.koushikdutta.async.callback.DataCallback; -import com.koushikdutta.async.http.libcore.Memory; - public class GZIPInputFilter extends InflaterInputFilter { private static final int FCOMMENT = 16; @@ -43,9 +38,7 @@ public class GZIPInputFilter extends InflaterInputFilter { public void onDataAvailable(final DataEmitter emitter, ByteBufferList bb) { if (mNeedsHeader) { final PushParser parser = new PushParser(emitter); - parser - .readBuffer(10) - .tap(new TapCallback() { + parser.readBuffer(10, new TapCallback<byte[]>() { int flags; boolean hcrc; public void tap(byte[] header) { @@ -61,17 +54,13 @@ public class GZIPInputFilter extends InflaterInputFilter { crc.update(header, 0, header.length); } if ((flags & FEXTRA) != 0) { - parser - .readBuffer(2) - .tap(new TapCallback() { + parser.readBuffer(2, new TapCallback<byte[]>() { public void tap(byte[] header) { if (hcrc) { crc.update(header, 0, 2); } int length = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff; - parser - .readBuffer(length) - .tap(new TapCallback() { + parser.readBuffer(length, new TapCallback<byte[]>() { public void tap(byte[] buf) { if (hcrc) { crc.update(buf, 0, buf.length); @@ -81,9 +70,9 @@ public class GZIPInputFilter extends InflaterInputFilter { }); } }); + } else { + next(); } - - next(); } private void next() { PushParser parser = new PushParser(emitter); @@ -106,26 +95,24 @@ public class GZIPInputFilter extends InflaterInputFilter { parser.until((byte)0, summer); } if (hcrc) { - parser.readBuffer(2); - } - else { - parser.noop(); - } - parser.tap(new TapCallback() { - public void tap(byte[] header) { - if (header != null) { + parser.readBuffer(2, new TapCallback<byte[]>() { + public void tap(byte[] header) { short crc16 = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN); if ((short) crc.getValue() != crc16) { report(new IOException("CRC mismatch")); return; } crc.reset(); - } - mNeedsHeader = false; - setDataEmitter(emitter); + mNeedsHeader = false; + setDataEmitter(emitter); // emitter.setDataCallback(GZIPInputFilter.this); - } - }); + } + }); + } + else { + mNeedsHeader = false; + setDataEmitter(emitter); + } } }); } diff --git a/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java b/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java index 5ec30c4..8fd8b6a 100644 --- a/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java +++ b/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java @@ -22,12 +22,10 @@ public class ByteUtilTests extends TestCase { return false; } }; - PushParser p = new PushParser(mock); - p + new PushParser(mock) .until((byte)0, new NullDataCallback()) - .readInt() - .tap(new TapCallback() { - public void tap(int arg) { + .readInt(new TapCallback<Integer>() { + public void tap(Integer arg) { valRead = arg; } }); -- GitLab From 55c375a209efe7fcb15467a8bab441d440ba5d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20Szyman=CC=81ski?= <zbigniew.szymanski@wearezeta.com> Date: Sun, 30 Mar 2014 12:08:25 +0200 Subject: [PATCH 2/3] Restored old TapCallback api, still supporting simplified operations. Added missing (removed earlier) methods in PushParser, implemented old TapCallback handling on top of simplified PushParser. --- .../com/koushikdutta/async/PushParser.java | 187 ++++++++++++++++-- .../com/koushikdutta/async/TapCallback.java | 3 +- .../async/http/filter/GZIPInputFilter.java | 17 +- .../async/test/ByteUtilTests.java | 59 +++++- 4 files changed, 243 insertions(+), 23 deletions(-) diff --git a/AndroidAsync/src/com/koushikdutta/async/PushParser.java b/AndroidAsync/src/com/koushikdutta/async/PushParser.java index 784729f..bfb6b74 100644 --- a/AndroidAsync/src/com/koushikdutta/async/PushParser.java +++ b/AndroidAsync/src/com/koushikdutta/async/PushParser.java @@ -1,15 +1,27 @@ package com.koushikdutta.async; +import android.util.Log; import com.koushikdutta.async.callback.DataCallback; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Hashtable; import java.util.LinkedList; public class PushParser implements DataCallback { + public interface ParseCallback<T> { + public void parsed(T data); + } + static abstract class Waiter { int length; + public Waiter(int length) { + this.length = length; + } /** * Consumes received data, and/or returns next waiter to continue reading instead of this waiter. * @param bb received data, bb.remaining >= length @@ -19,24 +31,24 @@ public class PushParser implements DataCallback { } static class IntWaiter extends Waiter { - TapCallback<Integer> callback; - public IntWaiter(TapCallback<Integer> callback) { + ParseCallback<Integer> callback; + public IntWaiter(ParseCallback<Integer> callback) { + super(4); this.callback = callback; - this.length = 4; } @Override public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - callback.tap(bb.getInt()); + callback.parsed(bb.getInt()); return null; } } static class BufferWaiter extends Waiter { - TapCallback<byte[]> callback; - public BufferWaiter(int length, TapCallback<byte[]> callback) { + ParseCallback<byte[]> callback; + public BufferWaiter(int length, ParseCallback<byte[]> callback) { + super(length); if (length <= 0) throw new IllegalArgumentException("length should be > 0"); - this.length = length; this.callback = callback; } @@ -44,17 +56,32 @@ public class PushParser implements DataCallback { public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { byte[] bytes = new byte[length]; bb.get(bytes); - callback.tap(bytes); + callback.parsed(bytes); return null; } } + static class LenBufferWaiter extends Waiter { + private final ParseCallback<byte[]> callback; + + public LenBufferWaiter(ParseCallback<byte[]> callback) { + super(4); + this.callback = callback; + } + + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + int length = bb.getInt(); + return new BufferWaiter(length, callback); + } + } + static class UntilWaiter extends Waiter { byte value; DataCallback callback; public UntilWaiter(byte value, DataCallback callback) { - this.length = 1; + super(1); this.value = value; this.callback = callback; } @@ -90,11 +117,48 @@ public class PushParser implements DataCallback { return this; } } + } + + private class TapArgCallback<T> implements ParseCallback<T> { + @Override + public void parsed(T data) { + args.add(data); + } + } + + private class TapWaiter extends Waiter { + private final TapCallback callback; + + public TapWaiter(TapCallback callback) { + super(0); + this.callback = callback; + } + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + Method method = getTap(callback); + method.setAccessible(true); + try { + method.invoke(callback, args.toArray()); + } catch (Exception e) { + Log.e("PushParser", "Error while invoking tap callback", e); + } + args.clear(); + return null; + } } + private Waiter noopWaiter = new Waiter(0) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(null); + return null; + } + }; + DataEmitter mEmitter; private LinkedList<Waiter> mWaiting = new LinkedList<Waiter>(); + private ArrayList<Object> args = new ArrayList<Object>(); ByteOrder order = ByteOrder.BIG_ENDIAN; public PushParser(DataEmitter s) { @@ -102,12 +166,12 @@ public class PushParser implements DataCallback { mEmitter.setDataCallback(this); } - public PushParser readInt(TapCallback<Integer> callback) { + public PushParser readInt(ParseCallback<Integer> callback) { mWaiting.add(new IntWaiter(callback)); return this; } - public PushParser readBuffer(int length, TapCallback<byte[]> callback) { + public PushParser readBuffer(int length, ParseCallback<byte[]> callback) { mWaiting.add(new BufferWaiter(length, callback)); return this; } @@ -117,6 +181,74 @@ public class PushParser implements DataCallback { return this; } + public PushParser readByte() { + mWaiting.add(new Waiter(1) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.get()); + return null; + } + }); + return this; + } + + public PushParser readShort() { + mWaiting.add(new Waiter(2) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.getShort()); + return null; + } + }); + return this; + } + + public PushParser readInt() { + mWaiting.add(new Waiter(4) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.getInt()); + return null; + } + }); + return this; + } + + public PushParser readLong() { + mWaiting.add(new Waiter(8) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.getLong()); + return null; + } + }); + return this; + } + + public PushParser readBuffer(int length) { + return (length == -1) ? readLenBuffer() : readBuffer(length, new TapArgCallback<byte[]>()); + } + + public PushParser readLenBuffer() { + mWaiting.add(new LenBufferWaiter(new TapArgCallback<byte[]>())); + return this; + } + + public PushParser readString() { + mWaiting.add(new LenBufferWaiter(new ParseCallback<byte[]>() { + @Override + public void parsed(byte[] data) { + args.add(new String(data)); + } + })); + return this; + } + + public PushParser noop() { + mWaiting.add(noopWaiter); + return this; + } + @Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { @@ -126,4 +258,37 @@ public class PushParser implements DataCallback { if (next != null) mWaiting.addFirst(next); } } + + public void tap(TapCallback callback) { + mWaiting.add(new TapWaiter(callback)); + } + + static Hashtable<Class, Method> mTable = new Hashtable<Class, Method>(); + static Method getTap(TapCallback callback) { + Method found = mTable.get(callback.getClass()); + if (found != null) + return found; + + for (Method method : callback.getClass().getMethods()) { + if ("tap".equals(method.getName())) { + mTable.put(callback.getClass(), method); + return method; + } + } + + // try the proguard friendly route, take the first/only method + // in case "tap" has been renamed + Method[] candidates = callback.getClass().getDeclaredMethods(); + if (candidates.length == 1) + return candidates[0]; + + String fail = + "-keep class * extends com.koushikdutta.async.TapCallback {\n" + + " *;\n" + + "}\n"; + + //null != "AndroidAsync: tap callback could not be found. Proguard? Use this in your proguard config:\n" + fail; + assert false; + return null; + } } diff --git a/AndroidAsync/src/com/koushikdutta/async/TapCallback.java b/AndroidAsync/src/com/koushikdutta/async/TapCallback.java index d6252f2..d8d7844 100644 --- a/AndroidAsync/src/com/koushikdutta/async/TapCallback.java +++ b/AndroidAsync/src/com/koushikdutta/async/TapCallback.java @@ -1,6 +1,5 @@ package com.koushikdutta.async; -public interface TapCallback<T> { - public void tap(T data); +public interface TapCallback { } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java b/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java index 1bbe5c4..1ee83d3 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java @@ -3,6 +3,7 @@ package com.koushikdutta.async.http.filter; import com.koushikdutta.async.*; import com.koushikdutta.async.callback.DataCallback; import com.koushikdutta.async.http.libcore.Memory; +import com.koushikdutta.async.PushParser.ParseCallback; import java.io.IOException; import java.nio.ByteBuffer; @@ -38,10 +39,10 @@ public class GZIPInputFilter extends InflaterInputFilter { public void onDataAvailable(final DataEmitter emitter, ByteBufferList bb) { if (mNeedsHeader) { final PushParser parser = new PushParser(emitter); - parser.readBuffer(10, new TapCallback<byte[]>() { + parser.readBuffer(10, new ParseCallback<byte[]>() { int flags; boolean hcrc; - public void tap(byte[] header) { + public void parsed(byte[] header) { short magic = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN); if (magic != (short) GZIPInputStream.GZIP_MAGIC) { report(new IOException(String.format("unknown format (magic number %x)", magic))); @@ -54,14 +55,14 @@ public class GZIPInputFilter extends InflaterInputFilter { crc.update(header, 0, header.length); } if ((flags & FEXTRA) != 0) { - parser.readBuffer(2, new TapCallback<byte[]>() { - public void tap(byte[] header) { + parser.readBuffer(2, new ParseCallback<byte[]>() { + public void parsed(byte[] header) { if (hcrc) { crc.update(header, 0, 2); } int length = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff; - parser.readBuffer(length, new TapCallback<byte[]>() { - public void tap(byte[] buf) { + parser.readBuffer(length, new ParseCallback<byte[]>() { + public void parsed(byte[] buf) { if (hcrc) { crc.update(buf, 0, buf.length); } @@ -95,8 +96,8 @@ public class GZIPInputFilter extends InflaterInputFilter { parser.until((byte)0, summer); } if (hcrc) { - parser.readBuffer(2, new TapCallback<byte[]>() { - public void tap(byte[] header) { + parser.readBuffer(2, new ParseCallback<byte[]>() { + public void parsed(byte[] header) { short crc16 = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN); if ((short) crc.getValue() != crc16) { report(new IOException("CRC mismatch")); diff --git a/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java b/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java index 8fd8b6a..0d2f5df 100644 --- a/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java +++ b/AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java @@ -24,8 +24,8 @@ public class ByteUtilTests extends TestCase { }; new PushParser(mock) .until((byte)0, new NullDataCallback()) - .readInt(new TapCallback<Integer>() { - public void tap(Integer arg) { + .readInt(new PushParser.ParseCallback<Integer>() { + public void parsed(Integer arg) { valRead = arg; } }); @@ -33,4 +33,59 @@ public class ByteUtilTests extends TestCase { Util.emitAllData(mock, new ByteBufferList(bytes)); assertEquals(valRead, 0x0A050505); } + + public void testPushParserTapUntil() { + valRead = 0; + FilteredDataEmitter mock = new FilteredDataEmitter() { + @Override + public boolean isPaused() { + return false; + } + }; + new PushParser(mock) + .until((byte)0, new NullDataCallback()) + .readInt() + .tap(new TapCallback() { + public void parsed(int arg) { + valRead = arg; + } + }); + byte[] bytes = new byte[] { 5, 5, 5, 5, 0, 10, 5, 5, 5 }; + Util.emitAllData(mock, new ByteBufferList(bytes)); + assertEquals(valRead, 0x0A050505); + } + + int readInt; + byte readByte; + String readString; + + public void testTapCallback() { + readInt = 0; + readByte = 0; + readString = ""; + + FilteredDataEmitter mock = new FilteredDataEmitter() { + @Override + public boolean isPaused() { + return false; + } + }; + new PushParser(mock) + .readInt() + .readByte() + .readString() + .tap(new TapCallback() { + void tap(int i, byte b, String s) { + readInt = i; + readByte = b; + readString = s; + } + }); + + byte[] bytes = new byte[] { 10, 5, 5, 5, 3, 0, 0, 0, 4, 116, 101, 115, 116 }; + Util.emitAllData(mock, new ByteBufferList(bytes)); + assertEquals(readInt, 0x0A050505); + assertEquals(readByte, (byte) 3); + assertEquals(readString, "test"); + } } -- GitLab From d7635ad779cf9dccf22ab494fccb60ded52685ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20Szyman=CC=81ski?= <zbigniew.szymanski@wearezeta.com> Date: Sun, 30 Mar 2014 12:47:07 +0200 Subject: [PATCH 3/3] Removed unnecessary object allocations for TapCallback api --- .../com/koushikdutta/async/PushParser.java | 107 ++++++++++-------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/AndroidAsync/src/com/koushikdutta/async/PushParser.java b/AndroidAsync/src/com/koushikdutta/async/PushParser.java index bfb6b74..686bcdb 100644 --- a/AndroidAsync/src/com/koushikdutta/async/PushParser.java +++ b/AndroidAsync/src/com/koushikdutta/async/PushParser.java @@ -3,7 +3,6 @@ package com.koushikdutta.async; import android.util.Log; import com.koushikdutta.async.callback.DataCallback; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -119,13 +118,6 @@ public class PushParser implements DataCallback { } } - private class TapArgCallback<T> implements ParseCallback<T> { - @Override - public void parsed(T data) { - args.add(data); - } - } - private class TapWaiter extends Waiter { private final TapCallback callback; @@ -148,7 +140,7 @@ public class PushParser implements DataCallback { } } - private Waiter noopWaiter = new Waiter(0) { + private Waiter noopArgWaiter = new Waiter(0) { @Override public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { args.add(null); @@ -156,6 +148,52 @@ public class PushParser implements DataCallback { } }; + private Waiter byteArgWaiter = new Waiter(1) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.get()); + return null; + } + }; + + private Waiter shortArgWaiter = new Waiter(2) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.getShort()); + return null; + } + }; + + private Waiter intArgWaiter = new Waiter(4) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.getInt()); + return null; + } + }; + + private Waiter longArgWaiter = new Waiter(8) { + @Override + public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { + args.add(bb.getLong()); + return null; + } + }; + + private ParseCallback<byte[]> byteArrayArgCallback = new ParseCallback<byte[]>() { + @Override + public void parsed(byte[] data) { + args.add(data); + } + }; + + private ParseCallback<byte[]> stringArgCallback = new ParseCallback<byte[]>() { + @Override + public void parsed(byte[] data) { + args.add(new String(data)); + } + }; + DataEmitter mEmitter; private LinkedList<Waiter> mWaiting = new LinkedList<Waiter>(); private ArrayList<Object> args = new ArrayList<Object>(); @@ -182,70 +220,41 @@ public class PushParser implements DataCallback { } public PushParser readByte() { - mWaiting.add(new Waiter(1) { - @Override - public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - args.add(bb.get()); - return null; - } - }); + mWaiting.add(byteArgWaiter); return this; } public PushParser readShort() { - mWaiting.add(new Waiter(2) { - @Override - public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - args.add(bb.getShort()); - return null; - } - }); + mWaiting.add(shortArgWaiter); return this; } public PushParser readInt() { - mWaiting.add(new Waiter(4) { - @Override - public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - args.add(bb.getInt()); - return null; - } - }); + mWaiting.add(intArgWaiter); return this; } public PushParser readLong() { - mWaiting.add(new Waiter(8) { - @Override - public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) { - args.add(bb.getLong()); - return null; - } - }); + mWaiting.add(longArgWaiter); return this; } public PushParser readBuffer(int length) { - return (length == -1) ? readLenBuffer() : readBuffer(length, new TapArgCallback<byte[]>()); + return (length == -1) ? readLenBuffer() : readBuffer(length, byteArrayArgCallback); } public PushParser readLenBuffer() { - mWaiting.add(new LenBufferWaiter(new TapArgCallback<byte[]>())); + mWaiting.add(new LenBufferWaiter(byteArrayArgCallback)); return this; } public PushParser readString() { - mWaiting.add(new LenBufferWaiter(new ParseCallback<byte[]>() { - @Override - public void parsed(byte[] data) { - args.add(new String(data)); - } - })); + mWaiting.add(new LenBufferWaiter(stringArgCallback)); return this; } public PushParser noop() { - mWaiting.add(noopWaiter); + mWaiting.add(noopArgWaiter); return this; } @@ -283,9 +292,9 @@ public class PushParser implements DataCallback { return candidates[0]; String fail = - "-keep class * extends com.koushikdutta.async.TapCallback {\n" + - " *;\n" + - "}\n"; + "-keep class * extends com.koushikdutta.async.TapCallback {\n" + + " *;\n" + + "}\n"; //null != "AndroidAsync: tap callback could not be found. Proguard? Use this in your proguard config:\n" + fail; assert false; -- GitLab