From d37d7791a71b554953abb190235414f26c840d0e Mon Sep 17 00:00:00 2001 From: javasabr Date: Wed, 13 Aug 2025 20:17:00 +0200 Subject: [PATCH 1/8] add new collection nodule --- rlib-collections/build.gradle | 5 + .../rlib/collections/array/Array.java | 180 +++++++++++++++++ .../rlib/collections/array/ArrayFactory.java | 12 ++ .../rlib/collections/array/MutableArray.java | 37 ++++ .../rlib/collections/array/UnsafeArray.java | 9 + .../collections/array/UnsafeMutableArray.java | 16 ++ .../collections/array/impl/AbstractArray.java | 191 ++++++++++++++++++ .../array/impl/AbstractMutableArray.java | 164 +++++++++++++++ .../array/impl/DefaultArrayIterator.java | 44 ++++ .../array/impl/DefaultMutableArray.java | 88 ++++++++ .../array/impl/ImmutableArray.java | 50 +++++ .../collections/array/impl/package-info.java | 4 + .../rlib/collections/array/package-info.java | 4 + .../rlib/collections/array/ArrayTest.java | 70 +++++++ .../javasabr/rlib/common/util/ArrayUtils.java | 21 ++ .../common/util/array/impl/FastArray.java | 2 +- settings.gradle | 3 +- 17 files changed, 898 insertions(+), 2 deletions(-) create mode 100644 rlib-collections/build.gradle create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/Array.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/package-info.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/package-info.java create mode 100644 rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java diff --git a/rlib-collections/build.gradle b/rlib-collections/build.gradle new file mode 100644 index 00000000..f8325ab9 --- /dev/null +++ b/rlib-collections/build.gradle @@ -0,0 +1,5 @@ + +dependencies { + api projects.rlibCommon + testImplementation projects.rlibLoggerImpl +} \ No newline at end of file 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 new file mode 100644 index 00000000..7f8afdce --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/Array.java @@ -0,0 +1,180 @@ +package javasabr.rlib.collections.array; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.RandomAccess; +import java.util.function.Function; +import java.util.stream.Stream; +import javasabr.rlib.collections.array.impl.DefaultArrayIterator; +import javasabr.rlib.collections.array.impl.ImmutableArray; +import org.jspecify.annotations.Nullable; + +/** + * @author JavaSaBr + */ +public interface Array extends Iterable, Serializable, Cloneable, RandomAccess { + + static Array of(E e1) { + return new ImmutableArray<>((Class) e1.getClass(), e1); + } + + static Array of(E e1, E e2) { + return new ImmutableArray<>((Class) e1.getClass(), e1, e2); + } + + static Array of(E e1, E e2, E e3) { + return new ImmutableArray<>((Class) e1.getClass(), e1, e2, e3); + } + + static Array of(E e1, E e2, E e3, E e4) { + return new ImmutableArray<>((Class) e1.getClass(), e1, e2, e3, e4); + } + + static Array of(Class type, E... elements) { + return new ImmutableArray<>(type, elements); + } + + Class type(); + + /** + * Returns the number of elements in this array. If this array + * contains more than {@code Integer.MAX_VALUE} elements, returns + * {@code Integer.MAX_VALUE}. + * + * @return the number of elements in this array + */ + int size(); + + /** + * Returns {@code true} if this array contains the specified element. + * More formally, returns {@code true} if and only if this array + * contains at least one element {@code e} such that + * {@code Objects.equals(object, e)}. + * + * @param object element whose presence in this array is to be tested + * @return {@code true} if this array contains the specified + * element + * @throws ClassCastException if the type of the specified element + * is incompatible with this array + * ({@linkplain Collection##optional-restrictions optional}) + * @throws NullPointerException if the specified element is null and this + * array does not permit null elements + * ({@linkplain Collection##optional-restrictions optional}) + */ + boolean contains(Object object); + + /** + * Returns {@code true} if this array contains all of the elements + * in the specified array. + * + * @param array array to be checked for containment in this collection + * @return {@code true} if this array contains all of the elements + * in the specified array + * @throws ClassCastException if the types of one or more elements + * in the specified array are incompatible with this + * array + * ({@linkplain Collection##optional-restrictions optional}) + * @throws NullPointerException if the specified array contains one + * or more null elements and this array does not permit null + * elements + * ({@linkplain Collection##optional-restrictions optional}) + * or if the specified collection is null. + * @see #contains(Object) + */ + boolean containsAll(Array array); + + /** + * Returns {@code true} if this array contains all of the elements + * in the specified collection. + * + * @param collection collection to be checked for containment in this array + * @return {@code true} if this array contains all of the elements + * in the specified collection + * @throws ClassCastException if the types of one or more elements + * in the specified collection are incompatible with this + * collection + * ({@linkplain Collection##optional-restrictions optional}) + * @throws NullPointerException if the specified collection contains one + * or more null elements and this array does not permit null + * elements + * ({@linkplain Collection##optional-restrictions optional}) + * or if the specified collection is null. + * @see #contains(Object) + */ + boolean containsAll(Collection collection); + + /** + * Returns {@code true} if this array contains all of the elements + * in the specified array. + * + * @param array array to be checked for containment in this collection + * @return {@code true} if this array contains all of the elements + * in the specified array + * @throws ClassCastException if the types of one or more elements + * in the specified array are incompatible with this + * array + * ({@linkplain Collection##optional-restrictions optional}) + * @throws NullPointerException if the specified array contains one + * or more null elements and this array does not permit null + * elements + * ({@linkplain Collection##optional-restrictions optional}) + * or if the specified collection is null. + * @see #contains(Object) + */ + boolean containsAll(Object[] array); + + /** + * Gets the first element of this array. + + * @return the retrieved element or null + */ + @Nullable E first(); + + E get(int index); + + /** + * Gets the last element of this array. + + * @return the retrieved element or null + */ + @Nullable E last(); + + @Override + default Iterator iterator() { + return new DefaultArrayIterator<>(this); + } + + /** + * @return the index of the object or -1. + */ + int indexOf(Object object); + + int lastIndexOf(Object object); + + T[] toArray(T[] newArray); + + /** + * Copy this array to the new array. + * + * @param the type parameter + * @param componentType the type of the new array. + * @return the copied array. + */ + T[] toArray(Class componentType); + + boolean isEmpty(); + + E[] toArray(); + + String toString(Function toString); + + /** + * Returns a sequential {@code Stream} with this array as its source. + * + * @return a sequential {@code Stream} over the elements in this array + */ + Stream stream(); + + UnsafeArray asUnsafe(); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java new file mode 100644 index 00000000..30e75263 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java @@ -0,0 +1,12 @@ +package javasabr.rlib.collections.array; + +import javasabr.rlib.collections.array.impl.DefaultMutableArray; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ArrayFactory { + + public static MutableArray mutableArray(Class type) { + return new DefaultMutableArray<>(type); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java new file mode 100644 index 00000000..1585b699 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java @@ -0,0 +1,37 @@ +package javasabr.rlib.collections.array; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Consumer; +import java.util.function.IntFunction; +import java.util.stream.Stream; + +public interface MutableArray extends Array, Collection { + + boolean addAll(Array array); + + /** + * @return the element previously at the specified position + */ + E remove(int index); + + void replace(int index, E element); + + @Override + Stream stream(); + + @Override + default T[] toArray(IntFunction generator) { + return Collection.super.toArray(generator); + } + + @Override + default Iterator iterator() { + return Array.super.iterator(); + } + + @Override + default void forEach(Consumer action) { + Array.super.forEach(action); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeArray.java new file mode 100644 index 00000000..eea2c7df --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeArray.java @@ -0,0 +1,9 @@ +package javasabr.rlib.collections.array; + +import org.jspecify.annotations.Nullable; + +public interface UnsafeArray extends Array { + @Nullable E[] wrapped(); + + E unsafeGet(int index); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java new file mode 100644 index 00000000..72788d65 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java @@ -0,0 +1,16 @@ +package javasabr.rlib.collections.array; + +import org.jspecify.annotations.Nullable; + +public interface UnsafeMutableArray extends UnsafeArray, MutableArray { + + UnsafeMutableArray prepareForSize(int expectedSize); + + UnsafeMutableArray unsafeAdd(E object); + + E unsafeRemove(int index); + + UnsafeMutableArray unsafeSet(int index, E element); + + UnsafeMutableArray trimToSize(); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java new file mode 100644 index 00000000..30b6c9b3 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java @@ -0,0 +1,191 @@ +package javasabr.rlib.collections.array.impl; + +import static javasabr.rlib.common.util.ClassUtils.unsafeCast; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.UnsafeArray; +import javasabr.rlib.common.util.ArrayUtils; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@Accessors(fluent = true) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractArray implements UnsafeArray { + + @Getter + final Class type; + + protected AbstractArray(Class type) { + this.type = type; + } + + @Override + public @Nullable E first() { + return isEmpty() ? null : get(0); + } + + @Override + public @Nullable E last() { + int size = size(); + if (size < 1) { + return null; + } + return get(size - 1); + } + + @Override + public E get(int index) { + checkIndex(index); + return unsafeGet(index); + } + + protected void checkIndex(int index) { + if (index < 0 || index >= size()) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + @Override + public boolean contains(Object object) { + var wrapped = wrapped(); + for (int i = 0, length = size(); i < length; i++) { + if (wrapped[i].equals(object)) { + return true; + } + } + return false; + } + + @Override + public boolean containsAll(Array array) { + + if (array.isEmpty()) { + return false; + } + + Object[] wrapped = array + .asUnsafe() + .wrapped(); + + for (int i = 0, length = array.size(); i < length; i++) { + if (!contains(wrapped[i])) { + return false; + } + } + + return true; + } + + @Override + public boolean containsAll(Collection collection) { + + if (collection.isEmpty()) { + return false; + } + + for (var element : collection) { + if (!contains(element)) { + return false; + } + } + + return true; + } + + @Override + public boolean containsAll(Object[] array) { + + if (array.length < 1) { + return false; + } + + for (Object element : array) { + if (!contains(element)) { + return false; + } + } + + return true; + } + + @Override + public int indexOf(Object object) { + var wrapped = wrapped(); + for (int i = 0, length = size(); i < length; i++) { + if (wrapped[i].equals(object)) { + return i; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object object) { + var wrapped = wrapped(); + for (int i = size() - 1; i >= 0; i--) { + if (wrapped[i].equals(object)) { + return i; + } + } + return -1; + } + + @Override + public void forEach(Consumer action) { + var wrapped = wrapped(); + for (int i = size() - 1; i >= 0; i--) { + action.accept(wrapped[i]); + } + } + + @Override + public T[] toArray(T[] newArray) { + + @Nullable E[] array = wrapped(); + + if (newArray.length >= size()) { + + for (int i = 0, j = 0, length = size(), newLength = newArray.length; i < length && j < newLength; i++) { + newArray[j++] = (T) array[i]; + } + + return newArray; + } + + Class arrayClass = unsafeCast(newArray.getClass()); + Class componentType = unsafeCast(arrayClass.getComponentType()); + + return toArray(componentType); + } + + @Override + public T[] toArray(Class componentType) { + T[] newArray = ArrayUtils.create(componentType, size()); + @Nullable E[] array = wrapped(); + System.arraycopy(array, 0, newArray, 0, size()); + return newArray; + } + + @Override + public E[] toArray() { + @Nullable E[] wrapped = wrapped(); + return Arrays.copyOf(wrapped, size(), (Class) wrapped.getClass()); + } + + @Override + public String toString(Function toString) { + return ArrayUtils.toString(wrapped(), size(), toString); + } + + @Override + public UnsafeArray asUnsafe() { + return this; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java new file mode 100644 index 00000000..9c068806 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java @@ -0,0 +1,164 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.UnsafeArray; +import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.common.util.ArrayUtils; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractMutableArray extends AbstractArray implements UnsafeMutableArray { + + protected static final int DEFAULT_CAPACITY = 10; + + public AbstractMutableArray(Class type) { + this(type, DEFAULT_CAPACITY); + } + public AbstractMutableArray(Class type, int capacity) { + super(type); + if (capacity < 0) { + throw new IllegalArgumentException("Negative capacity"); + } + wrapped(ArrayUtils.create(type, capacity)); + } + + @Override + public boolean add(E element) { + Objects.requireNonNull(element); + prepareForSize(size() + 1); + unsafeAdd(element); + return true; + } + + @Override + public boolean addAll(Array array) { + if (array.isEmpty()) { + return false; + } + int size = size(); + int elementsToAdd = array.size(); + prepareForSize(size + elementsToAdd); + processAdd(array, size, elementsToAdd); + return true; + } + + @Override + public boolean addAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } + int size = size(); + int elementsToAdd = collection.size(); + prepareForSize(size + elementsToAdd); + for (E element : collection) { + unsafeAdd(element); + } + return true; + } + + @Override + public void replace(int index, E element) { + checkIndex(index); + unsafeSet(index, element); + } + + @Override + public E remove(int index) { + checkIndex(index); + return unsafeRemove(index); + } + + @Override + public boolean remove(Object element) { + int index = indexOf(element); + if (index < 0) { + return false; + } + remove(index); + return true; + } + + @Override + public boolean removeAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } + int removed = 0; + for (Object element : collection) { + if (remove(element)) { + removed++; + } + } + return removed > 0; + } + + @Override + public boolean retainAll(Collection collection) { + + if (collection.isEmpty()) { + int size = size(); + clear(); + return size > 0; + } + + int removed = 0; + var wrapped = wrapped(); + + for (int i = 0, length = size(); i < length; i++) { + if (!collection.contains(wrapped[i])) { + unsafeRemove(i); + length--; + removed++; + } + } + + return removed > 0; + } + + @Override + public void clear() { + if (isEmpty()) { + return; + } + Arrays.fill(wrapped(), 0, size(), null); + size(0); + } + + @Override + public Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(wrapped(), 0, size(), Spliterator.NONNULL); + } + + protected void processAdd(Array array, int size, int elementsToAdd) { + System.arraycopy(array.asUnsafe().wrapped(), 0, wrapped(), size, elementsToAdd); + size(size + elementsToAdd); + } + + protected void processAdd(E[] array, int selfSize, int targetSize) { + System.arraycopy(array, 0, wrapped(), selfSize, targetSize); + size(selfSize + targetSize); + } + + protected abstract void size(int size); + + protected abstract void wrapped(@Nullable E[] wrapped); + + @Override + public UnsafeArray asUnsafe() { + return this; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java new file mode 100644 index 00000000..77cbc46b --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java @@ -0,0 +1,44 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import javasabr.rlib.collections.array.Array; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +/** + * @author JavaSaBr + */ +@FieldDefaults(level = AccessLevel.PRIVATE) +public class DefaultArrayIterator implements Iterator { + + final @Nullable E[] wrapped; + final int size; + + private int position; + + public DefaultArrayIterator(Array array) { + this.wrapped = array.asUnsafe().wrapped(); + this.size = array.size(); + } + + @Override + public boolean hasNext() { + return position < size; + } + + @Override + public E next() { + if (position >= size) { + throw new NoSuchElementException(); + } + //noinspection DataFlowIssue + return wrapped[position++]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java new file mode 100644 index 00000000..79e3a3bb --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java @@ -0,0 +1,88 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.Arrays; +import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.common.util.ArrayUtils; +import lombok.Getter; +import lombok.experimental.Accessors; +import org.jspecify.annotations.Nullable; + +@Getter +@Accessors(fluent = true) +public class DefaultMutableArray extends AbstractMutableArray { + + @Nullable E[] wrapped; + int size; + + public DefaultMutableArray(Class type) { + super(type); + } + + @Override + protected void size(int size) { + this.size = size; + } + + @Override + public boolean isEmpty() { + return size < 1; + } + + @Override + protected void wrapped(@Nullable E[] wrapped) { + this.wrapped = wrapped; + } + + @Override + public UnsafeMutableArray unsafeAdd(E object) { + wrapped[size++] = object; + return this; + } + + @Override + public UnsafeMutableArray unsafeSet(int index, E element) { + wrapped[index] = element; + return this; + } + + @Override + public E unsafeRemove(int index) { + + int numMoved = size - index - 1; + E element = wrapped[index]; + + if (numMoved > 0) { + System.arraycopy(wrapped, index + 1, wrapped, index, numMoved); + } + + size -= 1; + wrapped[size] = null; + + //noinspection DataFlowIssue + return element; + } + + @Override + public E unsafeGet(int index) { + //noinspection DataFlowIssue + return wrapped[index]; + } + + @Override + public UnsafeMutableArray prepareForSize(int expectedSize) { + if (expectedSize > wrapped.length) { + int newLength = Math.max((wrapped.length * 3) / 2, expectedSize); + wrapped = Arrays.copyOf(wrapped, newLength); + } + return this; + } + + @Override + public UnsafeMutableArray trimToSize() { + if (size == wrapped.length) { + return this; + } + wrapped = ArrayUtils.copyOfRange(wrapped, 0, size); + return this; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java new file mode 100644 index 00000000..043f5388 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java @@ -0,0 +1,50 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.stream.Stream; +import javasabr.rlib.collections.array.UnsafeArray; +import org.jspecify.annotations.Nullable; + +public class ImmutableArray extends AbstractArray implements UnsafeArray { + + final E[] wrapped; + + public ImmutableArray(Class type, E... elements) { + super(type); + this.wrapped = elements; + } + + @Override + public E unsafeGet(int index) { + return wrapped[index]; + } + + @Override + public Stream stream() { + return Stream.of(wrapped); + } + + @Override + public int size() { + return wrapped.length; + } + + @Override + public boolean isEmpty() { + return wrapped.length < 1; + } + + @Override + public E get(int index) { + + if (index < 0 || index >= size()) { + throw new IndexOutOfBoundsException(); + } + + return wrapped[index]; + } + + @Override + public @Nullable E[] wrapped() { + return wrapped; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/package-info.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/package-info.java new file mode 100644 index 00000000..681d6cfb --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.collections.array.impl; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/package-info.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/package-info.java new file mode 100644 index 00000000..8a8dfe9f --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.collections.array; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java new file mode 100644 index 00000000..77b488e4 --- /dev/null +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java @@ -0,0 +1,70 @@ +package javasabr.rlib.collections.array; + +import javasabr.rlib.common.util.array.ArrayFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author JavaSaBr + */ +public class ArrayTest { + + @Test + void arrayOfTest() { + + // when: + Array array = Array.of("First", "Second", "Third", " "); + + // then: + Assertions.assertEquals(4, array.size()); + Assertions.assertEquals("First", array.get(0)); + Assertions.assertEquals("Second", array.get(1)); + Assertions.assertEquals("Third", array.get(2)); + Assertions.assertEquals(" ", array.get(3)); + Assertions.assertEquals("First", array.first()); + Assertions.assertEquals(" ", array.last()); + + // then: + Assertions.assertArrayEquals( + new String[]{ + "First", + "Second", + "Third", + " " + }, array.stream().toArray()); + + // then: + Assertions.assertTrue(array.contains("Second")); + Assertions.assertFalse(array.contains("test")); + } + + @Test + void toArrayTest() { + + // when: + Array array = Array.of("First", "Second", "Third", " "); + + // then: + Assertions.assertArrayEquals( + new String[]{ + "First", + "Second", + "Third", + " " + }, array.toArray()); + Assertions.assertArrayEquals( + new String[]{ + "First", + "Second", + "Third", + " " + }, array.toArray(new String[4])); + Assertions.assertArrayEquals( + new String[]{ + "First", + "Second", + "Third", + " " + }, array.toArray(String.class)); + } +} diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java index 0cc5e06c..c361fe96 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java @@ -630,6 +630,27 @@ public static String toString(Array array) { return toString(array, Object::toString); } + public static String toString(T[] array, int size, Function toString) { + + if (array.length < 1) { + return "[]"; + } + + var builder = new StringBuilder(); + + for (int i = 0, last = size - 1; i < size; i++) { + builder.append(toString.apply(array[i])); + if (i == last) { + break; + } + builder.append(", "); + } + + builder.append("]"); + + return builder.toString(); + } + /** * Convert the array to a string presentation. * diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/array/impl/FastArray.java b/rlib-common/src/main/java/javasabr/rlib/common/util/array/impl/FastArray.java index be5be829..07920070 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/array/impl/FastArray.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/array/impl/FastArray.java @@ -155,7 +155,7 @@ public E fastRemove(int index) { public final E get(int index) { if (index < 0 || index >= size()) { - throw new NoSuchElementException(); + throw new IndexOutOfBoundsException(); } return array[index]; diff --git a/settings.gradle b/settings.gradle index 3d135fe4..e4b523ac 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,4 +14,5 @@ include ':rlib-plugin-system' include ':rlib-geometry' include ':rlib-classpath' include ':rlib-compiler' -include ':rlib-io' \ No newline at end of file +include ':rlib-io' +include ':rlib-collections' \ No newline at end of file From db4d3ee886f24c0a147b0b9604cf9d0a088abe6f Mon Sep 17 00:00:00 2001 From: javasabr Date: Thu, 14 Aug 2025 20:42:36 +0200 Subject: [PATCH 2/8] continue working on collections module --- rlib-classpath/build.gradle | 1 + .../rlib/classpath/ClassPathScanner.java | 19 ++-- .../classpath/impl/ClassPathScannerImpl.java | 47 ++++---- .../rlib/classpath/ClasspathScannerTests.java | 4 +- .../rlib/collections/array/Array.java | 34 +++++- .../collections/array/ArrayCollectors.java | 106 ++++++++++++++++++ .../rlib/collections/array/ArrayFactory.java | 9 +- .../array/ArrayIterationFunctions.java | 11 ++ .../rlib/collections/array/MutableArray.java | 11 ++ .../ReversedArrayIterationFunctions.java | 11 ++ .../collections/array/UnsafeMutableArray.java | 2 - .../collections/array/impl/AbstractArray.java | 17 ++- .../array/impl/AbstractMutableArray.java | 16 ++- .../impl/DefaultArrayIterationFunctions.java | 29 +++++ .../array/impl/DefaultMutableArray.java | 6 +- ...efaultReversedArrayIterationFunctions.java | 30 +++++ .../array/impl/ImmutableArray.java | 3 +- .../rlib/collections/array/ArrayTest.java | 1 - rlib-compiler/build.gradle | 1 + .../java/javasabr/rlib/compiler/Compiler.java | 2 +- .../compiler/impl/CompileEventListener.java | 8 +- .../impl/CompiledClassesClassLoader.java | 6 +- .../impl/CompiledJavaFileManager.java | 8 +- .../rlib/compiler/impl/JdkCompiler.java | 18 +-- .../javasabr/rlib/compiler/CompilerTests.java | 11 +- rlib-io/build.gradle | 1 + .../java/javasabr/rlib/io/util/FileUtils.java | 23 ++-- .../rlib/plugin/system/PluginSystem.java | 2 +- .../plugin/system/impl/BasePluginSystem.java | 32 +++--- 29 files changed, 367 insertions(+), 102 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java diff --git a/rlib-classpath/build.gradle b/rlib-classpath/build.gradle index 42096151..70a1fabd 100644 --- a/rlib-classpath/build.gradle +++ b/rlib-classpath/build.gradle @@ -2,5 +2,6 @@ dependencies { api projects.rlibCommon api projects.rlibIo + api projects.rlibCollections testImplementation projects.rlibLoggerImpl } \ No newline at end of file diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/ClassPathScanner.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/ClassPathScanner.java index 9410a4b4..32f0d315 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/ClassPathScanner.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/ClassPathScanner.java @@ -5,7 +5,8 @@ import java.net.URLClassLoader; import java.util.function.Predicate; import javasabr.rlib.classpath.impl.ClassPathScannerImpl; -import javasabr.rlib.common.util.array.Array; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.MutableArray; import org.jspecify.annotations.Nullable; /** @@ -48,32 +49,32 @@ public void scan(Predicate filter) {} void useSystemClassPath(boolean useSystemClasspath); default Array> findImplementations(Class interfaceClass) { - Array> result = Array.ofType(Class.class); + MutableArray> result = MutableArray.ofType(Class.class); findImplementationsTo(result, interfaceClass); return result; } - void findImplementationsTo(Array> container, Class interfaceClass); + void findImplementationsTo(MutableArray> container, Class interfaceClass); default Array> findInherited(Class parentClass) { - Array> result = Array.ofType(Class.class); + MutableArray> result = MutableArray.ofType(Class.class); findInheritedTo(result, parentClass); return result; } - void findInheritedTo(Array> container, Class parentClass); + void findInheritedTo(MutableArray> container, Class parentClass); default Array> findAnnotated(Class annotationClass) { - Array> result = Array.ofType(Class.class); + MutableArray> result = MutableArray.ofType(Class.class); findAnnotatedTo(result, annotationClass); return result; } - void findAnnotatedTo(Array> container, Class annotationClass); + void findAnnotatedTo(MutableArray> container, Class annotationClass); - void foundClassesTo(Array> container); + void foundClassesTo(MutableArray> container); - void foundResourcesTo(Array container); + void foundResourcesTo(MutableArray container); Array> foundClasses(); diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java index 14aa25d4..dc370977 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java @@ -15,11 +15,12 @@ import java.util.jar.JarInputStream; import java.util.zip.ZipException; import javasabr.rlib.classpath.ClassPathScanner; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.io.impl.ReuseBytesInputStream; import javasabr.rlib.io.impl.ReuseBytesOutputStream; import javasabr.rlib.common.util.ArrayUtils; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayFactory; import javasabr.rlib.io.util.IoUtils; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerManager; @@ -45,7 +46,7 @@ public class ClassPathScannerImpl implements ClassPathScanner { private static final String MODULE_INFO_CLASS = "module-info.class"; private static final String META_INF_PREFIX = "META-INF"; - final Array additionalPaths; + final MutableArray additionalPaths; final ClassLoader loader; Class[] classes; @@ -55,7 +56,7 @@ public class ClassPathScannerImpl implements ClassPathScanner { boolean useSystemClassPath; public ClassPathScannerImpl(ClassLoader classLoader) { - this.additionalPaths = ArrayFactory.newArray(String.class); + this.additionalPaths = MutableArray.ofType(String.class); this.loader = classLoader; this.classes = new Class[0]; this.resources = new String[0]; @@ -70,7 +71,7 @@ public void useSystemClassPath(boolean useSystemClassPath) { public void addClasses(Array> classes) { this.classes = ArrayUtils.combine( this.classes, - classes.toArray(ArrayUtils.EMPTY_CLASS_ARRAY), + classes.toArray(), Class.class); } @@ -78,12 +79,12 @@ public void addClasses(Array> classes) { public void addResources(Array resources) { this.resources = ArrayUtils.combine( this.resources, - resources.toArray(ArrayUtils.EMPTY_STRING_ARRAY), + resources.toArray(), String.class); } @Override - public void findImplementationsTo(Array> container, Class interfaceClass) { + public void findImplementationsTo(MutableArray> container, Class interfaceClass) { if (!interfaceClass.isInterface()) { throw new IllegalArgumentException("Class " + interfaceClass + " is not interface."); @@ -100,7 +101,7 @@ public void findImplementationsTo(Array> container, Class interf } @Override - public void findInheritedTo(Array> container, Class parentClass) { + public void findInheritedTo(MutableArray> container, Class parentClass) { if (Modifier.isFinal(parentClass.getModifiers())) { throw new IllegalArgumentException("Class " + parentClass + " is final class."); @@ -118,7 +119,7 @@ public void findInheritedTo(Array> container, Class parentClass) } @Override - public void findAnnotatedTo(Array> container, Class annotationClass) { + public void findAnnotatedTo(MutableArray> container, Class annotationClass) { for (Class klass : classes) { if (klass.isInterface() || isAbstract(klass.getModifiers()) || @@ -131,23 +132,23 @@ public void findAnnotatedTo(Array> container, Class> container) { + public void foundClassesTo(MutableArray> container) { container.addAll(classes); } @Override - public void foundResourcesTo(Array container) { + public void foundResourcesTo(MutableArray container) { container.addAll(resources); } @Override public Array> foundClasses() { - return Array.of(classes); + return Array.of(Class.class, classes); } @Override public Array foundResources() { - return Array.of(resources); + return Array.of(String.class, resources); } protected String[] calculatePathsToScan() { @@ -155,7 +156,7 @@ protected String[] calculatePathsToScan() { var systemClasspath = useSystemClassPath() ? classpathPaths() : ArrayUtils.EMPTY_STRING_ARRAY; var capacity = additionalPaths.size() + systemClasspath.length; - var result = Array.ofType(String.class, capacity); + var result = ArrayFactory.mutableArray(String.class, capacity); result.addAll(systemClasspath); result.addAll(additionalPaths); @@ -170,7 +171,7 @@ private void loadClass( @Nullable Path rootPath, @Nullable Path file, String name, - Array> container) { + MutableArray> container) { if (!name.endsWith(CLASS_EXTENSION)) { return; @@ -216,8 +217,8 @@ private void loadClass( private void scanDirectory( Path rootPath, - Array> classes, - Array resources, + MutableArray> classes, + MutableArray resources, Path directory) { LOGGER.debug(directory, "Scanning directory:[%s]"::formatted); try (DirectoryStream stream = Files.newDirectoryStream(directory)) { @@ -264,7 +265,7 @@ private void scanDirectory( } } - private void scanJar(Array> classes, Array resources, Path jarFile) { + private void scanJar(MutableArray> classes, MutableArray resources, Path jarFile) { LOGGER.debug(jarFile, "Scanning jar:[%s]"::formatted); if (!Files.exists(jarFile)) { @@ -287,8 +288,8 @@ private void scanJar(Array> classes, Array resources, Path jarF } private void scanJarInputStream( - Array> classes, - Array resources, + MutableArray> classes, + MutableArray resources, ReuseBytesOutputStream rout, ReuseBytesInputStream rin, byte[] buffer, @@ -315,7 +316,7 @@ private void scanJarInputStream( } } - private void scanJar(Array> classes, Array resources, InputStream jarFile) { + private void scanJar(MutableArray> classes, MutableArray resources, InputStream jarFile) { LOGGER.debug(jarFile, "Scanning jar:[%s]"::formatted); var rout = new ReuseBytesOutputStream(); @@ -337,8 +338,8 @@ public void scan(@Nullable Predicate filter) { var paths = calculatePathsToScan(); - Array> classes = Array.ofType(Class.class); - Array resources = Array.ofType(String.class); + MutableArray> classes = MutableArray.ofType(Class.class); + MutableArray resources = MutableArray.ofType(String.class); for (String path : paths) { var file = Path.of(path); diff --git a/rlib-classpath/src/test/java/javasabr/rlib/classpath/ClasspathScannerTests.java b/rlib-classpath/src/test/java/javasabr/rlib/classpath/ClasspathScannerTests.java index 69ccde92..e972fe97 100644 --- a/rlib-classpath/src/test/java/javasabr/rlib/classpath/ClasspathScannerTests.java +++ b/rlib-classpath/src/test/java/javasabr/rlib/classpath/ClasspathScannerTests.java @@ -1,8 +1,8 @@ package javasabr.rlib.classpath; import java.util.Collection; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.impl.AbstractArray; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.impl.AbstractArray; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.api.LoggerManager; import org.junit.jupiter.api.Assertions; 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 7f8afdce..3e0b3e98 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 @@ -3,11 +3,13 @@ import java.io.Serializable; import java.util.Collection; import java.util.Iterator; +import java.util.Optional; import java.util.RandomAccess; import java.util.function.Function; import java.util.stream.Stream; import javasabr.rlib.collections.array.impl.DefaultArrayIterator; import javasabr.rlib.collections.array.impl.ImmutableArray; +import javasabr.rlib.common.util.ClassUtils; import org.jspecify.annotations.Nullable; /** @@ -15,26 +17,44 @@ */ public interface Array extends Iterable, Serializable, Cloneable, RandomAccess { + static Array empty(Class type) { + return new ImmutableArray<>(ClassUtils.unsafeCast(type)); + } + static Array of(E e1) { - return new ImmutableArray<>((Class) e1.getClass(), e1); + return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1); } static Array of(E e1, E e2) { - return new ImmutableArray<>((Class) e1.getClass(), e1, e2); + return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1, e2); } static Array of(E e1, E e2, E e3) { - return new ImmutableArray<>((Class) e1.getClass(), e1, e2, e3); + return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1, e2, e3); } static Array of(E e1, E e2, E e3, E e4) { - return new ImmutableArray<>((Class) e1.getClass(), e1, e2, e3, e4); + return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1, e2, e3, e4); } - static Array of(Class type, E... elements) { + @SafeVarargs + static Array of(Class type, E... elements) { return new ImmutableArray<>(type, elements); } + @SafeVarargs + static Array optionals(Class type, Optional... elements) { + return Stream + .of(elements) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(ArrayCollectors.toArray(type)); + } + + static Array copyOf(MutableArray array) { + return new ImmutableArray<>(array.type(), array.toArray()); + } + Class type(); /** @@ -176,5 +196,9 @@ default Iterator iterator() { */ Stream stream(); + ReversedArrayIterationFunctions reversedIterations(); + + ArrayIterationFunctions iterations(); + UnsafeArray asUnsafe(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java new file mode 100644 index 00000000..ce87c751 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayCollectors.java @@ -0,0 +1,106 @@ +package javasabr.rlib.collections.array; + +import static java.util.Collections.unmodifiableSet; + +import java.util.EnumSet; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Collector.Characteristics; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ArrayCollectors { + + private static Set CH_ID = + unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); + + private static Set CH_ID_CONC = unmodifiableSet(EnumSet.of( + Characteristics.IDENTITY_FINISH, + Characteristics.CONCURRENT)); + + private static > Collector> collector( + Class type, + Function, M> arrayFactory) { + return new Collector<>() { + + private final Supplier supplier = () -> arrayFactory.apply(type); + + @Override + public Supplier supplier() { + return supplier; + } + + @Override + public BiConsumer accumulator() { + return MutableArray::add; + } + + @Override + public BinaryOperator combiner() { + return (source, toAdd) -> { + source.addAll(toAdd); + return source; + }; + } + + @Override + public Function> finisher() { + return Array::copyOf; + } + + @Override + public Set characteristics() { + return CH_ID; + } + }; + } + + private static > Collector mutableCollector( + Class type, + Function, A> arrayFactory) { + return new Collector<>() { + + private final Supplier supplier = () -> arrayFactory.apply(type); + + @Override + public Supplier supplier() { + return supplier; + } + + @Override + public BiConsumer accumulator() { + return MutableArray::add; + } + + @Override + public BinaryOperator combiner() { + return (source, toAdd) -> { + source.addAll(toAdd); + return source; + }; + } + + @Override + public Function finisher() { + return array -> array; + } + + @Override + public Set characteristics() { + return CH_ID; + } + }; + } + + public static Collector, MutableArray> toMutableArray(Class type) { + return mutableCollector(type, ArrayFactory::mutableArray); + } + + public static Collector, Array> toArray(Class type) { + return collector(type, ArrayFactory::mutableArray); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java index 30e75263..f8cdad0e 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java @@ -1,12 +1,17 @@ package javasabr.rlib.collections.array; import javasabr.rlib.collections.array.impl.DefaultMutableArray; +import javasabr.rlib.common.util.ClassUtils; import lombok.experimental.UtilityClass; @UtilityClass public class ArrayFactory { - public static MutableArray mutableArray(Class type) { - return new DefaultMutableArray<>(type); + public static MutableArray mutableArray(Class type) { + return new DefaultMutableArray<>(ClassUtils.unsafeCast(type)); + } + + public static MutableArray mutableArray(Class type, int capacity) { + return new DefaultMutableArray<>(ClassUtils.unsafeCast(type), capacity); } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java new file mode 100644 index 00000000..e46a98b6 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java @@ -0,0 +1,11 @@ +package javasabr.rlib.collections.array; + +import java.util.function.BiPredicate; +import org.jspecify.annotations.Nullable; + +public interface ArrayIterationFunctions { + + @Nullable E findAny(T arg1, BiPredicate filter); + + boolean anyMatch(T arg1, BiPredicate filter); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java index 1585b699..705d62e0 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java @@ -5,11 +5,22 @@ import java.util.function.Consumer; import java.util.function.IntFunction; import java.util.stream.Stream; +import javasabr.rlib.collections.array.impl.DefaultMutableArray; public interface MutableArray extends Array, Collection { + static MutableArray ofType(Class type) { + return new DefaultMutableArray<>(type); + } + boolean addAll(Array array); + default boolean addAll(MutableArray array) { + return addAll((Array) array); + } + + boolean addAll(E[] array); + /** * @return the element previously at the specified position */ diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java new file mode 100644 index 00000000..f9c405d6 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java @@ -0,0 +1,11 @@ +package javasabr.rlib.collections.array; + +import java.util.function.BiPredicate; +import org.jspecify.annotations.Nullable; + +public interface ReversedArrayIterationFunctions { + + @Nullable E findAny(T arg1, BiPredicate filter); + + boolean anyMatch(T arg1, BiPredicate filter); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java index 72788d65..74995114 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java @@ -1,7 +1,5 @@ package javasabr.rlib.collections.array; -import org.jspecify.annotations.Nullable; - public interface UnsafeMutableArray extends UnsafeArray, MutableArray { UnsafeMutableArray prepareForSize(int expectedSize); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java index 30b6c9b3..2d8617aa 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java @@ -7,8 +7,11 @@ import java.util.function.Consumer; import java.util.function.Function; import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayIterationFunctions; +import javasabr.rlib.collections.array.ReversedArrayIterationFunctions; import javasabr.rlib.collections.array.UnsafeArray; import javasabr.rlib.common.util.ArrayUtils; +import javasabr.rlib.common.util.ClassUtils; import lombok.AccessLevel; import lombok.Getter; import lombok.experimental.Accessors; @@ -22,8 +25,8 @@ public abstract class AbstractArray implements UnsafeArray { @Getter final Class type; - protected AbstractArray(Class type) { - this.type = type; + protected AbstractArray(Class type) { + this.type = ClassUtils.unsafeCast(type); } @Override @@ -145,6 +148,16 @@ public void forEach(Consumer action) { } } + @Override + public ReversedArrayIterationFunctions reversedIterations() { + return new DefaultReversedArrayIterationFunctions<>(this); + } + + @Override + public ArrayIterationFunctions iterations() { + return new DefaultArrayIterationFunctions<>(this); + } + @Override public T[] toArray(T[] newArray) { diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java index 9c068806..de1bdffa 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java @@ -20,10 +20,10 @@ public abstract class AbstractMutableArray extends AbstractArray implement protected static final int DEFAULT_CAPACITY = 10; - public AbstractMutableArray(Class type) { + public AbstractMutableArray(Class type) { this(type, DEFAULT_CAPACITY); } - public AbstractMutableArray(Class type, int capacity) { + public AbstractMutableArray(Class type, int capacity) { super(type); if (capacity < 0) { throw new IllegalArgumentException("Negative capacity"); @@ -51,6 +51,18 @@ public boolean addAll(Array array) { return true; } + @Override + public boolean addAll(E[] array) { + if (array.length < 1) { + return false; + } + int size = size(); + int elementsToAdd = array.length; + prepareForSize(size + elementsToAdd); + processAdd(array, size, elementsToAdd); + return true; + } + @Override public boolean addAll(Collection collection) { if (collection.isEmpty()) { diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java new file mode 100644 index 00000000..1494b22b --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java @@ -0,0 +1,29 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.function.BiPredicate; +import javasabr.rlib.collections.array.ArrayIterationFunctions; +import javasabr.rlib.collections.array.UnsafeArray; +import org.jspecify.annotations.Nullable; + +public record DefaultArrayIterationFunctions(UnsafeArray array) implements ArrayIterationFunctions { + + @Override + public @Nullable E findAny(T arg1, BiPredicate filter) { + + @Nullable E[] wrapped = array.wrapped(); + int size = array.size(); + + for (int i = 0; i < size; i++) { + E element = wrapped[i]; + if (filter.test(element, arg1)) { + return element; + } + } + return null; + } + + @Override + public boolean anyMatch(T arg1, BiPredicate filter) { + return findAny(arg1, filter) != null; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java index 79e3a3bb..65dfc195 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java @@ -14,10 +14,14 @@ public class DefaultMutableArray extends AbstractMutableArray { @Nullable E[] wrapped; int size; - public DefaultMutableArray(Class type) { + public DefaultMutableArray(Class type) { super(type); } + public DefaultMutableArray(Class type, int capacity) { + super(type, capacity); + } + @Override protected void size(int size) { this.size = size; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java new file mode 100644 index 00000000..b03120e9 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java @@ -0,0 +1,30 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.function.BiPredicate; +import javasabr.rlib.collections.array.ReversedArrayIterationFunctions; +import javasabr.rlib.collections.array.UnsafeArray; +import org.jspecify.annotations.Nullable; + +public record DefaultReversedArrayIterationFunctions(UnsafeArray array) implements + ReversedArrayIterationFunctions { + + @Override + public @Nullable E findAny(T arg1, BiPredicate filter) { + + @Nullable E[] wrapped = array.wrapped(); + int size = array.size(); + + for (int i = 0; i < size; i++) { + E element = wrapped[i]; + if (filter.test(arg1, element)) { + return element; + } + } + return null; + } + + @Override + public boolean anyMatch(T arg1, BiPredicate filter) { + return findAny(arg1, filter) != null; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java index 043f5388..80250acf 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java @@ -8,7 +8,8 @@ public class ImmutableArray extends AbstractArray implements UnsafeArray type, E... elements) { + @SafeVarargs + public ImmutableArray(Class type, E... elements) { super(type); this.wrapped = elements; } diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java index 77b488e4..533f06bf 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/array/ArrayTest.java @@ -1,6 +1,5 @@ package javasabr.rlib.collections.array; -import javasabr.rlib.common.util.array.ArrayFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/rlib-compiler/build.gradle b/rlib-compiler/build.gradle index 42096151..70a1fabd 100644 --- a/rlib-compiler/build.gradle +++ b/rlib-compiler/build.gradle @@ -2,5 +2,6 @@ dependencies { api projects.rlibCommon api projects.rlibIo + api projects.rlibCollections testImplementation projects.rlibLoggerImpl } \ No newline at end of file diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/Compiler.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/Compiler.java index 6d2aa47d..523b05d8 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/Compiler.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/Compiler.java @@ -2,7 +2,7 @@ import java.net.URI; import java.nio.file.Path; -import javasabr.rlib.common.util.array.Array; +import javasabr.rlib.collections.array.Array; /** * @author JavaSaBr diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompileEventListener.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompileEventListener.java index 6e14463b..8a13cef4 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompileEventListener.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompileEventListener.java @@ -1,6 +1,6 @@ package javasabr.rlib.compiler.impl; -import javasabr.rlib.common.util.array.Array; +import javasabr.rlib.collections.array.MutableArray; import javax.tools.Diagnostic; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; @@ -17,10 +17,10 @@ @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class CompileEventListener implements DiagnosticListener { - Array> diagnostics; + MutableArray> diagnostics; public CompileEventListener() { - this.diagnostics = Array.ofType(Diagnostic.class); + this.diagnostics = MutableArray.ofType(Diagnostic.class); } public void clear() { @@ -28,7 +28,7 @@ public void clear() { } @Override - public void report(Diagnostic diagnostic) { + public synchronized void report(Diagnostic diagnostic) { diagnostics.add(diagnostic); } } diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledClassesClassLoader.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledClassesClassLoader.java index 0ecfb855..881ba25b 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledClassesClassLoader.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledClassesClassLoader.java @@ -1,7 +1,7 @@ package javasabr.rlib.compiler.impl; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.Utils; -import javasabr.rlib.common.util.array.Array; import javasabr.rlib.compiler.CompiledClassData; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerManager; @@ -17,10 +17,10 @@ public class CompiledClassesClassLoader extends ClassLoader { private static final Logger LOGGER = LoggerManager.getLogger(CompiledClassesClassLoader.class); - Array compiledClassData; + MutableArray compiledClassData; public CompiledClassesClassLoader() { - this.compiledClassData = Array.ofType(CompiledClassData.class); + this.compiledClassData = MutableArray.ofType(CompiledClassData.class); } /** diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledJavaFileManager.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledJavaFileManager.java index fa076913..f3a7095e 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledJavaFileManager.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/CompiledJavaFileManager.java @@ -1,6 +1,6 @@ package javasabr.rlib.compiler.impl; -import javasabr.rlib.common.util.array.Array; +import javasabr.rlib.collections.array.MutableArray; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileObject; @@ -15,7 +15,7 @@ @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class CompiledJavaFileManager extends ForwardingJavaFileManager { - Array classNames; + MutableArray classNames; CompiledClassesClassLoader classLoader; public CompiledJavaFileManager( @@ -23,7 +23,7 @@ public CompiledJavaFileManager( CompiledClassesClassLoader classLoader) { super(fileManager); this.classLoader = classLoader; - this.classNames = Array.ofType(String.class); + this.classNames = MutableArray.ofType(String.class); } /** @@ -38,7 +38,7 @@ public String[] classNames() { } @Override - public JavaFileObject getJavaFileForOutput( + public synchronized JavaFileObject getJavaFileForOutput( Location location, String name, Kind kind, diff --git a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java index d1ae4a39..7f2f0acb 100644 --- a/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java +++ b/rlib-compiler/src/main/java/javasabr/rlib/compiler/impl/JdkCompiler.java @@ -3,8 +3,9 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayCollectors; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.compiler.Compiler; import javasabr.rlib.io.util.FileUtils; import javasabr.rlib.logger.api.Logger; @@ -31,6 +32,7 @@ public class JdkCompiler implements Compiler { private static final Logger LOGGER = LoggerManager.getLogger(JdkCompiler.class); + private static final Array EMPTY_OPTIONS = Array.empty(String.class); private static final Class[] EMPTY_CLASSES = new Class[0]; CompileEventListener listener; @@ -55,7 +57,7 @@ public JdkCompiler(boolean showDiagnostic) { @Override public Array> compileByUrls(Array urls) { return compile( - Array.empty(), + EMPTY_OPTIONS, urls .stream() .map(JavaFileSource::new) @@ -65,7 +67,7 @@ public Array> compileByUrls(Array urls) { @Override public Array> compileFiles(Array files) { return compile( - Array.empty(), + EMPTY_OPTIONS, files .stream() .map(JavaFileSource::new) @@ -75,7 +77,7 @@ public Array> compileFiles(Array files) { @Override public Array> compileDirectories(Array directories) { - Array files = Array.ofType(Path.class); + MutableArray files = MutableArray.ofType(Path.class); for (Path directory : directories) { if (!Files.exists(directory) || !Files.isDirectory(directory)) { @@ -86,11 +88,11 @@ public Array> compileDirectories(Array directories) { } if (files.isEmpty()) { - return Array.empty(); + return Array.empty(Class.class); } return compile( - Array.empty(), + EMPTY_OPTIONS, files .stream() .map(JavaFileSource::new) @@ -120,7 +122,7 @@ protected synchronized Array> compile( } } - Array> result = Array.ofType(Class.class); + MutableArray> result = MutableArray.ofType(Class.class); String[] classNames = fileManager.classNames(); LOGGER.debug(classNames.length, "Got [%s] compiled class names"::formatted); diff --git a/rlib-compiler/src/test/java/javasabr/rlib/compiler/CompilerTests.java b/rlib-compiler/src/test/java/javasabr/rlib/compiler/CompilerTests.java index f969ae02..21357927 100644 --- a/rlib-compiler/src/test/java/javasabr/rlib/compiler/CompilerTests.java +++ b/rlib-compiler/src/test/java/javasabr/rlib/compiler/CompilerTests.java @@ -3,8 +3,10 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.RecordComponent; import java.net.URISyntaxException; +import java.util.Comparator; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.common.util.array.Array; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.api.LoggerManager; import org.junit.jupiter.api.Assertions; @@ -32,13 +34,14 @@ void compileTest() // when: var compiler = CompilerFactory.newDefaultCompiler(); - var compiledClasses = compiler.compileByUrls(javaSources); + var compiledClasses = compiler.compileByUrls(javaSources) + .stream() + .sorted(Comparator.comparing(Class::getName)) + .collect(ArrayCollectors.toArray(Class.class)); // then: Assertions.assertEquals(3, compiledClasses.size()); - compiledClasses.sort((left, right) -> left.getName().compareTo(right.getName())); - Assertions.assertEquals("TestCompileDependency", compiledClasses.get(0).getName()); Assertions.assertEquals("TestCompileJavaSource", compiledClasses.get(1).getName()); Assertions.assertEquals("TestCompileRecord", compiledClasses.get(2).getName()); diff --git a/rlib-io/build.gradle b/rlib-io/build.gradle index f8325ab9..dec474f6 100644 --- a/rlib-io/build.gradle +++ b/rlib-io/build.gradle @@ -1,5 +1,6 @@ dependencies { api projects.rlibCommon + api projects.rlibCollections testImplementation projects.rlibLoggerImpl } \ No newline at end of file diff --git a/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java b/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java index fca12ce6..a3f1d611 100644 --- a/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java +++ b/rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java @@ -22,16 +22,16 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileTime; import java.util.Collection; +import java.util.Comparator; import java.util.Enumeration; import java.util.regex.Pattern; import java.util.stream.Stream; import java.util.zip.ZipInputStream; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.StringUtils; import javasabr.rlib.common.util.Utils; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayComparator; -import javasabr.rlib.common.util.array.ArrayFactory; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerManager; import org.jspecify.annotations.Nullable; @@ -43,7 +43,7 @@ public class FileUtils { private static final Logger LOGGER = LoggerManager.getLogger(FileUtils.class); - public static final ArrayComparator FILE_PATH_LENGTH_COMPARATOR = (first, second) -> { + public static final Comparator FILE_PATH_LENGTH_COMPARATOR = (first, second) -> { int firstLength = first.getNameCount(); int secondLength = second.getNameCount(); @@ -127,9 +127,9 @@ public static Array getFiles( Path directory, boolean includeDirectoriesToResult, String @Nullable ... extensions) { - Array result = ArrayFactory.newArray(Path.class); + MutableArray result = MutableArray.ofType(Path.class); collectFilesTo(result, directory, includeDirectoriesToResult, extensions); - return result; + return Array.copyOf(result); } public static Path[] getFiles(Package pckg, String @Nullable ... extensions) { @@ -153,7 +153,7 @@ public static Path[] getFiles(Package pckg, String @Nullable ... extensions) { return EMPTY_PATHS; } - var files = Array.ofType(Path.class); + var files = MutableArray.ofType(Path.class); while (urls.hasMoreElements()) { @@ -177,7 +177,7 @@ public static Path[] getFiles(Package pckg, String @Nullable ... extensions) { } public static void collectFilesTo( - Array container, + MutableArray container, Path directory, boolean includeDirectoriesToResult, String @Nullable ... extensions) { @@ -238,7 +238,9 @@ public static boolean hasExtensions(String path, String @Nullable [] extensions) } public static boolean hasExtensions(String path, @Nullable Array extensions) { - return extensions != null && extensions.anyMatchR(path, String::endsWith); + return extensions != null && extensions + .reversedIterations() + .anyMatch(path, String::endsWith); } public static boolean hasExtensions(String path, @Nullable Collection extensions) { @@ -600,11 +602,10 @@ private static void validateDirectory(Path directory) { } } - public static Stream stream(Path directory) { validateDirectory(directory); - var files = Array.ofType(Path.class); + var files = MutableArray.ofType(Path.class); try (var stream = Files.newDirectoryStream(directory)) { stream.forEach(files::add); diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/PluginSystem.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/PluginSystem.java index dccd4c17..679f8315 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/PluginSystem.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/PluginSystem.java @@ -1,7 +1,7 @@ package javasabr.rlib.plugin.system; import java.util.Optional; -import javasabr.rlib.common.util.array.Array; +import javasabr.rlib.collections.array.Array; import org.jspecify.annotations.Nullable; /** diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java index ed85700b..bd26df21 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java @@ -3,7 +3,6 @@ import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; import static java.util.concurrent.CompletableFuture.supplyAsync; import static javasabr.rlib.common.util.ObjectUtils.notNull; -import static javasabr.rlib.common.util.array.ArrayCollectors.toArray; import static javasabr.rlib.common.util.dictionary.DictionaryCollectors.toObjectDictionary; import java.io.IOException; @@ -24,10 +23,10 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javasabr.rlib.classpath.ClassPathScannerFactory; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; import javasabr.rlib.common.util.ClassUtils; import javasabr.rlib.common.util.Utils; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ReadOnlyArray; import javasabr.rlib.common.util.dictionary.ObjectDictionary; import javasabr.rlib.io.util.FileUtils; import javasabr.rlib.logger.api.Logger; @@ -62,8 +61,8 @@ protected record State( ObjectDictionary idToPlugin) { static final State EMPTY = new State( - Array.empty(), - Array.empty(), + Array.empty(PluginContainer.class), + Array.empty(Plugin.class), ObjectDictionary.empty(), ObjectDictionary.empty()); } @@ -116,24 +115,25 @@ protected BasePluginSystem preLoadImpl(Executor executor) { LOGGER.debug("Start pre-loading all plugins..."); State current = state.get(); - ReadOnlyArray>> futures = Array.optionals( + Array>> futures = Array.optionals( CompletionStage.class, embeddedPluginsPathOptional().map(path -> loadPlugins(path, executor, true)), installationPluginsPathOptional().map(path -> loadPlugins(path, executor, false))); + Array containers = futures .stream() .map(CompletionStage::toCompletableFuture) .map(CompletableFuture::join) .flatMap(Array::stream) - .collect(toArray(PluginContainer.class)); + .collect(ArrayCollectors.toArray(PluginContainer.class)); var idToContainer = containers .stream() .collect(toObjectDictionary(PluginContainer::id, container -> container)); State newState = new State( - ReadOnlyArray.wrap(containers), - Array.empty(), + containers, + Array.empty(Plugin.class), idToContainer, ObjectDictionary.empty()); @@ -175,7 +175,7 @@ protected BasePluginSystem initializeImpl(Executor executor) { State newState = new State( current.containers, - ReadOnlyArray.wrap(plugins.values(Plugin.class)), + Array.of(Plugin.class, plugins.values(Plugin.class).toArray()), current.idToContainer, plugins); @@ -252,7 +252,7 @@ protected CompletionStage> loadPlugins( .map(CompletionStage::toCompletableFuture) .map(CompletableFuture::join) .filter(Objects::nonNull) - .collect(toArray(PluginContainer.class)), executor); + .collect(ArrayCollectors.toArray(PluginContainer.class)), executor); } protected CompletionStage<@Nullable PluginContainer> loadPlugin( @@ -383,7 +383,7 @@ public PluginContainer getPluginContainer(String id) { @Override public Array plugins() { - return state.get().idToPlugin.values(Plugin.class); + return state.get().plugins; } @Nullable @@ -466,8 +466,8 @@ public Plugin installPlugin(Path file, boolean needInitialize) { idToPlugin.put(plugin.id(), plugin); State newState = new State( - ReadOnlyArray.wrap(idToContainer.values(PluginContainer.class)), - ReadOnlyArray.wrap(idToPlugin.values(Plugin.class)), + Array.of(PluginContainer.class, idToContainer.values(PluginContainer.class).toArray()), + Array.of(Plugin.class, idToPlugin.values(Plugin.class).toArray()), idToContainer, idToPlugin); @@ -499,8 +499,8 @@ public boolean removePlugin(Plugin plugin) { idToPlugin.remove(pluginId); State newState = new State( - ReadOnlyArray.wrap(idToContainer.values(PluginContainer.class)), - ReadOnlyArray.wrap(idToPlugin.values(Plugin.class)), + Array.of(PluginContainer.class, idToContainer.values(PluginContainer.class).toArray()), + Array.of(Plugin.class, idToPlugin.values(Plugin.class).toArray()), idToContainer, idToPlugin); From c4238e802f39c5f88972c7dc402265c60c6891b9 Mon Sep 17 00:00:00 2001 From: javasabr Date: Fri, 15 Aug 2025 21:05:07 +0200 Subject: [PATCH 3/8] continue working on collections module --- .../rlib/collections/array/MutableArray.java | 3 + .../array/impl/AbstractMutableArray.java | 2 +- .../collections/dictionary/Dictionary.java | 36 ++++ .../dictionary/DictionaryFactory.java | 11 + .../rlib/collections/dictionary/Entry.java | 12 ++ .../collections/dictionary/HashEntry.java | 5 + .../collections/dictionary/LinkedEntry.java | 9 + .../dictionary/LinkedHashEntry.java | 5 + .../dictionary/MutableRefDictionary.java | 42 ++++ .../collections/dictionary/RefDictionary.java | 33 +++ .../UnsafeMutableRefDictionary.java | 6 + .../dictionary/UnsafeRefDictionary.java | 8 + .../dictionary/impl/AbstractDictionary.java | 7 + .../impl/AbstractHashBasedDictionary.java | 13 ++ .../impl/AbstractHashBasedRefDictionary.java | 125 ++++++++++++ ...AbstractMutableHashBasedRefDictionary.java | 192 ++++++++++++++++++ .../impl/DefaultLinkedHashEntry.java | 50 +++++ .../impl/DefaultMutableRefHashDictionary.java | 81 ++++++++ .../dictionary/impl/EntryIterator.java | 45 ++++ .../impl/ImmutableHashBasedRefDictionary.java | 34 ++++ .../dictionary/impl/SimpleEntry.java | 18 ++ .../dictionary/impl/package-info.java | 4 + .../collections/dictionary/package-info.java | 4 + .../rlib/common/util/array/Array.java | 1 + .../common/util/dictionary/Dictionary.java | 1 + rlib-functions/build.gradle | 3 + settings.gradle | 3 +- 27 files changed, 751 insertions(+), 2 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryFactory.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Entry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/HashEntry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedEntry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedHashEntry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeMutableRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultMutableRefHashDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/ImmutableHashBasedRefDictionary.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/package-info.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/package-info.java create mode 100644 rlib-functions/build.gradle diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java index 705d62e0..5571222a 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/MutableArray.java @@ -45,4 +45,7 @@ default Iterator iterator() { default void forEach(Consumer action) { Array.super.forEach(action); } + + @Override + UnsafeMutableArray asUnsafe(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java index de1bdffa..b0bba59c 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java @@ -170,7 +170,7 @@ protected void processAdd(E[] array, int selfSize, int targetSize) { protected abstract void wrapped(@Nullable E[] wrapped); @Override - public UnsafeArray asUnsafe() { + public UnsafeMutableArray asUnsafe() { return this; } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java new file mode 100644 index 00000000..876f0ca1 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Dictionary.java @@ -0,0 +1,36 @@ +package javasabr.rlib.collections.dictionary; + +import java.util.Optional; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.MutableArray; +import org.jspecify.annotations.Nullable; + +/** + * @author JavaSaBr + */ +public interface Dictionary extends Iterable { + + boolean containsKey(K key); + + boolean containsValue(V value); + + boolean isEmpty(); + + int size(); + + @Nullable + V get(K key); + + Optional getOptional(K key); + + @Nullable + V getOrDefault(K key, V def); + + MutableArray keys(MutableArray container); + + Array keys(Class type); + + MutableArray values(MutableArray container); + + Array values(Class type); +} 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 new file mode 100644 index 00000000..251f2edb --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryFactory.java @@ -0,0 +1,11 @@ +package javasabr.rlib.collections.dictionary; + +import javasabr.rlib.collections.dictionary.impl.DefaultMutableRefHashDictionary; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class DictionaryFactory { + public static MutableRefDictionary mutableRefDictionary() { + return new DefaultMutableRefHashDictionary<>(); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Entry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Entry.java new file mode 100644 index 00000000..0f7d775c --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/Entry.java @@ -0,0 +1,12 @@ +package javasabr.rlib.collections.dictionary; + +import org.jspecify.annotations.Nullable; + +public interface Entry { + + K key(); + void key(K key); + + @Nullable V value(); + void value(@Nullable V value); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/HashEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/HashEntry.java new file mode 100644 index 00000000..86079db4 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/HashEntry.java @@ -0,0 +1,5 @@ +package javasabr.rlib.collections.dictionary; + +public interface HashEntry extends Entry { + int hash(); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedEntry.java new file mode 100644 index 00000000..55cc05e5 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedEntry.java @@ -0,0 +1,9 @@ +package javasabr.rlib.collections.dictionary; + +import org.jspecify.annotations.Nullable; + +public interface LinkedEntry extends Entry { + @Nullable + N next(); + void next(@Nullable N next); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedHashEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedHashEntry.java new file mode 100644 index 00000000..8f45ace1 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/LinkedHashEntry.java @@ -0,0 +1,5 @@ +package javasabr.rlib.collections.dictionary; + +public interface LinkedHashEntry> + extends LinkedEntry, HashEntry { +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java new file mode 100644 index 00000000..85361758 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java @@ -0,0 +1,42 @@ +package javasabr.rlib.collections.dictionary; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; + +public interface MutableRefDictionary extends RefDictionary { + + @Nullable + V getOrCompute(K key, Supplier factory); + + @Nullable + V getOrCompute(K key, Function factory); + + @Nullable + V getOrCompute(K key, T arg1, Function factory); + + /** + * @return the previous value for the key or null. + */ + @Nullable + V put(K key, @Nullable V value); + + /** + * @return the optional value of the previous value for the key. + */ + Optional putOptional(K key, @Nullable V value); + + /** + * @return the previous value for the key or null. + */ + @Nullable + V remove(K key); + + /** + * @return the optional value of the previous value for the key. + */ + Optional removeOptional(K key); + + RefDictionary toReadOnly(); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java new file mode 100644 index 00000000..6521eb81 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java @@ -0,0 +1,33 @@ +package javasabr.rlib.collections.dictionary; + +import javasabr.rlib.collections.dictionary.impl.ImmutableHashBasedRefDictionary; +import javasabr.rlib.collections.dictionary.impl.SimpleEntry; +import org.jspecify.annotations.Nullable; + +public interface RefDictionary extends Dictionary { + + static Entry entry(K key, @Nullable V value) { + return new SimpleEntry<>(key, value); + } + + static RefDictionary of() { + return ImmutableHashBasedRefDictionary.empty(); + } + + static RefDictionary of(K key, @Nullable V value) { + return ofEntries(entry(key, value)); + } + + static RefDictionary of(K k1, @Nullable V v1, K k2, @Nullable V v2) { + return ofEntries(entry(k1, v1), entry(k2, v2)); + } + + @SafeVarargs + static RefDictionary ofEntries(Entry... entries) { + MutableRefDictionary mutable = DictionaryFactory.mutableRefDictionary(); + for (Entry entry : entries) { + mutable.put(entry.key(), entry.value()); + } + return mutable.toReadOnly(); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeMutableRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeMutableRefDictionary.java new file mode 100644 index 00000000..23fac3de --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeMutableRefDictionary.java @@ -0,0 +1,6 @@ +package javasabr.rlib.collections.dictionary; + +public interface UnsafeMutableRefDictionary> + extends MutableRefDictionary, UnsafeRefDictionary { + +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeRefDictionary.java new file mode 100644 index 00000000..6783c971 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/UnsafeRefDictionary.java @@ -0,0 +1,8 @@ +package javasabr.rlib.collections.dictionary; + +import org.jspecify.annotations.Nullable; + +public interface UnsafeRefDictionary> extends RefDictionary { + + @Nullable E[] entries(); +} 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 new file mode 100644 index 00000000..a3a275c6 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractDictionary.java @@ -0,0 +1,7 @@ +package javasabr.rlib.collections.dictionary.impl; + +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/AbstractHashBasedDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedDictionary.java new file mode 100644 index 00000000..bdf9238d --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedDictionary.java @@ -0,0 +1,13 @@ +package javasabr.rlib.collections.dictionary.impl; + +public abstract class AbstractHashBasedDictionary extends AbstractDictionary { + + protected static int indexFor(int hash, int length) { + return hash & length - 1; + } + + protected static int hash(int hashcode) { + hashcode ^= hashcode >>> 20 ^ hashcode >>> 12; + return hashcode ^ hashcode >>> 7 ^ hashcode >>> 4; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java new file mode 100644 index 00000000..8104e750 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java @@ -0,0 +1,125 @@ +package javasabr.rlib.collections.dictionary.impl; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.MutableArray; +import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.collections.dictionary.LinkedHashEntry; +import javasabr.rlib.collections.dictionary.UnsafeRefDictionary; +import org.jspecify.annotations.Nullable; + +public abstract class AbstractHashBasedRefDictionary> + extends AbstractHashBasedDictionary + implements UnsafeRefDictionary { + + @Override + public boolean containsKey(K key) { + return findEntry(key) != null; + } + + @Override + public boolean containsValue(V value) { + + for (E entry : entries()) { + for (E nextEntry = entry; nextEntry != null; nextEntry = nextEntry.next()) { + if (Objects.equals(value, nextEntry.value())) { + return true; + } + } + } + + return false; + } + + @Nullable + @Override + public V get(K key) { + E entry = findEntry(key); + return entry == null ? null : entry.value(); + } + + @Nullable + @Override + public V getOrDefault(K key, V def) { + E entry = findEntry(key); + return entry == null ? def : entry.value(); + } + + @Override + public Optional getOptional(K key) { + return Optional.ofNullable(get(key)); + } + + @Override + public Iterator iterator() { + if (isEmpty()) { + return Collections.emptyIterator(); + } + return new EntryIterator<>(entries()); + } + + @Nullable + protected E findEntry(K key) { + + @Nullable E[] entries = entries(); + int hash = hash(key.hashCode()); + int entryIndex = indexFor(hash, entries.length); + + for (E entry = entries[entryIndex]; entry != null; entry = entry.next()) { + if (entry.hash() == hash && key.equals(entry.key())) { + return entry; + } + } + + return null; + } + + @Override + public Array keys(Class type) { + return Array.copyOf(keys(ArrayFactory.mutableArray(type, size()))); + } + + @Override + public MutableArray keys(MutableArray container) { + + UnsafeMutableArray unsafe = container.asUnsafe(); + unsafe.prepareForSize(container.size() + size()); + + for (E entry : entries()) { + while (entry != null) { + unsafe.unsafeAdd(entry.key()); + entry = entry.next(); + } + } + + return container; + } + + @Override + public Array values(Class type) { + return Array.copyOf(values(ArrayFactory.mutableArray(type, size()))); + } + + @Override + public MutableArray values(MutableArray container) { + + UnsafeMutableArray unsafe = container.asUnsafe(); + unsafe.prepareForSize(container.size() + size()); + + for (E entry : entries()) { + while (entry != null) { + V value = entry.value(); + if (value != null) { + unsafe.unsafeAdd(value); + } + entry = entry.next(); + } + } + + return container; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java new file mode 100644 index 00000000..3547f87c --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java @@ -0,0 +1,192 @@ +package javasabr.rlib.collections.dictionary.impl; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import javasabr.rlib.collections.dictionary.LinkedHashEntry; +import javasabr.rlib.collections.dictionary.UnsafeMutableRefDictionary; +import org.jspecify.annotations.Nullable; + +public abstract class AbstractMutableHashBasedRefDictionary> + extends AbstractHashBasedRefDictionary implements UnsafeMutableRefDictionary { + + protected static final int DEFAULT_INITIAL_CAPACITY = 16; + protected static final int DEFAULT_MAXIMUM_CAPACITY = 1 << 30; + protected static final float DEFAULT_LOAD_FACTOR = 0.75f; + + @Nullable + @Override + public V getOrCompute(K key, Function factory) { + + E entry = findEntry(key); + if (entry != null) { + return entry.value(); + } + + V newValue = factory.apply(key); + put(key, newValue); + return newValue; + } + + @Nullable + @Override + public V getOrCompute(K key, Supplier factory) { + E entry = findEntry(key); + if (entry != null) { + return entry.value(); + } + + V newValue = factory.get(); + put(key, newValue); + return newValue; + } + + @Nullable + @Override + public V getOrCompute(K key, T arg1, Function factory) { + E entry = findEntry(key); + if (entry != null) { + return entry.value(); + } + + V newValue = factory.apply(arg1); + put(key, newValue); + return newValue; + } + + @Nullable + @Override + public V put(K key, V value) { + + @Nullable E[] entries = entries(); + int hash = hash(key.hashCode()); + int entryIndex = indexFor(hash, entries.length); + + for (E entry = entries[entryIndex]; entry != null; entry = entry.next()) { + if (entry.hash() == hash && key.equals(entry.key())) { + V prev = entry.value(); + entry.value(value); + return prev; + } + } + + addEntry(hash, key, value, entryIndex); + return null; + } + + @Override + public Optional putOptional(K key, V value) { + return Optional.ofNullable(put(key, value)); + } + + @Nullable + @Override + public V remove(K key) { + E removed = removeEntryForKey(key); + if (removed == null) { + return null; + } + return removed.value(); + } + + @Override + public Optional removeOptional(K key) { + return Optional.ofNullable(remove(key)); + } + + @Nullable + protected E removeEntryForKey(K key) { + + @Nullable E[] entries = entries(); + int hash = hash(key.hashCode()); + int entryIndex = indexFor(hash, entries.length); + + E previosEntry = entries[entryIndex]; + E entry = previosEntry; + + while (entry != null) { + E nextEntry = entry.next(); + if (entry.hash() == hash && key.equals(entry.key())) { + decrementSize(); + if (previosEntry == entry) { + entries[entryIndex] = nextEntry; + } else { + previosEntry.next(nextEntry); + } + return entry; + } + previosEntry = entry; + entry = nextEntry; + } + + return null; + } + + protected void addEntry(int hash, K key, @Nullable V value, int index) { + + @Nullable E[] entries = entries(); + E currentEntry = entries[index]; + + var newEntry = allocate(hash, key, value, currentEntry); + entries[index] = newEntry; + + if (incrementSize() >= threshold()) { + resize(2 * entries.length); + } + } + + protected abstract int threshold(); + protected abstract void threshold(int threshold); + + protected abstract float loadFactor(); + + protected abstract int incrementSize(); + protected abstract int decrementSize(); + + protected abstract void entries(@Nullable E[] entries); + + protected abstract E allocate(int hash, K key, @Nullable V value, @Nullable E next); + + @Nullable + protected abstract E[] allocate(int length); + + protected final void resize(int newLength) { + + @Nullable E[] currentEntries = entries(); + int oldLength = currentEntries.length; + + if (oldLength >= DEFAULT_MAXIMUM_CAPACITY) { + threshold(Integer.MAX_VALUE); + return; + } + + @Nullable E[] newEntries = allocate(newLength); + + transfer(currentEntries, newEntries); + entries(newEntries); + threshold((int) (newLength * loadFactor())); + } + + protected void transfer( + @Nullable E[] currentEntries, + @Nullable E[] newEntries) { + + int newCapacity = newEntries.length; + + for (E entry : currentEntries) { + if (entry == null) { + continue; + } + do { + + E nextEntry = entry.next(); + int newIndex = indexFor(entry.hash(), newCapacity); + + entry.next(newEntries[newIndex]); + newEntries[newIndex] = entry; + entry = nextEntry; + + } while (entry != null); + } + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java new file mode 100644 index 00000000..495eb0ea --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java @@ -0,0 +1,50 @@ +package javasabr.rlib.collections.dictionary.impl; + +import javasabr.rlib.collections.dictionary.LinkedHashEntry; +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.PRIVATE) +public class DefaultLinkedHashEntry implements + LinkedHashEntry> { + + @Nullable + DefaultLinkedHashEntry next; + + K key; + @Nullable + V value; + + int hash; + + public DefaultLinkedHashEntry( + @Nullable DefaultLinkedHashEntry next, + K key, + @Nullable V value, + int hash) { + this.next = next; + this.key = key; + this.value = value; + this.hash = hash; + } + + @Override + public void next(@Nullable DefaultLinkedHashEntry next) { + this.next = next; + } + + @Override + public void key(K key) { + this.key = key; + } + + @Override + public void value(@Nullable V value) { + this.value = value; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultMutableRefHashDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultMutableRefHashDictionary.java new file mode 100644 index 00000000..f9e71f5c --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultMutableRefHashDictionary.java @@ -0,0 +1,81 @@ +package javasabr.rlib.collections.dictionary.impl; + +import java.util.Arrays; +import javasabr.rlib.collections.dictionary.RefDictionary; +import javasabr.rlib.common.util.ArrayUtils; +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.PRIVATE) +public class DefaultMutableRefHashDictionary extends + AbstractMutableHashBasedRefDictionary> { + + final float loadFactor; + + @Nullable DefaultLinkedHashEntry[] entries; + int size; + int threshold; + + public DefaultMutableRefHashDictionary() { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + public DefaultMutableRefHashDictionary(int initCapacity, float loadFactor) { + this.entries = ArrayUtils.create(DefaultLinkedHashEntry.class, initCapacity); + this.loadFactor = loadFactor; + this.threshold = (int) (initCapacity * loadFactor); + } + + @Override + public boolean isEmpty() { + return size < 1; + } + + + @Override + protected int incrementSize() { + return size++; + } + + @Override + protected int decrementSize() { + return size--; + } + + @Override + protected void threshold(int threshold) { + this.threshold = threshold; + } + + @Override + protected void entries(@Nullable DefaultLinkedHashEntry[] entries) { + this.entries = entries; + } + + @Override + protected DefaultLinkedHashEntry allocate( + int hash, + K key, + @Nullable V value, + @Nullable DefaultLinkedHashEntry next) { + return new DefaultLinkedHashEntry<>(next, key, value, hash); + } + + @Nullable + @Override + protected DefaultLinkedHashEntry[] allocate(int length) { + //noinspection unchecked + return new DefaultLinkedHashEntry[length]; + } + + @Override + public RefDictionary toReadOnly() { + @Nullable DefaultLinkedHashEntry[] copied = Arrays.copyOf(entries, entries.length); + return new ImmutableHashBasedRefDictionary<>(copied, size); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java new file mode 100644 index 00000000..029caa47 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java @@ -0,0 +1,45 @@ +package javasabr.rlib.collections.dictionary.impl; + +import java.util.Iterator; +import javasabr.rlib.collections.dictionary.LinkedEntry; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@FieldDefaults(level = AccessLevel.PRIVATE) +public class EntryIterator> implements Iterator<@Nullable V> { + + final @Nullable E[] entries; + @Nullable E next; + int index; + + public EntryIterator(@Nullable E[] entries) { + this.entries = entries; + findNext(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public V next() { + @SuppressWarnings("DataFlowIssue") + V value = next.value(); + findNext(); + return value; + } + + private void findNext() { + + E candidate; + if (next != null && (candidate = next.next()) != null) { + next = candidate; + return; + } + + //noinspection StatementWithEmptyBody + while (index < entries.length && (next = entries[index++]) == null); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/ImmutableHashBasedRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/ImmutableHashBasedRefDictionary.java new file mode 100644 index 00000000..43a6de7c --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/ImmutableHashBasedRefDictionary.java @@ -0,0 +1,34 @@ +package javasabr.rlib.collections.dictionary.impl; + +import javasabr.rlib.collections.dictionary.LinkedHashEntry; +import javasabr.rlib.collections.dictionary.RefDictionary; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class ImmutableHashBasedRefDictionary> + extends AbstractHashBasedRefDictionary { + + private static final RefDictionary EMPTY = + new ImmutableHashBasedRefDictionary<>(new DefaultLinkedHashEntry[0], 0); + + public static RefDictionary empty() { + //noinspection unchecked + return (RefDictionary) EMPTY; + } + + @Nullable E[] entries; + int size; + + @Override + public boolean isEmpty() { + return size < 1; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java new file mode 100644 index 00000000..9f53d687 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java @@ -0,0 +1,18 @@ +package javasabr.rlib.collections.dictionary.impl; + +import javasabr.rlib.collections.dictionary.Entry; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@Data +@AllArgsConstructor +@Accessors(fluent = true) +@FieldDefaults(level = AccessLevel.PRIVATE) +public class SimpleEntry implements Entry { + K key; + @Nullable V value; +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/package-info.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/package-info.java new file mode 100644 index 00000000..d00282cf --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.collections.dictionary.impl; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/package-info.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/package-info.java new file mode 100644 index 00000000..6607b8f1 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.collections.dictionary; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/array/Array.java b/rlib-common/src/main/java/javasabr/rlib/common/util/array/Array.java index c7d72aea..aae59abe 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/array/Array.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/array/Array.java @@ -43,6 +43,7 @@ * @author JavaSaBr */ @NullMarked +@Deprecated public interface Array extends Collection, Serializable, Reusable, Cloneable, RandomAccess { /** diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/dictionary/Dictionary.java b/rlib-common/src/main/java/javasabr/rlib/common/util/dictionary/Dictionary.java index d912e7c4..a22364c4 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/dictionary/Dictionary.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/dictionary/Dictionary.java @@ -14,6 +14,7 @@ * @author JavaSaBr */ @NullMarked +@Deprecated public interface Dictionary extends Iterable, Reusable { /** diff --git a/rlib-functions/build.gradle b/rlib-functions/build.gradle new file mode 100644 index 00000000..37ec34f9 --- /dev/null +++ b/rlib-functions/build.gradle @@ -0,0 +1,3 @@ + +dependencies { +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e4b523ac..264f3ee8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,4 +15,5 @@ include ':rlib-geometry' include ':rlib-classpath' include ':rlib-compiler' include ':rlib-io' -include ':rlib-collections' \ No newline at end of file +include ':rlib-collections' +include ':rlib-functions' \ No newline at end of file From 283b7e3de57f8e453e04b867e9cd0f03d92b3a31 Mon Sep 17 00:00:00 2001 From: javasabr Date: Sat, 16 Aug 2025 07:18:50 +0200 Subject: [PATCH 4/8] continue working on collections module --- .../dictionary/DictionaryCollectors.java | 87 +++++++++++++++++++ .../dictionary/DictionaryFactory.java | 6 ++ .../dictionary/MutableRefDictionary.java | 11 +++ .../collections/dictionary/RefDictionary.java | 2 +- .../exception/InitializePluginException.java | 16 +--- .../system/exception/PluginException.java | 3 - .../exception/PreloadPluginException.java | 16 +--- .../plugin/system/exception/package-info.java | 4 + .../plugin/system/impl/BasePluginSystem.java | 42 +++++---- 9 files changed, 140 insertions(+), 47 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java create mode 100644 rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/package-info.java diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java new file mode 100644 index 00000000..527e0234 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/DictionaryCollectors.java @@ -0,0 +1,87 @@ +package javasabr.rlib.collections.dictionary; + +import static java.util.Collections.unmodifiableSet; + +import java.util.EnumSet; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Collector.Characteristics; +import javasabr.rlib.common.util.ObjectUtils; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class DictionaryCollectors { + + static final Set CH_ID = unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); + + @Getter + @Accessors(fluent = true) + @RequiredArgsConstructor + @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) + static class CollectorImpl implements Collector { + + Supplier supplier; + BiConsumer accumulator; + BinaryOperator combiner; + Function finisher; + Set characteristics; + + CollectorImpl( + Supplier supplier, + BiConsumer accumulator, + BinaryOperator combiner, + Set characteristics) { + this(supplier, accumulator, combiner, a -> (R) a, characteristics); + } + } + + public static Collector, RefDictionary> toRefDictionary( + Function keyMapper) { + return new CollectorImpl<>( + DictionaryFactory::mutableRefDictionary, + uniqKeysAccumulator(keyMapper, Function.identity()), + MutableRefDictionary::append, + MutableRefDictionary::toReadOnly, + CH_ID); + } + + public static Collector, RefDictionary> toRefDictionary( + Function keyMapper, + Function valueMapper) { + return new CollectorImpl<>( + DictionaryFactory::mutableRefDictionary, + uniqKeysAccumulator(keyMapper, valueMapper), + MutableRefDictionary::append, + MutableRefDictionary::toReadOnly, + CH_ID); + } + + private static BiConsumer, T> uniqKeysAccumulator( + Function keyMapper, + Function valueMapper) { + return (map, element) -> { + + K key = keyMapper.apply(element); + V value = ObjectUtils.notNull(valueMapper.apply(element)); + V prev = map.put(key, value); + + if (prev != null) { + throw duplicateKeyException(key, prev, value); + } + }; + } + + private static IllegalStateException duplicateKeyException(Object key, Object left, Object right) { + return new IllegalStateException( + String.format("Duplicate key %s (attempted merging values %s and %s)", key, left, right)); + } +} 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 251f2edb..58e140e3 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 @@ -8,4 +8,10 @@ public class DictionaryFactory { public static MutableRefDictionary mutableRefDictionary() { return new DefaultMutableRefHashDictionary<>(); } + + public static MutableRefDictionary mutableRefDictionary( + Class keyType, + Class valueType) { + return new DefaultMutableRefHashDictionary<>(); + } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java index 85361758..88f58c87 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/MutableRefDictionary.java @@ -3,10 +3,17 @@ import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; +import javasabr.rlib.collections.dictionary.impl.DefaultMutableRefHashDictionary; import org.jspecify.annotations.Nullable; public interface MutableRefDictionary extends RefDictionary { + static MutableRefDictionary ofTypes( + Class keyType, + Class valueType) { + return new DefaultMutableRefHashDictionary<>(); + } + @Nullable V getOrCompute(K key, Supplier factory); @@ -22,6 +29,10 @@ public interface MutableRefDictionary extends RefDictionary { @Nullable V put(K key, @Nullable V value); + void putAll(RefDictionary dictionary); + + MutableRefDictionary append(RefDictionary dictionary); + /** * @return the optional value of the previous value for the key. */ diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java index 6521eb81..5ad14250 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java @@ -10,7 +10,7 @@ static Entry entry(K key, @Nullable V value) { return new SimpleEntry<>(key, value); } - static RefDictionary of() { + static RefDictionary empty() { return ImmutableHashBasedRefDictionary.empty(); } diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/InitializePluginException.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/InitializePluginException.java index b8bee9f0..fb64846e 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/InitializePluginException.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/InitializePluginException.java @@ -1,14 +1,15 @@ package javasabr.rlib.plugin.system.exception; import java.nio.file.Path; +import lombok.Getter; +import lombok.experimental.Accessors; import org.jspecify.annotations.NullMarked; /** - * The exception about problems with initializing plugins. - * * @author JavaSaBr */ -@NullMarked +@Getter +@Accessors(fluent = true) public class InitializePluginException extends PluginException { private final Path path; @@ -22,13 +23,4 @@ public InitializePluginException(String message, Path path, Throwable e) { super(message, e); this.path = path; } - - /** - * Get the path of the plugin. - * - * @return the path of the plugin. - */ - public Path getPath() { - return path; - } } diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PluginException.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PluginException.java index 00c6df68..bbbb076d 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PluginException.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PluginException.java @@ -1,13 +1,10 @@ package javasabr.rlib.plugin.system.exception; -import org.jspecify.annotations.NullMarked; - /** * The base implementation of a plugin exception. * * @author JavaSaBr */ -@NullMarked public class PluginException extends RuntimeException { public PluginException(String message) { diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PreloadPluginException.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PreloadPluginException.java index 8a5768c7..ba1ad586 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PreloadPluginException.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/PreloadPluginException.java @@ -1,14 +1,15 @@ package javasabr.rlib.plugin.system.exception; import java.nio.file.Path; +import lombok.Getter; +import lombok.experimental.Accessors; import org.jspecify.annotations.NullMarked; /** - * The exception about problems with pre-loading plugins. - * * @author JavaSaBr */ -@NullMarked +@Getter +@Accessors(fluent = true) public class PreloadPluginException extends PluginException { private final Path path; @@ -17,13 +18,4 @@ public PreloadPluginException(String message, Path path) { super(message); this.path = path; } - - /** - * Get the path of the plugin. - * - * @return the path of the plugin. - */ - public Path getPath() { - return path; - } } diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/package-info.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/package-info.java new file mode 100644 index 00000000..a7c0042e --- /dev/null +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/exception/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.plugin.system.exception; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java index bd26df21..66125fd7 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java @@ -25,6 +25,10 @@ import javasabr.rlib.classpath.ClassPathScannerFactory; import javasabr.rlib.collections.array.Array; import javasabr.rlib.collections.array.ArrayCollectors; +import javasabr.rlib.collections.dictionary.DictionaryCollectors; +import javasabr.rlib.collections.dictionary.DictionaryFactory; +import javasabr.rlib.collections.dictionary.MutableRefDictionary; +import javasabr.rlib.collections.dictionary.RefDictionary; import javasabr.rlib.common.util.ClassUtils; import javasabr.rlib.common.util.Utils; import javasabr.rlib.common.util.dictionary.ObjectDictionary; @@ -57,14 +61,14 @@ public class BasePluginSystem implements ConfigurablePluginSystem { protected record State( Array containers, Array plugins, - ObjectDictionary idToContainer, - ObjectDictionary idToPlugin) { + RefDictionary idToContainer, + RefDictionary idToPlugin) { static final State EMPTY = new State( Array.empty(PluginContainer.class), Array.empty(Plugin.class), - ObjectDictionary.empty(), - ObjectDictionary.empty()); + RefDictionary.empty(), + RefDictionary.empty()); } final ClassLoader baseLoader; @@ -129,13 +133,13 @@ protected BasePluginSystem preLoadImpl(Executor executor) { var idToContainer = containers .stream() - .collect(toObjectDictionary(PluginContainer::id, container -> container)); + .collect(DictionaryCollectors.toRefDictionary(PluginContainer::id)); State newState = new State( containers, Array.empty(Plugin.class), idToContainer, - ObjectDictionary.empty()); + RefDictionary.empty()); if (state.compareAndSet(current, newState)) { LOGGER.debug(containers, "Pre-loaded:%s"::formatted); @@ -171,7 +175,7 @@ protected BasePluginSystem initializeImpl(Executor executor) { .map(future -> future.thenApply(this::initializePlugin)) .map(CompletionStage::toCompletableFuture) .map(CompletableFuture::join) - .collect(toObjectDictionary(Plugin::id, plugin -> plugin)); + .collect(DictionaryCollectors.toRefDictionary(Plugin::id)); State newState = new State( current.containers, @@ -457,17 +461,17 @@ public Plugin installPlugin(Path file, boolean needInitialize) { } } - var idToContainer = ObjectDictionary.ofType(String.class, PluginContainer.class); - idToContainer.put(current.idToContainer); + var idToContainer = MutableRefDictionary.ofTypes(String.class, PluginContainer.class); + idToContainer.putAll(current.idToContainer); idToContainer.put(container.id(), container); - var idToPlugin = ObjectDictionary.ofType(String.class, Plugin.class); - idToPlugin.put(current.idToPlugin); + var idToPlugin = MutableRefDictionary.ofTypes(String.class, Plugin.class); + idToPlugin.putAll(current.idToPlugin); idToPlugin.put(plugin.id(), plugin); State newState = new State( - Array.of(PluginContainer.class, idToContainer.values(PluginContainer.class).toArray()), - Array.of(Plugin.class, idToPlugin.values(Plugin.class).toArray()), + idToContainer.values(PluginContainer.class), + idToPlugin.values(Plugin.class), idToContainer, idToPlugin); @@ -490,17 +494,17 @@ public boolean removePlugin(Plugin plugin) { return false; } - var idToContainer = ObjectDictionary.ofType(String.class, PluginContainer.class); - idToContainer.put(current.idToContainer); + var idToContainer = MutableRefDictionary.ofTypes(String.class, PluginContainer.class); + idToContainer.putAll(current.idToContainer); idToContainer.remove(pluginId); - var idToPlugin = ObjectDictionary.ofType(String.class, Plugin.class); - idToPlugin.put(current.idToPlugin); + var idToPlugin = MutableRefDictionary.ofTypes(String.class, Plugin.class); + idToPlugin.putAll(current.idToPlugin); idToPlugin.remove(pluginId); State newState = new State( - Array.of(PluginContainer.class, idToContainer.values(PluginContainer.class).toArray()), - Array.of(Plugin.class, idToPlugin.values(Plugin.class).toArray()), + idToContainer.values(PluginContainer.class), + idToPlugin.values(Plugin.class), idToContainer, idToPlugin); From 83c06f6bd8d5f513219b7053812b69dbc0b919a5 Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 Aug 2025 11:09:23 +0200 Subject: [PATCH 5/8] continue working on collections module --- .../collections/dictionary/RefDictionary.java | 3 +++ .../dictionary/impl/AbstractDictionary.java | 1 - .../impl/AbstractHashBasedRefDictionary.java | 14 ++++++++++- ...AbstractMutableHashBasedRefDictionary.java | 23 +++++++++++++++++++ .../impl/DefaultLinkedHashEntry.java | 19 +++------------ ...Iterator.java => LinkedEntryIterator.java} | 9 +++++--- .../dictionary/impl/SimpleEntry.java | 2 +- 7 files changed, 49 insertions(+), 22 deletions(-) rename rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/{EntryIterator.java => LinkedEntryIterator.java} (82%) diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java index 5ad14250..438389ab 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/RefDictionary.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.dictionary; +import java.util.function.BiConsumer; import javasabr.rlib.collections.dictionary.impl.ImmutableHashBasedRefDictionary; import javasabr.rlib.collections.dictionary.impl.SimpleEntry; import org.jspecify.annotations.Nullable; @@ -30,4 +31,6 @@ static RefDictionary ofEntries(Entry... entries) { } return mutable.toReadOnly(); } + + void forEach(BiConsumer consumer); } 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 a3a275c6..4eaf1934 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,5 +3,4 @@ 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/AbstractHashBasedRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java index 8104e750..62274774 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedRefDictionary.java @@ -4,6 +4,7 @@ import java.util.Iterator; import java.util.Objects; import java.util.Optional; +import java.util.function.BiConsumer; import javasabr.rlib.collections.array.Array; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; @@ -59,7 +60,7 @@ public Iterator iterator() { if (isEmpty()) { return Collections.emptyIterator(); } - return new EntryIterator<>(entries()); + return new LinkedEntryIterator<>(entries()); } @Nullable @@ -78,6 +79,17 @@ protected E findEntry(K key) { return null; } + @Override + public void forEach(BiConsumer consumer) { + for (E entry : entries()) { + while (entry != null) { + //noinspection DataFlowIssue + consumer.accept(entry.key(), entry.value()); + entry = entry.next(); + } + } + } + @Override public Array keys(Class type) { return Array.copyOf(keys(ArrayFactory.mutableArray(type, size()))); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java index 3547f87c..6174b74a 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractMutableHashBasedRefDictionary.java @@ -4,6 +4,8 @@ import java.util.function.Function; import java.util.function.Supplier; import javasabr.rlib.collections.dictionary.LinkedHashEntry; +import javasabr.rlib.collections.dictionary.MutableRefDictionary; +import javasabr.rlib.collections.dictionary.RefDictionary; import javasabr.rlib.collections.dictionary.UnsafeMutableRefDictionary; import org.jspecify.annotations.Nullable; @@ -79,6 +81,27 @@ public Optional putOptional(K key, V value) { return Optional.ofNullable(put(key, value)); } + @Override + public void putAll(RefDictionary dictionary) { + if (dictionary instanceof AbstractHashBasedRefDictionary hashBased) { + for (var entry : hashBased.entries()) { + while (entry != null) { + //noinspection DataFlowIssue,unchecked + put((K) entry.key(), (V) entry.value()); + entry = entry.next(); + } + } + } else { + dictionary.forEach(this::put); + } + } + + @Override + public MutableRefDictionary append(RefDictionary dictionary) { + putAll(dictionary); + return this; + } + @Nullable @Override public V remove(K key) { diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java index 495eb0ea..723d7d60 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/DefaultLinkedHashEntry.java @@ -3,12 +3,14 @@ import javasabr.rlib.collections.dictionary.LinkedHashEntry; import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; @Getter -@Accessors(fluent = true) +@Setter +@Accessors(fluent = true, chain = false) @FieldDefaults(level = AccessLevel.PRIVATE) public class DefaultLinkedHashEntry implements LinkedHashEntry> { @@ -32,19 +34,4 @@ public DefaultLinkedHashEntry( this.value = value; this.hash = hash; } - - @Override - public void next(@Nullable DefaultLinkedHashEntry next) { - this.next = next; - } - - @Override - public void key(K key) { - this.key = key; - } - - @Override - public void value(@Nullable V value) { - this.value = value; - } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/LinkedEntryIterator.java similarity index 82% rename from rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java rename to rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/LinkedEntryIterator.java index 029caa47..fa3af0cd 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/EntryIterator.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/LinkedEntryIterator.java @@ -7,13 +7,16 @@ import org.jspecify.annotations.Nullable; @FieldDefaults(level = AccessLevel.PRIVATE) -public class EntryIterator> implements Iterator<@Nullable V> { +public class LinkedEntryIterator> implements Iterator<@Nullable V> { final @Nullable E[] entries; - @Nullable E next; + + @Nullable + E next; + int index; - public EntryIterator(@Nullable E[] entries) { + public LinkedEntryIterator(@Nullable E[] entries) { this.entries = entries; findNext(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java index 9f53d687..61d73e98 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/SimpleEntry.java @@ -10,7 +10,7 @@ @Data @AllArgsConstructor -@Accessors(fluent = true) +@Accessors(fluent = true, chain = false) @FieldDefaults(level = AccessLevel.PRIVATE) public class SimpleEntry implements Entry { K key; From d2ea95b54764e720e52e5a4dc85ad086d57c6546 Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 Aug 2025 18:34:12 +0200 Subject: [PATCH 6/8] continue working on collections module --- .../classpath/impl/ClassPathScannerImpl.java | 4 +- .../impl/ManifestClassPathScannerImpl.java | 31 ++-- .../rlib/collections/array/ArrayFactory.java | 5 + .../array/ArrayIterationFunctions.java | 6 +- .../ReversedArrayIterationFunctions.java | 6 +- .../collections/array/UnsafeMutableArray.java | 2 +- .../array/impl/AbstractMutableArray.java | 12 +- .../array/impl/CopyOnWriteMutableArray.java | 171 ++++++++++++++++++ .../impl/DefaultArrayIterationFunctions.java | 11 ++ .../array/impl/DefaultMutableArray.java | 14 +- ...efaultReversedArrayIterationFunctions.java | 11 ++ .../javasabr/rlib/common/util/ThreadSafe.java | 6 + rlib-logger-impl/build.gradle | 1 + .../logger/impl/DefaultLoggerService.java | 16 +- 14 files changed, 262 insertions(+), 34 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java create mode 100644 rlib-common/src/main/java/javasabr/rlib/common/util/ThreadSafe.java diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java index dc370977..e0882981 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java @@ -151,7 +151,7 @@ public Array foundResources() { return Array.of(String.class, resources); } - protected String[] calculatePathsToScan() { + protected Array calculatePathsToScan() { var systemClasspath = useSystemClassPath() ? classpathPaths() : ArrayUtils.EMPTY_STRING_ARRAY; var capacity = additionalPaths.size() + systemClasspath.length; @@ -160,7 +160,7 @@ protected String[] calculatePathsToScan() { result.addAll(systemClasspath); result.addAll(additionalPaths); - return result.toArray(String.class); + return result; } protected String[] classpathPaths() { diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java index e56143cc..77055bdc 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ManifestClassPathScannerImpl.java @@ -8,9 +8,11 @@ import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; +import java.util.stream.Stream; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.Utils; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayFactory; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; @@ -32,9 +34,9 @@ public ManifestClassPathScannerImpl( this.classPathKey = classPathKey; } - protected String[] calculateManifestClassPath() { + protected Array calculateManifestClassPath() { - var result = Array.ofType(String.class); + var result = MutableArray.ofType(String.class); var currentThread = Thread.currentThread(); Path root = Utils.getRootFolderFromClass(rootClass); @@ -74,16 +76,21 @@ protected String[] calculateManifestClassPath() { } } - return result.toArray(String.class); + return result; } @Override - protected String[] calculatePathsToScan() { - - var result = ArrayFactory.newArraySet(String.class); - result.addAll(super.calculatePathsToScan()); - result.addAll(calculateManifestClassPath()); - - return result.toArray(String.class); + protected Array calculatePathsToScan() { + + Stream originalStream = super + .calculatePathsToScan() + .stream(); + Stream extraStream = calculateManifestClassPath() + .stream(); + + return Stream + .concat(originalStream, extraStream) + .distinct() + .collect(ArrayCollectors.toArray(String.class)); } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java index f8cdad0e..09f10d5a 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.array; +import javasabr.rlib.collections.array.impl.CopyOnWriteMutableArray; import javasabr.rlib.collections.array.impl.DefaultMutableArray; import javasabr.rlib.common.util.ClassUtils; import lombok.experimental.UtilityClass; @@ -14,4 +15,8 @@ public static MutableArray mutableArray(Class type) { public static MutableArray mutableArray(Class type, int capacity) { return new DefaultMutableArray<>(ClassUtils.unsafeCast(type), capacity); } + + public static MutableArray newCopyOnModifyArray(Class type) { + return new CopyOnWriteMutableArray<>(type); + } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java index e46a98b6..cf0beead 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java @@ -1,11 +1,15 @@ package javasabr.rlib.collections.array; +import java.util.function.BiConsumer; import java.util.function.BiPredicate; import org.jspecify.annotations.Nullable; public interface ArrayIterationFunctions { - @Nullable E findAny(T arg1, BiPredicate filter); + @Nullable + E findAny(T arg1, BiPredicate filter); + + ArrayIterationFunctions forEach(T arg1, BiConsumer consumer); boolean anyMatch(T arg1, BiPredicate filter); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java index f9c405d6..051670e3 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java @@ -1,11 +1,15 @@ package javasabr.rlib.collections.array; +import java.util.function.BiConsumer; import java.util.function.BiPredicate; import org.jspecify.annotations.Nullable; public interface ReversedArrayIterationFunctions { - @Nullable E findAny(T arg1, BiPredicate filter); + @Nullable + E findAny(T arg1, BiPredicate filter); + + ReversedArrayIterationFunctions forEach(T arg1, BiConsumer consumer); boolean anyMatch(T arg1, BiPredicate filter); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java index 74995114..7da5a8b7 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java @@ -4,7 +4,7 @@ public interface UnsafeMutableArray extends UnsafeArray, MutableArray { UnsafeMutableArray prepareForSize(int expectedSize); - UnsafeMutableArray unsafeAdd(E object); + UnsafeMutableArray unsafeAdd(E element); E unsafeRemove(int index); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java index b0bba59c..ce092fcb 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java @@ -155,14 +155,14 @@ public Spliterator spliterator() { return Spliterators.spliterator(wrapped(), 0, size(), Spliterator.NONNULL); } - protected void processAdd(Array array, int size, int elementsToAdd) { - System.arraycopy(array.asUnsafe().wrapped(), 0, wrapped(), size, elementsToAdd); - size(size + elementsToAdd); + protected void processAdd(Array array, int currentSize, int elementsToAdd) { + System.arraycopy(array.asUnsafe().wrapped(), 0, wrapped(), currentSize, elementsToAdd); + size(currentSize + elementsToAdd); } - protected void processAdd(E[] array, int selfSize, int targetSize) { - System.arraycopy(array, 0, wrapped(), selfSize, targetSize); - size(selfSize + targetSize); + protected void processAdd(E[] array, int currentSize, int elementsToAdd) { + System.arraycopy(array, 0, wrapped(), currentSize, elementsToAdd); + size(currentSize + elementsToAdd); } protected abstract void size(int size); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java new file mode 100644 index 00000000..4c08eb3f --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java @@ -0,0 +1,171 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.concurrent.atomic.AtomicReference; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.common.util.ThreadSafe; +import org.jspecify.annotations.Nullable; + +public class CopyOnWriteMutableArray extends AbstractMutableArray implements ThreadSafe { + + protected static final int LIMIT_ATTEMPTS = 1000; + + AtomicReference<@Nullable E[]> wrapped = new AtomicReference<>(); + + public CopyOnWriteMutableArray(Class type) { + super(type, 0); + } + + @Override + protected void size(int size) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return wrapped.get().length < 1; + } + + @Override + public int size() { + return wrapped.get().length; + } + + @Override + public @Nullable E[] wrapped() { + return wrapped.get(); + } + + @Override + protected void wrapped(@Nullable E[] wrapped) { + this.wrapped.set(wrapped); + } + + @Override + public boolean add(E element) { + unsafeAdd(element); + return true; + } + + @Override + public UnsafeMutableArray unsafeAdd(E element) { + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + @Nullable E[] currentArray = wrapped.get(); + @Nullable E[] copy = Arrays.copyOf(currentArray, currentArray.length + 1); + copy[currentArray.length] = element; + if (wrapped.compareAndSet(currentArray, copy)) { + return this; + } + } + throw new ConcurrentModificationException("Cannot successfully add new element"); + } + + @Override + protected void processAdd(E[] array, int currentSize, int elementsToAdd) { + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + @Nullable E[] currentArray = wrapped.get(); + @Nullable E[] copy = Arrays.copyOf(currentArray, currentSize + elementsToAdd); + System.arraycopy(array, 0, copy, currentSize, elementsToAdd); + if (wrapped.compareAndSet(currentArray, copy)) { + return; + } + } + throw new ConcurrentModificationException("Cannot add new elements"); + } + + @Override + protected void processAdd(Array array, int currentSize, int elementsToAdd) { + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + @Nullable E[] currentArray = wrapped.get(); + @Nullable E[] copy = Arrays.copyOf(currentArray, currentSize + elementsToAdd); + System.arraycopy(array.asUnsafe().wrapped(), 0, copy, currentSize, elementsToAdd); + if (wrapped.compareAndSet(currentArray, copy)) { + return; + } + } + throw new ConcurrentModificationException("Cannot add new elements"); + } + + @Override + public boolean addAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + @Nullable E[] currentArray = wrapped.get(); + int currentSize = currentArray.length; + + @Nullable E[] copy = Arrays.copyOf(currentArray, currentSize + collection.size()); + for (E element : collection) { + copy[currentSize++] = element; + } + + if (wrapped.compareAndSet(currentArray, copy)) { + return true; + } + } + throw new ConcurrentModificationException("Cannot add new elements"); + } + + @Override + public UnsafeMutableArray unsafeSet(int index, E element) { + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + @Nullable E[] currentArray = wrapped.get(); + @Nullable E[] copy = Arrays.copyOf(currentArray, currentArray.length); + copy[index] = element; + if (wrapped.compareAndSet(currentArray, copy)) { + return this; + } + } + throw new ConcurrentModificationException("Cannot successfully set new element"); + } + + @Override + public E unsafeRemove(int index) { + + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + + @Nullable E[] currentArray = wrapped.get(); + int currentSize = currentArray.length; + int lastInex = currentSize - 1; + + @Nullable E[] copy = Arrays.copyOf(currentArray, lastInex); + + if (index == lastInex) { + if (wrapped.compareAndSet(currentArray, copy)) { + return currentArray[index]; + } + continue; + } + + int numMoved = currentSize - index - 1; + + if (numMoved > 0) { + System.arraycopy(currentArray, index + 1, copy, index, numMoved); + } + + if (wrapped.compareAndSet(currentArray, copy)) { + return currentArray[index]; + } + } + throw new ConcurrentModificationException("Cannot successfully remove element"); + } + + @Override + public E unsafeGet(int index) { + return wrapped.get()[index]; + } + + @Override + public UnsafeMutableArray prepareForSize(int expectedSize) { + return this; + } + + @Override + public UnsafeMutableArray trimToSize() { + return this; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java index 1494b22b..3a9791bc 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.array.impl; +import java.util.function.BiConsumer; import java.util.function.BiPredicate; import javasabr.rlib.collections.array.ArrayIterationFunctions; import javasabr.rlib.collections.array.UnsafeArray; @@ -22,6 +23,16 @@ public record DefaultArrayIterationFunctions(UnsafeArray array) implements return null; } + @Override + public ArrayIterationFunctions forEach(T arg1, BiConsumer consumer) { + @Nullable E[] wrapped = array.wrapped(); + int size = array.size(); + for (int i = 0; i < size; i++) { + consumer.accept(wrapped[i], arg1); + } + return this; + } + @Override public boolean anyMatch(T arg1, BiPredicate filter) { return findAny(arg1, filter) != null; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java index 65dfc195..0c2aff70 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java @@ -2,7 +2,6 @@ import java.util.Arrays; import javasabr.rlib.collections.array.UnsafeMutableArray; -import javasabr.rlib.common.util.ArrayUtils; import lombok.Getter; import lombok.experimental.Accessors; import org.jspecify.annotations.Nullable; @@ -11,7 +10,12 @@ @Accessors(fluent = true) public class DefaultMutableArray extends AbstractMutableArray { - @Nullable E[] wrapped; + /* + It's initialized during object construction by setter #wrapped + */ + @SuppressWarnings("NotNullFieldNotInitialized") + @Nullable + E[] wrapped; int size; public DefaultMutableArray(Class type) { @@ -38,8 +42,8 @@ protected void wrapped(@Nullable E[] wrapped) { } @Override - public UnsafeMutableArray unsafeAdd(E object) { - wrapped[size++] = object; + public UnsafeMutableArray unsafeAdd(E element) { + wrapped[size++] = element; return this; } @@ -86,7 +90,7 @@ public UnsafeMutableArray trimToSize() { if (size == wrapped.length) { return this; } - wrapped = ArrayUtils.copyOfRange(wrapped, 0, size); + wrapped = Arrays.copyOfRange(wrapped, 0, size); return this; } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java index b03120e9..e3500c89 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java @@ -1,5 +1,6 @@ package javasabr.rlib.collections.array.impl; +import java.util.function.BiConsumer; import java.util.function.BiPredicate; import javasabr.rlib.collections.array.ReversedArrayIterationFunctions; import javasabr.rlib.collections.array.UnsafeArray; @@ -23,6 +24,16 @@ public record DefaultReversedArrayIterationFunctions(UnsafeArray array) im return null; } + @Override + public ReversedArrayIterationFunctions forEach(T arg1, BiConsumer consumer) { + @Nullable E[] wrapped = array.wrapped(); + int size = array.size(); + for (int i = 0; i < size; i++) { + consumer.accept(arg1, wrapped[i]); + } + return this; + } + @Override public boolean anyMatch(T arg1, BiPredicate filter) { return findAny(arg1, filter) != null; diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadSafe.java b/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadSafe.java new file mode 100644 index 00000000..9227eaee --- /dev/null +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/ThreadSafe.java @@ -0,0 +1,6 @@ +package javasabr.rlib.common.util; + +/** + * Interface to mark a class that it's a thread safe. + */ +public interface ThreadSafe {} diff --git a/rlib-logger-impl/build.gradle b/rlib-logger-impl/build.gradle index 05c70c97..dd159500 100644 --- a/rlib-logger-impl/build.gradle +++ b/rlib-logger-impl/build.gradle @@ -1,3 +1,4 @@ dependencies { api projects.rlibCommon + api projects.rlibCollections } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java index be5eb0c9..40b301b8 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java @@ -9,8 +9,8 @@ import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayFactory; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerFactory; import javasabr.rlib.logger.api.LoggerLevel; @@ -30,8 +30,8 @@ public class DefaultLoggerService implements LoggerFactory, LoggerService { static final LoggerLevel[] LOGGER_LEVELS = LoggerLevel.values(); ConcurrentMap loggers; - Array listeners; - Array writers; + MutableArray listeners; + MutableArray writers; Logger logger; DateTimeFormatter timeFormatter; @@ -123,8 +123,12 @@ public void write(LoggerLevel level, String loggerName, String logMessage) { private void write(LoggerLevel level, String resultMessage) { - listeners.forEachR(resultMessage, LoggerListener::println); - writers.forEachR(resultMessage, DefaultLoggerService::append); + listeners + .iterations() + .forEach(resultMessage, LoggerListener::println); + writers + .iterations() + .forEach(resultMessage, DefaultLoggerService::append); switch (level) { case INFO, DEBUG -> System.out.println(resultMessage); From 9e882f9e4b799134fce5bcc8fd2b50addd37a194 Mon Sep 17 00:00:00 2001 From: javasabr Date: Mon, 1 Sep 2025 18:31:20 +0200 Subject: [PATCH 7/8] continue working on collections module --- .../classpath/impl/ClassPathScannerImpl.java | 4 +- rlib-collections/build.gradle | 1 + .../rlib/collections/array/Array.java | 22 +++++---- .../array/ArrayIterationFunctions.java | 3 ++ .../ReversedArrayIterationFunctions.java | 3 ++ .../collections/array/impl/AbstractArray.java | 48 ++++++++++--------- .../array/impl/AbstractMutableArray.java | 14 ++---- .../array/impl/CopyOnWriteMutableArray.java | 33 ++++++++++++- .../impl/DefaultArrayIterationFunctions.java | 11 +++++ .../array/impl/DefaultMutableArray.java | 11 ++++- ...efaultReversedArrayIterationFunctions.java | 11 +++++ .../array/impl/ImmutableArray.java | 5 +- .../javasabr/rlib/functions/TriConsumer.java | 10 ++++ rlib-network/build.gradle | 1 + .../javasabr/rlib/network/Connection.java | 3 +- .../rlib/network/impl/AbstractConnection.java | 22 +++++---- .../registry/ReadablePacketRegistry.java | 4 +- .../impl/IdBasedReadablePacketRegistry.java | 4 +- .../server/impl/DefaultServerNetwork.java | 10 ++-- .../IdBasedReadablePacketRegistryTest.java | 13 +++-- .../plugin/system/impl/BasePluginSystem.java | 2 +- 21 files changed, 157 insertions(+), 78 deletions(-) create mode 100644 rlib-functions/src/main/java/javasabr/rlib/functions/TriConsumer.java diff --git a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java index e0882981..af6ae860 100644 --- a/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java +++ b/rlib-classpath/src/main/java/javasabr/rlib/classpath/impl/ClassPathScannerImpl.java @@ -143,12 +143,12 @@ public void foundResourcesTo(MutableArray container) { @Override public Array> foundClasses() { - return Array.of(Class.class, classes); + return Array.typed(Class.class, classes); } @Override public Array foundResources() { - return Array.of(String.class, resources); + return Array.typed(String.class, resources); } protected Array calculatePathsToScan() { diff --git a/rlib-collections/build.gradle b/rlib-collections/build.gradle index f8325ab9..8acc7aeb 100644 --- a/rlib-collections/build.gradle +++ b/rlib-collections/build.gradle @@ -1,5 +1,6 @@ dependencies { api projects.rlibCommon + api projects.rlibFunctions testImplementation projects.rlibLoggerImpl } \ No newline at end of file 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 3e0b3e98..4da6f45b 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 @@ -22,23 +22,23 @@ static Array empty(Class type) { } static Array of(E e1) { - return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1); + return new ImmutableArray<>(Object.class, e1); } static Array of(E e1, E e2) { - return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1, e2); + return new ImmutableArray<>(Object.class, e1, e2); } static Array of(E e1, E e2, E e3) { - return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1, e2, e3); + return new ImmutableArray<>(Object.class, e1, e2, e3); } static Array of(E e1, E e2, E e3, E e4) { - return new ImmutableArray<>(ClassUtils.unsafeCast(e1.getClass()), e1, e2, e3, e4); + return new ImmutableArray<>(Object.class, e1, e2, e3, e4); } @SafeVarargs - static Array of(Class type, E... elements) { + static Array typed(Class type, E... elements) { return new ImmutableArray<>(type, elements); } @@ -82,7 +82,7 @@ static Array copyOf(MutableArray array) { * array does not permit null elements * ({@linkplain Collection##optional-restrictions optional}) */ - boolean contains(Object object); + boolean contains(@Nullable Object object); /** * Returns {@code true} if this array contains all of the elements @@ -149,7 +149,8 @@ static Array copyOf(MutableArray array) { * @return the retrieved element or null */ - @Nullable E first(); + @Nullable + E first(); E get(int index); @@ -158,7 +159,8 @@ static Array copyOf(MutableArray array) { * @return the retrieved element or null */ - @Nullable E last(); + @Nullable + E last(); @Override default Iterator iterator() { @@ -168,9 +170,9 @@ default Iterator iterator() { /** * @return the index of the object or -1. */ - int indexOf(Object object); + int indexOf(@Nullable Object object); - int lastIndexOf(Object object); + int lastIndexOf(@Nullable Object object); T[] toArray(T[] newArray); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java index cf0beead..98576969 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayIterationFunctions.java @@ -2,6 +2,7 @@ import java.util.function.BiConsumer; import java.util.function.BiPredicate; +import javasabr.rlib.functions.TriConsumer; import org.jspecify.annotations.Nullable; public interface ArrayIterationFunctions { @@ -11,5 +12,7 @@ public interface ArrayIterationFunctions { ArrayIterationFunctions forEach(T arg1, BiConsumer consumer); + ArrayIterationFunctions forEach(F arg1, S arg2, TriConsumer consumer); + boolean anyMatch(T arg1, BiPredicate filter); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java index 051670e3..676a5780 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ReversedArrayIterationFunctions.java @@ -2,6 +2,7 @@ import java.util.function.BiConsumer; import java.util.function.BiPredicate; +import javasabr.rlib.functions.TriConsumer; import org.jspecify.annotations.Nullable; public interface ReversedArrayIterationFunctions { @@ -11,5 +12,7 @@ public interface ReversedArrayIterationFunctions { ReversedArrayIterationFunctions forEach(T arg1, BiConsumer consumer); + ReversedArrayIterationFunctions forEach(F arg1, S arg2, TriConsumer consumer); + boolean anyMatch(T arg1, BiPredicate filter); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java index 2d8617aa..63a7ad70 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java @@ -29,13 +29,15 @@ protected AbstractArray(Class type) { this.type = ClassUtils.unsafeCast(type); } + @Nullable @Override - public @Nullable E first() { + public E first() { return isEmpty() ? null : get(0); } + @Nullable @Override - public @Nullable E last() { + public E last() { int size = size(); if (size < 1) { return null; @@ -56,10 +58,13 @@ protected void checkIndex(int index) { } @Override - public boolean contains(Object object) { - var wrapped = wrapped(); + public boolean contains(@Nullable Object object) { + if (object == null) { + return false; + } + @Nullable E[] wrapped = wrapped(); for (int i = 0, length = size(); i < length; i++) { - if (wrapped[i].equals(object)) { + if (object.equals(wrapped[i])) { return true; } } @@ -68,12 +73,11 @@ public boolean contains(Object object) { @Override public boolean containsAll(Array array) { - if (array.isEmpty()) { return false; } - Object[] wrapped = array + @Nullable Object[] wrapped = array .asUnsafe() .wrapped(); @@ -88,41 +92,38 @@ public boolean containsAll(Array array) { @Override public boolean containsAll(Collection collection) { - if (collection.isEmpty()) { return false; } - for (var element : collection) { if (!contains(element)) { return false; } } - return true; } @Override public boolean containsAll(Object[] array) { - if (array.length < 1) { return false; } - for (Object element : array) { if (!contains(element)) { return false; } } - return true; } @Override - public int indexOf(Object object) { - var wrapped = wrapped(); + public int indexOf(@Nullable Object object) { + if (object == null) { + return -1; + } + @Nullable E[] wrapped = wrapped(); for (int i = 0, length = size(); i < length; i++) { - if (wrapped[i].equals(object)) { + if (object.equals(wrapped[i])) { return i; } } @@ -130,10 +131,13 @@ public int indexOf(Object object) { } @Override - public int lastIndexOf(Object object) { - var wrapped = wrapped(); + public int lastIndexOf(@Nullable Object object) { + if (object == null) { + return -1; + } + @Nullable E[] wrapped = wrapped(); for (int i = size() - 1; i >= 0; i--) { - if (wrapped[i].equals(object)) { + if (object.equals(wrapped[i])) { return i; } } @@ -142,8 +146,8 @@ public int lastIndexOf(Object object) { @Override public void forEach(Consumer action) { - var wrapped = wrapped(); - for (int i = size() - 1; i >= 0; i--) { + @Nullable E[] wrapped = wrapped(); + for (int i = 0, limit = size(); i < limit; i++) { action.accept(wrapped[i]); } } @@ -164,11 +168,9 @@ public T[] toArray(T[] newArray) { @Nullable E[] array = wrapped(); if (newArray.length >= size()) { - for (int i = 0, j = 0, length = size(), newLength = newArray.length; i < length && j < newLength; i++) { newArray[j++] = (T) array[i]; } - return newArray; } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java index ce092fcb..e63d8930 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java @@ -18,17 +18,8 @@ @FieldDefaults(level = AccessLevel.PROTECTED) public abstract class AbstractMutableArray extends AbstractArray implements UnsafeMutableArray { - protected static final int DEFAULT_CAPACITY = 10; - - public AbstractMutableArray(Class type) { - this(type, DEFAULT_CAPACITY); - } - public AbstractMutableArray(Class type, int capacity) { + protected AbstractMutableArray(Class type) { super(type); - if (capacity < 0) { - throw new IllegalArgumentException("Negative capacity"); - } - wrapped(ArrayUtils.create(type, capacity)); } @Override @@ -72,6 +63,7 @@ public boolean addAll(Collection collection) { int elementsToAdd = collection.size(); prepareForSize(size + elementsToAdd); for (E element : collection) { + Objects.requireNonNull(element); unsafeAdd(element); } return true; @@ -123,7 +115,7 @@ public boolean retainAll(Collection collection) { } int removed = 0; - var wrapped = wrapped(); + @Nullable E[] wrapped = wrapped(); for (int i = 0, length = size(); i < length; i++) { if (!collection.contains(wrapped[i])) { diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java index 4c08eb3f..c55e96f8 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java @@ -1,22 +1,28 @@ package javasabr.rlib.collections.array.impl; +import java.lang.invoke.VarHandle; import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.concurrent.atomic.AtomicReference; import javasabr.rlib.collections.array.Array; import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.ThreadSafe; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class CopyOnWriteMutableArray extends AbstractMutableArray implements ThreadSafe { protected static final int LIMIT_ATTEMPTS = 1000; - AtomicReference<@Nullable E[]> wrapped = new AtomicReference<>(); + AtomicReference<@Nullable E[]> wrapped; public CopyOnWriteMutableArray(Class type) { - super(type, 0); + super(type); + this.wrapped = new AtomicReference<>(ArrayUtils.create(type, 0)); } @Override @@ -154,6 +160,29 @@ public E unsafeRemove(int index) { throw new ConcurrentModificationException("Cannot successfully remove element"); } + @Override + public void clear() { + if (isEmpty()) { + return; + } + + @Nullable E[] emptyArray = null; + + for (int i = 0; i < LIMIT_ATTEMPTS; i++) { + @Nullable E[] currentArray = wrapped.get(); + if (currentArray.length < 1) { + return; + } + if (emptyArray == null) { + emptyArray = ArrayUtils.create(type, 0); + } + if (wrapped.compareAndSet(currentArray, emptyArray)) { + return; + } + } + throw new ConcurrentModificationException("Cannot successfully clear this array"); + } + @Override public E unsafeGet(int index) { return wrapped.get()[index]; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java index 3a9791bc..cc8c5a35 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterationFunctions.java @@ -4,6 +4,7 @@ import java.util.function.BiPredicate; import javasabr.rlib.collections.array.ArrayIterationFunctions; import javasabr.rlib.collections.array.UnsafeArray; +import javasabr.rlib.functions.TriConsumer; import org.jspecify.annotations.Nullable; public record DefaultArrayIterationFunctions(UnsafeArray array) implements ArrayIterationFunctions { @@ -33,6 +34,16 @@ public ArrayIterationFunctions forEach(T arg1, BiConsumer c return this; } + @Override + public ArrayIterationFunctions forEach(F arg1, S arg2, TriConsumer consumer) { + @Nullable E[] wrapped = array.wrapped(); + int size = array.size(); + for (int i = 0; i < size; i++) { + consumer.accept(wrapped[i], arg1, arg2); + } + return this; + } + @Override public boolean anyMatch(T arg1, BiPredicate filter) { return findAny(arg1, filter) != null; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java index 0c2aff70..8111d639 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java @@ -2,14 +2,20 @@ import java.util.Arrays; import javasabr.rlib.collections.array.UnsafeMutableArray; +import javasabr.rlib.common.util.ArrayUtils; +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 DefaultMutableArray extends AbstractMutableArray { + protected static final int DEFAULT_CAPACITY = 10; + /* It's initialized during object construction by setter #wrapped */ @@ -19,11 +25,12 @@ public class DefaultMutableArray extends AbstractMutableArray { int size; public DefaultMutableArray(Class type) { - super(type); + this(type, DEFAULT_CAPACITY); } public DefaultMutableArray(Class type, int capacity) { - super(type, capacity); + super(type); + this.wrapped = ArrayUtils.create(type, capacity); } @Override diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java index e3500c89..20799ace 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultReversedArrayIterationFunctions.java @@ -4,6 +4,7 @@ import java.util.function.BiPredicate; import javasabr.rlib.collections.array.ReversedArrayIterationFunctions; import javasabr.rlib.collections.array.UnsafeArray; +import javasabr.rlib.functions.TriConsumer; import org.jspecify.annotations.Nullable; public record DefaultReversedArrayIterationFunctions(UnsafeArray array) implements @@ -34,6 +35,16 @@ public ReversedArrayIterationFunctions forEach(T arg1, BiConsumer ReversedArrayIterationFunctions forEach(F arg1, S arg2, TriConsumer consumer) { + @Nullable E[] wrapped = array.wrapped(); + int size = array.size(); + for (int i = 0; i < size; i++) { + consumer.accept(arg1, arg2, wrapped[i]); + } + return this; + } + @Override public boolean anyMatch(T arg1, BiPredicate filter) { return findAny(arg1, filter) != null; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java index 80250acf..717ec00c 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/ImmutableArray.java @@ -2,11 +2,14 @@ import java.util.stream.Stream; import javasabr.rlib.collections.array.UnsafeArray; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class ImmutableArray extends AbstractArray implements UnsafeArray { - final E[] wrapped; + E[] wrapped; @SafeVarargs public ImmutableArray(Class type, E... elements) { diff --git a/rlib-functions/src/main/java/javasabr/rlib/functions/TriConsumer.java b/rlib-functions/src/main/java/javasabr/rlib/functions/TriConsumer.java new file mode 100644 index 00000000..45bc6de8 --- /dev/null +++ b/rlib-functions/src/main/java/javasabr/rlib/functions/TriConsumer.java @@ -0,0 +1,10 @@ +package javasabr.rlib.functions; + +@FunctionalInterface +public interface TriConsumer { + + /** + * Performs this operation on the given arguments. + */ + void accept(F arg1, S arg2, T arg3); +} diff --git a/rlib-network/build.gradle b/rlib-network/build.gradle index 0dd241d5..c8b65e5c 100644 --- a/rlib-network/build.gradle +++ b/rlib-network/build.gradle @@ -1,6 +1,7 @@ dependencies { api projects.rlibCommon api projects.rlibClasspath + api projects.rlibCollections api libs.project.reactor.core testRuntimeOnly projects.rlibLoggerImpl } 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 77d65ce3..b62581b5 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/Connection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/Connection.java @@ -1,6 +1,7 @@ package javasabr.rlib.network; import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import javasabr.rlib.common.function.NotNullBiConsumer; import javasabr.rlib.network.packet.ReadablePacket; import javasabr.rlib.network.packet.WritablePacket; @@ -67,7 +68,7 @@ class ReceivedPacketEvent, R extends ReadablePacket> * * @param consumer the consumer. */ - void onReceive(NotNullBiConsumer, ? super R> consumer); + void onReceive(BiConsumer, ? super R> consumer); /** * Get a stream of received packet events. 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 cd7961ee..e6630945 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 @@ -8,9 +8,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.StampedLock; import java.util.function.BiConsumer; -import javasabr.rlib.common.function.NotNullBiConsumer; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayFactory; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.linkedlist.LinkedList; import javasabr.rlib.common.util.linkedlist.LinkedListFactory; import javasabr.rlib.logger.api.Logger; @@ -60,7 +59,7 @@ public WritablePacketWithFeedback(CompletableFuture attachment, W packe protected final AtomicBoolean isWriting; protected final AtomicBoolean closed; - protected final Array, ? super R>> subscribers; + protected final MutableArray, ? super R>> subscribers; protected final int maxPacketsByRead; @@ -79,7 +78,7 @@ public AbstractConnection( this.network = network; this.isWriting = new AtomicBoolean(false); this.closed = new AtomicBoolean(false); - this.subscribers = ArrayFactory.newCopyOnModifyArray(NotNullBiConsumer.class); + this.subscribers = ArrayFactory.newCopyOnModifyArray(BiConsumer.class); this.remoteAddress = String.valueOf(NetworkUtils.getRemoteAddress(channel)); } @@ -95,12 +94,15 @@ protected void handleReceivedPacket(R packet) { LOGGER.debug( channel, packet, - (ch, pck) -> "Handle received packet: " + pck + " from: " + NetworkUtils.getRemoteAddress(ch)); - subscribers.forEachR(this, packet, BiConsumer::accept); + (ch, pck) -> "Handle received packet: %s from: %s".formatted(pck, NetworkUtils.getRemoteAddress(ch))); + + subscribers + .iterations() + .forEach(this, packet, BiConsumer::accept); } @Override - public void onReceive(NotNullBiConsumer, ? super R> consumer) { + public void onReceive(BiConsumer, ? super R> consumer) { subscribers.add(consumer); getPacketReader().startRead(); } @@ -118,7 +120,7 @@ public Flux receivedPackets() { protected void registerFluxOnReceivedEvents( FluxSink, ? extends R>> sink) { - NotNullBiConsumer, R> listener = + BiConsumer, R> listener = (connection, packet) -> sink.next(new ReceivedPacketEvent<>(connection, packet)); @@ -129,7 +131,7 @@ protected void registerFluxOnReceivedEvents( protected void registerFluxOnReceivedPackets(FluxSink sink) { - NotNullBiConsumer, R> listener = (connection, packet) -> sink.next(packet); + BiConsumer, R> listener = (connection, packet) -> sink.next(packet); onReceive(listener); diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java index b463b55d..89595677 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java @@ -2,8 +2,8 @@ import javasabr.rlib.classpath.ClassPathScanner; import javasabr.rlib.classpath.ClassPathScannerFactory; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayCollectors; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; import javasabr.rlib.network.annotation.PacketDescription; import javasabr.rlib.network.packet.IdBasedReadablePacket; import javasabr.rlib.network.packet.registry.impl.IdBasedReadablePacketRegistry; diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java index 7e418938..1ed08492 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java @@ -2,9 +2,9 @@ import java.util.Arrays; import java.util.function.Supplier; +import javasabr.rlib.collections.array.Array; import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.common.util.array.Array; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.annotation.PacketDescription; @@ -49,7 +49,7 @@ public IdBasedReadablePacketRegistry(Class type) { * id. */ public IdBasedReadablePacketRegistry register(Array> classes) { - return register(classes.array(), classes.size()); + return register(classes.toArray(), classes.size()); } /** diff --git a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java index faabd95c..69b5960d 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java @@ -17,11 +17,11 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.concurrent.GroupThreadFactory; import javasabr.rlib.common.util.ClassUtils; import javasabr.rlib.common.util.Utils; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayFactory; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.Network; @@ -73,7 +73,7 @@ public void failed(Throwable exc, DefaultServerNetwork network) { protected final AsynchronousChannelGroup group; protected final AsynchronousServerSocketChannel channel; - protected final Array> subscribers; + protected final MutableArray> subscribers; public DefaultServerNetwork( ServerNetworkConfig config, @@ -164,7 +164,9 @@ protected void acceptNext() { protected void onAccept(C connection) { connection.onConnected(); - subscribers.forEachR(connection, Consumer::accept); + subscribers + .iterations() + .forEach(connection, Consumer::accept); } @Override diff --git a/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java b/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java index 601ebc9f..dbfbd352 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java @@ -1,8 +1,7 @@ package javasabr.rlib.network; +import javasabr.rlib.collections.array.Array; import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.common.util.array.Array; -import javasabr.rlib.common.util.array.ArrayFactory; import javasabr.rlib.network.annotation.PacketDescription; import javasabr.rlib.network.impl.DefaultConnection; import javasabr.rlib.network.packet.IdBasedReadablePacket; @@ -60,7 +59,8 @@ Object shouldBeCreated() { @Test void shouldRegister3PacketsByArray() { - var registry = new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class).register(ArrayFactory.asArray( + var registry = new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class).register(Array.typed( + Class.class, Impl1.class, Impl2.class, Impl3.class)); @@ -110,11 +110,10 @@ void shouldRegister2PrivatePacketsBySingle() { @Test void shouldNotAcceptWrongTypes() { - var array = ArrayFactory.asArray(PrivateImpl1.class, PrivateImpl2.class, PublicImpl1.class, PublicImpl2.class); + var array = Array.typed(Class.class, PrivateImpl1.class, PrivateImpl2.class, PublicImpl1.class, PublicImpl2.class); - var registry = new IdBasedReadablePacketRegistry<>(PublicBase.class).register(ClassUtils.>>unsafeNNCast( - array)); + var registry = new IdBasedReadablePacketRegistry<>(PublicBase.class) + .register(ClassUtils.>>unsafeNNCast(array)); Assertions.assertTrue(registry.findById(1) instanceof PublicImpl1); Assertions.assertThrows(IllegalArgumentException.class, () -> registry.findById(2)); diff --git a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java index 66125fd7..95fca370 100644 --- a/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java +++ b/rlib-plugin-system/src/main/java/javasabr/rlib/plugin/system/impl/BasePluginSystem.java @@ -179,7 +179,7 @@ protected BasePluginSystem initializeImpl(Executor executor) { State newState = new State( current.containers, - Array.of(Plugin.class, plugins.values(Plugin.class).toArray()), + Array.typed(Plugin.class, plugins.values(Plugin.class).toArray()), current.idToContainer, plugins); From aa9e886d9c0d61ec0c42cfdd99df48e89905a20e Mon Sep 17 00:00:00 2001 From: javasabr Date: Wed, 3 Sep 2025 20:18:59 +0200 Subject: [PATCH 8/8] continue working on collections module --- build.gradle | 1 + gradle/libs.versions.toml | 2 +- .../rlib/collections/array/ArrayFactory.java | 7 +- .../array/LockableMutableArray.java | 15 ++ .../collections/array/impl/AbstractArray.java | 44 ++++ .../impl/AbstractLockableMutableArray.java | 61 ++++++ .../array/impl/AbstractMutableArray.java | 76 ++++++- .../array/impl/CopyOnWriteMutableArray.java | 13 +- .../array/impl/DefaultArrayIterator.java | 4 +- .../array/impl/DefaultMutableArray.java | 69 +----- .../impl/DefaultMutableArrayIterator.java | 43 ++++ .../impl/StampedLockBasedMutableArray.java | 50 +++++ .../collections/array/MutableArrayTest.java | 200 ++++++++++++++++++ .../javasabr/rlib/common/util/ArrayUtils.java | 5 +- .../logger/impl/DefaultLoggerService.java | 4 +- .../rlib/logger/impl/DefaultLoggerTest.java | 23 +- .../rlib/network/impl/AbstractConnection.java | 2 +- .../network/impl/ReuseBufferAllocator.java | 35 ++- .../server/impl/DefaultServerNetwork.java | 2 +- 19 files changed, 565 insertions(+), 91 deletions(-) create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/LockableMutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractLockableMutableArray.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArrayIterator.java create mode 100644 rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/StampedLockBasedMutableArray.java create mode 100644 rlib-collections/src/test/java/javasabr/rlib/collections/array/MutableArrayTest.java diff --git a/build.gradle b/build.gradle index ef55852d..c5069b27 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,7 @@ subprojects { annotationProcessor libs.lombok testImplementation libs.junit.api + testImplementation libs.junit.jupiter.params testCompileOnly libs.lombok testCompileOnly libs.jspecify testRuntimeOnly libs.junit.engine diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5caa7583..1b63cd29 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,6 +24,7 @@ jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" } lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" } junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" } +junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit-jupiter" } junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform-launcher" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } @@ -33,5 +34,4 @@ angus-mail = { module = "org.eclipse.angus:angus-mail", version.ref = "angus-mai testcontainers = { module = "org.testcontainers:testcontainers", version.ref = "testcontainers" } [bundles] -junit = ["junit-engine", "junit-api"] mail = ["jakarta-mail-api", "angus-mail"] \ No newline at end of file diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java index 09f10d5a..a5200d8b 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/ArrayFactory.java @@ -2,6 +2,7 @@ import javasabr.rlib.collections.array.impl.CopyOnWriteMutableArray; import javasabr.rlib.collections.array.impl.DefaultMutableArray; +import javasabr.rlib.collections.array.impl.StampedLockBasedMutableArray; import javasabr.rlib.common.util.ClassUtils; import lombok.experimental.UtilityClass; @@ -16,7 +17,11 @@ public static MutableArray mutableArray(Class type, int capaci return new DefaultMutableArray<>(ClassUtils.unsafeCast(type), capacity); } - public static MutableArray newCopyOnModifyArray(Class type) { + public static MutableArray copyOnModifyArray(Class type) { return new CopyOnWriteMutableArray<>(type); } + + public static LockableMutableArray stampedLockBasedArray(Class type) { + return new StampedLockBasedMutableArray<>(type); + } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/LockableMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/LockableMutableArray.java new file mode 100644 index 00000000..bd95cae6 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/LockableMutableArray.java @@ -0,0 +1,15 @@ +package javasabr.rlib.collections.array; + +import javasabr.rlib.common.util.ThreadSafe; + +public interface LockableMutableArray extends MutableArray, ThreadSafe { + + long readLock(); + void readUnlock(long stamp); + long tryOptimisticRead(); + + boolean validateLock(long stamp); + + long writeLock(); + void writeUnlock(long stamp); +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java index 63a7ad70..c0054265 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractArray.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import javasabr.rlib.collections.array.Array; @@ -51,6 +52,11 @@ public E get(int index) { return unsafeGet(index); } + @Override + public E unsafeGet(int index) { + return wrapped()[index]; + } + protected void checkIndex(int index) { if (index < 0 || index >= size()) { throw new ArrayIndexOutOfBoundsException(); @@ -191,14 +197,52 @@ public T[] toArray(Class componentType) { @Override public E[] toArray() { @Nullable E[] wrapped = wrapped(); + //noinspection unchecked return Arrays.copyOf(wrapped, size(), (Class) wrapped.getClass()); } @Override public String toString(Function toString) { + //noinspection NullableProblems return ArrayUtils.toString(wrapped(), size(), toString); } + @Override + public String toString() { + //noinspection NullableProblems + return ArrayUtils.toString(wrapped(), size(), String::valueOf); + } + + @Override + public boolean equals(Object another) { + + if (!(another instanceof Array array)) { + return false; + } + + //noinspection NullableProblems + Object[] wrapped = array + .asUnsafe() + .wrapped(); + + return Arrays.equals(wrapped(), 0, size(), wrapped, 0, array.size()); + } + + @Override + public int hashCode() { + + @Nullable E[] wrapped = wrapped(); + + int result = 1; + + for (int i = 0, wrappedLength = size(); i < wrappedLength; i++) { + Object element = wrapped[i]; + result = 31 * result + (element == null ? 0 : element.hashCode()); + } + + return result; + } + @Override public UnsafeArray asUnsafe() { return this; diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractLockableMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractLockableMutableArray.java new file mode 100644 index 00000000..44dd73d0 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractLockableMutableArray.java @@ -0,0 +1,61 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.concurrent.atomic.AtomicInteger; +import javasabr.rlib.collections.array.LockableMutableArray; +import javasabr.rlib.common.util.ArrayUtils; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractLockableMutableArray extends AbstractMutableArray implements + LockableMutableArray { + + protected static final int DEFAULT_CAPACITY = 10; + + final AtomicInteger size; + + @Getter + @Setter(AccessLevel.PROTECTED) + volatile @Nullable E[] wrapped; + + protected AbstractLockableMutableArray(Class type) { + this(type, DEFAULT_CAPACITY); + } + + protected AbstractLockableMutableArray(Class type, int capacity) { + super(type); + validateCapacity(capacity); + this.wrapped = ArrayUtils.create(type, capacity); + this.size = new AtomicInteger(0); + } + + @Override + public int size() { + return size.get(); + } + + @Override + protected void size(int size) { + this.size.set(size); + } + + @Override + public boolean isEmpty() { + return size.get() < 1; + } + + @Override + protected int getAndIncrementSize() { + return size.getAndIncrement(); + } + + @Override + protected int decrementAnGetSize() { + return size.decrementAndGet(); + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java index e63d8930..da44d519 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; @@ -69,12 +70,24 @@ public boolean addAll(Collection collection) { return true; } + @Override + public UnsafeMutableArray unsafeAdd(E element) { + wrapped()[getAndIncrementSize()] = element; + return this; + } + @Override public void replace(int index, E element) { checkIndex(index); unsafeSet(index, element); } + @Override + public UnsafeMutableArray unsafeSet(int index, E element) { + wrapped()[index] = element; + return this; + } + @Override public E remove(int index) { checkIndex(index); @@ -91,6 +104,24 @@ public boolean remove(Object element) { return true; } + @Override + public E unsafeRemove(int index) { + + int numMoved = size() - index - 1; + + @Nullable E[] wrapped = wrapped(); + E element = wrapped[index]; + + if (numMoved > 0) { + System.arraycopy(wrapped, index + 1, wrapped, index, numMoved); + } + + wrapped[decrementAnGetSize()] = null; + + //noinspection DataFlowIssue + return element; + } + @Override public boolean removeAll(Collection collection) { if (collection.isEmpty()) { @@ -130,11 +161,16 @@ public boolean retainAll(Collection collection) { @Override public void clear() { - if (isEmpty()) { - return; + int size = size(); + if (size > 0) { + Arrays.fill(wrapped(), 0, size, null); + size(0); } - Arrays.fill(wrapped(), 0, size(), null); - size(0); + } + + @Override + public Iterator iterator() { + return new DefaultMutableArrayIterator<>(this); } @Override @@ -159,10 +195,42 @@ protected void processAdd(E[] array, int currentSize, int elementsToAdd) { protected abstract void size(int size); + protected abstract int getAndIncrementSize(); + protected abstract int decrementAnGetSize(); + protected abstract void wrapped(@Nullable E[] wrapped); + @Override + public UnsafeMutableArray prepareForSize(int expectedSize) { + @Nullable E[] wrapped = wrapped(); + if (expectedSize > wrapped.length) { + int newLength = Math.max((wrapped.length * 3) / 2, expectedSize); + wrapped(Arrays.copyOf(wrapped, newLength)); + } + return this; + } + + + @Override + public UnsafeMutableArray trimToSize() { + @Nullable E[] wrapped = wrapped(); + int size = size(); + + if (size == wrapped.length) { + return this; + } + wrapped(Arrays.copyOfRange(wrapped, 0, size)); + return this; + } + @Override public UnsafeMutableArray asUnsafe() { return this; } + + protected static void validateCapacity(int capacity) { + if (capacity < 0) { + throw new IllegalArgumentException("Capacity cannot be negative"); + } + } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java index c55e96f8..8e94b296 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/CopyOnWriteMutableArray.java @@ -1,6 +1,5 @@ package javasabr.rlib.collections.array.impl; -import java.lang.invoke.VarHandle; import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; @@ -188,6 +187,8 @@ public E unsafeGet(int index) { return wrapped.get()[index]; } + /* disabled methods */ + @Override public UnsafeMutableArray prepareForSize(int expectedSize) { return this; @@ -197,4 +198,14 @@ public UnsafeMutableArray prepareForSize(int expectedSize) { public UnsafeMutableArray trimToSize() { return this; } + + @Override + protected int getAndIncrementSize() { + return 0; + } + + @Override + protected int decrementAnGetSize() { + return 0; + } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java index 77cbc46b..0e4708a8 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultArrayIterator.java @@ -10,13 +10,13 @@ /** * @author JavaSaBr */ -@FieldDefaults(level = AccessLevel.PRIVATE) +@FieldDefaults(level = AccessLevel.PROTECTED) public class DefaultArrayIterator implements Iterator { final @Nullable E[] wrapped; final int size; - private int position; + int position; public DefaultArrayIterator(Array array) { this.wrapped = array.asUnsafe().wrapped(); diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java index 8111d639..6e11b68a 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArray.java @@ -1,16 +1,16 @@ package javasabr.rlib.collections.array.impl; -import java.util.Arrays; -import javasabr.rlib.collections.array.UnsafeMutableArray; import javasabr.rlib.common.util.ArrayUtils; import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; @Getter -@Accessors(fluent = true) +@Setter(AccessLevel.PROTECTED) +@Accessors(fluent = true, chain = false) @FieldDefaults(level = AccessLevel.PROTECTED) public class DefaultMutableArray extends AbstractMutableArray { @@ -19,7 +19,6 @@ public class DefaultMutableArray extends AbstractMutableArray { /* It's initialized during object construction by setter #wrapped */ - @SuppressWarnings("NotNullFieldNotInitialized") @Nullable E[] wrapped; int size; @@ -30,74 +29,22 @@ public DefaultMutableArray(Class type) { public DefaultMutableArray(Class type, int capacity) { super(type); + validateCapacity(capacity); this.wrapped = ArrayUtils.create(type, capacity); } - @Override - protected void size(int size) { - this.size = size; - } - @Override public boolean isEmpty() { return size < 1; } @Override - protected void wrapped(@Nullable E[] wrapped) { - this.wrapped = wrapped; - } - - @Override - public UnsafeMutableArray unsafeAdd(E element) { - wrapped[size++] = element; - return this; - } - - @Override - public UnsafeMutableArray unsafeSet(int index, E element) { - wrapped[index] = element; - return this; - } - - @Override - public E unsafeRemove(int index) { - - int numMoved = size - index - 1; - E element = wrapped[index]; - - if (numMoved > 0) { - System.arraycopy(wrapped, index + 1, wrapped, index, numMoved); - } - - size -= 1; - wrapped[size] = null; - - //noinspection DataFlowIssue - return element; - } - - @Override - public E unsafeGet(int index) { - //noinspection DataFlowIssue - return wrapped[index]; - } - - @Override - public UnsafeMutableArray prepareForSize(int expectedSize) { - if (expectedSize > wrapped.length) { - int newLength = Math.max((wrapped.length * 3) / 2, expectedSize); - wrapped = Arrays.copyOf(wrapped, newLength); - } - return this; + protected int getAndIncrementSize() { + return size++; } @Override - public UnsafeMutableArray trimToSize() { - if (size == wrapped.length) { - return this; - } - wrapped = Arrays.copyOfRange(wrapped, 0, size); - return this; + protected int decrementAnGetSize() { + return --size; } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArrayIterator.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArrayIterator.java new file mode 100644 index 00000000..34e5af8d --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/DefaultMutableArrayIterator.java @@ -0,0 +1,43 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import javasabr.rlib.collections.array.UnsafeMutableArray; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +/** + * @author JavaSaBr + */ +@FieldDefaults(level = AccessLevel.PROTECTED) +public class DefaultMutableArrayIterator implements Iterator { + + final UnsafeMutableArray mutableArray; + + int size; + int position; + + public DefaultMutableArrayIterator(UnsafeMutableArray array) { + this.mutableArray = array; + this.size = array.size(); + } + + @Override + public boolean hasNext() { + return position < size; + } + + @Override + public E next() { + if (position >= size) { + throw new NoSuchElementException(); + } + return mutableArray.unsafeGet(position++); + } + + @Override + public void remove() { + mutableArray.remove(--position); + size--; + } +} diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/StampedLockBasedMutableArray.java b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/StampedLockBasedMutableArray.java new file mode 100644 index 00000000..8b2414f6 --- /dev/null +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/StampedLockBasedMutableArray.java @@ -0,0 +1,50 @@ +package javasabr.rlib.collections.array.impl; + +import java.util.concurrent.locks.StampedLock; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public class StampedLockBasedMutableArray extends AbstractLockableMutableArray { + + StampedLock stampedLock; + + public StampedLockBasedMutableArray(Class type) { + this(type, DEFAULT_CAPACITY); + } + + public StampedLockBasedMutableArray(Class type, int capacity) { + super(type, capacity); + this.stampedLock = new StampedLock(); + } + + @Override + public long readLock() { + return stampedLock.readLock(); + } + + @Override + public void readUnlock(long stamp) { + stampedLock.unlockRead(stamp); + } + + @Override + public long tryOptimisticRead() { + return stampedLock.tryOptimisticRead(); + } + + @Override + public boolean validateLock(long stamp) { + return stampedLock.validate(stamp); + } + + @Override + public long writeLock() { + return stampedLock.writeLock(); + } + + @Override + public void writeUnlock(long stamp) { + stampedLock.unlockWrite(stamp); + } +} diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/array/MutableArrayTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/array/MutableArrayTest.java new file mode 100644 index 00000000..02b35dfb --- /dev/null +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/array/MutableArrayTest.java @@ -0,0 +1,200 @@ +package javasabr.rlib.collections.array; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class MutableArrayTest { + + @ParameterizedTest + @MethodSource("generateMutableArrays") + @DisplayName("should correctly add elements") + void shouldCorrectlyAddElements(MutableArray mutableArray) { + + // when: + mutableArray.add("First"); + + // then: + Assertions.assertEquals(1, mutableArray.size()); + Assertions.assertEquals("First", mutableArray.get(0)); + + // when: + mutableArray.add("second"); + + // then: + Assertions.assertEquals(2, mutableArray.size()); + Assertions.assertEquals("second", mutableArray.get(1)); + + // when: + mutableArray.add("third"); + + // then: + Assertions.assertEquals(3, mutableArray.size()); + Assertions.assertEquals("third", mutableArray.get(2)); + Assertions.assertEquals(Array.of("First", "second", "third"), mutableArray); + + // when: + for(int i = 0; i < 100; i++) { + mutableArray.add("value_" + i); + } + + // then: + Assertions.assertEquals(103, mutableArray.size()); + Assertions.assertEquals("value_99", mutableArray.get(102)); + } + + @ParameterizedTest + @MethodSource("generateMutableArrays") + @DisplayName("should correctly remove elements") + void shouldCorrectlyRemoveElements(MutableArray mutableArray) { + + // given: + for(int i = 0; i < 101; i++) { + mutableArray.add("value_" + i); + } + + // when: + mutableArray.remove("value_0"); + + // then: + Assertions.assertEquals(100, mutableArray.size()); + Assertions.assertEquals("value_1", mutableArray.get(0)); + + // when: + mutableArray.remove("value_10"); + + // then: + Assertions.assertEquals(99, mutableArray.size()); + Assertions.assertEquals("value_1", mutableArray.get(0)); + Assertions.assertEquals("value_9", mutableArray.get(8)); + Assertions.assertEquals("value_11", mutableArray.get(9)); + + // when: + mutableArray.remove("value_100"); + + // then: + Assertions.assertEquals(98, mutableArray.size()); + Assertions.assertEquals("value_99", mutableArray.get(97)); + Assertions.assertEquals("value_98", mutableArray.get(96)); + } + + @ParameterizedTest + @MethodSource("generateMutableArrays") + @DisplayName("should correctly replace elements") + void shouldCorrectlyReplaceElements(MutableArray mutableArray) { + + // given: + for(int i = 0; i < 101; i++) { + mutableArray.add("value_" + i); + } + + // when: + mutableArray.replace(0, "value_200"); + + // then: + Assertions.assertEquals(101, mutableArray.size()); + Assertions.assertEquals("value_200", mutableArray.get(0)); + + // when: + mutableArray.replace(11, "value_211"); + + // then: + Assertions.assertEquals(101, mutableArray.size()); + Assertions.assertEquals("value_211", mutableArray.get(11)); + } + + @ParameterizedTest + @MethodSource("generateMutableArrays") + @DisplayName("should correctly add batch elements") + void shouldCorrectlyAddBatchElements(MutableArray mutableArray) { + + // given: + for(int i = 0; i < 21; i++) { + mutableArray.add("value_" + i); + } + + var anotherArray = Array.typed(String.class, "a1", "a2", "a3", "a4", "a5", "a6"); + var anotherList = List.of("l1", "l2", "l3", "l4", "l5", "l6"); + var anotherNativeArray = List.of("na1", "na2", "na3", "na4", "na5", "na6") + .toArray(String[]::new); + + // when: + mutableArray.addAll(anotherArray); + + // then: + Assertions.assertEquals(27, mutableArray.size()); + Assertions.assertEquals("value_0", mutableArray.get(0)); + Assertions.assertEquals("value_20", mutableArray.get(20)); + Assertions.assertEquals("a1", mutableArray.get(21)); + Assertions.assertEquals("a6", mutableArray.get(26)); + + // when: + mutableArray.addAll(anotherList); + + // then: + Assertions.assertEquals(33, mutableArray.size()); + Assertions.assertEquals("value_0", mutableArray.get(0)); + Assertions.assertEquals("value_20", mutableArray.get(20)); + Assertions.assertEquals("a1", mutableArray.get(21)); + Assertions.assertEquals("a6", mutableArray.get(26)); + Assertions.assertEquals("l1", mutableArray.get(27)); + Assertions.assertEquals("l6", mutableArray.get(32)); + + // when: + mutableArray.addAll(anotherNativeArray); + + // then: + Assertions.assertEquals(39, mutableArray.size()); + Assertions.assertEquals("value_0", mutableArray.get(0)); + Assertions.assertEquals("value_20", mutableArray.get(20)); + Assertions.assertEquals("a1", mutableArray.get(21)); + Assertions.assertEquals("a6", mutableArray.get(26)); + Assertions.assertEquals("l1", mutableArray.get(27)); + Assertions.assertEquals("l6", mutableArray.get(32)); + Assertions.assertEquals("na1", mutableArray.get(33)); + Assertions.assertEquals("na6", mutableArray.get(38)); + } + + @ParameterizedTest + @MethodSource("generateMutableArrays") + @DisplayName("should correctly remove batch elements") + void shouldCorrectlyRemoveBatchElements(MutableArray mutableArray) { + + // given: + for(int i = 0; i < 21; i++) { + mutableArray.add("value_" + i); + } + + var anotherArray = Array.typed(String.class, "a1", "a2", "a3", "a4", "a5", "a6"); + var anotherList = List.of("l1", "l2", "l3", "l4", "l5", "l6"); + var anotherNativeArray = List.of("na1", "na2", "na3", "na4", "na5", "na6") + .toArray(String[]::new); + + mutableArray.addAll(anotherArray); + mutableArray.addAll(anotherList); + mutableArray.addAll(anotherNativeArray); + + // when: + mutableArray.removeAll(anotherList); + + // then: + Assertions.assertEquals(33, mutableArray.size()); + Assertions.assertEquals("value_0", mutableArray.get(0)); + Assertions.assertEquals("value_20", mutableArray.get(20)); + Assertions.assertEquals("a1", mutableArray.get(21)); + Assertions.assertEquals("a6", mutableArray.get(26)); + Assertions.assertEquals("na1", mutableArray.get(27)); + Assertions.assertEquals("na6", mutableArray.get(32)); + } + + private static Stream generateMutableArrays() { + return Stream.of( + Arguments.of(ArrayFactory.mutableArray(String.class)), + Arguments.of(ArrayFactory.copyOnModifyArray(String.class)), + Arguments.of(ArrayFactory.stampedLockBasedArray(String.class))); + } +} diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java index c361fe96..bde49cf6 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/ArrayUtils.java @@ -636,7 +636,8 @@ public static String toString(T[] array, int size, Function toStr return "[]"; } - var builder = new StringBuilder(); + var builder = new StringBuilder(20); + builder.append('['); for (int i = 0, last = size - 1; i < size; i++) { builder.append(toString.apply(array[i])); @@ -646,7 +647,7 @@ public static String toString(T[] array, int size, Function toStr builder.append(", "); } - builder.append("]"); + builder.append(']'); return builder.toString(); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java index 40b301b8..6adb0336 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java @@ -41,8 +41,8 @@ public DefaultLoggerService() { this.loggers = new ConcurrentHashMap<>(); this.logger = new DefaultLogger("", this); this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); - this.listeners = ArrayFactory.newCopyOnModifyArray(LoggerListener.class); - this.writers = ArrayFactory.newCopyOnModifyArray(Writer.class); + this.listeners = ArrayFactory.copyOnModifyArray(LoggerListener.class); + this.writers = ArrayFactory.copyOnModifyArray(Writer.class); this.override = new int[LOGGER_LEVELS.length]; Arrays.fill(override, NOT_CONFIGURE); } diff --git a/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java b/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java index a05a1cb4..214f00c5 100644 --- a/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java +++ b/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java @@ -1,7 +1,7 @@ package javasabr.rlib.logger.impl; -import java.util.Collection; -import javasabr.rlib.common.util.array.ConcurrentArray; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.LockableMutableArray; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.api.LoggerListener; import javasabr.rlib.logger.api.LoggerManager; @@ -13,9 +13,17 @@ public class DefaultLoggerTest { - private static final ConcurrentArray WROTE_DATA = ConcurrentArray.ofType(String.class); + private static final LockableMutableArray WROTE_DATA = ArrayFactory + .stampedLockBasedArray(String.class); - private static final LoggerListener LOGGER_LISTENER = text -> WROTE_DATA.runInWriteLock(strings -> strings.add(text)); + private static final LoggerListener LOGGER_LISTENER = text -> { + long stamp = WROTE_DATA.writeLock(); + try { + WROTE_DATA.add(text); + } finally { + WROTE_DATA.writeUnlock(stamp); + } + }; @BeforeAll static void registerListener() { @@ -29,7 +37,12 @@ static void unregisterListener() { @BeforeEach void clearWroteData() { - WROTE_DATA.runInWriteLock(Collection::clear); + long stamp = WROTE_DATA.writeLock(); + try { + WROTE_DATA.clear(); + } finally { + WROTE_DATA.writeUnlock(stamp); + } } @Test 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 e6630945..9eb338d0 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 @@ -78,7 +78,7 @@ public AbstractConnection( this.network = network; this.isWriting = new AtomicBoolean(false); this.closed = new AtomicBoolean(false); - this.subscribers = ArrayFactory.newCopyOnModifyArray(BiConsumer.class); + this.subscribers = ArrayFactory.copyOnModifyArray(BiConsumer.class); this.remoteAddress = String.valueOf(NetworkUtils.getRemoteAddress(channel)); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java index 38634d3a..429310c8 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java @@ -1,10 +1,9 @@ package javasabr.rlib.network.impl; import java.nio.ByteBuffer; -import java.util.Collection; import java.util.function.Function; -import javasabr.rlib.common.util.array.ArrayFactory; -import javasabr.rlib.common.util.array.ConcurrentArray; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.LockableMutableArray; import javasabr.rlib.common.util.pools.Pool; import javasabr.rlib.common.util.pools.PoolFactory; import javasabr.rlib.logger.api.Logger; @@ -24,7 +23,7 @@ public class ReuseBufferAllocator implements BufferAllocator { protected final Pool readBufferPool; protected final Pool pendingBufferPool; protected final Pool writeBufferPool; - protected final ConcurrentArray byteBuffers; + protected final LockableMutableArray byteBuffers; protected final NetworkConfig config; @@ -33,7 +32,7 @@ public ReuseBufferAllocator(NetworkConfig config) { this.readBufferPool = PoolFactory.newConcurrentStampedLockPool(ByteBuffer.class); this.pendingBufferPool = PoolFactory.newConcurrentStampedLockPool(ByteBuffer.class); this.writeBufferPool = PoolFactory.newConcurrentStampedLockPool(ByteBuffer.class); - this.byteBuffers = ArrayFactory.newConcurrentStampedLockArray(ByteBuffer.class); + this.byteBuffers = ArrayFactory.stampedLockBasedArray(ByteBuffer.class); } @Override @@ -100,18 +99,29 @@ protected Function writeBufferFactory() { public ByteBuffer takeBuffer(int bufferSize) { // check of existing enough buffer for the size under read lock - var exist = byteBuffers.findAnyInReadLock(bufferSize, (required, buffer) -> required < buffer.capacity()); + ByteBuffer exist; + + long stamp = byteBuffers.readLock(); + try { + exist = byteBuffers + .iterations() + .findAny(bufferSize, (buffer, require) -> buffer.capacity() >= require); + } finally { + byteBuffers.readUnlock(stamp); + } // if we already possible have this buffer we need to take it under write lock if (exist != null) { - long stamp = byteBuffers.writeLock(); + stamp = byteBuffers.writeLock(); try { // re-find enough buffer again - exist = byteBuffers.findAny(bufferSize, (required, buffer) -> required < buffer.capacity()); + exist = byteBuffers + .iterations() + .findAny(bufferSize, (buffer, require) -> buffer.capacity() >= require); // take it from pool if exist - if (exist != null && byteBuffers.fastRemove(exist)) { + if (exist != null && byteBuffers.remove(exist)) { LOGGER.debug(exist, buffer -> "Reuse old buffer: " + buffer + " - (" + buffer.hashCode() + ")"); return exist; } @@ -151,7 +161,12 @@ public ReuseBufferAllocator putWriteBuffer(ByteBuffer buffer) { @Override public BufferAllocator putBuffer(ByteBuffer buffer) { LOGGER.debug(buffer, buf -> "Save used temp buffer: " + buf + " - (" + buf.hashCode() + ")"); - byteBuffers.runInWriteLock(buffer.clear(), Collection::add); + long stamp = byteBuffers.writeLock(); + try { + byteBuffers.add(buffer.clear()); + } finally { + byteBuffers.writeUnlock(stamp); + } return this; } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java index 69b5960d..56dfb6e1 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java @@ -111,7 +111,7 @@ public DefaultServerNetwork( this.group = uncheckedGet(executor, AsynchronousChannelGroup::withThreadPool); this.channel = uncheckedGet(group, AsynchronousServerSocketChannel::open); - this.subscribers = ArrayFactory.newCopyOnModifyArray(Consumer.class); + this.subscribers = ArrayFactory.copyOnModifyArray(Consumer.class); } @Override