From a240f0ab5b2bdc8501f7fa763d3b91bc07f20c79 Mon Sep 17 00:00:00 2001 From: Dmitry Timofeev Date: Wed, 28 Aug 2019 15:52:46 +0300 Subject: [PATCH 1/2] Add #stream to sets: A simple non-splittable implementation is OK for the sets are not currently sized. --- exonum-java-binding/CHANGELOG.md | 3 ++ .../storage/indices/KeySetIndexProxy.java | 28 +++++++++++++++++ .../storage/indices/ValueSetIndexProxy.java | 31 ++++++++++++++++++- .../KeySetIndexProxyIntegrationTest.java | 17 ++++++++++ .../ValueSetIndexProxyIntegrationTest.java | 21 +++++++++++-- 5 files changed, 96 insertions(+), 4 deletions(-) diff --git a/exonum-java-binding/CHANGELOG.md b/exonum-java-binding/CHANGELOG.md index 0ca7ed42e4..90d9a7806f 100644 --- a/exonum-java-binding/CHANGELOG.md +++ b/exonum-java-binding/CHANGELOG.md @@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. `CheckedProof#getIndexHash`, `ProofListIndexProxy#getIndexHash` and `ProofMapIndexProxy#getIndexHash` accordingly. +### Added +- `stream` for sets: KeySetIndex and ValueSetIndex. + ## [0.7.0] - 2019-07-17 ### Overview diff --git a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/KeySetIndexProxy.java b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/KeySetIndexProxy.java index 44ac047b9d..ea88b3ae7e 100644 --- a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/KeySetIndexProxy.java +++ b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/KeySetIndexProxy.java @@ -28,7 +28,11 @@ import com.exonum.binding.core.util.LibraryLoader; import com.google.protobuf.MessageLite; import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.LongSupplier; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * A key set is an index that contains no duplicate elements (keys). @@ -61,6 +65,11 @@ public final class KeySetIndexProxy extends AbstractIndexProxy implements Ite LibraryLoader.load(); } + // Note that we do *not* specify Spliterator.DISTINCT because it is documented in terms + // of Object#equals which this set does not use. + private static final int BASE_SPLITERATOR_CHARACTERISTICS = + Spliterator.NONNULL | Spliterator.ORDERED; + private final CheckingSerializerDecorator serializer; /** @@ -220,6 +229,25 @@ public Iterator iterator() { serializer::fromBytes); } + /** + * Returns a stream of the set elements. The elements are ordered lexicographically. + * + * @throws IllegalStateException if this set is not valid + */ + public Stream stream() { + return StreamSupport.stream( + Spliterators.spliteratorUnknownSize(iterator(), streamCharacteristics()), + false); + } + + private int streamCharacteristics() { + if (dbView.canModify()) { + return BASE_SPLITERATOR_CHARACTERISTICS; + } else { + return BASE_SPLITERATOR_CHARACTERISTICS | Spliterator.IMMUTABLE; + } + } + /** * Removes the element from this set. If it's not in the set, does nothing. * diff --git a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java index ffc4042500..6782a26349 100644 --- a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java +++ b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java @@ -33,7 +33,11 @@ import com.google.common.annotations.VisibleForTesting; import com.google.protobuf.MessageLite; import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.LongSupplier; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; /** @@ -67,6 +71,10 @@ public final class ValueSetIndexProxy extends AbstractIndexProxy static { LibraryLoader.load(); } + // Note that we do *not* specify Spliterator.DISTINCT because it is documented in terms + // of Object#equals which this set does not use. + private static final int BASE_SPLITERATOR_CHARACTERISTICS = + Spliterator.NONNULL | Spliterator.ORDERED; private final CheckingSerializerDecorator serializer; @@ -241,7 +249,7 @@ public Iterator hashes() { /** * Returns an iterator over the entries of this set. An entry is a hash-value pair. - * The entries are ordered by keys lexicographically. + * The entries are ordered by hashes lexicographically. * * @return an iterator over the entries of this set * @throws IllegalStateException if this set is not valid @@ -263,6 +271,27 @@ public Iterator> iterator() { private native void nativeIteratorFree(long iterNativeHandle); + /** + * Returns a stream of the entries in this set. An entry is a hash-value pair. + * The entries are ordered by hashes lexicographically. + * + * @throws IllegalStateException if this set is not valid + */ + public Stream> stream() { + return StreamSupport.stream( + Spliterators.spliteratorUnknownSize(iterator(), streamCharacteristics()), + false + ); + } + + private int streamCharacteristics() { + if (dbView.canModify()) { + return BASE_SPLITERATOR_CHARACTERISTICS; + } else { + return BASE_SPLITERATOR_CHARACTERISTICS | Spliterator.IMMUTABLE; + } + } + /** * An entry of a value set index: a hash-value pair. * diff --git a/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/KeySetIndexProxyIntegrationTest.java b/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/KeySetIndexProxyIntegrationTest.java index 5b328f111f..c8f346581b 100644 --- a/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/KeySetIndexProxyIntegrationTest.java +++ b/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/KeySetIndexProxyIntegrationTest.java @@ -19,6 +19,7 @@ import static com.exonum.binding.core.storage.indices.TestStorageItems.K1; import static com.exonum.binding.core.storage.indices.TestStorageItems.K9; import static com.exonum.binding.core.storage.indices.TestStorageItems.V1; +import static java.util.stream.Collectors.toList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -112,6 +113,22 @@ void testIterator() { }); } + @Test + void testStream() { + runTestWithView(database::createFork, (set) -> { + List elements = TestStorageItems.keys; + + elements.forEach(set::add); + + List streamElements = set.stream() + .collect(toList()); + + // Check that the stream includes all the elements added + // and that they appear in lexicographical order (the order of TestStorageItems.keys). + assertThat(streamElements, equalTo(elements)); + }); + } + @Test void removesAddedElement() { runTestWithView(database::createFork, (set) -> { diff --git a/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxyIntegrationTest.java b/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxyIntegrationTest.java index 08425da99c..7468442d5d 100644 --- a/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxyIntegrationTest.java +++ b/exonum-java-binding/core/src/test/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxyIntegrationTest.java @@ -19,6 +19,7 @@ import static com.exonum.binding.core.storage.indices.TestStorageItems.V1; import static com.exonum.binding.core.storage.indices.TestStorageItems.V2; import static com.exonum.binding.core.storage.indices.TestStorageItems.V9; +import static java.util.stream.Collectors.toList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -37,7 +38,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Collectors; import org.junit.jupiter.api.Test; class ValueSetIndexProxyIntegrationTest @@ -159,10 +159,25 @@ void testIterator() { }); } + @Test + void testStream() { + runTestWithView(database::createFork, (set) -> { + List elements = TestStorageItems.values; + + elements.forEach(set::add); + + List> entriesFromStream = set.stream() + .collect(toList()); + List> entriesExpected = getOrderedEntries(elements); + + assertThat(entriesFromStream, equalTo(entriesExpected)); + }); + } + private static List getOrderedHashes(List elements) { return getOrderedEntries(elements).stream() .map(ValueSetIndexProxy.Entry::getHash) - .collect(Collectors.toList()); + .collect(toList()); } private static List> getOrderedEntries(List elements) { @@ -170,7 +185,7 @@ private static List> getOrderedEntries(List ValueSetIndexProxy.Entry.from(getHashOf(value), value)) .sorted((e1, e2) -> UnsignedBytes.lexicographicalComparator() .compare(e1.getHash().asBytes(), e2.getHash().asBytes())) - .collect(Collectors.toList()); + .collect(toList()); } @Test From 5218111cd9bb4aa5a51aa25c4dbf4fe1e25bff5d Mon Sep 17 00:00:00 2001 From: Dmitry Timofeev Date: Wed, 28 Aug 2019 16:48:27 +0300 Subject: [PATCH 2/2] Fix checkstyle --- .../exonum/binding/core/storage/indices/ValueSetIndexProxy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java index 6782a26349..c889cccbb4 100644 --- a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java +++ b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/storage/indices/ValueSetIndexProxy.java @@ -71,6 +71,7 @@ public final class ValueSetIndexProxy extends AbstractIndexProxy static { LibraryLoader.load(); } + // Note that we do *not* specify Spliterator.DISTINCT because it is documented in terms // of Object#equals which this set does not use. private static final int BASE_SPLITERATOR_CHARACTERISTICS =