1 
2 /*
3  * Copyright 2011 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification, are
6  * permitted provided that the following conditions are met:
7  *
8  *    1. Redistributions of source code must retain the above copyright notice, this list of
9  *       conditions and the following disclaimer.
10  *
11  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *       of conditions and the following disclaimer in the documentation and/or other materials
13  *       provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
16  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * The views and conclusions contained in the software and documentation are those of the
26  * authors and should not be interpreted as representing official policies, either expressed
27  * or implied, of JogAmp Community.
28  */
29 
30 /*
31  * Created on Sunday, February 13 2011 15:17
32  */
33 package com.jogamp.common.nio;
34 
35 import java.nio.ByteBuffer;
36 import java.nio.CharBuffer;
37 import java.nio.DoubleBuffer;
38 import java.nio.FloatBuffer;
39 import java.nio.IntBuffer;
40 import java.nio.LongBuffer;
41 import java.nio.ShortBuffer;
42 
43 /**
44  * Buffer factory attempting to reduce buffer creation overhead.
45  * Direct ByteBuffers must be page aligned which increases creation overhead of
46  * small buffers significantly.
47  * This factory can be used as fixed size static or or dynamic allocating
48  * factory. The initial size and allocation size is configurable.
49  * <p>
50  * Fixed size factories may be used in systems with hard realtime requirements
51  * and/or predictable memory usage.
52  * </p>
53  * <p>
54  * concurrency info:<br/>
55  * <ul>
56  * <li>all create methods are threadsafe</li>
57  * <li>factories created with create(...) are <b>not</b> threadsafe</li>
58  * <li>factories created with createSynchronized(...) are threadsafe</li>
59  * </ul>
60  * </p>
61  *
62  * @author Michael Bien
63  */
64 public class CachedBufferFactory {
65 
66     /**
67      * default size for internal buffer allocation.
68      */
69     public static final int DEFAULT_ALLOCATION_SIZE = 1024 * 1024;
70 
71     private final int ALLOCATION_SIZE;
72     private ByteBuffer currentBuffer;
73 
CachedBufferFactory()74     private CachedBufferFactory() {
75         this(DEFAULT_ALLOCATION_SIZE, DEFAULT_ALLOCATION_SIZE);
76     }
77 
CachedBufferFactory(final int initialSize, final int allocationSize)78     private CachedBufferFactory(final int initialSize, final int allocationSize) {
79         currentBuffer = Buffers.newDirectByteBuffer(initialSize);
80         ALLOCATION_SIZE = allocationSize;
81     }
82 
83 
84     /**
85      * Creates a factory with initial size and allocation size set to
86      * {@link #DEFAULT_ALLOCATION_SIZE}.
87      */
create()88     public static CachedBufferFactory create() {
89         return new CachedBufferFactory();
90     }
91 
92     /**
93      * Creates a factory with the specified initial size. The allocation size is set to
94      * {@link #DEFAULT_ALLOCATION_SIZE}.
95      */
create(final int initialSize)96     public static CachedBufferFactory create(final int initialSize) {
97         return new CachedBufferFactory(initialSize, DEFAULT_ALLOCATION_SIZE);
98     }
99 
100     /**
101      * Creates a factory with the specified initial size. The allocation size is set to
102      * {@link #DEFAULT_ALLOCATION_SIZE}.
103      * @param fixed Creates a fixed size factory which will handle overflows (initial size)
104      * with RuntimeExceptions.
105      */
create(final int initialSize, final boolean fixed)106     public static CachedBufferFactory create(final int initialSize, final boolean fixed) {
107         return new CachedBufferFactory(initialSize, fixed?-1:DEFAULT_ALLOCATION_SIZE);
108     }
109 
110     /**
111      * Creates a factory with the specified initial size and allocation size.
112      */
create(final int initialSize, final int allocationSize)113     public static CachedBufferFactory create(final int initialSize, final int allocationSize) {
114         return new CachedBufferFactory(initialSize, allocationSize);
115     }
116 
117 
118     /**
119      * Synchronized version of {@link #create()}.
120      */
createSynchronized()121     public static CachedBufferFactory createSynchronized() {
122         return new SynchronizedCachedBufferFactory();
123     }
124 
125     /**
126      * Synchronized version of {@link #create(int)}.
127      */
createSynchronized(final int initialSize)128     public static CachedBufferFactory createSynchronized(final int initialSize) {
129         return new SynchronizedCachedBufferFactory(initialSize, DEFAULT_ALLOCATION_SIZE);
130     }
131 
132     /**
133      * Synchronized version of {@link #create(int, boolean)}.
134      */
createSynchronized(final int initialSize, final boolean fixed)135     public static CachedBufferFactory createSynchronized(final int initialSize, final boolean fixed) {
136         return new SynchronizedCachedBufferFactory(initialSize, fixed?-1:DEFAULT_ALLOCATION_SIZE);
137     }
138 
139     /**
140      * Synchronized version of {@link #create(int, int)}.
141      */
createSynchronized(final int initialSize, final int allocationSize)142     public static CachedBufferFactory createSynchronized(final int initialSize, final int allocationSize) {
143         return new CachedBufferFactory(initialSize, allocationSize);
144     }
145 
146     /**
147      * Returns true only if this factory does not allow to allocate more buffers
148      * as limited by the initial size.
149      */
isFixed()150     public boolean isFixed() {
151         return ALLOCATION_SIZE == -1;
152     }
153 
154     /**
155      * Returns the allocation size used to create new internal buffers.
156      * 0 means that the buffer will not grows, see {@link #isFixed()}.
157      */
getAllocationSize()158     public int getAllocationSize() {
159         return ALLOCATION_SIZE;
160     }
161 
162     /**
163      * @return true if buffer cannot grow, otherwise false
164      */
checkIfFixed()165     private void checkIfFixed() {
166         if(ALLOCATION_SIZE == 0) {
167             throw new RuntimeException("fixed size buffer factory ran out ouf bounds.");
168         }
169     }
170 
destroy()171     public void destroy() {
172         if(null != currentBuffer) {
173             currentBuffer.clear();
174             currentBuffer = null;
175         }
176     }
newDirectByteBuffer(final int size)177     public ByteBuffer newDirectByteBuffer(final int size) {
178 
179         // if large enough... just create it
180         if (size > currentBuffer.capacity()) {
181             checkIfFixed();
182             return Buffers.newDirectByteBuffer(size);
183         }
184 
185         // create new internal buffer if the old is running full
186         if (size > currentBuffer.remaining()) {
187             checkIfFixed();
188             currentBuffer = Buffers.newDirectByteBuffer(ALLOCATION_SIZE);
189         }
190 
191         currentBuffer.limit(currentBuffer.position() + size);
192         final ByteBuffer result = currentBuffer.slice().order(currentBuffer.order());
193         currentBuffer.position(currentBuffer.limit());
194         currentBuffer.limit(currentBuffer.capacity());
195         return result;
196     }
197 
198 
newDirectByteBuffer(final byte[] values, final int offset, final int lenght)199     public ByteBuffer newDirectByteBuffer(final byte[] values, final int offset, final int lenght) {
200         return (ByteBuffer)newDirectByteBuffer(lenght).put(values, offset, lenght).rewind();
201     }
202 
newDirectByteBuffer(final byte[] values, final int offset)203     public ByteBuffer newDirectByteBuffer(final byte[] values, final int offset) {
204         return newDirectByteBuffer(values, offset, values.length-offset);
205     }
206 
newDirectByteBuffer(final byte[] values)207     public ByteBuffer newDirectByteBuffer(final byte[] values) {
208         return newDirectByteBuffer(values, 0);
209     }
210 
newDirectDoubleBuffer(final int numElements)211     public DoubleBuffer newDirectDoubleBuffer(final int numElements) {
212         return newDirectByteBuffer(numElements * Buffers.SIZEOF_DOUBLE).asDoubleBuffer();
213     }
214 
newDirectDoubleBuffer(final double[] values, final int offset, final int lenght)215     public DoubleBuffer newDirectDoubleBuffer(final double[] values, final int offset, final int lenght) {
216         return (DoubleBuffer)newDirectDoubleBuffer(lenght).put(values, offset, lenght).rewind();
217     }
218 
newDirectDoubleBuffer(final double[] values, final int offset)219     public DoubleBuffer newDirectDoubleBuffer(final double[] values, final int offset) {
220         return newDirectDoubleBuffer(values, offset, values.length - offset);
221     }
222 
newDirectDoubleBuffer(final double[] values)223     public DoubleBuffer newDirectDoubleBuffer(final double[] values) {
224         return newDirectDoubleBuffer(values, 0);
225     }
226 
newDirectFloatBuffer(final int numElements)227     public FloatBuffer newDirectFloatBuffer(final int numElements) {
228         return newDirectByteBuffer(numElements * Buffers.SIZEOF_FLOAT).asFloatBuffer();
229     }
230 
newDirectFloatBuffer(final float[] values, final int offset, final int lenght)231     public FloatBuffer newDirectFloatBuffer(final float[] values, final int offset, final int lenght) {
232         return (FloatBuffer)newDirectFloatBuffer(lenght).put(values, offset, lenght).rewind();
233     }
234 
newDirectFloatBuffer(final float[] values, final int offset)235     public FloatBuffer newDirectFloatBuffer(final float[] values, final int offset) {
236         return newDirectFloatBuffer(values, offset, values.length - offset);
237     }
238 
newDirectFloatBuffer(final float[] values)239     public FloatBuffer newDirectFloatBuffer(final float[] values) {
240         return newDirectFloatBuffer(values, 0);
241     }
242 
newDirectIntBuffer(final int numElements)243     public IntBuffer newDirectIntBuffer(final int numElements) {
244         return newDirectByteBuffer(numElements * Buffers.SIZEOF_INT).asIntBuffer();
245     }
246 
newDirectIntBuffer(final int[] values, final int offset, final int lenght)247     public IntBuffer newDirectIntBuffer(final int[] values, final int offset, final int lenght) {
248         return (IntBuffer)newDirectIntBuffer(lenght).put(values, offset, lenght).rewind();
249     }
250 
newDirectIntBuffer(final int[] values, final int offset)251     public IntBuffer newDirectIntBuffer(final int[] values, final int offset) {
252         return newDirectIntBuffer(values, offset, values.length - offset);
253     }
254 
newDirectIntBuffer(final int[] values)255     public IntBuffer newDirectIntBuffer(final int[] values) {
256         return newDirectIntBuffer(values, 0);
257     }
258 
newDirectLongBuffer(final int numElements)259     public LongBuffer newDirectLongBuffer(final int numElements) {
260         return newDirectByteBuffer(numElements * Buffers.SIZEOF_LONG).asLongBuffer();
261     }
262 
newDirectLongBuffer(final long[] values, final int offset, final int lenght)263     public LongBuffer newDirectLongBuffer(final long[] values, final int offset, final int lenght) {
264         return (LongBuffer)newDirectLongBuffer(lenght).put(values, offset, lenght).rewind();
265     }
266 
newDirectLongBuffer(final long[] values, final int offset)267     public LongBuffer newDirectLongBuffer(final long[] values, final int offset) {
268         return newDirectLongBuffer(values, offset, values.length - offset);
269     }
270 
newDirectLongBuffer(final long[] values)271     public LongBuffer newDirectLongBuffer(final long[] values) {
272         return newDirectLongBuffer(values, 0);
273     }
274 
newDirectShortBuffer(final int numElements)275     public ShortBuffer newDirectShortBuffer(final int numElements) {
276         return newDirectByteBuffer(numElements * Buffers.SIZEOF_SHORT).asShortBuffer();
277     }
278 
newDirectShortBuffer(final short[] values, final int offset, final int lenght)279     public ShortBuffer newDirectShortBuffer(final short[] values, final int offset, final int lenght) {
280         return (ShortBuffer)newDirectShortBuffer(lenght).put(values, offset, lenght).rewind();
281     }
282 
newDirectShortBuffer(final short[] values, final int offset)283     public ShortBuffer newDirectShortBuffer(final short[] values, final int offset) {
284         return newDirectShortBuffer(values, offset, values.length - offset);
285     }
286 
newDirectShortBuffer(final short[] values)287     public ShortBuffer newDirectShortBuffer(final short[] values) {
288         return newDirectShortBuffer(values, 0);
289     }
290 
newDirectCharBuffer(final int numElements)291     public CharBuffer newDirectCharBuffer(final int numElements) {
292         return newDirectByteBuffer(numElements * Buffers.SIZEOF_SHORT).asCharBuffer();
293     }
294 
newDirectCharBuffer(final char[] values, final int offset, final int lenght)295     public CharBuffer newDirectCharBuffer(final char[] values, final int offset, final int lenght) {
296         return (CharBuffer)newDirectCharBuffer(lenght).put(values, offset, lenght).rewind();
297     }
298 
newDirectCharBuffer(final char[] values, final int offset)299     public CharBuffer newDirectCharBuffer(final char[] values, final int offset) {
300         return newDirectCharBuffer(values, offset, values.length - offset);
301     }
302 
newDirectCharBuffer(final char[] values)303     public CharBuffer newDirectCharBuffer(final char[] values) {
304         return newDirectCharBuffer(values, 0);
305     }
306 
307     @Override
equals(final Object obj)308     public boolean equals(final Object obj) {
309         if (obj == null) {
310             return false;
311         }
312         if (getClass() != obj.getClass()) {
313             return false;
314         }
315         final CachedBufferFactory other = (CachedBufferFactory) obj;
316         if (this.ALLOCATION_SIZE != other.ALLOCATION_SIZE) {
317             return false;
318         }
319         if (this.currentBuffer != other.currentBuffer && (this.currentBuffer == null || !this.currentBuffer.equals(other.currentBuffer))) {
320             return false;
321         }
322         return true;
323     }
324 
325     @Override
toString()326     public String toString() {
327         return getClass().getName()+"[static:"+isFixed()+" alloc size:"+getAllocationSize()+"]";
328     }
329 
330 
331     // nothing special, just synchronized
332     private static class SynchronizedCachedBufferFactory extends CachedBufferFactory {
333 
SynchronizedCachedBufferFactory()334         private SynchronizedCachedBufferFactory() {
335             super();
336         }
337 
SynchronizedCachedBufferFactory(final int size, final int step)338         private SynchronizedCachedBufferFactory(final int size, final int step) {
339             super(size, step);
340         }
341 
342         @Override
newDirectByteBuffer(final int size)343         public synchronized ByteBuffer newDirectByteBuffer(final int size) {
344             return super.newDirectByteBuffer(size);
345         }
346 
347     }
348 
349 }
350