private EntitiesOfTypeIterator(
     @NotNull final EntitiesOfTypeRangeIterable iterable, @NotNull final Cursor index) {
   super(iterable);
   setCursor(index);
   final ByteIterable key = LongBinding.longToCompressedEntry(min);
   checkHasNext(getCursor().getSearchKeyRange(key) != null);
 }
 @Override
 @Nullable
 public EntityId nextIdImpl() {
   if (hasNextImpl()) {
     explain(getType());
     final Cursor cursor = getCursor();
     final long localId = LongBinding.compressedEntryToLong(cursor.getKey());
     final EntityId result = new PersistentEntityId(entityTypeId, localId);
     checkHasNext(cursor.getNext());
     return result;
   }
   return null;
 }
 @Override
 protected long countImpl(@NotNull final PersistentStoreTransaction txn) {
   final Cursor cursor = openCursor(txn);
   if (cursor == null) {
     return 0;
   }
   try {
     final ByteIterable key = LongBinding.longToCompressedEntry(min);
     long result = 0;
     boolean success = cursor.getSearchKeyRange(key) != null;
     while (success) {
       if (max > LongBinding.compressedEntryToLong(cursor.getKey())) {
         break;
       }
       result++;
       success = cursor.getNextNoDup();
     }
     return result;
   } finally {
     cursor.close();
   }
 }
  public static int binarySearch(
      @NotNull final IByteIterableComparator comparator,
      final ByteIterable key,
      int low,
      int high,
      final int bytesPerLong,
      Log log,
      long startAddress) {
    final LogCache cache = log.cache;
    final int pageSize = log.getCachePageSize();
    final int mask = pageSize - 1; // page size is always a power of 2
    long leftAddress = -1L;
    byte[] leftPage = null;
    long rightAddress = -1L;
    byte[] rightPage = null;
    final BinarySearchIterator it = new BinarySearchIterator(pageSize);

    while (low <= high) {
      final int mid = (low + high + 1) >>> 1;
      final long midAddress = startAddress + (mid * bytesPerLong);
      it.offset = ((int) midAddress) & mask;
      it.address = midAddress - it.offset;
      boolean loaded = false;
      if (it.address == leftAddress) {
        it.page = leftPage;
      } else if (it.address == rightAddress) {
        it.page = rightPage;
      } else {
        it.page = cache.getPage(log, it.address).getBytesUnsafe();
        loaded = true;
      }

      final int cmp;
      final long address;
      final byte[] page;

      if (pageSize - it.offset < bytesPerLong) {
        final long nextAddress = (address = it.address) + pageSize;
        if (rightAddress == nextAddress) {
          it.nextPage = rightPage;
        } else {
          it.nextPage = cache.getPage(log, nextAddress).getBytesUnsafe();
          loaded = true;
        }
        page = it.page;
        cmp =
            comparator.compare(LongBinding.entryToUnsignedLong(it.asCompound(), bytesPerLong), key);
      } else {
        cmp = comparator.compare(LongBinding.entryToUnsignedLong(it, bytesPerLong), key);
        page = it.page;
        address = it.address;
      }

      if (cmp < 0) {
        // left < right
        low = mid + 1;
        if (loaded) {
          leftAddress = it.address;
          leftPage = it.page;
        }
      } else if (cmp > 0) {
        // left > right
        high = mid - 1;
        if (loaded) {
          rightAddress = address;
          rightPage = page;
        }
      } else {
        return mid; // key found
      }
    }
    return -(low + 1); // key not found.
  }
 private void checkHasNext(final boolean success) {
   hasNext = success && max >= LongBinding.compressedEntryToLong(getCursor().getKey());
 }
 @Override
 public long nextLong(final int length) {
   final long result = LongBinding.entryToUnsignedLong(page, offset, length);
   offset += length;
   return result;
 }