org.danilopianini.lang.HashUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.danilopianini.lang.HashUtils.java

Source

/*******************************************************************************
 * Copyright (C) 2009, 2015, Danilo Pianini and contributors
 * listed in the project's build.gradle or pom.xml file.
 *
 * This file is distributed under the terms of the Apache License, version 2.0
 *******************************************************************************/
package org.danilopianini.lang;

import static org.danilopianini.lang.Constants.DJB2_MAGIC;
import static org.danilopianini.lang.Constants.DJB2_SHIFT;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

import org.danilopianini.io.FileUtilities;

import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

/**
 */
public final class HashUtils {

    private static final HashFunction MURMUR32 = Hashing.murmur3_32();
    private static final HashFunction MURMUR128 = Hashing.murmur3_128();
    private static final Charset CHARSET = StandardCharsets.UTF_16;
    private static final int MASK_BYTE = 0xff;
    private static final int SHIFT3 = 24;
    private static final int SHIFT4 = 32;
    private static final int SHIFT5 = 40;
    private static final int SHIFT6 = 48;
    private static final int SHIFT7 = 56;

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final double data) {
        return MURMUR32.hashLong(Double.doubleToRawLongBits(data)).asInt();
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final long data) {
        return MURMUR32.hashLong(data).asInt();
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final float data) {
        return MURMUR32.hashInt(Float.floatToRawIntBits(data)).asInt();
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final CharSequence data) {
        return MURMUR32.hashString(data, CHARSET).asInt();
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final Integer data) {
        return data;
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final Float data) {
        return hash32(data.floatValue());
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final Double data) {
        return hash32(data.doubleValue());
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final Long data) {
        return hash32(data.longValue());
    }

    /**
     * Computes a 32bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 32bit hash
     */
    public static int hash32(final Object... data) {
        final Hasher h = MURMUR32.newHasher();
        if (data.length == 1) {
            populateHasher(data[0], h);
        } else {
            populateHasher(data, h);
        }
        return h.hash().asInt();
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final double data) {
        return MURMUR128.hashLong(Double.doubleToRawLongBits(data)).asLong();
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final long data) {
        return MURMUR128.hashLong(data).asLong();
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final float data) {
        return hash32(data);
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final CharSequence data) {
        return MURMUR128.hashString(data, CHARSET).asLong();
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final Integer data) {
        return data;
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final Float data) {
        return hash32(data);
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final Double data) {
        return hash64(data.doubleValue());
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final Long data) {
        return hash64(data.longValue());
    }

    /**
     * Computes a 64bit hash.
     * 
     * @param data
     *            the data to hash
     * @return a 64bit hash
     */
    public static long hash64(final Object... data) {
        final Hasher h = MURMUR128.newHasher();
        if (data.length == 1) {
            populateHasher(data[0], h);
        } else {
            populateHasher(data, h);
        }
        return h.hash().asLong();
    }

    private static void populateHasher(final Object data, final Hasher h) {
        if (data != null) {
            if (data instanceof Number) {
                final Number d = (Number) data;
                if (data instanceof Integer) {
                    h.putInt(d.intValue());
                } else if (data instanceof Double) {
                    h.putDouble(d.doubleValue());
                } else if (data instanceof Long) {
                    h.putLong(d.longValue());
                } else if (data instanceof Float) {
                    h.putFloat(d.floatValue());
                } else if (data instanceof Byte) {
                    h.putByte(d.byteValue());
                } else if (data instanceof Short) {
                    h.putShort(d.shortValue());
                } else {
                    h.putInt(data.hashCode());
                }
            } else if (data instanceof CharSequence) {
                h.putString((CharSequence) data, CHARSET);
            } else if (data.getClass().isArray()) {
                final int size = Array.getLength(data);
                for (int i = 0; i < size; i++) {
                    populateHasher(Array.get(data, i), h);
                }
            } else if (data instanceof Iterable) {
                for (final Object o : (Iterable<?>) data) {
                    populateHasher(o, h);
                }
            } else {
                h.putInt(data.hashCode());
            }
        }
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 32bit hash
     */
    public static int djb2int32(final byte... bytes) {
        int hash32 = Constants.DJB2_START;
        for (final byte b : bytes) {
            hash32 = hash32 * DJB2_MAGIC ^ b;
        }
        return hash32;
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 32bit hash
     */
    public static int djb2int32(final double... bytes) {
        return djb2int32(toByta(bytes));
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 32bit hash
     */
    public static int djb2int32(final int... bytes) {
        return djb2int32(toByta(bytes));
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 32bit hash
     * 
     * @deprecated This method may perform very badly, since it serializes objects in-memory. Prefer {@link HashUtils#hash32(Object...)}.
     */
    @Deprecated
    public static int djb2int32obj(final Serializable... bytes) {
        return djb2int32(FileUtilities.serializeObject(bytes));
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 64bit hash
     */
    public static long djb2long64(final byte... bytes) {
        long hash = Constants.DJB2_START;
        for (final byte b : bytes) {
            hash = ((hash << DJB2_SHIFT) + hash) + b;
        }
        return hash;
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 64bit hash
     */
    public static long djb2long64(final double... bytes) {
        return djb2long64(toByta(bytes));
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 64bit hash
     */
    public static long djb2long64(final int... bytes) {
        return djb2long64(toByta(bytes));
    }

    /**
     * @param bytes
     *            bytes
     * @return DJB2 64bit hash
     * 
     * @deprecated This method may perform very badly, since it serializes objects in-memory. Prefer {@link HashUtils#hash64(Object...)}.
     */
    @Deprecated
    public static long djb2long64obj(final Serializable... bytes) {
        return djb2long64(FileUtilities.serializeObject(bytes));
    }

    /**
     * @param data
     *            double
     * @return byte[]
     */
    public static byte[] toByta(final double data) {
        return toByta(Double.doubleToRawLongBits(data));
    }

    /**
     * @param data
     *            double[]
     * @return byte[]
     */
    public static byte[] toByta(final double[] data) {
        Objects.requireNonNull(data);
        final byte[] byts = new byte[data.length * 8];
        for (int i = 0; i < data.length; i++) {
            System.arraycopy(toByta(data[i]), 0, byts, i * 8, 8);
        }
        return byts;
    }

    /**
     * @param data
     *            float
     * @return byte[]
     */
    public static byte[] toByta(final float data) {
        return toByta(Float.floatToRawIntBits(data));
    }

    /**
     * @param data
     *            float[]
     * @return byte[]
     */
    public static byte[] toByta(final float[] data) {
        Objects.requireNonNull(data);
        final byte[] byts = new byte[data.length * 4];
        for (int i = 0; i < data.length; i++) {
            System.arraycopy(toByta(data[i]), 0, byts, i * 4, 4);
        }
        return byts;
    }

    /**
     * @param data
     *            int
     * @return byte[]
     */
    public static byte[] toByta(final int data) {
        return new byte[] { (byte) ((data >> SHIFT3) & MASK_BYTE), (byte) ((data >> 16) & MASK_BYTE),
                (byte) ((data >> 8) & MASK_BYTE), (byte) ((data >> 0) & MASK_BYTE), };
    }

    /**
     * @param data
     *            int[]
     * @return byte[]
     */
    public static byte[] toByta(final int[] data) {
        Objects.requireNonNull(data);
        final byte[] byts = new byte[data.length * 4];
        for (int i = 0; i < data.length; i++) {
            System.arraycopy(toByta(data[i]), 0, byts, i * 4, 4);
        }
        return byts;
    }

    /**
     * @param data
     *            long
     * @return byte[]
     */
    public static byte[] toByta(final long data) {
        return new byte[] { (byte) ((data >> SHIFT7) & MASK_BYTE), (byte) ((data >> SHIFT6) & MASK_BYTE),
                (byte) ((data >> SHIFT5) & MASK_BYTE), (byte) ((data >> SHIFT4) & MASK_BYTE),
                (byte) ((data >> SHIFT3) & MASK_BYTE), (byte) ((data >> 16) & MASK_BYTE),
                (byte) ((data >> 8) & MASK_BYTE), (byte) ((data >> 0) & MASK_BYTE), };
    }

    /**
     * @param data
     *            long[]
     * @return byte[]
     */
    public static byte[] toByta(final long[] data) {
        Objects.requireNonNull(data);
        final byte[] byts = new byte[data.length * 8];
        for (int i = 0; i < data.length; i++) {
            System.arraycopy(toByta(data[i]), 0, byts, i * 8, 8);
        }
        return byts;
    }

    /**
     * Runs System.identityHashCode(a) == System.identityHashCode(b). In most
     * JVM implementations, this is actually a pointer comparison. Nevertheless,
     * this is not guaranteed: handle with care
     * 
     * @param a
     *            an {@link Object}
     * @param b
     *            another {@link Object}
     * @return System.identityHashCode(a) == System.identityHashCode(b)
     */
    public static boolean pointerEquals(final Object a, final Object b) {
        return System.identityHashCode(a) == System.identityHashCode(b);
    }

    /**
     * 
     */
    private HashUtils() {
    }

}