/**
   * Shifts in byte b and mods with poly
   *
   * <p>If we have passed overflowed our window, we pop a byte
   */
  @Override
  public synchronized void pushByte(byte b) {
    Polynomial f = fingerprint;
    f = f.shiftLeft(byteShift);
    f = f.or(Polynomial.createFromLong(b & 0xFFL));
    f = f.mod(poly);

    fingerprint = f;

    if (bytesPerWindow > 0) {
      byteWindow.add(b);
      if (byteWindow.isFull()) popByte();
    }
  }
  /**
   * Removes the contribution of the first byte in the byte queue from the fingerprint.
   *
   * <p>Note that the shift necessary to calculate it's contribution can be sizeable, and the mod
   * calculation will be similarly slow. It is therefore done with the Polynomial to support
   * arbitrary sizes.
   *
   * <p>Note that despite this massive shift, the fingerprint will still result in a k-bit number at
   * the end of the calculation.
   */
  public synchronized void popByte() {
    byte b = byteWindow.poll();
    Polynomial f = Polynomial.createFromLong(b & 0xFFL);
    f = f.shiftLeft(windowShift);
    f = f.mod(poly);

    fingerprint = fingerprint.xor(f);
  }