2012-06-11 13:41:19 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2012 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.CharsetUtil;
|
|
|
|
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
import java.nio.CharBuffer;
|
|
|
|
import java.nio.charset.CharacterCodingException;
|
|
|
|
import java.nio.charset.Charset;
|
|
|
|
import java.nio.charset.CharsetDecoder;
|
|
|
|
import java.nio.charset.CharsetEncoder;
|
|
|
|
import java.nio.charset.CoderResult;
|
|
|
|
|
2012-12-21 22:22:40 +01:00
|
|
|
/**
|
|
|
|
* Utility class for operate on a {@link ByteBuf}
|
|
|
|
*/
|
2012-06-11 13:41:19 +02:00
|
|
|
public final class ByteBufUtil {
|
|
|
|
|
|
|
|
private static final char[] HEXDUMP_TABLE = new char[256 * 4];
|
|
|
|
|
|
|
|
static {
|
|
|
|
final char[] DIGITS = "0123456789abcdef".toCharArray();
|
|
|
|
for (int i = 0; i < 256; i ++) {
|
2012-11-09 09:26:11 +01:00
|
|
|
HEXDUMP_TABLE[ i << 1 ] = DIGITS[i >>> 4 & 0x0F];
|
|
|
|
HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F];
|
2012-06-11 13:41:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
|
|
|
|
* of the specified buffer's readable bytes.
|
|
|
|
*/
|
|
|
|
public static String hexDump(ByteBuf buffer) {
|
|
|
|
return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
|
|
|
|
* of the specified buffer's sub-region.
|
|
|
|
*/
|
|
|
|
public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
|
|
|
|
if (length < 0) {
|
|
|
|
throw new IllegalArgumentException("length: " + length);
|
|
|
|
}
|
|
|
|
if (length == 0) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
int endIndex = fromIndex + length;
|
|
|
|
char[] buf = new char[length << 1];
|
|
|
|
|
|
|
|
int srcIdx = fromIndex;
|
|
|
|
int dstIdx = 0;
|
|
|
|
for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
|
|
|
|
System.arraycopy(
|
|
|
|
HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
|
|
|
|
buf, dstIdx, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new String(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the hash code of the specified buffer. This method is
|
|
|
|
* useful when implementing a new buffer type.
|
|
|
|
*/
|
|
|
|
public static int hashCode(ByteBuf buffer) {
|
|
|
|
final int aLen = buffer.readableBytes();
|
|
|
|
final int intCount = aLen >>> 2;
|
|
|
|
final int byteCount = aLen & 3;
|
|
|
|
|
|
|
|
int hashCode = 1;
|
|
|
|
int arrayIndex = buffer.readerIndex();
|
|
|
|
if (buffer.order() == ByteOrder.BIG_ENDIAN) {
|
|
|
|
for (int i = intCount; i > 0; i --) {
|
|
|
|
hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
|
|
|
|
arrayIndex += 4;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = intCount; i > 0; i --) {
|
|
|
|
hashCode = 31 * hashCode + swapInt(buffer.getInt(arrayIndex));
|
|
|
|
arrayIndex += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = byteCount; i > 0; i --) {
|
|
|
|
hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hashCode == 0) {
|
|
|
|
hashCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hashCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns {@code true} if and only if the two specified buffers are
|
|
|
|
* identical to each other as described in {@code ChannelBuffer#equals(Object)}.
|
|
|
|
* This method is useful when implementing a new buffer type.
|
|
|
|
*/
|
|
|
|
public static boolean equals(ByteBuf bufferA, ByteBuf bufferB) {
|
|
|
|
final int aLen = bufferA.readableBytes();
|
|
|
|
if (aLen != bufferB.readableBytes()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int longCount = aLen >>> 3;
|
|
|
|
final int byteCount = aLen & 7;
|
|
|
|
|
|
|
|
int aIndex = bufferA.readerIndex();
|
|
|
|
int bIndex = bufferB.readerIndex();
|
|
|
|
|
|
|
|
if (bufferA.order() == bufferB.order()) {
|
|
|
|
for (int i = longCount; i > 0; i --) {
|
|
|
|
if (bufferA.getLong(aIndex) != bufferB.getLong(bIndex)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aIndex += 8;
|
|
|
|
bIndex += 8;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = longCount; i > 0; i --) {
|
|
|
|
if (bufferA.getLong(aIndex) != swapLong(bufferB.getLong(bIndex))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aIndex += 8;
|
|
|
|
bIndex += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = byteCount; i > 0; i --) {
|
|
|
|
if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aIndex ++;
|
|
|
|
bIndex ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compares the two specified buffers as described in {@link ByteBuf#compareTo(ByteBuf)}.
|
|
|
|
* This method is useful when implementing a new buffer type.
|
|
|
|
*/
|
|
|
|
public static int compare(ByteBuf bufferA, ByteBuf bufferB) {
|
|
|
|
final int aLen = bufferA.readableBytes();
|
|
|
|
final int bLen = bufferB.readableBytes();
|
|
|
|
final int minLength = Math.min(aLen, bLen);
|
|
|
|
final int uintCount = minLength >>> 2;
|
|
|
|
final int byteCount = minLength & 3;
|
|
|
|
|
|
|
|
int aIndex = bufferA.readerIndex();
|
|
|
|
int bIndex = bufferB.readerIndex();
|
|
|
|
|
|
|
|
if (bufferA.order() == bufferB.order()) {
|
|
|
|
for (int i = uintCount; i > 0; i --) {
|
|
|
|
long va = bufferA.getUnsignedInt(aIndex);
|
|
|
|
long vb = bufferB.getUnsignedInt(bIndex);
|
|
|
|
if (va > vb) {
|
|
|
|
return 1;
|
2012-11-12 01:31:40 +01:00
|
|
|
}
|
|
|
|
if (va < vb) {
|
2012-06-11 13:41:19 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
aIndex += 4;
|
|
|
|
bIndex += 4;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = uintCount; i > 0; i --) {
|
|
|
|
long va = bufferA.getUnsignedInt(aIndex);
|
|
|
|
long vb = swapInt(bufferB.getInt(bIndex)) & 0xFFFFFFFFL;
|
|
|
|
if (va > vb) {
|
|
|
|
return 1;
|
2012-11-12 01:31:40 +01:00
|
|
|
}
|
|
|
|
if (va < vb) {
|
2012-06-11 13:41:19 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
aIndex += 4;
|
|
|
|
bIndex += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = byteCount; i > 0; i --) {
|
|
|
|
short va = bufferA.getUnsignedByte(aIndex);
|
|
|
|
short vb = bufferB.getUnsignedByte(bIndex);
|
|
|
|
if (va > vb) {
|
|
|
|
return 1;
|
2012-11-12 01:31:40 +01:00
|
|
|
}
|
|
|
|
if (va < vb) {
|
2012-06-11 13:41:19 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
aIndex ++;
|
|
|
|
bIndex ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return aLen - bLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The default implementation of {@link ByteBuf#indexOf(int, int, byte)}.
|
|
|
|
* This method is useful when implementing a new buffer type.
|
|
|
|
*/
|
|
|
|
public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
|
|
|
|
if (fromIndex <= toIndex) {
|
|
|
|
return firstIndexOf(buffer, fromIndex, toIndex, value);
|
|
|
|
} else {
|
|
|
|
return lastIndexOf(buffer, fromIndex, toIndex, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The default implementation of {@link ByteBuf#indexOf(int, int, ByteBufIndexFinder)}.
|
|
|
|
* This method is useful when implementing a new buffer type.
|
|
|
|
*/
|
|
|
|
public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, ByteBufIndexFinder indexFinder) {
|
|
|
|
if (fromIndex <= toIndex) {
|
|
|
|
return firstIndexOf(buffer, fromIndex, toIndex, indexFinder);
|
|
|
|
} else {
|
|
|
|
return lastIndexOf(buffer, fromIndex, toIndex, indexFinder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggles the endianness of the specified 16-bit short integer.
|
|
|
|
*/
|
|
|
|
public static short swapShort(short value) {
|
|
|
|
return (short) (value << 8 | value >>> 8 & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggles the endianness of the specified 24-bit medium integer.
|
|
|
|
*/
|
|
|
|
public static int swapMedium(int value) {
|
|
|
|
int swapped = value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
|
|
|
|
if ((swapped & 0x800000) != 0) {
|
|
|
|
swapped |= 0xff000000;
|
|
|
|
}
|
|
|
|
return swapped;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggles the endianness of the specified 32-bit integer.
|
|
|
|
*/
|
|
|
|
public static int swapInt(int value) {
|
|
|
|
return swapShort((short) value) << 16 |
|
|
|
|
swapShort((short) (value >>> 16)) & 0xffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggles the endianness of the specified 64-bit long integer.
|
|
|
|
*/
|
|
|
|
public static long swapLong(long value) {
|
|
|
|
return (long) swapInt((int) value) << 32 |
|
|
|
|
swapInt((int) (value >>> 32)) & 0xffffffffL;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
|
|
|
|
fromIndex = Math.max(fromIndex, 0);
|
|
|
|
if (fromIndex >= toIndex || buffer.capacity() == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = fromIndex; i < toIndex; i ++) {
|
|
|
|
if (buffer.getByte(i) == value) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
|
|
|
|
fromIndex = Math.min(fromIndex, buffer.capacity());
|
|
|
|
if (fromIndex < 0 || buffer.capacity() == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = fromIndex - 1; i >= toIndex; i --) {
|
|
|
|
if (buffer.getByte(i) == value) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int firstIndexOf(
|
|
|
|
ByteBuf buffer, int fromIndex, int toIndex, ByteBufIndexFinder indexFinder) {
|
|
|
|
fromIndex = Math.max(fromIndex, 0);
|
|
|
|
if (fromIndex >= toIndex || buffer.capacity() == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = fromIndex; i < toIndex; i ++) {
|
|
|
|
if (indexFinder.find(buffer, i)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int lastIndexOf(
|
|
|
|
ByteBuf buffer, int fromIndex, int toIndex, ByteBufIndexFinder indexFinder) {
|
|
|
|
fromIndex = Math.min(fromIndex, buffer.capacity());
|
|
|
|
if (fromIndex < 0 || buffer.capacity() == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = fromIndex - 1; i >= toIndex; i --) {
|
|
|
|
if (indexFinder.find(buffer, i)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ByteBuffer encodeString(CharBuffer src, Charset charset) {
|
|
|
|
final CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
|
|
|
|
final ByteBuffer dst = ByteBuffer.allocate(
|
|
|
|
(int) ((double) src.remaining() * encoder.maxBytesPerChar()));
|
|
|
|
try {
|
|
|
|
CoderResult cr = encoder.encode(src, dst, true);
|
|
|
|
if (!cr.isUnderflow()) {
|
|
|
|
cr.throwException();
|
|
|
|
}
|
|
|
|
cr = encoder.flush(dst);
|
|
|
|
if (!cr.isUnderflow()) {
|
|
|
|
cr.throwException();
|
|
|
|
}
|
|
|
|
} catch (CharacterCodingException x) {
|
|
|
|
throw new IllegalStateException(x);
|
|
|
|
}
|
|
|
|
dst.flip();
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
static String decodeString(ByteBuffer src, Charset charset) {
|
|
|
|
final CharsetDecoder decoder = CharsetUtil.getDecoder(charset);
|
|
|
|
final CharBuffer dst = CharBuffer.allocate(
|
|
|
|
(int) ((double) src.remaining() * decoder.maxCharsPerByte()));
|
|
|
|
try {
|
|
|
|
CoderResult cr = decoder.decode(src, dst, true);
|
|
|
|
if (!cr.isUnderflow()) {
|
|
|
|
cr.throwException();
|
|
|
|
}
|
|
|
|
cr = decoder.flush(dst);
|
|
|
|
if (!cr.isUnderflow()) {
|
|
|
|
cr.throwException();
|
|
|
|
}
|
|
|
|
} catch (CharacterCodingException x) {
|
|
|
|
throw new IllegalStateException(x);
|
|
|
|
}
|
|
|
|
return dst.flip().toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
private ByteBufUtil() { }
|
|
|
|
}
|