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

com.pdd.pop.ext.glassfish.grizzly.memory.Buffers Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2008-2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.pdd.pop.ext.glassfish.grizzly.memory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.pdd.pop.ext.glassfish.grizzly.Appender;
import com.pdd.pop.ext.glassfish.grizzly.Buffer;
import com.pdd.pop.ext.glassfish.grizzly.Grizzly;
import com.pdd.pop.ext.glassfish.grizzly.localization.LogMessages;

/**
 * Class has useful methods to simplify the work with {@link Buffer}s.
 *
 * @see Buffer
 *
 * @author Alexey Stashok
 */

public class Buffers {
    private static final Logger LOGGER = Grizzly.logger(Buffers.class);

    private static final Appender APPENDER_DISPOSABLE = new BuffersAppender(true);
    private static final Appender APPENDER_NOT_DISPOSABLE = new BuffersAppender(false);

    /**
     * Get the {@link Appender} which knows how to append {@link Buffer}s.
     * Returned {@link Appender} uses the same {@link Buffer} appending rules as
     * described here {@link Buffers#appendBuffers(com.pdd.pop.ext.glassfish.grizzly.memory.MemoryManager, com.pdd.pop.ext.glassfish.grizzly.Buffer, com.pdd.pop.ext.glassfish.grizzly.Buffer, boolean)}.
     * 
     * @param isCompositeBufferDisposable if as the result of {@link Buffer}s
     *      appending a new {@link CompositeBuffer} will be created - its
     *      {@link CompositeBuffer#allowBufferDispose(boolean)} value will be set
     *      according to this parameter.
     * @return the {@link Buffer} {@link Appender}.
     */
    public static Appender getBufferAppender(
            final boolean isCompositeBufferDisposable) {
        return isCompositeBufferDisposable ?
                APPENDER_DISPOSABLE : APPENDER_NOT_DISPOSABLE;
    }
    
    private static class BuffersAppender implements Appender {
        private final boolean isCompositeBufferDisposable;

        public BuffersAppender(boolean isCompositeBufferDisposable) {
            this.isCompositeBufferDisposable = isCompositeBufferDisposable;
        }
        
        @Override
        public Buffer append(final Buffer element1, final Buffer element2) {
            return Buffers.appendBuffers(null, element1, element2,
                    isCompositeBufferDisposable);
        }
    }
    
    public static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
    @SuppressWarnings("unused")
    public static final ByteBuffer[] EMPTY_BYTE_BUFFER_ARRAY = new ByteBuffer[0];

    public static final Buffer EMPTY_BUFFER;

    static {
        EMPTY_BUFFER = new ByteBufferWrapper(ByteBuffer.allocate(0)) {
            @Override
            public void dispose() {
            }
        };
    }

    /**
     * Returns {@link Buffer}, which wraps the {@link String}.
     *
     * @param memoryManager {@link MemoryManager}, which should be
     *                       used for wrapping.
     * @param s {@link String}
     *
     * @return {@link Buffer} wrapper on top of passed {@link String}.
     */
    public static Buffer wrap(final MemoryManager memoryManager,
            final String s) {
        return wrap(memoryManager, s, Charset.defaultCharset());
    }

