├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── generate-site.sh ├── pom.xml └── src ├── docs ├── collectStats.png ├── collectStats.svg ├── collectWhile.png ├── doOnEmpty.png ├── doOnEmpty.svg ├── eclipse-junit.png ├── fetchPagesByRequest.png ├── fetchPagesByRequest.svg ├── file-queue.png ├── file-queue.svg ├── ignoreElementsThen.png ├── ignoreElementsThen.svg ├── mapLast.png ├── mapLast.svg ├── mapWithIndex.png ├── mapWithIndex.svg ├── match.png ├── match.svg ├── maxRequest.png ├── maxRequest.svg ├── mergeInterleaved.png ├── minRequest.png ├── minRequest.svg ├── onBackpressureBufferToFile.png ├── onBackpressureBufferToFile.svg ├── orderedMerge.png ├── orderedMerge.svg ├── rebatchRequests.png ├── rebatchRequests.svg ├── repeatLast.png ├── repeatLast.svg ├── reverse.png ├── reverse.svg ├── stateMachine.png ├── stateMachine.svg ├── toListWhile.png ├── toListWhile.svg ├── windowMinMax.png └── windowMinMax.svg ├── main ├── java │ └── com │ │ └── github │ │ └── davidmoten │ │ ├── rx2 │ │ ├── Actions.java │ │ ├── BiFunctions.java │ │ ├── BiPredicates.java │ │ ├── Bytes.java │ │ ├── Callables.java │ │ ├── Consumers.java │ │ ├── Flowables.java │ │ ├── Functions.java │ │ ├── IO.java │ │ ├── Maybes.java │ │ ├── Observables.java │ │ ├── Predicates.java │ │ ├── RetryWhen.java │ │ ├── SchedulerHelper.java │ │ ├── StateMachine.java │ │ ├── StateMachine2.java │ │ ├── Statistics.java │ │ ├── Strings.java │ │ ├── buffertofile │ │ │ ├── DataSerializer.java │ │ │ ├── Options.java │ │ │ ├── Serializer.java │ │ │ ├── SerializerString.java │ │ │ └── Serializers.java │ │ ├── exceptions │ │ │ ├── AssertionException.java │ │ │ └── ThrowingException.java │ │ ├── flowable │ │ │ ├── Burst.java │ │ │ ├── CachedFlowable.java │ │ │ ├── CloseableFlowableWithReset.java │ │ │ ├── Serialized.java │ │ │ └── Transformers.java │ │ ├── functions │ │ │ └── Consumer3.java │ │ ├── internal │ │ │ ├── SchedulerWithId.java │ │ │ ├── flowable │ │ │ │ ├── DelimitedStringLinkedList.java │ │ │ │ ├── FlowableCollectWhile.java │ │ │ │ ├── FlowableDoOnEmpty.java │ │ │ │ ├── FlowableFetchPagesByRequest.java │ │ │ │ ├── FlowableInsertMaybe.java │ │ │ │ ├── FlowableInsertTimeout.java │ │ │ │ ├── FlowableMapLast.java │ │ │ │ ├── FlowableMatch.java │ │ │ │ ├── FlowableMaxRequest.java │ │ │ │ ├── FlowableMergeInterleave.java │ │ │ │ ├── FlowableMinRequest.java │ │ │ │ ├── FlowableRepeat.java │ │ │ │ ├── FlowableRepeatingTransform.java │ │ │ │ ├── FlowableReverse.java │ │ │ │ ├── FlowableServerSocket.java │ │ │ │ ├── FlowableStateMachine.java │ │ │ │ ├── FlowableStringInputStream.java │ │ │ │ ├── FlowableStringSplitSimple.java │ │ │ │ ├── FlowableWindowMinMax.java │ │ │ │ ├── OnSubscribeCacheResettable.java │ │ │ │ ├── TransformerDecode.java │ │ │ │ ├── TransformerStateMachine.java │ │ │ │ ├── TransformerStringSplit.java │ │ │ │ └── buffertofile │ │ │ │ │ ├── FlowableOnBackpressureBufferToFile.java │ │ │ │ │ ├── MemoryMappedFile.java │ │ │ │ │ ├── Page.java │ │ │ │ │ ├── PagedQueue.java │ │ │ │ │ ├── Pages.java │ │ │ │ │ ├── SerializerBytes.java │ │ │ │ │ ├── SerializerJavaIO.java │ │ │ │ │ └── UnsafeAccess.java │ │ │ └── observable │ │ │ │ └── OnSubscribeCacheResetable.java │ │ ├── observable │ │ │ ├── CachedObservable.java │ │ │ ├── CloseableObservableWithReset.java │ │ │ └── Transformers.java │ │ └── util │ │ │ ├── Pair.java │ │ │ └── ZippedEntry.java │ │ └── util │ │ └── RingBuffer.java └── proguard │ └── proguard-rules.txt └── test ├── java └── com │ └── github │ └── davidmoten │ ├── rx2 │ ├── ActionsTest.java │ ├── Benchmarks.java │ ├── BiFunctionsTest.java │ ├── BiPredicatesTest.java │ ├── BytesTest.java │ ├── CallablesTest.java │ ├── ConsumersTest.java │ ├── FlowablesTest.java │ ├── FunctionsTest.java │ ├── MaybesTest.java │ ├── ObservablesTest.java │ ├── PredicatesTest.java │ ├── RetryWhenTest.java │ ├── SchedulerHelperTest.java │ ├── SpecializedMpscLinkedQueue.java │ ├── SpecializedMspcLinkedQueueTest.java │ ├── StateMachineTest.java │ ├── StringsSplitTest.java │ ├── StringsTest.java │ ├── buffertofile │ │ ├── FlowableOnBackpressureBufferToFileTest.java │ │ └── SerializersTest.java │ ├── flowable │ │ ├── FlowableDoNothing.java │ │ ├── SerializedTest.java │ │ └── TransformersTest.java │ ├── internal │ │ └── flowable │ │ │ ├── DelimitedStringLinkedListTest.java │ │ │ ├── FlowableCollectWhileTest.java │ │ │ ├── FlowableDoOnEmptyTest.java │ │ │ ├── FlowableFetchPagesByRequestTest.java │ │ │ ├── FlowableMapLastTest.java │ │ │ ├── FlowableMatchTest.java │ │ │ ├── FlowableMaxRequestTest.java │ │ │ ├── FlowableMergeInterleavedTest.java │ │ │ ├── FlowableMinRequestTest.java │ │ │ ├── FlowablePassThroughTest.java │ │ │ ├── FlowableRepeatingTest.java │ │ │ ├── FlowableRepeatingTransformTest.java │ │ │ ├── FlowableReverseTest.java │ │ │ ├── FlowableServerSocketTest.java │ │ │ ├── FlowableStateMachineTest.java │ │ │ ├── FlowableStringInputStreamTest.java │ │ │ ├── FlowableWindowMinMaxTest.java │ │ │ ├── SerializerJavaIOTest.java │ │ │ ├── TransformerDecodeTest.java │ │ │ ├── TransformerStringSplitTest.java │ │ │ └── buffertofile │ │ │ ├── MemoryMappedFileTest.java │ │ │ ├── PageListTest.java │ │ │ ├── PageTest.java │ │ │ └── UnsafeAccessTest.java │ ├── observable │ │ └── TransformersTest.java │ └── util │ │ └── PairTest.java │ └── util │ └── RingBufferTest.java └── resources ├── test.zip ├── test2.txt ├── test3.txt └── test4.txt /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | uses: davidmoten/workflows/.github/workflows/ci.yml@master 8 | with: 9 | jdk-matrix: '[ "8"]' 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | .project 11 | .classpath 12 | .settings 13 | /.idea/ 14 | *.iml -------------------------------------------------------------------------------- /generate-site.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | mvn site 4 | cd ../davidmoten.github.io 5 | git pull 6 | mkdir -p rxjava2-extras 7 | cp -r ../rxjava2-extras/target/site/* rxjava2-extras/ 8 | git add . 9 | git commit -am "update site reports" 10 | git push 11 | -------------------------------------------------------------------------------- /src/docs/collectStats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/collectStats.png -------------------------------------------------------------------------------- /src/docs/collectWhile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/collectWhile.png -------------------------------------------------------------------------------- /src/docs/doOnEmpty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/doOnEmpty.png -------------------------------------------------------------------------------- /src/docs/eclipse-junit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/eclipse-junit.png -------------------------------------------------------------------------------- /src/docs/fetchPagesByRequest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/fetchPagesByRequest.png -------------------------------------------------------------------------------- /src/docs/file-queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/file-queue.png -------------------------------------------------------------------------------- /src/docs/ignoreElementsThen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/ignoreElementsThen.png -------------------------------------------------------------------------------- /src/docs/mapLast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/mapLast.png -------------------------------------------------------------------------------- /src/docs/mapWithIndex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/mapWithIndex.png -------------------------------------------------------------------------------- /src/docs/match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/match.png -------------------------------------------------------------------------------- /src/docs/maxRequest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/maxRequest.png -------------------------------------------------------------------------------- /src/docs/mergeInterleaved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/mergeInterleaved.png -------------------------------------------------------------------------------- /src/docs/minRequest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/minRequest.png -------------------------------------------------------------------------------- /src/docs/onBackpressureBufferToFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/onBackpressureBufferToFile.png -------------------------------------------------------------------------------- /src/docs/orderedMerge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/orderedMerge.png -------------------------------------------------------------------------------- /src/docs/rebatchRequests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/rebatchRequests.png -------------------------------------------------------------------------------- /src/docs/repeatLast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/repeatLast.png -------------------------------------------------------------------------------- /src/docs/reverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/reverse.png -------------------------------------------------------------------------------- /src/docs/stateMachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/stateMachine.png -------------------------------------------------------------------------------- /src/docs/toListWhile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/toListWhile.png -------------------------------------------------------------------------------- /src/docs/windowMinMax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/docs/windowMinMax.png -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Actions.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import io.reactivex.functions.Action; 7 | 8 | public final class Actions { 9 | 10 | private Actions() { 11 | // prevent instantiation 12 | } 13 | 14 | public static Action setToTrue(final AtomicBoolean b) { 15 | return new Action() { 16 | 17 | @Override 18 | public void run() throws Exception { 19 | b.set(true); 20 | } 21 | 22 | }; 23 | } 24 | 25 | public static Action throwing(final Exception e) { 26 | return new Action() { 27 | 28 | @Override 29 | public void run() throws Exception { 30 | throw e; 31 | } 32 | 33 | }; 34 | } 35 | 36 | public static Action doNothing() { 37 | return DoNothingHolder.DO_NOTHING; 38 | } 39 | 40 | private static final class DoNothingHolder { 41 | static final Action DO_NOTHING = new Action() { 42 | @Override 43 | public void run() throws Exception { 44 | // do nothing! 45 | } 46 | }; 47 | } 48 | 49 | public static Action increment(final AtomicInteger x) { 50 | //TODO make holder 51 | return new Action() { 52 | 53 | @Override 54 | public void run() throws Exception { 55 | x.incrementAndGet(); 56 | } 57 | }; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/BiFunctions.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import com.github.davidmoten.rx2.exceptions.ThrowingException; 4 | 5 | import io.reactivex.functions.BiFunction; 6 | 7 | public final class BiFunctions { 8 | 9 | private BiFunctions() { 10 | // prevent instantiation 11 | } 12 | 13 | @SuppressWarnings("unchecked") 14 | public static BiFunction throwing() { 15 | return (BiFunction) ThrowingHolder.INSTANCE; 16 | } 17 | 18 | private static final class ThrowingHolder { 19 | static BiFunction INSTANCE = new BiFunction() { 20 | 21 | @Override 22 | public Object apply(Object t1, Object t2) throws Exception { 23 | throw new ThrowingException(); 24 | } 25 | }; 26 | } 27 | 28 | public static BiFunction collectStats() { 29 | return new BiFunction() { 30 | 31 | @Override 32 | public Statistics apply(Statistics s, T t) { 33 | return s.add(t); 34 | } 35 | }; 36 | } 37 | 38 | public static BiFunction constant(final S value) { 39 | // TODO make holder 40 | return new BiFunction() { 41 | 42 | @Override 43 | public S apply(T t1, R t2) throws Exception { 44 | return value; 45 | } 46 | }; 47 | } 48 | 49 | public static BiFunction toNull() { 50 | // TODO make holder 51 | return new BiFunction() { 52 | 53 | @Override 54 | public S apply(T t1, R t2) throws Exception { 55 | return null; 56 | } 57 | }; 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/BiPredicates.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import com.github.davidmoten.rx2.exceptions.ThrowingException; 4 | 5 | import io.reactivex.functions.BiPredicate; 6 | 7 | public final class BiPredicates { 8 | 9 | private BiPredicates() { 10 | // prevent instantiation 11 | } 12 | 13 | public static BiPredicate alwaysTrue() { 14 | // TODO make holder 15 | return new BiPredicate() { 16 | 17 | @Override 18 | public boolean test(T t1, R t2) throws Exception { 19 | return true; 20 | } 21 | }; 22 | } 23 | 24 | public static BiPredicate alwaysFalse() { 25 | // TODO make holder 26 | return new BiPredicate() { 27 | 28 | @Override 29 | public boolean test(T t1, R t2) throws Exception { 30 | return false; 31 | } 32 | }; 33 | } 34 | 35 | public static BiPredicate throwing() { 36 | // TODO make holder 37 | return new BiPredicate() { 38 | 39 | @Override 40 | public boolean test(T t1, R t2) throws Exception { 41 | throw new ThrowingException(); 42 | } 43 | }; 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Callables.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.Callable; 4 | 5 | public final class Callables { 6 | 7 | private Callables() { 8 | // prevent instantiation 9 | } 10 | 11 | public static Callable constant(final T object) { 12 | return new Callable() { 13 | 14 | @Override 15 | public T call() throws Exception { 16 | return object; 17 | } 18 | }; 19 | } 20 | 21 | public static Callable toNull() { 22 | // TODO make holder 23 | return new Callable() { 24 | 25 | @Override 26 | public T call() throws Exception { 27 | return null; 28 | } 29 | }; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Consumers.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.io.Closeable; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | import java.util.concurrent.atomic.AtomicReference; 9 | 10 | import com.github.davidmoten.rx2.exceptions.AssertionException; 11 | 12 | import io.reactivex.functions.Consumer; 13 | import io.reactivex.functions.LongConsumer; 14 | 15 | public final class Consumers { 16 | 17 | private Consumers() { 18 | // prevent instantiation 19 | } 20 | 21 | public static LongConsumer addLongTo(final List list) { 22 | return new LongConsumer() { 23 | 24 | @Override 25 | public void accept(long t) throws Exception { 26 | list.add(t); 27 | } 28 | 29 | }; 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | public static Consumer close() { 34 | return (Consumer) CloseHolder.INSTANCE; 35 | } 36 | 37 | private static final class CloseHolder { 38 | final static Consumer INSTANCE = new Consumer() { 39 | @Override 40 | public void accept(Closeable t) throws Exception { 41 | t.close(); 42 | } 43 | 44 | }; 45 | } 46 | 47 | public static Consumer increment(final AtomicInteger value) { 48 | return new Consumer() { 49 | @Override 50 | public void accept(Object t) throws Exception { 51 | value.incrementAndGet(); 52 | } 53 | }; 54 | } 55 | 56 | public static Consumer printStackTrace() { 57 | // TODO make holder 58 | return new Consumer() { 59 | @Override 60 | public void accept(Throwable e) throws Exception { 61 | e.printStackTrace(); 62 | } 63 | }; 64 | } 65 | 66 | @SuppressWarnings("unchecked") 67 | public static Consumer doNothing() { 68 | return (Consumer) DoNothingHolder.INSTANCE; 69 | } 70 | 71 | private static final class DoNothingHolder { 72 | static final Consumer INSTANCE = new Consumer() { 73 | 74 | @Override 75 | public void accept(Object t) throws Exception { 76 | // do nothing 77 | } 78 | }; 79 | } 80 | 81 | public static Consumer set(final AtomicReference value) { 82 | return new Consumer() { 83 | 84 | @Override 85 | public void accept(T t) throws Exception { 86 | value.set(t); 87 | } 88 | }; 89 | } 90 | 91 | public static Consumer set(final AtomicInteger value) { 92 | return new Consumer() { 93 | @Override 94 | public void accept(Integer t) throws Exception { 95 | value.set(t); 96 | } 97 | }; 98 | } 99 | 100 | public static Consumer decrement(final AtomicInteger value) { 101 | return new Consumer() { 102 | 103 | @Override 104 | public void accept(Object t) throws Exception { 105 | value.decrementAndGet(); 106 | } 107 | 108 | }; 109 | } 110 | 111 | @SuppressWarnings("unchecked") 112 | public static Consumer setToTrue(final AtomicBoolean value) { 113 | return (Consumer) new Consumer() { 114 | 115 | @Override 116 | public void accept(Object t) throws Exception { 117 | value.set(true); 118 | } 119 | }; 120 | } 121 | 122 | public static Consumer addTo(final List list) { 123 | return new Consumer() { 124 | 125 | @Override 126 | public void accept(T t) throws Exception { 127 | list.add(t); 128 | } 129 | }; 130 | } 131 | 132 | @SuppressWarnings("unchecked") 133 | public static Consumer println() { 134 | return (Consumer) PrintlnHolder.INSTANCE; 135 | } 136 | 137 | private static final class PrintlnHolder { 138 | static final Consumer INSTANCE = new Consumer() { 139 | @Override 140 | public void accept(Object t) throws Exception { 141 | System.out.println(t); 142 | } 143 | }; 144 | } 145 | 146 | public static Consumer assertBytesEquals(final byte[] expected) { 147 | // TODO make holder 148 | return new Consumer() { 149 | 150 | @Override 151 | public void accept(byte[] array) throws Exception { 152 | if (!Arrays.equals(expected, array)) { 153 | // TODO use custom exception 154 | throw new AssertionException("arrays not equal: expected=" + Arrays.toString(expected) + ",actual=" 155 | + Arrays.toString(array)); 156 | } 157 | } 158 | 159 | }; 160 | } 161 | 162 | public static LongConsumer printLong(final String prefix) { 163 | return new LongConsumer() { 164 | @Override 165 | public void accept(long t) throws Exception { 166 | System.out.println(prefix + t); 167 | } 168 | }; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Functions.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import com.github.davidmoten.rx2.exceptions.ThrowingException; 4 | 5 | import io.reactivex.functions.Function; 6 | 7 | public final class Functions { 8 | 9 | private Functions() { 10 | // prevent instantiation 11 | } 12 | 13 | public static Function constant(final T value) { 14 | return new Function() { 15 | 16 | @Override 17 | public T apply(Object t) throws Exception { 18 | return value; 19 | } 20 | }; 21 | } 22 | 23 | @SuppressWarnings("unchecked") 24 | public static Function identity() { 25 | return (Function) IdentityHolder.INSTANCE; 26 | } 27 | 28 | private static final class IdentityHolder { 29 | static final Function INSTANCE = new Function() { 30 | 31 | @Override 32 | public Object apply(Object t) throws Exception { 33 | return t; 34 | } 35 | }; 36 | } 37 | 38 | public static Function throwing() { 39 | // TODO make holder 40 | return new Function() { 41 | 42 | @Override 43 | public R apply(T t) { 44 | throw new ThrowingException(); 45 | } 46 | }; 47 | } 48 | 49 | public static Function toStringFunction() { 50 | // TODO make holder 51 | return new Function () { 52 | 53 | @Override 54 | public String apply(T t) throws Exception { 55 | return String.valueOf(t); 56 | } 57 | }; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/IO.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.util.concurrent.Callable; 7 | 8 | import com.github.davidmoten.rx2.internal.flowable.FlowableServerSocket; 9 | 10 | import io.reactivex.Flowable; 11 | import io.reactivex.functions.Action; 12 | import io.reactivex.functions.Consumer; 13 | import io.reactivex.functions.Predicate; 14 | import io.reactivex.internal.functions.Functions; 15 | 16 | public final class IO { 17 | 18 | private IO() { 19 | // prevent instantiation 20 | } 21 | 22 | public static ServerSocketBuilder serverSocket(final int port) { 23 | return new ServerSocketBuilder(new Callable() { 24 | @Override 25 | public ServerSocket call() throws IOException { 26 | return new ServerSocket(port); 27 | } 28 | }); 29 | } 30 | 31 | public static ServerSocketBuilder serverSocketAutoAllocatePort(final Consumer onAllocated) { 32 | return serverSocket(new Callable() { 33 | 34 | @Override 35 | public ServerSocket call() throws Exception { 36 | ServerSocket ss = new ServerSocket(0); 37 | onAllocated.accept(ss.getLocalPort()); 38 | return ss; 39 | } 40 | }); 41 | } 42 | 43 | public static ServerSocketBuilder serverSocket(Callable serverSocketFactory) { 44 | return new ServerSocketBuilder(serverSocketFactory); 45 | } 46 | 47 | public static final class ServerSocketBuilder { 48 | 49 | private final Callable serverSocketFactory; 50 | private int readTimeoutMs = Integer.MAX_VALUE; 51 | private int bufferSize = 8192; 52 | private Action preAcceptAction = Actions.doNothing(); 53 | private int acceptTimeoutMs = Integer.MAX_VALUE; 54 | private Predicate acceptSocket = Functions.alwaysTrue(); 55 | 56 | public ServerSocketBuilder(final Callable serverSocketFactory) { 57 | this.serverSocketFactory = serverSocketFactory; 58 | } 59 | 60 | public ServerSocketBuilder readTimeoutMs(int readTimeoutMs) { 61 | this.readTimeoutMs = readTimeoutMs; 62 | return this; 63 | } 64 | 65 | public ServerSocketBuilder bufferSize(int bufferSize) { 66 | this.bufferSize = bufferSize; 67 | return this; 68 | } 69 | 70 | public ServerSocketBuilder preAcceptAction(Action action) { 71 | this.preAcceptAction = action; 72 | return this; 73 | } 74 | 75 | public ServerSocketBuilder acceptTimeoutMs(int acceptTimeoutMs) { 76 | this.acceptTimeoutMs = acceptTimeoutMs; 77 | return this; 78 | } 79 | 80 | public ServerSocketBuilder acceptSocketIf(Predicate acceptSocket) { 81 | this.acceptSocket = acceptSocket; 82 | return this; 83 | } 84 | 85 | public Flowable> create() { 86 | return FlowableServerSocket.create(serverSocketFactory, readTimeoutMs, bufferSize, preAcceptAction, 87 | acceptTimeoutMs, acceptSocket); 88 | } 89 | 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Maybes.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import io.reactivex.Maybe; 4 | 5 | public final class Maybes { 6 | 7 | private Maybes() { 8 | // prevent instantiation 9 | } 10 | 11 | public static Maybe fromNullable(T t) { 12 | if (t == null) { 13 | return Maybe.empty(); 14 | } else { 15 | return Maybe.just(t); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Predicates.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import io.reactivex.functions.Predicate; 4 | 5 | public final class Predicates { 6 | 7 | private Predicates() { 8 | // prevent instantiation 9 | } 10 | 11 | @SuppressWarnings("unchecked") 12 | public static Predicate alwaysFalse() { 13 | return (Predicate) FalseHolder.INSTANCE; 14 | } 15 | 16 | private static final class FalseHolder { 17 | static final Predicate INSTANCE = new Predicate() { 18 | @Override 19 | public boolean test(Object t) throws Exception { 20 | return false; 21 | } 22 | }; 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | public static Predicate alwaysTrue() { 27 | return (Predicate) TrueHolder.INSTANCE; 28 | } 29 | 30 | private static final class TrueHolder { 31 | static final Predicate INSTANCE = new Predicate() { 32 | @Override 33 | public boolean test(Object t) throws Exception { 34 | return true; 35 | } 36 | }; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/SchedulerHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import com.github.davidmoten.rx2.internal.SchedulerWithId; 7 | 8 | import io.reactivex.Scheduler; 9 | import io.reactivex.Scheduler.Worker; 10 | 11 | public final class SchedulerHelper { 12 | 13 | private SchedulerHelper() { 14 | // prevent instantiation 15 | } 16 | 17 | public static Scheduler withThreadIdFromCallSite(Scheduler scheduler) { 18 | return new SchedulerWithId(scheduler, describeCallSite()); 19 | } 20 | 21 | public static Scheduler withThreadId(Scheduler scheduler, String id) { 22 | return new SchedulerWithId(scheduler, id); 23 | } 24 | 25 | private static String describeCallSite() { 26 | StackTraceElement[] elements = Thread.currentThread().getStackTrace(); 27 | StackTraceElement e = elements[3]; 28 | return e.getClassName() + ":" + e.getMethodName() + ":" + e.getLineNumber(); 29 | } 30 | 31 | public static void blockUntilWorkFinished(Scheduler scheduler, int numThreads, long timeout, TimeUnit unit) { 32 | final CountDownLatch latch = new CountDownLatch(numThreads); 33 | for (int i = 1; i <= numThreads; i++) { 34 | final Worker worker = scheduler.createWorker(); 35 | worker.schedule(new Runnable() { 36 | @Override 37 | public void run() { 38 | worker.dispose(); 39 | latch.countDown(); 40 | } 41 | }); 42 | } 43 | try { 44 | boolean finished = latch.await(timeout, unit); 45 | if (!finished) { 46 | throw new RuntimeException("timeout occured waiting for work to finish"); 47 | } 48 | } catch (InterruptedException e) { 49 | throw new RuntimeException(e); 50 | } 51 | } 52 | 53 | public static void blockUntilWorkFinished(Scheduler scheduler, int numThreads) { 54 | blockUntilWorkFinished(scheduler, numThreads, 1, TimeUnit.MINUTES); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/StateMachine.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.Callable; 4 | 5 | import com.github.davidmoten.guavamini.annotations.VisibleForTesting; 6 | import com.github.davidmoten.rx2.flowable.Transformers; 7 | import com.github.davidmoten.rx2.functions.Consumer3; 8 | 9 | import io.reactivex.BackpressureStrategy; 10 | import io.reactivex.FlowableEmitter; 11 | import io.reactivex.FlowableTransformer; 12 | import io.reactivex.functions.BiConsumer; 13 | import io.reactivex.functions.BiPredicate; 14 | import io.reactivex.functions.Function3; 15 | 16 | public final class StateMachine { 17 | 18 | private StateMachine() { 19 | // prevent instantiation 20 | } 21 | 22 | public interface Transition extends Function3, State> { 23 | 24 | // override so IDEs have better suggestions for parameters 25 | @Override 26 | State apply(State state, In value, FlowableEmitter FlowableEmitter); 27 | 28 | } 29 | 30 | public interface Transition2 extends Function3, State> { 31 | 32 | // override so IDEs have better suggestions for parameters 33 | @Override 34 | State apply(State state, In value, Emitter emitter); 35 | 36 | } 37 | 38 | public interface Completion extends BiPredicate> { 39 | 40 | // override so IDEs have better suggestions for parameters 41 | @Override 42 | boolean test(State state, FlowableEmitter emitter); 43 | 44 | } 45 | 46 | public interface Completion2 extends BiConsumer> { 47 | 48 | // override so IDEs have better suggestions for parameters 49 | @Override 50 | void accept(State state, Emitter emitter); 51 | 52 | } 53 | 54 | 55 | public interface Errored extends Consumer3> { 56 | 57 | // override so IDEs have better suggestions for parameters 58 | @Override 59 | void accept(State state, Throwable error, Emitter emitter); 60 | 61 | } 62 | 63 | public static Builder builder() { 64 | return new Builder(); 65 | } 66 | 67 | public static final class Builder { 68 | 69 | private Builder() { 70 | // prevent instantiation from other packages 71 | } 72 | 73 | public Builder2 initialStateFactory(Callable initialState) { 74 | return new Builder2(initialState); 75 | } 76 | 77 | public Builder2 initialState(final State initialState) { 78 | return initialStateFactory(Callables.constant(initialState)); 79 | } 80 | 81 | } 82 | 83 | public static final class Builder2 { 84 | 85 | private final Callable initialState; 86 | 87 | private Builder2(Callable initialState) { 88 | this.initialState = initialState; 89 | } 90 | 91 | public Builder3 transition(Transition transition) { 92 | return new Builder3(initialState, transition); 93 | } 94 | 95 | } 96 | 97 | public static final class Builder3 { 98 | 99 | private static final int DEFAULT_REQUEST_SIZE = 1; 100 | 101 | private final Callable initialState; 102 | private final Transition transition; 103 | private Completion completion = CompletionAlwaysTrueHolder.instance(); 104 | private BackpressureStrategy backpressureStrategy = BackpressureStrategy.BUFFER; 105 | private int requestBatchSize = DEFAULT_REQUEST_SIZE; 106 | 107 | private Builder3(Callable initialState, Transition transition) { 108 | this.initialState = initialState; 109 | this.transition = transition; 110 | } 111 | 112 | public Builder3 completion(Completion completion) { 113 | this.completion = completion; 114 | return this; 115 | } 116 | 117 | public Builder3 backpressureStrategy(BackpressureStrategy backpressureStrategy) { 118 | this.backpressureStrategy = backpressureStrategy; 119 | return this; 120 | } 121 | 122 | public Builder3 requestBatchSize(int value) { 123 | this.requestBatchSize = value; 124 | return this; 125 | } 126 | 127 | public FlowableTransformer build() { 128 | return Transformers.stateMachine(initialState, transition, completion, backpressureStrategy, 129 | requestBatchSize); 130 | } 131 | 132 | } 133 | 134 | @VisibleForTesting 135 | static final class CompletionAlwaysTrueHolder { 136 | 137 | private CompletionAlwaysTrueHolder() { 138 | // prevent instantiation 139 | } 140 | 141 | private static final Completion INSTANCE = new Completion() { 142 | @Override 143 | public boolean test(Object t1, FlowableEmitter t2) { 144 | return true; 145 | } 146 | }; 147 | 148 | @SuppressWarnings("unchecked") 149 | static Completion instance() { 150 | return (Completion) INSTANCE; 151 | } 152 | } 153 | 154 | public static interface Emitter { 155 | void onNext_(T t); 156 | 157 | void onError_(Throwable e); 158 | 159 | void onComplete_(); 160 | 161 | void cancel_(); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/StateMachine2.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.Callable; 4 | 5 | import org.reactivestreams.Publisher; 6 | 7 | import com.github.davidmoten.rx2.StateMachine.Completion2; 8 | import com.github.davidmoten.rx2.StateMachine.Errored; 9 | import com.github.davidmoten.rx2.StateMachine.Transition2; 10 | import com.github.davidmoten.rx2.internal.flowable.FlowableStateMachine; 11 | 12 | import io.reactivex.BackpressureStrategy; 13 | import io.reactivex.Flowable; 14 | import io.reactivex.FlowableTransformer; 15 | 16 | public class StateMachine2 { 17 | 18 | public static Builder builder() { 19 | return new Builder(); 20 | } 21 | 22 | public static final class Builder { 23 | 24 | private Builder() { 25 | // prevent instantiation from other packages 26 | } 27 | 28 | public Builder2 initialStateFactory(Callable initialState) { 29 | return new Builder2(initialState); 30 | } 31 | 32 | public Builder2 initialState(final State initialState) { 33 | return initialStateFactory(Callables.constant(initialState)); 34 | } 35 | 36 | } 37 | 38 | public static final class Builder2 { 39 | 40 | private final Callable initialState; 41 | 42 | private Builder2(Callable initialState) { 43 | this.initialState = initialState; 44 | } 45 | 46 | public Builder3 transition(Transition2 transition) { 47 | return new Builder3(initialState, transition); 48 | } 49 | 50 | } 51 | 52 | public static final class Builder3 { 53 | 54 | private static final int DEFAULT_REQUEST_SIZE = 1; 55 | 56 | private final Callable initialState; 57 | private final Transition2 transition; 58 | private Completion2 completion = null; 59 | private Errored errored = null; 60 | private BackpressureStrategy backpressureStrategy = BackpressureStrategy.BUFFER; 61 | private int requestBatchSize = DEFAULT_REQUEST_SIZE; 62 | 63 | private Builder3(Callable initialState, Transition2 transition) { 64 | this.initialState = initialState; 65 | this.transition = transition; 66 | } 67 | 68 | public Builder3 completion(Completion2 completion) { 69 | this.completion = completion; 70 | return this; 71 | } 72 | 73 | public Builder3 errored(Errored errored) { 74 | this.errored = errored; 75 | return this; 76 | } 77 | 78 | public Builder3 backpressureStrategy(BackpressureStrategy backpressureStrategy) { 79 | this.backpressureStrategy = backpressureStrategy; 80 | return this; 81 | } 82 | 83 | public Builder3 requestBatchSize(int value) { 84 | this.requestBatchSize = value; 85 | return this; 86 | } 87 | 88 | public FlowableTransformer build() { 89 | return new FlowableTransformer() { 90 | 91 | @Override 92 | public Publisher apply(Flowable source) { 93 | return new FlowableStateMachine(source, initialState, transition, completion, 94 | errored, backpressureStrategy, requestBatchSize); 95 | } 96 | }; 97 | } 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/Statistics.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | public final class Statistics { 4 | 5 | private final long count; 6 | private final double sumX; 7 | private final double sumX2; 8 | 9 | private Statistics(long count, double sumX, double sumX2) { 10 | this.count = count; 11 | this.sumX = sumX; 12 | this.sumX2 = sumX2; 13 | } 14 | 15 | public static Statistics create() { 16 | return new Statistics(0, 0, 0); 17 | } 18 | 19 | public Statistics add(Number number) { 20 | double x = number.doubleValue(); 21 | return new Statistics(count + 1, sumX + x, sumX2 + x * x); 22 | } 23 | 24 | public long count() { 25 | return count; 26 | } 27 | 28 | public double sum() { 29 | return sumX; 30 | } 31 | 32 | public double sumSquares() { 33 | return sumX2; 34 | } 35 | 36 | public double mean() { 37 | return sumX / count; 38 | } 39 | 40 | public double sd() { 41 | double m = mean(); 42 | return Math.sqrt(sumX2 / count - m * m); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | StringBuilder builder = new StringBuilder(); 48 | builder.append("Statistics [count="); 49 | builder.append(count); 50 | builder.append(", sum="); 51 | builder.append(sum()); 52 | builder.append(", sumSquares="); 53 | builder.append(sumSquares()); 54 | builder.append(", mean="); 55 | builder.append(mean()); 56 | builder.append(", sd="); 57 | builder.append(sd()); 58 | builder.append("]"); 59 | return builder.toString(); 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/buffertofile/DataSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.buffertofile; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public interface DataSerializer { 8 | 9 | void serialize(T t, DataOutput out) throws IOException; 10 | 11 | T deserialize(DataInput in) throws IOException; 12 | 13 | /** 14 | * Returns 0 to indicate unknown (unbounded) capacity. Otherwise returns a 15 | * value that will be used to size internal byte arrays to receive the 16 | * serialized bytes ready for deserialization. An appropriate sizeHint will 17 | * reduce array copying (like in `ByteArrayOutputStream`) to improve 18 | * performance. 19 | * 20 | * @return size hint to avoid byte array copying. 21 | */ 22 | int sizeHint(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/buffertofile/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.buffertofile; 2 | 3 | import java.io.IOException; 4 | 5 | public interface Serializer { 6 | 7 | /** 8 | * Returns a byte array of length > 0 that is the serialization of the 9 | * given value. Note that there are performance advantages if you ensure 10 | * that the byte array produced has a length that is a multiple of four. 11 | * 12 | * @param t 13 | * value to be serialized into a byte array, should not be null. 14 | * @return a byte array of length > 0 15 | * @throws IOException 16 | * on error 17 | */ 18 | byte[] serialize(T t) throws IOException; 19 | 20 | /** 21 | * Returns a non-null instance of T from the byte array of length > 0. 22 | * 23 | * @param bytes 24 | * byte array, should have length > 0 25 | * @return instance of T 26 | * @throws IOException 27 | * on error 28 | * @throws ClassNotFoundException 29 | * if class T not found 30 | */ 31 | T deserialize(byte[] bytes) throws IOException, ClassNotFoundException; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/buffertofile/SerializerString.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.buffertofile; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.Charset; 5 | 6 | public final class SerializerString implements Serializer { 7 | 8 | private final Charset charset; 9 | 10 | public SerializerString(Charset charset) { 11 | this.charset = charset; 12 | } 13 | 14 | @Override 15 | public byte[] serialize(String s) throws IOException { 16 | return s.getBytes(charset); 17 | } 18 | 19 | @Override 20 | public String deserialize(byte[] bytes) throws IOException, ClassNotFoundException { 21 | return new String(bytes, charset); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/buffertofile/Serializers.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.buffertofile; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.DataInputStream; 6 | import java.io.DataOutputStream; 7 | import java.io.IOException; 8 | import java.io.Serializable; 9 | import java.nio.charset.Charset; 10 | 11 | import com.github.davidmoten.rx2.internal.flowable.buffertofile.SerializerBytes; 12 | import com.github.davidmoten.rx2.internal.flowable.buffertofile.SerializerJavaIO; 13 | 14 | public final class Serializers { 15 | 16 | private static final Charset UTF_8 = Charset.forName("UTF-8"); 17 | 18 | private Serializers() { 19 | // prevent initialization 20 | } 21 | 22 | public static Serializer javaIO() { 23 | // TODO use holder 24 | return new SerializerJavaIO(); 25 | } 26 | 27 | public static Serializer bytes() { 28 | // TODO use holder 29 | return new SerializerBytes(); 30 | } 31 | 32 | public static Serializer utf8() { 33 | // TODO use holder 34 | return string(UTF_8); 35 | } 36 | 37 | public static Serializer string(Charset charset) { 38 | return new SerializerString(charset); 39 | } 40 | 41 | public static Serializer from(DataSerializer ds) { 42 | return new WrappedDataSerializer(ds); 43 | } 44 | 45 | private static final class WrappedDataSerializer implements Serializer { 46 | 47 | private final DataSerializer ds; 48 | 49 | WrappedDataSerializer(DataSerializer ds) { 50 | this.ds = ds; 51 | } 52 | 53 | @Override 54 | public byte[] serialize(T t) throws IOException { 55 | ByteArrayOutputStream bytes; 56 | int cap = ds.sizeHint(); 57 | if (cap > 0) 58 | bytes = new ByteArrayOutputStream(ds.sizeHint()); 59 | else 60 | bytes = new ByteArrayOutputStream(); 61 | DataOutputStream out = new DataOutputStream(bytes); 62 | ds.serialize(t, out); 63 | out.close(); 64 | return bytes.toByteArray(); 65 | } 66 | 67 | @Override 68 | public T deserialize(byte[] bytes) throws IOException, ClassNotFoundException { 69 | ByteArrayInputStream is = new ByteArrayInputStream(bytes); 70 | DataInputStream in = new DataInputStream(is); 71 | T t = ds.deserialize(in); 72 | in.close(); 73 | return t; 74 | } 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/exceptions/AssertionException.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.exceptions; 2 | 3 | public class AssertionException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = -349922011349921601L; 6 | 7 | public AssertionException(String message) { 8 | super(message); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/exceptions/ThrowingException.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.exceptions; 2 | 3 | public final class ThrowingException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 8336479045139136638L; 6 | 7 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/flowable/Burst.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016-present, RxJava Contributors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 5 | * compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See 11 | * the License for the specific language governing permissions and limitations under the License. 12 | */ 13 | package com.github.davidmoten.rx2.flowable; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.Queue; 18 | import java.util.concurrent.ConcurrentLinkedQueue; 19 | import java.util.concurrent.atomic.AtomicLong; 20 | 21 | import org.reactivestreams.Subscriber; 22 | import org.reactivestreams.Subscription; 23 | 24 | import io.reactivex.Flowable; 25 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 26 | import io.reactivex.internal.util.BackpressureHelper; 27 | 28 | /** 29 | * Creates {@link Flowable} of a number of items followed by either an error or 30 | * completion. Cancellation has no effect on preventing emissions until the 31 | * currently outstanding requests have been met. The primary purpose for the 32 | * existence of this class is testing that an operator that calls `onError` when 33 | * processing `onNext` for instance does not emit multiple terminal events. 34 | * 35 | * @param 36 | * the value type 37 | */ 38 | public final class Burst extends Flowable { 39 | 40 | private final List items; 41 | private final Throwable error; 42 | 43 | private Burst(Throwable error, List items) { 44 | if (items.isEmpty()) { 45 | throw new IllegalArgumentException("items cannot be empty"); 46 | } 47 | for (T item : items) { 48 | if (item == null) { 49 | throw new IllegalArgumentException("items cannot include null"); 50 | } 51 | } 52 | this.error = error; 53 | this.items = items; 54 | } 55 | 56 | @Override 57 | protected void subscribeActual(final Subscriber subscriber) { 58 | subscriber.onSubscribe(new Subscription() { 59 | 60 | final Queue q = new ConcurrentLinkedQueue(items); 61 | final AtomicLong requested = new AtomicLong(); 62 | volatile boolean cancelled; 63 | 64 | @Override 65 | public void request(long n) { 66 | if (cancelled) { 67 | // required by reactive-streams-jvm 3.6 68 | return; 69 | } 70 | if (SubscriptionHelper.validate(n)) { 71 | // just for testing, don't care about perf 72 | // so no attempt made to reduce volatile reads 73 | if (BackpressureHelper.add(requested, n) == 0) { 74 | if (q.isEmpty()) { 75 | return; 76 | } 77 | while (!q.isEmpty() && requested.get() > 0) { 78 | T item = q.poll(); 79 | requested.decrementAndGet(); 80 | subscriber.onNext(item); 81 | } 82 | if (q.isEmpty()) { 83 | if (error != null) { 84 | subscriber.onError(error); 85 | } else { 86 | subscriber.onComplete(); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | @Override 94 | public void cancel() { 95 | cancelled = true; 96 | } 97 | }); 98 | 99 | } 100 | 101 | @SuppressWarnings("unchecked") 102 | public static Builder item(T item) { 103 | return items(item); 104 | } 105 | 106 | public static Builder items(T... items) { 107 | return new Builder(Arrays.asList(items)); 108 | } 109 | 110 | public static final class Builder { 111 | 112 | private final List items; 113 | private Throwable error; 114 | 115 | private Builder(List items) { 116 | this.items = items; 117 | } 118 | 119 | public Flowable error(Throwable e) { 120 | this.error = e; 121 | return create(); 122 | } 123 | 124 | public Flowable create() { 125 | return new Burst(error, items); 126 | } 127 | 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/flowable/CachedFlowable.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.flowable; 2 | 3 | 4 | import io.reactivex.Flowable; 5 | import org.reactivestreams.Subscriber; 6 | 7 | import com.github.davidmoten.rx2.internal.flowable.OnSubscribeCacheResettable; 8 | 9 | public final class CachedFlowable extends Flowable { 10 | 11 | private final OnSubscribeCacheResettable cache; 12 | 13 | public CachedFlowable(Flowable source) { 14 | this(new OnSubscribeCacheResettable(source)); 15 | } 16 | 17 | CachedFlowable(OnSubscribeCacheResettable cache) { 18 | this.cache = cache; 19 | } 20 | 21 | public CachedFlowable reset() { 22 | cache.reset(); 23 | return this; 24 | } 25 | 26 | @Override 27 | protected void subscribeActual(Subscriber subscriber) { 28 | cache.subscribe(subscriber); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/flowable/CloseableFlowableWithReset.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.flowable; 2 | 3 | import io.reactivex.Flowable; 4 | 5 | public final class CloseableFlowableWithReset { 6 | 7 | private final Flowable flowable; 8 | private final Runnable closeAction; 9 | private final Runnable resetAction; 10 | 11 | public CloseableFlowableWithReset(Flowable flowable, Runnable closeAction, Runnable resetAction) { 12 | this.flowable = flowable; 13 | this.closeAction = closeAction; 14 | this.resetAction = resetAction; 15 | } 16 | 17 | public Flowable flowable() { 18 | return flowable; 19 | } 20 | 21 | public void reset() { 22 | resetAction.run(); 23 | } 24 | 25 | public void close() { 26 | closeAction.run(); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/functions/Consumer3.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.functions; 2 | 3 | public interface Consumer3 { 4 | 5 | void accept(A a, B b, C c); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/SchedulerWithId.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | import io.reactivex.Scheduler; 8 | import io.reactivex.disposables.Disposable; 9 | 10 | public final class SchedulerWithId extends Scheduler { 11 | 12 | private final Scheduler scheduler; 13 | private final String id; 14 | private final static Pattern pattern = Pattern.compile("\\bschedId=\\[[^\\]]+\\]+\\b"); 15 | 16 | public SchedulerWithId(Scheduler scheduler, String id) { 17 | this.scheduler = scheduler; 18 | this.id = "[" + id + "]"; 19 | } 20 | 21 | @Override 22 | public Worker createWorker() { 23 | 24 | final Worker worker = scheduler.createWorker(); 25 | Worker w = new Worker() { 26 | 27 | @Override 28 | public Disposable schedule(final Runnable action, long delayTime, TimeUnit unit) { 29 | Runnable a = new Runnable() { 30 | @Override 31 | public void run() { 32 | String name = null; 33 | try { 34 | name = setThreadName(); 35 | action.run(); 36 | } finally { 37 | if (name != null) { 38 | Thread.currentThread().setName(name); 39 | } 40 | } 41 | } 42 | }; 43 | return worker.schedule(a, delayTime, unit); 44 | } 45 | 46 | @Override 47 | public void dispose() { 48 | worker.dispose(); 49 | } 50 | 51 | @Override 52 | public boolean isDisposed() { 53 | return worker.isDisposed(); 54 | } 55 | 56 | }; 57 | return w; 58 | 59 | } 60 | 61 | private String setThreadName() { 62 | String name = Thread.currentThread().getName(); 63 | String newName = updateNameWithId(name, id); 64 | Thread.currentThread().setName(newName); 65 | return name; 66 | } 67 | 68 | private static String updateNameWithId(String name, String id) { 69 | final String newName; 70 | if (name == null) { 71 | newName = id; 72 | } else { 73 | Matcher matcher = pattern.matcher(name); 74 | if (matcher.find()) { 75 | newName = name.replace(matcher.group(), "schedId=" + id); 76 | } else { 77 | newName = name + "|schedId=" + id; 78 | } 79 | } 80 | return newName; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableDoOnEmpty.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import org.reactivestreams.Publisher; 4 | import org.reactivestreams.Subscriber; 5 | import org.reactivestreams.Subscription; 6 | 7 | import com.github.davidmoten.guavamini.Preconditions; 8 | 9 | import io.reactivex.Flowable; 10 | import io.reactivex.FlowableSubscriber; 11 | import io.reactivex.exceptions.Exceptions; 12 | import io.reactivex.functions.Action; 13 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 14 | 15 | /** 16 | * Calls a consumer just before completion if the stream was empty. 17 | * 18 | * @param 19 | * the value type 20 | */ 21 | public final class FlowableDoOnEmpty extends Flowable { 22 | 23 | private final Publisher source; 24 | private final Action onEmpty; 25 | 26 | public FlowableDoOnEmpty(Publisher source, Action onEmpty) { 27 | Preconditions.checkNotNull(source, "source cannot be null"); 28 | Preconditions.checkNotNull(onEmpty, "onEmpty cannot be null"); 29 | this.source = source; 30 | this.onEmpty = onEmpty; 31 | } 32 | 33 | @Override 34 | protected void subscribeActual(Subscriber child) { 35 | source.subscribe(new DoOnEmptySubscriber(child, onEmpty)); 36 | } 37 | 38 | private static final class DoOnEmptySubscriber implements FlowableSubscriber, Subscription { 39 | 40 | private final Subscriber child; 41 | private final Action onEmpty; 42 | // mutable state 43 | private boolean done; 44 | private boolean empty = true; 45 | private Subscription parent; 46 | 47 | DoOnEmptySubscriber(Subscriber child, Action onEmpty) { 48 | this.child = child; 49 | this.onEmpty = onEmpty; 50 | } 51 | 52 | @Override 53 | public void onSubscribe(Subscription parent) { 54 | if (SubscriptionHelper.validate(this.parent, parent)) { 55 | this.parent = parent; 56 | child.onSubscribe(this); 57 | } 58 | } 59 | 60 | @Override 61 | public void onComplete() { 62 | if (done) { 63 | return; 64 | } 65 | if (empty) { 66 | try { 67 | onEmpty.run(); 68 | } catch (Throwable e) { 69 | Exceptions.throwIfFatal(e); 70 | onError(e); 71 | return; 72 | } 73 | } 74 | done = true; 75 | child.onComplete(); 76 | } 77 | 78 | @Override 79 | public void onNext(T t) { 80 | empty = false; 81 | child.onNext(t); 82 | } 83 | 84 | @Override 85 | public void onError(Throwable e) { 86 | if (done) { 87 | return; 88 | } 89 | done = true; 90 | child.onError(e); 91 | } 92 | 93 | @Override 94 | public void cancel() { 95 | parent.cancel(); 96 | } 97 | 98 | @Override 99 | public void request(long n) { 100 | parent.request(n); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableFetchPagesByRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | 6 | import io.reactivex.BackpressureStrategy; 7 | import io.reactivex.Flowable; 8 | import io.reactivex.exceptions.Exceptions; 9 | import io.reactivex.functions.Action; 10 | import io.reactivex.functions.BiFunction; 11 | import io.reactivex.functions.Consumer; 12 | import io.reactivex.functions.LongConsumer; 13 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 14 | import io.reactivex.subjects.ReplaySubject; 15 | import io.reactivex.subjects.Subject; 16 | 17 | public final class FlowableFetchPagesByRequest { 18 | 19 | private FlowableFetchPagesByRequest() { 20 | // prevent instantiation 21 | } 22 | 23 | public static Flowable create(final BiFunction> fetch, 24 | final long start, final int maxConcurrency) { 25 | return Flowable.defer(new Callable>() { 26 | @Override 27 | public Flowable call() throws Exception { 28 | // need a ReplaySubject because multiple requests can come 29 | // through before concatEager has established subscriptions to 30 | // the subject 31 | final ReplaySubject> subject = ReplaySubject.create(); 32 | final AtomicLong position = new AtomicLong(start); 33 | LongConsumer request = new LongConsumer() { 34 | @Override 35 | public void accept(final long n) throws Exception { 36 | final long pos = position.getAndAdd(n); 37 | if (SubscriptionHelper.validate(n)) { 38 | Flowable flowable; 39 | try { 40 | flowable = fetch.apply(pos, n); 41 | } catch (Throwable e) { 42 | Exceptions.throwIfFatal(e); 43 | subject.onError(e); 44 | return; 45 | } 46 | // reduce allocations by incorporating the onNext 47 | // and onComplete actions into the mutable count 48 | // object 49 | final Count count = new Count(subject, n); 50 | flowable = flowable // 51 | .doOnNext(count) // 52 | .doOnComplete(count); 53 | subject.onNext(flowable); 54 | } 55 | } 56 | }; 57 | return Flowable // 58 | .concatEager(subject.serialize() // 59 | .toFlowable(BackpressureStrategy.BUFFER), maxConcurrency, 128) // 60 | .doOnRequest(request); 61 | } 62 | }); 63 | } 64 | 65 | private static final class Count implements Consumer, Action { 66 | private final Subject subject; 67 | private final long n; 68 | 69 | // mutable 70 | private long count; 71 | 72 | Count(Subject subject, long n) { 73 | this.subject = subject; 74 | this.n = n; 75 | } 76 | 77 | @Override 78 | public void accept(Object t) throws Exception { 79 | count++; 80 | } 81 | 82 | @Override 83 | public void run() throws Exception { 84 | if (count < n) { 85 | subject.onComplete(); 86 | } 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableMapLast.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | 5 | import org.reactivestreams.Subscriber; 6 | import org.reactivestreams.Subscription; 7 | 8 | import io.reactivex.Flowable; 9 | import io.reactivex.FlowableSubscriber; 10 | import io.reactivex.exceptions.Exceptions; 11 | import io.reactivex.functions.Function; 12 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 13 | 14 | public final class FlowableMapLast extends Flowable { 15 | 16 | private final Flowable source; 17 | private final Function function; 18 | 19 | public FlowableMapLast(Flowable source, Function function) { 20 | this.source = source; 21 | this.function = function; 22 | } 23 | 24 | @Override 25 | protected void subscribeActual(Subscriber s) { 26 | source.subscribe(new MapLastSubscriber(s, function)); 27 | } 28 | 29 | private final static class MapLastSubscriber implements FlowableSubscriber, Subscription { 30 | 31 | private static final Object EMPTY = new Object(); 32 | 33 | private final Subscriber actual; 34 | private final Function function; 35 | private final AtomicBoolean firstRequest = new AtomicBoolean(true); 36 | 37 | // mutable state 38 | @SuppressWarnings("unchecked") 39 | private T value = (T) EMPTY; 40 | private Subscription parent; 41 | private boolean done; 42 | 43 | public MapLastSubscriber(Subscriber actual, Function function) { 44 | this.actual = actual; 45 | this.function = function; 46 | } 47 | 48 | @Override 49 | public void onSubscribe(Subscription subscription) { 50 | if (SubscriptionHelper.validate(this.parent, subscription)) { 51 | this.parent = subscription; 52 | actual.onSubscribe(this); 53 | } 54 | } 55 | 56 | @Override 57 | public void onNext(T t) { 58 | if (done) { 59 | return; 60 | } 61 | if (value == EMPTY) { 62 | value = t; 63 | } else { 64 | actual.onNext(value); 65 | value = t; 66 | } 67 | } 68 | 69 | @Override 70 | public void onComplete() { 71 | if (done) { 72 | return; 73 | } 74 | if (value != EMPTY) { 75 | T value2; 76 | try { 77 | value2 = function.apply(value); 78 | } catch (Throwable e) { 79 | Exceptions.throwIfFatal(e); 80 | parent.cancel(); 81 | onError(e); 82 | return; 83 | } 84 | actual.onNext(value2); 85 | } 86 | done = true; 87 | actual.onComplete(); 88 | } 89 | 90 | @Override 91 | public void onError(Throwable e) { 92 | if (done) { 93 | return; 94 | } 95 | if (value != EMPTY) { 96 | actual.onNext(value); 97 | } 98 | done = true; 99 | actual.onError(e); 100 | } 101 | 102 | @Override 103 | public void cancel() { 104 | parent.cancel(); 105 | } 106 | 107 | @Override 108 | public void request(long n) { 109 | if (SubscriptionHelper.validate(n)) { 110 | if (firstRequest.compareAndSet(true, false)) { 111 | long m = n + 1; 112 | if (m < 0) { 113 | m = Long.MAX_VALUE; 114 | } 115 | parent.request(m); 116 | } else { 117 | parent.request(n); 118 | } 119 | } 120 | } 121 | 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableMinRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | 6 | import org.reactivestreams.Subscriber; 7 | import org.reactivestreams.Subscription; 8 | 9 | import com.github.davidmoten.guavamini.Preconditions; 10 | 11 | import io.reactivex.Flowable; 12 | import io.reactivex.FlowableSubscriber; 13 | import io.reactivex.internal.fuseable.SimplePlainQueue; 14 | import io.reactivex.internal.queue.SpscLinkedArrayQueue; 15 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 16 | import io.reactivex.internal.util.BackpressureHelper; 17 | 18 | public final class FlowableMinRequest extends Flowable { 19 | 20 | private final Flowable source; 21 | private final int[] minRequest; 22 | 23 | public FlowableMinRequest(Flowable source, int[] minRequests) { 24 | Preconditions.checkArgument(minRequests.length > 0, "minRequests length must be > 0"); 25 | for (int i = 0; i < minRequests.length; i++) { 26 | Preconditions.checkArgument(minRequests[i] > 0, "each item in minRequests must be > 0"); 27 | } 28 | this.source = source; 29 | this.minRequest = minRequests; 30 | } 31 | 32 | @Override 33 | protected void subscribeActual(Subscriber child) { 34 | source.subscribe(new MinRequestSubscriber(minRequest, child)); 35 | } 36 | 37 | @SuppressWarnings("serial") 38 | private static final class MinRequestSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { 39 | 40 | private final int[] minRequests; 41 | private int requestNum; 42 | private final Subscriber child; 43 | private final AtomicLong requested = new AtomicLong(); 44 | private final SimplePlainQueue queue = new SpscLinkedArrayQueue(16); 45 | 46 | private Subscription parent; 47 | private volatile boolean done; 48 | private Throwable error; 49 | private volatile boolean cancelled; 50 | private long count; 51 | 52 | MinRequestSubscriber(int[] minRequests, Subscriber child) { 53 | this.minRequests = minRequests; 54 | this.child = child; 55 | } 56 | 57 | @Override 58 | public void onSubscribe(Subscription parent) { 59 | if (SubscriptionHelper.validate(this.parent, parent)) { 60 | this.parent = parent; 61 | child.onSubscribe(this); 62 | } 63 | } 64 | 65 | @Override 66 | public void request(long n) { 67 | if (SubscriptionHelper.validate(n)) { 68 | BackpressureHelper.add(requested, n); 69 | drain(); 70 | } 71 | } 72 | 73 | @Override 74 | public void cancel() { 75 | cancelled = true; 76 | parent.cancel(); 77 | } 78 | 79 | @Override 80 | public void onNext(T t) { 81 | queue.offer(t); 82 | drain(); 83 | } 84 | 85 | @Override 86 | public void onError(Throwable e) { 87 | error = e; 88 | done = true; 89 | drain(); 90 | } 91 | 92 | @Override 93 | public void onComplete() { 94 | done = true; 95 | drain(); 96 | } 97 | 98 | private void drain() { 99 | if (getAndIncrement() == 0) { 100 | int missed = 1; 101 | while (true) { 102 | long r = requested.get(); 103 | long e = 0; 104 | boolean d = done; 105 | while (e != r) { 106 | if (cancelled) { 107 | queue.clear(); 108 | return; 109 | } 110 | T t = queue.poll(); 111 | if (t == null) { 112 | if (d) { 113 | terminate(); 114 | return; 115 | } else { 116 | break; 117 | } 118 | } else { 119 | child.onNext(t); 120 | e++; 121 | if (count != Long.MAX_VALUE) { 122 | count--; 123 | } 124 | } 125 | d = done; 126 | } 127 | if (d && queue.isEmpty()) { 128 | terminate(); 129 | return; 130 | } 131 | if (e != 0 && r != Long.MAX_VALUE) { 132 | r = requested.addAndGet(-e); 133 | } 134 | if (r != 0 && count == 0) { 135 | // requests from parent have arrived so request some 136 | // more 137 | int min = minRequests[requestNum]; 138 | if (requestNum != minRequests.length - 1) { 139 | requestNum++; 140 | } 141 | count = Math.max(r, min); 142 | parent.request(count); 143 | } 144 | missed = addAndGet(-missed); 145 | if (missed == 0) { 146 | return; 147 | } 148 | } 149 | } 150 | } 151 | 152 | private void terminate() { 153 | parent.cancel(); 154 | Throwable err = error; 155 | if (err != null) { 156 | error = null; 157 | child.onError(err); 158 | } else { 159 | child.onComplete(); 160 | } 161 | } 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableRepeat.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | import org.reactivestreams.Subscriber; 6 | import org.reactivestreams.Subscription; 7 | 8 | import io.reactivex.Flowable; 9 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 10 | import io.reactivex.internal.util.BackpressureHelper; 11 | 12 | public final class FlowableRepeat extends Flowable { 13 | 14 | private final T value; 15 | private final long count; 16 | 17 | public FlowableRepeat(T value, long count) { 18 | this.value = value; 19 | this.count = count; 20 | } 21 | 22 | @Override 23 | protected void subscribeActual(org.reactivestreams.Subscriber child) { 24 | RepeatSubscription sub = new RepeatSubscription(child, value, count); 25 | child.onSubscribe(sub); 26 | } 27 | 28 | @SuppressWarnings("serial") 29 | private static class RepeatSubscription extends AtomicLong implements Subscription { 30 | 31 | private final Subscriber child; 32 | private final T value; 33 | private final long count; 34 | 35 | private volatile boolean cancelled; 36 | private long counter; 37 | 38 | RepeatSubscription(Subscriber child, T value, long count) { 39 | this.child = child; 40 | this.value = value; 41 | this.count = count; 42 | this.counter = count; 43 | } 44 | 45 | @Override 46 | public void request(long n) { 47 | if (SubscriptionHelper.validate(n)) { 48 | if (BackpressureHelper.add(this, n) == 0) { 49 | long requested = n; 50 | long emitted = 0; 51 | do { 52 | emitted = requested; 53 | while (requested-- > 0 && !cancelled && (count == -1 || counter-- > 0)) { 54 | child.onNext(value); 55 | } 56 | } while ((requested = this.addAndGet(-emitted)) > 0); 57 | if (count >= 0 && !cancelled) { 58 | child.onComplete(); 59 | } 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public void cancel() { 66 | this.cancelled = true; 67 | } 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableReverse.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | 6 | import io.reactivex.Flowable; 7 | import io.reactivex.functions.Function; 8 | 9 | public final class FlowableReverse { 10 | 11 | private FlowableReverse() { 12 | // prevent instantiation 13 | } 14 | 15 | @SuppressWarnings("unchecked") 16 | public static Flowable reverse(Flowable source) { 17 | return source.toList().toFlowable() 18 | .flatMapIterable((Function, Iterable>) (Function) REVERSE_LIST); 19 | } 20 | 21 | private static final Function, Iterable> REVERSE_LIST = new Function, Iterable>() { 22 | @Override 23 | public Iterable apply(List list) { 24 | return reverse(list); 25 | } 26 | }; 27 | 28 | private static Iterable reverse(final List list) { 29 | return new Iterable() { 30 | 31 | @Override 32 | public Iterator iterator() { 33 | return new Iterator() { 34 | 35 | int i = list.size(); 36 | 37 | @Override 38 | public boolean hasNext() { 39 | return i > 0; 40 | } 41 | 42 | @Override 43 | public T next() { 44 | i--; 45 | return list.get(i); 46 | } 47 | 48 | @Override 49 | public void remove() { 50 | throw new UnsupportedOperationException(); 51 | } 52 | 53 | }; 54 | } 55 | }; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableServerSocket.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.net.SocketException; 8 | import java.net.SocketTimeoutException; 9 | import java.util.concurrent.Callable; 10 | 11 | import com.github.davidmoten.guavamini.annotations.VisibleForTesting; 12 | import com.github.davidmoten.rx2.Bytes; 13 | import com.github.davidmoten.rx2.Consumers; 14 | 15 | import io.reactivex.Emitter; 16 | import io.reactivex.Flowable; 17 | import io.reactivex.functions.Action; 18 | import io.reactivex.functions.Consumer; 19 | import io.reactivex.functions.Function; 20 | import io.reactivex.functions.Predicate; 21 | 22 | public final class FlowableServerSocket { 23 | 24 | private FlowableServerSocket() { 25 | // prevent instantiation 26 | } 27 | 28 | public static Flowable> create(final Callable serverSocketFactory, 29 | final int timeoutMs, final int bufferSize, Action preAcceptAction, int acceptTimeoutMs, 30 | Predicate acceptSocket) { 31 | Function>> FlowableFactory = createFlowableFactory(timeoutMs, 32 | bufferSize, preAcceptAction, acceptSocket); 33 | return Flowable., ServerSocket>using( // 34 | createServerSocketFactory(serverSocketFactory, acceptTimeoutMs), // 35 | FlowableFactory, // 36 | new Consumer() { 37 | // Note that in java 1.6, ServerSocket does not implement 38 | // Closeable 39 | @Override 40 | public void accept(ServerSocket ss) throws Exception { 41 | ss.close(); 42 | } 43 | }, // 44 | true); 45 | } 46 | 47 | private static Callable createServerSocketFactory( 48 | final Callable serverSocketFactory, final int acceptTimeoutMs) { 49 | return new Callable() { 50 | @Override 51 | public ServerSocket call() throws Exception { 52 | return createServerSocket(serverSocketFactory, acceptTimeoutMs); 53 | } 54 | }; 55 | } 56 | 57 | private static ServerSocket createServerSocket(Callable serverSocketCreator, long timeoutMs) 58 | throws Exception { 59 | ServerSocket s = serverSocketCreator.call(); 60 | s.setSoTimeout((int) timeoutMs); 61 | return s; 62 | } 63 | 64 | private static Function>> createFlowableFactory(final int timeoutMs, 65 | final int bufferSize, final Action preAcceptAction, final Predicate acceptSocket) { 66 | return new Function>>() { 67 | @Override 68 | public Flowable> apply(ServerSocket serverSocket) { 69 | return createServerSocketFlowable(serverSocket, timeoutMs, bufferSize, preAcceptAction, acceptSocket); 70 | } 71 | }; 72 | } 73 | 74 | private static Flowable> createServerSocketFlowable(final ServerSocket serverSocket, 75 | final long timeoutMs, final int bufferSize, final Action preAcceptAction, 76 | final Predicate acceptSocket) { 77 | return Flowable.generate( // 78 | new Consumer>>() { 79 | @Override 80 | public void accept(Emitter> emitter) throws Exception { 81 | acceptConnection(timeoutMs, bufferSize, serverSocket, emitter, preAcceptAction, acceptSocket); 82 | } 83 | }); 84 | } 85 | 86 | private static void acceptConnection(long timeoutMs, int bufferSize, ServerSocket ss, 87 | Emitter> emitter, Action preAcceptAction, Predicate acceptSocket) { 88 | Socket socket; 89 | while (true) { 90 | try { 91 | preAcceptAction.run(); 92 | socket = ss.accept(); 93 | if (!acceptSocket.test(socket)) { 94 | closeQuietly(socket); 95 | } else { 96 | emitter.onNext(createSocketFlowable(socket, timeoutMs, bufferSize)); 97 | break; 98 | } 99 | } catch (SocketTimeoutException e) { 100 | // timed out so will loop around again 101 | } catch (Throwable e) { 102 | // if the server socket has been closed then this is most likely 103 | // an unsubscribe so we don't try to report an error which would 104 | // just end up in RxJavaPlugins.onError as a stack trace in the 105 | // console. 106 | if (e instanceof SocketException && ("Socket closed".equals(e.getMessage()) 107 | || "Socket operation on nonsocket: configureBlocking".equals(e.getMessage()))) { 108 | break; 109 | } else { 110 | // unknown problem 111 | emitter.onError(e); 112 | break; 113 | } 114 | } 115 | } 116 | } 117 | 118 | @VisibleForTesting 119 | static void closeQuietly(Socket socket) { 120 | try { 121 | socket.close(); 122 | } catch (IOException e) { 123 | // ignore exception 124 | } 125 | } 126 | 127 | private static Flowable createSocketFlowable(final Socket socket, long timeoutMs, final int bufferSize) { 128 | setTimeout(socket, timeoutMs); 129 | return Flowable.using( // 130 | new Callable() { 131 | @Override 132 | public InputStream call() throws Exception { 133 | return socket.getInputStream(); 134 | } 135 | }, // 136 | new Function>() { 137 | @Override 138 | public Flowable apply(InputStream is) { 139 | return Bytes.from(is, bufferSize); 140 | } 141 | }, // 142 | Consumers.close(), // 143 | true); 144 | } 145 | 146 | private static void setTimeout(Socket socket, long timeoutMs) { 147 | try { 148 | socket.setSoTimeout((int) timeoutMs); 149 | } catch (SocketException e) { 150 | throw new RuntimeException(e); 151 | } 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableStringInputStream.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.io.*; 4 | import java.nio.charset.Charset; 5 | import java.util.concurrent.atomic.AtomicReference; 6 | 7 | import org.reactivestreams.*; 8 | 9 | import io.reactivex.FlowableSubscriber; 10 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 11 | 12 | /** 13 | * @author David Karnok 14 | * 15 | */ 16 | public final class FlowableStringInputStream { 17 | 18 | private FlowableStringInputStream() { 19 | throw new IllegalStateException("No instances!"); 20 | } 21 | 22 | public static InputStream createInputStream(Publisher source, Charset charset) { 23 | StringInputStream parent = new StringInputStream(charset); 24 | source.subscribe(parent); 25 | return parent; 26 | } 27 | 28 | static final class StringInputStream extends InputStream 29 | implements FlowableSubscriber { 30 | 31 | final AtomicReference upstream; 32 | 33 | final Charset charset; 34 | 35 | volatile byte[] bytes; 36 | 37 | int index; 38 | 39 | volatile boolean done; 40 | Throwable error; 41 | 42 | StringInputStream(Charset charset) { 43 | this.charset = charset; 44 | upstream = new AtomicReference(); 45 | } 46 | 47 | @Override 48 | public void onSubscribe(Subscription s) { 49 | if (SubscriptionHelper.setOnce(upstream, s)) { 50 | s.request(1); 51 | } 52 | } 53 | 54 | @Override 55 | public void onNext(String t) { 56 | bytes = t.getBytes(charset); 57 | synchronized (this) { 58 | notifyAll(); 59 | } 60 | } 61 | 62 | @Override 63 | public void onError(Throwable t) { 64 | error = t; 65 | done = true; 66 | synchronized (this) { 67 | notifyAll(); 68 | } 69 | } 70 | 71 | @Override 72 | public void onComplete() { 73 | done = true; 74 | synchronized (this) { 75 | notifyAll(); 76 | } 77 | } 78 | 79 | @Override 80 | public int read() throws IOException { 81 | for (;;) { 82 | byte[] a = awaitBufferIfNecessary(); 83 | if (a == null) { 84 | Throwable ex = error; 85 | if (ex != null) { 86 | if (ex instanceof IOException) { 87 | throw (IOException)ex; 88 | } 89 | throw new IOException(ex); 90 | } 91 | return -1; 92 | } 93 | int idx = index; 94 | if (idx == a.length) { 95 | index = 0; 96 | bytes = null; 97 | upstream.get().request(1); 98 | } else { 99 | int result = a[idx] & 0xFF; 100 | index = idx + 1; 101 | return result; 102 | } 103 | } 104 | } 105 | 106 | byte[] awaitBufferIfNecessary() throws IOException { 107 | byte[] a = bytes; 108 | if (a == null) { 109 | synchronized (this) { 110 | for (;;) { 111 | boolean d = done; 112 | a = bytes; 113 | if (a != null) { 114 | break; 115 | } 116 | if (d || upstream.get() == SubscriptionHelper.CANCELLED) { 117 | break; 118 | } 119 | try { 120 | wait(); 121 | } catch (InterruptedException ex) { 122 | if (upstream.get() != SubscriptionHelper.CANCELLED) { 123 | InterruptedIOException exc = new InterruptedIOException(); 124 | exc.initCause(ex); 125 | throw exc; 126 | } 127 | break; 128 | } 129 | } 130 | } 131 | } 132 | return a; 133 | } 134 | 135 | @Override 136 | public int read(byte[] b, int off, int len) throws IOException { 137 | if (off < 0 || len < 0 || off >= b.length || off + len > b.length) { 138 | throw new IndexOutOfBoundsException("b.length=" + b.length + ", off=" + off + ", len=" + len); 139 | } 140 | for (;;) { 141 | byte[] a = awaitBufferIfNecessary(); 142 | if (a == null) { 143 | Throwable ex = error; 144 | if (ex != null) { 145 | if (ex instanceof IOException) { 146 | throw (IOException)ex; 147 | } 148 | throw new IOException(ex); 149 | } 150 | return -1; 151 | } 152 | int idx = index; 153 | if (idx == a.length) { 154 | index = 0; 155 | bytes = null; 156 | upstream.get().request(1); 157 | } else { 158 | int r = 0; 159 | while (idx < a.length && len > 0) { 160 | b[off] = a[idx]; 161 | idx++; 162 | off++; 163 | r++; 164 | len--; 165 | } 166 | index = idx; 167 | return r; 168 | } 169 | } 170 | } 171 | 172 | @Override 173 | public int available() throws IOException { 174 | byte[] a = bytes; 175 | int idx = index; 176 | return a != null ? Math.max(0, a.length - idx) : 0; 177 | } 178 | 179 | @Override 180 | public void close() throws IOException { 181 | SubscriptionHelper.cancel(upstream); 182 | synchronized (this) { 183 | notifyAll(); 184 | } 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/FlowableWindowMinMax.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Comparator; 5 | import java.util.Deque; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import org.reactivestreams.Subscriber; 10 | import org.reactivestreams.Subscription; 11 | 12 | import com.github.davidmoten.guavamini.Preconditions; 13 | 14 | import io.reactivex.Flowable; 15 | import io.reactivex.FlowableSubscriber; 16 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 17 | 18 | /** 19 | * Uses a double-ended queue and collapses entries when they are redundant 20 | * (whenever a value is added to the queue all values at the end of the queue 21 | * that are greater or equal to that value are removed). 22 | * 23 | * @param 24 | * generic type of stream emissions 25 | */ 26 | public final class FlowableWindowMinMax extends Flowable { 27 | 28 | private final Flowable source; 29 | private final int windowSize; 30 | private final Comparator comparator; 31 | private final Metric metric; 32 | 33 | public FlowableWindowMinMax(Flowable source, int windowSize, Comparator comparator, Metric metric) { 34 | Preconditions.checkArgument(windowSize > 0, "windowSize must be greater than zero"); 35 | Preconditions.checkNotNull(comparator, "comparator cannot be null"); 36 | Preconditions.checkNotNull(metric, "metric cannot be null"); 37 | this.source = source; 38 | this.windowSize = windowSize; 39 | this.comparator = comparator; 40 | this.metric = metric; 41 | } 42 | 43 | @Override 44 | protected void subscribeActual(org.reactivestreams.Subscriber child) { 45 | source.subscribe(new WindowMinMaxSubscriber(windowSize, comparator, metric, child)); 46 | } 47 | 48 | private static final class WindowMinMaxSubscriber implements FlowableSubscriber, Subscription { 49 | 50 | private final int windowSize; 51 | private final Comparator comparator; 52 | private final Metric metric; 53 | private final Subscriber child; 54 | 55 | // map index to value 56 | private final Map values; 57 | 58 | // queue of indices 59 | private final Deque indices; 60 | 61 | private long count = 0; 62 | private Subscription parent; 63 | 64 | WindowMinMaxSubscriber(int windowSize, Comparator comparator, Metric metric, 65 | Subscriber child) { 66 | this.windowSize = windowSize; 67 | this.comparator = comparator; 68 | this.metric = metric; 69 | this.child = child; 70 | this.values = new HashMap(windowSize); 71 | this.indices = new ArrayDeque(windowSize); 72 | } 73 | 74 | @Override 75 | public void onSubscribe(Subscription parent) { 76 | if (SubscriptionHelper.validate(this.parent, parent)) { 77 | this.parent = parent; 78 | child.onSubscribe(this); 79 | parent.request(windowSize - 1); 80 | } 81 | } 82 | 83 | @Override 84 | public void request(long n) { 85 | if (SubscriptionHelper.validate(n)) { 86 | parent.request(n); 87 | } 88 | } 89 | 90 | @Override 91 | public void cancel() { 92 | parent.cancel(); 93 | // would be nice to clear the window here but would have performance 94 | // impact because would need to worry about allowing concurrent 95 | // changes to `indices` and `map` 96 | } 97 | 98 | @Override 99 | public void onComplete() { 100 | child.onComplete(); 101 | } 102 | 103 | @Override 104 | public void onError(Throwable e) { 105 | child.onError(e); 106 | } 107 | 108 | @Override 109 | public void onNext(T t) { 110 | count++; 111 | // add to queue 112 | addToQueue(t); 113 | if (count >= windowSize) { 114 | // emit max 115 | 116 | // head of queue is max 117 | Long head = indices.peekFirst(); 118 | final T value; 119 | if (head == count - windowSize) { 120 | // if window past that index then remove from map 121 | values.remove(indices.pollFirst()); 122 | value = values.get(indices.peekFirst()); 123 | } else { 124 | value = values.get(head); 125 | } 126 | child.onNext(value); 127 | } 128 | } 129 | 130 | private void addToQueue(T t) { 131 | Long v; 132 | while ((v = indices.peekLast()) != null && compare(t, values.get(v)) <= 0) { 133 | values.remove(indices.pollLast()); 134 | } 135 | values.put(count, t); 136 | indices.offerLast(count); 137 | } 138 | 139 | private int compare(T a, T b) { 140 | if (metric == Metric.MIN) { 141 | return comparator.compare(a, b); 142 | } else { 143 | return comparator.compare(b, a); 144 | } 145 | } 146 | } 147 | 148 | public enum Metric { 149 | MIN, MAX; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/OnSubscribeCacheResettable.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import io.reactivex.Flowable; 4 | import org.reactivestreams.Subscriber; 5 | 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | public final class OnSubscribeCacheResettable{ 9 | 10 | private final AtomicBoolean refresh = new AtomicBoolean(true); 11 | private final Flowable source; 12 | private volatile Flowable current; 13 | 14 | public OnSubscribeCacheResettable(Flowable source) { 15 | this.source = source; 16 | this.current = source; 17 | } 18 | 19 | public void subscribe(final Subscriber subscriber) { 20 | if (refresh.compareAndSet(true, false)) { 21 | current = source.cache(); 22 | } 23 | current.subscribe(subscriber); 24 | } 25 | 26 | public void reset() { 27 | refresh.set(true); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/TransformerDecode.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.CharBuffer; 5 | import java.nio.charset.CharacterCodingException; 6 | import java.nio.charset.CharsetDecoder; 7 | import java.nio.charset.CoderResult; 8 | import java.util.concurrent.Callable; 9 | 10 | import io.reactivex.BackpressureStrategy; 11 | import io.reactivex.FlowableEmitter; 12 | import io.reactivex.FlowableTransformer; 13 | import io.reactivex.functions.BiPredicate; 14 | import io.reactivex.functions.Function3; 15 | 16 | public final class TransformerDecode { 17 | 18 | private TransformerDecode() { 19 | // prevent instantiation 20 | } 21 | 22 | public static FlowableTransformer decode(final CharsetDecoder decoder, 23 | BackpressureStrategy backpressureStrategy, int batchSize) { 24 | Callable initialState = new Callable() { 25 | 26 | @Override 27 | public ByteBuffer call() { 28 | return null; 29 | } 30 | }; 31 | Function3, ByteBuffer> transition = new Function3, ByteBuffer>() { 32 | 33 | @Override 34 | public ByteBuffer apply(ByteBuffer last, byte[] next, FlowableEmitter o) { 35 | Result result = process(next, last, false, decoder, o); 36 | return result.leftOver; 37 | } 38 | }; 39 | BiPredicate> completion = new BiPredicate>() { 40 | 41 | @Override 42 | public boolean test(ByteBuffer last, FlowableEmitter subscriber) { 43 | return process(null, last, true, decoder, subscriber).canEmitFurther; 44 | } 45 | }; 46 | 47 | return com.github.davidmoten.rx2.flowable.Transformers.stateMachine(initialState, transition, completion, 48 | backpressureStrategy, batchSize); 49 | } 50 | 51 | private static final class Result { 52 | final ByteBuffer leftOver; 53 | final boolean canEmitFurther; 54 | 55 | Result(ByteBuffer leftOver, boolean canEmitFurther) { 56 | this.leftOver = leftOver; 57 | this.canEmitFurther = canEmitFurther; 58 | } 59 | 60 | } 61 | 62 | public static Result process(byte[] next, ByteBuffer last, boolean endOfInput, CharsetDecoder decoder, 63 | FlowableEmitter emitter) { 64 | if (emitter.isCancelled()) 65 | return new Result(null, false); 66 | 67 | ByteBuffer bb; 68 | if (last != null) { 69 | if (next != null) { 70 | // merge leftover in front of the next bytes 71 | bb = ByteBuffer.allocate(last.remaining() + next.length); 72 | bb.put(last); 73 | bb.put(next); 74 | bb.flip(); 75 | } else { // next == null 76 | bb = last; 77 | } 78 | } else { // last == null 79 | if (next != null) { 80 | bb = ByteBuffer.wrap(next); 81 | } else { // next == null 82 | return new Result(null, true); 83 | } 84 | } 85 | 86 | CharBuffer cb = CharBuffer.allocate((int) (bb.limit() * decoder.averageCharsPerByte())); 87 | CoderResult cr = decoder.decode(bb, cb, endOfInput); 88 | cb.flip(); 89 | 90 | if (cr.isError()) { 91 | try { 92 | cr.throwException(); 93 | } catch (CharacterCodingException e) { 94 | emitter.onError(e); 95 | return new Result(null, false); 96 | } 97 | } 98 | 99 | ByteBuffer leftOver; 100 | if (bb.remaining() > 0) { 101 | leftOver = bb; 102 | } else { 103 | leftOver = null; 104 | } 105 | 106 | String string = cb.toString(); 107 | if (!string.isEmpty()) 108 | emitter.onNext(string); 109 | 110 | return new Result(leftOver, true); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/TransformerStringSplit.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.regex.Pattern; 5 | 6 | import com.github.davidmoten.rx2.Callables; 7 | import com.github.davidmoten.rx2.flowable.Transformers; 8 | 9 | import io.reactivex.BackpressureStrategy; 10 | import io.reactivex.FlowableEmitter; 11 | import io.reactivex.FlowableTransformer; 12 | import io.reactivex.functions.BiPredicate; 13 | import io.reactivex.functions.Function3; 14 | 15 | public final class TransformerStringSplit { 16 | 17 | private TransformerStringSplit() { 18 | // prevent instantiation 19 | } 20 | 21 | public static FlowableTransformer split(final String pattern, final Pattern compiledPattern, 22 | final BackpressureStrategy backpressureStrategy, int batchSize) { 23 | Callable initialState = Callables.constant(null); 24 | Function3, String> transition = new Function3, String>() { 25 | 26 | @Override 27 | public String apply(String leftOver, String s, FlowableEmitter emitter) { 28 | // prepend leftover to the string before splitting 29 | if (leftOver != null) { 30 | s = leftOver + s; 31 | } 32 | 33 | String[] parts; 34 | if (compiledPattern != null) { 35 | parts = compiledPattern.split(s, -1); 36 | } else { 37 | parts = s.split(pattern, -1); 38 | } 39 | 40 | // can emit all parts except the last part because it hasn't 41 | // been terminated by the pattern/end-of-stream yet 42 | for (int i = 0; i < parts.length - 1; i++) { 43 | if (emitter.isCancelled()) { 44 | // won't be used so can return null 45 | return null; 46 | } 47 | emitter.onNext(parts[i]); 48 | } 49 | 50 | // we have to assign the last part as leftOver because we 51 | // don't know if it has been terminated yet 52 | return parts[parts.length - 1]; 53 | } 54 | }; 55 | 56 | BiPredicate> completion = new BiPredicate>() { 57 | 58 | @Override 59 | public boolean test(String leftOver, FlowableEmitter emitter) { 60 | if (leftOver != null && !emitter.isCancelled()) 61 | emitter.onNext(leftOver); 62 | // TODO is this check needed? 63 | if (!emitter.isCancelled()) 64 | emitter.onComplete(); 65 | return true; 66 | } 67 | }; 68 | return Transformers.stateMachine(initialState, transition, completion, backpressureStrategy, batchSize); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/MemoryMappedFile.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import java.io.File; 4 | import java.io.RandomAccessFile; 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.lang.reflect.Method; 7 | import java.nio.channels.FileChannel; 8 | 9 | import com.github.davidmoten.guavamini.annotations.VisibleForTesting; 10 | 11 | import sun.misc.Unsafe; 12 | import sun.nio.ch.FileChannelImpl; 13 | 14 | @SuppressWarnings("restriction") 15 | public final class MemoryMappedFile { 16 | 17 | private static final Unsafe unsafe; 18 | private static final Method mmap; 19 | private static final Method unmmap; 20 | private static final int BYTE_ARRAY_OFFSET; 21 | 22 | private final File file; 23 | private final long size; 24 | private long addr; 25 | 26 | static { 27 | unsafe = UnsafeAccess.unsafe(); 28 | mmap = getMethod(FileChannelImpl.class, "map0", int.class, long.class, long.class); 29 | unmmap = getMethod(FileChannelImpl.class, "unmap0", long.class, long.class); 30 | BYTE_ARRAY_OFFSET = unsafe.arrayBaseOffset(byte[].class); 31 | } 32 | 33 | public MemoryMappedFile(File file, long len) { 34 | this.file = file; 35 | this.size = roundTo4096(len); 36 | mapAndSetOffset(); 37 | } 38 | 39 | // Bundle reflection calls to get access to the given method 40 | @VisibleForTesting 41 | static Method getMethod(Class cls, String name, Class... params) { 42 | Method m; 43 | try { 44 | m = cls.getDeclaredMethod(name, params); 45 | } catch (Exception e) { 46 | throw new RuntimeException(e); 47 | } 48 | m.setAccessible(true); 49 | return m; 50 | } 51 | 52 | // Round to next 4096 bytes 53 | private static long roundTo4096(long i) { 54 | return (i + 0xfffL) & ~0xfffL; 55 | } 56 | 57 | // Given that the location and size have been set, map that location 58 | // for the given length and set this.addr to the returned offset 59 | private void mapAndSetOffset() { 60 | try { 61 | final RandomAccessFile backingFile = new RandomAccessFile(this.file, "rw"); 62 | backingFile.setLength(this.size); 63 | final FileChannel ch = backingFile.getChannel(); 64 | this.addr = (Long) mmap.invoke(ch, 1, 0L, this.size); 65 | 66 | ch.close(); 67 | backingFile.close(); 68 | } catch (Exception e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | 73 | // // Callers should synchronize to avoid calls in the middle of this, but 74 | // // it is undesirable to synchronize w/ all access methods. 75 | // public void remap(long nLen) throws Exception { 76 | // unmmap.invoke(null, addr, this.size); 77 | // this.size = roundTo4096(nLen); 78 | // mapAndSetOffset(); 79 | // } 80 | 81 | public void close() { 82 | try { 83 | unmmap.invoke(null, addr, this.size); 84 | if (!file.delete()) { 85 | throw new RuntimeException("could not delete " + file); 86 | } 87 | } catch (IllegalAccessException e) { 88 | throw new RuntimeException(e); 89 | } catch (IllegalArgumentException e) { 90 | throw new RuntimeException(e); 91 | } catch (InvocationTargetException e) { 92 | throw new RuntimeException(e); 93 | } 94 | } 95 | 96 | public int getInt(long pos) { 97 | return unsafe.getInt(pos + addr); 98 | } 99 | 100 | public void putByte(long pos, byte val) { 101 | unsafe.putByte(pos + addr, val); 102 | } 103 | 104 | public void putInt(long pos, int val) { 105 | unsafe.putInt(pos + addr, val); 106 | } 107 | 108 | public void putOrderedInt(long pos, int val) { 109 | unsafe.putOrderedInt(null, pos + addr, val); 110 | } 111 | 112 | public int getIntVolatile(long pos) { 113 | return unsafe.getIntVolatile(null, pos + addr); 114 | } 115 | 116 | // May want to have offset & length within data as well, for both of these 117 | public void getBytes(long pos, byte[] data, long offset, long length) { 118 | unsafe.copyMemory(null, pos + addr, data, BYTE_ARRAY_OFFSET + offset, length); 119 | } 120 | 121 | public void putBytes(long pos, byte[] data, long offset, long length) { 122 | unsafe.copyMemory(data, BYTE_ARRAY_OFFSET + offset, null, pos + addr, length); 123 | } 124 | 125 | public byte getByte(long pos) { 126 | return unsafe.getByte(pos + addr); 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/Page.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | 6 | public final class Page { 7 | 8 | private static final boolean debug = false; 9 | 10 | private final int pageSize; 11 | private final MemoryMappedFile mm; 12 | 13 | public Page(File file, int pageSize) { 14 | this.pageSize = pageSize; 15 | this.mm = new MemoryMappedFile(file, pageSize); 16 | } 17 | 18 | public int length() { 19 | return pageSize; 20 | } 21 | 22 | public void put(int position, byte[] bytes, int start, int length) { 23 | if (debug) 24 | println("put at " + this.hashCode() + ":" + position + " of " 25 | + Arrays.toString(Arrays.copyOfRange(bytes, start, start + length))); 26 | mm.putBytes(position, bytes, start, length); 27 | } 28 | 29 | public void putIntOrdered(int writePosition, int value) { 30 | if (debug) 31 | println("putIntOrdered at " + this.hashCode() + ":" + writePosition + " of " + value); 32 | mm.putOrderedInt(writePosition, value); 33 | } 34 | 35 | public void putInt(int writePosition, int value) { 36 | if (debug) 37 | println("putInt at " + this.hashCode() + ":" + writePosition + " of " + value); 38 | mm.putInt(writePosition, value); 39 | } 40 | 41 | public void get(byte[] dst, int offset, int readPosition, int length) { 42 | if (debug) 43 | println("getting at " + this.hashCode() + ":" + readPosition + " length=" + length); 44 | mm.getBytes(readPosition, dst, offset, length); 45 | } 46 | 47 | public int getIntVolatile(int readPosition) { 48 | int n = mm.getIntVolatile(readPosition); 49 | if (debug) 50 | println("getting int volatile at " + this.hashCode() + ":" + readPosition + "=" + n); 51 | return n; 52 | } 53 | 54 | public void close() { 55 | mm.close(); 56 | } 57 | 58 | static void println(String s) { 59 | System.out.println(Thread.currentThread().getName() + ": " + s); 60 | } 61 | 62 | public int avail(int position) { 63 | return pageSize - position; 64 | } 65 | 66 | public int getInt(int readPosition) { 67 | return mm.getInt(readPosition); 68 | } 69 | 70 | public void putByte(int currentWritePosition, byte b) { 71 | mm.putByte(currentWritePosition, b); 72 | } 73 | 74 | public byte getByte(int readPosition) { 75 | return mm.getByte(readPosition); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/Pages.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import java.io.File; 4 | import java.util.concurrent.Callable; 5 | 6 | import com.github.davidmoten.guavamini.Preconditions; 7 | 8 | import io.reactivex.internal.fuseable.SimplePlainQueue; 9 | import io.reactivex.internal.queue.SpscLinkedArrayQueue; 10 | 11 | public final class Pages { 12 | 13 | private static final boolean CHECK = false; 14 | 15 | private static final int QUEUE_INITIAL_CAPACITY = 16; 16 | private static final byte[] EMPTY = new byte[0]; 17 | 18 | private final Callable fileFactory; 19 | private final int pageSize; 20 | 21 | // read queue must be SPSC because is added to from the write thread 22 | private final SimplePlainQueue queue = new SpscLinkedArrayQueue(QUEUE_INITIAL_CAPACITY); 23 | 24 | Page writePage; 25 | int writePosition; 26 | 27 | Page readPage; 28 | int readPosition; 29 | 30 | Page markPage; 31 | int markPosition; 32 | 33 | public Pages(Callable fileFactory, int pageSize) { 34 | Preconditions.checkArgument(pageSize >= 4); 35 | Preconditions.checkArgument(pageSize % 4 == 0); 36 | this.fileFactory = fileFactory; 37 | this.pageSize = pageSize; 38 | } 39 | 40 | public int avail() { 41 | return writePage().avail(writePosition); 42 | } 43 | 44 | public void markForRewriteAndAdvance4Bytes() { 45 | markPage = writePage(); 46 | markPosition = writePosition; 47 | writePosition += 4; 48 | // putInt(markPage, 0); 49 | } 50 | 51 | public void putInt(int value) { 52 | putInt(writePage(), value); 53 | } 54 | 55 | private void putInt(Page page, int value) { 56 | if (CHECK) { 57 | int avail = page.length() - writePosition; 58 | if (avail < 0) 59 | throw new RuntimeException("unexpected"); 60 | } 61 | page.putInt(writePosition, value); 62 | writePosition += 4; 63 | } 64 | 65 | public void put(byte[] bytes, int offset, int length) { 66 | Page page = writePage(); 67 | if (CHECK) { 68 | if (length == 0) 69 | throw new IllegalArgumentException(); 70 | int avail = page.length() - writePosition; 71 | if (avail < 0) 72 | throw new RuntimeException("unexpected"); 73 | } 74 | page.put(writePosition, bytes, offset, length); 75 | writePosition += length; 76 | } 77 | 78 | public void putIntOrderedAtRewriteMark(int value) { 79 | // if there is any space at all in current page then it will be enough 80 | // for 4 bytes because we pad all offerings to the queue 81 | markPage.putIntOrdered(markPosition, value); 82 | markPage = null; 83 | } 84 | 85 | private Page writePage() { 86 | if (writePage == null || writePosition == pageSize) { 87 | createNewPage(); 88 | } 89 | return writePage; 90 | } 91 | 92 | private void createNewPage() { 93 | File file; 94 | try { 95 | file = fileFactory.call(); 96 | } catch (Exception e) { 97 | throw new RuntimeException(e); 98 | } 99 | writePage = new Page(file, pageSize); 100 | writePosition = 0; 101 | queue.offer(writePage); 102 | // System.out.println(Thread.currentThread().getName() + ": created 103 | // page " 104 | // + currentWritePage.hashCode()); 105 | } 106 | 107 | public int getInt() { 108 | if (readPage() == null) { 109 | return -1; 110 | } 111 | int rp = readPosition; 112 | if (CHECK) { 113 | int avail = readPage.length() - rp; 114 | if (avail < 4) 115 | throw new RuntimeException("unexpected"); 116 | } 117 | readPosition = rp + 4; 118 | return readPage.getInt(rp); 119 | } 120 | 121 | public byte[] get(int length) { 122 | byte[] result = new byte[length]; 123 | if (readPage() == null) { 124 | return EMPTY; 125 | } 126 | if (CHECK) { 127 | int avail = readPage.length() - readPosition; 128 | if (avail < length) 129 | throw new RuntimeException("unexpected"); 130 | } 131 | readPage.get(result, 0, readPosition, length); 132 | readPosition += length; 133 | return result; 134 | } 135 | 136 | private Page readPage() { 137 | if (readPage == null || readPosition >= pageSize) { 138 | if (readPage != null) { 139 | readPage.close(); 140 | } 141 | readPage = queue.poll(); 142 | readPosition = readPosition % pageSize; 143 | } 144 | return readPage; 145 | } 146 | 147 | public void putByte(byte b) { 148 | Page page = writePage(); 149 | if (CHECK) { 150 | int avail = page.length() - writePosition; 151 | if (avail < 0) 152 | throw new RuntimeException("unexpected"); 153 | } 154 | page.putByte(writePosition, b); 155 | writePosition += 1; 156 | } 157 | 158 | public byte getByte() { 159 | Page page = readPage(); 160 | if (CHECK) { 161 | int avail = page.length() - readPosition; 162 | if (avail < 1) 163 | throw new RuntimeException("unexpected"); 164 | } 165 | byte result = page.getByte(readPosition); 166 | readPosition += 1; 167 | return result; 168 | } 169 | 170 | public void moveReadPosition(int forward) { 171 | readPosition += forward; 172 | } 173 | 174 | public int getIntVolatile() { 175 | if (readPage() == null) { 176 | return -1; 177 | } else { 178 | int result = readPage.getIntVolatile(readPosition); 179 | readPosition += 4; 180 | return result; 181 | } 182 | } 183 | 184 | public void moveWritePosition(int forward) { 185 | writePosition += forward; 186 | } 187 | 188 | public void close() { 189 | // called from read thread 190 | if (readPage != null) { 191 | readPage.close(); 192 | readPage = null; 193 | } 194 | Page page; 195 | while ((page = queue.poll()) != null) { 196 | page.close(); 197 | } 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/SerializerBytes.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import java.io.IOException; 4 | 5 | import com.github.davidmoten.rx2.buffertofile.Serializer; 6 | 7 | public final class SerializerBytes implements Serializer { 8 | 9 | @Override 10 | public byte[] serialize(byte[] t) throws IOException { 11 | return t; 12 | } 13 | 14 | @Override 15 | public byte[] deserialize(byte[] bytes) throws ClassNotFoundException, IOException { 16 | return bytes; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/SerializerJavaIO.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | import java.io.Serializable; 9 | 10 | import com.github.davidmoten.rx2.buffertofile.Serializer; 11 | 12 | public final class SerializerJavaIO implements Serializer { 13 | 14 | @Override 15 | public byte[] serialize(Serializable t) throws IOException { 16 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 17 | ObjectOutputStream oos = new ObjectOutputStream(bos); 18 | oos.writeObject(t); 19 | bos.close(); 20 | return bos.toByteArray(); 21 | } 22 | 23 | @Override 24 | public T deserialize(byte[] bytes) throws ClassNotFoundException, IOException { 25 | ByteArrayInputStream bis = new ByteArrayInputStream(bytes); 26 | ObjectInputStream ois = null; 27 | try { 28 | ois = new ObjectInputStream(bis); 29 | @SuppressWarnings("unchecked") 30 | T t = (T) ois.readObject(); 31 | return t; 32 | } finally { 33 | if (ois != null) { 34 | ois.close(); 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/UnsafeAccess.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import sun.misc.Unsafe; 6 | 7 | @SuppressWarnings("restriction") 8 | public final class UnsafeAccess { 9 | 10 | private UnsafeAccess() { 11 | // prevent instantiation 12 | } 13 | 14 | private static Unsafe unsafe; 15 | 16 | static { 17 | try { 18 | Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); 19 | singleoneInstanceField.setAccessible(true); 20 | unsafe = (Unsafe) singleoneInstanceField.get(null); 21 | } catch (Exception e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | public static Unsafe unsafe() { 27 | return unsafe; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/internal/observable/OnSubscribeCacheResetable.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.observable; 2 | 3 | import io.reactivex.Observable; 4 | import io.reactivex.Observer; 5 | 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | public final class OnSubscribeCacheResetable{ 9 | 10 | private final AtomicBoolean refresh = new AtomicBoolean(true); 11 | private final Observable source; 12 | private volatile Observable current; 13 | 14 | public OnSubscribeCacheResetable(Observable source) { 15 | this.source = source; 16 | this.current = source; 17 | } 18 | 19 | public void subscribe(final Observer observer) { 20 | if (refresh.compareAndSet(true, false)) { 21 | current = source.cache(); 22 | } 23 | current.subscribe(observer); 24 | } 25 | 26 | public void reset() { 27 | refresh.set(true); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/observable/CachedObservable.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.observable; 2 | 3 | import com.github.davidmoten.rx2.internal.observable.OnSubscribeCacheResetable; 4 | 5 | import io.reactivex.Observable; 6 | import io.reactivex.Observer; 7 | 8 | public final class CachedObservable extends Observable { 9 | 10 | private final OnSubscribeCacheResetable cache; 11 | 12 | public CachedObservable(Observable source) { 13 | this(new OnSubscribeCacheResetable(source)); 14 | } 15 | 16 | CachedObservable(OnSubscribeCacheResetable cache) { 17 | this.cache = cache; 18 | } 19 | 20 | public CachedObservable reset() { 21 | cache.reset(); 22 | return this; 23 | } 24 | 25 | @Override 26 | protected void subscribeActual(Observer observer) { 27 | cache.subscribe(observer); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/observable/CloseableObservableWithReset.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.observable; 2 | 3 | import io.reactivex.Observable; 4 | 5 | public final class CloseableObservableWithReset { 6 | 7 | private final Observable observable; 8 | private final Runnable closeAction; 9 | private final Runnable resetAction; 10 | 11 | public CloseableObservableWithReset(Observable observable, Runnable closeAction, Runnable resetAction) { 12 | this.observable = observable; 13 | this.closeAction = closeAction; 14 | this.resetAction = resetAction; 15 | } 16 | 17 | public Observable observable() { 18 | return observable; 19 | } 20 | 21 | public void reset() { 22 | resetAction.run(); 23 | } 24 | 25 | public void close() { 26 | closeAction.run(); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/observable/Transformers.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.observable; 2 | 3 | import com.github.davidmoten.rx2.buffertofile.Options; 4 | 5 | public final class Transformers { 6 | 7 | private Transformers() { 8 | // prevent instantiation 9 | } 10 | 11 | public static Options.BuilderObservable onBackpressureBufferToFile() { 12 | return Options.builderObservable(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/util/Pair.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.util; 2 | 3 | public class Pair { 4 | 5 | private final T a; 6 | private final S b; 7 | 8 | public Pair(T a, S b) { 9 | this.a = a; 10 | this.b = b; 11 | } 12 | 13 | public static Pair create(T t, S s) { 14 | return new Pair(t, s); 15 | } 16 | 17 | public T a() { 18 | return a; 19 | } 20 | 21 | public S b() { 22 | return b; 23 | } 24 | 25 | public T left() { 26 | return a; 27 | } 28 | 29 | public S right() { 30 | return b; 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | final int prime = 31; 36 | int result = 1; 37 | result = prime * result + ((a == null) ? 0 : a.hashCode()); 38 | result = prime * result + ((b == null) ? 0 : b.hashCode()); 39 | return result; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (this == obj) 45 | return true; 46 | if (obj == null) 47 | return false; 48 | if (getClass() != obj.getClass()) 49 | return false; 50 | Pair other = (Pair) obj; 51 | if (a == null) { 52 | if (other.a != null) 53 | return false; 54 | } else if (!a.equals(other.a)) 55 | return false; 56 | if (b == null) { 57 | if (other.b != null) 58 | return false; 59 | } else if (!b.equals(other.b)) 60 | return false; 61 | return true; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | StringBuilder builder = new StringBuilder(); 67 | builder.append("Pair [left="); 68 | builder.append(a); 69 | builder.append(", right="); 70 | builder.append(b); 71 | builder.append("]"); 72 | return builder.toString(); 73 | } 74 | 75 | public T _1() { 76 | return a; 77 | } 78 | 79 | public S _2() { 80 | return b; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/rx2/util/ZippedEntry.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.util; 2 | 3 | import java.io.InputStream; 4 | import java.util.zip.ZipEntry; 5 | 6 | public final class ZippedEntry { 7 | 8 | final String name; // entry name 9 | final long time; // last modification time 10 | // final FileTime mtime; // last modification time, from extra field data 11 | // final FileTime atime; // last access time, from extra field data 12 | // final FileTime ctime; // creation time, from extra field data 13 | final long crc; // crc-32 of entry data 14 | final long size; // uncompressed size of entry data 15 | final long csize; // compressed size of entry data 16 | final int method; // compression method 17 | final byte[] extra; // optional extra field data for entry 18 | final String comment; // optional comment string for entry 19 | private final InputStream is; 20 | 21 | public ZippedEntry(ZipEntry e, InputStream is) { 22 | this.name = e.getName(); 23 | this.time = e.getTime(); 24 | // this.mtime = e.getLastModifiedTime(); 25 | // this.atime = e.getLastAccessTime(); 26 | // this.ctime = e.getCreationTime(); 27 | this.crc = e.getCrc(); 28 | this.size = e.getSize(); 29 | this.csize = e.getCompressedSize(); 30 | this.method = e.getMethod(); 31 | this.extra = e.getExtra(); 32 | this.comment = e.getComment(); 33 | this.is = is; 34 | } 35 | 36 | public InputStream getInputStream() { 37 | return is; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public long getTime() { 45 | return time; 46 | } 47 | 48 | // public FileTime getLastModifiedTime() { 49 | // return mtime; 50 | // } 51 | 52 | // public FileTime getLastAccessTime() { 53 | // return atime; 54 | // } 55 | 56 | // public FileTime getCreatedtime() { 57 | // return ctime; 58 | // } 59 | 60 | public long getCrc() { 61 | return crc; 62 | } 63 | 64 | public long getSize() { 65 | return size; 66 | } 67 | 68 | public long getCompressedSize() { 69 | return csize; 70 | } 71 | 72 | public int getMethod() { 73 | return method; 74 | } 75 | 76 | public byte[] getExtra() { 77 | return extra; 78 | } 79 | 80 | public String getComment() { 81 | return comment; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/github/davidmoten/util/RingBuffer.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.util; 2 | 3 | import java.util.Collection; 4 | import java.util.Iterator; 5 | import java.util.NoSuchElementException; 6 | import java.util.Queue; 7 | 8 | /** 9 | * Non-threadsafe implementation of a Ring Buffer. Does not accept nulls. 10 | * 11 | * @param 12 | */ 13 | // @NotThreadSafe 14 | public class RingBuffer implements Queue { 15 | 16 | private final T[] list; 17 | private int start; 18 | private int finish; 19 | 20 | @SuppressWarnings("unchecked") 21 | private RingBuffer(int size) { 22 | list = (T[]) new Object[size + 1]; 23 | } 24 | 25 | public static RingBuffer create(int size) { 26 | return new RingBuffer(size); 27 | } 28 | 29 | @Override 30 | public void clear() { 31 | finish = start; 32 | } 33 | 34 | @Override 35 | public Iterator iterator() { 36 | final int _start = start; 37 | final int _finish = finish; 38 | return new Iterator() { 39 | int i = _start; 40 | 41 | @Override 42 | public boolean hasNext() { 43 | return i != _finish; 44 | } 45 | 46 | @Override 47 | public T next() { 48 | T value = list[i]; 49 | i = (i + 1) % list.length; 50 | return value; 51 | } 52 | }; 53 | } 54 | 55 | @Override 56 | public T peek() { 57 | if (start == finish) 58 | return null; 59 | else 60 | return list[start]; 61 | } 62 | 63 | public boolean isEmpty() { 64 | return start == finish; 65 | } 66 | 67 | public int size() { 68 | if (start <= finish) 69 | return finish - start; 70 | else 71 | return finish - start + list.length; 72 | } 73 | 74 | public int maxSize() { 75 | return list.length - 1; 76 | } 77 | 78 | @Override 79 | public boolean contains(Object o) { 80 | return notImplemented(); 81 | } 82 | 83 | @Override 84 | public Object[] toArray() { 85 | return notImplemented(); 86 | } 87 | 88 | @Override 89 | public S[] toArray(S[] a) { 90 | return notImplemented(); 91 | } 92 | 93 | @Override 94 | public boolean remove(Object o) { 95 | return notImplemented(); 96 | } 97 | 98 | @Override 99 | public boolean containsAll(Collection c) { 100 | return notImplemented(); 101 | } 102 | 103 | private static T notImplemented() { 104 | throw new RuntimeException("Not implemented"); 105 | } 106 | 107 | @Override 108 | public boolean addAll(Collection c) { 109 | for (T t : c) 110 | add(t); 111 | return true; 112 | } 113 | 114 | @Override 115 | public boolean removeAll(Collection c) { 116 | return notImplemented(); 117 | } 118 | 119 | @Override 120 | public boolean retainAll(Collection c) { 121 | return notImplemented(); 122 | } 123 | 124 | @Override 125 | public boolean add(T t) { 126 | if (offer(t)) 127 | return true; 128 | else 129 | throw new IllegalStateException("Cannot add to queue because is full"); 130 | } 131 | 132 | @Override 133 | public boolean offer(T t) { 134 | if (t == null) 135 | throw new NullPointerException(); 136 | int currentFinish = finish; 137 | finish = (finish + 1) % list.length; 138 | if (finish == start) { 139 | return false; 140 | } else { 141 | list[currentFinish] = t; 142 | return true; 143 | } 144 | } 145 | 146 | @Override 147 | public T remove() { 148 | T t = poll(); 149 | if (t == null) 150 | throw new NoSuchElementException(); 151 | else 152 | return t; 153 | } 154 | 155 | @Override 156 | public T poll() { 157 | if (start == finish) { 158 | return null; 159 | } else { 160 | T value = list[start]; 161 | // don't hold a reference to a popped value 162 | list[start] = null; 163 | start = (start + 1) % list.length; 164 | return value; 165 | } 166 | } 167 | 168 | @Override 169 | public T element() { 170 | return notImplemented(); 171 | } 172 | 173 | @Override 174 | public String toString() { 175 | StringBuilder s = new StringBuilder(); 176 | s.append("RingBuffer["); 177 | boolean first = true; 178 | for (T value: this) { 179 | if (!first) { 180 | s.append(", "); 181 | } else { 182 | first = false; 183 | } 184 | s.append(String.valueOf(value)); 185 | } 186 | s.append("]"); 187 | return s.toString(); 188 | } 189 | } -------------------------------------------------------------------------------- /src/main/proguard/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -dontwarn sun.misc.** 2 | -dontwarn com.github.davidmoten.rx2.flowable.Serialized* 3 | -dontwarn com.github.davidmoten.rx2.internal.flowable.buffertofile.MemoryMappedFile 4 | -dontwarn com.github.davidmoten.rx2.internal.flowable.buffertofile.UnsafeAccess -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/ActionsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | import org.junit.Test; 8 | 9 | import com.github.davidmoten.junit.Asserts; 10 | 11 | public class ActionsTest { 12 | 13 | @Test 14 | public void testSetToTrue() throws Exception { 15 | AtomicBoolean b = new AtomicBoolean(); 16 | Actions.setToTrue(b).run(); 17 | assertTrue(b.get()); 18 | } 19 | 20 | @Test 21 | public void testIsUtility() { 22 | Asserts.assertIsUtilityClass(Actions.class); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/Benchmarks.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.regex.Pattern; 5 | 6 | import org.openjdk.jmh.annotations.Benchmark; 7 | import org.openjdk.jmh.annotations.Scope; 8 | import org.openjdk.jmh.annotations.State; 9 | import org.reactivestreams.Publisher; 10 | 11 | import io.reactivex.Emitter; 12 | import io.reactivex.Flowable; 13 | import io.reactivex.functions.Consumer; 14 | import rx.Observable; 15 | import rx.Observable.Transformer; 16 | import rx.observables.StringObservable; 17 | 18 | @State(Scope.Benchmark) 19 | public class Benchmarks { 20 | 21 | static final String lines = create(10000, 100); 22 | static final Flowable source = Flowable.just(lines, lines, lines, lines, lines); 23 | 24 | @Benchmark 25 | public String splitStandardTake5() { 26 | return source.compose(Strings.split("\n")).take(5).blockingLast(); 27 | } 28 | 29 | @Benchmark 30 | public String splitStandardWithPatternTake5() { 31 | return source.compose(Strings.split(Pattern.compile("\n"))).take(5).blockingLast(); 32 | } 33 | 34 | @Benchmark 35 | public String splitSimpleTake5() { 36 | return source.compose(Strings.splitSimple("\n")).take(5).blockingLast(); 37 | } 38 | 39 | @Benchmark 40 | public String splitStandard() { 41 | return source.compose(Strings.split("\n")).blockingLast(); 42 | } 43 | 44 | @Benchmark 45 | public String splitStandardWithPattern() { 46 | return source.compose(Strings.split(Pattern.compile("\n"))).blockingLast(); 47 | } 48 | 49 | @Benchmark 50 | public String splitSimple() { 51 | return source.compose(Strings.splitSimple("\n")).blockingLast(); 52 | } 53 | 54 | private static Flowable range = Flowable 55 | .defer(new Callable>() { 56 | @Override 57 | public Publisher call() throws Exception { 58 | return Flowable.generate(new Consumer>() { 59 | final int[] count = new int[1]; 60 | 61 | @Override 62 | public void accept(Emitter emitter) throws Exception { 63 | count[0]++; 64 | emitter.onNext(count[0]); 65 | if (count[0] == 1000) { 66 | emitter.onComplete(); 67 | } 68 | } 69 | }); 70 | } 71 | }); 72 | 73 | @Benchmark 74 | public Long mergeTwoStreams() { 75 | return Flowable.merge(Flowable.just(range, range, range)).count().blockingGet(); 76 | } 77 | 78 | @Benchmark 79 | public Long mergeTwoStreamsInterleaved() { 80 | return Flowables.mergeInterleaved(Flowable.just(range, range, range)) // 81 | .maxConcurrency(4) // 82 | .batchSize(128) // 83 | .build() // 84 | .count() // 85 | .blockingGet(); 86 | } 87 | 88 | @Benchmark 89 | public String splitRxJavaStringTake5() { 90 | return Observable.just(lines).compose(new Transformer() { 91 | @Override 92 | public Observable call(Observable o) { 93 | return StringObservable.split(o, "\n"); 94 | } 95 | }).take(5).last().toBlocking().last(); 96 | } 97 | 98 | @Benchmark 99 | public String splitRxJavaString() { 100 | return Observable.just(lines).compose(new Transformer() { 101 | @Override 102 | public Observable call(Observable o) { 103 | return StringObservable.split(o, "\n"); 104 | } 105 | }).last().toBlocking().last(); 106 | } 107 | 108 | private static String create(int lines, int lineLength) { 109 | StringBuilder s = new StringBuilder(lines * lineLength); 110 | for (int i = 0; i < lines; i++) { 111 | for (int j = 0; j < lineLength; j++) { 112 | s.append((char) (48 + (i * 137 + j) % 74)); 113 | } 114 | s.append("\n"); 115 | } 116 | return s.toString(); 117 | } 118 | 119 | public static void main(String[] args) { 120 | new Benchmarks().mergeTwoStreams(); 121 | } 122 | } -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/BiFunctionsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import com.github.davidmoten.junit.Asserts; 7 | 8 | public class BiFunctionsTest { 9 | 10 | @Test 11 | public void test() { 12 | Asserts.assertIsUtilityClass(BiFunctions.class); 13 | } 14 | 15 | @Test 16 | public void testConstant() throws Exception { 17 | Assert.assertEquals(1, (int) BiFunctions.constant(1).apply(new Object(), new Object())); 18 | } 19 | 20 | @Test 21 | public void testToNull() throws Exception { 22 | Assert.assertNull(BiFunctions.toNull().apply(new Object(), new Object())); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/BiPredicatesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import com.github.davidmoten.junit.Asserts; 7 | 8 | public class BiPredicatesTest { 9 | 10 | @Test 11 | public void isUtilityClass() { 12 | Asserts.assertIsUtilityClass(BiPredicates.class); 13 | } 14 | 15 | @Test 16 | public void testAlwaysTrue() throws Exception { 17 | Assert.assertTrue(BiPredicates.alwaysTrue().test(new Object(), new Object())); 18 | } 19 | 20 | @Test 21 | public void testAlwaysFalse() throws Exception { 22 | Assert.assertFalse(BiPredicates.alwaysFalse().test(new Object(), new Object())); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/CallablesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | 7 | public class CallablesTest { 8 | 9 | @Test 10 | public void testIsUtilityClass() { 11 | Asserts.assertIsUtilityClass(Callables.class); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/ConsumersTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.PrintStream; 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | 9 | import org.junit.Test; 10 | 11 | import com.github.davidmoten.junit.Asserts; 12 | 13 | public class ConsumersTest { 14 | 15 | @Test 16 | public void testIsUtilityClass() { 17 | Asserts.assertIsUtilityClass(Consumers.class); 18 | } 19 | 20 | @Test 21 | public void testPrintStackTrace() throws Exception { 22 | PrintStream err = System.err; 23 | try { 24 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 25 | System.setErr(new PrintStream(bytes)); 26 | Consumers.printStackTrace().accept(new RuntimeException()); 27 | String message = new String(bytes.toByteArray()); 28 | assertTrue(message.startsWith("java.lang.RuntimeException")); 29 | assertTrue(message.contains("ConsumersTest.testPrintStackTrace")); 30 | } finally { 31 | System.setErr(err); 32 | } 33 | } 34 | 35 | @Test 36 | public void testDoNothing() throws Exception { 37 | Consumers.doNothing().accept(new Object()); 38 | } 39 | 40 | @Test 41 | public void testSetToTrue() throws Exception { 42 | AtomicBoolean b = new AtomicBoolean(); 43 | Consumers.setToTrue(b).accept(new Object()); 44 | assertTrue(b.get()); 45 | } 46 | 47 | @Test 48 | public void testArraysEqualDoesNotThrow() throws Exception { 49 | Consumers.assertBytesEquals(new byte[] { 1, 2, 3 }).accept(new byte[] { 1, 2, 3 }); 50 | } 51 | 52 | @Test(expected = RuntimeException.class) 53 | public void testArraysNotEqualThrows() throws Exception { 54 | Consumers.assertBytesEquals(new byte[] { 1, 2, 4 }).accept(new byte[] { 1, 2, 3 }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/FlowablesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import io.reactivex.Flowable; 4 | import io.reactivex.annotations.NonNull; 5 | import io.reactivex.functions.Consumer; 6 | import io.reactivex.schedulers.Schedulers; 7 | import org.junit.Test; 8 | 9 | import com.github.davidmoten.junit.Asserts; 10 | import com.github.davidmoten.rx2.flowable.CloseableFlowableWithReset; 11 | 12 | import org.reactivestreams.Subscription; 13 | 14 | import java.util.List; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.concurrent.atomic.AtomicInteger; 17 | 18 | import static org.junit.Assert.assertTrue; 19 | 20 | public class FlowablesTest { 21 | 22 | @Test 23 | public void isUtilityClass() { 24 | Asserts.assertIsUtilityClass(Flowables.class); 25 | } 26 | 27 | 28 | @Test 29 | public void testCache() { 30 | 31 | final AtomicInteger subscriptionCount = new AtomicInteger(0); 32 | 33 | Flowable source = 34 | Flowable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon"); 35 | 36 | final CloseableFlowableWithReset> closeable = Flowables.cache( 37 | source.doOnSubscribe(new Consumer() { 38 | @Override 39 | public void accept(@NonNull Subscription subscription) throws Exception { 40 | subscriptionCount.incrementAndGet(); 41 | } 42 | } 43 | ).toList().toFlowable(), 3, TimeUnit.SECONDS, Schedulers.computation()); 44 | 45 | Flowable> timed = closeable.flowable().doOnNext(new Consumer>() { 46 | @Override 47 | public void accept(@NonNull List s) throws Exception { 48 | closeable.reset(); 49 | } 50 | }); 51 | 52 | timed.subscribe(); 53 | 54 | try { 55 | Thread.sleep(1000); 56 | } catch (InterruptedException e) { 57 | e.printStackTrace(); 58 | } 59 | 60 | timed.subscribe(); 61 | try { 62 | Thread.sleep(4000); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | 67 | timed.subscribe(); 68 | 69 | assertTrue(subscriptionCount.get() == 2); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/FunctionsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import com.github.davidmoten.junit.Asserts; 8 | 9 | public class FunctionsTest { 10 | 11 | @Test 12 | public void testIsUtilityClass() { 13 | Asserts.assertIsUtilityClass(Functions.class); 14 | } 15 | 16 | @Test 17 | public void testConstant() throws Exception { 18 | assertEquals("boo", Functions.constant("boo").apply("blah")); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/MaybesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import com.github.davidmoten.junit.Asserts; 7 | 8 | public final class MaybesTest { 9 | 10 | @Test 11 | public void isUtilityClass() { 12 | Asserts.assertIsUtilityClass(Maybes.class); 13 | } 14 | 15 | @Test 16 | public void testNull() { 17 | Assert.assertEquals(0, (long) Maybes.fromNullable(null).count().blockingGet()); 18 | } 19 | 20 | @Test 21 | public void testNotNull() { 22 | Assert.assertEquals(1, (long) Maybes.fromNullable(1000).count().blockingGet()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/ObservablesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import com.github.davidmoten.junit.Asserts; 4 | import com.github.davidmoten.rx2.observable.CloseableObservableWithReset; 5 | import io.reactivex.Flowable; 6 | import io.reactivex.Observable; 7 | import io.reactivex.annotations.NonNull; 8 | import io.reactivex.functions.Consumer; 9 | import io.reactivex.schedulers.Schedulers; 10 | import org.junit.Test; 11 | import org.reactivestreams.Subscription; 12 | 13 | import java.util.List; 14 | import java.util.concurrent.TimeUnit; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | import static org.junit.Assert.assertTrue; 18 | 19 | public class ObservablesTest { 20 | 21 | @Test 22 | public void isUtilityClass() { 23 | Asserts.assertIsUtilityClass(Observables.class); 24 | } 25 | 26 | @Test 27 | public void testCache() { 28 | 29 | final AtomicInteger subscriptionCount = new AtomicInteger(0); 30 | 31 | Flowable source = Flowable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon"); 32 | 33 | final CloseableObservableWithReset> closeable = Observables 34 | .cache(source.doOnSubscribe(new Consumer() { 35 | @Override 36 | public void accept(@NonNull Subscription subscription) throws Exception { 37 | subscriptionCount.incrementAndGet(); 38 | } 39 | }).toList().toObservable(), 3, TimeUnit.SECONDS, Schedulers.computation()); 40 | 41 | Observable> timed = closeable.observable() 42 | .doOnNext(new Consumer>() { 43 | @Override 44 | public void accept(@NonNull List s) throws Exception { 45 | closeable.reset(); 46 | } 47 | }); 48 | 49 | timed.subscribe(); 50 | 51 | try { 52 | Thread.sleep(1000); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | timed.subscribe(); 58 | try { 59 | Thread.sleep(4000); 60 | } catch (InterruptedException e) { 61 | e.printStackTrace(); 62 | } 63 | 64 | timed.subscribe(); 65 | 66 | assertTrue(subscriptionCount.get() == 2); 67 | 68 | // TODO assert stuff about closing 69 | // closeable.close(); 70 | // closeable.close(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/PredicatesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import com.github.davidmoten.junit.Asserts; 7 | 8 | public class PredicatesTest { 9 | 10 | @Test 11 | public void isUtilityClass() { 12 | Asserts.assertIsUtilityClass(Predicates.class); 13 | } 14 | 15 | @Test 16 | public void testAlwaysFalse() throws Exception { 17 | Assert.assertFalse(Predicates.alwaysFalse().test(new Object())); 18 | } 19 | 20 | @Test 21 | public void testAlwaysTrue() throws Exception { 22 | Assert.assertTrue(Predicates.alwaysTrue().test(new Object())); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/SchedulerHelperTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import com.github.davidmoten.junit.Asserts; 9 | 10 | import io.reactivex.Scheduler; 11 | import io.reactivex.Scheduler.Worker; 12 | import io.reactivex.schedulers.Schedulers; 13 | 14 | public class SchedulerHelperTest { 15 | 16 | @Test 17 | public void testIsUtilityClass() { 18 | Asserts.assertIsUtilityClass(SchedulerHelper.class); 19 | } 20 | 21 | @Test 22 | public void testWithId() { 23 | Scheduler s = SchedulerHelper.withThreadId(Schedulers.trampoline(), "boo"); 24 | final StringBuilder b = new StringBuilder(); 25 | String main = Thread.currentThread().getName(); 26 | s.createWorker().schedule(new Runnable() { 27 | 28 | @Override 29 | public void run() { 30 | b.append(Thread.currentThread().getName()); 31 | } 32 | }); 33 | assertEquals(main + "|schedId=[boo]", b.toString()); 34 | } 35 | 36 | @Test 37 | public void testDispose() { 38 | Scheduler s = SchedulerHelper.withThreadId(Schedulers.trampoline(), "boo"); 39 | Worker w = s.createWorker(); 40 | Assert.assertFalse(w.isDisposed()); 41 | w.dispose(); 42 | Assert.assertTrue(w.isDisposed()); 43 | } 44 | 45 | @Test 46 | public void testWithCallSite() { 47 | Scheduler s = SchedulerHelper.withThreadIdFromCallSite(Schedulers.trampoline()); 48 | final StringBuilder b = new StringBuilder(); 49 | String main = Thread.currentThread().getName(); 50 | s.createWorker().schedule(new Runnable() { 51 | 52 | @Override 53 | public void run() { 54 | b.append(Thread.currentThread().getName()); 55 | } 56 | }); 57 | System.out.println(b); 58 | assertEquals(main + "|schedId=[com.github.davidmoten.rx2.SchedulerHelperTest:testWithCallSite:47]", 59 | b.toString()); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/SpecializedMpscLinkedQueue.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import java.util.concurrent.atomic.AtomicReference; 4 | 5 | import io.reactivex.internal.queue.MpscLinkedQueue; 6 | 7 | /** 8 | * A multi-producer single consumer queue that is thread-safe and performant 9 | * under a standard drain scenario encountered in an RxJava operator. 10 | * 11 | * @param 12 | */ 13 | public final class SpecializedMpscLinkedQueue { 14 | 15 | private final AtomicReference> headTail = new AtomicReference>(); 16 | 17 | private SpecializedMpscLinkedQueue() { 18 | // constructor 19 | } 20 | 21 | public static SpecializedMpscLinkedQueue create() { 22 | return new SpecializedMpscLinkedQueue(); 23 | } 24 | 25 | // mutable 26 | private Node head; 27 | 28 | @SuppressWarnings("serial") 29 | private static final class Node extends AtomicReference> { 30 | // this.get is next 31 | 32 | // mutable 33 | T value; 34 | 35 | Node(T value) { 36 | this.value = value; 37 | } 38 | 39 | Node next() { 40 | return get(); 41 | } 42 | } 43 | 44 | private static final class HeadTail { 45 | final Node head; 46 | final Node tail; 47 | 48 | HeadTail(Node head, Node tail) { 49 | this.head = head; 50 | this.tail = tail; 51 | } 52 | } 53 | 54 | public void offer(T value) { 55 | // performs one volatile read, one CAS operation and one weak CAS operation per 56 | // call to this method (under contention can be higher) 57 | 58 | Node node = new Node(value); 59 | while (true) { 60 | HeadTail ht = headTail.get(); 61 | final HeadTail ht2; 62 | if (ht.head == null) { 63 | if (ht.tail == null) { 64 | ht2 = new HeadTail(node, node); 65 | } else { 66 | // ? 67 | throw new RuntimeException("unexpected"); 68 | } 69 | } 70 | } 71 | 72 | } 73 | 74 | public T poll() { 75 | return null; 76 | } 77 | 78 | MpscLinkedQueue q; 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/SpecializedMspcLinkedQueueTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNull; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | @Ignore 10 | public class SpecializedMspcLinkedQueueTest { 11 | 12 | @Test 13 | public void testEmptyQueuePoll() { 14 | SpecializedMpscLinkedQueue q = SpecializedMpscLinkedQueue.create(); 15 | assertNull(q.poll()); 16 | assertNull(q.poll()); 17 | } 18 | 19 | @Test 20 | public void testPushPoll() { 21 | SpecializedMpscLinkedQueue q = SpecializedMpscLinkedQueue.create(); 22 | q.offer(1); 23 | assertEquals(1, (int) q.poll()); 24 | assertNull(q.poll()); 25 | } 26 | 27 | @Test 28 | public void testPushPushPollPoll() { 29 | SpecializedMpscLinkedQueue q = SpecializedMpscLinkedQueue.create(); 30 | q.offer(1); 31 | q.offer(2); 32 | assertEquals(1, (int) q.poll()); 33 | assertEquals(2, (int) q.poll()); 34 | assertNull(q.poll()); 35 | } 36 | 37 | @Test 38 | public void testPushPushPollPushPollPoll() { 39 | SpecializedMpscLinkedQueue q = SpecializedMpscLinkedQueue.create(); 40 | q.offer(1); 41 | q.offer(2); 42 | assertEquals(1, (int) q.poll()); 43 | q.offer(3); 44 | assertEquals(2, (int) q.poll()); 45 | assertEquals(3, (int) q.poll()); 46 | assertNull(q.poll()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/StateMachineTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | 10 | import com.github.davidmoten.junit.Asserts; 11 | import com.github.davidmoten.rx2.StateMachine.Completion; 12 | import com.github.davidmoten.rx2.StateMachine.CompletionAlwaysTrueHolder; 13 | import com.github.davidmoten.rx2.StateMachine.Transition; 14 | import com.github.davidmoten.rx2.flowable.Transformers; 15 | 16 | import io.reactivex.BackpressureStrategy; 17 | import io.reactivex.Flowable; 18 | import io.reactivex.FlowableEmitter; 19 | import io.reactivex.FlowableTransformer; 20 | 21 | public class StateMachineTest { 22 | 23 | @Test 24 | public void testIsUtilityClass() { 25 | Asserts.assertIsUtilityClass(StateMachine.class); 26 | } 27 | 28 | @Test 29 | public void testIsUtilityClassCompletionAlwaysTrueHolder() { 30 | Asserts.assertIsUtilityClass(CompletionAlwaysTrueHolder.class); 31 | } 32 | 33 | @Test 34 | public void testBuilder() { 35 | FlowableTransformer collectIntoStringsOfMinLength3 = Transformers.stateMachine() // 36 | .initialState("") // 37 | .transition(new Transition() { 38 | @Override 39 | public String apply(String state, Integer value, FlowableEmitter emitter) { 40 | String state2 = state + value; 41 | if (state2.length() >= 3) { 42 | emitter.onNext(state2.substring(0, 3)); 43 | return state2.substring(3); 44 | } else { 45 | return state2; 46 | } 47 | } 48 | 49 | }) // 50 | .completion(new Completion() { 51 | @Override 52 | public boolean test(String state, FlowableEmitter emitter) { 53 | emitter.onNext(state); 54 | return true; 55 | } 56 | }) // 57 | .backpressureStrategy(BackpressureStrategy.BUFFER) // 58 | .requestBatchSize(128) // 59 | .build(); 60 | 61 | List list = Flowable.range(1, 13).compose(collectIntoStringsOfMinLength3).toList().blockingGet(); 62 | assertEquals(Arrays.asList("123", "456", "789", "101", "112", "13"), list); 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/StringsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2; 2 | 3 | import static com.github.davidmoten.rx2.Strings.decode; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNull; 6 | import static org.junit.Assert.fail; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.charset.Charset; 11 | import java.nio.charset.CharsetDecoder; 12 | 13 | import org.junit.Test; 14 | 15 | import com.github.davidmoten.guavamini.Lists; 16 | import com.github.davidmoten.junit.Asserts; 17 | 18 | import io.reactivex.Flowable; 19 | import io.reactivex.functions.BiFunction; 20 | 21 | public class StringsTest { 22 | 23 | @Test 24 | public void testMultibyteSpanningTwoBuffers() { 25 | Flowable src = Flowable.just(new byte[] { (byte) 0xc2 }, new byte[] { (byte) 0xa1 }); 26 | String out = decode(src, "UTF-8").blockingSingle(); 27 | 28 | assertEquals("\u00A1", out); 29 | } 30 | 31 | @Test 32 | public void testMalformedAtTheEndReplace() { 33 | Flowable src = Flowable.just(new byte[] { (byte) 0xc2 }); 34 | String out = decode(src, "UTF-8").blockingSingle(); 35 | 36 | // REPLACEMENT CHARACTER 37 | assertEquals("\uFFFD", out); 38 | } 39 | 40 | @Test 41 | public void testMalformedInTheMiddleReplace() { 42 | Flowable src = Flowable.just(new byte[] { (byte) 0xc2, 65 }); 43 | String out = decode(src, "UTF-8").blockingSingle(); 44 | 45 | // REPLACEMENT CHARACTER 46 | assertEquals("\uFFFDA", out); 47 | } 48 | 49 | @Test(expected = RuntimeException.class) 50 | public void testMalformedAtTheEndReport() { 51 | Flowable src = Flowable.just(new byte[] { (byte) 0xc2 }); 52 | CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); 53 | decode(src, charsetDecoder).blockingSingle(); 54 | } 55 | 56 | @Test(expected = RuntimeException.class) 57 | public void testMalformedInTheMiddleReport() { 58 | Flowable src = Flowable.just(new byte[] { (byte) 0xc2, 65 }); 59 | CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); 60 | decode(src, charsetDecoder).blockingSingle(); 61 | } 62 | 63 | @Test 64 | public void testPropagateError() { 65 | Flowable src = Flowable.just(new byte[] { 65 }); 66 | Flowable err = Flowable.error(new IOException()); 67 | CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); 68 | try { 69 | decode(Flowable.concat(src, err), charsetDecoder).toList().blockingGet(); 70 | fail(); 71 | } catch (RuntimeException e) { 72 | assertEquals(IOException.class, e.getCause().getClass()); 73 | } 74 | } 75 | 76 | @Test 77 | public void testPropagateErrorInTheMiddleOfMultibyte() { 78 | Flowable src = Flowable.just(new byte[] { (byte) 0xc2 }); 79 | Flowable err = Flowable.error(new IOException()); 80 | CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); 81 | try { 82 | decode(Flowable.concat(src, err), charsetDecoder).toList().blockingGet(); 83 | fail(); 84 | } catch (RuntimeException e) { 85 | assertEquals(IOException.class, e.getCause().getClass()); 86 | } 87 | } 88 | 89 | @Test 90 | public void testFromClasspath() { 91 | String expected = "hello world\nincoming message"; 92 | assertEquals(expected, Strings.fromClasspath("/test2.txt").reduce(new BiFunction() { 93 | @Override 94 | public String apply(String a, String b) { 95 | return a + b; 96 | } 97 | }).blockingGet()); 98 | } 99 | 100 | @Test 101 | public void testTrim() { 102 | Flowable.just(" hello ") // 103 | .map(Strings.trim()) // 104 | .test() // 105 | .assertValue("hello") // 106 | .assertComplete(); 107 | } 108 | 109 | @Test 110 | public void testTrimWithNull() throws Exception { 111 | assertNull(Strings.TRIM.apply(null)); 112 | } 113 | 114 | @Test 115 | public void testIsUtilityClass() { 116 | Asserts.assertIsUtilityClass(Strings.class); 117 | } 118 | 119 | @Test 120 | public void testConcatTransformer() { 121 | Flowable.just("hello ", "there") // 122 | .to(Strings.concat()) // 123 | .test() // 124 | .assertValue("hello there") // 125 | .assertComplete(); 126 | } 127 | 128 | @Test 129 | public void testConcat() { 130 | Strings.concat(Flowable.just("hello ", "there")) // 131 | .test() // 132 | .assertValue("hello there") // 133 | .assertComplete(); 134 | } 135 | 136 | @Test 137 | public void testJoinTransformer() { 138 | Flowable.just("hello ", "there") // 139 | .to(Strings.join()) // 140 | .test() // 141 | .assertValue("hello there") // 142 | .assertComplete(); 143 | } 144 | 145 | @Test 146 | public void testJoin() { 147 | Strings.join(Flowable.just("hello ", "there")) // 148 | .test() // 149 | .assertValue("hello there") // 150 | .assertComplete(); 151 | } 152 | 153 | @Test 154 | public void testStrings() { 155 | Flowable.just(12, 34) // 156 | .compose(Strings.strings()) // 157 | .test() // 158 | .assertValues("12", "34") // 159 | .assertComplete(); 160 | } 161 | 162 | @Test 163 | public void testFromFile() { 164 | Strings.from(new File("src/test/resources/test3.txt")).test().assertValue("hello there").assertComplete(); 165 | } 166 | 167 | @SuppressWarnings("unchecked") 168 | @Test 169 | public void testSplitLinesWithComments() { 170 | Strings.splitLinesSkipComments(StringsTest.class.getResourceAsStream("/test4.txt"), Strings.UTF_8, ",", "#") // 171 | .test() // 172 | .assertValues(Lists.newArrayList("23", "176", "FRED"), // 173 | Lists.newArrayList("13", "130", "JOHN")) 174 | .assertComplete(); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/buffertofile/SerializersTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.buffertofile; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import org.junit.Test; 9 | 10 | import com.github.davidmoten.junit.Asserts; 11 | import com.github.davidmoten.rx2.flowable.Transformers; 12 | 13 | import io.reactivex.Flowable; 14 | 15 | public class SerializersTest { 16 | 17 | @Test 18 | public void isUtilityClass() { 19 | Asserts.assertIsUtilityClass(Serializers.class); 20 | } 21 | 22 | @Test 23 | public void testDataSerializer() { 24 | // Demonstrates DataSerializer usage 25 | DataSerializer ds = new DataSerializer() { 26 | 27 | @Override 28 | public void serialize(Integer t, DataOutput out) throws IOException { 29 | out.writeInt(t); 30 | } 31 | 32 | @Override 33 | public Integer deserialize(DataInput in) throws IOException { 34 | return in.readInt(); 35 | } 36 | 37 | @Override 38 | public int sizeHint() { 39 | return 4; 40 | } 41 | }; 42 | Flowable.just(4) // 43 | .compose(Transformers.onBackpressureBufferToFile() // 44 | .serializer(ds)) // 45 | .test() // 46 | .awaitDone(5, TimeUnit.SECONDS) // 47 | .assertValue(4) // 48 | .assertComplete(); 49 | } 50 | 51 | @Test 52 | public void testDataSerializerWithUnboundedCapacity() { 53 | // Demonstrates DataSerializer usage 54 | DataSerializer ds = new DataSerializer() { 55 | 56 | @Override 57 | public void serialize(Integer t, DataOutput out) throws IOException { 58 | out.writeInt(t); 59 | } 60 | 61 | @Override 62 | public Integer deserialize(DataInput in) throws IOException { 63 | return in.readInt(); 64 | } 65 | 66 | @Override 67 | public int sizeHint() { 68 | return 0; 69 | } 70 | }; 71 | Flowable.just(4) // 72 | .compose(Transformers.onBackpressureBufferToFile() // 73 | .serializer(ds)) // 74 | .test() // 75 | .awaitDone(5, TimeUnit.SECONDS) // 76 | .assertValue(4) // 77 | .assertComplete(); 78 | } 79 | 80 | @Test 81 | public void testUtf8() { 82 | Flowable.just("abc") // 83 | .compose(Transformers.onBackpressureBufferToFile() // 84 | .serializerUtf8()) // 85 | .test() // 86 | .awaitDone(5, TimeUnit.SECONDS) // 87 | .assertValue("abc") // 88 | .assertComplete(); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/flowable/FlowableDoNothing.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.flowable; 2 | 3 | import org.reactivestreams.Subscriber; 4 | import org.reactivestreams.Subscription; 5 | 6 | import io.reactivex.Flowable; 7 | import io.reactivex.FlowableSubscriber; 8 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 9 | 10 | public final class FlowableDoNothing extends Flowable { 11 | 12 | private final Flowable source; 13 | 14 | public FlowableDoNothing(Flowable source) { 15 | this.source = source; 16 | } 17 | 18 | @Override 19 | protected void subscribeActual(Subscriber downstream) { 20 | source.subscribe(new DoNothingSubscriber(downstream)); 21 | } 22 | 23 | static final class DoNothingSubscriber implements // 24 | // receives onSubscribe then onNext + terminal events from upstream 25 | FlowableSubscriber, // 26 | // receives request and cancellation calls from downstream 27 | Subscription { 28 | 29 | private final Subscriber downstream; 30 | private Subscription upstream; 31 | 32 | DoNothingSubscriber(Subscriber downstream) { 33 | this.downstream = downstream; 34 | } 35 | 36 | @Override 37 | public void onSubscribe(Subscription upstream) { 38 | // this method is responsible for notifying downstream 39 | // where to send requests and cancellation by calling 40 | // downstream.onSubscribe with a Subscription object 41 | // (we reuse `this`) 42 | 43 | if (SubscriptionHelper.validate(this.upstream, upstream)) { 44 | downstream.onSubscribe(this); 45 | } 46 | } 47 | 48 | @Override 49 | public void onNext(T t) { 50 | // pass emission through to downstream 51 | 52 | // note downstream is not null because onSubscribe call 53 | // must finish before events arrive (contractual) 54 | downstream.onNext(t); 55 | } 56 | 57 | @Override 58 | public void onError(Throwable t) { 59 | // pass emission through to downstream 60 | downstream.onError(t); 61 | } 62 | 63 | @Override 64 | public void onComplete() { 65 | // pass emission through to downstream 66 | downstream.onComplete(); 67 | } 68 | 69 | @Override 70 | public void request(long n) { 71 | // requests received from downstream are passed to upstream 72 | 73 | // note that upstream is non-null because by specification 74 | // onSubscribe must terminate before request or cancel is 75 | // called on the Subscription passed into onSubscribe. 76 | if (SubscriptionHelper.validate(n)) { 77 | upstream.request(n); 78 | } 79 | } 80 | 81 | @Override 82 | public void cancel() { 83 | // a cancellation call from downstream 84 | 85 | // note that upstream is non-null because by specification 86 | // onSubscribe must terminate before request or cancel is 87 | // called on the Subscription passed into onSubscribe. 88 | upstream.cancel(); 89 | } 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/flowable/SerializedTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.flowable; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.io.File; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | import org.junit.Test; 11 | 12 | import com.github.davidmoten.junit.Asserts; 13 | import com.github.davidmoten.rx2.flowable.Serialized; 14 | 15 | import io.reactivex.Flowable; 16 | 17 | public class SerializedTest { 18 | 19 | @Test 20 | public void isUtilityClass() { 21 | Asserts.assertIsUtilityClass(Serialized.class); 22 | } 23 | 24 | @Test 25 | public void testSerializeAndDeserializeOfNonEmptyStream() { 26 | File file = new File("target/temp1"); 27 | file.delete(); 28 | Flowable source = Flowable.just(1, 2, 3); 29 | Serialized.write(source, file).subscribe(); 30 | assertTrue(file.exists()); 31 | assertTrue(file.length() > 0); 32 | List list = Serialized.read(file).toList().blockingGet(); 33 | assertEquals(Arrays.asList(1, 2, 3), list); 34 | } 35 | 36 | @Test 37 | public void testSerializeAndDeserializeOfNonEmptyStreamWithSmallBuffer() { 38 | File file = new File("target/temp2"); 39 | file.delete(); 40 | Flowable source = Flowable.just(1, 2, 3); 41 | Serialized.write(source, file, false, 1).subscribe(); 42 | assertTrue(file.exists()); 43 | assertTrue(file.length() > 0); 44 | List list = Serialized.read(file, 1).toList().blockingGet(); 45 | assertEquals(Arrays.asList(1, 2, 3), list); 46 | } 47 | 48 | @Test 49 | public void testSerializeAndDeserializeOfEmptyStream() { 50 | File file = new File("target/temp3"); 51 | file.delete(); 52 | Flowable source = Flowable.empty(); 53 | Serialized.write(source, file).subscribe(); 54 | assertTrue(file.exists()); 55 | List list = Serialized.read(file).toList().blockingGet(); 56 | assertTrue(list.isEmpty()); 57 | } 58 | 59 | @Test 60 | public void testSerializeAndDeserializeOfNonEmptyStreamUsingKryo() { 61 | File file = new File("target/temp4"); 62 | file.delete(); 63 | Flowable source = Flowable.just(1, 2, 3); 64 | Serialized.kryo().write(source, file).subscribe(); 65 | assertTrue(file.exists()); 66 | assertTrue(file.length() > 0); 67 | List list = Serialized.kryo().read(Integer.class, file).toList().blockingGet(); 68 | assertEquals(Arrays.asList(1, 2, 3), list); 69 | } 70 | 71 | @Test 72 | public void testSerializeAndDeserializeOfEmptyStreamUsingKryo() { 73 | File file = new File("target/temp5"); 74 | file.delete(); 75 | Flowable source = Flowable.empty(); 76 | Serialized.kryo().write(source, file).subscribe(); 77 | assertTrue(file.exists()); 78 | List list = Serialized.kryo().read(Integer.class, file).toList().blockingGet(); 79 | assertTrue(list.isEmpty()); 80 | } 81 | 82 | @Test 83 | public void testSerializeAndDeserializeOfPersonStreamUsingKryo() { 84 | File file = new File("target/temp6"); 85 | file.delete(); 86 | Flowable source = Flowable.just(new Person("fred", 24), new Person("jane", 32)); 87 | Serialized.kryo().write(source, file).subscribe(); 88 | assertTrue(file.exists()); 89 | List list = Serialized.kryo().read(Person.class, file).toList().blockingGet(); 90 | assertEquals(2, list.size()); 91 | assertEquals("fred", list.get(0).name); 92 | assertEquals(24, list.get(0).age); 93 | assertEquals("jane", list.get(1).name); 94 | assertEquals(32, list.get(1).age); 95 | } 96 | 97 | static class Person { 98 | // Note Person class doesn't need to implement Serializable to be 99 | // serialized by kryo 100 | 101 | final String name; 102 | final int age; 103 | 104 | Person() { 105 | // requires no-arg constructor to be serialized by kryo 106 | this("", 0); 107 | } 108 | 109 | Person(String name, int age) { 110 | this.name = name; 111 | this.age = age; 112 | } 113 | 114 | } 115 | } -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/DelimitedStringLinkedListTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNull; 5 | 6 | import org.junit.Test; 7 | 8 | public class DelimitedStringLinkedListTest { 9 | 10 | @Test 11 | public void testEmpty() { 12 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 13 | assertNull(s.next()); 14 | } 15 | 16 | @Test 17 | public void testNotEmptyNotFound() { 18 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 19 | s.add("boo"); 20 | assertNull(s.next()); 21 | assertEquals("boo", s.remaining()); 22 | } 23 | 24 | @Test 25 | public void testFindsFirst() { 26 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 27 | s.add("boo:and"); 28 | assertEquals("boo", s.next()); 29 | assertEquals(4, s.searchPosition()); 30 | assertNull(s.next()); 31 | assertEquals("and", s.remaining()); 32 | } 33 | 34 | @Test 35 | public void testFindsFirstLongDelimiter() { 36 | DelimitedStringLinkedList s = new DelimitedStringLinkedList("::"); 37 | s.add("boo::and"); 38 | assertEquals("boo", s.next()); 39 | assertEquals(5, s.searchPosition()); 40 | assertNull(s.next()); 41 | assertEquals("and", s.remaining()); 42 | } 43 | 44 | @Test 45 | public void testFindsTwo() { 46 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 47 | s.add("boo:and:sue"); 48 | assertEquals("boo", s.next()); 49 | assertEquals("and", s.next()); 50 | assertNull(s.next()); 51 | assertEquals("sue", s.remaining()); 52 | } 53 | 54 | @Test 55 | public void testFindsTwoLongDelimiter() { 56 | DelimitedStringLinkedList s = new DelimitedStringLinkedList("::"); 57 | s.add("boo::and::sue"); 58 | assertEquals("boo", s.next()); 59 | assertEquals("and", s.next()); 60 | assertNull(s.next()); 61 | assertEquals("sue", s.remaining()); 62 | } 63 | 64 | @Test 65 | public void testFindsThree() { 66 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 67 | s.add("boo:and:sue:me"); 68 | assertEquals("boo", s.next()); 69 | assertEquals("and", s.next()); 70 | assertEquals("sue", s.next()); 71 | assertNull(s.next()); 72 | assertEquals("me", s.remaining()); 73 | } 74 | 75 | @Test 76 | public void testFindsOneAcrossTwoDelimiterStartsSecond() { 77 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 78 | s.add("boo"); 79 | s.add(":and"); 80 | assertEquals("boo", s.next()); 81 | assertNull(s.next()); 82 | assertEquals("and", s.remaining()); 83 | } 84 | 85 | @Test 86 | public void testFindsOneAcrossTwoDelimiterEndsFirst() { 87 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 88 | s.add("boo:"); 89 | s.add("and"); 90 | assertEquals("boo", s.next()); 91 | assertNull(s.next()); 92 | assertEquals("and", s.remaining()); 93 | } 94 | 95 | @Test 96 | public void testFindsOneAcrossTwoDelimiterEndsFirstLongDelimiter() { 97 | DelimitedStringLinkedList s = new DelimitedStringLinkedList("::"); 98 | s.add("boo::"); 99 | s.add("and"); 100 | assertEquals("boo", s.next()); 101 | assertNull(s.next()); 102 | assertEquals("and", s.remaining()); 103 | } 104 | 105 | @Test 106 | public void testFindsOneAcrossTwoDelimiterSplitAcrossTwo() { 107 | DelimitedStringLinkedList s = new DelimitedStringLinkedList("::"); 108 | s.add("boo:"); 109 | assertNull(s.next()); 110 | assertEquals("boo:", s.remaining()); 111 | } 112 | 113 | @Test 114 | public void testFindsOneEndsWithDelimiter() { 115 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 116 | s.add("boo:"); 117 | assertEquals("boo", s.next()); 118 | assertNull(s.next()); 119 | assertNull(s.remaining()); 120 | } 121 | 122 | @Test 123 | public void testFindsOneAcrossTwoEndsWithDelimiter() { 124 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 125 | s.add("boo"); 126 | s.add(":"); 127 | assertEquals("boo", s.next()); 128 | assertNull(s.next()); 129 | assertNull(s.remaining()); 130 | } 131 | 132 | @Test 133 | public void testFindsNoneBecauseOnlyPartialMatchToDelimiter() { 134 | DelimitedStringLinkedList s = new DelimitedStringLinkedList("::"); 135 | s.add("boo:"); 136 | s.add("and"); 137 | assertNull(s.next()); 138 | } 139 | 140 | @Test 141 | public void testFindsOneAcrossTwoDelimiterMiddleFirst() { 142 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 143 | s.add("boo:a"); 144 | s.add("nd"); 145 | assertEquals("boo", s.next()); 146 | assertEquals(4, s.searchPosition()); 147 | assertNull(s.next()); 148 | assertEquals("and", s.remaining()); 149 | } 150 | 151 | @Test 152 | public void testFindsOneAcrossTwoDelimiterMiddleSecond() { 153 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 154 | s.add("bo"); 155 | s.add("o:and"); 156 | assertEquals("boo", s.next()); 157 | assertNull(s.next()); 158 | assertEquals("and", s.remaining()); 159 | } 160 | 161 | @Test 162 | public void testFindsOneAcrossThree() { 163 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 164 | s.add("bo"); 165 | s.add("o"); 166 | s.add(":and"); 167 | assertEquals("boo", s.next()); 168 | assertNull(s.next()); 169 | assertEquals("and", s.remaining()); 170 | } 171 | 172 | @Test 173 | public void testFindsOneAcrossFour() { 174 | DelimitedStringLinkedList s = new DelimitedStringLinkedList(":"); 175 | s.add("bo"); 176 | s.add("o"); 177 | s.add(":"); 178 | s.add("and"); 179 | assertEquals("boo", s.next()); 180 | assertNull(s.next()); 181 | assertEquals("and", s.remaining()); 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowableDoOnEmptyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.sql.SQLException; 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | 9 | import org.junit.Test; 10 | 11 | import com.github.davidmoten.rx2.Actions; 12 | import com.github.davidmoten.rx2.flowable.Transformers; 13 | 14 | import io.reactivex.BackpressureStrategy; 15 | import io.reactivex.Flowable; 16 | import io.reactivex.disposables.Disposable; 17 | import io.reactivex.functions.Action; 18 | import io.reactivex.subjects.PublishSubject; 19 | import io.reactivex.subscribers.TestSubscriber; 20 | 21 | public class FlowableDoOnEmptyTest { 22 | 23 | @Test 24 | public void testNonEmpty() { 25 | Flowable source = Flowable.just("Chicago", "Houston", "Phoenix"); 26 | 27 | final AtomicBoolean wasCalled = new AtomicBoolean(false); 28 | 29 | source.compose(Transformers.doOnEmpty(new Action() { 30 | @Override 31 | public void run() { 32 | wasCalled.set(true); 33 | } 34 | })).subscribe(); 35 | 36 | assertFalse(wasCalled.get()); 37 | } 38 | 39 | @Test 40 | public void testEmpty() { 41 | Flowable source = Flowable.empty(); 42 | 43 | final AtomicBoolean wasCalled = new AtomicBoolean(false); 44 | 45 | source.compose(Transformers.doOnEmpty(new Action() { 46 | @Override 47 | public void run() { 48 | wasCalled.set(true); 49 | } 50 | })).subscribe(); 51 | 52 | assertTrue(wasCalled.get()); 53 | } 54 | 55 | @Test 56 | public void testCancel() { 57 | final AtomicBoolean wasCalled = new AtomicBoolean(false); 58 | 59 | PublishSubject subject = PublishSubject.create(); 60 | 61 | Disposable disposable = subject.toFlowable(BackpressureStrategy.BUFFER) // 62 | .compose(Transformers.doOnEmpty(new Action() { 63 | @Override 64 | public void run() { 65 | wasCalled.set(true); 66 | } 67 | })).take(3).subscribe(); 68 | 69 | assertTrue(subject.hasObservers()); 70 | 71 | subject.onNext(0); 72 | subject.onNext(1); 73 | 74 | assertTrue(subject.hasObservers()); 75 | 76 | subject.onNext(2); 77 | 78 | assertFalse(subject.hasObservers()); 79 | 80 | disposable.dispose(); 81 | 82 | assertFalse(wasCalled.get()); 83 | } 84 | 85 | @Test 86 | public void testBackPressure() { 87 | final AtomicBoolean wasCalled = new AtomicBoolean(false); 88 | Flowable // 89 | .range(0, 1000) // 90 | .compose(Transformers.doOnEmpty(Actions.setToTrue(wasCalled))) // 91 | .test(0) // 92 | .requestMore(1) // 93 | .assertValueCount(1) // 94 | .assertNotTerminated(); 95 | assertFalse(wasCalled.get()); 96 | } 97 | 98 | @Test 99 | public void subscriberStateTest() { 100 | // final AtomicInteger counter = new AtomicInteger(0); 101 | // 102 | // final AtomicInteger callCount = new AtomicInteger(0); 103 | // 104 | // Flowable o = Flowable.defer(new Func0>() { 105 | // @Override 106 | // public Flowable call() { 107 | // return Flowable.range(1, counter.getAndIncrement() % 2); 108 | // } 109 | // }).compose(Transformers.doOnEmpty(Actions.increment0(callCount))); 110 | // 111 | // o.subscribe(); 112 | // o.subscribe(); 113 | // o.subscribe(); 114 | // o.subscribe(); 115 | // o.subscribe(); 116 | // 117 | // assert (callCount.get() == 3); 118 | } 119 | 120 | @Test 121 | public void ifSourceEmitsErrorThenDoOnEmptyIsNotRun() { 122 | AtomicBoolean set = new AtomicBoolean(false); 123 | Exception ex = new Exception("boo"); 124 | Flowable.error(ex) // 125 | .compose(Transformers.doOnEmpty(Actions.setToTrue(set))) // 126 | .test().assertError(ex).assertNoValues(); 127 | assertFalse(set.get()); 128 | } 129 | 130 | @Test 131 | public void ifOnEmptyActionThrowsExceptionThenSubscribeThrows() { 132 | Flowable.empty() // 133 | .compose(Transformers.doOnEmpty(Actions.throwing(new SQLException()))) // 134 | .test() // 135 | .assertNoValues() // 136 | .assertError(SQLException.class); 137 | } 138 | 139 | @Test 140 | public void ifOnEmptyActionThrowsNonFatalRuntimeExceptionThenErrorEmitted() { 141 | Flowable.empty() // 142 | .compose(Transformers.doOnEmpty(Actions.throwing(new NumberFormatException()))) // 143 | .test() // 144 | .assertNoValues() // 145 | .assertError(NumberFormatException.class); 146 | } 147 | 148 | @Test 149 | public void testUnsubscribeAfterActionButBeforeCompletionDoesNotAffectCompletion() { 150 | final TestSubscriber ts = TestSubscriber.create(); 151 | Flowable.empty() // 152 | .compose(Transformers.doOnEmpty(new Action() { 153 | @Override 154 | public void run() { 155 | ts.cancel(); 156 | } 157 | })).subscribe(ts); 158 | ts.assertNoValues(); 159 | ts.assertComplete(); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowableFetchPagesByRequestTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | import com.github.davidmoten.rx2.Flowables; 7 | import com.github.davidmoten.rx2.exceptions.ThrowingException; 8 | 9 | import io.reactivex.Flowable; 10 | import io.reactivex.functions.BiFunction; 11 | 12 | public class FlowableFetchPagesByRequestTest { 13 | final static BiFunction> FETCH = new BiFunction>() { 14 | @Override 15 | public Flowable apply(Long start, Long request) { 16 | return Flowable.rangeLong(start, request); 17 | } 18 | }; 19 | 20 | final static BiFunction> FETCH_LESS = new BiFunction>() { 21 | @Override 22 | public Flowable apply(Long start, Long request) { 23 | return Flowable.rangeLong(start, request - 1); 24 | } 25 | }; 26 | 27 | final static BiFunction> FETCH_NONE = new BiFunction>() { 28 | @Override 29 | public Flowable apply(Long start, Long request) { 30 | return Flowable.empty(); 31 | } 32 | }; 33 | 34 | final static BiFunction> FETCH_MORE = new BiFunction>() { 35 | @Override 36 | public Flowable apply(Long start, Long request) { 37 | return Flowable.rangeLong(start, request + 1); 38 | } 39 | 40 | }; 41 | 42 | @Test 43 | public void isUtilityClass() { 44 | Asserts.assertIsUtilityClass(FlowableFetchPagesByRequest.class); 45 | } 46 | 47 | @Test 48 | public void testFetchByRequest() { 49 | Flowables.fetchPagesByRequest(FETCH) // 50 | .test(0) // 51 | .assertNoValues() // 52 | .requestMore(1) // 53 | .assertValue(0L) // 54 | .requestMore(2) // 55 | .assertValues(0L, 1L, 2L) // 56 | .requestMore(3) // 57 | .assertValues(0L, 1L, 2L, 3L, 4L, 5L) // 58 | .assertNotTerminated(); 59 | } 60 | 61 | @Test 62 | public void testFetchByRequestNonZeroStart() { 63 | Flowables.fetchPagesByRequest(FETCH, 3) // 64 | .test(0) // 65 | .assertNoValues() // 66 | .requestMore(1) // 67 | .assertValue(3L) // 68 | .requestMore(2) // 69 | .assertValues(3L, 4L, 5L) // 70 | .requestMore(3) // 71 | .assertValues(3L, 4L, 5L, 6L, 7L, 8L) // 72 | .assertNotTerminated(); 73 | } 74 | 75 | @Test 76 | public void testFetchByRequestError() { 77 | final BiFunction> fetch = new BiFunction>() { 78 | @Override 79 | public Flowable apply(Long start, Long n) { 80 | throw new ThrowingException(); 81 | } 82 | 83 | }; 84 | Flowables.fetchPagesByRequest(fetch) // 85 | .test(1) // 86 | .assertNoValues() // 87 | .assertError(ThrowingException.class); 88 | } 89 | 90 | @Test 91 | public void testFetchCompletesIfReturnsLessThanRequested() { 92 | Flowables.fetchPagesByRequest(FETCH_LESS) // 93 | .test(100) // 94 | .assertValueCount(99) // 95 | .assertComplete(); 96 | } 97 | 98 | @Test 99 | public void testFetchCompletesIfNoneReturned() { 100 | Flowables.fetchPagesByRequest(FETCH_NONE) // 101 | .test() // 102 | .assertNoValues() // 103 | .assertComplete(); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowableMapLastTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import org.junit.Test; 10 | 11 | import com.github.davidmoten.rx2.Consumers; 12 | import com.github.davidmoten.rx2.flowable.Transformers; 13 | 14 | import io.reactivex.Flowable; 15 | import io.reactivex.functions.Function; 16 | 17 | public class FlowableMapLastTest { 18 | 19 | @Test 20 | public void testMapLastRequestAmount() { 21 | List list = new ArrayList(); 22 | Flowable.range(1, 10) // 23 | .doOnRequest(Consumers.addLongTo(list))// 24 | .compose(Transformers.mapLast(new Function() { 25 | @Override 26 | public Integer apply(Integer x) { 27 | return x + 1; 28 | } 29 | })).test(1) // 30 | .assertNotComplete() // 31 | .assertValues(1) // 32 | .requestMore(3) // 33 | .assertValues(1, 2, 3, 4); 34 | assertEquals(Arrays.asList(2L, 3L), list); 35 | } 36 | 37 | @Test 38 | public void testMapLastHandlesRequestOverflow() { 39 | List list = new ArrayList(); 40 | Flowable.range(1, 5) // 41 | .doOnRequest(Consumers.addLongTo(list))// 42 | .compose(Transformers.mapLast(new Function() { 43 | @Override 44 | public Integer apply(Integer x) { 45 | return x + 1; 46 | } 47 | })).test()// 48 | .assertComplete() // 49 | .assertValues(1, 2, 3, 4, 6); 50 | assertEquals(Arrays.asList(Long.MAX_VALUE), list); 51 | } 52 | 53 | @Test(expected = OutOfMemoryError.class) 54 | public void testMapLastHandlesFatalError() { 55 | Flowable.range(1, 5) // 56 | .compose(Transformers.mapLast(new Function() { 57 | @Override 58 | public Integer apply(Integer x) { 59 | throw new OutOfMemoryError(); 60 | } 61 | })).subscribe(); 62 | } 63 | 64 | @Test 65 | public void testMapLastHandlesNonFatalError() { 66 | final RuntimeException e = new RuntimeException(); 67 | Flowable.range(1, 5) // 68 | .compose(Transformers.mapLast(new Function() { 69 | @Override 70 | public Integer apply(Integer x) { 71 | throw e; 72 | } 73 | })).test() // 74 | .assertError(e); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowablePassThroughTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | import java.util.concurrent.atomic.AtomicReference; 10 | 11 | import org.junit.Test; 12 | import org.reactivestreams.Subscriber; 13 | import org.reactivestreams.Subscription; 14 | 15 | import io.reactivex.Flowable; 16 | import io.reactivex.internal.subscriptions.SubscriptionHelper; 17 | 18 | public class FlowablePassThroughTest { 19 | 20 | @Test 21 | public void test() { 22 | new FlowablePassThrough(Flowable.just(1, 2, 3)) // 23 | .test() // 24 | .assertValues(1, 2, 3) // 25 | .assertComplete(); 26 | } 27 | 28 | @Test 29 | public void testSubscriber() { 30 | final List list = new ArrayList(); 31 | new FlowablePassThrough(Flowable.just(1, 2, 3)) // 32 | .subscribe(new Subscriber() { 33 | 34 | @Override 35 | public void onSubscribe(Subscription s) { 36 | s.request(Long.MAX_VALUE); 37 | } 38 | 39 | @Override 40 | public void onNext(Integer t) { 41 | list.add(t); 42 | } 43 | 44 | @Override 45 | public void onError(Throwable t) { 46 | 47 | } 48 | 49 | @Override 50 | public void onComplete() { 51 | 52 | } 53 | }); 54 | assertEquals(Arrays.asList(1, 2, 3), list); 55 | } 56 | 57 | static final class FlowablePassThrough extends Flowable { 58 | 59 | private final Flowable source; 60 | 61 | public FlowablePassThrough(Flowable source) { 62 | this.source = source; 63 | } 64 | 65 | @Override 66 | protected void subscribeActual(Subscriber child) { 67 | source.subscribe(new PassThroughSubscriber(child)); 68 | } 69 | 70 | @SuppressWarnings("serial") 71 | private static final class PassThroughSubscriber extends AtomicLong 72 | implements Subscriber, Subscription { 73 | 74 | private final Subscriber child; 75 | private AtomicReference parent = new AtomicReference(); 76 | 77 | public PassThroughSubscriber(Subscriber child) { 78 | this.child = child; 79 | child.onSubscribe(this); 80 | } 81 | 82 | @Override 83 | public void onSubscribe(Subscription parent) { 84 | SubscriptionHelper.deferredSetOnce(this.parent, this, parent); 85 | } 86 | 87 | @Override 88 | public void request(long n) { 89 | SubscriptionHelper.deferredRequest(parent, this, n); 90 | } 91 | 92 | @Override 93 | public void cancel() { 94 | SubscriptionHelper.cancel(parent); 95 | } 96 | 97 | @Override 98 | public void onNext(T t) { 99 | System.out.println(t); 100 | child.onNext(t); 101 | } 102 | 103 | @Override 104 | public void onError(Throwable t) { 105 | child.onError(t); 106 | } 107 | 108 | @Override 109 | public void onComplete() { 110 | child.onComplete(); 111 | } 112 | 113 | } 114 | 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowableRepeatingTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | import com.github.davidmoten.rx2.Flowables; 10 | 11 | public class FlowableRepeatingTest { 12 | 13 | @Test 14 | public void testRepeatingTwo() { 15 | assertEquals(Arrays.asList(1000, 1000), Flowables.repeat(1000).take(2).toList().blockingGet()); 16 | } 17 | 18 | @Test 19 | public void testRepeatingZero() { 20 | Flowables.repeat(1000) // 21 | .test(0) // 22 | .assertNoValues() // 23 | .assertNotComplete() // 24 | .requestMore(1) // 25 | .assertValue(1000) // 26 | .assertNotComplete(); 27 | } 28 | 29 | @Test 30 | public void testRepeatingWithCount() { 31 | Flowables.repeat(1, 3) // 32 | .test() // 33 | .assertValues(1, 1, 1) // 34 | .assertComplete(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowableReverseTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | import com.github.davidmoten.rx2.flowable.Transformers; 7 | 8 | import io.reactivex.Flowable; 9 | 10 | public final class FlowableReverseTest { 11 | 12 | @Test 13 | public void testIsUtilityClass() { 14 | Asserts.assertIsUtilityClass(FlowableReverse.class); 15 | } 16 | 17 | @Test 18 | public void testEmpty() { 19 | Flowable.empty() // 20 | .compose(Transformers.reverse()) // 21 | .test() // 22 | .assertNoValues() // 23 | .assertComplete(); 24 | } 25 | 26 | @Test 27 | public void testOne() { 28 | Flowable.just(1) // 29 | .compose(Transformers.reverse()) // 30 | .test() // 31 | .assertValue(1) // 32 | .assertComplete(); 33 | } 34 | 35 | @Test 36 | public void testMany() { 37 | Flowable.just(1, 2, 3, 4, 5) // 38 | .compose(Transformers.reverse()) // 39 | .test() // 40 | .assertValues(5, 4, 3, 2, 1) // 41 | .assertComplete(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/FlowableWindowMinMaxTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.reactivestreams.Subscriber; 13 | import org.reactivestreams.Subscription; 14 | 15 | import com.github.davidmoten.rx2.flowable.Transformers; 16 | 17 | import io.reactivex.Flowable; 18 | 19 | public class FlowableWindowMinMaxTest { 20 | @Test 21 | public void testEmpty() { 22 | boolean empty = Flowable.empty().compose(Transformers.windowMin(5)).isEmpty() 23 | .blockingGet(); 24 | assertTrue(empty); 25 | } 26 | 27 | @Test 28 | public void testIncreasing() { 29 | List list = Flowable.just(1, 2, 3, 4).compose(Transformers.windowMin(2)).toList() 30 | .blockingGet(); 31 | assertEquals(Arrays.asList(1, 2, 3), list); 32 | } 33 | 34 | @Test 35 | public void testDecreasing() { 36 | List list = Flowable.just(4, 3, 2, 1).compose(Transformers.windowMin(2)).toList() 37 | .blockingGet(); 38 | assertEquals(Arrays.asList(3, 2, 1), list); 39 | } 40 | 41 | @Test 42 | public void testWindowSizeBiggerThanAvailableProducesEmptyList() { 43 | List list = Flowable.just(4, 3, 2, 1).compose(Transformers.windowMin(10)).toList() 44 | .blockingGet(); 45 | assertTrue(list.isEmpty()); 46 | } 47 | 48 | @Test 49 | public void testWindowMax() { 50 | List list = Flowable.just(4, 3, 2, 1).compose(Transformers.windowMax(2)).toList() 51 | .blockingGet(); 52 | assertEquals(Arrays.asList(4, 3, 2), list); 53 | } 54 | 55 | @Test(expected = IllegalArgumentException.class) 56 | public void testWindowSizeNegativeThrowsIAE() { 57 | Flowable.just(1).compose(Transformers.windowMax(-2)); 58 | } 59 | 60 | @Test(expected = IllegalArgumentException.class) 61 | public void testWindowSizeZeroThrowsIAE() { 62 | Flowable.just(1).compose(Transformers.windowMax(0)); 63 | } 64 | 65 | @Test 66 | public void testErrorPropagated() { 67 | RuntimeException r = new RuntimeException(); 68 | Flowable.error(r) // 69 | .compose(Transformers.windowMax(2)) // 70 | .test() // 71 | .assertError(r); 72 | } 73 | 74 | @Test 75 | public void testCancellation() { 76 | final AtomicInteger count = new AtomicInteger(); 77 | Flowable.just(3, 2, 5) // 78 | .compose(Transformers.windowMin(2)) // 79 | .subscribe(new Subscriber() { 80 | 81 | private Subscription s; 82 | 83 | @Override 84 | public void onSubscribe(Subscription s) { 85 | this.s = s; 86 | s.request(Long.MAX_VALUE); 87 | } 88 | 89 | @Override 90 | public void onNext(Integer t) { 91 | count.incrementAndGet(); 92 | s.cancel(); 93 | } 94 | 95 | @Override 96 | public void onError(Throwable t) { 97 | 98 | } 99 | 100 | @Override 101 | public void onComplete() { 102 | 103 | } 104 | }); 105 | Assert.assertEquals(1, count.get()); 106 | } 107 | 108 | @Test 109 | public void testExample() { 110 | Flowable.just(3, 2, 5, 1, 6, 4) // 111 | .compose(Transformers.windowMin(3)) // 112 | .test() // 113 | .assertValues(2, 1, 1, 1) // 114 | .assertComplete(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/SerializerJavaIOTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | 6 | import org.junit.Test; 7 | 8 | import com.github.davidmoten.rx2.buffertofile.Serializers; 9 | 10 | public class SerializerJavaIOTest { 11 | 12 | @Test(expected = IOException.class) 13 | public void testNoHeader() throws IOException, ClassNotFoundException { 14 | Serializers.javaIO().deserialize(new byte[] {}); 15 | } 16 | 17 | @Test(expected = IOException.class) 18 | public void testProblemAfterHeader() throws IOException, ClassNotFoundException { 19 | byte[] bytes = Serializers.javaIO().serialize(10); 20 | bytes = Arrays.copyOf(bytes, bytes.length - 1); 21 | Serializers.javaIO().deserialize(bytes); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/TransformerDecodeTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | 7 | public class TransformerDecodeTest { 8 | 9 | @Test 10 | public void testIsUtilClass() { 11 | Asserts.assertIsUtilityClass(TransformerDecode.class); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/TransformerStringSplitTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | 7 | public class TransformerStringSplitTest { 8 | 9 | @Test 10 | public void testIsUtilityClass() { 11 | Asserts.assertIsUtilityClass(TransformerStringSplit.class); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/MemoryMappedFileTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import org.junit.Test; 4 | 5 | public class MemoryMappedFileTest { 6 | 7 | @Test(expected = RuntimeException.class) 8 | public void testGetMethod() { 9 | MemoryMappedFile.getMethod(MemoryMappedFileTest.class, "hello"); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/PageListTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.io.File; 6 | import java.util.concurrent.Callable; 7 | 8 | import org.junit.Test; 9 | 10 | public class PageListTest { 11 | 12 | private static final Callable factory = new Callable() { 13 | int i = 0; 14 | 15 | @Override 16 | public File call() throws Exception { 17 | return new File("target/test" + (++i)); 18 | } 19 | }; 20 | 21 | @Test 22 | public void testPutOneAndReadLessThanPageSize() { 23 | Pages p = new Pages(factory, 8); 24 | p.putInt(1); 25 | assertEquals(1, p.getInt()); 26 | } 27 | 28 | @Test 29 | public void testPutTwoAndReadLessThanPageSize() { 30 | Pages p = new Pages(factory, 12); 31 | p.putInt(1); 32 | p.putInt(2); 33 | assertEquals(1, p.getInt()); 34 | assertEquals(2, p.getInt()); 35 | } 36 | 37 | @Test 38 | public void testPutTwoAndReadEqualsPageSize() { 39 | Pages p = new Pages(factory, 8); 40 | p.putInt(1); 41 | p.putInt(2); 42 | assertEquals(1, p.getInt()); 43 | assertEquals(2, p.getInt()); 44 | } 45 | 46 | @Test 47 | public void testPutTwoAndReadMoreThanPageSize() { 48 | Pages p = new Pages(factory, 4); 49 | p.putInt(1); 50 | p.putInt(2); 51 | assertEquals(1, p.getInt()); 52 | assertEquals(2, p.getInt()); 53 | } 54 | 55 | @Test 56 | public void testPutSixAndReadMultiplePages() { 57 | Pages p = new Pages(factory, 8); 58 | p.putInt(1); 59 | p.putInt(2); 60 | p.putInt(3); 61 | p.putInt(4); 62 | p.putInt(5); 63 | p.putInt(6); 64 | assertEquals(1, p.getInt()); 65 | assertEquals(2, p.getInt()); 66 | assertEquals(3, p.getInt()); 67 | assertEquals(4, p.getInt()); 68 | assertEquals(5, p.getInt()); 69 | assertEquals(6, p.getInt()); 70 | } 71 | 72 | // TODO test rewrite marking 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/PageTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.io.File; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class PageTest { 11 | 12 | @Test 13 | public void testPutAndRead() { 14 | Page page = new Page(new File("target/p1"), 100); 15 | byte[] bytes = new byte[] { 0, 1, 2, 3 }; 16 | page.put(0, bytes, 0, bytes.length); 17 | byte[] a = new byte[4]; 18 | page.get(a, 0, 0, 4); 19 | Assert.assertArrayEquals(bytes, a); 20 | a = new byte[2]; 21 | page.get(a, 0, 2, 2); 22 | Assert.assertArrayEquals(new byte[] { 2, 3 }, a); 23 | page.close(); 24 | } 25 | 26 | @Test 27 | public void testPageSize() { 28 | Page page = new Page(new File("target/p1"), 100); 29 | assertEquals(100, page.length()); 30 | page.close(); 31 | } 32 | 33 | @Test 34 | public void testPagePrintln() { 35 | Page.println("boo"); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/internal/flowable/buffertofile/UnsafeAccessTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.internal.flowable.buffertofile; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | 7 | public class UnsafeAccessTest { 8 | 9 | @Test 10 | public void isUtilityClass() { 11 | Asserts.assertIsUtilityClass(UnsafeAccess.class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/observable/TransformersTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.observable; 2 | 3 | import org.junit.Test; 4 | 5 | import com.github.davidmoten.junit.Asserts; 6 | import com.github.davidmoten.rx2.observable.Transformers; 7 | 8 | public class TransformersTest { 9 | 10 | @Test 11 | public void isUtilityClass() { 12 | Asserts.assertIsUtilityClass(Transformers.class); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/rx2/util/PairTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.rx2.util; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | public class PairTest { 10 | 11 | @Test 12 | public void testValues() { 13 | Pair p = Pair.create(1, 2); 14 | Assert.assertEquals(1, (int) p.a()); 15 | Assert.assertEquals(1, (int) p.left()); 16 | Assert.assertEquals(1, (int) p._1()); 17 | Assert.assertEquals(2, (int) p.b()); 18 | Assert.assertEquals(2, (int) p.right()); 19 | Assert.assertEquals(2, (int) p._2()); 20 | } 21 | 22 | @Test 23 | public void testHashCode() { 24 | Assert.assertEquals(994, Pair.create(1, 2).hashCode()); 25 | } 26 | 27 | @Test 28 | public void testHashCodeWithNulls() { 29 | Assert.assertEquals(961, Pair.create(null, null).hashCode()); 30 | } 31 | 32 | @Test 33 | public void testToString() { 34 | Assert.assertEquals("Pair [left=1, right=2]", Pair.create(1, 2).toString()); 35 | } 36 | 37 | @Test 38 | public void testEquals() { 39 | Pair a = Pair.create(1, 2); 40 | Pair b = Pair.create(1, 2); 41 | Pair c = Pair.create(1, 3); 42 | Pair d = Pair.create(3, 2); 43 | Pair e = Pair.create(3, 4); 44 | Pair f = Pair.create(1, null); 45 | Pair g = Pair.create(null, 2); 46 | Pair h = Pair.create(null, null); 47 | Pair i = Pair.create(null, null); 48 | assertTrue(a.equals(b)); 49 | assertTrue(a.equals(a)); 50 | assertFalse(a.equals(c)); 51 | assertFalse(a.equals(d)); 52 | assertFalse(a.equals(e)); 53 | assertFalse(a.equals(null)); 54 | assertFalse(a.equals(f)); 55 | assertFalse(a.equals(g)); 56 | assertFalse(a.equals(1)); 57 | assertTrue(f.equals(f)); 58 | assertTrue(g.equals(g)); 59 | assertFalse(f.equals(a)); 60 | assertFalse(g.equals(a)); 61 | assertTrue(h.equals(i)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/github/davidmoten/util/RingBufferTest.java: -------------------------------------------------------------------------------- 1 | package com.github.davidmoten.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.util.Iterator; 8 | 9 | import org.junit.Test; 10 | 11 | public class RingBufferTest { 12 | 13 | @Test 14 | public void testEmpty() { 15 | RingBuffer q = RingBuffer.create(3); 16 | assertTrue(q.isEmpty()); 17 | assertEquals(0, q.size()); 18 | } 19 | 20 | @Test 21 | public void testPush() { 22 | RingBuffer q = RingBuffer.create(3); 23 | q.add(1); 24 | assertFalse(q.isEmpty()); 25 | assertEquals(1, q.size()); 26 | assertEquals(1, (int) q.poll()); 27 | assertTrue(q.isEmpty()); 28 | } 29 | 30 | @Test 31 | public void testPushTwo() { 32 | RingBuffer q = RingBuffer.create(3); 33 | q.add(1); 34 | q.add(2); 35 | assertFalse(q.isEmpty()); 36 | assertEquals(2, q.size()); 37 | assertEquals(1, (int) q.poll()); 38 | assertEquals(2, (int) q.poll()); 39 | assertTrue(q.isEmpty()); 40 | q.add(3); 41 | q.add(4); 42 | assertEquals(3, (int) q.poll()); 43 | assertEquals(4, (int) q.poll()); 44 | } 45 | 46 | @Test(expected = RuntimeException.class) 47 | public void testPushThreeOverflows() { 48 | RingBuffer q = RingBuffer.create(2); 49 | q.add(1); 50 | q.add(2); 51 | q.add(3); 52 | assertFalse(q.isEmpty()); 53 | assertEquals(3, q.size()); 54 | assertEquals(1, (int) q.poll()); 55 | assertEquals(2, (int) q.poll()); 56 | assertEquals(3, (int) q.poll()); 57 | } 58 | 59 | public void testPushThreeInSizeThree() { 60 | RingBuffer q = RingBuffer.create(3); 61 | q.add(1); 62 | q.add(2); 63 | q.add(3); 64 | assertFalse(q.isEmpty()); 65 | assertEquals(3, q.size()); 66 | assertEquals(1, (int) q.poll()); 67 | assertEquals(2, (int) q.poll()); 68 | assertEquals(3, (int) q.poll()); 69 | } 70 | 71 | public void testPushThreeAndEnumerate() { 72 | RingBuffer q = RingBuffer.create(3); 73 | q.add(1); 74 | q.add(2); 75 | q.add(3); 76 | Iterator en = q.iterator(); 77 | assertEquals(1, (int) en.next()); 78 | assertEquals(1, (int) q.poll()); 79 | assertEquals(2, (int) en.next()); 80 | assertEquals(2, (int) q.poll()); 81 | assertEquals(3, (int) en.next()); 82 | assertEquals(3, (int) q.poll()); 83 | assertFalse(en.hasNext()); 84 | } 85 | } -------------------------------------------------------------------------------- /src/test/resources/test.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmoten/rxjava2-extras/f108c2792078c26dafac27921c208f8374911b49/src/test/resources/test.zip -------------------------------------------------------------------------------- /src/test/resources/test2.txt: -------------------------------------------------------------------------------- 1 | hello world 2 | incoming message -------------------------------------------------------------------------------- /src/test/resources/test3.txt: -------------------------------------------------------------------------------- 1 | hello there -------------------------------------------------------------------------------- /src/test/resources/test4.txt: -------------------------------------------------------------------------------- 1 | # example to use with Strings.splitLinesFilterComments 2 | # a comment line 3 | # age , height-cm, name 4 | 23,176,FRED 5 | 13,130,JOHN 6 | # finished 7 | --------------------------------------------------------------------------------