Speed up chunked framing 13/13413/3
authorRobert Varga <rovarga@cisco.com>
Fri, 5 Dec 2014 12:37:41 +0000 (13:37 +0100)
committerRobert Varga <rovarga@cisco.com>
Fri, 5 Dec 2014 15:35:05 +0000 (16:35 +0100)
Instead of allocating temporary byte arrays and ByteBufs, insert chunk
headers explicitly and transfer bytes directly from input message to
output.

Change-Id: Id300cbb9bc39b36e9a07def3d50c948edda7f570
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoder.java
opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoderTest.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageConstants.java

index c4cddb802ea35dd51b17d49d832114c404ae5085..2287a58602c7b9fd9638e17e4f26428c75c9f284 100644 (file)
@@ -8,13 +8,12 @@
 
 package org.opendaylight.controller.netconf.nettyutil.handler;
 
+import com.google.common.base.Charsets;
 import com.google.common.base.Preconditions;
 import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToByteEncoder;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageConstants;
-import org.opendaylight.controller.netconf.util.messages.NetconfMessageHeader;
 
 public class ChunkedFramingMechanismEncoder extends MessageToByteEncoder<ByteBuf> {
     public static final int DEFAULT_CHUNK_SIZE = 8192;
@@ -27,9 +26,8 @@ public class ChunkedFramingMechanismEncoder extends MessageToByteEncoder<ByteBuf
         this(DEFAULT_CHUNK_SIZE);
     }
 
-    public ChunkedFramingMechanismEncoder(int chunkSize) {
-        Preconditions.checkArgument(chunkSize > MIN_CHUNK_SIZE);
-        Preconditions.checkArgument(chunkSize < MAX_CHUNK_SIZE);
+    public ChunkedFramingMechanismEncoder(final int chunkSize) {
+        Preconditions.checkArgument(chunkSize >= MIN_CHUNK_SIZE && chunkSize <= MAX_CHUNK_SIZE, "Unsupported chunk size %s", chunkSize);
         this.chunkSize = chunkSize;
     }
 
@@ -38,19 +36,17 @@ public class ChunkedFramingMechanismEncoder extends MessageToByteEncoder<ByteBuf
     }
 
     @Override
-    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out)  {
-        while (msg.readableBytes() > chunkSize) {
-            ByteBuf chunk = Unpooled.buffer(chunkSize);
-            chunk.writeBytes(createChunkHeader(chunkSize));
-            chunk.writeBytes(msg.readBytes(chunkSize));
-            ctx.write(chunk);
-        }
-        out.writeBytes(createChunkHeader(msg.readableBytes()));
-        out.writeBytes(msg.readBytes(msg.readableBytes()));
-        out.writeBytes(NetconfMessageConstants.END_OF_CHUNK);
-    }
+    protected void encode(final ChannelHandlerContext ctx, final ByteBuf msg, final ByteBuf out)  {
+        do {
+            final int xfer = Math.min(chunkSize, msg.readableBytes());
+
+            out.writeBytes(NetconfMessageConstants.START_OF_CHUNK);
+            out.writeBytes(String.valueOf(xfer).getBytes(Charsets.US_ASCII));
+            out.writeByte('\n');
 
-    private ByteBuf createChunkHeader(int chunkSize) {
-        return Unpooled.wrappedBuffer(NetconfMessageHeader.toBytes(chunkSize));
+            out.writeBytes(msg, xfer);
+        } while (msg.isReadable());
+
+        out.writeBytes(NetconfMessageConstants.END_OF_CHUNK);
     }
 }
index 556bece43f372b28b5c0aea12d096152638dd2a3..4488c5e1be63bc2f82bfb01575df6b5cbaa7be33 100644 (file)
@@ -9,20 +9,16 @@
 package org.opendaylight.controller.netconf.nettyutil.handler;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Mockito.doAnswer;
-import com.google.common.collect.Lists;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Charsets;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import java.util.List;
+import java.nio.ByteBuffer;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.opendaylight.controller.netconf.util.messages.NetconfMessageConstants;
 
 public class ChunkedFramingMechanismEncoderTest {
 
@@ -48,30 +44,20 @@ public class ChunkedFramingMechanismEncoderTest {
 
     @Test
     public void testEncode() throws Exception {
-        final List<ByteBuf> chunks = Lists.newArrayList();
-        doAnswer(new Answer<Object>() {
-            @Override
-            public Object answer(final InvocationOnMock invocation) throws Throwable {
-                chunks.add((ByteBuf) invocation.getArguments()[0]);
-                return null;
-            }
-        }).when(ctx).write(anyObject());
-
         final ChunkedFramingMechanismEncoder encoder = new ChunkedFramingMechanismEncoder(chunkSize);
         final int lastChunkSize = 20;
         final ByteBuf src = Unpooled.wrappedBuffer(getByteArray(chunkSize * 4 + lastChunkSize));
         final ByteBuf destination = Unpooled.buffer();
         encoder.encode(ctx, src, destination);
-        assertEquals(4, chunks.size());
 
-        final int framingSize = "#256\n".getBytes().length + 1/* new line at end */;
+        assertEquals(1077, destination.readableBytes());
 
-        for (final ByteBuf chunk : chunks) {
-            assertEquals(chunkSize + framingSize, chunk.readableBytes());
-        }
+        byte[] buf = new byte[destination.readableBytes()];
+        destination.readBytes(buf);
+        String s = Charsets.US_ASCII.decode(ByteBuffer.wrap(buf)).toString();
 
-        final int lastFramingSize = "#20\n".length() + NetconfMessageConstants.END_OF_CHUNK.length + 1/* new line at end */;
-        assertEquals(lastChunkSize + lastFramingSize, destination.readableBytes());
+        assertTrue(s.startsWith("\n#256\na"));
+        assertTrue(s.endsWith("\n#20\naaaaaaaaaaaaaaaaaaaa\n##\n"));
     }
 
     private byte[] getByteArray(final int size) {
index 5c2770a8c1f2387211f4cdcf1a1ef762ef8f11d8..89285d18c021fe4bcaf9eec7dc9b3a4deb662715 100644 (file)
@@ -27,6 +27,7 @@ public final class NetconfMessageConstants {
 
     public static final int MAX_HEADER_LENGTH = 13;
 
+    public static final byte[] START_OF_CHUNK = "\n#".getBytes(Charsets.UTF_8);
     public static final byte[] END_OF_CHUNK = "\n##\n".getBytes(Charsets.UTF_8);
 
 }