package btools.util;

public final class ByteArrayUnifier implements IByteArrayUnifier {
  private byte[][] byteArrayCache;
  private int[] crcCrosscheck;
  private int size;

  public ByteArrayUnifier(int size, boolean validateImmutability) {
    this.size = size;
    byteArrayCache = new byte[size][];
    if (validateImmutability) crcCrosscheck = new int[size];
  }

  /**
   * Unify a byte array in order to reuse instances when possible.
   * The byte arrays are assumed to be treated as immutable,
   * allowing the reuse
   *
   * @param ab the byte array to unify
   * @return the cached instance or the input instanced if not cached
   */
  public byte[] unify(byte[] ab) {
    return unify(ab, 0, ab.length);
  }

  public byte[] unify(byte[] ab, int offset, int len) {
    int crc = Crc32.crc(ab, offset, len);
    int idx = (crc & 0xfffffff) % size;
    byte[] abc = byteArrayCache[idx];
    if (abc != null && abc.length == len) {
      int i = 0;
      while (i < len) {
        if (ab[offset + i] != abc[i])
          break;
        i++;
      }
      if (i == len)
        return abc;
    }
    if (crcCrosscheck != null) {
      if (byteArrayCache[idx] != null) {
        byte[] abold = byteArrayCache[idx];
        int crcold = Crc32.crc(abold, 0, abold.length);
        if (crcold != crcCrosscheck[idx])
          throw new IllegalArgumentException("ByteArrayUnifier: immutablity validation failed!");
      }
      crcCrosscheck[idx] = crc;
    }
    byte[] nab = new byte[len];
    System.arraycopy(ab, offset, nab, 0, len);
    byteArrayCache[idx] = nab;
    return nab;
  }
}
