From f9f881e59c78e3036cb3f956bc97cfc3197f620d Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sat, 24 Aug 2013 03:14:14 +0200 Subject: *Ringbuffer: Remove Ringbuffer.AllocEmptyArray interface to favor a more simple approach; Split 'grow' into 'growEmpty' and 'growFull' - java.lang.reflect.Array can instantiate an array w/ a given array-type and length - array-type is Class - We either deduct the array-type via array.getClass(), or pass it (ctor for empty Ringbuffer). - Split 'growBuffer(T[] newElements, int amount, ..)' into: - 'growEmptyBuffer(T[] newElements)' - 'growFullBuffer(int amount)' Allowing a more clean API w/ simpler semantics. --- src/java/com/jogamp/common/util/LFRingbuffer.java | 157 ++++++++++++--------- src/java/com/jogamp/common/util/Ringbuffer.java | 39 ++--- .../com/jogamp/common/util/SyncedRingbuffer.java | 137 +++++++++++------- 3 files changed, 189 insertions(+), 144 deletions(-) (limited to 'src/java/com/jogamp') diff --git a/src/java/com/jogamp/common/util/LFRingbuffer.java b/src/java/com/jogamp/common/util/LFRingbuffer.java index a6b441a..f704047 100644 --- a/src/java/com/jogamp/common/util/LFRingbuffer.java +++ b/src/java/com/jogamp/common/util/LFRingbuffer.java @@ -29,6 +29,7 @@ package com.jogamp.common.util; import java.io.PrintStream; +import java.lang.reflect.Array; /** * Simple implementation of {@link Ringbuffer}, @@ -51,7 +52,7 @@ import java.io.PrintStream; * * User needs to synchronize above methods w/ the lock-free * w/ {@link #get() get*(..)} and {@link #put(Object) put*(..)} methods, @@ -99,10 +100,7 @@ public class LFRingbuffer implements Ringbuffer { *
      *  Integer[] source = new Integer[10];
      *  // fill source with content ..
-     *  Ringbuffer rb = new LFRingbuffer(source, new Ringbuffer.AllocEmptyArray() {
-     *      public Integer[] newArray(int size) {
-     *          return new Integer[size];
-     *      } } );
+     *  Ringbuffer rb = new LFRingbuffer(source);
      * 
*

*

@@ -113,12 +111,12 @@ public class LFRingbuffer implements Ringbuffer { * and copy all elements from array copyFrom into the internal array. *

* @param copyFrom mandatory source array determining ring buffer's net {@link #capacity()} and initial content. - * @param allocEmptyArray implementation hook to allocate a new empty array of generic type T * @throws IllegalArgumentException if copyFrom is null */ - public LFRingbuffer(T[] copyFrom, AllocEmptyArray allocEmptyArray) throws IllegalArgumentException { + @SuppressWarnings("unchecked") + public LFRingbuffer(T[] copyFrom) throws IllegalArgumentException { capacityPlusOne = copyFrom.length + 1; - array = allocEmptyArray.newArray(capacityPlusOne); + array = (T[]) newArray(copyFrom.getClass(), capacityPlusOne); resetImpl(true, copyFrom); } @@ -127,10 +125,7 @@ public class LFRingbuffer implements Ringbuffer { *

* Example for a 10 element Integer array: *

-     *  Ringbuffer rb = new LFRingbuffer(10, new Ringbuffer.AllocEmptyArray() {
-     *      public Integer[] newArray(int size) {
-     *          return new Integer[size];
-     *      } } );
+     *  Ringbuffer rb = new LFRingbuffer(10, Integer[].class);
      * 
*

*

@@ -139,15 +134,15 @@ public class LFRingbuffer implements Ringbuffer { *

* Implementation will allocate an internal array of size capacity plus one. *

+ * @param arrayType the array type of the created empty internal array. * @param capacity the initial net capacity of the ring buffer - * @param allocEmptyArray implementation hook to allocate a new empty array of generic type T */ - public LFRingbuffer(int capacity, AllocEmptyArray allocEmptyArray) { + public LFRingbuffer(Class arrayType, int capacity) { capacityPlusOne = capacity+1; - array = allocEmptyArray.newArray(capacityPlusOne); - resetImpl(false, null); + array = (T[]) newArray(arrayType, capacityPlusOne); + resetImpl(false, null /* empty, nothing to copy */ ); } - + @Override public final T[] getInternalArray() { return array; } @@ -177,7 +172,7 @@ public class LFRingbuffer implements Ringbuffer { } System.arraycopy(copyFrom, 0, array, 0, copyFrom.length); array[capacityPlusOne-1] = null; // null 'plus-one' field! - } else if( full ) { + } else if ( full ) { throw new IllegalArgumentException("copyFrom array is null"); } readPos = capacityPlusOne - 1; @@ -337,65 +332,95 @@ public class LFRingbuffer implements Ringbuffer { } } } - + @Override - public void growBuffer(T[] newElements, int amount, AllocEmptyArray allocEmptyArray) throws IllegalStateException, IllegalArgumentException { + public final void growEmptyBuffer(final T[] newElements) throws IllegalStateException, IllegalArgumentException { synchronized( syncGlobal ) { - final boolean isFull = capacityPlusOne - 1 == size; - final boolean isEmpty = 0 == size; - if( !isFull && !isEmpty ) { - throw new IllegalStateException("Buffer neither full nor empty: "+this); + if( null == newElements ) { + throw new IllegalArgumentException("newElements is null"); } - if( isEmpty ) { - if( readPos != writePos ) { - throw new InternalError("R/W pos not equal at empty: "+this); - } - } else /* isFull */ { - final int wp1 = ( writePos + 1 ) % capacityPlusOne; - if( wp1 != readPos ) { - throw new InternalError("R != W+1 pos at full: "+this); - } + @SuppressWarnings("unchecked") + final Class arrayTypeInternal = (Class) array.getClass(); + @SuppressWarnings("unchecked") + final Class arrayTypeNew = (Class) newElements.getClass(); + if( arrayTypeInternal != arrayTypeNew ) { + throw new IllegalArgumentException("newElements array-type mismatch, internal "+arrayTypeInternal+", newElements "+arrayTypeNew); + } + if( 0 != size ) { + throw new IllegalStateException("Buffer is not empty: "+this); } - if( null != newElements && amount < newElements.length ) { - throw new IllegalArgumentException("amount "+amount+" < newElements "+newElements.length); + if( readPos != writePos ) { + throw new InternalError("R/W pos not equal: "+this); } - final int newCapacity = capacityPlusOne + amount; + if( readPos != writePos ) { + throw new InternalError("R/W pos not equal at empty: "+this); + } + + final int growAmount = newElements.length; + final int newCapacity = capacityPlusOne + growAmount; final T[] oldArray = array; - final T[] newArray = allocEmptyArray.newArray(newCapacity); + final T[] newArray = (T[]) newArray(arrayTypeInternal, newCapacity); + + // writePos == readPos + writePos += growAmount; // warp writePos to the end of the new data location - if( isFull ) { - // writePos == readPos - 1 - readPos = ( writePos + 1 + amount ) % newCapacity; // warp readPos to the end of the new data location - - if(writePos >= 0) { - System.arraycopy(oldArray, 0, newArray, 0, writePos+1); - } - if( null != newElements && newElements.length > 0 ) { - System.arraycopy(newElements, 0, newArray, writePos+1, newElements.length); - } - final int tail = capacityPlusOne-1-writePos; - if( tail > 0 ) { - System.arraycopy(oldArray, writePos+1, newArray, readPos, tail); - } - } else /* if ( isEmpty ) */ { - // writePos == readPos - writePos += amount; // warp writePos to the end of the new data location - - if( readPos >= 0 ) { - System.arraycopy(oldArray, 0, newArray, 0, readPos+1); - } - if( null != newElements && newElements.length > 0 ) { - System.arraycopy(newElements, 0, newArray, readPos+1, newElements.length); - } - final int tail = capacityPlusOne-1-readPos; - if( tail > 0 ) { - System.arraycopy(oldArray, readPos+1, newArray, writePos+1, tail); - } - size = amount; + if( readPos >= 0 ) { + System.arraycopy(oldArray, 0, newArray, 0, readPos+1); + } + if( growAmount > 0 ) { + System.arraycopy(newElements, 0, newArray, readPos+1, growAmount); } + final int tail = capacityPlusOne-1-readPos; + if( tail > 0 ) { + System.arraycopy(oldArray, readPos+1, newArray, writePos+1, tail); + } + size = growAmount; capacityPlusOne = newCapacity; array = newArray; } } + + @Override + public final void growFullBuffer(final int growAmount) throws IllegalStateException, IllegalArgumentException { + synchronized ( syncGlobal ) { + if( 0 > growAmount ) { + throw new IllegalArgumentException("amount "+growAmount+" < 0 "); + } + if( capacityPlusOne-1 != size ) { + throw new IllegalStateException("Buffer is not full: "+this); + } + final int wp1 = ( writePos + 1 ) % capacityPlusOne; + if( wp1 != readPos ) { + throw new InternalError("R != W+1 pos at full: "+this); + } + @SuppressWarnings("unchecked") + final Class arrayTypeInternal = (Class) array.getClass(); + + final int newCapacity = capacityPlusOne + growAmount; + final T[] oldArray = array; + final T[] newArray = (T[]) newArray(arrayTypeInternal, newCapacity); + + // writePos == readPos - 1 + readPos = ( writePos + 1 + growAmount ) % newCapacity; // warp readPos to the end of the new data location + + if(writePos >= 0) { + System.arraycopy(oldArray, 0, newArray, 0, writePos+1); + } + final int tail = capacityPlusOne-1-writePos; + if( tail > 0 ) { + System.arraycopy(oldArray, writePos+1, newArray, readPos, tail); + } + + capacityPlusOne = newCapacity; + array = newArray; + } + } + + @SuppressWarnings("unchecked") + private static T[] newArray(Class arrayType, int length) { + return ((Object)arrayType == (Object)Object[].class) + ? (T[]) new Object[length] + : (T[]) Array.newInstance(arrayType.getComponentType(), length); + } } \ No newline at end of file diff --git a/src/java/com/jogamp/common/util/Ringbuffer.java b/src/java/com/jogamp/common/util/Ringbuffer.java index 733a235..e524768 100644 --- a/src/java/com/jogamp/common/util/Ringbuffer.java +++ b/src/java/com/jogamp/common/util/Ringbuffer.java @@ -44,18 +44,6 @@ import java.io.PrintStream; */ public interface Ringbuffer { - /** - * Implementation hook for {@link #growBuffer(Object[], int, AllocEmptyArray)} - * to pass an implementation of {@link #newArray(int)}. - * @param type of array - */ - public static interface AllocEmptyArray { - /** - * Returns a new allocated empty array of generic type T with given size. - */ - public T[] newArray(int size); - } - /** Returns a short string representation incl. size/capacity and internal r/w index (impl. dependent). */ public String toString(); @@ -190,25 +178,28 @@ public interface Ringbuffer { public void waitForFreeSlots(int count) throws InterruptedException; /** - * Grows a full or empty ring buffer, increasing it's capacity about the amount. + * Grows an empty ring buffer, increasing it's capacity about the amount. *

* Growing an empty ring buffer increases it's size about the amount, i.e. renders it not empty. * The new elements are inserted at the read position, able to be read out via {@link #get()} etc. *

+ * + * @param newElements array of new full elements the empty buffer shall grow about. + * @throws IllegalStateException if buffer is not empty + * @throws IllegalArgumentException if newElements is null + */ + public void growEmptyBuffer(T[] newElements) throws IllegalStateException, IllegalArgumentException; + + /** + * Grows a full ring buffer, increasing it's capacity about the amount. *

* Growing a full ring buffer leaves the size intact, i.e. renders it not full. - * The new elements are inserted at the write position, able to be written to via {@link #put(Object)} etc. + * New null elements are inserted at the write position, able to be written to via {@link #put(Object)} etc. *

- * - * @param newElements array of new empty elements the buffer shall grow about, maybe null. - * If not null, array size must be <= amount * @param amount the amount of elements the buffer shall grow about - * @param allocEmptyArray implementation hook to allocate a new empty array of generic type T - * @throws IllegalStateException if buffer is neither full nor empty - * @throws IllegalArgumentException if newElements is given but is > amount + * + * @throws IllegalStateException if buffer is not full + * @throws IllegalArgumentException if amount is < 0 */ - public void growBuffer(T[] newElements, int amount, - AllocEmptyArray allocEmptyArray) throws IllegalStateException, - IllegalArgumentException; - + public void growFullBuffer(int amount) throws IllegalStateException, IllegalArgumentException; } \ No newline at end of file diff --git a/src/java/com/jogamp/common/util/SyncedRingbuffer.java b/src/java/com/jogamp/common/util/SyncedRingbuffer.java index b8ddbd6..747629b 100644 --- a/src/java/com/jogamp/common/util/SyncedRingbuffer.java +++ b/src/java/com/jogamp/common/util/SyncedRingbuffer.java @@ -29,6 +29,7 @@ package com.jogamp.common.util; import java.io.PrintStream; +import java.lang.reflect.Array; /** * Simple synchronized implementation of {@link Ringbuffer}. @@ -77,10 +78,7 @@ public class SyncedRingbuffer implements Ringbuffer { *
      *  Integer[] source = new Integer[10];
      *  // fill source with content ..
-     *  Ringbuffer rb = new SyncedRingbuffer(source, new Ringbuffer.AllocEmptyArray() {
-     *      public Integer[] newArray(int size) {
-     *          return new Integer[size];
-     *      } } );
+     *  Ringbuffer rb = new SyncedRingbuffer(source);
      * 
*

*

@@ -91,12 +89,12 @@ public class SyncedRingbuffer implements Ringbuffer { * and copy all elements from array copyFrom into the internal array. *

* @param copyFrom mandatory source array determining ring buffer's net {@link #capacity()} and initial content. - * @param allocEmptyArray implementation hook to allocate a new empty array of generic type T * @throws IllegalArgumentException if copyFrom is null */ - public SyncedRingbuffer(T[] copyFrom, AllocEmptyArray allocEmptyArray) throws IllegalArgumentException { + @SuppressWarnings("unchecked") + public SyncedRingbuffer(T[] copyFrom) throws IllegalArgumentException { capacity = copyFrom.length; - array = allocEmptyArray.newArray(capacity); + array = (T[]) newArray(copyFrom.getClass(), capacity); resetImpl(true, copyFrom); } @@ -105,10 +103,7 @@ public class SyncedRingbuffer implements Ringbuffer { *

* Example for a 10 element Integer array: *

-     *  Ringbuffer rb = new SyncedRingbuffer(10, new Ringbuffer.AllocEmptyArray() {
-     *      public Integer[] newArray(int size) {
-     *          return new Integer[size];
-     *      } } );
+     *  Ringbuffer rb = new SyncedRingbuffer(10, Integer[].class);
      * 
*

*

@@ -117,13 +112,13 @@ public class SyncedRingbuffer implements Ringbuffer { *

* Implementation will allocate an internal array of size capacity. *

+ * @param arrayType the array type of the created empty internal array. * @param capacity the initial net capacity of the ring buffer - * @param allocEmptyArray implementation hook to allocate a new empty array of generic type T */ - public SyncedRingbuffer(int capacity, AllocEmptyArray allocEmptyArray) { + public SyncedRingbuffer(Class arrayType, int capacity) { this.capacity = capacity; - this.array = allocEmptyArray.newArray(capacity); - resetImpl(false, null); + this.array = (T[]) newArray(arrayType, capacity); + resetImpl(false, null /* empty, nothing to copy */ ); } @Override @@ -160,7 +155,7 @@ public class SyncedRingbuffer implements Ringbuffer { throw new IllegalArgumentException("copyFrom array length "+copyFrom.length+" != capacity "+this); } System.arraycopy(copyFrom, 0, array, 0, copyFrom.length); - } else if( full ) { + } else if ( full ) { throw new IllegalArgumentException("copyFrom array is null"); } readPos = 0; @@ -325,57 +320,91 @@ public class SyncedRingbuffer implements Ringbuffer { } } + @Override - public void growBuffer(T[] newElements, int amount, AllocEmptyArray allocEmptyArray) throws IllegalStateException, IllegalArgumentException { + public final void growEmptyBuffer(final T[] newElements) throws IllegalStateException, IllegalArgumentException { synchronized ( syncGlobal ) { - final boolean isFull = capacity == size; - final boolean isEmpty = 0 == size; - if( !isFull && !isEmpty ) { - throw new IllegalStateException("Buffer neither full nor empty: "+this); + if( null == newElements ) { + throw new IllegalArgumentException("newElements is null"); + } + @SuppressWarnings("unchecked") + final Class arrayTypeInternal = (Class) array.getClass(); + @SuppressWarnings("unchecked") + final Class arrayTypeNew = (Class) newElements.getClass(); + if( arrayTypeInternal != arrayTypeNew ) { + throw new IllegalArgumentException("newElements array-type mismatch, internal "+arrayTypeInternal+", newElements "+arrayTypeNew); + } + if( 0 != size ) { + throw new IllegalStateException("Buffer is not empty: "+this); } if( readPos != writePos ) { throw new InternalError("R/W pos not equal: "+this); } - if( null != newElements && amount < newElements.length ) { - throw new IllegalArgumentException("amount "+amount+" < newElements "+newElements.length); + + final int growAmount = newElements.length; + final int newCapacity = capacity + growAmount; + final T[] oldArray = array; + final T[] newArray = (T[]) newArray(arrayTypeInternal, newCapacity); + + // writePos == readPos + writePos += growAmount; // warp writePos to the end of the new data location + + if( readPos > 0 ) { + System.arraycopy(oldArray, 0, newArray, 0, readPos); + } + if( growAmount > 0 ) { + System.arraycopy(newElements, 0, newArray, readPos, growAmount); + } + final int tail = capacity-readPos; + if( tail > 0 ) { + System.arraycopy(oldArray, readPos, newArray, writePos, tail); + } + size = growAmount; + + capacity = newCapacity; + array = newArray; + } + } + + @Override + public final void growFullBuffer(final int growAmount) throws IllegalStateException, IllegalArgumentException { + synchronized ( syncGlobal ) { + if( 0 > growAmount ) { + throw new IllegalArgumentException("amount "+growAmount+" < 0 "); + } + if( capacity != size ) { + throw new IllegalStateException("Buffer is not full: "+this); + } + if( readPos != writePos ) { + throw new InternalError("R/W pos not equal: "+this); } - final int newCapacity = capacity + amount; + @SuppressWarnings("unchecked") + final Class arrayTypeInternal = (Class) array.getClass(); + + final int newCapacity = capacity + growAmount; final T[] oldArray = array; - final T[] newArray = allocEmptyArray.newArray(newCapacity); + final T[] newArray = (T[]) newArray(arrayTypeInternal, newCapacity); - if( isFull ) { - // writePos == readPos - readPos += amount; // warp readPos to the end of the new data location - - if(writePos > 0) { - System.arraycopy(oldArray, 0, newArray, 0, writePos); - } - if( null != newElements && newElements.length > 0 ) { - System.arraycopy(newElements, 0, newArray, writePos, newElements.length); - } - final int tail = capacity-writePos; - if( tail > 0 ) { - System.arraycopy(oldArray, writePos, newArray, readPos, tail); - } - } else /* if ( isEmpty ) */ { - // writePos == readPos - writePos += amount; // warp writePos to the end of the new data location - - if( readPos > 0 ) { - System.arraycopy(oldArray, 0, newArray, 0, readPos); - } - if( null != newElements && newElements.length > 0 ) { - System.arraycopy(newElements, 0, newArray, readPos, newElements.length); - } - final int tail = capacity-readPos; - if( tail > 0 ) { - System.arraycopy(oldArray, readPos, newArray, writePos, tail); - } - size = amount; + // writePos == readPos + readPos += growAmount; // warp readPos to the end of the new data location + + if(writePos > 0) { + System.arraycopy(oldArray, 0, newArray, 0, writePos); + } + final int tail = capacity-writePos; + if( tail > 0 ) { + System.arraycopy(oldArray, writePos, newArray, readPos, tail); } capacity = newCapacity; array = newArray; } } + + @SuppressWarnings("unchecked") + private static T[] newArray(Class arrayType, int length) { + return ((Object)arrayType == (Object)Object[].class) + ? (T[]) new Object[length] + : (T[]) Array.newInstance(arrayType.getComponentType(), length); + } } -- cgit v1.2.3