/// <summary>
        /// Returns a buffer of the given size or greater.
        /// </summary>
        /// <exception cref="BufferAcquisitionException">If the buffer could
        /// not be taken from a pool, and the ActionOnBufferUnavailable
        /// is set to ThrowException, or if the specified size is greater
        /// than the maximum buffer size.</exception>
        /// <exception cref="ArgumentOutOfRangeException">If minimumSize is less than 0.</exception>
        public IBuffer GetBuffer(int minimumSize)
        {
            if (minimumSize < 0)
            {
                throw new ArgumentOutOfRangeException("minimumSize must be greater than or equal to 0");
            }
            if (minimumSize > options.MaxBufferSize)
            {
                throw new BufferAcquisitionException("Requested buffer " + minimumSize + " is larger " +
                                                     "than maximum buffer size " + options.MaxBufferSize);
            }

            // Work out the size of buffer to use, and where in the list to find
            // cached buffers
            int listIndex = 0;
            int size      = options.MinBufferSize;

            while (size < minimumSize)
            {
                size = CalculateNextSizeBand(size);
                listIndex++;
            }

            // Loop in case we need to find the next size up
            while (true)
            {
                CachedBuffer ret = FindAvailableBuffer(listIndex, size);
                if (ret != null)
                {
                    return(ret);
                }

                switch (options.ActionOnBufferUnavailable)
                {
                case Options.BufferUnavailableAction.ReturnUncached:
                    return(new CachedBuffer(minimumSize, options.ClearAfterUse));

                case Options.BufferUnavailableAction.ThrowException:
                    throw new BufferAcquisitionException("No buffers available");
                }
                // Must be "use bigger". Use an uncached buffer if we've reached the maximum size,
                // otherwise try the next size band
                if (size == options.MaxBufferSize)
                {
                    return(new CachedBuffer(minimumSize, options.ClearAfterUse));
                }
                size = CalculateNextSizeBand(size);
                listIndex++;
            }
        }
        /// <summary>
        /// Finds an available buffer from the list, creating a new buffer
        /// where appropriate.
        /// </summary>
        /// <param name="listIndex">Index into the list of buffer slots</param>
        /// <param name="size">Size of buffer to create if necessary</param>
        /// <returns>An available buffer, or null if none are available in the given band.</returns>
        CachedBuffer FindAvailableBuffer(int listIndex, int size)
        {
            using (padlock.Lock())
            {
                // Make sure there'll be an entry, even if it's null
                while (listIndex >= bufferBands.Count)
                {
                    bufferBands.Add(null);
                }

                // Create a new array of buffers if necessary
                CachedBuffer[] buffers = bufferBands[listIndex];
                if (buffers == null)
                {
                    buffers = new CachedBuffer[options.MaxBuffersPerSizeBand];
                    bufferBands[listIndex] = buffers;
                }

                // Look through all the buffers in this band for an available one, or an unused slot
                for (int i = 0; i < buffers.Length; i++)
                {
                    // No cached unused buffers. Create a new one.
                    if (buffers[i] == null)
                    {
                        buffers[i] = new CachedBuffer(size, options.ClearAfterUse);
                        return(buffers[i]);
                    }
                    if (buffers[i].Available)
                    {
                        buffers[i].Available = false;
                        return(buffers[i]);
                    }
                }
                return(null);
            }
        }
        /// <summary>
        /// Finds an available buffer from the list, creating a new buffer
        /// where appropriate.
        /// </summary>
        /// <param name="listIndex">Index into the list of buffer slots</param>
        /// <param name="size">Size of buffer to create if necessary</param>
        /// <returns>An available buffer, or null if none are available in the given band.</returns>
        CachedBuffer FindAvailableBuffer(int listIndex, int size)
        {
            using (padlock.Lock())
            {
                // Make sure there'll be an entry, even if it's null
                while (listIndex >= bufferBands.Count)
                {
                    bufferBands.Add(null);
                }

                // Create a new array of buffers if necessary
                CachedBuffer[] buffers = bufferBands[listIndex];
                if (buffers == null)
                {
                    buffers = new CachedBuffer[options.MaxBuffersPerSizeBand];
                    bufferBands[listIndex] = buffers;
                }

                // Look through all the buffers in this band for an available one, or an unused slot
                for (int i = 0; i < buffers.Length; i++)
                {
                    // No cached unused buffers. Create a new one.
                    if (buffers[i] == null)
                    {
                        buffers[i] = new CachedBuffer(size, options.ClearAfterUse);
                        return buffers[i];
                    }
                    if (buffers[i].Available)
                    {
                        buffers[i].Available = false;
                        return buffers[i];
                    }
                }
                return null;
            }
        }