package org.embulk.exec; import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.ByteBuf; import org.embulk.spi.Buffer; import org.embulk.spi.BufferAllocator; import com.google.inject.Inject; import org.embulk.config.ConfigSource; import org.embulk.spi.unit.ByteSize; public class PooledBufferAllocator implements BufferAllocator { private static final int DEFAULT_PAGE_SIZE = 32*1024; private final PooledByteBufAllocator nettyBuffer; private final int pageSize; @Inject public PooledBufferAllocator(@ForSystemConfig ConfigSource systemConfig, org.slf4j.ILoggerFactory factory) { this.nettyBuffer = new PooledByteBufAllocator(false); this.pageSize = systemConfig.get(ByteSize.class, "page_size", new ByteSize(DEFAULT_PAGE_SIZE)).getBytesInt(); } public Buffer allocate() { return allocate(pageSize); } public Buffer allocate(int minimumCapacity) { int size = this.pageSize; while (size < minimumCapacity) { size *= 2; } return new NettyByteBufBuffer(nettyBuffer.buffer(size)); } private static class NettyByteBufBuffer extends Buffer { private ByteBuf buf; private BufferReleasedBeforeAt doubleFreeCheck; public NettyByteBufBuffer(ByteBuf buf) { super(buf.array(), buf.arrayOffset(), buf.capacity()); this.buf = buf; } public void release() { if (doubleFreeCheck != null) { new BufferDoubleReleasedException(doubleFreeCheck).printStackTrace(); } if (buf != null) { buf.release(); buf = null; doubleFreeCheck = new BufferReleasedBeforeAt(); } } } static class BufferReleasedBeforeAt extends Throwable { } static class BufferDoubleReleasedException extends IllegalStateException { public BufferDoubleReleasedException(BufferReleasedBeforeAt releasedAt) { super("Detected double release() call of a buffer", releasedAt); } } }