All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rx.internal.operators.OperatorWindowWithSize Maven / Gradle / Ivy

The newest version!
/**
 * 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.functions.Action0;
import rx.internal.util.atomic.SpscLinkedArrayQueue;
import rx.subjects.*;
import rx.subscriptions.Subscriptions;

/**
 * Creates windows of values into the source sequence with skip frequency and size bounds.
 *
 * If skip == size then the windows are non-overlapping, otherwise, windows may overlap
 * or can be discontinuous. The returned Observable sequence is cold and need to be
 * consumed while the window operation is in progress.
 *
 * 

Note that this conforms the Rx.NET behavior, but does not match former RxJava * behavior, which operated as a regular buffer and mapped its lists to Observables.

* * @param the value type */ public final class OperatorWindowWithSize implements Operator, T> { final int size; final int skip; public OperatorWindowWithSize(int size, int skip) { this.size = size; this.skip = skip; } @Override public Subscriber call(Subscriber> child) { if (skip == size) { WindowExact parent = new WindowExact(child, size); child.add(parent.cancel); child.setProducer(parent.createProducer()); return parent; } else if (skip > size) { WindowSkip parent = new WindowSkip(child, size, skip); child.add(parent.cancel); child.setProducer(parent.createProducer()); return parent; } WindowOverlap parent = new WindowOverlap(child, size, skip); child.add(parent.cancel); child.setProducer(parent.createProducer()); return parent; } static final class WindowExact extends Subscriber implements Action0 { final Subscriber> actual; final int size; final AtomicInteger wip; final Subscription cancel; int index; Subject window; public WindowExact(Subscriber> actual, int size) { this.actual = actual; this.size = size; this.wip = new AtomicInteger(1); this.cancel = Subscriptions.create(this); this.add(cancel); this.request(0); } @Override public void onNext(T t) { int i = index; Subject w = window; if (i == 0) { wip.getAndIncrement(); w = UnicastSubject.create(size, this); window = w; actual.onNext(w); } i++; w.onNext(t); if (i == size) { index = 0; window = null; w.onCompleted(); } else { index = i; } } @Override public void onError(Throwable e) { Subject w = window; if (w != null) { window = null; w.onError(e); } actual.onError(e); } @Override public void onCompleted() { Subject w = window; if (w != null) { window = null; w.onCompleted(); } actual.onCompleted(); } Producer createProducer() { return new Producer() { @Override public void request(long n) { if (n < 0L) { throw new IllegalArgumentException("n >= 0 required but it was " + n); } if (n != 0L) { long u = BackpressureUtils.multiplyCap(size, n); WindowExact.this.request(u); } } }; } @Override public void call() { if (wip.decrementAndGet() == 0) { unsubscribe(); } } } static final class WindowSkip extends Subscriber implements Action0 { final Subscriber> actual; final int size; final int skip; final AtomicInteger wip; final Subscription cancel; int index; Subject window; public WindowSkip(Subscriber> actual, int size, int skip) { this.actual = actual; this.size = size; this.skip = skip; this.wip = new AtomicInteger(1); this.cancel = Subscriptions.create(this); this.add(cancel); this.request(0); } @Override public void onNext(T t) { int i = index; Subject w = window; if (i == 0) { wip.getAndIncrement(); w = UnicastSubject.create(size, this); window = w; actual.onNext(w); } i++; if (w != null) { w.onNext(t); } if (i == size) { index = i; window = null; w.onCompleted(); } else if (i == skip) { index = 0; } else { index = i; } } @Override public void onError(Throwable e) { Subject w = window; if (w != null) { window = null; w.onError(e); } actual.onError(e); } @Override public void onCompleted() { Subject w = window; if (w != null) { window = null; w.onCompleted(); } actual.onCompleted(); } Producer createProducer() { return new WindowSkipProducer(); } @Override public void call() { if (wip.decrementAndGet() == 0) { unsubscribe(); } } final class WindowSkipProducer extends AtomicBoolean implements Producer { /** */ private static final long serialVersionUID = 4625807964358024108L; @Override public void request(long n) { if (n < 0L) { throw new IllegalArgumentException("n >= 0 required but it was " + n); } if (n != 0L) { WindowSkip parent = WindowSkip.this; if (!get() && compareAndSet(false, true)) { long u = BackpressureUtils.multiplyCap(n, parent.size); long v = BackpressureUtils.multiplyCap(parent.skip - parent.size, 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 WindowOverlap extends Subscriber implements Action0 { final Subscriber> actual; final int size; final int skip; final AtomicInteger wip; final Subscription cancel; final ArrayDeque> windows; final AtomicLong requested; final AtomicInteger drainWip; final Queue> queue; Throwable error; volatile boolean done; int index; int produced; public WindowOverlap(Subscriber> actual, int size, int skip) { this.actual = actual; this.size = size; this.skip = skip; this.wip = new AtomicInteger(1); this.windows = new ArrayDeque>(); this.drainWip = new AtomicInteger(); this.requested = new AtomicLong(); this.cancel = Subscriptions.create(this); this.add(cancel); this.request(0); int maxWindows = (size + (skip - 1)) / skip; this.queue = new SpscLinkedArrayQueue>(maxWindows); } @Override public void onNext(T t) { int i = index; ArrayDeque> q = windows; if (i == 0 && !actual.isUnsubscribed()) { wip.getAndIncrement(); Subject w = UnicastSubject.create(16, this); q.offer(w); queue.offer(w); drain(); } for (Subject w : windows) { w.onNext(t); } int p = produced + 1; if (p == size) { produced = p - skip; Subject w = q.poll(); if (w != null) { w.onCompleted(); } } else { produced = p; } i++; if (i == skip) { index = 0; } else { index = i; } } @Override public void onError(Throwable e) { for (Subject w : windows) { w.onError(e); } windows.clear(); error = e; done = true; drain(); } @Override public void onCompleted() { for (Subject w : windows) { w.onCompleted(); } windows.clear(); done = true; drain(); } Producer createProducer() { return new WindowOverlapProducer(); } @Override public void call() { if (wip.decrementAndGet() == 0) { unsubscribe(); } } void drain() { AtomicInteger dw = drainWip; if (dw.getAndIncrement() != 0) { return; } final Subscriber> a = actual; final Queue> q = queue; int missed = 1; for (;;) { long r = requested.get(); long e = 0L; while (e != r) { boolean d = done; Subject v = q.poll(); boolean empty = v == null; if (checkTerminated(d, empty, a, q)) { return; } if (empty) { break; } a.onNext(v); e++; } if (e == r) { if (checkTerminated(done, q.isEmpty(), a, q)) { return; } } if (e != 0 && r != Long.MAX_VALUE) { requested.addAndGet(-e); } missed = dw.addAndGet(-missed); if (missed == 0) { break; } } } boolean checkTerminated(boolean d, boolean empty, Subscriber> a, Queue> q) { if (a.isUnsubscribed()) { q.clear(); return true; } if (d) { Throwable e = error; if (e != null) { q.clear(); a.onError(e); return true; } else if (empty) { a.onCompleted(); return true; } } return false; } final class WindowOverlapProducer extends AtomicBoolean implements Producer { /** */ private static final long serialVersionUID = 4625807964358024108L; @Override public void request(long n) { if (n < 0L) { throw new IllegalArgumentException("n >= 0 required but it was " + n); } if (n != 0L) { WindowOverlap parent = WindowOverlap.this; if (!get() && compareAndSet(false, true)) { long u = BackpressureUtils.multiplyCap(parent.skip, n - 1); long v = BackpressureUtils.addCap(u, parent.size); parent.request(v); } else { long u = BackpressureUtils.multiplyCap(parent.skip, n); WindowOverlap.this.request(u); } BackpressureUtils.getAndAddRequest(parent.requested, n); parent.drain(); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy