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

com.github.czyzby.websocket.serialization.impl.Size Maven / Gradle / Ivy

The newest version!
package com.github.czyzby.websocket.serialization.impl;

import com.github.czyzby.websocket.serialization.SerializationException;

/** Contains number sizes handled by the {@link Serializer} and {@link Deserializer}. Allows to convert different number
 * sizes for the needs of serialization.
 *
 * @author MJ */
public enum Size {
    /** Uses 1 byte to serialize numbers. Truncates shorts, ints and longs. Cannot serialize floats or doubles. As array
     * length size, can serialize array lengths up to {@link java.lang.Byte#MAX_VALUE}. */
    BYTE(1, Byte.MAX_VALUE) {
        @Override
        void serializeShort(final short value, final Serializer serializer) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
        }

        @Override
        void serializeInt(final int value, final Serializer serializer) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
        }

        @Override
        void serializeLong(final long value, final Serializer serializer) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
        }

        @Override
        void serializeFloat(final float value, final Serializer serializer) throws SerializationException {
            throw new SerializationException("Byte is too small to store float data. Cannot truncate.");
        }

        @Override
        void serializeDouble(final double value, final Serializer serializer) throws SerializationException {
            throw new SerializationException("Byte is too small to store double data. Cannot truncate");
        }

        @Override
        short deserializeShort(final Deserializer deserializer) {
            return deserializeByte(deserializer);
        }

        @Override
        int deserializeInt(final Deserializer deserializer) {
            return deserializeByte(deserializer);
        }

        @Override
        long deserializeLong(final Deserializer deserializer) {
            return deserializeByte(deserializer);
        }

        @Override
        float deserializeFloat(final Deserializer deserializer) throws SerializationException {
            throw new SerializationException("Byte is too small to store float data. Cannot deserialize.");
        }

        @Override
        double deserializeDouble(final Deserializer deserializer) throws SerializationException {
            throw new SerializationException("Byte is too small to store double data. Cannot deserialize.");
        }
    },
    /** Uses 2 bytes to serialize numbers. Uses 1 byte to serialize bytes and booleans. Truncates ints and longs. Cannot
     * serialize floats or doubles. As array length size, can serialize array lengths up to
     * {@link java.lang.Short#MAX_VALUE}. */
    SHORT(2, Short.MAX_VALUE) {
        @Override
        void serializeInt(final int value, final Serializer serializer) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 8);
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
        }

        @Override
        void serializeLong(final long value, final Serializer serializer) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 8);
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
        }

        @Override
        void serializeFloat(final float value, final Serializer serializer) throws SerializationException {
            throw new SerializationException("Short is too small to store float data. Cannot truncate.");
        }

        @Override
        void serializeDouble(final double value, final Serializer serializer) throws SerializationException {
            throw new SerializationException("Short is too small to store double data. Cannot truncate");
        }

        @Override
        int deserializeInt(final Deserializer deserializer) {
            return deserializeShort(deserializer);
        }

        @Override
        long deserializeLong(final Deserializer deserializer) {
            return deserializeShort(deserializer);
        }

        @Override
        float deserializeFloat(final Deserializer deserializer) throws SerializationException {
            throw new SerializationException("Short is too small to store float data. Cannot deserialize.");
        }

        @Override
        double deserializeDouble(final Deserializer deserializer) throws SerializationException {
            throw new SerializationException("Short is too small to store double data. Cannot deserialize.");
        }
    },
    /** Uses 4 bytes to serialize numbers. Uses 2 bytes to serialize shorts and 1 byte to serialize bytes and booleans.
     * Truncates longs and doubles. As array length size, can serialize array lengths up to
     * {@link java.lang.Integer#MAX_VALUE}. */
    INT(4, Integer.MAX_VALUE) {
        @Override
        void serializeLong(final long value, final Serializer serializer) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 24);
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 16);
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 8);
            serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
        }

        @Override
        void serializeFloat(final float value, final Serializer serializer) {
            serializeInt(Float.floatToIntBits(value), serializer);
        }

        @Override
        void serializeDouble(final double value, final Serializer serializer) {
            serializeFloat((float) value, serializer);
        }

        @Override
        long deserializeLong(final Deserializer deserializer) {
            return deserializeInt(deserializer);
        }

        @Override
        float deserializeFloat(final Deserializer deserializer) throws SerializationException {
            return Float.intBitsToFloat(deserializeInt(deserializer));
        }

        @Override
        double deserializeDouble(final Deserializer deserializer) throws SerializationException {
            return deserializeFloat(deserializer);
        }
    },
    /** Uses 8 bytes to serialize numbers. Uses 4 bytes to serialize ints, 2 bytes to serialize shorts and 1 byte to
     * serialize bytes and booleans. Expands floats to doubles, potentially allowing to store more exact data than
     * floats serialized as ints. As array length size, can serialize array lengths up to
     * {@link java.lang.Integer#MAX_VALUE} - since Java uses ints as array length indexes, there's no point in
     * serializing array lengths as longs. */
    LONG(8, Integer.MAX_VALUE) {
        @Override
        void serializeDouble(final double value, final Serializer serializer) {
            serializeLong(Double.doubleToLongBits(value), serializer);
        }

        @Override
        void serializeFloat(final float value, final Serializer serializer) {
            serializeDouble(value, serializer);
        }

        @Override
        float deserializeFloat(final Deserializer deserializer) {
            return (float) deserializeDouble(deserializer);
        }

        @Override
        double deserializeDouble(final Deserializer deserializer) {
            return Double.longBitsToDouble(deserializeLong(deserializer));
        }
    };

    static final int NULL_ARRAY_ID = -1;

    private final int bytesAmount;
    private final int maxArrayLength;

    private Size(final int bytesAmount, final int maxArrayLength) {
        this.bytesAmount = bytesAmount;
        this.maxArrayLength = maxArrayLength;
    }

    /** @return smallest number size big enough to store all possible array lengths in Java: {@link #INT}. */
    public static Size getDefaultArrayLengthSize() {
        return Size.INT;
    }

    /** @return bytes amount needed to serialize the number. */
    public int getBytesAmount() {
        return bytesAmount;
    }

    /** @return when using this number size for serializing array lengths, this is the maximum value of array length
     *         that can be stored with this size's bytes amount. */
    public int getMaxArrayLength() {
        return maxArrayLength;
    }

    // Number serialization methods:

    void serializeBoolean(final boolean value, final Serializer serializer) {
        if (value) {
            serializer.serializedData[serializer.currentByteArrayIndex++] = 1;
        } else {
            serializer.serializedData[serializer.currentByteArrayIndex++] = 0;
        }
    }

    void serializeByte(final byte value, final Serializer serializer) {
        serializer.serializedData[serializer.currentByteArrayIndex++] = value;
    }

    void serializeShort(final short value, final Serializer serializer) {
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 8);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
    }

    void serializeInt(final int value, final Serializer serializer) {
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 24);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 16);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 8);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
    }

    void serializeLong(final long value, final Serializer serializer) {
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 56);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 48);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 40);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 32);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 24);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 16);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) (value >>> 8);
        serializer.serializedData[serializer.currentByteArrayIndex++] = (byte) value;
    }

    abstract void serializeFloat(final float value, final Serializer serializer) throws SerializationException;

    abstract void serializeDouble(final double value, final Serializer serializer) throws SerializationException;

    // Array serialization methods:

    /** @param arrayLength array length to serialize with the selected size.
     * @throws SerializationException if array length is too big. */
    protected void validateArrayLengthToSerialize(final int arrayLength) throws SerializationException {
        if (maxArrayLength < arrayLength) {
            throw new SerializationException(name() + " number size is to small to store array length of: "
                    + arrayLength + ". Data would be lost upon serialization.");
        }
    }

    private void prepareArray(final Size arrayLengthSize, final Serializer serializer, final int arrayLength)
            throws SerializationException {
        // Checking if length is smaller than the max value that can be serialized with this number size:
        arrayLengthSize.validateArrayLengthToSerialize(arrayLength);
        // Ensuring capacity for serializing array length and each element:
        // (This might oversize the serialized data array, but only if the user requested to serialize the
        // array with number size bigger than the actual numbers - so that's basically programmer's mistake.)
        serializer.ensureCapacity(arrayLengthSize.bytesAmount + arrayLength * bytesAmount);
        // Serializing array length:
        serializer.serializeInt(arrayLength, arrayLengthSize);
    }

    void serializeBooleanArray(final boolean[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        prepareArray(arrayLengthSize, serializer, array.length);
        for (final boolean value : array) {
            serializeBoolean(value, serializer);
        }
    }

    void serializeByteArray(final byte[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        final int arrayLength = array.length;
        prepareArray(arrayLengthSize, serializer, arrayLength);

        if (arrayLength > 0) {
            System.arraycopy(array, 0, serializer.serializedData, serializer.currentByteArrayIndex, arrayLength);
            serializer.currentByteArrayIndex += arrayLength;
        }
    }

    void serializeShortArray(final short[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        prepareArray(arrayLengthSize, serializer, array.length);
        for (final short value : array) {
            serializeShort(value, serializer);
        }
    }

    void serializeIntArray(final int[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        prepareArray(arrayLengthSize, serializer, array.length);
        for (final int value : array) {
            serializeInt(value, serializer);
        }
    }

    void serializeLongArray(final long[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        prepareArray(arrayLengthSize, serializer, array.length);
        for (final long value : array) {
            serializeLong(value, serializer);
        }
    }

    void serializeFloatArray(final float[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        prepareArray(arrayLengthSize, serializer, array.length);
        for (final float value : array) {
            serializeFloat(value, serializer);
        }
    }

    void serializeDoubleArray(final double[] array, final Size arrayLengthSize, final Serializer serializer)
            throws SerializationException {
        if (array == null) {
            serializer.serializeInt(NULL_ARRAY_ID, arrayLengthSize);
            return;
        }
        prepareArray(arrayLengthSize, serializer, array.length);
        for (final double value : array) {
            serializeDouble(value, serializer);
        }
    }

    // Number deserialization methods:

    boolean deserializeBoolean(final Deserializer deserializer) {
        return deserializer.serializedData[deserializer.currentByteArrayIndex++] > 0;
    }

    byte deserializeByte(final Deserializer deserializer) {
        return deserializer.serializedData[deserializer.currentByteArrayIndex++];
    }

    short deserializeShort(final Deserializer deserializer) {
        return (short) ((deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 8
                | deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF);
    }

    int deserializeInt(final Deserializer deserializer) {
        return deserializer.serializedData[deserializer.currentByteArrayIndex++] << 24
                | (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 16
                | (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 8
                | deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF;
    }

    long deserializeLong(final Deserializer deserializer) {
        return (long) deserializer.serializedData[deserializer.currentByteArrayIndex++] << 56
                | (long) (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 48
                | (long) (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 40
                | (long) (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 32
                | (long) (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 24
                | (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 16
                | (deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF) << 8
                | deserializer.serializedData[deserializer.currentByteArrayIndex++] & 0xFF;
    }

    abstract float deserializeFloat(Deserializer deserializer) throws SerializationException;

    abstract double deserializeDouble(Deserializer deserializer) throws SerializationException;

    // Array deserialization methods:

    private static void validateArrayLengthToCreate(final int arraySize) throws SerializationException {
        if (arraySize < 0) {
            throw new SerializationException("Cannot deserialize array. Negative array length: " + arraySize);
        }
    }

    boolean[] deserializeBooleanArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        } else if (arraySize == 0) {
            return EmptyArrays.BOOLEAN;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize);
        final boolean[] array = new boolean[arraySize];
        for (int index = 0; index < arraySize; index++) {
            array[index] = deserializeBoolean(deserializer);
        }
        return array;
    }

    int deserializeBooleanArray(final Deserializer deserializer, final Size arrayLengthSize, final boolean[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize);
        validateCachedArrayLength(result.length, arraySize);
        for (int index = 0; index < arraySize; index++) {
            result[index] = deserializeBoolean(deserializer);
        }
        return arraySize;
    }

    static void validateCachedArrayLength(final int resultArrayLength, final int arraySize)
            throws SerializationException {
        if (resultArrayLength < arraySize) {
            throw new SerializationException("Passed array has is too small to contain serialized array data. Length: "
                    + resultArrayLength + ", required: " + arraySize);
        }
    }

    byte[] deserializeByteArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        }
        if (arraySize == 0) {
            return EmptyArrays.BYTE;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize);
        final byte[] array = new byte[arraySize];
        System.arraycopy(deserializer.serializedData, deserializer.currentByteArrayIndex, array, 0, arraySize);
        deserializer.currentByteArrayIndex += arraySize;
        return array;
    }

    int deserializeByteArray(final Deserializer deserializer, final Size arrayLengthSize, final byte[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize);
        validateCachedArrayLength(result.length, arraySize);
        System.arraycopy(deserializer.serializedData, deserializer.currentByteArrayIndex, result, 0, arraySize);
        deserializer.currentByteArrayIndex += arraySize;
        return arraySize;
    }

    short[] deserializeShortArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        }
        if (arraySize == 0) {
            return EmptyArrays.SHORT;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize * Math.min(bytesAmount, SHORT.bytesAmount));
        final short[] array = new short[arraySize];
        for (int index = 0; index < arraySize; index++) {
            array[index] = deserializeShort(deserializer);
        }
        return array;
    }

    int deserializeShortArray(final Deserializer deserializer, final Size arrayLengthSize, final short[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize * Math.min(bytesAmount, SHORT.bytesAmount));
        validateCachedArrayLength(result.length, arraySize);
        for (int index = 0; index < arraySize; index++) {
            result[index] = deserializeShort(deserializer);
        }
        return arraySize;
    }

    int[] deserializeIntArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        }
        if (arraySize == 0) {
            return EmptyArrays.INT;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize * Math.min(bytesAmount, INT.bytesAmount));
        final int[] array = new int[arraySize];
        for (int index = 0; index < arraySize; index++) {
            array[index] = deserializeInt(deserializer);
        }
        return array;
    }

    int deserializeIntArray(final Deserializer deserializer, final Size arrayLengthSize, final int[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize * Math.min(bytesAmount, INT.bytesAmount));
        validateCachedArrayLength(result.length, arraySize);
        for (int index = 0; index < arraySize; index++) {
            result[index] = deserializeInt(deserializer);
        }
        return arraySize;
    }

    long[] deserializeLongArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        }
        if (arraySize == 0) {
            return EmptyArrays.LONG;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize * bytesAmount);
        final long[] array = new long[arraySize];
        for (int index = 0; index < arraySize; index++) {
            array[index] = deserializeLong(deserializer);
        }
        return array;
    }

    int deserializeLongArray(final Deserializer deserializer, final Size arrayLengthSize, final long[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize * bytesAmount);
        validateCachedArrayLength(result.length, arraySize);
        for (int index = 0; index < arraySize; index++) {
            result[index] = deserializeLong(deserializer);
        }
        return arraySize;
    }

    float[] deserializeFloatArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        }
        if (arraySize == 0) {
            return EmptyArrays.FLOAT;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize * bytesAmount);
        final float[] array = new float[arraySize];
        for (int index = 0; index < arraySize; index++) {
            array[index] = deserializeFloat(deserializer);
        }
        return array;
    }

    int deserializeFloatArray(final Deserializer deserializer, final Size arrayLengthSize, final float[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize * bytesAmount);
        validateCachedArrayLength(result.length, arraySize);
        for (int index = 0; index < arraySize; index++) {
            result[index] = deserializeFloat(deserializer);
        }
        return arraySize;
    }

    double[] deserializeDoubleArray(final Deserializer deserializer, final Size arrayLengthSize)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize == NULL_ARRAY_ID) {
            return null;
        }
        if (arraySize == 0) {
            return EmptyArrays.DOUBLE;
        }
        validateArrayLengthToCreate(arraySize);
        deserializer.validateBytesAmountToDeserialize(arraySize * bytesAmount);
        final double[] array = new double[arraySize];
        for (int index = 0; index < arraySize; index++) {
            array[index] = deserializeDouble(deserializer);
        }
        return array;
    }

    int deserializeDoubleArray(final Deserializer deserializer, final Size arrayLengthSize, final double[] result)
            throws SerializationException {
        final int arraySize = deserializer.deserializeInt(arrayLengthSize);
        if (arraySize <= 0) {
            return 0;
        }
        deserializer.validateBytesAmountToDeserialize(arraySize * bytesAmount);
        validateCachedArrayLength(result.length, arraySize);
        for (int index = 0; index < arraySize; index++) {
            result[index] = deserializeDouble(deserializer);
        }
        return arraySize;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy