001    /*
002     * This file is part of muCommander, http://www.mucommander.com
003     * Copyright (C) 2002-2008 Maxence Bernard
004     *
005     * muCommander is free software; you can redistribute it and/or modify
006     * it under the terms of the GNU General Public License as published by
007     * the Free Software Foundation; either version 3 of the License, or
008     * (at your option) any later version.
009     *
010     * muCommander is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    
019    package com.mucommander.io;
020    
021    import com.mucommander.Debug;
022    
023    import java.nio.ByteBuffer;
024    import java.nio.CharBuffer;
025    import java.util.Vector;
026    
027    /**
028     * This class allows to share and reuse byte buffers to avoid excessive memory allocation and garbage collection.
029     * Methods that use byte buffers and that are called repeatedly will benefit from using this class.
030     *
031     * <p>This class works with two types of byte buffers indifferently:
032     * <ul>
033     *  <li>Byte array buffers (<code>byte[]</code>)</li>
034     *  <li><code>java.nio.ByteBuffer</code></li>
035     * </ul>
036     *
037     * <p>
038     * Usage of this class is similar to malloc/free:
039     * <ul>
040     *  <li>Call <code>#get*Buffer(int)</code> to retrieve a buffer instance of a specified size</li>
041     *  <li>Use the buffer</li>
042     *  <li>When finished using the buffer, call <code>#release*Buffer(byte[])</code> to make this buffer available for
043     * subsequent calls to <code>#get*Buffer(int)</code>. Failing to call this method will prevent the buffer from being
044     * used again and from being garbage-collected.</li>
045     * </ul>
046     * </p>
047     *
048     * <p>Note: this class is thread safe and thus can safely be used by concurrent threads.</p>
049     *
050     * @author Maxence Bernard
051     * @see com.mucommander.io.StreamUtils
052     */
053    public class BufferPool {
054    
055        /** List of BufferContainer instances that wraps available buffers */
056        private static Vector bufferContainers = new Vector();
057    
058        /** The initial default buffer size */
059        public final static int INITIAL_DEFAULT_BUFFER_SIZE = 65536;
060    
061        /** Size of buffers returned by get*Buffer methods without a size argument */
062        public static int defaultBufferSize = INITIAL_DEFAULT_BUFFER_SIZE;
063    
064        /** The initial max pool size */
065        public final static long INITIAL_POOL_LIMIT = 10485760;
066    
067        /** Maximum combined size of all pooled buffers, in bytes */
068        public static long maxPoolSize = INITIAL_POOL_LIMIT;
069    
070        /** Current combined size of all pooled buffers, in bytes */
071        public static long poolSize;
072    
073    
074        /**
075         * Convenience method that has the same effect as calling {@link #getByteArray(int)} with
076         * a length equal to {@link #getDefaultBufferSize()}.
077         *
078         * @return a byte array with a length of {@link # getDefaultBufferSize ()}
079         */
080        public static synchronized byte[] getByteArray() {
081            return getByteArray(getDefaultBufferSize());
082        }
083    
084        /**
085         * Returns a byte array of the specified length. This method first checks if a byte array of the specified length
086         * exists in the pool. If one is found, it is removed from the pool and returned. If not, a new instance is created
087         * and returned.
088         *
089         * <p>This method won't return the same buffer instance until it has been released with
090         * {@link #releaseByteArray(byte[])}.</p>
091         *
092         * <p>This method is a shorthand for {@link #getBuffer(com.mucommander.io.BufferPool.BufferFactory,int)} called
093         * with a {@link com.mucommander.io.BufferPool.ByteArrayFactory} instance.</p>.
094         *
095         * @param length length of the byte array
096         * @return a byte array of the specified size
097         */
098        public static synchronized byte[] getByteArray(int length) {
099            return (byte[])getBuffer(new ByteArrayFactory(), length);
100        }
101    
102        /**
103         * Convenience method that has the same effect as calling {@link #getCharArray(int)} with
104         * a length equal to {@link #getDefaultBufferSize()}.
105         *
106         * @return a char array with a length of {@link # getDefaultBufferSize}
107         */
108        public static synchronized char[] getCharArray() {
109            return getCharArray(getDefaultBufferSize());
110        }
111    
112        /**
113         * Returns a char array of the specified length. This method first checks if a char array of the specified length
114         * exists in the pool. If one is found, it is removed from the pool and returned. If not, a new instance is created
115         * and returned.
116         *
117         * <p>This method won't return the same buffer instance until it has been released with
118         * {@link #releaseCharArray(char[])}.</p>
119         *
120         * <p>This method is a shorthand for {@link #getBuffer(com.mucommander.io.BufferPool.BufferFactory,int)} called
121         * with a {@link com.mucommander.io.BufferPool.CharArrayFactory} instance.</p>.
122         *
123         * @param length length of the char array
124         * @return a char array of the specified length
125         */
126        public static synchronized char[] getCharArray(int length) {
127            return (char[])getBuffer(new CharArrayFactory(), length);
128        }
129    
130        /**
131         * Convenience method that has the same effect as calling {@link #getByteBuffer(int)} with
132         * a buffer capacity of {@link #getDefaultBufferSize()}.
133         *
134         * @return a ByteBuffer with a capacity equal to {@link # getDefaultBufferSize}
135         */
136        public static synchronized ByteBuffer getByteBuffer() {
137            return getByteBuffer(getDefaultBufferSize());
138        }
139    
140        /**
141         * Returns a ByteBuffer of the specified capacity. This method first checks if a ByteBuffer instance of the
142         * specified capacity exists in the pool. If one is found, it is removed from the pool and returned. If not,
143         * a new instance is created and returned.
144         *
145         * <p>This method won't return the same buffer instance until it has been released with
146         * {@link #releaseByteBuffer(ByteBuffer)}.</p>
147         *
148         * <p>This method is a shorthand for {@link #getBuffer(com.mucommander.io.BufferPool.BufferFactory,int)} called
149         * with a {@link com.mucommander.io.BufferPool.ByteBufferFactory} instance.</p>.
150    
151         * @param capacity capacity of the ByteBuffer
152         * @return a ByteBuffer with the specified capacity
153         */
154        public static synchronized ByteBuffer getByteBuffer(int capacity) {
155            return (ByteBuffer)getBuffer(new ByteBufferFactory(), capacity);
156        }
157    
158    
159        /**
160         * Convenience method that has the same effect as calling {@link #getCharBuffer(int)} with
161         * a buffer capacity of {@link #getDefaultBufferSize()}.
162         *
163         * @return a CharBuffer with a capacity equal to {@link # getDefaultBufferSize}
164         */
165        public static synchronized CharBuffer getCharBuffer() {
166            return getCharBuffer(getDefaultBufferSize());
167        }
168    
169        /**
170         * Returns a CharBuffer of the specified capacity. This method first checks if a CharBuffer instance of the
171         * specified capacity exists in the pool. If one is found, it is removed from the pool and returned. If not,
172         * a new instance is created and returned.
173         *
174         * <p>This method won't return the same buffer instance until it has been released with
175         * {@link #releaseCharBuffer(CharBuffer)}.</p>
176         *
177         * <p>This method is a shorthand for {@link #getBuffer(com.mucommander.io.BufferPool.BufferFactory,int)} called
178         * with a {@link com.mucommander.io.BufferPool.CharBufferFactory} instance.</p>.
179    
180         * @param capacity capacity of the CharBuffer
181         * @return a CharBuffer with the specified capacity
182         */
183        public static synchronized CharBuffer getCharBuffer(int capacity) {
184            return (CharBuffer)getBuffer(new CharBufferFactory(), capacity);
185        }
186    
187    
188        /**
189         * Convenience method that has the same effect as calling {@link #getBuffer(com.mucommander.io.BufferPool.BufferFactory, int)}
190         * with a size equal to {@link #getDefaultBufferSize()}.
191         *
192         * @param factory BufferFactory used to identify the target buffer class and create a new buffer (if necessary)
193         * @return a buffer with a size equal to {@link # getDefaultBufferSize}
194         */
195        public static synchronized Object getBuffer(BufferFactory factory) {
196            return getBuffer(factory, getDefaultBufferSize());
197        }
198    
199        /**
200         * Returns a byte array of the specified size. This method first checks if a buffer the same size as the specified
201         * one and a class compatible with the specified factory exists in the pool. If one is found, it is removed from the
202         * pool and returned.
203         * If not, a new instance is created and returned using {@link BufferFactory#newBuffer(int)}.
204         *
205         * <p>This method won't return the same buffer instance until it has been released with
206         * {@link #releaseBuffer(Object, BufferFactory)}.</p>
207         *
208         * @param factory BufferFactory used to identify the target buffer class and create a new buffer (if necessary)
209         * @param size size of the buffer
210         * @return a buffer of the specified size
211         */
212        public static synchronized Object getBuffer(BufferFactory factory, int size) {
213            int nbBuffers = bufferContainers.size();
214            BufferContainer bufferContainer;
215            Object buffer;
216    
217            // Looks for a buffer container in the pool that matches the specified size and buffer class.
218            for(int i=0; i<nbBuffers; i++) {
219                bufferContainer = (BufferContainer) bufferContainers.elementAt(i);
220                buffer = bufferContainer.getBuffer();
221    
222                // Caution: mind the difference between BufferContainer#getLength() and BufferContainer#getSize()
223                if(bufferContainer.getLength()==size && (factory.matchesBufferClass(buffer.getClass()))) {
224                    bufferContainers.removeElementAt(i);
225                    poolSize -= bufferContainer.getSize();
226    //                if(Debug.ON) Debug.trace("Returning buffer "+buffer+", size="+size);
227                    return buffer;
228                }
229            }
230    
231            if(Debug.ON) Debug.trace("Creating new buffer with "+factory+" size="+size, 3);
232    
233            // No buffer with the same class and size found in the pool, create a new one and return it
234            return factory.newBuffer(size);
235        }
236    
237    
238        /**
239         * Makes the given buffer available for further calls to {@link #getByteArray(int)} with the same buffer length.
240         * Returns <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in
241         * the pool.
242         *
243         * <p>After calling this method, the given buffer instance <b>must not be used</b>, otherwise it could get
244         * corrupted if other threads were using it.</p>
245         *
246         * @param buffer the buffer instance to make available for further use
247         * @return <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in the pool
248         * @throws IllegalArgumentException if specified buffer is null
249         */
250        public static synchronized boolean releaseByteArray(byte buffer[]) {
251            return releaseBuffer(buffer, new ByteArrayFactory());
252        }
253    
254        /**
255         * Makes the given buffer available for further calls to {@link #getCharArray(int)} with the same buffer length.
256         * Returns <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in
257         * the pool.
258         *
259         * <p>After calling this method, the given buffer instance <b>must not be used</b>, otherwise it could get
260         * corrupted if other threads were using it.</p>
261         *
262         * @param buffer the buffer instance to make available for further use
263         * @return <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in the pool
264         * @throws IllegalArgumentException if specified buffer is null
265         */
266        public static synchronized boolean releaseCharArray(char buffer[]) {
267            return releaseBuffer(buffer, new CharArrayFactory());
268        }
269    
270        /**
271         * Makes the given buffer available for further calls to {@link #getByteBuffer(int)} with the same buffer capacity.
272         * Returns <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in
273         * the pool.
274         *
275         * <p>After calling this method, the given buffer instance <b>must not be used</b>, otherwise it could get
276         * corrupted if other threads were using it.</p>
277         *
278         * @param buffer the buffer instance to make available for further use
279         * @return <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in the pool
280         * @throws IllegalArgumentException if specified buffer is null
281         */
282        public static synchronized boolean releaseByteBuffer(ByteBuffer buffer) {
283            return releaseBuffer(buffer, new ByteBufferFactory());
284        }
285    
286        /**
287         * Makes the given buffer available for further calls to {@link #getCharBuffer(int)} with the same buffer capacity.
288         * Returns <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in
289         * the pool.
290         *
291         * <p>After calling this method, the given buffer instance <b>must not be used</b>, otherwise it could get
292         * corrupted if other threads were using it.</p>
293         *
294         * @param buffer the buffer instance to make available for further use
295         * @return <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in the pool
296         * @throws IllegalArgumentException if specified buffer is null
297         */
298        public static synchronized boolean releaseCharBuffer(CharBuffer buffer) {
299            return releaseBuffer(buffer, new CharBufferFactory());
300        }
301    
302        /**
303         * Makes the given buffer available for further calls to {@link #getBuffer(com.mucommander.io.BufferPool.BufferFactory,int)} with the same buffer
304         * size and factory.
305         * Returns <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in 
306         * the pool or the pool size limit has been reached.
307         *
308         * <p>After calling this method, the given buffer instance <b>must not be used</b>, otherwise it could get
309         * corrupted if other threads were using it.</p>
310         *
311         * @param buffer the buffer instance to make available for further use
312         * @param factory the BufferFactory that was used to create the buffer
313         * @return <code>true</code> if the buffer was added to the pool, <code>false</code> if the buffer was already in the pool or the pool size limit has been reached
314         * @throws IllegalArgumentException if specified buffer is null
315         */
316        public static synchronized boolean releaseBuffer(Object buffer, BufferFactory factory) {
317            if(buffer==null)
318                throw new IllegalArgumentException("specified buffer is null");
319    
320            BufferContainer bufferContainer = factory.newBufferContainer(buffer);
321    
322            if(bufferContainers.contains(bufferContainer)) {
323                if(Debug.ON) Debug.trace("Warning: specified buffer is already in the pool: "+buffer, -1);
324                return false;
325            }
326    
327            long bufferSize = bufferContainer.getSize();        // size in bytes (!= length)
328    
329            if(maxPoolSize!=-1 && poolSize+bufferSize>maxPoolSize) {
330                if(Debug.ON) Debug.trace("Warning: maximum pool size reached, buffer not added to the pool"+buffer);
331                return false;
332            }
333    
334    //        if(Debug.ON) Debug.trace("Adding buffer to pool: "+buffer);
335    
336            bufferContainers.add(bufferContainer);
337            poolSize += bufferSize;
338    
339            return true;
340        }
341    
342        /**
343         * Returns <code>true</code> if the specified buffer is currently in the pool.
344         *
345         * <p>Note that it is not necessary (and thus not recommended for performance reasons) to call this method before
346         * calling <code>release*Buffer</code> as it already performs this test before adding a buffer to the pool.</p>
347         *
348         * @param buffer the buffer to look for in the pool
349         * @param factory the BufferFactory that was used to create the buffer
350         * @return <code>true</code> if the specified buffer is already in the pool
351         */
352        public static boolean containsBuffer(Object buffer, BufferFactory factory) {
353            return bufferContainers.contains(factory.newBufferContainer(buffer));
354        }
355    
356    
357        /**
358         * Returns the number of buffers that currently are in the pool. This method is provided for debugging
359         * purposes only.
360         *
361         * @return the number of buffers currently in the pool
362         */
363        public static int getBufferCount() {
364            return bufferContainers.size();
365        }
366    
367        /**
368         * Returns the number of buffers that currently are in the pool and whose Class are the same as the specified
369         * factory's. This method is provided for debugging purposes only.
370         *
371         * @param factory the BufferFactory
372         * @return the number of buffers currently in the pool
373         */
374        public static int getBufferCount(BufferFactory factory) {
375            int count = 0;        
376            int nbBuffers = bufferContainers.size();
377            for(int i=0; i<nbBuffers; i++) {
378                if(factory.matchesBufferClass(((BufferContainer)bufferContainers.elementAt(i)).getBuffer().getClass())) {
379                    count ++;
380                }
381            }
382    
383            return count;
384        }
385    
386        /**
387         * Returns the default size of buffers returned by <code>get*Buffer</code> methods without a <code>size</code>
388         * argument.
389         *
390         * <p>The default buffer size is initially set to {@link #INITIAL_DEFAULT_BUFFER_SIZE}.</p>
391         *
392         * @return the default size of buffers returned by <code>get*Buffer</code> methods without a <code>size</code> argument
393         */
394        public static int getDefaultBufferSize() {
395            return defaultBufferSize;
396        }
397    
398        /**
399         * Sets the default size of buffers returned by <code>get*Buffer</code> methods without a <code>size</code> argument.
400         *
401         * @param bufferSize the new buffer size
402         */
403        public static synchronized void setDefaultBufferSize(int bufferSize) {
404            BufferPool.defaultBufferSize = bufferSize;
405        }
406    
407    
408        /**
409         * Returns the combined size in bytes of all buffers that are currently in the pool.
410         *
411         * @return the combined size in bytes of all buffers that are currenty in the pool
412         */
413        public static long getPoolSize() {
414            return poolSize;
415        }
416    
417        /**
418         * Returns the maximum combined size in bytes for all buffers in the pool, <code>-1</code> for no limit.
419         * Before adding a buffer to the pool, <code>release*Buffer</code> methods ensure that the pool size will
420         * not be exceeded. If and only if that is the case, the buffer is added to the pool.
421         *
422         * <p>The max pool size is initially set to {@link #INITIAL_POOL_LIMIT}.</p>
423         *
424         * @return the maximum combined size in bytes for all buffers in the pool
425         */
426        public static long getMaxPoolSize() {
427            return maxPoolSize;
428        }
429    
430        /**
431         * Sets the maximum combined size in bytes for all buffers in the pool, <code>-1</code> for no limit.
432         * Before adding a buffer to the pool, <code>release*Buffer</code> methods ensure that the pool size will
433         * not be exceeded. If and only if that is the case, the buffer is added to the pool.
434         *
435         * @param maxPoolSize the maximum combined size in bytes for all buffers in the pool
436         */
437        public static synchronized void setMaxPoolSize(long maxPoolSize) {
438            BufferPool.maxPoolSize = maxPoolSize;
439        }
440    
441    
442        ///////////////////
443        // Inner classes //
444        ///////////////////
445    
446        /**
447         * Wraps a buffer instance and provides information about the wrapped buffer.
448         */
449        public static abstract class BufferContainer {
450    
451            /** The wrapped buffer instance */
452            protected Object buffer;
453    
454            /**
455             * Creates a new BufferContainer that wraps the given buffer.
456             *
457             * @param buffer the buffer instance to wrap
458             */
459            protected BufferContainer(Object buffer) {
460                this.buffer = buffer;
461            }
462    
463            /**
464             * Returns the wrapped buffer instance.
465             *
466             * @return the wrapped buffer instance
467             */
468            protected Object getBuffer() {
469                return buffer;
470            }
471    
472            /**
473             * Implements a shallow equal comparison.
474             */
475            public boolean equals(Object o) {
476                // Note: this method is used by Vector.contains()
477                return (o instanceof BufferContainer) && buffer == ((BufferContainer)o).buffer;
478            }
479    
480            /**
481             * Returns the length of the wrapped buffer instance.
482             *
483             * @return the length of the wrapped buffer instance
484             */
485            protected abstract int getLength();
486    
487            /**
488             * Returns the size of the wrapped buffer instance, expressed in bytes.
489             *
490             * @return the size of the wrapped buffer instance, expressed in bytes
491             */
492            protected abstract int getSize();
493        }
494    
495        /**
496         * A BufferFactory is responsible for creating buffer and {@link BufferContainer} instances, and for returning the buffer
497         * Class. The Class returned by {@link #getBufferClass()} may be a superclass or superinterface of the actual
498         * objects returned by {@link #newBuffer(int)}.
499         */
500        public static abstract class BufferFactory {
501    
502            /**
503             * Returns <code>true</code> if the class returned by {@link #getBufferClass()} is equal or a
504             * superclass/superinterface of the specified buffer class.
505             *
506             * @param bufferClass the buffer Class to test
507             * @return true if the class returned by <code>#getBufferClass()</code> is equal or a superclass/superinterface
508             * of the specified buffer class
509             */
510            public boolean matchesBufferClass(Class bufferClass) {
511                return getBufferClass().isAssignableFrom(bufferClass);
512            }
513    
514            /**
515             * Creates and returns a buffer instance of the specified size.
516             *
517             * @param size size of the buffer to create
518             * @return a buffer instance of the specified size
519             */
520            public abstract Object newBuffer(int size);
521    
522            /**
523             * Creates and returns a {@link BufferContainer} for the specified buffer instance.
524             *
525             * @param buffer the buffer to wrap in a BufferContainer
526             * @return returns a BufferContainer for the specified buffer instance
527             */
528            public abstract BufferContainer newBufferContainer(Object buffer);
529    
530            /**
531             * Returns the Class of buffer instances this factory creates. 
532             *
533             * @return the Class of buffer instances this factory creates
534             */
535            public abstract Class getBufferClass();
536        }
537    
538        /**
539         * This class is a {@link BufferFactory} implementation for byte array (<code>byte[]</code>) buffers.
540         */
541        public static class ByteArrayFactory extends BufferFactory {
542            public Object newBuffer(int size) {
543                return new byte[size];
544            }
545    
546            public BufferContainer newBufferContainer(Object buffer) {
547                return new BufferContainer(buffer) {
548                    protected int getLength() {
549                        return ((byte[])buffer).length;
550                    }
551    
552                    protected int getSize() {
553                        return getLength();
554                    }
555                };
556            }
557    
558            public Class getBufferClass() {
559                return byte[].class;
560            }
561        }
562    
563        /**
564         * This class is a {@link BufferFactory} implementation for char array (<code>char[]</code>) buffers.
565         */
566        public static class CharArrayFactory extends BufferFactory {
567            public Object newBuffer(int size) {
568                return new char[size];
569            }
570    
571            public BufferContainer newBufferContainer(Object buffer) {
572                return new BufferContainer(buffer) {
573                    protected int getLength() {
574                        return ((char[])buffer).length;
575                    }
576    
577                    protected int getSize() {
578                        return 2*getLength();
579                    }
580                };
581            }
582    
583            public Class getBufferClass() {
584                return char[].class;
585            }
586        }
587    
588        /**
589         * This class is a {@link BufferFactory} implementation for <code>java.nio.ByteBuffer</code> buffers.
590         * ByteBuffer instances created by {@link #newBuffer(int)} are direct ; the actually Class of those instances may be actually
591         * be <code>java.nio.DirectByteBuffer</code> and not <code>java.nio.ByteBuffer</code> as returned by
592         * {@link #getBufferClass()}.
593         */
594        public static class ByteBufferFactory extends BufferFactory {
595            public Object newBuffer(int size) {
596                // Note: the returned instance is actually a java.nio.DirectByteBuffer, this is why it's important to
597                // compare classes using Class#isAssignableFrom(Class)
598                return ByteBuffer.allocateDirect(size);
599            }
600    
601            public BufferContainer newBufferContainer(Object buffer) {
602                return new BufferContainer(buffer) {
603                    protected int getLength() {
604                        return ((ByteBuffer)buffer).capacity();
605                    }
606    
607                    protected int getSize() {
608                        return getLength();
609                    }
610                };
611            }
612    
613            public Class getBufferClass() {
614                return ByteBuffer.class;
615            }
616        }
617    
618        /**
619         * This class is a {@link BufferFactory} implementation for <code>java.nio.CharBuffer</code> buffers.
620         */
621        public static class CharBufferFactory extends BufferFactory {
622            public Object newBuffer(int size) {
623                return CharBuffer.allocate(size);
624            }
625    
626            public BufferContainer newBufferContainer(Object buffer) {
627                return new BufferContainer(buffer) {
628                    protected int getLength() {
629                        return ((CharBuffer)buffer).capacity();
630                    }
631    
632                    protected int getSize() {
633                        return 2*getLength();
634                    }
635                };
636            }
637    
638            public Class getBufferClass() {
639                return CharBuffer.class;
640            }
641        }
642    }