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

net.openhft.hashing.MetroHash Maven / Gradle / Ivy

package net.openhft.hashing;

import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static net.openhft.hashing.LongHashFunction.NATIVE_LITTLE_ENDIAN;

class MetroHash {
    private static final MetroHash INSTANCE = new MetroHash();
    private static final MetroHash NATIVE_METRO = NATIVE_LITTLE_ENDIAN ?
            MetroHash.INSTANCE : BigEndian.INSTANCE;

    //primes
    private static final long k0 = 0xD6D018F5L;
    private static final long k1 = 0xA2AA033BL;
    private static final long k2 = 0x62992FC1L;
    private static final long k3 = 0x30BC5B29L;

     long fetch64(Access access, T in, long off) {
        return access.getLong(in, off);
    }

     long fetch32(Access access, T in, long off) {
        return access.getUnsignedInt(in, off);
    }

     long fetch16(Access access, T in, long off) {
        return access.getUnsignedShort(in, off);
    }

     int fetch8(Access access, T in, long off) {
        return access.getUnsignedByte(in, off);
    }

    long toLittleEndian(long v) {
        return v;
    }

    int toLittleEndian(int v) {
        return v;
    }

    short toLittleEndian(short v) {
        return v;
    }


     long metroHash64(long seed, T input, Access access, long off, long length) {
        long remaining = length;

        long h = (seed + k2) * k0;

        if (length >= 32) {
            long v0 = h;
            long v1 = h;
            long v2 = h;
            long v3 = h;

            do {
                v0 += fetch64(access, input, off) * k0;
                v0 = Long.rotateRight(v0, 29) + v2;
                v1 += fetch64(access, input, off + 8) * k1;
                v1 = Long.rotateRight(v1, 29) + v3;
                v2 += fetch64(access, input, off + 16) * k2;
                v2 = Long.rotateRight(v2, 29) + v0;
                v3 += fetch64(access, input, off + 24) * k3;
                v3 = Long.rotateRight(v3, 29) + v1;

                off += 32;
                remaining -= 32;
            } while (remaining >= 32);

            v2 ^= Long.rotateRight(((v0 + v3) * k0) + v1, 37) * k1;
            v3 ^= Long.rotateRight(((v1 + v2) * k1) + v0, 37) * k0;
            v0 ^= Long.rotateRight(((v0 + v2) * k0) + v3, 37) * k1;
            v1 ^= Long.rotateRight(((v1 + v3) * k1) + v2, 37) * k0;

            h += v0 ^ v1;
        }

        if (remaining >= 16) {
            long v0 = h + (fetch64(access, input, off) * k2);
            v0 = Long.rotateRight(v0, 29) * k3;
            long v1 = h + (fetch64(access, input, off + 8) * k2);
            v1 = Long.rotateRight(v1, 29) * k3;
            v0 ^= Long.rotateRight(v0 * k0, 21) + v1;
            v1 ^= Long.rotateRight(v1 * k3, 21) + v0;
            h += v1;

            off += 16;
            remaining -= 16;
        }

        if (remaining >= 8) {
            h += fetch64(access, input, off) * k3;
            h ^= Long.rotateRight(h, 55) * k1;

            off += 8;
            remaining -= 8;
        }

        if (remaining >= 4) {
            h += fetch32(access, input, off) * k3;
            h ^= Long.rotateRight(h, 26) * k1;

            off += 4;
            remaining -= 4;
        }

        if (remaining >= 2) {
            h += fetch16(access, input, off) * k3;
            h ^= Long.rotateRight(h, 48) * k1;

            off += 2;
            remaining -= 2;
        }

        if (remaining >= 1) {
            h += fetch8(access, input, off) * k3;
            h ^= Long.rotateRight(h, 37) * k1;
        }

        return finalize(h);
    }

    private static long finalize(long h) {
        h ^= Long.rotateRight(h, 28);
        h *= k0;
        h ^= Long.rotateRight(h, 29);
        return h;
    }

