/**
   * Sets the block id, data and auxData for the block at (x, y, z), if the current data matches the
   * expected data.<br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param expectId the expected block id
   * @param expectData the expected block data
   * @param expectAuxData the expected block auxiliary data
   * @param newId the new block id
   * @param newData the new block data
   * @param newAuxData the new block auxiliary data
   * @return true if the block was set
   */
  @Override
  public boolean compareAndSetBlock(
      int x, int y, int z, short expectId, short expectData, short newId, short newData) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        short oldBlockId = blockIds.get(index);
        boolean oldReserved = auxStore.isReserved(oldBlockId);

        if (!oldReserved) {
          if (blockIds.get(index) != expectId || expectData != 0) {
            return false;
          }
        } else {
          int seq = auxStore.getSequence(oldBlockId);
          short oldId = auxStore.getId(oldBlockId);
          short oldData = auxStore.getData(oldBlockId);
          if (!testSequence(x, y, z, seq)) {
            continue;
          }
          if (oldId != expectId || oldData != expectData) {
            return false;
          }
        }

        if (newData == 0 && !auxStore.isReserved(newId)) {
          if (!blockIds.compareAndSet(index, oldBlockId, newId)) {
            continue;
          }
        } else {
          int newIndex = auxStore.add(newId, newData);
          if (!blockIds.compareAndSet(index, oldBlockId, (short) newIndex)) {
            auxStore.remove(newIndex);
            continue;
          }
        }

        if (oldReserved) {
          auxStore.remove(oldBlockId);
        }

        markDirty(x, y, z);
        return true;
      }
    } finally {
      atomicNotify(index);
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
 /**
  * Compresses the auxiliary store.<br>
  * <br>
  * This method should only be called when the store is guaranteed not to be accessed from any
  * other thread.<br>
  */
 @Override
 public void compress() {
   if (!compressing.compareAndSet(false, true)) {
     throw new IllegalStateException("Compression started while compression was in progress");
   }
   int length = side * side * side;
   AtomicIntArrayStore newAuxStore = new AtomicIntArrayStore(length);
   for (int i = 0; i < length; i++) {
     short blockId = blockIds.get(i);
     if (auxStore.isReserved(blockId)) {
       short storedId = auxStore.getId(blockId);
       short storedData = auxStore.getData(blockId);
       int newIndex = newAuxStore.add(storedId, storedData);
       if (!blockIds.compareAndSet(i, blockId, (short) newIndex)) {
         throw new IllegalStateException("Unstable block id data during compression step");
       }
     }
   }
   auxStore = newAuxStore;
   compressing.set(false);
 }
  private int getAndSetBlockRaw(int x, int y, int z, short id, short data) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        short oldBlockId = blockIds.get(index);
        boolean oldReserved = auxStore.isReserved(oldBlockId);
        if (data == 0 && !auxStore.isReserved(id)) {
          if (!blockIds.compareAndSet(index, oldBlockId, id)) {
            continue;
          }
        } else {
          int newIndex = auxStore.add(id, data);
          if (!blockIds.compareAndSet(index, oldBlockId, (short) newIndex)) {
            auxStore.remove(newIndex);
            continue;
          }
        }

        if (oldReserved) {
          return auxStore.remove(oldBlockId);
        }

        return BlockFullState.getPacked(oldBlockId, (short) 0);
      }
    } finally {
      markDirty(x, y, z);
      atomicNotify(index);
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }