docs: private IndexNode copyWith(byte[] key, int file, int offset, int size, OverwriteHook overwriteHook, RecordChange recordChange) throws IOException {
        if (leafNodes == null) throw new IllegalArgumentException();
        byte[] newPrefix;
        byte[][] newKeyParts;
        LeafNode[] newLeafNodes;
        if (leafNodes.length == 0) {
           overwriteHook.setOverwritten(false, -1, -1);
           if (overwriteHook.check(-1, -1)) {
              return new IndexNode(segment, prefix, keyParts, new LeafNode[]{ new LeafNode(file, offset, (short) 1)});
           } else {
              segment.getCompactor().free(file, size);
              return this;
           }
        }
  
        int insertPart = getInsertionPoint(key);
        LeafNode oldLeafNode = leafNodes[insertPart];
  
        short numRecords = oldLeafNode.numRecords;
        switch (recordChange) {
           case INCREASE:
           case INCREASE_FOR_OLD:
              if (numRecords == Short.MAX_VALUE) {
                 throw new IllegalStateException("Too many records for this key (short overflow)");
              }
              numRecords++;
              break;
           case MOVE:
              break;
           case DECREASE:
              numRecords--;
              break;
        }
  
        EntryRecord hak;
        try {
           hak = oldLeafNode.loadHeaderAndKey(segment.getFileProvider());
        } catch (IndexNodeOutdatedException e) {
           throw new IllegalStateException("Index cannot be outdated for segment updater thread", e);
        }
        int keyComp = compare(hak.getKey(), key);
        if (keyComp == 0) {
           if (numRecords > 0) {
              if (overwriteHook.check(oldLeafNode.file, oldLeafNode.offset)) {
                 newPrefix = prefix;
                 newKeyParts = keyParts;
                 newLeafNodes = new LeafNode[leafNodes.length];
                 System.arraycopy(leafNodes, 0, newLeafNodes, 0, leafNodes.length);
                 // Do not update the file and offset for DROPPED IndexRequests
                 if (recordChange == RecordChange.INCREASE || recordChange == RecordChange.MOVE) {
                    if (trace) {
                       log.trace(String.format("Overwriting %d:%d with %d:%d (%d)",
                             oldLeafNode.file, oldLeafNode.offset, file, offset, numRecords));
                    }
                    newLeafNodes[insertPart] = new LeafNode(file, offset, numRecords);
                    segment.getCompactor().free(oldLeafNode.file, hak.getHeader().totalLength());
                 } else {
                    if (trace) {
                       log.trace(String.format("Updating num records for %d:%d to %d", oldLeafNode.file, oldLeafNode.offset, numRecords));
                    }
                    newLeafNodes[insertPart] = new LeafNode(oldLeafNode.file, oldLeafNode.offset, numRecords);
                 }
                 overwriteHook.setOverwritten(true, oldLeafNode.file, oldLeafNode.offset);
              } else {
                 overwriteHook.setOverwritten(false, -1, -1);
                 segment.getCompactor().free(file, size);
                 return this;
              }
           } else {
              overwriteHook.setOverwritten(true, oldLeafNode.file, oldLeafNode.offset);
              if (keyParts.length <= 1) {
                 newPrefix = Util.EMPTY_BYTE_ARRAY;
                 newKeyParts = Util.EMPTY_BYTE_ARRAY_ARRAY;
              } else {
                 newPrefix = prefix;
                 newKeyParts = new byte[keyParts.length - 1][];
                 if (insertPart == keyParts.length) {
                    System.arraycopy(keyParts, 0, newKeyParts, 0, newKeyParts.length);
                 } else {
                    System.arraycopy(keyParts, 0, newKeyParts, 0, insertPart);
                    System.arraycopy(keyParts, insertPart + 1, newKeyParts, insertPart, newKeyParts.length - insertPart);
                 }
              }
              if (leafNodes.length > 0) {
                 newLeafNodes = new LeafNode[leafNodes.length - 1];
                 System.arraycopy(leafNodes, 0, newLeafNodes, 0, insertPart);
                 System.arraycopy(leafNodes, insertPart + 1, newLeafNodes, insertPart, newLeafNodes.length - insertPart);
              } else {
                 newLeafNodes = leafNodes;
              }
              segment.getCompactor().free(oldLeafNode.file, hak.getHeader().totalLength());
           }
        } else {
           // IndexRequest cannot be MOVED or DROPPED when the key is not in the index
           assert recordChange == RecordChange.INCREASE;
           overwriteHook.setOverwritten(false, -1, -1);
           // We have to insert the record even if this is a delete request and the key was not found
           // because otherwise we would have incorrect numRecord count. Eventually, Compactor will
           // drop the tombstone and update index, removing this node
           if (keyParts.length == 0) {
              // TODO: we may use unnecessarily long keys here and the key is never shortened
              newPrefix = keyComp > 0 ? key : hak.getKey();
           } else {
              newPrefix = commonPrefix(prefix, key);
           }
           newKeyParts = new byte[keyParts.length + 1][];
           newLeafNodes = new LeafNode[leafNodes.length + 1];
           copyKeyParts(keyParts, 0, newKeyParts, 0, insertPart, prefix, newPrefix);
           copyKeyParts(keyParts, insertPart, newKeyParts, insertPart + 1, keyParts.length - insertPart, prefix, newPrefix);
           if (keyComp > 0) {
              newKeyParts[insertPart] = substring(key, newPrefix.length, keyComp);
              System.arraycopy(leafNodes, 0, newLeafNodes, 0, insertPart + 1);
              System.arraycopy(leafNodes, insertPart + 1, newLeafNodes, insertPart + 2, leafNodes.length - insertPart - 1);
              newLeafNodes[insertPart + 1] = new LeafNode(file, offset, (short) 1);
           } else {
              newKeyParts[insertPart] = substring(hak.getKey(), newPrefix.length, -keyComp);
              System.arraycopy(leafNodes, 0, newLeafNodes, 0, insertPart);
              System.arraycopy(leafNodes, insertPart, newLeafNodes, insertPart + 1, leafNodes.length - insertPart);
              newLeafNodes[insertPart] = new LeafNode(file, offset, (short) 1);
           }
        }
        return new IndexNode(segment, newPrefix, newKeyParts, newLeafNodes);
     }