1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.accessorydisplay.common;
18
19import java.nio.ByteBuffer;
20
21/**
22 * Maintains a bounded pool of buffers.  Attempts to acquire buffers beyond the maximum
23 * count will block until other buffers are released.
24 */
25final class BufferPool {
26    private final int mInitialBufferSize;
27    private final int mMaxBufferSize;
28    private final ByteBuffer[] mBuffers;
29    private int mAllocated;
30    private int mAvailable;
31
32    public BufferPool(int initialBufferSize, int maxBufferSize, int maxBuffers) {
33        mInitialBufferSize = initialBufferSize;
34        mMaxBufferSize = maxBufferSize;
35        mBuffers = new ByteBuffer[maxBuffers];
36    }
37
38    public ByteBuffer acquire(int needed) {
39        synchronized (this) {
40            for (;;) {
41                if (mAvailable != 0) {
42                    mAvailable -= 1;
43                    return grow(mBuffers[mAvailable], needed);
44                }
45
46                if (mAllocated < mBuffers.length) {
47                    mAllocated += 1;
48                    return ByteBuffer.allocate(chooseCapacity(mInitialBufferSize, needed));
49                }
50
51                try {
52                    wait();
53                } catch (InterruptedException ex) {
54                }
55            }
56        }
57    }
58
59    public void release(ByteBuffer buffer) {
60        synchronized (this) {
61            buffer.clear();
62            mBuffers[mAvailable++] = buffer;
63            notifyAll();
64        }
65    }
66
67    public ByteBuffer grow(ByteBuffer buffer, int needed) {
68        int capacity = buffer.capacity();
69        if (capacity < needed) {
70            final ByteBuffer oldBuffer = buffer;
71            capacity = chooseCapacity(capacity, needed);
72            buffer = ByteBuffer.allocate(capacity);
73            oldBuffer.flip();
74            buffer.put(oldBuffer);
75        }
76        return buffer;
77    }
78
79    private int chooseCapacity(int capacity, int needed) {
80        while (capacity < needed) {
81            capacity *= 2;
82        }
83        if (capacity > mMaxBufferSize) {
84            if (needed > mMaxBufferSize) {
85                throw new IllegalArgumentException("Requested size " + needed
86                        + " is larger than maximum buffer size " + mMaxBufferSize + ".");
87            }
88            capacity = mMaxBufferSize;
89        }
90        return capacity;
91    }
92}
93