From f9f881e59c78e3036cb3f956bc97cfc3197f620d Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sat, 24 Aug 2013 03:14:14 +0200
Subject: *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.
---
 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/common')

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);
+    }
 }
-- 
cgit v1.2.3