private int splitNode(int parentAddress) throws IOException {
      final boolean indexLeaf = isIndexLeaf();

      if (doSanityCheck) {
        assert isFull();
        dump("before split:" + indexLeaf);
      }

      final boolean hashedLeaf = isHashedLeaf();
      final short recordCount = getChildrenCount();
      BtreeIndexNodeView parent = null;
      HashLeafData hashLeafData;

      try {
        if (parentAddress != 0) {
          parent = new BtreeIndexNodeView(parentAddress, true, null);
        }

        short maxIndex = (short)(getMaxChildrenCount() / 2);

        try (BtreeIndexNodeView newIndexNode = new BtreeIndexNodeView(nextPage(), true, null)) {
          syncWithStore(); // next page can cause ByteBuffer to be invalidated!
          if (parent != null) parent.syncWithStore();
          root.syncWithStore();

          newIndexNode.setIndexLeaf(indexLeaf); // newIndexNode becomes dirty

          int nextPage = getNextPage();
          setNextPage(newIndexNode.address);
          newIndexNode.setNextPage(nextPage);

          int medianKey;

          if (indexLeaf && hashedLeaf) {
            hashLeafData = new HashLeafData(this, recordCount);
            final int[] keys = hashLeafData.keys;

            hashLeafData.clean();

            final Int2IntMap map = hashLeafData.values;

            final int avg = keys.length / 2;
            medianKey = keys[avg];
            --hashedPagesCount;
            setChildrenCount((short)0);  // this node becomes dirty
            newIndexNode.setChildrenCount((short)0);

            for (int i = 0; i < avg; ++i) {
              int key = keys[i];
              insert(key, map.get(key));
              key = keys[avg + i];
              newIndexNode.insert(key, map.get(key));
            }
          }
          else {
            short recordCountInNewNode = (short)(recordCount - maxIndex);
            newIndexNode.setChildrenCount(recordCountInNewNode); // newIndexNode node becomes dirty

            ByteBuffer buffer = getBytes(indexToOffset(maxIndex), recordCountInNewNode * INTERIOR_SIZE);
            newIndexNode.putBytes(newIndexNode.indexToOffset(0), buffer);
            if (indexLeaf) {
              medianKey = newIndexNode.keyAt(0);
            }
            else {
              newIndexNode.setAddressAt(recordCountInNewNode, addressAt(recordCount));
              --maxIndex;
              medianKey = keyAt(maxIndex);     // key count is odd (since children count is even) and middle key goes to parent
            }
            setChildrenCount(maxIndex); // "this" node becomes dirty
          }

          if (parent != null) {
            if (doSanityCheck) {
              int medianKeyInParent = parent.search(medianKey);
              int ourKey = keyAt(0);
              int ourKeyInParent = parent.search(ourKey);
              parent.dump("About to insert " +
                          medianKey +
                          "," +
                          newIndexNode.address +
                          "," +
                          medianKeyInParent +
                          " our key " +
                          ourKey +
                          ", " +
                          ourKeyInParent);

              assert medianKeyInParent < 0;
              boolean b = !parent.isFull();
              assert b;
            }

            parent.insert(medianKey, -newIndexNode.address);

            if (doSanityCheck) {
              parent.dump("After modifying parent");
              int search = parent.search(medianKey);
              assert search >= 0;
              boolean b = parent.addressAt(search + 1) == -newIndexNode.address;
              assert b;

              dump("old node after split:");
              newIndexNode.dump("new node after split:");
            }
          }
          else {
            if (doSanityCheck) {
              root.getNodeView().dump("Splitting root:" + medianKey);
            }

            int newRootAddress = nextPage();
            newIndexNode.syncWithStore();
            syncWithStore();

            if (doSanityCheck) {
              System.out.println("Pages:" + pagesCount + ", elements:" + count + ", average:" + (height + 1));
            }
            root.setAddress(newRootAddress);
            parentAddress = newRootAddress;

            BtreeIndexNodeView rootNodeView = root.getNodeView();
            rootNodeView.setChildrenCount((short)1); // root becomes dirty
            rootNodeView.setKeyAt(0, medianKey);
            rootNodeView.setAddressAt(0, -address);
            rootNodeView.setAddressAt(1, -newIndexNode.address);

            if (doSanityCheck) {
              rootNodeView.dump("New root");
              dump("First child");
              newIndexNode.dump("Second child");
            }
          }
        }
      }
      finally {
        if (parent != null) {
          parent.close();
        }
      }

      return parentAddress;
    }
private int splitNode(int parentAddress)