Source:
<code>public CloseableIterator<Entry<KeyType>> iterator() { if (!initialized) { throw new ISE("Grouper should be initialized first"); } return new CloseableIterator<Entry<KeyType>>() { { final long startLoopAt = System.currentTimeMillis(); while ((curWriteIndex == -1 || curWriteIndex == 0) && !finished && !Thread.interrupted()) { if (System.currentTimeMillis() - startLoopAt > timeoutMs) { throw new RuntimeException(new TimeoutException()); } } nextReadIndex = 0; } @Override public boolean hasNext() { return !finished || remaining() > 0; } private int remaining() { if (curWriteIndex >= nextReadIndex) { <START> return curWriteIndex - nextReadIndex; <END> } else { return (maxSlotNum - nextReadIndex) + curWriteIndex; } } @Override public Entry<KeyType> next() { if (!hasNext()) { throw new NoSuchElementException(); } final int recordOffset = recordSize * nextReadIndex; final KeyType key = keySerde.fromByteBuffer(buffer, recordOffset); final Object[] values = new Object[aggregators.length]; for (int i = 0; i < aggregators.length; i++) { values[i] = aggregators[i].get(buffer, recordOffset + aggregatorOffsets[i]); } final int toBeUpdated = nextReadIndex == maxSlotNum - 1 ? 0 : nextReadIndex + 1; if (!finished) { final long startLoopAt = System.currentTimeMillis(); while (toBeUpdated == curWriteIndex && !finished && !Thread.interrupted()) { if (System.currentTimeMillis() - startLoopAt > timeoutMs) { throw new RuntimeException(new TimeoutException()); } } } nextReadIndex = toBeUpdated; return new Entry<>(key, values); } @Override public void close() throws IOException { } }; }</code><technical_language>curWriteIndex wrap previous line remaining() accidently return negative number</technical_language>

Target:
public CloseableIterator<Entry<KeyType>> iterator() { if (!initialized) { throw new ISE("Grouper should be initialized first"); } return new CloseableIterator<Entry<KeyType>>() { { increaseReadIndexTo(0); } @Override public boolean hasNext() { return !finished || remaining() > 0; } private int remaining() { if (curWriteIndex >= nextReadIndex) { return curWriteIndex - nextReadIndex; } else { return (maxNumSlots - nextReadIndex) + curWriteIndex; } } @Override public Entry<KeyType> next() { if (!hasNext()) { throw new NoSuchElementException(); } final int recordOffset = recordSize * nextReadIndex; final KeyType key = keySerde.fromByteBuffer(buffer, recordOffset); final Object[] values = new Object[aggregators.length]; for (int i = 0; i < aggregators.length; i++) { values[i] = aggregators[i].get(buffer, recordOffset + aggregatorOffsets[i]); } final int targetIndex = nextReadIndex == maxNumSlots - 1 ? 0 : nextReadIndex + 1; increaseReadIndexTo(targetIndex); return new Entry<>(key, values); } private void increaseReadIndexTo(int target) { final long startAtNs = System.nanoTime(); final long queryTimeoutAtNs = getQueryTimeoutAtNs(startAtNs); final long spinTimeoutAtNs = startAtNs + SPIN_FOR_TIMEOUT_THRESHOLD_NS; long timeoutNs = queryTimeoutAtNs - startAtNs; long spinTimeoutNs = SPIN_FOR_TIMEOUT_THRESHOLD_NS; while ((curWriteIndex == -1 || target == curWriteIndex) && !finished && !Thread.currentThread().isInterrupted()) { if (timeoutNs <= 0L) { throw new RuntimeException(new TimeoutException()); } if (spinTimeoutNs <= 0L) { Thread.yield(); } long now = System.nanoTime(); timeoutNs = queryTimeoutAtNs - now; spinTimeoutNs = spinTimeoutAtNs - now; } nextReadIndex = target; } @Override public void close() throws IOException { } }; }