From 3785ca9311270763119a81bc2048e23ce2be0ef7 Mon Sep 17 00:00:00 2001 From: Robert Borg Date: Tue, 19 Jan 2016 20:23:39 +0100 Subject: [PATCH] added support for Protobuf codec nano runtime Motivation: Netty was missing support for Protobuf nano runtime targeted at weaker systems such as Android devices. Modifications: Added ProtobufDecoderNano and ProtobufDecoderNano in order to provide support for Nano runtime. modified ProtobufVarint32FrameDecoder and ProtobufLengthFieldPrepender in order to remove any on either Nano or Lite runtime by copying the code for handling Protobuf varint32 in from Protobuf library. modified Licenses and NOTICE in order to reflect the changes i made. added Protobuf Nano runtime as optional dependency Result: Netty now supports Protobuf Nano runtime. --- NOTICE.txt | 2 +- codec/pom.xml | 5 + .../codec/protobuf/ProtobufDecoder.java | 18 +-- .../codec/protobuf/ProtobufDecoderNano.java | 88 ++++++++++++++ .../codec/protobuf/ProtobufEncoder.java | 9 +- .../codec/protobuf/ProtobufEncoderNano.java | 73 ++++++++++++ .../ProtobufVarint32FrameDecoder.java | 109 ++++++++++++----- .../ProtobufVarint32LengthFieldPrepender.java | 60 ++++++++-- .../handler/codec/protobuf/package-info.java | 4 +- .../ProtobufVarint32FrameDecoderTest.java | 18 +-- ...tobufVarint32LengthFieldPrependerTest.java | 111 +++++++++++++++++- license/LICENSE.protobuf.txt | 5 +- pom.xml | 5 + 13 files changed, 436 insertions(+), 71 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java create mode 100644 codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java diff --git a/NOTICE.txt b/NOTICE.txt index 952a6e418a..19d04a248e 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -146,7 +146,7 @@ and decompression library written by William Kinney. It can be obtained at: * HOMEPAGE: * https://code.google.com/p/jfastlz/ -This product optionally depends on 'Protocol Buffers', Google's data +This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data interchange format, which can be obtained at: * LICENSE: diff --git a/codec/pom.xml b/codec/pom.xml index a34b251608..0579705dee 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -39,6 +39,11 @@ protobuf-java true + + com.google.protobuf.nano + protobuf-javanano + true + org.jboss.marshalling jboss-marshalling diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java index a31e16461e..ed282467ff 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -33,10 +33,10 @@ import java.util.List; /** * Decodes a received {@link ByteBuf} into a * Google Protocol Buffers - * {@link Message} and {@link MessageLite}. Please note that this decoder must + * {@link Message} and {@link MessageLite}. Please note that this decoder must * be used with a proper {@link ByteToMessageDecoder} such as {@link ProtobufVarint32FrameDecoder} * or {@link LengthFieldBasedFrameDecoder} if you are using a stream-based - * transport such as TCP/IP. A typical setup for TCP/IP would be: + * transport such as TCP/IP. A typical setup for TCP/IP would be: *
  * {@link ChannelPipeline} pipeline = ...;
  *
@@ -53,7 +53,8 @@ import java.util.List;
  * and then you can use a {@code MyMessage} instead of a {@link ByteBuf}
  * as a message:
  * 
- * void channelRead({@link ChannelHandlerContext} ctx, MyMessage req) {
+ * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
+ *     MyMessage req = (MyMessage) msg;
  *     MyMessage res = MyMessage.newBuilder().setText(
  *                               "Did you say '" + req.getText() + "'?").build();
  *     ch.write(res);
@@ -101,7 +102,8 @@ public class ProtobufDecoder extends MessageToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out)
+            throws Exception {
         final byte[] array;
         final int offset;
         final int length = msg.readableBytes();
@@ -122,9 +124,11 @@ public class ProtobufDecoder extends MessageToMessageDecoder {
             }
         } else {
             if (HAS_PARSER) {
-                out.add(prototype.getParserForType().parseFrom(array, offset, length, extensionRegistry));
+                out.add(prototype.getParserForType().parseFrom(
+                        array, offset, length, extensionRegistry));
             } else {
-                out.add(prototype.newBuilderForType().mergeFrom(array, offset, length, extensionRegistry).build());
+                out.add(prototype.newBuilderForType().mergeFrom(
+                        array, offset, length, extensionRegistry).build());
             }
         }
     }
diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java
new file mode 100644
index 0000000000..c7915e4195
--- /dev/null
+++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.protobuf;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.List;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler.Sharable;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.MessageToMessageDecoder;
+import io.netty.util.internal.ObjectUtil;
+
+/**
+ * Decodes a received {@link ByteBuf} into a
+ * Google Protocol Buffers
+ * {@link MessageNano}. Please note that this decoder must
+ * be used with a proper {@link ByteToMessageDecoder} such as {@link LengthFieldBasedFrameDecoder}
+ * if you are using a stream-based transport such as TCP/IP. A typical setup for TCP/IP would be:
+ * 
+ * {@link ChannelPipeline} pipeline = ...;
+ *
+ * // Decoders
+ * pipeline.addLast("frameDecoder",
+ *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
+ * pipeline.addLast("protobufDecoder",
+ *                  new {@link ProtobufDecoderNano}(MyMessage.getDefaultInstance()));
+ *
+ * // Encoder
+ * pipeline.addLast("frameEncoder", new {@link io.netty.handler.codec.LengthFieldPrepender}(4));
+ * pipeline.addLast("protobufEncoder", new {@link ProtobufEncoderNano}());
+ * 
+ * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} + * as a message: + *
+ * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
+ *     MyMessage req = (MyMessage) msg;
+ *     MyMessage res = MyMessage.newBuilder().setText(
+ *                               "Did you say '" + req.getText() + "'?").build();
+ *     ch.write(res);
+ * }
+ * 
+ */ +@Sharable +public class ProtobufDecoderNano extends MessageToMessageDecoder { + private final Class clazz; + /** + * Creates a new instance. + */ + public ProtobufDecoderNano(Class clazz) { + this.clazz = ObjectUtil.checkNotNull(clazz, "You must provide a Class"); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) + throws Exception { + final byte[] array; + final int offset; + final int length = msg.readableBytes(); + if (msg.hasArray()) { + array = msg.array(); + offset = msg.arrayOffset() + msg.readerIndex(); + } else { + array = new byte[length]; + msg.getBytes(msg.readerIndex(), array, 0, length); + offset = 0; + } + MessageNano prototype = clazz.newInstance(); + out.add(MessageNano.mergeFrom(prototype, array, offset, length)); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java index a7e56c26bc..544ad449c9 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -50,7 +50,8 @@ import static io.netty.buffer.Unpooled.*; * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} * as a message: *
- * void channelRead({@link ChannelHandlerContext} ctx, MyMessage req) {
+ * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
+ *     MyMessage req = (MyMessage) msg;
  *     MyMessage res = MyMessage.newBuilder().setText(
  *                               "Did you say '" + req.getText() + "'?").build();
  *     ch.write(res);
@@ -60,8 +61,8 @@ import static io.netty.buffer.Unpooled.*;
 @Sharable
 public class ProtobufEncoder extends MessageToMessageEncoder {
     @Override
-    protected void encode(
-            ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List out) throws Exception {
+    protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List out)
+            throws Exception {
         if (msg instanceof MessageLite) {
             out.add(wrappedBuffer(((MessageLite) msg).toByteArray()));
             return;
diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java
new file mode 100644
index 0000000000..a2d684eccf
--- /dev/null
+++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.protobuf;
+
+import com.google.protobuf.nano.CodedOutputByteBufferNano;
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.List;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.MessageToMessageEncoder;
+
+/**
+ * Encodes the requested Google
+ * Protocol Buffers {@link MessageNano} into a
+ * {@link ByteBuf}. A typical setup for TCP/IP would be:
+ * 
+ * {@link ChannelPipeline} pipeline = ...;
+ *
+ * // Decoders
+ * pipeline.addLast("frameDecoder",
+ *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
+ * pipeline.addLast("protobufDecoder",
+ *                  new {@link ProtobufDecoderNano}(MyMessage.getDefaultInstance()));
+ *
+ * // Encoder
+ * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
+ * pipeline.addLast("protobufEncoder", new {@link ProtobufEncoderNano}());
+ * 
+ * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} + * as a message: + *
+ * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
+ *     MyMessage req = (MyMessage) msg;
+ *     MyMessage res = MyMessage.newBuilder().setText(
+ *                               "Did you say '" + req.getText() + "'?").build();
+ *     ch.write(res);
+ * }
+ * 
+ */ +@ChannelHandler.Sharable +public class ProtobufEncoderNano extends MessageToMessageEncoder { + @Override + protected void encode( + ChannelHandlerContext ctx, MessageNano msg, List out) throws Exception { + final int size = msg.getSerializedSize(); + final ByteBuf buffer = ctx.alloc().heapBuffer(size, size); + final byte[] array = buffer.array(); + CodedOutputByteBufferNano cobbn = CodedOutputByteBufferNano.newInstance(array, + buffer.arrayOffset(), buffer.capacity()); + msg.writeTo(cobbn); + buffer.writerIndex(size); + out.add(buffer); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java index b1abc5b410..ec8395b5f7 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -15,20 +15,19 @@ */ package io.netty.handler.codec.protobuf; +import java.io.IOException; +import java.util.List; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException; -import java.util.List; - -import com.google.protobuf.CodedInputStream; - /** * A decoder that splits the received {@link ByteBuf}s dynamically by the * value of the Google Protocol Buffers * Base - * 128 Varints integer length field in the message. For example: + * 128 Varints integer length field in the message. For example: *
  * BEFORE DECODE (302 bytes)       AFTER DECODE (300 bytes)
  * +--------+---------------+      +---------------+
@@ -37,7 +36,7 @@ import com.google.protobuf.CodedInputStream;
  * +--------+---------------+      +---------------+
  * 
* - * @see CodedInputStream + * @see {@link CodedInputStream } or {@link CodedInputByteBufferNano} */ public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder { @@ -45,33 +44,79 @@ public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder { // (just like LengthFieldBasedFrameDecoder) @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) + throws Exception { in.markReaderIndex(); - final byte[] buf = new byte[5]; - for (int i = 0; i < buf.length; i ++) { - if (!in.isReadable()) { - in.resetReaderIndex(); - return; - } - - buf[i] = in.readByte(); - if (buf[i] >= 0) { - int length = CodedInputStream.newInstance(buf, 0, i + 1).readRawVarint32(); - if (length < 0) { - throw new CorruptedFrameException("negative length: " + length); - } - - if (in.readableBytes() < length) { - in.resetReaderIndex(); - return; - } else { - out.add(in.readBytes(length)); - return; - } - } + int preIndex = in.readerIndex(); + int length = readRawVarint32(in); + if (preIndex == in.readerIndex()) { + return; + } + if (length < 0) { + throw new CorruptedFrameException("negative length: " + length); } - // Couldn't find the byte whose MSB is off. - throw new CorruptedFrameException("length wider than 32-bit"); + if (in.readableBytes() < length) { + in.resetReaderIndex(); + return; + } else { + out.add(in.readBytes(length)); + return; + } + } + + /** + * Reads variable length 32bit int from buffer + * + * @return decoded int if buffers readerIndex has been forwarded else nonsense value + * @throws IOException + */ + private static int readRawVarint32(ByteBuf buffer) throws IOException { + if (!buffer.isReadable()) { + return 0; + } + buffer.markReaderIndex(); + byte tmp = buffer.readByte(); + if (tmp >= 0) { + return tmp; + } else { + int result = tmp & 127; + if (!buffer.isReadable()) { + buffer.resetReaderIndex(); + return 0; + } + if ((tmp = buffer.readByte()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 127) << 7; + if (!buffer.isReadable()) { + buffer.resetReaderIndex(); + return 0; + } + if ((tmp = buffer.readByte()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 127) << 14; + if (!buffer.isReadable()) { + buffer.resetReaderIndex(); + return 0; + } + if ((tmp = buffer.readByte()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 127) << 21; + if (!buffer.isReadable()) { + buffer.resetReaderIndex(); + return 0; + } + result |= (tmp = buffer.readByte()) << 28; + if (tmp < 0) { + throw new CorruptedFrameException("malformed varint."); + } + } + } + } + return result; + } } } diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java index 9efd00ff26..572359671e 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -15,9 +15,9 @@ */ package io.netty.handler.codec.protobuf; -import com.google.protobuf.CodedOutputStream; +import java.io.IOException; + import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; @@ -25,16 +25,16 @@ import io.netty.handler.codec.MessageToByteEncoder; /** * An encoder that prepends the the Google Protocol Buffers * Base - * 128 Varints integer length field. For example: + * 128 Varints integer length field. For example: *
- * BEFORE DECODE (300 bytes)       AFTER DECODE (302 bytes)
+ * BEFORE ENCODE (300 bytes)       AFTER ENCODE (302 bytes)
  * +---------------+               +--------+---------------+
  * | Protobuf Data |-------------->| Length | Protobuf Data |
  * |  (300 bytes)  |               | 0xAC02 |  (300 bytes)  |
  * +---------------+               +--------+---------------+
  * 
* * - * @see CodedOutputStream + * @see {@link CodedOutputStream} or (@link CodedOutputByteBufferNano) */ @Sharable public class ProtobufVarint32LengthFieldPrepender extends MessageToByteEncoder { @@ -43,14 +43,48 @@ public class ProtobufVarint32LengthFieldPrepender extends MessageToByteEncoder>>= 7; + } + } + } + + /** + * Computes size of protobuf varint32 after encoding. + * @param value which is to be encoded. + * @return size of value encoded as protobuf varint32. + */ + static int computeRawVarint32Size(final int value) { + if ((value & (0xffffffff << 7)) == 0) { + return 1; + } + if ((value & (0xffffffff << 14)) == 0) { + return 2; + } + if ((value & (0xffffffff << 21)) == 0) { + return 3; + } + if ((value & (0xffffffff << 28)) == 0) { + return 4; + } + return 5; + } } diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java b/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java index 6d781c5be3..a1cb539d91 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java @@ -17,7 +17,7 @@ /** * Encoder and decoder which transform a * Google Protocol Buffers - * {@link com.google.protobuf.Message} into a {@link io.netty.buffer.ByteBuf} - * and vice versa. + * {@link com.google.protobuf.Message} and {@link com.google.protobuf.nano.MessageNano} into a + * {@link io.netty.buffer.ByteBuf} and vice versa. */ package io.netty.handler.codec.protobuf; diff --git a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java index 7de7ab7f50..7fa3352e7c 100644 --- a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -38,14 +38,15 @@ public class ProtobufVarint32FrameDecoderTest { @Test public void testTinyDecode() { byte[] b = { 4, 1, 1, 1, 1 }; - ch.writeInbound(wrappedBuffer(b, 0, 1)); + assertFalse(ch.writeInbound(wrappedBuffer(b, 0, 1))); assertThat(ch.readInbound(), is(nullValue())); - ch.writeInbound(wrappedBuffer(b, 1, 2)); + assertFalse(ch.writeInbound(wrappedBuffer(b, 1, 2))); assertThat(ch.readInbound(), is(nullValue())); - ch.writeInbound(wrappedBuffer(b, 3, b.length - 3)); + assertTrue(ch.writeInbound(wrappedBuffer(b, 3, b.length - 3))); assertThat( releaseLater((ByteBuf) ch.readInbound()), is(releaseLater(wrappedBuffer(new byte[] { 1, 1, 1, 1 })))); + assertFalse(ch.finish()); } @Test @@ -56,11 +57,14 @@ public class ProtobufVarint32FrameDecoderTest { } b[0] = -2; b[1] = 15; - ch.writeInbound(wrappedBuffer(b, 0, 127)); + assertFalse(ch.writeInbound(wrappedBuffer(b, 0, 1))); assertThat(ch.readInbound(), is(nullValue())); - ch.writeInbound(wrappedBuffer(b, 127, 600)); + assertFalse(ch.writeInbound(wrappedBuffer(b, 1, 127))); assertThat(ch.readInbound(), is(nullValue())); - ch.writeInbound(wrappedBuffer(b, 727, b.length - 727)); + assertFalse(ch.writeInbound(wrappedBuffer(b, 127, 600))); + assertThat(ch.readInbound(), is(nullValue())); + assertTrue(ch.writeInbound(wrappedBuffer(b, 727, b.length - 727))); assertThat(releaseLater((ByteBuf) ch.readInbound()), is(releaseLater(wrappedBuffer(b, 2, b.length - 2)))); + assertFalse(ch.finish()); } } diff --git a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java index d234a081e8..1fe9e86b2d 100644 --- a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java +++ b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -34,11 +34,115 @@ public class ProtobufVarint32LengthFieldPrependerTest { ch = new EmbeddedChannel(new ProtobufVarint32LengthFieldPrepender()); } + @Test + public void testSize1Varint() { + final int size = 1; + final int num = 10; + assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); + final byte[] buf = new byte[size + num]; + //0000 1010 + buf[0] = 0x0A; + for (int i = size; i < num + size; ++i) { + buf[i] = 1; + } + assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); + assertThat(releaseLater((ByteBuf) ch.readOutbound()), is(releaseLater(wrappedBuffer(buf)))); + assertFalse(ch.finish()); + } + + @Test + public void testSize2Varint() { + final int size = 2; + final int num = 266; + assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); + final byte[] buf = new byte[size + num]; + /** + * 8 A 0 2 + * 1000 1010 0000 0010 + * 0000 1010 0000 0010 + * 0000 0010 0000 1010 + * 000 0010 000 1010 + * + * 0000 0001 0000 1010 + * 0 1 0 A + * 266 + */ + + buf[0] = (byte) (0x8A & 0xFF); + buf[1] = 0x02; + for (int i = size; i < num + size; ++i) { + buf[i] = 1; + } + assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); + assertThat(releaseLater((ByteBuf) ch.readOutbound()), is(releaseLater(wrappedBuffer(buf)))); + assertFalse(ch.finish()); + } + + @Test + public void testSize3Varint() { + final int size = 3; + final int num = 0x4000; + assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); + final byte[] buf = new byte[size + num]; + /** + * 8 0 8 0 0 1 + * 1000 0000 1000 0000 0000 0001 + * 0000 0000 0000 0000 0000 0001 + * 0000 0001 0000 0000 0000 0000 + * 000 0001 000 0000 000 0000 + * + * 0 0000 0100 0000 0000 0000 + * 0 0 4 0 0 0 + * + */ + + buf[0] = (byte) (0x80 & 0xFF); + buf[1] = (byte) (0x80 & 0xFF); + buf[2] = 0x01; + for (int i = size; i < num + size; ++i) { + buf[i] = 1; + } + assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); + assertThat(releaseLater((ByteBuf) ch.readOutbound()), is(releaseLater(wrappedBuffer(buf)))); + assertFalse(ch.finish()); + } + + @Test + public void testSize4Varint() { + final int size = 4; + final int num = 0x200000; + assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); + final byte[] buf = new byte[size + num]; + /** + * 8 0 8 0 8 0 0 1 + * 1000 0000 1000 0000 1000 0000 0000 0001 + * 0000 0000 0000 0000 0000 0000 0000 0001 + * 0000 0001 0000 0000 0000 0000 0000 0000 + * 000 0001 000 0000 000 0000 000 0000 + * + * 0000 0010 0000 0000 0000 0000 0000 + * 0 2 0 0 0 0 0 + * + */ + + buf[0] = (byte) (0x80 & 0xFF); + buf[1] = (byte) (0x80 & 0xFF); + buf[2] = (byte) (0x80 & 0xFF); + buf[3] = 0x01; + for (int i = size; i < num + size; ++i) { + buf[i] = 1; + } + assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); + assertThat(releaseLater((ByteBuf) ch.readOutbound()), is(releaseLater(wrappedBuffer(buf)))); + assertFalse(ch.finish()); + } + @Test public void testTinyEncode() { byte[] b = { 4, 1, 1, 1, 1 }; - ch.writeOutbound(wrappedBuffer(b, 1, b.length - 1)); + assertTrue(ch.writeOutbound(wrappedBuffer(b, 1, b.length - 1))); assertThat(releaseLater((ByteBuf) ch.readOutbound()), is(releaseLater(wrappedBuffer(b)))); + assertFalse(ch.finish()); } @Test @@ -49,7 +153,8 @@ public class ProtobufVarint32LengthFieldPrependerTest { } b[0] = -2; b[1] = 15; - ch.writeOutbound(wrappedBuffer(b, 2, b.length - 2)); + assertTrue(ch.writeOutbound(wrappedBuffer(b, 2, b.length - 2))); assertThat(releaseLater((ByteBuf) ch.readOutbound()), is(releaseLater(wrappedBuffer(b)))); + assertFalse(ch.finish()); } } diff --git a/license/LICENSE.protobuf.txt b/license/LICENSE.protobuf.txt index 705db579c9..0ab3bdb01a 100644 --- a/license/LICENSE.protobuf.txt +++ b/license/LICENSE.protobuf.txt @@ -1,5 +1,6 @@ -Copyright 2008, Google Inc. -All rights reserved. +Protocol Buffers - Google's data interchange format +Copyright 2013 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/pom.xml b/pom.xml index 6ec2f3c729..c8c131060b 100644 --- a/pom.xml +++ b/pom.xml @@ -288,6 +288,11 @@ protobuf-java 2.5.0 + + com.google.protobuf.nano + protobuf-javanano + 3.0.0-alpha-4 +