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

gurux.dlms.GXByteBuffer Maven / Gradle / Ivy

There is a newer version: 4.0.72
Show newest version
//
// --------------------------------------------------------------------------
//  Gurux Ltd
// 
//
//
// Filename:        $HeadURL$
//
// Version:         $Revision$,
//                  $Date$
//                  $Author$
//
// Copyright (c) Gurux Ltd
//
//---------------------------------------------------------------------------
//
//  DESCRIPTION
//
// This file is a part of Gurux Device Framework.
//
// Gurux Device Framework is Open Source software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License 
// as published by the Free Software Foundation; version 2 of the License.
// Gurux Device Framework is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
// See the GNU General Public License for more details.
//
// More information of Gurux products: https://www.gurux.org
//
// This code is licensed under the GNU General Public License v2. 
// Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
//---------------------------------------------------------------------------

//
// --------------------------------------------------------------------------
package gurux.dlms;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

import gurux.dlms.enums.DataType;
import gurux.dlms.internal.GXCommon;

/**
 * Byte array class is used to save received bytes.
 * 
 * @author Gurux Ltd.
 */
public class GXByteBuffer {
    static final int ARRAY_CAPACITY = 10;
    /**
     * Byte array data.
     */
    private byte[] data;
    /**
     * Size of received bytes.
     */
    private int size;
    /**
     * Position of byte array.
     */
    private int position;

    /**
     * Constructor.
     */
    public GXByteBuffer() {

    }

    /**
     * Constructor.
     * 
     * @param capacity
     *            Buffer capacity.
     */
    public GXByteBuffer(final int capacity) {
        capacity(capacity);
    }

    /**
     * Constructor.
     * 
     * @param value
     *            Byte array to attach.
     */
    public GXByteBuffer(final byte[] value) {
        capacity(value.length);
        set(value);
    }

    /**
     * Constructor.
     * 
     * @param value
     *            Byte array to attach.
     */
    public GXByteBuffer(final GXByteBuffer value) {
        capacity(value.size - value.position);
        set(value);
    }

    /**
     * Clear buffer but do not release memory.
     */
    public final void clear() {
        position = 0;
        size = 0;
    }

    /**
     * Allocate new size for the array in bytes.
     * 
     * @param capacity
     *            Buffer capacity.
     */
    public final void capacity(final int capacity) {
        if (capacity == 0) {
            setData(null);
            size = 0;
            position = 0;
        } else {
            if (getData() == null) {
                setData(new byte[capacity]);
            } else {
                byte[] tmp = getData();
                setData(new byte[capacity]);
                if (size < capacity) {
                    System.arraycopy(tmp, 0, getData(), 0, size);
                } else {
                    System.arraycopy(tmp, 0, getData(), 0, capacity);
                    size(capacity);
                }
            }
        }
    }

    /**
     * Buffer capacity.
     * 
     * @return Buffer capacity.
     */
    public final int capacity() {
        if (getData() == null) {
            return 0;
        }
        return getData().length;
    }

    /**
     * @param value
     *            Buffer position.
     */
    public final void position(final int value) {
        if (value < 0 || value > size()) {
            throw new IllegalArgumentException("position");
        }
        position = value;
    }

    /**
     * @return Buffer position.
     */
    public final int position() {
        return this.position;
    }

    /**
     * @return Buffer size.
     */
    public final int size() {
        return size;
    }

    /**
     * @param value
     *            Buffer size.
     */
    public final void size(final int value) {
        if (value < 0 || value > capacity()) {
            throw new IllegalArgumentException("size");
        }
        size = value;
        if (position > size) {
            position = size;
        }
    }

    /**
     * @return Amount of non read bytes in the buffer.
     */
    public int available() {
        return size - position;
    }

    /**
     * @return Get buffer data as byte array.
     */
    public final byte[] array() {
        return subArray(0, size);
    }

    /**
     * Returns sub array from byte buffer.
     * 
     * @param index
     *            Start index.
     * @param count
     *            Byte count.
     * @return Sub array.
     */
    public final byte[] subArray(final int index, final int count) {
        byte[] tmp = new byte[count];
        if (count != 0) {
            System.arraycopy(data, index, tmp, 0, count);
        }
        return tmp;
    }

    /**
     * Move content from source to destination.
     * 
     * @param srcPos
     *            Source position.
     * @param destPos
     *            Destination position.
     * @param count
     *            Item count.
     */
    public final void move(final int srcPos, final int destPos, final int count) {
        if (count < 0) {
            throw new IllegalArgumentException("count");
        }
        if (count != 0) {
            System.arraycopy(data, srcPos, data, destPos, count);
            size((destPos + count));
            if (position > size) {
                position(size);
            }
        }
    }

    /**
     * Remove handled bytes. This can be used in debugging to remove handled
     * bytes.
     */
    public final void trim() {
        if (size == position) {
            size(0);
        } else {
            move(position, 0, size - position);
        }
        position(0);
    }

    /**
     * Push the given byte into this buffer at the current position, and then
     * increments the position.
     * 
     * @param item
     *            The byte to be added.
     */
    public final void setUInt8(final int item) {

        setUInt8(size, item);
        ++size;
    }

    /**
     * Push the given data type into this buffer at the current position, and
     * then increments the position.
     * 
     * @param item
     *            The UInt8 value to be added.
     */
    public final void setUInt8(final DataType item) {
        setUInt8(item.getValue());
    }

    /**
     * Set the UInt8 value into this buffer for given position, and then
     * increments the position.
     * 
     * @param index
     *            Buffer index where value is set.
     * @param item
     *            The UInt8 value to be added.
     */
    public final void setUInt8(final int index, final int item) {

        if (index >= capacity()) {
            capacity(index + ARRAY_CAPACITY);
        }
        data[index] = (byte) item;
    }

    /**
     * Push the UInt16 value into this buffer at the current position, and then
     * increments the position.
     * 
     * @param item
     *            The UInt16 value to be added.
     */
    public final void setUInt16(final int item) {
        setUInt16(size, item);
        size += 2;
    }

    /**
     * Set the UInt16 value into this buffer for the given position, and then
     * increments the position.
     * 
     * @param index
     *            Buffer index where value is set.
     * @param item
     *            The UInt16 value to be added.
     */
    public final void setUInt16(final int index, final int item) {

        if (index + 2 >= capacity()) {
            capacity(index + ARRAY_CAPACITY);
        }
        data[index] = (byte) ((item >> 8) & 0xFF);
        data[index + 1] = (byte) (item & 0xFF);
    }

    /**
     * Push the UInt32 value into this buffer at the current position, and then
     * increments the position.
     * 
     * @param item
     *            The UInt32 value to be added.
     */
    public final void setUInt32(final long item) {

        setUInt32(size, item);
        size += 4;
    }

    /**
     * Set the UInt32 value into this buffer for the given position, and then
     * increments the position.
     * 
     * @param index
     *            Buffer index where value is set.
     * @param item
     *            The UInt32 value to be added.
     */
    public final void setUInt32(final int index, final long item) {

        if (index + 4 >= capacity()) {
            capacity(index + ARRAY_CAPACITY);
        }
        data[index] = (byte) ((item >> 24) & 0xFF);
        data[index + 1] = (byte) ((item >> 16) & 0xFF);
        data[index + 2] = (byte) ((item >> 8) & 0xFF);
        data[index + 3] = (byte) (item & 0xFF);
    }

    /**
     * Push the UInt64 value into this buffer at the current position, and then
     * increments the position.
     * 
     * @param item
     *            The UInt64 value to be added.
     */
    public final void setUInt64(final long item) {

        setUInt64(size, item);
        size += 8;
    }

