Skip to content

Commit 8d02a62

Browse files
authored
Fix Streamable range traversal starts and maximum endpoints (#8337)
Use the same index-based step for asynchronous and enumerable consumption. Comparing the next index to the exclusive endpoint supports ranges ending at the primitive maximum without changing indexed collection. Generated-by: OpenAI Codex
1 parent dc37755 commit 8d02a62

3 files changed

Lines changed: 108 additions & 16 deletions

File tree

‎src/main/java/io/reactivex/rxjava4/internal/operators/streamable/StreamableRange.java‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,17 @@ static final class RangeStreamer<T>
3636

3737
int current;
3838

39+
int index;
40+
3941
RangeStreamer(int start, int end) {
4042
this.start = start;
41-
this.current = start - 1;
43+
this.index = start;
4244
this.end = end;
4345
}
4446

4547
@Override
4648
public @NonNull CompletionStage<Boolean> next() {
47-
if (++current >= end) {
48-
return NEXT_FALSE;
49-
}
50-
return NEXT_TRUE;
49+
return nextSync() ? NEXT_TRUE : NEXT_FALSE;
5150
}
5251

5352
@Override
@@ -57,6 +56,7 @@ static final class RangeStreamer<T>
5756

5857
@Override
5958
public @NonNull CompletionStage<Void> finish() {
59+
index = end;
6060
current = end;
6161
return FINISHED;
6262
}
@@ -72,10 +72,13 @@ public long limit() {
7272
}
7373

7474
@Override
75-
public boolean nextSync() throws Throwable {
76-
if (++current >= end) {
75+
public boolean nextSync() {
76+
int i = index;
77+
if (i == end) {
7778
return false;
7879
}
80+
current = i;
81+
index = i + 1;
7982
return true;
8083
}
8184
}

‎src/main/java/io/reactivex/rxjava4/internal/operators/streamable/StreamableRangeLong.java‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ static final class RangeLongStreamer<T>
4646

4747
@Override
4848
public @NonNull CompletionStage<Boolean> next() {
49-
long i = index;
50-
if (i >= end) {
51-
return NEXT_FALSE;
52-
}
53-
current = i;
54-
index = i + 1;
55-
return NEXT_TRUE;
49+
return nextSync() ? NEXT_TRUE : NEXT_FALSE;
5650
}
5751

5852
@Override
@@ -78,10 +72,13 @@ public long limit() {
7872
}
7973

8074
@Override
81-
public boolean nextSync() throws Throwable {
82-
if (++current >= end) {
75+
public boolean nextSync() {
76+
long i = index;
77+
if (i == end) {
8378
return false;
8479
}
80+
current = i;
81+
index = i + 1;
8582
return true;
8683
}
8784
}

‎src/test/java/io/reactivex/rxjava4/internal/operators/streamable/StreamableRangeTest.java‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,96 @@ public void longEnumerated() throws Throwable {
124124
.awaitDone(5, TimeUnit.SECONDS)
125125
.assertResult(List.of(1L, 2L, 3L));
126126
}
127+
128+
@Test
129+
public void maximum() throws Throwable {
130+
Streamable.range(Integer.MAX_VALUE - 1, 2)
131+
.test()
132+
.awaitDone(5, TimeUnit.SECONDS)
133+
.assertResult(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
134+
}
135+
136+
@Test
137+
public void maximumLong() throws Throwable {
138+
Streamable.rangeLong(Long.MAX_VALUE - 1, 2)
139+
.test()
140+
.awaitDone(5, TimeUnit.SECONDS)
141+
.assertResult(Long.MAX_VALUE - 1, Long.MAX_VALUE);
142+
}
143+
144+
@Test
145+
public void minimum() throws Throwable {
146+
Streamable.range(Integer.MIN_VALUE, 2)
147+
.test()
148+
.awaitDone(5, TimeUnit.SECONDS)
149+
.assertResult(Integer.MIN_VALUE, Integer.MIN_VALUE + 1);
150+
}
151+
152+
@Test
153+
public void minimumLong() throws Throwable {
154+
Streamable.rangeLong(Long.MIN_VALUE, 2)
155+
.test()
156+
.awaitDone(5, TimeUnit.SECONDS)
157+
.assertResult(Long.MIN_VALUE, Long.MIN_VALUE + 1);
158+
}
159+
160+
@Test
161+
public void maximumEnumerated() throws Throwable {
162+
Streamable.range(Integer.MAX_VALUE - 1, 2)
163+
.filter(_ -> true)
164+
.collect(Collectors.toList())
165+
.test()
166+
.awaitDone(5, TimeUnit.SECONDS)
167+
.assertResult(List.of(Integer.MAX_VALUE - 1, Integer.MAX_VALUE));
168+
}
169+
170+
@Test
171+
public void maximumEnumeratedLong() throws Throwable {
172+
Streamable.rangeLong(Long.MAX_VALUE - 1, 2)
173+
.filter(_ -> true)
174+
.collect(Collectors.toList())
175+
.test()
176+
.awaitDone(5, TimeUnit.SECONDS)
177+
.assertResult(List.of(Long.MAX_VALUE - 1, Long.MAX_VALUE));
178+
}
179+
180+
@Test
181+
public void minimumEnumeratedLong() throws Throwable {
182+
Streamable.rangeLong(Long.MIN_VALUE, 2)
183+
.filter(_ -> true)
184+
.collect(Collectors.toList())
185+
.test()
186+
.awaitDone(5, TimeUnit.SECONDS)
187+
.assertResult(List.of(Long.MIN_VALUE, Long.MIN_VALUE + 1));
188+
}
189+
190+
@Test
191+
public void longEnumeratedFromZero() throws Throwable {
192+
Streamable.rangeLong(0, 2)
193+
.filter(_ -> true)
194+
.collect(Collectors.toList())
195+
.test()
196+
.awaitDone(5, TimeUnit.SECONDS)
197+
.assertResult(List.of(0L, 1L));
198+
}
199+
200+
@Test
201+
public void longEnumeratedFromNegative() throws Throwable {
202+
Streamable.rangeLong(-2, 2)
203+
.filter(_ -> true)
204+
.collect(Collectors.toList())
205+
.test()
206+
.awaitDone(5, TimeUnit.SECONDS)
207+
.assertResult(List.of(-2L, -1L));
208+
}
209+
210+
@Test
211+
public void longEnumeratedFromOffset() throws Throwable {
212+
Streamable.rangeLong(5, 2)
213+
.filter(_ -> true)
214+
.collect(Collectors.toList())
215+
.test()
216+
.awaitDone(5, TimeUnit.SECONDS)
217+
.assertResult(List.of(5L, 6L));
218+
}
127219
}

0 commit comments

Comments
 (0)