/*
 * Copyright 2015 Gilga Einziger. All Rights Reserved.
 *
 * 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 com.github.benmanes.caffeine.cache.simulator.admission.tinycache;

/**
 * This is a hash function and parser tp simplify parsing the hash value, it split it to . This
 * class provide hash utilities, and parse the items.
 *
 * @author gilga1983@gmail.com (Gil Einziger)
 */
public final class HashFunctionParser {
  // currently chain is bounded to be 64.
  private static final int fpSize = 8; // this implementation assumes byte.
  private static final byte fpMask = (byte) 255; // (all bits in byte are 1, (logical value of -1));
  private static final long chainMask = 63L; // (6 first bit are set to 1).
  private final int nrSets;
  public final HashedItem fpaux; // used just to avoid allocating new memory as a return value.
  private final static long Seed64 = 0xe17a1465;
  private final static long m = 0xc6a4a7935bd1e995L;
  private final static int r = 47;

  public HashFunctionParser(int nrsets) {
    this.nrSets = nrsets;
    fpaux = new HashedItem(fpMask, fpMask, fpMask, 0L);
  }

  public HashedItem createHash(long item) {
    long h = (Seed64) ^ (m);
    item *= m;
    item ^= item >>> r;
    item *= m;

    h ^= item;
    h *= m;

    fpaux.fingerprint = (byte) h;
    // the next line is a dirty fix as I do not want the value of 0 as a fingerprint.
    // It can be eliminated if we want very short fingerprints.
    fpaux.fingerprint = (fpaux.fingerprint == 0L) ? 1 : fpaux.fingerprint;
    h >>>= fpSize;
    fpaux.chainId = (byte) (h & chainMask);
    h >>>= 6;
    fpaux.set = (int) ((h & Long.MAX_VALUE) % nrSets);

    fpaux.value = (item << 1) | 1;
    if (item == 0) {
      fpaux.value = 1;
    }
    return fpaux;
  }
}
