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