    private static class BigEndian extends MetroHash {
        private static final BigEndian INSTANCE = new BigEndian();

        private BigEndian() {
        }

        @Override
         long fetch64(Access access, T in, long off) {
            return Long.reverseBytes(super.fetch64(access, in, off));
        }

        @Override
         long fetch32(Access access, T in, long off) {
            return Integer.reverseBytes(access.getInt(in, off)) & 0xFFFFFFFFL;
        }

        @Override
         long fetch16(Access access, T in, long off) {
            return Short.reverseBytes((short)access.getShort(in, off)) & 0xFFFFL;
        }

        @Override
         int fetch8(Access access, T in, long off) {
            return super.fetch8(access, in, off);
        }

        @Override
        long toLittleEndian(long v) {
            return Long.reverseBytes(v);
        }

        @Override
        int toLittleEndian(int v) {
            return Integer.reverseBytes(v);
        }

        @Override
        short toLittleEndian(short v) {
            return Short.reverseBytes(v);
        }
    }

    private static class AsLongHashFunction extends LongHashFunction {
        private static final long serialVersionUID = 0L;
        private static final AsLongHashFunction SEEDLESS_INSTANCE = new AsLongHashFunction();
        private static final long VOID_HASH = MetroHash.finalize(k2 * k0);

        private Object readResolve() {
            return SEEDLESS_INSTANCE;
        }

        protected long seed() {
            return 0L;
        }

        @Override
        public long hashLong(long input) {
            input = NATIVE_METRO.toLittleEndian(input);
            long h = (seed() + k2) * k0;
            h += input * k3;
            h ^= Long.rotateRight(h, 55) * k1;

            return MetroHash.finalize(h);
        }

        @Override
        public long hashInt(int input) {
            input = NATIVE_METRO.toLittleEndian(input);
            long h = (seed() + k2) * k0;
            h += Primitives.unsignedInt(input) * k3;
            h ^= Long.rotateRight(h, 26) * k1;

            return MetroHash.finalize(h);
        }

        @Override
        public long hashShort(short input) {
            input = NATIVE_METRO.toLittleEndian(input);
            long h = (seed() + k2) * k0;
            h += Primitives.unsignedShort(input) * k3;
            h ^= Long.rotateRight(h, 48) * k1;

            return MetroHash.finalize(h);
        }

        @Override
        public long hashChar(char input) {
            return hashShort((short) input);
        }

        @Override
        public long hashByte(byte input) {
            long h = (seed() + k2) * k0;
            h += Primitives.unsignedByte(input) * k3;
            h ^= Long.rotateRight(h, 37) * k1;

            return MetroHash.finalize(h);
        }

        @Override
        public long hashVoid() {
            return VOID_HASH;
        }

        @Override
        public  long hash(T input, Access access, long off, long len) {
            long seed = seed();
            if (access.byteOrder(input) == LITTLE_ENDIAN) {
                return MetroHash.INSTANCE.metroHash64(seed, input, access, off, len);
            } else {
                return MetroHash.BigEndian.INSTANCE.metroHash64(seed, input, access, off, len);
            }
        }
    }

    static LongHashFunction asLongHashFunctionWithoutSeed() {
        return AsLongHashFunction.SEEDLESS_INSTANCE;
    }

    static LongHashFunction asLongHashFunctionWithSeed(long seed) {
        return new AsLongHashFunctionSeeded(seed);
    }

    private static class AsLongHashFunctionSeeded extends AsLongHashFunction {
        private static final long serialVersionUID = 0L;

        private final long seed;
        private final transient long voidHash;

        AsLongHashFunctionSeeded(long seed) {
            this.seed = seed;
            voidHash = MetroHash.finalize((seed + k2) * k0);
        }

        @Override
        public long hashVoid() {
            return voidHash;
        }

        @Override
        protected long seed() {
            return seed;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy