@Override
 public boolean compareAndSetBlock(
     int x, int y, int z, short expectId, short expectData, short newId, short newData) {
   int exp = BlockFullState.getPacked(expectId, expectData);
   int update = BlockFullState.getPacked(newId, newData);
   boolean success = store.compareAndSet(getIndex(x, y, z), exp, update);
   if (success && exp != update) {
     markDirty(x, y, z, exp, update);
   }
   return success;
 }
  /**
   * Atomically gets the full set of data associated with the block.<br>
   * <br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @return the full state of the block
   */
  @Override
  public int getFullData(int x, int y, int z) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        int seq = getSequence(x, y, z);
        short blockId = blockIds.get(index);
        if (auxStore.isReserved(blockId)) {
          int state = auxStore.getInt(blockId);
          if (testSequence(x, y, z, seq)) {
            return state;
          }
        } else {
          int state = BlockFullState.getPacked(blockId, (short) 0);
          return state;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
 @Override
 public short[] getDataArray(short[] array) {
   if (array.length != length) {
     array = new short[length];
   }
   for (int i = 0; i < length; i++) {
     array[i] = BlockFullState.getData(store.get(i));
   }
   return array;
 }
 @Override
 public int getAndSetBlock(int x, int y, int z, short id, short data) {
   int oldState = BlockFullState.getPacked(id, data);
   int newState = 0;
   try {
     return newState = store.set(getIndex(x, y, z), oldState);
   } finally {
     markDirty(x, y, z, oldState, newState);
   }
 }
 public AtomicPaletteBlockStore(
     int shift, boolean storeState, int dirtySize, short[] blocks, short[] data) {
   this(shift, storeState, dirtySize);
   if (blocks != null) {
     int[] initial = new int[Math.min(blocks.length, this.length)];
     for (int i = 0; i < blocks.length; i++) {
       short d = data != null ? data[i] : 0;
       initial[i] = BlockFullState.getPacked(blocks[i], d);
     }
     store.set(initial);
   }
 }
  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();
      }
    }
  }
 @Override
 public int getData(int x, int y, int z) {
   return BlockFullState.getData(getFullData(x, y, z));
 }