package org.jruby.util;

import java.nio.ByteOrder;

import org.jruby.util.unsafe.UnsafeHolder;

import sun.misc.Unsafe;

SipHash implementation with hand inlining the SIPROUND. To know details about SipHash, see; "a fast short-input PRF" https://www.131002.net/siphash/
Author:nahi@ruby-lang.org
/** * SipHash implementation with hand inlining the SIPROUND. * * To know details about SipHash, see; * "a fast short-input PRF" https://www.131002.net/siphash/ * * @author nahi@ruby-lang.org */
public class SipHashInline { public static long hash24(long k0, long k1, byte[] data) { return hash24(k0, k1, data, 0, data.length); } public static long hash24(long k0, long k1, byte[] src, int offset, int length) { long v0 = 0x736f6d6570736575L ^ k0; long v1 = 0x646f72616e646f6dL ^ k1; long v2 = 0x6c7967656e657261L ^ k0; long v3 = 0x7465646279746573L ^ k1; long m; int last = offset + length / 8 * 8; int i = offset; if (offset < 0) { throw new ArrayIndexOutOfBoundsException(offset); } else if (offset + length > src.length) { throw new ArrayIndexOutOfBoundsException(src.length); } // processing 8 bytes blocks in data while (i < last) { m = LongReader.INSTANCE.getLong(src, i); i += 8; // MSGROUND { v3 ^= m; /* SIPROUND with hand reordering * * SIPROUND in siphash24.c: * A: v0 += v1; * B: v1=ROTL(v1,13); * C: v1 ^= v0; * D: v0=ROTL(v0,32); * E: v2 += v3; * F: v3=ROTL(v3,16); * G: v3 ^= v2; * H: v0 += v3; * I: v3=ROTL(v3,21); * J: v3 ^= v0; * K: v2 += v1; * L: v1=ROTL(v1,17); * M: v1 ^= v2; * N: v2=ROTL(v2,32); * * Each dependency: * B -> A * C -> A, B * D -> C * F -> E * G -> E, F * H -> D, G * I -> H * J -> H, I * K -> C, G * L -> K * M -> K, L * N -> M * * Dependency graph: * D -> C -> B -> A * G -> F -> E * J -> I -> H -> D, G * N -> M -> L -> K -> C, G * * Resulting parallel friendly execution order: * -> ABCDHIJ * -> EFGKLMN */ // SIPROUND { v0 += v1; v2 += v3; v1 = (v1 << 13) | v1 >>> 51; v3 = (v3 << 16) | v3 >>> 48; v1 ^= v0; v3 ^= v2; v0 = (v0 << 32) | v0 >>> 32; v2 += v1; v0 += v3; v1 = (v1 << 17) | v1 >>> 47; v3 = (v3 << 21) | v3 >>> 43; v1 ^= v2; v3 ^= v0; v2 = (v2 << 32) | v2 >>> 32; // } // SIPROUND { v0 += v1; v2 += v3; v1 = (v1 << 13) | v1 >>> 51; v3 = (v3 << 16) | v3 >>> 48; v1 ^= v0; v3 ^= v2; v0 = (v0 << 32) | v0 >>> 32; v2 += v1; v0 += v3; v1 = (v1 << 17) | v1 >>> 47; v3 = (v3 << 21) | v3 >>> 43; v1 ^= v2; v3 ^= v0; v2 = (v2 << 32) | v2 >>> 32; // } v0 ^= m; // } } // packing the last block to long, as LE 0-7 bytes + the length in the top byte m = 0; for (i = offset + length - 1; i >= last; --i) { m <<= 8; m |= (long) src[i]; } m |= (long) length << 56; // MSGROUND { v3 ^= m; for (int j = 0; j < 2; j++) { // SIPROUND { v0 += v1; v2 += v3; v1 = (v1 << 13) | v1 >>> 51; v3 = (v3 << 16) | v3 >>> 48; v1 ^= v0; v3 ^= v2; v0 = (v0 << 32) | v0 >>> 32; v2 += v1; v0 += v3; v1 = (v1 << 17) | v1 >>> 47; v3 = (v3 << 21) | v3 >>> 43; v1 ^= v2; v3 ^= v0; v2 = (v2 << 32) | v2 >>> 32; // } } v0 ^= m; // } // finishing... v2 ^= 0xff; for (int j = 0; j < 4; j++) { // SIPROUND { v0 += v1; v2 += v3; v1 = (v1 << 13) | v1 >>> 51; v3 = (v3 << 16) | v3 >>> 48; v1 ^= v0; v3 ^= v2; v0 = (v0 << 32) | v0 >>> 32; v2 += v1; v0 += v3; v1 = (v1 << 17) | v1 >>> 47; v3 = (v3 << 21) | v3 >>> 43; v1 ^= v2; v3 ^= v0; v2 = (v2 << 32) | v2 >>> 32; // } } return v0 ^ v1 ^ v2 ^ v3; } private static abstract class LongReader { public abstract long getLong(byte[] src, int offset); public static final LongReader INSTANCE = createBestLongReader(); private static LongReader createBestLongReader() { try { if (UnsafeHolder.U != null) { if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { return new UnsafeLongReader(UnsafeHolder.U); } } } catch (Exception e) { } return new FallbackLongReader(); } private static final class FallbackLongReader extends LongReader { @Override public long getLong(byte[] src, int offset) { return (long) src[offset++] | (long) src[offset++] << 8 | (long) src[offset++] << 16 | (long) src[offset++] << 24 | (long) src[offset++] << 32 | (long) src[offset++] << 40 | (long) src[offset++] << 48 | (long) src[offset++] << 56 ; } } private static final class UnsafeLongReader extends LongReader { final Unsafe unsafe; final int byteArrayBaseOffset; public UnsafeLongReader(Unsafe unsafe) { this.unsafe = unsafe; this.byteArrayBaseOffset = unsafe.arrayBaseOffset(byte[].class); } @Override public final long getLong(byte[] src, int offset) { return unsafe.getLong(src, byteArrayBaseOffset + (long)offset); } } } }