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 */
32package com.jme3.util;
33
34import com.jme3.math.ColorRGBA;
35import com.jme3.math.Quaternion;
36import com.jme3.math.Vector2f;
37import com.jme3.math.Vector3f;
38import java.lang.reflect.InvocationTargetException;
39import java.lang.reflect.Method;
40import java.nio.*;
41import java.util.ArrayList;
42import java.util.Collections;
43import java.util.Map;
44import java.util.WeakHashMap;
45import java.util.concurrent.atomic.AtomicBoolean;
46import java.util.logging.Level;
47import 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 */
56public 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