All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.io.AbstractFixedByteArrayBuffer Maven / Gradle / Ivy

/*

 Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

 Contact:
 SYSTAP, LLC DBA Blazegraph
 2501 Calvert ST NW #106
 Washington, DC 20008
 [email protected]

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; version 2 of the License.

 This program 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */
/*
 * Created on Dec 23, 2007
 */

package com.bigdata.io;

import it.unimi.dsi.io.InputBitStream;

import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

import com.bigdata.util.BytesUtil;

/**
 * Efficient absolute get/put operations on a slice of a byte[]. This class is
 * not thread-safe under mutation because the operations are not atomic.
 * Concurrent operations on the same region of the slice can reveal partial
 * updates. This class is abstract. A concrete implementation need only
 * implement {@link #array()} and an appropriate constructor. This allows for
 * use cases where the backing byte[] is extensible. E.g., a fixed slice onto an
 * extensible {@link ByteArrayBuffer}.
 * 
 * @author Bryan Thompson
 * @version $Id$
 */
abstract public class AbstractFixedByteArrayBuffer implements IFixedDataRecord {

//    protected static final Logger log = Logger.getLogger(AbstractFixedByteArrayBuffer.class);
//    
//    protected static final boolean INFO = log.isInfoEnabled();

    /**
     * The start of the slice in the {@link #array()}.
     */
    private final int off;
    
    /**
     * The length of the slice in the {@link #array()}.
     */
    private final int len;
    
    final public int off() {
        
        return off;
        
    }
    
    final public int len() {
        
        return len;
        
    }

    /**
     * A slice wrapping the entire array.
     * 
     * @param array
     *            The array.
     */
    public static FixedByteArrayBuffer wrap(final byte[] array) {

        return new FixedByteArrayBuffer(array, 0/* off */, array.length/* len */);

    }

    /**
     * Protected constructor used to create a slice. The caller is responsible
     * for verifying that the slice is valid for the backing byte[] buffer.
     * 
     * @param off
     *            The offset of the start of the slice.
     * @param len
     *            The length of the slice.
     */
    protected AbstractFixedByteArrayBuffer(final int off, final int len) {

        if (off < 0)
            throw new IllegalArgumentException("off<0");

        if (len < 0)
            throw new IllegalArgumentException("len<0");

        this.off = off;

        this.len = len;

    }

    /*
     * Absolute get/put operations.
     */
    
    /**
     * Verify that an operation starting at the specified offset into the slice
     * and having the specified length is valid against the slice.
     * 
     * @param aoff
     *            The offset into the slice.
     * @param alen
     *            The #of bytes to be addressed starting from that offset.
     * 
     * @return true.
     * 
     * @throws IllegalArgumentException
     *             if the operation is not valid.
     */
    protected boolean rangeCheck(final int aoff, final int alen) {

        if (aoff < 0)
            throw new IndexOutOfBoundsException();

        if (alen < 0)
            throw new IndexOutOfBoundsException();

        if ((aoff + alen) > len) {

            /*
             * The operation run length at that offset would extend beyond the
             * end of the slice.
             */
            
            throw new IndexOutOfBoundsException();
            
        }

        return true;
        
    }

    final public void put(final int pos, final byte[] b) {

        put(pos, b, 0, b.length);

    }

    final public void put(final int dstoff,//
            final byte[] src, final int srcoff, final int srclen) {

        assert rangeCheck(dstoff, srclen);
        
        System.arraycopy(src, srcoff, array(), off + dstoff, srclen);

    }

    final public void get(final int srcoff, final byte[] dst) {
        
        get(srcoff, dst, 0/* dstoff */, dst.length);
        
    }
    
    final public void get(final int srcoff, final byte[] dst, final int dstoff,
            final int dstlen) {

        assert rangeCheck(srcoff, dstlen);

        System.arraycopy(array(), off + srcoff, dst, dstoff, dstlen);

    }

    final public void putByte(final int pos, final byte v) {

        assert rangeCheck(pos, 1);

        // adjust by the offset.
        array()[off + pos] = v;

    }

    final public byte getByte(final int pos) {

        assert rangeCheck(pos, 1);

        // adjust by the offset.
        return array()[off + pos];
        
    }
    
    final public void putShort(int pos, final short v) {

        assert rangeCheck(pos, 2);

        // adjust by the offset.
        pos += off;
        
        // big-endian
        array()[pos++] = (byte) (v >>> 8);
        array()[pos  ] = (byte) (v >>> 0);

    }

    final public short getShort(int pos) {

        assert rangeCheck(pos, 2);

        // adjust by the offset.
        pos += off;
        
        short v = 0;
        
        // big-endian.
        v += (0xff & array()[pos++]) <<  8;
        v += (0xff & array()[pos  ]) <<  0;

        return v;

    }

    final public void putInt(int pos, final int v){

        assert rangeCheck(pos, 4);
        
        // adjust by the offset.
        pos += off;

        array()[pos++] = (byte) (v >>> 24);
        array()[pos++] = (byte) (v >>> 16);
        array()[pos++] = (byte) (v >>> 8);
        array()[pos  ] = (byte) (v >>> 0);

    }

