@Override
  public <E extends Exception> int forEach(@NotNull Processor<E> processor) throws IOException, E {
    long storageLength = actualLength();
    int currentId = offsetToId(recordsStartOffset());
    for (int recordNo = 0; ; recordNo++) {
      long recordOffset = idToOffset(currentId);
      Page page = storage.pageByOffset(recordOffset);
      int offsetOnPage = storage.toOffsetInPage(recordOffset);
      ByteBuffer buffer = page.rawPageBuffer();
      RecordLayout recordLayout = RecordLayout.recordLayout(buffer, offsetOnPage);
      byte recordType = recordLayout.recordType();
      int recordCapacity = recordLayout.capacity(buffer, offsetOnPage);
      switch (recordType) {
        case RecordLayout.RECORD_TYPE_ACTUAL, RecordLayout.RECORD_TYPE_MOVED -> {
          int headerSize = recordLayout.headerSize();
          boolean isActual = recordType == RecordLayout.RECORD_TYPE_ACTUAL;
          int recordActualLength = isActual ? recordLayout.length(buffer, offsetOnPage) : -1;
          ByteBuffer slice = isActual ?
                             buffer.slice(offsetOnPage + headerSize, recordActualLength)
                               .asReadOnlyBuffer()
                               .order(buffer.order()) :
                             buffer.slice(offsetOnPage + headerSize, 0)
                               .asReadOnlyBuffer()
                               .order(buffer.order());
          boolean ok = processor.processRecord(currentId, recordCapacity, recordActualLength, slice);
          if (!ok) {
            return recordNo + 1;
          }
        }
        default -> {
          //just skip for now
        }
      }

      long nextRecordOffset = nextRecordOffset(recordOffset, recordLayout, recordCapacity);
      if (nextRecordOffset >= storageLength) {
        return recordNo;
      }

      currentId = offsetToId(nextRecordOffset);
    }
  }
@Override
  public <E extends Exception> int forEach(@NotNull Processor<E> processor)