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      */
clone(Buffer buf)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 
onBufferAllocated(Buffer buffer)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      */
createFloatBuffer(Vector3f... data)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      */
createFloatBuffer(Quaternion... data)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      */
createFloatBuffer(float... data)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      */
createVector3Buffer(int vertices)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      */
createVector3Buffer(FloatBuffer buf, int vertices)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      */
setInBuffer(ColorRGBA color, FloatBuffer buf, int index)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      */
setInBuffer(Quaternion quat, FloatBuffer buf, int index)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      */
setInBuffer(Vector3f vector, FloatBuffer buf, int index)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      */
populateFromBuffer(Vector3f vector, FloatBuffer buf, int index)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      */
getVector3Array(FloatBuffer buff)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      */
copyInternalVector3(FloatBuffer buf, int fromPos, int toPos)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      */
normalizeVector3(FloatBuffer buf, int index)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      */
addInBuffer(Vector3f toAdd, FloatBuffer buf, int index)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      */
multInBuffer(Vector3f toMult, FloatBuffer buf, int index)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      */
equals(Vector3f check, FloatBuffer buf, int index)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      */
createFloatBuffer(Vector2f... data)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      */
createVector2Buffer(int vertices)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      */
createVector2Buffer(FloatBuffer buf, int vertices)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      */
setInBuffer(Vector2f vector, FloatBuffer buf, int index)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      */
populateFromBuffer(Vector2f vector, FloatBuffer buf, int index)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      */
getVector2Array(FloatBuffer buff)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      */
copyInternalVector2(FloatBuffer buf, int fromPos, int toPos)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      */
normalizeVector2(FloatBuffer buf, int index)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      */
addInBuffer(Vector2f toAdd, FloatBuffer buf, int index)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      */
multInBuffer(Vector2f toMult, FloatBuffer buf, int index)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      */
equals(Vector2f check, FloatBuffer buf, int index)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      */
createIntBuffer(int... data)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      */
getIntArray(IntBuffer buff)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      */
getFloatArray(FloatBuffer buff)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      */
createDoubleBuffer(int size)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      */
createDoubleBuffer(DoubleBuffer buf, int size)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      */
clone(DoubleBuffer buf)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      */
createFloatBuffer(int size)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      */
copyInternal(FloatBuffer buf, int fromPos, int toPos, int length)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      */
clone(FloatBuffer buf)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      */
createIntBuffer(int size)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      */
createIntBuffer(IntBuffer buf, int size)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      */
clone(IntBuffer buf)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      */
createByteBuffer(int size)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      */
createByteBuffer(ByteBuffer buf, int size)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 
createByteBuffer(byte... data)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 
createByteBuffer(String data)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      */
clone(ByteBuffer buf)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      */
createShortBuffer(int size)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      */
createShortBuffer(ShortBuffer buf, int size)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 
createShortBuffer(short... data)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      */
clone(ShortBuffer buf)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      */
ensureLargeEnough(FloatBuffer buffer, int required)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 
ensureLargeEnough(ShortBuffer buffer, int required)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 
ensureLargeEnough(ByteBuffer buffer, int required)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 
printCurrentDirectMemory(StringBuilder store)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 
loadMethod(String className, String methodName)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 
loadCleanerMethods()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     */
destroyDirectBuffer(Buffer toBeDestroyed)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