@Override
  public final void close() throws IOException {
    try {
      if (buffers == null) return;

      // make local copy, then un-set early
      final ByteBuffer[] bufs = buffers;
      unsetBuffers();
      if (clones != null) {
        clones.remove(this);
      }

      if (isClone) return;

      // for extra safety unset also all clones' buffers:
      if (clones != null) {
        for (Iterator<ByteBufferIndexInput> it = this.clones.keyIterator(); it.hasNext(); ) {
          final ByteBufferIndexInput clone = it.next();
          assert clone.isClone;
          clone.unsetBuffers();
        }
        this.clones.clear();
      }

      for (final ByteBuffer b : bufs) {
        freeBuffer(b);
      }
    } finally {
      unsetBuffers();
    }
  }
  @Override
  public final ByteBufferIndexInput clone() {
    final ByteBufferIndexInput clone = buildSlice((String) null, 0L, this.length);
    try {
      clone.seek(getFilePointer());
    } catch (IOException ioe) {
      throw new AssertionError(ioe);
    }

    return clone;
  }
 @Override
 public void seek(long pos) throws IOException {
   // necessary in case offset != 0 and pos < 0, but pos >= -offset
   if (pos < 0L) {
     throw new IllegalArgumentException("Seeking to negative position: " + this);
   }
   super.seek(pos + offset);
 }
  /** Builds the actual sliced IndexInput (may apply extra offset in subclasses). * */
  protected ByteBufferIndexInput buildSlice(String sliceDescription, long offset, long length) {
    if (buffers == null) {
      throw new AlreadyClosedException("Already closed: " + this);
    }

    final ByteBuffer newBuffers[] = buildSlice(buffers, offset, length);
    final String newResourceDescription =
        (sliceDescription == null)
            ? toString()
            : (toString() + " [slice=" + sliceDescription + "]");
    final int ofs = (int) (offset & chunkSizeMask);

    final ByteBufferIndexInput clone =
        newCloneInstance(newResourceDescription, newBuffers, ofs, length);
    clone.isClone = true;

    // register the new clone in our clone list to clean it up on closing:
    if (clones != null) {
      this.clones.put(clone, Boolean.TRUE);
    }

    return clone;
  }