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

io.methvin.watcher.hashing.Murmur3F Maven / Gradle / Ivy

/*
 * Code adapted from Greenrobot Essentials Murmur3F.java (https://git.io/fAG0Z)
 *
 * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.methvin.watcher.hashing;

import java.math.BigInteger;
import java.util.zip.Checksum;

/** Murmur3F (MurmurHash3_x64_128) */
public class Murmur3F implements Checksum {

  private static final long C1 = 0x87c37b91114253d5L;
  private static final long C2 = 0x4cf5ad432745937fL;

  private final long seed;

  private long h1;
  private long h2;
  private int length;

  private int partialPos;
  private long partialK1;
  private long partialK2;

  private boolean finished;
  private long finishedH1;
  private long finishedH2;

  public Murmur3F() {
    seed = 0;
  }

  public Murmur3F(int seed) {
    this.seed = seed & 0xffffffffL; // unsigned 32 bit -> long
    h1 = h2 = this.seed;
  }

  @Override
  public void update(int b) {
    finished = false;
    switch (partialPos) {
      case 0:
        partialK1 = 0xff & b;
        break;
      case 1:
        partialK1 |= (0xff & b) << 8;
        break;
      case 2:
        partialK1 |= (0xff & b) << 16;
        break;
      case 3:
        partialK1 |= (0xffL & b) << 24;
        break;
      case 4:
        partialK1 |= (0xffL & b) << 32;
        break;
      case 5:
        partialK1 |= (0xffL & b) << 40;
        break;
      case 6:
        partialK1 |= (0xffL & b) << 48;
        break;
      case 7:
        partialK1 |= (0xffL & b) << 56;
        break;
      case 8:
        partialK2 = 0xff & b;
        break;
      case 9:
        partialK2 |= (0xff & b) << 8;
        break;
      case 10:
        partialK2 |= (0xff & b) << 16;
        break;
      case 11:
        partialK2 |= (0xffL & b) << 24;
        break;
      case 12:
        partialK2 |= (0xffL & b) << 32;
        break;
      case 13:
        partialK2 |= (0xffL & b) << 40;
        break;
      case 14:
        partialK2 |= (0xffL & b) << 48;
        break;
      case 15:
        partialK2 |= (0xffL & b) << 56;
        break;
    }

    partialPos++;
    if (partialPos == 16) {
      applyKs(partialK1, partialK2);
      partialPos = 0;
    }
    length++;
  }

  public void update(byte[] b) {
    update(b, 0, b.length);
  }

  @Override
  public void update(byte[] b, int off, int len) {
    finished = false;
    while (partialPos != 0 && len > 0) {
      update(b[off]);
      off++;
      len--;
    }

    int remainder = len & 0xF;
    int stop = off + len - remainder;
    for (int i = off; i < stop; i += 16) {
      long k1 = getLongLE(b, i);
      long k2 = getLongLE(b, i + 8);
      applyKs(k1, k2);
    }
    length += stop - off;

    for (int i = 0; i < remainder; i++) {
      update(b[stop + i]);
    }
  }

  private void applyKs(long k1, long k2) {
    k1 *= C1;
    k1 = Long.rotateLeft(k1, 31);
    k1 *= C2;
    h1 ^= k1;

    h1 = Long.rotateLeft(h1, 27);
    h1 += h2;
    h1 = h1 * 5 + 0x52dce729;

    k2 *= C2;
    k2 = Long.rotateLeft(k2, 33);
    k2 *= C1;
    h2 ^= k2;

    h2 = Long.rotateLeft(h2, 31);
    h2 += h1;
    h2 = h2 * 5 + 0x38495ab5;
  }

  private void checkFinished() {
    if (!finished) {
      finished = true;
      finishedH1 = h1;
      finishedH2 = h2;
      if (partialPos > 0) {
        if (partialPos > 8) {
          long k2 = partialK2 * C2;
          k2 = Long.rotateLeft(k2, 33);
          k2 *= C1;
          finishedH2 ^= k2;
        }
        long k1 = partialK1 * C1;
        k1 = Long.rotateLeft(k1, 31);
        k1 *= C2;
        finishedH1 ^= k1;
      }

      finishedH1 ^= length;
      finishedH2 ^= length;

      finishedH1 += finishedH2;
      finishedH2 += finishedH1;

      finishedH1 = fmix64(finishedH1);
      finishedH2 = fmix64(finishedH2);

      finishedH1 += finishedH2;
      finishedH2 += finishedH1;
    }
  }

  private long fmix64(long k) {
    k ^= k >>> 33;
    k *= 0xff51afd7ed558ccdL;
    k ^= k >>> 33;
    k *= 0xc4ceb9fe1a85ec53L;
    k ^= k >>> 33;
    return k;
  }

  @Override
  /**
   * Returns the lower 64 bits of the 128 bit hash (you can use just this value this as a 64 bit
   * hash).
   */
  public long getValue() {
    checkFinished();
    return finishedH1;
  }

  /** Returns the higher 64 bits of the 128 bit hash. */
  public long getValueHigh() {
    checkFinished();
    return finishedH2;
  }

  /** Positive value. */
  public BigInteger getValueBigInteger() {
    byte[] bytes = getValueBytesBigEndian();
    return new BigInteger(1, bytes);
  }

  /** Padded with leading 0s to ensure length of 32. */
  public String getValueHexString() {
    checkFinished();
    return getPaddedHexString(finishedH2) + getPaddedHexString(finishedH1);
  }

  private String getPaddedHexString(long value) {
    String string = Long.toHexString(value);
    while (string.length() < 16) {
      string = '0' + string;
    }
    return string;
  }

  public byte[] getValueBytesBigEndian() {
    checkFinished();
    byte[] bytes = new byte[16];
    for (int i = 0; i < 8; i++) {
      bytes[i] = (byte) ((finishedH2 >>> (56 - i * 8)) & 0xff);
    }
    for (int i = 0; i < 8; i++) {
      bytes[8 + i] = (byte) ((finishedH1 >>> (56 - i * 8)) & 0xff);
    }
    return bytes;
  }

  public byte[] getValueBytesLittleEndian() {
    checkFinished();
    byte[] bytes = new byte[16];
    for (int i = 0; i < 8; i++) {
      bytes[i] = (byte) ((finishedH1 >>> (i * 8)) & 0xff);
    }
    for (int i = 0; i < 8; i++) {
      bytes[8 + i] = (byte) ((finishedH2 >>> (i * 8)) & 0xff);
    }
    return bytes;
  }

  @Override
  public void reset() {
    h1 = h2 = seed;
    length = 0;
    partialPos = 0;
    finished = false;

    // The remainder is not really necessary, but looks nicer when debugging
    partialK1 = partialK2 = 0;
    finishedH1 = finishedH2 = 0;
  }

  private long getLongLE(byte[] bytes, int index) {
    return (bytes[index] & 0xff)
        | ((bytes[index + 1] & 0xff) << 8)
        | ((bytes[index + 2] & 0xff) << 16)
        | ((bytes[index + 3] & 0xffL) << 24)
        | ((bytes[index + 4] & 0xffL) << 32)
        | ((bytes[index + 5] & 0xffL) << 40)
        | ((bytes[index + 6] & 0xffL) << 48)
        | (((long) bytes[index + 7]) << 56);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy