diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java index e91a35241c..f52840d6f6 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java @@ -263,7 +263,7 @@ public class Snappy { inputLength = readPreamble(in); } - if (inputLength == 0 || in.readerIndex() - inIndex + in.readableBytes() < maxLength) { + if (inputLength == 0 || in.readerIndex() - inIndex + in.readableBytes() < maxLength - 5) { // Wait until we've got the entire chunk before continuing return; } @@ -330,22 +330,22 @@ public class Snappy { int length; switch(tag >> 2 & 0x3F) { case 60: - length = in.readByte() & 0x0ff; + length = in.readUnsignedByte(); break; case 61: - length = (in.readByte() & 0x0ff) - + ((in.readByte() & 0x0ff) << 8); + length = in.readUnsignedByte() + | (in.readUnsignedByte() << 8); break; case 62: - length = (in.readByte() & 0x0ff) - + ((in.readByte() & 0x0ff) << 8) - + ((in.readByte() & 0x0ff) << 16); + length = in.readUnsignedByte() + | (in.readUnsignedByte() << 8) + | (in.readUnsignedByte() << 16); break; case 64: - length = (in.readByte() & 0x0ff) - + ((in.readByte() & 0x0ff) << 8) - + ((in.readByte() & 0x0ff) << 16) - + ((in.readByte() & 0x0ff) << 24); + length = in.readUnsignedByte() + | (in.readUnsignedByte() << 8) + | (in.readUnsignedByte() << 16) + | (in.readUnsignedByte() << 24); break; default: length = tag >> 2 & 0x3F; @@ -368,9 +368,8 @@ public class Snappy { */ private static void decodeCopyWith1ByteOffset(byte tag, ByteBuf in, ByteBuf out) { int initialIndex = in.readerIndex(); - int length = 4 + ((tag & 0x0c) >> 2); - int offset = 1 + ((tag & 0x0e0) << 8) - + (in.readByte() & 0x0ff); + int length = 4 + ((tag & 0x01c) >> 2); + int offset = 1 + ((tag & 0x0e0) << 8 | in.readUnsignedByte()); validateOffset(offset, initialIndex); @@ -394,8 +393,7 @@ public class Snappy { private static void decodeCopyWith2ByteOffset(byte tag, ByteBuf in, ByteBuf out) { int initialIndex = in.readerIndex(); int length = 1 + (tag >> 2 & 0x03f); - int offset = 1 + (in.readByte() & 0x0ff) - + (in.readByte() & 0x0ff) << 8; + int offset = 1 + (in.readUnsignedByte() | in.readUnsignedByte() << 8); validateOffset(offset, initialIndex); @@ -419,10 +417,10 @@ public class Snappy { private static void decodeCopyWith4ByteOffset(byte tag, ByteBuf in, ByteBuf out) { int initialIndex = in.readerIndex(); int length = 1 + (tag >> 2 & 0x03F); - int offset = 1 + (in.readByte() & 0x0ff) - + ((in.readByte() & 0x0ff) << 8) - + ((in.readByte() & 0x0ff) << 16) - + ((in.readByte() & 0x0ff) << 24); + int offset = 1 + (in.readUnsignedByte() | + in.readUnsignedByte() << 8 | + in.readUnsignedByte() << 16 | + in.readUnsignedByte() << 24); validateOffset(offset, initialIndex); diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyChecksumUtil.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyChecksumUtil.java index 7209b514b4..643dc48fcc 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyChecksumUtil.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyChecksumUtil.java @@ -44,15 +44,11 @@ public final class SnappyChecksumUtil { public static int calculateChecksum(ByteBuf slice) { CRC32 crc32 = new CRC32(); try { - if (slice.hasArray()) { - crc32.update(slice.array()); - } else { - byte[] array = new byte[slice.readableBytes()]; - slice.markReaderIndex(); - slice.readBytes(array); - slice.resetReaderIndex(); - crc32.update(array); - } + byte[] array = new byte[slice.readableBytes()]; + slice.markReaderIndex(); + slice.readBytes(array); + slice.resetReaderIndex(); + crc32.update(array); return maskChecksum((int) crc32.getValue()); } finally { diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java index f5ddd97c18..e6cbe54033 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java @@ -84,8 +84,7 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { byte type = in.readByte(); chunkType = mapChunkType(type); - chunkLength = (in.readByte() & 0x0ff) - + ((in.readByte() & 0x0ff) << 8); + chunkLength = in.readUnsignedByte() | in.readUnsignedByte() << 8; // The spec mandates that reserved unskippable chunks must immediately // return an error, as we must assume that we cannot decode the stream @@ -130,10 +129,10 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { if (!started) { throw new CompressionException("Received UNCOMPRESSED_DATA tag before STREAM_IDENTIFIER"); } - checksum = in.readByte() & 0x0ff - + (in.readByte() << 8 & 0x0ff) - + (in.readByte() << 16 & 0x0ff) - + (in.readByte() << 24 & 0x0ff); + checksum = in.readUnsignedByte() + | (in.readUnsignedByte() << 8) + | (in.readUnsignedByte() << 16) + | (in.readUnsignedByte() << 24); if (validateChecksums) { ByteBuf data = in.readBytes(chunkLength); validateChecksum(data, checksum); @@ -146,10 +145,10 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { if (!started) { throw new CompressionException("Received COMPRESSED_DATA tag before STREAM_IDENTIFIER"); } - checksum = in.readByte() & 0x0ff - + (in.readByte() << 8 & 0x0ff) - + (in.readByte() << 16 & 0x0ff) - + (in.readByte() << 24 & 0x0ff); + checksum = in.readUnsignedByte() + | (in.readUnsignedByte() << 8) + | (in.readUnsignedByte() << 16) + | (in.readUnsignedByte() << 24); if (validateChecksums) { ByteBuf uncompressed = ctx.alloc().buffer(); snappy.decode(in, uncompressed, chunkLength); diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java index 7f7e371307..796936a49a 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java @@ -15,12 +15,12 @@ */ package io.netty.handler.codec.compression; +import static io.netty.handler.codec.compression.SnappyChecksumUtil.calculateChecksum; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToByteEncoder; -import static io.netty.handler.codec.compression.SnappyChecksumUtil.*; - /** * Compresses a {@link ByteBuf} using the Snappy framing format. * @@ -64,20 +64,19 @@ public class SnappyFramedEncoder extends ByteToByteEncoder { int subChunkLength = Math.min(Short.MAX_VALUE, chunkLength); out.writeByte(0); writeChunkLength(out, subChunkLength); - ByteBuf slice = in.slice(); + ByteBuf slice = in.slice(in.readerIndex(), subChunkLength); calculateAndWriteChecksum(slice, out); snappy.encode(slice, out, subChunkLength); + + in.readerIndex(slice.readerIndex()); } } else { out.writeByte(1); writeChunkLength(out, chunkLength); - ByteBuf slice = in.slice(); - calculateAndWriteChecksum(slice, out); - out.writeBytes(slice); + calculateAndWriteChecksum(in, out); + out.writeBytes(in, chunkLength); } - - in.readerIndex(in.readerIndex() + chunkLength); } /** diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java index 06d43781f9..9025111b55 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java @@ -17,12 +17,13 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedByteChannel; import org.junit.Test; import static org.junit.Assert.*; public class SnappyFramedDecoderTest { - private final SnappyFramedDecoder decoder = new SnappyFramedDecoder(); + private final EmbeddedByteChannel channel = new EmbeddedByteChannel(new SnappyFramedDecoder()); @Test(expected = CompressionException.class) public void testReservedUnskippableChunkTypeCausesError() throws Exception { @@ -30,7 +31,7 @@ public class SnappyFramedDecoderTest { 0x03, 0x01, 0x00, 0x00 }); - decoder.decode(null, in, null); + channel.writeInbound(in); } @Test(expected = CompressionException.class) @@ -39,7 +40,7 @@ public class SnappyFramedDecoderTest { -0x80, 0x05, 0x00, 'n', 'e', 't', 't', 'y' }); - decoder.decode(null, in, null); + channel.writeInbound(in); } @Test(expected = CompressionException.class) @@ -48,7 +49,7 @@ public class SnappyFramedDecoderTest { -0x80, 0x06, 0x00, 's', 'n', 'e', 't', 't', 'y' }); - decoder.decode(null, in, null); + channel.writeInbound(in); } @Test(expected = CompressionException.class) @@ -57,7 +58,7 @@ public class SnappyFramedDecoderTest { -0x7f, 0x06, 0x00, 's', 'n', 'e', 't', 't', 'y' }); - decoder.decode(null, in, null); + channel.writeInbound(in); } @Test(expected = CompressionException.class) @@ -66,7 +67,7 @@ public class SnappyFramedDecoderTest { 0x01, 0x05, 0x00, 'n', 'e', 't', 't', 'y' }); - decoder.decode(null, in, null); + channel.writeInbound(in); } @Test(expected = CompressionException.class) @@ -75,7 +76,7 @@ public class SnappyFramedDecoderTest { 0x00, 0x05, 0x00, 'n', 'e', 't', 't', 'y' }); - decoder.decode(null, in, null); + channel.writeInbound(in); } @Test @@ -85,9 +86,8 @@ public class SnappyFramedDecoderTest { -0x7f, 0x05, 0x00, 'n', 'e', 't', 't', 'y' }); - ByteBuf out = Unpooled.unmodifiableBuffer(Unpooled.EMPTY_BUFFER); - - decoder.decode(null, in, out); + channel.writeInbound(in); + assertNull(channel.readInbound()); assertEquals(17, in.readerIndex()); } @@ -99,14 +99,10 @@ public class SnappyFramedDecoderTest { 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 'n', 'e', 't', 't', 'y' }); - ByteBuf out = Unpooled.buffer(5); + channel.writeInbound(in); - decoder.decode(null, in, out); - - byte[] expected = { - 'n', 'e', 't', 't', 'y' - }; - assertArrayEquals(expected, out.array()); + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 'n', 'e', 't', 't', 'y' }); + assertEquals(expected, channel.readInbound()); } @Test @@ -119,13 +115,9 @@ public class SnappyFramedDecoderTest { 0x6e, 0x65, 0x74, 0x74, 0x79 // "netty" }); - ByteBuf out = Unpooled.buffer(5); + channel.writeInbound(in); - decoder.decode(null, in, out); - - byte[] expected = { - 'n', 'e', 't', 't', 'y' - }; - assertArrayEquals(expected, out.array()); + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 'n', 'e', 't', 't', 'y' }); + assertEquals(expected, channel.readInbound()); } } diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java index f8cd4c1225..2083ec97c7 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java @@ -17,12 +17,19 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedByteChannel; +import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class SnappyFramedEncoderTest { - private final SnappyFramedEncoder encoder = new SnappyFramedEncoder(); + private EmbeddedByteChannel channel; + + @Before + public void setUp() { + channel = new EmbeddedByteChannel(new SnappyFramedEncoder()); + } @Test public void testSmallAmountOfDataIsUncompressed() throws Exception { @@ -30,15 +37,14 @@ public class SnappyFramedEncoderTest { 'n', 'e', 't', 't', 'y' }); - ByteBuf out = Unpooled.buffer(21); + channel.writeOutbound(in); + assertTrue(channel.finish()); - encoder.encode(null, in, out); - - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { -0x80, 0x06, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, 0x01, 0x05, 0x00, 0x2d, -0x5a, -0x7e, -0x5e, 'n', 'e', 't', 't', 'y' - }; - assertArrayEquals(expected, out.array()); + }); + assertEquals(expected, channel.readOutbound()); } @Test @@ -48,18 +54,17 @@ public class SnappyFramedEncoderTest { 'n', 'e', 't', 't', 'y', 'n', 'e', 't', 't', 'y' }); - ByteBuf out = Unpooled.buffer(26); + channel.writeOutbound(in); + assertTrue(channel.finish()); - encoder.encode(null, in, out); - - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { -0x80, 0x06, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, 0x00, 0x14, 0x00, 0x7b, 0x1f, 0x65, 0x64, 0x14, 0x10, 'n', 'e', 't', 't', 'y', 0x3a, 0x05, 0x00 - }; - assertArrayEquals(expected, out.array()); + }); + assertEquals(expected, channel.readOutbound()); } @Test @@ -68,18 +73,17 @@ public class SnappyFramedEncoderTest { 'n', 'e', 't', 't', 'y' }); - ByteBuf out = Unpooled.buffer(33); - - encoder.encode(null, in, out); + channel.writeOutbound(in); in.readerIndex(0); // rewind the buffer to write the same data - encoder.encode(null, in, out); + channel.writeOutbound(in); + assertTrue(channel.finish()); - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { -0x80, 0x06, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, 0x01, 0x05, 0x00, 0x2d, -0x5a, -0x7e, -0x5e, 'n', 'e', 't', 't', 'y', 0x01, 0x05, 0x00, 0x2d, -0x5a, -0x7e, -0x5e, 'n', 'e', 't', 't', 'y', - }; - assertArrayEquals(expected, out.array()); + }); + assertEquals(expected, channel.readOutbound()); } /** @@ -123,9 +127,8 @@ public class SnappyFramedEncoderTest { -1, -1, -1, // copy -1, 1 // remainder }); - - ByteBuf out = Unpooled.buffer(274); - encoder.encode(null, in, out); + channel.writeOutbound(in); + assertTrue(channel.finish()); } } diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java new file mode 100644 index 0000000000..5017bf6216 --- /dev/null +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 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.compression; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.util.CharsetUtil; + +import org.junit.Assert; +import org.junit.Test; + +public class SnappyIntegrationTest { + private final EmbeddedByteChannel channel = new EmbeddedByteChannel(new SnappyFramedEncoder(), + new SnappyFramedDecoder()); + + @Test + public void testEncoderDecoderIdentity() throws Exception { + ByteBuf in = Unpooled.copiedBuffer( + "Netty has been designed carefully with the experiences " + + "earned from the implementation of a lot of protocols " + + "such as FTP, SMTP, HTTP, and various binary and " + + "text-based legacy protocols", CharsetUtil.US_ASCII + ); + channel.writeOutbound(in); + + channel.writeInbound(channel.readOutbound()); + + Assert.assertEquals(in, channel.readInbound()); + } +} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java index 87591e0d0e..58ab183610 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java @@ -18,9 +18,10 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.junit.After; -import org.junit.Assert; import org.junit.Test; +import static org.junit.Assert.*; + public class SnappyTest { private final Snappy snappy = new Snappy(); @@ -40,35 +41,35 @@ public class SnappyTest { snappy.decode(in, out, 7); // "netty" - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 0x6e, 0x65, 0x74, 0x74, 0x79 - }; - Assert.assertArrayEquals("Literal was not decoded correctly", expected, out.array()); + }); + assertEquals("Literal was not decoded correctly", expected, out); } @Test public void testDecodeCopyWith1ByteOffset() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x01, // preamble length + 0x0a, // preamble length 0x04 << 2, // literal tag + length 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" - 0x05 << 2 | 0x01, // copy with 1-byte offset + length + 0x01 << 2 | 0x01, // copy with 1-byte offset + length 0x05 // offset }); ByteBuf out = Unpooled.buffer(10); - snappy.decode(in, out, 9); + snappy.decode(in, out, 10); // "nettynetty" - we saved a whole byte :) - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 0x6e, 0x65, 0x74, 0x74, 0x79, 0x6e, 0x65, 0x74, 0x74, 0x79 - }; - Assert.assertArrayEquals("Copy was not decoded correctly", expected, out.array()); + }); + assertEquals("Copy was not decoded correctly", expected, out); } @Test(expected = CompressionException.class) public void testDecodeCopyWithTinyOffset() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x01, // preamble length + 0x0a, // preamble length 0x04 << 2, // literal tag + length 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" 0x05 << 2 | 0x01, // copy with 1-byte offset + length @@ -81,7 +82,7 @@ public class SnappyTest { @Test(expected = CompressionException.class) public void testDecodeCopyWithOffsetBeforeChunk() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x01, // preamble length + 0x0a, // preamble length 0x04 << 2, // literal tag + length 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" 0x05 << 2 | 0x01, // copy with 1-byte offset + length @@ -110,12 +111,12 @@ public class SnappyTest { ByteBuf out = Unpooled.buffer(7); snappy.encode(in, out, 5); - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 0x05, // preamble length 0x04 << 2, // literal tag + length 0x6e, 0x65, 0x74, 0x74, 0x79 // "netty" - }; - Assert.assertArrayEquals("Encoded literal was invalid", expected, out.array()); + }); + assertEquals("Encoded literal was invalid", expected, out); } @Test @@ -132,7 +133,7 @@ public class SnappyTest { // The only compressibility in the above are the words "the ", // and "protocols", so this is a literal, followed by a copy // followed by another literal, followed by another copy - byte[] expected = { + ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { -0x49, 0x01, // preamble length -0x10, 0x42, // literal tag + length @@ -163,8 +164,8 @@ public class SnappyTest { // Second copy (protocols) 0x15, 0x4c - }; + }); - Assert.assertArrayEquals("Encoded result was incorrect", expected, out.array()); + assertEquals("Encoded result was incorrect", expected, out); } }