diff options
author | Sven Gothel <[email protected]> | 2013-08-24 03:14:14 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-08-24 03:14:14 +0200 |
commit | f9f881e59c78e3036cb3f956bc97cfc3197f620d (patch) | |
tree | 9362ac0e063eb48ed7c563d0ad83953bc828ed9b /src/java/com/jogamp/common/util | |
parent | 30475c6bbeb9a5d48899b281ead8bb305679028d (diff) |
*Ringbuffer: Remove Ringbuffer<T>.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<? extends T[]>
- 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.
Diffstat (limited to 'src/java/com/jogamp/common/util')
-rw-r--r-- | src/java/com/jogamp/common/util/LFRingbuffer.java | 157 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/Ringbuffer.java | 39 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/SyncedRingbuffer.java | 137 |
3 files changed, 189 insertions, 144 deletions
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; * <ul> * <li>{@link #resetFull(Object[])}</li> * <li>{@link #clear()}</li> - * <li>{@link #growBuffer(Object[], int, AllocEmptyArray)}</li> + * <li>{@link #growEmptyBuffer(Object[])}</li> * </ul> * 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<T> implements Ringbuffer<T> { * <pre> * Integer[] source = new Integer[10]; * // fill source with content .. - * Ringbuffer<Integer> rb = new LFRingbuffer<Integer>(source, new Ringbuffer.AllocEmptyArray<Integer>() { - * public Integer[] newArray(int size) { - * return new Integer[size]; - * } } ); + * Ringbuffer<Integer> rb = new LFRingbuffer<Integer>(source); * </pre> * </p> * <p> @@ -113,12 +111,12 @@ public class LFRingbuffer<T> implements Ringbuffer<T> { * and copy all elements from array <code>copyFrom</code> into the internal array. * </p> * @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 <code>copyFrom</code> is <code>null</code> */ - public LFRingbuffer(T[] copyFrom, AllocEmptyArray<T> 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<T> implements Ringbuffer<T> { * <p> * Example for a 10 element Integer array: * <pre> - * Ringbuffer<Integer> rb = new LFRingbuffer<Integer>(10, new Ringbuffer.AllocEmptyArray<Integer>() { - * public Integer[] newArray(int size) { - * return new Integer[size]; - * } } ); + * Ringbuffer<Integer> rb = new LFRingbuffer<Integer>(10, Integer[].class); * </pre> * </p> * <p> @@ -139,15 +134,15 @@ public class LFRingbuffer<T> implements Ringbuffer<T> { * <p> * Implementation will allocate an internal array of size <code>capacity</code> <i>plus one</i>. * </p> + * @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<T> allocEmptyArray) { + public LFRingbuffer(Class<? extends T[]> 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<T> implements Ringbuffer<T> { } 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<T> implements Ringbuffer<T> { } } } - + @Override - public void growBuffer(T[] newElements, int amount, AllocEmptyArray<T> 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<? extends T[]> arrayTypeInternal = (Class<? extends T[]>) array.getClass(); + @SuppressWarnings("unchecked") + final Class<? extends T[]> arrayTypeNew = (Class<? extends T[]>) 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<? extends T[]> arrayTypeInternal = (Class<? extends T[]>) 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> T[] newArray(Class<? extends T[]> 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<T> { - /** - * Implementation hook for {@link #growBuffer(Object[], int, AllocEmptyArray)} - * to pass an implementation of {@link #newArray(int)}. - * @param <T> type of array - */ - public static interface AllocEmptyArray<T> { - /** - * 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<T> { 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. * <p> * 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. * </p> + * + * @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. * <p> * 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 <code>null</code> elements are inserted at the write position, able to be written to via {@link #put(Object)} etc. * </p> - * - * @param newElements array of new empty elements the buffer shall grow about, maybe <code>null</code>. - * If not <code>null</code>, array size must be <= <code>amount</code> * @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<T> 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<T> implements Ringbuffer<T> { * <pre> * Integer[] source = new Integer[10]; * // fill source with content .. - * Ringbuffer<Integer> rb = new SyncedRingbuffer<Integer>(source, new Ringbuffer.AllocEmptyArray<Integer>() { - * public Integer[] newArray(int size) { - * return new Integer[size]; - * } } ); + * Ringbuffer<Integer> rb = new SyncedRingbuffer<Integer>(source); * </pre> * </p> * <p> @@ -91,12 +89,12 @@ public class SyncedRingbuffer<T> implements Ringbuffer<T> { * and copy all elements from array <code>copyFrom</code> into the internal array. * </p> * @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 <code>copyFrom</code> is <code>null</code> */ - public SyncedRingbuffer(T[] copyFrom, AllocEmptyArray<T> 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<T> implements Ringbuffer<T> { * <p> * Example for a 10 element Integer array: * <pre> - * Ringbuffer<Integer> rb = new SyncedRingbuffer<Integer>(10, new Ringbuffer.AllocEmptyArray<Integer>() { - * public Integer[] newArray(int size) { - * return new Integer[size]; - * } } ); + * Ringbuffer<Integer> rb = new SyncedRingbuffer<Integer>(10, Integer[].class); * </pre> * </p> * <p> @@ -117,13 +112,13 @@ public class SyncedRingbuffer<T> implements Ringbuffer<T> { * <p> * Implementation will allocate an internal array of size <code>capacity</code>. * </p> + * @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<T> allocEmptyArray) { + public SyncedRingbuffer(Class<? extends T[]> 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<T> implements Ringbuffer<T> { 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<T> implements Ringbuffer<T> { } } + @Override - public void growBuffer(T[] newElements, int amount, AllocEmptyArray<T> 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<? extends T[]> arrayTypeInternal = (Class<? extends T[]>) array.getClass(); + @SuppressWarnings("unchecked") + final Class<? extends T[]> arrayTypeNew = (Class<? extends T[]>) 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<? extends T[]> arrayTypeInternal = (Class<? extends T[]>) 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> T[] newArray(Class<? extends T[]> arrayType, int length) { + return ((Object)arrayType == (Object)Object[].class) + ? (T[]) new Object[length] + : (T[]) Array.newInstance(arrayType.getComponentType(), length); + } } |