aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidAsync/src/com/koushikdutta/async/PushParser.java187
-rw-r--r--AndroidAsync/src/com/koushikdutta/async/TapCallback.java3
-rw-r--r--AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java17
-rw-r--r--AndroidAsyncTest/src/com/koushikdutta/async/test/ByteUtilTests.java59
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");
+ }
}