[#1907] LengthFieldPrepender should better extend MessageToMessageEncoder for less memory copies

This commit is contained in:
Norman Maurer 2013-10-11 06:38:18 +02:00
parent 16bf687aec
commit 42cce12498
2 changed files with 31 additions and 16 deletions

View File

@ -19,6 +19,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import java.util.List;
/** /**
* An encoder that prepends the length of the message. The length value is * An encoder that prepends the length of the message. The length value is
@ -47,7 +49,7 @@ import io.netty.channel.ChannelHandlerContext;
* </pre> * </pre>
*/ */
@Sharable @Sharable
public class LengthFieldPrepender extends MessageToByteEncoder<ByteBuf> { public class LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf> {
private final int lengthFieldLength; private final int lengthFieldLength;
private final boolean lengthIncludesLengthFieldLength; private final boolean lengthIncludesLengthFieldLength;
@ -128,7 +130,7 @@ public class LengthFieldPrepender extends MessageToByteEncoder<ByteBuf> {
} }
@Override @Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
int length = msg.readableBytes() + lengthAdjustment; int length = msg.readableBytes() + lengthAdjustment;
if (lengthIncludesLengthFieldLength) { if (lengthIncludesLengthFieldLength) {
length += lengthFieldLength; length += lengthFieldLength;
@ -145,32 +147,31 @@ public class LengthFieldPrepender extends MessageToByteEncoder<ByteBuf> {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"length does not fit into a byte: " + length); "length does not fit into a byte: " + length);
} }
out.writeByte((byte) length); out.add(ctx.alloc().buffer(1).writeByte((byte) length));
break; break;
case 2: case 2:
if (length >= 65536) { if (length >= 65536) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"length does not fit into a short integer: " + length); "length does not fit into a short integer: " + length);
} }
out.writeShort((short) length); out.add(ctx.alloc().buffer(2).writeShort((short) length));
break; break;
case 3: case 3:
if (length >= 16777216) { if (length >= 16777216) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"length does not fit into a medium integer: " + length); "length does not fit into a medium integer: " + length);
} }
out.writeMedium(length); out.add(ctx.alloc().buffer(3).writeMedium(length));
break; break;
case 4: case 4:
out.writeInt(length); out.add(ctx.alloc().buffer(4).writeInt(length));
break; break;
case 8: case 8:
out.writeLong(length); out.add(ctx.alloc().buffer(8).writeLong(length));
break; break;
default: default:
throw new Error("should not reach here"); throw new Error("should not reach here");
} }
out.add(msg.retain());
out.writeBytes(msg, msg.readerIndex(), msg.readableBytes());
} }
} }

View File

@ -24,7 +24,6 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static io.netty.buffer.Unpooled.*; import static io.netty.buffer.Unpooled.*;
import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class LengthFieldPrependerTest { public class LengthFieldPrependerTest {
@ -40,8 +39,13 @@ public class LengthFieldPrependerTest {
public void testPrependLength() throws Exception { public void testPrependLength() throws Exception {
final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4)); final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4));
ch.writeOutbound(msg); ch.writeOutbound(msg);
final ByteBuf buf = (ByteBuf) ch.readOutbound(); ByteBuf buf = (ByteBuf) ch.readOutbound();
assertThat(buf, is(wrappedBuffer(new byte[]{0, 0, 0, 1, 'A'}))); assertEquals(4, buf.readableBytes());
assertEquals(msg.readableBytes(), buf.readInt());
buf.release();
buf = (ByteBuf) ch.readOutbound();
assertSame(buf, msg);
buf.release(); buf.release();
} }
@ -49,8 +53,13 @@ public class LengthFieldPrependerTest {
public void testPrependLengthIncludesLengthFieldLength() throws Exception { public void testPrependLengthIncludesLengthFieldLength() throws Exception {
final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, true)); final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, true));
ch.writeOutbound(msg); ch.writeOutbound(msg);
final ByteBuf buf = (ByteBuf) ch.readOutbound(); ByteBuf buf = (ByteBuf) ch.readOutbound();
assertThat(buf, is(wrappedBuffer(new byte[]{0, 0, 0, 5, 'A'}))); assertEquals(4, buf.readableBytes());
assertEquals(5, buf.readInt());
buf.release();
buf = (ByteBuf) ch.readOutbound();
assertSame(buf, msg);
buf.release(); buf.release();
} }
@ -58,8 +67,13 @@ public class LengthFieldPrependerTest {
public void testPrependAdjustedLength() throws Exception { public void testPrependAdjustedLength() throws Exception {
final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, -1)); final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, -1));
ch.writeOutbound(msg); ch.writeOutbound(msg);
final ByteBuf buf = (ByteBuf) ch.readOutbound(); ByteBuf buf = (ByteBuf) ch.readOutbound();
assertThat(buf, is(wrappedBuffer(new byte[]{0, 0, 0, 0, 'A'}))); assertEquals(4, buf.readableBytes());
assertEquals(msg.readableBytes() - 1, buf.readInt());
buf.release();
buf = (ByteBuf) ch.readOutbound();
assertSame(buf, msg);
buf.release(); buf.release();
} }