@Override
  public int fromStream(final byte[] content, int offset) {
    final int size = OIntegerSerializer.INSTANCE.deserializeNative(content, offset);
    offset += OIntegerSerializer.INT_SIZE;

    dirtyPages = new HashSet<ODirtyPage>();

    for (int i = 0; i < size; i++) {
      long pageIndex = OLongSerializer.INSTANCE.deserializeNative(content, offset);
      offset += OLongSerializer.LONG_SIZE;

      String fileName = stringSerializer.deserializeNativeObject(content, offset);
      offset += stringSerializer.getObjectSize(fileName);

      long segment = OLongSerializer.INSTANCE.deserializeNative(content, offset);
      offset += OLongSerializer.LONG_SIZE;

      long position = OLongSerializer.INSTANCE.deserializeNative(content, offset);
      offset += OLongSerializer.LONG_SIZE;

      dirtyPages.add(
          new ODirtyPage(fileName, pageIndex, new OLogSequenceNumber(segment, position)));
    }

    return offset;
  }
  @Override
  public int serializedSize() {
    int size = OIntegerSerializer.INT_SIZE;

    for (ODirtyPage dirtyPage : dirtyPages) {
      size += 3 * OLongSerializer.LONG_SIZE;
      size += stringSerializer.getObjectSize(dirtyPage.getFileName());
    }

    return size;
  }
    public <K> void serializeChanges(
        Map<K, Change> changes, OBinarySerializer<K> keySerializer, byte[] stream, int offset) {
      OIntegerSerializer.INSTANCE.serializeLiteral(changes.size(), stream, offset);
      offset += OIntegerSerializer.INT_SIZE;

      for (Map.Entry<K, Change> entry : changes.entrySet()) {
        K key = entry.getKey();
        if (((OIdentifiable) key).getIdentity().isTemporary())
          key = ((OIdentifiable) key).getRecord();

        keySerializer.serialize(key, stream, offset);
        offset += keySerializer.getObjectSize(key);

        offset += entry.getValue().serialize(stream, offset);
      }
    }
  @Override
  public int toStream(final byte[] content, int offset) {
    OIntegerSerializer.INSTANCE.serializeNative(dirtyPages.size(), content, offset);
    offset += OIntegerSerializer.INT_SIZE;

    for (ODirtyPage dirtyPage : dirtyPages) {
      OLongSerializer.INSTANCE.serializeNative(dirtyPage.getPageIndex(), content, offset);
      offset += OLongSerializer.LONG_SIZE;

      stringSerializer.serializeNativeObject(dirtyPage.getFileName(), content, offset);
      offset += stringSerializer.getObjectSize(dirtyPage.getFileName());

      OLongSerializer.INSTANCE.serializeNative(dirtyPage.getLsn().getSegment(), content, offset);
      offset += OLongSerializer.LONG_SIZE;

      OLongSerializer.INSTANCE.serializeNative(dirtyPage.getLsn().getPosition(), content, offset);
      offset += OLongSerializer.LONG_SIZE;
    }

    return offset;
  }
  public boolean addEntry(int index, SBTreeEntry<K, V> treeEntry, boolean updateNeighbors)
      throws IOException {
    final int keySize = keySerializer.getObjectSize(treeEntry.key);
    int valueSize = 0;
    int entrySize = keySize;

    if (isLeaf) {
      assert valueSerializer.isFixedLength();
      valueSize = valueSerializer.getFixedLength();

      entrySize += valueSize;

      checkEntreeSize(entrySize);
    } else entrySize += 2 * (OLongSerializer.LONG_SIZE + OIntegerSerializer.INT_SIZE);

    int size = size();
    int freePointer = getIntValue(offset + FREE_POINTER_OFFSET);
    if (freePointer - entrySize
        < (size + 1) * OIntegerSerializer.INT_SIZE + POSITIONS_ARRAY_OFFSET) {
      if (size > 1) return false;
      else
        throw new OSBTreeException(
            "Entry size ('key + value') is more than is more than allowed "
                + (freePointer - 2 * OIntegerSerializer.INT_SIZE + POSITIONS_ARRAY_OFFSET)
                + " bytes, either increase page size using '"
                + OGlobalConfiguration.SBTREEBONSAI_BUCKET_SIZE.getKey()
                + "' parameter, or decrease 'key + value' size.");
    }

    if (index <= size - 1) {
      moveData(
          offset + POSITIONS_ARRAY_OFFSET + index * OIntegerSerializer.INT_SIZE,
          offset + POSITIONS_ARRAY_OFFSET + (index + 1) * OIntegerSerializer.INT_SIZE,
          (size - index) * OIntegerSerializer.INT_SIZE);
    }

    freePointer -= entrySize;

    setIntValue(offset + FREE_POINTER_OFFSET, freePointer);
    setIntValue(offset + POSITIONS_ARRAY_OFFSET + index * OIntegerSerializer.INT_SIZE, freePointer);
    setIntValue(offset + SIZE_OFFSET, size + 1);

    if (isLeaf) {
      byte[] serializedKey = new byte[keySize];
      keySerializer.serializeNativeObject(treeEntry.key, serializedKey, 0);

      setBinaryValue(offset + freePointer, serializedKey);
      freePointer += keySize;

      byte[] serializedValue = new byte[valueSize];
      valueSerializer.serializeNativeObject(treeEntry.value, serializedValue, 0);
      setBinaryValue(offset + freePointer, serializedValue);

    } else {
      setBucketPointer(offset + freePointer, treeEntry.leftChild);
      freePointer += OLongSerializer.LONG_SIZE + OIntegerSerializer.INT_SIZE;

      setBucketPointer(offset + freePointer, treeEntry.rightChild);
      freePointer += OLongSerializer.LONG_SIZE + OIntegerSerializer.INT_SIZE;

      byte[] serializedKey = new byte[keySize];
      keySerializer.serializeNativeObject(treeEntry.key, serializedKey, 0);
      setBinaryValue(offset + freePointer, serializedKey);

      size++;

      if (updateNeighbors && size > 1) {
        if (index < size - 1) {
          final int nextEntryPosition =
              getIntValue(
                  offset + POSITIONS_ARRAY_OFFSET + (index + 1) * OIntegerSerializer.INT_SIZE);
          setBucketPointer(offset + nextEntryPosition, treeEntry.rightChild);
        }

        if (index > 0) {
          final int prevEntryPosition =
              getIntValue(
                  offset + POSITIONS_ARRAY_OFFSET + (index - 1) * OIntegerSerializer.INT_SIZE);
          setBucketPointer(
              offset + prevEntryPosition + OLongSerializer.LONG_SIZE + OIntegerSerializer.INT_SIZE,
              treeEntry.leftChild);
        }
      }
    }

    return true;
  }