io.appulse.utils.BytesPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of utils-java Show documentation
Show all versions of utils-java Show documentation
Utils for Appulse Java projects
/*
* Copyright 2019 the original author or authors.
*
* 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 io.appulse.utils;
import static io.appulse.utils.SizeUnit.KILOBYTES;
import static java.util.Locale.ENGLISH;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toCollection;
import static lombok.AccessLevel.PRIVATE;
import java.nio.charset.Charset;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.IntStream;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import lombok.val;
/**
* The customisable {@link Bytes} pool.
*
* @author Artem Labazin
* @since 1.15.0
*/
@FieldDefaults(level = PRIVATE, makeFinal = true)
public class BytesPool {
private static final int DEFAULT_INITIAL_BUFFERS_COUNT = 2;
private static final int DEFAULT_MAXIMUM_BUFFERS_COUNT = Integer.MAX_VALUE;
private static final int DEFAULT_INITIAL_BUFFER_SIZE = (int) KILOBYTES.toBytes(8);
int maximumBuffersCount;
int initialBufferSizeBytes;
Function bufferCreateFunction;
BlockingQueue buffers;
LongAdder acquiredBuffersCount;
LongAdder totalElements;
Lock createNewLock;
@Builder
BytesPool (Integer initialBuffersCount,
Integer maximumBuffersCount,
Integer initialBufferSizeBytes,
Function bufferCreateFunction
) {
int initialBuffers = ofNullable(initialBuffersCount)
.filter(it -> it >= 0)
.filter(it -> it <= maximumBuffersCount)
.orElse(DEFAULT_INITIAL_BUFFERS_COUNT);
this.maximumBuffersCount = ofNullable(maximumBuffersCount)
.filter(it -> it >= 0)
.filter(it -> it >= initialBuffers)
.orElse(DEFAULT_MAXIMUM_BUFFERS_COUNT);
this.initialBufferSizeBytes = ofNullable(initialBufferSizeBytes)
.filter(it -> it > 0)
.orElse(DEFAULT_INITIAL_BUFFER_SIZE);
this.bufferCreateFunction = bufferCreateFunction;
totalElements = new LongAdder();
acquiredBuffersCount = new LongAdder();
createNewLock = new ReentrantLock(true);
buffers = IntStream.range(0, initialBuffers)
.mapToObj(it -> bufferCreateFunction.apply(initialBufferSizeBytes))
.peek(it -> totalElements.increment())
.collect(toCollection(LinkedBlockingQueue::new));
}
/**
* Return the pooled bytes buffer.
*
* @return the pooled {@link Bytes} instance
*/
@SneakyThrows
public PooledBytes acquire () {
val buffer = getOrCreateBuffer();
acquiredBuffersCount.increment();
buffer.reset();
return new PooledBytes(buffer);
}
/**
* Return the pooled bytes buffer with guaranteed minimal size.
*
* @param minSize expected minimal buffer size in mytes
*
* @return the pooled {@link Bytes} instance
*/
public PooledBytes acquire (int minSize) {
val result = acquire();
if (result.capacity() < minSize) {
result.capacity(minSize);
}
return result;
}
/**
* Release the acquired bytes buffer.
*
* @param buffer the buffer to release
*/
@SuppressWarnings("PMD.AccessorMethodGeneration")
public void release (@NonNull PooledBytes buffer) {
if (buffer.parent != this) {
throw new IllegalArgumentException("The buffer not from this pool");
}
val detached = buffer.detach();
if (!buffers.offer(detached)) {
throw new IllegalStateException("Unexpected behaviour");
}
acquiredBuffersCount.decrement();
}
/**
* Returns the acquired buffers count.
*
* @return acquired count
*/
public int getAcquiredCount () {
return acquiredBuffersCount.intValue();
}
/**
* Returns the free buffers count.
*
* @return free count
*/
public int getFreeCount () {
return maximumBuffersCount - acquiredBuffersCount.intValue();
}
/**
* Returns the total buffers count.
*
* @return total count
*/
public int getTotalCount () {
return totalElements.intValue();
}
private Bytes getOrCreateBuffer () throws InterruptedException {
val buffer = buffers.poll();
if (buffer != null) {
return buffer;
}
createNewLock.lock();
try {
if (totalElements.sum() >= maximumBuffersCount) {
return buffers.take();
}
val newBuffer = bufferCreateFunction.apply(initialBufferSizeBytes);
totalElements.increment();
return newBuffer;
} finally {
createNewLock.unlock();
}
}
/**
* Specific {@link Bytes} implementation for the pools.
*/
@FieldDefaults(level = PRIVATE)
@SuppressWarnings("PMD.LinguisticNaming")
public class PooledBytes extends BytesAbstract {
Bytes delegate;
BytesPool parent = BytesPool.this;
PooledBytes (Bytes delegate) {
super();
this.delegate = delegate;
}
/**
* Releases the {@code this} Bytes instance in its pool back.
*/
public void release () {
BytesPool.this.release(this);
}
@Override
public boolean isAutoResizable () {
validate();
return delegate.isAutoResizable();
}
@Override
public Bytes writeNB (byte[] bytes, int offset, int length) {
validate();
return delegate.writeNB(bytes, offset, length);
}
@Override
public Bytes write1B (byte value) {
validate();
return delegate.write1B(value);
}
@Override
public Bytes write2B (short value) {
validate();
return delegate.write2B(value);
}
@Override
public Bytes write4B (int value) {
validate();
return delegate.write4B(value);
}
@Override
public Bytes write8B (long value) {
validate();
return delegate.write8B(value);
}
@Override
public Bytes setNB (int index, byte[] bytes, int offset, int length) {
validate();
return delegate.setNB(index, bytes, offset, length);
}
@Override
public Bytes set1B (int index, byte value) {
validate();
return delegate.set1B(index, value);
}
@Override
public Bytes set2B (int index, short value) {
validate();
return delegate.set2B(index, value);
}
@Override
public Bytes set4B (int index, int value) {
validate();
return delegate.set4B(index, value);
}
@Override
public Bytes set8B (int index, long value) {
validate();
return delegate.set8B(index, value);
}
@Override
public byte readByte () {
validate();
return delegate.readByte();
}
@Override
public short readShort () {
validate();
return delegate.readShort();
}
@Override
public int readInt () {
validate();
return delegate.readInt();
}
@Override
public long readLong () {
validate();
return delegate.readLong();
}
@Override
public float readFloat () {
validate();
return delegate.readFloat();
}
@Override
public double readDouble () {
validate();
return delegate.readDouble();
}
@Override
public char readChar () {
validate();
return delegate.readChar();
}
@Override
public Bytes readBytes (byte[] destination, int offset, int length) {
validate();
return delegate.readBytes(destination, offset, length);
}
@Override
public byte getByte (int index) {
validate();
return delegate.getByte(index);
}
@Override
public short getShort (int index) {
validate();
return delegate.getShort(index);
}
@Override
public int getInt (int index) {
validate();
return delegate.getInt(index);
}
@Override
public long getLong (int index) {
validate();
return delegate.getLong(index);
}
@Override
public float getFloat (int index) {
validate();
return delegate.getFloat(index);
}
@Override
public double getDouble (int index) {
validate();
return delegate.getDouble(index);
}
@Override
public char getChar (int index) {
validate();
return delegate.getChar(index);
}
@Override
public byte[] getBytes (int index, int length) {
validate();
return delegate.getBytes(index, length);
}
@Override
public String getString (int index, int length, Charset charset) {
validate();
return delegate.getString(index, length, charset);
}
@Override
public int capacity () {
validate();
return delegate.capacity();
}
@Override
public void capacity (int bytes) {
validate();
delegate.capacity(bytes);
}
@Override
public int writerIndex () {
validate();
return delegate.writerIndex();
}
@Override
public Bytes writerIndex (int newIndex) {
validate();
return delegate.writerIndex(newIndex);
}
@Override
public int readerIndex () {
validate();
return delegate.readerIndex();
}
@Override
public Bytes readerIndex (int newIndex) {
validate();
return delegate.readerIndex(newIndex);
}
@Override
public byte[] array () {
validate();
return delegate.array();
}
private void validate () {
if (delegate != null) {
return;
}
val msg = String.format(ENGLISH, "Pooled bytes buffer already released");
throw new IllegalStateException(msg);
}
@SuppressWarnings("PMD.NullAssignment")
private Bytes detach () {
val result = delegate;
delegate = null;
return result;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy