From 720044511249c8ad85b986a0ea1c8a7cd8ab38ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20Szyman=CC=81ski?= Date: Sat, 29 Mar 2014 21:12:29 +0100 Subject: PushParser refactoring - simplified implementation, removed couple unused methods - type safe TapCallback - no need for reflection, no problems with proguard --- .../src/com/koushikdutta/async/PushParser.java | 347 ++++++--------------- .../src/com/koushikdutta/async/TapCallback.java | 3 +- .../async/http/filter/GZIPInputFilter.java | 53 ++-- 3 files changed, 119 insertions(+), 284 deletions(-) (limited to 'AndroidAsync') 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 mWaiting = new LinkedList(); +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 callback; + public IntWaiter(TapCallback 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 callback; + public BufferWaiter(int length, TapCallback 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 mWaiting = new LinkedList(); + ByteOrder order = ByteOrder.BIG_ENDIAN; + public PushParser(DataEmitter s) { mEmitter = s; - mReader = new DataEmitterReader(); - mEmitter.setDataCallback(mReader); - } - - private ArrayList mArgs = new ArrayList(); - 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 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 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 mTable = new Hashtable(); - 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 { + 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() { 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() { 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() { 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() { + 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); + } } }); } -- cgit v1.2.3