Jabit/core/src/main/java/ch/dissem/bitmessage/factory/BufferPool.java

108 lines
3.5 KiB
Java

/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.bitmessage.factory;
import ch.dissem.bitmessage.ports.NetworkHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.*;
/**
* A pool for {@link ByteBuffer}s. As they may use up a lot of memory,
* they should be reused as efficiently as possible.
*/
class BufferPool {
private static final Logger LOG = LoggerFactory.getLogger(BufferPool.class);
public static final BufferPool bufferPool = new BufferPool(256, 2048);
private final Map<Size, Integer> capacities = new EnumMap<>(Size.class);
private final Map<Size, Stack<ByteBuffer>> pools = new EnumMap<>(Size.class);
private BufferPool(int small, int medium) {
capacities.put(Size.HEADER, 24);
capacities.put(Size.SMALL, small);
capacities.put(Size.MEDIUM, medium);
capacities.put(Size.LARGE, NetworkHandler.MAX_PAYLOAD_SIZE);
pools.put(Size.HEADER, new Stack<ByteBuffer>());
pools.put(Size.SMALL, new Stack<ByteBuffer>());
pools.put(Size.MEDIUM, new Stack<ByteBuffer>());
pools.put(Size.LARGE, new Stack<ByteBuffer>());
}
public synchronized ByteBuffer allocate(int capacity) {
Size targetSize = getTargetSize(capacity);
Size s = targetSize;
do {
Stack<ByteBuffer> pool = pools.get(s);
if (!pool.isEmpty()) {
return pool.pop();
}
s = s.next();
} while (s != null);
LOG.debug("Creating new buffer of size " + targetSize);
return ByteBuffer.allocate(capacities.get(targetSize));
}
public synchronized ByteBuffer allocate() {
Stack<ByteBuffer> pool = pools.get(Size.HEADER);
if (!pool.isEmpty()) {
return pool.pop();
} else {
return ByteBuffer.allocate(capacities.get(Size.HEADER));
}
}
public synchronized void deallocate(ByteBuffer buffer) {
buffer.clear();
Size size = getTargetSize(buffer.capacity());
if (buffer.capacity() != capacities.get(size)) {
throw new IllegalArgumentException("Illegal buffer capacity " + buffer.capacity() +
" one of " + capacities.values() + " expected.");
}
pools.get(size).push(buffer);
}
private Size getTargetSize(int capacity) {
for (Size s : Size.values()) {
if (capacity <= capacities.get(s)) {
return s;
}
}
throw new IllegalArgumentException("Requested capacity too large: " +
"requested=" + capacity + "; max=" + capacities.get(Size.LARGE));
}
private enum Size {
HEADER, SMALL, MEDIUM, LARGE;
public Size next() {
switch (this) {
case SMALL:
return MEDIUM;
case MEDIUM:
return LARGE;
default:
return null;
}
}
}
}