//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.io;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import org.eclipse.jetty.util.BufferUtil;

A ByteBuffer pool.

Acquired buffers may be released but they do not need to; if they are released, they may be recycled and reused, otherwise they will be garbage collected as usual.

/** * <p>A {@link ByteBuffer} pool.</p> * <p>Acquired buffers may be {@link #release(ByteBuffer) released} but they do not need to; * if they are released, they may be recycled and reused, otherwise they will be garbage * collected as usual.</p> */
public interface ByteBufferPool {

Requests a ByteBuffer of the given size.

The returned buffer may have a bigger capacity than the size being requested but it will have the limit set to the given size.

Params:
  • size – the size of the buffer
  • direct – whether the buffer must be direct or not
See Also:
Returns:the requested buffer
/** * <p>Requests a {@link ByteBuffer} of the given size.</p> * <p>The returned buffer may have a bigger capacity than the size being * requested but it will have the limit set to the given size.</p> * * @param size the size of the buffer * @param direct whether the buffer must be direct or not * @return the requested buffer * @see #release(ByteBuffer) */
public ByteBuffer acquire(int size, boolean direct);

Returns a ByteBuffer, usually obtained with acquire(int, boolean) (but not necessarily), making it available for recycling and reuse.

Params:
  • buffer – the buffer to return
See Also:
/** * <p>Returns a {@link ByteBuffer}, usually obtained with {@link #acquire(int, boolean)} * (but not necessarily), making it available for recycling and reuse.</p> * * @param buffer the buffer to return * @see #acquire(int, boolean) */
public void release(ByteBuffer buffer);

Removes a ByteBuffer that was previously obtained with acquire(int, boolean).

The buffer will not be available for further reuse.

Params:
  • buffer – the buffer to remove
See Also:
/** * <p>Removes a {@link ByteBuffer} that was previously obtained with {@link #acquire(int, boolean)}.</p> * <p>The buffer will not be available for further reuse.</p> * * @param buffer the buffer to remove * @see #acquire(int, boolean) * @see #release(ByteBuffer) */
default void remove(ByteBuffer buffer) { }

Creates a new ByteBuffer of the given capacity and the given directness.

Params:
  • capacity – the ByteBuffer capacity
  • direct – the ByteBuffer directness
Returns:a newly allocated ByteBuffer
/** * <p>Creates a new ByteBuffer of the given capacity and the given directness.</p> * * @param capacity the ByteBuffer capacity * @param direct the ByteBuffer directness * @return a newly allocated ByteBuffer */
default ByteBuffer newByteBuffer(int capacity, boolean direct) { return direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity); } public static class Lease { private final ByteBufferPool byteBufferPool; private final List<ByteBuffer> buffers; private final List<Boolean> recycles; public Lease(ByteBufferPool byteBufferPool) { this.byteBufferPool = byteBufferPool; this.buffers = new ArrayList<>(); this.recycles = new ArrayList<>(); } public ByteBuffer acquire(int capacity, boolean direct) { ByteBuffer buffer = byteBufferPool.acquire(capacity, direct); BufferUtil.clearToFill(buffer); return buffer; } public void append(ByteBuffer buffer, boolean recycle) { buffers.add(buffer); recycles.add(recycle); } public void insert(int index, ByteBuffer buffer, boolean recycle) { buffers.add(index, buffer); recycles.add(index, recycle); } public List<ByteBuffer> getByteBuffers() { return buffers; } public long getTotalLength() { long length = 0; for (ByteBuffer buffer : buffers) { length += buffer.remaining(); } return length; } public int getSize() { return buffers.size(); } public void recycle() { for (int i = 0; i < buffers.size(); ++i) { ByteBuffer buffer = buffers.get(i); if (recycles.get(i)) release(buffer); } buffers.clear(); recycles.clear(); } public void release(ByteBuffer buffer) { byteBufferPool.release(buffer); } } public static class Bucket { private final Deque<ByteBuffer> _queue = new ConcurrentLinkedDeque<>(); private final int _capacity; private final int _maxSize; private final AtomicInteger _size; private long _lastUpdate = System.nanoTime(); public Bucket(int capacity, int maxSize) { _capacity = capacity; _maxSize = maxSize; _size = maxSize > 0 ? new AtomicInteger() : null; } public ByteBuffer acquire() { ByteBuffer buffer = queuePoll(); if (buffer == null) return null; if (_size != null) _size.decrementAndGet(); return buffer; } public void release(ByteBuffer buffer) { _lastUpdate = System.nanoTime(); BufferUtil.clear(buffer); if (_size == null) queueOffer(buffer); else if (_size.incrementAndGet() <= _maxSize) queueOffer(buffer); else _size.decrementAndGet(); } public void clear() { clear(null); } void clear(Consumer<ByteBuffer> memoryFn) { int size = _size == null ? 0 : _size.get() - 1; while (size >= 0) { ByteBuffer buffer = queuePoll(); if (buffer == null) break; if (memoryFn != null) memoryFn.accept(buffer); if (_size != null) { _size.decrementAndGet(); --size; } } } private void queueOffer(ByteBuffer buffer) { _queue.offerFirst(buffer); } private ByteBuffer queuePoll() { return _queue.poll(); } boolean isEmpty() { return _queue.isEmpty(); } int size() { return _queue.size(); } long getLastUpdate() { return _lastUpdate; } @Override public String toString() { return String.format("%s@%x{%d/%d@%d}", getClass().getSimpleName(), hashCode(), size(), _maxSize, _capacity); } } }