• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

amaembo / streamex / #677

02 Nov 2024 08:50AM UTC coverage: 99.673%. Remained the same
#677

push

amaembo
Optimize imports

5786 of 5805 relevant lines covered (99.67%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.6
/src/main/java/one/util/streamex/PairSpliterator.java
1
/*
2
 * Copyright 2015, 2024 StreamEx contributors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package one.util.streamex;
17

18
import java.util.Spliterator;
19
import java.util.function.*;
20

21
import static one.util.streamex.Internals.*;
22

23
/**
24
 * @author Tagir Valeev
25
 */
26
/* package */abstract class PairSpliterator<T, S extends Spliterator<T>, R, SS extends PairSpliterator<T, S, R, SS>>
27
        extends CloneableSpliterator<R, SS> {
28
    static final int MODE_PAIRS = 0;
29
    static final int MODE_MAP_FIRST = 1;
30
    static final int MODE_MAP_LAST = 2;
31
    static final int MODE_MAP_FIRST_OR_ELSE = 3;
32
    static final int MODE_MAP_LAST_OR_ELSE = 4;
33
    
34
    static final Sink<?> EMPTY = new Sink<>(null);
1✔
35
    // Common lock for all the derived spliterators
36
    final Object lock = new Object();
1✔
37
    final int mode;
38
    S source;
39
    @SuppressWarnings("unchecked")
1✔
40
    Sink<T> left = (Sink<T>) EMPTY;
41
    @SuppressWarnings("unchecked")
1✔
42
    Sink<T> right = (Sink<T>) EMPTY;
43

44
    static final class Sink<T> {
45
        Sink<T> other;
46
        private T payload = none();
1✔
47
        private final Object lock;
48

49
        Sink(Object lock) {
1✔
50
            this.lock = lock;
1✔
51
        }
1✔
52

53
        boolean push(T payload, BiConsumer<T, T> fn, boolean isLeft) {
54
            if (lock == null)
1✔
55
                return false;
1✔
56
            T otherPayload;
57
            synchronized (lock) {
1✔
58
                Sink<T> that = other;
1✔
59
                if (that == null)
1✔
60
                    return false;
1✔
61
                otherPayload = that.payload;
1✔
62
                if (otherPayload == NONE) {
1✔
63
                    this.payload = payload;
1✔
64
                    return false;
1✔
65
                }
66
                other = null;
1✔
67
                that.clear();
1✔
68
            }
1✔
69
            if (isLeft)
1✔
70
                fn.accept(payload, otherPayload);
1✔
71
            else
72
                fn.accept(otherPayload, payload);
1✔
73
            return true;
1✔
74
        }
75

76
        boolean connect(Sink<T> right, BiConsumer<T, T> fn) {
77
            if (lock == null)
1✔
78
                return false;
1✔
79
            T a, b;
80
            synchronized (lock) {
1✔
81
                Sink<T> leftLeft = this.other;
1✔
82
                Sink<T> rightRight = right.other;
1✔
83
                if (leftLeft == null || rightRight == null) {
1✔
84
                    if (rightRight != null)
1✔
85
                        rightRight.clear();
×
86
                    if (leftLeft != null)
1✔
87
                        leftLeft.clear();
1✔
88
                    return false;
1✔
89
                }
90
                rightRight.other = leftLeft;
1✔
91
                leftLeft.other = rightRight;
1✔
92
                if (leftLeft.payload == NONE || rightRight.payload == NONE)
1✔
93
                    return false;
1✔
94
                a = leftLeft.payload;
1✔
95
                b = rightRight.payload;
1✔
96
                leftLeft.clear();
1✔
97
                rightRight.clear();
1✔
98
            }
1✔
99
            fn.accept(a, b);
1✔
100
            return true;
1✔
101
        }
102

103
        void clear() {
104
            other = null;
1✔
105
            payload = none();
1✔
106
        }
1✔
107
    }
108

109
    PairSpliterator(S source, int mode, T headTail) {
1✔
110
        this.source = source;
1✔
111
        this.mode = mode;
1✔
112
        if (mode != MODE_PAIRS) {
1✔
113
            Sink<T> sink = new Sink<>(this.lock);
1✔
114
            Sink<T> other = new Sink<>(this.lock);
1✔
115
            sink.other = other;
1✔
116
            other.other = sink;
1✔
117
            other.push(headTail, null, true);
1✔
118
            if (mode == MODE_MAP_FIRST || mode == MODE_MAP_FIRST_OR_ELSE)
1✔
119
                this.left = sink;
1✔
120
            else
121
                this.right = sink;
1✔
122
        }
123
    }
1✔
124
    
125
    @Override
126
    public long estimateSize() {
127
        long size = source.estimateSize();
1✔
128
        if (size == Long.MAX_VALUE || size == 0)
1✔
129
            return size;
1✔
130
        return size - 1;
1✔
131
    }
132

133
    @Override
134
    public int characteristics() {
135
        return source.characteristics()
1✔
136
            & ((left == EMPTY && right == EMPTY ? SIZED : 0) | CONCURRENT | IMMUTABLE | ORDERED);
1✔
137
    }
138

139
    @SuppressWarnings("unchecked")
140
    @Override
141
    public SS trySplit() {
142
        S prefixSource = (S) source.trySplit();
1✔
143
        if (prefixSource == null)
1✔
144
            return null;
1✔
145
        SS clone = doClone();
1✔
146
        Sink<T> left = new Sink<>(lock);
1✔
147
        Sink<T> right = new Sink<>(lock);
1✔
148
        clone.source = prefixSource;
1✔
149
        clone.right = right.other = left;
1✔
150
        this.left = left.other = right;
1✔
151
        return clone;
1✔
152
    }
153

154
    void finish(BiConsumer<T, T> fn, T cur) {
155
        Sink<T> r = right, l = left;
1✔
156
        right = left = null;
1✔
157
        if (l != null) {
1✔
158
            l.connect(r, fn);
1✔
159
        } else if (r != null) {
1✔
160
            r.push(cur, fn, true);
1✔
161
        }
162
    }
1✔
163

164
    static class PSOfRef<T, R> extends PairSpliterator<T, Spliterator<T>, R, PSOfRef<T, R>> implements
165
            Consumer<T>, TailSpliterator<R> {
166
        private static final Object HEAD_TAIL = new Object();
1✔
167

168
        private final BiFunction<? super T, ? super T, ? extends R> mapper;
169
        private T cur;
170

171
        PSOfRef(BiFunction<? super T, ? super T, ? extends R> mapper, Spliterator<T> source) {
172
            super(source, MODE_PAIRS, null);
1✔
173
            this.mapper = mapper;
1✔
174
        }
1✔
175

176
        // Must be called only if T == R
177
        @SuppressWarnings("unchecked")
178
        PSOfRef(Function<? super T, ? extends R> mapper, Spliterator<T> source, boolean first) {
179
            super(source, first ? MODE_MAP_FIRST : MODE_MAP_LAST, (T) HEAD_TAIL);
1✔
180
            BiFunction<? super T, ? super T, ?> m = first ?
1✔
181
                    ((a, b) -> a == HEAD_TAIL ? mapper.apply(b) : (T) b) :
1✔
182
                    ((a, b) -> b == HEAD_TAIL ? mapper.apply(a) : (T) a);
1✔
183
            this.mapper = (BiFunction<? super T, ? super T, ? extends R>) m;
1✔
184
        }
1✔
185

186
        @SuppressWarnings("unchecked")
187
        PSOfRef(Function<? super T, ? extends R> boundMapper, Function<? super T, ? extends R> elseMapper, Spliterator<T> source, boolean first) {
188
            super(source, first ? MODE_MAP_FIRST_OR_ELSE : MODE_MAP_LAST_OR_ELSE, (T) HEAD_TAIL);
1✔
189
            this.mapper = first ? 
1✔
190
                ((a, b) -> a == HEAD_TAIL ? boundMapper.apply(b) : elseMapper.apply(b)) :
1✔
191
                ((a, b) -> b == HEAD_TAIL ? boundMapper.apply(a) : elseMapper.apply(a));
1✔
192
        }
1✔
193

194
        @Override
195
        public void accept(T t) {
196
            cur = t;
1✔
197
        }
1✔
198

199
        private BiConsumer<T, T> fn(Consumer<? super R> action) {
200
            return (a, b) -> action.accept(mapper.apply(a, b));
1✔
201
        }
202

203
        @Override
204
        public boolean tryAdvance(Consumer<? super R> action) {
205
            Sink<T> l = left, r = right;
1✔
206
            if (l != null) {
1✔
207
                left = null;
1✔
208
                if (!source.tryAdvance(this)) {
1✔
209
                    right = null;
1✔
210
                    return l.connect(r, fn(action));
1✔
211
                }
212
                if (l.push(cur, fn(action), false))
1✔
213
                    return true;
1✔
214
            }
215
            T prev = cur;
1✔
216
            if (!source.tryAdvance(this)) {
1✔
217
                right = null;
1✔
218
                return r != null && r.push(prev, fn(action), true);
1✔
219
            }
220
            action.accept(mapper.apply(prev, cur));
1✔
221
            return true;
1✔
222
        }
223

224
        @Override
225
        public void forEachRemaining(Consumer<? super R> action) {
226
            BiConsumer<T, T> fn = fn(action);
1✔
227
            source.forEachRemaining(next -> {
1✔
228
                if (left != null) {
1✔
229
                    left.push(cur = next, fn, false);
1✔
230
                    left = null;
1✔
231
                } else {
232
                    action.accept(mapper.apply(cur, cur = next));
1✔
233
                }
234
            });
1✔
235
            finish(fn, cur);
1✔
236
        }
1✔
237

238
        @Override
239
        public Spliterator<R> tryAdvanceOrTail(Consumer<? super R> action) {
240
            if (mode != MODE_MAP_FIRST || right != EMPTY) {
1✔
241
                return tryAdvance(action) ? this : null;
1✔
242
            }
243
            if (left != null) {
1✔
244
                Sink<T> l = left;
1✔
245
                left = null;
1✔
246
                source = TailSpliterator.tryAdvanceWithTail(source, this);
1✔
247
                if (source == null) {
1✔
248
                    right = null;
1✔
249
                    return null;
1✔
250
                }
251
                if (l.push(cur, fn(action), false))
1✔
252
                    return this;
1✔
253
            }
254
            @SuppressWarnings("unchecked")
255
            Spliterator<R> s = (Spliterator<R>) source;
1✔
256
            source = null;
1✔
257
            return s;
1✔
258
        }
259
        
260
        @Override
261
        public Spliterator<R> forEachOrTail(Consumer<? super R> action) {
262
            if (mode != MODE_MAP_FIRST || right != EMPTY) {
1✔
263
                forEachRemaining(action);
1✔
264
                return null;
1✔
265
            }
266
            while (true) {
267
                Spliterator<R> tail = tryAdvanceOrTail(action);
1✔
268
                if (tail != this)
1✔
269
                    return tail;
1✔
270
            }
1✔
271
        }
272
    }
273

274
    static final class PSOfInt extends PairSpliterator<Integer, Spliterator.OfInt, Integer, PSOfInt> implements
275
            Spliterator.OfInt, IntConsumer {
276
        private final IntBinaryOperator mapper;
277
        private final IntUnaryOperator unaryMapper;
278
        private int cur;
279

280
        PSOfInt(IntBinaryOperator mapper, IntUnaryOperator unaryMapper, Spliterator.OfInt source, int mode) {
281
            super(source, mode, null);
1✔
282
            this.mapper = mapper;
1✔
283
            this.unaryMapper = unaryMapper;
1✔
284
        }
1✔
285
        
286
        @Override
287
        public void accept(int t) {
288
            cur = t;
1✔
289
        }
1✔
290

291
        private BiConsumer<Integer, Integer> fn(IntConsumer action) {
292
            switch (mode) {
1✔
293
            case MODE_MAP_FIRST:
294
                return (a, b) -> action.accept(a == null ? unaryMapper.applyAsInt(b) : b);
1✔
295
            case MODE_MAP_LAST:
296
                return (a, b) -> action.accept(b == null ? unaryMapper.applyAsInt(a) : a);
1✔
297
            default:
298
                return (a, b) -> action.accept(mapper.applyAsInt(a, b));
1✔
299
            }
300
        }
301

302
        @Override
303
        public boolean tryAdvance(IntConsumer action) {
304
            Sink<Integer> l = left, r = right;
1✔
305
            if (l != null) {
1✔
306
                left = null;
1✔
307
                if (!source.tryAdvance(this)) {
1✔
308
                    right = null;
1✔
309
                    return l.connect(r, fn(action));
1✔
310
                }
311
                if (l.push(cur, fn(action), false))
1✔
312
                    return true;
1✔
313
            }
314
            int prev = cur;
1✔
315
            if (!source.tryAdvance(this)) {
1✔
316
                right = null;
1✔
317
                return r != null && r.push(prev, fn(action), true);
1✔
318
            }
319
            action.accept(mapper.applyAsInt(prev, cur));
1✔
320
            return true;
1✔
321
        }
322

323
        @Override
324
        public void forEachRemaining(IntConsumer action) {
325
            BiConsumer<Integer, Integer> fn = fn(action);
1✔
326
            source.forEachRemaining((int next) -> {
1✔
327
                if (left != null) {
1✔
328
                    left.push(cur = next, fn, false);
1✔
329
                    left = null;
1✔
330
                } else {
331
                    action.accept(mapper.applyAsInt(cur, cur = next));
1✔
332
                }
333
            });
1✔
334
            finish(fn, cur);
1✔
335
        }
1✔
336
    }
337

338
    static final class PSOfLong extends PairSpliterator<Long, Spliterator.OfLong, Long, PSOfLong> implements
339
            Spliterator.OfLong, LongConsumer {
340
        private final LongBinaryOperator mapper;
341
        private final LongUnaryOperator unaryMapper;
342
        private long cur;
343

344
        PSOfLong(LongBinaryOperator mapper, LongUnaryOperator unaryMapper, Spliterator.OfLong source, int mode) {
345
            super(source, mode, null);
1✔
346
            this.mapper = mapper;
1✔
347
            this.unaryMapper = unaryMapper;
1✔
348
        }
1✔
349

350
        @Override
351
        public void accept(long t) {
352
            cur = t;
1✔
353
        }
1✔
354

355
        private BiConsumer<Long, Long> fn(LongConsumer action) {
356
            switch (mode) {
1✔
357
            case MODE_MAP_FIRST:
358
                return (a, b) -> action.accept(a == null ? unaryMapper.applyAsLong(b) : b);
1✔
359
            case MODE_MAP_LAST:
360
                return (a, b) -> action.accept(b == null ? unaryMapper.applyAsLong(a) : a);
1✔
361
            default:
362
                return (a, b) -> action.accept(mapper.applyAsLong(a, b));
1✔
363
            }
364
        }
365

366
        @Override
367
        public boolean tryAdvance(LongConsumer action) {
368
            Sink<Long> l = left, r = right;
1✔
369
            if (l != null) {
1✔
370
                left = null;
1✔
371
                if (!source.tryAdvance(this)) {
1✔
372
                    right = null;
1✔
373
                    return l.connect(r, fn(action));
1✔
374
                }
375
                if (l.push(cur, fn(action), false))
1✔
376
                    return true;
1✔
377
            }
378
            long prev = cur;
1✔
379
            if (!source.tryAdvance(this)) {
1✔
380
                right = null;
1✔
381
                return r != null && r.push(prev, fn(action), true);
1✔
382
            }
383
            action.accept(mapper.applyAsLong(prev, cur));
1✔
384
            return true;
1✔
385
        }
386

387
        @Override
388
        public void forEachRemaining(LongConsumer action) {
389
            BiConsumer<Long, Long> fn = fn(action);
1✔
390
            source.forEachRemaining((long next) -> {
1✔
391
                if (left != null) {
1✔
392
                    left.push(cur = next, fn, false);
1✔
393
                    left = null;
1✔
394
                } else {
395
                    action.accept(mapper.applyAsLong(cur, cur = next));
1✔
396
                }
397
            });
1✔
398
            finish(fn, cur);
1✔
399
        }
1✔
400
    }
401

402
    static final class PSOfDouble extends PairSpliterator<Double, Spliterator.OfDouble, Double, PSOfDouble> implements
403
            Spliterator.OfDouble, DoubleConsumer {
404
        private final DoubleBinaryOperator mapper;
405
        private final DoubleUnaryOperator unaryMapper;
406
        private double cur;
407

408
        PSOfDouble(DoubleBinaryOperator mapper, DoubleUnaryOperator unaryMapper, Spliterator.OfDouble source, int mode) {
409
            super(source, mode, null);
1✔
410
            this.mapper = mapper;
1✔
411
            this.unaryMapper = unaryMapper;
1✔
412
        }
1✔
413

414
        @Override
415
        public void accept(double t) {
416
            cur = t;
1✔
417
        }
1✔
418

419
        private BiConsumer<Double, Double> fn(DoubleConsumer action) {
420
            switch (mode) {
1✔
421
            case MODE_MAP_FIRST:
422
                return (a, b) -> action.accept(a == null ? unaryMapper.applyAsDouble(b) : b);
1✔
423
            case MODE_MAP_LAST:
424
                return (a, b) -> action.accept(b == null ? unaryMapper.applyAsDouble(a) : a);
1✔
425
            default:
426
                return (a, b) -> action.accept(mapper.applyAsDouble(a, b));
1✔
427
            }
428
        }
429

430
        @Override
431
        public boolean tryAdvance(DoubleConsumer action) {
432
            Sink<Double> l = left, r = right;
1✔
433
            if (l != null) {
1✔
434
                left = null;
1✔
435
                if (!source.tryAdvance(this)) {
1✔
436
                    right = null;
1✔
437
                    return l.connect(r, fn(action));
1✔
438
                }
439
                if (l.push(cur, fn(action), false))
1✔
440
                    return true;
1✔
441
            }
442
            double prev = cur;
1✔
443
            if (!source.tryAdvance(this)) {
1✔
444
                right = null;
1✔
445
                return r != null && r.push(prev, fn(action), true);
1✔
446
            }
447
            action.accept(mapper.applyAsDouble(prev, cur));
1✔
448
            return true;
1✔
449
        }
450

451
        @Override
452
        public void forEachRemaining(DoubleConsumer action) {
453
            BiConsumer<Double, Double> fn = fn(action);
1✔
454
            source.forEachRemaining((double next) -> {
1✔
455
                if (left != null) {
1✔
456
                    left.push(cur = next, fn, false);
1✔
457
                    left = null;
1✔
458
                } else {
459
                    action.accept(mapper.applyAsDouble(cur, cur = next));
1✔
460
                }
461
            });
1✔
462
            finish(fn, cur);
1✔
463
        }
1✔
464
    }
465
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc