com.glaf.core.util.ByteBufferUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.glaf.core.util.ByteBufferUtils.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 com.glaf.core.util;

import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.util.Arrays;

import org.apache.commons.lang3.ArrayUtils;

/**
 * Utility methods to make ByteBuffers less painful The following should
 * illustrate the different ways byte buffers can be used
 * 
 * public void testArrayOffet() {
 * 
 * byte[] b = "test_slice_array".getBytes(); ByteBuffer bb =
 * ByteBuffer.allocate(1024);
 * 
 * assert bb.position() == 0; assert bb.limit() == 1024; assert bb.capacity() ==
 * 1024;
 * 
 * bb.put(b);
 * 
 * assert bb.position() == b.length; assert bb.remaining() == bb.limit() -
 * bb.position();
 * 
 * ByteBuffer bb2 = bb.slice();
 * 
 * assert bb2.position() == 0;
 * 
 * //slice should begin at other buffers current position assert
 * bb2.arrayOffset() == bb.position();
 * 
 * //to match the position in the underlying array one needs to //track
 * arrayOffset assert bb2.limit()+bb2.arrayOffset() == bb.limit();
 * 
 * 
 * assert bb2.remaining() == bb.remaining();
 * 
 * }
 * 
 * }
 * 
 */
public class ByteBufferUtils {
    public static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.wrap(ArrayUtils.EMPTY_BYTE_ARRAY);

    /**
     * Decode a String representation.
     * 
     * @param buffer
     *            a byte buffer holding the string representation
     * @param position
     *            the starting position in {@code buffer} to start decoding from
     * @param length
     *            the number of bytes from {@code buffer} to use
     * @param charset
     *            the String encoding charset
     * @return the decoded string
     */
    public static String string(ByteBuffer buffer, int position, int length, Charset charset)
            throws CharacterCodingException {
        ByteBuffer copy = buffer.duplicate();
        copy.position(position);
        copy.limit(copy.position() + length);
        return string(copy, charset);
    }

    /**
     * Decode a String representation.
     * 
     * @param buffer
     *            a byte buffer holding the string representation
     * @param charset
     *            the String encoding charset
     * @return the decoded string
     */
    public static String string(ByteBuffer buffer, Charset charset) throws CharacterCodingException {
        return charset.newDecoder().decode(buffer.duplicate()).toString();
    }

    /**
     * You should almost never use this. Instead, use the write* methods to
     * avoid copies.
     */
    public static byte[] getArray(ByteBuffer buffer) {
        int length = buffer.remaining();

        if (buffer.hasArray()) {
            int boff = buffer.arrayOffset() + buffer.position();
            if (boff == 0 && length == buffer.array().length)
                return buffer.array();
            else
                return Arrays.copyOfRange(buffer.array(), boff, boff + length);
        }
        // else, DirectByteBuffer.get() is the fastest route
        byte[] bytes = new byte[length];
        buffer.duplicate().get(bytes);

        return bytes;
    }

    /**
     * ByteBuffer adaptation of org.apache.commons.lang3.ArrayUtils.lastIndexOf
     * method
     * 
     * @param buffer
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index (i.e. BB position) to travers backwards from
     * @return the last index (i.e. BB position) of the value within the array
     *         [between buffer.position() and buffer.limit()]; <code>-1</code>
     *         if not found.
     */
    public static int lastIndexOf(ByteBuffer buffer, byte valueToFind, int startIndex) {
        assert buffer != null;

        if (startIndex < buffer.position()) {
            return -1;
        } else if (startIndex >= buffer.limit()) {
            startIndex = buffer.limit() - 1;
        }

        for (int i = startIndex; i >= buffer.position(); i--) {
            if (valueToFind == buffer.get(i))
                return i;
        }

        return -1;
    }

    /**
     * Encode a String in a ByteBuffer using the provided charset.
     * 
     * @param s
     *            the string to encode
     * @param charset
     *            the String encoding charset to use
     * @return the encoded string
     */
    public static ByteBuffer bytes(String s, Charset charset) {
        return ByteBuffer.wrap(s.getBytes(charset));
    }

    /**
     * @return a new copy of the data in @param buffer USUALLY YOU SHOULD USE
     *         ByteBuffer.duplicate() INSTEAD, which creates a new Buffer (so
     *         you can mutate its position without affecting the original)
     *         without copying the underlying array.
     */
    public static ByteBuffer clone(ByteBuffer buffer) {
        assert buffer != null;

        if (buffer.remaining() == 0)
            return EMPTY_BYTE_BUFFER;

        ByteBuffer clone = ByteBuffer.allocate(buffer.remaining());

        if (buffer.hasArray()) {
            System.arraycopy(buffer.array(), buffer.arrayOffset() + buffer.position(), clone.array(), 0,
                    buffer.remaining());
        } else {
            clone.put(buffer.duplicate());
            clone.flip();
        }

        return clone;
    }

    public static void arrayCopy(ByteBuffer buffer, int position, byte[] bytes, int offset, int length) {
        if (buffer.hasArray())
            System.arraycopy(buffer.array(), buffer.arrayOffset() + position, bytes, offset, length);
        else
            ((ByteBuffer) buffer.duplicate().position(position)).get(bytes, offset, length);
    }

    /**
     * Transfer bytes from one ByteBuffer to another. This function acts as
     * System.arrayCopy() but for ByteBuffers.
     * 
     * @param src
     *            the source ByteBuffer
     * @param srcPos
     *            starting position in the source ByteBuffer
     * @param dst
     *            the destination ByteBuffer
     * @param dstPos
     *            starting position in the destination ByteBuffer
     * @param length
     *            the number of bytes to copy
     */
    public static void arrayCopy(ByteBuffer src, int srcPos, ByteBuffer dst, int dstPos, int length) {
        if (src.hasArray() && dst.hasArray()) {
            System.arraycopy(src.array(), src.arrayOffset() + srcPos, dst.array(), dst.arrayOffset() + dstPos,
                    length);
        } else {
            if (src.limit() - srcPos < length || dst.limit() - dstPos < length)
                throw new IndexOutOfBoundsException();

            for (int i = 0; i < length; i++)

                dst.put(dstPos++, src.get(srcPos++));
        }
    }

    public static void writeWithLength(ByteBuffer bytes, DataOutput out) throws IOException {
        out.writeInt(bytes.remaining());
        write(bytes, out); // writing data bytes to output source
    }

    public static void write(ByteBuffer buffer, DataOutput out) throws IOException {
        if (buffer.hasArray()) {
            out.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
        } else {
            for (int i = buffer.position(); i < buffer.limit(); i++) {
                out.writeByte(buffer.get(i));
            }
        }
    }

    /**
     * Convert a byte buffer to an integer. Does not change the byte buffer
     * position.
     * 
     * @param bytes
     *            byte buffer to convert to integer
     * @return int representation of the byte buffer
     */
    public static int toInt(ByteBuffer bytes) {
        return bytes.getInt(bytes.position());
    }

    public static long toLong(ByteBuffer bytes) {
        return bytes.getLong(bytes.position());
    }

    public static float toFloat(ByteBuffer bytes) {
        return bytes.getFloat(bytes.position());
    }

    public static double toDouble(ByteBuffer bytes) {
        return bytes.getDouble(bytes.position());
    }

    public static ByteBuffer bytes(int i) {
        return ByteBuffer.allocate(4).putInt(0, i);
    }

    public static ByteBuffer bytes(long n) {
        return ByteBuffer.allocate(8).putLong(0, n);
    }

    public static ByteBuffer bytes(float f) {
        return ByteBuffer.allocate(4).putFloat(0, f);
    }

    public static ByteBuffer bytes(double d) {
        return ByteBuffer.allocate(8).putDouble(0, d);
    }

    public static InputStream inputStream(ByteBuffer bytes) {
        final ByteBuffer copy = bytes.duplicate();

        return new InputStream() {
            public int read() throws IOException {
                if (!copy.hasRemaining())
                    return -1;

                return copy.get() & 0xFF;
            }

            @Override
            public int read(byte[] bytes, int off, int len) throws IOException {
                if (!copy.hasRemaining())
                    return -1;

                len = Math.min(len, copy.remaining());
                copy.get(bytes, off, len);
                return len;
            }

            @Override
            public int available() throws IOException {
                return copy.remaining();
            }
        };
    }

    public static String bytesToHex(ByteBuffer bytes) {
        final int offset = bytes.position();
        final int size = bytes.remaining();
        final char[] c = new char[size * 2];
        for (int i = 0; i < size; i++) {
            final int bint = bytes.get(i + offset);
            c[i * 2] = Hex.byteToChar[(bint & 0xf0) >> 4];
            c[1 + i * 2] = Hex.byteToChar[bint & 0x0f];
        }
        return Hex.wrapCharArray(c);
    }

    public static ByteBuffer hexToBytes(String str) {
        return ByteBuffer.wrap(Hex.hexToBytes(str));
    }

    /**
     * Compare two ByteBuffer at specified offsets for length. Compares the non
     * equal bytes as unsigned.
     * 
     * @param bytes1
     *            First byte buffer to compare.
     * @param offset1
     *            Position to start the comparison at in the first array.
     * @param bytes2
     *            Second byte buffer to compare.
     * @param offset2
     *            Position to start the comparison at in the second array.
     * @param length
     *            How many bytes to compare?
     * @return -1 if byte1 is less than byte2, 1 if byte2 is less than byte1 or
     *         0 if equal.
     */
    public static int compareSubArrays(ByteBuffer bytes1, int offset1, ByteBuffer bytes2, int offset2, int length) {
        if (null == bytes1) {
            if (null == bytes2)
                return 0;
            else
                return -1;
        }
        if (null == bytes2)
            return 1;

        assert bytes1.limit() >= offset1
                + length : "The first byte array isn't long enough for the specified offset and length.";
        assert bytes2.limit() >= offset2
                + length : "The second byte array isn't long enough for the specified offset and length.";
        for (int i = 0; i < length; i++) {
            byte byte1 = bytes1.get(offset1 + i);
            byte byte2 = bytes2.get(offset2 + i);
            if (byte1 == byte2)
                continue;
            // compare non-equal bytes as unsigned
            return (byte1 & 0xFF) < (byte2 & 0xFF) ? -1 : 1;
        }
        return 0;
    }

    /**
     * ?flush?ByteBufferlimitposition0,?fill?
     * 
     * @param capacity
     * @return
     */
    public static ByteBuffer allocate(int capacity) {
        ByteBuffer buff = ByteBuffer.allocate(capacity);
        buff.limit(0);
        return buff;
    }

    public static ByteBuffer allocateDirect(int capacity) {
        ByteBuffer buff = ByteBuffer.allocateDirect(capacity);
        buff.limit(0);
        return buff;
    }

    /**
     * buffer, ?positoin limit ?0
     * 
     * @param buffer
     */
    public static void clear(ByteBuffer buffer) {
        if (buffer != null) {
            buffer.position(0);
            buffer.limit(0);
        }
    }

    /**
     * buffer, fill?
     * 
     * @param buffer
     */
    public static void clearToFill(ByteBuffer buffer) {
        if (buffer != null) {
            buffer.position(0);
            buffer.limit(buffer.capacity());
        }
    }

    /**
     * fill?
     * 
     * @param buffer
     * @return
     */
    public static int flipToFill(ByteBuffer buffer) {
        int position = buffer.position();
        int limit = buffer.limit();
        // flush??fill?
        if (position == limit) {
            buffer.position(0);
            buffer.limit(buffer.capacity());
            return 0;
        }
        // ?limit equal capacity,?
        int capacity = buffer.capacity();
        if (limit == capacity) {
            buffer.compact();
            return 0;
        }
        // ??
        buffer.position(limit);
        buffer.limit(capacity);
        return position;
    }

    /**
     * flush?????limit,??position, position0 ,
     * ByteBuffer.flip()
     * 
     * @param buffer
     * @param position
     */
    public static void flipToFlush(ByteBuffer buffer, int position) {
        buffer.limit(buffer.position());
        buffer.position(position);
    }

    /**
     * buffer?
     * 
     * @param buffer
     * @return
     */
    public static byte[] toArray(ByteBuffer buffer) {
        // ?heap buffer
        if (buffer.hasArray()) {
            byte[] array = buffer.array();
            int from = buffer.arrayOffset() + buffer.position();
            return Arrays.copyOfRange(array, from, from + buffer.remaining());
        }
        //  direct buffer
        else {
            byte[] to = new byte[buffer.remaining()];
            buffer.slice().get(to);
            return to;
        }
    }

    /**
     * ? remaining() ? limit - position
     * 
     * @param buff
     * @return
     */
    public static boolean isEmpty(ByteBuffer buff) {
        return buff == null || buff.remaining() == 0;
    }

    public static boolean hasContent(ByteBuffer buff) {
        return buff != null && buff.remaining() > 0;
    }

    public static boolean isFull(ByteBuffer buff) {
        return buff != null && buff.limit() == buff.capacity();
    }

    public static int length(ByteBuffer buffer) {
        return buffer == null ? 0 : buffer.remaining();
    }

    public static int space(ByteBuffer buffer) {
        if (buffer == null) {
            return 0;
        }
        return buffer.capacity() - buffer.limit();
    }

    public static boolean compact(ByteBuffer buffer) {
        if (buffer.position() == 0) {
            return false;
        }
        boolean full = buffer.limit() == buffer.capacity();
        buffer.compact().flip();
        return full && buffer.limit() < buffer.capacity();
    }

    /**
     * from to 
     * 
     * @param fromBuffer
     *            Buffer ? flush
     * @param toBuffer
     *            Buffer ? fill
     * @return number of bytes moved
     */
    public static int put(ByteBuffer fromBuffer, ByteBuffer toBuffer) {
        int put;
        int remaining = fromBuffer.remaining();
        if (remaining > 0) { // 
            if (remaining <= toBuffer.remaining()) {
                toBuffer.put(fromBuffer);
                put = remaining;
                // from 
                fromBuffer.position(fromBuffer.limit());
            }
            // heap buffer
            else if (fromBuffer.hasArray()) {
                put = toBuffer.remaining();
                // ??
                toBuffer.put(fromBuffer.array(), fromBuffer.arrayOffset() + fromBuffer.position(), put);
                fromBuffer.position(fromBuffer.position() + put);
            }
            // direct buffer
            else {
                // ??
                put = toBuffer.remaining();
                ByteBuffer slice = fromBuffer.slice();
                slice.limit(put);
                toBuffer.put(slice);
                fromBuffer.position(fromBuffer.position() + put);
            }
        } else {
            put = 0;
        }
        return put;
    }

    /**
     *  byte[] buffer
     * 
     * @param toBuffer
     * @param bytes
     * @param offset
     * @param len
     */
    public static void append(ByteBuffer toBuffer, byte[] bytes, int offset, int len) { // ?
        int pos = flipToFill(toBuffer);
        try {
            toBuffer.put(bytes, offset, len);
        } finally {
            // ?
            flipToFlush(toBuffer, pos);
        }
    }

    public static void readFrom(InputStream is, int needed, ByteBuffer buffer) throws IOException {
        ByteBuffer tmp = allocate(8192);
        while (needed > 0 && buffer.hasRemaining()) {
            int l = is.read(tmp.array(), 0, 8192);
            if (l < 0) {
                break;
            }
            tmp.position(0);
            tmp.limit(l);
            buffer.put(tmp);
        }
    }

}