// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.io; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import org.eclipse.jetty.util.BufferUtil; public class MappedByteBufferPool implements ByteBufferPool { private final ConcurrentMap<Integer, Bucket> directBuffers = new ConcurrentHashMap<>(); private final ConcurrentMap<Integer, Bucket> heapBuffers = new ConcurrentHashMap<>(); private final int _factor; private final int _maxQueue; private final Function<Integer, Bucket> _newBucket; public MappedByteBufferPool() { this(-1); } public MappedByteBufferPool(int factor) { this(factor,-1,null); } public MappedByteBufferPool(int factor,int maxQueue) { this(factor,maxQueue,null); } public MappedByteBufferPool(int factor,int maxQueue,Function<Integer, Bucket> newBucket) { _factor = factor<=0?1024:factor; _maxQueue = maxQueue; _newBucket = newBucket!=null?newBucket:i->new Bucket(this,i*_factor,_maxQueue); } @Override public ByteBuffer acquire(int size, boolean direct) { int b = bucketFor(size); ConcurrentMap<Integer, Bucket> buffers = bucketsFor(direct); Bucket bucket = buffers.get(b); if (bucket==null) return newByteBuffer(b*_factor, direct); return bucket.acquire(direct); } @Override public void release(ByteBuffer buffer) { if (buffer == null) return; // nothing to do // validate that this buffer is from this pool assert((buffer.capacity() % _factor) == 0); int b = bucketFor(buffer.capacity()); ConcurrentMap<Integer, Bucket> buckets = bucketsFor(buffer.isDirect()); Bucket bucket = buckets.computeIfAbsent(b,_newBucket); bucket.release(buffer); } public void clear() { directBuffers.values().forEach(Bucket::clear); directBuffers.clear(); heapBuffers.values().forEach(Bucket::clear); heapBuffers.clear(); } private int bucketFor(int size) { int bucket = size / _factor; if (size % _factor > 0) ++bucket; return bucket; } // Package local for testing ConcurrentMap<Integer, Bucket> bucketsFor(boolean direct) { return direct ? directBuffers : heapBuffers; } public static class Tagged extends MappedByteBufferPool { private final AtomicInteger tag = new AtomicInteger(); @Override public ByteBuffer newByteBuffer(int capacity, boolean direct) { ByteBuffer buffer = super.newByteBuffer(capacity + 4, direct); buffer.limit(buffer.capacity()); buffer.putInt(tag.incrementAndGet()); ByteBuffer slice = buffer.slice(); BufferUtil.clear(slice); return slice; } } }