From 09202c1d1980b6df1f1e3d5c116a82cf94d6fa14 Mon Sep 17 00:00:00 2001 From: javasabr Date: Mon, 24 Nov 2025 06:40:03 +0100 Subject: [PATCH 1/2] implement GC optimized int to ref dictionary --- .../dictionary/DictionaryFactory.java | 5 + ...zedMutableHashBasedIntToRefDictionary.java | 121 ++++++++++++++++++ .../ReusableLinkedHashIntToRefEntry.java | 55 ++++++++ .../impl/gc/optimized/package-info.java | 4 + .../dictionary/IntToRefDictionaryTest.java | 6 +- .../MutableIntToRefDictionaryTest.java | 3 +- .../rlib/common/util/NumberedEnumMap.java | 3 + 7 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/GcOptimizedMutableHashBasedIntToRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/ReusableLinkedHashIntToRefEntry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/package-info.java diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryFactory.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryFactory.java index 34a6fc1b..e18b13ae 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryFactory.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryFactory.java @@ -4,6 +4,7 @@ import javasabr.rlib.collections.dictionary.impl.DefaultMutableHashBasedLongToRefDictionary; import javasabr.rlib.collections.dictionary.impl.DefaultMutableHashBasedRefToRefDictionary; import javasabr.rlib.collections.dictionary.impl.StampedLockBasedHashBasedRefToRefDictionary; +import javasabr.rlib.collections.dictionary.impl.gc.optimized.GcOptimizedMutableHashBasedIntToRefDictionary; import lombok.experimental.UtilityClass; @UtilityClass @@ -16,6 +17,10 @@ public static MutableIntToRefDictionary mutableIntToRefDictionary() { return new DefaultMutableHashBasedIntToRefDictionary<>(); } + public static MutableIntToRefDictionary gcOptimizedIntToRefDictionary() { + return new GcOptimizedMutableHashBasedIntToRefDictionary<>(); + } + public static MutableLongToRefDictionary mutableLongToRefDictionary() { return new DefaultMutableHashBasedLongToRefDictionary<>(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/GcOptimizedMutableHashBasedIntToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/GcOptimizedMutableHashBasedIntToRefDictionary.java new file mode 100644 index 00000000..945f4efd --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/GcOptimizedMutableHashBasedIntToRefDictionary.java @@ -0,0 +1,121 @@ +package javasabr.rlib.collections.dictionary.impl.gc.optimized; + +import java.util.Arrays; +import java.util.Deque; +import javasabr.rlib.collections.deque.DequeFactory; +import javasabr.rlib.collections.dictionary.IntToRefDictionary; +import javasabr.rlib.collections.dictionary.impl.AbstractMutableHashBasedIntToRefDictionary; +import javasabr.rlib.collections.dictionary.impl.ImmutableHashBasedIntToRefDictionary; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@Getter +@Accessors(fluent = true) +@FieldDefaults(level = AccessLevel.PROTECTED) +public class GcOptimizedMutableHashBasedIntToRefDictionary extends + AbstractMutableHashBasedIntToRefDictionary> { + + private static final int MAX_POOL_SIZE = 40; + + final Deque> entryPool; + @Nullable ReusableLinkedHashIntToRefEntry[] entries; + int size; + int threshold; + + public GcOptimizedMutableHashBasedIntToRefDictionary() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + public GcOptimizedMutableHashBasedIntToRefDictionary(int initCapacity, float loadFactor) { + super(loadFactor); + //noinspection unchecked + this.entries = new ReusableLinkedHashIntToRefEntry[initCapacity]; + this.threshold = (int) (initCapacity * loadFactor); + this.entryPool = DequeFactory.arrayBasedBased(ReusableLinkedHashIntToRefEntry.class); + } + + @Override + public boolean isEmpty() { + return size < 1; + } + + @Override + protected int incrementSize() { + return size++; + } + + @Override + protected int decrementSize() { + return size--; + } + + @Override + public void clear() { + for (ReusableLinkedHashIntToRefEntry entry : entries()) { + while (entry != null) { + ReusableLinkedHashIntToRefEntry next = entry.next(); + deallocate(entry); + entry = next; + } + } + Arrays.fill(entries, null); + size = 0; + } + + @Override + protected void threshold(int threshold) { + this.threshold = threshold; + } + + @Override + protected void entries(@Nullable ReusableLinkedHashIntToRefEntry[] entries) { + this.entries = entries; + } + + @Nullable + @Override + public V remove(int key) { + ReusableLinkedHashIntToRefEntry entry = removeEntryForKey(key); + if (entry == null) { + return null; + } + V value = entry.value(); + deallocate(entry); + return value; + } + + @Override + protected ReusableLinkedHashIntToRefEntry allocate( + int hash, + int key, + @Nullable V value, + @Nullable ReusableLinkedHashIntToRefEntry next) { + ReusableLinkedHashIntToRefEntry reused = entryPool.pollLast(); + if (reused != null) { + return reused.reinit(next, key, value, hash); + } + return new ReusableLinkedHashIntToRefEntry<>(next, key, value, hash); + } + + private void deallocate(ReusableLinkedHashIntToRefEntry entry) { + if (entryPool.size() < MAX_POOL_SIZE) { + entryPool.addLast(entry.clear()); + } + } + + @Nullable + @Override + protected ReusableLinkedHashIntToRefEntry[] allocate(int length) { + //noinspection unchecked + return new ReusableLinkedHashIntToRefEntry[length]; + } + + @Override + public IntToRefDictionary toReadOnly() { + @Nullable ReusableLinkedHashIntToRefEntry[] copied = Arrays.copyOf(entries, entries.length); + return new ImmutableHashBasedIntToRefDictionary<>(copied, size); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/ReusableLinkedHashIntToRefEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/ReusableLinkedHashIntToRefEntry.java new file mode 100644 index 00000000..fa9d688d --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/ReusableLinkedHashIntToRefEntry.java @@ -0,0 +1,55 @@ +package javasabr.rlib.collections.dictionary.impl.gc.optimized; + +import javasabr.rlib.collections.dictionary.LinkedHashIntToRefEntry; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@Getter +@Setter +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ReusableLinkedHashIntToRefEntry implements + LinkedHashIntToRefEntry> { + + @Nullable + ReusableLinkedHashIntToRefEntry next; + + int key; + @Nullable + V value; + + int hash; + + public ReusableLinkedHashIntToRefEntry( + @Nullable ReusableLinkedHashIntToRefEntry next, + int key, + @Nullable V value, + int hash) { + this.next = next; + this.key = key; + this.value = value; + this.hash = hash; + } + + public ReusableLinkedHashIntToRefEntry clear() { + this.next = null; + this.value = null; + return this; + } + + public ReusableLinkedHashIntToRefEntry reinit( + @Nullable ReusableLinkedHashIntToRefEntry next, + int key, + @Nullable V value, + int hash) { + this.next = next; + this.key = key; + this.value = value; + this.hash = hash; + return this; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/package-info.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/package-info.java new file mode 100644 index 00000000..e184cc80 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/gc/optimized/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.collections.dictionary.impl.gc.optimized; + +import org.jspecify.annotations.NullMarked; diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java index 0152f956..fd613d11 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/IntToRefDictionaryTest.java @@ -183,12 +183,14 @@ private static Stream generateDictionaries() { return Stream.of( Arguments.of(source), - Arguments.of(DictionaryFactory.mutableIntToRefDictionary().append(source))); + Arguments.of(DictionaryFactory.mutableIntToRefDictionary().append(source)), + Arguments.of(DictionaryFactory.gcOptimizedIntToRefDictionary().append(source))); } private static Stream generateEmptyDictionaries() { return Stream.of( Arguments.of(IntToRefDictionary.empty()), - Arguments.of(DictionaryFactory.mutableIntToRefDictionary())); + Arguments.of(DictionaryFactory.mutableIntToRefDictionary()), + Arguments.of(DictionaryFactory.gcOptimizedIntToRefDictionary())); } } diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java index a018f9e9..cc72f7db 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/MutableIntToRefDictionaryTest.java @@ -158,6 +158,7 @@ void shouldBeEmptyAfterClear(MutableIntToRefDictionary dictionary) { private static Stream generateDictionaries() { return Stream.of( Arguments.of(MutableIntToRefDictionary.ofTypes(String.class)), - Arguments.of(DictionaryFactory.mutableIntToRefDictionary())); + Arguments.of(DictionaryFactory.mutableIntToRefDictionary()), + Arguments.of(DictionaryFactory.gcOptimizedIntToRefDictionary())); } } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java b/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java index 8ccfd169..8a8cd29d 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/NumberedEnumMap.java @@ -1,10 +1,13 @@ package javasabr.rlib.common.util; import java.util.stream.Stream; +import lombok.AccessLevel; import lombok.CustomLog; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; @CustomLog +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class NumberedEnumMap & NumberedEnum> { T[] values; From 188f318338dc3bfd3f7308f91585243682ee948d Mon Sep 17 00:00:00 2001 From: javasabr Date: Tue, 9 Dec 2025 19:13:46 +0100 Subject: [PATCH 2/2] extend API, update network connection API, add builders for array and dictionary --- README.md | 2 +- build.gradle | 2 +- .../rlib/collections/array/Array.java | 12 ++++ .../rlib/collections/array/ArrayBuilder.java | 40 +++++++++++ .../dictionary/RefToRefDictionary.java | 23 ++++++ .../dictionary/RefToRefDictionaryBuilder.java | 36 ++++++++++ .../dictionary/impl/AbstractDictionary.java | 1 + .../AbstractHashBasedRefToRefDictionary.java | 33 +++++++++ .../collections/array/ArrayBuilderTest.java | 26 +++++++ .../RefToRefDictionaryBuilderTest.java | 70 +++++++++++++++++++ .../rlib/network/StringNetworkLoadTest.java | 4 +- .../network/StringSslNetworkLoadTest.java | 4 +- .../javasabr/rlib/network/Connection.java | 8 +-- .../rlib/network/impl/AbstractConnection.java | 4 +- .../rlib/network/BaseNetworkTest.java | 4 +- .../rlib/network/DefaultNetworkTest.java | 10 +-- ...ingValidAndInvalidReceivedPacketsTest.java | 4 +- .../rlib/network/StringNetworkTest.java | 20 +++--- .../rlib/network/StringSslNetworkTest.java | 10 +-- 19 files changed, 277 insertions(+), 36 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayBuilder.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilder.java create mode 100644 rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayBuilderTest.java create mode 100644 rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilderTest.java diff --git a/README.md b/README.md index c2c4a75f..28fc0ea0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ repositories { } ext { - rlibVersion = "10.0.alpha8" + rlibVersion = "10.0.alpha9" } dependencies { diff --git a/build.gradle b/build.gradle index 44c3004c..a8d68183 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -rootProject.version = "10.0.alpha8" +rootProject.version = "10.0.alpha9" group = 'javasabr.rlib' allprojects { diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/Array.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/Array.java index f37f7a98..a40377d4 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/Array.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/Array.java @@ -23,6 +23,10 @@ static Array empty(Class type) { return new ImmutableArray<>(ClassUtils.unsafeCast(type)); } + static ArrayBuilder builder(Class type) { + return new ArrayBuilder<>(type); + } + static Array of(E single) { @SuppressWarnings("unchecked") Class type = (Class) single.getClass(); @@ -95,6 +99,14 @@ static Array copyOf(Array array) { return new ImmutableArray<>(array.type(), array.toArray()); } + static Array copyOf(Class type, Collection collection) { + if (collection instanceof MutableArray mutableArray) { + return copyOf(mutableArray); + } + E[] array = collection.toArray(ArrayUtils.create(type, collection.size())); + return ImmutableArray.trustWrap(array); + } + Class type(); /** diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayBuilder.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayBuilder.java new file mode 100644 index 00000000..06be898d --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayBuilder.java @@ -0,0 +1,40 @@ +package javasabr.rlib.collections.array; + +import java.util.Collection; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public final class ArrayBuilder { + + MutableArray elements; + + public ArrayBuilder(Class type) { + this.elements = ArrayFactory.mutableArray(type); + } + + public ArrayBuilder add(E element) { + elements.add(element); + return this; + } + + @SafeVarargs + public final ArrayBuilder add(E... other) { + elements.addAll(other); + return this; + } + + public ArrayBuilder add(Collection other) { + elements.addAll(other); + return this; + } + + public ArrayBuilder add(Array other) { + elements.addAll(other); + return this; + } + + public Array build() { + return Array.copyOf(elements); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionary.java index f7c90a79..cb489e04 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionary.java @@ -6,6 +6,21 @@ public interface RefToRefDictionary extends Dictionary { + static RefToRefDictionaryBuilder builder() { + return new RefToRefDictionaryBuilder<>(); + } + + static RefToRefDictionaryBuilder builder( + Class keyType, + Class valueType) { + return new RefToRefDictionaryBuilder<>(); + } + + static RefToRefDictionaryBuilder startWith(K key, V value) { + return new RefToRefDictionaryBuilder() + .put(key, value); + } + static RefToRefEntry entry(K key, V value) { return new SimpleRefToRefEntry<>(key, value); } @@ -22,6 +37,14 @@ static RefToRefDictionary of(K k1, V v1, K k2, V v2) { return ofEntries(entry(k1, v1), entry(k2, v2)); } + static RefToRefDictionary of(K k1, V v1, K k2, V v2, K k3, V v3) { + return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3)); + } + + static RefToRefDictionary of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4)); + } + @SafeVarargs static RefToRefDictionary ofEntries(RefToRefEntry... entries) { MutableRefToRefDictionary mutable = DictionaryFactory.mutableRefToRefDictionary(); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilder.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilder.java new file mode 100644 index 00000000..2c269c67 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilder.java @@ -0,0 +1,36 @@ +package javasabr.rlib.collections.dictionary; + +import java.util.Map; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class RefToRefDictionaryBuilder { + + MutableRefToRefDictionary elements; + + public RefToRefDictionaryBuilder() { + this.elements = DictionaryFactory.mutableRefToRefDictionary(); + } + + public RefToRefDictionaryBuilder put(K key, V value) { + this.elements.put(key, value); + return this; + } + + public RefToRefDictionaryBuilder put(RefToRefDictionary other) { + this.elements.putAll(other); + return this; + } + + public RefToRefDictionaryBuilder put(Map other) { + for (Map.Entry entry : other.entrySet()) { + elements.put(entry.getKey(), entry.getValue()); + } + return this; + } + + public RefToRefDictionary build() { + return elements.toReadOnly(); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractDictionary.java index 4eaf1934..c3570986 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractDictionary.java @@ -3,4 +3,5 @@ import javasabr.rlib.collections.dictionary.Dictionary; public abstract class AbstractDictionary implements Dictionary { + } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java index e66b2baf..4e3bc5f6 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefToRefDictionary.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.dictionary.impl; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -11,6 +12,7 @@ import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.collections.array.UnsafeMutableArray; import javasabr.rlib.collections.dictionary.LinkedHashEntry; +import javasabr.rlib.collections.dictionary.RefToRefDictionary; import javasabr.rlib.collections.dictionary.UnsafeRefToRefDictionary; import org.jspecify.annotations.Nullable; @@ -161,6 +163,37 @@ public MutableArray values(MutableArray container) { return container; } + @Override + public int hashCode() { + return Arrays.hashCode(entries()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RefToRefDictionary another)) { + return false; + } else if (size() != another.size()) { + return false; + } + + RefToRefDictionary toCompare = (RefToRefDictionary) obj; + + for (E entry : entries()) { + while (entry != null) { + if (!toCompare.containsKey(entry.key())) { + return false; + } + V value = entry.value(); + Object anotherValue = toCompare.get(entry.key()); + if (!Objects.equals(value, anotherValue)) { + return false; + } + entry = entry.next(); + } + } + return true; + } + @Override public String toString() { diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayBuilderTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayBuilderTest.java new file mode 100644 index 00000000..6cc36269 --- /dev/null +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayBuilderTest.java @@ -0,0 +1,26 @@ +package javasabr.rlib.collections.array; + +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ArrayBuilderTest { + + @Test + void shouldBuildArray() { + // when: + Array result = Array + .builder(String.class) + .add("first") + .add("second") + .add(Array.of("third", "fourth")) + .add("fifth", "sixth") + .add(List.of("seventh", "eight")) + .build(); + + // then: + Assertions + .assertThat(result) + .isEqualTo(Array.of("first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eight")); + } +} diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilderTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilderTest.java new file mode 100644 index 00000000..54e61c07 --- /dev/null +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/dictionary/RefToRefDictionaryBuilderTest.java @@ -0,0 +1,70 @@ +package javasabr.rlib.collections.dictionary; + +import java.util.Map; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RefToRefDictionaryBuilderTest { + + @Test + void shouldBuildDictionaryCorrectly() { + // when: + RefToRefDictionary dictionary = RefToRefDictionary + .builder() + .put(15, "15") + .put(22, "22") + .put(RefToRefDictionary.of(30, "30", 45, "45")) + .build(); + + // then: + Assertions + .assertThat(dictionary) + .isEqualTo(RefToRefDictionary.of(15, "15", 22, "22", 30, "30", 45, "45")); + } + + @Test + void shouldBuildWithTypesDictionaryCorrectly() { + // when: + RefToRefDictionary dictionary = RefToRefDictionary + .builder(Integer.class, String.class) + .put(15, "15") + .put(22, "22") + .put(RefToRefDictionary.of(30, "30", 45, "45")) + .build(); + + // then: + Assertions + .assertThat(dictionary) + .isEqualTo(RefToRefDictionary.of(15, "15", 22, "22", 30, "30", 45, "45")); + } + + @Test + void shouldBuildDictionaryUsingStartWithCorrectly() { + // when: + RefToRefDictionary dictionary = RefToRefDictionary + .startWith(15, "15") + .put(22, "22") + .put(RefToRefDictionary.of(30, "30", 45, "45")) + .build(); + + // then: + Assertions + .assertThat(dictionary) + .isEqualTo(RefToRefDictionary.of(15, "15", 22, "22", 30, "30", 45, "45")); + } + + @Test + void shouldBuildDictionaryBasedOnMap() { + // when: + RefToRefDictionary dictionary = RefToRefDictionary + .builder(Integer.class, String.class) + .put(Map.of(15, "15", 22, "22")) + .put(Map.of(30, "30", 45, "45")) + .build(); + + // then: + Assertions + .assertThat(dictionary) + .isEqualTo(RefToRefDictionary.of(15, "15", 22, "22", 30, "30", 45, "45")); + } +} diff --git a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java index d24ee458..3e8a7208 100644 --- a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java +++ b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java @@ -79,7 +79,7 @@ void connectAndSendMessages( ScheduledFuture schedule = executor.schedule( () -> { var message = newMessage(10, 10240); - connection.send(message); + connection.sendInBackground(message); }, delay, TimeUnit.MILLISECONDS); tasks.add(schedule); } @@ -146,7 +146,7 @@ void testServerWithMultiplyClients() { statistics .receivedClientPackersPerSecond() .accumulate(1); - connection.send(new StringWritableNetworkPacket<>("Echo: " + receivedPacket.data())); + connection.sendInBackground(new StringWritableNetworkPacket<>("Echo: " + receivedPacket.data())); statistics .sentEchoPackersPerSecond() .accumulate(1); diff --git a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java index 22e559ba..937b10f9 100644 --- a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java +++ b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java @@ -82,7 +82,7 @@ void connectAndSendMessages( ScheduledFuture schedule = executor.schedule( () -> { var message = newMessage(10, 10240); // 10240 - connection.send(message); + connection.sendInBackground(message); }, delay, TimeUnit.MILLISECONDS); tasks.add(schedule); } @@ -155,7 +155,7 @@ void testServerWithMultiplyClients() { statistics .receivedClientPackersPerSecond() .accumulate(1); - connection.send(new StringWritableNetworkPacket<>("Echo: " + receivedPacket.data())); + connection.sendInBackground(new StringWritableNetworkPacket<>("Echo: " + receivedPacket.data())); statistics .sentEchoPackersPerSecond() .accumulate(1); diff --git a/rlib-network/src/main/java/javasabr/rlib/network/Connection.java b/rlib-network/src/main/java/javasabr/rlib/network/Connection.java index 770a6c52..6426ddae 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/Connection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/Connection.java @@ -41,16 +41,16 @@ public String toString() { boolean closed(); /** - * Send a packet to connection's owner. + * Send a packet to connection's owner in background. */ - void send(WritableNetworkPacket packet); + void sendInBackground(WritableNetworkPacket packet); /** - * Send a packet to connection's owner with async feedback of this sending. + * Send a packet to connection's owner with async feedback of this action. * * @return the async result with true if the packet was sent or false if sending was failed. */ - CompletableFuture sendWithFeedback(WritableNetworkPacket packet); + CompletableFuture sendAsync(WritableNetworkPacket packet); /** * Register a consumer to handle received valid packets. diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java index 8adebcac..520e4f14 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java @@ -223,7 +223,7 @@ protected void handleSentPacket(WritableNetworkPacket packet, boolean result) } @Override - public final void send(WritableNetworkPacket packet) { + public final void sendInBackground(WritableNetworkPacket packet) { sendImpl(packet); } @@ -250,7 +250,7 @@ protected void queueAtFirst(WritableNetworkPacket packet) { } @Override - public CompletableFuture sendWithFeedback(WritableNetworkPacket packet) { + public CompletableFuture sendAsync(WritableNetworkPacket packet) { var asyncResult = new CompletableFuture(); sendImpl(new WritablePacketWithFeedback<>(asyncResult, packet)); if (closed()) { diff --git a/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java index 67649695..460da862 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java @@ -51,10 +51,10 @@ public boolean closed() { } @Override - public void send(WritableNetworkPacket packet) {} + public void sendInBackground(WritableNetworkPacket packet) {} @Override - public CompletableFuture sendWithFeedback(WritableNetworkPacket packet) { + public CompletableFuture sendAsync(WritableNetworkPacket packet) { return CompletableFuture.completedFuture(false); } diff --git a/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java index 9501789b..034b3eac 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java @@ -185,9 +185,9 @@ void echoNetworkTest() { var connection = event.connection(); var packet = event.packet(); if (packet instanceof ServerPackets.RequestEchoMessage request) { - connection.send(new ServerPackets.ResponseEchoMessage(request.message)); + connection.sendInBackground(new ServerPackets.ResponseEchoMessage(request.message)); } else if (packet instanceof ServerPackets.RequestServerTime request) { - connection.send(new ServerPackets.ResponseServerTime()); + connection.sendInBackground(new ServerPackets.ResponseServerTime()); } }) .subscribe(event -> log.info(event, "Received from client:[%s]"::formatted)); @@ -199,9 +199,9 @@ void echoNetworkTest() { .range(10, 100) .forEach(length -> { if (length % 2 == 0) { - connection.send(new ClientPackets.RequestServerTime()); + connection.sendInBackground(new ClientPackets.RequestServerTime()); } else { - connection.send(new ClientPackets.RequestEchoMessage(StringUtils.generate(length))); + connection.sendInBackground(new ClientPackets.RequestEchoMessage(StringUtils.generate(length))); } })) .flatMapMany(Connection::receivedEvents) @@ -269,7 +269,7 @@ public ByteBuffer takeBuffer(int bufferSize) { List messages = IntStream .range(0, packetCount) .mapToObj(value -> StringUtils.generate(random.nextInt(0, bufferSize))) - .peek(message -> clientToServer.send(new ClientPackets.RequestEchoMessage(message))) + .peek(message -> clientToServer.sendInBackground(new ClientPackets.RequestEchoMessage(message))) .toList(); List> receivedPackets = diff --git a/rlib-network/src/test/java/javasabr/rlib/network/HandlingValidAndInvalidReceivedPacketsTest.java b/rlib-network/src/test/java/javasabr/rlib/network/HandlingValidAndInvalidReceivedPacketsTest.java index 6edd39a7..cd09a10c 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/HandlingValidAndInvalidReceivedPacketsTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/HandlingValidAndInvalidReceivedPacketsTest.java @@ -106,9 +106,9 @@ void shouldCorrectlyReceiveValidAndInvalidPackets() throws InterruptedException .range(0, 30) .forEach(length -> { if (length % 5 == 0) { - connection.send(new ClientPackets.TestValidatablePacket(false)); + connection.sendInBackground(new ClientPackets.TestValidatablePacket(false)); } else { - connection.send(new ClientPackets.TestValidatablePacket(true)); + connection.sendInBackground(new ClientPackets.TestValidatablePacket(true)); } })) .subscribe(); diff --git a/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java index 2a88489c..40da803f 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java @@ -58,7 +58,7 @@ void echoNetworkTest() { .subscribe(event -> { String message = event.packet().data(); log.info(message, "Received from client:[%s]"::formatted); - event.connection().send(new StringWritableNetworkPacket<>("Echo: " + message)); + event.connection().sendInBackground(new StringWritableNetworkPacket<>("Echo: " + message)); }); ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); @@ -72,7 +72,7 @@ void echoNetworkTest() { int delay = ThreadLocalRandom .current() .nextInt(50); - executor.schedule(() -> connection.send(packet), delay, TimeUnit.MILLISECONDS); + executor.schedule(() -> connection.sendInBackground(packet), delay, TimeUnit.MILLISECONDS); })) .flatMapMany(connection -> connection.receivedEvents(RECEIVED_PACKET_TYPE)) .subscribe(event -> { @@ -127,7 +127,7 @@ public ByteBuffer takeBuffer(int bufferSize) { .mapToObj(value -> StringUtils.generate(random.nextInt(0, bufferSize))) .peek(message -> { log.info(message.length(), "Send [%s] symbols to server"::formatted); - clientToServer.send(new StringWritableNetworkPacket<>(message)); + clientToServer.sendInBackground(new StringWritableNetworkPacket<>(message)); }) .toList(); @@ -183,7 +183,7 @@ void shouldReceiveManyPacketsFromSmallToBigSize() { .nextInt(15); executor.schedule( () -> { - clientToServer.send(packet); + clientToServer.sendInBackground(packet); log.info(message.length(), "Send [%s] symbols to server"::formatted); }, delay, TimeUnit.MILLISECONDS); }) @@ -241,7 +241,7 @@ void shouldSendBiggerPacketThanWriteBuffer() { .nextInt(15); executor.schedule( () -> { - clientToServer.send(packet); + clientToServer.sendInBackground(packet); log.info(message.length(), "Send [%s] symbols to server"::formatted); }, delay, TimeUnit.MILLISECONDS); }) @@ -296,7 +296,7 @@ void testServerWithMultiplyClients() { .accepted() .flatMap(Connection::receivedEvents) .doOnNext(event -> receivedPacketsOnServer.incrementAndGet()) - .subscribe(event -> event.connection().send(newMessage(minMessageLength, maxMessageLength))); + .subscribe(event -> event.connection().sendInBackground(newMessage(minMessageLength, maxMessageLength))); Flux .fromStream(IntStream @@ -309,7 +309,7 @@ void testServerWithMultiplyClients() { var receivedEvents = connection.receivedEvents(); for (int i = 0; i < packetsPerClient; i++) { - connection.send(newMessage(minMessageLength, maxMessageLength)); + connection.sendInBackground(newMessage(minMessageLength, maxMessageLength)); sentPacketsToServer.incrementAndGet(); } @@ -354,7 +354,7 @@ void testServerWithMultiplyClientsUsingOldApi() { serverNetwork.onAccept(connection -> { connection.onReceiveValidPacket((con, packet) -> { receivedPacketsOnServer.incrementAndGet(); - con.send(newMessage(minMessageLength, maxMessageLength)); + con.sendInBackground(newMessage(minMessageLength, maxMessageLength)); }); connectedClients.countDown(); }); @@ -377,7 +377,7 @@ void testServerWithMultiplyClientsUsingOldApi() { })) .forEach(connection -> IntStream .range(0, packetsPerClient) - .peek(value -> connection.send(newMessage(minMessageLength, maxMessageLength))) + .peek(value -> connection.sendInBackground(newMessage(minMessageLength, maxMessageLength))) .forEach(val -> sentPacketsToServer.incrementAndGet())); Assertions.assertTrue( @@ -409,7 +409,7 @@ void shouldGetAllPacketWithFeedback() { List> asyncResults = IntStream .range(0, packetCount) .mapToObj(value -> StringUtils.generate(random.nextInt(0, bufferSize))) - .map(message -> clientToServer.sendWithFeedback(new StringWritableNetworkPacket<>(message))) + .map(message -> clientToServer.sendAsync(new StringWritableNetworkPacket<>(message))) .toList(); CompletableFuture diff --git a/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java index 72389f34..00d1b905 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java @@ -95,7 +95,7 @@ void serverSslNetworkTest() { .subscribe(event -> { var message = ((StringReadableNetworkPacket) event.packet()).data(); log.info(message, "Received from client:[%s]"::formatted); - event.connection().send(new StringWritableNetworkPacket<>("Echo: " + message)); + event.connection().sendInBackground(new StringWritableNetworkPacket<>("Echo: " + message)); }); SSLContext clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); @@ -154,7 +154,7 @@ void clientSslNetworkTest() { clientNetwork .connectReactive(new InetSocketAddress("localhost", serverPort)) - .doOnNext(connection -> connection.send(new StringWritableNetworkPacket<>("Hello SSL"))) + .doOnNext(connection -> connection.sendInBackground(new StringWritableNetworkPacket<>("Hello SSL"))) .doOnError(Throwable::printStackTrace) .flatMapMany(Connection::receivedEvents) .subscribe(event -> { @@ -230,7 +230,7 @@ void echoNetworkTest() { .subscribe(event -> { var message = ((StringReadableNetworkPacket) event.packet()).data(); log.info(message, "Received from client:[%s]"::formatted); - event.connection().send(new StringWritableNetworkPacket<>("Echo: " + message)); + event.connection().sendInBackground(new StringWritableNetworkPacket<>("Echo: " + message)); }); SSLContext clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); @@ -250,7 +250,7 @@ void echoNetworkTest() { .nextInt(2000); executor.schedule( () -> { - connection.send(packet); + connection.sendInBackground(packet); log.info(packet.data().length(), "Send [%s] symbols to server"::formatted); }, delay, TimeUnit.MILLISECONDS); })) @@ -303,7 +303,7 @@ void shouldReceiveManyPacketsFromSmallToBigSize() { var length = value % 3 == 0 ? bufferSize : random.nextInt(0, bufferSize / 2 - 1); return StringUtils.generate(length); }) - .peek(message -> clientToServer.send(new StringWritableNetworkPacket<>(message))) + .peek(message -> clientToServer.sendInBackground(new StringWritableNetworkPacket<>(message))) .toList(); List> receivedPackets =