HashCodeUtilTest.java

package zserio.runtime;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.math.BigInteger;

import org.junit.jupiter.api.Test;

import zserio.runtime.array.Array;
import zserio.runtime.array.ArrayTraits;
import zserio.runtime.array.ArrayType;
import zserio.runtime.array.RawArray;
import zserio.runtime.io.BitBuffer;

public class HashCodeUtilTest
{
    @Test
    public void simpleType()
    {
        final int hashSeed = 1;

        final boolean boolValue = true;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 1, HashCodeUtil.calcHashCode(hashSeed, boolValue));

        final Boolean boolValueBoxed = Boolean.valueOf(false);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, boolValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Boolean)null));

        final byte byteValue = 10;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, byteValue));

        final Byte byteValueBoxed = Byte.valueOf((byte)10);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, byteValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Byte)null));

        final short shortValue = 10;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, shortValue));

        final Short shortValueBoxed = Short.valueOf((short)10);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, shortValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Short)null));

        final int intValue = 10;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, intValue));
        final int intValue2 = -1;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER - 1, HashCodeUtil.calcHashCode(hashSeed, intValue2));

        final Integer intValueBoxed = Integer.valueOf(10);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, intValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Integer)null));

        final long longValue = -1;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, longValue));

        final Long longValueBoxed = Long.valueOf(10);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, longValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Long)null));

        final float floatValue = 10.0f;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + FloatUtil.convertFloatToInt(floatValue),
                HashCodeUtil.calcHashCode(hashSeed, floatValue));

        final Float floatValueBoxed = Float.valueOf(10.0f);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + FloatUtil.convertFloatToInt(floatValue),
                HashCodeUtil.calcHashCode(hashSeed, floatValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Float)null));

        final double doubleValue = 10.0;
        final long doubleValueAsLong = FloatUtil.convertDoubleToLong(doubleValue);
        final int expectedDoubleHashCode =
                HashCodeUtil.HASH_PRIME_NUMBER + (int)(doubleValueAsLong ^ (doubleValueAsLong >>> 32));
        assertEquals(expectedDoubleHashCode, HashCodeUtil.calcHashCode(hashSeed, doubleValue));

        final Double doubleValueBoxed = Double.valueOf(10.0);
        assertEquals(expectedDoubleHashCode, HashCodeUtil.calcHashCode(hashSeed, doubleValueBoxed));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Double)null));
    }

    @Test
    public void stringType()
    {
        final int hashSeed = 1;
        final String stringValue = String.valueOf('0');
        assertEquals(
                HashCodeUtil.HASH_PRIME_NUMBER + (int)'0', HashCodeUtil.calcHashCode(hashSeed, stringValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (String)null));
    }

    @Test
    public void bitBufferType()
    {
        final int hashSeed = 1;
        final BitBuffer bitBufferValue = new BitBuffer(new byte[] {});
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + HashCodeUtil.HASH_SEED,
                HashCodeUtil.calcHashCode(hashSeed, bitBufferValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (BitBuffer)null));
    }

    @Test
    public void enumType()
    {
        final int hashSeed = 1;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED + Color.NONE.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Color.NONE));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED + Color.RED.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Color.RED));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED + Color.BLUE.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Color.BLUE));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED + Color.BLACK.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Color.BLACK));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Color)null));
    }

    @Test
    public void bitmaskType()
    {
        final int hashSeed = 1;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED +
                                Permissions.Values.READ.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Permissions.Values.READ));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED +
                                Permissions.Values.WRITE.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Permissions.Values.WRITE));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED +
                                Permissions.Values.CREATE.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, Permissions.Values.CREATE));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Permissions)null));
    }

    @Test
    public void objectType()
    {
        final int hashSeed = 1;
        final DummyObject objectValue = new DummyObject(10);
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + 10, HashCodeUtil.calcHashCode(hashSeed, objectValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (DummyObject)null));
    }

    @Test
    public void arrayType()
    {
        final int hashSeed = 1;
        final Array arrayValue = new Array(new RawArray.IntRawArray(new int[] {3, 7}),
                new ArrayTraits.SignedBitFieldIntArrayTraits(32), ArrayType.NORMAL);
        final int rawArrayHashCode =
                (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED + 3) * HashCodeUtil.HASH_PRIME_NUMBER +
                7;
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + rawArrayHashCode,
                HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Array)null));
    }

    @Test
    public void simpleArrayType()
    {
        final int hashSeed = 1;

        final int expectedBooleanHashCode =
                (HashCodeUtil.HASH_PRIME_NUMBER + 0) * HashCodeUtil.HASH_PRIME_NUMBER + 1;
        final boolean[] boolArrayValue = new boolean[] {false, true};
        assertEquals(expectedBooleanHashCode, HashCodeUtil.calcHashCode(hashSeed, boolArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (boolean[])null));

        final int expectedIntegralHashCode =
                (HashCodeUtil.HASH_PRIME_NUMBER + 3) * HashCodeUtil.HASH_PRIME_NUMBER + 7;

        final byte[] byteArrayValue = new byte[] {3, 7};
        assertEquals(expectedIntegralHashCode, HashCodeUtil.calcHashCode(hashSeed, byteArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (byte[])null));

        final short[] shortArrayValue = new short[] {3, 7};
        assertEquals(expectedIntegralHashCode, HashCodeUtil.calcHashCode(hashSeed, shortArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (short[])null));

        final int[] intArrayValue = new int[] {3, 7};
        assertEquals(expectedIntegralHashCode, HashCodeUtil.calcHashCode(hashSeed, intArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (int[])null));

        final long[] longArrayValue = new long[] {3, 7};
        assertEquals(expectedIntegralHashCode, HashCodeUtil.calcHashCode(hashSeed, longArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (long[])null));

        final float[] floatArrayValue = new float[] {10.0f};
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + FloatUtil.convertFloatToInt(floatArrayValue[0]),
                HashCodeUtil.calcHashCode(hashSeed, floatArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (float[])null));

        final double[] doubleArrayValue = new double[] {10.0};
        final long doubleValueAsLong = FloatUtil.convertDoubleToLong(doubleArrayValue[0]);
        final int expectedDoubleArrayHashCode =
                HashCodeUtil.HASH_PRIME_NUMBER + (int)(doubleValueAsLong ^ (doubleValueAsLong >>> 32));
        assertEquals(expectedDoubleArrayHashCode, HashCodeUtil.calcHashCode(hashSeed, doubleArrayValue));
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (double[])null));
    }

    @Test
    public void bigIntegerArrayType()
    {
        final int hashSeed = 1;
        final BigInteger[] arrayValue = new BigInteger[] {BigInteger.valueOf(3), BigInteger.valueOf(7)};
        final int expectedHashCode = (HashCodeUtil.HASH_PRIME_NUMBER + 3) * HashCodeUtil.HASH_PRIME_NUMBER + 7;
        assertEquals(expectedHashCode, HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (BigInteger[])null));
    }

    @Test
    public void stringArrayType()
    {
        final int hashSeed = 1;
        final String[] arrayValue = new String[] {String.valueOf('0')};
        assertEquals(
                HashCodeUtil.HASH_PRIME_NUMBER + (int)'0', HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (String[])null));
    }

    @Test
    public void bitBufferArrayType()
    {
        final int hashSeed = 1;
        final BitBuffer[] arrayValue = new BitBuffer[] {new BitBuffer(new byte[] {})};
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER + HashCodeUtil.HASH_SEED,
                HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (BitBuffer[])null));
    }

    @Test
    public void enumArrayType()
    {
        final int hashSeed = 1;
        final Color[] arrayValue = new Color[] {Color.NONE};
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED + Color.NONE.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Color[])null));
    }

    @Test
    public void bitmaskArrayType()
    {
        final int hashSeed = 1;
        final Permissions[] arrayValue = new Permissions[] {Permissions.Values.READ};
        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER +
                        (HashCodeUtil.HASH_PRIME_NUMBER * HashCodeUtil.HASH_SEED +
                                Permissions.Values.READ.getValue()),
                HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (Permissions[])null));
    }

    @Test
    public void objectArrayType()
    {
        final int hashSeed = 1;
        final DummyObject[] arrayValue = new DummyObject[] {new DummyObject(3), new DummyObject(7)};
        assertEquals((HashCodeUtil.HASH_PRIME_NUMBER + 3) * HashCodeUtil.HASH_PRIME_NUMBER + 7,
                HashCodeUtil.calcHashCode(hashSeed, arrayValue));

        assertEquals(HashCodeUtil.HASH_PRIME_NUMBER, HashCodeUtil.calcHashCode(hashSeed, (DummyObject[])null));
    }

    // enum
    private static enum Color implements ZserioEnum, SizeOf {
        NONE(0),
        RED(2),
        BLUE(3),
        BLACK(7);

        private Color(int value)
        {
            this.value = value;
        }

        public int getValue()
        {
            return value;
        }

        @Override
        public Number getGenericValue()
        {
            return value;
        }

        @Override
        public int bitSizeOf()
        {
            return 0;
        }

        @Override
        public int bitSizeOf(long position)
        {
            return 0;
        }

        private int value;
    }

    // bitmask
    private static class Permissions implements ZserioBitmask, SizeOf
    {
        public Permissions(int value)
        {
            this.value = value;
        }

        @Override
        public boolean equals(Object other)
        {
            if (!(other instanceof Permissions))
                return false;

            return value == ((Permissions)other).value;
        }

        @Override
        public int hashCode()
        {
            int result = zserio.runtime.HashCodeUtil.HASH_SEED;
            result = HashCodeUtil.calcHashCode(result, value);
            return result;
        }

        public int getValue()
        {
            return value;
        }

        @Override
        public java.lang.Number getGenericValue()
        {
            return value;
        }

        @Override
        public int bitSizeOf()
        {
            return 0;
        }

        @Override
        public int bitSizeOf(long position)
        {
            return 0;
        }

        public static final class Values
        {
            public static final Permissions READ = new Permissions(1);
            public static final Permissions WRITE = new Permissions(2);
            public static final Permissions CREATE = new Permissions(4);
        }

        private int value;
    }

    private static class DummyObject implements SizeOf
    {
        public DummyObject(int hashCode)
        {
            this.hashCode = hashCode;
        }

        @Override
        public boolean equals(Object other)
        {
            if (!(other instanceof DummyObject))
                return false;

            return hashCode == ((DummyObject)other).hashCode;
        }

        @Override
        public int hashCode()
        {
            return hashCode;
        }

        @Override
        public int bitSizeOf()
        {
            return 0;
        }

        @Override
        public int bitSizeOf(long position)
        {
            return 0;
        }

        private final int hashCode;
    }
}