    /**
     * Returns {@link Buffer}, which wraps the {@link String} with the specific
     * {@link Charset}.
     *
     * @param memoryManager {@link MemoryManager}, which should be
     *                       used for wrapping.
     * @param s {@link String}
     * @param charset {@link Charset}, which will be used, when converting
     * {@link String} to byte array.
     *
     * @return {@link Buffer} wrapper on top of passed {@link String}.
     */
    public static Buffer wrap(final MemoryManager memoryManager,
            final String s, final Charset charset) {
        try {
            final byte[] byteRepresentation = s.getBytes(charset.name());
            return wrap(memoryManager, byteRepresentation);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Returns {@link Buffer}, which wraps the byte array.
     *
     * @param memoryManager {@link MemoryManager}, which should be
     *                       used for wrapping.
     * @param array byte array to wrap.
     *
     * @return {@link Buffer} wrapper on top of passed byte array.
     */
    public static Buffer wrap(final MemoryManager memoryManager,
            final byte[] array) {
        return wrap(memoryManager, array, 0, array.length);
    }

    /**
     * Returns {@link Buffer}, which wraps the part of byte array with
     * specific offset and length.
     *
     * @param memoryManager {@link MemoryManager}, which should be
     *                       used for wrapping.
     * @param array byte array to wrap
     * @param offset byte buffer offset
     * @param length byte buffer length
     *
     * @return {@link Buffer} wrapper on top of passed byte array.
     */
    public static Buffer wrap(MemoryManager memoryManager,
            final byte[] array, final int offset, final int length) {
        if (memoryManager == null) {
            memoryManager = getDefaultMemoryManager();
        }

        if (memoryManager instanceof WrapperAware) {
            return ((WrapperAware) memoryManager).wrap(array, offset, length);
        }

        final Buffer buffer = memoryManager.allocate(length);
        buffer.put(array, offset, length);
        buffer.flip();
        return buffer;
    }

    /**
     * Returns {@link Buffer}, which wraps the {@link ByteBuffer}.
     *
     * @param memoryManager {@link MemoryManager}, which should be
     *                       used for wrapping.
     * @param byteBuffer {@link ByteBuffer} to wrap
     *
     * @return {@link Buffer} wrapper on top of passed {@link ByteBuffer}.
     */
    public static Buffer wrap(final MemoryManager memoryManager,
            final ByteBuffer byteBuffer) {
        if (memoryManager instanceof WrapperAware) {
            return ((WrapperAware) memoryManager).wrap(byteBuffer);
        } else if (byteBuffer.hasArray()) {
            return wrap(memoryManager, byteBuffer.array(),
                    byteBuffer.arrayOffset() + byteBuffer.position(),
                    byteBuffer.remaining());
        }

        throw new IllegalStateException("Can not wrap ByteBuffer");
    }



    /**
     * Slice {@link ByteBuffer} of required size from big chunk.
     * Passed chunk position will be changed, after the slicing (chunk.position += size).
     *
     * @param chunk big {@link ByteBuffer} pool.
     * @param size required slice size.
     *
     * @return sliced {@link ByteBuffer} of required size.
     */
    public static ByteBuffer slice(final ByteBuffer chunk, final int size) {
        chunk.limit(chunk.position() + size);
        final ByteBuffer view = chunk.slice();
        chunk.position(chunk.limit());
        chunk.limit(chunk.capacity());

        return view;
    }


    /**
     * Get the {@link ByteBuffer}'s slice basing on its passed position and limit.
     * Position and limit values of the passed {@link ByteBuffer} won't be changed.
     * The result {@link ByteBuffer} position will be equal to 0, and limit
     * equal to number of sliced bytes (limit - position).
     *
     * @param byteBuffer {@link ByteBuffer} to slice/
     * @param position the position in the passed byteBuffer, the slice will start from.
     * @param limit the limit in the passed byteBuffer, the slice will be ended.
     *
     * @return sliced {@link ByteBuffer} of required size.
     */
    public static ByteBuffer slice(final ByteBuffer byteBuffer,
            final int position, final int limit) {
        final int oldPos = byteBuffer.position();
        final int oldLimit = byteBuffer.limit();

        setPositionLimit(byteBuffer, position, limit);

        final ByteBuffer slice = byteBuffer.slice();

        setPositionLimit(byteBuffer, oldPos, oldLimit);

        return slice;
    }

    public static String toStringContent(final ByteBuffer byteBuffer,
            Charset charset, final int position, final int limit) {

        if (charset == null) {
            charset = Charset.defaultCharset();
        }

        final int oldPosition = byteBuffer.position();
        final int oldLimit = byteBuffer.limit();
        setPositionLimit(byteBuffer, position, limit);
        
        try {
            return charset.decode(byteBuffer).toString();
        } finally {
            setPositionLimit(byteBuffer, oldPosition, oldLimit);
        }
    }

    public static void setPositionLimit(final Buffer buffer,
            final int position, final int limit) {
        buffer.limit(limit);
        buffer.position(position);
    }

    public static void setPositionLimit(final ByteBuffer buffer,
            final int position, final int limit) {
        buffer.limit(limit);
        buffer.position(position);
    }

    public static void put(final ByteBuffer srcBuffer, final int srcOffset,
            final int length, final ByteBuffer dstBuffer) {

        if (dstBuffer.remaining() < length) {
            LOGGER.log(Level.WARNING,
                    LogMessages.WARNING_GRIZZLY_BUFFERS_OVERFLOW_EXCEPTION(
                    srcBuffer, srcOffset, length, dstBuffer));
            throw new BufferOverflowException();
        }

        if (srcBuffer.hasArray() && dstBuffer.hasArray()) {

            System.arraycopy(srcBuffer.array(),
                    srcBuffer.arrayOffset() + srcOffset,
                    dstBuffer.array(),
                    dstBuffer.arrayOffset() + dstBuffer.position(),
                    length);
            dstBuffer.position(dstBuffer.position() + length);
        } else {
            for(int i = srcOffset; i < srcOffset + length; i++) {
                dstBuffer.put(srcBuffer.get(i));
            }
        }
    }

    public static void put(final Buffer src, final int position,
            final int length, final Buffer dstBuffer) {

        if (dstBuffer.remaining() < length) {
            throw new BufferOverflowException();
        }

        if (!src.isComposite()) {
            final ByteBuffer srcByteBuffer = src.toByteBuffer();
            if (srcByteBuffer.hasArray()) {
                dstBuffer.put(srcByteBuffer.array(),
                        srcByteBuffer.arrayOffset() + position, length);
            } else {
                for(int i=0; i srcBuffer.remaining()) {
                throw new BufferUnderflowException();
            }

            System.arraycopy(srcBuffer.array(),
                    srcBuffer.arrayOffset() + srcBuffer.position(),
                    dstBytes, dstOffset, length);
            srcBuffer.position(srcBuffer.position() + length);
        } else {
            srcBuffer.get(dstBytes, dstOffset, length);
        }
    }

    public static void put(final byte[] srcBytes, final int srcOffset,
            final int length, final ByteBuffer dstBuffer) {
        if (dstBuffer.hasArray()) {
            if (length > dstBuffer.remaining()) {
                throw new BufferOverflowException();
            }

            System.arraycopy(srcBytes, srcOffset, dstBuffer.array(),
                    dstBuffer.arrayOffset() + dstBuffer.position(), length);
            dstBuffer.position(dstBuffer.position() + length);
        } else {
            dstBuffer.put(srcBytes, srcOffset, length);
        }
    }

    /**
     * Append two {@link Buffer}s.
     * If one of the {@link Buffer}s is null - then another {@link Buffer} will
     * be returned as result.
     * If the first {@link Buffer} is {@link CompositeBuffer} then the second
     * {@link Buffer} will be appended to it via
     * {@link CompositeBuffer#append(Buffer)}, else if the second
     * {@link Buffer} is {@link CompositeBuffer} then the first {@link Buffer}
     * will be prepended to it via
     * {@link CompositeBuffer#prepend(com.pdd.pop.ext.glassfish.grizzly.Buffer)}.
     * If none of the {@link Buffer} parameters is null nor
     * {@link CompositeBuffer}s - then new {@link CompositeBuffer} will be created
     * and both {@link Buffer}s will be added there. The resulting
     * {@link CompositeBuffer} will be disallowed for disposal.
     * 
     * @param memoryManager the {@link MemoryManager} to use if a new {@link Buffer}
     *                      needs to be allocated in order to perform the requested
     *                      operation.
     * @param buffer1 the {@link Buffer} to append to.
     * @param buffer2 the {@link Buffer} to append.
     * 
     * @return the result of appending of two {@link Buffer}s.
     */
    public static Buffer appendBuffers(final MemoryManager memoryManager,
            final Buffer buffer1, final Buffer buffer2) {
        return appendBuffers(memoryManager, buffer1, buffer2, false);
    }
    
    /**
     * Append two {@link Buffer}s.
     * If one of the {@link Buffer}s is null - then another {@link Buffer} will
     * be returned as result.
     * If the first {@link Buffer} is {@link CompositeBuffer} then the second
     * {@link Buffer} will be appended to it via
     * {@link CompositeBuffer#append(Buffer)}, else if the second
     * {@link Buffer} is {@link CompositeBuffer} then the first {@link Buffer}
     * will be prepended to it via
     * {@link CompositeBuffer#prepend(com.pdd.pop.ext.glassfish.grizzly.Buffer)}.
     * If none of the {@link Buffer} parameters is null nor
     * {@link CompositeBuffer}s - then new {@link CompositeBuffer} will be created
     * and both {@link Buffer}s will be added there. The resulting
     * {@link CompositeBuffer} will be assigned according to the
     * isCompositeBufferDisposable parameter.
     * 
     * @param memoryManager the {@link MemoryManager} to use if a new {@link Buffer}
     *                      needs to be allocated in order to perform the requested
     *                      operation.
     * @param buffer1 the {@link Buffer} to append to.
     * @param buffer2 the {@link Buffer} to append.
     * @param isCompositeBufferDisposable flag indicating whether or not the
     *                                    resulting composite buffer may be disposed.
     * 
     * @return the result of appending of two {@link Buffer}s.
     */
    public static Buffer appendBuffers(final MemoryManager memoryManager,
            final Buffer buffer1, final Buffer buffer2,
            final boolean isCompositeBufferDisposable) {

        if (buffer1 == null) {
            return buffer2;
        } else if (buffer2 == null) {
            return buffer1;
        }
        
        if (buffer1.order() != buffer2.order()) {
            LOGGER.fine("Appending buffers with different ByteOrder."
                    + "The result Buffer's order will be the same as the first Buffer's ByteOrder");
            buffer2.order(buffer1.order());
        }
        
        // we can only append to or prepend buffer1 if the limit()
        // is the same as capacity.  If it's not, then appending or
        // prepending effectively clobbers the limit causing an invalid
        // view of the data.  So instead, we allocate a new CompositeBuffer
        // and append buffer1 and buffer2 to it.  The underlying buffers
        // aren't changed so the limit they have is maintained.
        if (buffer1.isComposite() && (buffer1.capacity() == buffer1.limit())) {
            ((CompositeBuffer) buffer1).append(buffer2);
            return buffer1;
        } if (buffer2.isComposite() && (buffer2.position() == 0)) {
            ((CompositeBuffer) buffer2).prepend(buffer1);
            return buffer2;
        } else {
            final CompositeBuffer compositeBuffer =
                    CompositeBuffer.newBuffer(memoryManager);

            compositeBuffer.order(buffer1.order());
            compositeBuffer.append(buffer1);
            compositeBuffer.append(buffer2);
            compositeBuffer.allowBufferDispose(isCompositeBufferDisposable);
            
            return compositeBuffer;
        }
    }

    /**
     * Fill the {@link Buffer} with the specific byte value. {@link Buffer}'s
     * position won't be changed.
     *
     * @param buffer {@link Buffer}
     * @param b value
     */
    public static void fill(final Buffer buffer, final byte b) {
        fill(buffer, buffer.position(), buffer.limit(), b);
    }

    /**
     * Fill the {@link Buffer}'s part [position, limit) with the specific byte value starting from the
     * {@link Buffer}'s position won't be changed.
     *
     * @param buffer {@link Buffer}
     * @param position {@link Buffer} position to start with (inclusive)
     * @param limit {@link Buffer} limit, where filling ends (exclusive)
     * @param b value
     */
    public static void fill(final Buffer buffer, final int position,
            final int limit, final byte b) {
        if (!buffer.isComposite()) {
            final ByteBuffer byteBuffer = buffer.toByteBuffer();
            fill(byteBuffer, position, limit, b);
        } else {
            final ByteBufferArray array = buffer.toByteBufferArray(position, limit);
            final ByteBuffer[] byteBuffers = array.getArray();
            final int size = array.size();

            for (int i = 0; i < size; i++) {
                final ByteBuffer byteBuffer = byteBuffers[i];
                fill(byteBuffer, b);
            }

            array.restore();
            array.recycle();
        }
    }

    /**
     * Fill the {@link ByteBuffer} with the specific byte value. {@link ByteBuffer}'s
     * position won't be changed.
     *
     * @param byteBuffer {@link ByteBuffer}
     * @param b value
     */
    public static void fill(final ByteBuffer byteBuffer, final byte b) {
        fill(byteBuffer, byteBuffer.position(), byteBuffer.limit(), b);
    }

    /**
     * Fill the {@link ByteBuffer}'s part [position, limit) with the specific byte value starting from the
     * {@link ByteBuffer}'s position won't be changed.
     *
     * @param byteBuffer {@link ByteBuffer}
     * @param position {@link ByteBuffer} position to start with (inclusive)
     * @param limit {@link Buffer} limit, where filling ends (exclusive)
     * @param b value
     */
    public static void fill(final ByteBuffer byteBuffer, final int position,
            final int limit, final byte b) {
        if (byteBuffer.hasArray()) {
            final int arrayOffset = byteBuffer.arrayOffset();
            Arrays.fill(byteBuffer.array(), arrayOffset + position,
                    arrayOffset + limit, b);
        } else {
            for (int i = position; i < limit; i++) {
                byteBuffer.put(i, b);
            }
        }
    }

    /**
     * Clones the source {@link Buffer}.
     * The method returns a new {@link Buffer} instance, which has the same content.
     * Please note, source and result {@link Buffer}s have the same content,
     * but it is *not* shared, so the following content changes in one of the
     * {@link Buffer}s won't be visible in another one.
     *
     * @param srcBuffer the source {@link Buffer}.
     * @return the cloned {@link Buffer}.
     */
    public static Buffer cloneBuffer(final Buffer srcBuffer) {
        return cloneBuffer(srcBuffer, srcBuffer.position(), srcBuffer.limit());
    }

    /**
     * Clones the source {@link Buffer}.
     * The method returns a new {@link Buffer} instance, which has the same content.
     * Please note, source and result {@link Buffer}s have the same content,
     * but it is *not* shared, so the following content changes in one of the
     * {@link Buffer}s won't be visible in another one.
     *
     * @param srcBuffer the source {@link Buffer}.
     * @param position the start position in the srcBuffer
     * @param limit the end position in the srcBuffer
     * @return the cloned {@link Buffer}.
     */
    public static Buffer cloneBuffer(final Buffer srcBuffer,
            final int position, final int limit) {
        final int srcLength = limit - position;
        if (srcLength == 0) { // make sure clone doesn't return EMPTY_BUFFER 
            return wrap(getDefaultMemoryManager(), EMPTY_BYTE_BUFFER);
        }
        
        final Buffer clone = getDefaultMemoryManager().allocate(srcLength);
        clone.put(srcBuffer, position, srcLength);
        clone.order(srcBuffer.order());
        
        return clone.flip();
    }

    /**
     * Reads data from the {@link FileChannel} into the {@link Buffer}.
     * 
     * @param fileChannel the {@link FileChannel} to read data from.
     * @param buffer the destination {@link Buffer}.
     * @return the number of bytes read, or -1 if the end of file is reached.
     * 
     * @throws IOException 
     */
    public static long readFromFileChannel(final FileChannel fileChannel,
            final Buffer buffer) throws IOException {

        final long bytesRead;
        
        if (!buffer.isComposite()) {
            final ByteBuffer bb = buffer.toByteBuffer();
            final int oldPos = bb.position();
            bytesRead = fileChannel.read(bb);
            bb.position(oldPos);
        } else {
            final ByteBufferArray array = buffer.toByteBufferArray();
            bytesRead = fileChannel.read(
                    array.getArray(), 0, array.size());

            array.restore();
            array.recycle();
        }
        
        if (bytesRead > 0) {
            buffer.position(buffer.position() + (int) bytesRead);
        }
        
        return bytesRead;
    }
    
    /**
     * Writes data from the {@link Buffer} into the {@link FileChannel}.
     * 
     * @param fileChannel the {@link FileChannel} to write data to.
     * @param buffer the source {@link Buffer}.
     * @return the number of bytes written, possibly zero.
     * 
     * @throws IOException 
     */
    @SuppressWarnings("UnusedDeclaration")
    public static long writeToFileChannel(final FileChannel fileChannel,
            final Buffer buffer) throws IOException {

        final long bytesWritten;
        
        if (!buffer.isComposite()) {
            final ByteBuffer bb = buffer.toByteBuffer();
            final int oldPos = bb.position();
            bytesWritten = fileChannel.write(bb);
            bb.position(oldPos);
        } else {
            final ByteBufferArray array = buffer.toByteBufferArray();
            bytesWritten = fileChannel.write(
                    array.getArray(), 0, array.size());

            array.restore();
            array.recycle();
        }

        if (bytesWritten > 0) {
            buffer.position(buffer.position() + (int) bytesWritten);
        }
        
        return bytesWritten;
    }

    /**
     * Returns the {@link Buffer}'s {@link String} representation in a form:
     * {@link Buffer#toString()} + "[" +  + "..." +  + "]"
     * For example:
     * 
HeapBuffer (1781633478) [pos=0 lim=285 cap=285][abcde...xyz]
* * @param buffer the {@link Buffer}, could be null * @param headBytesCount the number of heading bytes to include (larger or equal to 0) * @param tailBytesCount the number of tailing bytes to include (larger or equal to 0) * @return the {@link Buffer}'s {@link String} representation, or null, * if the {@link Buffer} is null */ public String toStringContent(final Buffer buffer, final int headBytesCount, final int tailBytesCount) { if (buffer == null) { return null; } return toStringContent(buffer, headBytesCount, tailBytesCount, Charset.defaultCharset()); } /** * Returns the {@link Buffer}'s {@link String} representation in a form: * {@link Buffer#toString()} + "[" + + "..." + + "]" * For example: *
HeapBuffer (1781633478) [pos=0 lim=285 cap=285][abcde...xyz]
* * @param buffer the {@link Buffer}, could be null * @param headBytesCount the number of heading bytes to include (larger or equal to 0) * @param tailBytesCount the number of tailing bytes to include (larger or equal to 0) * @param charset {@link Charset}, if null the {@link Charset#defaultCharset()} will * be used * @return the {@link Buffer}'s {@link String} representation, or null, * if the {@link Buffer} is null */ public String toStringContent(final Buffer buffer, final int headBytesCount, final int tailBytesCount, final Charset charset) { if (buffer == null) { return null; } if (headBytesCount < 0 || tailBytesCount < 0) { throw new IllegalArgumentException("count can't be negative"); } final String toString = buffer.toString(); final StringBuilder sb = new StringBuilder( toString.length() + headBytesCount + tailBytesCount + 5); sb.append(toString); if (buffer.remaining() <= headBytesCount + tailBytesCount) { sb.append('[').append(buffer.toStringContent(charset)).append(']'); } else { sb.append('['); if (headBytesCount > 0) { sb.append(buffer.toStringContent(charset, buffer.position(), buffer.position() + headBytesCount)); } sb.append("..."); if (tailBytesCount > 0) { sb.append(buffer.toStringContent(charset, buffer.limit() - tailBytesCount, buffer.limit())); } sb.append(']'); } return sb.toString(); } /** * Generates a hex dump of the provided {@link Buffer}. * @param appendable the {@link Appendable} to write the hex dump to. * @param buffer the {@link Buffer} to dump. * * @since 2.3.23 */ @SuppressWarnings("unused") public static void dumpBuffer(final Appendable appendable, final Buffer buffer) { final Formatter formatter = new Formatter(appendable); dumpBuffer0(formatter, appendable, buffer); } @SuppressWarnings("UnusedParameters") private static void dumpBuffer0(final Formatter formatter, final Appendable appendable, final Buffer buffer) { if (buffer.isComposite()) { final BufferArray bufferArray = buffer.toBufferArray(); final int size = bufferArray.size(); final Buffer[] buffers = bufferArray.getArray(); formatter.format("%s\n", buffer.toString()); for (int i = 0; i < size; i++) { dumpBuffer0(formatter, appendable, buffers[i]); } formatter.format("End CompositeBuffer (%d)", System.identityHashCode(buffer)); } else { dumpBuffer0(formatter, buffer); } } private static void dumpBuffer0(final Formatter formatter, final Buffer buffer) { formatter.format("%s\n", buffer.toString()); int line = 0; for (int i = 0, len = buffer.remaining() / 16; i < len; i++, line += 16) { byte b0 = buffer.get(line); byte b1 = buffer.get(line + 1); byte b2 = buffer.get(line + 2); byte b3 = buffer.get(line + 3); byte b4 = buffer.get(line + 4); byte b5 = buffer.get(line + 5); byte b6 = buffer.get(line + 6); byte b7 = buffer.get(line + 7); byte b8 = buffer.get(line + 8); byte b9 = buffer.get(line + 9); byte b10 = buffer.get(line + 10); byte b11 = buffer.get(line + 11); byte b12 = buffer.get(line + 12); byte b13 = buffer.get(line + 13); byte b14 = buffer.get(line + 14); byte b15 = buffer.get(line + 15); formatter.format(DumpStrings.DUMP_STRINGS[15], line, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, getChar(b0), getChar(b1), getChar(b2), getChar(b3), getChar(b4), getChar(b5), getChar(b6), getChar(b7), getChar(b8), getChar(b9), getChar(b10), getChar(b11), getChar(b12), getChar(b13), getChar(b14), getChar(b15)); } int remaining = buffer.remaining() % 16; if (remaining > 0) { final Object[] args = new Object[(remaining << 1) + 1]; args[0] = remaining + line; for (int i = 0, aIdx = 1; i < remaining; i++, aIdx++) { final int b = buffer.get(line + i); args[aIdx] = b; args[aIdx + remaining] = getChar(b); } formatter.format(DumpStrings.DUMP_STRINGS[remaining - 1], args); } } // --------------------------------------------------------- Private Methods private static char getChar(final int val) { final char c = (char) val; return ((Character.isWhitespace(c) || Character.isISOControl(c)) ? '.' : c); } private static MemoryManager getDefaultMemoryManager() { return MemoryManager.DEFAULT_MEMORY_MANAGER; } // ---------------------------------------------------------- Nested Classes private static final class DumpStrings { private static final String[] DUMP_STRINGS = { "%10d %02x %c\n", "%10d %02x %02x %c%c\n", "%10d %02x %02x %02x %c%c%c\n", "%10d %02x %02x %02x %02x %c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", "%10d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n" }; } // END DumpStrings }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy