rx.internal.operators.OperatorBufferWithSize Maven / Gradle / Ivy
/**
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package rx.internal.operators;
import java.util.*;
import java.util.concurrent.atomic.*;
import rx.*;
import rx.Observable;
import rx.Observable.Operator;
import rx.exceptions.MissingBackpressureException;
/**
* This operation takes
* values from the specified {@link Observable} source and stores them in all active chunks until the buffer
* contains a specified number of elements. The buffer is then emitted. Chunks are created after a certain
* amount of values have been received. When the source {@link Observable} completes or produces an error,
* the currently active chunks are emitted, and the event is propagated to all subscribed {@link Subscriber}s.
*
* Note that this operation can produce non-connected, connected non-overlapping, or overlapping
* chunks depending on the input parameters.
*
* @param the buffered value type
*/
public final class OperatorBufferWithSize implements Operator, T> {
final int count;
final int skip;
/**
* @param count
* the number of elements a buffer should have before being emitted
* @param skip
* the interval with which chunks have to be created. Note that when {@code skip == count}
* the operator will produce non-overlapping chunks. If
* {@code skip < count}, this buffer operation will produce overlapping chunks and if
* {@code skip > count} non-overlapping chunks will be created and some values will not be pushed
* into a buffer at all!
*/
public OperatorBufferWithSize(int count, int skip) {
if (count <= 0) {
throw new IllegalArgumentException("count must be greater than 0");
}
if (skip <= 0) {
throw new IllegalArgumentException("skip must be greater than 0");
}
this.count = count;
this.skip = skip;
}
@Override
public Subscriber super T> call(final Subscriber super List> child) {
if (skip == count) {
BufferExact parent = new BufferExact(child, count);
child.add(parent);
child.setProducer(parent.createProducer());
return parent;
}
if (skip > count) {
BufferSkip parent = new BufferSkip(child, count, skip);
child.add(parent);
child.setProducer(parent.createProducer());
return parent;
}
BufferOverlap parent = new BufferOverlap(child, count, skip);
child.add(parent);
child.setProducer(parent.createProducer());
return parent;
}
static final class BufferExact extends Subscriber {
final Subscriber super List> actual;
final int count;
List buffer;
public BufferExact(Subscriber super List> actual, int count) {
this.actual = actual;
this.count = count;
this.request(0L);
}
@Override
public void onNext(T t) {
List b = buffer;
if (b == null) {
b = new ArrayList(count);
buffer = b;
}
b.add(t);
if (b.size() == count) {
buffer = null;
actual.onNext(b);
}
}
@Override
public void onError(Throwable e) {
buffer = null;
actual.onError(e);
}
@Override
public void onCompleted() {
List b = buffer;
if (b != null) {
actual.onNext(b);
}
actual.onCompleted();
}
Producer createProducer() {
return new Producer() {
@Override
public void request(long n) {
if (n < 0L) {
throw new IllegalArgumentException("n >= required but it was " + n);
}
if (n != 0L) {
long u = BackpressureUtils.multiplyCap(n, count);
BufferExact.this.request(u);
}
}
};
}
}
static final class BufferSkip extends Subscriber {
final Subscriber super List> actual;
final int count;
final int skip;
long index;
List buffer;
public BufferSkip(Subscriber super List> actual, int count, int skip) {
this.actual = actual;
this.count = count;
this.skip = skip;
this.request(0L);
}
@Override
public void onNext(T t) {
long i = index;
List b = buffer;
if (i == 0) {
b = new ArrayList(count);
buffer = b;
}
i++;
if (i == skip) {
index = 0;
} else {
index = i;
}
if (b != null) {
b.add(t);
if (b.size() == count) {
buffer = null;
actual.onNext(b);
}
}
}
@Override
public void onError(Throwable e) {
buffer = null;
actual.onError(e);
}
@Override
public void onCompleted() {
List b = buffer;
if (b != null) {
buffer = null;
actual.onNext(b);
}
actual.onCompleted();
}
Producer createProducer() {
return new BufferSkipProducer();
}
final class BufferSkipProducer
extends AtomicBoolean
implements Producer {
/** */
private static final long serialVersionUID = 3428177408082367154L;
@Override
public void request(long n) {
if (n < 0) {
throw new IllegalArgumentException("n >= 0 required but it was " + n);
}
if (n != 0) {
BufferSkip parent = BufferSkip.this;
if (!get() && compareAndSet(false, true)) {
long u = BackpressureUtils.multiplyCap(n, parent.count);
long v = BackpressureUtils.multiplyCap(parent.skip - parent.count, n - 1);
long w = BackpressureUtils.addCap(u, v);
parent.request(w);
} else {
long u = BackpressureUtils.multiplyCap(n, parent.skip);
parent.request(u);
}
}
}
}
}
static final class BufferOverlap extends Subscriber {
final Subscriber super List> actual;
final int count;
final int skip;
long index;
final ArrayDeque> queue;
final AtomicLong requested;
long produced;
public BufferOverlap(Subscriber super List> actual, int count, int skip) {
this.actual = actual;
this.count = count;
this.skip = skip;
this.queue = new ArrayDeque>();
this.requested = new AtomicLong();
this.request(0L);
}
@Override
public void onNext(T t) {
long i = index;
if (i == 0) {
List b = new ArrayList(count);
queue.offer(b);
}
i++;
if (i == skip) {
index = 0;
} else {
index = i;
}
for (List list : queue) {
list.add(t);
}
List b = queue.peek();
if (b != null && b.size() == count) {
queue.poll();
produced++;
actual.onNext(b);
}
}
@Override
public void onError(Throwable e) {
queue.clear();
actual.onError(e);
}
@Override
public void onCompleted() {
long p = produced;
if (p != 0L) {
if (p > requested.get()) {
actual.onError(new MissingBackpressureException("More produced than requested? " + p));
return;
}
requested.addAndGet(-p);
}
BackpressureUtils.postCompleteDone(requested, queue, actual);
}
Producer createProducer() {
return new BufferOverlapProducer();
}
final class BufferOverlapProducer extends AtomicBoolean implements Producer {
/** */
private static final long serialVersionUID = -4015894850868853147L;
@Override
public void request(long n) {
BufferOverlap parent = BufferOverlap.this;
if (BackpressureUtils.postCompleteRequest(parent.requested, n, parent.queue, parent.actual)) {
if (n != 0L) {
if (!get() && compareAndSet(false, true)) {
long u = BackpressureUtils.multiplyCap(parent.skip, n - 1);
long v = BackpressureUtils.addCap(u, parent.count);
parent.request(v);
} else {
long u = BackpressureUtils.multiplyCap(parent.skip, n);
parent.request(u);
}
}
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy