All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.testifyproject.netty.buffer.ByteBufUtil Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
/*
 * 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 org.testifyproject.testifyprojectpliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org.testifyproject/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 org.testifyproject.testifyproject.netty.buffer;

import org.testifyproject.testifyproject.netty.util.AsciiString;
import org.testifyproject.testifyproject.netty.util.ByteProcessor;
import org.testifyproject.testifyproject.netty.util.CharsetUtil;
import org.testifyproject.testifyproject.netty.util.Recycler;
import org.testifyproject.testifyproject.netty.util.Recycler.Handle;
import org.testifyproject.testifyproject.netty.util.concurrent.FastThreadLocal;
import org.testifyproject.testifyproject.netty.util.internal.PlatformDependent;
import org.testifyproject.testifyproject.netty.util.internal.StringUtil;
import org.testifyproject.testifyproject.netty.util.internal.SystemPropertyUtil;
import org.testifyproject.testifyproject.netty.util.internal.logging.InternalLogger;
import org.testifyproject.testifyproject.netty.util.internal.logging.InternalLoggerFactory;

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;
import java.util.Arrays;
import java.util.Locale;

import static org.testifyproject.testifyproject.netty.util.internal.MathUtil.isOutOfBounds;
import static org.testifyproject.testifyproject.netty.util.internal.ObjectUtil.checkNotNull;
import static org.testifyproject.testifyproject.netty.util.internal.StringUtil.NEWLINE;
import static org.testifyproject.testifyproject.netty.util.internal.StringUtil.isSurrogate;

/**
 * A collection of utility methods that is related with handling {@link ByteBuf},
 * such as the generation of hex dump and swapping an integer's byte order.
 */
public final class ByteBufUtil {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
    private static final FastThreadLocal CHAR_BUFFERS = new FastThreadLocal() {
        @Override
        protected CharBuffer initialValue() throws Exception {
            return CharBuffer.allocate(1024);
        }
    };

    private static final byte WRITE_UTF_UNKNOWN = (byte) '?';
    private static final int MAX_CHAR_BUFFER_SIZE;
    private static final int THREAD_LOCAL_BUFFER_SIZE;

    static final ByteBufAllocator DEFAULT_ALLOCATOR;

    static {
        String allocType = SystemPropertyUtil.get(
                "org.testifyproject.testifyproject.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
        allocType = allocType.toLowerCase(Locale.US).trim();

        ByteBufAllocator alloc;
        if ("unpooled".equals(allocType)) {
            alloc = UnpooledByteBufAllocator.DEFAULT;
            logger.org.testifyproject.testifyprojectbug("-Dio.netty.allocator.type: {}", allocType);
        } else if ("pooled".equals(allocType)) {
            alloc = PooledByteBufAllocator.DEFAULT;
            logger.org.testifyproject.testifyprojectbug("-Dio.netty.allocator.type: {}", allocType);
        } else {
            alloc = PooledByteBufAllocator.DEFAULT;
            logger.org.testifyproject.testifyprojectbug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
        }

        DEFAULT_ALLOCATOR = alloc;

        THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("org.testifyproject.testifyproject.netty.threadLocalDirectBufferSize", 64 * 1024);
        logger.org.testifyproject.testifyprojectbug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);

        MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("org.testifyproject.testifyproject.netty.maxThreadLocalCharBufferSize", 16 * 1024);
        logger.org.testifyproject.testifyprojectbug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
    }

    /**
     * Returns a hex dump
     * of the specified buffer's readable bytes.
     */
    public static String hexDump(ByteBuf buffer) {
        return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
    }