    /**
     * Set the UInt64 value into this buffer for the given position, and then
     * increments the position.
     * 
     * @param index
     *            Buffer index where value is set.
     * @param item
     *            The UInt64 value to be added.
     */
    public final void setUInt64(final int index, final long item) {

        if (index + 8 >= capacity()) {
            capacity(index + ARRAY_CAPACITY);
        }
        data[size] = (byte) ((item >> 56) & 0xFF);
        data[size + 1] = (byte) ((item >> 48) & 0xFF);
        data[size + 2] = (byte) ((item >> 40) & 0xFF);
        data[size + 3] = (byte) ((item >> 32) & 0xFF);
        data[size + 4] = (byte) ((item >> 24) & 0xFF);
        data[size + 5] = (byte) ((item >> 16) & 0xFF);
        data[size + 6] = (byte) ((item >> 8) & 0xFF);
        data[size + 7] = (byte) (item & 0xFF);
    }

    /**
     * Push the float value into this buffer at the current position, and then
     * increments the position.
     * 
     * @param value
     *            Float value to be added.
     */
    public final void setFloat(final float value) {
        setFloat(size, value);
        size += 4;
    }

    /**
     * Set the float value into this buffer for the given position, and then
     * increments the position.
     * 
     * @param index
     *            Buffer index where value is set.
     * @param value
     *            The float value to be added.
     */

    public final void setFloat(final int index, final float value) {
        int tmp = Float.floatToIntBits(value);
        setUInt32(index, tmp);
    }

    /**
     * Push the double value into this buffer at the current position, and then
     * increments the position.
     * 
     * @param value
     *            Double value to be added.
     */
    public final void setDouble(final double value) {
        setDouble(size, value);
        size += 8;
    }

    /**
     * Set the double value into this buffer for the given position, and then
     * increments the position.
     * 
     * @param index
     *            Buffer index where value is set.
     * @param value
     *            The double value to be added.
     */
    public final void setDouble(final int index, final double value) {
        long tmp = Double.doubleToLongBits(value);
        setUInt64(index, tmp);
    }

    /**
     * @return UInt8 value from byte buffer.
     */
    public final short getUInt8() {
        short value = getUInt8(position());
        ++position;
        return value;
    }

    /**
     * @return Int8 value from byte buffer.
     */
    public final byte getInt8() {
        return (byte) getUInt8();
    }

    /**
     * Get UInt8 value from byte buffer from the given index..
     * 
     * @param index
     *            Buffer index.
     * @return UInt8 value.
     */
    public final short getUInt8(final int index) {
        if (index >= size) {
            throw new IllegalArgumentException("getUInt8");
        }
        return (short) (data[index] & 0xFF);
    }

    /**
     * @return UInt16 value from byte buffer.
     */
    public final int getUInt16() {
        int value = getUInt16(position());
        position += 2;
        return value;
    }

    /**
     * @return Int16 value from byte buffer.
     */
    public final short getInt16() {
        return (short) getUInt16();
    }

    /**
     * Get UInt16 value from byte buffer from the given index..
     * 
     * @param index
     *            Buffer index.
     * @return UInt16 value.
     */
    public final int getUInt16(final int index) {
        if (index + 2 > size) {
            throw new IllegalArgumentException("getUInt16");
        }
        return ((data[index] & 0xFF) << 8) | (data[index + 1] & 0xFF);
    }

    /**
     * Get Int32 value from the current position.
     * 
     * @return Int32 value.
     */
    public final long getUInt32() {
        long value = getUInt32(position());
        position += 4;
        return value;
    }

    /**
     * Get UInt24 value from the current position.
     * 
     * @return Int32 value.
     */
    public final int getUInt24() {
        int value = getUInt24(position());
        position += 3;
        return value;
    }

    /**
     * Get Int32 value from the current position.
     * 
     * @return Int32 value.
     */
    public final int getInt32() {
        int value = getInt32(position());
        position += 4;
        return value;
    }

    /**
     * Get UInt32 value from byte buffer from the given index..
     * 
     * @param index
     *            Buffer index.
     * @return UInt32 value.
     */

    public final int getInt32(final int index) {
        if (index + 4 > size) {
            throw new IllegalArgumentException("getUInt32");
        }
        return (data[index] & 0xFF) << 24 | (data[index + 1] & 0xFF) << 16
                | (data[index + 2] & 0xFF) << 8 | (data[index + 3] & 0xFF);
    }

    /**
     * Get UInt24 value from byte buffer from the given index..
     * 
     * @param index
     *            Buffer index.
     * @return UInt24 value.
     */
    public final int getUInt24(final int index) {
        if (index + 3 > size) {
            throw new IllegalArgumentException("getUInt24");
        }
        return (data[index] & 0xFF) << 16 | (data[index + 1] & 0xFF) << 8
                | (data[index + 2] & 0xFF);
    }

    /**
     * Get UInt32 value from byte buffer from the given index..
     * 
     * @param index
     *            Buffer index.
     * @return UInt32 value.
     */
    public final long getUInt32(final int index) {
        if (index + 4 > size) {
            throw new IllegalArgumentException("getUInt32");
        }
        long value = data[index] & 0xFF;
        value = value << 24;
        value |= (data[index + 1] & 0xFF) << 16;
        value |= (data[index + 2] & 0xFF) << 8;
        value |= (data[index + 3] & 0xFF);
        return value;
    }

    /**
     * @return Get float value from byte buffer.
     */
    public final float getFloat() {
        return Float.intBitsToFloat(getInt32());
    }

    /**
     * @return Get double value from byte buffer.
     */
    public final double getDouble() {
        return Double.longBitsToDouble(getInt64());
    }

    /**
     * @return Get Int64 value from byte buffer.
     */
    public final long getInt64() {
        long value = getInt64(position);
        position += 8;
        return value;
    }

    /**
     * Get Int64 value from byte buffer using the index.
     * 
     * @param index
     *            Byte index.
     * @return Int64 value.
     */
    public final long getInt64(final int index) {
        long value = (long) (data[index] & 0xFF) << 56;
        value |= (long) (data[index + 1] & 0xFF) << 48;
        value |= (long) (data[index + 2] & 0xFF) << 40;
        value |= (long) (data[index + 3] & 0xFF) << 32;
        value |= (long) (data[index + 4] & 0xFF) << 24;
        value |= (data[index + 5] & 0xFF) << 16;
        value |= (data[index + 6] & 0xFF) << 8;
        value |= (data[index + 7] & 0xFF);
        return value;
    }

    /**
     * @return the data
     */
    public final byte[] getData() {
        return data;
    }

    /**
     * @param value
     *            The data to set
     */
    public final void setData(final byte[] value) {
        data = value;
    }

    /**
     * @return Get UInt64 value from byte buffer.
     */
    public final BigInteger getUInt64() {
        BigInteger value = getUInt64(position);
        position += 8;
        return value;
    }

    /**
     * Get UInt64 value from byte buffer using index.
     * 
     * @param index
     *            Byte index.
     * @return UInt64 value.
     */
    public final BigInteger getUInt64(final int index) {
        long value = getInt64(index);
        BigInteger b = BigInteger.valueOf(value);
        if (b.compareTo(BigInteger.ZERO) < 0) {
            b = b.add(BigInteger.ONE.shiftLeft(64));
        }
        return b;
    }

    /**
     * Check is string ASCII string.
     * 
     * @param value
     *            String value.
     * @return Is ASCII string.
     */
    public static boolean isAsciiString(final String value) {
        if (value != null) {
            return StandardCharsets.US_ASCII.newEncoder().canEncode(value);
        }
        return true;
    }

    /**
     * Check is byte buffer ASCII string.
     * 
     * @param value
     *            Byte array.
     * @return Is ASCII string.
     */
    public static boolean isAsciiString(byte[] value) {
        if (value != null) {
            for (byte it : value) {
                if ((it < 32 || it > 127) && it != '\r' && it != '\n' && it != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Get string value from byte buffer.
     * 
     * @param count
     *            Amount of the chars to get.
     * @return String value.
     */
    public final String getString(final int count) {
        String str = getString(position, count, "ASCII");
        position += count;
        return str;
    }

    /**
     * Get string value from byte buffer.
     * 
     * @param index
     *            Byte index.
     * @param count
     *            Amount of the chars to get.
     * @return String value.
     */
    public final String getString(final int index, final int count) {
        return getString(index, count, "ASCII");
    }

    /**
     * Get string value from byte buffer.
     * 
     * @param index
     *            Byte index.
     * @param count
     *            Amount of the chars to get.
     * @param charsetName
     *            charset name.
     * @return String value.
     */
    public final String getString(final int index, final int count, final String charsetName) {
        if (index + count > size) {
            throw new IllegalArgumentException("getString");
        }
        try {
            byte[] tmp = subArray(index, count);
            if (charsetName.equalsIgnoreCase("ASCII")) {
                if (isAsciiString(tmp)) {
                    return new String(getData(), index, count, charsetName).replaceAll("[\\x00]",
                            "");
                } else {
                    return GXCommon.toHex(tmp, true);
                }
            } else {
                return new String(getData(), index, count, charsetName).replaceAll("[\\x00]", "");
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * Append the given byte array into this buffer.
     * 
     * @param value
     *            Data to append.
     */
    public final void set(final byte[] value) {
        if (value != null) {
            set(value, 0, value.length);
        }
    }

    /**
     * Push the given byte array into this buffer at the current position, and
     * then increments the position.
     * 
     * @param index
     *            Byte index.
     * @param value
     *            The value to be added.
     */
    public final void set(final int index, final byte[] value) {
        if (value != null) {
            move(index, value.length, size - index);
            set(value, index, value.length);
        }
    }

    /**
     * Set new value to byte array.
     * 
     * @param value
     *            Byte array to add.
     * @param index
     *            Byte index.
     * @param count
     *            Byte count.
     */
    public final void set(final byte[] value, final int index, final int count) {
        if (value != null && count != 0) {
            if (size + count > capacity()) {
                capacity(size + count + ARRAY_CAPACITY);
            }
            System.arraycopy(value, index, data, size, count);
            size += count;
        }
    }

    /**
     * @param value
     *            Set new value to byte array.
     */
    public final void set(final GXByteBuffer value) {
        if (value != null) {
            set(value, value.size() - value.position());
        }
    }

    /**
     * Set new value to byte array.
     * 
     * @param value
     *            Byte array to add.
     * @param count
     *            Byte count.
     */
    public final void set(final GXByteBuffer value, final int count) {
        if (size + count > capacity()) {
            capacity(size + count + ARRAY_CAPACITY);
        }
        if (count != 0) {
            System.arraycopy(value.data, value.position, data, size, count);
            size += count;
            value.position += count;
        }
    }

    /**
     * Add new object to the byte buffer.
     * 
     * @param value
     *            Value to add.
     */
    public final void add(final Object value) {
        if (value != null) {
            if (value instanceof byte[]) {
                set((byte[]) value);
            } else if (value instanceof Byte) {
                setUInt8(((Byte) value).intValue());
            } else if (value instanceof Short) {
                setUInt16(((Short) value).shortValue());
            } else if (value instanceof Integer) {
                setUInt32(((Integer) value).intValue());
            } else if (value instanceof Long) {
                setUInt64(((Long) value).longValue());
            } else if (value instanceof String) {
                try {
                    set(((String) value).getBytes("ASCII"));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e.getMessage());
                }
            } else if (value instanceof BigInteger) {
                set(((BigInteger) value).toByteArray());
            } else if (value instanceof Boolean) {
                if ((Boolean) value) {
                    setUInt8(1);
                } else {
                    setUInt8(0);
                }
            } else if (value instanceof GXUInt8) {
                setUInt16(((GXUInt8) value).shortValue());
            } else if (value instanceof GXUInt16) {
                setUInt16(((GXUInt16) value).shortValue());
            } else if (value instanceof GXUInt32) {
                setUInt32(((GXUInt32) value).longValue());
            } else {
                throw new RuntimeException("Invalid object type.");
            }
        }
    }

    /**
     * @param target
     *            get bytes from byte buffer.
     */
    public final void get(final byte[] target) {
        if (size - position < target.length) {
            throw new IllegalArgumentException("get");
        }
        System.arraycopy(getData(), position, target, 0, target.length);
        position += target.length;
    }

    /**
     * Compares, whether two given arrays are similar starting from current
     * position.
     * 
     * @param arr
     *            Array to compare.
     * @return True, if arrays are similar. False, if the arrays differ.
     */
    public final boolean compare(final byte[] arr) {
        if (size - position < arr.length) {
            return false;
        }
        byte[] bytes = new byte[arr.length];
        get(bytes);
        boolean ret = java.util.Arrays.equals(bytes, arr);
        if (!ret) {
            this.position -= arr.length;
        }
        return ret;
    }

    /**
     * Reverses the order of the given array.
     */
    public final void reverse() {
        if (size() == 0) {
            return;
        }
        int first = position;
        int last = size - 1;
        byte tmp;
        while (last > first) {
            tmp = data[last];
            data[last] = data[first];
            data[first] = tmp;
            --last;
            ++first;
        }
    }

    /**
     * Push the given hex string as byte array into this buffer at the current
     * position, and then increments the position.
     * 
     * @param value
     *            The hex string to be added.
     */
    public final void setHexString(final String value) {
        set(GXCommon.hexToBytes(value));
    }

    /**
     * Push the given hex string as byte array into this buffer at the current
     * position, and then increments the position.
     * 
     * @param index
     *            Byte index.
     * @param value
     *            The hex string to be added.
     */
    public final void setHexString(final int index, final String value) {
        set(index, GXCommon.hexToBytes(value));
    }

    /**
     * Push the given hex string as byte array into this buffer at the current
     * position, and then increments the position.
     * 
     * @param value
     *            Byte array to add.
     * @param index
     *            Byte index.
     * @param count
     *            Byte count.
     */
    public final void setHexString(final String value, final int index, final int count) {
        set(GXCommon.hexToBytes(value), index, count);
    }

    @Override
    public final String toString() {
        return GXCommon.toHex(data, true, 0, size);
    }

    /**
     * Get remaining data.
     * 
     * @return Remaining data as byte array.
     */
    public byte[] remaining() {
        return subArray(position, size - position);
    }

    /**
     * Get remaining data as a hex string.
     * 
     * @param addSpace
     *            Add space between bytes.
     * @return Remaining data as a hex string.
     */
    public String remainingHexString(final boolean addSpace) {
        return GXCommon.toHex(data, addSpace, position, size - position);
    }

    /**
     * Get data as hex string.
     * 
     * @param addSpace
     *            Add space between bytes.
     * @param index
     *            Byte index.
     * @return Data as hex string.
     */
    public final String toHex(final boolean addSpace, final int index) {
        return GXCommon.toHex(data, addSpace, index, size - index);
    }

    /**
     * Get data as hex string.
     * 
     * @param addSpace
     *            Add space between bytes.
     * @param index
     *            Byte index.
     * @param count
     *            Byte count.
     * @return Data as hex string.
     */
    public final String toHex(final boolean addSpace, final int index, final int count) {
        return GXCommon.toHex(data, addSpace, index, count);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy