/*
* Copyright 2014 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.buffer;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import org.junit.Test;
import java.nio.charset.Charset;
import java.util.Random;
import static io.netty.buffer.Unpooled.unreleasableBuffer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ByteBufUtilTest {
@Test
public void equalsBufferSubsections() {
byte[] b1 = new byte[128];
byte[] b2 = new byte[256];
Random rand = new Random();
rand.nextBytes(b1);
rand.nextBytes(b2);
final int iB1 = b1.length / 2;
final int iB2 = iB1 + b1.length;
final int length = b1.length - iB1;
System.arraycopy(b1, iB1, b2, iB2, length);
assertTrue(ByteBufUtil.equals(Unpooled.wrappedBuffer(b1), iB1, Unpooled.wrappedBuffer(b2), iB2, length));
}
private static int random(Random r, int min, int max) {
return r.nextInt((max - min) + 1) + min;
}
@Test
public void notEqualsBufferSubsections() {
byte[] b1 = new byte[50];
byte[] b2 = new byte[256];
Random rand = new Random();
rand.nextBytes(b1);
rand.nextBytes(b2);
final int iB1 = b1.length / 2;
final int iB2 = iB1 + b1.length;
final int length = b1.length - iB1;
System.arraycopy(b1, iB1, b2, iB2, length);
// Randomly pick an index in the range that will be compared and make the value at that index differ between
// the 2 arrays.
int diffIndex = random(rand, iB1, iB1 + length - 1);
++b1[diffIndex];
assertFalse(ByteBufUtil.equals(Unpooled.wrappedBuffer(b1), iB1, Unpooled.wrappedBuffer(b2), iB2, length));
}
@Test
public void notEqualsBufferOverflow() {
byte[] b1 = new byte[8];
byte[] b2 = new byte[16];
Random rand = new Random();
rand.nextBytes(b1);
rand.nextBytes(b2);
final int iB1 = b1.length / 2;
final int iB2 = iB1 + b1.length;
final int length = b1.length - iB1;
System.arraycopy(b1, iB1, b2, iB2, length - 1);
assertFalse(ByteBufUtil.equals(Unpooled.wrappedBuffer(b1), iB1, Unpooled.wrappedBuffer(b2), iB2,
Math.max(b1.length, b2.length) * 2));
}
@Test (expected = IllegalArgumentException.class)
public void notEqualsBufferUnderflow() {
byte[] b1 = new byte[8];
byte[] b2 = new byte[16];
Random rand = new Random();
rand.nextBytes(b1);
rand.nextBytes(b2);
final int iB1 = b1.length / 2;
final int iB2 = iB1 + b1.length;
final int length = b1.length - iB1;
System.arraycopy(b1, iB1, b2, iB2, length - 1);
assertFalse(ByteBufUtil.equals(Unpooled.wrappedBuffer(b1), iB1, Unpooled.wrappedBuffer(b2), iB2,
-1));
}
@Test
public void testWriteUsAscii() {
String usAscii = "NettyRocks";
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeAscii(buf2, usAscii);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUsAsciiWrapped() {
String usAscii = "NettyRocks";
ByteBuf buf = unreleasableBuffer(Unpooled.buffer(16));
assertWrapped(buf);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = unreleasableBuffer(Unpooled.buffer(16));
assertWrapped(buf2);
ByteBufUtil.writeAscii(buf2, usAscii);
assertEquals(buf, buf2);
buf.unwrap().release();
buf2.unwrap().release();
}
@Test
public void testWriteUtf8() {
String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, usAscii);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8Surrogates() {
// leading surrogate + trailing surrogate
String surrogateString = new StringBuilder(2)
.append('a')
.append('\uD800')
.append('\uDC00')
.append('b')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidOnlyTrailingSurrogate() {
String surrogateString = new StringBuilder(2)
.append('a')
.append('\uDC00')
.append('b')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidOnlyLeadingSurrogate() {
String surrogateString = new StringBuilder(2)
.append('a')
.append('\uD800')
.append('b')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidSurrogatesSwitched() {
String surrogateString = new StringBuilder(2)
.append('a')
.append('\uDC00')
.append('\uD800')
.append('b')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidTwoLeadingSurrogates() {
String surrogateString = new StringBuilder(2)
.append('a')
.append('\uD800')
.append('\uD800')
.append('b')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidTwoTrailingSurrogates() {
String surrogateString = new StringBuilder(2)
.append('a')
.append('\uDC00')
.append('\uDC00')
.append('b')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidEndOnLeadingSurrogate() {
String surrogateString = new StringBuilder(2)
.append('\uD800')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8InvalidEndOnTrailingSurrogate() {
String surrogateString = new StringBuilder(2)
.append('\uDC00')
.toString();
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUsAsciiString() {
AsciiString usAscii = new AsciiString("NettyRocks");
ByteBuf buf = Unpooled.buffer(16);
buf.writeBytes(usAscii.toString().getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = Unpooled.buffer(16);
ByteBufUtil.writeAscii(buf2, usAscii);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
@Test
public void testWriteUtf8Wrapped() {
String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = unreleasableBuffer(Unpooled.buffer(16));
assertWrapped(buf);
buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = unreleasableBuffer(Unpooled.buffer(16));
assertWrapped(buf2);
ByteBufUtil.writeUtf8(buf2, usAscii);
assertEquals(buf, buf2);
buf.release();
buf2.release();
}
private static void assertWrapped(ByteBuf buf) {
assertTrue(buf instanceof WrappedByteBuf);
}
@Test
public void testDecodeUsAscii() {
testDecodeString("This is a test", CharsetUtil.US_ASCII);
}
@Test
public void testDecodeUtf8() {
testDecodeString("Some UTF-8 like äÄ∏ŒŒ", CharsetUtil.UTF_8);
}
private static void testDecodeString(String text, Charset charset) {
ByteBuf buffer = Unpooled.copiedBuffer(text, charset);
assertEquals(text, ByteBufUtil.decodeString(buffer, 0, buffer.readableBytes(), charset));
buffer.release();
}
@Test
public void testToStringDoesNotThrowIndexOutOfBounds() {
CompositeByteBuf buffer = Unpooled.compositeBuffer();
try {
byte[] bytes = "1234".getBytes(CharsetUtil.UTF_8);
buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes));
buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes));
assertEquals("1234", buffer.toString(bytes.length, bytes.length, CharsetUtil.UTF_8));
} finally {
buffer.release();
}
}
@Test
public void testIsTextWithUtf8() {
byte[][] validUtf8Bytes = new byte[][]{
"netty".getBytes(CharsetUtil.UTF_8),
new byte[]{(byte) 0x24},
new byte[]{(byte) 0xC2, (byte) 0xA2},
new byte[]{(byte) 0xE2, (byte) 0x82, (byte) 0xAC},
new byte[]{(byte) 0xF0, (byte) 0x90, (byte) 0x8D, (byte) 0x88},
new byte[]{(byte) 0x24,
(byte) 0xC2, (byte) 0xA2,
(byte) 0xE2, (byte) 0x82, (byte) 0xAC,
(byte) 0xF0, (byte) 0x90, (byte) 0x8D, (byte) 0x88} // multiple characters
};
for (byte[] bytes : validUtf8Bytes) {
assertIsText(bytes, true, CharsetUtil.UTF_8);
}
byte[][] invalidUtf8Bytes = new byte[][]{
new byte[]{(byte) 0x80},
new byte[]{(byte) 0xF0, (byte) 0x82, (byte) 0x82, (byte) 0xAC}, // Overlong encodings
new byte[]{(byte) 0xC2}, // not enough bytes
new byte[]{(byte) 0xE2, (byte) 0x82}, // not enough bytes
new byte[]{(byte) 0xF0, (byte) 0x90, (byte) 0x8D}, // not enough bytes
new byte[]{(byte) 0xC2, (byte) 0xC0}, // not correct bytes
new byte[]{(byte) 0xE2, (byte) 0x82, (byte) 0xC0}, // not correct bytes
new byte[]{(byte) 0xF0, (byte) 0x90, (byte) 0x8D, (byte) 0xC0}, // not correct bytes
new byte[]{(byte) 0xC1, (byte) 0x80}, // out of lower bound
new byte[]{(byte) 0xE0, (byte) 0x80, (byte) 0x80}, // out of lower bound
new byte[]{(byte) 0xED, (byte) 0xAF, (byte) 0x80} // out of upper bound
};
for (byte[] bytes : invalidUtf8Bytes) {
assertIsText(bytes, false, CharsetUtil.UTF_8);
}
}
@Test
public void testIsTextWithoutOptimization() {
byte[] validBytes = new byte[]{(byte) 0x01, (byte) 0xD8, (byte) 0x37, (byte) 0xDC};
byte[] invalidBytes = new byte[]{(byte) 0x01, (byte) 0xD8};
assertIsText(validBytes, true, CharsetUtil.UTF_16LE);
assertIsText(invalidBytes, false, CharsetUtil.UTF_16LE);
}
@Test
public void testIsTextWithAscii() {
byte[] validBytes = new byte[]{(byte) 0x00, (byte) 0x01, (byte) 0x37, (byte) 0x7F};
byte[] invalidBytes = new byte[]{(byte) 0x80, (byte) 0xFF};
assertIsText(validBytes, true, CharsetUtil.US_ASCII);
assertIsText(invalidBytes, false, CharsetUtil.US_ASCII);
}
@Test
public void testIsTextWithInvalidIndexAndLength() {
ByteBuf buffer = Unpooled.buffer();
try {
buffer.writeBytes(new byte[4]);
int[][] validIndexLengthPairs = new int[][] {
new int[]{4, 0},
new int[]{0, 4},
new int[]{1, 3},
};
for (int[] pair : validIndexLengthPairs) {
assertTrue(ByteBufUtil.isText(buffer, pair[0], pair[1], CharsetUtil.US_ASCII));
}
int[][] invalidIndexLengthPairs = new int[][]{
new int[]{4, 1},
new int[]{-1, 2},
new int[]{3, -1},
new int[]{3, -2},
new int[]{5, 0},
new int[]{1, 5},
};
for (int[] pair : invalidIndexLengthPairs) {
try {
ByteBufUtil.isText(buffer, pair[0], pair[1], CharsetUtil.US_ASCII);
fail("Expected IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
// expected
}
}
} finally {
buffer.release();
}
}
private static void assertIsText(byte[] bytes, boolean expected, Charset charset) {
ByteBuf buffer = Unpooled.buffer();
try {
buffer.writeBytes(bytes);
assertEquals(expected, ByteBufUtil.isText(buffer, charset));
} finally {
buffer.release();
}
}
}