Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.util;
     33 
     34 import com.jme3.math.ColorRGBA;
     35 import com.jme3.math.Quaternion;
     36 import com.jme3.math.Vector2f;
     37 import com.jme3.math.Vector3f;
     38 import java.lang.reflect.InvocationTargetException;
     39 import java.lang.reflect.Method;
     40 import java.nio.*;
     41 import java.util.ArrayList;
     42 import java.util.Collections;
     43 import java.util.Map;
     44 import java.util.WeakHashMap;
     45 import java.util.concurrent.atomic.AtomicBoolean;
     46 import java.util.logging.Level;
     47 import java.util.logging.Logger;
     48 
     49 /**
     50  * <code>BufferUtils</code> is a helper class for generating nio buffers from
     51  * jME data classes such as Vectors and ColorRGBA.
     52  *
     53  * @author Joshua Slack
     54  * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
     55  */
     56 public final class BufferUtils {
     57 
     58     private static final Map<Buffer, Object> trackingHash = Collections.synchronizedMap(new WeakHashMap<Buffer, Object>());
     59     private static final Object ref = new Object();
     60 
     61     // Note: a WeakHashMap is really bad here since the hashCode() and
     62     //       equals() behavior of buffers will vary based on their contents.
     63     //       As it stands, put()'ing an empty buffer will wipe out the last
     64     //       empty buffer with the same size.  So any tracked memory calculations
     65     //       could be lying.
     66     //       Besides, the hashmap behavior isn't even being used here and
     67     //       yet the expense is still incurred.  For example, a newly allocated
     68     //       10,000 byte buffer will iterate through the whole buffer of 0's
     69     //       to calculate the hashCode and then potentially do it again to
     70     //       calculate the equals()... which by the way is guaranteed for
     71     //       every empty buffer of an existing size since they will always produce
     72     //       the same hashCode().
     73     //       It would be better to just keep a straight list of weak references
     74     //       and clean out the dead every time a new buffer is allocated.
     75     //       WeakHashMap is doing that anyway... so there is no extra expense
     76     //       incurred.
     77     //       Recommend a ConcurrentLinkedQueue of WeakReferences since it
     78     //       supports the threading semantics required with little extra overhead.
     79     private static final boolean trackDirectMemory = false;
     80 
     81     /**
     82      * Creates a clone of the given buffer. The clone's capacity is
     83      * equal to the given buffer's limit.
     84      *
     85      * @param buf The buffer to clone
     86      * @return The cloned buffer
     87      */
     88     public static Buffer clone(Buffer buf) {
     89         if (buf instanceof FloatBuffer) {
     90             return clone((FloatBuffer) buf);
     91         } else if (buf instanceof ShortBuffer) {
     92             return clone((ShortBuffer) buf);
     93         } else if (buf instanceof ByteBuffer) {
     94             return clone((ByteBuffer) buf);
     95         } else if (buf instanceof IntBuffer) {
     96             return clone((IntBuffer) buf);
     97         } else if (buf instanceof DoubleBuffer) {
     98             return clone((DoubleBuffer) buf);
     99         } else {
    100             throw new UnsupportedOperationException();
    101         }
    102     }
    103 
    104     private static void onBufferAllocated(Buffer buffer){
    105         /*
    106         StackTraceElement[] stackTrace = new Throwable().getStackTrace();
    107         int initialIndex = 0;
    108 
    109         for (int i = 0; i < stackTrace.length; i++){
    110             if (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){
    111                 initialIndex = i;
    112                 break;
    113             }
    114         }
    115 
    116         int allocated = buffer.capacity();
    117         int size = 0;
    118 
    119         if (buffer instanceof FloatBuffer){
    120             size = 4;
    121         }else if (buffer instanceof ShortBuffer){
    122             size = 2;
    123         }else if (buffer instanceof ByteBuffer){
    124             size = 1;
    125         }else if (buffer instanceof IntBuffer){
    126             size = 4;
    127         }else if (buffer instanceof DoubleBuffer){
    128             size = 8;
    129         }
    130 
    131         allocated *= size;
    132 
    133         for (int i = initialIndex; i < stackTrace.length; i++){
    134             StackTraceElement element = stackTrace[i];
    135             if (element.getClassName().startsWith("java")){
    136                 break;
    137             }
    138 
    139             try {
    140                 Class clazz = Class.forName(element.getClassName());
    141                 if (i == initialIndex){
    142                     System.out.println(clazz.getSimpleName()+"."+element.getMethodName()+"():" + element.getLineNumber() + " allocated " + allocated);
    143                 }else{
    144                     System.out.println(" at " + clazz.getSimpleName()+"."+element.getMethodName()+"()");
    145                 }
    146             } catch (ClassNotFoundException ex) {
    147             }
    148         }*/
    149 
    150         if (trackDirectMemory){
    151             trackingHash.put(buffer, ref);
    152         }
    153     }
    154 
    155     /**
    156      * Generate a new FloatBuffer using the given array of Vector3f objects.
    157      * The FloatBuffer will be 3 * data.length long and contain the vector data
    158      * as data[0].x, data[0].y, data[0].z, data[1].x... etc.
    159      *
    160      * @param data array of Vector3f objects to place into a new FloatBuffer
    161      */
    162     public static FloatBuffer createFloatBuffer(Vector3f... data) {
    163         if (data == null) {
    164             return null;
    165         }
    166         FloatBuffer buff = createFloatBuffer(3 * data.length);
    167         for (int x = 0; x < data.length; x++) {
    168             if (data[x] != null) {
    169                 buff.put(data[x].x).put(data[x].y).put(data[x].z);
    170             } else {
    171                 buff.put(0).put(0).put(0);
    172             }
    173         }
    174         buff.flip();
    175         return buff;
    176     }
    177 
    178     /**
    179      * Generate a new FloatBuffer using the given array of Quaternion objects.
    180      * The FloatBuffer will be 4 * data.length long and contain the vector data.
    181      *
    182      * @param data array of Quaternion objects to place into a new FloatBuffer
    183      */
    184     public static FloatBuffer createFloatBuffer(Quaternion... data) {
    185         if (data == null) {
    186             return null;
    187         }
    188         FloatBuffer buff = createFloatBuffer(4 * data.length);
    189         for (int x = 0; x < data.length; x++) {
    190             if (data[x] != null) {
    191                 buff.put(data[x].getX()).put(data[x].getY()).put(data[x].getZ()).put(data[x].getW());
    192             } else {
    193                 buff.put(0).put(0).put(0);
    194             }
    195         }
    196         buff.flip();
    197         return buff;
    198     }
    199 
    200     /**
    201      * Generate a new FloatBuffer using the given array of float primitives.
    202      * @param data array of float primitives to place into a new FloatBuffer
    203      */
    204     public static FloatBuffer createFloatBuffer(float... data) {
    205         if (data == null) {
    206             return null;
    207         }
    208         FloatBuffer buff = createFloatBuffer(data.length);
    209         buff.clear();
    210         buff.put(data);
    211         buff.flip();
    212         return buff;
    213     }
    214 
    215     /**
    216      * Create a new FloatBuffer of an appropriate size to hold the specified
    217      * number of Vector3f object data.
    218      *
    219      * @param vertices
    220      *            number of vertices that need to be held by the newly created
    221      *            buffer
    222      * @return the requested new FloatBuffer
    223      */
    224     public static FloatBuffer createVector3Buffer(int vertices) {
    225         FloatBuffer vBuff = createFloatBuffer(3 * vertices);
    226         return vBuff;
    227     }
    228 
    229     /**
    230      * Create a new FloatBuffer of an appropriate size to hold the specified
    231      * number of Vector3f object data only if the given buffer if not already
    232      * the right size.
    233      *
    234      * @param buf
    235      *            the buffer to first check and rewind
    236      * @param vertices
    237      *            number of vertices that need to be held by the newly created
    238      *            buffer
    239      * @return the requested new FloatBuffer
    240      */
    241     public static FloatBuffer createVector3Buffer(FloatBuffer buf, int vertices) {
    242         if (buf != null && buf.limit() == 3 * vertices) {
    243             buf.rewind();
    244             return buf;
    245         }
    246 
    247         return createFloatBuffer(3 * vertices);
    248     }
    249 
    250     /**
    251      * Sets the data contained in the given color into the FloatBuffer at the
    252      * specified index.
    253      *
    254      * @param color
    255      *            the data to insert
    256      * @param buf
    257      *            the buffer to insert into
    258      * @param index
    259      *            the postion to place the data; in terms of colors not floats
    260      */
    261     public static void setInBuffer(ColorRGBA color, FloatBuffer buf,
    262             int index) {
    263         buf.position(index * 4);
    264         buf.put(color.r);
    265         buf.put(color.g);
    266         buf.put(color.b);
    267         buf.put(color.a);
    268     }
    269 
    270     /**
    271      * Sets the data contained in the given quaternion into the FloatBuffer at the
    272      * specified index.
    273      *
    274      * @param quat
    275      *            the {@link Quaternion} to insert
    276      * @param buf
    277      *            the buffer to insert into
    278      * @param index
    279      *            the postion to place the data; in terms of quaternions not floats
    280      */
    281     public static void setInBuffer(Quaternion quat, FloatBuffer buf,
    282             int index) {
    283         buf.position(index * 4);
    284         buf.put(quat.getX());
    285         buf.put(quat.getY());
    286         buf.put(quat.getZ());
    287         buf.put(quat.getW());
    288     }
    289 
    290     /**
    291      * Sets the data contained in the given Vector3F into the FloatBuffer at the
    292      * specified index.
    293      *
    294      * @param vector
    295      *            the data to insert
    296      * @param buf
    297      *            the buffer to insert into
    298      * @param index
    299      *            the postion to place the data; in terms of vectors not floats
    300      */
    301     public static void setInBuffer(Vector3f vector, FloatBuffer buf, int index) {
    302         if (buf == null) {
    303             return;
    304         }
    305         if (vector == null) {
    306             buf.put(index * 3, 0);
    307             buf.put((index * 3) + 1, 0);
    308             buf.put((index * 3) + 2, 0);
    309         } else {
    310             buf.put(index * 3, vector.x);
    311             buf.put((index * 3) + 1, vector.y);
    312             buf.put((index * 3) + 2, vector.z);
    313         }
    314     }
    315 
    316     /**
    317      * Updates the values of the given vector from the specified buffer at the
    318      * index provided.
    319      *
    320      * @param vector
    321      *            the vector to set data on
    322      * @param buf
    323      *            the buffer to read from
    324      * @param index
    325      *            the position (in terms of vectors, not floats) to read from
    326      *            the buf
    327      */
    328     public static void populateFromBuffer(Vector3f vector, FloatBuffer buf, int index) {
    329         vector.x = buf.get(index * 3);
    330         vector.y = buf.get(index * 3 + 1);
    331         vector.z = buf.get(index * 3 + 2);
    332     }
    333 
    334     /**
    335      * Generates a Vector3f array from the given FloatBuffer.
    336      *
    337      * @param buff
    338      *            the FloatBuffer to read from
    339      * @return a newly generated array of Vector3f objects
    340      */
    341     public static Vector3f[] getVector3Array(FloatBuffer buff) {
    342         buff.clear();
    343         Vector3f[] verts = new Vector3f[buff.limit() / 3];
    344         for (int x = 0; x < verts.length; x++) {
    345             Vector3f v = new Vector3f(buff.get(), buff.get(), buff.get());
    346             verts[x] = v;
    347         }
    348         return verts;
    349     }
    350 
    351     /**
    352      * Copies a Vector3f from one position in the buffer to another. The index
    353      * values are in terms of vector number (eg, vector number 0 is postions 0-2
    354      * in the FloatBuffer.)
    355      *
    356      * @param buf
    357      *            the buffer to copy from/to
    358      * @param fromPos
    359      *            the index of the vector to copy
    360      * @param toPos
    361      *            the index to copy the vector to
    362      */
    363     public static void copyInternalVector3(FloatBuffer buf, int fromPos, int toPos) {
    364         copyInternal(buf, fromPos * 3, toPos * 3, 3);
    365     }
    366 
    367     /**
    368      * Normalize a Vector3f in-buffer.
    369      *
    370      * @param buf
    371      *            the buffer to find the Vector3f within
    372      * @param index
    373      *            the position (in terms of vectors, not floats) of the vector
    374      *            to normalize
    375      */
    376     public static void normalizeVector3(FloatBuffer buf, int index) {
    377         TempVars vars = TempVars.get();
    378         Vector3f tempVec3 = vars.vect1;
    379         populateFromBuffer(tempVec3, buf, index);
    380         tempVec3.normalizeLocal();
    381         setInBuffer(tempVec3, buf, index);
    382         vars.release();
    383     }
    384 
    385     /**
    386      * Add to a Vector3f in-buffer.
    387      *
    388      * @param toAdd
    389      *            the vector to add from
    390      * @param buf
    391      *            the buffer to find the Vector3f within
    392      * @param index
    393      *            the position (in terms of vectors, not floats) of the vector
    394      *            to add to
    395      */
    396     public static void addInBuffer(Vector3f toAdd, FloatBuffer buf, int index) {
    397         TempVars vars = TempVars.get();
    398         Vector3f tempVec3 = vars.vect1;
    399         populateFromBuffer(tempVec3, buf, index);
    400         tempVec3.addLocal(toAdd);
    401         setInBuffer(tempVec3, buf, index);
    402         vars.release();
    403     }
    404 
    405     /**
    406      * Multiply and store a Vector3f in-buffer.
    407      *
    408      * @param toMult
    409      *            the vector to multiply against
    410      * @param buf
    411      *            the buffer to find the Vector3f within
    412      * @param index
    413      *            the position (in terms of vectors, not floats) of the vector
    414      *            to multiply
    415      */
    416     public static void multInBuffer(Vector3f toMult, FloatBuffer buf, int index) {
    417         TempVars vars = TempVars.get();
    418         Vector3f tempVec3 = vars.vect1;
    419         populateFromBuffer(tempVec3, buf, index);
    420         tempVec3.multLocal(toMult);
    421         setInBuffer(tempVec3, buf, index);
    422         vars.release();
    423     }
    424 
    425     /**
    426      * Checks to see if the given Vector3f is equals to the data stored in the
    427      * buffer at the given data index.
    428      *
    429      * @param check
    430      *            the vector to check against - null will return false.
    431      * @param buf
    432      *            the buffer to compare data with
    433      * @param index
    434      *            the position (in terms of vectors, not floats) of the vector
    435      *            in the buffer to check against
    436      * @return
    437      */
    438     public static boolean equals(Vector3f check, FloatBuffer buf, int index) {
    439         TempVars vars = TempVars.get();
    440         Vector3f tempVec3 = vars.vect1;
    441         populateFromBuffer(tempVec3, buf, index);
    442         boolean eq = tempVec3.equals(check);
    443         vars.release();
    444         return eq;
    445     }
    446 
    447     // // -- VECTOR2F METHODS -- ////
    448     /**
    449      * Generate a new FloatBuffer using the given array of Vector2f objects.
    450      * The FloatBuffer will be 2 * data.length long and contain the vector data
    451      * as data[0].x, data[0].y, data[1].x... etc.
    452      *
    453      * @param data array of Vector2f objects to place into a new FloatBuffer
    454      */
    455     public static FloatBuffer createFloatBuffer(Vector2f... data) {
    456         if (data == null) {
    457             return null;
    458         }
    459         FloatBuffer buff = createFloatBuffer(2 * data.length);
    460         for (int x = 0; x < data.length; x++) {
    461             if (data[x] != null) {
    462                 buff.put(data[x].x).put(data[x].y);
    463             } else {
    464                 buff.put(0).put(0);
    465             }
    466         }
    467         buff.flip();
    468         return buff;
    469     }
    470 
    471     /**
    472      * Create a new FloatBuffer of an appropriate size to hold the specified
    473      * number of Vector2f object data.
    474      *
    475      * @param vertices
    476      *            number of vertices that need to be held by the newly created
    477      *            buffer
    478      * @return the requested new FloatBuffer
    479      */
    480     public static FloatBuffer createVector2Buffer(int vertices) {
    481         FloatBuffer vBuff = createFloatBuffer(2 * vertices);
    482         return vBuff;
    483     }
    484 
    485     /**
    486      * Create a new FloatBuffer of an appropriate size to hold the specified
    487      * number of Vector2f object data only if the given buffer if not already
    488      * the right size.
    489      *
    490      * @param buf
    491      *            the buffer to first check and rewind
    492      * @param vertices
    493      *            number of vertices that need to be held by the newly created
    494      *            buffer
    495      * @return the requested new FloatBuffer
    496      */
    497     public static FloatBuffer createVector2Buffer(FloatBuffer buf, int vertices) {
    498         if (buf != null && buf.limit() == 2 * vertices) {
    499             buf.rewind();
    500             return buf;
    501         }
    502 
    503         return createFloatBuffer(2 * vertices);
    504     }
    505 
    506     /**
    507      * Sets the data contained in the given Vector2F into the FloatBuffer at the
    508      * specified index.
    509      *
    510      * @param vector
    511      *            the data to insert
    512      * @param buf
    513      *            the buffer to insert into
    514      * @param index
    515      *            the postion to place the data; in terms of vectors not floats
    516      */
    517     public static void setInBuffer(Vector2f vector, FloatBuffer buf, int index) {
    518         buf.put(index * 2, vector.x);
    519         buf.put((index * 2) + 1, vector.y);
    520     }
    521 
    522     /**
    523      * Updates the values of the given vector from the specified buffer at the
    524      * index provided.
    525      *
    526      * @param vector
    527      *            the vector to set data on
    528      * @param buf
    529      *            the buffer to read from
    530      * @param index
    531      *            the position (in terms of vectors, not floats) to read from
    532      *            the buf
    533      */
    534     public static void populateFromBuffer(Vector2f vector, FloatBuffer buf, int index) {
    535         vector.x = buf.get(index * 2);
    536         vector.y = buf.get(index * 2 + 1);
    537     }
    538 
    539     /**
    540      * Generates a Vector2f array from the given FloatBuffer.
    541      *
    542      * @param buff
    543      *            the FloatBuffer to read from
    544      * @return a newly generated array of Vector2f objects
    545      */
    546     public static Vector2f[] getVector2Array(FloatBuffer buff) {
    547         buff.clear();
    548         Vector2f[] verts = new Vector2f[buff.limit() / 2];
    549         for (int x = 0; x < verts.length; x++) {
    550             Vector2f v = new Vector2f(buff.get(), buff.get());
    551             verts[x] = v;
    552         }
    553         return verts;
    554     }
    555 
    556     /**
    557      * Copies a Vector2f from one position in the buffer to another. The index
    558      * values are in terms of vector number (eg, vector number 0 is postions 0-1
    559      * in the FloatBuffer.)
    560      *
    561      * @param buf
    562      *            the buffer to copy from/to
    563      * @param fromPos
    564      *            the index of the vector to copy
    565      * @param toPos
    566      *            the index to copy the vector to
    567      */
    568     public static void copyInternalVector2(FloatBuffer buf, int fromPos, int toPos) {
    569         copyInternal(buf, fromPos * 2, toPos * 2, 2);
    570     }
    571 
    572     /**
    573      * Normalize a Vector2f in-buffer.
    574      *
    575      * @param buf
    576      *            the buffer to find the Vector2f within
    577      * @param index
    578      *            the position (in terms of vectors, not floats) of the vector
    579      *            to normalize
    580      */
    581     public static void normalizeVector2(FloatBuffer buf, int index) {
    582         TempVars vars = TempVars.get();
    583         Vector2f tempVec2 = vars.vect2d;
    584         populateFromBuffer(tempVec2, buf, index);
    585         tempVec2.normalizeLocal();
    586         setInBuffer(tempVec2, buf, index);
    587         vars.release();
    588     }
    589 
    590     /**
    591      * Add to a Vector2f in-buffer.
    592      *
    593      * @param toAdd
    594      *            the vector to add from
    595      * @param buf
    596      *            the buffer to find the Vector2f within
    597      * @param index
    598      *            the position (in terms of vectors, not floats) of the vector
    599      *            to add to
    600      */
    601     public static void addInBuffer(Vector2f toAdd, FloatBuffer buf, int index) {
    602         TempVars vars = TempVars.get();
    603         Vector2f tempVec2 = vars.vect2d;
    604         populateFromBuffer(tempVec2, buf, index);
    605         tempVec2.addLocal(toAdd);
    606         setInBuffer(tempVec2, buf, index);
    607         vars.release();
    608     }
    609 
    610     /**
    611      * Multiply and store a Vector2f in-buffer.
    612      *
    613      * @param toMult
    614      *            the vector to multiply against
    615      * @param buf
    616      *            the buffer to find the Vector2f within
    617      * @param index
    618      *            the position (in terms of vectors, not floats) of the vector
    619      *            to multiply
    620      */
    621     public static void multInBuffer(Vector2f toMult, FloatBuffer buf, int index) {
    622         TempVars vars = TempVars.get();
    623         Vector2f tempVec2 = vars.vect2d;
    624         populateFromBuffer(tempVec2, buf, index);
    625         tempVec2.multLocal(toMult);
    626         setInBuffer(tempVec2, buf, index);
    627         vars.release();
    628     }
    629 
    630     /**
    631      * Checks to see if the given Vector2f is equals to the data stored in the
    632      * buffer at the given data index.
    633      *
    634      * @param check
    635      *            the vector to check against - null will return false.
    636      * @param buf
    637      *            the buffer to compare data with
    638      * @param index
    639      *            the position (in terms of vectors, not floats) of the vector
    640      *            in the buffer to check against
    641      * @return
    642      */
    643     public static boolean equals(Vector2f check, FloatBuffer buf, int index) {
    644         TempVars vars = TempVars.get();
    645         Vector2f tempVec2 = vars.vect2d;
    646         populateFromBuffer(tempVec2, buf, index);
    647         boolean eq = tempVec2.equals(check);
    648         vars.release();
    649         return eq;
    650     }
    651 
    652     ////  -- INT METHODS -- ////
    653     /**
    654      * Generate a new IntBuffer using the given array of ints. The IntBuffer
    655      * will be data.length long and contain the int data as data[0], data[1]...
    656      * etc.
    657      *
    658      * @param data
    659      *            array of ints to place into a new IntBuffer
    660      */
    661     public static IntBuffer createIntBuffer(int... data) {
    662         if (data == null) {
    663             return null;
    664         }
    665         IntBuffer buff = createIntBuffer(data.length);
    666         buff.clear();
    667         buff.put(data);
    668         buff.flip();
    669         return buff;
    670     }
    671 
    672     /**
    673      * Create a new int[] array and populate it with the given IntBuffer's
    674      * contents.
    675      *
    676      * @param buff
    677      *            the IntBuffer to read from
    678      * @return a new int array populated from the IntBuffer
    679      */
    680     public static int[] getIntArray(IntBuffer buff) {
    681         if (buff == null) {
    682             return null;
    683         }
    684         buff.clear();
    685         int[] inds = new int[buff.limit()];
    686         for (int x = 0; x < inds.length; x++) {
    687             inds[x] = buff.get();
    688         }
    689         return inds;
    690     }
    691 
    692     /**
    693      * Create a new float[] array and populate it with the given FloatBuffer's
    694      * contents.
    695      *
    696      * @param buff
    697      *            the FloatBuffer to read from
    698      * @return a new float array populated from the FloatBuffer
    699      */
    700     public static float[] getFloatArray(FloatBuffer buff) {
    701         if (buff == null) {
    702             return null;
    703         }
    704         buff.clear();
    705         float[] inds = new float[buff.limit()];
    706         for (int x = 0; x < inds.length; x++) {
    707             inds[x] = buff.get();
    708         }
    709         return inds;
    710     }
    711 
    712     //// -- GENERAL DOUBLE ROUTINES -- ////
    713     /**
    714      * Create a new DoubleBuffer of the specified size.
    715      *
    716      * @param size
    717      *            required number of double to store.
    718      * @return the new DoubleBuffer
    719      */
    720     public static DoubleBuffer createDoubleBuffer(int size) {
    721         DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
    722         buf.clear();
    723         onBufferAllocated(buf);
    724         return buf;
    725     }
    726 
    727     /**
    728      * Create a new DoubleBuffer of an appropriate size to hold the specified
    729      * number of doubles only if the given buffer if not already the right size.
    730      *
    731      * @param buf
    732      *            the buffer to first check and rewind
    733      * @param size
    734      *            number of doubles that need to be held by the newly created
    735      *            buffer
    736      * @return the requested new DoubleBuffer
    737      */
    738     public static DoubleBuffer createDoubleBuffer(DoubleBuffer buf, int size) {
    739         if (buf != null && buf.limit() == size) {
    740             buf.rewind();
    741             return buf;
    742         }
    743 
    744         buf = createDoubleBuffer(size);
    745         return buf;
    746     }
    747 
    748     /**
    749      * Creates a new DoubleBuffer with the same contents as the given
    750      * DoubleBuffer. The new DoubleBuffer is seperate from the old one and
    751      * changes are not reflected across. If you want to reflect changes,
    752      * consider using Buffer.duplicate().
    753      *
    754      * @param buf
    755      *            the DoubleBuffer to copy
    756      * @return the copy
    757      */
    758     public static DoubleBuffer clone(DoubleBuffer buf) {
    759         if (buf == null) {
    760             return null;
    761         }
    762         buf.rewind();
    763 
    764         DoubleBuffer copy;
    765         if (buf.isDirect()) {
    766             copy = createDoubleBuffer(buf.limit());
    767         } else {
    768             copy = DoubleBuffer.allocate(buf.limit());
    769         }
    770         copy.put(buf);
    771 
    772         return copy;
    773     }
    774 
    775     //// -- GENERAL FLOAT ROUTINES -- ////
    776     /**
    777      * Create a new FloatBuffer of the specified size.
    778      *
    779      * @param size
    780      *            required number of floats to store.
    781      * @return the new FloatBuffer
    782      */
    783     public static FloatBuffer createFloatBuffer(int size) {
    784         FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
    785         buf.clear();
    786         onBufferAllocated(buf);
    787         return buf;
    788     }
    789 
    790     /**
    791      * Copies floats from one position in the buffer to another.
    792      *
    793      * @param buf
    794      *            the buffer to copy from/to
    795      * @param fromPos
    796      *            the starting point to copy from
    797      * @param toPos
    798      *            the starting point to copy to
    799      * @param length
    800      *            the number of floats to copy
    801      */
    802     public static void copyInternal(FloatBuffer buf, int fromPos, int toPos, int length) {
    803         float[] data = new float[length];
    804         buf.position(fromPos);
    805         buf.get(data);
    806         buf.position(toPos);
    807         buf.put(data);
    808     }
    809 
    810     /**
    811      * Creates a new FloatBuffer with the same contents as the given
    812      * FloatBuffer. The new FloatBuffer is seperate from the old one and changes
    813      * are not reflected across. If you want to reflect changes, consider using
    814      * Buffer.duplicate().
    815      *
    816      * @param buf
    817      *            the FloatBuffer to copy
    818      * @return the copy
    819      */
    820     public static FloatBuffer clone(FloatBuffer buf) {
    821         if (buf == null) {
    822             return null;
    823         }
    824         buf.rewind();
    825 
    826         FloatBuffer copy;
    827         if (buf.isDirect()) {
    828             copy = createFloatBuffer(buf.limit());
    829         } else {
    830             copy = FloatBuffer.allocate(buf.limit());
    831         }
    832         copy.put(buf);
    833 
    834         return copy;
    835     }
    836 
    837     //// -- GENERAL INT ROUTINES -- ////
    838     /**
    839      * Create a new IntBuffer of the specified size.
    840      *
    841      * @param size
    842      *            required number of ints to store.
    843      * @return the new IntBuffer
    844      */
    845     public static IntBuffer createIntBuffer(int size) {
    846         IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
    847         buf.clear();
    848         onBufferAllocated(buf);
    849         return buf;
    850     }
    851 
    852     /**
    853      * Create a new IntBuffer of an appropriate size to hold the specified
    854      * number of ints only if the given buffer if not already the right size.
    855      *
    856      * @param buf
    857      *            the buffer to first check and rewind
    858      * @param size
    859      *            number of ints that need to be held by the newly created
    860      *            buffer
    861      * @return the requested new IntBuffer
    862      */
    863     public static IntBuffer createIntBuffer(IntBuffer buf, int size) {
    864         if (buf != null && buf.limit() == size) {
    865             buf.rewind();
    866             return buf;
    867         }
    868 
    869         buf = createIntBuffer(size);
    870         return buf;
    871     }
    872 
    873     /**
    874      * Creates a new IntBuffer with the same contents as the given IntBuffer.
    875      * The new IntBuffer is seperate from the old one and changes are not
    876      * reflected across. If you want to reflect changes, consider using
    877      * Buffer.duplicate().
    878      *
    879      * @param buf
    880      *            the IntBuffer to copy
    881      * @return the copy
    882      */
    883     public static IntBuffer clone(IntBuffer buf) {
    884         if (buf == null) {
    885             return null;
    886         }
    887         buf.rewind();
    888 
    889         IntBuffer copy;
    890         if (buf.isDirect()) {
    891             copy = createIntBuffer(buf.limit());
    892         } else {
    893             copy = IntBuffer.allocate(buf.limit());
    894         }
    895         copy.put(buf);
    896 
    897         return copy;
    898     }
    899 
    900     //// -- GENERAL BYTE ROUTINES -- ////
    901     /**
    902      * Create a new ByteBuffer of the specified size.
    903      *
    904      * @param size
    905      *            required number of ints to store.
    906      * @return the new IntBuffer
    907      */
    908     public static ByteBuffer createByteBuffer(int size) {
    909         ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
    910         buf.clear();
    911         onBufferAllocated(buf);
    912         return buf;
    913     }
    914 
    915     /**
    916      * Create a new ByteBuffer of an appropriate size to hold the specified
    917      * number of ints only if the given buffer if not already the right size.
    918      *
    919      * @param buf
    920      *            the buffer to first check and rewind
    921      * @param size
    922      *            number of bytes that need to be held by the newly created
    923      *            buffer
    924      * @return the requested new IntBuffer
    925      */
    926     public static ByteBuffer createByteBuffer(ByteBuffer buf, int size) {
    927         if (buf != null && buf.limit() == size) {
    928             buf.rewind();
    929             return buf;
    930         }
    931 
    932         buf = createByteBuffer(size);
    933         return buf;
    934     }
    935 
    936     public static ByteBuffer createByteBuffer(byte... data) {
    937         ByteBuffer bb = createByteBuffer(data.length);
    938         bb.put(data);
    939         bb.flip();
    940         return bb;
    941     }
    942 
    943     public static ByteBuffer createByteBuffer(String data) {
    944         byte[] bytes = data.getBytes();
    945         ByteBuffer bb = createByteBuffer(bytes.length);
    946         bb.put(bytes);
    947         bb.flip();
    948         return bb;
    949     }
    950 
    951     /**
    952      * Creates a new ByteBuffer with the same contents as the given ByteBuffer.
    953      * The new ByteBuffer is seperate from the old one and changes are not
    954      * reflected across. If you want to reflect changes, consider using
    955      * Buffer.duplicate().
    956      *
    957      * @param buf
    958      *            the ByteBuffer to copy
    959      * @return the copy
    960      */
    961     public static ByteBuffer clone(ByteBuffer buf) {
    962         if (buf == null) {
    963             return null;
    964         }
    965         buf.rewind();
    966 
    967         ByteBuffer copy;
    968         if (buf.isDirect()) {
    969             copy = createByteBuffer(buf.limit());
    970         } else {
    971             copy = ByteBuffer.allocate(buf.limit());
    972         }
    973         copy.put(buf);
    974 
    975         return copy;
    976     }
    977 
    978     //// -- GENERAL SHORT ROUTINES -- ////
    979     /**
    980      * Create a new ShortBuffer of the specified size.
    981      *
    982      * @param size
    983      *            required number of shorts to store.
    984      * @return the new ShortBuffer
    985      */
    986     public static ShortBuffer createShortBuffer(int size) {
    987         ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
    988         buf.clear();
    989         onBufferAllocated(buf);
    990         return buf;
    991     }
    992 
    993     /**
    994      * Create a new ShortBuffer of an appropriate size to hold the specified
    995      * number of shorts only if the given buffer if not already the right size.
    996      *
    997      * @param buf
    998      *            the buffer to first check and rewind
    999      * @param size
   1000      *            number of shorts that need to be held by the newly created
   1001      *            buffer
   1002      * @return the requested new ShortBuffer
   1003      */
   1004     public static ShortBuffer createShortBuffer(ShortBuffer buf, int size) {
   1005         if (buf != null && buf.limit() == size) {
   1006             buf.rewind();
   1007             return buf;
   1008         }
   1009 
   1010         buf = createShortBuffer(size);
   1011         return buf;
   1012     }
   1013 
   1014     public static ShortBuffer createShortBuffer(short... data) {
   1015         if (data == null) {
   1016             return null;
   1017         }
   1018         ShortBuffer buff = createShortBuffer(data.length);
   1019         buff.clear();
   1020         buff.put(data);
   1021         buff.flip();
   1022         return buff;
   1023     }
   1024 
   1025     /**
   1026      * Creates a new ShortBuffer with the same contents as the given ShortBuffer.
   1027      * The new ShortBuffer is seperate from the old one and changes are not
   1028      * reflected across. If you want to reflect changes, consider using
   1029      * Buffer.duplicate().
   1030      *
   1031      * @param buf
   1032      *            the ShortBuffer to copy
   1033      * @return the copy
   1034      */
   1035     public static ShortBuffer clone(ShortBuffer buf) {
   1036         if (buf == null) {
   1037             return null;
   1038         }
   1039         buf.rewind();
   1040 
   1041         ShortBuffer copy;
   1042         if (buf.isDirect()) {
   1043             copy = createShortBuffer(buf.limit());
   1044         } else {
   1045             copy = ShortBuffer.allocate(buf.limit());
   1046         }
   1047         copy.put(buf);
   1048 
   1049         return copy;
   1050     }
   1051 
   1052     /**
   1053      * Ensures there is at least the <code>required</code> number of entries left after the current position of the
   1054      * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.
   1055      * @param buffer buffer that should be checked/copied (may be null)
   1056      * @param required minimum number of elements that should be remaining in the returned buffer
   1057      * @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as
   1058      * the input buffer, not null
   1059      */
   1060     public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
   1061         if (buffer == null || (buffer.remaining() < required)) {
   1062             int position = (buffer != null ? buffer.position() : 0);
   1063             FloatBuffer newVerts = createFloatBuffer(position + required);
   1064             if (buffer != null) {
   1065                 buffer.rewind();
   1066                 newVerts.put(buffer);
   1067                 newVerts.position(position);
   1068             }
   1069             buffer = newVerts;
   1070         }
   1071         return buffer;
   1072     }
   1073 
   1074     public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
   1075         if (buffer == null || (buffer.remaining() < required)) {
   1076             int position = (buffer != null ? buffer.position() : 0);
   1077             ShortBuffer newVerts = createShortBuffer(position + required);
   1078             if (buffer != null) {
   1079                 buffer.rewind();
   1080                 newVerts.put(buffer);
   1081                 newVerts.position(position);
   1082             }
   1083             buffer = newVerts;
   1084         }
   1085         return buffer;
   1086     }
   1087 
   1088     public static ByteBuffer ensureLargeEnough(ByteBuffer buffer, int required) {
   1089         if (buffer == null || (buffer.remaining() < required)) {
   1090             int position = (buffer != null ? buffer.position() : 0);
   1091             ByteBuffer newVerts = createByteBuffer(position + required);
   1092             if (buffer != null) {
   1093                 buffer.rewind();
   1094                 newVerts.put(buffer);
   1095                 newVerts.position(position);
   1096             }
   1097             buffer = newVerts;
   1098         }
   1099         return buffer;
   1100     }
   1101 
   1102     public static void printCurrentDirectMemory(StringBuilder store) {
   1103         long totalHeld = 0;
   1104         // make a new set to hold the keys to prevent concurrency issues.
   1105         ArrayList<Buffer> bufs = new ArrayList<Buffer>(trackingHash.keySet());
   1106         int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;
   1107         int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0;
   1108         for (Buffer b : bufs) {
   1109             if (b instanceof ByteBuffer) {
   1110                 totalHeld += b.capacity();
   1111                 bBufsM += b.capacity();
   1112                 bBufs++;
   1113             } else if (b instanceof FloatBuffer) {
   1114                 totalHeld += b.capacity() * 4;
   1115                 fBufsM += b.capacity() * 4;
   1116                 fBufs++;
   1117             } else if (b instanceof IntBuffer) {
   1118                 totalHeld += b.capacity() * 4;
   1119                 iBufsM += b.capacity() * 4;
   1120                 iBufs++;
   1121             } else if (b instanceof ShortBuffer) {
   1122                 totalHeld += b.capacity() * 2;
   1123                 sBufsM += b.capacity() * 2;
   1124                 sBufs++;
   1125             } else if (b instanceof DoubleBuffer) {
   1126                 totalHeld += b.capacity() * 8;
   1127                 dBufsM += b.capacity() * 8;
   1128                 dBufs++;
   1129             }
   1130         }
   1131         long heapMem = Runtime.getRuntime().totalMemory()
   1132                 - Runtime.getRuntime().freeMemory();
   1133 
   1134         boolean printStout = store == null;
   1135         if (store == null) {
   1136             store = new StringBuilder();
   1137         }
   1138         store.append("Existing buffers: ").append(bufs.size()).append("\n");
   1139         store.append("(b: ").append(bBufs).append("  f: ").append(fBufs).append("  i: ").append(iBufs).append("  s: ").append(sBufs).append("  d: ").append(dBufs).append(")").append("\n");
   1140         store.append("Total   heap memory held: ").append(heapMem / 1024).append("kb\n");
   1141         store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
   1142         store.append("(b: ").append(bBufsM / 1024).append("kb  f: ").append(fBufsM / 1024).append("kb  i: ").append(iBufsM / 1024).append("kb  s: ").append(sBufsM / 1024).append("kb  d: ").append(dBufsM / 1024).append("kb)").append("\n");
   1143         if (printStout) {
   1144             System.out.println(store.toString());
   1145         }
   1146     }
   1147 
   1148     private static final AtomicBoolean loadedMethods = new AtomicBoolean(false);
   1149     private static Method cleanerMethod = null;
   1150     private static Method cleanMethod = null;
   1151     private static Method viewedBufferMethod = null;
   1152     private static Method freeMethod = null;
   1153 
   1154     private static Method loadMethod(String className, String methodName){
   1155         try {
   1156             Method method = Class.forName(className).getMethod(methodName);
   1157             method.setAccessible(true);
   1158             return method;
   1159         } catch (NoSuchMethodException ex) {
   1160             return null; // the method was not found
   1161         } catch (SecurityException ex) {
   1162             return null; // setAccessible not allowed by security policy
   1163         } catch (ClassNotFoundException ex) {
   1164             return null; // the direct buffer implementation was not found
   1165         }
   1166     }
   1167 
   1168     private static void loadCleanerMethods() {
   1169         // If its already true, exit, if not, set it to true.
   1170         if (loadedMethods.getAndSet(true)) {
   1171             return;
   1172         }
   1173         // This could potentially be called many times if used from multiple
   1174         // threads
   1175         synchronized (loadedMethods) {
   1176             // Oracle JRE / OpenJDK
   1177             cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
   1178             cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
   1179             viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
   1180 
   1181             // Apache Harmony
   1182             freeMethod = loadMethod("org.apache.harmony.nio.internal.DirectBuffer", "free");
   1183 
   1184             // GUN Classpath (not likely)
   1185             //finalizeMethod = loadMethod("java.nio.DirectByteBufferImpl", "finalize");
   1186         }
   1187     }
   1188 
   1189     /**
   1190     * Direct buffers are garbage collected by using a phantom reference and a
   1191     * reference queue. Every once a while, the JVM checks the reference queue and
   1192     * cleans the direct buffers. However, as this doesn't happen
   1193     * immediately after discarding all references to a direct buffer, it's
   1194     * easy to OutOfMemoryError yourself using direct buffers. This function
   1195     * explicitly calls the Cleaner method of a direct buffer.
   1196     *
   1197     * @param toBeDestroyed
   1198     *          The direct buffer that will be "cleaned". Utilizes reflection.
   1199     *
   1200     */
   1201     public static void destroyDirectBuffer(Buffer toBeDestroyed) {
   1202         if (!toBeDestroyed.isDirect()) {
   1203             return;
   1204         }
   1205 
   1206         loadCleanerMethods();
   1207 
   1208         try {
   1209             if (freeMethod != null) {
   1210                 freeMethod.invoke(toBeDestroyed);
   1211             } else {
   1212                 Object cleaner = cleanerMethod.invoke(toBeDestroyed);
   1213                 if (cleaner != null) {
   1214                     cleanMethod.invoke(cleaner);
   1215                 } else {
   1216                     // Try the alternate approach of getting the viewed buffer first
   1217                     Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
   1218                     if (viewedBuffer != null) {
   1219                         destroyDirectBuffer((Buffer) viewedBuffer);
   1220                     } else {
   1221                         Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);
   1222                     }
   1223                 }
   1224             }
   1225         } catch (IllegalAccessException ex) {
   1226             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
   1227         } catch (IllegalArgumentException ex) {
   1228             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
   1229         } catch (InvocationTargetException ex) {
   1230             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
   1231         } catch (SecurityException ex) {
   1232             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
   1233         }
   1234     }
   1235 }
   1236