/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.common.buffer.impl; import java.nio.ByteBuffer; /** * Provides buffer slices or blocks off of a central * set of buffers. */ public class BlockByteBuffer { private static class ByteBufferHolder { int size; volatile ByteBuffer buffer; public ByteBufferHolder(int size) { this.size = size; } public ByteBuffer duplicate(boolean direct) { if (buffer == null) { synchronized (this) { if (buffer == null) { this.buffer = allocate(size, direct); } } } return buffer.duplicate(); } } private static class BlockByteBufferData { int blockAddressBits; int segmentAddressBits; int segmentSize; int blockSize; int blockCount; boolean direct; ByteBufferHolder[] origBuffers; } private BlockByteBufferData data; private ByteBuffer[] buffers; /** * Creates a new {@link BlockByteBuffer} where each buffer segment will be * 1 << segmentAddressBits (max of 30), and a total size of (1 << blockAddressBits)*blockCount. * @param segmentAddressBits * @param blockCount * @param blockAddressBits * @param direct */ public BlockByteBuffer(int segmentAddressBits, int blockCount, int blockAddressBits, boolean direct) { this.data = new BlockByteBufferData(); this.data.segmentAddressBits = segmentAddressBits; this.data.blockAddressBits = blockAddressBits; this.data.blockSize = 1 << blockAddressBits; this.data.segmentSize = 1 << this.data.segmentAddressBits; this.data.blockCount = blockCount; long size = ((long)blockCount)<<blockAddressBits; int fullSegments = (int)(size>>segmentAddressBits); int lastSegmentSize = (int) (size&(data.segmentSize-1)); int segments = fullSegments; if (lastSegmentSize > 0) { segments++; } this.data.origBuffers = new ByteBufferHolder[segments]; this.data.direct = direct; buffers = new ByteBuffer[segments]; for (int i = 0; i < fullSegments; i++) { this.data.origBuffers[i] = new ByteBufferHolder(data.segmentSize); } if (lastSegmentSize > 0) { this.data.origBuffers[fullSegments] = new ByteBufferHolder(lastSegmentSize); } } public ByteBuffer[] getBuffers() { return buffers; } private BlockByteBuffer() { } public static ByteBuffer allocate(int size, boolean direct) { if (direct) { ByteBuffer newBuffer = ByteBuffer.allocateDirect(size); int longsPerSegment = size>>3; //manually initialize until java 7 when it's mandated (although this may already have been performed) for (int j = 0; j < longsPerSegment; j++) { newBuffer.putLong(0); } return newBuffer; } return ByteBuffer.allocate(size); } public BlockByteBuffer duplicate() { BlockByteBuffer dup = new BlockByteBuffer(); dup.data = data; dup.buffers = new ByteBuffer[buffers.length]; return dup; } /** * Return a buffer positioned at the given start byte. * It is assumed that the caller will handle blocks in * a thread safe manner. * @param startIndex * @return */ public ByteBuffer getByteBuffer(int block) { if (block < 0 || block >= data.blockCount) { throw new IndexOutOfBoundsException("Invalid block " + block); //$NON-NLS-1$ } int segment = block>>(data.segmentAddressBits-data.blockAddressBits); ByteBuffer bb = buffers[segment]; if (bb == null) { bb = buffers[segment] = data.origBuffers[segment].duplicate(data.direct); } else { bb.rewind(); } int position = (block<<data.blockAddressBits)&(data.segmentSize-1); bb.limit(position + data.blockSize); bb.position(position); return bb; } }