/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.jlan.server.memory;
import java.util.Vector;
/**
* Byte Buffer List Class
*
* <p>Contains a list of byte buffers of the same size. The list has an initial and maximum
* size.
*
* @author gkspencer
*/
public class ByteBufferList {
// Buffer size, initial allocation and maximum allocation
private int m_bufSize;
private int m_initAlloc;
private int m_maxAlloc;
// Byte buffers
private Vector<byte[]> m_bufList;
// Count of buffers currently allocated out
private int m_allocCount;
// Statistics
private long m_statAllocs;
private long m_statWaits;
private long m_statWaitExpired;
/**
* Class constructor
*
* @param bufSize int
* @param initAlloc int
* @param maxAlloc
*/
public ByteBufferList(int bufSize, int initAlloc, int maxAlloc) {
m_bufSize = bufSize;
m_initAlloc = initAlloc;
m_maxAlloc = maxAlloc;
// Validate the settings
if ( m_bufSize <= 0 || m_initAlloc < 0 || m_maxAlloc <= 0 || (m_initAlloc > m_maxAlloc))
throw new RuntimeException("Invalid ByteBufferList parameters, size=" + m_bufSize + ", alloc=" + m_initAlloc + "/" + m_maxAlloc);
// Allocate the initial buffer
allocateInitialBuffers();
}
/**
* Return the buffer size
*
* @return int
*/
public final int getBufferSize() {
return m_bufSize;
}
/**
* Return the initial allocation size
*
* @return int
*/
public final int getInitialAllocation() {
return m_initAlloc;
}
/**
* Return the maximum allocation size
*
* @return int
*/
public final int getMaximumAllocation() {
return m_maxAlloc;
}
/**
* Return the count of available buffers
*
* @return int
*/
public final int getAvailableCount() {
return m_bufList.size();
}
/**
* Return the count of buffers currently allocated out
*
* @return int
*/
public final int getAllocatedCount() {
return m_allocCount;
}
/**
* Return the allocations statistic
*
* @return long
*/
public final long getStatAllocationCounter() {
return m_statAllocs;
}
/**
* Return the allocation wait statistic
*
* @return long
*/
public final long getStatAllocationWaits() {
return m_statWaits;
}
/**
* Return the allocation wait expired statistic
*
* @return long
*/
public final long getStatAllocationWaitsExpired() {
return m_statWaitExpired;
}
/**
* Allocate a buffer
*
* @param long waitTime
* @return byte[]
*/
public final byte[] allocateBuffer( long waitTime) {
// Use the buffer list as the lock
byte[] buf = null;
synchronized (m_bufList) {
// Check if there is a buffer available
if ( m_bufList.size() > 0) {
// Remove a buffer from the available list
buf = m_bufList.remove( 0);
m_allocCount++;
// Update the stats
m_statAllocs++;
}
else if ( m_allocCount < m_maxAlloc) {
// Allocate a new buffer for this request
buf = new byte[ m_bufSize];
m_allocCount++;
// Update the stats
m_statAllocs++;
}
else if ( waitTime > 0) {
try {
// Update the stats
m_statWaits++;
// Wait for a buffer to be released
m_bufList.wait( waitTime);
// Check if there is a buffer
if ( m_bufList.size() > 0) {
buf = m_bufList.remove( 0);
m_allocCount++;
}
else {
// Update the stats
m_statWaitExpired++;
}
}
catch ( InterruptedException ex) {
}
}
}
// Return the allocated buffer, or null if there are no buffers available
return buf;
}
/**
* Release a buffer back to the pool
*
* @param buf byte[]
*/
public final void releaseBuffer( byte[] buf) {
// Make sure it is one of our buffers
if ( buf == null || buf.length != m_bufSize)
return;
// Use the buffer list as the lock
synchronized (m_bufList) {
// If the list is empty then signal that a buffer is available
if ( m_bufList.size() == 0)
m_bufList.notify();
// Release the buffer back to the available list
m_bufList.add( buf);
m_allocCount--;
}
}
/**
* Shrink the buffer list back to the initial allocation size
*
* @return Count of buffers released
*/
public final int shrinkList() {
// Check if the buffer list has more than the initial allocation of buffers
int removedCnt = 0;
if ( m_bufList.size() > m_initAlloc) {
// Use the buffer list as the lock
synchronized ( m_bufList) {
// Remove buffers from the available buffer list
while ( m_bufList.size() > m_initAlloc) {
m_bufList.remove( 0);
removedCnt++;
}
}
}
// Return the count of buffers removed from the list
return removedCnt;
}
/**
* Allocate the initial byte buffer list
*/
private final void allocateInitialBuffers() {
// Allocate the buffer list
if ( getInitialAllocation() > 0)
m_bufList = new Vector<byte[]>( getInitialAllocation());
else
m_bufList = new Vector<byte[]>();
// Allocte the byte buffers
if ( getInitialAllocation() > 0) {
for ( int i = 0; i < getInitialAllocation(); i++)
m_bufList.add( new byte[ getBufferSize()]);
}
}
/**
* Return the buffer list as a string
*
* @return String
*/
public String toString() {
StringBuilder str = new StringBuilder();
str.append("[Bufsize=");
str.append( getBufferSize());
str.append(",Init=");
str.append( getInitialAllocation());
str.append(",Max=");
str.append( getMaximumAllocation());
str.append(",Avail=");
str.append( getAvailableCount());
str.append(",Alloc=");
str.append( getAllocatedCount());
str.append(",Stats=");
str.append( getStatAllocationCounter());
str.append("/");
str.append( getStatAllocationWaits());
str.append("/");
str.append( getStatAllocationWaitsExpired());
str.append( "]");
return str.toString();
}
}