    final public int getInt( int pos ) {

        assert rangeCheck(pos, 4);

        // adjust by the offset.
        pos += off;
        
        int v = 0;
        
        // big-endian.
        v += (0xff & array()[pos++]) << 24;
        v += (0xff & array()[pos++]) << 16;
        v += (0xff & array()[pos++]) <<  8;
        v += (0xff & array()[pos  ]) <<  0;

        return v;
        
    }
    
    final public void putFloat(final int pos, final float f) {

        putInt( pos, Float.floatToIntBits(f) );

    }

    final public float getFloat( final int pos ) {
        
        return Float.intBitsToFloat( getInt( pos ) );
        
    }
    
    final public void putLong( int pos, final long v) {

        assert rangeCheck(pos, 8);
        
        // adjust by the offset.
        pos += off;

        // big-endian.
        array()[pos++] = (byte) (v >>> 56);
        array()[pos++] = (byte) (v >>> 48);
        array()[pos++] = (byte) (v >>> 40);
        array()[pos++] = (byte) (v >>> 32);
        array()[pos++] = (byte) (v >>> 24);
        array()[pos++] = (byte) (v >>> 16);
        array()[pos++] = (byte) (v >>> 8);
        array()[pos  ] = (byte) (v >>> 0);

    }

    final public void putDouble( final int pos, final double d) {

        putLong( pos, Double.doubleToLongBits(d) );
        
    }

    final public long getLong( int pos ) {

        assert rangeCheck(pos, 8);
        
        // adjust by the offset.
        pos += off;
        
        long v = 0L;
        
        // big-endian.
        v += (0xffL & array()[pos++]) << 56;
        v += (0xffL & array()[pos++]) << 48;
        v += (0xffL & array()[pos++]) << 40;
        v += (0xffL & array()[pos++]) << 32;
        v += (0xffL & array()[pos++]) << 24;
        v += (0xffL & array()[pos++]) << 16;
        v += (0xffL & array()[pos++]) <<  8;
        v += (0xffL & array()[pos  ]) <<  0;

        return v;
        
    }
    
    final public double getDouble( final int pos ) {
        
        return Double.longBitsToDouble( getLong( pos ) );
        
    }

    final public boolean getBit(final long bitIndex) {

        assert rangeCheck(BytesUtil.byteIndexForBit(bitIndex), 1);

        // convert off() to a bit offset and then address the bit at the
        // caller's index
        return BytesUtil.getBit(array(), (off << 3) + bitIndex);

    }

    final public boolean setBit(final long bitIndex, final boolean value) {

        assert rangeCheck(BytesUtil.byteIndexForBit(bitIndex), 1);

        // convert off() to a bit offset and then address the bit at the
        // caller's index
        return BytesUtil.setBit(array(), (off << 3) + bitIndex, value);

    }

    final public byte[] toByteArray() {
        
        final byte[] tmp = new byte[len];

        System.arraycopy(array(), off/* srcPos */, tmp/* dst */,
                0/* destPos */, len);
        
        return tmp;
        
    }

    final public ByteBuffer asByteBuffer() {

        return ByteBuffer.wrap(array(), off, len);
        
    }

    public AbstractFixedByteArrayBuffer slice(final int aoff, final int alen) {

        assert rangeCheck(aoff, alen);

        return new AbstractFixedByteArrayBuffer(off() + aoff, alen) {

            public byte[] array() {

                return AbstractFixedByteArrayBuffer.this.array();

            }

        };

    }

    /*
     * IFixedDataRecord
     */
    
    public DataInputBuffer getDataInput() {

        return new DataInputBuffer(array(), off, len);
        
    }

    public InputBitStream getInputBitStream() {

//        /*
//         * We have to double-wrap the buffer to ensure that it reads from just
//         * the slice since InputBitStream does not have a constructor which
//         * accepts a slice of the form (byte[], off, len). [It would be nice if
//         * InputBitStream handled the slice natively since should be faster per
//         * its own javadoc.]
//         * 
//         * Note: The reflection test semantics are not quite what I would want.
//         * If you specify [false] then the code does not even test for the
//         * RepositionableStream interface. Ideally, it would always do that but
//         * skip the reflection on the getChannel() method when it was false.
//         */
//        return new InputBitStream(getDataInput(), 0/* unbuffered */, true/* reflectionTest */);
        
        /*
         * This directly wraps the slice.  This is much faster.
         */
        return new InputBitStream(array(), off, len);

    }

    final public void writeOn(final OutputStream os) throws IOException {
        
        os.write(array(), off, len);
        
    }
    
    final public void writeOn(final DataOutput out) throws IOException {
        
        out.write(array(), off, len);

    }

    final public void writeOn(final OutputStream os, final int aoff,
            final int alen) throws IOException {

        if (aoff < 0) // check starting pos.
            throw new IllegalArgumentException();

        if (aoff + alen > this.len) // check run length.
            throw new IllegalArgumentException();

        os.write(array(), off + aoff, alen);

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy