2af769f6dc
Motivation It would be useful to be able to write UTF-8 encoded subsequence of CharSequence characters to a ByteBuf without needing to create a temporary object via CharSequence#subSequence(). Modification Add overloads of ByteBufUtil writeUtf8, reserveAndWriteUtf8 and utf8Bytes methods which take explicit subsequence bounds. Result More efficient writing of substrings to byte buffers possible
805 lines
28 KiB
Java
805 lines
28 KiB
Java
/*
|
|
* 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.ByteOrder;
|
|
import java.nio.charset.Charset;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import static io.netty.buffer.Unpooled.unreleasableBuffer;
|
|
import static org.junit.Assert.assertArrayEquals;
|
|
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 decodeRandomHexBytesWithEvenLength() {
|
|
decodeRandomHexBytes(256);
|
|
}
|
|
|
|
@Test
|
|
public void decodeRandomHexBytesWithOddLength() {
|
|
decodeRandomHexBytes(257);
|
|
}
|
|
|
|
private static void decodeRandomHexBytes(int len) {
|
|
byte[] b = new byte[len];
|
|
Random rand = new Random();
|
|
rand.nextBytes(b);
|
|
String hexDump = ByteBufUtil.hexDump(b);
|
|
for (int i = 0; i <= len; i++) { // going over sub-strings of various lengths including empty byte[].
|
|
byte[] b2 = Arrays.copyOfRange(b, i, b.length);
|
|
byte[] decodedBytes = ByteBufUtil.decodeHexDump(hexDump, i * 2, (len - i) * 2);
|
|
assertArrayEquals(b2, decodedBytes);
|
|
}
|
|
}
|
|
|
|
@Test(expected = IllegalArgumentException.class)
|
|
public void decodeHexDumpWithOddLength() {
|
|
ByteBufUtil.decodeHexDump("abc");
|
|
}
|
|
|
|
@Test(expected = IllegalArgumentException.class)
|
|
public void decodeHexDumpWithInvalidChar() {
|
|
ByteBufUtil.decodeHexDump("fg");
|
|
}
|
|
|
|
@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));
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void writeShortBE() {
|
|
int expected = 0x1234;
|
|
|
|
ByteBuf buf = Unpooled.buffer(2).order(ByteOrder.BIG_ENDIAN);
|
|
ByteBufUtil.writeShortBE(buf, expected);
|
|
assertEquals(expected, buf.readShort());
|
|
buf.resetReaderIndex();
|
|
assertEquals(ByteBufUtil.swapShort((short) expected), buf.readShortLE());
|
|
buf.release();
|
|
|
|
buf = Unpooled.buffer(2).order(ByteOrder.LITTLE_ENDIAN);
|
|
ByteBufUtil.writeShortBE(buf, expected);
|
|
assertEquals((short) expected, buf.readShortLE());
|
|
buf.resetReaderIndex();
|
|
assertEquals(ByteBufUtil.swapShort((short) expected), buf.readShort());
|
|
buf.release();
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void setShortBE() {
|
|
int shortValue = 0x1234;
|
|
|
|
ByteBuf buf = Unpooled.wrappedBuffer(new byte[2]).order(ByteOrder.BIG_ENDIAN);
|
|
ByteBufUtil.setShortBE(buf, 0, shortValue);
|
|
assertEquals(shortValue, buf.readShort());
|
|
buf.resetReaderIndex();
|
|
assertEquals(ByteBufUtil.swapShort((short) shortValue), buf.readShortLE());
|
|
buf.release();
|
|
|
|
buf = Unpooled.wrappedBuffer(new byte[2]).order(ByteOrder.LITTLE_ENDIAN);
|
|
ByteBufUtil.setShortBE(buf, 0, shortValue);
|
|
assertEquals((short) shortValue, buf.readShortLE());
|
|
buf.resetReaderIndex();
|
|
assertEquals(ByteBufUtil.swapShort((short) shortValue), buf.readShort());
|
|
buf.release();
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void writeMediumBE() {
|
|
int mediumValue = 0x123456;
|
|
|
|
ByteBuf buf = Unpooled.buffer(4).order(ByteOrder.BIG_ENDIAN);
|
|
ByteBufUtil.writeMediumBE(buf, mediumValue);
|
|
assertEquals(mediumValue, buf.readMedium());
|
|
buf.resetReaderIndex();
|
|
assertEquals(ByteBufUtil.swapMedium(mediumValue), buf.readMediumLE());
|
|
buf.release();
|
|
|
|
buf = Unpooled.buffer(4).order(ByteOrder.LITTLE_ENDIAN);
|
|
ByteBufUtil.writeMediumBE(buf, mediumValue);
|
|
assertEquals(mediumValue, buf.readMediumLE());
|
|
buf.resetReaderIndex();
|
|
assertEquals(ByteBufUtil.swapMedium(mediumValue), buf.readMedium());
|
|
buf.release();
|
|
}
|
|
|
|
@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 testWriteUsAsciiSwapped() {
|
|
String usAscii = "NettyRocks";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
|
|
SwappedByteBuf buf2 = new SwappedByteBuf(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 testWriteUsAsciiComposite() {
|
|
String usAscii = "NettyRocks";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
|
|
ByteBuf buf2 = Unpooled.compositeBuffer().addComponent(
|
|
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24));
|
|
// write some byte so we start writing with an offset.
|
|
buf2.writeByte(1);
|
|
ByteBufUtil.writeAscii(buf2, usAscii);
|
|
|
|
// Skip the previously written byte.
|
|
assertEquals(buf, buf2.skipBytes(1));
|
|
|
|
buf.release();
|
|
buf2.release();
|
|
}
|
|
|
|
@Test
|
|
public void testWriteUsAsciiCompositeWrapped() {
|
|
String usAscii = "NettyRocks";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
|
|
ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent(
|
|
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)));
|
|
// write some byte so we start writing with an offset.
|
|
buf2.writeByte(1);
|
|
ByteBufUtil.writeAscii(buf2, usAscii);
|
|
|
|
// Skip the previously written byte.
|
|
assertEquals(buf, buf2.skipBytes(1));
|
|
|
|
buf.release();
|
|
buf2.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 testWriteUtf8Composite() {
|
|
String utf8 = "Some UTF-8 like äÄ∏ŒŒ";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8));
|
|
ByteBuf buf2 = Unpooled.compositeBuffer().addComponent(
|
|
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24));
|
|
// write some byte so we start writing with an offset.
|
|
buf2.writeByte(1);
|
|
ByteBufUtil.writeUtf8(buf2, utf8);
|
|
|
|
// Skip the previously written byte.
|
|
assertEquals(buf, buf2.skipBytes(1));
|
|
|
|
buf.release();
|
|
buf2.release();
|
|
}
|
|
|
|
@Test
|
|
public void testWriteUtf8CompositeWrapped() {
|
|
String utf8 = "Some UTF-8 like äÄ∏ŒŒ";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8));
|
|
ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent(
|
|
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)));
|
|
// write some byte so we start writing with an offset.
|
|
buf2.writeByte(1);
|
|
ByteBufUtil.writeUtf8(buf2, utf8);
|
|
|
|
// Skip the previously written byte.
|
|
assertEquals(buf, buf2.skipBytes(1));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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);
|
|
assertEquals(buf.readableBytes(), ByteBufUtil.utf8Bytes(surrogateString));
|
|
|
|
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 testWriteUtf8Subsequence() {
|
|
String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8));
|
|
ByteBuf buf2 = Unpooled.buffer(16);
|
|
ByteBufUtil.writeUtf8(buf2, usAscii, 5, 18);
|
|
|
|
assertEquals(buf, buf2);
|
|
|
|
buf.release();
|
|
buf2.release();
|
|
}
|
|
|
|
@Test
|
|
public void testReserveAndWriteUtf8Subsequence() {
|
|
String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8));
|
|
ByteBuf buf2 = Unpooled.buffer(16);
|
|
int count = ByteBufUtil.reserveAndWriteUtf8(buf2, usAscii, 5, 18, 16);
|
|
|
|
assertEquals(buf, buf2);
|
|
assertEquals(buf.readableBytes(), count);
|
|
|
|
buf.release();
|
|
buf2.release();
|
|
}
|
|
|
|
@Test
|
|
public void testUtf8BytesSubsequence() {
|
|
String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
|
|
assertEquals(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8).length,
|
|
ByteBufUtil.utf8Bytes(usAscii, 5, 18));
|
|
}
|
|
|
|
private static int[][] INVALID_RANGES = new int[][] {
|
|
{ -1, 5 }, { 5, 30 }, { 10, 5 }
|
|
};
|
|
|
|
interface TestMethod {
|
|
int invoke(Object... args);
|
|
}
|
|
|
|
private void testInvalidSubsequences(TestMethod method) {
|
|
for (int [] range : INVALID_RANGES) {
|
|
ByteBuf buf = Unpooled.buffer(16);
|
|
try {
|
|
method.invoke(buf, "Some UTF-8 like äÄ∏ŒŒ", range[0], range[1]);
|
|
fail("Did not throw IndexOutOfBoundsException for range (" + range[0] + ", " + range[1] + ")");
|
|
} catch (IndexOutOfBoundsException iiobe) {
|
|
// expected
|
|
} finally {
|
|
assertFalse(buf.isReadable());
|
|
buf.release();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testWriteUtf8InvalidSubsequences() {
|
|
testInvalidSubsequences(new TestMethod() {
|
|
@Override
|
|
public int invoke(Object... args) {
|
|
return ByteBufUtil.writeUtf8((ByteBuf) args[0], (String) args[1],
|
|
(Integer) args[2], (Integer) args[3]);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testReserveAndWriteUtf8InvalidSubsequences() {
|
|
testInvalidSubsequences(new TestMethod() {
|
|
@Override
|
|
public int invoke(Object... args) {
|
|
return ByteBufUtil.reserveAndWriteUtf8((ByteBuf) args[0], (String) args[1],
|
|
(Integer) args[2], (Integer) args[3], 32);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testUtf8BytesInvalidSubsequences() {
|
|
testInvalidSubsequences(new TestMethod() {
|
|
@Override
|
|
public int invoke(Object... args) {
|
|
return ByteBufUtil.utf8Bytes((String) args[1], (Integer) args[2], (Integer) args[3]);
|
|
}
|
|
});
|
|
}
|
|
|
|
@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 = {
|
|
"netty".getBytes(CharsetUtil.UTF_8),
|
|
{(byte) 0x24},
|
|
{(byte) 0xC2, (byte) 0xA2},
|
|
{(byte) 0xE2, (byte) 0x82, (byte) 0xAC},
|
|
{(byte) 0xF0, (byte) 0x90, (byte) 0x8D, (byte) 0x88},
|
|
{(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 = {
|
|
{(byte) 0x80},
|
|
{(byte) 0xF0, (byte) 0x82, (byte) 0x82, (byte) 0xAC}, // Overlong encodings
|
|
{(byte) 0xC2}, // not enough bytes
|
|
{(byte) 0xE2, (byte) 0x82}, // not enough bytes
|
|
{(byte) 0xF0, (byte) 0x90, (byte) 0x8D}, // not enough bytes
|
|
{(byte) 0xC2, (byte) 0xC0}, // not correct bytes
|
|
{(byte) 0xE2, (byte) 0x82, (byte) 0xC0}, // not correct bytes
|
|
{(byte) 0xF0, (byte) 0x90, (byte) 0x8D, (byte) 0xC0}, // not correct bytes
|
|
{(byte) 0xC1, (byte) 0x80}, // out of lower bound
|
|
{(byte) 0xE0, (byte) 0x80, (byte) 0x80}, // out of lower bound
|
|
{(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 = {(byte) 0x01, (byte) 0xD8, (byte) 0x37, (byte) 0xDC};
|
|
byte[] invalidBytes = {(byte) 0x01, (byte) 0xD8};
|
|
|
|
assertIsText(validBytes, true, CharsetUtil.UTF_16LE);
|
|
assertIsText(invalidBytes, false, CharsetUtil.UTF_16LE);
|
|
}
|
|
|
|
@Test
|
|
public void testIsTextWithAscii() {
|
|
byte[] validBytes = {(byte) 0x00, (byte) 0x01, (byte) 0x37, (byte) 0x7F};
|
|
byte[] invalidBytes = {(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 = {
|
|
{4, 0},
|
|
{0, 4},
|
|
{1, 3},
|
|
};
|
|
for (int[] pair : validIndexLengthPairs) {
|
|
assertTrue(ByteBufUtil.isText(buffer, pair[0], pair[1], CharsetUtil.US_ASCII));
|
|
}
|
|
int[][] invalidIndexLengthPairs = {
|
|
{4, 1},
|
|
{-1, 2},
|
|
{3, -1},
|
|
{3, -2},
|
|
{5, 0},
|
|
{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();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testUtf8Bytes() {
|
|
final String s = "Some UTF-8 like äÄ∏ŒŒ";
|
|
checkUtf8Bytes(s);
|
|
}
|
|
|
|
@Test
|
|
public void testUtf8BytesWithSurrogates() {
|
|
final String s = "a\uD800\uDC00b";
|
|
checkUtf8Bytes(s);
|
|
}
|
|
|
|
@Test
|
|
public void testUtf8BytesWithNonSurrogates3Bytes() {
|
|
final String s = "a\uE000b";
|
|
checkUtf8Bytes(s);
|
|
}
|
|
|
|
@Test
|
|
public void testUtf8BytesWithNonSurrogatesNonAscii() {
|
|
final char nonAscii = (char) 0x81;
|
|
final String s = "a" + nonAscii + "b";
|
|
checkUtf8Bytes(s);
|
|
}
|
|
|
|
private static void checkUtf8Bytes(final CharSequence charSequence) {
|
|
final ByteBuf buf = Unpooled.buffer(ByteBufUtil.utf8MaxBytes(charSequence));
|
|
try {
|
|
final int writtenBytes = ByteBufUtil.writeUtf8(buf, charSequence);
|
|
final int utf8Bytes = ByteBufUtil.utf8Bytes(charSequence);
|
|
assertEquals(writtenBytes, utf8Bytes);
|
|
} finally {
|
|
buf.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();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testIsTextMultiThreaded() throws Throwable {
|
|
final ByteBuf buffer = Unpooled.copiedBuffer("Hello, World!", CharsetUtil.ISO_8859_1);
|
|
|
|
try {
|
|
final AtomicInteger counter = new AtomicInteger(60000);
|
|
final AtomicReference<Throwable> errorRef = new AtomicReference<Throwable>();
|
|
List<Thread> threads = new ArrayList<Thread>();
|
|
for (int i = 0; i < 10; i++) {
|
|
Thread thread = new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
while (errorRef.get() == null && counter.decrementAndGet() > 0) {
|
|
assertTrue(ByteBufUtil.isText(buffer, CharsetUtil.ISO_8859_1));
|
|
}
|
|
} catch (Throwable cause) {
|
|
errorRef.compareAndSet(null, cause);
|
|
}
|
|
}
|
|
});
|
|
threads.add(thread);
|
|
}
|
|
for (Thread thread : threads) {
|
|
thread.start();
|
|
}
|
|
|
|
for (Thread thread : threads) {
|
|
thread.join();
|
|
}
|
|
|
|
Throwable error = errorRef.get();
|
|
if (error != null) {
|
|
throw error;
|
|
}
|
|
} finally {
|
|
buffer.release();
|
|
}
|
|
}
|
|
}
|