    /**
     * Returns a hex dump
     * of the specified buffer's sub-region.
     */
    public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
        return HexUtil.hexDump(buffer, fromIndex, length);
    }

    /**
     * Returns a hex dump
     * of the specified byte array.
     */
    public static String hexDump(byte[] array) {
        return hexDump(array, 0, array.length);
    }

    /**
     * Returns a hex dump
     * of the specified byte array's sub-region.
     */
    public static String hexDump(byte[] array, int fromIndex, int length) {
        return HexUtil.hexDump(array, fromIndex, length);
    }

    /**
     * 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 for {@code length} bytes starting at {@code aStartIndex}
     * index for the {@code a} buffer and {@code bStartIndex} index for the {@code b} buffer.
     * A more org.testifyproject.testifyprojectpact way to express this is:
     * 

* {@code a[aStartIndex : aStartIndex + length] == b[bStartIndex : bStartIndex + length]} */ public static boolean equals(ByteBuf a, int aStartIndex, ByteBuf b, int bStartIndex, int length) { if (aStartIndex < 0 || bStartIndex < 0 || length < 0) { throw new IllegalArgumentException("All indexes and lengths must be non-negative"); } if (a.writerIndex() - length < aStartIndex || b.writerIndex() - length < bStartIndex) { return false; } final int longCount = length >>> 3; final int byteCount = length & 7; if (a.order() == b.order()) { for (int i = longCount; i > 0; i --) { if (a.getLong(aStartIndex) != b.getLong(bStartIndex)) { return false; } aStartIndex += 8; bStartIndex += 8; } } else { for (int i = longCount; i > 0; i --) { if (a.getLong(aStartIndex) != swapLong(b.getLong(bStartIndex))) { return false; } aStartIndex += 8; bStartIndex += 8; } } for (int i = byteCount; i > 0; i --) { if (a.getByte(aStartIndex) != b.getByte(bStartIndex)) { return false; } aStartIndex ++; bStartIndex ++; } return true; } /** * Returns {@code true} if and only if the two specified buffers are * identical to each other as org.testifyproject.testifyprojectscribed in {@link ByteBuf#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; } return equals(bufferA, bufferA.readerIndex(), bufferB, bufferB.readerIndex(), aLen); } /** * Compares the two specified buffers as org.testifyproject.testifyprojectscribed in {@link ByteBuf#org.testifyproject.testifyprojectpareTo(ByteBuf)}. * This method is useful when implementing a new buffer type. */ public static int org.testifyproject.testifyprojectpare(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; } if (va < vb) { 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; } if (va < vb) { 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; } if (va < vb) { return -1; } aIndex ++; bIndex ++; } return aLen - bLen; } /** * The org.testifyproject.testifyprojectfault 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); } } /** * Toggles the endianness of the specified 16-bit short integer. */ public static short swapShort(short value) { return Short.reverseBytes(value); } /** * 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 Integer.reverseBytes(value); } /** * Toggles the endianness of the specified 64-bit long integer. */ public static long swapLong(long value) { return Long.reverseBytes(value); } /** * Read the given amount of bytes into a new {@link ByteBuf} that is allocated from the {@link ByteBufAllocator}. */ public static ByteBuf readBytes(ByteBufAllocator alloc, ByteBuf buffer, int length) { boolean release = true; ByteBuf dst = alloc.buffer(length); try { buffer.readBytes(dst); release = false; return dst; } finally { if (release) { dst.release(); } } } 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; } return buffer.forEachByte(fromIndex, toIndex - fromIndex, new ByteProcessor.IndexOfProcessor(value)); } 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; } return buffer.forEachByteDesc(toIndex, fromIndex - toIndex, new ByteProcessor.IndexOfProcessor(value)); } /** * Encode a {@link CharSequence} in UTF-8 and write * it to a {@link ByteBuf}. * * This method returns the actual number of bytes written. */ public static int writeUtf8(ByteBuf buf, CharSequence seq) { if (buf == null) { throw new NullPointerException("buf"); } if (seq == null) { throw new NullPointerException("seq"); } // UTF-8 uses max. 3 bytes per char, so calculate the worst case. final int len = seq.length(); final int maxSize = len * 3; buf.ensureWritable(maxSize); for (;;) { if (buf instanceof AbstractByteBuf) { return writeUtf8((AbstractByteBuf) buf, seq, len); } else if (buf instanceof WrappedByteBuf) { // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. buf = buf.unwrap(); } else { byte[] bytes = seq.toString().getBytes(CharsetUtil.UTF_8); buf.writeBytes(bytes); return bytes.length; } } } // Fast-Path implementation private static int writeUtf8(AbstractByteBuf buffer, CharSequence seq, int len) { int oldWriterIndex = buffer.writerIndex; int writerIndex = oldWriterIndex; // We can use the _set methods as these not need to do any index checks and reference checks. // This is possible as we called ensureWritable(...) before. for (int i = 0; i < len; i++) { char c = seq.charAt(i); if (c < 0x80) { buffer._setByte(writerIndex++, (byte) c); } else if (c < 0x800) { buffer._setByte(writerIndex++, (byte) (0xc0 | (c >> 6))); buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f))); } else if (isSurrogate(c)) { if (!Character.isHighSurrogate(c)) { buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); continue; } final char c2; try { // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid // duplicate bounds checking with charAt. If an IndexOutOfBoundsException is thrown we will // re-throw a more informative exception org.testifyproject.testifyprojectscribing the problem. c2 = seq.charAt(++i); } catch (IndexOutOfBoundsException e) { buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); break; } if (!Character.isLowSurrogate(c2)) { buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); continue; } int codePoint = Character.toCodePoint(c, c2); // See http://www.unicode.org.testifyproject/versions/Unicode7.0.0/ch03.pdf#G2630. buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); } else { buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12))); buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f))); buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f))); } } // update the writerIndex without any extra checks for performance reasons buffer.writerIndex = writerIndex; return writerIndex - oldWriterIndex; } /** * Encode a {@link CharSequence} in ASCII and write it * to a {@link ByteBuf}. * * This method returns the actual number of bytes written. */ public static int writeAscii(ByteBuf buf, CharSequence seq) { if (buf == null) { throw new NullPointerException("buf"); } if (seq == null) { throw new NullPointerException("seq"); } // ASCII uses 1 byte per char final int len = seq.length(); buf.ensureWritable(len); if (seq instanceof AsciiString) { AsciiString asciiString = (AsciiString) seq; buf.writeBytes(asciiString.array(), asciiString.arrayOffset(), asciiString.length()); } else { for (;;) { if (buf instanceof AbstractByteBuf) { writeAscii((AbstractByteBuf) buf, seq, len); break; } else if (buf instanceof WrappedByteBuf) { // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. buf = buf.unwrap(); } else { buf.writeBytes(seq.toString().getBytes(CharsetUtil.US_ASCII)); } } } return len; } // Fast-Path implementation private static void writeAscii(AbstractByteBuf buffer, CharSequence seq, int len) { int writerIndex = buffer.writerIndex; // We can use the _set methods as these not need to do any index checks and reference checks. // This is possible as we called ensureWritable(...) before. for (int i = 0; i < len; i++) { buffer._setByte(writerIndex++, (byte) seq.charAt(i)); } // update the writerIndex without any extra checks for performance reasons buffer.writerIndex = writerIndex; } /** * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which * is allocated via the {@link ByteBufAllocator}. */ public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) { return encodeString0(alloc, false, src, charset); } static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset) { final CharsetEncoder encoder = CharsetUtil.getEncoder(charset); int length = (int) ((double) src.remaining() * encoder.maxBytesPerChar()); boolean release = true; final ByteBuf dst; if (enforceHeap) { dst = alloc.heapBuffer(length); } else { dst = alloc.buffer(length); } try { final ByteBuffer dstBuf = dst.internalNioBuffer(0, length); final int pos = dstBuf.position(); CoderResult cr = encoder.encode(src, dstBuf, true); if (!cr.isUnderflow()) { cr.throwException(); } cr = encoder.flush(dstBuf); if (!cr.isUnderflow()) { cr.throwException(); } dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos); release = false; return dst; } catch (CharacterCodingException x) { throw new IllegalStateException(x); } finally { if (release) { dst.release(); } } } static String org.testifyproject.testifyprojectcodeString(ByteBuf src, int readerIndex, int len, Charset charset) { if (len == 0) { return StringUtil.EMPTY_STRING; } final CharsetDecoder org.testifyproject.testifyprojectcoder = CharsetUtil.getDecoder(charset); final int maxLength = (int) ((double) len * org.testifyproject.testifyprojectcoder.maxCharsPerByte()); CharBuffer dst = CHAR_BUFFERS.get(); if (dst.length() < maxLength) { dst = CharBuffer.allocate(maxLength); if (maxLength <= MAX_CHAR_BUFFER_SIZE) { CHAR_BUFFERS.set(dst); } } else { dst.clear(); } if (src.nioBufferCount() == 1) { // Use internalNioBuffer(...) to reduce object creation. org.testifyproject.testifyprojectcodeString(org.testifyproject.testifyprojectcoder, src.internalNioBuffer(readerIndex, len), dst); } else { // We use a heap buffer as CharsetDecoder is most likely able to use a fast-path if src and dst buffers // are both backed by a byte array. ByteBuf buffer = src.alloc().heapBuffer(len); try { buffer.writeBytes(src, readerIndex, len); // Use internalNioBuffer(...) to reduce object creation. org.testifyproject.testifyprojectcodeString(org.testifyproject.testifyprojectcoder, buffer.internalNioBuffer(0, len), dst); } finally { // Release the temporary buffer again. buffer.release(); } } return dst.flip().toString(); } private static void org.testifyproject.testifyprojectcodeString(CharsetDecoder org.testifyproject.testifyprojectcoder, ByteBuffer src, CharBuffer dst) { try { CoderResult cr = org.testifyproject.testifyprojectcoder.org.testifyproject.testifyprojectcode(src, dst, true); if (!cr.isUnderflow()) { cr.throwException(); } cr = org.testifyproject.testifyprojectcoder.flush(dst); if (!cr.isUnderflow()) { cr.throwException(); } } catch (CharacterCodingException x) { throw new IllegalStateException(x); } } /** * Returns a cached thread-local direct buffer, if available. * * @return a cached thread-local direct buffer, if available. {@code null} otherwise. */ public static ByteBuf threadLocalDirectBuffer() { if (THREAD_LOCAL_BUFFER_SIZE <= 0) { return null; } if (PlatformDependent.hasUnsafe()) { return ThreadLocalUnsafeDirectByteBuf.newInstance(); } else { return ThreadLocalDirectByteBuf.newInstance(); } } /** * Create a copy of the underlying storage from {@code buf} into a byte array. * The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes. */ public static byte[] getBytes(ByteBuf buf) { return getBytes(buf, buf.readerIndex(), buf.readableBytes()); } /** * Create a copy of the underlying storage from {@code buf} into a byte array. * The copy will start at {@code start} and copy {@code length} bytes. */ public static byte[] getBytes(ByteBuf buf, int start, int length) { return getBytes(buf, start, length, true); } /** * Return an array of the underlying storage from {@code buf} into a byte array. * The copy will start at {@code start} and copy {@code length} bytes. * If {@code copy} is true a copy will be made of the memory. * If {@code copy} is false the underlying storage will be shared, if possible. */ public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) { if (isOutOfBounds(start, length, buf.capacity())) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + ") <= " + "buf.capacity(" + buf.capacity() + ')'); } if (buf.hasArray()) { if (copy || start != 0 || length != buf.capacity()) { int baseOffset = buf.arrayOffset() + start; return Arrays.copyOfRange(buf.array(), baseOffset, baseOffset + length); } else { return buf.array(); } } byte[] v = new byte[length]; buf.getBytes(start, v); return v; } /** * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. * @param src The source of the data to copy. * @param srcIdx the starting offset of characters to copy. * @param dst the org.testifyproject.testifyprojectstination byte array. * @param dstIdx the starting offset in the org.testifyproject.testifyprojectstination byte array. * @param length the number of characters to copy. */ public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) { if (isOutOfBounds(srcIdx, length, src.length())) { throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + src.length() + ')'); } checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length); } /** * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. * @param src The source of the data to copy. * @param srcIdx the starting offset of characters to copy. * @param dst the org.testifyproject.testifyprojectstination byte array. * @param length the number of characters to copy. */ public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) { if (isOutOfBounds(srcIdx, length, src.length())) { throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + src.length() + ')'); } checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length); } /** * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans. */ public static String prettyHexDump(ByteBuf buffer) { return prettyHexDump(buffer, buffer.readerIndex(), buffer.readableBytes()); } /** * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans, * starting at the given {@code offset} using the given {@code length}. */ public static String prettyHexDump(ByteBuf buffer, int offset, int length) { return HexUtil.prettyHexDump(buffer, offset, length); } /** * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified * {@link StringBuilder} that is easy to read by humans. */ public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf) { appendPrettyHexDump(dump, buf, buf.readerIndex(), buf.readableBytes()); } /** * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified * {@link StringBuilder} that is easy to read by humans, starting at the given {@code offset} using * the given {@code length}. */ public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) { HexUtil.appendPrettyHexDump(dump, buf, offset, length); } /* Separate class so that the expensive static initialization is only done when needed */ private static final class HexUtil { private static final char[] BYTE2CHAR = new char[256]; private static final char[] HEXDUMP_TABLE = new char[256 * 4]; private static final String[] HEXPADDING = new String[16]; private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4]; private static final String[] BYTE2HEX = new String[256]; private static final String[] BYTEPADDING = new String[16]; static { final char[] DIGITS = "0123456789abcdef".toCharArray(); for (int i = 0; i < 256; i ++) { HEXDUMP_TABLE[ i << 1 ] = DIGITS[i >>> 4 & 0x0F]; HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F]; } int i; // Generate the lookup table for hex dump paddings for (i = 0; i < HEXPADDING.length; i ++) { int padding = HEXPADDING.length - i; StringBuilder buf = new StringBuilder(padding * 3); for (int j = 0; j < padding; j ++) { buf.append(" "); } HEXPADDING[i] = buf.toString(); } // Generate the lookup table for the start-offset header in each row (up to 64KiB). for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) { StringBuilder buf = new StringBuilder(12); buf.append(NEWLINE); buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L)); buf.setCharAt(buf.length() - 9, '|'); buf.append('|'); HEXDUMP_ROWPREFIXES[i] = buf.toString(); } // Generate the lookup table for byte-to-hex-dump conversion for (i = 0; i < BYTE2HEX.length; i ++) { BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i); } // Generate the lookup table for byte dump paddings for (i = 0; i < BYTEPADDING.length; i ++) { int padding = BYTEPADDING.length - i; StringBuilder buf = new StringBuilder(padding); for (int j = 0; j < padding; j ++) { buf.append(' '); } BYTEPADDING[i] = buf.toString(); } // Generate the lookup table for byte-to-char conversion for (i = 0; i < BYTE2CHAR.length; i ++) { if (i <= 0x1f || i >= 0x7f) { BYTE2CHAR[i] = '.'; } else { BYTE2CHAR[i] = (char) i; } } } private 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); } private static String hexDump(byte[] array, 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, (array[srcIdx] & 0xFF) << 1, buf, dstIdx, 2); } return new String(buf); } private static String prettyHexDump(ByteBuf buffer, int offset, int length) { if (length == 0) { return StringUtil.EMPTY_STRING; } else { int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4; StringBuilder buf = new StringBuilder(rows * 80); appendPrettyHexDump(buf, buffer, offset, length); return buf.toString(); } } private static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) { if (isOutOfBounds(offset, length, buf.capacity())) { throw new IndexOutOfBoundsException( "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length + ") <= " + "buf.capacity(" + buf.capacity() + ')'); } if (length == 0) { return; } dump.append( " +-------------------------------------------------+" + NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" + NEWLINE + "+--------+-------------------------------------------------+----------------+"); final int startIndex = offset; final int fullRows = length >>> 4; final int remainder = length & 0xF; // Dump the rows which have 16 bytes. for (int row = 0; row < fullRows; row ++) { int rowStartIndex = (row << 4) + startIndex; // Per-row prefix. appendHexDumpRowPrefix(dump, row, rowStartIndex); // Hex dump int rowEndIndex = rowStartIndex + 16; for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2HEX[buf.getUnsignedByte(j)]); } dump.append(" |"); // ASCII dump for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]); } dump.append('|'); } // Dump the last row which has less than 16 bytes. if (remainder != 0) { int rowStartIndex = (fullRows << 4) + startIndex; appendHexDumpRowPrefix(dump, fullRows, rowStartIndex); // Hex dump int rowEndIndex = rowStartIndex + remainder; for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2HEX[buf.getUnsignedByte(j)]); } dump.append(HEXPADDING[remainder]); dump.append(" |"); // Ascii dump for (int j = rowStartIndex; j < rowEndIndex; j ++) { dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]); } dump.append(BYTEPADDING[remainder]); dump.append('|'); } dump.append(NEWLINE + "+--------+-------------------------------------------------+----------------+"); } private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) { if (row < HEXDUMP_ROWPREFIXES.length) { dump.append(HEXDUMP_ROWPREFIXES[row]); } else { dump.append(NEWLINE); dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L)); dump.setCharAt(dump.length() - 9, '|'); dump.append('|'); } } } static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf { private static final Recycler RECYCLER = new Recycler() { @Override protected ThreadLocalUnsafeDirectByteBuf newObject(Handle handle) { return new ThreadLocalUnsafeDirectByteBuf(handle); } }; static ThreadLocalUnsafeDirectByteBuf newInstance() { ThreadLocalUnsafeDirectByteBuf buf = RECYCLER.get(); buf.setRefCnt(1); return buf; } private final Handle handle; private ThreadLocalUnsafeDirectByteBuf(Handle handle) { super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE); this.handle = handle; } @Override protected void org.testifyproject.testifyprojectallocate() { if (capacity() > THREAD_LOCAL_BUFFER_SIZE) { super.org.testifyproject.testifyprojectallocate(); } else { clear(); RECYCLER.recycle(this, handle); } } } static final class ThreadLocalDirectByteBuf extends UnpooledDirectByteBuf { private static final Recycler RECYCLER = new Recycler() { @Override protected ThreadLocalDirectByteBuf newObject(Handle handle) { return new ThreadLocalDirectByteBuf(handle); } }; static ThreadLocalDirectByteBuf newInstance() { ThreadLocalDirectByteBuf buf = RECYCLER.get(); buf.setRefCnt(1); return buf; } private final Handle handle; private ThreadLocalDirectByteBuf(Handle handle) { super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE); this.handle = handle; } @Override protected void org.testifyproject.testifyprojectallocate() { if (capacity() > THREAD_LOCAL_BUFFER_SIZE) { super.org.testifyproject.testifyprojectallocate(); } else { clear(); RECYCLER.recycle(this, handle); } } } private ByteBufUtil() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy